// http://akelpad.sourceforge.net/forum/viewtopic.php?p=10491#10491
// Version: 2015-01-08
// Author: KDJ
//
// *** Sort in columns ***
//
// Usage:
//   Call("Scripts::Main", 1, "SortInColumns.js")
//
// Shortcut keys in dialog box:
//   Enter - Sort
//   Esc   - Close
//   Shift+Alt+Right (Left, Down, Up, End, Home, PgDn, PgUp) - move dialog box.
//
// Remark:
//   To effect was clearly visible, you should use a fixed-width font.

var sTxtCaption   = "列排序";
var sTxtSortRange = "排序范围";
var sTxtFrom      = "从";
var sTxtTo        = "到";
var sTxtColumns   = "列(&C):";
var sTxtLines     = "行(&L):";
var sTxtSortKeys  = "排序关键字 - 列";
var sTxtKey       = "关键字";
var sTxtDesc      = "降序(&D)";
var sTxtIgCase    = "忽略大小写";
var sTxtString    = "字串";
var sTxtLocale    = "字母顺序";
var sTxtNum       = "数字顺序";
var sTxtAllKeys   = "所有关键字";
var sTxtDescAll   = "降序(&D)";
var sTxtIgCaseAll = "忽略大小写(&I)";
var sTxtStringAll = "字串(&S)";
var sTxtLocaleAll = "字母顺序(&A)";
var sTxtNumAll    = "数字顺序(&N)";
var sTxtReverse   = "反向排列行(&O)";
var sTxtCopyToCB  = "复制结果到剪贴板，而不是替换文本(&T)";
var sTxtSort      = "排序";
var sTxtUndo      = "撤消(&U)";
var sTxtRedo      = "恢复(&R)";
var sTxtNoRangeC  = "未指定列范围，无法排序。";
var sTxtNoRangeL  = "未指定行范围，无法排序。";
var sTxtNoKey1    = "第一个排序关键字中未指定列范围。";

var DT_DWORD    = 3;
var BM_GETCHECK = 240;
var BM_SETCHECK = 241;

var oSys = AkelPad.SystemFunction();
var hMainWnd     = AkelPad.GetMainWnd();
var hEditWnd     = AkelPad.GetEditWnd();
var hGuiFont     = oSys.Call("gdi32::GetStockObject", 17 /*DEFAULT_GUI_FONT*/);
var hInstDLL   = AkelPad.GetInstanceDll();
var sClassName = "AkelPad::Scripts::" + WScript.ScriptName + "::" + hInstDLL;
var aDesc      = [0,0,0,0,0];
var aIgCase    = [1,1,1,1,1];
var aString    = [1,1,1,1,1];
var aLocale    = [0,0,0,0,0];
var aNum       = [0,0,0,0,0];
var bDescAll   = 0;
var bIgCaseAll = 1;
var nMethod    = 1;
var bReverse   = 0;
var bCopyToCB  = 0;
var nUndoLimit = 0;
var nRedoLimit = 0;
var nWordWrap;
var nLastLine;
var nLastCol;
var hWndDlg;
var lpBuffer;
var bGetSel;
var bIsReturn;
var hFocus;
var nWndPosX;
var nWndPosY;

ReadWriteIni(0);

var aWnd        = [];
var IDSORTRANGE = 1000;
var IDFROM      = 1001;
var IDTO        = 1002;
var IDCOLUMNS   = 1003;
var IDBEGCOL    = 1004;
var IDENDCOL    = 1005;
var IDLINES     = 1006;
var IDBEGLINE   = 1007;
var IDENDLINE   = 1008;
var IDSORTK1    = 1009;
var IDSORTK2    = 1010;
var IDSORTK3    = 1011;
var IDSORTK4    = 1012;
var IDSORTK5    = 1013;
var IDSORTK6    = 1014;
var IDFROM1     = 1015;
var IDTO1       = 1016;
var IDALLKEYS   = 1017;
var IDKEY1      = 1018;
var IDBEGCOL1   = 1019;
var IDENDCOL1   = 1020;
var IDDESC1     = 1021;
var IDIGCASE1   = 1022;
var IDSTRING1   = 1023;
var IDLOCALE1   = 1024;
var IDNUM1      = 1025;
var IDKEY2      = 1026;
var IDBEGCOL2   = 1027;
var IDENDCOL2   = 1028;
var IDDESC2     = 1029;
var IDIGCASE2   = 1030;
var IDSTRING2   = 1031;
var IDLOCALE2   = 1032;
var IDNUM2      = 1033;
var IDKEY3      = 1034;
var IDBEGCOL3   = 1035;
var IDENDCOL3   = 1036;
var IDDESC3     = 1037;
var IDIGCASE3   = 1038;
var IDSTRING3   = 1039;
var IDLOCALE3   = 1040;
var IDNUM3      = 1041;
var IDKEY4      = 1042;
var IDBEGCOL4   = 1043;
var IDENDCOL4   = 1044;
var IDDESC4     = 1045;
var IDIGCASE4   = 1046;
var IDSTRING4   = 1047;
var IDLOCALE4   = 1048;
var IDNUM4      = 1049;
var IDKEY5      = 1050;
var IDBEGCOL5   = 1051;
var IDENDCOL5   = 1052;
var IDDESC5     = 1053;
var IDIGCASE5   = 1054;
var IDSTRING5   = 1055;
var IDLOCALE5   = 1056;
var IDNUM5      = 1057;
var IDDESCALL   = 1058;
var IDIGCASEALL = 1059;
var IDSTRINGALL = 1060;
var IDLOCALEALL = 1061;
var IDNUMALL    = 1062;
var IDREVERSE   = 1063;
var IDCOPYTOCB  = 1064;
var IDSORT      = 1065;
var IDUNDO      = 1066;
var IDREDO      = 1067;

var CLASS   = 0;
var WND     = 1;
var EXSTYLE = 2;
var STYLE   = 3;
var X       = 4;
var Y       = 5;
var W       = 6;
var H       = 7;
var TXT     = 8;

//0x50000000 - WS_VISIBLE|WS_CHILD
//0x50000007 - WS_VISIBLE|WS_CHILD|BS_GROUPBOX
//0x50010000 - WS_VISIBLE|WS_CHILD|WS_TABSTOP
//0x50010003 - WS_VISIBLE|WS_CHILD|WS_TABSTOP|BS_AUTOCHECKBOX
//0x50012080 - WS_VISIBLE|WS_CHILD|WS_TABSTOP|ES_AUTOHSCROLL|ES_NUMBER
//Windows               CLASS, WND, EXSTYLE,      STYLE,   X,   Y,   W,   H, TXT
aWnd[IDSORTRANGE] = ["BUTTON",   0,       0, 0x50000007,  10,  10, 195,  85, sTxtSortRange];
aWnd[IDFROM     ] = ["STATIC",   0,       0, 0x50000000,  80,  25,  55,  13, sTxtFrom];
aWnd[IDTO       ] = ["STATIC",   0,       0, 0x50000000, 140,  25,  55,  13, sTxtTo];
aWnd[IDCOLUMNS  ] = ["STATIC",   0,       0, 0x50000000,  20,  40,  60,  13, sTxtColumns];
aWnd[IDBEGCOL   ] = ["EDIT",     0,   0x200, 0x50012080,  80,  40,  55,  20, ""];
aWnd[IDENDCOL   ] = ["EDIT",     0,   0x200, 0x50012080, 140,  40,  55,  20, ""];
aWnd[IDLINES    ] = ["STATIC",   0,       0, 0x50000000,  20,  65,  60,  13, sTxtLines];
aWnd[IDBEGLINE  ] = ["EDIT",     0,   0x200, 0x50012080,  80,  65,  55,  20, ""];
aWnd[IDENDLINE  ] = ["EDIT",     0,   0x200, 0x50012080, 140,  65,  55,  20, ""];
aWnd[IDSORTK1   ] = ["BUTTON",   0,       0, 0x50000007,  10, 110, 290,  82, sTxtSortKeys];
aWnd[IDSORTK2   ] = ["BUTTON",   0,       0, 0x50000007,  10, 185, 290,  82, ""];
aWnd[IDSORTK3   ] = ["BUTTON",   0,       0, 0x50000007,  10, 260, 290,  82, ""];
aWnd[IDSORTK4   ] = ["BUTTON",   0,       0, 0x50000007,  10, 335, 290,  82, ""];
aWnd[IDSORTK5   ] = ["BUTTON",   0,       0, 0x50000007,  10, 410, 290,  82, ""];
aWnd[IDSORTK6   ] = ["BUTTON",   0,       0, 0x50000007,  10, 485, 290,  82, ""];
aWnd[IDFROM1    ] = ["STATIC",   0,       0, 0x50000000,  80, 125,  55,  13, sTxtFrom];
aWnd[IDTO1      ] = ["STATIC",   0,       0, 0x50000000, 140, 125,  55,  13, sTxtTo];
aWnd[IDALLKEYS  ] = ["STATIC",   0,       0, 0x50000000,  20, 515,  90,  13, sTxtAllKeys];
aWnd[IDKEY1     ] = ["STATIC",   0,       0, 0x50000000,  20, 140,  60,  13, sTxtKey+" &1:"];
aWnd[IDBEGCOL1  ] = ["EDIT",     0,   0x200, 0x50012080,  80, 140,  55,  20, ""];
aWnd[IDENDCOL1  ] = ["EDIT",     0,   0x200, 0x50012080, 140, 140,  55,  20, ""];
aWnd[IDDESC1    ] = ["BUTTON",   0,       0, 0x50010003, 205, 125,  90,  16, sTxtDesc];
aWnd[IDIGCASE1  ] = ["BUTTON",   0,       0, 0x50010003, 205, 145,  90,  16, sTxtIgCase];
aWnd[IDSTRING1  ] = ["BUTTON",   0,       0, 0x50010003,  50, 170,  60,  16, sTxtString];
aWnd[IDLOCALE1  ] = ["BUTTON",   0,       0, 0x50010003, 110, 170,  90,  16, sTxtLocale];
aWnd[IDNUM1     ] = ["BUTTON",   0,       0, 0x50010003, 205, 170,  90,  16, sTxtNum];
aWnd[IDKEY2     ] = ["STATIC",   0,       0, 0x50000000,  20, 215,  60,  13, sTxtKey+" &2:"];
aWnd[IDBEGCOL2  ] = ["EDIT",     0,   0x200, 0x50012080,  80, 215,  55,  20, ""];
aWnd[IDENDCOL2  ] = ["EDIT",     0,   0x200, 0x50012080, 140, 215,  55,  20, ""];
aWnd[IDDESC2    ] = ["BUTTON",   0,       0, 0x50010003, 205, 200,  90,  16, sTxtDesc];
aWnd[IDIGCASE2  ] = ["BUTTON",   0,       0, 0x50010003, 205, 220,  90,  16, sTxtIgCase];
aWnd[IDSTRING2  ] = ["BUTTON",   0,       0, 0x50010003,  50, 245,  60,  16, sTxtString];
aWnd[IDLOCALE2  ] = ["BUTTON",   0,       0, 0x50010003, 110, 245,  90,  16, sTxtLocale];
aWnd[IDNUM2     ] = ["BUTTON",   0,       0, 0x50010003, 205, 245,  90,  16, sTxtNum];
aWnd[IDKEY3     ] = ["STATIC",   0,       0, 0x50000000,  20, 290,  60,  13, sTxtKey+" &3:"];
aWnd[IDBEGCOL3  ] = ["EDIT",     0,   0x200, 0x50012080,  80, 290,  55,  20, ""];
aWnd[IDENDCOL3  ] = ["EDIT",     0,   0x200, 0x50012080, 140, 290,  55,  20, ""];
aWnd[IDDESC3    ] = ["BUTTON",   0,       0, 0x50010003, 205, 275,  90,  16, sTxtDesc];
aWnd[IDIGCASE3  ] = ["BUTTON",   0,       0, 0x50010003, 205, 295,  90,  16, sTxtIgCase];
aWnd[IDSTRING3  ] = ["BUTTON",   0,       0, 0x50010003,  50, 320,  60,  16, sTxtString];
aWnd[IDLOCALE3  ] = ["BUTTON",   0,       0, 0x50010003, 110, 320,  90,  16, sTxtLocale];
aWnd[IDNUM3     ] = ["BUTTON",   0,       0, 0x50010003, 205, 320,  90,  16, sTxtNum];
aWnd[IDKEY4     ] = ["STATIC",   0,       0, 0x50000000,  20, 365,  60,  13, sTxtKey+" &4:"];
aWnd[IDBEGCOL4  ] = ["EDIT",     0,   0x200, 0x50012080,  80, 365,  55,  20, ""];
aWnd[IDENDCOL4  ] = ["EDIT",     0,   0x200, 0x50012080, 140, 365,  55,  20, ""];
aWnd[IDDESC4    ] = ["BUTTON",   0,       0, 0x50010003, 205, 350,  90,  16, sTxtDesc];
aWnd[IDIGCASE4  ] = ["BUTTON",   0,       0, 0x50010003, 205, 370,  90,  16, sTxtIgCase];
aWnd[IDSTRING4  ] = ["BUTTON",   0,       0, 0x50010003,  50, 395,  60,  16, sTxtString];
aWnd[IDLOCALE4  ] = ["BUTTON",   0,       0, 0x50010003, 110, 395,  90,  16, sTxtLocale];
aWnd[IDNUM4     ] = ["BUTTON",   0,       0, 0x50010003, 205, 395,  90,  16, sTxtNum];
aWnd[IDKEY5     ] = ["STATIC",   0,       0, 0x50000000,  20, 440,  60,  13, sTxtKey+" &5:"];
aWnd[IDBEGCOL5  ] = ["EDIT",     0,   0x200, 0x50012080,  80, 440,  55,  20, ""];
aWnd[IDENDCOL5  ] = ["EDIT",     0,   0x200, 0x50012080, 140, 440,  55,  20, ""];
aWnd[IDDESC5    ] = ["BUTTON",   0,       0, 0x50010003, 205, 425,  90,  16, sTxtDesc];
aWnd[IDIGCASE5  ] = ["BUTTON",   0,       0, 0x50010003, 205, 445,  90,  16, sTxtIgCase];
aWnd[IDSTRING5  ] = ["BUTTON",   0,       0, 0x50010003,  50, 470,  60,  16, sTxtString];
aWnd[IDLOCALE5  ] = ["BUTTON",   0,       0, 0x50010003, 110, 470,  90,  16, sTxtLocale];
aWnd[IDNUM5     ] = ["BUTTON",   0,       0, 0x50010003, 205, 470,  90,  16, sTxtNum];
aWnd[IDDESCALL  ] = ["BUTTON",   0,       0, 0x50010003, 205, 500,  90,  16, sTxtDescAll];
aWnd[IDIGCASEALL] = ["BUTTON",   0,       0, 0x50010003, 205, 520,  90,  16, sTxtIgCaseAll];
aWnd[IDSTRINGALL] = ["BUTTON",   0,       0, 0x50010003,  50, 545,  60,  16, sTxtStringAll];
aWnd[IDLOCALEALL] = ["BUTTON",   0,       0, 0x50010003, 110, 545,  90,  16, sTxtLocaleAll];
aWnd[IDNUMALL   ] = ["BUTTON",   0,       0, 0x50010003, 205, 545,  90,  16, sTxtNumAll];
aWnd[IDREVERSE  ] = ["BUTTON",   0,       0, 0x50010003,  20, 575, 170,  16, sTxtReverse];
aWnd[IDCOPYTOCB ] = ["BUTTON",   0,       0, 0x50010003,  20, 595, 270,  16, sTxtCopyToCB];
aWnd[IDSORT     ] = ["BUTTON",   0,       0, 0x50010000, 220,  15,  80,  23, sTxtSort];
aWnd[IDUNDO     ] = ["BUTTON",   0,       0, 0x50010000, 220,  47,  80,  23, sTxtUndo];
aWnd[IDREDO     ] = ["BUTTON",   0,       0, 0x50010000, 220,  72,  80,  23, sTxtRedo];

if (hEditWnd)
{
  nWordWrap = AkelPad.SendMessage(hEditWnd, 3241 /*AEM_GETWORDWRAP*/, 0, 0);
  if (nWordWrap > 0) AkelPad.Command(4209 /*IDM_VIEW_WORDWRAP*/);

  if (! AkelPad.SendMessage(hEditWnd, 3127 /*AEM_GETCOLUMNSEL*/, 0, 0))
    AkelPad.SendMessage(hEditWnd, 3128 /*AEM_UPDATESEL*/, 1 /*AESELT_COLUMNON*/, 0);

  nLastLine = AkelPad.SendMessage(hEditWnd, 3129 /*AEM_GETLINENUMBER*/, 0 /*AEGL_LINECOUNT*/, 0);
  nLastCol  = LineMaxLength() + 1;

  if (AkelPad.WindowRegisterClass(sClassName))
  {
    if (lpBuffer = AkelPad.MemAlloc(256 * _TSIZE))
    {
      hWndDlg = oSys.Call("User32::CreateWindowEx" + _TCHAR,
                  0,               //dwExStyle
                  sClassName,      //lpClassName
                  sTxtCaption,     //lpWindowName
                  0x90CA0000,      //WS_VISIBLE|WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX
                  0,               //x
                  0,               //y
                  315,             //nWidth
                  650,             //nHeight
                  hMainWnd,        //hWndParent
                  0,               //ID
                  hInstDLL,        //hInstance
                  DialogCallback); //Script function callback. To use it class must be registered by WindowRegisterClass.
      if (hWndDlg)
      {
        //Disable main window, to make dialog modal
        oSys.Call("User32::EnableWindow", hMainWnd, false);

        //Message loop
        AkelPad.WindowGetMessage();
      }
      AkelPad.MemFree(lpBuffer);
    }
    AkelPad.WindowUnregisterClass(sClassName);
  }

  if (nWordWrap > 0) AkelPad.Command(4209 /*IDM_VIEW_WORDWRAP*/);
}

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

  if (uMsg == 1) //WM_CREATE
  {
    for (i = 1000; i < aWnd.length; ++i)
    {
      aWnd[i][WND] = oSys.Call("User32::CreateWindowEx" + _TCHAR,
                       aWnd[i][EXSTYLE], //dwExStyle
                       aWnd[i][CLASS],   //lpClassName
                       0,                //lpWindowName
                       aWnd[i][STYLE],   //dwStyle
                       aWnd[i][X],       //x
                       aWnd[i][Y],       //y
                       aWnd[i][W],       //nWidth
                       aWnd[i][H],       //nHeight
                       hWnd,             //hWndParent
                       i,                //ID
                       hInstDLL,         //hInstance
                       0);               //lpParam
      SetWindowFontAndText(aWnd[i][WND], hGuiFont, aWnd[i][TXT]);
    }

    CheckButtonsAll(0);
    CheckUndoRedo();

    bGetSel = GetSelection();

    //Set window position
    if ((nWndPosX == undefined) || (nWndPosY == undefined))
      MoveWindow(hMainWnd, hWnd, "RT");
    else
      MoveWindow(hMainWnd, hWnd, [nWndPosX, nWndPosY]);
  }

  else if (uMsg == 7) //WM_SETFOCUS
  {
    oSys.Call("User32::SetFocus", aWnd[IDBEGCOL][WND]);
    AkelPad.SendMessage(aWnd[IDBEGCOL][WND], 177 /*EM_SETSEL*/, 0, -1);
  }

  else if (uMsg == 256) //WM_KEYDOWN
  {
    if (wParam == 9) //VK_TAB
      SetSelection();
    else if (wParam == 27) //VK_ESCAPE
      oSys.Call("User32::PostMessage" + _TCHAR, hWnd, 16 /*WM_CLOSE*/, 0, 0);
    else if (wParam == 13) //VK_RETURN
    {
      if (! bIsReturn)
      {
        hFocus = oSys.Call("User32::GetFocus");
        if ((hFocus != aWnd[IDSORT][WND]) && (hFocus != aWnd[IDUNDO][WND]) && (hFocus != aWnd[IDREDO][WND]))
          oSys.Call("User32::PostMessage" + _TCHAR, hWnd, 273 /*WM_COMMAND*/, IDSORT, 0);
      }
    }
  }

  else if (uMsg == 257) //WM_KEYUP
    bIsReturn = 0;

  else if ((uMsg == 260) /*WM_SYSKEYDOWN*/ &&
           (oSys.Call("User32::GetAsyncKeyState", 0xA0 /*VK_LSHIFT*/)))
  {
    if (wParam == 0x27) //VK_RIGHT
      MoveWindow(hMainWnd, hWnd, "R");
    else if (wParam == 0x25) //VK_LEFT
      MoveWindow(hMainWnd, hWnd, "L");
    else if (wParam == 0x28) //VK_DOWN
      MoveWindow(hMainWnd, hWnd, "D");
    else if (wParam == 0x26) //VK_UP
      MoveWindow(hMainWnd, hWnd, "U");
    else if (wParam == 0x23) //VK_END
      MoveWindow(hMainWnd, hWnd, "E");
    else if (wParam == 0x24) //VK_HOME
      MoveWindow(hMainWnd, hWnd, "H");
    else if (wParam == 0x22) //VK_NEXT
      MoveWindow(hMainWnd, hWnd, "B");
    else if (wParam == 0x21) //VK_PRIOR
      MoveWindow(hMainWnd, hWnd, "T");
  }

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

    if ((nLowParam == IDBEGCOL)  || (nLowParam == IDENDCOL)  ||
        (nLowParam == IDBEGLINE) || (nLowParam == IDENDLINE) ||
        (nLowParam == IDBEGCOL1) || (nLowParam == IDENDCOL1) ||
        (nLowParam == IDBEGCOL2) || (nLowParam == IDENDCOL2) ||
        (nLowParam == IDBEGCOL3) || (nLowParam == IDENDCOL3) ||
        (nLowParam == IDBEGCOL4) || (nLowParam == IDENDCOL4) ||
        (nLowParam == IDBEGCOL5) || (nLowParam == IDENDCOL5))
    {
      CheckEditValue(nLowParam);
      if (bGetSel)
        SetSelection();
    }

    else if (((nLowParam >= IDDESC1) && (nLowParam <= IDNUM1)) ||
             ((nLowParam >= IDDESC2) && (nLowParam <= IDNUM2)) ||
             ((nLowParam >= IDDESC3) && (nLowParam <= IDNUM3)) ||
             ((nLowParam >= IDDESC4) && (nLowParam <= IDNUM4)) ||
             ((nLowParam >= IDDESC5) && (nLowParam <= IDNUM5)))
    {
      CheckButtons1(nLowParam);
      SetSelection();
    }

    else if ((nLowParam >= IDDESCALL) && (nLowParam <= IDCOPYTOCB))
    {
      CheckButtonsAll(nLowParam);
      SetSelection();
    }

    else if (nLowParam == IDSORT)
    {
      oSys.Call("User32::SetFocus", aWnd[IDBEGCOL][WND]);
      SetSelection();
      if (SortColumns())
      {
        SetSelection();
        CheckUndoRedo(nLowParam);
      }
    }

    else if (nLowParam == IDUNDO)
    {
      bIsReturn = 1;
      CheckUndoRedo(nLowParam);
      oSys.Call("User32::SetFocus", aWnd[IDBEGCOL][WND]);
    }

    else if (nLowParam == IDREDO)
    {
      bIsReturn = 1;
      CheckUndoRedo(nLowParam);
      oSys.Call("User32::SetFocus", aWnd[IDBEGCOL][WND]);
    }
  }

  else if (uMsg == 16) //WM_CLOSE
  {
    ReadWriteIni(1);

    //Enable main window
    oSys.Call("User32::EnableWindow", hMainWnd, true);
    //Destroy dialog
    oSys.Call("User32::DestroyWindow", hWnd);
  }

  else if (uMsg == 2) //WM_DESTROY
  {
    //Exit message loop
    oSys.Call("User32::PostQuitMessage", 0);
  }

  else
    SetDefID(hWnd);

  return 0;
}

function SetWindowFontAndText(hWnd, hFont, sText)
{
  AkelPad.SendMessage(hWnd, 48 /*WM_SETFONT*/, hFont, true);
  oSys.Call("User32::SetWindowText" + _TCHAR, hWnd, sText);
}

function MoveWindow(hWndParent, hWnd, vAction)
{
  var aRectParent;
  var aRect;
  var nX;
  var nY;

  if (! hWndParent)
    hWndParent=oSys.Call("User32::GetDesktopWindow");

  aRectParent = GetWindowPos(hWndParent);
  aRect       = GetWindowPos(hWnd);

  nX = aRect.left;
  nY = aRect.top;

  if (vAction == "R") //Move right
    nX = aRect.left + ((aRect.left < aRectParent.right - 50) ? 20 : 0);
  else if (vAction == "L") //Move left
    nX = aRect.left - ((aRect.right > aRectParent.left + 50) ? 20 : 0);
  else if (vAction == "D") //Move down
    nY = aRect.top + ((aRect.top < aRectParent.bottom - 50) ? 20 : 0);
  else if (vAction == "U") //Move up
    nY = aRect.top - ((aRect.bottom > aRectParent.top + 50) ? 20 : 0);
  else if (vAction == "E") //Move end (right)
    nX = aRect.left + (aRectParent.right - aRect.right);
  else if (vAction == "H") //Move home (left)
    nX = aRect.left + (aRectParent.left - aRect.left);
  else if (vAction == "B") //Move bottom
    nY = aRect.top + (aRectParent.bottom - aRect.bottom);
  else if (vAction == "T") //Move top
    nY = aRect.top + (aRectParent.top - aRect.top);
  else if (vAction == "C") //Center window
  {
    nX = aRectParent.left + ((aRectParent.right  - aRectParent.left) / 2 - (aRect.right  - aRect.left) / 2);
    nY = aRectParent.top  + ((aRectParent.bottom - aRectParent.top)  / 2 - (aRect.bottom - aRect.top)  / 2);
  }
  else if (vAction == "LT") //Move left top
  {
    nX = aRectParent.left;
    nY = aRectParent.top;
  }
  else if (vAction == "RT") //Move right top
  {
    nX = aRect.left + (aRectParent.right - aRect.right);
    nY = aRectParent.top;
  }
  else if (vAction == "LB") //Move left bottom
  {
    nX = aRectParent.left;
    nY = aRect.top + (aRectParent.bottom - aRect.bottom);
  }
  else if (vAction == "RB") //Move right bottom
  {
    nX = aRect.left + (aRectParent.right - aRect.right);
    nY = aRect.top + (aRectParent.bottom - aRect.bottom);
  }
  else
  {
    nX = vAction[0];
    nY = vAction[1];
  }

  oSys.Call("User32::SetWindowPos", hWnd, 0, nX, nY, 0, 0, 0x15 /*SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOSIZE*/);
}

function GetWindowPos(hWnd)
{
  var lpRect = AkelPad.MemAlloc(16) //sizeof(RECT);
  var aRect  = [];

  oSys.Call("User32::GetWindowRect", hWnd, lpRect);

  aRect.left   = AkelPad.MemRead(_PtrAdd(lpRect,  0), DT_DWORD);
  aRect.top    = AkelPad.MemRead(_PtrAdd(lpRect,  4), DT_DWORD);
  aRect.right  = AkelPad.MemRead(_PtrAdd(lpRect,  8), DT_DWORD);
  aRect.bottom = AkelPad.MemRead(_PtrAdd(lpRect, 12), DT_DWORD);
  AkelPad.MemFree(lpRect);

  return aRect;
}

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

function SetDefID(hWnd)
{
  var nID    = 1000;
  var hFocus = oSys.Call("User32::GetFocus");

  while ((nID < aWnd.length) && (hFocus != aWnd[nID][WND]))
    ++nID;

  if ((nID != IDUNDO) && (nID != IDREDO))
    nID = IDSORT;

  oSys.Call("User32::DefDlgProc" + _TCHAR, hWnd, 1025 /*DM_SETDEFID*/, nID, 0);
}

function ReadWriteIni(bWrite)
{
  var oFSO     = new ActiveXObject("Scripting.FileSystemObject");
  var sIniName = WScript.ScriptFullName.substring(0, WScript.ScriptFullName.lastIndexOf(".")) + ".ini";
  var aRect;
  var oFile;
  var sIniTxt;

  if (bWrite)
  {
    aRect = GetWindowPos(hWndDlg);

    sIniTxt = 'nWndPosX='   + aRect.left + ';\r\n' +
              'nWndPosY='   + aRect.top  + ';\r\n' +
              'aDesc=['     + aDesc + '];\r\n' +
              'aIgCase=['   + aIgCase + '];\r\n' +
              'aString=['   + aString + '];\r\n' +
              'aLocale=['   + aLocale + '];\r\n' +
              'aNum=['      + aNum + '];\r\n' +
              'bDescAll='   + bDescAll + ';\r\n' +
              'bIgCaseAll=' + bIgCaseAll + ';\r\n' +
              'nMethod='    + nMethod + ';\r\n' +
              'bReverse='   + bReverse + ';\r\n' +
              'bCopyToCB='  + bCopyToCB + ';'

    oFile = oFSO.OpenTextFile(sIniName, 2, true, 0);
    oFile.Write(sIniTxt);
    oFile.Close();
  }

  else if (oFSO.FileExists(sIniName))
  {
    try
    {
      eval(AkelPad.ReadFile(sIniName));
    }
    catch (oError)
    {
    }
  }
}

function CheckButtonsAll(nButton)
{
  var nDif = IDDESC2 - IDDESC1;
  var i;

  if (nButton == IDDESCALL)
  {
    bDescAll = AkelPad.SendMessage(aWnd[IDDESCALL][WND], BM_GETCHECK, 0, 0);
    aDesc    = [bDescAll, bDescAll, bDescAll, bDescAll, bDescAll];
  }
  else if (nButton == IDIGCASEALL)
  {
    bIgCaseAll = AkelPad.SendMessage(aWnd[IDIGCASEALL][WND], BM_GETCHECK, 0, 0);
    aIgCase    = [bIgCaseAll, bIgCaseAll, bIgCaseAll, bIgCaseAll, bIgCaseAll];
  }
  else if ((nButton == IDSTRINGALL) || (nButton == IDLOCALEALL) || (nButton == IDNUMALL))
  {
    if (nMethod == nButton - IDSTRINGALL + 1)
      nMethod = 0;
    else
    {
      nMethod = nButton - IDSTRINGALL + 1;
      aString = [0,0,0,0,0];
      aLocale = [0,0,0,0,0];
      aNum    = [0,0,0,0,0];
      if (nMethod == 1)
        aString = [1,1,1,1,1];
      else if (nMethod == 2)
        aLocale = [1,1,1,1,1];
      else if (nMethod == 3)
        aNum = [1,1,1,1,1];
    }
  }
  else if (nButton == IDREVERSE)
    bReverse = AkelPad.SendMessage(aWnd[IDREVERSE][WND],  BM_GETCHECK, 0, 0);
  else if (nButton == IDCOPYTOCB)
    bCopyToCB  = AkelPad.SendMessage(aWnd[IDCOPYTOCB][WND],  BM_GETCHECK, 0, 0);

  AkelPad.SendMessage(aWnd[IDDESCALL][WND],   BM_SETCHECK, bDescAll,       0);
  AkelPad.SendMessage(aWnd[IDIGCASEALL][WND], BM_SETCHECK, bIgCaseAll,     0);
  AkelPad.SendMessage(aWnd[IDSTRINGALL][WND], BM_SETCHECK, (nMethod == 1), 0);
  AkelPad.SendMessage(aWnd[IDLOCALEALL][WND], BM_SETCHECK, (nMethod == 2), 0);
  AkelPad.SendMessage(aWnd[IDNUMALL][WND],    BM_SETCHECK, (nMethod == 3), 0);
  AkelPad.SendMessage(aWnd[IDREVERSE][WND],   BM_SETCHECK, bReverse,       0);
  AkelPad.SendMessage(aWnd[IDCOPYTOCB][WND],  BM_SETCHECK, bCopyToCB,      0);

  for (i = 0; i < 5; ++i)
  {
    AkelPad.SendMessage(aWnd[IDDESC1   + i * nDif][WND], BM_SETCHECK, aDesc[i],   0);
    AkelPad.SendMessage(aWnd[IDIGCASE1 + i * nDif][WND], BM_SETCHECK, aIgCase[i], 0);
    AkelPad.SendMessage(aWnd[IDSTRING1 + i * nDif][WND], BM_SETCHECK, aString[i], 0);
    AkelPad.SendMessage(aWnd[IDLOCALE1 + i * nDif][WND], BM_SETCHECK, aLocale[i], 0);
    AkelPad.SendMessage(aWnd[IDNUM1    + i * nDif][WND], BM_SETCHECK, aNum[i],    0);
  }

  for (i = IDSORTK1; i <= IDNUMALL; ++i)
    oSys.Call("User32::EnableWindow", aWnd[i][WND], ! bReverse);
}

function CheckButtons1(nButton)
{
  var nDif = IDDESC2 - IDDESC1;
  var nKey;
  var i;

  if (((nButton - IDDESC1) % nDif) == 0)
  {
    aDesc[(nButton - IDDESC1) / nDif] = AkelPad.SendMessage(aWnd[nButton][WND], BM_GETCHECK, 0, 0);
    if (aDesc[0] && aDesc[1] && aDesc[2] && aDesc[3] && aDesc[4])
      bDescAll = 1;
    else
      bDescAll = 0;
    AkelPad.SendMessage(aWnd[IDDESCALL][WND], BM_SETCHECK, bDescAll, 0);
  }
  else if (((nButton - IDIGCASE1) % nDif) == 0)
  {
    aIgCase[(nButton - IDIGCASE1) / nDif] = AkelPad.SendMessage(aWnd[nButton][WND], BM_GETCHECK, 0, 0);
    if (aIgCase[0] && aIgCase[1] && aIgCase[2] && aIgCase[3] && aIgCase[4])
      bIgCaseAll = 1;
    else
      bIgCaseAll = 0;
    AkelPad.SendMessage(aWnd[IDIGCASEALL][WND], BM_SETCHECK, bIgCaseAll, 0);
  }
  else
  {
    nKey = Math.floor((nButton - IDDESC1) / nDif);
    aString[nKey] = 0;
    aLocale[nKey] = 0;
    aNum[nKey]    = 0;

    if (((nButton - IDSTRING1) % nDif) == 0)
      aString[nKey] = 1;
    else if (((nButton - IDLOCALE1) % nDif) == 0)
      aLocale[nKey] = 1;
    else if (((nButton - IDNUM1) % nDif) == 0)
      aNum[nKey] = 1;

    AkelPad.SendMessage(aWnd[IDSTRING1 + nKey * nDif][WND], BM_SETCHECK, aString[nKey], 0);
    AkelPad.SendMessage(aWnd[IDLOCALE1 + nKey * nDif][WND], BM_SETCHECK, aLocale[nKey], 0);
    AkelPad.SendMessage(aWnd[IDNUM1    + nKey * nDif][WND], BM_SETCHECK, aNum[nKey],    0);

    if (aString[0] && aString[1] && aString[2] && aString[3] && aString[4])
      nMethod = 1;
    else if (aLocale[0] && aLocale[1] && aLocale[2] && aLocale[3] && aLocale[4])
      nMethod = 2;
    else if (aNum[0] && aNum[1] && aNum[2] && aNum[3] && aNum[4])
      nMethod = 3;
    else
      nMethod = 0;

    AkelPad.SendMessage(aWnd[IDSTRINGALL][WND], BM_SETCHECK, (nMethod == 1), 0);
    AkelPad.SendMessage(aWnd[IDLOCALEALL][WND], BM_SETCHECK, (nMethod == 2), 0);
    AkelPad.SendMessage(aWnd[IDNUMALL][WND],    BM_SETCHECK, (nMethod == 3), 0);
  }
}

function CheckUndoRedo(nButton)
{
  var sTxtUndo = aWnd[IDUNDO][TXT];
  var sTxtRedo = aWnd[IDREDO][TXT];

  if (nButton == IDSORT)
  {
    if (nUndoLimit < AkelPad.SendMessage(hEditWnd, 3084 /*AEM_GETUNDOLIMIT*/, 0, 0))
      ++nUndoLimit;
    nRedoLimit = 0;
  }
  else if ((nButton == IDUNDO) && (nUndoLimit))
  {
    --nUndoLimit;
    ++nRedoLimit;
    AkelPad.SendMessage(hEditWnd, 3077 /*AEM_UNDO*/, 0, 0);
  }
  else if ((nButton == IDREDO) && (nRedoLimit))
  {
    ++nUndoLimit;
    --nRedoLimit;
    AkelPad.SendMessage(hEditWnd, 3078 /*AEM_REDO*/, 0, 0);
  }

  if (nUndoLimit)
    sTxtUndo += " (" + nUndoLimit + ")";
  if (nRedoLimit)
    sTxtRedo += " (" + nRedoLimit + ")";

  SetWindowFontAndText(aWnd[IDUNDO][WND], hGuiFont, sTxtUndo);
  SetWindowFontAndText(aWnd[IDREDO][WND], hGuiFont, sTxtRedo);

  oSys.Call("User32::EnableWindow", aWnd[IDUNDO][WND], nUndoLimit);
  oSys.Call("User32::EnableWindow", aWnd[IDREDO][WND], nRedoLimit);
}

function CheckEditValue(nIdWnd)
{
  var sEditValue;

  oSys.Call("User32::GetWindowText" + _TCHAR, aWnd[nIdWnd][WND], lpBuffer, 256);
  sEditValue = AkelPad.MemRead(lpBuffer, _TSTR);

  if ((nIdWnd == IDBEGLINE) || (nIdWnd == IDENDLINE))
  {
    if (sEditValue == 0)
    {
      SetWindowFontAndText(aWnd[nIdWnd][WND], hGuiFont, "1");
      AkelPad.SendMessage(aWnd[nIdWnd][WND], 177 /*EM_SETSEL*/, 0, -1);
    }
    else if (sEditValue > nLastLine)
    {
      SetWindowFontAndText(aWnd[nIdWnd][WND], hGuiFont, String(nLastLine));
      AkelPad.SendMessage(aWnd[nIdWnd][WND], 177 /*EM_SETSEL*/, 0, -1);
    }
  }

  else if ((nIdWnd == IDBEGCOL)  || (nIdWnd == IDENDCOL) ||
           (nIdWnd == IDBEGCOL1) || (nIdWnd == IDENDCOL1))
  {
    if (sEditValue == 0)
    {
      SetWindowFontAndText(aWnd[nIdWnd][WND], hGuiFont, "1");
      AkelPad.SendMessage(aWnd[nIdWnd][WND], 177 /*EM_SETSEL*/, 0, -1);
    }
    else if (sEditValue > nLastCol)
    {
      SetWindowFontAndText(aWnd[nIdWnd][WND], hGuiFont, String(nLastCol));
      AkelPad.SendMessage(aWnd[nIdWnd][WND], 177 /*EM_SETSEL*/, 0, -1);
    }
  }

  else if ((nIdWnd == IDBEGCOL2) || (nIdWnd == IDENDCOL2) ||
           (nIdWnd == IDBEGCOL3) || (nIdWnd == IDENDCOL3) ||
           (nIdWnd == IDBEGCOL4) || (nIdWnd == IDENDCOL4) ||
           (nIdWnd == IDBEGCOL5) || (nIdWnd == IDENDCOL5))
  {
    if (sEditValue == "0")
    {
      SetWindowFontAndText(aWnd[nIdWnd][WND], hGuiFont, "");
      AkelPad.SendMessage(aWnd[nIdWnd][WND], 177 /*EM_SETSEL*/, 0, -1);
    }
    else if (sEditValue > nLastCol)
    {
      SetWindowFontAndText(aWnd[nIdWnd][WND], hGuiFont, String(nLastCol));
      AkelPad.SendMessage(aWnd[nIdWnd][WND], 177 /*EM_SETSEL*/, 0, -1);
    }
  }
}

function GetSelection()
{
  var lpBegSel = AkelPad.MemAlloc(_X64 ? 24 : 12 /*sizeof(AECHARINDEX)*/);
  var lpEndSel = AkelPad.MemAlloc(_X64 ? 24 : 12 /*sizeof(AECHARINDEX)*/);
  var nLine1, nLine2, nCol1, nCol2;

  AkelPad.SendMessage(hEditWnd, 3123 /*AEM_EXGETSEL*/, lpBegSel, lpEndSel);

  nLine1 = AkelPad.MemRead(lpBegSel, DT_DWORD) + 1;
  nLine2 = AkelPad.MemRead(lpEndSel, DT_DWORD) + 1;

  if (AkelPad.MemRead(_PtrAdd(lpBegSel, _X64 ? 16 : 8), DT_DWORD) < AkelPad.MemRead(_PtrAdd(lpEndSel, _X64 ? 16 : 8), DT_DWORD))
  {
    nCol1 = AkelPad.MemRead(_PtrAdd(lpBegSel, _X64 ? 16 : 8), DT_DWORD) + 1;
    nCol2 = AkelPad.MemRead(_PtrAdd(lpEndSel, _X64 ? 16 : 8), DT_DWORD) + 1;
  }
  else
  {
    nCol1 = AkelPad.MemRead(_PtrAdd(lpEndSel, _X64 ? 16 : 8), DT_DWORD) + 1;
    nCol2 = AkelPad.MemRead(_PtrAdd(lpBegSel, _X64 ? 16 : 8), DT_DWORD) + 1;
  }

  SetWindowFontAndText(aWnd[IDBEGLINE][WND], hGuiFont, String(nLine1));
  SetWindowFontAndText(aWnd[IDENDLINE][WND], hGuiFont, String(nLine2));
  SetWindowFontAndText(aWnd[IDBEGCOL][WND],  hGuiFont, String(nCol1));
  SetWindowFontAndText(aWnd[IDENDCOL][WND],  hGuiFont, String(nCol2));
  SetWindowFontAndText(aWnd[IDBEGCOL1][WND], hGuiFont, String(nCol1));
  SetWindowFontAndText(aWnd[IDENDCOL1][WND], hGuiFont, String(nCol2));

  AkelPad.MemFree(lpBegSel);
  AkelPad.MemFree(lpEndSel);
  return 1;
}

function SetSelection()
{
  var hWndFocus = oSys.Call("User32::GetFocus");
  var lpSelect  = AkelPad.MemAlloc(_X64 ? 56 : 32 /*sizeof(AESELECTION)*/);
  var lpBegSel  = lpSelect;
  var lpEndSel  = _PtrAdd(lpSelect, _X64 ? 24 : 12);
  var lpSelFlag = _PtrAdd(lpSelect, _X64 ? 48 : 24);
  var nLine1, nLine2, nCol1, nCol2, i;

  for (i = 1000; i < aWnd.length; ++i)
  {
    if (aWnd[i][WND] == hWndFocus)
      break;
  }

  if ((i >= IDBEGCOL1) && (i <= IDNUM1))
    i = IDBEGCOL1;
  else if ((i >= IDBEGCOL2) && (i <= IDNUM2))
    i = IDBEGCOL2;
  else if ((i >= IDBEGCOL3) && (i <= IDNUM3))
    i = IDBEGCOL3;
  else if ((i >= IDBEGCOL4) && (i <= IDNUM4))
    i = IDBEGCOL4;
  else if ((i >= IDBEGCOL5) && (i <= IDNUM5))
    i = IDBEGCOL5;
  else
    i = IDBEGCOL;

  oSys.Call("User32::GetWindowText" + _TCHAR, aWnd[IDBEGLINE][WND], lpBuffer, 256);
  nLine1 = Number(AkelPad.MemRead(lpBuffer, _TSTR)) - 1;
  oSys.Call("User32::GetWindowText" + _TCHAR, aWnd[IDENDLINE][WND], lpBuffer, 256);
  nLine2 = Number(AkelPad.MemRead(lpBuffer, _TSTR)) - 1;
  oSys.Call("User32::GetWindowText" + _TCHAR, aWnd[i][WND], lpBuffer, 256);
  nCol1 = Number(AkelPad.MemRead(lpBuffer, _TSTR)) - 1;
  oSys.Call("User32::GetWindowText" + _TCHAR, aWnd[i + 1][WND], lpBuffer, 256);
  nCol2 = Number(AkelPad.MemRead(lpBuffer, _TSTR)) - 1;

  if ((nCol1 < 0) || (nCol2 < 0))
    nCol1 = nCol2 = 0;

  AkelPad.MemCopy(lpBegSel, nLine1, DT_DWORD);
  AkelPad.MemCopy(_PtrAdd(lpBegSel, _X64 ? 16 : 8), nCol1, DT_DWORD);
  AkelPad.MemCopy(lpEndSel, nLine2, DT_DWORD);
  AkelPad.MemCopy(_PtrAdd(lpEndSel, _X64 ? 16 : 8), nCol2, DT_DWORD);
  AkelPad.MemCopy(lpSelFlag, 1, DT_DWORD);

  AkelPad.SendMessage(hEditWnd, 3132 /*AEM_INDEXUPDATE*/, 0, lpBegSel);
  AkelPad.SendMessage(hEditWnd, 3132 /*AEM_INDEXUPDATE*/, 0, lpEndSel);

  AkelPad.SendMessage(hEditWnd, 3126 /*AEM_SETSEL*/, 0, lpSelect);

  AkelPad.MemFree(lpSelect);
}

function LineMaxLength()
{
  var nMaxLenL = 0;
  var nBegLine;
  var nLenLine;
  var i;

  for (i = 0; i < nLastLine; ++i)
  {
    nBegLine = AkelPad.SendMessage(hEditWnd, 187 /*EM_LINEINDEX*/, i, 0);
    nLenLine = AkelPad.SendMessage(hEditWnd, 193 /*EM_LINELENGTH*/, nBegLine, 0);
    if (nLenLine > nMaxLenL)
      nMaxLenL = nLenLine;
  }

  return nMaxLenL;
}

function SortColumns()
{
  var aDesc   = new Array(5);
  var aIgCase = new Array(5);
  var aString = new Array(5);
  var aLocale = new Array(5);
  var aNum    = new Array(5);
  var aCol    = [[], [], [], [], [], []];
  var aTxt    = [];
  var sText   = "";
  var nDif    = IDBEGCOL2 - IDBEGCOL1;
  var nLine1;
  var nLine2;
  var i;

  for (i = 0; i < 5; ++i)
  {
    aDesc[i]   = AkelPad.SendMessage(aWnd[IDDESC1   + i * nDif][WND], BM_GETCHECK, 0, 0);
    aIgCase[i] = AkelPad.SendMessage(aWnd[IDIGCASE1 + i * nDif][WND], BM_GETCHECK, 0, 0);
    aString[i] = AkelPad.SendMessage(aWnd[IDSTRING1 + i * nDif][WND], BM_GETCHECK, 0, 0);
    aLocale[i] = AkelPad.SendMessage(aWnd[IDLOCALE1 + i * nDif][WND], BM_GETCHECK, 0, 0);
    aNum[i]    = AkelPad.SendMessage(aWnd[IDNUM1    + i * nDif][WND], BM_GETCHECK, 0, 0);
  }

  oSys.Call("User32::GetWindowText" + _TCHAR, aWnd[IDBEGCOL][WND], lpBuffer, 256);
  aCol[0][0] = Number(AkelPad.MemRead(lpBuffer, _TSTR));
  oSys.Call("User32::GetWindowText" + _TCHAR, aWnd[IDENDCOL][WND], lpBuffer, 256);
  aCol[0][1] = Number(AkelPad.MemRead(lpBuffer, _TSTR));
  oSys.Call("User32::GetWindowText" + _TCHAR, aWnd[IDBEGLINE][WND], lpBuffer, 256);
  nLine1 = Number(AkelPad.MemRead(lpBuffer, _TSTR));
  oSys.Call("User32::GetWindowText" + _TCHAR, aWnd[IDENDLINE][WND], lpBuffer, 256);
  nLine2 = Number(AkelPad.MemRead(lpBuffer, _TSTR));
  for (i = 1; i < 6; ++i)
  {
    oSys.Call("User32::GetWindowText" + _TCHAR, aWnd[IDBEGCOL1 + (i - 1) * nDif][WND], lpBuffer, 256);
    aCol[i][0] = Number(AkelPad.MemRead(lpBuffer, _TSTR));
    oSys.Call("User32::GetWindowText" + _TCHAR, aWnd[IDENDCOL1 + (i - 1) * nDif][WND], lpBuffer, 256);
    aCol[i][1] = Number(AkelPad.MemRead(lpBuffer, _TSTR));
    if ((aCol[i][0] == 0) || (aCol[i][1] == 0) || (aCol[i][0] == aCol[i][1]))
    {
      aCol[i][0] = aCol[i][1] = 0;
      SetWindowFontAndText(aWnd[IDBEGCOL1 + (i - 1) * nDif][WND], hGuiFont, String(aCol[i][0]));
      SetWindowFontAndText(aWnd[IDENDCOL1 + (i - 1) * nDif][WND], hGuiFont, String(aCol[i][1]));
    }
  }

  if (aCol[0][0] == aCol[0][1])
  {
    AkelPad.MessageBox(hWndDlg, sTxtNoRangeC, sTxtCaption, 48 /*MB_ICONEXCLAMATION*/);
    oSys.Call("User32::SetFocus", aWnd[IDENDCOL][WND]);
    AkelPad.SendMessage(aWnd[IDENDCOL][WND], 177 /*EM_SETSEL*/, 0, -1);
    return 0;
  }

  if (nLine1 == nLine2)
  {
    AkelPad.MessageBox(hWndDlg, sTxtNoRangeL, sTxtCaption, 48 /*MB_ICONEXCLAMATION*/);
    oSys.Call("User32::SetFocus", aWnd[IDENDLINE][WND]);
    AkelPad.SendMessage(aWnd[IDENDLINE][WND], 177 /*EM_SETSEL*/, 0, -1);
    return 0;
  }

  if ((aCol[1][0] == aCol[1][1]) && (! bReverse))
  {
    AkelPad.MessageBox(hWndDlg, sTxtNoKey1, sTxtCaption, 48 /*MB_ICONEXCLAMATION*/);
    oSys.Call("User32::SetFocus", aWnd[IDENDCOL1][WND]);
    AkelPad.SendMessage(aWnd[IDENDCOL1][WND], 177 /*EM_SETSEL*/, 0, -1);
    return 0;
  }

  for (i = 5; i >= 0; --i)
  {
    if (aCol[i][0] == 0)
    {
      aCol.splice(i, 1);
      aDesc.splice(i - 1, 1);
      aIgCase.splice(i - 1, 1);
      aString.splice(i - 1, 1);
      aLocale.splice(i - 1, 1);
      aNum.splice(i - 1, 1);
    }
    else
    {
      --aCol[i][0];
      --aCol[i][1];
    }
  }
  --nLine1;
  --nLine2;

  if (bReverse)
  {
    aTxt  = GetTextRangeColumn([nLine1, aCol[0][0]], [nLine2, aCol[0][1]]);
    sText = aTxt.reverse().join("\r");
  }
  else
  {
    aTxt.length = aCol.length;
    for (i = 0; i < aTxt.length; ++i)
      aTxt[i] = GetTextRangeColumn([nLine1, aCol[i][0]], [nLine2, aCol[i][1]]);

    aTxt = ArrayTransp(aTxt);

    ArraySort(aTxt, aDesc, aIgCase, aString, aLocale, aNum);

    for (i = 0; i < aTxt.length; ++i)
    {
      sText += aTxt[i][0] + "\r";
    }
    sText = sText.slice(0, -1);
  }

  AkelPad.SendMessage(aWnd[IDBEGCOL][WND], 177 /*EM_SETSEL*/, 0, -1);

  if (bCopyToCB)
  {
    AkelPad.SetClipboardText(sText);
    return 0;
  }
  else
  {
    AkelPad.ReplaceSel(sText);
    return 1;
  }
}

function GetTextRangeColumn(aBeg, aEnd, bReturnText, nNewLine, bFillSpaces)
{
  var nBegLine1;
  var nBegLine2;
  var nLenLine2;
  var nLine1;
  var nLine2;
  var nCol1;
  var nCol2;
  var aLines;
  var nWidth;
  var i;

  if (aBeg[0] < aEnd[0])
  {
    nLine1 = aBeg[0];
    nLine2 = aEnd[0];
  }
  else
  {
    nLine1 = aEnd[0];
    nLine2 = aBeg[0];
  }

  if (aBeg[1] < aEnd[1])
  {
    nCol1 = aBeg[1];
    nCol2 = aEnd[1];
  }
  else
  {
    nCol1 = aEnd[1];
    nCol2 = aBeg[1];
  }

	nBegLine1 = AkelPad.SendMessage(hEditWnd, 187 /*EM_LINEINDEX*/, nLine1, 0);
	nBegLine2 = AkelPad.SendMessage(hEditWnd, 187 /*EM_LINEINDEX*/, nLine2, 0);
	nLenLine2 = AkelPad.SendMessage(hEditWnd, 193 /*EM_LINELENGTH*/, nBegLine2, 0);
  aLines    = AkelPad.GetTextRange(nBegLine1, nBegLine2 + nLenLine2, 1 /*"\r"*/).split("\r");
  nWidth    = nCol2 - nCol1;

  for (i = 0; i < aLines.length; ++i)
  {
    aLines[i] = aLines[i].substring(nCol1, nCol2);
    if (bFillSpaces)
      while (aLines[i].length < nWidth)
        aLines[i] += " ";
  }

  if (bReturnText)
  {
    if ((! nNewLine) || (nNewLine == 1))
      pNewLine = "\r";
    else if (nNewLine == 2)
      pNewLine = "\n";
    else
      pNewLine = "\r\n";

    return aLines.join(pNewLine);
  }
  else
    return aLines;
}

function ArrayTransp(aArr)
{
  var nLen1 = aArr.length;
  var nLen2 = aArr[0].length;
  var aArr2 = [];
  var i, n;

  aArr2.length = nLen2;

  for (n = 0; n < nLen2; ++n)
  {
    aArr2[n] = [];
    for (i = 0; i < nLen1; ++i)
    {
      aArr2[n].push(aArr[i][n]);
    }
  }
  return aArr2;
}

function ArraySort(aTxt, aDesc, aIgCase, aString, aLocale, aNum)
{
  var lpStrA = AkelPad.MemAlloc(nLastCol * _TSIZE);
  var lpStrB = AkelPad.MemAlloc(nLastCol * _TSIZE);
  var nComp;
  var i;

  aTxt.sort(function(a, b) {
    for (i = 0; i < aDesc.length; ++i)
    {
      nComp = CompareValue(a[i + 1], b[i + 1], lpStrA, lpStrB, aDesc[i], aIgCase[i], aString[i], aLocale[i], aNum[i]);
      if (nComp != 0)
        return nComp;
    }
    return 0; });

  AkelPad.MemFree(lpStrA);
  AkelPad.MemFree(lpStrB);
}

function CompareValue(ValA, ValB, lpStrA, lpStrB, bDesc, bIgCase, bString, bLocale, bNum)
{
  var nResult;

  if (bString)
  {
    if (bIgCase)
    {
      ValA = ValA.toUpperCase();
      ValB = ValB.toUpperCase();
    }
    if (ValA > ValB)
      nResult = bDesc ? -1 : 1;
    else if (ValA < ValB)
      nResult = bDesc ? 1 : -1;
    else
      nResult = 0;
  }
  else if (bLocale)
  {
    AkelPad.MemCopy(lpStrA, ValA, _TSTR);
    AkelPad.MemCopy(lpStrB, ValB, _TSTR);
    nResult = oSys.Call("kernel32::lstrcmp" + (bIgCase ? "i" : "") + _TCHAR, lpStrA, lpStrB);
    if (bDesc)
      nResult = -nResult;
  }
  else
  {
    var oRegExp = /(-?\d+\.?\d*)|(-?\.\d+)/;

    ValA    = oRegExp.exec(ValA);
    ValB    = oRegExp.exec(ValB);
    ValA    = ValA ? parseFloat(ValA[0]) : 0;
    ValB    = ValB ? parseFloat(ValB[0]) : 0;
    nResult = bDesc ? (ValB - ValA) : (ValA - ValB);
  }

  return nResult;
}
