// http://akelpad.sourceforge.net/forum/viewtopic.php?p=17303#17303
// Version: 2016-06-05
// Author: KDJ
//
// *** Operations on the text from plugins: Coder, ContextMenu, Hotkeys, SpecialChar and ToolBar* ***
//
// Usage:
//   Call("Scripts::Main", 1, "PluginText.js")
//
// Keyboard and mouse:
//   Ctrl+A - select all items on list
//   double click on list item - edit plugin text, if text is opened in current tab - close it
//
// Remarks:
//   The script should be saved in Unicode format.
//   The text is read from plugin settings (.ini file or registry).
//   For the syntax highlighting uses akelmenu.coder by Infocatcher: http://infocatcher.ucoz.net/akelpad/coder/_akelmenu.coder

var oSys     = AkelPad.SystemFunction();
var hMainWnd = AkelPad.GetMainWnd();
var hInstDLL = AkelPad.GetInstanceDll();
var sClass   = "AkelPad::Scripts::" + WScript.ScriptName + "::" + hInstDLL;

GetLangStrings();

if (IsScriptRunning() || IsSDI())
  WScript.Quit();

var DT_UNICODE = 1;
var DT_QWORD   = 2;
var DT_DWORD   = 3;
var DT_WORD    = 4;
var DT_BYTE    = 5;

var oFSO      = new ActiveXObject("Scripting.FileSystemObject");
var bSetInReg = (SendMessage(hMainWnd, 1222 /*AKD_GETMAININFO*/, 5 /*MI_SAVESETTINGS*/, 0) == 1 /*SS_REGISTRY*/);
var nBufSize  = 1024;
var lpBuffer  = AkelPad.MemAlloc(nBufSize);
var lpLVITEM  = AkelPad.MemAlloc(_X64 ? 72 : 60); //sizeof(LVITEM)

AkelPad.MemCopy(lpLVITEM, 0x0001 /*LVIF_TEXT*/, DT_DWORD);
AkelPad.MemCopy(_PtrAdd(lpLVITEM, _X64 ? 24 : 20), lpBuffer, DT_QWORD);
AkelPad.MemCopy(_PtrAdd(lpLVITEM, _X64 ? 32 : 24), nBufSize, DT_DWORD);

var nWndMinW  = 500;
var nWndMinH  = 262;
var nWndPosX  = 240;
var nWndPosY  = 160;
var nWndPosW  = 0;
var nWndPosH  = 0;
var oPlugText = {};
var aFrame    = [];
var sTextDirS;
var hDlg;

ReadIni();

var CLASS = 0;
var HWND  = 1;
var STYLE = 2;
var TXT   = 3;

var aDlg   = [];
var IDSET  = 2000;
var IDDIR  = 2001;
var IDPLUG = 2002;
var IDB1   = 2003;
var IDB2   = 2004;
var IDB3   = 2005;
var IDB4   = 2006;
var IDB5   = 2007;
var IDB6   = 2008;
var IDB7   = 2009;
var IDB8   = 2010;
var IDB9   = 2011;

//0x50000000 - WS_VISIBLE|WS_CHILD
//0x50000080 - WS_VISIBLE|WS_CHILD|SS_NOPREFIX
//0x50818009 - WS_VISIBLE|WS_CHILD|WS_BORDER|WS_TABSTOP|LVS_NOSORTHEADER|LVS_SHOWSELALWAYS|LVS_REPORT
//Windows     CLASS,        HWND,      STYLE, TXT
aDlg[IDSET ]=["STATIC",        0, 0x50000080, bSetInReg ? sTxtSetInReg : sTxtSetInIni];
aDlg[IDDIR ]=["STATIC",        0, 0x50000080, sTxtDir + ExpandDirName(sTextDirS)];
aDlg[IDPLUG]=["SysListView32", 0, 0x50818009, ""];
aDlg[IDB1  ]=["BUTTON",        0, 0x50000000, sTxtEditText + " (F1)"];
aDlg[IDB2  ]=["BUTTON",        0, 0x50000000, sTxtLoadText + " (F2)"];
aDlg[IDB3  ]=["BUTTON",        0, 0x50000000, sTxtSaveTextFile + " (F3)"];
aDlg[IDB4  ]=["BUTTON",        0, 0x50000000, sTxtEditTextFile + " (F4)"];
aDlg[IDB5  ]=["BUTTON",        0, 0x50000000, sTxtLoadTextFile + " (F5)"];
aDlg[IDB6  ]=["BUTTON",        0, 0x50000000, sTxtRenameFile + " (F7)"];
aDlg[IDB7  ]=["BUTTON",        0, 0x50000000, sTxtDeleteFile + " (F8)"];
aDlg[IDB8  ]=["BUTTON",        0, 0x50000000, sTxtChangeDir + " (F9)"];
aDlg[IDB9  ]=["BUTTON",        0, 0x50000000, (bSetInReg ? sTxtOpenRegEdit : sTxtEditIniFile) + " (Shift+F4)"];

if (AkelPad.WindowSubClass(1 /*WSC_MAINPROC*/, MainCallback, 1048 /*AKDN_FRAME_DESTROY*/, 1078 /*AKDN_OPENDOCUMENT_FINISH*/))
{
  AkelPad.ScriptNoMutex();
  AkelPad.WindowRegisterClass(sClass);

  hDlg = oSys.Call("User32::CreateWindowExW",
    0,              //dwExStyle
    sClass,         //lpClassName
    sTxtScriptName, //lpWindowName
    0x90CE0000,     //WS_POPUP|WS_VISIBLE|WS_CAPTION|WS_MINIMIZEBOX|WS_SYSMENU|WS_SIZEBOX
    nWndPosX,       //x
    nWndPosY,       //y
    nWndPosW,       //nWidth
    nWndPosH,       //nHeight
    hMainWnd,       //hWndParent
    0,              //ID
    hInstDLL,       //hInstance
    DialogCallback);//Script function callback. To use it class must be registered by WindowRegisterClass.

  AkelPad.WindowGetMessage();
  AkelPad.WindowUnregisterClass(sClass);
  AkelPad.WindowUnsubClass(1 /*WSC_MAINPROC*/);
}

AkelPad.MemFree(lpBuffer);
AkelPad.MemFree(lpLVITEM);

function MainCallback(hWnd, uMsg, wParam, lParam)
{
  if ((uMsg == 1048 /*AKDN_FRAME_DESTROY*/) || (uMsg == 1078 /*AKDN_OPENDOCUMENT_FINISH*/) && (lParam == 0 /*EOD_SUCCESS*/))
  {
    var lpFrame = (uMsg == 1048 /*AKDN_FRAME_DESTROY*/) ? lParam : SendMessage(hMainWnd, 1290 /*AKD_FRAMEFINDW*/, 1 /*FWF_CURRENT*/, 0);

    for (var i = 0; i < aFrame.length; ++i)
    {
      if (aFrame[i] == lpFrame)
      {
        aFrame[i] = 0;
        break;
      }
    }
  }

  return 0;
}

function DialogCallback(hWnd, uMsg, wParam, lParam)
{
  var hGuiFont, nID, i;

  if (uMsg == 1) //WM_CREATE
  {
    hGuiFont = oSys.Call("Gdi32::GetStockObject", 17 /*DEFAULT_GUI_FONT*/);

    for (i = IDSET; i < aDlg.length; ++i)
    {
      aDlg[i][HWND] = oSys.Call("User32::CreateWindowExW",
        0,             //dwExStyle
        aDlg[i][CLASS],//lpClassName
        aDlg[i][TXT],  //lpWindowName
        aDlg[i][STYLE],//dwStyle
        0,             //x
        0,             //y
        0,             //nWidth
        0,             //nHeight
        hWnd,          //hWndParent
        i,             //ID
        hInstDLL,      //hInstance
        0);            //lpParam

      SendMessage(aDlg[i][HWND], 48 /*WM_SETFONT*/, hGuiFont, true);
    }

    FillPlugList();

    if (! GetItemCountLV())
    {
      AkelPad.MessageBox(hWnd, sTxtNoTexts, sTxtScriptName, 0x30 /*MB_ICONWARNING*/);
      oSys.Call("User32::DestroyWindow", hWnd);
    }
  }

  else if (uMsg == 7) //WM_SETFOCUS
    oSys.Call("User32::SetFocus", aDlg[IDPLUG][HWND]);

  else if (uMsg == 36) //WM_GETMINMAXINFO
  {
    AkelPad.MemCopy(_PtrAdd(lParam, 24), nWndMinW, DT_DWORD); //ptMinTrackSize_x
    AkelPad.MemCopy(_PtrAdd(lParam, 28), nWndMinH, DT_DWORD); //ptMinTrackSize_y
  }

  else if (uMsg == 5) //WM_SIZE
    ResizeDlg(LoWord(lParam), HiWord(lParam));

  else if (uMsg == 15) //WM_PAINT
    PaintSizeGrip();

  else if (uMsg == 256) //WM_KEYDOWN
  {
    if ((wParam == 0x70 /*VK_F1*/) && (! Ctrl()) && (! Shift()) && (! Alt()))
      EditText(0);
    else if ((wParam == 0x71 /*VK_F2*/) && (! Ctrl()) && (! Shift()) && (! Alt()))
      LoadTextToPlugin();
    else if ((wParam == 0x72 /*VK_F3*/) && (! Ctrl()) && (! Shift()) && (! Alt()))
      EditText(1);
    else if ((wParam == 0x73 /*VK_F4*/) && (! Ctrl()) && (! Alt()))
    {
      if (Shift())
        bSetInReg ? OpenInRegEdit() : EditIniFile();
      else      
        EditTextFile();
    }
    else if ((wParam == 0x74 /*VK_F5*/) && (! Ctrl()) && (! Shift()) && (! Alt()))
      LoadTextToPlugin(1);
    else if ((wParam == 0x76 /*VK_F7*/) && (! Ctrl()) && (! Shift()) && (! Alt()))
      RenameTextFile();
    else if (((wParam == 0x77 /*VK_F8*/) || (wParam == 0x2E /*VK_DELETE*/)) && (! Ctrl()) && (! Alt()))
      DeleteTextFile();
    else if ((wParam == 0x78 /*VK_F9*/) && (! Ctrl()) && (! Shift()) && (! Alt()))
      ChangeTextsDir();
    else if ((wParam == 0x41 /*A key*/) && Ctrl() && (! Shift()) && (! Alt()))
    {
      for (i = 0; i < GetItemCountLV(); ++i)
        SetSelLV(i);
    }
  }

  else if ((uMsg == 0x004E /*WM_NOTIFY*/) && (wParam == IDPLUG))
  {
    switch (AkelPad.MemRead(_PtrAdd(lParam, _X64 ? 16 : 8), DT_DWORD))
    {
      case -3 : //NM_DBLCLK
        if (AkelPad.MemRead(_PtrAdd(lParam, _X64 ? 24 : 12) /*NMITEMACTIVATE.iItem*/, DT_DWORD) > -1)
        {
          EditText(0, 1);
          break;
        }
      case -2 : //NM_CLICK
      case -5 : //NM_RCLICK
      case -6 : //NM_RDBLCLK
        if (AkelPad.MemRead(_PtrAdd(lParam, _X64 ? 24 : 12) /*NMITEMACTIVATE.iItem*/, DT_DWORD) == -1)
          SetSelLV(SendMessage(aDlg[IDPLUG][HWND], 0x100C /*LVM_GETNEXTITEM*/, -1, 0x0001 /*LVNI_FOCUSED*/));
    }
  }

  else if (uMsg == 273) //WM_COMMAND
  {
    nID = LoWord(wParam);

    if ((nID >= IDB1) && (nID <= IDB9))
    {
      SendMessage(lParam, 0x00F4 /*BM_SETSTYLE*/, 0 /*BS_PUSHBUTTON*/, 0);

      if (nID == IDB1)
        EditText(0);
      else if (nID == IDB2)
        LoadTextToPlugin();
      else if (nID == IDB3)
        EditText(1);
      else if (nID == IDB4)
        EditTextFile();
      else if (nID == IDB5)
        LoadTextToPlugin(1);
      else if (nID == IDB6)
        RenameTextFile();
      else if (nID == IDB7)
        DeleteTextFile();
      else if (nID == IDB8)
        ChangeTextsDir();
      else if (nID == IDB9)
        bSetInReg ? OpenInRegEdit() : EditIniFile();

      oSys.Call("User32::SetFocus", aDlg[IDPLUG][HWND]);
    }

    else if (nID == 2 /*IDCANCEL*/)
      SendMessage(hWnd, 16 /*WM_CLOSE*/, 0, 0);
  }

  else if (uMsg == 16) //WM_CLOSE
  {
    WriteIni();
    oSys.Call("User32::PostQuitMessage", 0);
    oSys.Call("User32::DestroyWindow", hWnd);
  }

  return 0;
}

function LoWord(nDwNum)
{
  return nDwNum & 0xFFFF;
}

function HiWord(nDwNum)
{
  return (nDwNum >> 16) & 0xFFFF;
}

function Ctrl()
{
  return oSys.Call("User32::GetKeyState", 0x11 /*VK_CONTROL*/) & 0x8000;
}

function Shift()
{
  return oSys.Call("User32::GetKeyState", 0x10 /*VK_SHIFT*/) & 0x8000;
}

function Alt()
{
  return oSys.Call("user32::GetKeyState", 0x12 /*VK_MENU*/) & 0x8000;
}

function SendMessage(hWnd, uMsg, wParam, lParam)
{
  return AkelPad.SendMessage(hWnd, uMsg, wParam, lParam);
}

function ResizeDlg(nW, nH)
{
  var nFlags = 0x14; //SWP_NOACTIVATE|SWP_NOZORDER
  var nBW    = Math.round((nW - 4 * 8) / 3);
  var i;

  for (i = 0; i < 2; ++i)
  {
    oSys.Call("User32::SetWindowPos",
      aDlg[IDSET + i][HWND], 0,
      8,
      10 + 15 * i,
      nW - 2 * 8,
      13,
      nFlags);
  }
  oSys.Call("User32::SetWindowPos",
    aDlg[IDPLUG][HWND], 0,
    8,
    45,
    nW - 2 * 8,
    nH - 3 * 23 - 2 * 2 - 2 * 8 - 45,
    nFlags);
  for (i = 0; i < 3; ++i)
  {
    oSys.Call("User32::SetWindowPos",
      aDlg[IDB1 + i][HWND], 0,
      8,
      nH - 3 * 23 - 2 * 2 - 8 + 25 * i,
      nBW,
      23,
      nFlags);
    oSys.Call("User32::SetWindowPos",
      aDlg[IDB4 + i][HWND], 0,
      nBW + 2 * 8,
      nH - 3 * 23 - 2 * 2 - 8 + 25 * i,
      nBW,
      23,
      nFlags);
    oSys.Call("User32::SetWindowPos",
      aDlg[IDB7 + i][HWND], 0,
      nW - nBW - 8,
      nH - 3 * 23 - 2 * 2 - 8 + 25 * i,
      nBW,
      23,
      nFlags);
  }

  for (i = 0; i < 3; ++i)
    SendMessage(aDlg[IDPLUG][HWND], 0x101E /*LVM_SETCOLUMNWIDTH*/, i, -2 /*LVSCW_AUTOSIZE_USEHEADER*/);

  SendMessage(aDlg[IDPLUG][HWND], 0x1013 /*LVM_ENSUREVISIBLE*/, GetNextSelLV(-1), false);
  oSys.Call("user32::InvalidateRect", hDlg, 0, true);
}

function PaintSizeGrip()
{
  var lpPaint = AkelPad.MemAlloc(_X64 ? 72 : 64); //sizeof(PAINTSTRUCT)
  var lpRect  = AkelPad.MemAlloc(16); //sizeof(RECT)
  var hDC;

  if (hDC = oSys.Call("User32::BeginPaint", hDlg, lpPaint))
  {
    oSys.Call("User32::GetClientRect", hDlg, lpRect);

    AkelPad.MemCopy(_PtrAdd(lpRect, 0), AkelPad.MemRead(_PtrAdd(lpRect,  8), DT_DWORD) - oSys.Call("User32::GetSystemMetrics",  2 /*SM_CXVSCROLL*/), DT_DWORD);
    AkelPad.MemCopy(_PtrAdd(lpRect, 4), AkelPad.MemRead(_PtrAdd(lpRect, 12), DT_DWORD) - oSys.Call("User32::GetSystemMetrics", 20 /*SM_CYVSCROLL*/), DT_DWORD);

    oSys.Call("User32::DrawFrameControl", hDC, lpRect, 3 /*DFC_SCROLL*/, 0x8 /*DFCS_SCROLLSIZEGRIP*/);
    oSys.Call("User32::EndPaint", hDlg, lpPaint);
  }

  AkelPad.MemFree(lpPaint);
  AkelPad.MemFree(lpRect);
}

function GetItemCountLV()
{
  return SendMessage(aDlg[IDPLUG][HWND], 0x1004 /*LVM_GETITEMCOUNT*/, 0, 0);
}

function GetItemTextLV(nItem, nSubItem)
{
  AkelPad.MemCopy(_PtrAdd(lpLVITEM, 8), nSubItem, DT_DWORD);
  SendMessage(aDlg[IDPLUG][HWND], 0x1073 /*LVM_GETITEMTEXTW*/, nItem, lpLVITEM);
  return AkelPad.MemRead(lpBuffer, DT_UNICODE);
}

function SetItemTextLV(nItem, nSubItem, sText)
{
  AkelPad.MemCopy(_PtrAdd(lpLVITEM, 4), nItem,    DT_DWORD);
  AkelPad.MemCopy(_PtrAdd(lpLVITEM, 8), nSubItem, DT_DWORD);
  AkelPad.MemCopy(lpBuffer, sText, DT_UNICODE);
  SendMessage(aDlg[IDPLUG][HWND], 0x1074 /*LVM_SETITEMTEXTW*/, nItem, lpLVITEM);
}

function GetNextSelLV(nItem)
{
  return SendMessage(aDlg[IDPLUG][HWND], 0x100C /*LVM_GETNEXTITEM*/, nItem, 0x0002 /*LVNI_SELECTED*/);
}

function GetSelArrayLV()
{
  var aSel  = [];
  var nItem = -1;

  while (true)
  {
    nItem = GetNextSelLV(nItem);

    if (nItem < 0)
      break;

    aSel.push(nItem);
  }

  return aSel;
}

function SetSelLV(nItem)
{
  AkelPad.MemCopy(_PtrAdd(lpLVITEM, 12), 0x0003 /*LVIS_SELECTED|LVIS_FOCUSED*/, DT_DWORD);
  AkelPad.MemCopy(_PtrAdd(lpLVITEM, 16), 0x0003 /*LVIS_SELECTED|LVIS_FOCUSED*/, DT_DWORD);
  SendMessage(aDlg[IDPLUG][HWND], 0x102B /*LVM_SETITEMSTATE*/, nItem, lpLVITEM);
  SendMessage(aDlg[IDPLUG][HWND], 0x1013 /*LVM_ENSUREVISIBLE*/, nItem, false);
}

function InsertItemLV(nItem, aItems)
{
  var i;

  AkelPad.MemCopy(_PtrAdd(lpLVITEM, 4), nItem, DT_DWORD);
  AkelPad.MemCopy(_PtrAdd(lpLVITEM, 8),     0, DT_DWORD);
  AkelPad.MemCopy(lpBuffer, aItems[0], DT_UNICODE);
  SendMessage(aDlg[IDPLUG][HWND], 0x104D /*LVM_INSERTITEMW*/, 0, lpLVITEM);

  for (i = 1; i < aItems.length; ++i)
  {
    AkelPad.MemCopy(_PtrAdd(lpLVITEM, 8), i, DT_DWORD);
    AkelPad.MemCopy(lpBuffer, aItems[i], DT_UNICODE);
    SendMessage(aDlg[IDPLUG][HWND], 0x1074 /*LVM_SETITEMTEXTW*/, nItem, lpLVITEM);
  }
}

function FillPlugList()
{
  var aPlugList  = [];
  var lpLVCOLUMN = AkelPad.MemAlloc(_X64 ? 56 : 44); //sizeof(LVCOLUMN)
  var nMask      = 5; //LVCF_FMT|LVCF_TEXT
  var aFmt       = [0 /*LVCFMT_LEFT*/, 0, 0]; //Alignment of the column header
  var aText      = [AkelPad.MemStrPtr(sTxtPlugin), AkelPad.MemStrPtr(sTxtKeyTextName), AkelPad.MemStrPtr(sTxtTextFile)];
  var i;

  if (bSetInReg)
    GetPluginsFromReg(aPlugList);
  else
    GetPluginsFromIni(aPlugList);

  SendMessage(aDlg[IDPLUG][HWND], 0x1036 /*LVM_SETEXTENDEDLISTVIEWSTYLE*/, 0, 0x0021 /*LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES*/);
  AkelPad.MemCopy(lpLVCOLUMN, nMask, DT_DWORD);

  for (i = 0; i < aFmt.length; ++i)
  {
    AkelPad.MemCopy(_PtrAdd(lpLVCOLUMN, 4), aFmt[i], DT_DWORD);
    AkelPad.MemCopy(_PtrAdd(lpLVCOLUMN, _X64 ? 16 : 12), aText[i], DT_QWORD);
    SendMessage(aDlg[IDPLUG][HWND], 0x1061 /*LVM_INSERTCOLUMNW*/, i, lpLVCOLUMN);
  }
  AkelPad.MemFree(lpLVCOLUMN);

  for (i = 0; i < aPlugList.length; ++i)
  {
    if (oPlugText[aPlugList[i][0]] &&
        oPlugText[aPlugList[i][0]][aPlugList[i][1]] &&
        oPlugText[aPlugList[i][0]][aPlugList[i][1]].File)
      aPlugList[i].push(oPlugText[aPlugList[i][0]][aPlugList[i][1]].File);
    else
      aPlugList[i].push(aPlugList[i][0] + "." + aPlugList[i][1].replace(/[<>:"/\\|?*]/g, "_") + ".akelmenu");

    InsertItemLV(i, aPlugList[i]);
    aFrame[i] = 0;

    if (oPlugText[aPlugList[i][0]] &&
        oPlugText[aPlugList[i][0]][aPlugList[i][1]] &&
        oPlugText[aPlugList[i][0]][aPlugList[i][1]].Sel)
      SetSelLV(i);
  }

  if (GetNextSelLV(-1) < 0)
    SetSelLV(0);
}

function GetPluginsFromIni(aPlugList)
{
  var oRE1      = /^\/.+(?==)/gm;
  var oRE2      = /^.+Text(?==)/gm;
  var aPlugName = [["Coder", oRE1], ["ContextMenu", oRE2], ["Hotkeys", oRE2], ["SpecialChar", oRE2]];
  var sPlugDir  = AkelPad.GetAkelDir(4 /*ADTYPE_PLUGS*/) + "\\";
  var hFindFile = oSys.Call("Kernel32::FindFirstFileW", sPlugDir + "ToolBar*.ini", lpBuffer);
  var sToolBarName;
  var aText;
  var i, n;

  if (hFindFile != -1) //INVALID_HANDLE_VALUE
  {
    do
    {
      if (! (AkelPad.MemRead(lpBuffer, DT_DWORD) & 16 /*FILE_ATTRIBUTE_DIRECTORY*/))
      {
        sToolBarName = AkelPad.MemRead(_PtrAdd(lpBuffer, 44 /*offsetof(WIN32_FIND_DATAW, cFileName)*/), DT_UNICODE);
        sToolBarName = sToolBarName.substring(sToolBarName.lastIndexOf("\\") + 1, sToolBarName.lastIndexOf("."));
        aPlugName.push([sToolBarName, oRE2]);
      }
    }
    while (oSys.Call("Kernel32::FindNextFileW", hFindFile, lpBuffer));

    oSys.Call("Kernel32::FindClose", hFindFile);
  }

  for (i = 0; i < aPlugName.length; ++i)
  {
    if ((oFSO.FileExists(sPlugDir + aPlugName[i][0] + ".ini")) &&
        (aText = AkelPad.ReadFile(sPlugDir + aPlugName[i][0] + ".ini", 0x10 /*OD_ADT_NOMESSAGES*/, 1200 /*UTF-16LE*/, 1 /*bBOM*/).match(aPlugName[i][1])))
    {
      for (n = 0; n < aText.length; ++n)
        aPlugList.push([aPlugName[i][0], aText[n]]);
    }
  }
}

function GetPluginsFromReg(aPlugList)
{
  var oRE1       = /^\/.+/;
  var oRE2       = /.+Text$/;
  var aPlugName  = [["Coder", oRE1], ["ContextMenu", oRE2], ["Hotkeys", oRE2], ["SpecialChar", oRE2]];
  var sPlugKey   = "Software\\Akelsoft\\AkelPad\\Plugs";
  var lpCount    = AkelPad.MemAlloc(4);
  var lpNameLen  = AkelPad.MemAlloc(4);
  var lpType     = AkelPad.MemAlloc(4);
  var lpName;
  var nNameLen;
  var sToolBarName;
  var sValueName;
  var hKey;
  var i;

  if (hKey = GetRegKeyHandle(0x80000001 /*HKEY_CURRENT_USER*/, sPlugKey, 0x0009 /*KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE*/))
  {
    if (! oSys.Call("Advapi32::RegQueryInfoKeyW",
            hKey,     //hKey
            0,        //lpClass
            0,        //lpcClass
            0,        //lpReserved
            lpCount,  //lpcSubKeys
            lpNameLen,//lpcMaxSubKeyLen
            0,        //lpcMaxClassLen
            0,        //lpcValues
            0,        //lpcMaxValueNameLen
            0,        //lpcMaxValueLen
            0,        //lpcbSecurityDescri
            0))       //lpftLastWriteTime
    {
      nNameLen = AkelPad.MemRead(lpNameLen, DT_DWORD) + 1;
      lpName   = AkelPad.MemAlloc(nNameLen * 2);

      for (i = 0; i < AkelPad.MemRead(lpCount, DT_DWORD); ++i)
      {
        AkelPad.MemCopy(lpNameLen, nNameLen, DT_DWORD);
        if (! oSys.Call("Advapi32::RegEnumKeyExW",
                hKey,     //hKey
                i,        //dwIndex
                lpName,   //lpName
                lpNameLen,//lpcName
                0,        //lpReserved
                0,        //lpClass
                0,        //lpcClass
                0))       //lpftLastWriteTime
        {
          sToolBarName = AkelPad.MemRead(lpName, DT_UNICODE);
          if (sToolBarName.indexOf("ToolBar") == 0)
            aPlugName.push([sToolBarName, oRE2]);
        }
      }
      AkelPad.MemFree(lpName);
    }
    oSys.Call("Advapi32::RegCloseKey", hKey);
  }

  for (i = 0; i < aPlugName.length; ++i)
  {
    if (hKey = GetRegKeyHandle(0x80000001 /*HKEY_CURRENT_USER*/, sPlugKey + "\\" + aPlugName[i][0], 0x0001 /*KEY_QUERY_VALUE*/))
    {
      if (! oSys.Call("Advapi32::RegQueryInfoKeyW",
              hKey,     //hKey
              0,        //lpClass
              0,        //lpcClass
              0,        //lpReserved
              0,        //lpcSubKeys
              0,        //lpcMaxSubKeyLen
              0,        //lpcMaxClassLen
              lpCount,  //lpcValues
              lpNameLen,//lpcMaxValueNameLen
              0,        //lpcMaxValueLen
              0,        //lpcbSecurityDescri
              0))       //lpftLastWriteTime
      {
        nNameLen = AkelPad.MemRead(lpNameLen, DT_DWORD) + 1;
        lpName   = AkelPad.MemAlloc(nNameLen * 2);

        for (n = 0; n < AkelPad.MemRead(lpCount, DT_DWORD); ++n)
        {
          AkelPad.MemCopy(lpNameLen, nNameLen, DT_DWORD);
          if (! oSys.Call("Advapi32::RegEnumValueW",
                  hKey,     //hKey
                  n,        //dwIndex
                  lpName,   //lpValueName
                  lpNameLen,//lpcchValueName
                  0,        //lpReserved
                  lpType,   //lpType
                  0,        //lpData
                  0))       //lpcbData
          {
            sValueName = AkelPad.MemRead(lpName, DT_UNICODE);
            if (aPlugName[i][1].test(sValueName) && (AkelPad.MemRead(lpType, DT_DWORD) == 3 /*REG_BINARY*/))
              aPlugList.push([aPlugName[i][0], sValueName]);
          }
        }
        AkelPad.MemFree(lpName);
      }
      oSys.Call("Advapi32::RegCloseKey", hKey);
    }
  }

  AkelPad.MemFree(lpCount);
  AkelPad.MemFree(lpNameLen);
  AkelPad.MemFree(lpType);
}

function GetRegKeyHandle(hParentKey, sSubKey, nAccess)
{
  var lpKey  = AkelPad.MemAlloc(_X64 ? 8 : 4);
  var hKey   = 0;
  var nError = oSys.Call("Advapi32::RegOpenKeyExW",
                 hParentKey,//hKey
                 sSubKey,   //lpSubKey
                 0,         //ulOptions
                 nAccess,   //samDesired
                 lpKey);    //phkResult

  if (! nError)
    hKey = AkelPad.MemRead(lpKey, DT_QWORD);

  AkelPad.MemFree(lpKey);

  return hKey;
}

function EditText(bSave, bMouseClick)
{
  var sTextDir  = ExpandDirName(sTextDirS);
  var sPlugDir  = AkelPad.GetAkelDir(4 /*ADTYPE_PLUGS*/) + "\\";
  var aSel      = GetSelArrayLV();
  var aSaved    = [];
  var aNotSaved = [];
  var sMessage  = "";
  var hKey;
  var lpType;
  var lpData;
  var lpDataLen;
  var sIniFile;
  var oRE;
  var bWasSaved;
  var i, n;

  if (bSave && (AkelPad.MessageBox(hDlg, sTxtWantSave, sTxtScriptName, 0x21 /*MB_DEFBUTTON1|MB_ICONQUESTION|MB_OKCANCEL*/) != 1 /*IDOK*/))
    return;

  if (bSetInReg)
  {
    lpType    = AkelPad.MemAlloc(4);
    lpDataLen = AkelPad.MemAlloc(4);

    for (i = 0; i < aSel.length; ++i)
    {
      bWasSaved = false;

      if (hKey = GetRegKeyHandle(0x80000001 /*HKEY_CURRENT_USER*/, "Software\\Akelsoft\\AkelPad\\Plugs\\" + GetItemTextLV(aSel[i], 0), 0x0001 /*KEY_QUERY_VALUE*/))
      {
        oSys.Call("Advapi32::RegQueryInfoKeyW",
          hKey,     //hKey
          0,        //lpClass
          0,        //lpcClass
          0,        //lpReserved
          0,        //lpcSubKeys
          0,        //lpcMaxSubKeyLen
          0,        //lpcMaxClassLen
          0,        //lpcValues
          0,        //lpcMaxValueNameLen
          lpDataLen,//lpcMaxValueLen
          0,        //lpcbSecurityDescri
          0);       //lpftLastWriteTime

        lpData = AkelPad.MemAlloc(AkelPad.MemRead(lpDataLen, DT_DWORD));

        if ((! oSys.Call("Advapi32::RegQueryValueExW",
                 hKey,                     //hKey
                 GetItemTextLV(aSel[i], 1),//lpValueName
                 0,                        //lpReserved
                 lpType,                   //lpType
                 lpData,                   //lpData
                 lpDataLen))               //lpcbData
            && (AkelPad.MemRead(lpType, DT_DWORD) == 3 /*REG_BINARY*/))
        {
          if (bSave)
          {
            if (AkelPad.WriteFile(sTextDir + GetItemTextLV(aSel[i], 2), AkelPad.MemRead(lpData, DT_UNICODE), -1, 1200 /*UTF-16LE*/, 1 /*bBOM*/) == 0 /*ESD_SUCCESS*/)
            {
              aSaved.push(aSel[i]);
              bWasSaved = true;
            }
          }
          else
            OpenText(lpData, aSel[i], bMouseClick);
        }

        oSys.Call("Advapi32::RegCloseKey", hKey);
        AkelPad.MemFree(lpData);
      }

      if (bSave && (! bWasSaved))
        aNotSaved.push(aSel[i]);
    }

    AkelPad.MemFree(lpType);
    AkelPad.MemFree(lpDataLen);
  }

  else
  {
    for (i = 0; i < aSel.length; ++i)
    {
      bWasSaved = false;
      sIniFile  = sPlugDir + GetItemTextLV(aSel[i], 0) + ".ini";

      if (oFSO.FileExists(sIniFile))
      {
        oRE = new RegExp(escapeRegExp(GetItemTextLV(aSel[i], 1)) + "=([\\dA-F]*)");

        if (oRE.test(AkelPad.ReadFile(sIniFile, 0x10 /*OD_ADT_NOMESSAGES*/, 1200 /*UTF-16LE*/, 1 /*bBOM*/)))
        {
          lpData = AkelPad.MemAlloc(RegExp.$1.length / 2);

          for (n = 0; n < RegExp.$1.length; n += 2)
            AkelPad.MemCopy(_PtrAdd(lpData, n / 2), parseInt(RegExp.$1.substr(n, 2), 16), DT_BYTE);

          if (bSave)
          {
            if (AkelPad.WriteFile(sTextDir + GetItemTextLV(aSel[i], 2), AkelPad.MemRead(lpData, DT_UNICODE), -1, 1200 /*UTF-16LE*/, 1 /*bBOM*/) == 0 /*ESD_SUCCESS*/)
            {
              aSaved.push(aSel[i]);
              bWasSaved = true;
            }
          }
          else
            OpenText(lpData, aSel[i], bMouseClick);

          AkelPad.MemFree(lpData);
        }
      }

      if (bSave && (! bWasSaved))
        aNotSaved.push(aSel[i]);
    }
  }

  if (bSave)
  {
    if (aSaved.length)
    {
      sMessage += sTxtSaved + "\n";
      for (i = 0; i < aSaved.length; ++i)
        sMessage += sTextDir + GetItemTextLV(aSaved[i], 2) + "\n";
    }

    if (aNotSaved.length)
    {
      sMessage += "\n" + sTxtNotSaved + "\n";
      for (i = 0; i < aNotSaved.length; ++i)
        sMessage += sTextDir + GetItemTextLV(aNotSaved[i], 2) + "\n";
    }

    AkelPad.MessageBox(hDlg, sMessage, sTxtScriptName, 0x40 /*MB_ICONINFORMATION*/);
  }
}

function OpenText(lpData, nSel, bMouseClick)
{
  var hFocus;

  if (aFrame[nSel])
  {
    if (bMouseClick && (aFrame[nSel] == SendMessage(hMainWnd, 1290 /*AKD_FRAMEFINDW*/, 1 /*FWF_CURRENT*/, 0)))
    {
      hFocus = oSys.Call("User32::GetFocus");

      oSys.Call("User32::EnableWindow", hDlg, 0);
      AkelPad.ScriptNoMutex(0x20 /*ULT_UNLOCKSENDMESSAGE*/);

      AkelPad.Command(4318 /*IDM_WINDOW_FRAMECLOSE*/);

      AkelPad.ScriptNoMutex(0x10 /*ULT_LOCKSENDMESSAGE*/);
      oSys.Call("User32::EnableWindow", hDlg, 1);
      oSys.Call("User32::SetFocus", hFocus);
    }
    else
      SendMessage(hMainWnd, 1285 /*AKD_FRAMEACTIVATE*/, 0, aFrame[nSel]);
  }
  else
  {
    var sTabText = GetItemTextLV(nSel, 0) + ": " + GetItemTextLV(nSel, 1);
    var lpText   = AkelPad.MemAlloc((sTabText.length + 1) * 2);
    var lpTCITEM = AkelPad.MemAlloc(_X64 ? 40 : 28 /*sizeof(TCITEM)*/);
    var hWndTab  = SendMessage(hMainWnd, 1222 /*AKD_GETMAININFO*/, 13 /*MI_WNDTAB*/, 0);
    var nTab;

    SendMessage(hMainWnd, 273 /*WM_COMMAND*/, 4101 /*IDM_FILE_NEW*/, 1 /*lParam=TRUE*/);
    AkelPad.Command(4125 /*IDM_FILE_REOPENAS_UTF16LE*/);
    AkelPad.Command(4186 /*IDM_EDIT_NEWLINE_MAC*/);
    oSys.Call("User32::SetWindowTextW", AkelPad.GetEditWnd(), lpData);

    if (AkelPad.IsPluginRunning("Coder::HighLight"))
      AkelPad.Call("Coder::Settings", 1, "akelmenu");

    aFrame[nSel] = SendMessage(hMainWnd, 1290 /*AKD_FRAMEFINDW*/, 1 /*FWF_CURRENT*/, 0);
    nTab         = SendMessage(hWndTab, 4875 /*TCM_GETCURSEL*/, 0, 0);

    AkelPad.MemCopy(lpText, sTabText, 1 /*DT_UNICODE*/);
    AkelPad.MemCopy(lpTCITEM, 1 /*TCIF_TEXT*/, 3 /*DT_DWORD*/); //mask
    AkelPad.MemCopy(_PtrAdd(lpTCITEM, _X64 ? 16 : 12), lpText, 2 /*DT_QWORD*/); //pszText

    SendMessage(hWndTab, 4925 /*TCM_SETITEMW*/, nTab, lpTCITEM);

    AkelPad.MemFree(lpTCITEM);
    AkelPad.MemFree(lpText);
  }
}

function LoadTextToPlugin(bFromFile)
{
  var nCurFrame  = SendMessage(hMainWnd, 1290 /*AKD_FRAMEFINDW*/, 1 /*FWF_CURRENT*/, 0);
  var sTextDir   = ExpandDirName(sTextDirS);
  var sPlugDir   = AkelPad.GetAkelDir(4 /*ADTYPE_PLUGS*/) + "\\";
  var aSel       = GetSelArrayLV();
  var aLoaded    = [];
  var aNotLoaded = [];
  var sMessage   = "";
  var sPrevPlug  = "";
  var hKey;
  var lpType;
  var sIniFile;
  var sBakFile;
  var sText;
  var sIniText;
  var nSize;
  var lpData;
  var oRE;
  var bWasLoaded;
  var i, n;

  if (AkelPad.MessageBox(hDlg, sTxtWantLoad, sTxtScriptName, 0x21 /*MB_DEFBUTTON1|MB_ICONQUESTION|MB_OKCANCEL*/) != 1 /*IDOK*/)
    return;

  if (bSetInReg)
  {
    lpType = AkelPad.MemAlloc(4);

    for (i = 0; i < aSel.length; ++i)
    {
      bWasLoaded = false;
      hKey       = GetRegKeyHandle(0x80000001 /*HKEY_CURRENT_USER*/, "Software\\Akelsoft\\AkelPad\\Plugs\\" + GetItemTextLV(aSel[i], 0), 0x0003 /*KEY_SET_VALUE|KEY_QUERY_VALUE*/);
      sText      = bFromFile ? AkelPad.ReadFile(sTextDir + GetItemTextLV(aSel[i], 2), 0x10 /*OD_ADT_NOMESSAGES*/, 1200 /*UTF-16LE*/, 1 /*bBOM*/) : GetTextAE(aSel[i]);

      if (hKey
          && (! oSys.Call("Advapi32::RegQueryValueExW",
                  hKey,                     //hKey
                  GetItemTextLV(aSel[i], 1),//lpValueName
                  0,                        //lpReserved
                  lpType,                   //lpType
                  0,                        //lpData
                  0))                       //lpcbData
          && (AkelPad.MemRead(lpType, DT_DWORD) == 3 /*REG_BINARY*/)
          && sText)
      {
        //create registry key backup file
        if (GetItemTextLV(aSel[i], 0) != sPrevPlug)
        {
          sBakFile = sTextDir + GetItemTextLV(aSel[i], 0) + ".reg.bak";
          DeleteFile(sBakFile);
          AkelPad.Exec("reg.exe export HKEY_CURRENT_USER\\Software\\Akelsoft\\AkelPad\\Plugs\\" + GetItemTextLV(aSel[i], 0) + " " + sBakFile, "", 1);
          sPrevPlug = GetItemTextLV(aSel[i], 0);
        }

        nSize  = (sText.length + 1) * 2;
        lpData = AkelPad.MemAlloc(nSize);
        AkelPad.MemCopy(lpData, sText, DT_UNICODE);

        if ((! oSys.Call("Advapi32::RegSetValueExW",
                 hKey,                     //hKey
                 GetItemTextLV(aSel[i], 1),//lpValueName
                 0,                        //Reserved
                 3,                        //dwType = REG_BINARY
                 lpData,                   //lpData
                 nSize)))                  //cbData
        {
          aLoaded.push(aSel[i]);
          bWasLoaded = true;
        }

        AkelPad.MemFree(lpData);
      }

      oSys.Call("Advapi32::RegCloseKey", hKey);
      if (! bWasLoaded)
        aNotLoaded.push(aSel[i]);
    }

    AkelPad.MemFree(lpType);
  }

  else
  {
    for (i = 0; i < aSel.length; ++i)
    {
      bWasLoaded = false;
      sIniFile   = sPlugDir + GetItemTextLV(aSel[i], 0) + ".ini";
      sText      = bFromFile ? AkelPad.ReadFile(sTextDir + GetItemTextLV(aSel[i], 2), 0x10 /*OD_ADT_NOMESSAGES*/, 1200 /*UTF-16LE*/, 1 /*bBOM*/) : GetTextAE(aSel[i]);

      if (oFSO.FileExists(sIniFile) && sText)
      {
        oRE      = new RegExp(escapeRegExp(GetItemTextLV(aSel[i], 1)) + "=[\\dA-F]*");
        sIniText = AkelPad.ReadFile(sIniFile, 0x10 /*OD_ADT_NOMESSAGES*/, 1200 /*UTF-16LE*/, 1 /*bBOM*/);
        nSize    = (sText.length + 1) * 2;
        lpData   = AkelPad.MemAlloc(nSize);
        AkelPad.MemCopy(lpData, sText, DT_UNICODE);

        sText = "";
        for (n = 0; n < nSize; ++n)
          sText += PadL0(AkelPad.MemRead(_PtrAdd(lpData, n), DT_BYTE).toString(16).toUpperCase());
        AkelPad.MemFree(lpData);

        if (sText && oRE.test(sIniText))
        {
          //create backup .ini file
          if (GetItemTextLV(aSel[i], 0) != sPrevPlug)
          {
            sBakFile = sTextDir + GetItemTextLV(aSel[i], 0) + ".ini.bak";
            CopyFile(sIniFile, sBakFile);
            sPrevPlug = GetItemTextLV(aSel[i], 0);
          }

          if (AkelPad.WriteFile(sIniFile, sIniText.replace(oRE, GetItemTextLV(aSel[i], 1) + "=" + sText), -1, 1200 /*UTF-16LE*/, 1 /*bBOM*/) == 0 /*ESD_SUCCESS*/)
          {
            aLoaded.push(aSel[i]);
            bWasLoaded = true;
          }
        }
      }

      if (! bWasLoaded)
        aNotLoaded.push(aSel[i]);
    }
  }

  if (aLoaded.length)
  {
    sMessage += sTxtLoaded + "\n";

    for (i = 0; i < aLoaded.length; ++i)
    {
      sMessage += GetItemTextLV(aLoaded[i], 0) + ": " + GetItemTextLV(aLoaded[i], 1) + "\n";
      RestartPlugin(GetItemTextLV(aLoaded[i], 0));
    }

    if ((! bFromFile) && AkelPad.IsPluginRunning("Coder::HighLight"))
    {
      for (i = 0; i < aFrame.length; ++i)
      {
        if (aFrame[i])
        {
          SendMessage(hMainWnd, 1285 /*AKD_FRAMEACTIVATE*/, 0, aFrame[i]);
          AkelPad.Call("Coder::Settings", 1, "akelmenu");
        }
      }
    }
  }

  if (aNotLoaded.length)
  {
    sMessage += "\n" + sTxtNotLoaded + "\n";

    for (i = 0; i < aNotLoaded.length; ++i)
      sMessage += GetItemTextLV(aNotLoaded[i], 0) + ": " + GetItemTextLV(aNotLoaded[i], 1) + "\n";
  }

  if (! bFromFile)
    SendMessage(hMainWnd, 1285 /*AKD_FRAMEACTIVATE*/, 0, nCurFrame);

  AkelPad.MessageBox(hDlg, sMessage, sTxtScriptName, 0x40 /*MB_ICONINFORMATION*/);
}

function GetTextAE(nSel)
{
  if (aFrame[nSel])
  {
    SendMessage(hMainWnd, 1285 /*AKD_FRAMEACTIVATE*/, 0, aFrame[nSel]);
    return AkelPad.GetTextRange(0, -1, 0 /*New line as is*/);
  }
}

function PadL0(sStr)
{
  if (sStr.length == 1)
    sStr = "0" + sStr;

  return sStr;
}

function RestartPlugin(sPlugName)
{
  var bCoderHL;
  var bCoderCF;
  var bCoderAC;

  if (sPlugName == "Coder")
  {
    if (AkelPad.IsPluginRunning(sPlugName + "::HighLight"))
    {
      bCoderHL = 1;
      AkelPad.Call(sPlugName + "::HighLight");
    }
    if (AkelPad.IsPluginRunning(sPlugName + "::CodeFold"))
    {
      bCoderCF = 1;
      AkelPad.Call(sPlugName + "::CodeFold");
    }
    if (AkelPad.IsPluginRunning(sPlugName + "::AutoComplete"))
    {
      bCoderAC = 1;
      AkelPad.Call(sPlugName + "::AutoComplete");
    }

    if (bCoderHL)
      AkelPad.Call(sPlugName + "::HighLight");
    if (bCoderCF)
      AkelPad.Call(sPlugName + "::CodeFold");
    if (bCoderAC)
      AkelPad.Call(sPlugName + "::AutoComplete");
  }
  else if ((sPlugName == "ContextMenu") || (sPlugName == "Hotkeys"))
  {
    if (AkelPad.IsPluginRunning(sPlugName + "::Main"))
    {
      AkelPad.Call(sPlugName + "::Main", 10);
      AkelPad.Call(sPlugName + "::Main", 10);
    }
  }
  //SpecialChar, ToolBar*
  else
  {
    if (AkelPad.IsPluginRunning(sPlugName + "::Main"))
    {
      AkelPad.Call(sPlugName + "::Main");
      AkelPad.Call(sPlugName + "::Main");
    }
  }
}

function EditTextFile()
{
  var sTextDir = ExpandDirName(sTextDirS);
  var aSel     = GetSelArrayLV();
  var aNoFile  = [];
  var sTextFile;
  var i;

  for (i = 0; i < aSel.length; ++i)
  {
    sTextFile = sTextDir + GetItemTextLV(aSel[i], 2);

    if (oFSO.FileExists(sTextFile))
      AkelPad.OpenFile(sTextFile);
    else
      aNoFile.push(aSel[i]);
  }

  if (aNoFile.length)
  {
    sMessage = sTxtNoFiles + "\n";
    for (i = 0; i < aNoFile.length; ++i)
      sMessage += sTextDir + GetItemTextLV(aNoFile[i], 2) + "\n";

    AkelPad.MessageBox(hDlg, sMessage, sTxtScriptName, 0x30 /*MB_ICONWARNING*/);
  }
}

function RenameTextFile()
{
  var aSel = GetSelArrayLV();
  var sOldName;
  var sNewName;

  for (i = 0; i < aSel.length; ++i)
  {
    sOldName = GetItemTextLV(aSel[i], 2);
    sNewName = GetFileName(sTxtRenameFile, sOldName + "\n\n" + sTxtNewName, sOldName);

    if (sNewName)
    {
      if (sNewName != sOldName)
      {
        SetItemTextLV(aSel[i], 2, sNewName);
        RenameFile(ExpandDirName(sTextDirS) + sOldName, ExpandDirName(sTextDirS) + sNewName);
      }
    }
    else
      break;
  }
}

function GetFileName(sCaption, sLabel, sName)
{
  while (true)
  {
    sName = AkelPad.InputBox(hDlg, sCaption, sLabel, sName);

    if (sName)
    {
      sName = sName.replace(/[ .]+$/, "");

      if (/^(CON|PRN|AUX|NUL|COM1|COM2|COM3|COM4|COM5|COM6|COM7|COM8|COM9|LPT1|LPT2|LPT3|LPT4|LPT5|LPT6|LPT7|LPT8|LPT9)$/i.test(sName))
        AkelPad.MessageBox(hDlg, sName + "\n\n" + sTxtBadName, sCaption, 0x30 /*MB_ICONWARNING*/);
      else if (/[<>:"/\\|?*]/.test(sName))
        AkelPad.MessageBox(hDlg, sName + "\n\n" + sTxtBadChar, sCaption, 0x30 /*MB_ICONWARNING*/);
      else
        break;
    }
    else
      break;
  }

  return sName;
}

function DeleteTextFile()
{
  var sTextDir    = ExpandDirName(sTextDirS);
  var aSel        = GetSelArrayLV();
  var aDeleted    = [];
  var aNotDeleted = [];
  var sMessage    = "";

  if (AkelPad.MessageBox(hDlg, sTxtWantDelete, sTxtScriptName, 0x21 /*MB_DEFBUTTON1|MB_ICONQUESTION|MB_OKCANCEL*/) == 1 /*IDOK*/)
  {
    for (i = 0; i < aSel.length; ++i)
    {
      if (DeleteFile(sTextDir + GetItemTextLV(aSel[i], 2)))
        aDeleted.push(aSel[i]);
      else
        aNotDeleted.push(aSel[i]);
    }

    if (aDeleted.length)
    {
      sMessage += sTxtDeleted + "\n";
      for (i = 0; i < aDeleted.length; ++i)
        sMessage += sTextDir + GetItemTextLV(aDeleted[i], 2) + "\n";
    }

    if (aNotDeleted.length)
    {
      sMessage += "\n" + sTxtNotDeleted + "\n";
      for (i = 0; i < aNotDeleted.length; ++i)
        sMessage += sTextDir + GetItemTextLV(aNotDeleted[i], 2) + "\n";
    }

    AkelPad.MessageBox(hDlg, sMessage, sTxtScriptName, 0x40 /*MB_ICONINFORMATION*/);
  }
}

//function written by Infocatcher
function escapeRegExp(str)
{
  return str.replace(/[\\\/.^$+*?|()\[\]{}]/g, "\\$&");
}

function ChangeTextsDir()
{
  var sLabel  = "%a - " + sTxtAkelDir + "\n%p - " + sTxtPlugDir + "\n%variable% - " + sTxtEnvVar;
  var sDirS   = sTextDirS;
  var bChange = 0;
  var sDirE;

  while (true)
  {
    sDirS = AkelPad.InputBox(hDlg, sTxtChangeDir, sLabel, sDirS);

    if (sDirS)
    {
      if (sDirS.slice(-1) != "\\")
        sDirS += "\\";

      sDirE = ExpandDirName(sDirS);

      if (oFSO.FolderExists(sDirE))
      {
        bChange = 1;
        break;
      }
      else if (AkelPad.MessageBox(hDlg, sDirE + "\n\n" + sTxtDirNoExists, sTxtScriptName, 0x21 /*MB_DEFBUTTON1|MB_ICONQUESTION|MB_OKCANCEL*/) == 1 /*IDOK*/)
      {
        if (oSys.Call("Kernel32::CreateDirectoryW", sDirE, 0))
        {
          bChange = 1;
          break;
        }
        else
          AkelPad.MessageBox(hDlg, sDirE + "\n\n" + sTxtDirCreFail, sTxtScriptName, 0x30 /*MB_ICONWARNING*/);
      }
    }
    else
      break;
  }

  if (bChange)
  {
    sTextDirS = sDirS;
    oSys.Call("User32::SetWindowTextW", aDlg[IDDIR][HWND], sTxtDir + sDirE);
  }
}

function ExpandDirName(sDir)
{
  sDir = sDir.replace(/%a\\/g, AkelPad.GetAkelDir() + "\\");
  sDir = sDir.replace(/%p\\/g, AkelPad.GetAkelDir(4 /*ADTYPE_PLUGS*/) + "\\");
  sDir = sDir.replace(/%.+?%/g, ExpandEnvironmentString);

  if (oSys.Call("Kernel32::GetFullPathNameW", sDir, nBufSize / 2, lpBuffer, 0))
    sDir = AkelPad.MemRead(lpBuffer, DT_UNICODE);

  return sDir;
}

function ExpandEnvironmentString(sStr)
{
  if (oSys.Call("Kernel32::ExpandEnvironmentStringsW", sStr, lpBuffer, nBufSize / 2))
    sStr = AkelPad.MemRead(lpBuffer, DT_UNICODE);

  return sStr;
}

function EditIniFile()
{
  var sPlugDir = AkelPad.GetAkelDir(4 /*ADTYPE_PLUGS*/) + "\\";
  var aSel     = GetSelArrayLV();
  var aNoFile  = [];
  var sIniFile;
  var nSelStart;
  var sMessage;
  var i;

  for (i = 0; i < aSel.length; ++i)
  {
    sIniFile = sPlugDir + GetItemTextLV(aSel[i], 0) + ".ini";

    if (oFSO.FileExists(sIniFile))
    {
      if (AkelPad.OpenFile(sIniFile) == 0 /*EOD_SUCCESS*/)
      {
        nSelStart = AkelPad.TextFind(AkelPad.GetEditWnd(), GetItemTextLV(aSel[i], 1), 0x00200001 /*FR_DOWN|FR_BEGINNING*/);
        if (nSelStart > -1)
          AkelPad.SetSel(nSelStart, nSelStart + GetItemTextLV(aSel[i], 1).length);
      }
    }
    else
      aNoFile.push(aSel[i]);
  }

  if (aNoFile.length)
  {
    sMessage = sTxtNoFiles + "\n";
    for (i = 0; i < aNoFile.length; ++i)
      sMessage += sPlugDir + GetItemTextLV(aSel[i], 0) + ".ini" + "\n";

    AkelPad.MessageBox(hDlg, sMessage, sTxtScriptName, 0x30 /*MB_ICONWARNING*/);
  }
}

function OpenInRegEdit()
{
  var aSel     = GetSelArrayLV();
  var hKey     = GetRegKeyHandle(0x80000001 /*HKEY_CURRENT_USER*/, "Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit", 0x0002 /*KEY_SET_VALUE*/);
  var sValue   = "HKEY_CURRENT_USER\\Software\\Akelsoft\\AkelPad\\Plugs\\" + GetItemTextLV(aSel[0], 0);
  var nSize    = (sValue.length + 1) * 2;
  var lpData   = AkelPad.MemAlloc(nSize);
  var sProcess = "regedit.exe";

  TerminateProcess(sProcess);

  AkelPad.MemCopy(lpData, sValue, DT_UNICODE);

  oSys.Call("Advapi32::RegSetValueExW",
    hKey,     //hKey
    "LastKey",//lpValueName
    0,        //Reserved
    1,        //dwType = REG_SZ
    lpData,   //lpData
    nSize);   //cbData
  
  oSys.Call("Advapi32::RegCloseKey", hKey);
  AkelPad.MemFree(lpData);

  AkelPad.Exec("rundll32.exe shell32, ShellExec_RunDLL " + sProcess);
}

function TerminateProcess(sProcess)
{
  var lpPIDs  = AkelPad.MemAlloc(4000);
  var lpBytes = AkelPad.MemAlloc(4);
  var lpName  = AkelPad.MemAlloc(260 * 2);
  var hProcess;
  var i;

  sProcess = sProcess.toUpperCase();

  oSys.Call("Psapi::EnumProcesses", lpPIDs, 4000, lpBytes);

  for (i = 0; i < AkelPad.MemRead(lpBytes, DT_DWORD) / 4; ++i)
  {
    if (hProcess = oSys.Call("Kernel32::OpenProcess",
                     0x0411 /*PROCESS_QUERY_INFORMATION|PROCESS_VM_READ|PROCESS_TERMINATE*/,
                     0,
                     AkelPad.MemRead(_PtrAdd(lpPIDs, i * 4), DT_DWORD)))
    {
      oSys.Call("Psapi::GetModuleBaseNameW", hProcess, 0, lpName, 260);

      if (AkelPad.MemRead(lpName, DT_UNICODE).toUpperCase() == sProcess)
        oSys.Call("Kernel32::TerminateProcess", hProcess, 0);

      oSys.Call("Kernel32::CloseHandle", hProcess);
    }
  }

  AkelPad.MemFree(lpPIDs);
  AkelPad.MemFree(lpBytes);
  AkelPad.MemFree(lpName);
}

function CopyFile(sFromFile, sToFile)
{
  try
  {
    oFSO.CopyFile(sFromFile, sToFile, 1 /*overwrite*/);
    return 1;
  }
  catch (oError)
  {}
}

function DeleteFile(sFile)
{
  try
  {
    oFSO.DeleteFile(sFile, 1 /*force*/);
    return 1;
  }
  catch (oError)
  {}
}

function RenameFile(sOldName, sNewName)
{
  DeleteFile(sNewName);

  try
  {
    oFSO.MoveFile(sOldName, sNewName);
    return 1;
  }
  catch (oError)
  {}
}

function IsScriptRunning()
{
  var hDlg = oSys.Call("User32::FindWindowExW", 0, 0, sClass, 0);

  if (hDlg)
  {
    if (! oSys.Call("User32::IsWindowVisible", hDlg))
      oSys.Call("User32::ShowWindow", hDlg, 8 /*SW_SHOWNA*/);
    if (oSys.Call("User32::IsIconic", hDlg))
      oSys.Call("User32::ShowWindow", hDlg, 9 /*SW_RESTORE*/);

    oSys.Call("User32::SetForegroundWindow", hDlg);
    return true;
  }
}

function IsSDI()
{
  if (! AkelPad.IsMDI())
  {
    AkelPad.MessageBox(hMainWnd, sTxtOnlyMDI, sTxtScriptName, 0x30 /*MB_ICONWARNING*/);
    return true;
  }
}

function ReadIni()
{
  try
  {
    eval(AkelPad.ReadFile(WScript.ScriptFullName.replace(/\.js$/i, ".ini"), 0x10 /*OD_ADT_NOMESSAGES*/, 1200 /*UTF-16LE*/, 1 /*bBOM*/));
  }
  catch (oError)
  {}

  if (nWndPosW < nWndMinW) nWndPosW = nWndMinW;
  if (nWndPosH < nWndMinH) nWndPosH = nWndMinH;

  if ((! sTextDirS) || (! oFSO.FolderExists(ExpandDirName(sTextDirS))))
    sTextDirS = "%p\\";
  else if (sTextDirS.slice(-1) != "\\")
    sTextDirS += "\\";
}

function WriteIni()
{
  var lpRect = AkelPad.MemAlloc(16); //sizeof(RECT)
  var sPlug;
  var sText;
  var sIniText;
  var i, n;

  oSys.Call("User32::GetWindowRect", hDlg, lpRect);
  nWndPosX = AkelPad.MemRead(_PtrAdd(lpRect,  0), DT_DWORD);
  nWndPosY = AkelPad.MemRead(_PtrAdd(lpRect,  4), DT_DWORD);
  nWndPosW = AkelPad.MemRead(_PtrAdd(lpRect,  8), DT_DWORD) - nWndPosX;
  nWndPosH = AkelPad.MemRead(_PtrAdd(lpRect, 12), DT_DWORD) - nWndPosY;
  AkelPad.MemFree(lpRect);

  oPlugText = {};
  for (i = 0; i < GetItemCountLV(); ++i)
  {
    sPlug = GetItemTextLV(i, 0);
    sText = GetItemTextLV(i, 1);

    if (! oPlugText[sPlug])
      oPlugText[sPlug] = {};

    if (! oPlugText[sPlug][sText])
      oPlugText[sPlug][sText] = {Sel: SendMessage(aDlg[IDPLUG][HWND], 0x102C /*LVM_GETITEMSTATE*/, i, 0x0002 /*LVIS_SELECTED*/), File: GetItemTextLV(i, 2)};
  }

  sIniText =
    'nWndPosX='   + nWndPosX + ';\r\n' +
    'nWndPosY='   + nWndPosY + ';\r\n' +
    'nWndPosW='   + nWndPosW + ';\r\n' +
    'nWndPosH='   + nWndPosH + ';\r\n' +
    'sTextDirS="' + sTextDirS.replace(/[\\"]/g, "\\$&") + '";\r\noPlugText={';

  for (i in oPlugText)
  {
    sIniText += '"' + i + '":{';
    for (n in oPlugText[i])
    {
      sIniText += '"' + n + '":{Sel:' + oPlugText[i][n].Sel + ',File:"' + oPlugText[i][n].File + '"},';
    }
    if (sIniText.slice(-1) == ",")
      sIniText = sIniText.slice(0, -1);
    sIniText += '},';
  }
  if (sIniText.slice(-1) == ",")
    sIniText = sIniText.slice(0, -1);
  sIniText += '};';

  AkelPad.WriteFile(WScript.ScriptFullName.replace(/\.js$/i, ".ini"), sIniText, sIniText.length, 1200 /*UTF-16LE*/, 1 /*bBOM*/);
}

function GetLangStrings()
{
  sTxtScriptName   = "插件文本";
  sTxtOnlyMDI      = "这个脚本仅运行于 MDI/PMDI 模式。";
  sTxtNoTexts      = "插件设置中没有任何文本。";
  sTxtSetInReg     = "设置存储在注册表中";
  sTxtSetInIni     = "设置存储在 AkelPad.ini 文件";
  sTxtDir          = "保存文本文件的目录:";
  sTxtPlugin       = "插件";
  sTxtKeyTextName  = "键 - 文本名称";
  sTxtTextFile     = "文本文件关联到的键";
  sTxtEditText     = "编辑插件文本";
  sTxtLoadText     = "载入文本到插件键";
  sTxtSaveTextFile = "保存插件文本到文本文件";
  sTxtEditTextFile = "编辑文本文件";
  sTxtLoadTextFile = "载入文本文件到插件键";
  sTxtRenameFile   = "重命名文本文件";
  sTxtDeleteFile   = "删除文本文件";
  sTxtChangeDir    = "更改保存文本文件的目录";
  sTxtEditIniFile  = "编辑 .ini 文件";
  sTxtOpenRegEdit  = "在注册表编辑器中打开";
  sTxtLoaded       = "已载入:";
  sTxtNotLoaded    = "未载入:";
  sTxtSaved        = "已保存:";
  sTxtNoSaved      = "未保存:";
  sTxtNoFiles      = "无文件:";
  sTxtWantSave     = "是否想要提取选定的插件键并保存到文本文件?";
  sTxtWantLoad     = "是否想要载入选中的文本文件到插件的键?";
  sTxtNewName      = "新名称:";
  sTxtBadName      = "非法名称。";
  sTxtBadChar      = "名称中含有非法字符。";
  sTxtWantDelete   = "是否想要删除选中的文本文件?";
  sTxtDeleted      = "已删除:";
  sTxtNoDeleted    = "未删除:";
  sTxtEnvVar       = "环境变量";
  sTxtAkelDir      = "AkelPad 目录";
  sTxtPlugDir      = "插件目录";
  sTxtDirNoExists  = "此目录不存在。是否想要创建它?";
  sTxtDirCreFail   = "创建目录失败。";
}
