1: /*
2: ============================================================================
3: xoblite™ -> an advanced "extended shell" for Microsoft® Windows® 11
4: Copyright © 2002-2026 Karl Henrik Henriksson [qwilk/@xoblite]
5: Copyright © 2001-2004 The Blackbox for Windows Development Team
6: http://xoblite.net/ + https://github.com/xoblite/
7: ============================================================================
8:
9: Blackbox for Windows is free software, released under the
10: GNU General Public License (GPL version 2 or later), with an extension
11: that allows linking of proprietary modules under a controlled interface.
12: What this means is that plugins etc. are allowed to be released
13: under any license the author wishes. Please note, however, that the
14: original Blackbox gradient math code used in Blackbox for Windows
15: is available under the BSD license.
16:
17: http://www.fsf.org/licenses/gpl.html
18: http://www.fsf.org/licenses/gpl-faq.html#LinkingOverControlledInterface
19: http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
20:
21: This program is distributed in the hope that it will be useful,
22: but WITHOUT ANY WARRANTY; without even the implied warranty of
23: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24: GNU General Public License for more details.
25:
26: For additional license information, please read the included license.html.
27:
28: ============================================================================
29: */
30:
31: #include "MenuCommon.h"
32:
33: #include "..\Settings\Settings.h"
34: #include "..\Plugins\PluginManager.h"
35: #include "..\Graphics\BImage.h"
36:
37: #include "PreviewItem.h"
38: #include "StringItem.h"
39:
40: #include "..\Console\ConsoleMenu.h"
41: #include "..\Dock\DockMenu.h"
42: #include "..\Hotkeys\HotkeysMenu.h"
43: #include "..\Plugins\PluginsMenu.h"
44: #include "..\Settings\ConfigMenu.h"
45: #include "..\Settings\StylesMenu.h"
46: #include "..\Settings\ThemesMenu.h"
47: #include "..\Toolbar\ToolbarMenu.h"
48: #include "..\Workspaces\WorkspacesMenu.h"
49:
50: #include "..\Toolbar\Toolbar.h"
51: #include "..\Desktop\Wallpaper.h"
52:
53: #include <io.h>
54: #include <shlwapi.h>
55:
56: #include <gdiplus.h>
57: using namespace Gdiplus;
58: //GdiplusStartupInput gdiplusStartupInput;
59: //ULONG_PTR gdiplusToken;
60:
61: extern PreviewItem* pPreviewItem;
62: extern Settings *pSettings;
63: extern Toolbar *pToolbar;
64: extern PluginManager *pPluginManager;
65: extern Wallpaper* pWallpaper;
66:
67: int menuCommonMessageSubscription[] = { BB_MENU, BB_HIDEMENU, BB_RECONFIGURE, 0 };
68:
69: //===========================================================================
70:
71: MenuCommon::MenuCommon()
72: {
73: m_pMainMenu = NULL;
74: m_pStylesMenu = NULL;
75: m_pThemesMenu = NULL;
76: m_pWorkspacesMenu = NULL;
77: m_pConfigMenu = NULL;
78: m_pPluginMenu = NULL;
79:
80: m_hTitleFont = 0;
81: m_hFrameFont = 0;
82: m_hGripFont = 0;
83: m_hEditStringFont = 0;
84:
85: currentStyleMenuItem = NULL;
86:
87: // Start GDI+... (used by e.g. the menu PreviewItem)
88: // GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
89: }
90:
91: //====================
92:
93: MenuCommon::~MenuCommon()
94: {
95: if (m_pMainMenu)
96: {
97: // Unsubscribe to previously subscribed Blackbox messages...
98: SendMessage (GetBBWnd(), BB_UNREGISTERMESSAGE, (WPARAM)m_MenusVector[0]->GetWindow(), (LPARAM)menuCommonMessageSubscription);
99: }
100:
101: // Delete all menu objects...
102: if (m_pMainMenu) delete m_pMainMenu;
103: if (m_pStylesMenu) delete m_pStylesMenu;
104: if (m_pThemesMenu) delete m_pThemesMenu;
105: if (m_pWorkspacesMenu) delete m_pWorkspacesMenu;
106: if (m_pConfigMenu) delete m_pConfigMenu;
107: if (m_pPluginMenu) DelMenu(m_pPluginMenu);
108:
109: // Delete all menu fonts...
110: DeleteObject(m_hTitleFont);
111: DeleteObject(m_hFrameFont);
112: DeleteObject(m_hGripFont);
113: DeleteObject(m_hEditStringFont);
114:
115: // Shutdown GDI+... (used by e.g. the menu PreviewItem)
116: // GdiplusShutdown(gdiplusToken);
117: }
118:
119: //===========================================================================
120: // Function: Initialize
121: // Purpose:
122: //===========================================================================
123:
124: void MenuCommon::Initialize(HINSTANCE hInstance)
125: {
126: hInst = hInstance;
127:
128: // Read menu configuration and update menus...
129: Configure();
130: UpdateMainMenu(true);
131:
132: // Assign a default menu root if not explicitly defined...
133: if (!m_pMainMenu)
134: {
135: if (!m_MenusVector.empty()) m_pMainMenu = m_MenusVector[0];
136: }
137:
138: if (m_pMainMenu)
139: {
140: // Subscribe to Blackbox messages applicable to menus...
141: SendMessage(GetBBWnd(), BB_REGISTERMESSAGE, (WPARAM) m_MenusVector[0]->GetWindow(), (LPARAM)menuCommonMessageSubscription);
142: }
143: }
144:
145: //===========================================================================
146: // Function: Configure
147: // Purpose:
148: //===========================================================================
149:
150: void MenuCommon::Configure()
151: {
152: // menu.title configuration
153:
154: //====================
155:
156: // menu.frame configuration
157:
158: //====================
159:
160: // menu.hilite (= selected item) configuration
161:
162: //====================
163:
164: // menu.frame top/bottom padding
165:
166: //====================
167:
168: // Menu bullet configuration...
169: m_nBulletSize = (5 * pSettings->scalingFactorHiDPI);
170: m_nBulletStyle = pSettings->menuBullet;
171: if (!_stricmp(pSettings->menuBulletPosition, "LeftJustify") || !_stricmp(pSettings->menuBulletPosition, "Left")) m_nBulletPosition = DT_LEFT;
172: else m_nBulletPosition = DT_RIGHT;
173:
174: //====================
175:
176: SIZE size;
177: HDC fonthdc = CreateDC("DISPLAY", NULL, NULL, NULL);
178:
179: //====================
180:
181: // Create the menu.title font... (common for all menus)
182: if (m_hTitleFont) DeleteObject(m_hTitleFont);
183: if (strnlen_s(pSettings->MenuTitle->Font, sizeofArray(pSettings->MenuTitle->Font))) m_hTitleFont = CreateFont(pSettings->MenuTitle->FontHeight, 0, 0, 0, pSettings->MenuTitle->FontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, pSettings->MenuTitle->Font);
184: else
185: {
186: LOGFONT logFont;
187: SystemParametersInfo(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
188: m_hTitleFont = CreateFontIndirect(&logFont);
189: }
190:
191: // Calculate the menu.title item height... (common for all menus)
192: if (pSettings->MenuTitle->parentRelative || (pSettings->MenuTitle->FontHeight < 2))
193: {
194: m_nTitleDisabled = true;
195: m_nTitleHeight = pSettings->MenuFrame->borderWidth;
196: }
197: else
198: {
199: m_nTitleDisabled = false;
200:
201: HGDIOBJ oldTitleFont = SelectObject(fonthdc, m_hTitleFont);
202: if (!_stricmp(pSettings->MenuTitle->Font, "nu"))
203: {
204: // ...special handling of an odd font...
205: size.cy = 7 * pSettings->scalingFactorHiDPI;
206: }
207: else GetTextExtentPoint32(fonthdc, "TQkgfp/", 8, &size);
208:
209: if (pSettings->MenuTitle->FontOutline)
210: {
211: // Note: We add to the height for outlined text only; for text shadows we don't [yet?] as they're supposed to be just that, shadows...
212: size.cy += (2 * pSettings->scalingFactorHiDPI);
213: }
214:
215: if (pSettings->MenuTitle->marginWidth > 0)
216: {
217: m_nTitleHeight = size.cy + (2 * pSettings->MenuTitle->marginWidth) + (2 * pSettings->MenuTitle->borderWidth);
218:
219: // char msg[255];
220: // sprintf_s(msg, sizeofArray(msg), "MenuCommon -> m_nTitleHeight == %d", m_nTitleHeight);
221: // SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_INFORMATION_MESSAGE, (LPARAM)msg);
222: }
223: else
224: {
225: if (pSettings->MenuTitle->bevelstyle != BEVEL_FLAT)
226: {
227: if (pSettings->MenuTitle->bevelposition == BEVEL2) m_nTitleHeight = size.cy + 6; // ...i.e. above+below the text we have a bevel of 2 pixels + a "minimum" padding of 1 pixel -> adding 6 pixels to the height...
228: else m_nTitleHeight = size.cy + 4; // ...i.e. above+below the text we have a bevel of 1 pixel + a "minimum" padding of 1 pixel -> adding 4 pixels to the height...
229: }
230: else m_nTitleHeight = size.cy + 2; // ...i.e. above+below the text we only have a "minimum" padding of 1 pixel -> adding 2 pixels to the height...
231:
232: m_nTitleHeight += (2 * pSettings->MenuTitle->borderWidth); // ...and then we add the border on each side to the height as well. Done!
233: }
234:
235: // int minTitleFontHeight = pSettings->MenuTitle->FontHeight + (2 * pSettings->MenuTitle->borderWidth);
236: // if (pSettings->MenuTitle->FontOutline) minTitleFontHeight += 2;
237: // if (m_nTitleHeight < minTitleFontHeight) m_nTitleHeight = minTitleFontHeight;
238:
239: // if (!pSettings->doubleScaleHiDPI) m_nTitleHeight = m_nTitleHeight | 1; // Force odd sized height for non-HiDPI menu bullets to be vertically centered... (cf. heights with HiDPI will instead "always" be even due to the 2x everything)
240:
241: SelectObject(fonthdc, oldTitleFont);
242: }
243:
244: //====================
245:
246: // Create the menu.frame font... (common for all menus)
247: if (m_hFrameFont) DeleteObject(m_hFrameFont);
248: if (strnlen_s(pSettings->MenuFrame->Font, sizeofArray(pSettings->MenuFrame->Font))) m_hFrameFont = CreateFont(pSettings->MenuFrame->FontHeight, 0, 0, 0, pSettings->MenuFrame->FontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, pSettings->MenuFrame->Font);
249: else m_hFrameFont = m_hTitleFont;
250:
251: // Calculate the menu.frame item == menu hilite item height... (common for all menus)
252: HGDIOBJ oldFrameFont = SelectObject(fonthdc, m_hFrameFont);
253: if (!_stricmp(pSettings->MenuTitle->Font, "nu"))
254: {
255: // ...special handling of an odd font...
256: size.cy = 7 * pSettings->scalingFactorHiDPI;
257: }
258: else GetTextExtentPoint32(fonthdc, "TQkgfp/", 8, &size);
259:
260: if (pSettings->MenuFrame->FontOutline || pSettings->MenuActive->FontOutline)
261: {
262: // Note: We add to the height for outlined text only; for text shadows we don't [yet?] as they're supposed to be just that, shadows...
263: size.cy += (2 * pSettings->scalingFactorHiDPI);
264: }
265:
266: // if (pSettings->MenuFrame->marginWidth > 0)
267: // {
268: // m_nSubmenuHeight = size.cy + (2 * pSettings->MenuFrame->marginWidth);
269: // }
270: // else
271: {
272: if (pSettings->MenuActive->bevelstyle != BEVEL_FLAT)
273: {
274: // if (pSettings->MenuActive->bevelposition == BEVEL2) m_nSubmenuHeight = size.cy + 4;
275: // else m_nSubmenuHeight = size.cy + 2;
276: if (pSettings->MenuActive->bevelposition == BEVEL2) m_nSubmenuHeight = size.cy + 6; // ...i.e. above+below the text we have a bevel of 2 pixels + a "minimum" padding of 1 pixel -> adding 6 pixels to the height...
277: else m_nSubmenuHeight = size.cy + 4; // ...i.e. above+below the text we have a bevel of 1 pixel + a "minimum" padding of 1 pixel -> adding 4 pixels to the height...
278: }
279: else m_nSubmenuHeight = size.cy + 2; // ...i.e. above+below the text we only have a "minimum" padding of 1 pixel -> adding 2 pixels to the height...
280:
281: m_nSubmenuHeight += (2 * pSettings->MenuActive->borderWidth); // ...and then we add the border on each side to the height as well. Done!
282: }
283:
284: if (m_nSubmenuHeight < pSettings->MenuFrame->FontHeight) m_nSubmenuHeight = pSettings->MenuFrame->FontHeight; // Failsafe? (e.g. potential menu.frame/hilite mismatch corner cases)
285:
286: // if (!pSettings->doubleScaleHiDPI) m_nSubmenuHeight = m_nSubmenuHeight | 1; // Force odd sized height for non-HiDPI menu bullets to be vertically centered... (cf. heights with HiDPI will instead "always" be even due to the 2x everything)
287:
288: SelectObject(fonthdc, oldFrameFont);
289:
290: //====================
291:
292: if (!pSettings->MenuGrip->parentRelative)
293: {
294: m_nGripDisabled = false;
295:
296: // Create the menu.grip font... (common for all menus)
297: if (m_hGripFont) DeleteObject(m_hGripFont);
298: if (strnlen_s(pSettings->MenuGrip->Font, sizeofArray(pSettings->MenuGrip->Font))) m_hGripFont = CreateFont(pSettings->MenuGrip->FontHeight, 0, 0, 0, pSettings->MenuGrip->FontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, pSettings->MenuGrip->Font);
299: else m_hGripFont = m_hTitleFont;
300:
301: // Calculate the menu.grip item height... (common for all menus)
302: if (pSettings->MenuGrip->FontHeight >= 2)
303: {
304: // menu.grip.fontHeight was defined in the style file
305: // -> use as input to grip height regardless whether text is drawn or not...
306: HGDIOBJ oldGripFont = SelectObject(fonthdc, m_hGripFont);
307: if (!_stricmp(pSettings->MenuTitle->Font, "nu"))
308: {
309: // ...special handling of an odd font...
310: size.cy = 7 * pSettings->scalingFactorHiDPI;
311: }
312: else GetTextExtentPoint32(fonthdc, "TQkgfp/", 8, &size);
313:
314: if (pSettings->MenuGrip->FontOutline)
315: {
316: // Note: We add to the height for outlined text only; for text shadows we don't [yet?] as they're supposed to be just that, shadows...
317: size.cy += (2 * pSettings->scalingFactorHiDPI);
318: }
319:
320: if (pSettings->MenuGrip->marginWidth != 0)
321: {
322: m_nGripHeight = size.cy + (2 * pSettings->MenuGrip->marginWidth) + (2 * pSettings->MenuGrip->borderWidth);
323: }
324: else
325: {
326: if (pSettings->MenuGrip->bevelstyle != BEVEL_FLAT)
327: {
328: if (pSettings->MenuGrip->bevelposition == BEVEL2) m_nGripHeight = size.cy + 6; // ...i.e. above+below the text we have a bevel of 2 pixels + a "minimum" padding of 1 pixel -> adding 6 pixels to the height...
329: else m_nGripHeight = size.cy + 4; // ...i.e. above+below the text we have a bevel of 1 pixel + a "minimum" padding of 1 pixel -> adding 4 pixels to the height...
330: }
331: else m_nGripHeight = size.cy + 2; // ...i.e. above+below the text we only have a "minimum" padding of 1 pixel -> adding 2 pixels to the height...
332:
333: m_nGripHeight += (2 * pSettings->MenuGrip->borderWidth); // ...and then we add the border on each side to the height as well. Done!
334: }
335:
336: // int minimumGripHeight = pSettings->MenuGrip->FontHeight + (2 * pSettings->MenuGrip->borderWidth);
337: // if (pSettings->MenuGrip->FontOutline) minimumGripHeight += 2;
338: // if (m_nGripHeight < minimumGripHeight) m_nGripHeight = minimumGripHeight;
339:
340: SelectObject(fonthdc, oldGripFont);
341: }
342: else
343: {
344: // menu.grip.fontHeight was not defined in the style file
345: // -> menu.grip.marginWidth defines the grip height (excluding borders)...
346: // int tempHeight = pSettings->MenuGrip->marginWidth;
347: // if (tempHeight < 4) tempHeight = 4;
348: // m_nGripHeight = tempHeight + (pSettings->MenuGrip->borderWidth * 2);
349: if (pSettings->MenuGrip->marginWidth == 0)
350: {
351: m_nGripDisabled = true;
352: m_nGripHeight = pSettings->MenuFrame->borderWidth;
353: }
354: else
355: {
356: if (pSettings->MenuGrip->borderWidth < pSettings->MenuFrame->borderWidth)
357: {
358: m_nGripHeight = pSettings->MenuGrip->marginWidth + (pSettings->MenuFrame->borderWidth * 2);
359: }
360: else m_nGripHeight = pSettings->MenuGrip->marginWidth + (pSettings->MenuGrip->borderWidth * 2);
361: }
362: }
363: /*
364: int tempHeight;
365:
366: if (pSettings->MenuGrip->FontHeight > 0)
367: {
368: // menu.grip.fontHeight was defined in the style file -> use as input to grip height regardless whether text is drawn or not...
369: tempHeight = pSettings->MenuGrip->FontHeight + (pSettings->MenuGrip->marginWidth * 2);
370: }
371: else
372: {
373: // menu.grip.fontHeight was not defined in the style file -> menu.grip.marginWidth defines the grip height (excluding borders)...
374: tempHeight = pSettings->MenuGrip->marginWidth;
375: }
376:
377: if (tempHeight < 4) tempHeight = 4;
378: m_nGripHeight = tempHeight + (pSettings->MenuGrip->borderWidth * 2);
379: */
380: }
381: else // -> menu.grip disabled
382: {
383: m_nGripDisabled = true;
384: m_nGripHeight = pSettings->MenuFrame->borderWidth;
385: }
386:
387: //====================
388:
389: // Create the font for the StringItem editor popup window... (common for all menus, always using the Windows default font - Segoe UI - for easy readability)
390: if (m_hEditStringFont) DeleteObject(m_hEditStringFont);
391: int editStringFontSize = (15 * pSettings->scalingFactorHiDPI);
392: m_hEditStringFont = CreateFont(editStringFontSize, 0, 0, 0, FW_BOLD, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Segoe UI");
393:
394: //====================
395:
396: DeleteDC(fonthdc);
397:
398: //====================
399:
400: // Calculate the height of menu separators... (common for all menus)
401: m_nSeparatorHeight = (5 * pSettings->scalingFactorHiDPI);
402:
403: // Calculate the height of menu margins... (common for all menus)
404: m_nMarginHeight = pSettings->MenuFrame->marginWidth;
405:
406: //====================
407:
408: // Calculate the width of menu indents... (common for all menus)
409: // int smallIndent = 3 + pSettings->MenuFrame->marginWidth + pSettings->MenuFrame->borderWidth;
410: // if (pSettings->doubleScaleHiDPI) smallIndent += 3;
411: // int bulletIndent = 13 + pSettings->MenuFrame->marginWidth + pSettings->MenuFrame->borderWidth;
412: // if (pSettings->doubleScaleHiDPI) bulletIndent += 13;
413: int smallIndent = pSettings->MenuFrame->borderWidth + pSettings->MenuFrame->marginWidth + ((m_nSubmenuHeight - m_nBulletSize) / 4);
414: // int bulletIndent = (2 * (pSettings->MenuFrame->borderWidth + pSettings->MenuFrame->marginWidth)) + m_nBulletSize + ((m_nSubmenuHeight - m_nBulletSize) / 2);
415: int bulletIndent = pSettings->MenuFrame->borderWidth + pSettings->MenuFrame->marginWidth + ((m_nSubmenuHeight - m_nBulletSize) / 2) + m_nBulletSize + ((m_nSubmenuHeight - m_nBulletSize) / 4);
416:
417: if (m_nBulletStyle == MENU_BULLET_EMPTY)
418: {
419: // If no bullet is used, we use equal left/right indents...
420: // (using large indent to still fit bool indicators)
421: m_nLeftIndent = bulletIndent;
422: m_nRightIndent = bulletIndent;
423: }
424: else if (m_nBulletPosition == DT_LEFT)
425: {
426: // If the bullet is on the left, we make the right indent smaller...
427: m_nLeftIndent = bulletIndent;
428: m_nRightIndent = smallIndent;
429: }
430: else
431: {
432: // If the bullet is on the right, we make the left indent smaller...
433: m_nLeftIndent = smallIndent;
434: m_nRightIndent = bulletIndent;
435: }
436:
437: //====================
438:
439: // Calculate submenu gap for rounded corner menus...
440: // (nb. dependence toward the HiDPI adjustments calculated above, so order is important)
441: if (pSettings->MenuFrame->borderWidth > 0) pSettings->submenuGap = (pSettings->MenuFrame->borderWidth * 2); // Note: Use borderWidth*2 due to how menu borders overlap for non-rounded menus!
442: else if (pSettings->MenuFrame->marginWidth > 0) pSettings->submenuGap = pSettings->MenuFrame->marginWidth;
443: else pSettings->submenuGap = 2 * pSettings->scalingFactorHiDPI;
444: }
445:
446: //===========================================================================
447: // Function: UpdateMainMenu
448: // Purpose:
449: //===========================================================================
450:
451: void MenuCommon::UpdateMainMenu(bool createAllMenus)
452: {
453: if (!FileExists(pSettings->menuFile)) pSettings->WriteDefaultMenu();
454:
455: FILE *file = FileOpen(pSettings->menuFile);
456:
457: //====================
458:
459: // Failsafe: The menu.rc configuration file is considered invalid if it does not contain a [begin]...
460: if (!_stricmp(ReadString(pSettings->menuFile, "[begin]", ""), ""))
461: {
462: FileClose(file);
463:
464: char msg[MAX_LINE_LENGTH];
465: strcpy_s(msg, sizeofArray(msg), pSettings->menuFile);
466: strcat_s(msg, sizeofArray(msg),
467: "\n\nYour menu configuration file does not seem to be valid. \n\n"
468: "Do you want to create a new menu configuration file with default items? \n\n"
469: "(clicking \"No\" will open your current menu configuration file for editing) ");
470:
471: if (MessageBox(GetDesktopWindow(), msg, "xoblite", MB_YESNO | MB_ICONERROR | MB_SETFOREGROUND | MB_TOPMOST) == IDYES)
472: {
473: pSettings->WriteDefaultMenu();
474: }
475: else PostMessage(GetBBWnd(), BB_EDITFILE, 1, 0); // Open the current menu.rc file for editing...
476:
477: file = FileOpen(pSettings->menuFile);
478: }
479:
480: //====================
481:
482: // Create the Main menu...
483: if (createAllMenus)
484: {
485: m_pMainMenu = new Menu(hInst);
486: HANDLE hFind = FindFirstFile(pSettings->menuFile, &m_pMainMenu->data);
487: FindClose(hFind);
488: m_MenusVector.push_back(m_pMainMenu);
489: }
490: else m_pMainMenu->DeleteMenuItems();
491:
492: ParseFolder(file, m_pMainMenu);
493:
494: m_pMainMenu->Invalidate();
495: m_pMainMenu->Validate();
496:
497: FileClose(file);
498:
499: //====================
500:
501: if (createAllMenus)
502: {
503: // Create the Styles menu...
504: m_pStylesMenu = new StylesMenu("Styles");
505: m_MenusVector.push_back(m_pStylesMenu);
506:
507: // Create the Themes menu...
508: m_pThemesMenu = new ThemesMenu("Themes");
509: m_MenusVector.push_back(m_pThemesMenu);
510:
511: // Create the Workspaces menu...
512: m_pWorkspacesMenu = new WorkspacesMenu("Workspaces");
513: m_MenusVector.push_back(m_pWorkspacesMenu);
514:
515: // Create the Configuration menu...
516: m_pConfigMenu = new ConfigMenu("Configuration");
517: m_MenusVector.push_back(m_pConfigMenu);
518: }
519: }
520:
521: //===========================================================================
522: // Function: ParseFolder
523: // Purpose:
524: //===========================================================================
525:
526: void MenuCommon::ParseFolder(FILE *file, Menu *pMenu)
527: {
528: for (;;)
529: {
530: char szLineBuffer[MAX_LINE_LENGTH] = "";
531:
532: if (!ReadNextCommand(file, szLineBuffer, sizeof(szLineBuffer))) break;
533: if (!ParseLine(file, pMenu, szLineBuffer)) break;
534: }
535: }
536:
537: //===========================================================================
538: // Function: ParseLine
539: // Purpose:
540: //===========================================================================
541:
542: char itemCommand[MAX_LINE_LENGTH], itemLabel[MAX_LINE_LENGTH], itemData[MAX_LINE_LENGTH];
543:
544: bool MenuCommon::ParseLine(FILE *file, Menu *pMenu, char *pszLine)
545: {
546: itemCommand[0] = itemLabel[0] = itemData[0] = '\0';
547: MenuItem* pMenuItem = NULL;
548: LPSTR tokenBegin, tokenEnd, tokenPtr = pszLine;
549: int nLen;
550:
551: //====================
552:
553: // Skip whitespaces at the beginning of the line...
554: while (*pszLine && isspace(*pszLine)) pszLine++;
555: /*
556: // Check if the line is a valid config line (if it contains a [ it's considered ok)
557: if (!strchr(pszLine, '[')) return true;
558:
559: // Substitute any applivation environment variables, e.g. "$ProgramFiles$"
560: if (strchr(pszLine, '$')) ReplaceShellFolders(pszLine);
561: // Substitute any "%VAR%" for the VAR value if it's a real VAR
562: if (strchr(pszLine, '%')) ReplaceEnvVars(pszLine);
563:
564: // Extract the menu command tag (e.g. [exec])...
565: strcpy_s(itemData, sizeofArray(itemData), Tokenize(pszLine, itemCommand, " "));
566: // Check if the line contains a label tag and if so extract it...
567: if (*itemData == '(') strcpy_s(itemData, sizeofArray(itemData), Tokenize(itemData+1, itemLabel, ")"));
568: // Check if the line contains a data tag and if so extract it...
569: if (*itemData == '{') Tokenize(itemData+1, itemData, "}");
570: */
571:
572: // Extract the menu command tag (e.g. [exec])...
573: tokenBegin = strchr(tokenPtr, '[');
574: if (tokenBegin != NULL)
575: {
576: tokenEnd = strchr(tokenBegin, ']');
577: if (tokenEnd != NULL)
578: {
579: nLen = tokenEnd-tokenBegin+1;
580: if (nLen > 0)
581: {
582: // strncpy(itemCommand, tokenBegin, nLen);
583: strncpy_s(itemCommand, sizeof(itemCommand), tokenBegin, nLen);
584: itemCommand[nLen] = '\0';
585: }
586: else return true;
587: tokenPtr = tokenEnd+1;
588: }
589: else return true;
590: }
591: else return true; // Not a valid menu config line since it does not include a command token, skip to the next line...
592:
593: // Check if the line contains a label tag and if so extract it...
594: tokenBegin = strchr(tokenPtr, '(');
595: if (tokenBegin != NULL)
596: {
597: tokenEnd = strchr(tokenBegin++, ')');
598: if (tokenEnd != NULL)
599: {
600: nLen = tokenEnd-tokenBegin;
601: if (nLen > 0)
602: {
603: // strncpy(itemLabel, tokenBegin, nLen);
604: strncpy_s(itemLabel, sizeof(itemLabel), tokenBegin, nLen);
605: itemLabel[nLen] = '\0';
606: }
607: tokenPtr = tokenEnd+1;
608: }
609: }
610:
611: // Check if the line contains a data tag and if so extract it...
612: tokenBegin = strchr(tokenPtr, '{');
613: if (tokenBegin != NULL)
614: {
615: tokenEnd = strchr(tokenBegin++, '}');
616: if (tokenEnd != NULL)
617: {
618: nLen = tokenEnd-tokenBegin;
619: if (nLen > 0)
620: {
621: // strncpy(itemData, tokenBegin, nLen);
622: strncpy_s(itemData, sizeof(itemData), tokenBegin, nLen);
623: itemData[nLen] = '\0';
624: // Substitute any applivation environment variables, e.g. "$ProgramFiles$"
625: if (strchr(itemData, '$')) ReplaceShellFolders(itemData);
626: // Substitute any "%VAR%" for the VAR value if it's a real VAR
627: if (strchr(itemData, '%')) ReplaceEnvVars(itemData);
628: }
629: }
630: }
631:
632: //====================
633:
634: // If the line contains [begin] we create a title item...
635: if (!_stricmp(itemCommand, "[begin]"))
636: {
637: // If no menu title has been defined, display xoblite version...
638: if (!_stricmp(itemLabel, ""))
639: {
640: strcpy_s(m_szHotListName, sizeofArray(m_szHotListName), "xoblite ");
641: strcat_s(m_szHotListName, sizeofArray(m_szHotListName), (LPSTR)GetBBVersion());
642: }
643: else strcpy_s(m_szHotListName, sizeofArray(m_szHotListName), itemLabel);
644:
645: AddHeaderItem(pMenu, m_szHotListName);
646: return true;
647: }
648:
649: //====================
650:
651: // If the line contains [submenu] we create a subfolder...
652: else if (!_stricmp(itemCommand, "[submenu]"))
653: {
654: // NOTE: We need to save the label string as the ParseLine function uses a single
655: // set of temporary item strings which is also used when parsing the subfolder items
656: // (i.e. the current item label will be replaced by subfolder item labels)
657: char folderLabel[MAX_LINE_LENGTH];
658: strcpy_s(folderLabel, sizeofArray(folderLabel), itemLabel);
659:
660: Menu* pSub = new Menu(hInst);
661: AddHeaderItem(pSub, strnlen_s(itemData, sizeofArray(itemData)) ? itemData : itemLabel);
662: ParseFolder(file, pSub);
663:
664: pSub->Invalidate();
665: pSub->Validate();
666:
667: pMenuItem = new FolderItem(pSub, folderLabel, NULL);
668: }
669:
670: //====================
671:
672: // If the line contains [end] we terminate the line parsing by returning false...
673: else if (!_stricmp(itemCommand, "[end]"))
674: {
675: AddFooterItem(pMenu, itemLabel);
676: return false;
677: }
678:
679: //====================
680:
681: // If the line contains [include] we parse the file to be included...
682: else if (!_stricmp(itemCommand, "[include]"))
683: {
684: char path[MAX_LINE_LENGTH];
685: strcpy_s(path, sizeofArray(path), itemLabel);
686: if (strchr(path, '\"')) StrRemoveEncap(path);
687: if (strchr(path, '$')) ReplaceShellFolders(path);
688: if (strchr(path, '%')) ReplaceEnvVars(path);
689:
690: if (!FileExists(path))
691: {
692: // If the file does not exist, display an error messagebox...
693: // char msg[MAX_LINE_LENGTH];
694: // strcpy_s(msg, sizeofArray(msg), "xoblite was unable to include...\n\n");
695: // strcat_s(msg, sizeofArray(msg), itemLabel);
696: // strcat_s(msg, sizeofArray(msg), "\n\n...into the menu. Please check your menu configuration file. ");
697: // MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONERROR | MB_SETFOREGROUND | MB_TOPMOST);
698: // ...and insert a placeholder "error" menu item...
699: CreateMenuItem(pMenu, MENUITEM_NOP, NULL, NULL, "...[include] failed!", false);
700: }
701: else
702: {
703: // ...otherwise open and parse the file...
704: FILE* includeFile = FileOpen(path);
705: ParseFolder(includeFile, pMenu);
706: FileClose(includeFile);
707: }
708:
709: return true;
710: }
711:
712: //====================
713:
714: // Check if the line is pointing to a dynamic folder...
715: else if (!_stricmp(itemCommand, "[path]"))
716: {
717: pMenuItem = new SpecialFolder(itemLabel, NULL, itemData, FOLDER_PATH);
718: }
719: /*
720: else if (!_stricmp(itemCommand, "[stylesmenu]"))
721: {
722: pMenuItem = new SpecialFolder(itemLabel, NULL, itemData, FOLDER_STYLES);
723: }
724: else if (!_stricmp(itemCommand, "[themesmenu]"))
725: {
726: pMenuItem = new SpecialFolder(itemLabel, NULL, pSettings->themesFolder, FOLDER_THEMES);
727: }
728:
729: else if (!_stricmp(itemCommand, "[stylesdir]"))
730: {
731: InsertFolder(pMenu, itemLabel, FOLDER_STYLES, false);
732: return true;
733: }
734: else if (!_stricmp(itemCommand, "[stylestree]"))
735: {
736: InsertFolder(pMenu, itemLabel, FOLDER_STYLES, true);
737: return true;
738: }
739: */
740: //====================
741: // Check if the line is specifying a special item...
742: /*
743: else if (!_stricmp(itemCommand, "[workspaces]"))
744: {
745: pMenuItem = new FolderItem(new WorkspacesMenu(itemLabel ? itemLabel : "Workspaces"), itemLabel ? itemLabel : "Workspaces", NULL);
746: }
747: else if (!_stricmp(itemCommand, "[tasks]"))
748: {
749: // pMenuItem = new WorkspacesMenuItem("Tasks", L"Tasks", 254);
750: pMenuItem = new WorkspacesMenuItem("Icons", L"Icons", 255);
751: }
752: */
753: else if (!_stricmp(itemCommand, "[configuration]") || !_stricmp(itemCommand, "[config]"))
754: {
755: pMenuItem = new FolderItem(new ConfigMenu(itemLabel ? itemLabel : "Configuration"), itemLabel ? itemLabel : "Configuration", NULL);
756: }
757:
758: //====================
759:
760: else if (!_stricmp(itemCommand, "[separator]"))
761: {
762: CreateMenuItem(pMenu, MENUITEM_SEPARATOR, NULL, NULL, NULL, false);
763: return true;
764: }
765: else if (!_stricmp(itemCommand, "[nop]"))
766: {
767: CreateMenuItem(pMenu, MENUITEM_NOP, NULL, NULL, itemLabel, false);
768: return true;
769: }
770: else
771: {
772: // All other menu items, including regular [exec] items etc...
773: CreateMenuItem(pMenu, MENUITEM_COMMAND, itemCommand, itemData, itemLabel, false);
774: return true;
775: }
776:
777: //====================
778:
779: // Finally, we add the new item to the menu...
780: pMenuItem->SetHeight(m_nSubmenuHeight);
781: pMenu->AddMenuItem(pMenuItem);
782:
783: return true;
784: }
785:
786: //===========================================================================
787: // Function: AddHeaderItem/AddFooterItem
788: // Purpose:
789: //===========================================================================
790:
791: void MenuCommon::AddHeaderItem(Menu* pMenu, LPSTR pszTitle)
792: {
793: // Placeholder fix to support Unicode text for the menu titles...
794: char pszTitleANSI[MAX_LINE_LENGTH];
795: strcpy_s(pszTitleANSI, sizeofArray(pszTitleANSI), pszTitle);
796: strcat_s(pszTitleANSI, sizeofArray(pszTitleANSI), "\0"); // Just in case...
797: wchar_t pszTitleUnicode[MAX_LINE_LENGTH];
798: MultiByteToWideChar(CP_UTF8, 0, pszTitleANSI, strnlen_s(pszTitleANSI, sizeofArray(pszTitleANSI))+1, pszTitleUnicode, MAX_LINE_LENGTH);
799:
800: MenuItem* pHeader = new MenuItem(MENUITEM_HEADER, pszTitle, pszTitleUnicode, NULL, NULL, false);
801: pHeader->SetHeight(m_nTitleHeight);
802: pHeader->SetSortPriority(PRIORITY_HEADER);
803: pMenu->AddMenuItem(pHeader);
804:
805: MenuItem* pMenuItem = new MenuItem(MENUITEM_MARGINPAD, "", NULL, NULL, NULL, false);
806: pMenuItem->SetHeight(m_nMarginHeight);
807: pMenuItem->SetSortPriority(PRIORITY_MARGINPAD_TOP);
808: pMenu->AddMenuItem(pMenuItem);
809: }
810:
811: //====================
812:
813: void MenuCommon::AddFooterItem(Menu* pMenu, LPSTR pszTitle)
814: {
815: // Placeholder fix to support Unicode text for the menu grips...
816: char pszTitleANSI[MAX_LINE_LENGTH];
817: strcpy_s(pszTitleANSI, sizeofArray(pszTitleANSI), pszTitle);
818: strcat_s(pszTitleANSI, sizeofArray(pszTitleANSI), "\0"); // Just in case...
819: wchar_t pszTitleUnicode[MAX_LINE_LENGTH];
820: MultiByteToWideChar(CP_UTF8, 0, pszTitleANSI, strnlen_s(pszTitleANSI, sizeofArray(pszTitleANSI))+1, pszTitleUnicode, MAX_LINE_LENGTH);
821:
822: if (pMenu->m_MenuItems.size() <= 2) // -> Only the menu's title and top marginpad previously created -> This is an otherwise *empty* menu/submenu with no regular menu frame items!
823: {
824: CreateMenuItem(pMenu, MENUITEM_NOP, NULL, NULL, "(No items)", false); // Note: This cunningly also prevents crashes if menu.titles+grips are disabled too... (i.e. no title/frame/grip oddball scenario)
825: }
826:
827: MenuItem* pMenuItem = new MenuItem(MENUITEM_MARGINPAD, "", NULL, NULL, NULL, false);
828: pMenuItem->SetHeight(m_nMarginHeight);
829: pMenuItem->SetSortPriority(PRIORITY_MARGINPAD_BOTTOM);
830: pMenu->AddMenuItem(pMenuItem);
831:
832: MenuItem* pBorder = new MenuItem(MENUITEM_FOOTER, pszTitle, pszTitleUnicode, NULL, NULL, false);
833: pBorder->SetHeight(m_nGripHeight);
834: pBorder->SetSortPriority(PRIORITY_FOOTER);
835: pMenu->AddMenuItem(pBorder);
836: }
837:
838: //===========================================================================
839: // Function: InsertFolder
840: // Purpose:
841: //===========================================================================
842:
843: void MenuCommon::InsertFolder(Menu *pMenu, char* pszFolder, int menuType, bool includeSubFolders)
844: {
845: char folder[MAX_LINE_LENGTH];
846: strcpy_s(folder, sizeofArray(folder), pszFolder);
847:
848: MenuItem* pItem = NULL;
849: char itemCmd[MAX_LINE_LENGTH], itemTitle[MAX_LINE_LENGTH], dirPath[MAX_LINE_LENGTH];
850: bool directoryFound = false, itemFound = false;
851: bool currentStyleFound;
852:
853: //====================
854:
855: // If we're creating a [themesmenu] subfolder we first add the default theme to the menu...
856: if (menuType == FOLDER_THEMES)
857: {
858: char bbpath[MAX_LINE_LENGTH];
859: GetBlackboxPath(bbpath, MAX_LINE_LENGTH);
860: bbpath[strnlen_s(bbpath, sizeofArray(bbpath))-1] = 0;
861: CreateMenuItem(pMenu, MENUITEM_COMMAND, "[theme]", bbpath, "[Default]", !IsInString(pSettings->SF_currentThemePath, "themes"));
862: }
863:
864: //====================
865:
866: char folderToken[MAX_LINE_LENGTH], folderPattern[MAX_LINE_LENGTH];
867:
868: while (strnlen_s(folder, sizeofArray(folder)))
869: {
870: // Get the next folder path token from the input string...
871: strcpy_s(folder, sizeofArray(folder), Tokenize(folder, folderToken, "|"));
872: if (folderToken[0] == '\"') StrRemoveEncap(folderToken);
873:
874: // Does the folder path already contain a wildcard?
875: LPSTR wildcardPointer = strchr(folderToken, '*');
876: if (wildcardPointer != NULL)
877: {
878: // If so, we use the folder path without alteration as search pattern...
879: strcpy_s(folderPattern, sizeofArray(folderPattern), folderToken);
880: int i = (int)(wildcardPointer - folderToken);
881: folderToken[i] = '\0';
882: }
883: else
884: {
885: // If not, we use the default "all files" search pattern...
886: if (folderToken[strnlen_s(folderToken, sizeofArray(folderToken))] != '\\') strcat_s(folderToken, sizeofArray(folderToken), "\\");
887: strcpy_s(folderPattern, sizeofArray(folderPattern), folderToken);
888: strcat_s(folderPattern, sizeofArray(folderPattern), "*");
889: }
890:
891: //====================
892:
893: // Using this pattern, we then search for items in the directory
894: // and process them according to name, attributes etc.
895: _finddata_t found;
896: long handle = _findfirst(folderPattern, &found);
897:
898: if (handle != -1)
899: {
900: do
901: {
902: // Skip hidden files...
903: if (strcmp(found.name, ".") == 0 || strcmp(found.name, "..") == 0 || (found.attrib & _A_HIDDEN))
904: continue;
905:
906: //====================
907:
908: if (found.attrib & _A_SUBDIR)
909: {
910: if (menuType == FOLDER_THEMES) // [themesmenu] items
911: {
912: // To get the full path to the file we copy
913: // the folder path and add the filename...
914: strcpy_s(itemCmd, sizeofArray(itemCmd), folderToken);
915: strcat_s(itemCmd, sizeofArray(itemCmd), found.name);
916:
917: // The title of the menu item is the name of the file
918: strcpy_s(itemTitle, sizeofArray(itemTitle), found.name);
919:
920: // Finally, we create the new menu item...
921: CreateMenuItem(pMenu, MENUITEM_COMMAND, "[theme]", itemCmd, itemTitle, !_stricmp(pSettings->SF_currentThemePath, itemCmd));
922: }
923: else if (includeSubFolders)
924: {
925: // To get the full path to the file we copy the folder path
926: // and add the filename, then add quotes to be on the safe
927: // side during further processing if the path contains spaces...
928: strcpy_s(dirPath, sizeofArray(dirPath), "\"");
929: strcat_s(dirPath, sizeofArray(dirPath), folderToken);
930: strcat_s(dirPath, sizeofArray(dirPath), found.name);
931: strcat_s(dirPath, sizeofArray(dirPath), "\"");
932:
933: // Subfolders are created by adding a new SpecialFolder...
934: pItem = new SpecialFolder(found.name, NULL, dirPath, menuType);
935: pItem->SetHeight(m_nSubmenuHeight);
936: pMenu->AddMenuItem(pItem);
937:
938: directoryFound = true;
939: }
940: }
941:
942: //====================
943:
944: else
945: {
946: if (menuType == FOLDER_STYLES) // [stylesmenu] items
947: {
948: if (IsInString(found.name, ".3dc")) continue;
949:
950: // To get the full path to the file we copy
951: // the folder path and add the filename...
952: strcpy_s(itemCmd, sizeofArray(itemCmd), folderToken);
953: strcat_s(itemCmd, sizeofArray(itemCmd), found.name);
954:
955: // The title of the menu item is the name of the file
956: strcpy_s(itemTitle, sizeofArray(itemTitle), found.name);
957: // Remove any .style or .sty extensions...
958: if (IsInString(itemTitle, ".sty")) PathRemoveExtension(itemTitle);
959:
960: // Is this file the currently applied style?
961: // If so, show the "current style" indicator for this menu item...
962: currentStyleFound = !_stricmp(itemCmd, pSettings->styleFile);
963:
964: // Finally, we create the new menu item...
965: MenuItem *newMenuItem = CreateMenuItem(pMenu, MENUITEM_COMMAND, "[style]", itemCmd, itemTitle, currentStyleFound);
966: if (currentStyleFound) currentStyleMenuItem = newMenuItem;
967:
968: itemFound = true;
969: }
970: else if (menuType == FOLDER_PATH) // [path] items
971: {
972: // To get the full path to the file we copy the folder path
973: // and add the filename, then add quotes to be on the safe
974: // side during further processing if the path contains spaces...
975: strcpy_s(itemCmd, sizeofArray(itemCmd), "\"");
976: strcat_s(itemCmd, sizeofArray(itemCmd), folderToken);
977: strcat_s(itemCmd, sizeofArray(itemCmd), found.name);
978: strcat_s(itemCmd, sizeofArray(itemCmd), "\"");
979:
980: // The title of the menu item is the name of the file,
981: // minus .lnk if it's a symbolic link or .url if it's a URL...
982: strcpy_s(itemTitle, sizeofArray(itemTitle), found.name);
983: if (IsInString(itemTitle, ".lnk") || IsInString(itemTitle, ".url")) PathRemoveExtension(itemTitle);
984:
985: // // Finally, we create the new menu item...
986: // CreateMenuItem(pMenu, MENUITEM_COMMAND, "[exec]", itemCmd, itemTitle, false);
987: // Finally, we create the new menu item... (nb. we convert the title to Unicode here
988: // only to avoid possible later re-conversion mishaps via the regular CreateMenuItem())
989: wchar_t itemTitleUnicode[MAX_LINE_LENGTH];
990: MultiByteToWideChar(CP_ACP, 0, itemTitle, strnlen_s(itemTitle, sizeofArray(itemTitle))+1, itemTitleUnicode, MAX_LINE_LENGTH);
991: CreateMenuItemUnicode(pMenu, MENUITEM_COMMAND, "[exec]", itemCmd, itemTitleUnicode, false);
992:
993: itemFound = true;
994: }
995: }
996:
997: //====================
998: }
999: while (_findnext(handle, &found) == 0);
1000: }
1001:
1002: _findclose(handle);
1003: }
1004:
1005: //====================
1006:
1007: if (directoryFound && itemFound) CreateMenuItem(pMenu, MENUITEM_SEPARATOR, NULL, NULL, NULL, false);
1008:
1009: // When the search is completed, we sort the items,
1010: // but skip the title and top marginWidth padding...
1011: pMenu->Sort(2,0);
1012: }
1013:
1014: //===========================================================================
1015: // Function: Hide
1016: // Purpose:
1017: //===========================================================================
1018:
1019: /*
1020: void MenuCommon::Show(int index)
1021: {
1022: m_MenusVector[index]->Show();
1023: SetFocus(m_MenusVector[index]->GetWindow());
1024: }
1025: */
1026: //====================
1027:
1028: bool MenuCommon::Hide(int index)
1029: {
1030: bool menusHaveBeenHidden = false;
1031:
1032: // Hide all core shell menus...
1033: // (note that since we're addressing the top level parent
1034: // menus we only need to call using the HIDE_CHILDREN flag,
1035: // which will hide the top level menus *and* all of their submenus)
1036: for (int i=0; i < (int)m_MenusVector.size(); i++)
1037: {
1038: if (m_MenusVector[i]->Hide(HIDE_CHILDREN)) menusHaveBeenHidden = true;
1039: }
1040:
1041: // Hide all plugin menus...
1042: if ((m_pPluginMenu != NULL) && !m_pPluginMenu->IsPinned()) // Note: At least for now we only support a single open/pinned plugin menu at a time.
1043: {
1044: if (IsWindowVisible(m_pPluginMenu->GetWindow()))
1045: {
1046: ShowWindow(m_pPluginMenu->GetWindow(), SW_HIDE);
1047: menusHaveBeenHidden = true;
1048: }
1049: // if (m_pPluginMenu->Hide(HIDE_PARENTS)) menusHaveBeenHidden = true;
1050: if (m_pPluginMenu->Hide(HIDE_CHILDREN)) menusHaveBeenHidden = true;
1051: }
1052:
1053: if (pPreviewItem) pPreviewItem->Hide();
1054:
1055: if (menusHaveBeenHidden) PlaySoundFX(SFX_MENU_HIDE);
1056:
1057: return menusHaveBeenHidden;
1058: }
1059:
1060: //===========================================================================
1061: // Function: CreateMenuItem / CreateMenuItemUnicode
1062: // Purpose:
1063: //===========================================================================
1064:
1065: MenuItem* MenuCommon::CreateMenuItem(Menu *pMenu, int itemType, char* pszCommand, char* pszArgument, char* pszTitle, bool isSelected)
1066: {
1067: MenuItem *newMenuItem = NULL;
1068:
1069: wchar_t pszTitleUnicode[MAX_LINE_LENGTH];
1070: if (!pSettings->disableUnicodeParsing && (pszTitle != NULL))
1071: {
1072: char pszTitleANSI[MAX_LINE_LENGTH];
1073: strcpy_s(pszTitleANSI, sizeofArray(pszTitleANSI), pszTitle);
1074: strcat_s(pszTitleANSI, sizeofArray(pszTitleANSI), "\0"); // Just in case...
1075: MultiByteToWideChar(CP_UTF8, 0, pszTitleANSI, strnlen_s(pszTitleANSI, sizeofArray(pszTitleANSI))+1, pszTitleUnicode, MAX_LINE_LENGTH);
1076: }
1077: else wcscpy_s(pszTitleUnicode, sizeofArray(pszTitleUnicode), L"\0");
1078:
1079: if (pMenu)
1080: {
1081: if (itemType == MENUITEM_SEPARATOR)
1082: {
1083: if (pSettings->menuSeparators)
1084: {
1085: newMenuItem = new MenuItem(MENUITEM_SEPARATOR, "", pszTitleUnicode, NULL, NULL, false);
1086:
1087: // Set a higher sort priority for [separator] items to make them sort
1088: // before any regular menu items (i.e. between folders regular menu items)
1089: newMenuItem->SetSortPriority(PRIORITY_SEPARATOR);
1090:
1091: newMenuItem->SetHeight(m_nSeparatorHeight);
1092: pMenu->AddMenuItem(newMenuItem);
1093: return newMenuItem;
1094: }
1095: else return NULL;
1096: }
1097:
1098: //====================
1099:
1100: else if (itemType == MENUITEM_NOP)
1101: {
1102: if (pszTitle == NULL) newMenuItem = new MenuItem(MENUITEM_NOP, "", NULL, NULL, NULL, false);
1103: else newMenuItem = new MenuItem(MENUITEM_NOP, pszTitle, pszTitleUnicode, NULL, NULL, false);
1104: }
1105:
1106: //====================
1107:
1108: else if (itemType == MENUITEM_EDITSTRING) // Used by MakeMenuItemString
1109: {
1110: newMenuItem = new StringItem(pszTitle, pszTitleUnicode, pszCommand, pszArgument, isSelected);
1111: }
1112:
1113: //====================
1114:
1115: else // [exec] MENUITEM_COMMAND, MENUITEM_BOOLEAN, MENUITEM_EDITINT (used by MakeMenuItemInt), etc
1116: {
1117: newMenuItem = new MenuItem(itemType, pszTitle, pszTitleUnicode, pszCommand, pszArgument, isSelected);
1118: }
1119:
1120: //====================
1121:
1122: newMenuItem->SetHeight(m_nSubmenuHeight);
1123: pMenu->AddMenuItem(newMenuItem);
1124: }
1125:
1126: return newMenuItem;
1127: }
1128:
1129: //====================
1130:
1131: MenuItem* MenuCommon::CreateMenuItemUnicode(Menu* pMenu, int itemType, char* pszCommand, char* pszArgument, wchar_t* pszTitleUnicode, bool isSelected)
1132: {
1133: MenuItem *newMenuItem = NULL;
1134:
1135: if (pMenu && (itemType == MENUITEM_COMMAND))
1136: {
1137: newMenuItem = new MenuItem(itemType, "", pszTitleUnicode, pszCommand, pszArgument, isSelected);
1138: newMenuItem->SetHeight(m_nSubmenuHeight);
1139: pMenu->AddMenuItem(newMenuItem);
1140: }
1141:
1142: return newMenuItem;
1143: }
1144:
1145: //===========================================================================
1146: // Function: Invoke
1147: // Purpose:
1148: //===========================================================================
1149:
1150: void MenuCommon::Invoke(MenuItem* item, int button)
1151: {
1152: if (item->itemType == MENUITEM_COMMAND || item->itemType == MENUITEM_BOOLEAN)
1153: {
1154: if (item->m_pszCommand == NULL) return;
1155:
1156: if (item->m_pszCommand[0] == '@') // -> Direct bro@m command (i.e. not via an [exec] type menu item, see below)
1157: {
1158: char broam[MAX_LINE_LENGTH];
1159: strcpy_s(broam, sizeofArray(broam), item->m_pszCommand);
1160: if (item->m_pszArgument != NULL)
1161: {
1162: if (strlen(item->m_pszArgument))
1163: {
1164: strcat_s(broam, sizeofArray(broam), " ");
1165: strcat_s(broam, sizeofArray(broam), item->m_pszArgument);
1166: }
1167: }
1168:
1169: // Update the menu item boolean status if applicable...
1170: if (item->m_selectionGroup > GROUP_DEFAULT)
1171: {
1172: item->m_pParent->ClearSelectionGroup(item->m_selectionGroup);
1173: item->m_isSelected = true;
1174: }
1175: else if (item->itemType == MENUITEM_BOOLEAN) item->ToggleSelected();
1176:
1177: // Hide any currently open non-pinned menu(s) first, just in case the command is to open *another* menu...
1178: if (item->m_pParent->IsPinned() || (button >= 2)) item->m_pParent->UpdateMenuWindow();
1179: else HideAfterInvoke(item, button);
1180:
1181: // Execute the command associated with the menu item...
1182: BBSmartExecute(broam);
1183: PlaySoundFX(SFX_MENU_CLICK);
1184:
1185: return;
1186: }
1187: else if (item->m_pszArgument != NULL)
1188: {
1189: if (!_stricmp(item->m_pszCommand, "[exec]") && (item->m_pszArgument[0] == '@')) // -> Non-direct bro@m command (i.e. via an [exec] type menu item)
1190: {
1191: // Update the menu item boolean status if applicable...
1192: if (item->m_selectionGroup > GROUP_DEFAULT)
1193: {
1194: item->m_pParent->ClearSelectionGroup(item->m_selectionGroup);
1195: item->m_isSelected = true;
1196: }
1197: else if (item->itemType == MENUITEM_BOOLEAN) item->ToggleSelected();
1198:
1199: // Hide any currently open non-pinned menu(s) first, just in case the command is to open *another* menu...
1200: if (item->m_pParent->IsPinned() || (button >= 2)) item->m_pParent->UpdateMenuWindow();
1201: else HideAfterInvoke(item, button);
1202:
1203: // Execute the command associated with the menu item...
1204: BBSmartExecute(item->m_pszArgument);
1205: PlaySoundFX(SFX_MENU_CLICK);
1206:
1207: return;
1208: }
1209: }
1210: }
1211:
1212: //====================
1213:
1214: if (item->itemType == MENUITEM_EDITSTRING)
1215: {
1216: StringItem* si = (StringItem*)item;
1217: if (si->m_pszCommand == NULL) return;
1218:
1219: char broam[MAX_LINE_LENGTH];
1220:
1221: if (IsInString(si->m_pszCommand, "@BBInterface"))
1222: {
1223: // Add "" encapsulation to the broam parameters string to
1224: // allow spaces in strings sent back to BBInterface...
1225: char encappedInit[MAX_LINE_LENGTH];
1226: strcpy_s(encappedInit, sizeofArray(encappedInit), si->m_pszInit);
1227: int nLen = strnlen_s(encappedInit, sizeofArray(encappedInit));
1228: memmove(encappedInit+1, encappedInit, strnlen_s(encappedInit, sizeofArray(encappedInit)));
1229: encappedInit[0] = encappedInit[nLen+1] = '\"';
1230: encappedInit[nLen+2] = '\0';
1231: sprintf_s(broam, sizeofArray(broam), "%s %s", si->m_pszCommand, encappedInit);
1232: }
1233: else sprintf_s(broam, sizeofArray(broam), "%s %s", si->m_pszCommand, si->m_pszInit);
1234:
1235: // Update the menu item boolean status if applicable...
1236: if (si->m_selectionGroup > GROUP_DEFAULT)
1237: {
1238: si->m_pParent->ClearSelectionGroup(si->m_selectionGroup);
1239: si->m_isSelected = true;
1240: }
1241:
1242: // Hide any currently open non-pinned menu(s) first, just in case the command is to open *another* menu...
1243: if (si->m_pParent->IsPinned()) si->m_pParent->UpdateMenuWindow();
1244: else Hide();
1245:
1246: // Execute the command associated with the menu item...
1247: BBSmartExecute(broam);
1248: PlaySoundFX(SFX_MENU_CLICK);
1249:
1250: return;
1251: }
1252:
1253: //====================
1254:
1255: if (item->itemType == MENUITEM_EDITINT)
1256: {
1257: if (item->m_pszCommand == NULL) return;
1258:
1259: if (item->m_mousePos.x > (item->m_nWidth / 2))
1260: {
1261: if (item->m_currentValue == item->m_maxValue) return;
1262:
1263: // Increase the value...
1264: if ((button == 2) || (GetAsyncKeyState(VK_CONTROL) & 0x8000))
1265: {
1266: if (item->m_currentValue <= (item->m_maxValue - 10)) item->m_currentValue = item->m_currentValue + 10;
1267: else item->m_currentValue = item->m_maxValue;
1268: }
1269: else if (item->m_currentValue < item->m_maxValue) item->m_currentValue++;
1270: }
1271: else
1272: {
1273: if (item->m_currentValue == item->m_minValue) return;
1274:
1275: // Decrease the value...
1276: if ((button == 2) || (GetAsyncKeyState(VK_CONTROL) & 0x8000))
1277: {
1278: if (item->m_currentValue >= (item->m_minValue + 10)) item->m_currentValue = item->m_currentValue - 10;
1279: else item->m_currentValue = item->m_minValue;
1280: }
1281: else if (item->m_currentValue > item->m_minValue) item->m_currentValue--;
1282: }
1283:
1284: // Update the menu item's title text... (nb. both ANSI and Unicode representations, for now at least)
1285: char temp[MAX_LINE_LENGTH];
1286: sprintf_s(temp, sizeofArray(temp), "%s <%d>", item->m_pszOriginalTitleANSI, item->m_currentValue);
1287: item->SetTitleANSI(temp);
1288: wchar_t tempUnicode[MAX_LINE_LENGTH];
1289: swprintf(tempUnicode, sizeof(tempUnicode), L"%s <%d>", item->m_pszOriginalTitleUnicode, item->m_currentValue);
1290: item->SetTitleUnicode(tempUnicode);
1291:
1292: // Revalidate and update the menu item's parent menu...
1293: // (both as the item's width may have changed along with its title text,
1294: // and because the cached menu bitmap may need to be updated accordingly)
1295: item->m_pParent->Invalidate();
1296: item->m_pParent->Validate();
1297: item->m_pParent->UpdateMenuWindow();
1298:
1299: // Broadcast the updated value to the recipient(s)...
1300: sprintf_s(temp, sizeofArray(temp), "%s %d", item->m_pszCommand, item->m_currentValue);
1301: SendMessage(GetBBWnd(), BB_BROADCAST, 0, (LPARAM)temp);
1302: PlaySoundFX(SFX_MENU_CLICK);
1303:
1304: return;
1305: }
1306:
1307: //====================
1308:
1309: if (item->m_pszCommand != NULL)
1310: {
1311: // BELOW: Normal menu commands (i.e. [something] format as used by menu.rc)
1312:
1313: if (!strncmp(item->m_pszCommand, "[", 1))
1314: {
1315: if (!_stricmp(item->m_pszCommand, "[exec]"))
1316: {
1317: if (button == 2) // Right mouse button...
1318: {
1319: if (!strlen(item->m_pszArgument)) return;
1320: if (item->m_pszArgument[0] == '@') return;
1321:
1322: // We only need to check the last 4 characters to get the extension...
1323: char path[MAX_LINE_LENGTH];
1324: strcpy_s(path, sizeofArray(path), item->m_pszArgument);
1325: if (path[0] == '\"') StrRemoveEncap(path);
1326: int pointer = strnlen_s(path, sizeofArray(path)) - 4;
1327:
1328: if (!_stricmp(&path[pointer], ".jpg")
1329: || !_stricmp(&path[pointer], ".png")
1330: || !_stricmp(&path[pointer], ".bmp")
1331: || !_stricmp(&path[pointer], "jpeg"))
1332: {
1333: // -> Set the image to be the desktop wallpaper...
1334: // (please note that we leave the menu open in this case)
1335: if (GetAsyncKeyState(VK_CONTROL) & 0x8000) pWallpaper->SetWallpaper(path, "center"); // Is the control key held down? -> center mode
1336: else if (GetAsyncKeyState(VK_MENU) & 0x8000) pWallpaper->SetWallpaper(path, "tile"); // Is the alt key held down? -> tile mode
1337: else if (GetAsyncKeyState(VK_SHIFT) & 0x8000) pWallpaper->SetWallpaper(path, "span"); // Is the shift key held down? -> span mode
1338: else pWallpaper->SetWallpaper(path, "stretch"); // Default -> stretch/full mode
1339: PlaySoundFX(SFX_MENU_CLICK);
1340: pSettings->Statistics.changedWallpaper++; // Update statistics...
1341: return;
1342: }
1343: else if (!_strnicmp(item->m_pszArgument, "http", 4))
1344: {
1345: // -> Right click on HTTP(S) URL:
1346: // Instead of opening the link in the default browser,
1347: // download it to temporary file "xoblite.tmp"
1348: // (nb. this is typically only for some specific use cases)
1349: char path[MAX_PATH];
1350: GetBlackboxPath(path, MAX_PATH);
1351: strcat_s(path, sizeofArray(path), "xoblite.tmp");
1352: Log(path, path);
1353: DownloadFile(item->m_pszArgument, path);
1354: PlaySoundFX(SFX_MENU_CLICK);
1355: return;
1356: }
1357: else
1358: {
1359: if (IsInString(path, "http://")) return;
1360:
1361: // If the item points to an application .exe we execute that just
1362: // like on a left click... (but keeping the menu open afterwards)
1363: if (IsInString(path, ".exe")) BBSmartExecute(item->m_pszArgument); // -> Regular .exe apps
1364: else if (!_strnicmp(item->m_pszArgument, "ms-", 3) && (item->m_pszArgument[strlen(item->m_pszArgument) - 1] == ':')) BBSmartExecute(item->m_pszArgument); // -> Windows system apps, e.g. ms-settings: and ms-windows-store:
1365: else
1366: {
1367: // ...otherwise we open an Explorer
1368: // window where the file is selected...
1369: char arg[MAX_LINE_LENGTH];
1370: strcpy_s(arg, sizeofArray(arg), "/e,/select,");
1371: strcat_s(arg, sizeofArray(arg), path);
1372: BBExecute(GetDesktopWindow(), NULL, "explorer.exe", arg, NULL, SW_SHOWNORMAL, false);
1373:
1374: }
1375: PlaySoundFX(SFX_MENU_CLICK);
1376: return;
1377: }
1378: }
1379:
1380: //====================
1381:
1382: else if (button == 3) // Mid mouse button...
1383: {
1384: if (!strlen(item->m_pszArgument)) return;
1385: if (item->m_pszArgument[0] == '@') return;
1386:
1387: char string[MAX_LINE_LENGTH];
1388: strcpy_s(string, sizeofArray(string), item->m_pszArgument);
1389: if (string[0] == '\"') StrRemoveEncap(string);
1390:
1391: // Is the control key held down? -> copy path *without* the Blackbox
1392: // or current theme directory, adding quotes to enable portability
1393: // (useful for e.g. wallpaper rootCommands)
1394: if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
1395: {
1396: char string2[MAX_LINE_LENGTH];
1397:
1398: if (!pSettings->usingDefaultTheme && IsInString(string, pSettings->SF_currentThemePath))
1399: {
1400: strcpy_s(string2, sizeofArray(string2), "\"$CurrentTheme$");
1401: strcat_s(string2, sizeofArray(string2), &string[strnlen_s(pSettings->SF_currentThemePath, sizeofArray(pSettings->SF_currentThemePath))]);
1402: strcat_s(string2, sizeofArray(string2), "\"");
1403: }
1404: else if (IsInString(string, pSettings->SF_blackboxPath))
1405: {
1406: // strcpy_s(string2, sizeofArray(string2), "\"$Blackbox$");
1407: strcpy_s(string2, sizeofArray(string2), "\"");
1408: strcat_s(string2, sizeofArray(string2), &string[strnlen_s(pSettings->SF_blackboxPath, sizeofArray(pSettings->SF_blackboxPath)) + 1]);
1409: strcat_s(string2, sizeofArray(string2), "\"");
1410: }
1411: else
1412: {
1413: strcpy_s(string2, sizeofArray(string2), "\"");
1414: strcat_s(string2, sizeofArray(string2), string);
1415: strcat_s(string2, sizeofArray(string2), "\"");
1416: }
1417:
1418: // If the alt key is held down too we create a
1419: // bsetbg -full command including the path as argument...
1420: if (GetAsyncKeyState(VK_MENU) & 0x8000)
1421: {
1422: strcpy_s(string, sizeofArray(string), "rootCommand: bsetbg -full ");
1423: strcat_s(string, sizeofArray(string), string2);
1424: CopyStringToClipboard(string);
1425: }
1426: else CopyStringToClipboard(string2);
1427: }
1428: // ...if not we copy the full path to the file (without quotes)...
1429: else CopyStringToClipboard(string);
1430: }
1431:
1432: //====================
1433:
1434: else // Left mouse button...
1435: {
1436: /*
1437: if (item->itemType == MENUITEM_BOOLEAN) item->ToggleSelected();
1438:
1439: if (item->m_pParent->IsPinned()) item->m_pParent->UpdateMenuWindow();
1440: else HideAfterInvoke(item, button);
1441: */
1442: if (!item->m_pParent->IsPinned()) HideAfterInvoke(item, button);
1443:
1444: BBSmartExecute(item->m_pszArgument);
1445: PlaySoundFX(SFX_MENU_CLICK);
1446:
1447: return;
1448: }
1449: }
1450:
1451: //====================
1452:
1453: else if (!_stricmp(item->m_pszCommand, "[shutdown]")) {
1454: HideAfterInvoke(item, button);
1455: PostMessage(GetBBWnd(), BB_SHUTDOWN, 0, 0);
1456: return;
1457: }
1458: else if (!_stricmp(item->m_pszCommand, "[reboot]")) {
1459: HideAfterInvoke(item, button);
1460: PostMessage(GetBBWnd(), BB_SHUTDOWN, 1, 0);
1461: return;
1462: }
1463: else if (!_stricmp(item->m_pszCommand, "[logoff]")) {
1464: HideAfterInvoke(item, button);
1465: PostMessage(GetBBWnd(), BB_SHUTDOWN, 2, 0);
1466: return;
1467: }
1468: else if (!_stricmp(item->m_pszCommand, "[hibernate]")) {
1469: HideAfterInvoke(item, button);
1470: PostMessage(GetBBWnd(), BB_SHUTDOWN, 3, 0);
1471: return;
1472: }
1473: else if (!_stricmp(item->m_pszCommand, "[standby]")) {
1474: HideAfterInvoke(item, button);
1475: PostMessage(GetBBWnd(), BB_SHUTDOWN, 4, 0);
1476: return;
1477: }
1478: else if (!_stricmp(item->m_pszCommand, "[lockworkstation]")) {
1479: HideAfterInvoke(item, button);
1480: PostMessage(GetBBWnd(), BB_SHUTDOWN, 5, 0);
1481: return;
1482: }
1483:
1484: //====================
1485:
1486: else if (!_stricmp(item->m_pszCommand, "[run]")) {
1487: HideAfterInvoke(item, button);
1488: PostMessage(GetBBWnd(), BB_RUN, 0, 0);
1489: return;
1490: }
1491:
1492: //====================
1493:
1494: else if (!_stricmp(item->m_pszCommand, "[restart]"))
1495: {
1496: HideAfterInvoke(item, button);
1497: PostMessage(GetBBWnd(), BB_RESTART, 0, 0);
1498: return;
1499: }
1500: else if (!_stricmp(item->m_pszCommand, "[reconfigure]") || !_stricmp(item->m_pszCommand, "[reconfig]"))
1501: {
1502: HideAfterInvoke(item, button);
1503: PostMessage(GetBBWnd(), BB_SETSTYLE, 0, 0);
1504: return;
1505: }
1506: else if (!_stricmp(item->m_pszCommand, "[exit]"))
1507: {
1508: HideAfterInvoke(item, button);
1509: PostMessage(GetBBWnd(), BB_QUIT, 0, 0);
1510: return;
1511: }
1512:
1513: //====================
1514:
1515: else if (!_stricmp(item->m_pszCommand, "[style]"))
1516: {
1517: if (button == 2) // Right mouse button
1518: {
1519: // Open the chosen style in the preferred editor...
1520: BBExecute(GetDesktopWindow(), NULL, pSettings->preferredEditor, item->m_pszArgument, NULL, SW_SHOWNORMAL, false);
1521: PlaySoundFX(SFX_MENU_CLICK);
1522: HideAfterInvoke(item, button);
1523: }
1524: else if (button == 3) // Mid mouse button
1525: {
1526: if (!strlen(item->m_pszArgument)) return;
1527: if (item->m_pszArgument[0] == '@') return;
1528:
1529: // Copy the full path of the style to the clipboard...
1530: char string[MAX_LINE_LENGTH];
1531: strcpy_s(string, sizeofArray(string), item->m_pszArgument);
1532: if (string[0] == '\"') StrRemoveEncap(string);
1533: CopyStringToClipboard(string);
1534: PlaySoundFX(SFX_MENU_CLICK);
1535: }
1536: else // Left mouse button
1537: {
1538: // Update the "current style" indicator to reflect the style change...
1539: if (currentStyleMenuItem != NULL) currentStyleMenuItem->m_isSelected = false;
1540: currentStyleMenuItem = item;
1541: item->m_isSelected = true;
1542:
1543: // Apply the new style...
1544: HideAfterInvoke(item, button);
1545: PostMessage(GetBBWnd(), BB_SETSTYLE, 0, (LPARAM)item->m_pszArgument);
1546: }
1547:
1548: return;
1549: }
1550:
1551: //====================
1552:
1553: else if (!_stricmp(item->m_pszCommand, "[theme]"))
1554: {
1555: if (button == 2) // Right mouse button
1556: {
1557: // Open the theme folder in Explorer...
1558: char arg[MAX_LINE_LENGTH];
1559: strcpy_s(arg, sizeofArray(arg), "/e,");
1560: strcat_s(arg, sizeofArray(arg), item->m_pszArgument);
1561: BBExecute(GetDesktopWindow(), NULL, "explorer.exe", arg, NULL, SW_SHOWNORMAL, false);
1562: PlaySoundFX(SFX_MENU_CLICK);
1563: }
1564: else // Left mouse button
1565: {
1566: HideAfterInvoke(item, button);
1567: PostMessage(GetBBWnd(), BB_SETTHEME, 0, (LPARAM)item->m_pszArgument);
1568: }
1569:
1570: return;
1571: }
1572:
1573: //====================
1574: /*
1575: else if (!_stricmp(item->m_pszCommand, "[loadplugin]"))
1576: {
1577: HideAfterInvoke(item, button);
1578: pPluginManager->LoadPluginDialog();
1579: return;
1580: }
1581: */
1582: //====================
1583: /*
1584: else if (!_stricmp(item->m_pszCommand, "[editstyle]"))
1585: {
1586: HideAfterInvoke(item, button);
1587: PostMessage(GetBBWnd(), BB_EDITFILE, 0, 0);
1588: PlaySoundFX(SFX_MENU_CLICK);
1589: return;
1590: }
1591: else if (!_stricmp(item->m_pszCommand, "[editmenu]"))
1592: {
1593: HideAfterInvoke(item, button);
1594: PostMessage(GetBBWnd(), BB_EDITFILE, 1, 0);
1595: PlaySoundFX(SFX_MENU_CLICK);
1596: return;
1597: }
1598: else if (!_stricmp(item->m_pszCommand, "[editplugins]"))
1599: {
1600: HideAfterInvoke(item, button);
1601: PostMessage(GetBBWnd(), BB_EDITFILE, 2, 0);
1602: PlaySoundFX(SFX_MENU_CLICK);
1603: return;
1604: }
1605: else if (!_stricmp(item->m_pszCommand, "[editextensions]"))
1606: {
1607: HideAfterInvoke(item, button);
1608: PostMessage(GetBBWnd(), BB_EDITFILE, 3, 0);
1609: PlaySoundFX(SFX_MENU_CLICK);
1610: return;
1611: }
1612: else if (!_stricmp(item->m_pszCommand, "[editblackbox]"))
1613: {
1614: HideAfterInvoke(item, button);
1615: PostMessage(GetBBWnd(), BB_EDITFILE, 4, 0);
1616: PlaySoundFX(SFX_MENU_CLICK);
1617: return;
1618: }
1619: */
1620: //====================
1621: /*
1622: else if (!_stricmp(item->m_pszCommand, "[aboutstyle]"))
1623: {
1624: HideAfterInvoke(item, button);
1625: char aboutStyle[MAX_LINE_LENGTH], a1[MAX_LINE_LENGTH], a2[MAX_LINE_LENGTH], a3[MAX_LINE_LENGTH], a4[MAX_LINE_LENGTH], a5[MAX_LINE_LENGTH];
1626: strcpy_s(a1, sizeofArray(a1), ReadString(pSettings->styleFile, "style.name:", "[Style name not specified]"));
1627: strcpy_s(a2, sizeofArray(a2), ReadString(pSettings->styleFile, "style.author:", "[Author not specified]"));
1628: strcpy_s(a3, sizeofArray(a3), ReadString(pSettings->styleFile, "style.date:", ""));
1629: strcpy_s(a4, sizeofArray(a4), ReadString(pSettings->styleFile, "style.credits:", ""));
1630: strcpy_s(a5, sizeofArray(a5), ReadString(pSettings->styleFile, "style.comments:", ""));
1631: sprintf_s(aboutStyle, sizeofArray(aboutStyle), "%s \nby %s \n%s \n\n%s \n\n%s ", a1, a2, a3, a4, a5);
1632: // MessageBox(GetBBWnd(), aboutStyle, "xoblite: Style information", MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION);
1633: SendMessage(GetBBWnd(), BB_POPUPMESSAGE, (WPARAM)"About the current style...", (LPARAM)aboutStyle);
1634: PlaySoundFX(SFX_MENU_CLICK);
1635: return;
1636: }
1637: else if (!_stricmp(item->m_pszCommand, "[aboutplugins]"))
1638: {
1639: HideAfterInvoke(item, button);
1640: pPluginManager->AboutPlugins();
1641: PlaySoundFX(SFX_MENU_CLICK);
1642: return;
1643: }
1644: */
1645: //====================
1646: /*
1647: else if (!_stricmp(item->m_pszCommand, "[toggletoolbar]"))
1648: {
1649: HideAfterInvoke(item, button);
1650: PostMessage(GetBBWnd(), BB_TOGGLETOOLBAR, 0, 0);
1651: return;
1652: }
1653: else if (!_stricmp(item->m_pszCommand, "[toggleplugins]"))
1654: {
1655: HideAfterInvoke(item, button);
1656: PostMessage(GetBBWnd(), BB_TOGGLEPLUGINS, 0, 0);
1657: return;
1658: }
1659: else if (!_stricmp(item->m_pszCommand, "[toggledock]"))
1660: {
1661: HideAfterInvoke(item, button);
1662: PostMessage(GetBBWnd(), BB_TOGGLEDOCK, 0, 0);
1663: return;
1664: }
1665: else if (!_stricmp(item->m_pszCommand, "[toggleconsole]"))
1666: {
1667: HideAfterInvoke(item, button);
1668: PostMessage(GetBBWnd(), BB_TOGGLECONSOLE, 0, 0);
1669: return;
1670: }
1671: */
1672: //====================
1673: /*
1674: else if (!_stricmp(item->m_pszCommand, "[add]"))
1675: {
1676: HideAfterInvoke(item, button);
1677: PostMessage(GetBBWnd(), BB_WORKSPACE, 2, 0);
1678: return;
1679: }
1680: else if (!_stricmp(item->m_pszCommand, "[del]"))
1681: {
1682: HideAfterInvoke(item, button);
1683: PostMessage(GetBBWnd(), BB_WORKSPACE, 3, 0);
1684: return;
1685: }
1686: else if (!_stricmp(item->m_pszCommand, "[gather]"))
1687: {
1688: HideAfterInvoke(item, button);
1689: PostMessage(GetBBWnd(), BB_WORKSPACE, 5, 0);
1690: return;
1691: }
1692: */
1693: //====================
1694:
1695: // else HideAfterInvoke(1); // The command was unvalid -> close the menu!
1696: }
1697:
1698: //===========================================================================
1699:
1700: // BELOW: Some additional xoblite internal menu handling... (using <something> format)
1701:
1702: else if (!strncmp(item->m_pszCommand, "<", 1))
1703: {
1704: if (!_stricmp(item->m_pszCommand, "<task_item>"))
1705: {
1706: HideAfterInvoke(item, button);
1707:
1708: // Convert the hexadecimal format string back to HWND... (pretty cool, eh? :D )
1709: HWND hWnd = (HWND)strtoul(item->m_pszArgument, 0, 16);
1710: // ...and bring the window to the front of the z-order!
1711: if (IsIconic(hWnd)) ShowWindow(hWnd, SW_RESTORE);
1712: SetForegroundWindow(hWnd);
1713: SendMessage(GetBBWnd(), BB_BRINGTOFRONT, 0, (LPARAM)hWnd);
1714:
1715: return;
1716: }
1717:
1718: //===========================================================================
1719:
1720: // Some remaining legacy configuration menu handling...
1721: /*
1722: else if (!_stricmp(item->m_pszCommand, "<config_menu_item>"))
1723: {
1724: HideAfterInvoke(item, button);
1725:
1726: if (!_stricmp(item->m_pszArgument, "ClickToFocus"))
1727: {
1728: strcpy_s(pSettings->focusModel, sizeofArray(pSettings->focusModel), item->m_pszArgument);
1729: SystemParametersInfo(SPI_SETACTIVEWINDOWTRACKING, 0, (PVOID)false, SPIF_SENDCHANGE);
1730: SystemParametersInfo(SPI_SETACTIVEWNDTRKZORDER, 0, (PVOID)false, SPIF_SENDCHANGE);
1731: WriteString(pSettings->xobrcDefaultFile, "session.screen0.focusModel:", pSettings->focusModel);
1732: FindAndSelect(item, "Click to focus", true);
1733: FindAndSelect(item, "Sloppy focus", false);
1734: FindAndSelect(item, "Auto raise", false);
1735: return;
1736: }
1737: else if (!_stricmp(item->m_pszArgument, "SloppyFocus"))
1738: {
1739: strcpy_s(pSettings->focusModel, sizeofArray(pSettings->focusModel), item->m_pszArgument);
1740: SystemParametersInfo(SPI_SETACTIVEWINDOWTRACKING, 0, (PVOID)true, SPIF_SENDCHANGE);
1741: SystemParametersInfo(SPI_SETACTIVEWNDTRKZORDER, 0, (PVOID)false, SPIF_SENDCHANGE);
1742: WriteString(pSettings->xobrcDefaultFile, "session.screen0.focusModel:", pSettings->focusModel);
1743: FindAndSelect(item, "Click to focus", false);
1744: FindAndSelect(item, "Sloppy focus", true);
1745: FindAndSelect(item, "Auto raise", false);
1746: return;
1747: }
1748: else if (!_stricmp(item->m_pszArgument, "AutoRaiseSloppyFocus"))
1749: {
1750: strcpy_s(pSettings->focusModel, sizeofArray(pSettings->focusModel), item->m_pszArgument);
1751: SystemParametersInfo(SPI_SETACTIVEWINDOWTRACKING, 0, (PVOID)true, SPIF_SENDCHANGE);
1752: SystemParametersInfo(SPI_SETACTIVEWNDTRKZORDER, 0, (PVOID)true, SPIF_SENDCHANGE);
1753: WriteString(pSettings->xobrcDefaultFile, "session.screen0.focusModel:", pSettings->focusModel);
1754: FindAndSelect(item, "Click to focus", false);
1755: FindAndSelect(item, "Sloppy focus", false);
1756: FindAndSelect(item, "Auto raise", true);
1757: return;
1758: }
1759: else if (!_stricmp(item->m_pszArgument, "SmartPlacementRows"))
1760: {
1761: strcpy_s(pSettings->windowPlacement, sizeofArray(pSettings->windowPlacement), "RowSmartPlacement");
1762: WriteString(pSettings->xobrcDefaultFile, "session.screen0.windowPlacement:", pSettings->windowPlacement);
1763: return;
1764: }
1765: else if (!_stricmp(item->m_pszArgument, "SmartPlacementColumns"))
1766: {
1767: strcpy_s(pSettings->windowPlacement, sizeofArray(pSettings->windowPlacement), "ColSmartPlacement");
1768: WriteString(pSettings->xobrcDefaultFile, "session.screen0.windowPlacement:", pSettings->windowPlacement);
1769: return;
1770: }
1771: else if (!_stricmp(item->m_pszArgument, "CascadePlacement"))
1772: {
1773: strcpy_s(pSettings->windowPlacement, sizeofArray(pSettings->windowPlacement), "CascadePlacement");
1774: WriteString(pSettings->xobrcDefaultFile, "session.screen0.windowPlacement:", pSettings->windowPlacement);
1775: return;
1776: }
1777: else if (!_stricmp(item->m_pszArgument, "LeftToRight"))
1778: {
1779: strcpy_s(pSettings->rowPlacementDirection, sizeofArray(pSettings->rowPlacementDirection), item->m_pszArgument);
1780: WriteString(pSettings->xobrcDefaultFile, "session.screen0.rowPlacementDirection:", pSettings->rowPlacementDirection);
1781: return;
1782: }
1783: else if (!_stricmp(item->m_pszArgument, "RightToLeft"))
1784: {
1785: strcpy_s(pSettings->rowPlacementDirection, sizeofArray(pSettings->rowPlacementDirection), item->m_pszArgument);
1786: WriteString(pSettings->xobrcDefaultFile, "session.screen0.rowPlacementDirection:", pSettings->rowPlacementDirection);
1787: return;
1788: }
1789: else if (!_stricmp(item->m_pszArgument, "TopToBottom"))
1790: {
1791: strcpy_s(pSettings->colPlacementDirection, sizeofArray(pSettings->colPlacementDirection), item->m_pszArgument);
1792: WriteString(pSettings->xobrcDefaultFile, "session.screen0.colPlacementDirection:", pSettings->colPlacementDirection);
1793: return;
1794: }
1795: else if (!_stricmp(item->m_pszArgument, "BottomToTop"))
1796: {
1797: strcpy_s(pSettings->colPlacementDirection, sizeofArray(pSettings->colPlacementDirection), item->m_pszArgument);
1798: WriteString(pSettings->xobrcDefaultFile, "session.screen0.colPlacementDirection:", pSettings->colPlacementDirection);
1799: return;
1800: }
1801: }
1802: */
1803: }
1804: }
1805: }
1806:
1807: //===========================================================================
1808: // Function: HideAfterInvoke
1809: // Purpose: Updates+hides the parent menu after a menu item is invoked
1810: //===========================================================================
1811:
1812: void MenuCommon::HideAfterInvoke(MenuItem* item, int button)
1813: {
1814: if (item->itemType == MENUITEM_BOOLEAN)
1815: {
1816: if (!item->m_isSelected) item->m_isSelected = true;
1817: else item->m_isSelected = false;
1818: }
1819:
1820: //====================
1821:
1822: if (item->m_pParent->IsPinned() || (button != 1))
1823: {
1824: item->m_pParent->UpdateMenuWindow();
1825: }
1826:
1827: //====================
1828:
1829: if (button == 1)
1830: {
1831: // First we set focus to the toolbar to prevent
1832: // the focus from going back to the previous task
1833: // after the menu is closed...
1834: // if (pSettings->followActive) SetForegroundWindow(pToolbar->hToolbarWnd);
1835: // ...and then we hide all non-pinned menus...
1836: Hide();
1837: }
1838: }
1839:
1840: //===========================================================================
1841: // Function: DrawMenuBullet
1842: // Purpose: Draws any kind of menu bullet (i.e. triangle/square/circle/etc)
1843: //===========================================================================
1844:
1845: void MenuCommon::DrawMenuBullet(FolderItem* item, HDC hDC, int nTop, int type)
1846: {
1847: if (type == MENU_BULLET_EMPTY) return;
1848:
1849: //====================
1850:
1851: HPEN hPen, hOldPen;
1852:
1853: COLORREF penColor;
1854: if (item->IsActive()) penColor = pSettings->MenuActive->BulletColor;
1855: else penColor = pSettings->MenuFrame->BulletColor;
1856: hPen = CreatePen(PS_SOLID, 1, penColor);
1857:
1858: hOldPen = (HPEN) SelectObject(hDC, hPen);
1859: SelectObject(hDC, GetStockObject(NULL_BRUSH)); // -> Do not automatically fill any drawn shapes below...
1860:
1861: int bulletPadding = (item->GetHeight() - m_nBulletSize) / 2;
1862:
1863: int x;
1864: if (m_nBulletPosition == DT_RIGHT)
1865: {
1866: // x = item->GetWidth() - pSettings->MenuFrame->borderWidth - pSettings->MenuFrame->marginWidth;
1867: // if (pSettings->doubleScaleHiDPI) x -= 15;
1868: // else x -= 8;
1869: x = item->GetWidth() - pSettings->MenuFrame->borderWidth - pSettings->MenuFrame->marginWidth - bulletPadding - (m_nBulletSize / 2);
1870: // x = item->GetWidth() - m_nRightIndent + (m_nBulletSize / 2);
1871: }
1872: else
1873: {
1874: // x = pSettings->MenuFrame->borderWidth + pSettings->MenuFrame->marginWidth;
1875: // if (pSettings->doubleScaleHiDPI) x += 15;
1876: // else x += 8;
1877: x = pSettings->MenuFrame->borderWidth + pSettings->MenuFrame->marginWidth + bulletPadding + (m_nBulletSize / 2);
1878: // x = m_nLeftIndent - (m_nBulletSize / 2);
1879: }
1880:
1881: int y = nTop + (item->GetHeight() / 2);
1882:
1883: RECT bulletRect;
1884: // if (pSettings->doubleScaleHiDPI) SetRect(&bulletRect, x-5, y-5, x+5, y+5);
1885: if (pSettings->scalingFactorHiDPI == 4) SetRect(&bulletRect, x-10, y-10, x+10, y+10); // -> 20x20 pixel bullet
1886: else if (pSettings->scalingFactorHiDPI == 3) SetRect(&bulletRect, x-7, y-7, x+8, y+8); // -> 15x15 pixel bullet
1887: else if (pSettings->scalingFactorHiDPI == 2) SetRect(&bulletRect, x-5, y-5, x+5, y+5); // -> 10x10 pixel bullet
1888: else SetRect(&bulletRect, x-2, y-2, x+3, y+3); // -> 5x5 pixel bullet
1889:
1890: //====================
1891:
1892: if (type == MENU_BULLET_IMAGE)
1893: {
1894: if (pSettings->MenuFrameBullet->Image.bitmap != NULL)
1895: {
1896: // Draw menu bullet image, preserving any per pixel alpha transparency...
1897: // (-> supporting both 32bpp ARGB and 24bpp RGB format images)
1898:
1899: if (item->IsActive()) DrawImageIntoRect(hDC, bulletRect, pSettings->MenuActiveBullet, true, false);
1900: else DrawImageIntoRect(hDC, bulletRect, pSettings->MenuFrameBullet, true, false);
1901: /*
1902: HDC buf = CreateCompatibleDC(hDC);
1903: int bulletWidth, bulletHeight;
1904: if (item->IsActive() && (pSettings->MenuActiveBullet->Image.bitmap != NULL))
1905: {
1906: DeleteObject(SelectObject(buf, pSettings->MenuActiveBullet->Image.bitmap));
1907: bulletWidth = pSettings->MenuActiveBullet->Image.width;
1908: bulletHeight = pSettings->MenuActiveBullet->Image.height;
1909: }
1910: else
1911: {
1912: DeleteObject(SelectObject(buf, pSettings->MenuFrameBullet->Image.bitmap));
1913: bulletWidth = pSettings->MenuFrameBullet->Image.width;
1914: bulletHeight = pSettings->MenuFrameBullet->Image.height;
1915: }
1916:
1917: SetStretchBltMode(hDC, HALFTONE);
1918: // if (pSettings->doubleScaleHiDPI) StretchBlt(hDC, x-5, y-5, 10, 10, buf, 0, 0, imageWidth, imageHeight, SRCCOPY);
1919: // else StretchBlt(hDC, x-2, y-2, 5, 5, buf, 0, 0, imageWidth, imageHeight, SRCCOPY);
1920: // ### Note: While AlphaBlend() can be used to draw 32bpp ARGB images with per pixel alpha transparency, it also scales images down
1921: // much less smoothly than StretchBlt() as it reverts from HALFTONE (i.e. pixel averaging) to COLORONCOLOR (i.e. pixel deleting) mode;
1922: // for more information see https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-alphablend ###
1923: BLENDFUNCTION bf;
1924: ZeroMemory(&bf, sizeof(BLENDFUNCTION));
1925: bf.BlendOp = AC_SRC_OVER;
1926: bf.BlendFlags = 0;
1927: bf.SourceConstantAlpha = 255;
1928: bf.AlphaFormat = AC_SRC_ALPHA;
1929: if (pSettings->doubleScaleHiDPI) AlphaBlend(hDC, x-5, y-5, 10, 10, buf, 0, 0, bulletWidth, bulletHeight, bf);
1930: else AlphaBlend(hDC, x-2, y-2, 5, 5, buf, 0, 0, bulletWidth, bulletHeight, bf);
1931: DeleteDC(buf);
1932: */
1933: }
1934: }
1935: else if (type == MENU_BULLET_TRIANGLE)
1936: {
1937: if (m_nBulletPosition == DT_LEFT) // Backward pointing arrow
1938: {
1939: if (pSettings->doubleScaleHiDPI)
1940: {
1941: MoveToEx(hDC, x - 3, y, NULL);
1942: LineTo(hDC, x - 3, y + 1);
1943: MoveToEx(hDC, x - 2, y - 1, NULL);
1944: LineTo(hDC, x - 2, y + 2);
1945: MoveToEx(hDC, x - 1, y - 2, NULL);
1946: LineTo(hDC, x - 1, y + 3);
1947: MoveToEx(hDC, x, y - 3, NULL);
1948: LineTo(hDC, x, y + 4);
1949: MoveToEx(hDC, x + 1, y - 4, NULL);
1950: LineTo(hDC, x + 1, y + 5);
1951: }
1952: else
1953: {
1954: MoveToEx(hDC, x - 3, y, NULL);
1955: LineTo(hDC, x - 3, y + 1);
1956: MoveToEx(hDC, x - 2, y - 1, NULL);
1957: LineTo(hDC, x - 2, y + 2);
1958: MoveToEx(hDC, x - 1, y - 2, NULL);
1959: LineTo(hDC, x - 1, y + 3);
1960: MoveToEx(hDC, x, y - 3, NULL);
1961: LineTo(hDC, x, y + 4);
1962: }
1963: }
1964: else // Forward pointing arrow (default)
1965: {
1966: if (pSettings->doubleScaleHiDPI)
1967: {
1968: MoveToEx(hDC, x - 2, y - 4, NULL);
1969: LineTo(hDC, x - 2, y + 5);
1970: MoveToEx(hDC, x - 1, y - 3, NULL);
1971: LineTo(hDC, x - 1, y + 4);
1972: MoveToEx(hDC, x, y - 2, NULL);
1973: LineTo(hDC, x, y + 3);
1974: MoveToEx(hDC, x + 1, y - 1, NULL);
1975: LineTo(hDC, x + 1, y + 2);
1976: MoveToEx(hDC, x + 2, y, NULL);
1977: LineTo(hDC, x + 2, y + 1);
1978: }
1979: else
1980: {
1981: MoveToEx(hDC, x - 1, y - 3, NULL);
1982: LineTo(hDC, x - 1, y + 4);
1983: MoveToEx(hDC, x, y - 2, NULL);
1984: LineTo(hDC, x, y + 3);
1985: MoveToEx(hDC, x + 1, y - 1, NULL);
1986: LineTo(hDC, x + 1, y + 2);
1987: MoveToEx(hDC, x + 2, y, NULL);
1988: LineTo(hDC, x + 2, y + 1);
1989: }
1990: }
1991:
1992: }
1993:
1994: //====================
1995:
1996: else if (type == MENU_BULLET_SQUARE)
1997: {
1998: // if (pSettings->doubleScaleHiDPI)
1999: // {
2000: // Rectangle(hDC, x - 5, y - 5, x + 5, y + 5);
2001: // Rectangle(hDC, x - 4, y - 4, x + 4, y + 4);
2002: // }
2003: // else
2004: // {
2005: // // ### To be updated to use Rectangle(). cf. HiDPI above, easier to read... ###
2006: //
2007: // MoveToEx(hDC, x + 2, y + 2, NULL);
2008: // LineTo(hDC, x + 2, y - 3);
2009: // LineTo(hDC, x - 3, y - 3);
2010: // LineTo(hDC, x - 3, y + 2);
2011: // LineTo(hDC, x + 2, y + 2);
2012: // }
2013:
2014: for (int n = 1; n <= pSettings->scalingFactorHiDPI; n++)
2015: {
2016: Rectangle(hDC, bulletRect.left, bulletRect.top, bulletRect.right, bulletRect.bottom);
2017: InflateRect(&bulletRect, -1, -1);
2018: }
2019:
2020: // if (item->IsActive()) DrawGlyph(hDC, bulletRect, GLYPH_MENU_SQUARE, pSettings->MenuActive->BulletColor);
2021: // else DrawGlyph(hDC, bulletRect, GLYPH_MENU_SQUARE, pSettings->MenuFrame->BulletColor);
2022: }
2023:
2024: //====================
2025:
2026: else if (type == MENU_BULLET_CIRCLE)
2027: {
2028: if (pSettings->doubleScaleHiDPI)
2029: {
2030: Ellipse(hDC, x - 5, y - 5, x + 5, y + 5);
2031:
2032: COLORREF blendColor = pSettings->MixColors(GetPixel(hDC, x, y), penColor);
2033:
2034: // Smooth out outer edge...
2035: SetPixel(hDC, x - 2, y - 5, blendColor);
2036: SetPixel(hDC, x + 1, y - 5, blendColor);
2037: SetPixel(hDC, x - 5, y - 2, blendColor);
2038: SetPixel(hDC, x - 5, y + 1, blendColor);
2039: SetPixel(hDC, x + 4, y - 2, blendColor);
2040: SetPixel(hDC, x + 4, y + 1, blendColor);
2041: SetPixel(hDC, x - 2, y + 4, blendColor);
2042: SetPixel(hDC, x + 1, y + 4, blendColor);
2043:
2044: // Smooth out inner edge...
2045: SetPixel(hDC, x - 1, y - 4, blendColor);
2046: SetPixel(hDC, x, y - 4, blendColor);
2047: SetPixel(hDC, x - 3, y - 3, blendColor);
2048: SetPixel(hDC, x + 2, y - 3, blendColor);
2049: SetPixel(hDC, x - 4, y - 1, blendColor);
2050: SetPixel(hDC, x - 4, y, blendColor);
2051: SetPixel(hDC, x + 3, y - 1, blendColor);
2052: SetPixel(hDC, x + 3, y, blendColor);
2053: SetPixel(hDC, x - 3, y + 2, blendColor);
2054: SetPixel(hDC, x + 2, y + 2, blendColor);
2055: SetPixel(hDC, x - 1, y + 3, blendColor);
2056: SetPixel(hDC, x, y + 3, blendColor);
2057:
2058: // Smooth out corner pixels... (these pixels are more visually noticeable if off, so here we blend per pixel as well)
2059: // blendColor = pSettings->MixColors(GetPixel(hDC, x, y), blendColor);
2060: blendColor = pSettings->MixColors(GetPixel(hDC, x - 4, y - 4), blendColor);
2061: SetPixel(hDC, x - 4, y - 4, blendColor);
2062: blendColor = pSettings->MixColors(GetPixel(hDC, x + 3, y - 4), blendColor);
2063: SetPixel(hDC, x + 3, y - 4, blendColor);
2064: blendColor = pSettings->MixColors(GetPixel(hDC, x - 4, y + 3), blendColor);
2065: SetPixel(hDC, x - 4, y + 3, blendColor);
2066: blendColor = pSettings->MixColors(GetPixel(hDC, x + 3, y + 3), blendColor);
2067: SetPixel(hDC, x + 3, y + 3, blendColor);
2068: }
2069: else
2070: {
2071: Arc(hDC, x - 4, y - 3, x + 3, y + 4, x - 1, y - 3, x - 1, y - 3);
2072: // Ellipse(hDC, x - 4, y - 3, x + 3, y + 4);
2073: }
2074: }
2075:
2076: //====================
2077:
2078: else if (type == MENU_BULLET_DIAMOND)
2079: {
2080: if (pSettings->doubleScaleHiDPI)
2081: {
2082: MoveToEx(hDC, x - 4, y, NULL);
2083: LineTo(hDC, x - 4, y + 1);
2084: MoveToEx(hDC, x - 3, y - 1, NULL);
2085: LineTo(hDC, x - 3, y + 2);
2086: MoveToEx(hDC, x - 2, y - 2, NULL);
2087: LineTo(hDC, x - 2, y + 3);
2088: MoveToEx(hDC, x - 1, y - 3, NULL);
2089: LineTo(hDC, x - 1, y + 4);
2090: MoveToEx(hDC, x, y - 4, NULL);
2091: LineTo(hDC, x, y + 5);
2092: MoveToEx(hDC, x + 1, y - 3, NULL);
2093: LineTo(hDC, x + 1, y + 4);
2094: MoveToEx(hDC, x + 2, y - 2, NULL);
2095: LineTo(hDC, x + 2, y + 3);
2096: MoveToEx(hDC, x + 3, y - 1, NULL);
2097: LineTo(hDC, x + 3, y + 2);
2098: MoveToEx(hDC, x + 4, y, NULL);
2099: LineTo(hDC, x + 4, y + 1);
2100: }
2101: else
2102: {
2103: // ### To be updated to not use a loop. cf. HiDPI above, easier to read... ###
2104:
2105: int r = 3;
2106:
2107: int fromX, toX, lineY;
2108: for (fromX = x, toX = x + 1, lineY = y - r; lineY <= y + r; lineY++)
2109: {
2110: MoveToEx(hDC, fromX, lineY, NULL);
2111: LineTo(hDC, toX, lineY);
2112: if (lineY < y) fromX--, toX++;
2113: else fromX++, toX--;
2114: }
2115: }
2116: }
2117:
2118: //====================
2119:
2120: else if (type == MENU_BULLET_TRIPLE)
2121: {
2122: if (pSettings->doubleScaleHiDPI)
2123: {
2124: if (item->IsActive()) // Draw horizontal lines...
2125: {
2126: MoveToEx(hDC, x + 4, y - 5, NULL);
2127: LineTo(hDC, x - 6, y - 5);
2128: MoveToEx(hDC, x + 4, y - 4, NULL);
2129: LineTo(hDC, x - 6, y - 4);
2130:
2131: MoveToEx(hDC, x + 4, y - 1, NULL);
2132: LineTo(hDC, x - 6, y - 1);
2133: MoveToEx(hDC, x + 4, y, NULL);
2134: LineTo(hDC, x - 6, y);
2135:
2136: MoveToEx(hDC, x + 4, y + 3, NULL);
2137: LineTo(hDC, x - 6, y + 3);
2138: MoveToEx(hDC, x + 4, y + 4, NULL);
2139: LineTo(hDC, x - 6, y + 4);
2140: }
2141: else // Draw vertical lines...
2142: {
2143: MoveToEx(hDC, x + 4, y + 4, NULL);
2144: LineTo(hDC, x + 4, y - 6);
2145: MoveToEx(hDC, x + 3, y + 4, NULL);
2146: LineTo(hDC, x + 3, y - 6);
2147:
2148: MoveToEx(hDC, x, y + 4, NULL);
2149: LineTo(hDC, x, y - 6);
2150: MoveToEx(hDC, x - 1, y + 4, NULL);
2151: LineTo(hDC, x - 1, y - 6);
2152:
2153: MoveToEx(hDC, x - 4, y + 4, NULL);
2154: LineTo(hDC, x - 4, y - 6);
2155: MoveToEx(hDC, x - 5, y + 4, NULL);
2156: LineTo(hDC, x - 5, y - 6);
2157: }
2158: }
2159: else
2160: {
2161: if (item->IsActive()) // Draw horizontal lines...
2162: {
2163: MoveToEx(hDC, x + 2, y - 2, NULL);
2164: LineTo(hDC, x - 3, y - 2);
2165: MoveToEx(hDC, x + 2, y, NULL);
2166: LineTo(hDC, x - 3, y);
2167: MoveToEx(hDC, x + 2, y + 2, NULL);
2168: LineTo(hDC, x - 3, y + 2);
2169: }
2170: else // Draw vertical lines...
2171: {
2172: MoveToEx(hDC, x + 2, y + 2, NULL);
2173: LineTo(hDC, x + 2, y - 3);
2174: MoveToEx(hDC, x, y + 2, NULL);
2175: LineTo(hDC, x, y - 3);
2176: MoveToEx(hDC, x - 2, y + 2, NULL);
2177: LineTo(hDC, x - 2, y - 3);
2178: }
2179: }
2180: }
2181:
2182: //====================
2183:
2184: else if (type == MENU_BULLET_COMMENT)
2185: {
2186: if (pSettings->doubleScaleHiDPI)
2187: {
2188: MoveToEx(hDC, x - 1, y - 5, NULL);
2189: LineTo(hDC, x - 6, y);
2190: MoveToEx(hDC, x, y - 5, NULL);
2191: LineTo(hDC, x - 6, y + 1);
2192: MoveToEx(hDC, x, y - 4, NULL);
2193: LineTo(hDC, x - 5, y + 1);
2194:
2195: MoveToEx(hDC, x + 3, y - 1, NULL);
2196: LineTo(hDC, x - 2, y + 4);
2197: MoveToEx(hDC, x + 4, y - 1, NULL);
2198: LineTo(hDC, x - 2, y + 5);
2199: MoveToEx(hDC, x + 4, y, NULL);
2200: LineTo(hDC, x - 1, y + 5);
2201: }
2202: else
2203: {
2204: MoveToEx(hDC, x, y - 3, NULL);
2205: LineTo(hDC, x - 4, y + 1);
2206: MoveToEx(hDC, x + 2, y - 1, NULL);
2207: LineTo(hDC, x - 2, y + 3);
2208: }
2209: }
2210:
2211: //====================
2212:
2213: else if (type == MENU_BULLET_GRID)
2214: {
2215: if (pSettings->doubleScaleHiDPI)
2216: {
2217: // Draw horizontal lines x 3... (nb. the same draw commands as Triple active!)
2218: MoveToEx(hDC, x + 4, y - 5, NULL);
2219: LineTo(hDC, x - 6, y - 5);
2220: MoveToEx(hDC, x + 4, y - 4, NULL);
2221: LineTo(hDC, x - 6, y - 4);
2222:
2223: MoveToEx(hDC, x + 4, y - 1, NULL);
2224: LineTo(hDC, x - 6, y - 1);
2225: MoveToEx(hDC, x + 4, y, NULL);
2226: LineTo(hDC, x - 6, y);
2227:
2228: MoveToEx(hDC, x + 4, y + 3, NULL);
2229: LineTo(hDC, x - 6, y + 3);
2230: MoveToEx(hDC, x + 4, y + 4, NULL);
2231: LineTo(hDC, x - 6, y + 4);
2232:
2233: // Draw vertical lines x 3... (nb. the same draw commands as Triple non-active!)
2234: MoveToEx(hDC, x + 4, y + 4, NULL);
2235: LineTo(hDC, x + 4, y - 6);
2236: MoveToEx(hDC, x + 3, y + 4, NULL);
2237: LineTo(hDC, x + 3, y - 6);
2238:
2239: MoveToEx(hDC, x, y + 4, NULL);
2240: LineTo(hDC, x, y - 6);
2241: MoveToEx(hDC, x - 1, y + 4, NULL);
2242: LineTo(hDC, x - 1, y - 6);
2243:
2244: MoveToEx(hDC, x - 4, y + 4, NULL);
2245: LineTo(hDC, x - 4, y - 6);
2246: MoveToEx(hDC, x - 5, y + 4, NULL);
2247: LineTo(hDC, x - 5, y - 6);
2248: }
2249: else
2250: {
2251: // Draw horizontal lines x 3...
2252: MoveToEx(hDC, x + 1, y - 2, NULL);
2253: LineTo(hDC, x - 3, y - 2);
2254: MoveToEx(hDC, x + 1, y, NULL);
2255: LineTo(hDC, x - 3, y);
2256: MoveToEx(hDC, x + 1, y + 2, NULL);
2257: LineTo(hDC, x - 3, y + 2);
2258:
2259: // Draw vertical lines x 3...
2260: MoveToEx(hDC, x + 2, y + 2, NULL);
2261: LineTo(hDC, x + 2, y - 3);
2262: MoveToEx(hDC, x, y + 2, NULL);
2263: LineTo(hDC, x, y - 3);
2264: MoveToEx(hDC, x - 2, y + 2, NULL);
2265: LineTo(hDC, x - 2, y - 3);
2266: }
2267: }
2268:
2269: //====================
2270:
2271: else if (type == MENU_BULLET_QUAD)
2272: {
2273: if (pSettings->doubleScaleHiDPI)
2274: {
2275: Rectangle(hDC, x - 5, y - 5, x - 1, y - 1);
2276: Rectangle(hDC, x + 1, y - 5, x + 5, y - 1);
2277: Rectangle(hDC, x - 5, y + 1, x - 1, y + 5);
2278: Rectangle(hDC, x + 1, y + 1, x + 5, y + 5);
2279: }
2280: else
2281: {
2282: // ##### WORK IN PROGRESS :) #####
2283: }
2284: }
2285:
2286: //====================
2287:
2288: SelectObject(hDC, hOldPen);
2289: DeleteObject(hPen);
2290: DeleteObject(hOldPen);
2291: }
2292:
2293: //===========================================================================
2294: // Function: DrawMenuIndicator
2295: // Purpose: Draws a "menu item selected" indicator
2296: //===========================================================================
2297:
2298: void MenuCommon::DrawMenuIndicator(MenuItem* item, HDC hDC, int nTop)
2299: {
2300: /*
2301: int x;
2302: if (m_nBulletPosition == DT_RIGHT)
2303: {
2304: x = item->GetWidth() - pSettings->MenuFrame->borderWidth - pSettings->MenuFrame->marginWidth;
2305: if (pSettings->doubleScaleHiDPI) x -= 15;
2306: else x -= 8;
2307: }
2308: else
2309: {
2310: x = pSettings->MenuFrame->borderWidth + pSettings->MenuFrame->marginWidth;
2311: if (pSettings->doubleScaleHiDPI) x += 15;
2312: else x += 8;
2313: }
2314:
2315: int y = nTop + ((item->GetHeight()) / 2);
2316:
2317: RECT indicatorRect;
2318:
2319: if (pSettings->doubleScaleHiDPI)
2320: {
2321: // Indicator bullet -> 10x10 pixels filled square
2322: indicatorRect.left = x - 5;
2323: indicatorRect.top = y - 5;
2324: indicatorRect.right = x + 5;
2325: indicatorRect.bottom = y + 5;
2326: }
2327: else
2328: {
2329: // Indicator bullet -> 5x5 pixels filled square
2330: indicatorRect.left = x - 2;
2331: indicatorRect.top = y - 2;
2332: indicatorRect.right = x + 3;
2333: indicatorRect.bottom = y + 3;
2334: }
2335: */
2336: int indicatorPadding = (item->GetHeight() - m_nBulletSize) / 2;
2337:
2338: int x;
2339: // if (m_nBulletPosition == DT_RIGHT) x = item->GetWidth() - m_nRightIndent; // pSettings->MenuFrame->borderWidth - pSettings->MenuFrame->marginWidth - indicatorPadding - (m_nBulletSize / 2);
2340: // else x = m_nLeftIndent; // pSettings->MenuFrame->borderWidth + pSettings->MenuFrame->marginWidth + indicatorPadding + (m_nBulletSize / 2);
2341: // if (m_nBulletPosition == DT_RIGHT) x = item->GetWidth() - m_nRightIndent + (m_nBulletSize / 2);
2342: // else x = m_nLeftIndent - (m_nBulletSize / 2)
2343: if (m_nBulletPosition == DT_RIGHT) x = item->GetWidth() - pSettings->MenuFrame->borderWidth - pSettings->MenuFrame->marginWidth - indicatorPadding - (m_nBulletSize / 2);
2344: else x = pSettings->MenuFrame->borderWidth + pSettings->MenuFrame->marginWidth + indicatorPadding + (m_nBulletSize / 2);
2345:
2346:
2347: int y = nTop + (item->GetHeight() / 2);
2348:
2349: RECT indicatorRect;
2350: if (pSettings->scalingFactorHiDPI == 4) SetRect(&indicatorRect, x-10, y-10, x+10, y+10); // -> 20x20 pixel indicator
2351: else if (pSettings->scalingFactorHiDPI == 3) SetRect(&indicatorRect, x-7, y-7, x+8, y+8); // -> 15x15 pixel indicator
2352: else if (pSettings->scalingFactorHiDPI == 2) SetRect(&indicatorRect, x-5, y-5, x+5, y+5); // -> 10x10 pixel indicator
2353: else SetRect(&indicatorRect, x-2, y-2, x+3, y+3); // -> 5x5 pixel indicator
2354:
2355: if (pSettings->MenuIndicator->type == B_IMAGEFROMFILE)
2356: {
2357: // Draw menu indicator image, preserving any per pixel alpha transparency...
2358: // (-> supporting both 32bpp ARGB and 24bpp RGB format images)
2359: DrawImageIntoRect(hDC, indicatorRect, pSettings->MenuIndicator, true, false);
2360: /*
2361: HDC buf = CreateCompatibleDC(hDC);
2362: DeleteObject(SelectObject(buf, pSettings->MenuIndicator->Image.bitmap));
2363: SetStretchBltMode(hDC, HALFTONE);
2364: // ### Note: While AlphaBlend() can be used to draw 32bpp ARGB images with per pixel alpha transparency, it also scales images down
2365: // much less smoothly than StretchBlt() as it reverts from HALFTONE (i.e. pixel averaging) to COLORONCOLOR (i.e. pixel deleting) mode;
2366: // for more information see https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-alphablend ###
2367: BLENDFUNCTION bf;
2368: ZeroMemory(&bf, sizeof(BLENDFUNCTION));
2369: bf.BlendOp = AC_SRC_OVER;
2370: bf.BlendFlags = 0;
2371: bf.SourceConstantAlpha = 255;
2372: bf.AlphaFormat = AC_SRC_ALPHA;
2373: if (pSettings->doubleScaleHiDPI) AlphaBlend(hDC, indicatorRect.left, indicatorRect.top, 10, 10, buf, 0, 0, pSettings->MenuIndicator->Image.width, pSettings->MenuIndicator->Image.height, bf);
2374: else AlphaBlend(hDC, indicatorRect.left, indicatorRect.top, 5, 5, buf, 0, 0, pSettings->MenuIndicator->Image.width, pSettings->MenuIndicator->Image.height, bf);
2375: DeleteDC(buf);
2376: */
2377: }
2378: else MakeGradientSuper(hDC, indicatorRect, pSettings->MenuIndicator->type, pSettings->MenuIndicator->Color1, pSettings->MenuIndicator->Color2, pSettings->MenuIndicator->Color3, pSettings->MenuIndicator->Color4, pSettings->MenuIndicator->Color5, pSettings->MenuIndicator->Color6, pSettings->MenuIndicator->Color7, pSettings->MenuIndicator->Color8, pSettings->MenuIndicator->interlaced, pSettings->MenuIndicator->bevelstyle, pSettings->MenuIndicator->bevelposition, pSettings->bevelWidth, 0, 0);
2379: }
2380:
2381: //===========================================================================
2382: // Function: DrawMenuPinned
2383: // Purpose: Draws a "menu pinned" indicator
2384: //===========================================================================
2385:
2386: int MenuCommon::DrawMenuPinned(MenuItem* item, HDC hDC, RECT itemRect)
2387: {
2388: RECT tempRect;
2389: bool failsafe = false;
2390:
2391: int height = item->GetHeight();
2392: if (pSettings->doubleScaleHiDPI && (height < 12)) failsafe = true;
2393: else if (!pSettings->doubleScaleHiDPI && (height < 7)) failsafe = true;
2394:
2395: CopyRect(&tempRect, &itemRect);
2396:
2397: int y = tempRect.top + height/2;
2398:
2399: // If menu bullets are drawn on the right we draw the
2400: // menu pinned indicator on the left, and vice versa...
2401: int x;
2402: if (m_nBulletPosition == DT_LEFT) x = tempRect.right - y;
2403: else x = tempRect.left + y;
2404:
2405: if (pSettings->doubleScaleHiDPI)
2406: {
2407: tempRect.left = x - 5;
2408: tempRect.top = y - 5;
2409: tempRect.right = x + 5;
2410: tempRect.bottom = y + 5;
2411: }
2412: else
2413: {
2414: tempRect.left = x - 2;
2415: tempRect.top = y - 2;
2416: tempRect.right = x + 3;
2417: tempRect.bottom = y + 3;
2418: }
2419:
2420: if (failsafe) SetPixel(hDC, x, y, pSettings->MenuIndicator->Color1); // Simple fallback...
2421: else
2422: {
2423: // MakeGradientSuper(hDC, tempRect, pSettings->MenuIndicator->type, pSettings->MenuIndicator->Color1, pSettings->MenuIndicator->Color2, pSettings->MenuIndicator->Color3, pSettings->MenuIndicator->Color4, pSettings->MenuIndicator->Color5, pSettings->MenuIndicator->Color6, pSettings->MenuIndicator->Color7, pSettings->MenuIndicator->Color8, false, pSettings->MenuIndicator->bevelstyle, pSettings->MenuIndicator->bevelposition, pSettings->bevelWidth, pSettings->MenuFrame->borderColor, 1);
2424: CreateBorder(hDC, &tempRect, pSettings->MenuTitle->TextColor, 1);
2425: // InflateRect(&tempRect, -1, -1);
2426: // CreateBorder(hDC, &tempRect, pSettings->MenuTitle->ShadowColor, 1);
2427: // InflateRect(&tempRect, -1, -1);
2428: InflateRect(&tempRect, -2, -2);
2429: CreateBorder(hDC, &tempRect, pSettings->MenuTitle->TextColor, 1);
2430: }
2431: /*
2432: if (m_nBulletPosition == DT_RIGHT)
2433: {
2434: // -> Menu pinned indicator on the left (see above)
2435: if (pSettings->MenuTitle->Justify == DT_CENTER) return (y * 2);
2436: else if (pSettings->MenuTitle->Justify == DT_LEFT) return (y * 2);
2437: else return 0;
2438: }
2439: else if (m_nBulletPosition == DT_LEFT)
2440: {
2441: // -> Menu pinned indicator on the right (see above)
2442: if (pSettings->MenuTitle->Justify == DT_CENTER)
2443: {
2444: if (pSettings->doubleScaleHiDPI) return (y + 5);
2445: else return (y + 3);
2446: }
2447: else if (pSettings->MenuTitle->Justify == DT_RIGHT) return (y * 2);
2448: else return 0;
2449: }
2450:
2451: return 0; // Failsafe
2452: */
2453: return (y * 2);
2454: }
2455:
2456: //===========================================================================
2457: // Function: FindActiveAndDeactivate
2458: // Purpose: Finds and deactivates the currently active menu item
2459: //===========================================================================
2460:
2461: bool MenuCommon::FindActiveAndDeactivate(Menu* m, bool repaint)
2462: {
2463: MENUITERATOR i;
2464: bool success = false;
2465:
2466: for (i = m->m_MenuItems.begin(); i != m->m_MenuItems.end(); ++i)
2467: {
2468: if ((*i)->IsActive())
2469: {
2470: if (repaint || ((*i)->itemType == MENUITEM_FOLDER)) (*i)->Active(false); // -> Automatically triggers a repaint... (nb. always done for folder items, which also need to close submenus)
2471: else (*i)->m_bActive = false; // -> Caller will have to trigger a repaint later...
2472:
2473: // return true;
2474: success = true;
2475: }
2476: }
2477:
2478: // return false; // If we did not find any active item...
2479: return success;
2480: }
2481:
2482: //===========================================================================
2483: // Function: FindAndSelect
2484: // Purpose: Finds a specific menu item and sets its "selected" flag to true
2485: //===========================================================================
2486:
2487: void MenuCommon::FindAndSelect(MenuItem* item, LPSTR title, bool selected)
2488: {
2489: MENUITERATOR i;
2490:
2491: for (i = item->m_pParent->m_MenuItems.begin(); i != item->m_pParent->m_MenuItems.end(); ++i)
2492: {
2493: if (!_stricmp((*i)->m_pszTitleANSI, title))
2494: {
2495: (*i)->m_isSelected = selected;
2496: return;
2497: }
2498: }
2499: }
2500:
2501: //===========================================================================
2502: // Function: ScrollToItem
2503: // Purpose: Scrolls to a specific item in a menu
2504: //===========================================================================
2505:
2506: void MenuCommon::ScrollToItem(Menu* m, MenuItem* item)
2507: {
2508: if ((m == NULL) || (item == NULL)) return;
2509:
2510: // Activate the specified menu item...
2511: FindActiveAndDeactivate(m, false);
2512: if (pPreviewItem) pPreviewItem->Hide();
2513: item->Active(true);
2514:
2515: // If needed, scroll the menu to make the specified menu item visible on the screen...
2516: int screenY = GetSystemMetrics(SM_YVIRTUALSCREEN);
2517: int screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
2518: if ((m->menuY+item->m_nTop) >= screenHeight)
2519: {
2520: m->menuY = screenHeight - item->m_nTop - m_nSubmenuHeight;
2521: }
2522: else if ((m->menuY+item->m_nTop) <= screenY)
2523: {
2524: m->menuY = screenY + item->m_nTop - m_nSubmenuHeight;
2525: }
2526: SetWindowPos(m->hMenuWnd, HWND_TOP, m->menuX, m->menuY, 0, 0, SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOSIZE);
2527:
2528: if (item->itemType != MENUITEM_FOLDER) PlaySoundFX(SFX_MENU_NAVIGATE);
2529: }
2530:
2531: //===========================================================================
2532: