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: #include "Menu.h" 
    33: #include "MenuItem.h" 
    34: #include "PreviewItem.h" 
    35: #include "StringItem.h" 
    36: #include "..\API\BBApi.h" 
    37: #include "..\Settings\Settings.h" 
    38: #include <algorithm> 
    39: 
    40: const char szMenuName[] = "BBMenu"; // Window class etc.
    41: 
    42: extern BImage* pBImage;
    43: extern Settings* pSettings;
    44: extern MenuCommon* pMenuCommon;
    45: extern PreviewItem* pPreviewItem;
    46: 
    47: vector<Menu*> g_Menues;
    48: int Menu::m_nInstances = 0;
    49: 
    50: int menuMessageSubscription[] = { BB_RECONFIGURE, BB_REDRAWGUI, 0 };
    51: 
    52: //===========================================================================
    53: 
    54: Menu::Menu(HINSTANCE hInstance)
    55: {
    56:     hMenuInstance = hInstance;
    57: 
    58:     isValidated = false;
    59:     m_pParent = NULL;
    60:     hMenuWnd = NULL;
    61:     m_pszFolderPath = NULL;
    62:     ZeroMemory(&data, sizeof(data));
    63: 
    64:     isPinned = false;
    65:     m_bMoved = false;
    66:     mouseIsHovering = false;
    67:     keyboardNavigationInProgress = false;
    68: 
    69:     menuHDC = cachedMenuBackground = cachedMenuActive = NULL;
    70:     cachedMenuGradientsExist = false;
    71: 
    72:     //====================
    73: 
    74:     // If this is the first menu instance created, we also create a common
    75:     // window class to be used by this and all future menu instances...
    76:     if (m_nInstances == 0)      
    77:     {   
    78:         WNDCLASS wc;
    79:         ZeroMemory(&wc, sizeof(wc));
    80:         wc.hInstance = hMenuInstance;
    81:         wc.lpfnWndProc = MenuWindowProc;
    82:         wc.lpszClassName = szMenuName;
    83:         wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    84:         wc.hbrBackground = NULL;
    85:         wc.style = CS_DBLCLKS;
    86: 
    87:         if (!RegisterClass(&wc))
    88:         {
    89:             MessageBox(0, "Error registering menu window class!", szMenuName, MB_OK | MB_ICONERROR | MB_TOPMOST);
    90:             Log("Menu", "Error registering window class!");
    91:             return;
    92:         }
    93:     }
    94: 
    95:     // Add the new menu instance to the global list of menues...
    96:     g_Menues.push_back(this);
    97: 
    98:     //====================
    99: 
   100:     // Create a window for this particular menu instance...
   101:     hMenuWnd =  CreateWindowEx(
   102:         WS_EX_TOOLWINDOW | WS_EX_ACCEPTFILES | WS_EX_TOPMOST | WS_EX_LAYERED,   // window style
   103:         szMenuName,                                                             // window class
   104:         NULL,                                                                   // window name
   105:         WS_POPUP,                                                               // window parameters
   106:         0,                                                                      // x position
   107:         0,                                                                      // y position
   108:         0,                                                                      // window width
   109:         0,                                                                      // window height
   110:         GetBBWnd(),                                                             // owner window
   111:         NULL,                                                                   // no menu
   112:         hMenuInstance,                                                          // hInstance
   113:         NULL                                                                    // no window creation data
   114:     );
   115: 
   116:     if (!hMenuWnd)
   117:     {
   118:         MessageBox(0, "Error creating menu window!", szMenuName, MB_OK | MB_ICONERROR | MB_TOPMOST);
   119:         Log("Menu", "Error creating window!");
   120:         return;
   121:     }
   122: 
   123:     m_nInstances++;
   124: 
   125:     // Hide the menu window... (nb. menues are not shown until opened/navigated to by the user) 
   126:     ShowWindow(hMenuWnd, SW_HIDE);
   127:     // Make the menu window sticky...
   128:     MakeSticky(hMenuWnd);
   129: 
   130:     // Subscribe to Blackbox messages applicable to menus...
   131:     SendMessage(GetBBWnd(), BB_REGISTERMESSAGE, (WPARAM)hMenuWnd, (LPARAM)menuMessageSubscription);
   132: }
   133: 
   134: //===========================================================================
   135: 
   136: Menu::~Menu()
   137: {
   138:     // Unsubscribe to previously subscribed Blackbox messages...
   139:     SendMessage (GetBBWnd(), BB_UNREGISTERMESSAGE, (WPARAM)hMenuWnd, (LPARAM)menuMessageSubscription);
   140: 
   141:     // Remove this menu instance to the global list of menues...
   142:     for (unsigned int i=0; i<g_Menues.size(); i++)
   143:     {
   144:         if (g_Menues[i] == this)
   145:         {   
   146:             g_Menues.erase(g_Menues.begin()+i);
   147:             break;
   148:         }
   149:     }
   150: 
   151:     // Destroy the menu window...
   152:     if (hMenuWnd) DestroyWindow(hMenuWnd);
   153:     hMenuWnd = NULL;
   154: 
   155:     // Delete the cached gradients...
   156:     if (cachedMenuBackground) DeleteDC(cachedMenuBackground);
   157:     if (cachedMenuActive) DeleteDC(cachedMenuActive);
   158:     if (menuHDC) DeleteDC(menuHDC);
   159: 
   160:     // Delete all menu items belonging to this menu...
   161:     DeleteMenuItems();
   162: 
   163:     // Decrease the global count of menu instances...
   164:     m_nInstances--;
   165:     if (m_nInstances == 0)
   166:     {
   167:         // This is the last menu instance,
   168:         // so we can unregister the common window class...
   169:         UnregisterClass(szMenuName, hMenuInstance);
   170:     }
   171: 
   172:     // ...and finally, clean up some miscellaneous stuff...
   173:     if (m_pszFolderPath) free(m_pszFolderPath);
   174:     m_pszFolderPath = NULL;
   175: }
   176: 
   177: //===========================================================================
   178: // Function: UpdateMenuWindow
   179: // Purpose: ...
   180: //===========================================================================
   181: 
   182: void Menu::UpdateMenuWindow()
   183: {
   184:     if (!isValidated) return;
   185: 
   186:     //====================
   187: 
   188:     // Get the bounding rect of the entire menu...
   189:     GetClientRect(hMenuWnd, &menuRect);
   190:     menuWidth = menuRect.right - menuRect.left;
   191:     menuHeight = menuRect.bottom - menuRect.top;
   192: 
   193:     // Get the bounding rect of the menu title...
   194:     CopyRect(&menuTitleRect, &menuRect);
   195:     menuTitleRect.bottom = pMenuCommon->m_nTitleHeight;
   196: 
   197:     if (pSettings->MenuFrame->borderWidth > pSettings->MenuTitle->borderWidth)
   198:     {
   199:         // Offset the menu title according to a bbLean bug/feature... (see further below)
   200:         OffsetRect(&menuTitleRect, 0, (pSettings->MenuFrame->borderWidth - pSettings->MenuTitle->borderWidth));
   201:         InflateRect(&menuTitleRect, (pSettings->MenuFrame->borderWidth - pSettings->MenuTitle->borderWidth), 0);
   202:         // ##### WORK IN PROGRESS - COMMENTED OUT EQUIVALENT CODE IN THE "Draw menu title" SECTION BELOW #####
   203:     }
   204: 
   205:     // Get the bounding rect of the menu grip... (drawn only if defined in the style file)
   206:     if (!pSettings->MenuGrip->parentRelative)
   207:     {
   208:         CopyRect(&menuGripRect, &menuRect);
   209:         menuGripRect.top = menuGripRect.bottom - pMenuCommon->m_nGripHeight;
   210: 
   211:         if (pSettings->MenuFrame->borderWidth > pSettings->MenuGrip->borderWidth)
   212:         {
   213:             // Offset the menu grip according to a bbLean bug/feature... (see further below)
   214:             OffsetRect(&menuGripRect, 0, (pSettings->MenuFrame->borderWidth - pSettings->MenuGrip->borderWidth));
   215:         }
   216:     }
   217: 
   218:     // Get the bounding rect of the menu frame...
   219:     CopyRect(&menuFrameRect, &menuRect);
   220: //  if (!pMenuCommon->m_nTitleDisabled) menuFrameRect.top = pMenuCommon->m_nTitleHeight - pSettings->MenuFrame->borderWidth;
   221:     if (!pMenuCommon->m_nTitleDisabled) menuFrameRect.top = menuTitleRect.bottom - pSettings->MenuFrame->borderWidth;
   222:     else menuFrameRect.top = 0;
   223: //  if (!pSettings->MenuGrip->parentRelative) menuFrameRect.bottom = menuFrameRect.bottom - pMenuCommon->m_nGripHeight + pSettings->MenuFrame->borderWidth;
   224:     if (!pSettings->MenuGrip->parentRelative) menuFrameRect.bottom = menuGripRect.top + pSettings->MenuFrame->borderWidth;
   225:     else menuFrameRect.bottom = menuRect.bottom;
   226: 
   227:     // Get the dimensions of the menu hilite/active...
   228:     menuActiveRect.left = pSettings->MenuFrame->borderWidth + pSettings->MenuFrame->marginWidth;
   229:     menuActiveRect.right = menuRect.right - pSettings->MenuFrame->borderWidth - pSettings->MenuFrame->marginWidth;
   230:     menuActiveWidth = menuActiveRect.right - menuActiveRect.left;
   231:     menuActiveHeight = pMenuCommon->m_nSubmenuHeight;
   232: 
   233:     //====================
   234: 
   235:     if (menuHDC == NULL) menuHDC = CreateCompatibleDC(NULL);
   236: 
   237:     HDC hdc = GetWindowDC(hMenuWnd);
   238:     HBITMAP bufbmp = CreateCompatibleBitmap(hdc, menuWidth, menuHeight);
   239:     DeleteObject(SelectObject(menuHDC, bufbmp));
   240:     ReleaseDC(hMenuWnd, hdc);
   241: 
   242:     //====================
   243: 
   244:     // If we have not yet created cached menu gradients, let's do that...
   245:     if (!cachedMenuGradientsExist)
   246:     {
   247:         RECT r;
   248:         HBITMAP tempBitmap, oldBitmap;
   249: 
   250:         int menuTitleWidth = menuTitleRect.right - menuTitleRect.left;
   251:         int menuTitleHeight = menuTitleRect.bottom - menuTitleRect.top;
   252:         int menuFrameWidth = menuFrameRect.right - menuFrameRect.left;
   253:         int menuFrameHeight = menuFrameRect.bottom - menuFrameRect.top;
   254: 
   255:         //====================
   256: 
   257:         // The menu title, frame and (if enabled) grip do not move,
   258:         // so we can draw them directly onto the same bitmap...
   259:         if (cachedMenuBackground) DeleteDC(cachedMenuBackground);
   260:         cachedMenuBackground = CreateCompatibleDC(NULL);
   261: 
   262:         tempBitmap = CreateCompatibleBitmap(menuHDC, menuWidth, menuHeight);
   263:         oldBitmap = (HBITMAP)SelectObject(cachedMenuBackground, tempBitmap);
   264:         DeleteObject(oldBitmap);
   265: 
   266:         // Draw menu frame...
   267:         if (pSettings->MenuFrame->type == B_IMAGEFROMFILE) DrawImageIntoRect(cachedMenuBackground, menuFrameRect, pSettings->MenuFrame, false, true);
   268:         else MakeGradientSuper(cachedMenuBackground, menuFrameRect, pSettings->MenuFrame->type, pSettings->MenuFrame->Color1, pSettings->MenuFrame->Color2, pSettings->MenuFrame->Color3, pSettings->MenuFrame->Color4, pSettings->MenuFrame->Color5, pSettings->MenuFrame->Color6, pSettings->MenuFrame->Color7, pSettings->MenuFrame->Color8, pSettings->MenuFrame->interlaced, pSettings->MenuFrame->bevelstyle, pSettings->MenuFrame->bevelposition, pSettings->bevelWidth, pSettings->MenuFrame->borderColor, pSettings->MenuFrame->borderWidth);
   269: 
   270:         // Draw menu title...
   271:         if (!pMenuCommon->m_nTitleDisabled)
   272:         {
   273:             if ((pSettings->MenuTitle->borderColor != pSettings->MenuFrame->borderColor) || (pSettings->MenuTitle->borderWidth < pSettings->MenuFrame->borderWidth))
   274:             {
   275:                 // A bug/feature (?) in bbLean draws the menu.frame border along the entire menu edge, i.e. the
   276:                 // menu.title border is only visible in between the title and the frame, not along the edges
   277:                 // of the menu. As long as the menu.title and menu.frame borders are the same width and colour,
   278:                 // this does not make any difference. But if they are not, drawing the menu border this way will
   279:                 // create a distinct change in appearance of the menu that is often used by bbLean stylists,
   280:                 // e.g. "Urban Tower" by Shawan -> http://www.boxshots.org/style/3889 . This is not the way the
   281:                 // menu is rendered in our "baseline" bb4nix, but... Let's call this a Request For Comments! ;)
   282:                 // ---------------------------------------------------------------------------------------------
   283:                 // A variant of this bug/feature is when there is a menu.frame border but no menu.title border,
   284:                 // e.g. cthu1hu's "ridge" style -> http://www.boxshots.org/style/4335
   285: 
   286:                 RECT tempTitleRect;
   287:                 CopyRect(&tempTitleRect, &menuTitleRect);
   288:                 if (pSettings->MenuTitle->borderWidth == 0)
   289:                 {
   290: //                  InflateRect(&tempTitleRect, -pSettings->MenuFrame->borderWidth, -pSettings->MenuFrame->borderWidth);
   291: //                  tempTitleRect.bottom += pSettings->MenuFrame->borderWidth;
   292:                 }
   293: 
   294:                 // Draw the menu title gradient, or image (stretch resized as applicable to fit the x/y dimensions of the menu item), as applicable...
   295:                 if (pSettings->MenuTitle->type == B_IMAGEFROMFILE) DrawImageIntoRect(cachedMenuBackground, tempTitleRect, pSettings->MenuTitle, false, true);
   296:                 else MakeGradientSuper(cachedMenuBackground, tempTitleRect, pSettings->MenuTitle->type, pSettings->MenuTitle->Color1, pSettings->MenuTitle->Color2, pSettings->MenuTitle->Color3, pSettings->MenuTitle->Color4, pSettings->MenuTitle->Color5, pSettings->MenuTitle->Color6, pSettings->MenuTitle->Color7, pSettings->MenuTitle->Color8, pSettings->MenuTitle->interlaced, pSettings->MenuTitle->bevelstyle, pSettings->MenuTitle->bevelposition, pSettings->bevelWidth, pSettings->MenuTitle->borderColor, pSettings->MenuTitle->borderWidth);
   297: 
   298:                 // Draw the menu.frame border along the entire menu edge...
   299:                 CreateBorder(cachedMenuBackground, &menuRect, pSettings->MenuFrame->borderColor, pSettings->MenuFrame->borderWidth);
   300:             }
   301:             else
   302:             {
   303:                 // Draw the menu title gradient, or image (stretch resized as applicable to fit the x/y dimensions of the menu item), as applicable...
   304:                 if (pSettings->MenuTitle->type == B_IMAGEFROMFILE) DrawImageIntoRect(cachedMenuBackground, menuTitleRect, pSettings->MenuTitle, false, true);
   305:                 else MakeGradientSuper(cachedMenuBackground, menuTitleRect, pSettings->MenuTitle->type, pSettings->MenuTitle->Color1, pSettings->MenuTitle->Color2, pSettings->MenuTitle->Color3, pSettings->MenuTitle->Color4, pSettings->MenuTitle->Color5, pSettings->MenuTitle->Color6, pSettings->MenuTitle->Color7, pSettings->MenuTitle->Color8, pSettings->MenuTitle->interlaced, pSettings->MenuTitle->bevelstyle, pSettings->MenuTitle->bevelposition, pSettings->bevelWidth, pSettings->MenuTitle->borderColor, pSettings->MenuTitle->borderWidth);
   306:             }
   307:         }
   308: 
   309:         // Draw menu grip...
   310:         if (!pSettings->MenuGrip->parentRelative)
   311:         {
   312:             if ((pSettings->MenuGrip->borderColor != pSettings->MenuFrame->borderColor) || (pSettings->MenuGrip->borderWidth < pSettings->MenuFrame->borderWidth))
   313:             {
   314:                 // Using the same bug/feature approach for the grip as described above for the title...
   315: 
   316:                 RECT tempGripRect;
   317:                 CopyRect(&tempGripRect, &menuGripRect);
   318:                 if (pSettings->MenuGrip->borderWidth == 0)
   319:                 {
   320:                     InflateRect(&tempGripRect, -pSettings->MenuFrame->borderWidth, -pSettings->MenuFrame->borderWidth);
   321:                     tempGripRect.top -= pSettings->MenuFrame->borderWidth;
   322:                 }
   323: 
   324:                 // Draw the menu grip gradient, or image (stretch resized as applicable to fit the x/y dimensions of the menu item), as applicable...
   325:                 if (pSettings->MenuGrip->type == B_IMAGEFROMFILE) DrawImageIntoRect(cachedMenuBackground, tempGripRect, pSettings->MenuGrip, false, true);
   326:                 else MakeGradientSuper(cachedMenuBackground, tempGripRect, pSettings->MenuGrip->type, pSettings->MenuGrip->Color1, pSettings->MenuGrip->Color2, pSettings->MenuGrip->Color3, pSettings->MenuGrip->Color4, pSettings->MenuGrip->Color5, pSettings->MenuGrip->Color6, pSettings->MenuGrip->Color7, pSettings->MenuGrip->Color8, pSettings->MenuGrip->interlaced, pSettings->MenuGrip->bevelstyle, pSettings->MenuGrip->bevelposition, pSettings->bevelWidth, pSettings->MenuGrip->borderColor, pSettings->MenuGrip->borderWidth);
   327: 
   328:                 // Draw the menu.frame border along the menu edge, but this time only for the bottom part
   329:                 // of the menu to avoid messing up any menu title/frame border overlaps... (see above)
   330:                 RECT borderRect;
   331:                 CopyRect(&borderRect, &menuRect);
   332:                 int top = menuFrameRect.top + pSettings->MenuFrame->borderWidth + 1;
   333: 
   334:                 HPEN borderPen = CreatePen(PS_SOLID, 1, pSettings->MenuFrame->borderColor);
   335:                 HPEN oldPen = (HPEN) SelectObject(cachedMenuBackground, borderPen);
   336:                 for (int i = 0; i < pSettings->MenuFrame->borderWidth; i++)
   337:                 {
   338:                     // Draw border...
   339:                     MoveToEx(cachedMenuBackground, borderRect.left, top, NULL);
   340:                     LineTo(cachedMenuBackground, borderRect.left, borderRect.bottom-1);
   341:                     LineTo(cachedMenuBackground, borderRect.right-1, borderRect.bottom-1);
   342:                     LineTo(cachedMenuBackground, borderRect.right-1, top);
   343: 
   344:                     InflateRect(&borderRect, -1, -1); // Shrink rectangle by 1 pixel...
   345:                 }
   346:                 SelectObject(cachedMenuBackground, oldPen);
   347:                 DeleteObject(borderPen);
   348:             }
   349:             else
   350:             {
   351:                 // Draw the menu grip gradient, or image (stretch resized as applicable to fit the x/y dimensions of the menu item), as applicable...
   352:                 if (pSettings->MenuGrip->type == B_IMAGEFROMFILE) DrawImageIntoRect(cachedMenuBackground, menuGripRect, pSettings->MenuGrip, false, true);
   353:                 else MakeGradientSuper(cachedMenuBackground, menuGripRect, pSettings->MenuGrip->type, pSettings->MenuGrip->Color1, pSettings->MenuGrip->Color2, pSettings->MenuGrip->Color3, pSettings->MenuGrip->Color4, pSettings->MenuGrip->Color5, pSettings->MenuGrip->Color6, pSettings->MenuGrip->Color7, pSettings->MenuGrip->Color8, pSettings->MenuGrip->interlaced, pSettings->MenuGrip->bevelstyle, pSettings->MenuGrip->bevelposition, pSettings->bevelWidth, pSettings->MenuGrip->borderColor, pSettings->MenuGrip->borderWidth);
   354:             }
   355:         }
   356: 
   357:         // ##################################################################################################
   358:         // ##### BELOW: EXPERIMENTAL SUPPORT FOR GRADIENT BORDERS, NOT YET MEANT FOR PUBLIC CONSUMPTION #####
   359:         // ##################################################################################################
   360:         if (!pSettings->MenuFrameBorder->parentRelative && (pSettings->MenuFrame->borderWidth > 0)
   361:             && (pMenuCommon->m_nTitleDisabled || (pSettings->MenuFrame->borderWidth >= pSettings->MenuTitle->borderWidth))
   362:             && (pMenuCommon->m_nGripDisabled || (pSettings->MenuFrame->borderWidth >= pSettings->MenuGrip->borderWidth)))
   363:         {
   364:             CreateBorderSuper(cachedMenuBackground, menuRect, pSettings->MenuFrameBorder->type, pSettings->MenuFrameBorder->Color1, pSettings->MenuFrameBorder->Color2, pSettings->MenuFrameBorder->Color3, pSettings->MenuFrameBorder->Color4, pSettings->MenuFrameBorder->Color5, pSettings->MenuFrameBorder->Color6, pSettings->MenuFrameBorder->Color7, pSettings->MenuFrameBorder->Color8, pSettings->MenuFrame->borderWidth);
   365:         }
   366: 
   367:         DeleteObject(tempBitmap);
   368: 
   369:         //====================
   370: 
   371:         // Should the menu's corners be rounded off? If so, we need to perform some related pixel re-shuffling
   372:         // *before* any subsequent per pixel alpha operations... (see further below; these in turn affect pixel data)
   373:         if (pSettings->menuRoundedCorners)
   374:         {
   375:             if (!pMenuCommon->m_nTitleDisabled)
   376:             {
   377:                 if (pSettings->MenuFrame->borderWidth >= pSettings->MenuTitle->borderWidth) PrepareCorner(cachedMenuBackground, menuRect, CORNER_TOPLEFT | CORNER_TOPRIGHT, pSettings->MenuTitle->bevelstyle, pSettings->MenuTitle->bevelposition, 1, pSettings->MenuFrame->borderWidth);
   378:                 else PrepareCorner(cachedMenuBackground, menuRect, CORNER_TOPLEFT | CORNER_TOPRIGHT, pSettings->MenuTitle->bevelstyle, pSettings->MenuTitle->bevelposition, 1, pSettings->MenuTitle->borderWidth);
   379:             }
   380:             else PrepareCorner(cachedMenuBackground, menuRect, CORNER_TOPLEFT | CORNER_TOPRIGHT, pSettings->MenuFrame->bevelstyle, pSettings->MenuFrame->bevelposition, 1, pSettings->MenuFrame->borderWidth);
   381: 
   382:             if (!pMenuCommon->m_nGripDisabled)
   383:             {
   384:                 if (pSettings->MenuFrame->borderWidth >= pSettings->MenuGrip->borderWidth) PrepareCorner(cachedMenuBackground, menuRect, CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT, pSettings->MenuGrip->bevelstyle, pSettings->MenuGrip->bevelposition, 1, pSettings->MenuFrame->borderWidth);
   385:                 else PrepareCorner(cachedMenuBackground, menuRect, CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT, pSettings->MenuGrip->bevelstyle, pSettings->MenuGrip->bevelposition, 1, pSettings->MenuGrip->borderWidth);
   386:             }
   387:             else PrepareCorner(cachedMenuBackground, menuRect, CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT, pSettings->MenuFrame->bevelstyle, pSettings->MenuFrame->bevelposition, 1, pSettings->MenuFrame->borderWidth);
   388:         }
   389: 
   390:         //====================
   391: 
   392:         // We can also draw all menu items in their *non-active* state onto the same bitmap...
   393:         SetBkMode(cachedMenuBackground, TRANSPARENT);
   394: 
   395:         MENUITERATOR i;
   396:         for (i = m_MenuItems.begin(); i != m_MenuItems.end(); i++)
   397:         {
   398:             DrawMenuItem(cachedMenuBackground, (MenuItem*)(*i), false);
   399:         }
   400: 
   401:         //====================
   402: 
   403:         // The menu hilite/active follows the mouse pointer,
   404:         // so we need to be able to draw it separately from
   405:         // the menu title and frame, i.e. it needs its own bitmap...
   406:         if (cachedMenuActive) DeleteDC(cachedMenuActive);
   407: 
   408:         cachedMenuActive = CreateCompatibleDC(NULL);
   409:         tempBitmap = CreateCompatibleBitmap(menuHDC, menuActiveWidth, menuActiveHeight);
   410:         oldBitmap = (HBITMAP)SelectObject(cachedMenuActive, tempBitmap);
   411:         DeleteObject(oldBitmap);
   412: 
   413:         // Draw the menu active gradient, or image (stretch resized as applicable to fit the x/y dimensions of the menu item), as applicable...
   414:         SetRect(&r, 0, 0, menuActiveWidth, menuActiveHeight);
   415:         if (pSettings->MenuActive->type == B_IMAGEFROMFILE) DrawImageIntoRect(cachedMenuActive, r, pSettings->MenuActive, false, true);
   416:         else MakeGradientSuper(cachedMenuActive, r, pSettings->MenuActive->type, pSettings->MenuActive->Color1, pSettings->MenuActive->Color2, pSettings->MenuActive->Color3, pSettings->MenuActive->Color4, pSettings->MenuActive->Color5, pSettings->MenuActive->Color6, pSettings->MenuActive->Color7, pSettings->MenuActive->Color8, pSettings->MenuActive->interlaced, pSettings->MenuActive->bevelstyle, pSettings->MenuActive->bevelposition, pSettings->bevelWidth, pSettings->MenuActive->borderColor, pSettings->MenuActive->borderWidth);
   417: 
   418:         DeleteObject(tempBitmap);
   419: 
   420:         //====================
   421: 
   422:         // Finally, we set the "cached menu gradients created" indicator...
   423:         cachedMenuGradientsExist = true;
   424:     }
   425: 
   426:     //====================
   427: 
   428:     // Copy the cached menu background into the display buffer...
   429:     BitBlt(menuHDC, 0, 0, menuWidth, menuHeight, cachedMenuBackground, 0, 0, SRCCOPY);
   430: 
   431:     //====================
   432: 
   433:     // Draw the active menu item and some dynamically updating things
   434:     // (e.g. boolean indicators etc) on top of the copied menu background...
   435:     MENUITERATOR i;
   436:     for (i = m_MenuItems.begin(); i != m_MenuItems.end(); i++)
   437:     {
   438:         // Is this the currently active menu item?
   439:         if ((*i)->IsActive())
   440:         {
   441:             SetBkMode(menuHDC, TRANSPARENT);
   442: 
   443:             if (pSettings->MenuActive->type != B_PARENTRELATIVE)
   444:             {
   445:                 // Unless it is parentRelative, we first copy the
   446:                 // cached menu.active gradient into the display buffer...
   447:                 RECT itemRect;
   448:                 (*i)->GetItemRect(&itemRect);
   449:                 menuActiveRect.top = itemRect.top;
   450:                 menuActiveRect.bottom = itemRect.top + menuActiveHeight;
   451:                 BitBlt(menuHDC, menuActiveRect.left, menuActiveRect.top, menuActiveWidth, menuActiveHeight, cachedMenuActive, 0, 0, SRCCOPY);
   452:             }
   453: 
   454:             // ...and then draw text, glyphs etc as applicable on top of it...
   455:             DrawMenuItem(menuHDC, (MenuItem*)(*i), true);
   456:         }
   457: 
   458:         //====================
   459: 
   460:         if ((*i)->itemType == MENUITEM_EDITSTRING)
   461:         {
   462:             StringItem* si = (StringItem*)(*i);
   463:             if (si->editboxActive)
   464:             {
   465:                 // Draw the "OK?" button...
   466:                 RECT itemRect;
   467:                 (*i)->GetItemRect(&itemRect);
   468:                 int padding = pSettings->MenuFrame->borderWidth + pSettings->MenuFrame->marginWidth;
   469:                 InflateRect(&itemRect, -padding, 0);
   470:                 MakeGradientSuper(menuHDC, itemRect, pSettings->MenuActive->type, pSettings->MenuActive->Color1, pSettings->MenuActive->Color2, pSettings->MenuActive->Color3, pSettings->MenuActive->Color4, pSettings->MenuActive->Color5, pSettings->MenuActive->Color6, pSettings->MenuActive->Color7, pSettings->MenuActive->Color8, pSettings->MenuActive->interlaced, pSettings->MenuActive->bevelstyle, pSettings->MenuActive->bevelposition, pSettings->bevelWidth, pSettings->MenuActive->borderColor, pSettings->MenuActive->borderWidth);
   471:                 if (pMenuCommon->m_hFrameFont != NULL) SelectObject(menuHDC, pMenuCommon->m_hFrameFont);
   472:                 DrawTextWithEffects(menuHDC, itemRect, "OK?", DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOCLIP, pSettings->MenuActive->TextColor, pSettings->MenuActive->FontOutline, pSettings->MenuActive->OutlineColor, pSettings->MenuActive->FontShadow, pSettings->MenuActive->ShadowColor, pSettings->MenuActive->ShadowX, pSettings->MenuActive->ShadowY);
   473:             }
   474:         }
   475: 
   476:         //====================
   477: 
   478:         if ((*i)->m_isSelected) pMenuCommon->DrawMenuIndicator((*i), menuHDC, (*i)->m_nTop);
   479:     }
   480: 
   481:     //====================
   482: 
   483:     // Apply per pixel alpha transparency selectively per menu element...
   484:     AlphaRect(menuHDC, menuRect, pSettings->menuTransparencyAlpha);
   485: 
   486:     RECT r;
   487: 
   488:     if (!pMenuCommon->m_nTitleDisabled)
   489:     {
   490:         if (pSettings->menuTitleTransparencyAlpha != pSettings->menuTransparencyAlpha)
   491:         {
   492:             SetRect(&r, pSettings->MenuFrame->borderWidth, pSettings->MenuFrame->borderWidth, menuRect.right-pSettings->MenuFrame->borderWidth, menuTitleRect.bottom);
   493:             AlphaRect(menuHDC, r, pSettings->menuTitleTransparencyAlpha);
   494:         }
   495:     }
   496: 
   497:     if (pSettings->menuFrameTransparencyAlpha != pSettings->menuTransparencyAlpha)
   498:     {
   499:         if (!pMenuCommon->m_nTitleDisabled)
   500:         {
   501:             if (!pMenuCommon->m_nGripDisabled) SetRect(&r, pSettings->MenuFrame->borderWidth, menuTitleRect.bottom, menuRect.right-pSettings->MenuFrame->borderWidth, menuGripRect.top-pSettings->MenuFrame->borderWidth);
   502:             else SetRect(&r, pSettings->MenuFrame->borderWidth, menuTitleRect.bottom, menuRect.right-pSettings->MenuFrame->borderWidth, menuRect.bottom-pSettings->MenuFrame->borderWidth);
   503:         }
   504:         else
   505:         {
   506:             if (!pMenuCommon->m_nGripDisabled) SetRect(&r, pSettings->MenuFrame->borderWidth, pSettings->MenuFrame->borderWidth, menuRect.right-pSettings->MenuFrame->borderWidth, menuGripRect.top-pSettings->MenuFrame->borderWidth);
   507:             else SetRect(&r, pSettings->MenuFrame->borderWidth, pSettings->MenuFrame->borderWidth, menuRect.right-pSettings->MenuFrame->borderWidth, menuRect.bottom-pSettings->MenuFrame->borderWidth);
   508:         }
   509:         AlphaRect(menuHDC, r, pSettings->menuFrameTransparencyAlpha);
   510:     }
   511: 
   512:     if (!pMenuCommon->m_nGripDisabled)
   513:     {
   514:         if (pSettings->menuGripTransparencyAlpha != pSettings->menuTransparencyAlpha)
   515:         {
   516:             SetRect(&r, pSettings->MenuFrame->borderWidth, menuGripRect.top, menuGripRect.right - pSettings->MenuFrame->borderWidth, menuRect.bottom - pSettings->MenuFrame->borderWidth);
   517:             AlphaRect(menuHDC, r, pSettings->menuGripTransparencyAlpha);
   518:         }
   519:     }
   520: 
   521:     for (i = m_MenuItems.begin(); i != m_MenuItems.end(); i++)
   522:     {
   523:         if ((*i)->m_bActive)
   524:         {
   525: //          (*i)->GetItemRect(&r);
   526: //          if (!pSettings->MenuActive->parentRelative) AlphaRect(menuHDC, r, pSettings->menuActiveTransparencyAlpha);
   527:             if (!pSettings->MenuActive->parentRelative) AlphaRect(menuHDC, menuActiveRect, pSettings->menuActiveTransparencyAlpha);
   528:         }
   529:     }
   530: 
   531:     if (pSettings->menuRoundedCorners)
   532:     {
   533:         if (!pMenuCommon->m_nTitleDisabled) AlphaCorner(menuHDC, menuRect, CORNER_TOPLEFT | CORNER_TOPRIGHT, 0, 0, 0, pSettings->MenuTitle->borderWidth, 0, pSettings->menuTransparencyAlpha);
   534:         else AlphaCorner(menuHDC, menuRect, CORNER_TOPLEFT | CORNER_TOPRIGHT, 0, 0, 0, pSettings->MenuFrame->borderWidth, 0, pSettings->menuTransparencyAlpha);
   535:         if (!pMenuCommon->m_nGripDisabled) AlphaCorner(menuHDC, menuRect, CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT, 0, 0, 0, pSettings->MenuGrip->borderWidth, 0, pSettings->menuTransparencyAlpha);
   536:         else AlphaCorner(menuHDC, menuRect, CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT, 0, 0, 0, pSettings->MenuFrame->borderWidth, 0, pSettings->menuTransparencyAlpha);
   537:     }
   538: 
   539:     AlphaApply(menuHDC, menuRect);
   540: 
   541:     //====================
   542: 
   543:     if (bufbmp) DeleteObject(bufbmp);
   544: 
   545:     POINT pt;
   546:     pt.x = menuX, pt.y = menuY;
   547:     POINT ptSrc;
   548:     ptSrc.x = 0, ptSrc.y = 0;
   549: 
   550:     BLENDFUNCTION bf;
   551:     bf.BlendOp = AC_SRC_OVER;
   552:     bf.BlendFlags = 0;
   553:     bf.AlphaFormat = AC_SRC_ALPHA;
   554:     bf.SourceConstantAlpha = (unsigned char)255;
   555: 
   556:     SIZE windowSize = { menuWidth, menuHeight };
   557:     BOOL result = UpdateLayeredWindow(hMenuWnd, NULL, &pt, &windowSize, menuHDC, &ptSrc, 0, &bf, ULW_ALPHA);
   558: /*
   559:     if (!menuHDC) SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_INFORMATION_MESSAGE, (LPARAM)"xoblite -> Menu -> Invalid menuHDC!");
   560: 
   561:     if (result == 0)
   562:     {
   563:         int error = GetLastError();
   564:         char msg[255];
   565:         sprintf_s(msg, sizeofArray(msg), "xoblite -> Menu -> UpdateLayeredWindow failed! [error code %d]", error);
   566:         SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_ERROR_MESSAGE, (LPARAM)msg);
   567:     }
   568: */
   569: }
   570: 
   571: //===========================================================================
   572: // Function: DrawMenuItem
   573: // Purpose: Support function to UpdateMenuWindow (see above)
   574: //===========================================================================
   575: 
   576: void Menu::DrawMenuItem(HDC hdc, MenuItem* i, bool active)
   577: {
   578:     if (i->itemType == MENUITEM_MARGINPAD) return;
   579: 
   580:     SetBkMode(hdc, TRANSPARENT);
   581: 
   582:     RECT itemRect, textRect;
   583:     i->GetItemRect(&itemRect);
   584:     i->GetTitleRect(&textRect);
   585: 
   586:     //====================
   587: 
   588:     if (i->itemType == MENUITEM_FOLDER)
   589:     {
   590:         pMenuCommon->DrawMenuBullet((FolderItem*)i, hdc, itemRect.top, pMenuCommon->m_nBulletStyle);
   591:     }
   592: 
   593:     //====================
   594: 
   595:     if (i->itemType == MENUITEM_HEADER)
   596:     {
   597:         if (!pMenuCommon->m_nTitleDisabled)
   598:         {
   599:             // Is the menu pinned? If so, let's draw the menu pinned indicator on the title item...
   600:             if (IsPinned())
   601:             {
   602:                 int indentText = pMenuCommon->DrawMenuPinned(i, hdc, itemRect);
   603:                 if (pSettings->MenuTitle->Justify == DT_CENTER)
   604:                 {
   605:                     textRect.left = itemRect.left + indentText;
   606:                     textRect.right = itemRect.right - indentText;
   607:                 }
   608:                 else if (pMenuCommon->m_nBulletPosition == DT_LEFT) textRect.right = itemRect.right - indentText;
   609:                 else textRect.left = itemRect.left + indentText;
   610:             }
   611: 
   612:             // Draw the menu title text...
   613:             if (pMenuCommon->m_hTitleFont != NULL) SelectObject(hdc, pMenuCommon->m_hTitleFont);
   614:             if (wcslen(i->m_pszTitleUnicode) > 0) DrawTextWithEffectsUnicode(hdc, textRect, i->m_pszTitleUnicode, i->GetDrawTextFormat(), pSettings->MenuTitle->TextColor, pSettings->MenuTitle->FontOutline, pSettings->MenuTitle->OutlineColor, pSettings->MenuTitle->FontShadow, pSettings->MenuTitle->ShadowColor, pSettings->MenuTitle->ShadowX, pSettings->MenuTitle->ShadowY);
   615:             else DrawTextWithEffects(hdc, textRect, i->m_pszTitleANSI, i->GetDrawTextFormat(), pSettings->MenuTitle->TextColor, pSettings->MenuTitle->FontOutline, pSettings->MenuTitle->OutlineColor, pSettings->MenuTitle->FontShadow, pSettings->MenuTitle->ShadowColor, pSettings->MenuTitle->ShadowX, pSettings->MenuTitle->ShadowY);
   616:         }
   617:     }
   618: 
   619:     //====================
   620: 
   621:     else if (i->itemType == MENUITEM_FOOTER)
   622:     {
   623:         if (!pSettings->MenuGrip->parentRelative)
   624:         {
   625:             if ((pSettings->MenuGrip->FontHeight > 0) && strlen(i->m_pszTitleANSI))
   626:             {
   627:                 // Draw the menu grip text...
   628:                 if (pMenuCommon->m_hGripFont != NULL) SelectObject(hdc, pMenuCommon->m_hGripFont);
   629:                 if (wcslen(i->m_pszTitleUnicode) > 0) DrawTextWithEffectsUnicode(hdc, textRect, i->m_pszTitleUnicode, i->GetDrawTextFormat(), pSettings->MenuGrip->TextColor, pSettings->MenuGrip->FontOutline, pSettings->MenuGrip->OutlineColor, pSettings->MenuGrip->FontShadow, pSettings->MenuGrip->ShadowColor, pSettings->MenuGrip->ShadowX, pSettings->MenuGrip->ShadowY);
   630:                 else DrawTextWithEffects(hdc, textRect, i->m_pszTitleANSI, i->GetDrawTextFormat(), pSettings->MenuGrip->TextColor, pSettings->MenuGrip->FontOutline, pSettings->MenuGrip->OutlineColor, pSettings->MenuGrip->FontShadow, pSettings->MenuGrip->ShadowColor, pSettings->MenuGrip->ShadowX, pSettings->MenuGrip->ShadowY);
   631:             }
   632:         }
   633:     }
   634: 
   635:     //====================
   636: 
   637:     else if (i->itemType == MENUITEM_SEPARATOR)
   638:     {
   639:         RECT sepRect;
   640:         sepRect.left = itemRect.left + pSettings->MenuFrame->borderWidth + pSettings->MenuFrame->marginWidth + 3;
   641:         sepRect.right = itemRect.left + i->GetWidth() - pSettings->MenuFrame->borderWidth - pSettings->MenuFrame->marginWidth - 3;
   642:         if (pSettings->MenuSeparator->ShadowX < 0) sepRect.left -= pSettings->MenuSeparator->ShadowX;
   643:         else sepRect.right -= pSettings->MenuSeparator->ShadowX;
   644: 
   645:         sepRect.top = itemRect.top + (2 * pSettings->scalingFactorHiDPI);
   646:         sepRect.bottom = sepRect.top + (1 * pSettings->scalingFactorHiDPI);
   647: 
   648:         if (pSettings->MenuSeparator->FontShadow)
   649:         {
   650:             OffsetRect(&sepRect, pSettings->MenuSeparator->ShadowX, pSettings->MenuSeparator->ShadowY);
   651:             if (pSettings->MenuSeparator->type == B_MIRRORHORIZONTAL) MakeGradientSuper(hdc, sepRect, B_SPLITHORIZONTAL, GetPixel(hdc, sepRect.left, sepRect.top), pSettings->MenuSeparator->ShadowColor, pSettings->MenuSeparator->ShadowColor, GetPixel(hdc, sepRect.right, sepRect.bottom), 0, 0, 0, 0, false, BEVEL_FLAT, BEVEL1, 0, 0, 0);
   652:             else MakeGradientSuper(hdc, sepRect, B_SOLID, pSettings->MenuSeparator->ShadowColor, 0, 0, 0, 0, 0, 0, 0, false, BEVEL_FLAT, BEVEL1, 0, 0, 0);
   653:             OffsetRect(&sepRect, -pSettings->MenuSeparator->ShadowX, -pSettings->MenuSeparator->ShadowY);
   654:         }
   655: 
   656: //      MakeGradientSuper(hdc, sepRect, B_MIRRORHORIZONTAL, pSettings->MenuFrame->Color, pSettings->MenuSeparator->Color, 0, 0, 0, 0, 0, 0, false, BEVEL_FLAT, BEVEL1, 0, 0, 0);
   657:         MakeGradientSuper(hdc, sepRect, pSettings->MenuSeparator->type, pSettings->MenuSeparator->Color1, pSettings->MenuSeparator->Color2, pSettings->MenuSeparator->Color3, pSettings->MenuSeparator->Color4, pSettings->MenuSeparator->Color5, pSettings->MenuSeparator->Color6, pSettings->MenuSeparator->Color7, pSettings->MenuSeparator->Color8, false, BEVEL_FLAT, BEVEL1, 0, 0, 0);
   658:     }
   659: 
   660:     //====================
   661: 
   662:     else if (i->itemType == MENUITEM_NOP)
   663:     {
   664:         if (pMenuCommon->m_hFrameFont != NULL) SelectObject(hdc, pMenuCommon->m_hFrameFont);
   665: //      DrawTextWithEffects(hdc, textRect, i->m_pszTitleANSI, i->GetDrawTextFormat(), pSettings->MenuFrame->disabledColor, pSettings->MenuFrame->FontOutline, pSettings->MenuFrame->OutlineColor, pSettings->MenuFrame->FontShadow, pSettings->MenuFrame->ShadowColor, pSettings->MenuFrame->ShadowX, pSettings->MenuFrame->ShadowY);
   666:         if (wcslen(i->m_pszTitleUnicode) > 0) DrawTextWithEffectsUnicode(hdc, textRect, i->m_pszTitleUnicode, i->GetDrawTextFormat(), pSettings->MenuFrame->disabledColor, false, 0, pSettings->MenuFrame->FontShadow, pSettings->MenuFrame->ShadowColor, pSettings->MenuFrame->ShadowX, pSettings->MenuFrame->ShadowY);
   667:         else DrawTextWithEffects(hdc, textRect, i->m_pszTitleANSI, i->GetDrawTextFormat(), pSettings->MenuFrame->disabledColor, false, 0, pSettings->MenuFrame->FontShadow, pSettings->MenuFrame->ShadowColor, pSettings->MenuFrame->ShadowX, pSettings->MenuFrame->ShadowY);
   668:     }
   669: 
   670:     //====================
   671: 
   672:     else if (i->itemType == MENUITEM_EDITSTRING)
   673:     {
   674:         StringItem* si = (StringItem*)i;
   675:         if (!si->editboxActive)
   676:         {
   677:             // Draw the regular string item text... (i.e. OK button hidden)
   678:             if (pMenuCommon->m_hFrameFont != NULL) SelectObject(hdc, pMenuCommon->m_hFrameFont);
   679: 
   680:             if (wcslen(i->m_pszTitleUnicode) > 0)
   681:             {
   682:                 if (active) DrawTextWithEffectsUnicode(hdc, textRect, i->m_pszTitleUnicode, i->GetDrawTextFormat(), pSettings->MenuActive->TextColor, pSettings->MenuActive->FontOutline, pSettings->MenuActive->OutlineColor, pSettings->MenuActive->FontShadow, pSettings->MenuActive->ShadowColor, pSettings->MenuActive->ShadowX, pSettings->MenuActive->ShadowY);
   683:                 else DrawTextWithEffectsUnicode(hdc, textRect, i->m_pszTitleUnicode, i->GetDrawTextFormat(), pSettings->MenuFrame->TextColor, pSettings->MenuFrame->FontOutline, pSettings->MenuFrame->OutlineColor, pSettings->MenuFrame->FontShadow, pSettings->MenuFrame->ShadowColor, pSettings->MenuFrame->ShadowX, pSettings->MenuFrame->ShadowY);
   684:             }
   685:             else
   686:             {
   687:                 if (active) DrawTextWithEffects(hdc, textRect, i->m_pszTitleANSI, i->GetDrawTextFormat(), pSettings->MenuActive->TextColor, pSettings->MenuActive->FontOutline, pSettings->MenuActive->OutlineColor, pSettings->MenuActive->FontShadow, pSettings->MenuActive->ShadowColor, pSettings->MenuActive->ShadowX, pSettings->MenuActive->ShadowY);
   688:                 else DrawTextWithEffects(hdc, textRect, i->m_pszTitleANSI, i->GetDrawTextFormat(), pSettings->MenuFrame->TextColor, pSettings->MenuFrame->FontOutline, pSettings->MenuFrame->OutlineColor, pSettings->MenuFrame->FontShadow, pSettings->MenuFrame->ShadowColor, pSettings->MenuFrame->ShadowX, pSettings->MenuFrame->ShadowY);
   689:             }
   690: 
   691: //          if (i->m_isSelected) pMenuCommon->DrawMenuIndicator(i, hdc, i->m_nTop);
   692:         }
   693:     }
   694: 
   695:     //====================
   696: 
   697:     else // MENUITEM_COMMAND, MENUITEM_EDITINT, etc
   698:     {
   699: 
   700:         if (pMenuCommon->m_hFrameFont != NULL) SelectObject(hdc, pMenuCommon->m_hFrameFont);
   701: 
   702:         if (wcslen(i->m_pszTitleUnicode) > 0)
   703:         {
   704:             if (active) DrawTextWithEffectsUnicode(hdc, textRect, i->m_pszTitleUnicode, i->GetDrawTextFormat(), pSettings->MenuActive->TextColor, pSettings->MenuActive->FontOutline, pSettings->MenuActive->OutlineColor, pSettings->MenuActive->FontShadow, pSettings->MenuActive->ShadowColor, pSettings->MenuActive->ShadowX, pSettings->MenuActive->ShadowY);
   705:             else DrawTextWithEffectsUnicode(hdc, textRect, i->m_pszTitleUnicode, i->GetDrawTextFormat(), pSettings->MenuFrame->TextColor, pSettings->MenuFrame->FontOutline, pSettings->MenuFrame->OutlineColor, pSettings->MenuFrame->FontShadow, pSettings->MenuFrame->ShadowColor, pSettings->MenuFrame->ShadowX, pSettings->MenuFrame->ShadowY);
   706:         }
   707:         else
   708:         {
   709:             if (active) DrawTextWithEffects(hdc, textRect, i->m_pszTitleANSI, i->GetDrawTextFormat(), pSettings->MenuActive->TextColor, pSettings->MenuActive->FontOutline, pSettings->MenuActive->OutlineColor, pSettings->MenuActive->FontShadow, pSettings->MenuActive->ShadowColor, pSettings->MenuActive->ShadowX, pSettings->MenuActive->ShadowY);
   710:             else DrawTextWithEffects(hdc, textRect, i->m_pszTitleANSI, i->GetDrawTextFormat(), pSettings->MenuFrame->TextColor, pSettings->MenuFrame->FontOutline, pSettings->MenuFrame->OutlineColor, pSettings->MenuFrame->FontShadow, pSettings->MenuFrame->ShadowColor, pSettings->MenuFrame->ShadowX, pSettings->MenuFrame->ShadowY);
   711:         }
   712: 
   713: //      if (i->m_isSelected) pMenuCommon->DrawMenuIndicator(i, hdc, i->m_nTop);
   714:     }
   715: }
   716: 
   717: //===========================================================================
   718: // Function: Show
   719: // Purpose: Shows the menu at calculated or specified screen coordinates
   720: //===========================================================================
   721: 
   722: void Menu::Show()
   723: {
   724:     POINT p;
   725:     GetCursorPos(&p);
   726: 
   727:     if (m_pParent != NULL)
   728:     {
   729:         RECT r;
   730:         GetWindowRect(m_pParent->GetWindow(), &r);
   731:         int width = r.right - r.left;
   732:         p.x -= width / 2;
   733:         p.y -= pMenuCommon->m_nTitleHeight / 2;
   734:         Show(r.right - 5, p.y - 5);
   735:     }
   736:     else
   737:     {
   738:         RECT r;
   739:         GetWindowRect(GetWindow(), &r);
   740:         int width = r.right - r.left;
   741:         p.x -= width / 2;
   742:         p.y -= pMenuCommon->m_nTitleHeight / 2;
   743:         Show(p.x, p.y);
   744:     }
   745: }
   746: 
   747: //====================
   748: 
   749: void Menu::Show(int x, int y)
   750: {
   751:     OnShow(true);
   752: 
   753:     GetClientRect(hMenuWnd, &menuRect);
   754:     menuWidth = menuRect.right - menuRect.left;
   755:     menuHeight = menuRect.bottom - menuRect.top;
   756: 
   757:     //====================
   758: 
   759:     RECT screenRect;
   760:     POINT pt = { x ,y };
   761: 
   762:     HMONITOR hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
   763: 
   764:     MONITORINFO mi;
   765:     mi.cbSize = sizeof(mi);
   766: 
   767:     if (GetMonitorInfo(hMon, &mi))
   768:     {
   769:         screenRect.left = mi.rcMonitor.left;
   770:         screenRect.top = mi.rcMonitor.top;
   771:         screenRect.right = mi.rcMonitor.right;
   772:         screenRect.bottom = mi.rcMonitor.bottom;
   773:     }
   774:     else
   775:     {
   776:         screenRect.left = 0;
   777:         screenRect.top = 0;
   778:         screenRect.right = GetSystemMetrics(SM_CXVIRTUALSCREEN);
   779:         screenRect.bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN);
   780:     }
   781: 
   782:     if ((pt.x + menuWidth) > screenRect.right) pt.x = screenRect.right - menuWidth;
   783:     if ((pt.y + menuHeight) > screenRect.bottom) pt.y = screenRect.bottom - menuHeight;
   784: 
   785:     if (pt.x < screenRect.left) pt.x = screenRect.left;
   786:     if (pt.y < screenRect.top) pt.y = screenRect.top;
   787: 
   788:     menuX = pt.x;
   789:     menuY = pt.y;
   790: 
   791:     //====================
   792: 
   793:     UpdateMenuWindow();
   794: 
   795:     SetWindowPos(hMenuWnd, HWND_TOP, menuX, menuY, 0, 0, SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOSIZE);
   796: //  ShowWindow(hMenuWnd, SW_SHOWNOACTIVATE);
   797:     ShowWindow(hMenuWnd, SW_SHOWNORMAL);
   798: 
   799:     if (m_pParent == NULL) SetForegroundWindow(hMenuWnd); // Note: Submenus set focus themselves, see FolderItem.cpp
   800: }
   801: 
   802: //===========================================================================
   803: // Function: Hide
   804: // Purpose: Hides the menu
   805: //===========================================================================
   806: 
   807: bool Menu::Hide(int h)
   808: {
   809:     bool menusHaveBeenHidden = false;
   810: 
   811:     if (h != HIDE_OTHERS) // Hide this menu?
   812:     {
   813:         if (!IsPinned())
   814:         {
   815:             if (IsWindowVisible(hMenuWnd))
   816:             {
   817:                 if (m_pParent != NULL) SetForegroundWindow(m_pParent->GetWindow());
   818: 
   819:                 ShowWindow(hMenuWnd, SW_HIDE);
   820:                 OnShow(false);
   821: 
   822:                 // Destroy the cached menu gradients to save GDI objects + memory...
   823:                 cachedMenuGradientsExist = false;
   824:                 if (cachedMenuBackground) DeleteDC(cachedMenuBackground);
   825:                 if (cachedMenuActive) DeleteDC(cachedMenuActive);
   826:                 cachedMenuBackground = cachedMenuActive = 0;
   827: 
   828:                 keyboardNavigationInProgress = false;
   829: 
   830:                 menusHaveBeenHidden = true;
   831:             }
   832:         }
   833: 
   834:         MENUITERATOR item;
   835:         for (item = m_MenuItems.begin(); item != m_MenuItems.end(); item++)
   836:         {
   837:             (*item)->Active(false);
   838: 
   839:             if ((*item)->itemType == MENUITEM_EDITSTRING)
   840:             {
   841:                 StringItem* si = (StringItem*)*item;
   842:                 if (si->editboxActive) // Any open string editor window belonging to the menu item?
   843:                 {
   844:                     // If so, we destroy the string editor window and
   845:                     // clear the global "menu item editbox open" flag...
   846:                     si->DestroyEditWindow();
   847:                     pSettings->menuEditboxAlreadyActive = false;
   848:                     si->editboxActive = false;
   849:                 }
   850:             }
   851:         }
   852:     }
   853: 
   854:     //====================
   855: 
   856:     if (h == HIDE_CHILDREN || h == HIDE_OTHERS)  // Hide child menus?
   857:     {
   858:         for (unsigned int i = 0; i < m_Children.size(); i++)
   859:         {
   860:             // Hide all child menus of this menu...
   861:             if (IsWindowVisible(m_Children[i]->GetWindow()) && !m_Children[i]->IsPinned())
   862:             {
   863:                 ShowWindow(m_Children[i]->GetWindow(), SW_HIDE);
   864:                 menusHaveBeenHidden = true;
   865:             }
   866:             // Hide any lower level child menus... (i.e. children of children and downwards)
   867:             if (m_Children[i]->Hide(HIDE_CHILDREN)) menusHaveBeenHidden = true;
   868:         }
   869:     }
   870: 
   871:     //====================
   872: 
   873:     if (h == HIDE_PARENTS || h == HIDE_OTHERS) // Hide parent menus?
   874:     {
   875:         if (m_pParent)
   876:         {
   877:             // Hide parent menu...
   878:             if (IsWindowVisible(m_pParent->GetWindow()) && !m_pParent->IsPinned())
   879:             {
   880:                 ShowWindow(m_pParent->GetWindow(), SW_HIDE);
   881:                 menusHaveBeenHidden = true;
   882:             }
   883:             // Hide any higher level parent menus... (i.e. parent of parent and upwards)
   884:             if (m_pParent->Hide(HIDE_PARENTS)) menusHaveBeenHidden = true;
   885:         }
   886:     }
   887: 
   888:     //====================
   889: 
   890:     return menusHaveBeenHidden;
   891: }
   892: 
   893: //===========================================================================
   894: // Functions: Mouse + TrackMouseProc/MouseLeave
   895: // Purpose: ...
   896: //===========================================================================
   897: 
   898: void Menu::Mouse(UINT nMsg, int x, int y)
   899: {
   900:     if (!isValidated) return;
   901:     
   902:     MENUITERATOR item;
   903:     POINT pt = {x, y};
   904: 
   905:     //====================
   906: 
   907:     if (!m_bMoved)
   908:     {
   909:         for (item = m_MenuItems.begin(); item != m_MenuItems.end() && !m_MenuItems.empty(); item++) // Note: By checking for the empty list we keep the core shell from crashing if a message box is opened, the shell reconfigured, and the message box closed... / Tres`ni
   910:         {
   911:             (*item)->Mouse(nMsg, pt);
   912:         }
   913:     }
   914: 
   915:     //====================
   916: 
   917:     if (nMsg == WM_MOUSEMOVE)
   918:     {
   919:         RECT r;
   920:         m_bMoved = false;
   921:         GetWindowRect(hMenuWnd, &r);
   922:         ClientToScreen(hMenuWnd, &pt);
   923: 
   924:         int screenTop = GetSystemMetrics(SM_YVIRTUALSCREEN);
   925:         int screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
   926: 
   927:         if ((r.bottom > screenHeight) && (pt.y + 1 >= screenHeight))
   928:         {
   929:             // The popup is larger than the screen and the
   930:             // cursor is at the bottom -> scroll down a bit...
   931: //          r.top -= pSettings->scrollSpeed;
   932:             r.top -= pMenuCommon->m_nSubmenuHeight;
   933:             m_bMoved = true;
   934:         }
   935:         else if ((r.top < screenTop) && (pt.y <= screenTop))
   936:         {
   937:             // The popup is larger than the screen and the
   938:             // cursor is at the top -> scroll up at bit...
   939: //          r.top += pSettings->scrollSpeed;
   940:             r.top += pMenuCommon->m_nSubmenuHeight;
   941:             m_bMoved = true;
   942:         }
   943: 
   944:         if (m_bMoved)
   945:         {
   946:             pMenuCommon->FindActiveAndDeactivate(this, true);
   947:             if (pPreviewItem) pPreviewItem->Hide();
   948:             menuX = r.left;
   949:             menuY = r.top;
   950:             SetWindowPos(hMenuWnd, NULL, menuX, menuY, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOSENDCHANGING);
   951:         }
   952: 
   953:         // Track the mouse...
   954:         if (!mouseIsHovering)
   955:         {
   956:             mouseIsHovering = true;
   957:             SetTimer(hMenuWnd, MENU_TRACK_MOUSE_TIMER, 100, Menu::TrackMouseProc);
   958:         }
   959:     }
   960:     
   961:     //====================
   962: 
   963:     else if (nMsg == WM_MOUSEWHEEL)
   964:     {
   965:         if (pPreviewItem) pPreviewItem->Hide();
   966: 
   967:         RECT r;
   968:         GetWindowRect(hMenuWnd, &r);
   969: 
   970:         // Only allow mousewheel scrolling if the menu is taller than the screen...
   971:         int height = r.bottom - r.top;
   972:         int screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
   973:         if (height < screenHeight) return;
   974: 
   975:         //====================
   976: 
   977:         if (x > 0) // Scrolling upwards...
   978:         {
   979: //          r.top += pSettings->wheelSpeed;
   980:             r.top += pMenuCommon->m_nSubmenuHeight;
   981:             r.bottom = r.top + height;
   982: 
   983:             if (r.top > 0)
   984:             {
   985:                 r.top = 0;
   986:                 r.bottom = height;
   987:             }
   988:         }
   989:         else // Scrolling downwards...
   990:         {
   991: //          r.top -= pSettings->wheelSpeed;
   992:             r.top -= pMenuCommon->m_nSubmenuHeight;
   993:             r.bottom = r.top + height;
   994: 
   995:             if (r.bottom < screenHeight)
   996:             {
   997:                 r.top = screenHeight - height;
   998:                 r.bottom = screenHeight;
   999:             }
  1000:         }
  1001: 
  1002:         //====================
  1003: 
  1004:         menuX = r.left;
  1005:         menuY = r.top;
  1006:         SetWindowPos(hMenuWnd, NULL, menuX, menuY, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOSENDCHANGING);
  1007:     }
  1008: }
  1009: 
  1010: //===========================================================================
  1011: 
  1012: void Menu::TrackMouseProc(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
  1013: {
  1014:     if (pSettings->menuEditboxAlreadyActive) return;
  1015: 
  1016:     POINT pt;
  1017:     HWND hWndNew;
  1018: 
  1019:     GetCursorPos(&pt);
  1020:     hWndNew = WindowFromPoint(pt);
  1021: 
  1022: //  if (hWnd != GetForegroundWindow()) SetForegroundWindow(hWnd);
  1023: 
  1024:     if (::GetParent(hWndNew) != hWnd)
  1025:     {
  1026:         RECT rect;
  1027:             
  1028:         GetClientRect(hWnd, &rect);
  1029:         MapWindowPoints(hWnd, NULL, (LPPOINT)&rect,2);
  1030:         
  1031:         if (!PtInRect(&rect, pt) || (hWndNew != hWnd)) 
  1032:         {
  1033:             KillTimer(hWnd, idEvent);
  1034:             PostMessage(hWnd, WM_MOUSELEAVE, 0, 0);
  1035:         }
  1036:     }
  1037: }
  1038: 
  1039: //===========================================================================
  1040: 
  1041: void Menu::MouseLeave()
  1042: {
  1043:     if (!isValidated) return;
  1044: 
  1045:     POINT p;
  1046:     HWND hWnd;
  1047: 
  1048:     GetCursorPos(&p);
  1049:     hWnd = WindowFromPoint(p);
  1050: 
  1051:     if (!IsChildWindow(hWnd)) pMenuCommon->FindActiveAndDeactivate(this, true);
  1052:     if (pPreviewItem) pPreviewItem->Hide();
  1053: 
  1054:     mouseIsHovering = false;
  1055: }
  1056: 
  1057: //===========================================================================
  1058: // Function: AddMenuItem
  1059: // Purpose: ...
  1060: //===========================================================================
  1061: 
  1062: void Menu::AddMenuItem(MenuItem* m)
  1063: {
  1064:     m_MenuItems.push_back(m);
  1065: 
  1066:     // Notify the menu item that it has been attached...
  1067:     m->Attached(this);
  1068: }
  1069: 
  1070: //===========================================================================
  1071: /*
  1072: void Menu::AddMenuItem(MenuItem *m, bool (*pfnSort)(void *, MenuItem *, MenuItem *), void *pvSortParam)
  1073: {
  1074:     MENUITERATOR i;
  1075: 
  1076:     for (i=m_MenuItems.begin(); i != m_MenuItems.end(); i++)
  1077:     {
  1078:         if (!pfnSort(pvSortParam, m, *i))
  1079:         {
  1080:             m_MenuItems.insert(i, m);
  1081:             break;
  1082:         }
  1083:     }
  1084: 
  1085:     if (i == m_MenuItems.end()) m_MenuItems.push_back(m);
  1086: 
  1087:     m->Attached(this);
  1088: }
  1089: */
  1090: 
  1091: //===========================================================================
  1092: // Function: DeleteMenuItems
  1093: // Purpose: ...
  1094: //===========================================================================
  1095: 
  1096: void Menu::DeleteMenuItems()
  1097: {
  1098:     // Block access to the menu structure until validated again...
  1099:     Invalidate();
  1100: 
  1101:     // Remove the menu from the set of global menues...
  1102:     MENUITERATOR item;
  1103:     for (item = m_MenuItems.begin(); item != m_MenuItems.end(); ++item)
  1104:     {
  1105:         delete *item;
  1106:     }
  1107:     m_MenuItems.clear();
  1108: }
  1109: 
  1110: //===========================================================================
  1111: // Function: Activate
  1112: // Purpose: Called when the menu window is activated or de-activated
  1113: //===========================================================================
  1114: 
  1115: void Menu::Activate(int fActive, HWND hWnd)
  1116: {
  1117:     // Nothing to do here... :)
  1118: }
  1119: 
  1120: //===========================================================================
  1121: // Function: IsActive
  1122: // Purpose: ...
  1123: //===========================================================================
  1124: 
  1125: bool Menu::IsActive()
  1126: {
  1127:     return GetActiveWindow() == hMenuWnd;
  1128: }
  1129: 
  1130: //===========================================================================
  1131: // Function: Timer
  1132: // Purpose: ...
  1133: //===========================================================================
  1134: 
  1135: void Menu::Timer(int nTimer)
  1136: {
  1137:     if (!isValidated) return;
  1138: 
  1139:     MENUITERATOR item;
  1140:     for (item = m_MenuItems.begin(); item !=  m_MenuItems.end(); item++)
  1141:     {
  1142:         (*item)->Timer(nTimer);
  1143:     }
  1144: 
  1145:     OnTimer(nTimer);
  1146: }
  1147: 
  1148: //===========================================================================
  1149: // Function: SetMenuFolderPath
  1150: // Purpose: ...
  1151: //===========================================================================
  1152: 
  1153: void Menu::SetMenuFolderPath(char* pszFolderPath)
  1154: {
  1155:     if (m_pszFolderPath) free(m_pszFolderPath);
  1156:     m_pszFolderPath = pszFolderPath ? _strdup(pszFolderPath) : _strdup("");
  1157: }
  1158: 
  1159: //===========================================================================
  1160: // Function: NcHitTest
  1161: // Purpose: 
  1162: //===========================================================================
  1163: 
  1164: LRESULT Menu::NcHitTest(int x, int y)
  1165: {
  1166:     if (!isValidated) return 0;
  1167: 
  1168:     LRESULT r;
  1169:     MENUITERATOR item;
  1170: 
  1171:     for (item=m_MenuItems.begin(); item != m_MenuItems.end(); item++)
  1172:     {
  1173:         if ((r = (*item)->NcHitTest(x, y)) != 0) return r;
  1174:     }
  1175: 
  1176:     return 0;
  1177: }
  1178: 
  1179: //===========================================================================
  1180: // Function: GetWindow
  1181: // Purpose: ...
  1182: //===========================================================================
  1183: 
  1184: HWND Menu::GetWindow()
  1185: {
  1186:     return hMenuWnd;
  1187: }
  1188: 
  1189: 
  1190: //===========================================================================
  1191: // Functions: SetParent/GetParent
  1192: // Purpose: Sets/gets the parent menu of this menu
  1193: //===========================================================================
  1194: 
  1195: void Menu::SetParent(Menu* pParent)
  1196: {
  1197:     m_pParent = pParent;
  1198: }
  1199: 
  1200: //====================
  1201: 
  1202: Menu* Menu::GetParent()
  1203: {
  1204:     return m_pParent;
  1205: }
  1206: 
  1207: //===========================================================================
  1208: // Functions: AddChild/RemoveChild
  1209: // Purpose: Adds/removes a child menu (submenu) to this menu
  1210: //===========================================================================
  1211: 
  1212: void Menu::AddChild(Menu* pChild)
  1213: {
  1214:     m_Children.push_back(pChild);
  1215: }
  1216: 
  1217: //====================
  1218: 
  1219: void Menu::RemoveChild(Menu* pChild)
  1220: {
  1221:     for (unsigned int i=0; i<m_Children.size(); i++)
  1222:     {
  1223:         if (m_Children[i] == pChild) m_Children.erase(m_Children.begin()+i);
  1224:     }
  1225: }
  1226: 
  1227: //===========================================================================
  1228: // Function: IsChildWindow
  1229: // Purpose: Helper function that returns true if hWnd is a child of this menu
  1230: //===========================================================================
  1231: 
  1232: bool Menu::IsChildWindow(HWND hWnd)
  1233: {
  1234:     for (unsigned int i = 0; i < m_Children.size(); i++)
  1235:     {
  1236:         if (hWnd == m_Children[i]->hMenuWnd) return true;
  1237:         else if (m_Children[i]->IsChildWindow(hWnd)) return true;
  1238:     }
  1239: 
  1240:     return false;
  1241: }
  1242: 
  1243: //===========================================================================
  1244: // Function: Moving
  1245: // Purpose: 
  1246: //===========================================================================
  1247: 
  1248: void Menu::Moving()
  1249: {
  1250:     if (!isValidated) return;
  1251: 
  1252:     if (pPreviewItem) pPreviewItem->Hide();
  1253: 
  1254:     MENUITERATOR item;
  1255:     HCURSOR hCurs = LoadCursor(NULL, IDC_SIZEALL);
  1256: 
  1257:     for (item=m_MenuItems.begin(); item != m_MenuItems.end(); item++)
  1258:     {
  1259:         (*item)->Moving();
  1260:         SetCursor(hCurs);
  1261:     }
  1262: }
  1263: 
  1264: //===========================================================================
  1265: // Function: OnUser
  1266: // Purpose: 
  1267: //===========================================================================
  1268: 
  1269: bool Menu::OnUser(int nMessage, WPARAM wParam, LPARAM lParam, LRESULT &lResult)
  1270: {
  1271:     if (!isValidated) return false;
  1272: 
  1273:     for (MENUITERATOR i = m_MenuItems.begin(); i != m_MenuItems.end(); i++)
  1274:     {
  1275:         if ((*i)->OnUser(nMessage, wParam, lParam, lResult)) return true;
  1276:     }
  1277: 
  1278:     return false;
  1279: }
  1280: 
  1281: //===========================================================================
  1282: // Function: Command
  1283: // Purpose: 
  1284: //===========================================================================
  1285: 
  1286: LRESULT Menu::Command(WPARAM wParam, LPARAM lParam)
  1287: {
  1288:     if (!isValidated) return 0;
  1289: 
  1290:     LRESULT l=0;
  1291:     MENUITERATOR item;
  1292: 
  1293:     for (item = m_MenuItems.begin(); item != m_MenuItems.end(); ++item)
  1294:     {
  1295:         l = (*item)->Command(wParam, lParam);
  1296:         if (l != 0) break;
  1297:     }
  1298: 
  1299:     return l;
  1300: }
  1301: 
  1302: //===========================================================================
  1303: // Function: Sort
  1304: // Purpose: Sorts the menu items in this menu
  1305: //===========================================================================
  1306: 
  1307: void Menu::Sort(int beginOffset, int endOffset)
  1308: {
  1309:     // Sort the menu items...
  1310:     sort(m_MenuItems.begin()+beginOffset, m_MenuItems.end()-endOffset, MenuItem::Compare);
  1311: }
  1312: 
  1313: //===========================================================================
  1314: // Functions: Invalidate/Validate
  1315: // Purpose: Validates all aspects of the menu, including dimensions etc.
  1316: //          Invalidate() forces validation the next time Validate() is called
  1317: //===========================================================================
  1318: 
  1319: void Menu::Invalidate()
  1320: {
  1321:     isValidated = false;
  1322: }
  1323: 
  1324: //====================
  1325: 
  1326: void Menu::Validate()
  1327: {
  1328:     if (isValidated) return;
  1329: 
  1330:     //====================
  1331: 
  1332:     // Calculate width padding for the menu title, frame and grip items...
  1333: 
  1334:     int titleWidthPadding;
  1335:     if (pSettings->MenuTitle->marginWidth > 0)
  1336:     {
  1337:         titleWidthPadding = (2 * pSettings->MenuTitle->borderWidth) + (2 * pSettings->MenuTitle->marginWidth);
  1338:     }
  1339:     else // "Legacy fallback"
  1340:     {
  1341:         titleWidthPadding = (2 * pSettings->MenuTitle->borderWidth) + (2 * pSettings->bevelWidth) + 4;
  1342:     }
  1343:     if (pSettings->MenuTitle->FontOutline) titleWidthPadding += 2;
  1344: 
  1345:     int frameWidthPadding;
  1346:     frameWidthPadding = pMenuCommon->m_nLeftIndent + pMenuCommon->m_nRightIndent + pSettings->bevelWidth;
  1347:     if (pSettings->MenuFrame->FontOutline || pSettings->MenuActive->FontOutline) frameWidthPadding += 2;
  1348: 
  1349:     int gripWidthPadding;
  1350:     if (pSettings->MenuGrip->marginWidth > 0)
  1351:     {
  1352:         gripWidthPadding = (2 * pSettings->MenuGrip->borderWidth) + (2 * pSettings->MenuGrip->marginWidth);
  1353:     }
  1354:     else // "Legacy fallback"
  1355:     {
  1356:         gripWidthPadding = (2 * pSettings->MenuGrip->borderWidth) + (2 * pSettings->bevelWidth) + 4;
  1357:     }
  1358:     if (pSettings->MenuGrip->FontOutline) gripWidthPadding += 2;
  1359: 
  1360:     //====================
  1361: 
  1362:     MENUITERATOR i;
  1363:     HDC hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
  1364:     HFONT hFontSaved = NULL;
  1365:     SIZE size;
  1366:     int tempMenuWidth = 0;
  1367: 
  1368:     for (i = m_MenuItems.begin(); i != m_MenuItems.end(); i++)
  1369:     {
  1370:         if ((strlen((*i)->m_pszTitleANSI) > 0) || (wcslen((*i)->m_pszTitleUnicode) > 0))
  1371:         {
  1372:             // Get the non-truncated width of this menu item's text...
  1373:             if ((*i)->itemType == MENUITEM_HEADER) hFontSaved = (HFONT) SelectObject(hDC, pMenuCommon->m_hTitleFont);
  1374:             else if ((*i)->itemType == MENUITEM_FOOTER) hFontSaved = (HFONT) SelectObject(hDC, pMenuCommon->m_hGripFont);
  1375:             else hFontSaved = (HFONT) SelectObject(hDC, pMenuCommon->m_hFrameFont);
  1376: 
  1377:             if (wcslen((*i)->m_pszTitleUnicode) > 0) GetTextExtentPoint32W(hDC, (*i)->m_pszTitleUnicode, wcslen((*i)->m_pszTitleUnicode), &size);
  1378:             else GetTextExtentPoint32(hDC, (*i)->m_pszTitleANSI, strlen((*i)->m_pszTitleANSI), &size);
  1379: 
  1380:             SelectObject(hDC, hFontSaved);
  1381: 
  1382:             if ((*i)->itemType == MENUITEM_HEADER)
  1383:             {
  1384:                 if (!pSettings->MenuTitle->parentRelative && (pSettings->MenuTitle->FontHeight > 0))
  1385:                 {
  1386:                     (*i)->SetWidth(size.cx + 2 + titleWidthPadding);
  1387:                 }
  1388:                 else (*i)->SetWidth(0); // menu.title disabled, do not include it when calculating the menu width...
  1389:             }
  1390:             else if ((*i)->itemType == MENUITEM_FOOTER)
  1391:             {
  1392:                 if (!pSettings->MenuGrip->parentRelative && (pSettings->MenuGrip->FontHeight > 0))
  1393:                 {
  1394:                     (*i)->SetWidth(size.cx + 2 + gripWidthPadding);
  1395:                 }
  1396:                 else (*i)->SetWidth(0); // menu.grip disabled, do not include it when calculating the menu width...
  1397:             }
  1398:             else (*i)->SetWidth(size.cx + 2 + frameWidthPadding);
  1399: 
  1400:             // Find the widest menu item belonging to this menu...
  1401:             tempMenuWidth = max((*i)->GetWidth(), tempMenuWidth);
  1402:         }
  1403:     }
  1404: 
  1405:     DeleteDC(hDC);
  1406: 
  1407:     //====================
  1408: 
  1409:     // Make sure that no menu item is wider than the maximum... (-> 300 pixels multiplied by the HiDPI scaling factor)
  1410:     tempMenuWidth = min((tempMenuWidth + (pSettings->MenuFrame->borderWidth * 2)), (300 * pSettings->scalingFactorHiDPI));
  1411: 
  1412:     //====================
  1413: 
  1414:     int tempMenuHeight = 0;
  1415: 
  1416:     if (pSettings->MenuFrame->borderWidth > pSettings->MenuTitle->borderWidth)
  1417:     {
  1418:         // Offset menu title according to bbLean bug/feature... (see above)
  1419:         tempMenuHeight = pSettings->MenuFrame->borderWidth - pSettings->MenuTitle->borderWidth;
  1420:     }
  1421:     else tempMenuHeight = 0;
  1422: /*
  1423:     if (!pSettings->MenuTitle->parentRelative && ((pSettings->MenuTitle->FontHeight > 0) || (pSettings->MenuTitle->marginWidth > 0)))
  1424:     {
  1425:         if (pSettings->MenuFrame->borderWidth > pSettings->MenuTitle->borderWidth) tempMenuHeight += (pSettings->MenuFrame->borderWidth - pSettings->MenuTitle->borderWidth);
  1426:     }
  1427: */
  1428:     for (i = m_MenuItems.begin(); i != m_MenuItems.end(); i++)
  1429:     {
  1430:         (*i)->SetWidth(tempMenuWidth);
  1431:         (*i)->SetPosition(0, tempMenuHeight);
  1432: 
  1433:         tempMenuHeight += (*i)->GetHeight();
  1434:     }
  1435: /*
  1436:     if (!pSettings->MenuGrip->parentRelative && ((pSettings->MenuGrip->FontHeight > 0) || (pSettings->MenuGrip->marginWidth > 0)))
  1437:     {
  1438:         if (pSettings->MenuFrame->borderWidth > pSettings->MenuGrip->borderWidth) tempMenuHeight -= (pSettings->MenuFrame->borderWidth - pSettings->MenuGrip->borderWidth);
  1439:     }
  1440: */
  1441:     //====================
  1442: 
  1443:     SetWindowPos(hMenuWnd, HWND_TOPMOST, 0, 0, tempMenuWidth, tempMenuHeight, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOCOPYBITS|SWP_NOSENDCHANGING);
  1444: 
  1445: //  HRGN menuRegion = CreateRoundRectRgn(0,0,tempMenuWidth+1,tempMenuHeight+1,12,12);
  1446: //  SetWindowRgn(hMenuWnd, menuRegion, true);
  1447: //  DeleteObject(menuRegion);
  1448: 
  1449:     // Refresh menu gradients cache and repaint the window...
  1450:     cachedMenuGradientsExist = false;
  1451:     if (cachedMenuBackground) DeleteDC(cachedMenuBackground);
  1452:     if (cachedMenuActive) DeleteDC(cachedMenuActive);
  1453:     cachedMenuBackground = cachedMenuActive = 0;
  1454: //  UpdateMenuWindow();
  1455: 
  1456:     isValidated = true;
  1457: }
  1458: 
  1459: //===========================================================================
  1460: // Functions: TogglePinned/IsPinned
  1461: // Purpose: 
  1462: //===========================================================================
  1463: 
  1464: void Menu::TogglePinned()
  1465: {
  1466:     Menu* pParent = this;
  1467:     while (pParent->m_pParent != NULL) pParent = pParent->m_pParent;
  1468:     // For the time being, we do not allow pinning of plugin menus... (tbd)
  1469: //  if (pParent == pMenuCommon->m_pPluginMenu) return;
  1470:     // ...and there's no use pinning the themes menu since clicking
  1471:     // on a [theme] menu item will restart the core elements anyway...
  1472:     if (pParent == pMenuCommon->m_pThemesMenu) return;
  1473: 
  1474:     //====================
  1475: 
  1476:     if (isPinned)
  1477:     {
  1478:         // Unpin and hide the menu...
  1479:         isPinned = false;
  1480:         if (pParent == pMenuCommon->m_pStylesMenu) pMenuCommon->stylesMenusPinned--;
  1481:         pMenuCommon->Hide();
  1482:     }
  1483:     else
  1484:     {
  1485:         // Pin the menu...
  1486:         isPinned = true;
  1487:         if (pParent == pMenuCommon->m_pStylesMenu) pMenuCommon->stylesMenusPinned++;
  1488:     }
  1489: 
  1490:     Invalidate();
  1491:     Validate();
  1492: 
  1493:     UpdateMenuWindow();
  1494: }
  1495: 
  1496: //====================
  1497: 
  1498: bool Menu::IsPinned()
  1499: {
  1500:     return isPinned;
  1501: };
  1502: 
  1503: //===========================================================================
  1504: // Function: ClearSelectionGroup
  1505: // Purpose: 
  1506: //===========================================================================
  1507: 
  1508: void Menu::ClearSelectionGroup(int group)
  1509: {
  1510:     MENUITERATOR i;
  1511:     for (i = m_MenuItems.begin(); i != m_MenuItems.end(); i++)
  1512:     {
  1513:         if ((*i)->m_selectionGroup == group) (*i)->m_isSelected = false;
  1514:     }
  1515: }
  1516: 
  1517: //===========================================================================
  1518: // Function: MenuWindowProc
  1519: // Purpose: Window procedure for all popup menus
  1520: //===========================================================================
  1521: 
  1522: LRESULT CALLBACK MenuWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1523: {
  1524:     POINT point;
  1525:     Menu* p = NULL;
  1526:     LRESULT ret = 0;
  1527: 
  1528:     //====================
  1529: 
  1530:     // Find the menu that should receive the message...
  1531:     for (unsigned int i=0; i < g_Menues.size(); i++)
  1532:     {
  1533:         if (hwnd == g_Menues[i]->GetWindow())
  1534:         {
  1535:             p = g_Menues[i];
  1536:             break;
  1537:         }
  1538:     }
  1539: 
  1540:     // Return DefWindowProc if no matching menu could be found...
  1541:     if (p == NULL) return DefWindowProc(hwnd, uMsg, wParam, lParam);
  1542:     // ...or if the receiving menu is not validated...
  1543: //  if (!p->isValidated) return DefWindowProc(hwnd, uMsg, wParam, lParam);
  1544: 
  1545:     //====================
  1546: 
  1547:     if (uMsg >= WM_USER) // Nb. this includes messages such as BB_DESKTOPINFO etc
  1548:     {
  1549:         LRESULT lResult = 0;
  1550: 
  1551:         if (p->OnUser((int) uMsg, wParam, lParam, lResult)) return lResult;
  1552:     }
  1553: 
  1554:     //====================
  1555: 
  1556:     switch (uMsg)
  1557:     {
  1558:         case WM_NCHITTEST:
  1559:         {
  1560: /*
  1561:             point.x = GET_X_LPARAM(lParam);
  1562:             point.y = GET_Y_LPARAM(lParam);
  1563:             ScreenToClient(hwnd, &point);
  1564:             ret = p->NcHitTest(point.x, point.y);
  1565:             if (ret == 0) ret = HTCLIENT;
  1566: */
  1567:             if (!p->isValidated) return HTCLIENT;
  1568: 
  1569:             GetCursorPos(&point);
  1570:             ScreenToClient(hwnd, &point);
  1571:             RECT r;
  1572:             GetClientRect(hwnd, &r);
  1573:             r.top += pMenuCommon->m_nTitleHeight;
  1574:             r.bottom -= pMenuCommon->m_nGripHeight;
  1575:             r.left += pSettings->MenuFrame->borderWidth;
  1576:             r.right -= pSettings->MenuFrame->borderWidth;
  1577: 
  1578:             if (!PtInRect(&r, point))
  1579:             {
  1580: //              if (!p->GetParent() || p->IsPinned()) return HTCAPTION;
  1581:                 return HTCAPTION; // -> PRELIMINARY: Allow dragging away submenus from its parent without prior pinning == subject to change/reversal pending any negative side-effects
  1582:             }
  1583:             
  1584:             return HTCLIENT;
  1585:         }
  1586:         break;
  1587: 
  1588:         //====================
  1589: 
  1590:         case WM_NCMOUSEMOVE:
  1591:         {
  1592:             if (!pSettings->menuEditboxAlreadyActive && !p->keyboardNavigationInProgress)
  1593:             {
  1594:                 point.x = GET_X_LPARAM(lParam);
  1595:                 point.y = GET_Y_LPARAM(lParam);
  1596:                 ScreenToClient(hwnd, &point);
  1597:                 p->Mouse(WM_MOUSEMOVE, point.x, point.y);
  1598:             }
  1599: 
  1600:             return 0;
  1601:         }
  1602:         break;
  1603: 
  1604:         //====================
  1605: 
  1606:         case WM_NCLBUTTONUP:
  1607:         case WM_NCRBUTTONUP:
  1608:         case WM_NCMBUTTONUP: // Midclick on title items -> Pin/Unpin menu
  1609:         case WM_NCLBUTTONDBLCLK: // Doubleclick on title items -> Pin/Unpin menu
  1610:         {
  1611:             point.x = GET_X_LPARAM(lParam);
  1612:             point.y = GET_Y_LPARAM(lParam);
  1613:             ScreenToClient(hwnd, &point);
  1614:             p->Mouse(uMsg, point.x, point.y);
  1615:             return 0;
  1616:         }
  1617:         break;
  1618: /*
  1619:         case WM_NCLBUTTONDOWN:
  1620:         case WM_NCRBUTTONDOWN:
  1621:         case WM_NCMBUTTONDOWN:
  1622:         {
  1623:             return DefWindowProc(hwnd, uMsg, wParam, lParam);
  1624:         }
  1625:         break;
  1626: */
  1627:         //====================
  1628: 
  1629:         case WM_MOUSEMOVE:
  1630:         {
  1631:             if (!pSettings->menuEditboxAlreadyActive)
  1632:             {
  1633:                 // Hack to make keyboard navigation work
  1634:                 // when the mouse is above the window...
  1635:                 static int lastX = 0;
  1636:                 static int lastY = 0;
  1637: 
  1638:                 if (lastX == GET_X_LPARAM(lParam) && lastY == GET_Y_LPARAM(lParam))
  1639:                     return 0;
  1640: 
  1641:                 lastX = GET_X_LPARAM(lParam);
  1642:                 lastY = GET_Y_LPARAM(lParam);
  1643: 
  1644:                 p->Mouse(uMsg, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  1645:             }
  1646: 
  1647:             return 0;
  1648:         }
  1649:         break;
  1650: 
  1651:         //====================
  1652: 
  1653:         case WM_LBUTTONUP:
  1654:         case WM_RBUTTONUP:
  1655:         case WM_MBUTTONUP:
  1656:         case WM_LBUTTONDOWN:
  1657:         case WM_RBUTTONDOWN:
  1658:         case WM_MBUTTONDOWN:
  1659:         case WM_LBUTTONDBLCLK:
  1660:         {
  1661:             p->Mouse(uMsg, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  1662:             return 0;
  1663:         }
  1664:         break;
  1665: 
  1666:         //====================
  1667: 
  1668:         case WM_EXITSIZEMOVE:
  1669:         {
  1670:             RECT r;
  1671:             GetWindowRect(p->GetWindow(), &r);
  1672:             p->menuX = r.left;
  1673:             p->menuY = r.top;
  1674:             return 0;
  1675:         }
  1676:         break;
  1677: 
  1678:         //====================
  1679: 
  1680:         case WM_MOUSEWHEEL:
  1681:         {
  1682:             if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) p->Mouse(uMsg, 0, 0);
  1683:             else p->Mouse(uMsg, 1, 0);
  1684:             return 0;
  1685:         }
  1686:         break;
  1687: 
  1688:         // case WM_MOUSEHWHEEL: { } break;
  1689: 
  1690:         //====================
  1691: 
  1692:         case WM_KEYDOWN: // -> Allow keyboard navigation when the menu is the foreground window...
  1693:         {
  1694:             p->keyboardNavigationInProgress = true;
  1695:             KillTimer(p->GetWindow(), MENU_TRACK_MOUSE_TIMER);
  1696: 
  1697:             switch(wParam)
  1698:             {
  1699:                 case VK_DOWN:   // Move to the next menu item
  1700:                 case VK_UP:     // Move to the previous menu item
  1701:                 case VK_RIGHT:  // Enter submenu (if the menu item is a folder item)
  1702:                 case VK_LEFT:   // Return from submenu (moving back to the parent menu)
  1703:                 case VK_HOME:   // Jump to the first menu item
  1704:                 case VK_END:    // Jump to the last menu item
  1705:                 case VK_SPACE:  // Simulate a *right* click on command, boolean or string editing items, keeping the menu open.
  1706:                 case VK_RETURN: // Simulate a *left* click on command items, closing the menu.
  1707:                 case VK_PRIOR:  // (PageUp) Increase the value of integer editing menu items
  1708:                 case VK_ADD:    // (NumPad Add) ...
  1709:                 case VK_NEXT:   // (PageDown) Decrease the value of integer editing menu items
  1710:                 case VK_SUBTRACT: // (NumPad Subtract) ...
  1711:                 {
  1712:                     MENUITERATOR i1, i2;
  1713:                     Menu* m = NULL;
  1714: 
  1715:                     if (wParam == VK_HOME) // -> Jump to the first menu item!
  1716:                     {
  1717:                         pMenuCommon->FindActiveAndDeactivate(p, false);
  1718:                         if (pPreviewItem) pPreviewItem->Hide();
  1719: 
  1720:                         int screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
  1721:                         int screenY = GetSystemMetrics(SM_YVIRTUALSCREEN);
  1722:                         if (p->menuHeight > screenHeight) p->menuY = screenY; // -> Scroll to the top if the menu is taller than the screen...
  1723: 
  1724:                         i1 = p->m_MenuItems.begin() + 2; // -> Skip MENUITEM_HEADER (i.e. menu title item) and first MENUITEM_MARGINPAD (i.e. padding in between the menu title and frame)
  1725:                         (*i1)->Active(true);
  1726: 
  1727:                         if ((*i1)->itemType != MENUITEM_FOLDER) PlaySoundFX(SFX_MENU_NAVIGATE);
  1728:                     }
  1729:                     else if (wParam == VK_END) // -> Jump to the last menu item!
  1730:                     {
  1731:                         pMenuCommon->FindActiveAndDeactivate(p, false);
  1732:                         if (pPreviewItem) pPreviewItem->Hide();
  1733: 
  1734:                         int screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
  1735:                         if (p->menuHeight > screenHeight) p->menuY = screenHeight - p->menuHeight; // -> Scroll to the bottom if the menu is taller than the screen...
  1736: 
  1737:                         i1 = p->m_MenuItems.end() - 3; // -> Skip MENUITEM_FOOTER (i.e. menu grip or non-visible footer item) and last MENUITEM_MARGINPAD (i.e. padding in between the menu frame and footer/grip)
  1738:                         (*i1)->Active(true);
  1739: 
  1740:                         if ((*i1)->itemType != MENUITEM_FOLDER) PlaySoundFX(SFX_MENU_NAVIGATE);
  1741:                     }
  1742:                     else
  1743:                     {
  1744:                         bool activeFound = false;
  1745:                         for (i1 = p->m_MenuItems.begin(); i1 != p->m_MenuItems.end(); i1++)
  1746:                         {
  1747:                             if ((*i1)->IsActive())
  1748:                             {
  1749:                                 m = (*i1)->m_pParent;
  1750:                                 activeFound = true;
  1751:                                 break;
  1752:                             }
  1753:                         }
  1754: 
  1755:                         if (!activeFound)
  1756:                         {
  1757:                             i1 = p->m_MenuItems.begin();
  1758:                             i1++; // Skip MENUITEM_HEADER (i.e. menu title item)
  1759:                             i1++; // Skip first MENUITEM_MARGINPAD (i.e. padding in between the menu title and frame)
  1760:                             (*i1)->Active(true);
  1761:                         }
  1762:                         else
  1763:                         {
  1764:                             i2 = i1;
  1765: 
  1766:                             if (wParam == VK_DOWN)
  1767:                             {
  1768:                                 while (i2 < p->m_MenuItems.end()-2)
  1769:                                 {
  1770:                                     i2++;
  1771:                                     if ((*i2)->m_pParent == m)
  1772:                                     {
  1773:                                         if ((*i2)->itemType != MENUITEM_SEPARATOR && (*i2)->itemType != MENUITEM_MARGINPAD && (*i2)->itemType != MENUITEM_FOOTER)
  1774:                                         {
  1775:                                             (*i1)->Active(false);
  1776:                                             if (pPreviewItem) pPreviewItem->Hide();
  1777: 
  1778:                                             // Allow menu scrolling if the menu is taller than the screen...
  1779:                                             int screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
  1780:                                             if (p->menuHeight > screenHeight)
  1781:                                             {
  1782:                                                 p->menuY -= pMenuCommon->m_nSubmenuHeight;
  1783:                                                 if ((p->menuY + p->menuHeight) < screenHeight) p->menuY = screenHeight - p->menuHeight;
  1784:                                             }
  1785: 
  1786:                                             (*i2)->Active(true);
  1787: 
  1788:                                             if ((*i2)->itemType != MENUITEM_FOLDER) PlaySoundFX(SFX_MENU_NAVIGATE);
  1789:                                             break;
  1790:                                         }
  1791:                                     }
  1792:                                 }
  1793:                             }
  1794:                             else if (wParam == VK_UP)
  1795:                             {
  1796:                                 while (i2 > p->m_MenuItems.begin()+2)
  1797:                                 {
  1798:                                     i2--;
  1799:                                     if ((*i2)->m_pParent == m)
  1800:                                     {
  1801:                                         if ((*i2)->itemType != MENUITEM_SEPARATOR && (*i2)->itemType != MENUITEM_MARGINPAD && (*i2)->itemType != MENUITEM_HEADER)
  1802:                                         {
  1803:                                             (*i1)->Active(false);
  1804:                                             if (pPreviewItem) pPreviewItem->Hide();
  1805: 
  1806:                                             // Allow menu scrolling if the menu is taller than the screen...
  1807:                                             int screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
  1808:                                             if (p->menuHeight > screenHeight)
  1809:                                             {
  1810:                                                 p->menuY += pMenuCommon->m_nSubmenuHeight;
  1811:                                                 int screenY = GetSystemMetrics(SM_YVIRTUALSCREEN);
  1812:                                                 if (p->menuY > screenY) p->menuY = screenY;
  1813:                                             }
  1814: 
  1815:                                             (*i2)->Active(true);
  1816: 
  1817:                                             if ((*i2)->itemType != MENUITEM_FOLDER) PlaySoundFX(SFX_MENU_NAVIGATE);
  1818:                                             break;
  1819:                                         }
  1820:                                     }
  1821:                                 }
  1822:                             }
  1823:                             else if (wParam == VK_RIGHT)
  1824:                             {
  1825:                                 if ((*i1)->itemType == MENUITEM_FOLDER)
  1826:                                 {
  1827:                                     FolderItem* item = (FolderItem*)*i1;
  1828:                                     item->ShowSubMenu();
  1829:                                     SetForegroundWindow((*i1)->m_pSubMenu->GetWindow());
  1830:                                     SendMessage((*i1)->m_pSubMenu->GetWindow(), WM_KEYDOWN, VK_HOME, NULL);
  1831:                                     return 0;
  1832:                                 }
  1833:                             }
  1834:                             else if (wParam == VK_LEFT)
  1835:                             {
  1836:                                 if ((*i1)->m_pParent->GetParent() != NULL)
  1837:                                 {
  1838:                                     (*i1)->Active(false);
  1839:                                     (*i1)->m_pParent->Hide(HIDE_THIS);
  1840:                                     if (pPreviewItem) pPreviewItem->Hide();
  1841:                                     Menu* parentMenu = (*i1)->m_pParent->GetParent();
  1842:                                     SetForegroundWindow(parentMenu->GetWindow());
  1843:                                     PlaySoundFX(SFX_MENU_NAVIGATE);
  1844:                                 }
  1845:                             }
  1846:                             else if (wParam == VK_SPACE)
  1847:                             {
  1848:                                 if (((*i1)->itemType == MENUITEM_COMMAND) || ((*i1)->itemType == MENUITEM_BOOLEAN) || ((*i1)->itemType == MENUITEM_EDITSTRING)) (*i1)->Invoke(2); // -> Simulating a *right* click on command, boolean or string editing items, keeping the menu open...
  1849:                                 return 0;
  1850:                             }
  1851:                             else if (wParam == VK_RETURN)
  1852:                             {
  1853:                                 if (((*i1)->itemType == MENUITEM_COMMAND)) (*i1)->Invoke(1); // -> Simulating a *left* click on command items, closing the menu.
  1854:                                 return 0;
  1855:                             }
  1856:                             else if ((wParam == VK_PRIOR) || (wParam == VK_ADD))
  1857:                             {
  1858:                                 if ((*i1)->itemType == MENUITEM_EDITINT)
  1859:                                 {
  1860:                                     POINT pt;
  1861:                                     pt.x = (*i1)->m_nLeft + (*i1)->m_nWidth - 1; // Simulating a mouse click on the right hand side of the menu item -> Increase value!
  1862:                                     pt.y = (*i1)->m_nTop;
  1863:                                     (*i1)->Mouse(WM_LBUTTONUP, pt);
  1864:                                     return 0;
  1865:                                 }
  1866:                             }
  1867:                             else if ((wParam == VK_NEXT) || (wParam == VK_SUBTRACT))
  1868:                             {
  1869:                                 if ((*i1)->itemType == MENUITEM_EDITINT)
  1870:                                 {
  1871:                                     POINT pt;
  1872:                                     pt.x = (*i1)->m_nLeft + 1; // Simulating a mouse click on the left hand side of the menu item -> Decrease value!
  1873:                                     pt.y = (*i1)->m_nTop;
  1874:                                     (*i1)->Mouse(WM_LBUTTONUP, pt);
  1875:                                     return 0;
  1876:                                 }
  1877:                             }
  1878:                         }
  1879:                     }
  1880: 
  1881: //                  p->UpdateMenuWindow();
  1882:                     return 0;
  1883:                 }
  1884:                 break;
  1885: 
  1886:                 case VK_INSERT: // Toggle menu pinned
  1887:                 {
  1888: //                  if (!p->isPinned) p->isPinned = true;
  1889: //                  else p->isPinned = false;
  1890: //                  p->UpdateMenuWindow();
  1891:                     p->TogglePinned();
  1892:                     return 0;
  1893:                 }
  1894:                 break;
  1895: 
  1896:                 case VK_ESCAPE: // Close the menu
  1897:                 case VK_DELETE:
  1898:                 {
  1899:                     if (pPreviewItem) pPreviewItem->Hide();
  1900:                     if (pMenuCommon) pMenuCommon->Hide();
  1901:                     return 0;
  1902:                 }
  1903:                 break;
  1904: 
  1905:                 default: { return 0; }
  1906:             }
  1907:         }
  1908:         break;
  1909: 
  1910:         //====================
  1911: 
  1912:         case WM_CHAR: // -> Allow keyboard search on the *first* character of menu item titles when the menu is the foreground window...
  1913:         {
  1914:             p->keyboardNavigationInProgress = true;
  1915:             KillTimer(p->GetWindow(), MENU_TRACK_MOUSE_TIMER);
  1916: 
  1917:             char typeAhead = tolower((char)wParam); // If applicable, use the lowercase version of the character...
  1918: 
  1919:             MENUITERATOR i;
  1920:             bool activeFound = false, searchFound = false;
  1921: 
  1922:             for (i = p->m_MenuItems.begin()+2; i != p->m_MenuItems.end()-2; i++) // Skip MENUITEM_HEADER/MARGINPAD/FOOTER items...
  1923:             {
  1924:                 if (!activeFound)
  1925:                 {
  1926:                     // Skip up to the currently active menu item... (i.e. accept any *following* match)
  1927:                     if ((*i)->IsActive()) activeFound = true;
  1928:                     continue;
  1929:                 }
  1930: 
  1931:                 if (((tolower((*i)->m_pszTitleANSI[0]) == typeAhead)))
  1932:                 {
  1933:                     searchFound = true;
  1934:                     pMenuCommon->ScrollToItem(p, (*i));
  1935:                     break;
  1936:                 }
  1937:             }
  1938: 
  1939:             if (!searchFound)
  1940:             {
  1941:                 for (i = p->m_MenuItems.begin()+2; i != p->m_MenuItems.end()-2; i++) // Skip MENUITEM_HEADER/MARGINPAD/FOOTER items...
  1942:                 {
  1943:                     if (((tolower((*i)->m_pszTitleANSI[0]) == typeAhead))) // Accept *any* match...
  1944:                     {
  1945:                         pMenuCommon->ScrollToItem(p, (*i));
  1946:                         break;
  1947:                     }
  1948:                 }
  1949:             }
  1950: 
  1951:             return 0;
  1952:         }
  1953: 
  1954:         //====================
  1955: 
  1956:         case WM_CLOSE:
  1957:             return 0;
  1958: 
  1959:         //====================
  1960: 
  1961:         case WM_ACTIVATE:
  1962:         {
  1963:             p->Activate(LOWORD(wParam), (HWND)lParam);
  1964:         }
  1965:         break;
  1966: 
  1967:         //====================
  1968: 
  1969:         case WM_TIMER:
  1970:         {
  1971:             p->Timer(wParam);
  1972:             return 0;
  1973:         }
  1974:         break;
  1975: 
  1976:         //====================
  1977: 
  1978:         case WM_MOVING:
  1979:         {
  1980:             p->Moving();
  1981:         }
  1982:         break;
  1983: 
  1984:         //====================
  1985: /*
  1986:         case WM_WINDOWPOSCHANGING:
  1987:         {
  1988:             if (IsWindowVisible(hwnd)) SnapWindowToEdge((WINDOWPOS*)lParam, pSettings->edgeSnapThreshold, true);
  1989:         }
  1990:         break;
  1991: */
  1992:         //====================
  1993: /*
  1994:         case WM_ERASEBKGND:
  1995:         return true;
  1996: */
  1997:         //====================
  1998: 
  1999:         case WM_COMMAND:
  2000:         {
  2001:             if (pSettings->menuEditboxAlreadyActive)
  2002:             {
  2003: //              char msg[300];
  2004: //              if (HIWORD(wParam) == EN_CHANGE) sprintf_s(msg, sizeofArray(msg), "Menu -> WM_COMMAND -> EN_CHANGE received...");
  2005: //              else if (HIWORD(wParam) == EN_UPDATE) sprintf_s(msg, sizeofArray(msg), "Menu -> WM_COMMAND -> EN_UPDATE received...");
  2006: //              else sprintf_s(msg, sizeofArray(msg), "Menu -> WM_COMMAND -> Unknown message received... -> 0x%x", (int)wParam);
  2007: //              SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_INFORMATION_MESSAGE, (LPARAM)msg);
  2008:                 return 0;
  2009:             }
  2010:             else p->Command(wParam, lParam);
  2011:         }
  2012:         break;
  2013: 
  2014:         //====================
  2015: 
  2016:         case WM_MOUSELEAVE:
  2017:         {
  2018:             p->MouseLeave();
  2019:         }
  2020:         break;
  2021: 
  2022:         //====================
  2023: /*
  2024:         case WM_DROPFILES:
  2025:         {
  2026:             static TCHAR source[MAX_LINE_LENGTH];
  2027:             DragQueryFile((HDROP)wParam, 0, source, sizeof(source));
  2028:             DragFinish((HDROP)wParam);
  2029:             
  2030:             RECT r;
  2031:             MENUITERATOR item;
  2032:             for (item = p->m_MenuItems.begin(); item != p->m_MenuItems.end() && !p->m_MenuItems.empty(); item++)
  2033:             {
  2034:                 POINT mousepos;
  2035:                 GetCursorPos(&mousepos);
  2036:                 ScreenToClient(hwnd, &mousepos);
  2037:                 (*item)->GetItemRect(&r);
  2038: 
  2039:                 if (PtInRect(&r, mousepos) && (*item)->itemType == MENUITEM_COMMAND)
  2040:                 {
  2041:                     if (!strlen((*item)->m_pszArgument)) break;
  2042: 
  2043:                     char destination[MAX_PATH];
  2044:                     strcpy_s(destination, sizeofArray(destination), (*item)->m_pszArgument);
  2045:                     if (destination[0] == '\"') StrRemoveEncap(destination);
  2046:                     if (destination[1] == ':')
  2047:                     {
  2048:                         if (source[0] == '\"') StrRemoveEncap(source);
  2049:                         char tempSource[MAX_PATH];
  2050:                         strcpy_s(tempSource, sizeofArray(tempSource), source);
  2051:                         int n = GetParentFolder(tempSource);
  2052: 
  2053:                         GetParentFolder(destination);
  2054:                         strcat_s(destination, sizeofArray(destination), &source[n]);
  2055: 
  2056:                         // Move file from the source folder to the destination folder...
  2057:                         MoveFile(source, destination);
  2058: 
  2059:                         // Look for a line that should "always" exist in a style file; if it
  2060:                         // does we create a [style] menu item, otherwise an [exec] menu item...
  2061:                         char cmd[8];
  2062:                         if (strlen(ReadString(destination, "menu.frame", ""))) strcpy_s(cmd, sizeofArray(cmd), "[style]");
  2063:                         else strcpy_s(cmd, sizeofArray(cmd), "[exec]");
  2064: 
  2065:                         // Create a new menu item for the moved file in the destination menu...
  2066:                         // (note that this new menu item gets added *after* the bottom items,
  2067:                         // so we need to sort the menu items afterwards)...
  2068:                         pMenuCommon->CreateMenuItem(p, MENUITEM_COMMAND, cmd, destination, &source[n], false);
  2069:                         p->Sort(2,0); // Skip the header item and top marginWidth padding item...
  2070: 
  2071:                         // Recalculate menu dimensions and redraw window...
  2072:                         p->Invalidate();
  2073:                         p->Validate();
  2074:                 }
  2075:                     break;
  2076:                 }
  2077:             }
  2078: 
  2079:             return 0;
  2080:         }
  2081:         break;
  2082: */
  2083:         //====================
  2084: 
  2085:         case BB_RECONFIGURE:
  2086:         case BB_REDRAWGUI:
  2087:         {
  2088:             if ((uMsg == BB_REDRAWGUI) && !(wParam & BBRG_MENU)) break;
  2089: 
  2090:             // Block access to the menu...
  2091:             p->Invalidate();
  2092: 
  2093:             // Set new menu item dimensions based on values calculated in MenuCommon...
  2094:             MENUITERATOR m;
  2095:             for (m = p->m_MenuItems.begin(); m != p->m_MenuItems.end(); m++)
  2096:             {
  2097:                 if ((*m)->itemType == MENUITEM_HEADER) (*m)->SetHeight(pMenuCommon->m_nTitleHeight);
  2098:                 else if ((*m)->itemType == MENUITEM_SEPARATOR) (*m)->SetHeight(pMenuCommon->m_nSeparatorHeight);
  2099:                 else if ((*m)->itemType == MENUITEM_MARGINPAD) (*m)->SetHeight(pMenuCommon->m_nMarginHeight);
  2100:                 else if ((*m)->itemType == MENUITEM_FOOTER) (*m)->SetHeight(pMenuCommon->m_nGripHeight);
  2101:                 else (*m)->SetHeight(pMenuCommon->m_nSubmenuHeight);
  2102:             }
  2103: 
  2104:             // Recalculate the menu dimensions...
  2105:             p->Validate();
  2106:             // ...and update the menu window if its currently visible...
  2107:             if (IsWindowVisible(p->hMenuWnd)) p->UpdateMenuWindow();
  2108:         }
  2109:         break;
  2110: 
  2111:         //====================
  2112: 
  2113:         case BB_MENU:
  2114:         {
  2115:             Menu* pMenu = NULL;
  2116: 
  2117:             if (wParam == 0) // Main menu
  2118:             {
  2119:                 WIN32_FIND_DATA oldData = p->data;
  2120:                 HANDLE hFind = FindFirstFile(pSettings->menuFile, &p->data);
  2121:                 FindClose(hFind);
  2122:                 if (CompareFileTime(&p->data.ftLastWriteTime, &oldData.ftLastWriteTime) == 1)
  2123:                 {
  2124:                     pMenuCommon->Hide();
  2125:                     pMenuCommon->UpdateMainMenu(false);
  2126:                 }
  2127: 
  2128:                 pMenu = pMenuCommon->m_pMainMenu;
  2129:             }
  2130:             else if (wParam == 1) // Workspaces menu
  2131:             {
  2132:                 pMenu = pMenuCommon->m_pWorkspacesMenu;
  2133:             }
  2134:             else if (wParam == 2 || wParam == 3 || wParam == 4) // Configuration menu (nb. previously used for the legacy Toolbar/Systembar/Dock menus, now replaced with the common Configuration menu)
  2135:             {
  2136:                 pMenu = pMenuCommon->m_pConfigMenu;
  2137:             }
  2138:             else if (wParam == 5) // Styles menu
  2139:             {
  2140:                 pMenu = pMenuCommon->m_pStylesMenu;
  2141:             }
  2142:             else if (wParam == 6) // Themes menu
  2143:             {
  2144:                 pMenu = pMenuCommon->m_pThemesMenu;
  2145:             }
  2146: 
  2147:             if (pMenu != NULL)
  2148:             {
  2149:                 pMenuCommon->Hide();
  2150:                 pMenu->Show();
  2151:             }
  2152:         }
  2153:         break;
  2154: 
  2155:         //====================
  2156: 
  2157:         case BB_HIDEMENU:
  2158:         {
  2159:             pMenuCommon->Hide();
  2160: //          p->Hide(HIDE_PARENTS);
  2161: //          p->Hide(HIDE_CHILDREN);
  2162:         }
  2163:         break;
  2164: 
  2165:         //====================
  2166: 
  2167:         case WM_CTLCOLOREDIT: // -> Used by StringItem editor popup dialog windows
  2168:         {
  2169:             // SetTextColor((HDC)wParam, pSettings->MenuFrame->TextColor);
  2170:             // SetBkColor((HDC)wParam, pSettings->MenuFrame->Color);
  2171:             SetTextColor((HDC)wParam, 0x000000);
  2172:             SetBkColor((HDC)wParam, 0xffffff);
  2173:             return (LRESULT)GetStockObject(NULL_BRUSH);
  2174:         }
  2175:         break;
  2176: 
  2177:         //====================
  2178: 
  2179:         default:
  2180:         {
  2181:             ret = DefWindowProc(hwnd, uMsg, wParam, lParam);
  2182:         }
  2183: 
  2184:         //====================
  2185:     }
  2186: 
  2187:     return ret;
  2188: }
  2189: 
  2190: //===========================================================================
  2191: