Please Lexikos, don't stop developing it! Not saying that it's faulty/buggy, because it's not, I'm just wondering if some more combinations are possible!
But, if you're really gonna stop it, please solve my issue:
Remeber that version in which we held down Win and pressed numpadbuttons, in order to move the window to the region of the screen associated to that number? (for example: Win+Numpad1 would move the window to the bottom-left corner of the window, and so on).
So, I have it appended to my main-daily script. It is...
Code:
gosub WindowPadInit
; WindowPad:
;
; Move and resize windows with Win+Numpad.
; Win+Numpad1 = Fill bottom-left quarter of screen
; Win+Numpad2 = Fill bottom half of screen
; etc.
;
; Move windows across monitors. For example:
; Win+Numpad4 places the window on the left half of the screen.
; Win+Numpad4 again moves it to the monitor to the right.
;
; Quick monitor switch:
; Win+Numpad5 places the window in the center of the screen.
; Win+Numpad5 again moves the window to the next monitor.
; (This works by monitor number, not necessarily left to right.)
;
; QUICKER Monitor Switch:
; Win+NumpadDot switches to the next monitor (1->2->3->1 etc.)
; Win+NumpadDiv moves ALL windows to monitor 2.
; Win+NumpadMult moves ALL windows to monitor 1.
;
; Other shortcuts:
; Win+Numpad0 toggles maximize.
; Insert (or some other key) can be used in place of "Win".
;
WindowPadInit:
; Exclusion examples:
GroupAdd, GatherExclude, ahk_class SideBar_AppBarWindow
; These two come in pairs for the Vista sidebar gadgets:
GroupAdd, GatherExclude, ahk_class SideBar_HTMLHostWindow ; gadget content
GroupAdd, GatherExclude, ahk_class BasicWindow ; gadget shadow/outline
; Comma-delimited list of processes to exclude.
;ProcessGatherExcludeList = sidebar.exe
; (ProcessGatherExcludeList excludes ALL windows belonging to those processes,
; including windows you may not want to exclude, like the sidebar config window.)
Prefix_Active = # ; Win+Numpad = Move active window
Prefix_Other = #! ; Alt+Win+Numpad = Move previously active window
; Note: Shift (+) should not be used, as +Numpad is hooked by the OS
; to do left/right/up/down/etc. (reverse Numlock) -- at least on Vista.
EasyKey = Insert ; Insert is near Numpad on my keyboard...
; Note: Prefix_Other must not be a sub-string of Prefix_Active.
; (If you want it to be, first edit the line "if (InStr(A_ThisHotkey, Prefix_Other))")
; Width and Height Factors for Win+Numpad5 (center key.)
CenterWidthFactor = 1.0
CenterHeightFactor = 1.0
Hotkey, IfWinActive ; in case this is included in another script...
Loop, 9
{ ; Register hotkeys.
Hotkey, %Prefix_Active%Numpad%A_Index%, DoMoveWindowInDirection
Hotkey, %Prefix_Other%Numpad%A_Index%, DoMoveWindowInDirection
; OPTIONAL
if EasyKey
Hotkey, %EasyKey% & Numpad%A_Index%, DoMoveWindowInDirection
}
Hotkey, %Prefix_Active%Numpad0, DoMaximizeToggle
Hotkey, %Prefix_Other%Numpad0, DoMaximizeToggle
Hotkey, %Prefix_Active%NumpadDot, MoveWindowToNextScreen
Hotkey, %Prefix_Other%NumpadDot, MoveWindowToNextScreen
Hotkey, %Prefix_Active%NumpadDiv, GatherWindowsLeft
Hotkey, %Prefix_Active%NumpadMult, GatherWindowsRight
if (EasyKey) {
Hotkey, %EasyKey% & Numpad0, DoMaximizeToggle
Hotkey, %EasyKey% & NumpadDot, MoveWindowToNextScreen
Hotkey, %EasyKey% & NumpadDiv, GatherWindowsLeft
Hotkey, %EasyKey% & NumpadMult, GatherWindowsRight
Hotkey, *%EasyKey%, SendEasyKey ; let EasyKey's original function work (on release)
}
return
SendEasyKey:
Send {Blind}{%EasyKey%}
return
; This is actually based on monitor number, so if your secondary is on the
; right, you may want to switch these around.
GatherWindowsLeft:
GatherWindows(2)
return
GatherWindowsRight:
GatherWindows(1)
return
; Hotkey handler.
DoMoveWindowInDirection:
DoMoveWindowInDirection()
return
DoMoveWindowInDirection()
{
local dir, dir0, dir1, dir2, widthFactor, heightFactor
; Define constants.
if (!Directions1) {
dir = -1:+1,0:+1,+1:+1,-1:0,0:0,+1:0,-1:-1,0:-1,+1:-1
StringSplit, Directions, dir, `,
}
gosub WP_SetLastFoundWindowByHotkey
; Determine which direction we want to go.
if (!RegExMatch(A_ThisHotkey, "\d+", dir) or !Directions%dir%)
{
MsgBox Error: "%A_ThisHotkey%" was registered and I can't figure out which number it is!
return
}
dir := Directions%dir%
StringSplit, dir, dir, :
; Determine width/height factors.
if (dir1 or dir2) { ; to a side
widthFactor := dir1 ? 0.5 : 1.0
heightFactor := dir2 ? 0.5 : 1.0
} else { ; to center
widthFactor := CenterWidthFactor
heightFactor := CenterHeightFactor
}
; Move the window!
MoveWindowInDirection(dir1, dir2, widthFactor, heightFactor)
}
return
WP_SetLastFoundWindowByHotkey:
; Set Last Found Window.
if (InStr(A_ThisHotkey, Prefix_Other))
WinPreviouslyActive()
else
WinExist("A")
return
; "Maximize"
DoMaximizeToggle:
MaximizeToggle()
return
MaximizeToggle()
{
gosub WP_SetLastFoundWindowByHotkey
WinGet, state, MinMax
if state
WinRestore
else
WinMaximize
}
; Does the grunt work of the script.
MoveWindowInDirection(sideX, sideY, widthFactor, heightFactor, screenMoveOnly=false)
{
WinGetPos, x, y, w, h
; Determine which monitor contains the center of the window.
m := GetMonitorAt(x+w/2, y+h/2)
; Get work area of active monitor.
gosub CalcMonitorStats
; Calculate possible new position for window.
gosub CalcNewPosition
; If the window is already there,
if (newx "," newy "," neww "," newh) = (x "," y "," w "," h)
{ ; ..move to the next monitor along instead.
if (sideX or sideY)
{ ; Move in the direction of sideX or sideY.
SysGet, monB, Monitor, %m% ; get bounds of entire monitor (vs. work area)
x := (sideX=0) ? (x+w/2) : (sideX>0 ? monBRight : monBLeft) + sideX
y := (sideY=0) ? (y+h/2) : (sideY>0 ? monBBottom : monBTop) + sideY
newm := GetMonitorAt(x, y, m)
}
else
{ ; Move to center (Numpad5)
newm := m+1
SysGet, mon, MonitorCount
if (newm > mon)
newm := 1
}
if (newm != m)
{ m := newm
; Move to opposite side of monitor (left of a monitor is another monitor's right edge)
sideX *= -1
sideY *= -1
; Get new monitor's work area.
gosub CalcMonitorStats
}
; Calculate new position for window.
gosub CalcNewPosition
}
; Restore before resizing...
WinGet, state, MinMax
if state
WinRestore
; Finally, move the window!
SetWinDelay, -1
WinMove,,, newx, newy, neww, newh
return
CalcNewPosition:
; Calculate new size.
if (IsResizable()) {
neww := Round(monWidth * widthFactor)
newh := Round(monHeight * heightFactor)
} else {
neww := w
newh := h
}
; Calculate new position.
newx := Round(monLeft + (sideX+1) * (monWidth - neww)/2)
newy := Round(monTop + (sideY+1) * (monHeight - newh)/2)
return
CalcMonitorStats:
; Get work area (excludes taskbar-reserved space.)
SysGet, mon, MonitorWorkArea, %m%
monWidth := monRight - monLeft
monHeight := monBottom - monTop
return
}
; Get the index of the monitor containing the specified x and y co-ordinates.
GetMonitorAt(x, y, default=1)
{
SysGet, m, MonitorCount
; Iterate through all monitors.
Loop, %m%
{ ; Check if the window is on this monitor.
SysGet, Mon, Monitor, %A_Index%
if (x >= MonLeft && x <= MonRight && y >= MonTop && y <= MonBottom)
return A_Index
}
return default
}
IsResizable()
{
WinGet, Style, Style
return (Style & 0x40000) ; WS_SIZEBOX
}
; Note: This may not work properly with always-on-top windows. (Needs testing)
WinPreviouslyActive()
{
active := WinActive("A")
WinGet, win, List
; Find the active window.
; (Might not be win1 if there are always-on-top windows?)
Loop, %win%
if (win%A_Index% = active)
{
if (A_Index < win)
N := A_Index+1
; hack for PSPad: +1 seems to get the document (child!) window, so do +2
ifWinActive, ahk_class TfPSPad
N += 1
break
}
; Use WinExist to set Last Found Window (for consistency with WinActive())
return WinExist("ahk_id " . win%N%)
}
;
; Switch without moving/resizing (relative to screen)
;
MoveWindowToNextScreen:
gosub WP_SetLastFoundWindowByHotkey
WinGet, state, MinMax
if state = 1
{ ; Maximized windows don't move correctly on XP
; (and possibly other versions of Windows)
WinRestore
MoveWindowToNextScreen()
WinMaximize
}
else
MoveWindowToNextScreen()
return
MoveWindowToNextScreen()
{
WinGetPos, x, y, w, h
; Determine which monitor contains the center of the window.
ms := GetMonitorAt(x+w/2, y+h/2)
; Determine which monitor to move to.
md := ms+1
SysGet, mon, MonitorCount
if (md > mon)
md := 1
; This may happen if someone tries it with only one screen. :P
if (md = ms)
return
; Get source and destination work areas (excludes taskbar-reserved space.)
SysGet, ms, MonitorWorkArea, %ms%
SysGet, md, MonitorWorkArea, %md%
msw := msRight - msLeft, msh := msBottom - msTop
mdw := mdRight - mdLeft, mdh := mdBottom - mdTop
; Calculate new size.
if (IsResizable()) {
w *= (mdw/msw)
h *= (mdh/msh)
}
SetWinDelay, -1
; Move window, using resolution difference to scale co-ordinates.
WinMove,,, mdLeft + (x-msLeft)*(mdw/msw), mdTop + (y-msTop)*(mdh/msh), w, h
}
;
; "Gather" windows on a specific screen.
;
GatherWindows(md=1)
{
global ProcessGatherExcludeList
SetWinDelay, -1 ; Makes a BIG difference to perceived performance.
; List all visible windows.
WinGet, win, List
; Copy bounds of all monitors to an array.
SysGet, mc, MonitorCount
Loop, %mc%
SysGet, mon%A_Index%, MonitorWorkArea, %A_Index%
; Destination monitor
mdx := mon%md%Left
mdy := mon%md%Top
mdw := mon%md%Right - mdx
mdh := mon%md%Bottom - mdy
Loop, %win%
{
; If this window matches the GatherExclude group, don't touch it.
if (WinExist("ahk_group GatherExclude ahk_id " . win%A_Index%))
continue
; Set Last Found Window.
if (!WinExist("ahk_id " . win%A_Index%))
continue
WinGet, procname, ProcessName
; Check process (program) exclusion list.
if procname in %ProcessGatherExcludeList%
continue
WinGetPos, x, y, w, h
; Determine which monitor this window is on.
xc := x+w/2, yc := y+h/2
ms := 1
Loop, %mc%
if (xc >= mon%A_Index%Left && xc <= mon%A_Index%Right
&& yc >= mon%A_Index%Top && yc <= mon%A_Index%Bottom)
{
ms := A_Index
break
}
; If already on destination monitor, skip this window.
if (ms = md)
continue
; Source monitor
msx := mon%ms%Left
msy := mon%ms%Top
msw := mon%ms%Right - msx
msh := mon%ms%Bottom - msy
; If the window is resizable, scale it by the monitors' resolution difference.
if (IsResizable()) {
w *= (mdw/msw)
h *= (mdh/msh)
}
WinGet, state, MinMax
if state = 1
WinRestore
; Move window, using resolution difference to scale co-ordinates.
WinMove,,, mdx + (x-msx)*(mdw/msw), mdy + (y-msy)*(mdh/msh), w, h
if state = 1
WinMaximize
}
}
Note that I'm not using any .ini file.
Now, you present us the new version, with that even better arranged window scheme... and I'd like to append it to my script too.
It would be...
Code:
; WindowPad v1.54
;
; Credits:
; Concept based on HiRes Screen Splitter by JOnGliko.
; Written from scratch by Lexikos to support multiple monitors.
; NumpadDot key functionality suggested by bobbo.
;
; Built with AutoHotkey v1.0.47
;
WindowPadInit: ; for backwards-compatibility. :)
WindowPad_Init(WINDOWPAD_INI_PATH)
return
WindowPad_Init(IniPath="")
{
global WINDOWPAD_INI_PATH
; if A_IsCompiled ; Load icons from my custom WindowPad.exe.
; {
; ; Default icon is 32x32, so doesn't look good in the tray.
; Menu, TRAY, Icon, %A_ScriptFullPath%, 11
; }
; else
if (A_LineFile = A_ScriptFullPath)
{ ; Set the tray icon, but only if not included in some other script.
WindowPad_SetTrayIcon(true)
; Use OnMessage to catch "Suspend Hotkeys" or "Pause Script"
; so the "disabled" icon can be used.
OnMessage(0x111, "WM_COMMAND")
}
if IniPath =
IniPath = %A_ScriptDir%\WindowPad.ini
WINDOWPAD_INI_PATH := IniPath
WindowPad_LoadSettings(IniPath)
}
WindowPad_LoadSettings(ininame)
{
local v
; Misc Options
IniRead, v, %ininame%, Options, TitleMatchMode, %A_TitleMatchMode%
SetTitleMatchMode, %v%
; Hotkeys: Exclude Windows
v := WindowPad_INI_GetList(ininame, "Exclude Windows", "Window")
Loop, Parse, v, `n
GroupAdd, HotkeyExclude, %A_LoopField%
; Read the Hotkeys section in.
v := WindowPad_INI_ReadSection(ininame, "Hotkeys")
; Replace the first = with ::.
; ('=' is required for WritePrivateProfileSection to work properly.)
v := RegExReplace(v, "m`a)^(.*?)=", "$1::")
Hotkey, IfWinNotActive, ahk_group HotkeyExclude
Hotkey_Params(v)
; Gather: Exclude Windows
v := WindowPad_INI_GetList(ininame, "Gather: Exclude Windows", "Window")
Loop, Parse, v, `n
GroupAdd, GatherExclude, %A_LoopField%
; Gather: Exclude Processes
ProcessGatherExcludeList := WindowPad_INI_GetList(ininame
, "Gather: Exclude Processes", "Process", ",")
}
WindowPad_INI_GetList(ininame, Section, Key, Delim="`n")
{
v := WindowPad_INI_ReadSection(ininame, Section)
Loop, Parse, v, `n
{
pos := InStr(A_LoopField, "=")
if (pos && SubStr(A_LoopField,1,pos-1) = Key)
list .= (list ? Delim : "") . SubStr(A_LoopField, pos+1)
}
return list
}
WindowPad_INI_ReadSection(Filename, Section)
{
; Expand relative paths, since GetPrivateProfileSection only searches %A_WinDir%.
Loop, %Filename%, 0
Filename := A_LoopFileLongPath
VarSetCapacity(text, 0x7FFF, 0)
len := DllCall("GetPrivateProfileSection"
, "str", Section, "str", text, "uint", 0x7FFF, "str", Filename)
; Each line within the section is terminated with a null character.
; Replace each delimiting null char with a newline:
Loop, % len-1
if (NumGet(text, A_Index-1, "UChar") = 0)
NumPut(10, text, A_Index-1, "UChar") ; \0 -> \n
; Windows Me/98/95:
; The returned string includes comments.
;
; This removes comments. Also, I'm not sure if leading/trailing space is
; automatically removed on Win9x, so the regex removes that too.
if A_OSVersion in WIN_ME,WIN_98,WIN_95
text := RegExReplace(text, "m`n)^[ `t]*(?:;.*`n?|`n)|^[ `t]+|[ `t]+$")
return text
}
WindowPadMove(P)
{
StringSplit, P, P, `,, %A_Space%%A_Tab%
; Params: 1:dirX, 2:dirY, 3:widthFactor, 4:heightFactor, 5:window
; dirX and dirY are required.
if P1 is not number
return
if P2 is not number
return
WindowPad_WinExist(P5)
if !WinExist()
return
; Determine width/height factors.
if (P1 or P2) { ; to a side
widthFactor := P3+0 ? P3 : (P1 ? 0.5 : 1.0)
heightFactor := P4+0 ? P4 : (P2 ? 0.5 : 1.0)
} else { ; to center
widthFactor := P3+0 ? P3 : 1.0
heightFactor := P4+0 ? P4 : 1.0
}
; Move the window!
MoveWindowInDirection(P1, P2, widthFactor, heightFactor)
}
return
MaximizeToggle(P)
{
WindowPad_WinExist(P)
WinGet, state, MinMax
if state
WinRestore
else
WinMaximize
}
; Does the grunt work of the script.
MoveWindowInDirection(sideX, sideY, widthFactor, heightFactor)
{
WinGetPos, x, y, w, h
; Determine which monitor contains the center of the window.
m := GetMonitorAt(x+w/2, y+h/2)
; Get work area of active monitor.
gosub CalcMonitorStats
; Calculate possible new position for window.
gosub CalcNewPosition
; If the window is already there,
if (newx "," newy "," neww "," newh) = (x "," y "," w "," h)
{ ; ..move to the next monitor along instead.
if (sideX or sideY)
{ ; Move in the direction of sideX or sideY.
SysGet, monB, Monitor, %m% ; get bounds of entire monitor (vs. work area)
x := (sideX=0) ? (x+w/2) : (sideX>0 ? monBRight : monBLeft) + sideX
y := (sideY=0) ? (y+h/2) : (sideY>0 ? monBBottom : monBTop) + sideY
newm := GetMonitorAt(x, y, m)
}
else
{ ; Move to center (Numpad5)
newm := m+1
SysGet, mon, MonitorCount
if (newm > mon)
newm := 1
}
if (newm != m)
{ m := newm
; Move to opposite side of monitor (left of a monitor is another monitor's right edge)
sideX *= -1
sideY *= -1
; Get new monitor's work area.
gosub CalcMonitorStats
}
else
{ ; No monitor to move to, alternate size of window instead.
if sideX
widthFactor /= 2
else if sideY
heightFactor /= 2
gosub CalcNewPosition
}
; Calculate new position for window.
gosub CalcNewPosition
}
; Restore before resizing...
WinGet, state, MinMax
if state
WinRestore
; Finally, move the window!
SetWinDelay, 0
WinMove,,, newx, newy, neww, newh
return
CalcNewPosition:
; Calculate new size.
if (IsResizable()) {
neww := Round(monWidth * widthFactor)
newh := Round(monHeight * heightFactor)
} else {
neww := w
newh := h
}
; Calculate new position.
newx := Round(monLeft + (sideX+1) * (monWidth - neww)/2)
newy := Round(monTop + (sideY+1) * (monHeight - newh)/2)
return
CalcMonitorStats:
; Get work area (excludes taskbar-reserved space.)
SysGet, mon, MonitorWorkArea, %m%
monWidth := monRight - monLeft
monHeight := monBottom - monTop
return
}
; Get the index of the monitor containing the specified x and y co-ordinates.
GetMonitorAt(x, y, default=1)
{
SysGet, m, MonitorCount
; Iterate through all monitors.
Loop, %m%
{ ; Check if the window is on this monitor.
SysGet, Mon, Monitor, %A_Index%
if (x >= MonLeft && x <= MonRight && y >= MonTop && y <= MonBottom)
return A_Index
}
return default
}
IsResizable()
{
WinGetClass, Class
if Class = Chrome_XPFrame
return true
WinGet, Style, Style
return (Style & 0x40000) ; WS_SIZEBOX
}
WindowPad_WinExist(WinTitle)
{
if WinTitle = P
return WinPreviouslyActive()
if WinTitle = M
{
MouseGetPos,,, win
return WinExist("ahk_id " win)
}
return WinExist(WinTitle!="" ? WinTitle : "A")
}
; Note: This may not work properly with always-on-top windows. (Needs testing)
WinPreviouslyActive()
{
active := WinActive("A")
WinGet, win, List
; Find the active window.
; (Might not be win1 if there are always-on-top windows?)
Loop, %win%
if (win%A_Index% = active)
{
if (A_Index < win)
N := A_Index+1
; hack for PSPad: +1 seems to get the document (child!) window, so do +2
ifWinActive, ahk_class TfPSPad
N += 1
break
}
; Use WinExist to set Last Found Window (for consistency with WinActive())
return WinExist("ahk_id " . win%N%)
}
;
; Switch without moving/resizing (relative to screen)
;
WindowScreenMove(P)
{
SetWinDelay, 0
StringSplit, P, P, `,, %A_Space%%A_Tab%
; 1:Next|Prev|Num, 2:Window
WindowPad_WinExist(P2)
WinGet, state, MinMax
if state = 1
WinRestore
WinGetPos, x, y, w, h
; Determine which monitor contains the center of the window.
ms := GetMonitorAt(x+w/2, y+h/2)
SysGet, mc, MonitorCount
; Determine which monitor to move to.
if P1 in ,N,Next
{
md := ms+1
if (md > mc)
md := 1
}
else if P1 in P,Prev,Previous
{
md := ms-1
if (md < 1)
md := mc
}
else if P1 is integer
md := P1
if (md=ms or (md+0)="" or md<1 or md>mc)
return
; Get source and destination work areas (excludes taskbar-reserved space.)
SysGet, ms, MonitorWorkArea, %ms%
SysGet, md, MonitorWorkArea, %md%
msw := msRight - msLeft, msh := msBottom - msTop
mdw := mdRight - mdLeft, mdh := mdBottom - mdTop
; Calculate new size.
if (IsResizable()) {
w *= (mdw/msw)
h *= (mdh/msh)
}
; Move window, using resolution difference to scale co-ordinates.
WinMove,,, mdLeft + (x-msLeft)*(mdw/msw), mdTop + (y-msTop)*(mdh/msh), w, h
if state = 1
WinMaximize
}
;
; "Gather" windows on a specific screen.
;
GatherWindows(md=1)
{
global ProcessGatherExcludeList
SetWinDelay, 0
; List all visible windows.
WinGet, win, List
; Copy bounds of all monitors to an array.
SysGet, mc, MonitorCount
Loop, %mc%
SysGet, mon%A_Index%, MonitorWorkArea, %A_Index%
if md = M
{ ; Special exception for 'M', since the desktop window
; spreads across all screens.
CoordMode, Mouse, Screen
MouseGetPos, x, y
md := GetMonitorAt(x, y, 0)
}
else if md is not integer
{ ; Support A, P and WinTitle.
; (Gather at screen containing specified window.)
WindowPad_WinExist(md)
WinGetPos, x, y, w, h
md := GetMonitorAt(x+w/2, y+h/2, 0)
}
if (md<1 or md>mc)
return
; Destination monitor
mdx := mon%md%Left
mdy := mon%md%Top
mdw := mon%md%Right - mdx
mdh := mon%md%Bottom - mdy
Loop, %win%
{
; If this window matches the GatherExclude group, don't touch it.
if (WinExist("ahk_group GatherExclude ahk_id " . win%A_Index%))
continue
; Set Last Found Window.
if (!WinExist("ahk_id " . win%A_Index%))
continue
WinGet, procname, ProcessName
; Check process (program) exclusion list.
if procname in %ProcessGatherExcludeList%
continue
WinGetPos, x, y, w, h
; Determine which monitor this window is on.
xc := x+w/2, yc := y+h/2
ms := 0
Loop, %mc%
if (xc >= mon%A_Index%Left && xc <= mon%A_Index%Right
&& yc >= mon%A_Index%Top && yc <= mon%A_Index%Bottom)
{
ms := A_Index
break
}
; If already on destination monitor, skip this window.
if (ms = md)
continue
WinGet, state, MinMax
if (state = 1) {
WinRestore
WinGetPos, x, y, w, h
}
if ms
{
; Source monitor
msx := mon%ms%Left
msy := mon%ms%Top
msw := mon%ms%Right - msx
msh := mon%ms%Bottom - msy
; If the window is resizable, scale it by the monitors' resolution difference.
if (IsResizable()) {
w *= (mdw/msw)
h *= (mdh/msh)
}
; Move window, using resolution difference to scale co-ordinates.
WinMove,,, mdx + (x-msx)*(mdw/msw), mdy + (y-msy)*(mdh/msh), w, h
}
else
{ ; Window not on any monitor, move it to center.
WinMove,,, mdx + (mdw-w)/2, mdy + (mdh-h)/2
}
if state = 1
WinMaximize
}
}
Hotkeys(P)
{
local this_hotkey, P0, P1, P2, SectionVar, hotkeys, wait_for_keyup, m, m1, pos, k
static key_regex = "^(?:.* & )?[#!^+&<>*~$]*(.+)"
this_hotkey := A_ThisHotkey
StringSplit, P, P, `,, %A_Space%%A_Tab%
; 1:Section, 2:Options
if !P1
goto HC_SendThisHotkeyAndReturn
pos := RegExMatch(P2, "i)(?<=\bD)\d*\.?\d*", m)
if pos
{
P2 := SubStr(P2, 1, pos-2) . SubStr(P2, pos+StrLen(m))
if (m+0 = "")
m := 0.1
Input, k, L1 T%m%
if ErrorLevel != Timeout
{
gosub HC_SendThisHotkey
Send %k%
return
}
}
SectionVar := RegExReplace(P1, "[^\w#@$?\[\]]", "_")
hotkeys := Hotkeys_%SectionVar%
if hotkeys =
{
; Load each hotkeys section on first use. Since the ini file may be
; edited between enabling and disabling the hotkeys, loading them
; each and every time would be hazardous.
hotkeys := WindowPad_INI_ReadSection(WINDOWPAD_INI_PATH, "Hotkeys: " P1)
if hotkeys =
goto HC_SendThisHotkeyAndReturn
; key=command -> key::command
hotkeys := RegExReplace(hotkeys, "m`a)^(.*?)=", "$1::")
Hotkeys_%SectionVar% := hotkeys
}
; If Options were omitted and this is a key-down hotkey,
; automatically disable the hotkeys when the key is released.
if (wait_for_keyup := (P2="" && SubStr(this_hotkey,-2) != " up"))
P2 = On ; Explicit "on" in case hotkey exists but is disabled.
Hotkey, IfWinNotActive, ahk_group HotkeyExclude
Hotkey_Params(hotkeys, P2)
if (wait_for_keyup)
{
if (!RegExMatch(this_hotkey, key_regex, m) || GetKeyState(m1)="") {
MsgBox, % "Error retrieving primary key of hotkey in Hotkeys().`n"
. "`tHotkey: " this_hotkey "`n"
. "`tResult: " m1
. "`nPlease inform Lexikos. Tip: Press Ctrl+C to copy this message."
return
}
KeyWait, %m1%
Hotkey_Params(hotkeys, "Off")
; A_ThisHotkey: "The key name of the *most recently executed* hotkey"
;if(some other hotkey was executed during KeyWait)
if (this_hotkey = A_ThisHotkey)
goto HC_SendThisHotkeyAndReturn
}
return
HC_SendThisHotkey:
HC_SendThisHotkeyAndReturn:
if ! InStr(this_hotkey, "~")
if (RegExMatch(this_hotkey, key_regex, m) && GetKeyState(m1)!="") {
Hotkey, %this_hotkey%, Off
Send {Blind}{%m1%}
Hotkey, %this_hotkey%, On
}
return
}
; Hotkey_Params( line [, Options ] )
; Associates a hotkey with a parameter string.
;
; Expects a newline(`n)-delimited list of hotkeys in the form:
; Hotkey:: LabelName, Params
;
; Note:
; - Spaces are optional.
; - As with hotkey labels, there should be no space between 'Hotkey' and '::'.
; - Unlike the Hotkey command, LabelName MUST NOT be omitted.
; - Params MUST NOT contain a newline character (`n).
; - Params may contain zero or more commas.
; - , (comma) is supported as a hotkey.
; - Unlike the Hotkey command, 'Toggle' should be specified in the Options, not as a label.
;
; Returns the number of hotkeys successfully enabled/disabled.
;
Hotkey_Params(line, Options="")
{
static List ; List of hotkeys and associated labels + parameters.
count = 0
; Note: The parsing loop operates on a temporary copy of 'line',
; so 'line' can be (and is) reused within the loop.
Loop, Parse, line, `n, %A_Space%%A_Tab%
{
; Clear ErrorLevel in case UseErrorLevel is (not) specified.
ErrorLevel =
if ! RegExMatch(A_LoopField, "^\s*(?<Hotkey>.+?)::\s*(?<Label>.+?)(?:,\s*(?<Params>.*?))?\s*$", line)
continue
if (!IsLabel(lineLabel))
continue
if Options = Toggle ; Not supported as an option (must be Label.)
{
; Toggle hotkey. If it doesn't exist, the next line will enable it.
Hotkey, %lineHotkey%, Toggle, UseErrorLevel
; Ensure the hotkey will execute the correct label.
Hotkey, %lineHotkey%, ExecuteHotkeyWithParams, UseErrorLevel
} else
Hotkey, %lineHotkey%, ExecuteHotkeyWithParams, %Options%
; Check ErrorLevel in case UseErrorLevel was specified.
if ErrorLevel
continue
; Rebuild line to remove whitespace.
line := lineHotkey "::" lineLabel "," lineParams
; Update an existing hotkey's label + params,
temp := RegExReplace(List, "m`n)^\Q" lineHotkey "\E::.*$", line, repl, 1)
if (repl > 0)
List := temp
else ; or add a new hotkey to the list.
List .= (List ? "`n" : "") . line
count += 1
}
return count
ExecuteHotkeyWithParams:
; Local variables can't be used from the subroutine, so use a function.
Hotkey_Params_Execute(A_ThisHotkey, List)
return
}
Hotkey_Params_Execute(Hotkey, ByRef List)
{
global Params ; Parameters of hotkey currently executing.
if (RegExMatch(List, "m`n)^\Q" Hotkey "\E::(?<Label>.+?),(?<Params>.*?)$", a))
{
Params := aParams
gosub %aLabel%
}
}
;
; Tray Icon Override
;
WM_COMMAND(wParam, lParam)
{
static IsPaused, IsSuspended
Critical
id := wParam & 0xFFFF
if id in 65305,65404,65306,65403
{ ; "Suspend Hotkeys" or "Pause Script"
if id in 65306,65403 ; pause
IsPaused := ! IsPaused
else ; at this point, A_IsSuspended has not yet been toggled.
IsSuspended := ! A_IsSuspended
WindowPad_SetTrayIcon(!(IsPaused or IsSuspended))
}
}
WindowPad_SetTrayIcon(is_enabled)
{
icon := is_enabled ? "tray.ico" : "disabled.ico"
icon = %A_ScriptDir%\icons\%icon%
; avoid an error message if the icon doesn't exist
IfExist, %icon%
Menu, TRAY, Icon, %icon%,, 1
}
GetLastMinimizedWindow()
{
WinGet, w, List
Loop %w%
{
wi := w%A_Index%
WinGet, m, MinMax, ahk_id %wi%
if m = -1 ; minimized
{
lastFound := wi
break
}
}
return "ahk_id " . (lastFound ? lastFound : 0)
}
;
; Commands usable in WindowPad.ini
;
GatherWindows:
GatherWindows(Params)
return
WindowPadMove:
WindowPadMove(Params)
return
WindowScreenMove:
WindowScreenMove(Params)
return
MaximizeToggle:
MaximizeToggle(Params)
return
Hotkeys:
Hotkeys(Params)
return
Send:
Send, %Params%
return
Minimize:
if WindowPad_WinExist(Params)
PostMessage, 0x112, 0xF020 ; WM_SYSCOMMAND, SC_MINIMIZE
return
Unminimize:
if WinExist(GetLastMinimizedWindow())
WinRestore
return
Restore:
if WindowPad_WinExist(Params)
WinRestore
return
But, when I execute my script after appending that, I get the error:
Quote:
"Error at line 10098
Line Text: WindowPadInit
Error: Duplicate label
The program will now exit."
I tried taking the second instance of the "WindowPadInit and rename to WindowPadInit
2." Then, the error was:
Quote:
Error at line ....
Line text: MaximizeToggle(P)
Error: Duplicate function definition
The program will exit.
What to do to make them live together in harmony?
Thanks in advance!