Страница 1 из 5

Drag&Drop на главной панели инструментов

Добавлено: Вт июн 16, 2026 23:37
Orion9
Реализация Drag&Drop при помощи плагина Autorun. Скрипт предназначен для перетаскивания кнопок на главной панели инструментов и отображения дополнительной информации о кнопках, включая их индекс.

Информация о кнопках отображается в подсказке при наведении указателя мыши с удержанием Shift (можно сначала навести указатель на кнопку, затем нажать Shift). Перетаскивание работает с удержанием CTRL (нужен только для захвата, при перетаскивании удерживать не требуется), но только после отображения первой подсказки. Первый вызов подсказки загружает информацию о панели (из соответствующего bar-файла) для дальнейшей работы с ним. Сочетание CTRL + ПКМ вызывает контекстное меню с дополнительными опциями. Меню становится доступным только после загрузки информации о панели (отображения первой подсказки).

[cut="Плагин Autorun"]Для работы рекомендуется установить последнюю версию плагина. На данный момент это 2.2.21.3 (15.10.2025)

Инструкция по установке плагина
https://total.darkhost.ru/wiki/doku.php?id=ru:autorun

Особенности последней версии. Для версии 2.2.21.3 нет полного архива с дополнительными модулями (субплагинами) и файлами справки, установку придется проводить в два этапа. Сначала нужно установить версию 2.2.21 от 28.09.2025
https://total.darkhost.ru/files/autorun ... 1_beta.zip
Поверх неё накатить последний модуль от 15.10.2025
https://total.darkhost.ru/files/temp/au ... 251015.zip[/cut]
[cut="autorun.cfg"]

Код: Выделить всё

Pragma AutorunBlockUnload

LoadLibrary Plugins\Autorun_Tweaks.dll
LoadLibrary Plugins\Autorun_Sysinfo.dll
LoadLibrary Plugins\Autorun_Runtime.dll
LoadLibrary Plugins\Autorun_Process.dll

Pragma Include %COMMANDER_PATH%\wdx\Autorun\Bars.aucfg

Pragma AutorunFinalizeSection

If gHBarWndProc > 0 Then 
    DllCall("SetWindowLong" & (auX64 ? "PtrW" : "W"), _
            "hwnd", hBarWnd, "int", -4, "long_ptr", gHBarWndProc, "ptr")
EndIf
[/cut]
[cut="Bars.aucfg"]

Код: Выделить всё

Pragma IncludeOnce

# наведение на главную панель
ControlSetHint /F /D:50 28 "HBarShowHint"

# правый клик с удержанием Ctrl
ControlSetMouseAction /R /K:C 28 "HBarShowMenu"

Global gDragIndex, gDropIndex
Global gHBarButtons = 0, gHBarDivs = 0

Global gHBarName, gHBarFile, gHBarFileTs
Global gHBarTxt, gHBarCP = "ANSI", gHBarWincmd = 0, gHBarDbg = false
Global gHBarHint = 0, gHBarVerb, gHBarCancel, gHBarCancelLoad, gHBarLoadError = 0
Global gHBarList = List(), gHBarMap = List(), gHBarUndo, gHBarUndoTs, gHBarUndoLoad

Global gHBarWndProc, hBarWnd = 0
Global gHBarWP = Callback("HBarWndProc", "hwnd;uint;wparam;lparam")

Func HBarWndProc(hWnd, uMsg, wParam, lParam)
    Static IsDrag = 0, _
           hTotal = DllCall("GetModuleHandleW", "Ptr", 0, "handle"), _
           MK_SHIFT        = 0x0004, _      
           MK_CONTROL      = 0x0008, _
           MK_LBUTTON      = 0x0001, _
           WM_MOUSEMOVE    = 0x0200, _
           WM_LBUTTONDOWN  = 0x0201, _
           WM_LBUTTONUP    = 0x0202
    Static drag_system = DllCall("LoadCursor", "Ptr", 0, "Ptr", 32646), _
           stop_system = DllCall("LoadCursor", "Ptr", 0, "Ptr", 32648), _
           drag_cursor = DllCall("LoadCursor", "Ptr", hTotal, "Ptr", 22), _
           stop_cursor = DllCall("LoadCursor", "Ptr", hTotal, "Ptr", 21)
    Static SM_CXDRAG = 68, SM_CYDRAG = 69, _
           dragX = GetSystemMetrics(SM_CXDRAG), dragY = GetSystemMetrics(SM_CYDRAG), _
           startX, startY
    Local x, y, pos
    If gHBarLoadError Then 
       Return DllCall("CallWindowProcW", _
              "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
    EndIf
    Switch uMsg
       Case WM_LBUTTONDOWN
          If IsDrag = 0 And BitAND(wParam, MK_CONTROL) Then
             If FileGetTime(gHBarFile) <> gHBarFileTs Then
                ShowDragHint("Операция невозможна. Файл панели изменился.")
                Return DllCall("CallWindowProcW", _
                "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
             Endif
             #MouseGetPos("x","y")
             Static buf = Buffer(8)	# POINT
             buf.Zero()
             x = BitAND(lParam, 0xFFFF)
             y = BitAND(BitShift(lParam,16), 0xFFFF)
             startX = x
             startY = y
             buf.SetNum(0, "int", x, "int", y)
             DllCall("ClientToScreen", "hwnd", hWnd, "ptr", buf.ptr)
             pos = buf.GetNum(0, "int64")             
             #x = buf.GetNum(0)
             #y = buf.GetNum(4)
             #If DllCall("DragDetect", "hwnd", hWnd, "int64", MakeInt(y, x, 2)) Then
             If DllCall("DragDetect", "hwnd", hWnd, "int64", pos) Then             
                IsDrag = 1
                WinSetStyle(0x80000, 3, hWnd)
                DllCall("SetLayeredWindowAttributes", "hwnd", hWnd, "ptr", 0, "byte", 128, "dword", 2)
                x = buf.GetNum(0)
                y = buf.GetNum(4)                           
                gDragIndex = HBarGetIdx(1, x, y)
             Else
                IsDrag = 0
                DllCall("CallWindowProcW", _
                   "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam) 
                DllCall("CallWindowProcW", _
                   "ptr", gHBarWndProc, "hwnd", hWnd, "uint", WM_LBUTTONUP, "wparam", wParam, "lparam", lParam)
                Return 0
             EndIf
          EndIf    
       Case WM_LBUTTONUP
          If IsDrag = 1 Then
             DllCall("ReleaseCapture")
             IsDrag = 0
             If WinFromPoint() = hWnd Then
                MouseGetPos("x","y")
                gDropIndex = HBarGetIdx(2, x, y)
                If gDragIndex <> gDropIndex Then
                   If gHBarHint Then ShowDragHint("Index " & gDragIndex & " To Index: " & gDropIndex)
                   HBarDragnDrop(gDragIndex, gDropIndex)
                EndIf
             EndIf
             WinSetStyle(0x80000, 5, hWnd)
             #Return 0
             #lParam = MakeInt(y, x + 200, 2)
             lParam = -1000
          EndIf
       Case WM_MOUSEMOVE
          If IsDrag = 1 And BitAND(wParam, MK_LBUTTON) Then
             If WinFromPoint() = hWnd Then
                DllCall("SetCursor", "Ptr", drag_cursor)
             Else
                DllCall("SetCursor", "Ptr", stop_cursor)               
             EndIf
          EndIf   
    EndSwitch
    Return DllCall("CallWindowProcW", _
                   "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
EndFunc

Func HBarShowHint()
    # панель в режиме drag'n'drop
    If WinHasStyle(0x80000, 1, hBarWnd) Then Return  
    # состояние CapsLock и Shift
    If BitAND(DllCall("GetKeyState", "int", 0x14, "short"), 1) = 0 Then
       If Not IsPressed(0x10) Then Return
    EndIf
    # дескриптор горизонтальной панели 
    Local hWnd = RequestInfo(28)
    If hWnd = 0 Then 
       Return "Окно панели не найдено " & hWnd
    EndIf
    # глобальные переменные дескриптора и оконной процедуры
    If hBarWnd <> hWnd Then
       hBarWnd = hWnd
       gHBarWndProc = DllCall("SetWindowLong" & (auX64 ? "PtrW" : "W"), _
                             "hwnd", hBarWnd, "int", -4, "long_ptr", gHBarWP.Ptr, "ptr")
    EndIf
    # поиск файла панели
    If gHBarName <> WinGetText(hBarWnd) Then
       gHBarUndo = false
       gHBarUndoLoad = ""
       gHBarCancel = false
       gHBarCancelLoad = ""       
       gHBarFile = HBarGetFile()
       If Not FileExist(gHBarFile) Then
          gHBarName = ""
          Return "Файл панели не найден " & gHBarFile
       EndIf
       # метка времени
       gHBarFileTs = FileGetTime(gHBarFile)
       # загрузка панели       
       HBarLoad()
       If gHBarLoadError Then Return HBarGetHint()
    ElseIf FileGetTime(gHBarFile) <> gHBarFileTs Then
       gHBarUndo = false
       gHBarUndoLoad = ""
       If Not FileExist(gHBarFile) Then
          gHBarName = ""
          Return "Файл панели не найден " & gHBarFile
       EndIf   
       gHBarFileTs = FileGetTime(gHBarFile)
       HBarLoad()
       If gHBarLoadError Then Return HBarGetHint()       
    EndIf
    # индекс кнопки под курсором
    gDragIndex = HBarGetIdx()
    If Not gDragIndex Then Return "Не удалось определить индекс кнопки"
    Return HBarGetHint()  
EndFunc

Func HBarGetFile()
    Local bar, txt
    # информация о файле панели в wincmd.ini
    IniRead /R bar %COMMANDER_INI% "Buttonbar" "Buttonbar" "%COMMANDER_PATH%\DEFAULT.BAR"
    # раскрытие переменной окружения
    bar = Set(bar) 
    # в конфигурации указано только имя файла
    If FileGetDir(bar) = "" Then bar = COMMANDER_PATH & "\" & bar
    gHBarWincmd = bar
    # чтение текста окна панели
    txt = WinGetText(hBarWnd)
    gHBarName = txt
    # на панель загружен временный .bar файл
    If Not StrPos(bar, txt) Then       
       bar = COMMANDER_PATH & "\Bars\" & txt
    EndIf
    If Not FileExist(bar) Then
       bar = GetCurrentPath() & "\" & txt
    EndIf     
    # проверка существования файла панели
    If Not FileExist(bar) Then
       bar = COMMANDER_PATH & "\" & txt
    EndIf      
    Return bar
EndFunc

Func HBarLoad()
    gHBarLoadError = 0
    Local bar = FileRead(gHBarFile, 128, "RAW")
    Local bom = "FFFE"
    Local utf = "5B0042007500740074006F006E006200610072005D000D000A00"
    If ERROR = 1 Then
       gHBarLoadError = 1
       gHBarCP = "Unknown"       
       Return
    EndIf   
    If StrLeft(bar, 4) = "FFFE" Then 
       gHBarCP = "UTF-16 BOM"
    Else
       IF StrPos(bar, utf) Then
          gHBarCP = "UTF-16 без BOM"
       Else
          gHBarCP = "ANSI"
       EndIf
    EndIf
    # загрузка файла в массив
    gHBarList.Count = 0
    gHBarList.LoadFromFile(gHBarFile, gHBarCP)    
    # количество кнопок в файле
    Local nCount = 0
    IniRead /R nCount  %gHBarFile% "Buttonbar" "Buttoncount" 0
    # обнуление счетчиков
    gHBarDivs = 0
    gHBarButtons = 0
    # локальные переменные
    Local j, key, val, found, oButton = Map(), oCmd = Map()
    # поиск кнопок и команд
    For j = 0 To gHBarList.Count - 1        
        found = 0    
        If StrLeft(gHBarList[j], 6) = "button" Then  
           found = 1
        ElseIf StrLeft(gHBarList[j], 3) = "cmd" Then  
           found = 2
        EndIf
        If found > 0 Then
           key = StrLower(StrPart(gHBarList[j], "=", 1))
           val = StrLen(StrPart(gHBarList[j], "=", 2))
           If found = 1 Then
              oButton.Set(key, val)              
           Else
              oCmd.Set(key, val)          
           EndIf
        EndIf
    Next
    gHBarMap.Count = 0    
    # подсчет кнопок и разделителей
    For j = 1 To nCount     
        key = "button" & j
        val = oButton.Get(key, 0)         
        If val > 0 Then
           # количество кнопок                 
           gHBarButtons += 1
           gHBarMap.Add(1)
        Else
           # количество разделителей  
           gHBarDivs += 1   
           gHBarMap.Add(0)
        EndIf
    Next
    Free(oButton, oCmd)
    If gHBarButtons = 0 Then gHBarLoadError = 1
    # карта панели
    gHBarTxt = auCRLF & auCRLF & "Loaded" & ": " & gHBarFile & " > " & gHBarCP & " "
    If gHBarLoadError Then gHBarTxt &= " -- ERROR LOADING"
    gHBarTxt &= auCRLF
    For j = 0 To gHBarMap.Count - 1
        gHBarTxt &= gHBarMap[j] & "  "
        If j > 0 and Mod(j, 50) = 0 Then gHBarTxt &= auCRLF
    Next
    gHBarTxt &= auCRLF & "Wincmd.ini: " & gHBarWincmd
EndFunc

Func HBarGetIdx(MousePos = false, PosX = 0, PosY = 0)
    # дескриптор монитора окна ТС
    Local hMon = DllCall("MonitorFromWindow", "hwnd", AUTORUN_TCHANDLE, "dword", 2)
    # буфер для структуры MONITORINFOEX
    Local buf = Buffer(104)
    buf.Zero()
    buf.SetNum(0, "dword", 104)
    DllCall("GetMonitorInfoW", "ptr", hMon, "ptr", buf.ptr)
    # имя монитора
    Local sMon = buf.GetStr(40)
    # информация о масштабировании экрана
    Local DC = DllCall("GetDC", "int", 0)
    Local nDPI = DllCall("GetDeviceCaps", "handle", DC, "int", 88) # LOGPIXELSX
    Local nScale = DllCall("MulDiv", "int", 100, "int", nDPI, "int", 96)
    DllCall("ReleaseDC", "int", 0, "handle", DC)     
    # локальные переменные
    Local x, y, w, h, mx, my, idx, row, rows, rowh, dpi, b, extra
    # координаты и размеры окна панели
    WinGetPos("x", "y", "w", "h", hBarWnd) 
    # координаты указателя мыши
    If MousePos Then
       mx = PosX
       my = PosY
    Else
       MouseGetPos("mx", "my")
    EndIf   
    # чтение информации о высоте кнопок
    IniRead /R b %COMMANDER_INI% "Buttonbar" "Buttonheight"
    IniRead /R dpi %COMMANDER_INI% "Buttonbar" "DefaultDpi" %'nDPI'
    Local b_ini = b, b_calc
    # масштабирование экрана
    If dpi <> nDPI Then
       IniRead /R d %COMMANDER_INI% "Buttonbar" %'"Buttonheight" & nDPI' 0
       If d <> 0 Then 
          b = d
       Else   
          b = Round(b / (dpi / nDPI), 0)
          b_calc = "*"
       EndIf
    EndIf   
    # количество рядов кнопок на панели
    rows = Floor(h/b)
    If rows = 0 Then Return HBarShowHint("Error number of rows can't be zero")
    # количество дополнительных пикселей
    extra = Mod(h,b)
    # ширина разделителя    
    Local div = Floor(b/3)
    # высота одного ряда
    rowh = b + 3
    # номер ряда под указателем
    row = Ceil((my - y)/rowh)
    # абстрактные координаты по оси х
    Local tx = (mx - x + 1) + ((row - 1) * w), cx = 0
    # индекс кнопки в ряду    
    If gHBarDivs = 0 Then
       idx = (Floor((mx - x)/b) + 1) + (row-1)*(Floor(w/b))
       If idx > gHBarButtons Then idx = gHBarButtons
    Else
       j = 1
       idx = 0       
       For i = 0 To gHBarMap.Count - 1
           cx += (gHBarMap[i] = 0 ? div : b)
           ex = j*w - cx
           If i + 1 <= gHBarMap.Count - 1 Then
              If ex < (gHBarMap[i+1] = 0 ? div : b) Then
                 cx += ex
                 j += 1
              EndIf
           Else
              cx += ex
           EndIf   
           If cx >= tx Then
              idx = i + 1
              Break
           EndIf   
       Next        
    EndIf
    # дополнительная информация
    gHBarVerb = "mouse=" & mx & ", " & my & auCRLF & _
                "bar=" & x & ", " & y & ", " & w & ", " & h & auCRLF &  _
                "map=" & tx & ", " & cx & auCRLF & _
                "row=" & row & " of " & rows & auCRLF & _
                "height=" & rowh & " (" & extra & ")" & " b=" & b & b_calc & " (" & b_ini & ")" & auCRLF & _
                "buttons=" & gHBarButtons & " + " & gHBarDivs & " divs" & auCRLF & _ 
                "width=" & b & ", " & div & auCRLF & _
                "mon=" & HMon & " dpi=" & nDPI & " scale=" & nScale & "%"
    Local bSysDbg = false
    If bSysDbg Then
       OutputDebugString("" & auCRLF & (Not MousePos ? "Hint" : "Proc " & MousePos) & auCRLF & gHBarVerb & auCRLF & "idx=" & idx)
    EndIf   
    Return idx
EndFunc

Func HBarGetHint()
    # чтение информации о кнопке из .bar фйла
    Local sMenu, sButton, sCmd, sParam, sPath, sIcon, text
    IniRead sMenu   %gHBarFile% "Buttonbar" %'"menu"   & gDragIndex' ""
    IniRead sButton %gHBarFile% "Buttonbar" %'"button" & gDragIndex' ""
    IniRead sCmd    %gHBarFile% "Buttonbar" %'"cmd"    & gDragIndex' ""
    IniRead sParam  %gHBarFile% "Buttonbar" %'"param"  & gDragIndex' ""
    IniRead sPath   %gHBarFile% "Buttonbar" %'"path"   & gDragIndex' ""
    IniRead sIcon   %gHBarFile% "Buttonbar" %'"iconic" & gDragIndex' ""
    # удаление подсказки тотала
    Local hTip = WinFind(0, "TToolTip")
    If hTip > 0 Then DllCall("DestroyWindow", "handle", hTip)
    # возврат текста     
    text = "index=" & gDragIndex & " of " & gHBarButtons+gHBarDivs & auCRLF & _           
           "menu=" & sMenu & auCRLF & _
           "button=" & sButton & auCRLF & _
           "cmd=" & sCmd & auCRLF & _
           "param=" & sParam & auCRLF & _
           "path=" & sPath  & auCRLF & _
           "iconic=" & sIcon & (gHBarDbg ? auCRLF & auCRLF & gHBarVerb & gHBarTxt : "")
    Return text
EndFunc

Func HBarDragnDrop(DragIdx, DropIdx)
    Static aItems = List("button","cmd","param","path","menu","iconic")
    Local T1, T2, bTL = 0
    If bTL Then T1 = GetUptime()    
    Local i, j, k, txt, key, new
    txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf   
    # временная замена индекса
    For j = 0 To aItems.Count - 1
       key = aItems[j] & DragIdx & "="
       new = aItems[j] & 9999 & "="
       txt = StrReplace(txt, key, new)          
    Next
    If bTL Then   
       T2 = Round(GetUptime() - T1, 0) / 1000
       OutputDebugString("Время операции 1: " &  StrFormat("%.3f", T2) & " sec")
       OutputDebugString("Drag & Drop: " &  DragIdx & " -> " & DropIdx)       
    EndIf   
    If DragIdx < DropIdx Then
       For i = DragIdx + 1 To DropIdx
            For k = 0 To aItems.Count - 1
               key = aItems[k] & i & "="
               new = aItems[k] & i - 1 & "="       
               txt = StrReplace(txt, key, new)
               #If bTL Then OutputDebugString("DragIdx < " & key & " -> " & new)               
            Next
        Next
     Else
       For i = DragIdx - 1 TO DropIdx Step -1
            For k = 0 To aItems.Count - 1
               key = aItems[k] & i & "="
               new = aItems[k] & i + 1 & "="       
               txt = StrReplace(txt, key, new)
               #If bTL Then OutputDebugString("DragIdx > " & key & " -> " & new)               
            Next
        Next
     EndIf
    If bTL Then   
       T2 = Round(GetUptime() - T1, 0) / 1000
       OutputDebugString("Время операции 2: " &  StrFormat("%.3f", T2) & " sec")
    EndIf 
    For j = 0 To aItems.Count - 1
       key = aItems[j] & 9999 & "="
       new = aItems[j] & DropIdx & "="
       txt = StrReplace(txt, key, new)          
    Next
    gHBarList.Text = txt
    gHBarList.SaveToFile(gHBarFile, gHBarCP)
    gHBarUndoTs = FileGetTime(gHBarFile)
    gHBarFileTs = FileGetTime(gHBarFile)    
    If gHBarUndoTs Then 
       gHBarUndo = true
    Else
       gHBarUndo = false    
       gHBarUndoLoad = ""   
    EndIf   
    HBarLoad()
    If bTL Then   
       T2 = Round(GetUptime() - T1, 0) / 1000
       OutputDebugString("Время операции 3: " &  StrFormat("%.3f", T2) & " sec")
    EndIf     
    SendCommand(2945)     # cm_ReloadBarIcons
EndFunc

Func ShowDragHint(DragHint)
    Local dark = IniRead(COMMANDER_INI, "Configuration", "DarkMode", 0)
    SetHintParam("ShowHint", "Font", 13, "Tahoma")
    SetHintParam("ShowHint", "BackColor", 0x0F0F0F)
    SetHintParam("ShowHint", "Text", 0xFFFFFF)
    SetHintParam("ShowHint", "DarkBackColor", 0xFF0000)
    SetHintParam("ShowHint", "DarkText", 0xFFFFFF)    
    ShowHint(auCRLF & "    " & DragHint & "    " & auCRLF, 0, 0, 1000, 1)
    WinAlign(LAST_HINT_WINDOW)
    If Not dark Then
       WinSetStyle(0x80000, 3, LAST_HINT_WINDOW)
       DllCall("SetLayeredWindowAttributes", _
                "hwnd", LAST_HINT_WINDOW, "ptr", 0, "byte", 192, "dword", 2)
    EndIf                                         
    Sleep(100)
    SetHintParam("ShowHint", "Reload")  
EndFunc

Func HBarShowMenu()
    Local hWnd = RequestInfo(28)
    If hWnd = 0 Then 
       ShowDragHint("Окно панели не найдено")
       Return
    EndIf
    If gHBarName <> WinGetText(hWnd) Then
       ShowDragHint("Нет сведений о файле панели" & auCRLF & "    " & _
                    "Используйте Shift + наведение для активации")
       Return
    EndIf
    ShowPopupMenu /D /F /I:0 "HBarMenu"
EndFunc

Func HBarMenu()
    Local txt
    Local bak1 = FileChangeExt(gHBarFile, "bak1")
    Local bak2 = FileChangeExt(gHBarFile, "bak2")    
    txt &= 'MENUITEM "'  & gHBarName & '", em_aucmd /D' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Загружена как ANSI", em_aucmd /D ' & (gHBarCP = "ANSI" ? "/C" : "") & ' -1' & auCRLF
    txt &= 'MENUITEM "Загружена как UTF-16", em_aucmd /D ' & (StrPos(gHBarCP, "UTF-16") ? "/C" : "") & ' -1' & auCRLF
    txt &= 'MENUITEM "Отладочная информация", em_aucmd ' & (gHBarDbg ?  "/C" : "") & ' -1 HBarDebugInfo' & auCRLF                 
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Отменить действие", em_aucmd ' & (gHBarUndo ?  "" : "/D") & ' -1 HBarUndo' & auCRLF
    txt &= 'MENUITEM "Отменить последние действия", em_aucmd ' & (gHBarCancel ?  "" : "/D") & ' -1 HBarCancel' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Резервная копия 1", em_aucmd ' & (FileExist(bak1) ? "" : "/D") & ' -1 HBarLoadBak 1' & auCRLF
    txt &= 'MENUITEM "Резервная копия 2", em_aucmd ' & (FileExist(bak2) ? "" : "/D") & ' -1 HBarLoadBak 2' & auCRLF    
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Сделать резервную копию 1", em_aucmd -1 HBarSaveBak 1' & auCRLF
    txt &= 'MENUITEM "Сделать резервную копию 2", em_aucmd -1 HBarSaveBak 2' & auCRLF
    Return txt
EndFunc

Func HBarDebugInfo()
    gHBarDbg = Not gHBarDbg
    If gHBarDbg Then
       ShowDragHint("Отладочная информация включена")
    Else
       ShowDragHint("Отладочная информация выключена")    
    EndIf
EndFunc

Func HBarSetCodePage(nCodePage)
    Local sCP = (nCodePage = 1 ? "ANSI" : "UTF-16")
    Local bLoadQ = gHBarCP <> sCP
    gHBarCP = sCP
    If bLoadQ Then
       MsgBox("Открыть панель в кодировке " & gHBarCP & "?", "Autorun", 3+64+0)
       If EXTENDED = 6 Then 
          HBarLoad()
          ShowDragHint("Панель открыта как " & gHBarCP)
          SendCommand(2945)  
       EndIf   
    EndIf
EndFunc

Func HBarSaveBak(nBak)
     Local bak = FileChangeExt(gHBarFile, "bak" & nBak)
     If FileExist(bak) Then
        MsgBox("Файл существует" & auCRLF & auCRLF & _
                bak & auCRLF & auCRLF & "Перезаписать?", "Autorun", 3+32+0)
        If EXTENDED <> 6 Then Return
     EndIf
     FileCopy(gHBarFile, bak, 1)
     ShowDragHint("Файл сохранен " & bak)
EndFunc

Func HBarLoadBak(nBak)
     Local bak = FileChangeExt(gHBarFile, "bak" & nBak)
     If Not FileExist(bak) Then
        MsgBox("Файл не существует " & bak, "Autorun")
        Return
     EndIf
     If Not FileExist(gHBarFile) Then
        MsgBox("Файл не существует " & gHBarFile, "Autorun")
        Return
     EndIf     
     MsgBox("Файл " & gHBarFile & " будет перезаписан файлом " & _
                bak & auCRLF & auCRLF & "Продолжить?", "Autorun", 3+32+0)
     If EXTENDED <> 6 Then Return
     FileCopy(bak, gHBarFile, 1)     
     HBarLoad()
     ShowDragHint("Файл загружен " & bak)
     SendCommand(2945)
EndFunc

Func HBarUndo()
    If Not gHBarUndo Then Return
    If FileGetTime(gHBarFile) <> gHBarUndoTs Then
       MsgBox("Файл " & gHBarFile & " изменился на диске" & auCRLF & auCRLF & _
              "Отмена действия невозможна", "Autorun", 64)
       Return       
    EndIf
    FileWrite(gHBarFile, gHBarUndoLoad, gHBarCP)    
    If ERROR = 1 Then
       MsgBox("Ошибка записи " & gHBarFile, "Autorun", 64)
    Else
       gHBarUndo = false
       gHBarUndoLoad = ""
       #gHBarFileTs = FileGetTime(gHBarFile)                  
       HBarLoad()
       ShowDragHint("Действие отменено")
       SendCommand(2945)
    EndIf   
EndFunc

Func HBarCancel()
    If Not gHBarCancel Then Return
    If gHBarCancelLoad = "" Then Return MsgBox("Пустое значение CancelLoad")
    MsgBox("Файл будет перезаписан" & auCRLF & auCRLF & _
            gHBarFile & auCRLF & auCRLF & "Продолжить?", "Autorun", 3+32+0)
    If EXTENDED <> 6 Then Return
    FileWrite(gHBarFile, gHBarCancelLoad, gHBarCP)    
    If ERROR = 1 Then
       MsgBox("Ошибка записи " & gHBarFile, "Autorun", 64)
    Else
       gHBarCancel = false
       gHBarCancelLoad = ""
       #gHBarFileTs = FileGetTime(gHBarFile)           
       HBarLoad()
       ShowDragHint("Первоначальная панель загружена")
       SendCommand(2945)
    EndIf   
EndFunc

#{  
    поиск индекса по ключу и значению:
    
    bar = COMMANDER_PATH & "\Bars\Main.bar"
    
    idx = FindButtonIdx(bar, "cmd", "61007")
    idx = FindButtonIdx(bar, "menu", "Auto_bars")   
    idx = FindButtonIdx(bar, "button", "WCMICONS.dll,72") 
    
    MsgBox(FindButtonIdx(bar, "param", '/ef "tcm(60011,1)"'))
#}
Func FindButtonIdx(ButtonBar, ButtonKey, ButtonVal)
    Local txt, pos, len, sub, idx = 0
    If Not FileExist(ButtonBar) Then
       ShowDragHint("Панель не найдена " & ButtonBar)
       Return 0   
    EndIf   
    len = StrLen(ButtonKey)
    ProcessExecGetOutput txt %COMSPEC% '/c type "%ButtonBar%"'
    pos = StrPos(txt, "=" & ButtonVal)
    If pos = 0 Then Return 0
    For i = 0 To 3
       sub = StrMid(txt, pos-len-i, len)
       If sub = ButtonKey Then
          idx = StrMid(txt, pos-i, i)
          If IsInt(idx) Then Break
       Endif
    Next
    # перепроверка индекса
    If idx > 0 Then
       IniRead txt %ButtonBar% "Buttonbar" %"ButtonKey & idx"
       If StrPos(txt, ButtonVal) > 0 Then Return idx
    EndIf
    Return 0
EndFunc
[/cut]
Функционал дает возможность почувствовать, как на самом деле приятно ощущается drag'n'drop на панели. Странно, что сам Гислер не хочет его реализовывать, не смотря на то, что расчет координат для кнопок уже есть (копирование и вставка через меню, перетаскивание значков с файловой панели), а это ведь самое сложное.

[cut="Скриншот"]Изображение[/cut]

Добавлено от 17.06.2026.

В последней версии скрипта я исправил несколько багов, которые приводили к неправильному перетаскиванию кнопок. Пока проблем больше не вижу, но это не значит, что их нет или не может быть. Желательно делать резервные копии перед использованием.

Окно панели (класс TButtonBar в ТС х32 и класс Window в ТС х64) дает информацию только об имени файла панели без пути к нему. Это накладывает ограничения при работе с bar-файлами. Сейчас путь к файлу панели ищется только в корне ТС, подкаталоге \Bars и текущем каталоге активной (файловой) панели. Можно при помощи команды OPENBAR загрузить bar-файл на главную панель и работать с ним, но нужно иметь в виду возможный конфликт имён. При одинаковых именах bar-файлов функционал может не работать или даже вредить.

Функционал хоть и рабочий, но больше демонстрационный. Безопасно использовать только подсказку, которая ничем не навредит.

P.S. Если кому-то интересно, на скрине используется функционал раскрытия главной панели инструментов в несколько строк и загрузки иконок на главную панель.

Добавлено от 25.06.2026.

[cut="Последний модуль от 25.06.2026"]MIME-Version: 1.0
Content-Type: application/octet-stream; name="Bars.7z"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="Bars.7z"

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

[/cut]

Добавлено: Ср июн 17, 2026 13:52
sa
Orion9
Хорошо бы учесть при расчёте координат вариант с использованием переноса строки в панели инструментов (значение -2 в поле команды кнопки).

Добавлено: Ср июн 17, 2026 23:08
Orion9
sa
Я разрывом на главной панели никогда не пользовался, поэтому и в функционал это не попало. Но да, надо будет подумать над разрывом в будущих версиях. Хотя код скрипта открыт, и даже комментарии кое-где имеются :D

Еще раз хочу снять с себя ответственность за возможную порчу панели. Я скрипт изначально вынес из темы Autorun, чтобы больше пользователей могло видеть, какие продвинутые вещи можно делать на языке этого плагина. О простых вещах и говорить нечего. Но функционал может быть "бажным", что не удивительно. Даже Гислер опасается делать drag&drop из-за косячного переноса, притом что он имеет доступ ко всем внутренним структурам и ничем неограниченному языку программирования. Используя Autorun, приходится заниматься обратным инженегрингом и постоянным поиском лазеек, а дело это, как известно, весьма неблагодарное )

Добавлено: Чт июн 18, 2026 12:37
Orion9
sa писал(а):значение -2 в поле команды кнопки
Думал, что без поллитра обойтись не получится (давно я этот алгоритм делал), но оказалось все проще. Нужно заменить два фрагмента в коде:
[cut]

Код: Выделить всё

# подсчет кнопок и разделителей
For j = 1 To nCount     
    key = "button" & j
    val = oButton.Get(key, 0)         
    If val > 0 Then
       # количество кнопок                 
       gHBarButtons += 1
       gHBarMap.Add(1)
    Else
       # количество разделителей  
       gHBarDivs += 1   
       If oCmd.Get("cmd" & j, 0) = 2 Then
          gHBarMap.Add(-2)           
       Else 
          gHBarMap.Add(0)
       EndIf   
    EndIf
Next
[/cut]
[cut]

Код: Выделить всё

# индекс кнопки в ряду    
If gHBarDivs = 0 Then
   idx = (Floor((mx - x)/b) + 1) + (row-1)*(Floor(w/b))
   If idx > gHBarButtons Then idx = gHBarButtons
Else
   j = 1
   idx = 0       
   For i = 0 To gHBarMap.Count - 1
       If gHBarMap[i] = -2 Then
          cx += ex           
          j += 1
          Continue
       EndIf
       cx += (gHBarMap[i] = 0 ? div : b)
       ex = j*w - cx
       If i + 1 <= gHBarMap.Count - 1 Then
          If ex < (gHBarMap[i+1] = 0 ? div : b) Then
             cx += ex
             j += 1
          EndIf
       Else
          cx += ex
       EndIf   
       If cx >= tx Then
          idx = i + 1
          Break
       EndIf   
   Next        
EndIf
[/cut]
Есть два недостатка. Значение ключей "cmd" при сохранении в объект oCmd не сохраняются, а сохраняется только их размер (не знаю, зачем я так делал, наверное, хотел каким-то образом ускориться), в результате разрыв строки проверяется не по значению "-2", а по размеру 2. Но поскольку вероятность команды с двумя символами очень мала, пока оставил так, но нужно будет переделать.
Второй недостаток — индекс после разрыва показывает на начало следующего ряда, но это мелочь, хотя тоже в идеале нужно переделать.
В остальном вроде работает. Но как уже писалось, пока все это только на свой страх и риск :)

Забыл упомянуть, что в модуле действует клавиша CapsLock. Она закрепляет Shift, чтобы не держать его каждый раз при вызове подсказки.

Если руки доберуться, добавлю больше комментариев в модуль со временем, особенно если косяков больше не обнаружится и если Гислер вдруг сам не сделает Drag&Drop на панели :D

Добавлено: Пт июн 19, 2026 09:48
sa
Orion9 писал(а):давно я этот алгоритм делал
Я подумал, что скрипт выложен по горячим следам...

Остался пока на начальной версии.
Для неё, чтобы всё правильно работало, в панели инструментов забивал строки (до разрыва -2) дополнительными кнопками (у меня TC развёрнут на весь экран).
Вот на такой панели с добавленными кнопками, версия с исправлениями и дала сбои с определением индекса (вплоть до того, что у всех кнопок третьей строки он повторялся, хотя отладочная карта панели выглядела правильно).

Добавлено: Пт июн 19, 2026 11:47
Orion9
sa писал(а):Я подумал, что скрипт выложен по горячим следам
Да нет. Еще осенью делал этот алгоритм, но были разные баги, поэтому скрипт долго находился в теме Autorun. В последней версии у меня получилось исправить эти баги и я довольно долго тестировал, прежде чем открыть тему в разделе автоматизации, но, видимо, недостаточно долго :D
sa писал(а):Остался пока на начальной версии.
Мне пока не удалось воспроизвести этот косяк, но уверен, что он есть. Так-то оно вроде работает, ниже на скрине это видно. Косяк, о котором я писал, тоже проявляется. Пустое поле за разделителем строк определяет индекс кнопки в следующем ряду, в результате перенос за разделитель приводит к переносу в следующий ряд на второе место. Это, конечно, мелочь, но Гислера-то все работает (первое действие вырезки и вставки) :D
[cut="Скрин"]Изображение[/cut]
Вообще реально удивляет: алгоритм с расчетом есть, а драгендропа нет :shock: Притом что алгоритм для расчета координат кнопок в разы сложнее, чем drag&drop. Drag&drop - это буквально полтора-два десятка строк в скрипте.

Добавлено: Пт июн 19, 2026 16:55
sa
Возможно, моя проблема с кнопками разрыва строки возникает из-за того, что при расчёте индекса кнопки неверно учитывается ширина кнопок разрыва, которая берётся из расчёта 1/3 ширины кнопки (как для обычного разделителя), а на самом деле равна 0.

Добавлено: Пт июн 19, 2026 23:23
Orion9
sa
А какие дополнительные кнопки вы добавляли перед разрывом, тоже разрывы? Да, я такое не проверял, пришлось доставать заначку, те самые поллитра :D В общем, сделал что-то, сам не понял что, но вроде работает, хотя тоже не без изъянов.

[cut="Скрин"]Изображение[/cut]
Если закидывать за разделитель, то кнопка попадает в следующий ряд, но хотя бы на первое место. Если такое поведение сбивает с толку, то лучше не закидывать через разделитель :D

Понятно, что в перспективе, скорее всего, придется пересматривать алгоритм. Но пока лучше обойтись малой кровью. Нужно заменить фрагмент
[cut]

Код: Выделить всё

If gHBarMap[i] = -2 Then
   cx += ex           
   j += 1
   Continue
EndIf
[/cut]
На:
[cut]

Код: Выделить всё

If gHBarMap[i] = -2 Then
   j += 1
   cx += ex           
   If i > 0 AND gHBarMap[i-1] = -2 Then
      cx += w - ex
   EndIf
   If cx >= tx Then
      idx = i + 1
      Break
   EndIf
   Continue
EndIf
[/cut]

Добавлено: Сб июн 20, 2026 12:09
Orion9
Опохмелился немного с утра... :)

В общем, если добавить одну строку между

Код: Выделить всё

If DragIdx < DropIdx Then
   For i = DragIdx + 1 To DropIdx
Т.е.

Код: Выделить всё

If DragIdx < DropIdx Then
   If gHBarMap[DropIdx-1] = -2 Then DropIdx -= 1
   For i = DragIdx + 1 To DropIdx
То теперь работает корректно, почти как у Гислера

[cut]Изображение[/cut]
Но у Гислера все-равно лучше и правильнее. У него работает так: если при вставке кнопки указатель мыши ближе к левому краю, то вставка происходит перед этой кнопкой; если ближе к правому, то после этой кнопки. В скрипте не так. Если индекс перетаскиваемой кнопки больше индекса вставляемой кнопки, то новая кнопка встанет перед старой. Если индекс меньше, то кнопка встанет после.

Есть еще один баг, на скрине он виден. Если панель заканчивается разрывом -2, то перенос за такой разрыв приводит к потере кнопки. Для такого случая нужно отдельно вводить условие, но пока в ближайшее время не смогу поковыряться. Случай, конечно, больше надуманный, однако его тоже надо предусмотреть.

В общем, уже лучше. Но еще есть над чем работать.

Добавлено: Пн июн 22, 2026 12:48
Orion9
Мда, надеялся, с наскока получится все решить, но увы.

Добавил небольшую приблуду для первой и последней кнопки

[cut]

Код: Выделить всё

If gHBarMap[i] = -2 Then
   # первая кнопка разрыв
   If i = 0 And gHBarMap[i] = -2 Then 
      cx += w
   EndIf               
   j += 1
   cx += ex           
   If i > 0 And gHBarMap[i-1] = -2 Then
      cx += w - ex
   EndIf
   # последняя кнопка разрыв
   If i = gHBarMap.Count - 1 And gHBarMap[i] = -2 Then 
      cx += w                 
   EndIf              
   If cx >= tx Then
      idx = i + 1
      Break
   EndIf
   Continue
EndIf
[/cut]
[cut="Полный модуль"]

Код: Выделить всё

Pragma IncludeOnce

# наведение на главную панель
ControlSetHint /F /D:50 28 "HBarShowHint"

# правый клик с удержанием Ctrl
ControlSetMouseAction /R /K:C 28 "HBarShowMenu"

Global gDragIndex, gDropIndex
Global gHBarButtons = 0, gHBarDivs = 0

Global gHBarName, gHBarFile, gHBarFileTs
Global gHBarTxt, gHBarCP = "ANSI", gHBarWincmd = 0, gHBarDbg = 0
Global gHBarHint = 0, gHBarVerb, gHBarCancel, gHBarCancelLoad, gHBarLoadError = 0
Global gHBarList = List(), gHBarMap = List(), gHBarUndo, gHBarUndoTs, gHBarUndoLoad

Global gHBarWndProc, hBarWnd = 0
Global gHBarWP = Callback("HBarWndProc", "hwnd;uint;wparam;lparam")

Func HBarWndProc(hWnd, uMsg, wParam, lParam)
    Static IsDrag = 0, _
           hTotal = DllCall("GetModuleHandleW", "Ptr", 0, "handle"), _
           MK_SHIFT        = 0x0004, _      
           MK_CONTROL      = 0x0008, _
           MK_LBUTTON      = 0x0001, _
           WM_MOUSEMOVE    = 0x0200, _
           WM_LBUTTONDOWN  = 0x0201, _
           WM_LBUTTONUP    = 0x0202
    Static drag_system = DllCall("LoadCursor", "Ptr", 0, "Ptr", 32646), _
           stop_system = DllCall("LoadCursor", "Ptr", 0, "Ptr", 32648), _
           drag_cursor = DllCall("LoadCursor", "Ptr", hTotal, "Ptr", 22), _
           stop_cursor = DllCall("LoadCursor", "Ptr", hTotal, "Ptr", 21)
    Static SM_CXDRAG = 68, SM_CYDRAG = 69, _
           dragX = GetSystemMetrics(SM_CXDRAG), dragY = GetSystemMetrics(SM_CYDRAG), _
           startX, startY
    Local x, y, pos
    If gHBarLoadError Then 
       Return DllCall("CallWindowProcW", _
              "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
    EndIf
    Switch uMsg
       Case WM_LBUTTONDOWN
          If IsDrag = 0 And BitAND(wParam, MK_CONTROL) Then
             If FileGetTime(gHBarFile) <> gHBarFileTs Then
                ShowDragHint("Операция невозможна. Файл панели изменился.")
                Return DllCall("CallWindowProcW", _
                "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
             Endif
             #MouseGetPos("x","y")
             Static buf = Buffer(8)	# POINT
             buf.Zero()
             x = BitAND(lParam, 0xFFFF)
             y = BitAND(BitShift(lParam,16), 0xFFFF)
             startX = x
             startY = y
             buf.SetNum(0, "int", x, "int", y)
             DllCall("ClientToScreen", "hwnd", hWnd, "ptr", buf.ptr)
             pos = buf.GetNum(0, "int64")             
             #x = buf.GetNum(0)
             #y = buf.GetNum(4)
             #If DllCall("DragDetect", "hwnd", hWnd, "int64", MakeInt(y, x, 2)) Then
             If DllCall("DragDetect", "hwnd", hWnd, "int64", pos) Then             
                IsDrag = 1
                WinSetStyle(0x80000, 3, hWnd)
                DllCall("SetLayeredWindowAttributes", "hwnd", hWnd, "ptr", 0, "byte", 128, "dword", 2)
                x = buf.GetNum(0)
                y = buf.GetNum(4)                           
                gDragIndex = HBarGetIdx(1, x, y)
             Else
                IsDrag = 0
                DllCall("CallWindowProcW", _
                   "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam) 
                DllCall("CallWindowProcW", _
                   "ptr", gHBarWndProc, "hwnd", hWnd, "uint", WM_LBUTTONUP, "wparam", wParam, "lparam", lParam)
                Return 0
             EndIf
          EndIf    
       Case WM_LBUTTONUP
          If IsDrag = 1 Then
             DllCall("ReleaseCapture")
             IsDrag = 0
             If WinFromPoint() = hWnd Then
                MouseGetPos("x","y")
                gDropIndex = HBarGetIdx(2, x, y)
                If gDragIndex <> gDropIndex Then
                   If gHBarHint Then ShowDragHint("Index " & gDragIndex & " To Index: " & gDropIndex)
                   HBarDragnDrop(gDragIndex, gDropIndex)
                EndIf
             EndIf
             WinSetStyle(0x80000, 5, hWnd)
             #Return 0
             #lParam = MakeInt(y, x + 200, 2)
             lParam = -1000
          EndIf
       Case WM_MOUSEMOVE
          If IsDrag = 1 And BitAND(wParam, MK_LBUTTON) Then
             If WinFromPoint() = hWnd Then
                DllCall("SetCursor", "Ptr", drag_cursor)
             Else
                DllCall("SetCursor", "Ptr", stop_cursor)               
             EndIf
          EndIf   
    EndSwitch
    Return DllCall("CallWindowProcW", _
                   "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
EndFunc

Func HBarShowHint()
    # панель в режиме drag'n'drop
    If WinHasStyle(0x80000, 1, hBarWnd) Then Return  
    # состояние CapsLock и Shift
    If BitAND(DllCall("GetKeyState", "int", 0x14, "short"), 1) = 0 Then
       If Not IsPressed(0x10) Then Return
    EndIf
    # дескриптор горизонтальной панели 
    Local hWnd = RequestInfo(28)
    If hWnd = 0 Then 
       Return "Окно панели не найдено " & hWnd
    EndIf
    # глобальные переменные дескриптора и оконной процедуры
    If hBarWnd <> hWnd Then
       hBarWnd = hWnd
       gHBarWndProc = DllCall("SetWindowLong" & (auX64 ? "PtrW" : "W"), _
                             "hwnd", hBarWnd, "int", -4, "long_ptr", gHBarWP.Ptr, "ptr")
    EndIf
    # поиск файла панели
    If gHBarName <> WinGetText(hBarWnd) Then
       gHBarUndo = false
       gHBarUndoLoad = ""
       gHBarCancel = false
       gHBarCancelLoad = ""       
       gHBarFile = HBarGetFile()
       If Not FileExist(gHBarFile) Then
          gHBarName = ""
          Return "Файл панели не найден " & gHBarFile
       EndIf
       # метка времени
       gHBarFileTs = FileGetTime(gHBarFile)
       # загрузка панели       
       HBarLoad()
       If gHBarLoadError Then Return HBarGetHint()
    ElseIf FileGetTime(gHBarFile) <> gHBarFileTs Then
       gHBarUndo = false
       gHBarUndoLoad = ""
       If Not FileExist(gHBarFile) Then
          gHBarName = ""
          Return "Файл панели не найден " & gHBarFile
       EndIf   
       gHBarFileTs = FileGetTime(gHBarFile)
       HBarLoad()
       If gHBarLoadError Then Return HBarGetHint()       
    EndIf
    # индекс кнопки под курсором
    gDragIndex = HBarGetIdx()
    If Not gDragIndex And Not gHBarDbg Then Return "Не удалось определить индекс кнопки"
    Return HBarGetHint()  
EndFunc

Func HBarGetFile()
    Local bar, txt
    # информация о файле панели в wincmd.ini
    IniRead /R bar %COMMANDER_INI% "Buttonbar" "Buttonbar" "%COMMANDER_PATH%\DEFAULT.BAR"
    # раскрытие переменной окружения
    bar = Set(bar) 
    # в конфигурации указано только имя файла
    If FileGetDir(bar) = "" Then bar = COMMANDER_PATH & "\" & bar
    gHBarWincmd = bar
    # чтение текста окна панели
    txt = WinGetText(hBarWnd)
    gHBarName = txt
    # на панель загружен временный .bar файл
    If Not StrPos(bar, txt) Then       
       bar = COMMANDER_PATH & "\Bars\" & txt
    EndIf
    If Not FileExist(bar) Then
       bar = GetCurrentPath() & "\" & txt
    EndIf     
    # проверка существования файла панели
    If Not FileExist(bar) Then
       bar = COMMANDER_PATH & "\" & txt
    EndIf      
    Return bar
EndFunc

Func HBarLoad()
    gHBarLoadError = 0
    Local bar = FileRead(gHBarFile, 128, "RAW")
    Local bom = "FFFE"
    Local utf = "5B0042007500740074006F006E006200610072005D000D000A00"
    If ERROR = 1 Then
       gHBarLoadError = 1
       gHBarCP = "Unknown"       
       Return
    EndIf   
    If StrLeft(bar, 4) = "FFFE" Then 
       gHBarCP = "UTF-16 BOM"
    Else
       IF StrPos(bar, utf) Then
          gHBarCP = "UTF-16 без BOM"
       Else
          gHBarCP = "ANSI"
       EndIf
    EndIf
    # загрузка файла в массив
    gHBarList.Count = 0
    gHBarList.LoadFromFile(gHBarFile, gHBarCP)    
    # количество кнопок в файле
    Local nCount = 0
    IniRead /R nCount  %gHBarFile% "Buttonbar" "Buttoncount" 0
    # обнуление счетчиков
    gHBarDivs = 0
    gHBarButtons = 0
    # локальные переменные
    Local j, key, val, found, oButton = Map(), oCmd = Map()
    # поиск кнопок и команд
    For j = 0 To gHBarList.Count - 1        
        found = 0    
        If StrLeft(gHBarList[j], 6) = "button" Then  
           found = 1
        ElseIf StrLeft(gHBarList[j], 3) = "cmd" Then  
           found = 2
        EndIf
        If found > 0 Then
           key = StrLower(StrPart(gHBarList[j], "=", 1))
           val = StrLen(StrPart(gHBarList[j], "=", 2))
           If found = 1 Then
              oButton.Set(key, val)              
           Else
              oCmd.Set(key, val)          
           EndIf
        EndIf
    Next
    gHBarMap.Count = 0    
    # подсчет кнопок и разделителей
    For j = 1 To nCount     
        key = "button" & j
        val = oButton.Get(key, 0)         
        If val > 0 Then
           # количество кнопок                 
           gHBarButtons += 1
           gHBarMap.Add(1)
        Else
           # количество разделителей  
           gHBarDivs += 1   
           If oCmd.Get("cmd" & j, 0) = 2 Then
              gHBarMap.Add(-2)           
           Else 
              gHBarMap.Add(0)
           EndIf   
        EndIf
    Next
    Free(oButton, oCmd)
    If gHBarButtons = 0 Then gHBarLoadError = 1
    # карта панели
    gHBarTxt = auCRLF & auCRLF & "Loaded" & ": " & gHBarFile & " > " & gHBarCP & " "
    If gHBarLoadError Then gHBarTxt &= " -- ERROR LOADING"
    gHBarTxt &= auCRLF
    For j = 0 To gHBarMap.Count - 1
        gHBarTxt &= gHBarMap[j] & "  "
        If j > 0 and Mod(j, 50) = 0 Then gHBarTxt &= auCRLF
    Next
    gHBarTxt &= auCRLF & "Wincmd.ini: " & gHBarWincmd
EndFunc

Func HBarGetIdx(MousePos = false, PosX = 0, PosY = 0)
    # дескриптор монитора окна ТС
    Local hMon = DllCall("MonitorFromWindow", "hwnd", AUTORUN_TCHANDLE, "dword", 2)
    # буфер для структуры MONITORINFOEX
    Local buf = Buffer(104)
    buf.Zero()
    buf.SetNum(0, "dword", 104)
    DllCall("GetMonitorInfoW", "ptr", hMon, "ptr", buf.ptr)
    # имя монитора
    Local sMon = buf.GetStr(40)
    # информация о масштабировании экрана
    Local DC = DllCall("GetDC", "int", 0)
    Local nDPI = DllCall("GetDeviceCaps", "handle", DC, "int", 88) # LOGPIXELSX
    Local nScale = DllCall("MulDiv", "int", 100, "int", nDPI, "int", 96)
    DllCall("ReleaseDC", "int", 0, "handle", DC)     
    # локальные переменные
    Local x, y, w, h, mx, my, idx, row, rows, rowh, dpi, b, extra
    # координаты и размеры окна панели
    WinGetPos("x", "y", "w", "h", hBarWnd) 
    # координаты указателя мыши
    If MousePos Then
       mx = PosX
       my = PosY
    Else
       MouseGetPos("mx", "my")
    EndIf   
    # чтение информации о высоте кнопок
    IniRead /R b %COMMANDER_INI% "Buttonbar" "Buttonheight"
    IniRead /R dpi %COMMANDER_INI% "Buttonbar" "DefaultDpi" %'nDPI'
    Local b_ini = b, b_calc
    # масштабирование экрана
    If dpi <> nDPI Then
       IniRead /R d %COMMANDER_INI% "Buttonbar" %'"Buttonheight" & nDPI' 0
       If d <> 0 Then 
          b = d
       Else   
          b = Round(b / (dpi / nDPI), 0)
          b_calc = "*"
       EndIf
    EndIf   
    # количество рядов кнопок на панели
    rows = Floor(h/b)
    If rows = 0 Then Return HBarShowHint("Error number of rows can't be zero")
    # количество дополнительных пикселей
    extra = Mod(h,b)
    # ширина разделителя    
    Local div = Floor(b/3)
    # высота одного ряда
    rowh = b + 3
    # номер ряда под указателем
    row = Ceil((my - y)/rowh)
    # абстрактные координаты по оси х
    Local tx = (mx - x + 1) + ((row - 1) * w), cx = 0, ex = 0
    # индекс кнопки в ряду    
    If gHBarDivs = 0 Then
       idx = (Floor((mx - x)/b) + 1) + (row-1)*(Floor(w/b))
       If idx > gHBarButtons Then idx = gHBarButtons
    Else
       j = 1
       idx = 0       
       For i = 0 To gHBarMap.Count - 1
           If gHBarMap[i] = -2 Then
              # первая кнопка разрыв
              If i = 0 And gHBarMap[i] = -2 Then 
                 cx += w
              EndIf               
              j += 1
              cx += ex           
              If i > 0 And gHBarMap[i-1] = -2 Then
                 cx += w - ex
              EndIf
              # последняя кнопка разрыв
              If i = gHBarMap.Count - 1 And gHBarMap[i] = -2 Then 
                 cx += w                 
              EndIf              
              If cx >= tx Then
                 idx = i + 1
                 Break
              EndIf
              Continue
           EndIf
           cx += (gHBarMap[i] = 0 ? div : b)
           ex = j*w - cx
           If i + 1 <= gHBarMap.Count - 1 Then
              If ex < (gHBarMap[i+1] = 0 ? div : b) Then
                 cx += ex
                 j += 1
              EndIf
           Else
              cx += ex
           EndIf   
           If cx >= tx Then
              idx = i + 1
              Break
           EndIf   
       Next        
    EndIf
    # дополнительная информация
    gHBarVerb = "mouse=" & mx & ", " & my & auCRLF & _
                "bar=" & x & ", " & y & ", " & w & ", " & h & auCRLF &  _
                "map=" & tx & ", " & cx & auCRLF & _
                "row=" & row & " of " & rows & auCRLF & _
                "height=" & rowh & " (" & extra & ")" & " b=" & b & b_calc & " (" & b_ini & ")" & auCRLF & _
                "buttons=" & gHBarButtons & " + " & gHBarDivs & " divs" & auCRLF & _ 
                "width=" & b & ", " & div & auCRLF & _
                "mon=" & HMon & " dpi=" & nDPI & " scale=" & nScale & "%"
    Local bSysDbg = false
    If bSysDbg Then
       OutputDebugString("" & auCRLF & (Not MousePos ? "Hint" : "Proc " & MousePos) & auCRLF & gHBarVerb & auCRLF & "idx=" & idx)
    EndIf   
    Return idx
EndFunc

Func HBarGetHint()
    # чтение информации о кнопке из .bar фйла
    Local sMenu, sButton, sCmd, sParam, sPath, sIcon, text
    IniRead sMenu   %gHBarFile% "Buttonbar" %'"menu"   & gDragIndex' ""
    IniRead sButton %gHBarFile% "Buttonbar" %'"button" & gDragIndex' ""
    IniRead sCmd    %gHBarFile% "Buttonbar" %'"cmd"    & gDragIndex' ""
    IniRead sParam  %gHBarFile% "Buttonbar" %'"param"  & gDragIndex' ""
    IniRead sPath   %gHBarFile% "Buttonbar" %'"path"   & gDragIndex' ""
    IniRead sIcon   %gHBarFile% "Buttonbar" %'"iconic" & gDragIndex' ""
    # удаление подсказки тотала
    Local hTip = WinFind(0, "TToolTip")
    If hTip > 0 Then DllCall("DestroyWindow", "handle", hTip)
    # возврат текста     
    text = "index=" & gDragIndex & " of " & gHBarButtons+gHBarDivs & auCRLF & _           
           "menu=" & sMenu & auCRLF & _
           "button=" & sButton & auCRLF & _
           "cmd=" & sCmd & auCRLF & _
           "param=" & sParam & auCRLF & _
           "path=" & sPath  & auCRLF & _
           "iconic=" & sIcon & (gHBarDbg ? auCRLF & auCRLF & gHBarVerb & gHBarTxt : "")
    Return text
EndFunc

Func HBarDragnDrop(DragIdx, DropIdx)
    Static aItems = List("button","cmd","param","path","menu","iconic")
    Local T1, T2, bTL = 0
    If bTL Then T1 = GetUptime()    
    Local i, j, k, txt, key, new
    txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf   
    # временная замена индекса
    For j = 0 To aItems.Count - 1
       key = aItems[j] & DragIdx & "="
       new = aItems[j] & 9999 & "="
       txt = StrReplace(txt, key, new)          
    Next
    If bTL Then   
       T2 = Round(GetUptime() - T1, 0) / 1000
       OutputDebugString("Время операции 1: " &  StrFormat("%.3f", T2) & " sec")
       OutputDebugString("Drag & Drop: " &  DragIdx & " -> " & DropIdx)       
    EndIf   
    If DragIdx < DropIdx Then
       If gHBarMap[DropIdx-1] = -2 Then DropIdx -= 1
       For i = DragIdx + 1 To DropIdx
            For k = 0 To aItems.Count - 1
               key = aItems[k] & i & "="
               new = aItems[k] & i - 1 & "="       
               txt = StrReplace(txt, key, new)
               #If bTL Then OutputDebugString("DragIdx < " & key & " -> " & new)               
            Next
        Next
     Else
       For i = DragIdx - 1 TO DropIdx Step -1
            For k = 0 To aItems.Count - 1
               key = aItems[k] & i & "="
               new = aItems[k] & i + 1 & "="       
               txt = StrReplace(txt, key, new)
               #If bTL Then OutputDebugString("DragIdx > " & key & " -> " & new)               
            Next
        Next
     EndIf
    If bTL Then   
       T2 = Round(GetUptime() - T1, 0) / 1000
       OutputDebugString("Время операции 2: " &  StrFormat("%.3f", T2) & " sec")
    EndIf 
    For j = 0 To aItems.Count - 1
       key = aItems[j] & 9999 & "="
       new = aItems[j] & DropIdx & "="
       txt = StrReplace(txt, key, new)          
    Next
    gHBarList.Text = txt
    gHBarList.SaveToFile(gHBarFile, gHBarCP)
    gHBarUndoTs = FileGetTime(gHBarFile)
    gHBarFileTs = FileGetTime(gHBarFile)    
    If gHBarUndoTs Then 
       gHBarUndo = true
    Else
       gHBarUndo = false    
       gHBarUndoLoad = ""   
    EndIf   
    HBarLoad()
    If bTL Then   
       T2 = Round(GetUptime() - T1, 0) / 1000
       OutputDebugString("Время операции 3: " &  StrFormat("%.3f", T2) & " sec")
    EndIf     
    SendCommand(2945)     # cm_ReloadBarIcons
EndFunc

Func ShowDragHint(DragHint)
    Local dark = IniRead(COMMANDER_INI, "Configuration", "DarkMode", 0)
    SetHintParam("ShowHint", "Font", 13, "Tahoma")
    SetHintParam("ShowHint", "BackColor", 0x0F0F0F)
    SetHintParam("ShowHint", "Text", 0xFFFFFF)
    SetHintParam("ShowHint", "DarkBackColor", 0xFF0000)
    SetHintParam("ShowHint", "DarkText", 0xFFFFFF)    
    ShowHint(auCRLF & "    " & DragHint & "    " & auCRLF, 0, 0, 1000, 1)
    WinAlign(LAST_HINT_WINDOW)
    If Not dark Then
       WinSetStyle(0x80000, 3, LAST_HINT_WINDOW)
       DllCall("SetLayeredWindowAttributes", _
                "hwnd", LAST_HINT_WINDOW, "ptr", 0, "byte", 192, "dword", 2)
    EndIf                                         
    Sleep(100)
    SetHintParam("ShowHint", "Reload")  
EndFunc

Func HBarShowMenu()
    Local hWnd = RequestInfo(28)
    If hWnd = 0 Then 
       ShowDragHint("Окно панели не найдено")
       Return
    EndIf
    If gHBarName <> WinGetText(hWnd) Then
       ShowDragHint("Нет сведений о файле панели" & auCRLF & "    " & _
                    "Используйте Shift + наведение для активации")
       Return
    EndIf
    ShowPopupMenu /D /F /I:0 "HBarMenu"
EndFunc

Func HBarMenu()
    Local txt
    Local bak1 = FileChangeExt(gHBarFile, "bak1")
    Local bak2 = FileChangeExt(gHBarFile, "bak2")    
    txt &= 'MENUITEM "'  & gHBarName & '", em_aucmd /D' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Загружена как ANSI", em_aucmd /D ' & (gHBarCP = "ANSI" ? "/C" : "") & ' -1' & auCRLF
    txt &= 'MENUITEM "Загружена как UTF-16", em_aucmd /D ' & (StrPos(gHBarCP, "UTF-16") ? "/C" : "") & ' -1' & auCRLF
    txt &= 'MENUITEM "Отладочная информация", em_aucmd ' & (gHBarDbg ?  "/C" : "") & ' -1 HBarDebugInfo' & auCRLF                 
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Отменить действие", em_aucmd ' & (gHBarUndo ?  "" : "/D") & ' -1 HBarUndo' & auCRLF
    txt &= 'MENUITEM "Отменить последние действия", em_aucmd ' & (gHBarCancel ?  "" : "/D") & ' -1 HBarCancel' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Резервная копия 1", em_aucmd ' & (FileExist(bak1) ? "" : "/D") & ' -1 HBarLoadBak 1' & auCRLF
    txt &= 'MENUITEM "Резервная копия 2", em_aucmd ' & (FileExist(bak2) ? "" : "/D") & ' -1 HBarLoadBak 2' & auCRLF    
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Сделать резервную копию 1", em_aucmd -1 HBarSaveBak 1' & auCRLF
    txt &= 'MENUITEM "Сделать резервную копию 2", em_aucmd -1 HBarSaveBak 2' & auCRLF
    Return txt
EndFunc

Func HBarDebugInfo()
    gHBarDbg = Not gHBarDbg
    If gHBarDbg Then
       ShowDragHint("Отладочная информация включена")
    Else
       ShowDragHint("Отладочная информация выключена")    
    EndIf
EndFunc

Func HBarSetCodePage(nCodePage)
    Local sCP = (nCodePage = 1 ? "ANSI" : "UTF-16")
    Local bLoadQ = gHBarCP <> sCP
    gHBarCP = sCP
    If bLoadQ Then
       MsgBox("Открыть панель в кодировке " & gHBarCP & "?", "Autorun", 3+64+0)
       If EXTENDED = 6 Then 
          HBarLoad()
          ShowDragHint("Панель открыта как " & gHBarCP)
          SendCommand(2945)  
       EndIf   
    EndIf
EndFunc

Func HBarSaveBak(nBak)
     Local bak = FileChangeExt(gHBarFile, "bak" & nBak)
     If FileExist(bak) Then
        MsgBox("Файл существует" & auCRLF & auCRLF & _
                bak & auCRLF & auCRLF & "Перезаписать?", "Autorun", 3+32+0)
        If EXTENDED <> 6 Then Return
     EndIf
     FileCopy(gHBarFile, bak, 1)
     ShowDragHint("Файл сохранен " & bak)
EndFunc

Func HBarLoadBak(nBak)
     Local bak = FileChangeExt(gHBarFile, "bak" & nBak)
     If Not FileExist(bak) Then
        MsgBox("Файл не существует " & bak, "Autorun")
        Return
     EndIf
     If Not FileExist(gHBarFile) Then
        MsgBox("Файл не существует " & gHBarFile, "Autorun")
        Return
     EndIf     
     MsgBox("Файл " & gHBarFile & " будет перезаписан файлом " & _
                bak & auCRLF & auCRLF & "Продолжить?", "Autorun", 3+32+0)
     If EXTENDED <> 6 Then Return
     FileCopy(bak, gHBarFile, 1)     
     HBarLoad()
     ShowDragHint("Файл загружен " & bak)
     SendCommand(2945)
EndFunc

Func HBarUndo()
    If Not gHBarUndo Then Return
    If FileGetTime(gHBarFile) <> gHBarUndoTs Then
       MsgBox("Файл " & gHBarFile & " изменился на диске" & auCRLF & auCRLF & _
              "Отмена действия невозможна", "Autorun", 64)
       Return       
    EndIf
    FileWrite(gHBarFile, gHBarUndoLoad, gHBarCP)    
    If ERROR = 1 Then
       MsgBox("Ошибка записи " & gHBarFile, "Autorun", 64)
    Else
       gHBarUndo = false
       gHBarUndoLoad = ""
       #gHBarFileTs = FileGetTime(gHBarFile)                  
       HBarLoad()
       ShowDragHint("Действие отменено")
       SendCommand(2945)
    EndIf   
EndFunc

Func HBarCancel()
    If Not gHBarCancel Then Return
    If gHBarCancelLoad = "" Then Return MsgBox("Пустое значение CancelLoad")
    MsgBox("Файл будет перезаписан" & auCRLF & auCRLF & _
            gHBarFile & auCRLF & auCRLF & "Продолжить?", "Autorun", 3+32+0)
    If EXTENDED <> 6 Then Return
    FileWrite(gHBarFile, gHBarCancelLoad, gHBarCP)    
    If ERROR = 1 Then
       MsgBox("Ошибка записи " & gHBarFile, "Autorun", 64)
    Else
       gHBarCancel = false
       gHBarCancelLoad = ""
       #gHBarFileTs = FileGetTime(gHBarFile)           
       HBarLoad()
       ShowDragHint("Первоначальная панель загружена")
       SendCommand(2945)
    EndIf   
EndFunc

#{  
    поиск индекса по ключу и значению:
    
    bar = COMMANDER_PATH & "\Bars\Main.bar"
    
    idx = FindButtonIdx(bar, "cmd", "61007")
    idx = FindButtonIdx(bar, "menu", "Auto_bars")   
    idx = FindButtonIdx(bar, "button", "WCMICONS.dll,72") 
    
    MsgBox(FindButtonIdx(bar, "param", '/ef "tcm(60011,1)"'))
#}
Func FindButtonIdx(ButtonBar, ButtonKey, ButtonVal)
    Local txt, pos, len, sub, idx = 0
    If Not FileExist(ButtonBar) Then
       ShowDragHint("Панель не найдена " & ButtonBar)
       Return 0   
    EndIf   
    len = StrLen(ButtonKey)
    ProcessExecGetOutput txt %COMSPEC% '/c type "%ButtonBar%"'
    pos = StrPos(txt, "=" & ButtonVal)
    If pos = 0 Then Return 0
    For i = 0 To 3
       sub = StrMid(txt, pos-len-i, len)
       If sub = ButtonKey Then
          idx = StrMid(txt, pos-i, i)
          If IsInt(idx) Then Break
       Endif
    Next
    # перепроверка индекса
    If idx > 0 Then
       IniRead txt %ButtonBar% "Buttonbar" %"ButtonKey & idx"
       If StrPos(txt, ButtonVal) > 0 Then Return idx
    EndIf
    Return 0
EndFunc
[/cut]
Но все-равно в последний ряд кнопка не встает из-за ограничений, введенных ранее, но теперь она хотя бы не теряеется и встает корректно перед разделителем. Чтобы переместить кнопку в последний ряд после разделителя (разрыва), придется использовать вырезку и вставку

[cut]Изображение[/cut]
Пока такие ограничения. Возможно, такими они и остануться, ибо не велика беда :)

Добавлено: Пн июн 22, 2026 14:09
sa
Orion9 писал(а):А какие дополнительные кнопки вы добавляли перед разрывом, тоже разрывы?
Нет, обычные.
Вот для простоты пример со стандартной панелью инструментов TC. Вставляем в неё два разрыва (после кнопок "Назад" и "Поиск файлов") и уменьшаем окно TC по горизонтали до размера, чтобы в верхней строке не оставалось места для ещё одной кнопки:
https://disk.yandex.ru/i/BDsT9hxN5UCEkg
Получаем повторяющийся индекс (23) для всех кнопок нижней строки.
Стоит немного расширить окно - результат нормальный:
https://disk.yandex.ru/i/l8xxs088-qCYLw

Добавлено: Пн июн 22, 2026 18:51
Orion9
sa писал(а): Получаем повторяющийся индекс (23) для всех кнопок нижней строки.
Да, понял теперь. Удалось повторить эту проблему.

В общем-то, вы были правы: нулевая ширина определялась неправильно, а вернее, она вообще не определялась (просто не была предусмотрена).

Обновленный фрагмент кода, начиная с абстрактных координат

[cut]

Код: Выделить всё

# абстрактные координаты по оси х
Local tx = (mx - x + 1) + ((row - 1) * w), cx = 0, ex = 0
# индекс кнопки в ряду    
If gHBarDivs = 0 Then
   idx = (Floor((mx - x)/b) + 1) + (row-1)*(Floor(w/b))
   If idx > gHBarButtons Then idx = gHBarButtons
Else
   j = 1
   idx = 0       
   For i = 0 To gHBarMap.Count - 1
       If gHBarMap[i] = -2 Then              
          If i = 0 Then cx += w    # первая кнопка разрыв
          j += 1
          cx += ex           
          If i > 0 And gHBarMap[i-1] = -2 Then
             cx += w - ex
          EndIf
          If i = gHBarMap.Count - 1 Then cx += w    # последняя кнопка разрыв
          If cx >= tx Then
             idx = i + 1
             Break
          EndIf
          Continue
       EndIf
       cx += (gHBarMap[i] = 0 ? div : b)
       ex = j*w - cx
       If i + 1 <= gHBarMap.Count - 1 Then
          next_button = gHBarMap[i+1]
          next_width = (next_button = -2 ? 0 : (next_button = 0 ? div : b))
          If ex < next_width Then
             cx += ex
             j += 1
          EndIf
       Else
          cx += ex
       EndIf   
       If cx >= tx Then
          idx = i + 1
          Break
       EndIf   
   Next        
EndIf
[/cut]
Я там поудалял лишние gHBarMap = -2 (сразу на трезвую голову тяжело думается), да и переменные надо заменить на нормальные, а не эти cx, ex, что это вообще такое, ассемблер что ли :lol:

Я тут заметил еще, что в ТС х64 подсказка тотала не удаляется и тем самым мешаеет часто, сразу забыл для этой версии сделать

Код: Выделить всё

# удаление подсказки тотала
Local hTip = WinFind(0, auX64 ? "HintWindow" : "TToolTip")
[cut="Полный последний модуль"]

Код: Выделить всё

Pragma IncludeOnce

# наведение на главную панель
ControlSetHint /F /D:50 28 "HBarShowHint"

# правый клик с удержанием Ctrl
ControlSetMouseAction /R /K:C 28 "HBarShowMenu"

Global gDragIndex, gDropIndex
Global gHBarButtons = 0, gHBarDivs = 0

Global gHBarName, gHBarFile, gHBarFileTs
Global gHBarTxt, gHBarCP = "ANSI", gHBarWincmd = 0, gHBarDbg = 0
Global gHBarHint = 0, gHBarVerb, gHBarCancel, gHBarCancelLoad, gHBarLoadError = 0
Global gHBarList = List(), gHBarMap = List(), gHBarUndo, gHBarUndoTs, gHBarUndoLoad

Global gHBarWndProc, hBarWnd = 0
Global gHBarWP = Callback("HBarWndProc", "hwnd;uint;wparam;lparam")

Func HBarWndProc(hWnd, uMsg, wParam, lParam)
    Static IsDrag = 0, _
           hTotal = DllCall("GetModuleHandleW", "Ptr", 0, "handle"), _
           MK_SHIFT        = 0x0004, _      
           MK_CONTROL      = 0x0008, _
           MK_LBUTTON      = 0x0001, _
           WM_MOUSEMOVE    = 0x0200, _
           WM_LBUTTONDOWN  = 0x0201, _
           WM_LBUTTONUP    = 0x0202
    Static drag_system = DllCall("LoadCursor", "Ptr", 0, "Ptr", 32646), _
           stop_system = DllCall("LoadCursor", "Ptr", 0, "Ptr", 32648), _
           drag_cursor = DllCall("LoadCursor", "Ptr", hTotal, "Ptr", 22), _
           stop_cursor = DllCall("LoadCursor", "Ptr", hTotal, "Ptr", 21)
    Static SM_CXDRAG = 68, SM_CYDRAG = 69, _
           dragX = GetSystemMetrics(SM_CXDRAG), dragY = GetSystemMetrics(SM_CYDRAG), _
           startX, startY
    Local x, y, pos
    If gHBarLoadError Then 
       Return DllCall("CallWindowProcW", _
              "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
    EndIf
    Switch uMsg
       Case WM_LBUTTONDOWN
          If IsDrag = 0 And BitAND(wParam, MK_CONTROL) Then
             If FileGetTime(gHBarFile) <> gHBarFileTs Then
                ShowDragHint("Операция невозможна. Файл панели изменился.")
                Return DllCall("CallWindowProcW", _
                "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
             Endif
             #MouseGetPos("x","y")
             Static buf = Buffer(8)	# POINT
             buf.Zero()
             x = BitAND(lParam, 0xFFFF)
             y = BitAND(BitShift(lParam,16), 0xFFFF)
             startX = x
             startY = y
             buf.SetNum(0, "int", x, "int", y)
             DllCall("ClientToScreen", "hwnd", hWnd, "ptr", buf.ptr)
             pos = buf.GetNum(0, "int64")             
             #x = buf.GetNum(0)
             #y = buf.GetNum(4)
             #If DllCall("DragDetect", "hwnd", hWnd, "int64", MakeInt(y, x, 2)) Then
             If DllCall("DragDetect", "hwnd", hWnd, "int64", pos) Then             
                IsDrag = 1
                WinSetStyle(0x80000, 3, hWnd)
                DllCall("SetLayeredWindowAttributes", "hwnd", hWnd, "ptr", 0, "byte", 128, "dword", 2)
                x = buf.GetNum(0)
                y = buf.GetNum(4)                           
                gDragIndex = HBarGetIdx(1, x, y)
             Else
                IsDrag = 0
                DllCall("CallWindowProcW", _
                   "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam) 
                DllCall("CallWindowProcW", _
                   "ptr", gHBarWndProc, "hwnd", hWnd, "uint", WM_LBUTTONUP, "wparam", wParam, "lparam", lParam)
                Return 0
             EndIf
          EndIf    
       Case WM_LBUTTONUP
          If IsDrag = 1 Then
             DllCall("ReleaseCapture")
             IsDrag = 0
             If WinFromPoint() = hWnd Then
                MouseGetPos("x","y")
                gDropIndex = HBarGetIdx(2, x, y)
                If gDragIndex <> gDropIndex Then
                   If gHBarHint Then ShowDragHint("Index " & gDragIndex & " To Index: " & gDropIndex)
                   HBarDragnDrop(gDragIndex, gDropIndex)
                EndIf
             EndIf
             WinSetStyle(0x80000, 5, hWnd)
             #Return 0
             #lParam = MakeInt(y, x + 200, 2)
             lParam = -1000
          EndIf
       Case WM_MOUSEMOVE
          If IsDrag = 1 And BitAND(wParam, MK_LBUTTON) Then
             If WinFromPoint() = hWnd Then
                DllCall("SetCursor", "Ptr", drag_cursor)
             Else
                DllCall("SetCursor", "Ptr", stop_cursor)               
             EndIf
          EndIf   
    EndSwitch
    Return DllCall("CallWindowProcW", _
                   "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
EndFunc

Func HBarShowHint()
    # панель в режиме drag'n'drop
    If WinHasStyle(0x80000, 1, hBarWnd) Then Return  
    # состояние CapsLock и Shift
    If BitAND(DllCall("GetKeyState", "int", 0x14, "short"), 1) = 0 Then
       If Not IsPressed(0x10) Then Return
    EndIf
    # дескриптор горизонтальной панели 
    Local hWnd = RequestInfo(28)
    If hWnd = 0 Then 
       Return "Окно панели не найдено " & hWnd
    EndIf
    # глобальные переменные дескриптора и оконной процедуры
    If hBarWnd <> hWnd Then
       hBarWnd = hWnd
       gHBarWndProc = DllCall("SetWindowLong" & (auX64 ? "PtrW" : "W"), _
                             "hwnd", hBarWnd, "int", -4, "long_ptr", gHBarWP.Ptr, "ptr")
    EndIf
    # поиск файла панели
    If gHBarName <> WinGetText(hBarWnd) Then
       gHBarUndo = false
       gHBarUndoLoad = ""
       gHBarCancel = false
       gHBarCancelLoad = ""       
       gHBarFile = HBarGetFile()
       If Not FileExist(gHBarFile) Then
          gHBarName = ""
          Return "Файл панели не найден " & gHBarFile
       EndIf
       # метка времени
       gHBarFileTs = FileGetTime(gHBarFile)
       # загрузка панели       
       HBarLoad()
       If gHBarLoadError Then Return HBarGetHint()
    ElseIf FileGetTime(gHBarFile) <> gHBarFileTs Then
       gHBarUndo = false
       gHBarUndoLoad = ""
       If Not FileExist(gHBarFile) Then
          gHBarName = ""
          Return "Файл панели не найден " & gHBarFile
       EndIf   
       gHBarFileTs = FileGetTime(gHBarFile)
       HBarLoad()
       If gHBarLoadError Then Return HBarGetHint()       
    EndIf
    # индекс кнопки под курсором
    gDragIndex = HBarGetIdx()
    If Not gDragIndex And Not gHBarDbg Then Return "Не удалось определить индекс кнопки"
    Return HBarGetHint()  
EndFunc

Func HBarGetFile()
    Local bar, txt
    # информация о файле панели в wincmd.ini
    IniRead /R bar %COMMANDER_INI% "Buttonbar" "Buttonbar" "%COMMANDER_PATH%\DEFAULT.BAR"
    # раскрытие переменной окружения
    bar = Set(bar) 
    # в конфигурации указано только имя файла
    If FileGetDir(bar) = "" Then bar = COMMANDER_PATH & "" & bar
    gHBarWincmd = bar
    # чтение текста окна панели
    txt = WinGetText(hBarWnd)
    gHBarName = txt
    # на панель загружен временный .bar файл
    If Not StrPos(bar, txt) Then       
       bar = COMMANDER_PATH & "\Bars" & txt
    EndIf
    If Not FileExist(bar) Then
       bar = GetCurrentPath() & "" & txt
    EndIf     
    # проверка существования файла панели
    If Not FileExist(bar) Then
       bar = COMMANDER_PATH & "" & txt
    EndIf      
    Return bar
EndFunc

Func HBarLoad()
    gHBarLoadError = 0
    Local bar = FileRead(gHBarFile, 128, "RAW")
    Local bom = "FFFE"
    Local utf = "5B0042007500740074006F006E006200610072005D000D000A00"
    If ERROR = 1 Then
       gHBarLoadError = 1
       gHBarCP = "Unknown"       
       Return
    EndIf   
    If StrLeft(bar, 4) = "FFFE" Then 
       gHBarCP = "UTF-16 BOM"
    Else
       IF StrPos(bar, utf) Then
          gHBarCP = "UTF-16 без BOM"
       Else
          gHBarCP = "ANSI"
       EndIf
    EndIf
    # загрузка файла в массив
    gHBarList.Count = 0
    gHBarList.LoadFromFile(gHBarFile, gHBarCP)    
    # количество кнопок в файле
    Local nCount = 0
    IniRead /R nCount  %gHBarFile% "Buttonbar" "Buttoncount" 0
    # обнуление счетчиков
    gHBarDivs = 0
    gHBarButtons = 0
    # локальные переменные
    Local j, key, val, found, oButton = Map(), oCmd = Map()
    # поиск кнопок и команд
    For j = 0 To gHBarList.Count - 1        
        found = 0    
        If StrLeft(gHBarList[j], 6) = "button" Then  
           found = 1
        ElseIf StrLeft(gHBarList[j], 3) = "cmd" Then  
           found = 2
        EndIf
        If found > 0 Then
           key = StrLower(StrPart(gHBarList[j], "=", 1))
           val = StrLen(StrPart(gHBarList[j], "=", 2))
           If found = 1 Then
              oButton.Set(key, val)              
           Else
              oCmd.Set(key, val)          
           EndIf
        EndIf
    Next
    gHBarMap.Count = 0    
    # подсчет кнопок и разделителей
    For j = 1 To nCount     
        key = "button" & j
        val = oButton.Get(key, 0)         
        If val > 0 Then
           # количество кнопок                 
           gHBarButtons += 1
           gHBarMap.Add(1)
        Else
           # количество разделителей  
           gHBarDivs += 1   
           If oCmd.Get("cmd" & j, 0) = 2 Then
              gHBarMap.Add(-2)           
           Else 
              gHBarMap.Add(0)
           EndIf   
        EndIf
    Next
    Free(oButton, oCmd)
    If gHBarButtons = 0 Then gHBarLoadError = 1
    # карта панели
    gHBarTxt = auCRLF & auCRLF & "Loaded" & ": " & gHBarFile & " > " & gHBarCP & " "
    If gHBarLoadError Then gHBarTxt &= " -- ERROR LOADING"
    gHBarTxt &= auCRLF
    For j = 0 To gHBarMap.Count - 1
        gHBarTxt &= gHBarMap[j] & "  "
        If j > 0 and Mod(j, 50) = 0 Then gHBarTxt &= auCRLF
    Next
    gHBarTxt &= auCRLF & "Wincmd.ini: " & gHBarWincmd
EndFunc

Func HBarGetIdx(MousePos = false, PosX = 0, PosY = 0)
    # дескриптор монитора окна ТС
    Local hMon = DllCall("MonitorFromWindow", "hwnd", AUTORUN_TCHANDLE, "dword", 2)
    # буфер для структуры MONITORINFOEX
    Local buf = Buffer(104)
    buf.Zero()
    buf.SetNum(0, "dword", 104)
    DllCall("GetMonitorInfoW", "ptr", hMon, "ptr", buf.ptr)
    # имя монитора
    Local sMon = buf.GetStr(40)
    # информация о масштабировании экрана
    Local DC = DllCall("GetDC", "int", 0)
    Local nDPI = DllCall("GetDeviceCaps", "handle", DC, "int", 88) # LOGPIXELSX
    Local nScale = DllCall("MulDiv", "int", 100, "int", nDPI, "int", 96)
    DllCall("ReleaseDC", "int", 0, "handle", DC)     
    # локальные переменные
    Local x, y, w, h, mx, my, idx, row, rows, rowh, dpi, b, extra
    # координаты и размеры окна панели
    WinGetPos("x", "y", "w", "h", hBarWnd) 
    # координаты указателя мыши
    If MousePos Then
       mx = PosX
       my = PosY
    Else
       MouseGetPos("mx", "my")
    EndIf   
    # чтение информации о высоте кнопок
    IniRead /R b %COMMANDER_INI% "Buttonbar" "Buttonheight"
    IniRead /R dpi %COMMANDER_INI% "Buttonbar" "DefaultDpi" %'nDPI'
    Local b_ini = b, b_calc
    # масштабирование экрана
    If dpi <> nDPI Then
       IniRead /R d %COMMANDER_INI% "Buttonbar" %'"Buttonheight" & nDPI' 0
       If d <> 0 Then 
          b = d
       Else   
          b = Round(b / (dpi / nDPI), 0)
          b_calc = "*"
       EndIf
    EndIf   
    # количество рядов кнопок на панели
    rows = Floor(h/b)
    If rows = 0 Then Return HBarShowHint("Error number of rows can't be zero")
    # количество дополнительных пикселей
    extra = Mod(h,b)
    # ширина разделителя    
    Local div = Floor(b/3)
    # высота одного ряда
    rowh = b + 3
    # номер ряда под указателем
    row = Ceil((my - y)/rowh)
    # абстрактные координаты по оси х
    Local tx = (mx - x + 1) + ((row - 1) * w), cx = 0, ex = 0
    # индекс кнопки в ряду    
    If gHBarDivs = 0 Then
       idx = (Floor((mx - x)/b) + 1) + (row-1)*(Floor(w/b))
       If idx > gHBarButtons Then idx = gHBarButtons
    Else
       j = 1
       idx = 0       
       For i = 0 To gHBarMap.Count - 1
           If gHBarMap[i] = -2 Then              
              If i = 0 Then cx += w    # первая кнопка разрыв
              j += 1
              cx += ex           
              If i > 0 And gHBarMap[i-1] = -2 Then
                 cx += w - ex
              EndIf
              If i = gHBarMap.Count - 1 Then cx += w    # последняя кнопка разрыв
              If cx >= tx Then
                 idx = i + 1
                 Break
              EndIf
              Continue
           EndIf
           cx += (gHBarMap[i] = 0 ? div : b)
           ex = j*w - cx
           If i + 1 <= gHBarMap.Count - 1 Then
              next_button = gHBarMap[i+1]
              next_width = (next_button = -2 ? 0 : (next_button = 0 ? div : b))
              If ex < next_width Then
                 cx += ex
                 j += 1
              EndIf
           Else
              cx += ex
           EndIf   
           If cx >= tx Then
              idx = i + 1
              Break
           EndIf   
       Next        
    EndIf
    # дополнительная информация
    gHBarVerb = "mouse=" & mx & ", " & my & auCRLF & _
                "bar=" & x & ", " & y & ", " & w & ", " & h & auCRLF &  _
                "map=" & tx & ", " & cx & auCRLF & _
                "row=" & row & " of " & rows & auCRLF & _
                "height=" & rowh & " (" & extra & ")" & " b=" & b & b_calc & " (" & b_ini & ")" & auCRLF & _
                "buttons=" & gHBarButtons & " + " & gHBarDivs & " divs" & auCRLF & _ 
                "width=" & b & ", " & div & auCRLF & _
                "mon=" & HMon & " dpi=" & nDPI & " scale=" & nScale & "%"
    Local bSysDbg = false
    If bSysDbg Then
       OutputDebugString("" & auCRLF & (Not MousePos ? "Hint" : "Proc " & MousePos) & auCRLF & gHBarVerb & auCRLF & "idx=" & idx)
    EndIf   
    Return idx
EndFunc

Func HBarGetHint()
    # чтение информации о кнопке из .bar фйла
    Local sMenu, sButton, sCmd, sParam, sPath, sIcon, text
    IniRead sMenu   %gHBarFile% "Buttonbar" %'"menu"   & gDragIndex' ""
    IniRead sButton %gHBarFile% "Buttonbar" %'"button" & gDragIndex' ""
    IniRead sCmd    %gHBarFile% "Buttonbar" %'"cmd"    & gDragIndex' ""
    IniRead sParam  %gHBarFile% "Buttonbar" %'"param"  & gDragIndex' ""
    IniRead sPath   %gHBarFile% "Buttonbar" %'"path"   & gDragIndex' ""
    IniRead sIcon   %gHBarFile% "Buttonbar" %'"iconic" & gDragIndex' ""
    # удаление подсказки тотала
    Local hTip = WinFind(0, auX64 ? "HintWindow" : "TToolTip")
    If hTip > 0 Then DllCall("DestroyWindow", "handle", hTip)
    # возврат текста     
    text = "index=" & gDragIndex & " of " & gHBarButtons+gHBarDivs & auCRLF & _           
           "menu=" & sMenu & auCRLF & _
           "button=" & sButton & auCRLF & _
           "cmd=" & sCmd & auCRLF & _
           "param=" & sParam & auCRLF & _
           "path=" & sPath  & auCRLF & _
           "iconic=" & sIcon & (gHBarDbg ? auCRLF & auCRLF & gHBarVerb & gHBarTxt : "")
    Return text
EndFunc

Func HBarDragnDrop(DragIdx, DropIdx)
    Static aItems = List("button","cmd","param","path","menu","iconic")
    Local T1, T2, bTL = 0
    If bTL Then T1 = GetUptime()    
    Local i, j, k, txt, key, new
    txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf   
    # временная замена индекса
    For j = 0 To aItems.Count - 1
       key = aItems[j] & DragIdx & "="
       new = aItems[j] & 9999 & "="
       txt = StrReplace(txt, key, new)          
    Next
    If bTL Then   
       T2 = Round(GetUptime() - T1, 0) / 1000
       OutputDebugString("Время операции 1: " &  StrFormat("%.3f", T2) & " sec")
       OutputDebugString("Drag & Drop: " &  DragIdx & " -> " & DropIdx)       
    EndIf   
    If DragIdx < DropIdx Then
       If gHBarMap[DropIdx-1] = -2 Then DropIdx -= 1
       For i = DragIdx + 1 To DropIdx
            For k = 0 To aItems.Count - 1
               key = aItems[k] & i & "="
               new = aItems[k] & i - 1 & "="       
               txt = StrReplace(txt, key, new)
               #If bTL Then OutputDebugString("DragIdx < " & key & " -> " & new)               
            Next
        Next
     Else
       For i = DragIdx - 1 TO DropIdx Step -1
            For k = 0 To aItems.Count - 1
               key = aItems[k] & i & "="
               new = aItems[k] & i + 1 & "="       
               txt = StrReplace(txt, key, new)
               #If bTL Then OutputDebugString("DragIdx > " & key & " -> " & new)               
            Next
        Next
     EndIf
    If bTL Then   
       T2 = Round(GetUptime() - T1, 0) / 1000
       OutputDebugString("Время операции 2: " &  StrFormat("%.3f", T2) & " sec")
    EndIf 
    For j = 0 To aItems.Count - 1
       key = aItems[j] & 9999 & "="
       new = aItems[j] & DropIdx & "="
       txt = StrReplace(txt, key, new)          
    Next
    gHBarList.Text = txt
    gHBarList.SaveToFile(gHBarFile, gHBarCP)
    gHBarUndoTs = FileGetTime(gHBarFile)
    gHBarFileTs = FileGetTime(gHBarFile)    
    If gHBarUndoTs Then 
       gHBarUndo = true
    Else
       gHBarUndo = false    
       gHBarUndoLoad = ""   
    EndIf   
    HBarLoad()
    If bTL Then   
       T2 = Round(GetUptime() - T1, 0) / 1000
       OutputDebugString("Время операции 3: " &  StrFormat("%.3f", T2) & " sec")
    EndIf     
    SendCommand(2945)     # cm_ReloadBarIcons
EndFunc

Func ShowDragHint(DragHint)
    Local dark = IniRead(COMMANDER_INI, "Configuration", "DarkMode", 0)
    SetHintParam("ShowHint", "Font", 13, "Tahoma")
    SetHintParam("ShowHint", "BackColor", 0x0F0F0F)
    SetHintParam("ShowHint", "Text", 0xFFFFFF)
    SetHintParam("ShowHint", "DarkBackColor", 0xFF0000)
    SetHintParam("ShowHint", "DarkText", 0xFFFFFF)    
    ShowHint(auCRLF & "    " & DragHint & "    " & auCRLF, 0, 0, 1000, 1)
    WinAlign(LAST_HINT_WINDOW)
    If Not dark Then
       WinSetStyle(0x80000, 3, LAST_HINT_WINDOW)
       DllCall("SetLayeredWindowAttributes", _
                "hwnd", LAST_HINT_WINDOW, "ptr", 0, "byte", 192, "dword", 2)
    EndIf                                         
    Sleep(100)
    SetHintParam("ShowHint", "Reload")  
EndFunc

Func HBarShowMenu()
    Local hWnd = RequestInfo(28)
    If hWnd = 0 Then 
       ShowDragHint("Окно панели не найдено")
       Return
    EndIf
    If gHBarName <> WinGetText(hWnd) Then
       ShowDragHint("Нет сведений о файле панели" & auCRLF & "    " & _
                    "Используйте Shift + наведение для активации")
       Return
    EndIf
    ShowPopupMenu /D /F /I:0 "HBarMenu"
EndFunc

Func HBarMenu()
    Local txt
    Local bak1 = FileChangeExt(gHBarFile, "bak1")
    Local bak2 = FileChangeExt(gHBarFile, "bak2")    
    txt &= 'MENUITEM "'  & gHBarName & '", em_aucmd /D' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Загружена как ANSI", em_aucmd /D ' & (gHBarCP = "ANSI" ? "/C" : "") & ' -1' & auCRLF
    txt &= 'MENUITEM "Загружена как UTF-16", em_aucmd /D ' & (StrPos(gHBarCP, "UTF-16") ? "/C" : "") & ' -1' & auCRLF
    txt &= 'MENUITEM "Отладочная информация", em_aucmd ' & (gHBarDbg ?  "/C" : "") & ' -1 HBarDebugInfo' & auCRLF                 
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Отменить действие", em_aucmd ' & (gHBarUndo ?  "" : "/D") & ' -1 HBarUndo' & auCRLF
    txt &= 'MENUITEM "Отменить последние действия", em_aucmd ' & (gHBarCancel ?  "" : "/D") & ' -1 HBarCancel' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Резервная копия 1", em_aucmd ' & (FileExist(bak1) ? "" : "/D") & ' -1 HBarLoadBak 1' & auCRLF
    txt &= 'MENUITEM "Резервная копия 2", em_aucmd ' & (FileExist(bak2) ? "" : "/D") & ' -1 HBarLoadBak 2' & auCRLF    
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Сделать резервную копию 1", em_aucmd -1 HBarSaveBak 1' & auCRLF
    txt &= 'MENUITEM "Сделать резервную копию 2", em_aucmd -1 HBarSaveBak 2' & auCRLF
    Return txt
EndFunc

Func HBarDebugInfo()
    gHBarDbg = Not gHBarDbg
    If gHBarDbg Then
       ShowDragHint("Отладочная информация включена")
    Else
       ShowDragHint("Отладочная информация выключена")    
    EndIf
EndFunc

Func HBarSetCodePage(nCodePage)
    Local sCP = (nCodePage = 1 ? "ANSI" : "UTF-16")
    Local bLoadQ = gHBarCP <> sCP
    gHBarCP = sCP
    If bLoadQ Then
       MsgBox("Открыть панель в кодировке " & gHBarCP & "?", "Autorun", 3+64+0)
       If EXTENDED = 6 Then 
          HBarLoad()
          ShowDragHint("Панель открыта как " & gHBarCP)
          SendCommand(2945)  
       EndIf   
    EndIf
EndFunc

Func HBarSaveBak(nBak)
     Local bak = FileChangeExt(gHBarFile, "bak" & nBak)
     If FileExist(bak) Then
        MsgBox("Файл существует" & auCRLF & auCRLF & _
                bak & auCRLF & auCRLF & "Перезаписать?", "Autorun", 3+32+0)
        If EXTENDED <> 6 Then Return
     EndIf
     FileCopy(gHBarFile, bak, 1)
     ShowDragHint("Файл сохранен " & bak)
EndFunc

Func HBarLoadBak(nBak)
     Local bak = FileChangeExt(gHBarFile, "bak" & nBak)
     If Not FileExist(bak) Then
        MsgBox("Файл не существует " & bak, "Autorun")
        Return
     EndIf
     If Not FileExist(gHBarFile) Then
        MsgBox("Файл не существует " & gHBarFile, "Autorun")
        Return
     EndIf     
     MsgBox("Файл " & gHBarFile & " будет перезаписан файлом " & _
                bak & auCRLF & auCRLF & "Продолжить?", "Autorun", 3+32+0)
     If EXTENDED <> 6 Then Return
     FileCopy(bak, gHBarFile, 1)     
     HBarLoad()
     ShowDragHint("Файл загружен " & bak)
     SendCommand(2945)
EndFunc

Func HBarUndo()
    If Not gHBarUndo Then Return
    If FileGetTime(gHBarFile) <> gHBarUndoTs Then
       MsgBox("Файл " & gHBarFile & " изменился на диске" & auCRLF & auCRLF & _
              "Отмена действия невозможна", "Autorun", 64)
       Return       
    EndIf
    FileWrite(gHBarFile, gHBarUndoLoad, gHBarCP)    
    If ERROR = 1 Then
       MsgBox("Ошибка записи " & gHBarFile, "Autorun", 64)
    Else
       gHBarUndo = false
       gHBarUndoLoad = ""
       #gHBarFileTs = FileGetTime(gHBarFile)                  
       HBarLoad()
       ShowDragHint("Действие отменено")
       SendCommand(2945)
    EndIf   
EndFunc

Func HBarCancel()
    If Not gHBarCancel Then Return
    If gHBarCancelLoad = "" Then Return MsgBox("Пустое значение CancelLoad")
    MsgBox("Файл будет перезаписан" & auCRLF & auCRLF & _
            gHBarFile & auCRLF & auCRLF & "Продолжить?", "Autorun", 3+32+0)
    If EXTENDED <> 6 Then Return
    FileWrite(gHBarFile, gHBarCancelLoad, gHBarCP)    
    If ERROR = 1 Then
       MsgBox("Ошибка записи " & gHBarFile, "Autorun", 64)
    Else
       gHBarCancel = false
       gHBarCancelLoad = ""
       #gHBarFileTs = FileGetTime(gHBarFile)           
       HBarLoad()
       ShowDragHint("Первоначальная панель загружена")
       SendCommand(2945)
    EndIf   
EndFunc

#{  
    поиск индекса по ключу и значению:
    
    bar = COMMANDER_PATH & "\Bars\Main.bar"
    
    idx = FindButtonIdx(bar, "cmd", "61007")
    idx = FindButtonIdx(bar, "menu", "Auto_bars")   
    idx = FindButtonIdx(bar, "button", "WCMICONS.dll,72") 
    
    MsgBox(FindButtonIdx(bar, "param", '/ef "tcm(60011,1)"'))
#}
Func FindButtonIdx(ButtonBar, ButtonKey, ButtonVal)
    Local txt, pos, len, sub, idx = 0
    If Not FileExist(ButtonBar) Then
       ShowDragHint("Панель не найдена " & ButtonBar)
       Return 0   
    EndIf   
    len = StrLen(ButtonKey)
    ProcessExecGetOutput txt %COMSPEC% '/c type "%ButtonBar%"'
    pos = StrPos(txt, "=" & ButtonVal)
    If pos = 0 Then Return 0
    For i = 0 To 3
       sub = StrMid(txt, pos-len-i, len)
       If sub = ButtonKey Then
          idx = StrMid(txt, pos-i, i)
          If IsInt(idx) Then Break
       Endif
    Next
    # перепроверка индекса
    If idx > 0 Then
       IniRead txt %ButtonBar% "Buttonbar" %"ButtonKey & idx"
       If StrPos(txt, ButtonVal) > 0 Then Return idx
    EndIf
    Return 0
EndFunc
[/cut]
Функционал - топ. Жаль, что Гислер сам не хочет его делать. Особенно мне нравится, что разделители можно преносить наряду с другими кнопками и разрывами.

Добавлено: Пн июн 22, 2026 21:18
sa
Orion9 писал(а): Полный последний модуль
Этот вариант как будто работает с переносами строк! Спасибо за терпение!
А левую/правую половину кнопки (как у Гислера) сложно будет учесть при переносе? Или заначка закончилась уже?:)

Добавлено: Пн июн 22, 2026 23:29
Orion9
sa писал(а):А левую/правую половину кнопки (как у Гислера) сложно будет учесть при переносе? Или заначка закончилась уже?
Заначка-то всегда найдется, но для таких случаев нужен особый вид цифрового опьянения — тот самый грув, под который сам Гислер отжигал в лучшие годы на танцполе )

[cut="Mo-Do"]Dance with me tonight, if you are my fly(?) (муха что ли), if you are my dream :mrgreen: [/cut]
Если серьезно, то можно, конечно, сделать. Переменная tx указывает текущие координаты мыши в абстрактной плоскости, а cx обозначает границу текущей кнопки. Если отнять от сх ширину текущей кнопки, то получится нижняя граница кнопки. Останется только посмотреть к чему ближе tx — к верхней границе или к нижней.

Однако не все так просто. Еще придется переделывать HBarDragnDrop (сборку новой панели) с учетом возможных отклонений индекса в ту или иную сторону, а это чуть сложнее.

В общем, можно будет подумать, хотя мне текущий вариант тоже по-своему нравится. Хотелось бы только убедиться, что косяков больше нету. Парочку мы отловили, но это не значит, что других нет :) Нужно больше тестов, а алгоритм я так и так буду пересматривать, чтобы избавится от этих cx, ex и т.п. Именно из-за них и пришлось поднимать поллитра (из-за трудночитаемасти алгоритма с такими малоинформативными именами), будто не сам это делал, фактически заново пришлось вникать )

Добавлено: Вт июн 23, 2026 08:36
sa
Orion9 писал(а):Перетаскивание работает с удержанием CTRL
С самого начала поменял у себя на SHIFT - как-то привычнее для перетаскивания и, наоборот, вместо Shift назначил Ctrl. Глядишь, со временем CTRL для операции копирования пригодится :).