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

Обсуждение вопросов автоматизации Total Commander с помощью сторонних программ и скриптовых технологий.

Модераторы: CaptainFlint, Nik, d-view, Моторокер, Avada

sa
Сообщения: 2758
Зарегистрирован: Ср апр 29, 2009 18:56

Сообщение sa »

Orion9 писал(а):сделал сохранение
По-моему, файл не перезаписывается (не меняет дату и время модификации), если имя остаётся прежним. Это немного настораживает…
Аватара пользователя
Orion9
Сообщения: 1293
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

sa писал(а):Это немного настораживает…
Согласен. Сделал сообщение, что нельзя в тот же файл сохранить.

С удалением, конечно, надо подумать, но это дело второе. Меня больше интересовал перенос значка, и вроде получилось. Кода совсем немного, но приложу весь модуль, поскольку было много мелких правок, включая WinRedraw(0), который помогает лучше перерисовывать, чем скрытие панели.

Перенос значка - копирование + сброс с удержанием перемещения :)
beta

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

Pragma IncludeOnce

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

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

Global gDragIndex, gDropIndex, gSmartIndex
Global gHBarButtons = 0, gHBarDivs = 0, gHBarSmart = 1

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, _
           MoveButton      = MK_CONTROL, _
           CopyButton      = MK_SHIFT
    Static drag_system = DllCall("LoadCursor", "Ptr", 0, "Ptr", 32646), _
           stop_system = DllCall("LoadCursor", "Ptr", 0, "Ptr", 32648), _
           icon_cursor = DllCall("LoadCursor", "Ptr", hTotal, "Ptr", 13), _           
           kick_cursor = DllCall("LoadCursor", "Ptr", hTotal, "Ptr", 26), _
           copy_cursor = DllCall("LoadCursor", "Ptr", hTotal, "Ptr", 25), _
           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, MoveButton) Or BitAND(wParam, CopyButton)) Then
             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
                If FileGetTime(gHBarFile) <> gHBarFileTs Then
                   ShowDragHint("Операция невозможна. Файл панели изменился.")
                   Return DllCall("CallWindowProcW", _
                   "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
                Endif                
                If BitAND(wParam, MoveButton) And BitAND(wParam, CopyButton) Then
                   IsDrag = MoveButton
                Else
                   IsDrag = wParam
                EndIf
                WinSetStyle(0x80000, 3, hWnd)
                If BitAND(wParam, MoveButton) Then
                   DllCall("SetLayeredWindowAttributes", "hwnd", hWnd, "ptr", 0, "byte", 128, "dword", 2)
                Else   
                   DllCall("SetLayeredWindowAttributes", "hwnd", hWnd, "ptr", 0, "byte", 216, "dword", 2)
                EndIf   
                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
             Local nDragMode = IsDrag
             DllCall("ReleaseCapture")
             IsDrag = 0
             If WinFromPoint() = hWnd Then
                MouseGetPos("x","y")
                gSmartIndex = 0
                gDropIndex = HBarGetIdx(2, x, y)
                If gDragIndex <> gDropIndex Then
                   If gHBarHint Then ShowDragHint("Index " & gDragIndex & " To Index: " & gDropIndex)
                   If BitAND(nDragMode, MoveButton) Then
                      HBarDragnDrop(gDragIndex, gDropIndex)
                   Else
                      If BitAND(wParam, MoveButton) Then
                         HBarDragIcon(gDragIndex, gDropIndex)
                      Else
                         HBarDragnCopy(gDragIndex, gDropIndex)
                      EndIf
                   EndIf   
                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
                If BitAND(IsDrag, CopyButton) Then
                   DllCall("SetCursor", "Ptr", BitAND(wParam, MoveButton) ? icon_cursor : copy_cursor)
                Else   
                   DllCall("SetCursor", "Ptr", drag_cursor)
                EndIf 
                #DllCall("SetCursor", "Ptr", BitAND(IsDrag, MoveButton) ? drag_cursor : copy_cursor)
             Else
                y = BitAND(BitShift(lParam,16), 0xFFFF)
                #WinSetText(lParam & " y=" & y)
                If BitAND(wParam, MoveButton) And lParam > 0 And y > 200 Then
                   DllCall("SetCursor", "Ptr", kick_cursor)
                Else   
                   DllCall("SetCursor", "Ptr", stop_cursor)
                EndIf    
             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 ShowDragHint("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, idx_padding = 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
           idx_padding = 0
           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
                 idx_padding += ex
                 j += 1
              EndIf
           Else
              cx += ex
              idx_padding = ex
           EndIf   
           If cx >= tx Then
              idx = i + 1
              Break
           EndIf   
       Next
    EndIf
    idx_button = gHBarMap[idx-1]
    idx_width = (idx_button = -2 ? 0 : (idx_button = 0 ? div : b))
    idx_pixel = tx - (cx - idx_padding - idx_width)
    idx_float = idx_width > 0 ? idx_pixel / idx_width : 0
    # дробный индекс    
    gSmartIndex = idx + Round(idx_float, 3)
    If Floor(gSmartIndex) > idx Then gSmartIndex = idx + 0.999
    # дополнительная информация
    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 & "%" & auCRLF & auCRLF & _ 
                "smart=" & gSmartIndex & auCRLF & _
                "button=" & idx_button & " width=" & idx_width & " pixel=" & idx_pixel
    Local bSysDbg = false
    If bSysDbg Then
       OutputDebugString("" & auCRLF & (Not MousePos ? "Hint" : "Proc " & MousePos) & auCRLF & gHBarVerb & auCRLF & "idx=" & idx)
    EndIf
    If MousePos <> 2 Then gSmartIndex = 0
    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 gHBarSmart Then
       If gSmartIndex > DropIdx + 0.499 Then
          If DragIdx >= DropIdx Then
             DropIdx += 1
          EndIf
       Else
          If DragIdx < DropIdx Then
             DropIdx -= 1
          EndIf
       EndIf
    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 HBarReadIndex(Index)
    Local key, val, txt
    Static aItems = List("button","cmd","param","path","menu","iconic")
    # сохранение индекса
     For j = 0 To aItems.Count - 1
        key = aItems[j] & Index
        val = IniRead(gHBarFile, "Buttonbar", key) 
        If val <> '' Then 
           If StrLeft(val, 1) ='"' And StrRight(val, 1) ='"' Then val = '"' & val & '"'
           txt &= key & '=' & val & auCRLF
       EndIf  
     Next 
     Return StrTrim(txt)
EndFunc

Func HBarDragnCopy(DragIdx, DropIdx)
    Local key, val, ini, new
    Static aItems = List("button","cmd","param","path","menu","iconic")
    # сохранение кнопки
    For j = 0 To aItems.Count - 1
       key = aItems[j] & DragIdx
       val = IniRead(gHBarFile, "Buttonbar", key) 
       If val <> '' Then 
          If StrLeft(val, 1) ='"' And StrRight(val, 1) ='"' Then val = '"' & val & '"'
          ini &= key & '=' & val & auCRLF
       EndIf   
    Next 
    ini = StrTrim(ini)
    If ini = '' Then Return ShowDragHint("Нет данных о кнопке " & DragIdx)
    # сохранение панели    
    Local txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf
    # умный сброс по границам кнопки
    If gHBarSmart Then
       If gSmartIndex > DropIdx + 0.499 Then
          DropIdx += 1 
       EndIf
    EndIf
    If DropIdx <= gHBarMap.Count Then
       # замена ключей со сдвигом +1
       For i = gHBarMap.Count To DropIdx Step -1
           For j = 0 To aItems.Count - 1
               key = aItems[j] & i & "="
               new = aItems[j] & i + 1 & "="
               txt = StrReplace(txt, key, new)
           Next
       Next
    EndIf   
    # добавление сохраненной кнопки
    For j = 0 To aItems.Count - 1
        key = aItems[j] & DragIdx & "="
        new = aItems[j] & DropIdx & "="
        ini = StrReplace(ini, key, new)
    Next
    txt &= auCRLF & ini
    # обновление панели
    gHBarList.Text = txt
    gHBarList.SaveToFile(gHBarFile, gHBarCP)
    IniWrite(gHBarFile, "Buttonbar", "Buttoncount", gHBarMap.Count+1)
    gHBarUndoTs = FileGetTime(gHBarFile)
    gHBarFileTs = FileGetTime(gHBarFile)    
    If gHBarUndoTs Then 
       gHBarUndo = true
    Else
       gHBarUndo = false    
       gHBarUndoLoad = ""   
    EndIf   
    HBarLoad()   
    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 SEPARATOR' & auCRLF    
    txt &= 'MENUITEM "Smart Drag&&Drop", em_aucmd ' & (gHBarSmart ?  "/C" : "") & ' -1 HBarSmart' & auCRLF     
    txt &= 'MENUITEM "Отладочная информация", em_aucmd ' & (gHBarDbg ?  "/C" : "") & ' -1 HBarDebugInfo' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF      
    txt &= 'MENUITEM "Сохранить...", em_aucmd -1 HBarSaveAs' & auCRLF      
    txt &= 'POPUP "Загрузить панель"' & auCRLF
    txt &= HBarAddBarMenu() & auCRLF      
    txt &= 'END_POPUP' & auCRLF               
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'POPUP "Удалить кнопки за разрывом"' & auCRLF
    txt &= HBarBreakMenu() & auCRLF      
    txt &= 'END_POPUP' & 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 HBarSmart()
    gHBarSmart = Not gHBarSmart
    If gHBarSmart Then
       ShowDragHint("Smart Drag&Drop включен")
    Else
       ShowDragHint("Smart Drag&Drop отключен")    
    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)
     WinRedraw(0)     
     #WinRedraw(2)
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)
       WinRedraw(0)
    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)
       WinRedraw(0)
    EndIf   
EndFunc

gHBarBars = list()
    
Func HBarAddBarMenu()
    Local sBars = FileFind(COMMANDER_PATH, "*.bar", -1, 1)
    #sBars = StrTrim(sBars)
    Local text
    gHBarBars.Count = 0
    gHBarBars.Text = sBars
    For i = 0 To gHBarBars.Count - 1
        text = text & 'MENUITEM "' & gHBarBars[i] & '", em_aucmd -1 HBarMergeOne ' & i & auCRLF 
    Next
    Return text
EndFunc

Func HBarMergeOne(FileIndex)
    Local bar = gHBarBars[FileIndex]
    c = IniRead(bar, "Buttonbar", "Buttoncount", 0)
    If c = 0 Then 
       MsgBox("Файл не содержит данных о кнопках" & auCRLF & auCRLF & bar, "Autorun", 0+48)
       Return
    EndIf
    If FileGetTime(gHBarFile) <> gHBarFileTs Then
       MsgBox("Операция невозможна. Файл главной панели изменился.")
       Return
    Endif
    Local text =  FileRead(bar), count = gHBarMap.Count + 1
    Local rext = 'button' & count & '=' & auCRLF & _
                 'cmd' & count & '=-2' & auCRLF & 'iconic' & count & '=0' & auCRLF 
    Local i = 0
    Local rex = RegExp('(?i)(button|cmd|param|path|menu|iconic)(\d+?)=(.*?)\R', text)
    If rex.Exec() Then 
        Do 
           #If StrLower(rex.Match[2]) = 'button' Then i += 1
           rext &= rex.Match[1] & rex.Match[2]+count & '=' & rex.Match[3] & auCRLF
        Until not rex.ExecNext()
    EndIf     
    Free(rex)
    #If i < 1 Then Return MsgBox("Error: the number of buttons can't be zero")
    #rext = StrTrim(rext)
    # сохранение панели
    Local txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf    
    txt &= auCRLF & rext
    # обновление панели
    gHBarList.Text = txt
    gHBarList.SaveToFile(gHBarFile, gHBarCP)
    IniWrite(gHBarFile, "Buttonbar", "Buttoncount", count+c)
    gHBarUndoTs = FileGetTime(gHBarFile)
    gHBarFileTs = FileGetTime(gHBarFile)    
    If gHBarUndoTs Then 
       gHBarUndo = true
    Else
       gHBarUndo = false    
       gHBarUndoLoad = ""   
    EndIf   
    HBarLoad()   
    SendCommand(2945)     # cm_ReloadBarIcons    
    #SendMessage(AUTORUN_TCHANDLE, 11, 0, 0)
    #SendCommand(2901, 0, 1)
    #SendCommand(2901, 0, 1)   # cm_VisButtonBar
    #Sleep(10)
    #SendMessage(AUTORUN_TCHANDLE, 11, 1, 0)
    WinRedraw(0)    
    #WinRedraw(2)    
EndFunc

Func HBarBreakMenu()
    Local text
    For i = 0 To gHBarMap.Count - 1
        If gHBarMap[i] = -2 Then
           text = text & 'MENUITEM "Индекс разрыва: ' & i+1 & '", em_aucmd -1 HBarBreakErase ' & i+1 & auCRLF
        EndIf   
    Next
    If text = "" Then text = 'MENUITEM "Нет разрывов", em_aucmd /D -1'
    Return text
EndFunc

Func HBarBreakErase(BreakIndex)
    If FileGetTime(gHBarFile) <> gHBarFileTs Then
       MsgBox("Операция невозможна. Файл главной панели изменился.")
       Return
    Endif
    If BreakIndex < 1 Then
       MsgBox("Индекс разрыва не может быть меньше 2")
       Return
    Endif    
    Local text =  gHBarList.Text, rext = ""
    Local rex = RegExp('(?i)(button|cmd|param|path|menu|iconic)(\d+?)=(.*?)\R', text)    
    If rex.Exec() Then 
        Do 
           If rex.Match[2] < BreakIndex Then
              rext &= rex.Match[1] & rex.Match[2] & '=' & rex.Match[3] & auCRLF
           EndIf   
        Until not rex.ExecNext()
    EndIf
    Free(rex)
    If rext = "" Then Return MsgBox("Ошибка удаления. Buttoncount=0, BreakIndex=" & BreakIndex)
    Local txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf
    # обновление панели
    gHBarList.Text = "[Buttonbar]" & auCRLF & "Buttoncount=" & BreakIndex-1 & auCRLF & rext
    gHBarList.SaveToFile(gHBarFile, gHBarCP)
    gHBarUndoTs = FileGetTime(gHBarFile)
    gHBarFileTs = FileGetTime(gHBarFile)    
    If gHBarUndoTs Then 
       gHBarUndo = true
    Else
       gHBarUndo = false
       gHBarUndoLoad = ""
    EndIf   
    HBarLoad()   
    #SendMessage(AUTORUN_TCHANDLE, 11, 0, 0)
    #SendCommand(2901, 0, 1)
    #SendCommand(2901, 0, 1)
    #Sleep(10)
    #SendMessage(AUTORUN_TCHANDLE, 11, 1, 0)
    SendCommand(2945)     # cm_ReloadBarIcons        
    WinRedraw(0)    
    #WinRedraw(2)    
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

Func HBarSaveAs()
    # буфер для имени файла
    Local file = Buffer(1024)
    file.Zero()
    file.SetStr(gHBarFile)

    # буфер для фильтра расширений
    Local fstr = "Bar files (*.bar)" & Chr(0) & "*.bar" & Chr(0) & Chr(0)
    Local flen = StrLen(fstr) * 2
    Local filt = Buffer(flen)
    filt.Zero()
    filt.SetStr(fstr)
    
    # буфер для структуры OPENFILENAMEW    
    Local ofn = Buffer(auX64 ? 152 : 76)
    ofn.Zero()
    ofn.SetNum(0, "UInt", ofn.size)    # lStructSize
    If auX64 Then
       ofn.SetNum(8,  "Ptr",   AUTORUN_TCHANDLE)   # hwndOwner
       ofn.SetNum(24, "Ptr",   filt.ptr)           # lpstrFilter
       ofn.SetNum(48, "Ptr",   file.ptr)           # lpstrFile
       ofn.SetNum(56, "Dword", file.size)          # nMaxFile
       ofn.SetNum(96, "Dword", 0x00000802)         # 0x00000002 | 0x00000800
    Else
       ofn.SetNum(4,  "Ptr",   AUTORUN_TCHANDLE)   # hwndOwner
       ofn.SetNum(12, "Ptr",   filt.ptr)           # lpstrFilter
       ofn.SetNum(28, "Ptr",   file.ptr)           # lpstrFile
       ofn.SetNum(32, "Dword", file.size)          # nMaxFile
       ofn.SetNum(52, "Dword", 0x00000802)         # 0x00000002 | 0x00000800       
    EndIf
    
    If DllCall("comdlg32\GetSaveFileNameW", "Ptr", ofn.ptr) Then
       Local new = file.GetStr()
       If new <> gHBarFile Then 
          gHBarList.SaveToFile(new, gHBarCP)
       Else
          MsgBox("Файл не может быть сохранен сам в себя","Autorun", 48)
       EndIf   
    EndIf
    
    Free(file, filt, ofn)
EndFunc

Func HBarDragIcon(DragIdx, DropIdx)
    # сохранение значка
    Local val = IniRead(gHBarFile, "Buttonbar", "button" & DragIdx) 
    If val = '' Then 
       Return ShowDragHint("Нет данных о значке button" & DragIdx)
    Else 
       If StrLeft(val, 1) ='"' And StrRight(val, 1) ='"' Then val = '"' & val & '"'  
    EndIf
    # сохранение панели    
    Local txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf
    # обновление панели
    IniWrite(gHBarFile, "Buttonbar", "button" & DropIdx, val)
    gHBarUndoTs = FileGetTime(gHBarFile)
    gHBarFileTs = FileGetTime(gHBarFile)    
    If gHBarUndoTs Then 
       gHBarUndo = true
    Else
       gHBarUndo = false    
       gHBarUndoLoad = ""   
    EndIf   
    HBarLoad()   
    SendCommand(2945)     # cm_ReloadBarIcons
EndFunc
Показать
Изображение
Аватара пользователя
Orion9
Сообщения: 1293
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

sa
Да, я немного ступил, когда вы говорили об удалении. Думал, речь идет о выделении дипазона, но увы, в Autorun нет возможности делать поля ввода, за исключением самой простой формы, да и то через VBScript.

Что касается удаления одной кнопки, сделать его было несложно (самая последняя функция в модуле). Пока сделал так: вынос кнопки на 200 пикселей за панель (от начала панели) + удержание кнопки переноса удаляет ее. При этом появляется соответствующий значок. DPI не учитывается, что может быть проблемой при большом масштабировании, но это пока только для теста. Все можно изменить. Может быть, у вас появятся какие-то идеи, если не понравится. Я сам пока не понял: вроде бы нормально, но...

Относительно диапазона, сделал в меню удаления за разрывом дополнительные пункты для удаления кнопок между разрывов. Немного потестировал — вроде работает. Но косяки, конечно, могут быть. Пока не буду на первой странице делать обновления, поскольку модуль еще в режиме тестирования.
Показать

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

Pragma IncludeOnce

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

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

# Win + Backspace
# отмена последнего действия 
SetHotkeyAction /K:W /H:BS /DM /S "HBarUndo"

Global gDragIndex, gDropIndex, gSmartIndex
Global gHBarButtons = 0, gHBarDivs = 0, gHBarSmart = 1

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, _
           MoveButton      = MK_CONTROL, _
           CopyButton      = MK_SHIFT
    Static drag_system = DllCall("LoadCursor", "Ptr", 0, "Ptr", 32646), _
           stop_system = DllCall("LoadCursor", "Ptr", 0, "Ptr", 32648), _
           icon_cursor = DllCall("LoadCursor", "Ptr", hTotal, "Ptr", 13), _           
           kick_cursor = DllCall("LoadCursor", "Ptr", hTotal, "Ptr", 26), _
           copy_cursor = DllCall("LoadCursor", "Ptr", hTotal, "Ptr", 25), _
           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, MoveButton) Or BitAND(wParam, CopyButton)) Then
             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
                If FileGetTime(gHBarFile) <> gHBarFileTs Then
                   ShowDragHint("Операция невозможна. Файл панели изменился.")
                   Return DllCall("CallWindowProcW", _
                   "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
                Endif                
                If BitAND(wParam, MoveButton) And BitAND(wParam, CopyButton) Then
                   IsDrag = MoveButton
                Else
                   IsDrag = wParam
                EndIf
                WinSetStyle(0x80000, 3, hWnd)
                If BitAND(wParam, MoveButton) Then
                   DllCall("SetLayeredWindowAttributes", "hwnd", hWnd, "ptr", 0, "byte", 128, "dword", 2)
                Else   
                   DllCall("SetLayeredWindowAttributes", "hwnd", hWnd, "ptr", 0, "byte", 216, "dword", 2)
                EndIf   
                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
             Local nDragMode = IsDrag
             DllCall("ReleaseCapture")
             IsDrag = 0
             If WinFromPoint() = hWnd Then
                MouseGetPos("x","y")
                gSmartIndex = 0
                gDropIndex = HBarGetIdx(2, x, y)
                If gDragIndex <> gDropIndex Then
                   If gHBarHint Then ShowDragHint("Index " & gDragIndex & " To Index: " & gDropIndex)
                   If BitAND(nDragMode, MoveButton) Then                      
                      HBarDragnDrop(gDragIndex, gDropIndex)
                   Else
                      If BitAND(wParam, MoveButton) Then
                         HBarDragIcon(gDragIndex, gDropIndex)
                      Else
                         HBarDragnCopy(gDragIndex, gDropIndex)
                      EndIf
                   EndIf   
                EndIf
             Else
                y = BitAND(BitShift(lParam,16), 0xFFFF)
                If BitAND(wParam, MoveButton) And lParam > 0 And y > 200 Then HBarDragDelete(gDragIndex)
             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
                If BitAND(IsDrag, CopyButton) Then
                   DllCall("SetCursor", "Ptr", BitAND(wParam, MoveButton) ? icon_cursor : copy_cursor)
                Else   
                   DllCall("SetCursor", "Ptr", drag_cursor)
                EndIf 
                #DllCall("SetCursor", "Ptr", BitAND(IsDrag, MoveButton) ? drag_cursor : copy_cursor)
             Else
                y = BitAND(BitShift(lParam,16), 0xFFFF)
                #WinSetText(lParam & " y=" & y)
                If BitAND(wParam, MoveButton) And lParam > 0 And y > 200 Then
                   DllCall("SetCursor", "Ptr", kick_cursor)
                Else   
                   DllCall("SetCursor", "Ptr", stop_cursor)
                EndIf    
             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 ShowDragHint("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, idx_padding = 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
           idx_padding = 0
           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
                 idx_padding += ex
                 j += 1
              EndIf
           Else
              cx += ex
              idx_padding = ex
           EndIf   
           If cx >= tx Then
              idx = i + 1
              Break
           EndIf   
       Next
    EndIf
    idx_button = gHBarMap[idx-1]
    idx_width = (idx_button = -2 ? 0 : (idx_button = 0 ? div : b))
    idx_pixel = tx - (cx - idx_padding - idx_width)
    idx_float = idx_width > 0 ? idx_pixel / idx_width : 0
    # дробный индекс    
    gSmartIndex = idx + Round(idx_float, 3)
    If Floor(gSmartIndex) > idx Then gSmartIndex = idx + 0.999
    # дополнительная информация
    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 & "%" & auCRLF & auCRLF & _ 
                "smart=" & gSmartIndex & auCRLF & _
                "button=" & idx_button & " width=" & idx_width & " pixel=" & idx_pixel
    Local bSysDbg = false
    If bSysDbg Then
       OutputDebugString("" & auCRLF & (Not MousePos ? "Hint" : "Proc " & MousePos) & auCRLF & gHBarVerb & auCRLF & "idx=" & idx)
    EndIf
    If MousePos <> 2 Then gSmartIndex = 0
    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 gHBarSmart Then
       If gSmartIndex > DropIdx + 0.499 Then
          If DragIdx >= DropIdx Then
             DropIdx += 1
          EndIf
       Else
          If DragIdx < DropIdx Then
             DropIdx -= 1
          EndIf
       EndIf
    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 HBarReadIndex(Index)
    Local key, val, txt
    Static aItems = List("button","cmd","param","path","menu","iconic")
    # сохранение индекса
     For j = 0 To aItems.Count - 1
        key = aItems[j] & Index
        val = IniRead(gHBarFile, "Buttonbar", key) 
        If val <> '' Then 
           If StrLeft(val, 1) ='"' And StrRight(val, 1) ='"' Then val = '"' & val & '"'
           txt &= key & '=' & val & auCRLF
       EndIf  
     Next 
     Return StrTrim(txt)
EndFunc

Func HBarDragnCopy(DragIdx, DropIdx)
    Local key, val, ini, new
    Static aItems = List("button","cmd","param","path","menu","iconic")
    # сохранение кнопки
    For j = 0 To aItems.Count - 1
       key = aItems[j] & DragIdx
       val = IniRead(gHBarFile, "Buttonbar", key) 
       If val <> '' Then 
          If StrLeft(val, 1) ='"' And StrRight(val, 1) ='"' Then val = '"' & val & '"'
          ini &= key & '=' & val & auCRLF
       EndIf   
    Next 
    ini = StrTrim(ini)
    If ini = '' Then Return ShowDragHint("Нет данных о кнопке " & DragIdx)
    # сохранение панели    
    Local txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf
    # умный сброс по границам кнопки
    If gHBarSmart Then
       If gSmartIndex > DropIdx + 0.499 Then
          DropIdx += 1 
       EndIf
    EndIf
    If DropIdx <= gHBarMap.Count Then
       # замена ключей со сдвигом +1
       For i = gHBarMap.Count To DropIdx Step -1
           For j = 0 To aItems.Count - 1
               key = aItems[j] & i & "="
               new = aItems[j] & i + 1 & "="
               txt = StrReplace(txt, key, new)
           Next
       Next
    EndIf   
    # добавление сохраненной кнопки
    For j = 0 To aItems.Count - 1
        key = aItems[j] & DragIdx & "="
        new = aItems[j] & DropIdx & "="
        ini = StrReplace(ini, key, new)
    Next
    txt &= auCRLF & ini
    # обновление панели
    gHBarList.Text = txt
    gHBarList.SaveToFile(gHBarFile, gHBarCP)
    IniWrite(gHBarFile, "Buttonbar", "Buttoncount", gHBarMap.Count+1)
    gHBarUndoTs = FileGetTime(gHBarFile)
    gHBarFileTs = FileGetTime(gHBarFile)    
    If gHBarUndoTs Then 
       gHBarUndo = true
    Else
       gHBarUndo = false    
       gHBarUndoLoad = ""   
    EndIf   
    HBarLoad()   
    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 SEPARATOR' & auCRLF    
    txt &= 'MENUITEM "Smart Drag&&Drop", em_aucmd ' & (gHBarSmart ?  "/C" : "") & ' -1 HBarSmart' & auCRLF     
    txt &= 'MENUITEM "Отладочная информация", em_aucmd ' & (gHBarDbg ?  "/C" : "") & ' -1 HBarDebugInfo' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF      
    txt &= 'MENUITEM "Сохранить...", em_aucmd -1 HBarSaveAs' & auCRLF      
    txt &= 'POPUP "Загрузить панель"' & auCRLF
    txt &= HBarAddBarMenu() & auCRLF      
    txt &= 'END_POPUP' & auCRLF               
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'POPUP "Удалить кнопки за разрывом"' & auCRLF
    txt &= HBarBreakMenu() & auCRLF      
    txt &= 'END_POPUP' & 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 HBarSmart()
    gHBarSmart = Not gHBarSmart
    If gHBarSmart Then
       ShowDragHint("Smart Drag&Drop включен")
    Else
       ShowDragHint("Smart Drag&Drop отключен")    
    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)
     WinRedraw(0)     
     #WinRedraw(2)
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)
       WinRedraw(0)
    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)
       WinRedraw(0)
    EndIf   
EndFunc

gHBarBars = list()
    
Func HBarAddBarMenu()
    Local sBars = FileFind(COMMANDER_PATH, "*.bar", -1, 1)
    #sBars = StrTrim(sBars)
    Local text
    gHBarBars.Count = 0
    gHBarBars.Text = sBars
    For i = 0 To gHBarBars.Count - 1
        text = text & 'MENUITEM "' & gHBarBars[i] & '", em_aucmd -1 HBarMergeOne ' & i & auCRLF 
    Next
    Return text
EndFunc

Func HBarMergeOne(FileIndex)
    Local bar = gHBarBars[FileIndex]
    c = IniRead(bar, "Buttonbar", "Buttoncount", 0)
    If c = 0 Then 
       MsgBox("Файл не содержит данных о кнопках" & auCRLF & auCRLF & bar, "Autorun", 0+48)
       Return
    EndIf
    If FileGetTime(gHBarFile) <> gHBarFileTs Then
       MsgBox("Операция невозможна. Файл главной панели изменился.")
       Return
    Endif
    Local text =  FileRead(bar), count = gHBarMap.Count + 1
    Local rext = 'button' & count & '=' & auCRLF & _
                 'cmd' & count & '=-2' & auCRLF & 'iconic' & count & '=0' & auCRLF 
    Local i = 0
    Local rex = RegExp('(?i)(button|cmd|param|path|menu|iconic)(\d+?)=(.*?)\R', text)
    If rex.Exec() Then 
        Do 
           #If StrLower(rex.Match[2]) = 'button' Then i += 1
           rext &= rex.Match[1] & rex.Match[2]+count & '=' & rex.Match[3] & auCRLF
        Until not rex.ExecNext()
    EndIf     
    Free(rex)
    #If i < 1 Then Return MsgBox("Error: the number of buttons can't be zero")
    #rext = StrTrim(rext)
    # сохранение панели
    Local txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf    
    txt &= auCRLF & rext
    # обновление панели
    gHBarList.Text = txt
    gHBarList.SaveToFile(gHBarFile, gHBarCP)
    IniWrite(gHBarFile, "Buttonbar", "Buttoncount", count+c)
    gHBarUndoTs = FileGetTime(gHBarFile)
    gHBarFileTs = FileGetTime(gHBarFile)    
    If gHBarUndoTs Then 
       gHBarUndo = true
    Else
       gHBarUndo = false    
       gHBarUndoLoad = ""   
    EndIf   
    HBarLoad()   
    SendCommand(2945)     # cm_ReloadBarIcons    
    WinRedraw(0) 
EndFunc

Func HBarBreakMenu()
    Local text, br = List()
    For i = 0 To gHBarMap.Count - 1
        If gHBarMap[i] = -2 Then
           br.Add(i+1)
           text &= 'MENUITEM "Индекс разрыва: ' & i+1 & '", em_aucmd -1 HBarBreakErase ' & i+1 & ' ' & i+1 & auCRLF
        EndIf   
    Next
    If text = "" Then Return 'MENUITEM "Нет разрывов", em_aucmd /D -1'
    If br.Count > 1 Then
       Local r1 = br[0], r2 = 0
       text &= 'MENUITEM SEPARATOR' & auCRLF         
       For i = 1 To br.Count - 1
           r2 = br[i]
           If r2 > 0 Then
              text &= 'MENUITEM "Удалить кнопки между ' & r1 & ' - ' & r2 & '", em_aucmd -1 HBarBreakErase ' & r1 & ' ' & r2 & auCRLF
           EndIf
           r1 = r2
           r2 = 0
       Next
    EndIf
    Return text
EndFunc

Func HBarBreakErase(BreakIndex, StopIndex)
    Local Count = 0, Range = StopIndex-BreakIndex+1
    If FileGetTime(gHBarFile) <> gHBarFileTs Then
       MsgBox("Операция невозможна. Файл главной панели изменился.")
       Return
    Endif
    If BreakIndex < 1 Then
       MsgBox("Индекс разрыва не может быть меньше 2")
       Return
    Endif    
    Local text =  gHBarList.Text, rext = ""
    Local rex = RegExp('(?i)(button|cmd|param|path|menu|iconic)(\d+?)=(.*?)\R', text)    
    If rex.Exec() Then 
        Do 
           If BreakIndex = StopIndex Then
              If rex.Match[2] < BreakIndex Then
                 Count += 1
                 rext &= rex.Match[1] & rex.Match[2] & '=' & rex.Match[3] & auCRLF
              EndIf
           Else
              If rex.Match[2] < BreakIndex Then
                 Count += 1              
                 rext &= rex.Match[1] & rex.Match[2] & '=' & rex.Match[3] & auCRLF
              ElseIf rex.Match[2] > StopIndex Then
                 Count += 1              
                 rext &= rex.Match[1] & rex.Match[2]-Range & '=' & rex.Match[3] & auCRLF                 
              EndIf
           EndIf
        Until not rex.ExecNext()
    EndIf
    Free(rex)
    If rext = "" Then Return MsgBox("Ошибка удаления. Buttoncount=0, BreakIndex=" & BreakIndex)
    Local txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf
    # обновление панели
    Local NewCount = 0
    If BreakIndex = StopIndex Then
       NewCount = BreakIndex - 1
    Else 
       NewCount = StopIndex - BreakIndex
       NewCount = gHBarMap.Count - NewCount - 1
    EndIf   
    gHBarList.Text = "[Buttonbar]" & auCRLF & "Buttoncount=" & NewCount & auCRLF & rext
    gHBarList.SaveToFile(gHBarFile, gHBarCP)
    gHBarUndoTs = FileGetTime(gHBarFile)
    gHBarFileTs = FileGetTime(gHBarFile)    
    If gHBarUndoTs Then 
       gHBarUndo = true
    Else
       gHBarUndo = false
       gHBarUndoLoad = ""
    EndIf   
    HBarLoad()   
    SendCommand(2945)     # cm_ReloadBarIcons        
    WinRedraw(0)
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

Func HBarSaveAs()
    # буфер для имени файла
    Local file = Buffer(1024)
    file.Zero()
    file.SetStr(gHBarFile)

    # буфер для фильтра расширений
    Local fstr = "Bar files (*.bar)" & Chr(0) & "*.bar" & Chr(0) & Chr(0)
    Local flen = StrLen(fstr) * 2
    Local filt = Buffer(flen)
    filt.Zero()
    filt.SetStr(fstr)
    
    # буфер для структуры OPENFILENAMEW    
    Local ofn = Buffer(auX64 ? 152 : 76)
    ofn.Zero()
    ofn.SetNum(0, "UInt", ofn.size)    # lStructSize
    If auX64 Then
       ofn.SetNum(8,  "Ptr",   AUTORUN_TCHANDLE)   # hwndOwner
       ofn.SetNum(24, "Ptr",   filt.ptr)           # lpstrFilter
       ofn.SetNum(48, "Ptr",   file.ptr)           # lpstrFile
       ofn.SetNum(56, "Dword", file.size)          # nMaxFile
       ofn.SetNum(96, "Dword", 0x00000802)         # 0x00000002 | 0x00000800
    Else
       ofn.SetNum(4,  "Ptr",   AUTORUN_TCHANDLE)   # hwndOwner
       ofn.SetNum(12, "Ptr",   filt.ptr)           # lpstrFilter
       ofn.SetNum(28, "Ptr",   file.ptr)           # lpstrFile
       ofn.SetNum(32, "Dword", file.size)          # nMaxFile
       ofn.SetNum(52, "Dword", 0x00000802)         # 0x00000002 | 0x00000800       
    EndIf
    
    If DllCall("comdlg32\GetSaveFileNameW", "Ptr", ofn.ptr) Then
       Local new = file.GetStr()
       If new <> gHBarFile Then 
          gHBarList.SaveToFile(new, gHBarCP)
       Else
          MsgBox("Файл не может быть сохранен сам в себя","Autorun", 48)
       EndIf   
    EndIf
    
    Free(file, filt, ofn)
EndFunc

Func HBarDragIcon(DragIdx, DropIdx)
    # сохранение значка
    Local val = IniRead(gHBarFile, "Buttonbar", "button" & DragIdx) 
    If val = '' Then 
       Return ShowDragHint("Нет данных о значке button" & DragIdx)
    Else 
       If StrLeft(val, 1) ='"' And StrRight(val, 1) ='"' Then val = '"' & val & '"'  
    EndIf
    # сохранение панели    
    Local txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf
    # обновление панели
    IniWrite(gHBarFile, "Buttonbar", "button" & DropIdx, val)
    gHBarUndoTs = FileGetTime(gHBarFile)
    gHBarFileTs = FileGetTime(gHBarFile)    
    If gHBarUndoTs Then 
       gHBarUndo = true
    Else
       gHBarUndo = false    
       gHBarUndoLoad = ""   
    EndIf   
    HBarLoad()   
    SendCommand(2945)     # cm_ReloadBarIcons
EndFunc

Func HBarDragDelete(DragIdx)
    Static aItems = List("button","cmd","param","path","menu","iconic")
    # сохранение панели для откатов
    Local txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf          
    # замена индекса удаляемой кнопки
    Local key, new
    For j = 0 To aItems.Count - 1
        key = aItems[j] & DragIdx & "="
        new = aItems[j] & 8888 & "="
        txt = StrReplace(txt, key, new)
    Next    
    # замена ключей со сдвигом -1
    For i = DragIdx + 1 To gHBarMap.Count
        For k = 0 To aItems.Count - 1
            key = aItems[k] & i & "="
            new = aItems[k] & i - 1 & "="
            txt = StrReplace(txt, key, new)
        Next
    Next
    # обновление панели
    gHBarList.Text = txt
    gHBarList.SaveToFile(gHBarFile, gHBarCP)
    # удаление ключей кнопки из файла    
    For j = 0 To aItems.Count - 1
        key = aItems[j] & 8888
        IniDelete(gHBarFile, "Buttonbar", key)
    Next
    # обновление общего количества кнопок    
    IniWrite(gHBarFile, "Buttonbar", "Buttoncount", gHBarMap.Count-1)
    # запоминание времени последнего изменения
    gHBarUndoTs = FileGetTime(gHBarFile)
    gHBarFileTs = FileGetTime(gHBarFile)
    If gHBarUndoTs Then 
       gHBarUndo = true
    Else
       gHBarUndo = false    
       gHBarUndoLoad = ""   
    EndIf
    # загрузка обновленного айла панели
    HBarLoad()   
    SendCommand(2945)     # cm_ReloadBarIcons
EndFunc
P.S. Я добавил горячую клавишу в заголовок для отмены последнего действия Win+Backspace, можно изменить.
sa
Сообщения: 2758
Зарегистрирован: Ср апр 29, 2009 18:56

Сообщение sa »

Orion9 писал(а):Перенос значка
Работает. Лучше, наверное, запретить перенос значка на разделители, по крайней мере на разрывы – это, к тому же чревато ошибками в работе скрипта. См. ранее:
в результате разрыв строки проверяется не по значению "-2", а по размеру 2. Но поскольку вероятность команды с двумя символами очень мала, пока оставил так, но нужно будет переделать.
Orion9 писал(а):Что касается удаления одной кнопки, сделать его было несложно
Работает. С учётом возможности отмены действия, в отличие от удаления средствами TC, наверное, это полезная функция.
Orion9 писал(а):дополнительные пункты для удаления кнопок между разрывов
Возможно, удобнее один из двух разрывов оставлять, но не факт...
А так всё наглядно получилось, тем более, что копировать разрывы для выделения диапазона несложно.
Аватара пользователя
Orion9
Сообщения: 1293
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

sa писал(а):Лучше, наверное, запретить перенос значка на разделители, по крайней мере на разрывы – это, к тому же чревато ошибками в работе скрипта
Да, я посмотрю, что там. Пока не успел основательно все протестировать, но постараюсь сделать это в ближайшее время.
sa писал(а):С учётом возможности отмены действия, в отличие от удаления средствами TC, наверное, это полезная функция.
Вроде бы. Но само движение вниз для вас оказалось удобным? Я сначала делал, чтобы при касании верхней границы экрана кнопку можно было удалять, но доделав до смены значка, понял, что при расрытом на весь экран ТС это неудобно - расстояние слишком мальнькое. Вообще, вариантов как-то мало, наверное, пусть пока так останется, а там посмотрим.
sa писал(а):Возможно, удобнее один из двух разрывов оставлять, но не факт...
Да нет, тут я промахнулся. Конечно же лучше оставлять! Исправлю. А также сделаю в меню пункты для добавления разрыва и разделителя, чтобы при их отсутствии не нужно было залезать в диалог изменения панели. А копировать разрывы и разделители действительно удобно, не говоря уж о кнопках, спасибо за идею :D
Аватара пользователя
Orion9
Сообщения: 1293
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

sa писал(а):Лучше, наверное, запретить перенос значка на разделители, по крайней мере на разрывы – это, к тому же чревато ошибками в работе скрипта.
Да, это приводит к серьезным ошибкам! Там вообще на ниточке все держится. Если в файле панели будет мальшее отклонение от нормы, то проблемы будут обязательно. Видимо, придется пересматривать считывание панели с учетом разных нюансов, ведь сейчас кнопки просто определяются по наличию ключа button=, а разделители по cmd= с учетом, что ключ button= пустой.
Fix:

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

Func HBarDragIcon(DragIdx, DropIdx)
    # проверка индекса получателя
    If gHBarMap[DropIdx-1] <> 1 Then Return ShowDragHint("Получатель не может быть разделителем") 
    # сохранение значка
    Local val = IniRead(gHBarFile, "Buttonbar", "button" & DragIdx) 
sa писал(а):Возможно, удобнее один из двух разрывов оставлять
Обновленная функция
Показать

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

Func HBarBreakErase(BreakIndex, StopIndex)
    Local Count = 0, Range = StopIndex-BreakIndex
    If FileGetTime(gHBarFile) <> gHBarFileTs Then
       MsgBox("Операция невозможна. Файл главной панели изменился.")
       Return
    Endif
    If BreakIndex < 1 Then
       MsgBox("Индекс разрыва не может быть меньше 2")
       Return
    Endif    
    Local text =  gHBarList.Text, rext = ""
    Local rex = RegExp('(?i)(button|cmd|param|path|menu|iconic)(\d+?)=(.*?)(\R|\Z)', text)    
    If rex.Exec() Then 
        Do 
           If BreakIndex = StopIndex Then
              If rex.Match[2] < BreakIndex Then
                 Count += 1
                 rext &= rex.Match[1] & rex.Match[2] & '=' & rex.Match[3] & auCRLF
              EndIf
           Else
              If rex.Match[2] < BreakIndex Then
                 Count += 1              
                 rext &= rex.Match[1] & rex.Match[2] & '=' & rex.Match[3] & auCRLF
              ElseIf rex.Match[2] >= StopIndex Then
                 Count += 1              
                 rext &= rex.Match[1] & rex.Match[2]-Range & '=' & rex.Match[3] & auCRLF                 
              EndIf
           EndIf
        Until not rex.ExecNext()
    EndIf
    Free(rex)
    If rext = "" Then Return MsgBox("Ошибка удаления. Buttoncount=0, BreakIndex=" & BreakIndex)
    Local txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf
    # обновление панели
    Local NewCount = 0
    If BreakIndex = StopIndex Then
       NewCount = BreakIndex - 1
    Else 
       NewCount = gHBarMap.Count - Range
    EndIf   
    gHBarList.Text = "[Buttonbar]" & auCRLF & "Buttoncount=" & NewCount & auCRLF & rext
    gHBarList.SaveToFile(gHBarFile, gHBarCP)
    gHBarUndoTs = FileGetTime(gHBarFile)
    gHBarFileTs = FileGetTime(gHBarFile)    
    If gHBarUndoTs Then 
       gHBarUndo = true
    Else
       gHBarUndo = false
       gHBarUndoLoad = ""
    EndIf   
    HBarLoad()   
    SendCommand(2945)     # cm_ReloadBarIcons        
    WinRedraw(0)
EndFunc
Добавил к регулярному выражению \Z, иначе последний ключ терялся, если панель заканчивается eof, а не \r\n. В функции HBarMergeOne это тоже нужно подправить.

+Новые два пункта

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

    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Добавить разрыв", em_aucmd -1 HBarAddButton -2' & auCRLF
    txt &= 'MENUITEM "Добавить разделитель", em_aucmd -1 HBarAddButton 1' & auCRLF    
    txt &= 'POPUP "Удалить кнопки за разрывом"' & auCRLF
Показать

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

Func HBarAddButton(IndexType)
    # сохранение панели    
    Local txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf
    # добавление новых ключей
    IniWrite(gHBarFile, "Buttonbar", "button" & gHBarMap.Count+1)
    IniWrite(gHBarFile, "Buttonbar", "iconic" & gHBarMap.Count+1, 0)
    If IndexType = -2 Then
       IniWrite(gHBarFile, "Buttonbar", "cmd" & gHBarMap.Count+1, -2)
    EndIf
    # обновление общего количества кнопок    
    IniWrite(gHBarFile, "Buttonbar", "Buttoncount", gHBarMap.Count+1)
    # запоминание времени последнего изменения    
    gHBarUndoTs = FileGetTime(gHBarFile)
    gHBarFileTs = FileGetTime(gHBarFile)    
    If gHBarUndoTs Then 
       gHBarUndo = true
    Else
       gHBarUndo = false    
       gHBarUndoLoad = ""
    EndIf   
    HBarLoad()   
    SendCommand(2945)     # cm_ReloadBarIcons
    WinRedraw(0)
    WinRedraw(2)    
EndFunc
Здесь все было просто. И последний фикс

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

SetHotkeyAction /K:W /H:BS "HBarUndo"
Иначе клавиша Win чудила с ключами /DM /S, скорее всего из-за последнего.

Ну вот, пока так. Остальные испытания чуть позже. Кстати, WinRedraw(0) все-равно не всегда помогает, увы.
sa
Сообщения: 2758
Зарегистрирован: Ср апр 29, 2009 18:56

Сообщение sa »

Orion9 писал(а):WinRedraw(0) все-равно не всегда помогает
У себя убрал WinRedraw, каждую SendCommand(2945) заменил на две SendCommand(2901) везде, кроме функций переноса значка (HBarDragIcon) и HBarSetCodePage – в них количество строк панели как будто не должно изменяться.
Аватара пользователя
Orion9
Сообщения: 1293
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

sa писал(а):каждую SendCommand(2945) заменил на две SendCommand(2901)
А блокировку окна через SendMessage(AUTORUN_TCHANDLE, 11, 0, 0) перед этим используете?

А так - да, придется откатиться, поскольку использование SendCommand(2945)+WinRedraw(0) не всегда срабатывает. И если при удалении кнопок могут хотя бы оставаться пустые полосы, то при загрузке новой панели экран иногда не обновляется, и не понятно сработала ли команда или нет. Причины такого поведения не известны.

Чуть позже добавлю полный модуль - нужно зафиксировать все последние изменения, тем более, что новых идей больше нет.

Хотя можно было бы сделать меню для открытия панелей через OPENBAR, но не хочется это делать через команду в usercmd.ini.
sa
Сообщения: 2758
Зарегистрирован: Ср апр 29, 2009 18:56

Сообщение sa »

Orion9 писал(а):А блокировку окна через SendMessage(AUTORUN_TCHANDLE, 11, 0, 0) перед этим используете?
Сейчас попробовал включать/отключать блокировку – ощутимой разницы не заметил (а ведь когда-то видел её!).
Orion9 писал(а):при загрузке новой панели экран иногда не обновляется, и не понятно сработала ли команда или нет
Возможно, это следствие установленного ограничения на количество отображаемых строк (например, BarLimitLines=3).
Аватара пользователя
Orion9
Сообщения: 1293
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

sa писал(а):Сейчас попробовал включать/отключать блокировку – ощутимой разницы не заметил
Я понял. Как раз сейчас сделаю дополнительные функциии для сохранения и обновления панели, заодно и попробую. Давно надо было это сделать, иначе один и тот же код повторяется от функции к функции. Будет примерно так
Показать

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

Func HBarSaveState(ByRef Text)
    Text = gHBarList.Text
    gHBarUndoLoad = Text
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = Text
    EndIf      
EndFunc

Func HBarUpdateReload(Reopen = 0)
    gHBarUndoTs = FileGetTime(gHBarFile)
    gHBarFileTs = FileGetTime(gHBarFile)    
    If gHBarUndoTs Then 
       gHBarUndo = true
    Else
       gHBarUndo = false    
       gHBarUndoLoad = ""   
    EndIf   
    HBarLoad()
    If Reopen Then
       SendMessage(AUTORUN_TCHANDLE, 11, 0, 0)
       SendCommand(2901, 0, 1)
       SendCommand(2901, 0, 1)     # cm_VisButtonBar
       Sleep(10)
       SendMessage(AUTORUN_TCHANDLE, 11, 1, 0)
       WinRedraw(0)       
   Else    
       SendCommand(2945)     # cm_ReloadBarIcons 
   EndIf   
EndFunc
sa писал(а):Возможно, это следствие установленного ограничения на количество отображаемых строк
Может быть. А может просто из-за того, что функция все еще тестовая?
Показать
WinRedraw

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

WinRedraw([<flags>[, <winhandle>]])
WinRedraw [<flags>[ <winhandle>]]
Перерисовывает окно.

:!: В данный момент функция находится в тестовой стадии, состав и формат выводимой информации, интерфейс функции и т. д. могут измениться в любой момент.
Аватара пользователя
Orion9
Сообщения: 1293
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

sa
Все-таки пришлось оставить WinRedraw(0), поскольку две SendCommand(2901) не работают с временной панелью, загруженной на главную через OPENBAR. Придется держать это в уме. Может быть, потом найдется другое решение.

Исправил некоторые ошибки. Но модуль все еще сырой, нужно больше тестов перед тем, как добавить его на первую страницу.
Показать

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

Pragma IncludeOnce

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

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

# Win + Backspace
# отмена последнего действия 
SetHotkeyAction /K:W /H:BS "HBarUndo"

Global gDragIndex, gDropIndex, gSmartIndex
Global gHBarButtons = 0, gHBarDivs = 0, gHBarSmart = 1

Global gHBarName, gHBarFile, gHBarFileTs, gHBarReady = 0
Global gHBarTxt, gHBarCP = "ANSI", gHBarWincmd = 0, gHBarDbg = 0
Global gHBarHint = 0, gHBarVerb, gHBarCancel, gHBarCancelLoad, gHBarLoadError = 1
Global gHBarList = List(), gHBarMap = List(), gHBarBars = 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, _
           MoveButton      = MK_CONTROL, _
           CopyButton      = MK_SHIFT
    Static drag_system = DllCall("LoadCursor", "Ptr", 0, "Ptr", 32646), _
           stop_system = DllCall("LoadCursor", "Ptr", 0, "Ptr", 32648), _
           icon_cursor = DllCall("LoadCursor", "Ptr", hTotal, "Ptr", 13), _           
           kick_cursor = DllCall("LoadCursor", "Ptr", hTotal, "Ptr", 26), _
           copy_cursor = DllCall("LoadCursor", "Ptr", hTotal, "Ptr", 25), _
           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
    Switch uMsg
       Case WM_LBUTTONDOWN
          If IsDrag = 0 And (BitAND(wParam, MoveButton) Or BitAND(wParam, CopyButton)) Then
             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
                If gHBarLoadError Or gHBarList.Count = 0 Then
                   ShowDragHint("Панель не загружена")
                   Return DllCall("CallWindowProcW", _
                   "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
                Endif
                If FileGetTime(gHBarFile) <> gHBarFileTs Then
                   ShowDragHint("Файл панели изменился")
                   Return DllCall("CallWindowProcW", _
                   "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
                Endif
                If BitAND(wParam, MoveButton) And BitAND(wParam, CopyButton) Then
                   IsDrag = MoveButton
                Else
                   IsDrag = wParam
                EndIf
                WinSetStyle(0x80000, 3, hWnd)
                If BitAND(wParam, MoveButton) Then
                   DllCall("SetLayeredWindowAttributes", "hwnd", hWnd, "ptr", 0, "byte", 128, "dword", 2)
                Else   
                   DllCall("SetLayeredWindowAttributes", "hwnd", hWnd, "ptr", 0, "byte", 216, "dword", 2)
                EndIf   
                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
             Local nDragMode = IsDrag
             DllCall("ReleaseCapture")
             IsDrag = 0
             If WinFromPoint() = hWnd Then
                MouseGetPos("x","y")
                gSmartIndex = 0
                gDropIndex = HBarGetIdx(2, x, y)
                If gDragIndex <> gDropIndex Then
                   If gHBarHint Then ShowDragHint("Index " & gDragIndex & " To Index: " & gDropIndex)
                   If BitAND(nDragMode, MoveButton) Then                      
                      HBarDragDrop(gDragIndex, gDropIndex)
                   Else
                      If BitAND(wParam, MoveButton) Then
                         HBarDragIcon(gDragIndex, gDropIndex)
                      Else
                         HBarDragCopy(gDragIndex, gDropIndex)
                      EndIf
                   EndIf   
                EndIf
             Else
                y = BitAND(BitShift(lParam,16), 0xFFFF)
                If BitAND(wParam, MoveButton) And lParam > 0 And y > 200 Then HBarDragDelete(gDragIndex)
             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
                If BitAND(IsDrag, CopyButton) Then
                   DllCall("SetCursor", "Ptr", BitAND(wParam, MoveButton) ? icon_cursor : copy_cursor)
                Else   
                   DllCall("SetCursor", "Ptr", drag_cursor)
                EndIf 
                #DllCall("SetCursor", "Ptr", BitAND(IsDrag, MoveButton) ? drag_cursor : copy_cursor)
             Else
                y = BitAND(BitShift(lParam,16), 0xFFFF)
                #WinSetText(lParam & " y=" & y)
                If BitAND(wParam, MoveButton) And lParam > 0 And y > 200 Then
                   DllCall("SetCursor", "Ptr", kick_cursor)
                Else   
                   DllCall("SetCursor", "Ptr", stop_cursor)
                EndIf    
             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")
       Return                    
    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 = 1
    Local bar = FileRead(gHBarFile, 128, "RAW")
    Local bom = "FFFE"
    Local utf = "5B0042007500740074006F006E006200610072005D000D000A00"
    If ERROR = 1 Then
       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)
    gHBarLoadError = gHBarMap.Count > 0 ? 0 : 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
    #OutputDebugString("HBarLoad=" & gHBarMap.Count)
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 ShowDragHint("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, idx_padding = 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
           idx_padding = 0
           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
                 idx_padding += ex
                 j += 1
              EndIf
           Else
              cx += ex
              idx_padding = ex
           EndIf   
           If cx >= tx Then
              idx = i + 1
              Break
           EndIf   
       Next
    EndIf
    If idx = 0 Then Return idx
    
    Local SysDbg = 0    
    If SysDbg Then 
       OutputDebugString("idx=" & idx & " gHBarMap=" & gHBarMap.Count)
    EndIf   
    
    idx_button = gHBarMap[idx-1]
    idx_width = (idx_button = -2 ? 0 : (idx_button = 0 ? div : b))
    idx_pixel = tx - (cx - idx_padding - idx_width)
    idx_float = idx_width > 0 ? idx_pixel / idx_width : 0
    # дробный индекс    
    gSmartIndex = idx + Round(idx_float, 3)
    If Floor(gSmartIndex) > idx Then gSmartIndex = idx + 0.999
    # дополнительная информация
    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 & "%" & auCRLF & auCRLF & _ 
                "smart=" & gSmartIndex & auCRLF & _
                "button=" & idx_button & " width=" & idx_width & " pixel=" & idx_pixel
    If SysDbg Then
       OutputDebugString("" & auCRLF & (Not MousePos ? "Hint" : "Proc " & MousePos) & auCRLF & gHBarVerb & auCRLF & "idx=" & idx)
    EndIf
    If MousePos <> 2 Then gSmartIndex = 0
    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 HBarDragDrop(DragIdx, DropIdx)
    Static aItems = List("button","cmd","param","path","menu","iconic")
    Local T1, T2, bTL = 0
    Local i, j, k, txt, key, new
    
    # режим отладки
    If bTL Then T1 = GetUptime()

    # сохранение файла панели     
    HBarSaveState(txt)
    
    # временная замена индекса DragIdx = 9999
    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 gHBarSmart Then
       If gSmartIndex > DropIdx + 0.499 Then
          If DragIdx >= DropIdx Then
             DropIdx += 1
          EndIf
       Else
          If DragIdx < DropIdx Then
             DropIdx -= 1
          EndIf
       EndIf
    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 
    
    # обратная замена индекса DragIdx -> DropIdx 
    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)
    
    # обновление панели
    HBarUpdateReload()
    
    If bTL Then   
       T2 = Round(GetUptime() - T1, 0) / 1000
       OutputDebugString("Время операции 3: " &  StrFormat("%.3f", T2) & " sec")
    EndIf
EndFunc

Func HBarReadIndex(Index)
    Local key, val, txt
    Static aItems = List("button","cmd","param","path","menu","iconic")
    # сохранение индекса
     For j = 0 To aItems.Count - 1
        key = aItems[j] & Index
        val = IniRead(gHBarFile, "Buttonbar", key) 
        If val <> '' Then 
           If StrLeft(val, 1) ='"' And StrRight(val, 1) ='"' Then val = '"' & val & '"'
           txt &= key & '=' & val & auCRLF
       EndIf  
     Next 
     Return StrTrim(txt)
EndFunc

Func HBarDragCopy(DragIdx, DropIdx)
    Local key, val, ini, new, txt
    Static aItems = List("button","cmd","param","path","menu","iconic")
    
    # сохранение кнопки
    For j = 0 To aItems.Count - 1
       key = aItems[j] & DragIdx
       val = IniRead(gHBarFile, "Buttonbar", key) 
       If val <> '' Then 
          If StrLeft(val, 1) ='"' And StrRight(val, 1) ='"' Then val = '"' & val & '"'
          ini &= key & '=' & val & auCRLF
       EndIf   
    Next 
    ini = StrTrim(ini)
    If ini = '' Then Return ShowDragHint("Нет данных о кнопке " & DragIdx)
    
    # сохранение файла панели    
    HBarSaveState(txt)
    
    # умный сброс по границам кнопки
    If gHBarSmart Then
       If gSmartIndex > DropIdx + 0.499 Then
          DropIdx += 1 
       EndIf
    EndIf

    # сдвиг индексов    
    If DropIdx <= gHBarMap.Count Then
       # замена ключей со сдвигом +1
       For i = gHBarMap.Count To DropIdx Step -1
           For j = 0 To aItems.Count - 1
               key = aItems[j] & i & "="
               new = aItems[j] & i + 1 & "="
               txt = StrReplace(txt, key, new)
           Next
       Next
    EndIf
    
    # добавление сохраненной кнопки
    For j = 0 To aItems.Count - 1
        key = aItems[j] & DragIdx & "="
        new = aItems[j] & DropIdx & "="
        ini = StrReplace(ini, key, new)
    Next
    txt &= auCRLF & ini
    
    # сохранение панели
    gHBarList.Text = txt
    gHBarList.SaveToFile(gHBarFile, gHBarCP)
    IniWrite(gHBarFile, "Buttonbar", "Buttoncount", gHBarMap.Count+1)
    
    # обновление панели    
    HBarUpdateReload()
EndFunc

Func HBarSaveState(ByRef Text)
    Text = gHBarList.Text
    gHBarUndoLoad = Text
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = Text
    EndIf      
EndFunc

Func HBarUpdateReload(Reopen = 0)
    # временя последнего изменения
    gHBarUndoTs = FileGetTime(gHBarFile)
    gHBarFileTs = FileGetTime(gHBarFile)    
    If gHBarUndoTs Then 
       gHBarUndo = true
    Else
       gHBarUndo = false    
       gHBarUndoLoad = ""   
    EndIf
    
    # загрузка файла панели    
    HBarLoad()
    
    # обновление панели
    If Reopen Then
       SendMessage(AUTORUN_TCHANDLE, 11, 0, 0)
       SendCommand(2901, 0, 1)
       SendCommand(2901, 0, 1)     # cm_VisButtonBar
       Sleep(10)
       SendMessage(AUTORUN_TCHANDLE, 11, 1, 0)
   Else    
       SendCommand(2945)     # cm_ReloadBarIcons 
   EndIf
   WinRedraw(1)   
   WinRedraw(0)
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 SEPARATOR' & auCRLF    
    txt &= 'MENUITEM "Smart Drag&&Drop", em_aucmd ' & (gHBarSmart ?  "/C" : "") & ' -1 HBarSmart' & auCRLF     
    txt &= 'MENUITEM "Отладочная информация", em_aucmd ' & (gHBarDbg ?  "/C" : "") & ' -1 HBarDebugInfo' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF      
    txt &= 'MENUITEM "Сохранить...", em_aucmd -1 HBarSaveAs' & auCRLF      
    txt &= 'POPUP "Загрузить панель"' & auCRLF
    txt &= HBarAddBarMenu() & auCRLF      
    txt &= 'END_POPUP' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Добавить разрыв", em_aucmd -1 HBarAddButton -2' & auCRLF
    txt &= 'MENUITEM "Добавить разделитель", em_aucmd -1 HBarAddButton 1' & auCRLF    
    txt &= 'POPUP "Удалить кнопки за разрывом"' & auCRLF
    txt &= HBarBreakMenu() & auCRLF      
    txt &= 'END_POPUP' & 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 HBarSmart()
    gHBarSmart = Not gHBarSmart
    If gHBarSmart Then
       ShowDragHint("Smart Drag&Drop включен")
    Else
       ShowDragHint("Smart Drag&Drop отключен")    
    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)     
     HBarUpdateReload()
     ShowDragHint("Файл загружен " & bak)
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)
       #SendCommand(2901)
       #SendCommand(2901)       
       WinRedraw(0)
    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)
       #SendCommand(2901)
       #SendCommand(2901)       
       WinRedraw(0)
    EndIf   
EndFunc
    
Func HBarAddBarMenu()
    Local sBars = FileFind(COMMANDER_PATH, "*.bar", -1, 1)
    #sBars = StrTrim(sBars)
    Local text
    gHBarBars.Count = 0
    gHBarBars.Text = sBars
    For i = 0 To gHBarBars.Count - 1
        text = text & 'MENUITEM "' & gHBarBars[i] & '", em_aucmd -1 HBarMergeOne ' & i & auCRLF 
    Next
    Return text
EndFunc

Func HBarMergeOne(FileIndex)
    Local bar = gHBarBars[FileIndex]
    c = IniRead(bar, "Buttonbar", "Buttoncount", 0)
    If c = 0 Then 
       MsgBox("Файл не содержит данных о кнопках" & auCRLF & auCRLF & bar, "Autorun", 0+48)
       Return
    EndIf
    If FileGetTime(gHBarFile) <> gHBarFileTs Then
       MsgBox("Операция невозможна. Файл на диске изменился." & auCRLF & FileGetTime(gHBarFile) & "<>" & gHBarFileTs)
       Return
    Endif
    Local text =  FileRead(bar), count = gHBarMap.Count + 1
    Local rext = 'button' & count & '=' & auCRLF & _
                 'cmd' & count & '=-2' & auCRLF & 'iconic' & count & '=0' & auCRLF 
    Local i = 0
    Local rex = RegExp('(?i)(button|cmd|param|path|menu|iconic)(\d+?)=(.*?)(\R|\Z)', text)
    If rex.Exec() Then 
        Do 
           #If StrLower(rex.Match[1]) = 'button' Then i += 1
           If StrCompare(rex.Match[1], 'button') = 0 Then i += 1
           rext &= rex.Match[1] & rex.Match[2]+count & '=' & rex.Match[3] & auCRLF
        Until not rex.ExecNext()
    EndIf     
    Free(rex)
    If i < 1 Then 
       Return MsgBox("Не удалось найти ни одной кнопки" & auCRLF & auCRLF & _
                     "Buttoncount=" & c & " RegExp=" & i & auCRLF & & bar, "Autorun", 16)
    ElseIf c <> i Then
       MsgBox("Количество кнопок не совпадает" & auCRLF & auCRLF & _
              "Buttoncount=" & c & " RegExp=" & i & auCRLF & & bar, "Autorun", 48)
    EndIf 
    # сохранение панели
    Local txt = gHBarList.Text
    HBarSaveState(txt)    
    txt &= auCRLF & rext
    # обновление данных
    gHBarList.Text = txt
    gHBarList.SaveToFile(gHBarFile, gHBarCP)
    IniWrite(gHBarFile, "Buttonbar", "Buttoncount", count+c)
    # обновление панели
    HBarUpdateReload()
    ShowDragHint("Добавлено " & i+1 & " кнопок")
EndFunc

Func HBarBreakMenu()
    Local text, br = List()
    For i = 0 To gHBarMap.Count - 1
        If gHBarMap[i] = -2 Then
           br.Add(i+1)
           text &= 'MENUITEM "Индекс разрыва: ' & i+1 & '", em_aucmd -1 HBarBreakErase ' & i+1 & ' ' & i+1 & auCRLF
        EndIf   
    Next
    If text = "" Then Return 'MENUITEM "Нет разрывов", em_aucmd /D -1'
    If br.Count > 1 Then
       Local r1 = br[0], r2 = 0
       text &= 'MENUITEM SEPARATOR' & auCRLF         
       For i = 1 To br.Count - 1
           r2 = br[i]
           If r2 > 0 Then
              text &= 'MENUITEM "Удалить кнопки между ' & r1 & ' - ' & r2 & '", em_aucmd -1 HBarBreakErase ' & r1 & ' ' & r2 & auCRLF
           EndIf
           r1 = r2
           r2 = 0
       Next
    EndIf
    Return text
EndFunc

Func HBarBreakErase(BreakIndex, StopIndex)
    Local Count = 0, txt, _
          Range = StopIndex - BreakIndex
          
    If FileGetTime(gHBarFile) <> gHBarFileTs Then
       MsgBox("Операция невозможна. Файл на диске изменился." & auCRLF & FileGetTime(gHBarFile) & "<>" & gHBarFileTs)
       Return
    Endif
    
    If BreakIndex < 1 Then
       MsgBox("Индекс разрыва не может быть меньше 2")
       Return
    Endif    
    
    Local text =  gHBarList.Text, rext = ""
    Local rex = RegExp('(?i)(button|cmd|param|path|menu|iconic)(\d+?)=(.*?)(\R|\Z)', text)    
    If rex.Exec() Then 
        Do 
           If BreakIndex = StopIndex Then
              If rex.Match[2] < BreakIndex Then
                 Count += 1
                 rext &= rex.Match[1] & rex.Match[2] & '=' & rex.Match[3] & auCRLF
              EndIf
           Else
              If rex.Match[2] < BreakIndex Then
                 Count += 1              
                 rext &= rex.Match[1] & rex.Match[2] & '=' & rex.Match[3] & auCRLF
              ElseIf rex.Match[2] >= StopIndex Then
                 Count += 1              
                 rext &= rex.Match[1] & rex.Match[2]-Range & '=' & rex.Match[3] & auCRLF                 
              EndIf
           EndIf
        Until not rex.ExecNext()
    EndIf
    Free(rex)    
    If rext = "" Then Return MsgBox("Ошибка удаления. Buttoncount=0, BreakIndex=" & BreakIndex)
    
    # сохранение панели    
    HBarSaveState(txt)
    
    # обновление файла
    Local NewCount = 0
    If BreakIndex = StopIndex Then
       NewCount = BreakIndex - 1
    Else 
       NewCount = gHBarMap.Count - Range
    EndIf
    ShowDragHint("Удалено " & gHBarMap.Count - NewCount & " кнопок")    
    gHBarList.Text = "[Buttonbar]" & auCRLF & "Buttoncount=" & NewCount & auCRLF & rext
    gHBarList.SaveToFile(gHBarFile, gHBarCP)
    
    # обновление панели    
    HBarUpdateReload()
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

Func HBarSaveAs()
    # буфер для имени файла
    Local file = Buffer(1024)
    file.Zero()
    file.SetStr(gHBarFile)

    # буфер для фильтра расширений
    Local fstr = "Bar files (*.bar)" & Chr(0) & "*.bar" & Chr(0) & Chr(0)
    Local flen = StrLen(fstr) * 2
    Local filt = Buffer(flen)
    filt.Zero()
    filt.SetStr(fstr)
    
    # буфер для структуры OPENFILENAMEW    
    Local ofn = Buffer(auX64 ? 152 : 76)
    ofn.Zero()
    ofn.SetNum(0, "UInt", ofn.size)    # lStructSize
    If auX64 Then
       ofn.SetNum(8,  "Ptr",   AUTORUN_TCHANDLE)   # hwndOwner
       ofn.SetNum(24, "Ptr",   filt.ptr)           # lpstrFilter
       ofn.SetNum(48, "Ptr",   file.ptr)           # lpstrFile
       ofn.SetNum(56, "Dword", file.size)          # nMaxFile
       ofn.SetNum(96, "Dword", 0x00000802)         # 0x00000002 | 0x00000800
    Else
       ofn.SetNum(4,  "Ptr",   AUTORUN_TCHANDLE)   # hwndOwner
       ofn.SetNum(12, "Ptr",   filt.ptr)           # lpstrFilter
       ofn.SetNum(28, "Ptr",   file.ptr)           # lpstrFile
       ofn.SetNum(32, "Dword", file.size)          # nMaxFile
       ofn.SetNum(52, "Dword", 0x00000802)         # 0x00000002 | 0x00000800       
    EndIf
    
    If DllCall("comdlg32\GetSaveFileNameW", "Ptr", ofn.ptr) Then
       Local new = file.GetStr()
       If new <> gHBarFile Then 
          gHBarList.SaveToFile(new, gHBarCP)
       Else
          MsgBox("Файл не может быть сохранен сам в себя","Autorun", 48)
       EndIf   
    EndIf
    
    Free(file, filt, ofn)
EndFunc

Func HBarDragIcon(DragIdx, DropIdx)
    Local txt
    
    # проверка индекса получателя
    If gHBarMap[DropIdx-1] <> 1 Then Return ShowDragHint("Получатель не может быть разделителем")
    
    # сохранение значка
    Local val = IniRead(gHBarFile, "Buttonbar", "button" & DragIdx) 
    If val = '' Then 
       Return ShowDragHint("Нет данных о значке button" & DragIdx)
    Else 
       If StrLeft(val, 1) ='"' And StrRight(val, 1) ='"' Then val = '"' & val & '"'  
    EndIf
    
    # сохранение панели    
    HBarSaveState(txt)
    
    # обновление значка
    IniWrite(gHBarFile, "Buttonbar", "button" & DropIdx, val)
    
    # обновление панели    
    HBarUpdateReload()
EndFunc

Func HBarDragDelete(DragIdx)
    Local txt
    Static aItems = List("button","cmd","param","path","menu","iconic")
    
    # сохранение панели    
    HBarSaveState(txt)
    
    # замена индекса удаляемой кнопки
    Local key, new
    For j = 0 To aItems.Count - 1
        key = aItems[j] & DragIdx & "="
        new = aItems[j] & 8888 & "="
        txt = StrReplace(txt, key, new)
    Next
    
    # замена ключей со сдвигом -1
    For i = DragIdx + 1 To gHBarMap.Count
        For k = 0 To aItems.Count - 1
            key = aItems[k] & i & "="
            new = aItems[k] & i - 1 & "="
            txt = StrReplace(txt, key, new)
        Next
    Next
    
    # сохранение панели
    gHBarList.Text = txt
    gHBarList.SaveToFile(gHBarFile, gHBarCP)
    
    # удаление ключей кнопки из файла
    For j = 0 To aItems.Count - 1
        key = aItems[j] & 8888
        IniDelete(gHBarFile, "Buttonbar", key)
    Next
    
    # обновление общего количества кнопок    
    IniWrite(gHBarFile, "Buttonbar", "Buttoncount", gHBarMap.Count-1)
    
    # обновление панели
    HBarUpdateReload()        
EndFunc

Func HBarAddButton(IndexType)
    Local txt
    If FileGetTime(gHBarFile) <> gHBarFileTs Then
       MsgBox("Операция невозможна. Файл на диске изменился." & auCRLF & FileGetTime(gHBarFile) & "<>" & gHBarFileTs)
       Return
    Endif    
    # сохранение панели
    HBarSaveState(txt)
    
    # добавление новых ключей
    IniWrite(gHBarFile, "Buttonbar", "button" & gHBarMap.Count+1)
    IniWrite(gHBarFile, "Buttonbar", "iconic" & gHBarMap.Count+1, 0)
    If IndexType = -2 Then
       IniWrite(gHBarFile, "Buttonbar", "cmd" & gHBarMap.Count+1, -2)
    EndIf
    
    # обновление общего количества кнопок
    IniWrite(gHBarFile, "Buttonbar", "Buttoncount", gHBarMap.Count+1)
    
    # обновление панели
    HBarUpdateReload()
    ShowDragHint("Новый индекс " & gHBarMap.Count)
EndFunc
Все четыре операции Drag&Drop:

- Переренос
- Копирование
- Копирование значка
- Удаление
Показать
Изображение
sa
Сообщения: 2758
Зарегистрирован: Ср апр 29, 2009 18:56

Сообщение sa »

Orion9 писал(а):Все-таки пришлось оставить WinRedraw(0), поскольку две SendCommand(2901) не работают с временной панелью
А ведь связка
SendCommand(2945)
WinRedraw(1)
WinRedraw(0)

удачной оказалась – при удалении кнопок между разрывами пустых полос не остаётся как будто!
Аватара пользователя
Orion9
Сообщения: 1293
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

sa
Да, вроде лучше стало, но все-равно, кажется, не всегда срабатывает. Нужно это учитывать, т.е. пока придется иметь в виду, что при операциях добавления и удаления, если они приводят к новому количеству строк на панели, экран не всегда может обновиться, нужно вручную его обновлять изменением размера окна ТС. Но чаще все-таки автообновление срабатывает, чем нет.

Вчера пропустил одну строку в функции HBarMergeOne. Нужно удалить лишнее присвоение.

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

# сохранение панели
Local txt = gHBarList.Text
HBarSaveState(txt)
Оно происходит в самой HBarSaveState()

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

# сохранение панели
Local txt
HBarSaveState(txt)
Еще одно небольшое изменение в HBarWndProc:
Показать

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

If gHBarLoadError Or gHBarList.Count = 0 Then
   ShowDragHint("Панель не загружена")
   Return DllCall("CallWindowProcW", _
   "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
Endif
If FileGetTime(gHBarFile) <> gHBarFileTs Then
   ShowDragHint("Файл панели изменился")
   Return DllCall("CallWindowProcW", _
   "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
Endif
С параметром lParam = -1000 не будет ненужного клика над кнопкой.
Показать

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

Local InfoMsg
If gHBarLoadError Or gHBarList.Count = 0 Then
   InfoMsg = "Панель не загружена"              
ElseIf FileGetTime(gHBarFile) <> gHBarFileTs Then
   InfoMsg = "Файл панели изменился"
Endif
If InfoMsg <> "" Then
   ShowDragHint(InfoMsg)
   lParam = -1000
   Return DllCall("CallWindowProcW", _
         "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
EndIf
Самый надежный способ использования — загрузить файл панели вызовом подсказки над последней кнопкой. Если информация о кнопке правильная, значит все операции с загруженной панелью тоже будут правильные, т.к. карта панели составлена правильно. Но какие-то ошибки все-равно могут быть, пока полностью исключать их нельзя.
sa
Сообщения: 2758
Зарегистрирован: Ср апр 29, 2009 18:56

Сообщение sa »

Попробовал такую связку команд (без всяких WinRedraw):
SendCommand(2945)
SendCommand(2919)
SendCommand(2919)

Вроде она работает и с постоянной и с временной панелью…
Аватара пользователя
Orion9
Сообщения: 1293
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

Классную лазейку вы нашли, спасибо!

Тестировал где-то час, вроде работает! :D

Попробовал как с включеннной кнопкой истории, так и выключенной. Разные операции на панели с 500 кнопками! На которой, к тому же, куча разрывов и разделителей.

Вообще, час пролетел незаметно: прикольно кнопки туда сюда гонять :)

Сделал дополнительное меню для открытия временной панели через OPENBAR. В usercmd.ini нужна команда

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

[em_open_bar_path]
cmd=OPENBAR
param=%A0
Толком не протестировал, но, кажется, есть какие-то проблемы. Меню вызывается через Shift+ ПКМ

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

# правый клик с удержанием Shift
ControlSetMouseAction /R /K:S 28 "HBarShowOpenMenu"
Показать

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

Func HBarShowOpenMenu()
    ShowPopupMenu /D /F /I:0 "HBarOpenBarMenu"
EndFunc

Func HBarOpenBarMenu()
    Local sBars = FileFind(COMMANDER_PATH, "*.bar", -1, 1)
    Local text
    gHBarBars.Count = 0
    gHBarBars.Text = sBars
    For i = 0 To gHBarBars.Count - 1
        text = text & 'MENUITEM "' & gHBarBars[i] & '", em_aucmd -1 HBarOpenBar ' & i & auCRLF 
    Next
    Return text
EndFunc

Func HBarOpenBar(FileIndex)
    Local bar = gHBarBars[FileIndex]
    c = IniRead(bar, "Buttonbar", "Buttoncount", 0)
    If c = 0 Then 
       MsgBox("Файл не содержит данных о кнопках" & auCRLF & auCRLF & bar, "Autorun", 0+48)
       Return
    EndIf
    If gHBarWndProc > 0 Then 
        DllCall("SetWindowLong" & (auX64 ? "PtrW" : "W"), _
                "hwnd", hBarWnd, "int", -4, "long_ptr", gHBarWndProc, "ptr")
        hBarWnd = 0 
    EndIf    
    CommandExec("em_open_bar_path", bar)
EndFunc    
Надо будет его еще потестировать хорошенько. Вроде удобно.
Ответить