Orion9

|
Posted: Sat Nov 01, 2025 20:45 Post subject: |
|
|
И всё-таки я ещё раз спрошу у Гислера: ну и где здесь "случайное" перетаскивание кнопок на панели?
 Hidden text
Ладно, шучу я. Понятно, что он просто не захотел этим заморачиваться, а я взял и заморочился
Последняя версия модуля.
 Bars.aucfg | Code: | Pragma IncludeOnce
# наведение на главную панель
ControlSetHint /F /D:50 28 "HBarShowHint"
# правый клик с удержанием Ctrl
ControlSetMouseAction /R /K:C 28 "HBarShowMenu"
Global gDragIndex, gDropIndex
Global gHBarButtons = 0, gHBarDivs = 0
Global gHBarHint = 0, gHBarVerb, gHBarCancel, gHBarCancelLoad
Global gHBarName, gHBarFile, gHBarTxt, gHBarCP = "ANSI", gHBarWincmd = 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)
Switch uMsg
Case WM_LBUTTONDOWN
If IsDrag = 0 And BitAND(wParam, MK_CONTROL) Then
MouseGetPos("x","y")
If DllCall("DragDetect", "hwnd", hWnd, "int64", MakeInt(y, x, 2)) Then
IsDrag = 1
WinSetStyle(0x80000, 3, hWnd)
DllCall("SetLayeredWindowAttributes", "hwnd", hWnd, "ptr", 0, "byte", 128, "dword", 2)
gDragIndex = HBarGetIdx()
EndIf
EndIf
Case WM_LBUTTONUP
If IsDrag = 1 Then
gDropIndex = HBarGetIdx()
If WinFromPoint() = hWnd Then
If gDragIndex <> gDropIndex Then
If gHBarHint Then ShowDragHint("Index " & gDragIndex & " To Index: " & gDropIndex)
HBarDragnDrop(gDragIndex, gDropIndex)
EndIf
EndIf
WinSetStyle(0x80000, 5, hWnd)
IsDrag = 0
MouseGetPos("x","y")
lParam = MakeInt(y, x + 200, 2)
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()
If WinHasStyle(0x80000, 1, hBarWnd) Then Return
# состояние Ctrl
Local bMap = IsPressed(0x11)
# состояние CapsLock и Shift
If Not bMap Then
If BitAND(DllCall("GetKeyState", "int", 0x14, "short"), 1) = 0 Then
If Not IsPressed(0x10) Then Return
EndIf
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 Return "Файл панели не найден " & gHBarFile
# загрузка панели
HBarLoad()
EndIf
# индекс кнопки под курсором
gDragIndex = HBarGetIdx()
If Not gDragIndex Then Return "Не удалось определить индекс кнопки"
Return HBarGetHint(bMap)
EndFunc
Func HBarGetFile()
Local bar, txt
# информация о файле панели в wincmd.ini
IniRead 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 = COMMANDER_PATH & "\" & txt
EndIf
Return bar
EndFunc
Func HBarLoad()
# загрузка файла в массив
gHBarList.Count = 0
gHBarList.LoadFromFile(gHBarFile, gHBarCP)
# количество кнопок в файле
Local nCount = 0
IniRead nCount %gHBarFile% "Buttonbar" "Buttoncount" 0
# обнуление счетчиков
gHBarDivs = 0
gHBarButtons = 0
# локальные переменные
Local j, key, val, found, oButton = Map(), oCmd = Map()
# поиск кнопок и команд
For j = 0 To gHBarList.Count - 1
found = 0
If StrLeft(gHBarList[j], 6) = "button" Then
found = 1
ElseIf StrLeft(gHBarList[j], 3) = "cmd" Then
found = 2
EndIf
If found > 0 Then
key = StrLower(StrPart(gHBarList[j], "=", 1))
val = StrLen(StrPart(gHBarList[j], "=", 2))
If found = 1 Then
oButton.Set(key, val)
Else
oCmd.Set(key, val)
EndIf
EndIf
Next
gHBarMap.Count = 0
# подсчет кнопок и разделителей
For j = 1 To nCount
key = "button" & j
val = oButton.Get(key, 0)
If val > 0 Then
# количество кнопок
gHBarButtons += 1
gHBarMap.Add(1)
Else
# количество разделителей
gHBarDivs += 1
gHBarMap.Add(0)
EndIf
Next
Free(oButton, oCmd)
# карта панели
gHBarTxt = auCRLF & auCRLF & "Loaded: " & gHBarFile & 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()
# дескриптор монитора окна ТС
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)
# координаты указателя мыши
MouseGetPos("mx", "my")
# чтение информации о высоте кнопок
IniRead b %COMMANDER_INI% "Buttonbar" "Buttonheight"
IniRead dpi %COMMANDER_INI% "Buttonbar" "DefaultDpi" %'nDPI'
Local b_ini = b, b_calc
# масштабирование экрана
If dpi <> nDPI Then
IniRead d %COMMANDER_INI% "Buttonbar" %'"Buttonheight" & nDPI' 0
If d <> 0 Then
b = d
Else
b = Round(b / (dpi / nDPI), 0)
b_calc = "*"
EndIf
EndIf
# количество рядов кнопок на панели
rows = Floor(h/b)
If rows = 0 Then Return HBarShowHint("Error number of rows can't be zero")
# количество дополнительных пикселей
extra = Mod(h,b)
# ширина разделителя
Local div = Floor(b/3)
# высота одного ряда
rowh = b + 3
# номер ряда под указателем
row = Ceil((my - y)/rowh)
# абстрактные координаты по оси х
Local tx = (mx - x + 1) + ((row - 1) * w), cx = 0
# индекс кнопки в ряду
If gHBarDivs = 0 Then
idx = (Floor((mx - x)/b) + 1) + (row-1)*(Floor(w/b))
Else
j = 1
idx = 0
For i = 0 To gHBarMap.Count - 1
cx += (gHBarMap[i] = 0 ? div : b)
ex = j*w - cx
If i + 1 <= gHBarMap.Count - 1 Then
If ex < (gHBarMap[i+1] = 0 ? div : b) Then
cx += ex
j += 1
EndIf
Else
cx += ex
EndIf
If cx >= tx Then
idx = i + 1
Break
EndIf
Next
EndIf
# дополнительная информация
gHBarVerb = "mouse=" & mx & ", " & my & auCRLF & _
"bar=" & x & ", " & y & ", " & w & ", " & h & auCRLF & _
"map=" & tx & ", " & cx & auCRLF & _
"row=" & row & " of " & rows & auCRLF & _
"height=" & rowh & " (" & extra & ")" & " b=" & b & b_calc & " (" & b_ini & ")" & auCRLF & _
"buttons=" & gHBarButtons & " + " & gHBarDivs & " divs" & auCRLF & _
"width=" & b & ", " & div & auCRLF & _
"mon=" & HMon & " dpi=" & nDPI & " scale=" & nScale & "%"
Return idx
EndFunc
Func HBarGetHint(bMap)
# чтение информации о кнопке из .bar фйла
Local sMenu, sButton, sCmd, sParam, sPath, sIcon
IniRead sMenu %gHBarFile% "Buttonbar" %'"menu" & gDragIndex' ""
IniRead sButton %gHBarFile% "Buttonbar" %'"button" & gDragIndex' ""
IniRead sCmd %gHBarFile% "Buttonbar" %'"cmd" & gDragIndex' ""
IniRead sParam %gHBarFile% "Buttonbar" %'"param" & gDragIndex' ""
IniRead sPath %gHBarFile% "Buttonbar" %'"path" & gDragIndex' ""
IniRead sIcon %gHBarFile% "Buttonbar" %'"iconic" & gDragIndex' ""
# удаление подсказки тотала
Local hTip = WinFind(0, "TToolTip")
If hTip > 0 Then DllCall("DestroyWindow", "handle", hTip)
# возврат текста
Return gHBarVerb & auCRLF & _
"" & auCRLF & _
"index=" & gDragIndex & " of " & gHBarButtons+gHBarDivs & auCRLF & _
"menu=" & sMenu & auCRLF & _
"button=" & sButton & auCRLF & _
"cmd=" & sCmd & auCRLF & _
"param=" & sParam & auCRLF & _
"path=" & sPath & auCRLF & _
"iconic=" & sIcon & (bMap ? gHBarTxt : "")
EndFunc
Func HBarDragnDrop(DragIdx, DropIdx)
Static aItems = List("button","cmd","param","path","menu","iconic")
Local T1, T2, bTL = 0
If bTL Then T1 = GetUptime()
Local i, j, k, txt, key, new
txt = gHBarList.Text
gHBarUndoLoad = txt
If Not gHBarCancel Then
gHBarCancel = true
gHBarCancelLoad = txt
EndIf
# временная замена индекса
For j = 0 To aItems.Count - 1
key = aItems[j] & DragIdx & "="
new = aItems[j] & 9999 & "="
txt = StrReplace(txt, key, new)
Next
If bTL Then
T2 = Round(GetUptime() - T1, 0) / 1000
OutputDebugString("Время операции 1: " & StrFormat("%.3f", T2) & " sec")
EndIf
If DragIdx < DropIdx Then
For i = DragIdx + 1 To DropIdx
For k = 0 To aItems.Count - 1
key = aItems[k] & i & "="
new = aItems[k] & i - 1 & "="
txt = StrReplace(txt, key, new)
If bTL Then OutputDebugString("DragIdx < " & key & " -> " & new)
Next
Next
Else
For i = DragIdx - 1 TO DropIdx Step -1
For k = 0 To aItems.Count - 1
key = aItems[k] & i & "="
new = aItems[k] & i + 1 & "="
txt = StrReplace(txt, key, new)
If bTL Then OutputDebugString("DragIdx > " & key & " -> " & new)
Next
Next
EndIf
If bTL Then
T2 = Round(GetUptime() - T1, 0) / 1000
OutputDebugString("Время операции 2: " & StrFormat("%.3f", T2) & " sec")
EndIf
For j = 0 To aItems.Count - 1
key = aItems[j] & 9999 & "="
new = aItems[j] & DropIdx & "="
txt = StrReplace(txt, key, new)
Next
gHBarList.Text = txt
gHBarList.SaveToFile(gHBarFile, gHBarCP)
gHBarUndoTs = FileGetTime(gHBarFile)
If gHBarUndoTs Then
gHBarUndo = true
Else
gHBarUndo = false
gHBarUndoLoad = ""
EndIf
HBarLoad()
If bTL Then
T2 = Round(GetUptime() - T1, 0) / 1000
OutputDebugString("Время операции 3: " & StrFormat("%.3f", T2) & " sec")
EndIf
SendCommand(2945) # cm_ReloadBarIcons
EndFunc
Func ShowDragHint(DragHint)
SetHintParam("ShowHint", "Font", 14, "Arial")
SetHintParam("ShowHint", "BackColor", 0x000000)
SetHintParam("ShowHint", "Text", 0xFFFFFF)
SetHintParam("ShowHint", "DarkBackColor", 0xFF0000)
SetHintParam("ShowHint", "DarkText", 0xFFFFFF)
ShowHint(DragHint, "", "", 3000, 1)
WinAlign(LAST_HINT_WINDOW)
Sleep(10)
SetHintParam("ShowHint", "Reload")
EndFunc
Func HBarShowMenu()
Local hWnd = RequestInfo(28)
If hWnd = 0 Then
ShowDragHint("Окно панели не найдено")
Return
EndIf
If gHBarName <> WinGetText(hWnd) Then
ShowDragHint("Нет сведений о файле панели")
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 ' & (gHBarCP = "ANSI" ? "/C" : "") & ' -1 HBarSetCodePage 1' & auCRLF
txt &= 'MENUITEM "Открывать как UTF-16", em_aucmd ' & (gHBarCP = "UTF-16" ? "/C" : "") & ' -1 HBarSetCodePage 2' & 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 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 = ""
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 = ""
HBarLoad()
ShowDragHint("Первоначальная панель загружена")
SendCommand(2945)
EndIf
EndFunc |
Добавлена отмена действия и возврат к первоначальной панели, а также возможность создания бэкапов. Исправлено масштабирование экрана. Ветка с загрузкой панели в ANSI или UTF-16 получается не сильно нужна и остается пока недоделанной по причинам описанным выше. Однако важное замечание: если панель хранится в юникоде, по умолчанию она сохраняться будет как ANSI, если не поставить соответствующую галочку в меню. Галочка между сессиями не сохраняется, тоже имейте это в виду. У меня все панели храняться в ANSI, а к модулю я могу еще вернуться не скоро, хотя автосохранение в корректной кодировке само напрашивается.
Окно панели хранит только имя файла, информация о пути недоступна. Поэтому файлы ищутся только в каталоге ТС или подкаталоге \Bars. Если и там, и там есть одинаковое имя файла, это может привести к проблемам.
У Гислера висит своя оконная процедура, во избежание проблем приходится передавать ей управление. Чтобы избавится от клика, которая посылает эта процедура, используется подмена х-координаты указателя мыши:
| Code: | MouseGetPos("x","y")
lParam = MakeInt(y, x + 200, 2) |
Клик происходит на 200 пикселей правее и не приводит к запуску кнопки. Но если используется слишком большое масштабирование, запуск может произойти.
В остальном проблем не замечено. Пользуюсь с большим удовольствием )
| Loopbak wrote: | | А всё потому, что это не реальная функция в dll, а враппер |
Понятно. Спасибо, что разобрались. Хотел было использовать что-то вроде:
| Code: | Local pX, pY
Local nScale = 0, bRes
If DllCall("Ntdll.dll\IsWindows8Point1OrGreater", "bool") Then
bRes = DllCall("Shcore.dll\GetDpiForMonitor", "hwnd", hMon, "int", 0, "uint*", @pX, "uint*", @pY)
bRes = DllCall("Shcore.dll\GetScaleFactorForMonitor", "hwnd", hMon, "dword*", @nScale)
nDPI = pX
EndIf |
Но почитав немного про эти функции и про DPI в целом, понял, что оно того не стоит.
| Loopbak wrote: | | Так задумано. Если файл содержит BOM, он всегда читается в кодировке, определяемой BOM. |
Имхо, правильно задумано. А если во взятом таким образом массиве есть маркер BOM то сохраняться через SaveToFile он тоже будет корректно?
Добавлено спустя 4 минуты:
Забыл написать, что меню вызывается по правому клику мыши с зажатым Ctrl. |
|