Well Shove-It has been shoving windows back on to the screen for the last 20 years. Once described as one of "10 minor Windows utilities I can't do without" (by some guy on the Internet). The only problem is, the person that made Shove-it went away. It has been unavailable for ~10 years. But now it is back!!!Shove-It makes program windows behave by keeping them from going off the screen. It also keeps them from slipping under the Taskbar when you have the Taskbar at the top, where it surely belongs.
With Shove-it (AHK), most of the functionality of the old Shove-it has returned.
Code: Select all
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
#SingleInstance force
; #Persistent
; #InstallKeybdHook ; needed for $ HotKey modifier (no trigger self) and others (e.g, Inactivity)
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
/*
;==============================================================================
;========== What This Script Does ============================================
;==============================================================================
;
; Shove-It_AHK
;
; Takes windows that are off the screen and shoves them back on the screen (resizing if needed).
; In multi-monitor settings, shoves windows that bridges screens back onto 1 screen.
;
; The original Shove-It (v1.5a) was created by Phord Software in ~1997
;
; Their website (http://www.phord.com) is now defunct, but their software -- written for the age of Win95 -- still runs well on Win10. I have used it daily on my computers for the last 20 years. (Good return on the registration fee there.)
;
; In a spell of procrastination or boredom, I felt the urge to re-write Shove-It using AutoHotKey. Most of the original features are implemented in this version (no quick-drag or windoids). Actually, with the Phrod website down and the Help files in a format Win10 doesn’t use (*.HLP) I really don’t know how the features I don’t use work. I could fire up a Win7 VM or decompile the HLP file, but I just don’t care that much. After all, I don’t use those features and my testing regime is “For a week, I used it the way I normally use Shove-it, and it seemed to work” (not a formal system).
; I have no idea why I felt the need to re-write a perfectly functional program, but I did and it was fun.
;
; (c) 2018, Ahbi Santini
;
;==============================================================================
*/
; =========================================
; ===== Includes
; =========================================
;BeginRegion
; #Include %A_ScriptDir%\Library\Library.ahk
; #Include %A_ScriptDir%\Library\HtmDlg.ahk
;EndRegion
; =========================================
; ===== Configuration Variables
; =========================================
;BeginRegion
; none outside the INIfile
; See Main() below
;EndRegion
; ------------------------------------------------------------------------
; Set up SystemTray Menu
;BeginRegion
FileGetTime, ScriptLastModified, %A_ScriptFullPath%, M
FormatTime, ScriptLastModifiedX, %ScriptLastModified%
trayTipString = ; Only the first 127 characters of Text are displayed
( LTrim
---- %A_ScriptName% ----
Shoves windows that are off the screen back on the screen.
Last modified:
%ScriptLastModifiedX%
)
Menu, Tray, Tip, %trayTipString% ; Create system tray mouse-over tooltip
; Set script icon (if there is one; icon name is same as the script's name w/ *.ico extension)
IconName := getSupportFilename("ico")
IfExist %IconName%
{
Menu, Tray, Icon, %IconName%
}
GoSub Main_CreateDebugMenu
;EndRegion
;==============================================================================
;========== Main Script ======================================================
;==============================================================================
;BeginRegion
; SetTitleMatchMode, 1
DetectHiddenText, On
; Set up the Settings variables. Everything else assumes they are already set up. So, better do that first.
global Settings := new SettingsClass()
global scriptStartTime := A_Now
; Shove things any time a window is created by hooking into SHELLHOOK
Gui +LastFound
hWnd := WinExist()
DllCall( "RegisterShellHookWindow", UInt,hWnd )
MsgNum := DllCall( "RegisterWindowMessage", Str,"SHELLHOOK" )
OnMessage( MsgNum, "ShellMessage" )
; When the script ends, perform this function
OnExit("ExitFunction")
; create some GUIs
GoSub DebugGuiCreate
; GoSub MenuHandler_ShowDebugGUI
; GoSub MenuHandler_ShowPropertiesGUI
; now do the actual work
Monitors := [{}]
Monitors := buildMonitorsArray()
Loop
{ ; check for windows that need a good shoving
isLButtonDown := GetKeyState("LButton","P")
if (!isLButtonDown) ; avoid shoving windows while you're moving the window
{
GoSub Main_TestAndShoveWindows ; the SHELLHOOK above is the reason for moving this to a subroutine. Also useful if we ever use a menu item to shove things.
}
; CPU cycle saving Sleep the loop
Sleep, Settings.shovePeriod_Secs * 1000
}
Return
;EndRegion
;==============================================================================
;========== HotKeys ==========================================================
;==============================================================================
;BeginRegion
; -------- Hotkey Definitions Reminder ----------
; # - Win
; ! - Alt
; ^ - Control
; + - Shift
; ~ - pass key command to OS
; example -- $>!v:: ; $ (no trigger self) > (right key of a pair) ---- Alt (!) + V
;EndRegion
; =========================================
; ===== Functions
; =========================================
;BeginRegion
buildMonitorsArray()
{ ; create an array of MonitorClass objects that detail every monitor in the system
Monitors := [{}]
List_MonSection = ; Debug output string
SysGet, MonitorCount, MonitorCount
SysGet, MonitorPrimary, MonitorPrimary
List_MonSection.= "Monitor Count:`t" MonitorCount "`nPrimary Monitor:`t" MonitorPrimary "`n"
Loop, %MonitorCount%
{
Monitors[A_Index] := new MonitorClass(A_Index)
List_MonSection.= "`n" Monitors[A_Index].toString()
}
return Monitors
}
buildWindowsArray(Monitors)
{ ; create an array of what windows are in existence right now
List_WinSection = ; Debug output string
Windows := [{}]
WinGet,WinList,List,,,Program Manager ; get a list of all the windows in the system
loop,%WinList%
{
CurrentHWND := WinList%A_Index%
WinGetTitle,WinTitle,ahk_id %CurrentHWND%
If WinTitle AND !InStr(List,WinTitle)
{
; create the window object
Windows[A_Index] := new WindowClass(CurrentHWND)
; figure out what monitor owns the window (if it spans 2 monitors, figure out the most owning monitor)
targetMonitor := whichMonitor(Windows[A_Index], Monitors)
Windows[A_Index].monitor := targetMonitor
; determine if the window should be shoved (skip windows that we shouldn't touch)
if (Windows[A_Index].class != "Windows.UI.Core.CoreWindow") && (Windows[A_Index].isMinMax == 0)
{
shoveFlags := determineHowToShove(Windows[A_Index], Monitors[targetMonitor])
}
else
{
shoveFlags := {}
shoveFlags.needsShove := false
}
Windows[A_Index].shoveFlags := shoveFlags
; Debug GUI output
; -------------------------------------
if (Windows[A_Index].class != "Windows.UI.Core.CoreWindow")
{
List_WinSection.= "`n`n"
List_WinSection.= Windows[A_Index].toString()
List_WinSection.="`tMonitor: " Windows[A_Index].monitor
if(Windows[A_Index].monitor > 0)
{
monTmp := Windows[A_Index].monitor
List_WinSection.= "`n`tMLeft: " Monitors[monTmp].Left "`t`tMTop: " Monitors[monTmp].Top "`tMRight: " Monitors[monTmp].Right "`tMBottom: " Monitors[monTmp].Bottom
}
else
{
List_WinSection.= "`n`tMLeft: " "---" "`t`tMTop: " "---" "`tMRight: " "---" "`tMBottom: " "---"
}
strTmp := "Do NOT Move"
if (shoveFlags.needsShove)
{
; strTmp := shoveWindow(Windows[A_Index], Monitors[targetMonitor])
List_WinSection.= "`n`tShove?: " shoveFlags.needsShove "`tReX: " shoveFlags.needsResizeX "`tReY: " shoveFlags.needsResizeY
List_WinSection.= "`n`tS->R: " shoveFlags.shoveFromLeft "`tS->L: " shoveFlags.shoveFromRight "`tS->D: " shoveFlags.shoveFromTop "`tS->U: " shoveFlags.shoveFromBottom
}
; List_WinSection.= "`n`tCmd: " strTmp
List_WinSection.= "`n`n"
}
}
}
List_WinSection.="`n"
; update the Debug GUI
List =
GuiControl, Debug: ,WinList,%List%
List := List_MonSection . List_WinSection
GuiControl, Debug: ,WinList,%List%
return Windows
}
whichMonitor(window, Monitors)
{ ; determine to which monitor a window belongs (by how much of the window is on a monitor; default Primary)
strTmp := "WinT: " . window.title "`n"
SysGet, MonitorPrimary, MonitorPrimary
bestMon := MonitorPrimary
bestPerct := 1 / 100000 ; some value greater than zero
Loop, % Monitors.length()
{
perctInMon := percentageInMonitor(window, Monitors[A_Index])
if(perctInMon > bestPerct)
{
bestPerct := perctInMon
bestMon := A_Index
}
}
return bestMon
}
percentageInMonitor(window, monitor)
{ ; compute how much of the window is within/displayed by the monitor
percentage := 0
winArea := window.Width * window.Height
subWindow := {}
; create a rectangle that is the portion of the window displayed by the monitor
subWindow.Top := greaterOf( window.Top, monitor.TopWA )
subWindow.Bottom := lesserOf( window.Bottom, monitor.BottomWA )
subWindow.Left := greaterOf( window.Left, monitor.LeftWA )
subWindow.Right := lesserOf( window.Right, monitor.RightWA )
; clean up the sub-window rectangle
subWindow.Width := subWindow.Right - subWindow.Left
if ( subWindow.Width < 0)
{
subWindow.Width := 0
}
subWindow.Height := subWindow.Bottom - subWindow.Top
if ( subWindow.Height < 0)
{
subWindow.Height := 0
}
subArea := subWindow.Width * subWindow.Height
; how much of the window is within the monitor?
percentage := subArea / winArea
; Debug output
; StrTmp.= "`nMon: " . A_Index . "`t%: " . perctInMon
; strTmp.= "`n`twT: " window.Top "`twB: " window.Bottom "`twL: " window.Left "`twR: " window.Right
; strTmp.= "`n`tmT: " monitor.TopWA "`tmB: " monitor.BottomWA "`tmL: " monitor.LeftWA "`tmR: " monitor.RightWA
; strTmp.= "`n`tsT: " subWindow.Top "`tsB: " subWindow.Bottom "`tsL: " subWindow.Left "`tsR: " subWindow.Right
; strTmp.= "`n`tsHeight: " subWindow.Height "`tsWidth: " subWindow.Width
; strTmp.= "`n`twinArea: " winArea "`tsubArea: " subArea "`t%: " . perctInMon
; infoMsgBox(StrTmp)
return percentage
}
determineHowToShove(window, targetMonitor)
{ ; create a shoveFlags object that has a bunch of bit flags that indicate which way/if a window is to be shoved/resized
shoveFlags := {}
; strTmp:= Window.Title
; strTmp.= "`nLeft: " Window.Left "`tTop: " Window.Top "`tRight: " Window.Right "`tBottom: " Window.Bottom
; strTmp.= "`nMLeft: " targetMonitor.LeftWA "`tMTop: " targetMonitor.TopWA "`tMRight: " targetMonitor.RightWA "`tMBottom: " targetMonitor.BottomWA
; infoMsgBox(strTMp, 3)
; off the Left side
if (window.Left < targetMonitor.LeftWA)
{
shoveFlags.shoveFromLeft := true
}
else
{
shoveFlags.shoveFromLeft := false
}
; off the Right side
if (window.Right > targetMonitor.RightWA)
{
shoveFlags.shoveFromRight := true
}
else
{
shoveFlags.shoveFromRight := false
}
; off the Top
if (window.Top < targetMonitor.TopWA)
{
shoveFlags.shoveFromTop := true
}
else
{
shoveFlags.shoveFromTop := false
}
; off the Bottom
if (window.Bottom > targetMonitor.BottomWA)
{
shoveFlags.shoveFromBottom := true
}
else
{
shoveFlags.shoveFromBottom := false
}
; resize flags
if (window.Width > targetMonitor.WidthWA)
{
shoveFlags.needsResizeX := true
}
else
{
shoveFlags.needsResizeX := false
}
if (window.Height > targetMonitor.HeightWA)
{
shoveFlags.needsResizeY := true
}
else
{
shoveFlags.needsResizeY := false
}
; summary flags
shoveFlags.needsShove := arrayOR([shoveFlags.shoveFromLeft, shoveFlags.shoveFromRight, shoveFlags.shoveFromTop, shoveFlags.shoveFromBottom])
; really not bit flags, which annoys me but the best place for them
shoveFlags.targetMonitorNumber := targetMonitor.number
shoveFlags.percentageInMonitor := percentageInMonitor(window, targetMonitor)
; only allow shoving if the settings allow it
shoveFlags := gateFlagsWithSettings(shoveFlags)
return shoveFlags
}
gateFlagsWithSettings(shoveFlags)
{ ; now take the raw shoveFlags object and AND it with the Settings variables
shoveFlags.shoveFromLeft := (shoveFlags.shoveFromLeft AND Settings.canShoveFromLeft)
shoveFlags.shoveFromRight := (shoveFlags.shoveFromRight AND Settings.canShoveFromRight)
shoveFlags.shoveFromTop := (shoveFlags.shoveFromTop AND Settings.canShoveFromTop)
shoveFlags.shoveFromBottom := (shoveFlags.shoveFromBottom AND Settings.canShoveFromBottom)
shoveFlags.needsResizeX := (shoveFlags.needsResizeX AND Settings.canResize)
shoveFlags.needsResizeY := (shoveFlags.needsResizeY AND Settings.canResize)
; summary flags
shoveFlags.needsShove := arrayOR([shoveFlags.shoveFromLeft, shoveFlags.shoveFromRight, shoveFlags.shoveFromTop, shoveFlags.shoveFromBottom])
if(shoveFlags.needsShove)
{
; tmpStr := "Percent IN: " shoveFlags.percentageInMonitor "`n" "Percent 2B: " Settings.percentOffBeforeShove
if ( (1 - shoveFlags.percentageInMonitor) >= (Settings.percentOffBeforeShove / 100) )
{
; tmpStr .= "`n" "SHOVE!!!"
}
else
{
shoveFlags.needsShove := false
; tmpStr .= "`n" "Ignore"
}
; infoMsgBox(tmpStr, 2)
}
return shoveFlags
}
shoveWindow(window, targetMonitor)
{ ; actually shove the window
strTmp := -1 ; set to an error value
shoveFlags := window.shoveFlags ; copy to a more manageable name
if (shoveFlags.needsShove) ; skip if it doesn't need shoving
{
; initialize to the same dimensions as the original window
WinID := window.WinID
newLeft := window.Left
newTop := window.Top
newWidth := window.Width
newHeight := window.Height
; resize
if (shoveFlags.needsResizeX)
{
newWidth:= targetMonitor.WidthWA
}
if (shoveFlags.needsResizeY)
{
newHeight:= targetMonitor.HeightWA
}
; determine how to move (set left corner)
if (shoveFlags.shoveFromLeft)
{
newLeft:= targetMonitor.LeftWA
}
else if (shoveFlags.shoveFromRight)
{
newLeft:= targetMonitor.RightWA - newWidth
}
; determine how to move (set top corner)
if (shoveFlags.shoveFromTop)
{
newTop:= targetMonitor.TopWA
}
else if (shoveFlags.shoveFromBottom)
{
newTop:= targetMonitor.BottomWA - newHeight
}
; move
strTmp := "WinMove, ahk_id " . WinID . ", , " . newLeft . ", " . newTop . ", " . newWidth ", " . newHeight
WinMove, ahk_id %WinID%, , %newLeft%, %newTop%, %newWidth%, %newHeight%
Settings.shovedAnotherWindow()
}
return strTmp
}
; helper functions
; ------------------------
greaterOf(A,B)
{
if( A >= B )
{
return A
}
else
{
return B
}
}
lesserOf(A,B)
{
if( A <= B )
{
return A
}
else
{
return B
}
}
AddCommasToNumber(numb, useDecimals=false)
{ ; re-format a number with commas (or as dictated by the locale)
; from Skan & PhiLho
; http://autohotkey.com/board/topic/41644-formatting-numbers-with-commas/?p=259900
; https://autohotkey.com/board/topic/11642-addcommas-function-solved/#entry74565
LOCALE_USER_DEFAULT = 0x400
VarSetCapacity(newNumb, 32)
DllCall("GetNumberFormat"
, "UInt", LOCALE_USER_DEFAULT ; LCID Locale
, "UInt", 0 ; DWORD dwFlags
, "Str", numb ; LPCTSTR lpValue
, "UInt", 0 ; CONST NUMBERFMT* lpFormat
, "Str", newNumb ; LPTSTR lpNumberStr
, "Int", 32) ; int cchNumber
if(!useDecimals)
{
newNumb := SubStr(newNumb,1,StrLen(newNumb) - 3)
}
return newNumb
}
arrayOR(array)
{ ; take an array of Booleans of arbitrary length and OR them together.
; Input variables
; array[] (Booleans) -> an array of Booleans (e.g., bit flags)
; Output variables
; result (Boolean) -> the OR of the array[]
For index, arrayVal in array
{
if (arrayVal)
{
return true ; at least 1 value is true
}
}
return false ; if you got here all the values were false
}
reverseArrayOrder(inArray)
{ ; take an array and re-index it such that the order is reversed (an array of [1]-[10], [1] goes to [10] and [10] goes to [1])
outArray := {}
loop, % inArray.length()
{
inArrayIndex := inArray.length() - A_Index + 1
outArray[A_Index] := inArray[inArrayIndex]
}
return outArray
}
getSupportFilename(fileExten, addDot=true)
{ ; provides a filename of a support file (e.g., ini, ico), regardless of whether the script is compiled or not
; Input variables
; fileExten -> the file extension of the support file (place in quotes)
; addDot -> should a '.' be added before fileExten? (Default=true)
; Output variables
; a filename of the script with the extension replaced with fileExten
if(fileExten != "")
{
if(addDot)
{
replacementStr = .%fileExten%
}
else
{
replacementStr = %fileExten%
}
}
else
{
replacementStr := ""
}
if(A_IsCompiled)
{
StringReplace, returnName, A_ScriptName,.exe,%replacementStr%, All
}
else
{
StringReplace, returnName, A_ScriptName,.ahk,%replacementStr%, All
}
return returnName
}
ConvertSecondsToDDHHMMSS(TimeInSeconds, ShowLeadingZeros = true, LastLeadingZero = "m")
{ ; convert seconds to Days, Hours, Minutes & Seconds (ddhhmmss)
; Input variables
; TimeInSeconds -> time in seconds
; ShowLeadingZeros -> should leading zeros be shown in te output (default = true)
; LastLeadingZero -> provides a middle ground for leaidng zeros; when ShowLeadingZeros==true, where do the leading zeros start?
; For example, a value of "m" would result in an output of "00:ss". A value of "h" would result in an output of "00:mm:ss"
; Valid values are "d", "h", and "m". Anything else defaults to "d".
; Output variables
; ddhhmmss -> time in minutes & seconds (mmss)
; from http://www.autohotkey.com/docs/commands/FormatTime.htm
time = 19990101 ; *Midnight* of an arbitrary date.
time += %TimeInSeconds%, seconds
if(TimeInSeconds >= 86400) ; > 1 day
{
FormatTime, hhmmss, %time%, HH:mm:ss ; throw away any "days" info (treat TimeInMinutes as if it is < 1440)
NumberOfDays := TimeInSeconds // 86400
NumberOfDays := Floor(NumberOfDays) ; // doesn't convert to Int as much as I'd like it to
ddhhmmss := NumberOfDays . ":" . hhmmss
if(NumberOfDays < 10)
{
ddhhmmss := "0" . NumberOfDays . ":" . hhmmss
}
}
else if(TimeInSeconds < 60) ; less than 1 minute
{
if(ShowLeadingZeros)
{
if(LastLeadingZero = "m")
{
FormatTime, ddhhmmss, %time%, '00':ss ; throw away any "days" info (treat TimeInMinutes as if it is < 1440)
}
else if(LastLeadingZero = "h")
{
FormatTime, ddhhmmss, %time%, '00:00':ss ; throw away any "days" info (treat TimeInMinutes as if it is < 1440)
}
else
{
FormatTime, ddhhmmss, %time%, '00:00:00':ss ; throw away any "days" info (treat TimeInMinutes as if it is < 1440)
}
}
else
{
ddhhmmss := TimeInSeconds ; throw away any "days" info (treat TimeInMinutes as if it is < 1440)
}
}
else if(TimeInSeconds < 3600) ; less than 1 hour
{
if(ShowLeadingZeros)
{
if(LastLeadingZero = "m")
{
FormatTime, ddhhmmss, %time%, mm:ss ; throw away any "days" info (treat TimeInMinutes as if it is < 1440)
}
else if(LastLeadingZero = "h")
{
FormatTime, ddhhmmss, %time%, '00':mm:ss ; throw away any "days" info (treat TimeInMinutes as if it is < 1440)
}
else
{
FormatTime, ddhhmmss, %time%, '00:00':mm:ss ; throw away any "days" info (treat TimeInMinutes as if it is < 1440)
}
}
else
{
FormatTime, ddhhmmss, %time%, mm:ss ; throw away any "days" info (treat TimeInMinutes as if it is < 1440)
}
}
else
{
if(ShowLeadingZeros)
{
if(LastLeadingZero = "m")
{
FormatTime, ddhhmmss, %time%, HH:mm:ss ; throw away any "days" info (treat TimeInMinutes as if it is < 1440)
}
else if(LastLeadingZero = "h")
{
FormatTime, ddhhmmss, %time%, HH:mm:ss ; throw away any "days" info (treat TimeInMinutes as if it is < 1440)
}
else
{
FormatTime, ddhhmmss, %time%, '00':HH:mm:ss ; throw away any "days" info (treat TimeInMinutes as if it is < 1440)
}
}
else
{
FormatTime, ddhhmmss, %time%, HH:mm:ss ; throw away any "days" info (treat TimeInMinutes as if it is < 1440)
}
}
return ddhhmmss ; This method is used to support more than 24 hours worth of sections.
}
; functions triggered by pre-registered events
; ----------------------------------------------------------
ShellMessage( wParam,lParam )
{ ; called when Windows creates an event (e.g., a window is created)
; not useful in this script but standard actions for ShellMessage
WinGetTitle, Title, ahk_id %lParam%
WinGetClass, Class, ahk_id %lParam% ; is a String with # first (e.g., "#123456") or an actual name
; http://msdn.microsoft.com/en-us/library/windows/desktop/ms644991%28v=vs.85%29.aspx
If ( wParam = 1 ) ; HSHELL_WINDOWCREATED := 1
{
; shove the windows
GoSub Main_TestAndShoveWindows
}
return
}
ExitFunction()
{ ; called when the script ends/closes
; this isn't the best way to do this. I should do something where the value is calculated in real-time. Probably in the get/set of the object.
; but it is fluff, and this works well enough, and I am moving on
scriptEndTime := A_Now
scriptEndTimeSave := scriptEndTime
EnvSub, scriptEndTime, %scriptStartTime%, seconds
runTimeSave := Settings.runningTime
Settings.runningTime := Settings.runningTime + scriptEndTime
; tmpStr := "Start: " scriptStartTime "`n" "End : " scriptEndTimeSave "`n" "RunTm: " runTimeSave "`n" "NewTm: " Settings.runningTime
; infoMsgBox(tmpStr)
Settings.writeINIFileOnClose()
return
}
;EndRegion
; =========================================
; ===== Subroutines / Labels
; =========================================
;BeginRegion
Main_TestAndShoveWindows:
{ ; 1/2 of the main program, test all the windows and shove them if needed
; get a list of all windows currently in the system
Windows := [{}]
Windows := buildWindowsArray(Monitors)
; now test the windows' position and shove them as needed
Loop, % Windows.length()
{
targetMonitor := Windows[A_Index].monitor
shoveWindow(Windows[A_Index], Monitors[targetMonitor]) ; both tests and shoves the window
}
}
Return
Main_CreateMiniMenu:
{ ; delete the Tray Menu and re-create it with the minimal options
Menu, Tray, DeleteAll
Menu, tray, NoStandard ; Remove all standard menu items
Menu, Tray, add, Properties, MenuHandler_ShowPropertiesGUI
Menu, Tray, Default, Properties
Menu, Tray, add ; Creates a separator line.
Menu, Tray, add, Exit Shove-It, MenuHandler_ExitApp
}
Return
Main_CreateDebugMenu:
{ ; delete the Tray Menu and re-create it with the debug options
Menu, Tray, DeleteAll
Menu, tray, NoStandard ; Remove all standard menu items
Menu, Tray, add, Properties, MenuHandler_ShowPropertiesGUI
Menu, Tray, Default, Properties
Menu, Tray, add ; Creates a separator line.
Menu, Tray, add, Write _DEFAULT_ INI values, MenuHandler_WriteDefaultVariables
Menu, Tray, add, Re-load INI values, MenuHandler_ReLoadINIVariables
Menu, Tray, add ; Creates a separator line.
Menu, SubMenu_StandardMenuItems, add
Menu, SubMenu_StandardMenuItems, DeleteAll
Menu, tray, add, Standard Menu, :SubMenu_StandardMenuItems
Menu, SubMenu_StandardMenuItems, Standard
Menu, Tray, add, Show Debug GUI, MenuHandler_ShowDebugGUI
Menu, Tray, add ; Creates a separator line.
Menu, Tray, add, Exit Shove-It, MenuHandler_ExitApp
}
Return
MenuHandler_ShowPropertiesGUI:
{ ; create and show the Properties GUI
GoSub PropertiesGuiCreate
GoSub PropertiesGuiUpdateSettings
Gui, Properties: Show, w420 h290, %A_Scriptname%
}
Return
MenuHandler_ShowDebugGUI:
{ ; show the DEBUG GUI (which is always updating with debug info about the windows, unless I changed that in a future version)
tmpHgt := A_ScreenHeight - 75
Gui, Debug: Show, w600 h%tmpHgt% X0 Y0
}
Return
MenuHandler_ExitApp:
ExitApp
Return
MenuHandler_ReLoadINIVariables:
{ ; re-reads the INI file
Settings.readINIFile()
}
Return
MenuHandler_WriteDefaultVariables:
{ ; Writes out default INI values
IniName := getSupportFilename("ini")
Settings.writeDefaultINIFile(IniName)
Settings.readINIFile()
}
Return
;EndRegion
; =========================================
; ===== GUI Subroutines / Labels
; =========================================
;BeginRegion
; Properties GUI
; --------------------------
;BeginRegion
PropertiesGuiCreate:
{
Gui, Properties: New, -MinimizeBox -MaximizeBox +Theme -DPIScale, %A_Scriptname%
Gui, Properties: Add, Button, x230 y260 w80 h20 gPropertiesButtonOK, &OK
Gui, Properties: Add, Button, x327 y260 w80 h20 gPropertiesGuiEscape, &Cancel
Gui, Properties: Add, Tab3, x10 y10 w400 h240 vTabID, General|Advanced|Statistics|About
Gui, Properties: Tab, 1 ; General options
Gui, Properties: Add, GroupBox, x50 y50 w250 h100, Shove-it from the:
Gui, Properties: Add, CheckBox, x150 y70 h20 vCheckboxTopID, Top
Gui, Properties: Add, CheckBox, x150 y125 h20 vCheckboxBottomID, Bottom
Gui, Properties: Add, CheckBox, x60 y95 h20 vCheckboxLeftID, Left
Gui, Properties: Add, CheckBox, x250 y95 h20 vCheckboxRightID, Right
Gui, Properties: Add, CheckBox, x50 y160 h23 vCheckboxResizeID, Resize windows when necessary
Gui, Properties: Add, CheckBox, x50 y185 h23 vCheckboxHideDebugID, Hide Debug menu options
; I added the hideIcon feature and then realized it was kind of pointless and annoying.
; Once hidden, you can't get to the Properties GUI to undo it. So, you have to resort to manually editing the INI file.
; Gui, Properties: Add, CheckBox, x50 y210 h23 vCheckboxHideIconID, Hide Shove-it Icon
; GuiControl, Properties: Disable, Hide Shove-it Icon
Gui, Properties: Add, CheckBox, x50 y210 h23 vCheckboxWindoidID, Shove windoids off of active window buttons
GuiControl, Properties: Disable, Shove windoids off of active window buttons ; I will probably never enable this option. I have never used this but had an odd affection for it. Hence, its survival here.
Gui, Properties: Tab, 2 ; Advanced options
Gui, Properties: Add, Text, x50 y75 h20 +0x200, Auto-shove every:
shovePeriod_Str := Settings.shovePeriod_Str
Gui, Properties: Add, DropDownList, x150 y75 w120 vDropDownListID Choose1 AltSubmit, %shovePeriod_Str%
Gui, Properties: Add, Text, x300 y75 h20 +0x200, seconds.
Gui, Properties: Add, GroupBox, x50 y125 w330 h75
Gui, Properties: Add, Slider, x55 y135 w320 TickInterval10 ToolTip Range0-100 AltSubmit vSliderVariable gPropertiesSliderEvent, SliderVariable
Gui, Properties: Add, Text, x55 y175 h20 +0x200 w320 Center vSliderTextID, Only shove windows which are at least 888 off the screen.
GoSub PropertiesSliderEvent
Gui, Properties: Tab, 3 ; Statistics
Gui, Properties: Add, Text, x50 y70 h20 +0x200 vStatisticsInstalledTextID, Installed on: Wednesday, December, 25, 2019.
Gui, Properties: Add, Text, x50 y100 h20 +0x200 vStatisticsTimeRunTextID, Running time: 1,000,000,000 days, 88 hours, and 10 minutes.
Gui, Properties: Add, Text, x50 y130 h20 +0x200 vStatisticsWinsShovedTextID, Shoved 888,888,888,888 windows so far.
Gui, Properties: Tab, 4 ; About
aboutStr =
( LTrim
The original Shove-It (v1.5a) was created by Phord Software in ~1997
`tTheir website (http://www.phord.com) is now defunct, but their software -- written for the age of Win95 -- still runs well on Win10. I have used it daily on my computers for the last 20 years. (Good return on the registration fee there.)
`tIn a spell of procrastination or boredom, I felt the urge to re-write Shove-It using AutoHotKey. Most of the original features are implemented in this version (no quick-drag or windoids). Actually, with the Phrod website down and the Help files in a format Win10 doesn’t use (*.HLP) I really don’t know how the features I don’t use work. I could fire up a Win7 VM or decompile the HLP file, but I just don’t care that much. After all, I don’t use those features and my testing regime is “For a week, I used it the way I normally use Shove-it, and it seemed to work” (not a formal system).
`tI have no idea why I felt the need to re-write a perfectly functional program, but I did and it was fun.
(c) 2018, Ahbi Santini
)
Gui, Properties: Add, Edit, w380 h200 vAboutTextID +Wrap +vScroll ReadOnly, %aboutStr%
; Gui, Properties: Add, ActiveX, x10 y10 w400 h270 vWB, Shell.Explorer ; The final parameter is the name of the ActiveX component.
; aboutMsgBox(trayTipString)
; AboutHtmlFilename := getSupportFilename("---AboutHtmDlg.htm", false)
; AboutHtmlFilename := getTempFilename(AboutHtmlFilename)
; WB.Navigate(AboutHtmlFilename) ; This is specific to the web browser control.
}
Return
PropertiesGuiUpdateSettings:
{
GuiControl, Properties: Choose, TabID, 1 ; really here for testing, so I can bring up the tab I am working on
; Tab 1
CheckboxTopID := Settings.canShoveFromTop
GuiControl, Properties: , CheckboxTopID, %CheckboxTopID%
CheckboxBottomID := Settings.canShoveFromBottom
GuiControl, Properties: , CheckboxBottomID, %CheckboxBottomID%
CheckboxLeftID := Settings.canShoveFromLeft
GuiControl, Properties: , CheckboxLeftID, %CheckboxLeftID%
CheckboxRightID := Settings.canShoveFromRight
GuiControl, Properties: , CheckboxRightID, %CheckboxRightID%
; CheckboxHideIconID := Settings.hideTrayIcon
; GuiControl, Properties: , CheckboxHideIconID, %CheckboxHideIconID%
CheckboxHideDebugID := Settings.hideDebugMenu
GuiControl, Properties: , CheckboxHideDebugID, %CheckboxHideDebugID%
CheckboxResizeID := Settings.canResize
GuiControl, Properties: , CheckboxResizeID, %CheckboxResizeID%
CheckboxWindoidID := Settings.canShoveWinoids
GuiControl, Properties: , CheckboxWindoidID, %CheckboxWindoidID%
; Tab 2
DropDownListID := Settings.shovePeriod_Numb
GuiControl, Properties: Choose, DropDownListID, %DropDownListID%
SliderVariable := Settings.percentOffBeforeShove
GuiControl, Properties: , SliderVariable, %SliderVariable%
GoSub PropertiesSliderEvent
; Tab 3
tmpVar := Settings.installedOn
FormatTime, OutputVar , %tmpVar%, LongDate
StatisticsInstalledTextID := "Installed on: " OutputVar
GuiControl, Properties: , StatisticsInstalledTextID, %StatisticsInstalledTextID%
tmpStr := ConvertSecondsToDDHHMMSS(Settings.runningTime, false)
tmpStrArr := StrSplit(tmpStr, ":")
tmpStrArr := reverseArrayOrder(tmpStrArr)
StatisticsTimeRunTextID := "Running time: "
if (tmpStrArr.length() = 4 ) ; has days
{
StatisticsTimeRunTextID .= AddCommasToNumber(tmpStrArr[4]) " days, "
}
if (tmpStrArr.length() >= 3 ) ; has hours
{
StatisticsTimeRunTextID .= tmpStrArr[3] " hours, and "
}
if (tmpStrArr.length() >= 2 ) ; has minutes
{
StatisticsTimeRunTextID .= tmpStrArr[2] " minutes."
}
GuiControl, Properties: , StatisticsTimeRunTextID, %StatisticsTimeRunTextID%
StatisticsWinsShovedTextID := "Shoved " AddCommasToNumber(Settings.numbWindowsShoved) " windows so far."
GuiControl, Properties: , StatisticsWinsShovedTextID, %StatisticsWinsShovedTextID%
}
Return
PropertiesSliderEvent:
{
if (SliderVariable < 1)
{
GuiControl, Properties: , SliderTextID, Shove windows which are off the screen, even a bit.
}
else if (SliderVariable > 99)
{
GuiControl, Properties: , SliderTextID, Only shove windows which are totally off the screen.
}
else
{
GuiControl, Properties: , SliderTextID, Only shove windows which are at least %SliderVariable%`% off the screen.
}
}
Return
PropertiesGuiEscape:
PropertiesGuiClose:
Gui, Properties: Cancel
Return
PropertiesButtonOK:
{
Gui, Properties: Submit
Settings.canShoveFromLeft := CheckboxLeftID
Settings.canShoveFromRight := CheckboxRightID
Settings.canShoveFromTop := CheckboxTopID
Settings.canShoveFromBottom := CheckboxBottomID
Settings.canResize := CheckboxResizeID
; Settings.hideTrayIcon := CheckboxHideIconID
Settings.hideDebugMenu := CheckboxHideDebugID
Settings.canShoveWinoids := CheckboxWindoidID
Settings.shovePeriod_Numb := DropDownListID
Settings.percentOffBeforeShove := SliderVariable
Settings.writeINIFile()
GoSub PropertiesGuiClose
}
Return
; --------------------------
;EndRegion
; DEBUG GUI
; --------------------------
;BeginRegion
DebugGuiCreate:
{
Gui, Debug: New, , DEBUG: %A_Scriptname%
Gui, Debug:Add,ListBox,vWinList w580 r30
GuiControl, Debug:+HScroll,WinList
Gui Debug: +Delimiter`n
Gui Debug: +Resize
global List =
global List_MonSection =
}
Return
DebugGuiClose:
Gui, Debug: Cancel
Return
DebugGuiSize:
{ ; resize the GUI controls
if ErrorLevel = 1 ; The window has been minimized. No action needed.
return
; Otherwise, the window has been resized or maximized. Resize the Edit control to match.
NewWidth := A_GuiWidth - 20
NewHeight := A_GuiHeight - 20
GuiControl, Debug: Move, WinList, W%NewWidth% H%NewHeight%
}
Return
; --------------------------
;EndRegion
;EndRegion
; =========================================
; ===== Classes
; =========================================
;BeginRegion
class MonitorClass
{ ; Class that has all/most of the information about a given monitor
__New(aNumber=1)
{
; Input variables
; aNumber -> the monitor's ID number (e.g., 1 or 2)
SysGet, MonitorPrimary, MonitorPrimary
this._primaryMonitor := MonitorPrimary
SysGet, MonitorName, MonitorName, %aNumber%
SysGet, Monitor, Monitor, %aNumber%
SysGet, MonitorWorkArea, MonitorWorkArea, %aNumber%
this.number := aNumber
this.name := MonitorName
this.left := MonitorLeft
this.right := MonitorRight
this.top := MonitorTop
this.bottom := MonitorBottom
this.leftWA := MonitorWorkAreaLeft
this.rightWA := MonitorWorkAreaRight
this.topWA := MonitorWorkAreaTop
this.bottomWA := MonitorWorkAreaBottom
this.Width := MonitorRight - MonitorLeft
this.Height := MonitorBottom - MonitorTop
this.WidthWA := MonitorWorkAreaRight - MonitorWorkAreaLeft
this.HeightWA := MonitorWorkAreaBottom - MonitorWorkAreaTop
if ( aNumber == MonitorPrimary )
{
this.isPrimary := true
}
else
{
this.isPrimary := false
}
return this
}
toString()
{ ; output debug information as a formatted string
outStr := "Monitor:`t#" this.number "`n"
outStr .= "`tName:`t" this.name "`t" "isPrimary:`t" this.isPrimary "`n"
; Δ - file needs to be saved/encoded as Unicode/UTF-8-BOM to see the Delta symbol
tmpVal := this.left - this.leftWA
outStr .= "`tLeft:`t" this.left " (" this.leftWA " work; Δ " tmpVal ")`n"
tmpVal := this.right - this.rightWA
outStr .= "`tRight:`t" this.right " (" this.rightWA " work; Δ " tmpVal ")`n`"
tmpVal := this.top - this.topWA
outStr .= "`tTop:`t" this.top " (" this.topWA " work; Δ " tmpVal ")`n"
tmpVal := this.bottom - this.bottomWA
outStr .= "`tBottom:`t" this.bottom " (" this.bottomWA " work; Δ " tmpVal ")`n"
tmpVal := this.width - this.widthWA
outStr .= "`tWidth:`t" this.Width " (" this.WidthWA " work; Δ " tmpVal ")`n"
tmpVal := this.height - this.heightWA
outStr .= "`tHeight:`t" this.Height " (" this.HeightWA " work; Δ " tmpVal ") `n"
return outStr
}
}
class WindowClass
{ ; Class that has all/most of the information about a given window. Does not include GUI control (i.e., button-level) information
__New(WinHandle)
{
; Input variables
; WinHandle -> the window's handle/ID number (e.g., 1 or 2)
WinGetTitle,WinTitle,ahk_id %WinHandle%
this.Title := WinTitle
WinGetClass,WinClass,ahk_id %WinHandle%
this.Class := WinClass
WinGetPos, _X, _Y, _Width, _Height, ahk_id %WinHandle%
this.X := _X
this.Y := _Y
this.Width := _Width
this.Height := _Height
this.Left := _X
this.Right := _X + _Width
this.Top := _Y
this.Bottom := _Y + _Height
WinGet,_WinID,ID,ahk_id %WinHandle% ; this really should already be WinHandle but in a fit of safety
this.WinID := _WinID
WinGet,_PID,PID,ahk_id %WinHandle%
this.PID := _PID
WinGet,_ProcessName,ProcessName,ahk_id %WinHandle%
this.ProcessName := _ProcessName
WinGet,_ProcessPath,ProcessPath,ahk_id %WinHandle%
this.ProcessPath := _ProcessPath
WinGet,WinMinMax, MinMax, ahk_id %WinHandle%
this.isMinMax := WinMinMax
WinGet,WinStyle, Style, ahk_id %WinHandle%
this.Style := WinStyle
WinGet,WinExStyle, ExStyle, ahk_id %WinHandle%
this.ExStyle := WinExStyle
if (WinStyle & 0x8000000) ; 0x8000000 is WS_DISABLED.
{
this.isDisabled := true
}
else
{
this.isDisabled := false
}
if (WinStyle & 0x10000000) ; 0x10000000 is WS_VISIBLE
{
this.isVisible := true
}
else
{
this.isVisible := false
}
if (WinExStyle & 0x8) ; 0x8 is WS_EX_TOPMOST.
{
this.isAlwaysOnTop := true
}
else
{
this.isAlwaysOnTop := false
}
return this
}
toString()
{ ; output debug information as a formatted string
outStr := ""
outStr .= this.Title "`n"
outStr .= "`tClass: " this.Class "`tPID: " this.PID "`tWinID (HWND): " this.WinID "`n"
outStr .= "`tProcessName: " this.ProcessName "`n"
outStr .= "`tProcessPath: " this.ProcessPath "`n"
outStr .= "`tMin/Max: " this.isMinMax "`tDisabled: " this.isDisabled "`tVisible: " this.isVisible "`n"
outStr .= "`tOnTop: " this.isAlwaysOnTop "`t" "`tStyle: " this.Style "`t" "`tExStyle: " this.ExStyle "`n"
outStr .= "`tX: " this.X "`t`tY: " this.Y "`t" "`tWidth: " this.Width "`tHeight: " this.Height "`n"
outStr .= "`tLeft: " this.Left "`tTop: " this.Top "`t" "`tRight: " this.Right "`tBottom: " this.Bottom "`n"
return outStr
}
}
class SettingsClass
{ ; Class that handles the settings variables, read/writes them to an INI file
__New(aIniFile="", aSectionName="")
{
; Input variables
; aIniFile -> the INI file to read/write (default is A_Scriptname.INI)
; aSectionName -> the section of the INI file to use (default is A_ComputerName)
if(aIniFile="")
{
aIniFile := this.getSupportFilename("ini")
}
this.iniFile := aIniFile
if(aSectionName="")
{
this.SectionName := A_ComputerName
}
else
{
this.SectionName := aSectionName
}
this.shovePeriod_Str := "0.5|1|2|3|5" ; not in INI file
this.readINIFile()
return this
}
; Properties
; ---------------------------------------------
shovePeriod_Secs
{ ; make sure shovePeriod_Secs & shovePeriod_Str are in sync (i.e., they include each other's values)
get
{
return this._shovePeriod_Secs
}
set
{
if value is number
{
tmpStr := this.shovePeriod_Str ; this is always defined via _New()
if( !InStr(tmpStr, value) ) ; if a new value, add it to shovePeriod_Str, then sort shovePeriod_Str by period of time
{
tmpStr.= "|" value
Sort, tmpStr, N D|
this.shovePeriod_Str := tmpStr
}
}
else
{
value := 0.5 ; default to first value in shovePeriod_Str
}
return this._shovePeriod_Secs := value
}
}
shovePeriod_Numb
{ ; a pseudo-property. Really a function that pretends to be a property. The Index of shovePeriod_Secs in shovePeriod_Str
get
{
if (this.shovePeriod_Secs = "")
{
this.shovePeriod_Secs := 0.5 ; default to first value in shovePeriod_Str
}
tmpSecs := this.shovePeriod_Secs
tmpStr := this.shovePeriod_Str ; this is always defined via _New()
Loop, Parse, tmpStr, |
{
infoStr := "tmpStr: " tmpStr "`n" "Field: " A_LoopField "`n" "tmpSecs: " tmpSecs "`n" "Index: " A_Index
; infoMsgBox(infoStr, 3)
if (A_LoopField = tmpSecs)
{
return A_Index
}
}
return 1 ; default to first value in shovePeriod_Str
}
set
{
tmpStr := this.shovePeriod_Str ; this is always defined via _New()
Loop, Parse, tmpStr, |
{
if (A_Index = value)
{
return this.shovePeriod_Secs := A_LoopField
}
retVal := A_LoopField
}
return this.shovePeriod_Secs := retVal ; default to last value in shovePeriod_Str
}
}
; I added the hideIcon feature and then realized it was kind of pointless and annoying.
; Once hidden, you can't get to the Properties GUI to undo it. So, you have to resort to manually editing the INI file.
hideTrayIcon
{
get
{
return false
; return this._hideTrayIcon
}
set
{
return false ; Do nothing
; if( A_IconHidden != value ) ; has the setting changed?
; {
; if (value)
; {
; Menu, Tray, NoIcon
; }
; else
; {
; Menu, Tray, Icon
; }
; }
; return this._hideTrayIcon := value
}
}
hideDebugMenu
{ ; redraw the Tray Menu based upon the value of this property
get
{
return this._hideDebugMenu
}
set
{
if (value)
{
; I don't like calling functions outside a class definition but it is the right thing to do here
GoSub Main_CreateMiniMenu
}
else
{
; I don't like calling functions outside a class definition but it is the right thing to do here
GoSub Main_CreateDebugMenu
}
return this._hideDebugMenu := value
}
}
; Methods (of suitable importance)
; ---------------------------------------------
shovedAnotherWindow()
{ ; every time a window is shoved increment this.numbWindowsShoved and write to the INI value
this.numbWindowsShoved++
; maybe we should only write every X times something is shoved?
errLvl := this.writeINIFileValue("Number_Of_Windows_Shoved", this.numbWindowsShoved)
return errLvl
}
readINIFile(aIniFile="")
{ ; get the INI variables from the file (various default values)
; Input variables
; aIniFile -> the INI file to read/write (default is this.iniFile set in _New())
if(aIniFile="")
{
aIniFile := this.iniFile
}
retValF := FileExist(aIniFile) ; indicate if the INI file already existed (and hopefully the default values where NOT used)
IniRead, OutputVarSectionNames, aIniFile
secVar := this.SectionName
retValS := InStr(OutputVarSectionNames, %secVar%) ; is there the Section we need?
retVal := (retValF AND retValS)
if (!retVal) ; write an INI file if we don't have one with the needed Section
{
this.writeDefaultINIFile(aIniFile)
}
this.canShoveFromLeft := this.readINIFileValue("Shove_From_Left", true, aIniFile)
this.canShoveFromRight := this.readINIFileValue("Shove_From_Right", true, aIniFile)
this.canShoveFromTop := this.readINIFileValue("Shove_From_Top", true, aIniFile)
this.canShoveFromBottom := this.readINIFileValue("Shove_From_Bottom", true, aIniFile)
this.canResize := this.readINIFileValue("Resize_If_Needed", true, aIniFile)
this.hideTrayIcon := this.readINIFileValue("Hide_Tray_Icon", false, aIniFile)
this.hideDebugMenu := this.readINIFileValue("Hide_Debug_Menu", true, aIniFile)
this.canShoveWinoids := this.readINIFileValue("Shove_Winoids", false, aIniFile)
this.shovePeriod_Secs := this.readINIFileValue("AutoShove_Seconds", 0.5, aIniFile)
this.percentOffBeforeShove := this.readINIFileValue("Percentage_Off_Before_Shove", 0.000001, aIniFile)
this.runningTime := this.readINIFileValue("Running_Time", 0, aIniFile)
this.numbWindowsShoved := this.readINIFileValue("Number_Of_Windows_Shoved", 0, aIniFile)
defInstallTime := A_Now
this.installedOn := this.readINIFileValue("Installed_On", defInstallTime, aIniFile)
if (defInstallTime = this.installedOn)
{
; Installed_On is the one value that is never written outside of writeDefINI(). So, write it here too.
this.writeINIFileValue("Installed_On", defInstallTime, aIniFile)
}
return retVal
}
writeINIFile(aIniFile="")
{ ; write user changeable settings to the INI file
; Input variables
; aIniFile -> the INI file to read/write (default is this.iniFile set in _New())
; Output variables
; errorLevel (Boolean) -> did an error occur while writing to the INI file?
if(aIniFile="")
{
aIniFile := this.iniFile
}
errLvl := []
errLvl[1] := this.writeINIFileValue("Shove_From_Left", this.canShoveFromLeft, aIniFile)
errLvl[2] := this.writeINIFileValue("Shove_From_Right", this.canShoveFromRight, aIniFile)
errLvl[3] := this.writeINIFileValue("Shove_From_Top", this.canShoveFromTop, aIniFile)
errLvl[4] := this.writeINIFileValue("Shove_From_Bottom", this.canShoveFromBottom, aIniFile)
errLvl[5] := this.writeINIFileValue("Resize_If_Needed", this.canResize, aIniFile)
; errLvl[6] := this.writeINIFileValue("Hide_Tray_Icon", this.hideTrayIcon, aIniFile)
errLvl[6] := this.writeINIFileValue("Hide_Debug_Menu", this.hideDebugMenu, aIniFile)
errLvl[7] := this.writeINIFileValue("Shove_Winoids", this.canShoveWinoids, aIniFile)
errLvl[8] := this.writeINIFileValue("AutoShove_Seconds", this.shovePeriod_Secs, aIniFile)
errLvl[9] := this.writeINIFileValue("Percentage_Off_Before_Shove", this.percentOffBeforeShove, aIniFile)
retVal := this.arrayOR(errLvl) ; indicate if any error occurred
return retVal
}
writeDefaultINIFile(aIniFile="")
{ ; write default values ot the INI file, including the non-user changeable ones (i.e., installed_on)
; Input variables
; aIniFile -> the INI file to read/write (default is this.iniFile set in _New())
; Output variables
; errorLevel (Boolean) -> did an error occur while writing to the INI file?
if(aIniFile="")
{
aIniFile := this.iniFile
}
secVar := this.SectionName
IniDelete, %aIniFile%, %secVar%
errLvl := []
errLvl[1] := this.writeINIFileValue("Shove_From_Left", 1, aIniFile)
errLvl[2] := this.writeINIFileValue("Shove_From_Right", 1, aIniFile)
errLvl[3] := this.writeINIFileValue("Shove_From_Top", 1, aIniFile)
errLvl[4] := this.writeINIFileValue("Shove_From_Bottom", 1, aIniFile)
errLvl[5] := this.writeINIFileValue("Resize_If_Needed", 1, aIniFile)
; errLvl[6] := this.writeINIFileValue("Hide_Tray_Icon", 0, aIniFile)
errLvl[6] := this.writeINIFileValue("Hide_Debug_Menu", 1, aIniFile)
errLvl[7] := this.writeINIFileValue("Shove_Winoids", 0, aIniFile)
errLvl[8] := this.writeINIFileValue("AutoShove_Seconds", 0.5, aIniFile)
errLvl[9] := this.writeINIFileValue("Percentage_Off_Before_Shove", 0, aIniFile)
errLvl[10] := this.writeINIFileValue("Installed_On", A_Now, aIniFile)
errLvl[11] := this.writeINIFileValue("Running_Time", 0, aIniFile)
errLvl[12] := this.writeINIFileValue("Number_Of_Windows_Shoved", 0, aIniFile)
retVal := this.arrayOR(errLvl) ; indicate if any error occurred
return retVal
}
writeINIFileOnClose()
{ ; write non-user editable values on the close of the script.
; Output variables
; errorLevel (Boolean) -> did an error occur while writing to the INI file?
errLvl := []
errLvl[1] := this.writeINIFileValue("Running_Time", this.runningTime, aIniFile)
errLvl[2] := this.writeINIFileValue("Number_Of_Windows_Shoved", this.numbWindowsShoved, aIniFile)
retVal := this.arrayOR(errLvl) ; indicate if any error occurred
return retVal
}
; Methods (helpers)
; ---------------------------------------------
writeINIFileValue(aKey, aValue, aIniFile="", aSection="")
{ ; a wrapper to IniWrite
; Input variables
; aKey/aValue -> the key/value to write to the INI file
; aIniFile -> the INI file to read/write (default is this.iniFile set in _New())
; aSectionName -> the section of the INI file to use (default is A_ComputerName)
; Output variables
; errorLevel (Boolean) -> did an error occur while writing to the INI file?
if(aIniFile="")
{
aIniFile := this.iniFile
}
if(aSection="")
{
aSection := this.SectionName
}
IniWrite, %aValue%, %aIniFile%, %aSection%, %aKey%
return ErrorLevel
}
readINIFileValue(aKey, aDefValue, aIniFile="", aSection="")
{ ; a wrapper to IniRead (mostly a workaround for the fact that you can't use obj.var syntax in IniRead)
; Input variables
; aKey -> the key to read from the INI file
; aDefValue -> if the aKey cannot be read, supply this as the returned value
; aIniFile -> the INI file to read/write (default is this.iniFile set in _New())
; aSectionName -> the section of the INI file to use (default is A_ComputerName)
; Output variables
; Value -> the value associated with the aKey
if(aIniFile="")
{
aIniFile := this.iniFile
}
if(aSection="")
{
aSection := this.SectionName
}
IniRead, OutVar, %aIniFile%, %aSection%, %aKey%, %aDefValue%
return OutVar
}
getSupportFilename(fileExten, addDot=true)
{ ; provides a filename of a support file (e.g., ini, ico), regardless of whether the script is compiled or not
; Input variables
; fileExten -> the file extension of the support file (place in quotes)
; addDot -> should a '.' be added before fileExten? (Default=true)
; Output variables
; a filename of the script with the extension replaced with fileExten
if(fileExten != "")
{
if(addDot)
{
replacementStr = .%fileExten%
}
else
{
replacementStr = %fileExten%
}
}
else
{
replacementStr := ""
}
if(A_IsCompiled)
{
StringReplace, returnName, A_ScriptName,.exe,%replacementStr%, All
}
else
{
StringReplace, returnName, A_ScriptName,.ahk,%replacementStr%, All
}
return returnName
}
arrayOR(array)
{ ; take an array of Booleans of arbitrary length and OR them together.
; Input variables
; array[] (Booleans) -> an array of Booleans (e.g., bit flags)
; Output variables
; result (Boolean) -> the OR of the array[]
For index, arrayVal in array
{
if (arrayVal)
{
return true ; at least 1 value is true
}
}
return false ; if you got here all the values were false
}
}
;EndRegion
/*
;==============================================================================
;========== Version History ===================================================
;==============================================================================
; v0.5 2018-03-07
; * basic information gathering and display in the Debug GUI
;
; v1.0 2018-04-24
; * all major functions present
; * seems to work
;
; v1.1 2018-04-27
; * pulled out Hide Icon
; * added Hide Debug menu (moved Menu create to subroutines)
; * cleaned up code, moved anything called from the Library.ahk file to this file
; * I actually did try to pull up the original HLP files. They appear to be essentially empty (no text of note, about the same as trayTipString).
;
;==============================================================================
*/