From ec16fab7d2e6a5d650f9662e18e9c764d3dd5826 Mon Sep 17 00:00:00 2001 From: Chris Goepfrich Date: Thu, 20 Jul 2023 21:18:58 -0400 Subject: [PATCH 1/3] Add ability to install available versions --- inc/launcher-common.ahk | 5 +- ui-dash.ahk | 7 ++- ui-installer.ahk | 103 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 ui-installer.ahk diff --git a/inc/launcher-common.ahk b/inc/launcher-common.ahk index 039a500..f654a91 100644 --- a/inc/launcher-common.ahk +++ b/inc/launcher-common.ahk @@ -51,9 +51,10 @@ ReadHashes(path, filter?) { return filemap } -GetUsableAutoHotkeyExes() { +; Added override to refresh from csv after a new version has been installed +GetUsableAutoHotkeyExes(reset := false) { static files - if IsSet(files) { + if IsSet(files) && !reset { trace '![Launcher] returning hashes again' return files } diff --git a/ui-dash.ahk b/ui-dash.ahk index b7a2109..82bc048 100644 --- a/ui-dash.ahk +++ b/ui-dash.ahk @@ -11,6 +11,7 @@ #include ui-launcherconfig.ahk #include ui-editor.ahk #include ui-newscript.ahk +#include ui-installer.ahk DashRegKey := 'HKCU\Software\AutoHotkey\Dash' @@ -43,8 +44,8 @@ class AutoHotkeyDashGui extends AutoHotkeyUxGui { , "Launch settings", "Configure how .ahk files are opened") lv.Add("Icon" addIcon("notepad.exe", 1) , "Editor settings", "Set your default script editor") - ; lv.Add("Icon" addIcon("mmc.exe") - ; , "Maintenance", "Repair settings or add/remove versions") + lv.Add("Icon" addIcon("mmc.exe") + , "Maintenance", "Repair settings or add/remove versions") ; lv.Add(, "Auto-start", "Run scripts automatically at logon") ; lv.Add(, "Downloads", "Get related tools") @@ -129,6 +130,8 @@ class AutoHotkeyDashGui extends AutoHotkeyUxGui { LauncherConfigGui.Show() case "Editor": DefaultEditorGui.Show() + case "Maintenance": + VersionInstallerGui.Show() } } diff --git a/ui-installer.ahk b/ui-installer.ahk new file mode 100644 index 0000000..dd8a17d --- /dev/null +++ b/ui-installer.ahk @@ -0,0 +1,103 @@ +#requires AutoHotkey v2.0 + +#NoTrayIcon +#SingleInstance Off + +#include inc\common.ahk +#include inc\ui-base.ahk +#include inc\launcher-common.ahk + +class VersionInstallerGui extends AutoHotkeyUxGui { + availableVersions := [] + installedVersions := Map() + + __new() { + super.__new('Install AutoHotkey Versions') + + il := IL_Create(,, false) + IL_Add(il, 'shell32.dll', 297) ; green checkmark + + availableLV := this.addListView('w200 r6 Section vAvailable', ['Latest Versions']) + DllCall('uxtheme\SetWindowTheme', 'ptr', availableLV.hwnd, 'wstr', 'Explorer', 'ptr', 0) + availableLV.ModifyCol(1, 'Logical') + availableLV.SetImageList(il, 1) + availableLV.OnEvent('ContextMenu', 'Menu') + availableLV.Add('Icon0', 'Loading...') + + installedLV := this.addListView('wp r6 ys vInstalled', ['Versions Installed']) + DllCall('uxtheme\SetWindowTheme', 'ptr', installedLV.hwnd, 'wstr', 'Explorer', 'ptr', 0) + installedLV.ModifyCol(1, 'Logical') + installedLV.Add('', 'Loading...') + + installedLV.ModifyCol(1, 'Sort AutoHdr') + } + + ; lookup latest available versions for each minor version + getAvailableVersions() { + req := ComObject('Msxml2.XMLHTTP') + req.open('GET', 'https://www.autohotkey.com/download/versions.txt') + req.onreadystatechange := handleResponse + req.send() + + handleResponse() { + if (req.readyState == 4 && req.status == 200) { + this.availableVersions := StrSplit(trim(req.responseText, ' `t`r`n'), '`n', '`r') + this.listAvailableVersions() + } + } + } + + ; display available versions + listAvailableVersions() { + this['Available'].Delete() + + for version in this.availableVersions { + icon := 'Icon' . (this.installedVersions.Has(version)) + this['Available'].Add(icon, version) + } + } + + ; Discover and cache installed versions to determine if any of the available versions are already installed + listInstalledVersions(refresh := false) { + this.installedVersions.Clear() + this['Installed'].Delete() + + for exe, info in GetUsableAutoHotkeyExes(refresh) { + if (!this.installedVersions.Has(info.Version)) { + this.installedVersions[info.Version] := info.Version + this['Installed'].Add('', info.Version) + } + } + } + + ; version string and the row number from the available version's listview + installVersion(v, item) { + ; is install-version.ahk supposed to pass a non-zero ExitCode to ExitApp when 'abort' is called? + if (RunWait(Format('"{}" /script "{}\install-version.ahk" "{}"', A_AhkPath, A_ScriptDir, v)) != 0) { + MsgBox('A problem was encountered while attempting to install AutoHotkey version: ' . v, 'Installation Issue') + } + else { + ; update installed item to show checkmark and update list of intalled versions + this['Available'].Modify(item, 'Icon1') + this.listInstalledVersions(true) + } + } + + Menu(ctrl, item, IsRightClick, X, Y) { + if (item) { + version := this['Available'].GetText(item) + managerMenu := Menu() + managerMenu.Add('Install ' . version, (*) => this.installVersion(version, item)) + managerMenu.show(x, y) + } + } + + ; override show to automatically lookup versions once gui is shown + ; didn't want to delay showing the gui due to synchronous operation + Show(opts?) { + super.Show(opts?) + + this.listInstalledVersions() + this.getAvailableVersions() + } +} From 3b1845568825d451494daa8c5bd62d162cf58a67 Mon Sep 17 00:00:00 2001 From: kczx3 Date: Thu, 3 Aug 2023 21:54:18 -0400 Subject: [PATCH 2/3] Revert "Add ability to install available versions" This reverts commit ec16fab7d2e6a5d650f9662e18e9c764d3dd5826. --- inc/launcher-common.ahk | 5 +- ui-dash.ahk | 7 +-- ui-installer.ahk | 103 ---------------------------------------- 3 files changed, 4 insertions(+), 111 deletions(-) delete mode 100644 ui-installer.ahk diff --git a/inc/launcher-common.ahk b/inc/launcher-common.ahk index f654a91..039a500 100644 --- a/inc/launcher-common.ahk +++ b/inc/launcher-common.ahk @@ -51,10 +51,9 @@ ReadHashes(path, filter?) { return filemap } -; Added override to refresh from csv after a new version has been installed -GetUsableAutoHotkeyExes(reset := false) { +GetUsableAutoHotkeyExes() { static files - if IsSet(files) && !reset { + if IsSet(files) { trace '![Launcher] returning hashes again' return files } diff --git a/ui-dash.ahk b/ui-dash.ahk index 82bc048..b7a2109 100644 --- a/ui-dash.ahk +++ b/ui-dash.ahk @@ -11,7 +11,6 @@ #include ui-launcherconfig.ahk #include ui-editor.ahk #include ui-newscript.ahk -#include ui-installer.ahk DashRegKey := 'HKCU\Software\AutoHotkey\Dash' @@ -44,8 +43,8 @@ class AutoHotkeyDashGui extends AutoHotkeyUxGui { , "Launch settings", "Configure how .ahk files are opened") lv.Add("Icon" addIcon("notepad.exe", 1) , "Editor settings", "Set your default script editor") - lv.Add("Icon" addIcon("mmc.exe") - , "Maintenance", "Repair settings or add/remove versions") + ; lv.Add("Icon" addIcon("mmc.exe") + ; , "Maintenance", "Repair settings or add/remove versions") ; lv.Add(, "Auto-start", "Run scripts automatically at logon") ; lv.Add(, "Downloads", "Get related tools") @@ -130,8 +129,6 @@ class AutoHotkeyDashGui extends AutoHotkeyUxGui { LauncherConfigGui.Show() case "Editor": DefaultEditorGui.Show() - case "Maintenance": - VersionInstallerGui.Show() } } diff --git a/ui-installer.ahk b/ui-installer.ahk deleted file mode 100644 index dd8a17d..0000000 --- a/ui-installer.ahk +++ /dev/null @@ -1,103 +0,0 @@ -#requires AutoHotkey v2.0 - -#NoTrayIcon -#SingleInstance Off - -#include inc\common.ahk -#include inc\ui-base.ahk -#include inc\launcher-common.ahk - -class VersionInstallerGui extends AutoHotkeyUxGui { - availableVersions := [] - installedVersions := Map() - - __new() { - super.__new('Install AutoHotkey Versions') - - il := IL_Create(,, false) - IL_Add(il, 'shell32.dll', 297) ; green checkmark - - availableLV := this.addListView('w200 r6 Section vAvailable', ['Latest Versions']) - DllCall('uxtheme\SetWindowTheme', 'ptr', availableLV.hwnd, 'wstr', 'Explorer', 'ptr', 0) - availableLV.ModifyCol(1, 'Logical') - availableLV.SetImageList(il, 1) - availableLV.OnEvent('ContextMenu', 'Menu') - availableLV.Add('Icon0', 'Loading...') - - installedLV := this.addListView('wp r6 ys vInstalled', ['Versions Installed']) - DllCall('uxtheme\SetWindowTheme', 'ptr', installedLV.hwnd, 'wstr', 'Explorer', 'ptr', 0) - installedLV.ModifyCol(1, 'Logical') - installedLV.Add('', 'Loading...') - - installedLV.ModifyCol(1, 'Sort AutoHdr') - } - - ; lookup latest available versions for each minor version - getAvailableVersions() { - req := ComObject('Msxml2.XMLHTTP') - req.open('GET', 'https://www.autohotkey.com/download/versions.txt') - req.onreadystatechange := handleResponse - req.send() - - handleResponse() { - if (req.readyState == 4 && req.status == 200) { - this.availableVersions := StrSplit(trim(req.responseText, ' `t`r`n'), '`n', '`r') - this.listAvailableVersions() - } - } - } - - ; display available versions - listAvailableVersions() { - this['Available'].Delete() - - for version in this.availableVersions { - icon := 'Icon' . (this.installedVersions.Has(version)) - this['Available'].Add(icon, version) - } - } - - ; Discover and cache installed versions to determine if any of the available versions are already installed - listInstalledVersions(refresh := false) { - this.installedVersions.Clear() - this['Installed'].Delete() - - for exe, info in GetUsableAutoHotkeyExes(refresh) { - if (!this.installedVersions.Has(info.Version)) { - this.installedVersions[info.Version] := info.Version - this['Installed'].Add('', info.Version) - } - } - } - - ; version string and the row number from the available version's listview - installVersion(v, item) { - ; is install-version.ahk supposed to pass a non-zero ExitCode to ExitApp when 'abort' is called? - if (RunWait(Format('"{}" /script "{}\install-version.ahk" "{}"', A_AhkPath, A_ScriptDir, v)) != 0) { - MsgBox('A problem was encountered while attempting to install AutoHotkey version: ' . v, 'Installation Issue') - } - else { - ; update installed item to show checkmark and update list of intalled versions - this['Available'].Modify(item, 'Icon1') - this.listInstalledVersions(true) - } - } - - Menu(ctrl, item, IsRightClick, X, Y) { - if (item) { - version := this['Available'].GetText(item) - managerMenu := Menu() - managerMenu.Add('Install ' . version, (*) => this.installVersion(version, item)) - managerMenu.show(x, y) - } - } - - ; override show to automatically lookup versions once gui is shown - ; didn't want to delay showing the gui due to synchronous operation - Show(opts?) { - super.Show(opts?) - - this.listInstalledVersions() - this.getAvailableVersions() - } -} From b9f5936cccf8306256984deed3c53fb3fad4d8cf Mon Sep 17 00:00:00 2001 From: kczx3 Date: Thu, 3 Aug 2023 21:55:50 -0400 Subject: [PATCH 3/3] Add update available banner to the Dash --- ui-dash.ahk | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 4 deletions(-) diff --git a/ui-dash.ahk b/ui-dash.ahk index b7a2109..2377373 100644 --- a/ui-dash.ahk +++ b/ui-dash.ahk @@ -11,13 +11,14 @@ #include ui-launcherconfig.ahk #include ui-editor.ahk #include ui-newscript.ahk +#include install.ahk DashRegKey := 'HKCU\Software\AutoHotkey\Dash' class AutoHotkeyDashGui extends AutoHotkeyUxGui { __new() { super.__new("AutoHotkey Dash") - + lv := this.AddListMenu('vLV LV0x40 w250', ["Name", "Desc"]) lv.OnEvent("Click", "ItemClicked") lv.OnEvent("ItemFocus", "ItemFocused") @@ -49,13 +50,14 @@ class AutoHotkeyDashGui extends AutoHotkeyUxGui { ; lv.Add(, "Downloads", "Get related tools") lv.AutoSize() - lv.GetPos(,,, &h) + lv.GetPos(, &y, &w, &h) if !RegRead(DashRegKey, 'SuppressIntro', false) { this.SetFont('s12') this.AddText('yp x+m', "Welcome!") this.SetFont('s9') - this.AddText('xp', "This is the Dash. It provides access to tools, settings and help files.") + dashIntro := this.AddText('xp', "This is the Dash. It provides access to tools, settings and help files.") + dashIntro.GetPos(,, &dw) this.AddText('xp', "To learn how to use AutoHotkey, refer to:") this.AddLink('xp', " ( @@ -71,9 +73,25 @@ class AutoHotkeyDashGui extends AutoHotkeyUxGui { checkBox.GetPos(,,, &hc) checkBox.Move(, h - hc) checkBox.OnEvent('Click', 'SetIntroPref') + + } + + if (this.newVersion := IsUpdateAvailable()) { + ; ensure the background spans the entire width of the window + fullW := (w + (dw ?? 0) + this.MarginX * (2 + IsSet(dw))) + this.AddText(Format('vUpdateBanner Backgroundb8e2e7 x0 y{} w{} h30', h + this.MarginY, fullW)) + + ; SysLink controls do not support transparent backgrounds it seems + updateLink := this.AddLink('vUpdateLink yp+5 Backgroundb8e2e7', Format('Update available to: {}', this.newVersion)) + updateLink.OnEvent("Click", 'UpdateVersion') + updateLink.GetPos(,, &uw, &uh) + updateLink.Move(fullW // 2 - uw // 2) + + ; remove the margin after the calculations otherwise the background text control won't ever reach the right edge + this.MarginX := 0 } - this.Show("Hide h" (h + this.MarginY*2)) + this.Show("Hide h" (y + h + (uh ?? 0) + this.MarginY * (1 + IsSet(uh)))) } LinkClicked(ctrl, id, href) { @@ -136,6 +154,18 @@ class AutoHotkeyDashGui extends AutoHotkeyUxGui { static WM_CHANGEUISTATE := 0x127 ; 295 SendMessage WM_CHANGEUISTATE, 0x10001, 0, lv } + + UpdateVersion(*) { + if (RunWait(Format('"{}" /script "{}\install-version.ahk" "{}"', A_AhkPath, A_ScriptDir, this.newVersion))) { + ; Error installing + } + else { + this.GetPos(,,, &h) + this['UpdateLink'].Enabled := false + this['UpdateBanner'].GetPos(,,, &uh) + this.Show("h" . (h - uh)) + } + } } ShowHelpFile() { @@ -197,4 +227,55 @@ ShowHelpFile() { openIt(f, *) => Run(f) } +; lookup latest available versions for each minor version +getAvailableVersions() { + req := ComObject('Msxml2.XMLHTTP') + req.open('GET', 'https://www.autohotkey.com/download/versions.txt', false) + req.send() + + if (req.status != 200) { + return false + } + + try return StrSplit(trim(req.responseText, ' `t`r`n'), '`n', '`r') + catch { + return false + } +} + +; Discover and cache installed versions to determine if any of the available versions are already installed +findHightestVersionInstalled(refresh := false) { + highestVersion := "0.0.0" + inst := Installation() + inst.ResolveInstallDir() + versions := inst.GetComponents() + + for version in versions { + if (VerCompare(highestVersion, version) < 0) { + highestVersion := version + } + } + + return highestVersion +} + +IsUpdateAvailable() { + verInstalled := findHightestVersionInstalled(true) + verAvailable := getAvailableVersions() + + if (!verAvailable) { + return false + } + + verToSuggest := "" + for v in verAvailable { + if (VerCompare(v, verInstalled) > 0) { + verToSuggest := v + break ; stop at the first version that's greater than the highest installed + } + } + + return verToSuggest +} + AutoHotkeyDashGui.Show()