
; TIME-STAMP 2025-02-17 14:16:12

#cs ====	Favoriten Menü   ====

	Dynamisch anpassbares Menü für App-Favoriten

	Kompilierte *.exe in eigenem Ordner speichern, dort wird dann die zugehörige INI-Datei erstellt.

	Einfügen in Autostart:
	• <Win+R> "shell:startup" (oder händisch öffnen: "C:\Users\USER\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup")
	• Link zur *.exe einfügen

	Einblenden Menü:		<Ctrl+Shift+M>
	Ausblenden Menü:		<Esc>			(nur erforderlich, wenn kein Menüpunkt gewählt wird)
	Menüprogramm Beenden:	<Ctrl+Alt+M>

	Links-Klick Menüeintrag:	Ausführen der hinterlegten Anwendung
	Rechts-Klick Menüeintrag:	Anzeige Anwendungspfad als Tooltipp

#ce ====

#include <Array.au3>
#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <GuiEdit.au3>
#include <GuiListView.au3>
#include <ListViewConstants.au3>
#include <Misc.au3>
#include <StaticConstants.au3>
#include <StructureConstants.au3>
#include <WindowsConstants.au3>

If _Singleton(@ScriptName, 1) = 0 Then
	MsgBox(48+262144, @ScriptName, 'Eine Instanz dieses Programms ist bereits gestartet!')
	Exit
EndIf

; Gui nicht mit ESC schließen
Opt("GUICloseOnESC", 0)
; kein Traymenü anzeigen
Opt("TrayMenuMode", 1)
; Trayicon verstecken
Opt("TrayIconHide", 1)


Global $INI = @ScriptDir & "\" & StringLeft(@ScriptName, StringInStr(@ScriptName, '.', 0, -1)) & "ini"
If Not FileExists($INI) Then
    IniWrite($INI, 'remarks', 'Info', '[apps] "Alias=Anwendungspfad"')
    IniWrite($INI, 'winitem', 'logout', '0')
    IniWrite($INI, 'winitem', 'restart', '0')
    IniWrite($INI, 'winitem', 'shutdown', '0')
EndIf
Global $aApps[1][2]

Global $SHELL32DLL = @WindowsDir & '\System32\SHELL32.dll'
Global $PROGRAMFILES = StringLeft(@WindowsDir, 3) & "Program Files"

; Variablen Manage-Gui (für Hinzufügen/Entfernen von Apps)
Global $hGuiNew, $hTab
Global $tabNew, $inPath, $btSelect, $sSel, $inAlias, $btNext, $btEnd, $aBuffer[1][2] = [[Null]], $sRes, $sPathLast = $PROGRAMFILES
Global $tabDel, $btDel, $LVDel, $hLVDel
Global $icoLogout, $icoRestart , $icoShutdown, $cbLogout, $cbRestart, $cbShutdown

_Manage_CreateGUI()

; Variablen Main-Gui (Menü)
Global $hGUImain, $idDummy
Global $nWGUImain = 220, $nHGUImain = @DesktopHeight
Global $nXGUImain = 0, $nYGUImain = 0
Global $idLVmain, $hLVmain
Global $nXLVmain = 5, $nYLVmain = 5
Global $nWLVmain = $nWGUImain - (2 * $nXLVmain)
Global $nHLVmain = $nHGUImain - (2 * $nYLVmain)
Global $bManage = False	; Schleifenvariable zum Öffnen 'Anwendungen verwalten'

_Main_Create()

; Hotkey zum Ausblenden des Menüs an GUI-Main koppeln
Global $aAccKeys[1][2] = [['{ESC}', $idDummy]]
GUISetAccelerators($aAccKeys, $hGUImain)

_Main_MenuSetEntries()

; Hotkey zum Einblenden des Menüs
HotKeySet("^+m", "_Main_ShowGui")	; Strg+Umsch+M
; Hotkey zum Beenden der Anwendung
HotKeySet("^!m", "_Exit")			; Strg+Alt+M

GUIRegisterMsg($WM_NOTIFY, "_WM_NOTIFY")



Global $aMsg
While True
	If $bManage Then
		_GUICtrlListView_DeleteAllItems($hLVDel)
		_Manage_SetLVDelete()
        If IniRead($INI, 'winitem', 'logout', '0') = '1' Then GUICtrlSetState($cbLogout, $GUI_CHECKED)
        If IniRead($INI, 'winitem', 'restart', '0') = '1' Then GUICtrlSetState($cbRestart, $GUI_CHECKED)
        If IniRead($INI, 'winitem', 'shutdown', '0') = '1' Then GUICtrlSetState($cbShutdown, $GUI_CHECKED)
		$bManage = False
		GUISetState(@SW_SHOW, $hGuiNew)
	EndIf
	$aMsg = GUIGetMsg(1)
	Switch $aMsg[1]
		Case $hGUImain
			Switch $aMsg[0]
				Case $idDummy
					GUISetState(@SW_HIDE, $hGUImain)
			EndSwitch
		Case $hGuiNew
			Switch $aMsg[0]
                Case -3
					GUISetState(@SW_HIDE, $hGuiNew)
                    GUICtrlSetData($inAlias, '')
                    GUICtrlSetData($inPath, '')
					$sPathLast = $PROGRAMFILES
					_Main_MenuSetEntries()
					GUISetState(@SW_SHOW, $hGUImain)
				Case $btSelect
					$sSel = FileOpenDialog('Anwendung', $sPathLast, 'Application (*.exe)', 1+2)
					If $sSel <> '' Then
						GUICtrlSetData($inPath, $sSel)
						$sPathLast = StringLeft($sSel, StringInStr($sSel, '\', 0, -1) -1)
						$sAlias = GUICtrlRead($inAlias)
						If $sAlias = '' Then
							$sAlias = StringTrimLeft($sSel, StringInStr($sSel, '\', 0, -1))
							$sAlias = StringLeft($sAlias, StringInStr($sAlias, '.', 0, -1) -1)
							GUICtrlSetData($inAlias, $sAlias)
						EndIf
						GUICtrlSetState($btNext, $GUI_ENABLE)
						GUICtrlSetState($btEnd, $GUI_ENABLE)
					EndIf
				Case $btNext	; Zwischenspeichern
					If $aBuffer[0][0] <> Null Then ReDim $aBuffer[UBound($aBuffer)+1][2]
					$aBuffer[UBound($aBuffer)-1][0] = GUICtrlRead($inAlias)
					$aBuffer[UBound($aBuffer)-1][1] = GUICtrlRead($inPath)
					GUICtrlSetData($inAlias, '')
					GUICtrlSetData($inPath, '')
				Case $btEnd
					$sRes = ''
					If $aBuffer[0][0] <> Null Then
						For $i = 0 To UBound($aBuffer) -1
							$sRes &= _IniWriteOnce($INI, 'apps', $aBuffer[$i][0], $aBuffer[$i][1]) & @CRLF
						Next
						ReDim $aBuffer[1][2]
						$aBuffer[0][0] = Null
					EndIf
					If GUICtrlRead($inAlias) <> '' And GUICtrlRead($inPath) <> '' Then
						$sRes &= _IniWriteOnce($INI, 'apps', GUICtrlRead($inAlias), GUICtrlRead($inPath)) & @CRLF
						GUICtrlSetData($inAlias, '')
						GUICtrlSetData($inPath, '')
					EndIf
					If $sRes <> '' Then MsgBox(262144+64, 'Übernahme Apps', $sRes)
					GUISetState(@SW_HIDE, $hGuiNew)
                    _Main_MenuSetEntries()
                    GUISetState(@SW_SHOW, $hGUImain)
                Case $cbLogout
                    IniWrite($INI, 'winitem', 'logout', (BitAND(GUICtrlRead($cbLogout), $GUI_CHECKED) ? '1' : '0'))
                Case $cbRestart
                    IniWrite($INI, 'winitem', 'restart', (BitAND(GUICtrlRead($cbRestart), $GUI_CHECKED) ? '1' : '0'))
                Case $cbShutdown
                    IniWrite($INI, 'winitem', 'shutdown', (BitAND(GUICtrlRead($cbShutdown), $GUI_CHECKED) ? '1' : '0'))
				Case $btDel
					GUISetState(@SW_HIDE, $hGuiNew)
					; Einträge mit entferntem Haken aus der INI löschen
					For $i = 0 To _GUICtrlListView_GetItemCount($hLVDel) -1
						If Not _GUICtrlListView_GetItemChecked($hLVDel, $i) Then
							IniDelete($INI, 'apps', _GUICtrlListView_GetItemText($hLVDel, $i))
						EndIf
					Next
					_Main_MenuSetEntries()
                    GUISetState(@SW_SHOW, $hGUImain)
			EndSwitch
	EndSwitch
WEnd


Func _IniWriteOnce($_ini, $_section, $_key, $_value)	; Key kann nicht mit neuem Wert überschrieben werden
	Local $aSec = IniReadSection($_ini, $_section)
	If Not @error Then
		For $i = 1 To $aSec[0][0]
			If $aSec[$i][0] = $_key Then Return '"' & $_key & '" - [Fehler] Bereits vorhanden!'
		Next
	EndIf
	IniWrite($_ini, $_section, $_key, $_value)
	Return '"' & $_key & '" - [OK] Eingetragen'
EndFunc


Func _Exit()
	Exit
EndFunc


Func _Main_ShowGui()
	GUISetState(@SW_SHOW, $hGUImain)
EndFunc


Func _Main_Create()
	$hGUImain = GUICreate('', $nWGUImain, $nHGUImain, $nXGUImain, $nYGUImain, $WS_POPUP, $WS_EX_TOPMOST)
	GUISetIcon($SHELL32DLL, -323)
	$idDummy = GUICtrlCreateDummy()
	$idLVmain = GUICtrlCreateListView('', $nXLVmain, $nYLVmain, $nWLVmain, $nHLVmain)
	GUICtrlSetStyle($idLVmain, BitOR($LVS_NOCOLUMNHEADER,$LVS_SMALLICON))
	$hLVmain = GUICtrlGetHandle($idLVmain)
	_GUICtrlListView_SetColumnWidth($hLVmain, 0, $LVSCW_AUTOSIZE_USEHEADER)
EndFunc


Func _Main_MenuSetEntries()
	; Reset Size & Position
	WinMove($hGUImain, '', $nXGUImain, $nYGUImain, $nWGUImain, $nHGUImain)
	ControlMove($hGUImain, '', $idLVmain, $nXLVmain, $nYLVmain, $nWLVmain, $nHLVmain)

	_GUICtrlListView_DeleteAllItems($hLVmain)	; alle Einträge im Listview löschen
	$aApps = _ReadAppsFromINI()					; gespeicherte Apps aus INI lesen

	; Eintrag für Verwaltung an Index 0
	$aApps[0][0] = '>> EINTRÄGE VERWALTEN <<'
	$aApps[0][1] = 'Verwalten'

	; Einträge für Abmelden, Neustart und Shutdown zufügen, wenn gewählt
    If IniRead($INI, 'winitem', 'logout', '0') = 1 Then
        ReDim $aApps[UBound($aApps)+1][2]
        $aApps[UBound($aApps)-1][0] = '>> ABMELDEN <<'
        $aApps[UBound($aApps)-1][1] = 'Sleep'
    EndIf
    If IniRead($INI, 'winitem', 'restart', '0') = 1 Then
        ReDim $aApps[UBound($aApps)+1][2]
        $aApps[UBound($aApps)-1][0] = '>> NEUSTART <<'
        $aApps[UBound($aApps)-1][1] = 'Restart'
    EndIf
    If IniRead($INI, 'winitem', 'shutdown', '0') = 1 Then
        ReDim $aApps[UBound($aApps)+1][2]
        $aApps[UBound($aApps)-1][0] = '>> HERUNTERFAHREN <<'
        $aApps[UBound($aApps)-1][1] = 'Shutdown'
    EndIf

	For $i = 0 To UBound($aApps) -1
		_GUICtrlCreateListViewImgItem($aApps[$i][0], $idLVmain, $aApps[$i][1])
	Next

	; Position Menü (relativ zur Taskbar) & Größe (abhängig von Menüeinträgen) ändern
	Local $aView = _GUICtrlListView_GetItemRect($hLVmain, _GUICtrlListView_GetItemCount($hLVmain)-1, $LVIR_LABEL)
	Local $iDiffX = $aView[2] - ($nWGUImain - (2 * $nXLVmain))	; Änderung der Listviewbreite für Anpassung Fensterbreite
	Local $iDiffY = $aView[3] - ($nHGUImain - (2 * $nYLVmain))	; Änderung der Listviewhöhe für Anpassung Fensterhöhe
	Local $aWin = WinGetPos($hGUImain)
	Local $tTB = _GetTaskBarProps(True)
	Local $nX = $aWin[0], $nY = $aWin[1], $nW = $aWin[2], $nH = $aWin[3]
	Switch $tTB.sEdge
		Case 'LEFT'
			$nX = $tTB.right
			$nY = 0
		Case 'TOP'
			$nX = 0
			$nY = $tTB.bottom
		Case 'RIGHT'
			$nX = $tTB.left - $nW
			$nY = 0
		Case 'BOTTOM'
			$nX = 0
			$nY = $tTB.top - ($nH + ($iDiffY)) - $nYLVmain
	EndSwitch
	WinMove($hGUImain, '', $nX, $nY, $nW, $nH + ($iDiffY) + 2*$nYLVmain)
	ControlMove($hGUImain, '', $idLVmain, $nXLVmain, $nYLVmain, $nW - (2 * $nXLVmain), $nHLVmain + ($iDiffY) + 2*$nYLVmain)
EndFunc


Func _GUICtrlCreateListViewImgItem($_sText, $_idLV, $_sPathImg, $_iOrdImg=-1)
	Switch $_sPathImg
		Case 'Verwalten'
			$_sPathImg = $SHELL32DLL
			$_iOrdImg = -322
		Case 'Sleep'
			$_sPathImg = $SHELL32DLL
			$_iOrdImg = -212
		Case 'Restart'
			$_sPathImg = $SHELL32DLL
			$_iOrdImg = -239
		Case 'Shutdown'
			$_sPathImg = $SHELL32DLL
			$_iOrdImg = -216
	EndSwitch
	GUICtrlCreateListViewItem('  ' & $_sText, $_idLV)
	GuiCtrlSetImage(-1, $_sPathImg, $_iOrdImg)
EndFunc


Func _ReadAppsFromINI()
	Local $a = IniReadSection($INI, "apps")
	If Not IsArray($a) Then
		$a = StringSplit('|', '|', 2)
		ReDim $a[1][2]
	Else
		_ArraySort($a, 0, 1)
	EndIf
	Return $a
EndFunc


Func _Manage_CreateGUI()
	$hGuiNew = GUICreate('Anwendungen verwalten', 600, 400, 80, (@DesktopHeight/2 -200), -1, $WS_EX_TOPMOST)
	GUISetIcon($SHELL32DLL, -323)
	$hTab = GUICtrlCreateTab(10, 5, 580, 390)

	$tabNew = GUICtrlCreateTabItem('Neue Apps')
	GUICtrlCreateLabel('Programmpfad', 20, 53, 80)
	$inPath = GUICtrlCreateInput('', 100, 50, 455, 20, $ES_READONLY)
	$btSelect = GUICtrlCreateButton('...', 560, 50, 20, 20)
	GUICtrlCreateLabel('Anzeigename', 20, 83, 80)
	$inAlias = GUICtrlCreateInput('', 100, 80, 455, 20)
	_GUICtrlEdit_SetCueBanner($inAlias, 'Kurzname eintragen', True)
	GUICtrlCreateLabel('Eintrag Zwischenspeichern oder gespeicherte/aktuelle Daten Übernehmen', 100, 113)
	$btNext = GUICtrlCreateButton('Zwischenspeicher', 100, 140, 120, 20)
	GUICtrlSetState(-1, $GUI_DISABLE)
	$btEnd = GUICtrlCreateButton('Übernahme/Ende', 435, 140, 120, 20)
	GUICtrlSetState(-1, $GUI_DISABLE)
    GUICtrlCreateGroup(' Windows Menüeinträge (Änderungen werden beim Schließen des Fensters übernommen)', 20, 180, 560, 100)
    $icoLogout   = GUICtrlCreateIcon($SHELL32DLL, -212, 128, 210, 32, 32, $SS_ICON)
    $icoRestart  = GUICtrlCreateIcon($SHELL32DLL, -239, 268, 210, 32, 32, $SS_ICON)
    $icoShutdown = GUICtrlCreateIcon($SHELL32DLL, -216, 408, 210, 32, 32, $SS_ICON)
    $cbLogout   = GUICtrlCreateCheckbox(' Abmelden', 100, 245, Default, Default, $BS_ICON)
    $cbRestart  = GUICtrlCreateCheckbox(' Neu Starten', 230, 245, Default, Default, $BS_ICON)
    $cbShutdown = GUICtrlCreateCheckbox(' Herunterfahren', 370, 245, Default, Default, $BS_ICON)
    GUICtrlCreateGroup("", -99, -99, 1, 1)
	GUICtrlCreateTabItem('')

	$tabDel = GUICtrlCreateTabItem('Apps Löschen')
	GUICtrlCreateLabel('Für zu löschende Apps die Markierung entfernen', 20, 43)
	$btDel = GUICtrlCreateButton('Übernehmen', 500, 40, 80, 20)
	$LVDel = GUICtrlCreateListView('App|Pfad', 20, 65, 560, 325, BitAND(Default,$LVS_NOCOLUMNHEADER), _
										BitOR($LVS_EX_CHECKBOXES,$LVS_EX_GRIDLINES))
	$hLVDel = GUICtrlGetHandle($LVDel)
	_GUICtrlListView_SetColumnWidth($hLVDel, 0, 150)
	_GUICtrlListView_SetColumnWidth($hLVDel, 1, $LVSCW_AUTOSIZE_USEHEADER)
	GUICtrlCreateTabItem('')
EndFunc


Func _Manage_SetLVDelete()
	Local $aApp = _ReadAppsFromINI()
	If UBound($aApps) > 1 Then
		For $i = 1 To UBound($aApp) -1
			GUICtrlCreateListViewItem($aApp[$i][0] & '|' & $aApp[$i][1], $LVDel)
			GUICtrlSetState(-1, $GUI_CHECKED)
		Next
	EndIf
EndFunc


Func _WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
	#forceref $hWnd, $iMsg, $wParam
	Local $HWndLV = $hLVmain
	If Not IsHWnd($HWndLV) Then $HWndLV = GUICtrlGetHandle($HWndLV)
	Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
	Local $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
	Local $iCode = DllStructGetData($tNMHDR, "Code")
	Local $idx
	Switch $hWndFrom
		Case $HWndLV
			$idx = _GUICtrlListView_GetSelectedIndices($HWndLV)
			Switch $iCode
				Case $LVN_ITEMCHANGED
					ToolTip('')
				Case $NM_CLICK
					If $aApps[$idx][1] = "Verwalten" Then
						$bManage = True
						GUISetState(@SW_HIDE, $hGUImain)
					ElseIf $aApps[$idx][1] = "Sleep" Then
						GUISetState(@SW_HIDE, $hGUImain)
						Shutdown(0+32) ; $SD_LOGOFF
					ElseIf $aApps[$idx][1] = "Restart" Then
						GUISetState(@SW_HIDE, $hGUImain)
						Shutdown(1+2+4) ; $SD_LOGOFF + $SD_REBOOT + $SD_FORCE
					ElseIf $aApps[$idx][1] = "Shutdown" Then
						GUISetState(@SW_HIDE, $hGUImain)
						Shutdown(1+4+8) ; $SD_SHUTDOWN + $SD_FORCE + $SD_POWERDOWN
					Else
						ShellExecute($aApps[$idx][1])
						GUISetState(@SW_HIDE, $hGUImain)
					EndIf
				Case $NM_RCLICK
					ToolTip($aApps[$idx][1])
			EndSwitch
	EndSwitch
	Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY


;===================================================================================================
; Function Name....: _GetTaskBarProps
; Description......: gibt Eigenschaften und Position der Taskbar zurück
; Parameter(s).....: [optional] $_bReturnStruct - Rückgabe als (True) Struktur, (False - Standard) Array
; Return Value(s)..: Array / Struktur mit den Taskbarwerten
; Author...........: BugFix
;===================================================================================================
Func _GetTaskBarProps($_bReturnStruct=False)
    Local Const $ABM_GETSTATE = 0x4         ; specify the cbSize.
    Local Const $ABM_GETTASKBARPOS = 0x5    ; specify the cbSize.
    Local Const $ABM_GETAUTOHIDEBAR = 0x7   ; specify the cbSize and uEdge. Returns the handle to the autohide appbar

    ; ABM_GETSTATE
    Local Const $ABS_AUTOHIDE = 0x1
    Local Const $ABS_ALWAYSONTOP = 0x2  ; ab Win7 nicht mehr!! --> wenn NICHT $ABS_AUTOHIDE ist es $ABS_ALWAYSONTOP

    ; uEdge - ABM_GETAUTOHIDEBAR
    Local Const $ABE_LEFT = 0
    Local Const $ABE_TOP = 1
    Local Const $ABE_RIGHT = 2
    Local Const $ABE_BOTTOM = 3

    Local $hWndAppBar = DllCall("user32.dll", 'long', "FindWindowA", 'str', "Shell_traywnd", 'str', "")[0]
    Local $iState, $iEdge, $hWnd, $shell32 = DllOpen("shell32")
    Local $tRC = DllStructCreate("struct;int left;int top;int right;int bottom;endstruct")
    Local $tAPPBARDATA = DllStructCreate("dword cbSize;int hWnd;uint;uint uEdge;int left;int top;int right;int bottom;int")
    $tAPPBARDATA.cbSize = DllStructGetSize($tAPPBARDATA)
    $tAPPBARDATA.hWnd = $hWndAppBar

    ; get pos
    $ret = DllCall($shell32, 'int', 'SHAppBarMessage', 'int', $ABM_GETTASKBARPOS, 'ptr', DllStructGetPtr($tAPPBARDATA))
    For $i = 1 To 4
        DllStructSetData($tRC, $i, DllStructGetData($tAPPBARDATA, $i+4))
    Next

    ; get state
    $ret = DllCall($shell32, 'int', 'SHAppBarMessage', 'int', $ABM_GETSTATE, 'ptr', DllStructGetPtr($tAPPBARDATA))
    $iState = $ret[0]

    ; get Edge
    If $iState = $ABS_AUTOHIDE Then
        For $i = 0 To 3
            $tAPPBARDATA.uEdge = $i
            $ret = DllCall($shell32, 'int', 'SHAppBarMessage', 'int', $ABM_GETAUTOHIDEBAR, 'ptr', DllStructGetPtr($tAPPBARDATA))
            $hWnd = $ret[0]
            If $hWnd Then
                $iEdge = $i
                ExitLoop
            EndIf
        Next
        DllClose($shell32)
    Else
        If $tRC.top < 1 And $tRC.left < 1 Then
                If $tRC.bottom > $tRC.right Then $iEdge = 0
                If $tRC.right > $tRC.bottom Then $iEdge = 1
        ElseIf $tRC.top < 1 And $tRC.left > 0 Then
            $iEdge = 2
        Else
            $iEdge = 3
        EndIf
    EndIf

    ; return array or structure: [.hWnd, .autohide(True/False), .uEdge(0..3), .sEdge(left..bottom), .left, .top, .right, .bottom]
    Local $aEdge[] = ['LEFT','TOP','RIGHT','BOTTOM']
    Local $aRet[] = [$hWndAppBar, ($iState = $ABS_AUTOHIDE), $iEdge, $aEdge[$iEdge], $tRC.left, $tRC.top, $tRC.right, $tRC.bottom]
    Local $tRet = DllStructCreate('int hWnd;bool autohide;uint uEdge;char sEdge[' & StringLen($aEdge[$iEdge]) & '];int left;int top;int right;int bottom;')
    For $i = 0 To UBound($aRet) -1
        DllStructSetData($tRet, $i+1, $aRet[$i])
    Next

    Return ($_bReturnStruct ? $tRet : $aRet)
EndFunc  ;==>_GetTaskBarProps