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

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

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

Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

sa писал(а):С самого начала поменял у себя на SHIFT
Да, если кому-то нужно, можно и CapsLock на ScrollLock поменять, но лучше этого не делать: не на всех клавиатурах может оказаться эта кнопка. Самому приходится часто работать с тачпадом, поэтому стараюсь не вешать ничего лишнего на колеса мыши и прочие нестандартные кнопки.
sa писал(а):Глядишь, со временем CTRL для операции копирования пригодится
Вы имеете в виду drag&drop без переноса с копипованием? Сделать такое получится, но это будет отклонением от стандартов :)

По поводу текущего drag&drop, если кто-то будет пользоваться (выше уже писалось, но думаю, можно еще раз акцентировать). Сейчас перетаскивание работает так: перенос кнопки назад ставит эту кнопку перед другой кнопкой (кнопкой, на которую осуществляется сброс); перенос кнопки вперед ставит эту кнопку за другой кнопкой. Это изначальные, вынужденные ограничения. Без этих ораничений не было бы возможности при переносе назад поставить кнопку перед самой первой кнопкой, а при переносе вперед — после самой последней.

В принципе, это никак не мешает. Всегда можно сделать повторный drag&drop в нужном направлении для нужного результата
[cut]Изображение[/cut]
Да, можно будет в перспективе попробовать сделать как у Гислера, т.е. учитывать к какому краю ближе сбросывается кнопка, но и сейчас все (по-своему) работает.
sa
Сообщения: 2758
Зарегистрирован: Ср апр 29, 2009 18:56

Сообщение sa »

Orion9 писал(а):но это будет отклонением от стандартов
На то и Autorun, чтобы ниспровергать стандарты! :)
Тем более, что Drag&Drop с кнопками будет достаточно похож на Drag&Drop с файлами: c Shift (или Alt)- переносить, с Ctrl – копировать.
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

sa
Это еще не совсем доработанный модуль, но попробуйте. Shift - копирование, Ctrl - перенос.

[cut]

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

Pragma IncludeOnce

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

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

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

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

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

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

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

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

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

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

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

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

Func 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 txt &= key & '=' & val & auCRLF
     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 ini &= key & '=' & val & auCRLF
    Next 
    ini = StrTrim(ini)
    If ini = '' Then Return HBarShowHint("Нет данных о кнопке " & DragIdx)
    # сохранение панели    
    Local txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf
    # замена ключей со сдвигом +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
    # добавление сохраненной кнопки
    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 "Отладочная информация", em_aucmd ' & (gHBarDbg ?  "/C" : "") & ' -1 HBarDebugInfo' & auCRLF                 
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Отменить действие", em_aucmd ' & (gHBarUndo ?  "" : "/D") & ' -1 HBarUndo' & auCRLF
    txt &= 'MENUITEM "Отменить последние действия", em_aucmd ' & (gHBarCancel ?  "" : "/D") & ' -1 HBarCancel' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Резервная копия 1", em_aucmd ' & (FileExist(bak1) ? "" : "/D") & ' -1 HBarLoadBak 1' & auCRLF
    txt &= 'MENUITEM "Резервная копия 2", em_aucmd ' & (FileExist(bak2) ? "" : "/D") & ' -1 HBarLoadBak 2' & auCRLF    
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Сделать резервную копию 1", em_aucmd -1 HBarSaveBak 1' & auCRLF
    txt &= 'MENUITEM "Сделать резервную копию 2", em_aucmd -1 HBarSaveBak 2' & auCRLF
    Return txt
EndFunc

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

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

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

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

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

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

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

Сообщение sa »

Orion9
Быстро!
Что пока заметил:
1) Копирование кнопки вперед ставит её не за указанной кнопкой (как при переносе), а перед указанной.
2) При копировании кнопки с содержимым в кавычках (в bar-файле кавычки, соответственно, задвоены), кавычки пропадают.
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

sa писал(а):Быстро!
Расчитывал, что будет еще быстрее! Думал, что получится по минимуму подкрутить в готовых функциях, как в прошлый раз, но только время на это потратил. В итоге пришлось делать отдельную функцию для копирования.
sa писал(а):1) Копирование кнопки вперед ставит её не за указанной кнопкой (как при переносе), а перед указанной.
Это обусловлено простотой алгоритма. Сейчас копируемая кнопка встает на место (индекс) старой кнопки, сдвигая старую кнопку вперед. Это не сильно мешает, хотя и создает некоторую путаницу по сравнению с тем, как работает перенос кнопок. Главное сейчас добиться, чтобы ничего не глючило, а алгоритм с учетом границ кнопок расставит все по местам.
sa писал(а):кавычки пропадают.
А разве при простом копировании через меню они не пропадают? Хотя кто его знает, сейчас кнопка просто считывается через IniRead, может это приводит к потере, я пока не успел посмотреть. Отвлекся на подготовку к переходу на дробный индекс. Это позволит определить на какой половине кнопки находится указатель. Сейчас эта информация уже доступна в отладочной информации как "index_float". В принципе, это только первый шаг, но понятно куда дальше двигаться.
[cut]

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

Pragma IncludeOnce

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

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

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

Global gHBarName, gHBarFile, gHBarFileTs
Global gHBarTxt, gHBarCP = "ANSI", gHBarWincmd = 0, gHBarDbg = 1
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_SHIFT, _
           CopyButton      = MK_CONTROL
    Static drag_system = DllCall("LoadCursor", "Ptr", 0, "Ptr", 32646), _
           stop_system = DllCall("LoadCursor", "Ptr", 0, "Ptr", 32648), _
           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
                IsDrag = wParam
                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")
                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
                      HBarDragnCopy(gDragIndex, gDropIndex)
                   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
                
                DllCall("SetCursor", "Ptr", BitAND(wParam, MoveButton) ? drag_cursor : copy_cursor)
             Else
                DllCall("SetCursor", "Ptr", stop_cursor)               
             EndIf
          EndIf   
    EndSwitch
    Return DllCall("CallWindowProcW", _
                   "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
EndFunc

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

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

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

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

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

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

Func 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 txt &= key & '=' & val & auCRLF
     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 ini &= key & '=' & val & auCRLF
    Next 
    ini = StrTrim(ini)
    If ini = '' Then Return HBarShowHint("Нет данных о кнопке " & DragIdx)
    # сохранение панели    
    Local txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf
    # замена ключей со сдвигом +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
    # добавление сохраненной кнопки
    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 "Отладочная информация", em_aucmd ' & (gHBarDbg ?  "/C" : "") & ' -1 HBarDebugInfo' & auCRLF                 
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Отменить действие", em_aucmd ' & (gHBarUndo ?  "" : "/D") & ' -1 HBarUndo' & auCRLF
    txt &= 'MENUITEM "Отменить последние действия", em_aucmd ' & (gHBarCancel ?  "" : "/D") & ' -1 HBarCancel' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Резервная копия 1", em_aucmd ' & (FileExist(bak1) ? "" : "/D") & ' -1 HBarLoadBak 1' & auCRLF
    txt &= 'MENUITEM "Резервная копия 2", em_aucmd ' & (FileExist(bak2) ? "" : "/D") & ' -1 HBarLoadBak 2' & auCRLF    
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Сделать резервную копию 1", em_aucmd -1 HBarSaveBak 1' & auCRLF
    txt &= 'MENUITEM "Сделать резервную копию 2", em_aucmd -1 HBarSaveBak 2' & auCRLF
    Return txt
EndFunc

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

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

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

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

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

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

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

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

MoveButton      = MK_SHIFT, _
CopyButton      = MK_CONTROL
т.ч. можно сменить при необходимости. Иконка при копировании другая, но это почти не видно. При копировании фон имеет другую прозрачность, чтобы как-то отличать эти две операции.

А вообще, копирование вещь прикольная. Я, признаюсь, немного недооценил эту операцию. Особенно удобно разделители множить :)
sa
Сообщения: 2758
Зарегистрирован: Ср апр 29, 2009 18:56

Сообщение sa »

Orion9 писал(а):А разве при простом копировании через меню они не пропадают?
Нет.
Orion9 писал(а):Иконка при копировании другая
Если при переносе отпустить клавишу Shift, то, по-моему, точно такая же (с плюсиком), что и при копировании.
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

Твою ж Швейцарию. Кажись, работает :D

[cut]

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

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), _
           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
                IsDrag = wParam
                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
                      HBarDragnCopy(gDragIndex, gDropIndex)
                   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
                DllCall("SetCursor", "Ptr", BitAND(IsDrag, MoveButton) ? drag_cursor : copy_cursor)
             Else
                DllCall("SetCursor", "Ptr", stop_cursor)               
             EndIf
          EndIf   
    EndSwitch
    Return DllCall("CallWindowProcW", _
                   "ptr", gHBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
EndFunc

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

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

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

Func HBarGetIdx(MousePos = false, PosX = 0, PosY = 0)
    # дескриптор монитора окна ТС
    Local hMon = DllCall("MonitorFromWindow", "hwnd", AUTORUN_TCHANDLE, "dword", 2)
    # буфер для структуры MONITORINFOEX
    Local buf = Buffer(104)
    buf.Zero()
    buf.SetNum(0, "dword", 104)
    DllCall("GetMonitorInfoW", "ptr", hMon, "ptr", buf.ptr)
    # имя монитора
    Local sMon = buf.GetStr(40)
    # информация о масштабировании экрана
    Local DC = DllCall("GetDC", "int", 0)
    Local nDPI = DllCall("GetDeviceCaps", "handle", DC, "int", 88) # LOGPIXELSX
    Local nScale = DllCall("MulDiv", "int", 100, "int", nDPI, "int", 96)
    DllCall("ReleaseDC", "int", 0, "handle", DC)     
    # локальные переменные
    Local x, y, w, h, mx, my, idx, row, rows, rowh, dpi, b, extra
    # координаты и размеры окна панели
    WinGetPos("x", "y", "w", "h", hBarWnd) 
    # координаты указателя мыши
    If MousePos Then
       mx = PosX
       my = PosY
    Else
       MouseGetPos("mx", "my")
    EndIf   
    # чтение информации о высоте кнопок
    IniRead /R b %COMMANDER_INI% "Buttonbar" "Buttonheight"
    IniRead /R dpi %COMMANDER_INI% "Buttonbar" "DefaultDpi" %'nDPI'
    Local b_ini = b, b_calc
    # масштабирование экрана
    If dpi <> nDPI Then
       IniRead /R d %COMMANDER_INI% "Buttonbar" %'"Buttonheight" & nDPI' 0
       If d <> 0 Then 
          b = d
       Else   
          b = Round(b / (dpi / nDPI), 0)
          b_calc = "*"
       EndIf
    EndIf   
    # количество рядов кнопок на панели
    rows = Floor(h/b)
    If rows = 0 Then Return HBarShowHint("Error number of rows can't be zero")
    # количество дополнительных пикселей
    extra = Mod(h,b)
    # ширина разделителя    
    Local div = Floor(b/3)
    # высота одного ряда
    rowh = b + 3
    # номер ряда под указателем
    row = Ceil((my - y)/rowh)
    # абстрактные координаты по оси х
    Local tx = (mx - x + 1) + ((row - 1) * w), cx = 0, ex = 0, 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, old
    txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf          
    old = HBarReadIndex(DragIdx)
    # временная замена индекса
    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 txt &= key & '=' & val & auCRLF
     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 ini &= key & '=' & val & auCRLF
    Next 
    ini = StrTrim(ini)
    If ini = '' Then Return HBarShowHint("Нет данных о кнопке " & DragIdx)
    # сохранение панели    
    Local txt = gHBarList.Text
    gHBarUndoLoad = txt
    If Not gHBarCancel Then
       gHBarCancel = true
       gHBarCancelLoad = txt
    EndIf
    # умный сброс по границам кнопки
    If gHBarSmart Then
       If gSmartIndex > DropIdx + 0.500 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 ' & (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)
EndFunc

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

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

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

sa
Исправил клавишу Shift, спасибо, что помогаете, я бы один не смог столько тестов провести. А вот с кавычками пока не понял. Если не трудно, хотелось бы пример кнопки, но мне кажется, что это функция IniRead их удаляет. Не хотелось бы переделывать через поиск в массиве или через регэксресы. А если навести на эту кнопку перед копированием, в подсказке тоже без кавычек отображается? Если да, то все, кирдык. Тогда точно сама IniRead их удаляет.
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

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

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

# сохранение кнопки
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 
sa
Сообщения: 2758
Зарегистрирован: Ср апр 29, 2009 18:56

Сообщение sa »

Orion9 писал(а):Твою ж Швейцарию. Кажись, работает :D
Вроде всё хорошо!
Из замеченного:
MENUITEM "Smart Drag&&Drop"
Ну и для себя, как обычно, поменял назначение Shift и Ctrl:

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

           MoveButton      = MK_SHIFT, _
           CopyButton      = MK_CONTROL
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

sa писал(а):Вроде всё хорошо!
Вроде бы да!

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

[cut]Изображение[/cut]
В остальном же, чик-чик-чик, как в пятнашки сыграл :mrgreen:
sa писал(а): Из замеченного:
Да, спасибо, подправил. Думал, что придется справку открывать, чтобы найти как это сделать :)

P.S. Прикрепил последний модуль к первому сообщению в топике, чтобы зафиксировать все изменения. Надеюсь, крупных ошибок больше нет, тех, которые могут привести к порче панели, но будьте осторожнее!
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

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

[cut]

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

    txt &= 'MENUITEM "Отладочная информация", em_aucmd ' & (gHBarDbg ?  "/C" : "") & ' -1 HBarDebugInfo' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & 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
[/cut]
фрагмент можно добавить в конец модуля

[cut]

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

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)
    Sleep(10)
    SendMessage(AUTORUN_TCHANDLE, 11, 1, 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)
    WinRedraw(2)
EndFunc
[/cut]
Поскольку используются функции для работы с регулярными выражениями, в autorun.cfg должен быть подключен субплагин

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

LoadLibrary Plugins\Autorun_RegExp.dll
Если коротко о функционале, можно открыть несколько доролнительных панелей и перетаскивать значки на главной панели. Пока только для теста, могут быть ошибки. По этой причине не стал обновлять модуль на первой странице. Есть планы сделать удаление кнопок с панели и копирование только значка при переносе, ради чего, собственно, это все и задумывалось.
sa
Сообщения: 2758
Зарегистрирован: Ср апр 29, 2009 18:56

Сообщение sa »

Orion9 писал(а):Сделал два новых, пока эксперементальных пункта меню,
На первый взгляд пункты работают!
Спасибо, удобно!
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

sa писал(а):На первый взгляд пункты работают!
Вроде бы! Хотелось бы в это верить :D

Еще не успел хорошо протестировать, но заметил, что есть проблемы с обновлением окна панели. Видимо, cm_ReloadBarIcons не расчитана на то, что при обновлении значков может меняться количество строк, поэтому пришлось заменить на cm_VisButtonBar. Но я это сделал для только для новых пунктов, а при откатах все еще может проявиться. Изменение размеров окна тотала временно помогает, но надо избавиться от этой проблемы.

Юникодные бары тоже еще не проверял, не успел просто. Вообще, могут быть какие-то ошибки.

Я еще не решил, как сделать удаление и копирование значков кнопок. Вроде напрашивается использование Alt, но не хочется целиком занимать эту кнопку. Наверное, лучше следить за ее состоянием при сбросе.

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

P.S. Надо сделать еще сохранение панели. Думал, сейчас по-быстренькому спрошу у ИИ готовый код для Autohotkey и переведу его на Autorun, а он мне, зараза, нерабочий код подсунул, пришлось пересчитывать за ним структуру :)
[cut="ИИ"]

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

file := Buffer(1024 * 2, 0)

filter := "Text Files (*.txt)`0*.txt`0All Files (*.*)`0*.*`0`0"
filterBuf := Buffer(StrPut(filter, "UTF-16") * 2)
StrPut(filter, filterBuf, "UTF-16")

; OPENFILENAMEW size (x64)
ofn := Buffer(152, 0)

NumPut("UInt", ofn.Size, ofn, 0)
NumPut("Ptr", A_ScriptHwnd, ofn, 8)
NumPut("Ptr", filterBuf.Ptr, ofn, 24)
NumPut("Ptr", file.Ptr, ofn, 48)
NumPut("UInt", 1024, ofn, 56)
NumPut("UInt", 0x00000002 | 0x00000800, ofn, 80)
NumPut("Ptr", StrPtr("txt"), ofn, 96)

if DllCall("comdlg32\GetSaveFileNameW", "Ptr", ofn)
    MsgBox StrGet(file, "UTF-16")
else
    MsgBox "Cancelled or error."
[/cut]
[cut="Коррекция"]

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

NumPut("UInt", 0x00000002 | 0x00000800, ofn, 96)
NumPut("Ptr", StrPtr("txt"), ofn, 104)
[/cut]
В обшем, сделаем. Правда придется еще и для х32 отдельно считать, поскольку этот код только для х64.
sa
Сообщения: 2758
Зарегистрирован: Ср апр 29, 2009 18:56

Сообщение sa »

Orion9 писал(а):есть проблемы с обновлением окна панели ...
при откатах все еще может проявиться. Изменение размеров окна тотала временно помогает
Да, этот момент я тоже заметил.
Orion9 писал(а):Я еще не решил, как сделать удаление
Для отдельной кнопки вполне можно пользоваться и стандартным вариантом TC – через контекстное меню кнопки, пункт "Вырезать".
Для группы удаляемых кнопок хорошо бы добавить пункт меню (в дополнение к "Удалить кнопки за разрывом") для указания мышью крайних кнопок диапазона.
Orion9 писал(а):Копирование значка может пригодиться ...
ТС плохо к этому приспособлен.
С версии 11.57 стало удобнее с настройкой кнопки: индекс значка теперь можно менять напрямую в поле ввода.
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

sa писал(а):Для отдельной кнопки вполне можно пользоваться и стандартным вариантом TC – через контекстное меню кнопки, пункт "Вырезать".
Да, надо еще посмотреть, что будет удобнее. Если требуется удалить несколько кнопок, то Drag&Drop, по идее, должен быть удобен. Но надо проверить.
sa писал(а):хорошо бы добавить пункт меню (в дополнение к "Удалить кнопки за разрывом") для указания мышью крайних кнопок диапазона.
Маркирировать выбор будет, наверное, сложно. Проще добавить два разрыва и удалить все между ними, но тут надо подумать.
sa писал(а):С версии 11.57 стало удобнее с настройкой кнопки
Да, есть улучшения, не спорю, но все-равно процесс мучителен :)

Посмотрим, что получится. Пока только сделал сохранение. Оказалось сложнее, чем я думал, но фича полезная. Пока плохо протестированная, но вот

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

    txt &= 'MENUITEM "Отладочная информация", em_aucmd ' & (gHBarDbg ?  "/C" : "") & ' -1 HBarDebugInfo' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF      
    txt &= 'MENUITEM "Сохранить...", em_aucmd -1 HBarSaveAs' & auCRLF      
    txt &= 'POPUP "Загрузить панель"' & auCRLF
[cut]

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

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)
    EndIf
    
    Free(file, filt, ofn)
EndFunc
[/cut]
Ответить