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: