Class Main_Class extends ThumbWindow { Static WM_DESTROY := 0x02, WM_SIZE := 0x05, WM_NCCALCSIZE := 0x83, WM_NCHITTEST := 0x84, WM_NCLBUTTONDOWN := 0xA1, WM_SYSKEYDOWN := 0x104, WM_SYSKEYUP := 0x105, WM_MOUSEMOVE := 0x200, WM_LBUTTONDOWN := 0x201, WM_LBUTTONUP := 0x0202, WM_RBUTTONDOWN := 0x0204, WM_RBUTTONUP := 0x0205, WM_KEYDOWN := 0x0100, WM_MOVE := 0x03, WM_MOUSELEAVE := 0x02A2 ;! This key is for the internal Hotkey to bring the Window in forgeround ;! it is possible this key needs to be changed if Windows updates and changes the unused virtual keys static virtualKey := "vk0xE8" LISTENERS := [ Main_Class.WM_LBUTTONDOWN, Main_Class.WM_RBUTTONDOWN ;Main_Class.WM_SIZE, ;Main_Class.WM_MOVE ] EVEExe := "Ahk_Exe exefile.exe" ; Values for WM_NCHITTEST ; Size from the invisible edge for resizing border_size := 4 HT_VALUES := [[13, 12, 14], [10, 1, 11], [16, 15, 17]] ;### Predifining Arrays and Maps ######### EventHooks := Map() ThumbWindows := {} ThumbHwnd_EvEHwnd := Map() AutoForwardGroups := [] AutoForwardEnabled := Map() ; Map of group index => enabled state AutoForwardVisited := Map() ; Map of group index => array of visited window names AutoForwardToggleMode := Map() ; Map of group index => toggle mode setting AutoForwardProcessedWindow := 0 ; Track which window hwnd already got its input processed AutoForwardToggleVKCodes := [] ; Array of VK codes for toggle hotkeys AutoForwardTimer := 0 ; Timer reference for TriggerAutoForward AutoForwardRunning := false ; Flag to prevent re-entrancy AutoForwardTimerPending := false ; Flag to prevent multiple timers from being scheduled CharacterNameOverlay := 0 ; GUI overlay for displaying current character name CharacterNameOverlayLastText := "" ; Track last displayed text to prevent flashing CharacterNameOverlayLastColor := "" ; Track last color to detect changes LastActiveCharacter := "" ; Track last active character for AddCharacterToGroup CharacterNameOverlayX := 0 ; Stored X position for dragging CharacterNameOverlayY := 0 ; Stored Y position for dragging CharacterNameOverlayDragging := false ; Flag for drag state CharacterNameOverlayOffsetX := 0 ; Drag offset X CharacterNameOverlayOffsetY := 0 ; Drag offset Y CharacterGroupPositions := Map() ; Track last known positions: groupName -> characterName -> index __New() { This._JSON := Load_JSON() This.default_JSON := JSON.Load(default_JSON) This.TrayMenu() This.MinimizeDelay := This.Minimizeclients_Delay ;Hotkey to trigger by the script to get permissions t bring a Window in foreground ;Register all posible modifire combinations prefixArr := ["","^","!", "#", "+", "+^", "+#", "+!", "^#", "^!","#!", "^+!", "^+#", "^#!", "+!#","^+#!"] for index, prefix in prefixArr Hotkey( prefix . Main_Class.virtualKey, ObjBindMethod(This, "ActivateForgroundWindow"), "S P1") ; Register Hotkey for Puase Hotkeys if the user has is Set if (This.Suspend_Hotkeys_Hotkey != "") { HotIf (*) => WinExist(This.EVEExe) try { Hotkey This.Suspend_Hotkeys_Hotkey, ( * ) => This.Suspend_Hotkeys(), "S1" } catch ValueError as e { MsgBox(e.Message ": --> " e.Extra " <-- in: Global Settings -> Suspend Hotkeys-Hotkey" ) } } ; Register Hotkey for Reload Script if the user has is Set if (This.Reload_Script_Hotkey != "") { HotIf (*) => WinExist(This.EVEExe) try { Hotkey This.Reload_Script_Hotkey, ( * ) => This.Reload_Script(), "S1" } catch ValueError as e { MsgBox(e.Message ": --> " e.Extra " <-- in: Global Settings -> Reload Script-Hotkey" ) } } ; Register Hotkey for Add Character to Group if the user has it Set if (This.AddCharacterToGroupHotkey != "") { HotIf (*) => WinActive(This.EVEExe) try { Hotkey This.AddCharacterToGroupHotkey, ( * ) => This.AddCharacterToGroup(), "S1" } catch ValueError as e { MsgBox(e.Message ": --> " e.Extra " <-- in: Global Settings -> Add Character to Group-Hotkey" ) } } ; The Timer property for Asycn Minimizing. this.timer := ObjBindMethod(this, "EVEMinimize") This.Register_CharSelectionScreen_Hotkeys() ;margins for DwmExtendFrameIntoClientArea. higher values extends the shadow This.margins := Buffer(16, 0) NumPut("Int", 0, This.margins) ;Register all messages wich are inside LISTENERS for i, message in this.LISTENERS OnMessage(message, ObjBindMethod(This, "_OnMessage")) ;Property for the delay to hide Thumbnails if not client is in foreground and user has set Hide on lost Focus This.CheckforActiveWindow := ObjBindMethod(This, "HideOnLostFocusTimer") ;The Main Timer who checks for new EVE Windows or closes Windows SetTimer(ObjBindMethod(This, "HandleMainTimer"), 50) SetTimer(ObjBindMethod(This, "UpdateCharacterNameOverlay"), 100) This.Save_Settings_Delay_Timer := ObjBindMethod(This, "SaveJsonToFile") ;Timer property to remove Thumbnails for closed EVE windows This.DestroyThumbnails := ObjBindMethod(This, "EvEWindowDestroy") This.DestroyThumbnailsToggle := 1 ;Register the Hotkeys for cycle groups This.Register_Hotkey_Groups() This.BorderActive := 0 This.ClientsInCharScreen := Map() ; Create character name overlay This.CreateCharacterNameOverlay() return This } CreateCharacterNameOverlay() { This.CharacterNameOverlay := Gui("+LastFound -Caption +ToolWindow AlwaysOnTop -SysMenu", "CharacterNameOverlay") This.CharacterNameOverlay.BackColor := "040101" This.CharacterNameOverlay.MarginX := 20 This.CharacterNameOverlay.MarginY := 10 textWeight := This.CharacterNameOverlayTextBold ? "w700" : "w400" This.CharacterNameOverlay.SetFont("s" This.CharacterNameOverlayTextSize " " textWeight " c" This.CharacterNameOverlayTextColor, "Arial") nameCtrl := This.CharacterNameOverlay.Add("Text", "vCharacterNameText", "") nameCtrl.Opt("+Background040101") nameCtrl.OnEvent("Click", ObjBindMethod(This, "CharacterNameOverlay_Drag")) This.CharacterNameOverlay.SetFont("s" This.CharacterNameOverlayHotkeySize " w400 c" This.CharacterNameOverlayHotkeyColor, "Arial") hotkeyCtrl := This.CharacterNameOverlay.Add("Text", "vCharacterHotkeyText x+0 w100", "") hotkeyCtrl.Opt("+Background040101") hotkeyCtrl.OnEvent("Click", ObjBindMethod(This, "CharacterNameOverlay_Drag")) This.CharacterNameOverlay.SetFont("s14 w400 c" This.CharacterNameOverlayTextColor, "Arial") autoCycleCtrl := This.CharacterNameOverlay.Add("Text", "vAutoCycleIndicator w30", "") autoCycleCtrl.Opt("+Background040101") autoCycleCtrl.OnEvent("Click", ObjBindMethod(This, "CharacterNameOverlay_Drag")) WinSetTransColor("040101", This.CharacterNameOverlay.Hwnd) This.CharacterNameOverlayX := This.CharacterNameOverlayPosition["x"] ? This.CharacterNameOverlayPosition["x"] : A_ScreenWidth - 340 This.CharacterNameOverlayY := This.CharacterNameOverlayPosition["y"] ? This.CharacterNameOverlayPosition["y"] : 20 SetTimer(ObjBindMethod(This, "CharacterNameOverlay_CheckDrag"), 10) } UpdateCharacterNameOverlay() { if (!This.CharacterNameOverlay) return try { if (WinActive(This.EVEExe)) { activeTitle := This.CleanTitle(WinGetTitle("A")) if (activeTitle != "" && activeTitle != This.CharacterNameOverlayLastText) { if (This.CharacterNameOverlayLastText != "") This.LastActiveCharacter := This.CharacterNameOverlayLastText This.CharacterNameOverlayLastText := activeTitle This.CharacterNameOverlay["CharacterNameText"].Text := activeTitle } if (activeTitle != "") { inGroup := This.IsCharacterInGroup(activeTitle) isSuspended := A_IsSuspended baseColor := This.CharacterNameOverlayTextColor if (isSuspended) { textColor := This.TintColorRed(baseColor) } else { textColor := inGroup ? This.TintColor(baseColor, 0.15) : baseColor } if (textColor != This.CharacterNameOverlayLastColor || activeTitle != This.CharacterNameOverlayLastText) { textWeight := This.CharacterNameOverlayTextBold ? "w700" : "w400" This.CharacterNameOverlay.SetFont("s" This.CharacterNameOverlayTextSize " " textWeight " c" textColor, "Arial") This.CharacterNameOverlay["CharacterNameText"].SetFont("s" This.CharacterNameOverlayTextSize " " textWeight " c" textColor, "Arial") This.CharacterNameOverlayLastColor := textColor } } hotkeyText := "" hotkeyValue := This._Hotkeys[activeTitle] if (hotkeyValue && hotkeyValue != "" && hotkeyValue != 0) { hotkeyText := hotkeyValue } This.CharacterNameOverlay["CharacterHotkeyText"].Text := hotkeyText autoCycleActive := false for groupIdx, Arr in This.AutoForwardGroups { if (This.AutoForwardEnabled[groupIdx]) { autoCycleActive := true break } } This.CharacterNameOverlay["AutoCycleIndicator"].Text := autoCycleActive ? "♲" : "" nameWidth := 20 + (StrLen(activeTitle) * (This.CharacterNameOverlayTextSize * 0.7)) if (nameWidth < 150) nameWidth := 150 This.CharacterNameOverlay["CharacterNameText"].Move(, , nameWidth) ControlGetPos(&nameX, , &nameW, , This.CharacterNameOverlay["CharacterNameText"]) This.CharacterNameOverlay["CharacterHotkeyText"].Move(nameX + nameW, , , ) ControlGetPos(&hotkeyX, &hotkeyY, &hotkeyW, &hotkeyH, This.CharacterNameOverlay["CharacterHotkeyText"]) ControlGetPos(, , , &nameH, This.CharacterNameOverlay["CharacterNameText"]) This.CharacterNameOverlay["AutoCycleIndicator"].Move(hotkeyX, hotkeyY + hotkeyH, , ) This.CharacterNameOverlay.Show("AutoSize NoActivate") if (This.CharacterNameOverlayX = 0) This.CharacterNameOverlayX := A_ScreenWidth - 340 WinMove(This.CharacterNameOverlayX, This.CharacterNameOverlayY, , , This.CharacterNameOverlay.Hwnd) if (!GetKeyState("LButton", "P")) WinSetExStyle("+0x20", This.CharacterNameOverlay.Hwnd) } else if (This.CharacterNameOverlayLastText != "") { This.CharacterNameOverlayLastText := "" This.CharacterNameOverlay.Show("Hide") } } catch { } } CharacterNameOverlay_CheckDrag() { if (!This.CharacterNameOverlay) return if (!WinExist("ahk_id " This.CharacterNameOverlay.Hwnd)) return MouseGetPos(&mx, &my) WinGetPos(&wx, &wy, &ww, &wh, This.CharacterNameOverlay.Hwnd) if (mx >= wx && mx <= wx + ww && my >= wy && my <= wy + wh) { if (GetKeyState("RButton", "P")) { WinSetExStyle("-0x20", This.CharacterNameOverlay.Hwnd) if (!This.CharacterNameOverlayDragging) { This.CharacterNameOverlayDragging := true CoordMode("Mouse", "Screen") MouseGetPos(&startX, &startY) WinGetPos(&winX, &winY, , , This.CharacterNameOverlay.Hwnd) This.CharacterNameOverlayOffsetX := startX - winX This.CharacterNameOverlayOffsetY := startY - winY } MouseGetPos(¤tX, ¤tY) newX := currentX - This.CharacterNameOverlayOffsetX newY := currentY - This.CharacterNameOverlayOffsetY if (newX < 0) newX := 0 if (newY < 0) newY := 0 if (newX + 340 > A_ScreenWidth) newX := A_ScreenWidth - 340 WinMove(newX, newY, , , This.CharacterNameOverlay.Hwnd) This.CharacterNameOverlayX := newX This.CharacterNameOverlayY := newY This.CharacterNameOverlayPosition["x"] := newX This.CharacterNameOverlayPosition["y"] := newY SetTimer(This.Save_Settings_Delay_Timer, -200) } else { if (This.CharacterNameOverlayDragging) { This.CharacterNameOverlayDragging := false WinSetExStyle("+0x20", This.CharacterNameOverlay.Hwnd) } } } else { if (This.CharacterNameOverlayDragging && !GetKeyState("LButton", "P")) { This.CharacterNameOverlayDragging := false WinSetExStyle("+0x20", This.CharacterNameOverlay.Hwnd) } } } CharacterNameOverlay_Drag(*) { ; Handler for click events - but we use timer-based dragging instead } HandleMainTimer() { static HideShowToggle := 0, WinList := {} try WinList := WinGetList(This.EVEExe) Catch return ; If any EVE Window exist if (WinList.Length) { ;Check if a window exist without Thumbnail and if the user is in Character selection screen or not for index, hwnd in WinList { WinList.%hwnd% := { Title: This.CleanTitle(WinGetTitle(hwnd)) } if (WinList.%hwnd%.Title == "") { This.ClientsInCharScreen[hwnd] := WinList.%hwnd%.Title } ;if the User disables the Thumbnails we can skip all the code below this if (This.DisableLiveThumbnail) { This.DisableLiveThumb(hwnd, WinList.%hwnd%.Title, WinList) continue } if !This.ThumbWindows.HasProp(hwnd) { This.EVE_WIN_Created(hwnd, WinList.%hwnd%.title) if (!This.HideThumbnailsOnLostFocus) This.ShowThumb(hwnd, "Show") HideShowToggle := 1 } ;if in Character selection screen else if (This.ThumbWindows.HasProp(hwnd)) { if (This.ThumbWindows.%hwnd%["Window"].Title != WinList.%hwnd%.Title && WinList.%hwnd%.Title = "") { This.ThumbWindows.%hwnd%["Window"].Title := "Char Screen" ;This.ThumbWindows.%hwnd%["TextOverlay"]["OverlayText"].value := "Char Screen" if (This.ThumbWindows.%hwnd%["Window"].Title == "Char Screen" && WinList.%hwnd%.Title != "") { This.EVENameChange(hwnd, WinList.%hwnd%.Title) } } else if (This.ThumbWindows.%hwnd%["Window"].Title != WinList.%hwnd%.Title) { This.EVENameChange(hwnd, WinList.%hwnd%.Title) } } } try { ;if HideThumbnailsOnLostFocus is selectet check if a eve window is still in foreground, runs a timer once with a delay to prevent stuck thumbnails ActiveProcessName := WinGetProcessName("A") if ((DllCall("IsIconic","UInt", WinActive("ahk_exe exefile.exe")) || ActiveProcessName != "exefile.exe") && !HideShowToggle && This.HideThumbnailsOnLostFocus) { SetTimer(This.CheckforActiveWindow, -500) HideShowToggle := 1 This.UpdateCharacterNameOverlay() } else if ( ActiveProcessName = "exefile.exe" && !DllCall("IsIconic","UInt", WinActive("ahk_exe exefile.exe"))) { Ahwnd := WinExist("A") if HideShowToggle { for EVEHWND in This.ThumbWindows.OwnProps() { This.ShowThumb(EVEHWND, "Show") } HideShowToggle := 0 This.BorderActive := 0 } ; sets the Border to the active window thumbnail else if (Ahwnd != This.BorderActive) { ;Shows the Thumbnail on top of other thumbnails if (This.ShowThumbnailsAlwaysOnTop) WinSetAlwaysOnTop(1,This.ThumbWindows.%Ahwnd%["Window"].Hwnd ) This.ShowActiveBorder(Ahwnd) This.UpdateThumb_AfterActivation(, Ahwnd) This.BorderActive := Ahwnd } } } } ; Check if a Thumbnail exist without EVE Window. if so destroy the Thumbnail and free memory if ( This.DestroyThumbnailsToggle ) { for k, v in This.ThumbWindows.Clone().OwnProps() { if !Winlist.HasProp(k) { SetTimer(This.DestroyThumbnails, -500) This.DestroyThumbnailsToggle := 0 } } } } ; The function for the timer which gets started if no EVE window is in focus HideOnLostFocusTimer() { Try { ForegroundPName := WinGetProcessName("A") if (ForegroundPName = "exefile.exe") { if (DllCall("IsIconic", "UInt", WinActive("ahk_exe exefile.exe"))) { for EVEHWND in This.ThumbWindows.OwnProps() { This.ShowThumb(EVEHWND, "Hide") } } } else if (ForegroundPName != "exefile.exe") { for EVEHWND in This.ThumbWindows.OwnProps() { This.ShowThumb(EVEHWND, "Hide") } } } } ;Register set Hotkeys by the user in settings RegisterHotkeys(title, EvE_hwnd) { static registerGroups := 0 ;if the user has set Hotkeys in Options if (This._Hotkeys[title]) { hk := This._Hotkeys[title] ;if the user has selected Global Hotkey. This means the Hotkey will alsways trigger as long at least 1 EVE Window exist. ;if a Window does not Exist which was assigned to the hotkey the hotkey will be dissabled until the Window exist again if(This.Global_Hotkeys) { HotIf (*) => WinExist(This.EVEExe) && WinExist("EVE - " title ) && !WinActive("EVE-X-Preview - Settings") try { Hotkey hk, (*) => This.ActivateEVEWindow_WithModifierCheck(,,title, hk), "P1" } catch ValueError as e { MsgBox(e.Message ": --> " e.Extra " <-- in Profile Settings - " This.LastUsedProfile " Hotkeys" ) } } ;if the user has selected (Win Active) the hotkeys will only trigger if at least 1 EVE Window is Active and in Focus ;This makes it possible to still use all keys outside from EVE else { HotIf (*) => WinExist("EVE - " title ) && WinActive(This.EVEExe) try { Hotkey hk, (*) => This.ActivateEVEWindow_WithModifierCheck(,,title, hk),"P1" } catch ValueError as e { MsgBox(e.Message ": --> " e.Extra " <-- in Profile Settings - " This.LastUsedProfile " Hotkeys" ) } } } } ; Wrapper that checks for extra modifiers before activating window ActivateEVEWindow_WithModifierCheck(hwnd?, ThisHotkey?, title?, hk?) { if (IsSet(hk) && This.HasExtraModifiers(hk)) return This.ActivateEVEWindow(hwnd?, ThisHotkey?, title?) } ; Check if extra modifiers are pressed beyond what's in the hotkey string HasExtraModifiers(hk) { ; Check which modifiers are in the hotkey hasCtrl := InStr(hk, "^") || InStr(hk, "ctrl", false) hasAlt := InStr(hk, "!") || InStr(hk, "alt", false) hasShift := InStr(hk, "+") || InStr(hk, "shift", false) hasWin := InStr(hk, "#") || InStr(hk, "win", false) ; Return true if any modifier is pressed that's NOT in the hotkey if (!hasCtrl && GetKeyState("Ctrl", "P")) return true if (!hasAlt && GetKeyState("Alt", "P")) return true if (!hasShift && GetKeyState("Shift", "P")) return true if (!hasWin && (GetKeyState("LWin", "P") || GetKeyState("RWin", "P"))) return true return false } Register_CharSelectionScreen_Hotkeys(){ if (This.CharScreenHotkey != "" ) { HotIf (*) => WinExist("ahk_exe " This.EVEExe) try { Hotkey(This.CharScreenHotkey, ObjBindMethod(This, "CycleCharScreen"),"P1" ) } } } CycleCharScreen(*){ static Index := 1 Arr := [] list := "" WinList := WinGetList(This.EVEExe) if (WinList != "") { for index, hwnd in WinList { if (This.CleanTitle(WinGetTitle(hwnd)) = "") Arr.Push(hwnd) } if (Arr.Length >= 1 ) { for i, hwnds in Arr { list .= hwnds "," } list := Sort(list, "N D,") Arr := StrSplit(list, ",") Arr.Pop() for i, hwnds in Arr { index := i if (WinActive("ahk_id " hwnds)) { index := i + 1 if (index > Arr.Length) index := 1 break } else { index := 1 } } This.ActivateEVEWindow(Arr[index],,) } } } ;Register the Hotkeys for cycle Groups if any set Register_Hotkey_Groups() { static Fkey := "", BKey := "", Arr := [] if (IsObject(This.Hotkey_Groups) && This.Hotkey_Groups.Count != 0) { for k, v in This.Hotkey_Groups { ; Check if auto-forward is enabled for this group hasAutoForward := v.Has("AutoForwardOnKeypress") && v["AutoForwardOnKeypress"] ; ForwardsHotkey and BackwardsHotkey only trigger when EVE window is active if( v["ForwardsHotkey"] != "" ) { Fkey := v["ForwardsHotkey"], Arr := v["Characters"] HotIf ObjBindMethod(This, "OnWinActive", Arr) try { Hotkey( v["ForwardsHotkey"], ObjBindMethod(This, "Cycle_Hotkey_Groups",Arr,"ForwardsHotkey"), "P1") } catch ValueError as e { MsgBox(e.Message ": --> " e.Extra " <-- in Profile Settings - " This.LastUsedProfile " - Hotkey Groups - " k " - Forwards Hotkey" ) } } if( v["BackwardsHotkey"] != "" ) { Fkey := v["BackwardsHotkey"], Arr := v["Characters"] HotIf ObjBindMethod(This, "OnWinActive", Arr) try { Hotkey( v["BackwardsHotkey"], ObjBindMethod(This, "Cycle_Hotkey_Groups",Arr,"BackwardsHotkey"), "P1") } catch ValueError as e { MsgBox(e.Message ": --> " e.Extra " <-- in Profile Settings - " This.LastUsedProfile " Hotkey Groups - " k " - Backwards Hotkey" ) } } ; Store auto-forward groups and register separate toggle hotkey if (hasAutoForward) { groupIdx := This.AutoForwardGroups.Length + 1 This.AutoForwardGroups.Push(v["Characters"]) This.AutoForwardEnabled[groupIdx] := false This.AutoForwardVisited[groupIdx] := [] This.AutoForwardToggleMode[groupIdx] := v.Has("AutoForwardToggle") ? v["AutoForwardToggle"] : true ; Register separate AutoForwardToggleHotkey if set if (v.Has("AutoForwardToggleHotkey") && v["AutoForwardToggleHotkey"] != "") { toggleKey := v["AutoForwardToggleHotkey"] vkCode := GetKeyVK(toggleKey) if (vkCode) { found := false for code in This.AutoForwardToggleVKCodes { if (code = vkCode) { found := true break } } if (!found) { This.AutoForwardToggleVKCodes.Push(vkCode) } } if(This.Global_Hotkeys) { HotIf ObjBindMethod(This, "OnWinExist", v["Characters"]) } else { HotIf ObjBindMethod(This, "OnWinActive", v["Characters"]) } try { Hotkey(toggleKey, ObjBindMethod(This, "ToggleAutoForward", groupIdx, v["Characters"]), "P1") } catch ValueError as e { MsgBox(e.Message ": --> " e.Extra " <-- in Profile Settings - " This.LastUsedProfile " - Hotkey Groups - " k " - Auto-Forward Toggle Hotkey" ) } } } } } ; Register keyboard/mouse hook if any auto-forward groups exist if (This.AutoForwardGroups.Length > 0) { This.RegisterAutoForwardHook() } } ; Toggle auto-forward for a group ToggleAutoForward(groupIdx, Arr, *) { if (This.AutoForwardEnabled[groupIdx]) { ; Disable This.AutoForwardEnabled[groupIdx] := false This.AutoForwardVisited[groupIdx] := [] } else { ; Enable and mark current window as first visited This.AutoForwardEnabled[groupIdx] := true This.AutoForwardVisited[groupIdx] := [] try { currentHwnd := WinGetID("A") This.AutoForwardProcessedWindow := currentHwnd } This.AutoForwardTimerPending := false ; Reset timer pending flag try { ActiveTitle := This.CleanTitle(WinGetTitle("A")) This.AutoForwardVisited[groupIdx].Push(ActiveTitle) } } } ; Register a low-level input hook for auto-forward RegisterAutoForwardHook() { ; Use low-level keyboard hook to catch ALL keys including with modifiers This.KeyboardHook := DllCall("SetWindowsHookEx", "Int", 13, "Ptr", CallbackCreate(ObjBindMethod(This, "LowLevelKeyboardProc"), "Fast", 3), "Ptr", 0, "UInt", 0, "Ptr") ; Use low-level mouse hook This.MouseHook := DllCall("SetWindowsHookEx", "Int", 14, "Ptr", CallbackCreate(ObjBindMethod(This, "LowLevelMouseProc"), "Fast", 3), "Ptr", 0, "UInt", 0, "Ptr") } ; Low-level mouse hook callback LowLevelMouseProc(nCode, wParam, lParam) { static WM_LBUTTONUP := 0x202, WM_RBUTTONUP := 0x205, WM_MBUTTONUP := 0x208 if (nCode < 0 || (wParam != WM_LBUTTONUP && wParam != WM_RBUTTONUP && wParam != WM_MBUTTONUP)) return DllCall("CallNextHookEx", "Ptr", 0, "Int", nCode, "Ptr", wParam, "Ptr", lParam) if (!This.IsAutoForwardActive()) return DllCall("CallNextHookEx", "Ptr", 0, "Int", nCode, "Ptr", wParam, "Ptr", lParam) try { if (This.AutoForwardRunning || This.AutoForwardTimerPending) return DllCall("CallNextHookEx", "Ptr", 0, "Int", nCode, "Ptr", wParam, "Ptr", lParam) This.AutoForwardTimerPending := true This.AutoForwardTimer := ObjBindMethod(This, "TriggerAutoForward") SetTimer(This.AutoForwardTimer, -1) } return DllCall("CallNextHookEx", "Ptr", 0, "Int", nCode, "Ptr", wParam, "Ptr", lParam) } ; Low-level keyboard hook callback LowLevelKeyboardProc(nCode, wParam, lParam) { static WM_KEYUP := 0x0101, WM_SYSKEYUP := 0x0105 if (nCode >= 0 && (wParam = WM_KEYUP || wParam = WM_SYSKEYUP)) { vkCode := NumGet(lParam, 0, "UInt") ; Ignore modifier keys themselves if (vkCode = 0x10 || vkCode = 0x11 || vkCode = 0x12 || vkCode = 0x5B || vkCode = 0x5C || vkCode = 0xA0 || vkCode = 0xA1 || vkCode = 0xA2 || vkCode = 0xA3 || vkCode = 0xA4 || vkCode = 0xA5) { return DllCall("CallNextHookEx", "Ptr", 0, "Int", nCode, "Ptr", wParam, "Ptr", lParam) } ; Ignore toggle hotkeys for code in This.AutoForwardToggleVKCodes { if (code = vkCode) { return DllCall("CallNextHookEx", "Ptr", 0, "Int", nCode, "Ptr", wParam, "Ptr", lParam) } } if (!This.IsAutoForwardActive()) return DllCall("CallNextHookEx", "Ptr", 0, "Int", nCode, "Ptr", wParam, "Ptr", lParam) try { if (This.AutoForwardRunning || This.AutoForwardTimerPending) return DllCall("CallNextHookEx", "Ptr", 0, "Int", nCode, "Ptr", wParam, "Ptr", lParam) This.AutoForwardTimerPending := true This.AutoForwardTimer := ObjBindMethod(This, "TriggerAutoForward") SetTimer(This.AutoForwardTimer, -1) } } ; Always pass to next hook return DllCall("CallNextHookEx", "Ptr", 0, "Int", nCode, "Ptr", wParam, "Ptr", lParam) } ; Check if any auto-forward group window is active AND enabled IsAutoForwardActive() { try { ActiveTitle := This.CleanTitle(WinGetTitle("A")) for groupIdx, Arr in This.AutoForwardGroups { if (This.AutoForwardEnabled[groupIdx] && This.OnWinActive(Arr)) { for index, name in Arr { if (name = ActiveTitle) return true } } } } return false } ; Check if current window should auto-forward and trigger it TriggerAutoForward() { if (This.AutoForwardRunning) return This.AutoForwardRunning := true This.AutoForwardTimerPending := false This.AutoForwardTimer := 0 if (This.AutoForwardDelay > 0) Sleep(This.AutoForwardDelay) try { ActiveTitle := This.CleanTitle(WinGetTitle("A")) for groupIdx, Arr in This.AutoForwardGroups { if (!This.AutoForwardEnabled[groupIdx] || !This.OnWinActive(Arr)) continue for index, name in Arr { if (name != ActiveTitle) continue This.Cycle_Hotkey_Groups(Arr, "ForwardsHotkey") try { newWindow := WinGetID("A") This.AutoForwardProcessedWindow := newWindow } catch { This.AutoForwardRunning := false return } try { newTitle := This.CleanTitle(WinGetTitle("A")) alreadyVisited := false for visited in This.AutoForwardVisited[groupIdx] { if (visited = newTitle) { alreadyVisited := true break } } if (!alreadyVisited) This.AutoForwardVisited[groupIdx].Push(newTitle) } if (!This.AutoForwardToggleMode[groupIdx]) { existingCount := 0 for n in Arr { if (WinExist("EVE - " n " Ahk_Exe exefile.exe")) existingCount++ } if (This.AutoForwardVisited[groupIdx].Length >= existingCount) { This.AutoForwardEnabled[groupIdx] := false This.AutoForwardVisited[groupIdx] := [] } } This.AutoForwardRunning := false return } } } finally { This.AutoForwardRunning := false } } ; The method to make it possible to cycle throw the EVE Windows. Used with the Hotkey Groups Cycle_Hotkey_Groups(Arr, direction,*) { static Index := 0 length := Arr.Length if (direction == "ForwardsHotkey") { Try Index := (n := IsActiveWinInGroup(This.CleanTitle(WinGetTitle("A")), Arr)) ? n+1 : 1 if (Index > length) Index := 1 if (This.OnWinExist(Arr)) { Try { if !(WinExist("EVE - " This.CleanTitle(Arr[Index]))) { while (!(WinExist("EVE - " This.CleanTitle(Arr[Index])))) { index += 1 if (Index > length) Index := 1 } } This.ActivateEVEWindow(,,This.CleanTitle(Arr[Index]), true) } } } else if (direction == "BackwardsHotkey") { Try Index := (n := IsActiveWinInGroup(This.CleanTitle(WinGetTitle("A")), Arr)) ? n-1 : length if (Index <= 0) Index := length if (This.OnWinExist(Arr)) { if !(WinExist("EVE - " This.CleanTitle(Arr[Index]))) { while (!(WinExist("EVE - " This.CleanTitle(Arr[Index])))) { Index -= 1 if (Index <= 0) Index := length } } This.ActivateEVEWindow(,,This.CleanTitle(Arr[Index]), true) } } IsActiveWinInGroup(Title, Arr) { for index, names in Arr { if names = Title return index } return false } } DisableLiveThumb(hwnd, title, arr) { for hwnds, name in This.ThumbWindows.Clone().OwnProps() { if (!arr.HasProp(hwnds)) { This.ThumbWindows.DeleteProp(hwnds) } } if !(This.ThumbWindows.HasProp(hwnd)) { This.ThumbWindows.%hwnd% := "" This.RegisterHotkeys(title, hwnd) } } ; To Check if atleast One Win stil Exist in the Array for the cycle groups hotkeys OnWinExist(Arr, *) { for index, Name in Arr { If ( WinExist("EVE - " Name " Ahk_Exe exefile.exe") && !WinActive("EVE-X-Preview - Settings") ) { return true } } return false } OnWinActive(Arr, *) { If (This.OnWinExist(Arr) && WinActive("Ahk_exe exefile.exe")) { return true } return false } ;## Updates the Thumbnail in the GUI after Activation ;## Do not Update thumbnails from minimized windows or this will leed in no picture for the Thumbnail UpdateThumb_AfterActivation(event?, hwnd?) { MinMax := -1 try MinMax := WinGetMinMax("ahk_id " hwnd) if (This.ThumbWindows.HasProp(hwnd)) { if !(MinMax == -1) { This.Update_Thumb(false, This.ThumbWindows.%hwnd%["Window"].Hwnd) } } } ;This function updates the Thumbnails and hotkeys if the user switches Charakters in the character selection screen EVENameChange(hwnd, title) { if (This.ThumbWindows.HasProp(hwnd)) { This.SetThumbnailText[hwnd] := title ; moves the Window to the saved positions if any stored, a bit of sleep is usfull to give the window time to move before creating the thumbnail This.RestoreClientPossitions(hwnd, title) if (title = "") { This.EvEWindowDestroy(hwnd, title) This.EVE_WIN_Created(hwnd,title) } else If (This.ThumbnailPositions.Has(title)) { This.EvEWindowDestroy(hwnd, title) This.EVE_WIN_Created(hwnd,title) rect := This.ThumbnailPositions[title] This.ShowThumb(hwnd, "Hide") This.ThumbMove( rect["x"], rect["y"], rect["width"], rect["height"], This.ThumbWindows.%hwnd% ) This.BorderSize(This.ThumbWindows.%hwnd%["Window"].Hwnd, This.ThumbWindows.%hwnd%["Border"].Hwnd) This.Update_Thumb(true) If ( This.HideThumbnailsOnLostFocus && WinActive(This.EVEExe) || !This.HideThumbnailsOnLostFocus && !WinActive(This.EVEExe) || !This.HideThumbnailsOnLostFocus && WinActive(This.EVEExe)) { for k, v in This.ThumbWindows.OwnProps() This.ShowThumb(k, "Show") } } This.BorderActive := 0 This.RegisterHotkeys(title, hwnd) } } ;#### Gets Called after receiveing a mesage from the Listeners ;#### Handels Window Border, Resize, Activation _OnMessage(wparam, lparam, msg, hwnd) { If (This.ThumbHwnd_EvEHwnd.Has(hwnd) ) { ; Move the Window with right mouse button If (msg == Main_Class.WM_RBUTTONDOWN) { while (GetKeyState("RButton")) { if !(GetKeyState("LButton")) { ;sleep 1 This.Mouse_DragMove(wparam, lparam, msg, hwnd) This.Window_Snap(hwnd, This.ThumbWindows) } else This.Mouse_ResizeThumb(wparam, lparam, msg, hwnd) } return 0 } ; Wparam - 9 Ctrl+Lclick ; 5 Shift+Lclick ; 13 Shift+ctrl+click Else If (msg == Main_Class.WM_LBUTTONDOWN) { ;Activates the EVE Window by clicking on the Thumbnail if (wparam = 1) { if !(WinActive(This.ThumbHwnd_EvEHwnd[hwnd])) This.ActivateEVEWindow(hwnd) } ; Ctrl+Lbutton, Minimizes the Window on whose thumbnail the user clicks else if (wparam = 9) { ; Minimize if (!GetKeyState("RButton")) PostMessage 0x0112, 0xF020, , , This.ThumbHwnd_EvEHwnd[hwnd] } return 0 } } } ; Creates a new thumbnail if a new window got created EVE_WIN_Created(Win_Hwnd, Win_Title) { ; Moves the Window to the saved possition if any are stored This.RestoreClientPossitions(Win_Hwnd, Win_Title) ;Creates the Thumbnail and stores the EVE Hwnd in the array If !(This.ThumbWindows.HasProp(Win_Hwnd)) { This.ThumbWindows.%Win_Hwnd% := This.Create_Thumbnail(Win_Hwnd, Win_Title) This.ThumbHwnd_EvEHwnd[This.ThumbWindows.%Win_Hwnd%["Window"].Hwnd] := Win_Hwnd ;if the User is in character selection screen show the window always if (This.ThumbWindows.%Win_Hwnd%["Window"].Title = "") { This.SetThumbnailText[Win_Hwnd] := Win_Title ;if the Title is just "EVE" that means it is in the Charakter selection screen ;in this case show always the Thumbnail This.ShowThumb(Win_Hwnd, "Show") return } ;if the user loged in into a Character then move the Thumbnail to the right possition else If (This.ThumbnailPositions.Has(Win_Title)) { This.SetThumbnailText[Win_Hwnd] := Win_Title rect := This.ThumbnailPositions[Win_Title] This.ThumbMove( rect["x"], rect["y"], rect["width"], rect["height"], This.ThumbWindows.%Win_Hwnd% ) This.BorderSize(This.ThumbWindows.%Win_Hwnd%["Window"].Hwnd, This.ThumbWindows.%Win_Hwnd%["Border"].Hwnd) This.Update_Thumb(true) If ( This.HideThumbnailsOnLostFocus && WinActive(This.EVEExe) || !This.HideThumbnailsOnLostFocus && !WinActive(This.EVEExe) || !This.HideThumbnailsOnLostFocus && WinActive(This.EVEExe)) { for k, v in This.ThumbWindows.OwnProps() This.ShowThumb(k, "Show") } } This.RegisterHotkeys(Win_Title, Win_Hwnd) } } ;if a EVE Window got closed this destroyes the Thumbnail and frees the memory. EvEWindowDestroy(hwnd?, WinTitle?) { if (IsSet(hwnd) && This.ThumbWindows.HasProp(hwnd)) { for k, v in This.ThumbWindows.%hwnd% { if (K = "Thumbnail") continue v.Destroy() ;This.ThumbWindows.%Win_Hwnd%.Delete() } This.ThumbWindows.DeleteProp(hwnd) Return } ;If a EVE Windows get destroyed for Win_Hwnd, v in This.ThumbWindows.Clone().OwnProps() { if (!WinExist("Ahk_Id " Win_Hwnd)) { if (IsObject(v)) for k, guiObj in v { if (K = "Thumbnail") continue guiObj.Destroy() } This.ThumbWindows.DeleteProp(Win_Hwnd) } } This.DestroyThumbnailsToggle := 1 } ActivateEVEWindow(hwnd?,ThisHotkey?, title?, fromAutoForward := false) { ; Disable all auto-forward groups (emergency shutoff) - but not when called from auto-forward itself if (!fromAutoForward) { for groupIdx in This.AutoForwardEnabled { This.AutoForwardEnabled[groupIdx] := false This.AutoForwardVisited[groupIdx] := [] } } ; If the user clicks the Thumbnail then hwnd stores the Thumbnail Hwnd. Here the Hwnd gets changed to the contiguous EVE window hwnd if (IsSet(hwnd) && This.ThumbHwnd_EvEHwnd.Has(hwnd)) { hwnd := WinExist(This.ThumbHwnd_EvEHwnd[hwnd]) title := This.CleanTitle(WinGetTitle("Ahk_id " Hwnd)) } ;if the user presses the Hotkey Else if (IsSet(title)) { title := "EVE - " title hwnd := WinExist(title " Ahk_exe exefile.exe") } ;return when the user tries to bring a window to foreground which is already in foreground if (WinActive("Ahk_id " hwnd)) Return If (DllCall("IsIconic", "UInt", hwnd)) { if (This.AlwaysMaximize) || ( This.TrackClientPossitions && This.ClientPossitions[This.CleanTitle(title)]["IsMaximized"] ) { ; ; Maximize This.ShowWindowAsync(hwnd, 3) } else { ; Restore This.ShowWindowAsync(hwnd) } } Else { ; Use the virtual key to trigger the internal Hotkey. This.ActivateHwnd := hwnd SendEvent("{Blind}{" Main_Class.virtualKey "}") } ;Sets the timer to minimize client if the user enable this. if (This.MinimizeInactiveClients) { This.wHwnd := hwnd SetTimer(This.timer, -This.MinimizeDelay) } } ;The function for the Internal Hotkey to bring a not minimized window in foreground ActivateForgroundWindow(*) { ; 2 attempts for brining the window in foreground try { if !(DllCall("SetForegroundWindow", "UInt", This.ActivateHwnd)) { DllCall("SetForegroundWindow", "UInt", This.ActivateHwnd) } ;If the user has selected to always maximize. this prevents wrong sized windows on heavy load. if (This.AlwaysMaximize && WinGetMinMax("ahk_id " This.ActivateHwnd) = 0) || ( This.TrackClientPossitions && This.ClientPossitions[This.CleanTitle(WinGetTitle("Ahk_id " This.ActivateHwnd))]["IsMaximized"] && WinGetMinMax("ahk_id " This.ActivateHwnd) = 0 ) This.ShowWindowAsync(This.ActivateHwnd, 3) } Return } ; Minimize All windows after Activting one with the exception of Titels in the DontMinimize Wintitels ; gets called by the timer to run async EVEMinimize() { for EveHwnd, GuiObj in This.ThumbWindows.OwnProps() { ;ThumbHwnd := GuiObj["Window"].Hwnd try WinTitle := WinGetTitle("Ahk_Id " EveHwnd) catch continue if (EveHwnd = This.wHwnd || Dont_Minimze_Enum(EveHwnd, WinTitle) || WinTitle == "EVE" || WinTitle = "") continue else { ; Just to make sure its not minimizeing the active Window if !(EveHwnd = WinExist("A")) { This.ShowWindowAsync(EveHwnd, 11) } } } ;to check which names are in the list that should not be minimized Dont_Minimze_Enum(hwnd, EVEwinTitle) { WinTitle := This.CleanTitle(EVEwinTitle) if !(WinTitle = "") { for k in This.Dont_Minimize_Clients { value := This.CleanTitle(k) if value == WinTitle return 1 } return 0 } } } ; Function t move the Thumbnails into the saved positions from the user ThumbMove(x := "", y := "", Width := "", Height := "", GuiObj := "") { for Names, Obj in GuiObj { if (Names = "Thumbnail") continue WinMove(x, y, Width, Height, Obj.Hwnd) } } ;Saves the possitions of all Windows and stores Client_Possitions() { IDs := WinGetList("Ahk_Exe " This.EVEExe) for k, v in IDs { Title := This.CleanTitle(WinGetTitle("Ahk_id " v)) if !(Title = "") { ;If Minimzed then restore before saving the coords if (DllCall("IsIconic", "UInt", v)) { This.ShowWindowAsync(v) ;wait for getting Active for maximum of 2 seconds if (WinWaitActive("Ahk_Id " v, , 2)) { Sleep(200) WinGetPos(&X, &Y, &Width, &Height, "Ahk_Id " v) ;If the Window is Maximized if (DllCall("IsZoomed", "UInt", v)) { This.ClientPossitions[Title] := [X, Y, Width, Height, 1] } else { This.ClientPossitions[Title] := [X, Y, Width, Height, 0] } } } ;If the Window is not Minimized else { WinGetPos(&X, &Y, &Width, &Height, "Ahk_Id " v) ;is the window Maximized? if (DllCall("IsZoomed", "UInt", v)) { This.ClientPossitions[Title] := [X, Y, Width, Height, 1] } else This.ClientPossitions[Title] := [X, Y, Width, Height, 0] } } } SetTimer(This.Save_Settings_Delay_Timer, -200) } ;Restore the clients to the saved positions RestoreClientPossitions(hwnd, title) { if (This.TrackClientPossitions) { if ( This.TrackClientPossitions && This.ClientPossitions[title] ) { if (DllCall("IsIconic", "UInt", hwnd) && This.ClientPossitions[title]["IsMaximized"] || DllCall("IsZoomed", "UInt", hwnd) && This.ClientPossitions[title]["IsMaximized"]) { This.SetWindowPlacement(hwnd,This.ClientPossitions[title]["x"], This.ClientPossitions[title]["y"], This.ClientPossitions[title]["width"], This.ClientPossitions[title]["height"], 9 ) This.ShowWindowAsync(hwnd, 3) Return } else if (DllCall("IsIconic", "UInt", hwnd) && !This.ClientPossitions[title]["IsMaximized"] || DllCall("IsZoomed", "UInt", hwnd) && !This.ClientPossitions[title]["IsMaximized"]) { This.SetWindowPlacement(hwnd,This.ClientPossitions[title]["x"], This.ClientPossitions[title]["y"], This.ClientPossitions[title]["width"], This.ClientPossitions[title]["height"], 9 ) This.ShowWindowAsync(hwnd, 4) Return } else if ( This.ClientPossitions[title]["IsMaximized"]) { This.SetWindowPlacement(hwnd,This.ClientPossitions[title]["x"], This.ClientPossitions[title]["y"], This.ClientPossitions[title]["width"], This.ClientPossitions[title]["height"] ) This.ShowWindowAsync(hwnd, 3) Return } else if ( !This.ClientPossitions[title]["IsMaximized"]) { This.SetWindowPlacement(hwnd,This.ClientPossitions[title]["x"], This.ClientPossitions[title]["y"], This.ClientPossitions[title]["width"], This.ClientPossitions[title]["height"], 4 ) This.ShowWindowAsync(hwnd, 4) Return } } } } ;*WinApi Functions ;Gets the normal possition from the Windows. Not to use for Maximized Windows GetWindowPlacement(hwnd) { DllCall("User32.dll\GetWindowPlacement", "Ptr", hwnd, "Ptr", WP := Buffer(44)) Lo := NumGet(WP, 28, "Int") ; X coordinate of the upper-left corner of the window in its original restored state To := NumGet(WP, 32, "Int") ; Y coordinate of the upper-left corner of the window in its original restored state Wo := NumGet(WP, 36, "Int") - Lo ; Width of the window in its original restored state Ho := NumGet(WP, 40, "Int") - To ; Height of the window in its original restored state CMD := NumGet(WP, 8, "Int") ; ShowCMD flags := NumGet(WP, 4, "Int") ; flags MinX := NumGet(WP, 12, "Int") MinY := NumGet(WP, 16, "Int") MaxX := NumGet(WP, 20, "Int") MaxY := NumGet(WP, 24, "Int") WP := "" return { X: Lo, Y: to, W: Wo, H: Ho , cmd: CMD, flags: flags, MinX: MinX, MinY: MinY, MaxX: MaxX, MaxY: MaxY } } ;Moves the window to the given possition immediately SetWindowPlacement(hwnd:="", X:="", Y:="", W:="", H:="", action := 9) { ;hwnd := hwnd = "" ? WinExist("A") : hwnd DllCall("User32.dll\GetWindowPlacement", "Ptr", hwnd, "Ptr", WP := Buffer(44)) Lo := NumGet(WP, 28, "Int") ; X coordinate of the upper-left corner of the window in its original restored state To := NumGet(WP, 32, "Int") ; Y coordinate of the upper-left corner of the window in its original restored state Wo := NumGet(WP, 36, "Int") - Lo ; Width of the window in its original restored state Ho := NumGet(WP, 40, "Int") - To ; Height of the window in its original restored state L := X = "" ? Lo : X ; X coordinate of the upper-left corner of the window in its new restored state T := Y = "" ? To : Y ; Y coordinate of the upper-left corner of the window in its new restored state R := L + (W = "" ? Wo : W) ; X coordinate of the bottom-right corner of the window in its new restored state B := T + (H = "" ? Ho : H) ; Y coordinate of the bottom-right corner of the window in its new restored state NumPut("UInt",action,WP,8) NumPut("UInt",L,WP,28) NumPut("UInt",T,WP,32) NumPut("UInt",R,WP,36) NumPut("UInt",B,WP,40) Return DllCall("User32.dll\SetWindowPlacement", "Ptr", hwnd, "Ptr", WP) } ShowWindowAsync(hWnd, nCmdShow := 9) { DllCall("ShowWindowAsync", "UInt", hWnd, "UInt", nCmdShow) } GetActiveWindow() { Return DllCall("GetActiveWindow", "Ptr") } SetActiveWindow(hWnd) { Return DllCall("SetActiveWindow", "Ptr", hWnd) } SetFocus(hWnd) { Return DllCall("SetFocus", "Ptr", hWnd) } SetWindowPos(hWnd, x, y, w, h, hWndInsertAfter := 0, uFlags := 0x0020) { ; SWP_FRAMECHANGED 0x0020 ; SWP_SHOWWINDOW 0x40 Return DllCall("SetWindowPos", "Ptr", hWnd, "Ptr", hWndInsertAfter, "Int", x, "Int", y, "Int", w, "Int", h, "UInt", uFlags) } ;removes "EVE" from the Titel and leaves only the Character names CleanTitle(title) { Return RegExReplace(title, "^(?i)eve(?:\s*-\s*)?\b", "") ;RegExReplace(title, "(?i)eve\s*-\s*", "") } AddCharacterToGroup(*) { if (!WinActive(This.EVEExe)) return currentTitle := This.CleanTitle(WinGetTitle("A")) if (currentTitle = "") return targetGroupName := "" if (IsObject(This.Hotkey_Groups) && This.Hotkey_Groups.Count != 0) { for groupName, groupData in This.Hotkey_Groups { if (IsObject(groupData["Characters"])) { for index, charName in groupData["Characters"] { if (charName = currentTitle) { targetGroupName := groupName break } } if (targetGroupName != "") break } } } if (targetGroupName = "") { if (IsObject(This.Hotkey_Groups) && This.Hotkey_Groups.Count != 0) { for groupName, groupData in This.Hotkey_Groups { if (IsObject(groupData["Characters"]) && groupData["Characters"].Length > 0) { targetGroupName := groupName break } } } } if (targetGroupName = "") return if (!IsObject(This.Hotkey_Groups[targetGroupName]["Characters"])) This.Hotkey_Groups[targetGroupName]["Characters"] := [] if (!This.CharacterGroupPositions.Has(targetGroupName)) This.CharacterGroupPositions[targetGroupName] := Map() alreadyInGroup := false removeIndex := 0 for index, charName in This.Hotkey_Groups[targetGroupName]["Characters"] { if (charName = currentTitle) { alreadyInGroup := true removeIndex := index break } } if (!alreadyInGroup) { previousIndex := This.CharacterGroupPositions[targetGroupName].Has(currentTitle) ? This.CharacterGroupPositions[targetGroupName][currentTitle] : -1 currentLength := This.Hotkey_Groups[targetGroupName]["Characters"].Length if (previousIndex >= 1 && previousIndex <= currentLength) { This.Hotkey_Groups[targetGroupName]["Characters"].InsertAt(previousIndex, currentTitle) } else { This.Hotkey_Groups[targetGroupName]["Characters"].Push(currentTitle) previousIndex := This.Hotkey_Groups[targetGroupName]["Characters"].Length } This.CharacterGroupPositions[targetGroupName][currentTitle] := previousIndex ToolTip("Added " currentTitle " to group") SetTimer(() => ToolTip(), -1500) This.UpdateCharacterNameOverlay() } else { This.CharacterGroupPositions[targetGroupName][currentTitle] := removeIndex This.Hotkey_Groups[targetGroupName]["Characters"].RemoveAt(removeIndex) ToolTip("Removed " currentTitle " from group") SetTimer(() => ToolTip(), -1500) This.UpdateCharacterNameOverlay() } } IsCharacterInGroup(charName) { if (!IsObject(This.Hotkey_Groups) || This.Hotkey_Groups.Count = 0) return false for groupName, groupData in This.Hotkey_Groups { if (IsObject(groupData["Characters"])) { for index, name in groupData["Characters"] { if (name = charName) return true } } } return false } TintColor(hexColor, tintAmount := 0.2) { hexColor := RegExReplace(hexColor, "^#|^0x", "") if (StrLen(hexColor) != 6) return hexColor r := Integer("0x" SubStr(hexColor, 1, 2)) g := Integer("0x" SubStr(hexColor, 3, 2)) b := Integer("0x" SubStr(hexColor, 5, 2)) r := Round(r + (255 - r) * tintAmount) g := Round(g + (255 - g) * tintAmount) b := Round(b + (255 - b) * tintAmount) r := (r > 255 ? 255 : r < 0 ? 0 : r) g := (g > 255 ? 255 : g < 0 ? 0 : g) b := (b > 255 ? 255 : b < 0 ? 0 : b) return Format("{:02X}{:02X}{:02X}", r, g, b) } TintColorRed(hexColor) { hexColor := RegExReplace(hexColor, "^#|^0x", "") if (StrLen(hexColor) != 6) return hexColor r := Integer("0x" SubStr(hexColor, 1, 2)) g := Integer("0x" SubStr(hexColor, 3, 2)) b := Integer("0x" SubStr(hexColor, 5, 2)) r := Round(r + (255 - r)) g := Round(g - g) b := Round(b - b) r := (r > 255 ? 255 : r < 0 ? 0 : r) g := (g > 255 ? 255 : g < 0 ? 0 : g) b := (b > 255 ? 255 : b < 0 ? 0 : b) return Format("{:02X}{:02X}{:02X}", r, g, b) } SaveJsonToFile() { FileDelete("EVE-X-Preview.json") FileAppend(JSON.Dump(This._JSON, , " "), "EVE-X-Preview.json") } }