Что нового

Создание вложенного меню по данным из ini-файла

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
По поводу иконок. Если объединить последний код в этой ветке с этим, то получится то, что надо. Иконки будут работать только в Windows Vista и выше.

Settings.ini

Код:
[Menu]
Open=Файл>Открыть
Save=Файл>Сохранить
SaveAs=Файл>Сохранить как...
Copy=Редактировать>Копировать
Paste=Редактировать>Вставить
Separator=*
Skin=Настройки>Интерфейс>Скин
Size=Настройки>Интерфейс>Размеры
Color=Настройки>Интерфейс>Цвет
Separator=Настройки>Интерфейс>*
Expert=Настройки>Интерфейс>Дополнительно...
Miscellaneous=Настройки>Общие
Separator=*
Exit=Выход

[Icon]
Open=shell32.dll,45
Save=shell32.dll,258
SaveAs=shell32.dll,68
Copy=shell32.dll,134
Paste=shell32.dll,260
Skin=shell32.dll,303
Size=shell32.dll,240
Color=mspaint.exe,0
Expert=shell32.dll,300
Miscellaneous=shell32.dll,21
Exit=shell32.dll,131

Example.au3

Код:
#NoTrayIcon

#Include <Constants.au3>
#Include <GDIPlus.au3>
#Include <GUIMenu.au3>
#Include <WinAPIEx.au3>
#Include <WindowsConstants.au3>

Opt('TrayMenuMode', 3)

Global $ID

#cs

$ID[i][0] - ID of the menu item
$ID[i][1] - Command

#ce

If Not _IniCreateTrayMenu(-1, @ScriptDir & '\Settings.ini', $ID) Then
	MsgBox(16, 'Error', 'Ошибка создания меню.')
	Exit
EndIf

TraySetState()

While 1
	$Msg = TrayGetMsg()
	Switch $Msg
		Case 0
			ContinueLoop
		Case Else
			For $i = 1 To $ID[0][0]
				If $ID[$i][0] = $Msg Then
					; Здесь проверяются все пункты меню по их ключу в .ini файле
					Switch $ID[$i][1]
;						Case 'Open'

;						Case 'Save'

;						Case ...

						Case 'Exit'
							Exit
						Case Else
							MsgBox(0, '', $ID[$i][1])
					EndSwitch
					ExitLoop
				EndIf
			Next
	EndSwitch
WEnd

Func _Create32BitHBITMAP($hIcon, $fDelete = 0)

	Local $tBI, $tBIHDR, $hDC, $hDstDC, $hDstSv, $hSrcDC, $hSrcSv, $hBitmap, $hImage, $hObj, $tBits, $pBits, $aIcon, $Width, $Height, $Alpha = True, $Error = False

	$aIcon = _WinAPI_GetIconInfo($hIcon)
	If Not IsArray($aIcon) Then
		Return 0
	EndIf
	$tBI = DllStructCreate($tagBITMAP)
	If _WinAPI_GetObject($aIcon[5], DllStructGetSize($tBI), DllStructGetPtr($tBI)) Then
		$Width = DllStructGetData($tBI, 'bmWidth')
		$Height = DllStructGetData($tBI, 'bmHeight')
		$tBits = DllStructCreate('byte[' & (4 * $Width * $Height) & ']')
		If _WinAPI_GetBitmapBits($aIcon[5], DllStructGetSize($tBits), DllStructGetPtr($tBits)) Then
;~			If StringReplace(StringRegExpReplace(DllStructGetData($tBits, 1), '0x|(.{6})(.{2})', '\2'), '0', '') Then
			If StringRegExp(DllStructGetData($tBits, 1), '^(0x)?(.{6}(00))+\Z') Then
				$Alpha = 0
			EndIf
		Else
			$Error = 1
		EndIf
	Else
		$Error = 1
	EndIf
	For $i = 4 To 5
		_WinAPI_DeleteObject($aIcon[$i])
	Next
	If $Error Then
		Return 0
	EndIf
	$tBIHDR = DllStructCreate($tagBITMAPINFOHEADER)
	DllStructSetData($tBIHDR, 'biSize', DllStructGetSize($tBIHDR))
	DllStructSetData($tBIHDR, 'biWidth', $Width)
	DllStructSetData($tBIHDR, 'biHeight', $Height)
	DllStructSetData($tBIHDR, 'biPlanes', 1)
	DllStructSetData($tBIHDR, 'biBitCount', 32)
	DllStructSetData($tBIHDR, 'biCompression', $BI_RGB)
	$hDC = _WinAPI_GetDC(0)
	$hDstDC = _WinAPI_CreateCompatibleDC($hDC)
	$hBitmap = _WinAPI_CreateDIBSection($hDC, $tBIHDR, $DIB_RGB_COLORS, $pBits)
	$hDstSv = _WinAPI_SelectObject($hDstDC, $hBitmap)
	If $Alpha Then
		_WinAPI_DrawIconEx($hDstDC, 0, 0, $hIcon, 0, 0, 0, 0, $DI_NORMAL)
	Else
		_GDIPlus_Startup()
		$hImage = DllCall($ghGDIPDll, 'uint', 'GdipCreateBitmapFromHICON', 'ptr', $hIcon, 'ptr*', 0)
		If (Not @error) And (Not $hImage[0]) Then
			$hObj = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage[2])
			_GDIPlus_ImageDispose($hImage[2])
		Else
			$hObj = 0
		EndIf
		_GDIPlus_Shutdown()
		If $hObj Then
			$hSrcDC = _WinAPI_CreateCompatibleDC($hDC)
			$hSrcSv = _WinAPI_SelectObject($hSrcDC, $hObj)
			_WinAPI_BitBlt($hDstDC, 0, 0, $Width, $Height, $hSrcDC, 0, 0, $SRCCOPY)
			_WinAPI_SelectObject($hSrcDC, $hSrcSv)
			_WinAPI_DeleteObject($hObj)
			_WinAPI_DeleteDC($hSrcDC)
		Else
			$Error = 1
		EndIf
	EndIf
	_WinAPI_ReleaseDC(0, $hDC)
	_WinAPI_SelectObject($hDstDC, $hDstSv)
	_WinAPI_DeleteDC($hDstDC)
	If $fDelete Then
		_WinAPI_DestroyIcon($hIcon)
	EndIf
	If $Error Then
		_WinAPI_DeleteObject($hBitmap)
		Return 0
	EndIf
	Return $hBitmap
EndFunc   ;==>_Create32BitHBITMAP

Func _IniCreateTrayMenu($CtrlID, $sPath, ByRef $aID, $sRoot = '', $iLevel = 0)

	Static $aIco, $aIni

	Local $hWnd, $Icon, $Item, $Menu, $Path, $Index = -1

	If Not $iLevel Then
		$aIni = IniReadSection($sPath, 'Menu')
		If @error Then
			Return 0
		Else
			$Item = 0
			For $i = 1 To $aIni[0][0]
				If ($aIni[$i][1]) And (StringRegExpReplace($aIni[$i][1], '^.*>', '') <> '*') Then
					$Item += 1
				EndIf
			Next
			If $Item Then
				Dim $aID[$Item + 1][2] = [[0]]
			Else
				$aID = 0
				Return 0
			EndIf
		EndIf
		If _WinAPI_GetVersion() >= '6.0' Then
			$aIco = IniReadSection($sPath, 'Icon')
		Else
			$aIco = 0
		EndIf
	EndIf
	While 1
		$aIni[0][1] += 1
		If $aIni[0][1] > $aIni[0][0] Then
			Return 1
		EndIf
		$aIni[$aIni[0][1]][1] = StringRegExpReplace(StringRegExpReplace($aIni[$aIni[0][1]][1], '\A>*|>*\Z', ''), '>+', '>')
		If Not $aIni[$aIni[0][1]][1] Then
			ContinueLoop
		EndIf
		$Path = StringRegExpReplace($aIni[$aIni[0][1]][1], '>.[^>]*\Z', '')
		$Item = StringSplit($aIni[$aIni[0][1]][1], '>')
		If $Item[0] = 1 Then
			If $sRoot Then
				$aIni[0][1] -= 1
				Return 1
			EndIf
		Else
			If $Path <> $sRoot Then
				If (Not $sRoot) Or (StringInStr($Path, $sRoot) = 1) Then
					If $sRoot Then
						$Item = StringSplit(StringReplace($aIni[$aIni[0][1]][1], $sRoot & '>', '', 1), '>')
						$Path = $sRoot & '>' & $Item[1]
					Else
						$Item = StringSplit($aIni[$aIni[0][1]][1], '>')
						$Path = $Item[1]
					EndIf
					$aIni[0][1] -= 1
					$Menu = TrayCreateMenu($Item[1], $CtrlID)
					If Not _IniCreateTrayMenu($Menu, '', $aID, $Path, $iLevel + 1) Then
						Return 0
					EndIf
					$Index += 1
				Else
					$aIni[0][1] -= 1
					Return 1
				EndIf
				ContinueLoop
			EndIf
		EndIf
		$Index += 1
		If $Item[$Item[0]] <> '*' Then
			$aID[0][0] += 1
			$aID[$aID[0][0]][0] = TrayCreateItem($Item[$Item[0]], $CtrlID)
			$aID[$aID[0][0]][1] = $aIni[$aIni[0][1]][0]
		Else
			TrayCreateItem('', $CtrlID)
		EndIf
		If IsArray($aIco) Then
			For $i = 1 To $aIco[0][0]
				If $aIco[$i][0] = $aIni[$aIni[0][1]][0] Then
					If $CtrlID <> -1 Then
						$hWnd = TrayItemGetHandle($CtrlID)
					Else
						$hWnd = TrayItemGetHandle(0)
					EndIf
					$Icon = _WinAPI_PathParseIconLocation($aIco[$i][1])
					If Not @error Then
						_GUICtrlMenu_SetItemBmp($hWnd, $Index, _Create32BitHBITMAP(_WinAPI_ShellExtractIcon($Icon[0], $Icon[1], 16, 16), 1))
					EndIf
					ExitLoop
				EndIf
			Next
		EndIf
	WEnd
	Return 1
EndFunc   ;==>_IniCreateTrayMenu


Тоже самое, только для контекстного меню.

Код:
#Include <Array.au3>
#Include <Constants.au3>
#Include <GDIPlus.au3>
#Include <GUIConstantsEx.au3>
#Include <GUIMenu.au3>
#Include <WinAPIEx.au3>
#Include <WindowsConstants.au3>

Global $ID

#cs

$ID[i][0] - ID of the menu item
$ID[i][1] - Command

#ce

GUICreate('MyGUI', 400, 400)

$Menu = GUICtrlCreateContextMenu()
If Not _IniCreateMenu($Menu, @ScriptDir & '\Settings.ini', $ID) Then
	MsgBox(16, 'Error', 'Ошибка создания меню.')
	Exit
Else
;	_ArrayDisplay($ID)
EndIf

GUISetState()

While 1
	$Msg = GUIGetMsg()
	Switch $Msg
		Case 0
			ContinueLoop
		Case $GUI_EVENT_CLOSE
			Exit
		Case Else
			For $i = 1 To $ID[0][0]
				If $ID[$i][0] = $Msg Then
					; Здесь проверяются все пункты меню по их ключу в .ini файле
					Switch $ID[$i][1]
;						Case 'Open'

;						Case 'Save'

;						Case ...

						Case 'Exit'
							Exit
						Case Else
							ConsoleWrite($ID[$i][1] & @CR)
					EndSwitch
					ExitLoop
				EndIf
			Next
	EndSwitch
WEnd

Func _Create32BitHBITMAP($hIcon, $fDelete = 0)

	Local $tBI, $tBIHDR, $hDC, $hDstDC, $hDstSv, $hSrcDC, $hSrcSv, $hBitmap, $hImage, $hObj, $tBits, $pBits, $aIcon, $Width, $Height, $Alpha = True, $Error = False

	$aIcon = _WinAPI_GetIconInfo($hIcon)
	If Not IsArray($aIcon) Then
		Return 0
	EndIf
	$tBI = DllStructCreate($tagBITMAP)
	If _WinAPI_GetObject($aIcon[5], DllStructGetSize($tBI), DllStructGetPtr($tBI)) Then
		$Width = DllStructGetData($tBI, 'bmWidth')
		$Height = DllStructGetData($tBI, 'bmHeight')
		$tBits = DllStructCreate('byte[' & (4 * $Width * $Height) & ']')
		If _WinAPI_GetBitmapBits($aIcon[5], DllStructGetSize($tBits), DllStructGetPtr($tBits)) Then
;~			If StringReplace(StringRegExpReplace(DllStructGetData($tBits, 1), '0x|(.{6})(.{2})', '\2'), '0', '') Then
			If StringRegExp(DllStructGetData($tBits, 1), '^(0x)?(.{6}(00))+\Z') Then
				$Alpha = 0
			EndIf
		Else
			$Error = 1
		EndIf
	Else
		$Error = 1
	EndIf
	For $i = 4 To 5
		_WinAPI_DeleteObject($aIcon[$i])
	Next
	If $Error Then
		Return 0
	EndIf
	$tBIHDR = DllStructCreate($tagBITMAPINFOHEADER)
	DllStructSetData($tBIHDR, 'biSize', DllStructGetSize($tBIHDR))
	DllStructSetData($tBIHDR, 'biWidth', $Width)
	DllStructSetData($tBIHDR, 'biHeight', $Height)
	DllStructSetData($tBIHDR, 'biPlanes', 1)
	DllStructSetData($tBIHDR, 'biBitCount', 32)
	DllStructSetData($tBIHDR, 'biCompression', $BI_RGB)
	$hDC = _WinAPI_GetDC(0)
	$hDstDC = _WinAPI_CreateCompatibleDC($hDC)
	$hBitmap = _WinAPI_CreateDIBSection($hDC, $tBIHDR, $DIB_RGB_COLORS, $pBits)
	$hDstSv = _WinAPI_SelectObject($hDstDC, $hBitmap)
	If $Alpha Then
		_WinAPI_DrawIconEx($hDstDC, 0, 0, $hIcon, 0, 0, 0, 0, $DI_NORMAL)
	Else
		_GDIPlus_Startup()
		$hImage = DllCall($ghGDIPDll, 'uint', 'GdipCreateBitmapFromHICON', 'ptr', $hIcon, 'ptr*', 0)
		If (Not @error) And (Not $hImage[0]) Then
			$hObj = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage[2])
			_GDIPlus_ImageDispose($hImage[2])
		Else
			$hObj = 0
		EndIf
		_GDIPlus_Shutdown()
		If $hObj Then
			$hSrcDC = _WinAPI_CreateCompatibleDC($hDC)
			$hSrcSv = _WinAPI_SelectObject($hSrcDC, $hObj)
			_WinAPI_BitBlt($hDstDC, 0, 0, $Width, $Height, $hSrcDC, 0, 0, $SRCCOPY)
			_WinAPI_SelectObject($hSrcDC, $hSrcSv)
			_WinAPI_DeleteObject($hObj)
			_WinAPI_DeleteDC($hSrcDC)
		Else
			$Error = 1
		EndIf
	EndIf
	_WinAPI_ReleaseDC(0, $hDC)
	_WinAPI_SelectObject($hDstDC, $hDstSv)
	_WinAPI_DeleteDC($hDstDC)
	If $fDelete Then
		_WinAPI_DestroyIcon($hIcon)
	EndIf
	If $Error Then
		_WinAPI_DeleteObject($hBitmap)
		Return 0
	EndIf
	Return $hBitmap
EndFunc   ;==>_Create32BitHBITMAP

Func _IniCreateMenu($CtrlID, $sPath, ByRef $aID, $sRoot = '', $iLevel = 0)

	Static $aIco, $aIni

	Local $Icon, $Item, $Menu, $Path, $Index = -1

	If Not $iLevel Then
		$aIni = IniReadSection($sPath, 'Menu')
		If @error Then
			Return 0
		Else
			$Item = 0
			For $i = 1 To $aIni[0][0]
				If ($aIni[$i][1]) And (StringRegExpReplace($aIni[$i][1], '^.*>', '') <> '*') Then
					$Item += 1
				EndIf
			Next
			If $Item Then
				Dim $aID[$Item + 1][2] = [[0]]
			Else
				$aID = 0
				Return 0
			EndIf
		EndIf
		If _WinAPI_GetVersion() >= '6.0' Then
			$aIco = IniReadSection($sPath, 'Icon')
		Else
			$aIco = 0
		EndIf
	EndIf
	While 1
		$aIni[0][1] += 1
		If $aIni[0][1] > $aIni[0][0] Then
			Return 1
		EndIf
		$aIni[$aIni[0][1]][1] = StringRegExpReplace(StringRegExpReplace($aIni[$aIni[0][1]][1], '\A>*|>*\Z', ''), '>+', '>')
		If Not $aIni[$aIni[0][1]][1] Then
			ContinueLoop
		EndIf
		$Path = StringRegExpReplace($aIni[$aIni[0][1]][1], '>.[^>]*\Z', '')
		$Item = StringSplit($aIni[$aIni[0][1]][1], '>')
		If $Item[0] = 1 Then
			If $sRoot Then
				$aIni[0][1] -= 1
				Return 1
			EndIf
		Else
			If $Path <> $sRoot Then
				If (Not $sRoot) Or (StringInStr($Path, $sRoot) = 1) Then
					If $sRoot Then
						$Item = StringSplit(StringReplace($aIni[$aIni[0][1]][1], $sRoot & '>', '', 1), '>')
						$Path = $sRoot & '>' & $Item[1]
					Else
						$Item = StringSplit($aIni[$aIni[0][1]][1], '>')
						$Path = $Item[1]
					EndIf
					$aIni[0][1] -= 1
					$Menu = GUICtrlCreateMenu($Item[1], $CtrlID)
					If Not _IniCreateMenu($Menu, '', $aID, $Path, $iLevel + 1) Then
						Return 0
					EndIf
					$Index += 1
				Else
					$aIni[0][1] -= 1
					Return 1
				EndIf
				ContinueLoop
			EndIf
		EndIf
		$Index += 1
		If $Item[$Item[0]] <> '*' Then
			$aID[0][0] += 1
			$aID[$aID[0][0]][0] = GUICtrlCreateMenuItem($Item[$Item[0]], $CtrlID)
			$aID[$aID[0][0]][1] = $aIni[$aIni[0][1]][0]
		Else
			GUICtrlCreateMenuItem('', $CtrlID)
		EndIf
		If IsArray($aIco) Then
			For $i = 1 To $aIco[0][0]
				If $aIco[$i][0] = $aIni[$aIni[0][1]][0] Then
					$Icon = _WinAPI_PathParseIconLocation($aIco[$i][1])
					If Not @error Then
						_GUICtrlMenu_SetItemBmp(GUICtrlGetHandle($CtrlID), $Index, _Create32BitHBITMAP(_WinAPI_ShellExtractIcon($Icon[0], $Icon[1], 16, 16), 1))
					EndIf
					ExitLoop
				EndIf
			Next
		EndIf
	WEnd
	Return 1
EndFunc   ;==>_IniCreateMenu


post_img_072.png
 

Garrett

Модератор
Локальный модератор
Сообщения
3,999
Репутация
967
Yashied
IMHO это всё дело, должно укладываться в одну готовую функцию _IniCreateMenu("menu.ini", 0)

Второе значение:
0 - контекстного меню
1 - меню в трее

P.S. Однозначно в "Полезняшки"
 

sss

Продвинутый
Сообщения
332
Репутация
96
А можно прикрутить иконки в Windows XP?
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Спасибо за предложенные варианты!

В итоге я написал несколько модифицированный вариант gregaz'а

Код:
$aIni = IniReadSection("Программа.ini", "Меню")
    Dim $aMenuItems[$aIni[0][0]+1]
    $aMenuItems[0] = $aIni[0][0]

	For $i = 1 To $aIni[0][0]
		$aMenu=StringRegExp ( $aIni[$i][0] ,"([^>]*)\s*>\s*([^>]*)\s*", 3)
		if @error = 0 Then 
			If Not StringInStr ($submenues, $aMenu[0]) Then
				$hMenu=GUICtrlCreateMenu($aMenu[0], $nContextMenu)
				$submenues = $submenues & $aMenu[0]
				$aMenuItems[$i] = GUICtrlCreateMenuItem($aMenu[1], $hMenu)
			Else 
				$aMenuItems[$i] = GUICtrlCreateMenuItem($aMenu[1], $hMenu)
			EndIf
		Else
			$aMenuItems[$i] = GUICtrlCreateMenuItem($aIni[$i][0], $nContextMenu)
		EndIf
	Next



Т.е. названия всех сабменюшек добавляются в строку $submenues.
Новое сабменю создается только в том случае, если имя этого сабменю не было добавлено в $submenues ранее.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
Мне если честно с трудом представляется надобность создания динамического меню учитывая то, что опрос действий пунктов данного меню, нужно “прошивать” в скрипте.
Если уже делать меню из ini, то и функций запуска (команды каждого пункта) должны в нём (в ini) прописываться.
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Тогда уж наверное надо упростить :
Код:
;........................
If Not StringInStr ($submenues, $aMenu[0]) Then
   $hMenu=GUICtrlCreateMenu($aMenu[0], $nContextMenu)
   $submenues = $submenues & $aMenu[0]
EndIf   
$aMenuItems[$i] = GUICtrlCreateMenuItem($aMenu[1], $hMenu)
;........................
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
CreatoR сказал(а):
Мне если честно с трудом представляется надобность создания динамического меню...

Мне тоже.

:smile:
 

Dark-Side

Знающий
Сообщения
72
Репутация
17
Garrett сказал(а):
Yashied
IMHO это всё дело, должно укладываться в одну готовую функцию _IniCreateMenu("menu.ini", 0)

Второе значение:
0 - контекстного меню
1 - меню в трее

P.S. Однозначно в "Полезняшки"

Да, Yashied, оформи это, пожалуйста, как функцию :smile:
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
Yashied сказал(а):
CreatoR сказал(а):
Мне если честно с трудом представляется надобность создания динамического меню...

Мне тоже.

:smile:
Ты немного “выбил” суть из контекста таким цитированием :-X... я даже очень представляю себе надобность динамического меню, и даже пользуюсь им, но вот “прошивать” часть исполнения этого меню в скрипте, мне видится бесполезным.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Ну почему же? Можно сделать набор предустановленных действий, например:

Copy
Paste
Save
SaveAs
и т.д.

А пользователь только формирует саму структуру меню, так сказать под себя. А для собственных команд можно добавить раздел "[Actions]", аналогично "[Icons]". Но я на этом не настаиваю.

:smile:
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Если кто оформит это в нормальную функцию, то я буду очень рад!
 

sss

Продвинутый
Сообщения
332
Репутация
96
полностью согласен с Yashied: сейчас сам такую менюшку делаю, уже 38 предуст. действий). Думал про [Actions]: писать туда код AutoItовский неудобно, проще сделать предуст. действие, а если писать какими-то операторами типа execute explorer.exe - все равно надо заносить в сам скрипт. Я сделал так - дописал в секцию инфо строку вида Run1_path=C:\WINDOWS\explorer.exe и сделал действие Run1, которое запускало файл, который там указан. Не оптимально, но удобно.
 

Webarion

Осваивающий
Сообщения
143
Репутация
25
Оставлю, чтобы было :smile:
Мне больше по душе собирать меню из структуры, где вложенность создаётся посредством табов в текстовом файле:

menu.txt - при создании текстового файла, все <TAB> заменить на реальные табуляторы. Будет красивее
Код:
#********************************************************
 Конфигурационный Файл меню
#********************************************************

Файл
<TAB>Открыть|open ;можно определить ключ для обработки клика, но можно и без него
<TAB>Сохранить|param1=Test params ;если необходимы параметры
<TAB>Сохранить как
Редактировать
<TAB>Копировать
<TAB>Вставить
- ; это разделитель
Настройки
<TAB>Интерфейс
<TAB><TAB>Скин
<TAB><TAB>Размеры
<TAB><TAB>Цвет
<TAB><TAB>-
<TAB><TAB>Дополнительно...
<TAB>Общие
-
Выход

Example.au3
Код:
GUICreate('MyGUI', 400, 400)

$Menu = GUICtrlCreateContextMenu()

Global $itemID[$Menu + 1]

CreateMenu(@ScriptDir & '\menu.txt', $Menu)

GUISetState()

;**********************************************************************
; ОБРАБОТКА КЛИКА
;**********************************************************************
While 1
	Local $Msg = GUIGetMsg()
	Switch $Msg
		Case 0
			ContinueLoop
		Case Else
			If $Msg > $Menu And $Msg < UBound($itemID) Then
				$params = $itemID[$Msg][1]
				$sName = _getParam($params, 'name')

				If _getParam($params, 'open') Then ConsoleWrite('Получен клик по ключу open. ') ; обрабатываем пункт по ключу

				If $sName = 'Сохранить' Then
					ConsoleWrite('Получен клик по имени «' & $sName & '». Параметр пункта param1=' & _getParam($params, 'param1') & @CRLF) ; обрабатываем пункт по названию
				Else ; для примера, показываем любой кликнутый пункт
					If $sName Then ConsoleWrite('Кликнут пункт «' & $sName & '»' & @CRLF) ;
				EndIf

				If _getParam($params, 'Выход') Then Exit

			EndIf

	EndSwitch
WEnd


;**********************************************************************
; ФУНКЦИЯ ДОБАВЛЕНИЯ КОНТЕКСТНОГО МЕНЮ
;**********************************************************************
Func CreateMenu($source, $tb = -1)
	Local $aLines[0], $parsLine, $prevTabs, $nextTabs, $sLine, $sName, $LV[] = [$tb]
	If FileExists($source) Then
		Local $hFileOpen = FileOpen($source, 0)
		If $hFileOpen = -1 Then
			MsgBox(4096, 'Ошибка', 'Ошибка чтения конфигурационного файла меню [' & $source & ']')
			Return SetError(1, 0, 0)
		EndIf
		$source = FileRead($hFileOpen)
		FileClose($hFileOpen)
	EndIf
	$source = StringRegExpReplace($source, '(.*?)\s*(;.*)', '$1')  ; удаляет однострочный комментарий после точки с запятой
	$source = StringRegExpReplace($source, '#\d*\D*#.*', '') 	     ; удаляет блок многострочного комментария #...#...
	$source = StringRegExpReplace($source, '^\s*\r|^\s*\r?\n', '') ; удаляет первую пустую строку
	$source = StringRegExpReplace($source, '\s*[\r\n]+', @LF)  ; удаляет пустые строки
	$aLines = StringSplit($source, @LF)
	For $i = 1 To $aLines[0] ;проходим по всем строкам $aLines[$i] - необработанная строка с табами
		$parsLine = StringSplit($aLines[$i], @TAB)
		$sLine = $parsLine[$parsLine[0]]
		If Not $sLine Then ContinueLoop
		$prevTabs = $parsLine[0] - 1 ; получаем количество табов в строке

		; проверяем сколько табов имеет следующий пункт меню
		If UBound($aLines) > $i + 1 Then
			For $tt = $i + 1 To UBound($aLines) - 1; идём дальше
				$nextLine = StringStripWS($aLines[$tt], 2)
				$nextTabs = StringSplit($nextLine, @TAB)[0] - 1
				If $nextLine Or $nextTabs Then ExitLoop ; выходим из поиска следующей строки, если она не пустая или имеет табы
			Next
		Else
			$nextTabs = $prevTabs
		EndIf
		;

		If $sLine = '-' Then ; если разделитель
			_ArrAdd($itemID, GUICtrlCreateMenuItem('', $LV[$prevTabs])) ; ставим разделитель и записываем его ID
		Else ; если пункт не разделитель
			$sName = _getParam($sLine, 'name') ; получаем имя из строки
			;ПАПКА
			If $nextTabs > $prevTabs Then ; ПАПКА МЕНЮ (проверка, если следующий является вложенным)
				_ArrAdd($itemID, GUICtrlCreateMenu($sName, $LV[$prevTabs]), $sLine)
				If $nextTabs >= UBound($LV) Then ReDim $LV[$nextTabs + 1]
				$LV[$nextTabs] = $itemID[UBound($itemID) - 1][0]
			EndIf
			;ПУНКТ
			If $nextTabs <= $prevTabs Then
				_ArrAdd($itemID, GUICtrlCreateMenuItem($sName, $LV[$prevTabs]), $sLine) ;добавляем пункт меню
			EndIf
		EndIf
	Next
EndFunc   ;==>CreateMenu

Func _getParam($sParams, $sGet = 'name')
	Local $aKey
	$aKey = StringRegExp($sParams, '(?:^|\|)\s*(?i:' & $sGet & ')\s*=\s*([^\|]*?)\s*(?:\||\z)', 1) ; извлекает значение параметра
	If IsArray($aKey) And $aKey[0] Then Return $aKey[0]
	If $sGet = 'name' Then $aKey = StringRegExp($sParams, '(?:^|\|)\s*([^|\s][^=]*?)\s*(?:\||\z)', 1) ; извлекает имя
	If IsArray($aKey) And $aKey[0] Then Return $aKey[0]
	$aKey = StringRegExp($sParams, '(?:^|\|)\s*(' & $sGet & ')\s*(?:\||\z)', 0) ; определяет присутствие одиночного ключа
	If $aKey Then Return 1
	Return 0
EndFunc   ;==>_getParam

Func _ArrAdd(ByRef $aArray, $vVal1, $vVal2 = '')
	ReDim $aArray[UBound($aArray) + 1][2]
	$aArray[UBound($aArray) - 1][0] = $vVal1
	$aArray[UBound($aArray) - 1][1] = $vVal2
EndFunc   ;==>_ArrAdd


Это выручает, когда у меня в приложении несколько десятков пунктов с настройками и параметрами, или если в меню подгружается большой список файлов.
 
Верх