From e50aebe4d623b0e3a30831b5097911bfc52cdad0 Mon Sep 17 00:00:00 2001 From: Klesh Wong Date: Thu, 8 Jul 2021 16:16:21 +0800 Subject: [PATCH] [feature] add alternative clipboard --- win/ahk/ClipboardManager.ahk | 60 ++++++ win/{ => ahk}/JSON.ahk | 0 win/{ => ahk}/WinGetPosEx.ahk | 0 win/ahk/WindowManager.ahk | 250 +++++++++++++++++++++++ win/win.ahk | 363 ++++++---------------------------- 5 files changed, 374 insertions(+), 299 deletions(-) create mode 100644 win/ahk/ClipboardManager.ahk rename win/{ => ahk}/JSON.ahk (100%) rename win/{ => ahk}/WinGetPosEx.ahk (100%) create mode 100644 win/ahk/WindowManager.ahk diff --git a/win/ahk/ClipboardManager.ahk b/win/ahk/ClipboardManager.ahk new file mode 100644 index 0000000..14441d5 --- /dev/null +++ b/win/ahk/ClipboardManager.ahk @@ -0,0 +1,60 @@ +; CLIPBOARD + +InitClipboardManager() { +} + +ConvertClipboardToPlainText() { + ClipBoard = %ClipBoard% +} + +ConvertClipboardToUnixPath() { + ClipBoard := StrReplace(ClipBoard, "\", "/") ; +} + +BorrowClipboard(funcName, params*) { + Clip0 = %ClipBoardAll% + result := %funcName%(params*) + ClipBoard = %Clip0% + VarSetCapacity(Clip0, 0) ; Free memory + return result +} + +PasteAsPlainText() { + BorrowClipboard("PasteAs", "ConvertClipboardToPlainText") +} + +PasteAsUnixPath() { + BorrowClipboard("PasteAs", "ConvertClipboardToUnixPath") +} + +PasteAs(convertFuncName) { + %convertFuncName%() + Send ^v + Sleep 500 +} + +CopyAndFetch() { + Clipboard = + Send, ^c ; simulate Ctrl+C (=selection in clipboard) + ClipWait, 0, 1 ; wait until clipboard contains data + selection = %Clipboard% ; save the content of the clipboard + return selection +} + +GetSelectedText() { + return BorrowClipboard("CopyAndFetch") +} + +AlternativeCopy() { + global ALTERNATIVE_CLIPBOARD + ALTERNATIVE_CLIPBOARD := GetSelectedText() +} + +AlternativePaste() { + BorrowClipboard("PasteAs", "LoadAlternativeToClipboard") +} + +LoadAlternativeToClipboard() { + global ALTERNATIVE_CLIPBOARD + Clipboard = %ALTERNATIVE_CLIPBOARD% +} diff --git a/win/JSON.ahk b/win/ahk/JSON.ahk similarity index 100% rename from win/JSON.ahk rename to win/ahk/JSON.ahk diff --git a/win/WinGetPosEx.ahk b/win/ahk/WinGetPosEx.ahk similarity index 100% rename from win/WinGetPosEx.ahk rename to win/ahk/WinGetPosEx.ahk diff --git a/win/ahk/WindowManager.ahk b/win/ahk/WindowManager.ahk new file mode 100644 index 0000000..873ade5 --- /dev/null +++ b/win/ahk/WindowManager.ahk @@ -0,0 +1,250 @@ +; WINDOWS MANAGER +; global RATIO := 0.618 +global RATIO := 0.382 +global ID_SEEN := Object() +global ARRANGEMENT := Object() +global ARRANGEMENT_PATH := A_AppData . "\arrangement.json" +global PADDING := 10 + +; ========================= +; KEY BINDING +; ========================= +; Send cursor to the center of window while switching +~#1 Up:: +~#2 Up:: +~#3 Up:: +~#4 Up:: +~#5 Up:: +~#6 Up:: +~#7 Up:: +~#8 Up:: +~#9 Up:: + Sleep 100 + SetCursorToCenterOfActiveWin() + return +~!Tab Up:: + while (GetKeyState("Alt") != 0 or GetKeyState("Tab") != 0) { + Sleep 50 + } + Sleep 100 + WinGetPos, x, y, w, h, A + SetCursorToCenterOfActiveWin() + return + + +; ========================= +; FUNCTION +; ========================= + +InitWindowManager() { + LoadArrangement() + SetTimer, AdjustNewWindow, 1000 +} + +SetCursorPos(x, y) { + DllCall("SetCursorPos", "int", x, "int", y) +} + +FocusWinUnderCursor() { + MouseGetPos, MouseX, MouseY, WinId + WinActivate, ahk_id %WinId% +} + +SetCursorToCenterOfActiveWin() { + WinGetPos x, y, w, h, A + SetCursorPos(x + w / 2, y + h / 2) +} + +FocusWinByPos(x, y) { + SetCursorPos(x, y) + FocusWinUnderCursor() + SetCursorToCenterOfActiveWin() +} + +GetCursorMonGeometry(ByRef x, ByRef y, ByRef w, ByRef h) { + MouseGetPos, MouseX, MouseY + SysGet, mc, MonitorCount + ; find current monitor + mi := 0 + loop { + SysGet, mon, MonitorWorkArea, %mi% + if (monLeft < MouseX and monRight > MouseX) { + x := monLeft + y := monTop + w := monRight - monLeft + h := monBottom - monTop + return + } + } +} + +FocusWinByDirection(direction) { + global RATIO + GetCursorMonGeometry(x, y, w, h) + wf := RATIO / 2 + hf := 0.5 + if (direction = "right") + wf := RATIO + (1 - RATIO) / 2 + FocusWinByPos(x + w * wf, y + h * hf) +} + +MoveActiveWinByDirection(direction) { + WinGet, isMax, MinMax, A + if (isMax) { + WinRestore, A + } + global RATIO + global PADDING + GetCursorMonGeometry(x, y, w, h) + activeWinId := WinExist("A") + WinGetPosEx(activeWinId, wx, wy, ww, wh, l, t, r, b) + wx := x + wy := y + ww := floor(w * RATIO) + wh := h + if (direction = "right") { + wx := ww + floor(PADDING / 2) + ww := w - ww + } else { + wx := wx + PADDING + } + ww := ww - floor(PADDING * 1.5) + wy := wy + PADDING + wh := wh - PADDING * 2 + WinMove, A,, wx - l, wy - t, ww + l + r, wh + t + b + SaveActiveWindowDirection(direction) +} + +SaveArrangement() { + global ARRANGEMENT + global ARRANGEMENT_PATH + file := FileOpen(ARRANGEMENT_PATH, "w") + file.Write(JSON.Dump(ARRANGEMENT,, 2)) + file.Close() +} + +LoadArrangement() { + global ARRANGEMENT + global ARRANGEMENT_PATH + try { + FileRead, temp, %ARRANGEMENT_PATH% + ARRANGEMENT := JSON.Load(temp) + } catch { + ARRANGEMENT := Object() + } + if not IsObject(ARRANGEMENT) { + ARRANGEMENT := Object() + } + if not IsObject(ARRANGEMENT["windows"]) { + ARRANGEMENT["windows"] := Object() + } + if not IsObject(ARRANGEMENT["blacklist"]) { + ARRANGEMENT["blacklist"] := Object() + } + if not IsObject(ARRANGEMENT["whitelist"]) { + ARRANGEMENT["whitelist"] := Object() + } +} + +GetActiveWindowPath() { + WinGet processPath, ProcessPath, A + WinGetClass windowClass, A + return processPath . ":" . windowClass +} + +IsActiveWindowSeen() { + global ID_SEEN + WinGet winId, ID, A + seen := ID_SEEN.HasKey(winId) + ID_SEEN[winId] := true + return seen +} + +BlacklistArrangementForActiveWindow() { + global ARRANGEMENT + windowPath := GetActiveWindowPath() + ARRANGEMENT["blacklist"][windowPath] := true + ARRANGEMENT["whitelist"].Delete(windowPath) + SaveArrangement() +} + +WhitelistArrangementForActiveWindow() { + global ARRANGEMENT + windowPath := GetActiveWindowPath() + ARRANGEMENT["whitelist"][windowPath] := true + ARRANGEMENT["blacklist"].Delete(windowPath) + SaveArrangement() +} + +IgnoreArrangementForActiveWindow() { + global ARRANGEMENT + windowPath := GetActiveWindowPath() + ARRANGEMENT["whitelist"].Delete(windowPath) + ARRANGEMENT["blacklist"].Delete(windowPath) +} + +IsActiveWindowBorderless() { + WinGet s, Style, A + if (not s & +0xC00000) { + return true + } + return false +} + +IsActiveWindowSizeboxed() { + WinGet, s, Style, A + return s & 0x40000 +} + +IsActiveWindowArrangable() { + global ARRANGEMENT + if (ARRANGEMENT["blacklist"].HasKey(GetActiveWindowPath())) { + return false + } + if (ARRANGEMENT["whitelist"].HasKey(GetActiveWindowPath())) { + return true + } + if (IsActiveWindowBorderless()) { + return false + } + return true +} + +SaveActiveWindowDirection(direction) { + global ARRANGEMENT + key := GetActiveWindowPath() + ARRANGEMENT["windows"][key] := direction + SaveArrangement() +} + +ActiveWinInfo() { + WinGetTitle, title, A + WinGetClass, klass, A + WinGet processPath, ProcessPath, A + WinGet id, ID, A + return Format("{1}:{2}[{3}]{4}", processPath, klass, id, title) +} + +AdjustNewWindow() { + seen := IsActiveWindowSeen() + arrangable := IsActiveWindowArrangable() + wininfo := ActiveWinInfo() + if not seen { + LogDebug(Format("win: {1}, seen: {2}, arrangable: {3}", wininfo, seen, arrangable)) + } + if not seen and arrangable { + windowPath := GetActiveWindowPath() + if ARRANGEMENT["windows"].HasKey(windowPath) { + MoveActiveWinByDirection(ARRANGEMENT["windows"][windowPath]) + } + } +} + +ToggleActiveWinMaximum() { + WinGet, isMax, MinMax, A + if (isMax) { + WinRestore, A + } else { + WinMaximize, A + } +} diff --git a/win/win.ahk b/win/win.ahk index e2d57e8..a1e8a10 100644 --- a/win/win.ahk +++ b/win/win.ahk @@ -5,87 +5,89 @@ SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory. CoordMode, Mouse, Screen ; mouse coordinates relative to the screen ; ========================= -; CONFIGURATION +; DEBUGGING ; ========================= -; global RATIO := 0.618 -global RATIO := 0.382 -global ID_SEEN := Object() -global ARRANGEMENT := Object() -global ARRANGEMENT_PATH := A_AppData . "\arrangement.json" -global PADDING := 10 -LoadArrangement() -;WatchNewWindow() -SetTimer, AdjustNewWindow, 1000 +global DEBUGGING := false + +ToggleDebugging() { + global DEBUGGING + DEBUGGING := not DEBUGGING +} + +LogDebug(msg) { + global DEBUGGING + if (not DEBUGGING) { + return + } + FormatTIme, now, , MM-dd HH:mm:ss + log := FileOpen("d:\win.ahk.log", "a") + log.WriteLine(Format("[{1}] {2}", now, msg)) + log.Close() +} + + ; ========================= -; BINDINGS +; LIBS ; ========================= +InitWindowManager() +InitClipboardManager() +#Include, ahk\JSON.ahk +#Include, ahk\WinGetPosEx.ahk +#Include, ahk\WindowManager.ahk +#Include, ahk\ClipboardManager.ahk + + +; ========================= +; KEY BINDINGS +; ========================= +; Super + Shift + r => Reload ahk +#+r::Reload +; Ctrl + ESC => Ctrl+` ^Esc:: Send ^{``} -*#q:: !F4 -#Esc:: Reload +; Win + q => Close window +#q:: !F4 +; Ctrl+ backspace => Delete all text ^BS:: - Send ^a - Send {BS} - return + Send ^a + Send {BS} + return +; Win + = => Increase Volume #=::SoundSet, +5 +; Win + - => Decrease volume #-::SoundSet, -5 +; Win + \ => Toggle mute #\::Send {Volume_Mute} +; Win + backspace => Lock #BS::#l + +; WINDOW MANAGER + +; Win + f => Toggle window maximum #f:: ToggleActiveWinMaximum() -^#p::ShowActiveWinGeometry() +; Win + j => Focus right window #j:: FocusWinByDirection("right") +; Win + k => Focus left window #k:: FocusWinByDirection("left") +; Win + Shift + j => Move active window to right side #+j::MoveActiveWinByDirection("right") +; Win + Shift + k => Move active window to left side #+k::MoveActiveWinByDirection("left") -#+r::reload -#i::IgnoreArrangementForActiveWindow() -#+i::UnignoreArrangementForActiveWindow() -#t::ShowDebug() +; Win + Shift + b => Blacklist active window so it won't be arranged when launched +#+b::BlacklistArrangementForActiveWindow() +; Win + Shift + b => Whitelist active window so it always be arranged when launched +#+w::WhitelistArrangementForActiveWindow() +; Win + Shift + i => Remove active window from Blacklist/Whitelist +#+i::IgnoreArrangementForActiveWindow() +; Win + Shift + d => Toggle debug logging +#+d::ToggleDebugging() -; Ctrl + Alt + v : paste as plain text -^!v:: - Clip0 = %ClipBoardAll% - ClipBoard = %ClipBoard% ; Convert to plain text - Send ^v - Sleep 1000 - ClipBoard = %Clip0% - VarSetCapacity(Clip0, 0) ; Free memory -Return -; Ctrl + Alt + Shift + v : paste as plain test a replace \ with / -^+!v:: - Clip0 = %ClipBoardAll% - ClipBoard := StrReplace(ClipBoard, "\", "/") ; Convert to plain text - Send ^v - Sleep 1000 - ClipBoard = %Clip0% - VarSetCapacity(Clip0, 0) ; Free memory -Return +; CLIPBOARD MANAGER +#c::AlternativeCopy() +#v::AlternativePaste() -; move cursor to center of window after switching -~#1 Up:: -~#2 Up:: -~#3 Up:: -~#4 Up:: -~#5 Up:: -~#6 Up:: -~#7 Up:: -~#8 Up:: -~#9 Up:: - Sleep 100 - SetCursorToCenterOfActiveWin() - return -~!Tab Up:: - while (GetKeyState("Alt") != 0 or GetKeyState("Tab") != 0) { - Sleep 50 - } - Sleep 100 - WinGetPos, x, y, w, h, A - SetCursorToCenterOfActiveWin() - return -; ========================= ; CAPSLOCK AS HYBRID KEY -; ========================= ; Capslock & h:: Send {Left} ; Capslock & j:: Send {Down} @@ -108,240 +110,3 @@ Return ; SetCapsLockState % !GetKeyState("CapsLock", "T") ; Return ; Capslock::return - -; ========================= -; FUNCTIONS -; ========================= - -#Include, WinGetPosEx.ahk -#Include, JSON.ahk - -ShowGeometry(x, y, w, h) { - MsgBox, , Geometry,% Format("x:{}, y:{}, w: {}, h: {}", x, y, w, h) -} - -ShowActiveWinGeometry() { - WinGetPos x, y, w, h, A - ShowGeometry(x, y, w, h) -} - -SetCursorPos(x, y) { - DllCall("SetCursorPos", "int", x, "int", y) -} - -FocusWinUnderCursor() { - MouseGetPos, MouseX, MouseY, WinId - WinActivate, ahk_id %WinId% -} - -SetCursorToCenterOfActiveWin() { - WinGetPos x, y, w, h, A - SetCursorPos(x + w / 2, y + h / 2) -} - -FocusWinByPos(x, y) { - SetCursorPos(x, y) - FocusWinUnderCursor() - SetCursorToCenterOfActiveWin() -} - -GetCursorMonGeometry(ByRef x, ByRef y, ByRef w, ByRef h) { - MouseGetPos, MouseX, MouseY - SysGet, mc, MonitorCount - ; find current monitor - mi := 0 - loop { - SysGet, mon, MonitorWorkArea, %mi% - if (monLeft < MouseX and monRight > MouseX) { - x := monLeft - y := monTop - w := monRight - monLeft - h := monBottom - monTop - return - } - } -} - -FocusWinByDirection(direction) { - global RATIO - GetCursorMonGeometry(x, y, w, h) - wf := RATIO / 2 - hf := 0.5 - if (direction = "right") - wf := RATIO + (1 - RATIO) / 2 - FocusWinByPos(x + w * wf, y + h * hf) -} - -MoveActiveWinByDirection(direction) { - WinGet, isMax, MinMax, A - if (isMax) { - WinRestore, A - } - global RATIO - global PADDING - GetCursorMonGeometry(x, y, w, h) - activeWinId := WinExist("A") - WinGetPosEx(activeWinId, wx, wy, ww, wh, l, t, r, b) - wx := x - wy := y - ww := floor(w * RATIO) - wh := h - if (direction = "right") { - wx := ww + floor(PADDING / 2) - ww := w - ww - } else { - wx := wx + PADDING - } - ww := ww - floor(PADDING * 1.5) - wy := wy + PADDING - wh := wh - PADDING * 2 - WinMove, A,, wx - l, wy - t, ww + l + r, wh + t + b - SaveActiveWindowDirection(direction) -} - -SaveArrangement() { - global ARRANGEMENT - global ARRANGEMENT_PATH - file := FileOpen(ARRANGEMENT_PATH, "w") - file.Write(JSON.Dump(ARRANGEMENT,, 2)) - file.Close() -} - -LoadArrangement() { - global ARRANGEMENT - global ARRANGEMENT_PATH - try { - FileRead, temp, %ARRANGEMENT_PATH% - ARRANGEMENT := JSON.Load(temp) - } catch { - ARRANGEMENT := Object() - } - if not IsObject(ARRANGEMENT) { - ARRANGEMENT := Object() - } - if not IsObject(ARRANGEMENT["windows"]) { - ARRANGEMENT["windows"] := Object() - } - if not IsObject(ARRANGEMENT["ignore"]) { - ARRANGEMENT["ignore"] := Object() - } -} - -GetActiveWindowClassPath() { - WinGet processPath, ProcessPath, A - WinGetClass windowClass, A - return processPath . ":" . windowClass -} - -IsActiveWindowSeen() { - global ID_SEEN - WinGet winId, ID, A - seen := ID_SEEN.HasKey(winId) - ID_SEEN[winId] := true - return seen -} - -IgnoreArrangementForActiveWindow() { - global ARRANGEMENT - ARRANGEMENT["ignore"][GetActiveWindowClassPath()] := true - SaveArrangement() -} - -UnignoreArrangementForActiveWindow() { - global ARRANGEMENT - ARRANGEMENT["ignore"].Delete(GetActiveWindowClassPath()) - SaveArrangement() -} - -IsActiveWindowBorderless() { - WinGet s, Style, A - if (not s & +0xC00000) { - return true - } - return false -} - -IsActiveWindowSizeboxed() { - WinGet, s, Style, A - return s & 0x40000 -} - -IsActiveWindowIgnored() { - global ARRANGEMENT - if (ARRANGEMENT["ignore"].HasKey(GetActiveWindowClassPath())) { - return true - } - ; WinGetTitle, title, A - ; if (title = "") { - ; return true - ; } - if (not IsActiveWindowSizeboxed()) { - return true - } - return false -} - -SaveActiveWindowDirection(direction) { - global ARRANGEMENT - key := GetActiveWindowClassPath() - ARRANGEMENT["windows"][key] := direction - SaveArrangement() -} - -ActiveWinInfo() { - WinGetTitle, title, A - WinGetClass, klass, A - WinGet processPath, ProcessPath, A - return processPath . " " . klass . " " . title -} - -AdjustNewWindow() { - global log - seen := IsActiveWindowSeen() - ignored := IsActiveWindowIgnored() - wininfo := ActiveWinInfo() - if not seen { - LogDebug(Format("win: {1}, seen: {2}, ignore: {3}", wininfo, seen, ignored)) - } - if not seen and not ignored { - classPath := GetActiveWindowClassPath() - if ARRANGEMENT["windows"].HasKey(classPath) { - MoveActiveWinByDirection(ARRANGEMENT["windows"][classPath]) - } - } -} - -ToggleActiveWinMaximum() { - WinGet, isMax, MinMax, A - if (isMax) { - WinRestore, A - } else { - WinMaximize, A - } -} - -GetSelectedText() { - tmp = %ClipboardAll% ; save clipboard - Clipboard := "" ; clear clipboard - Send, ^c ; simulate Ctrl+C (=selection in clipboard) - ClipWait, 0, 1 ; wait until clipboard contains data - selection = %Clipboard% ; save the content of the clipboard - Clipboard = %tmp% ; restore old content of the clipboard - return selection -} - -ShowDebug() { - ShowObject(IsActiveWindowSizeboxed()) -} - -ShowObject(obj) { - msg := JSON.Dump(obj) - MsgBox, %msg% -} - -LogDebug(msg) { - FormatTIme, now, , MM-dd HH:mm:ss - log := FileOpen("d:\win.ahk.log", "a") - log.WriteLine(Format("[{1}] {2}", now, msg)) - log.Close() -} \ No newline at end of file