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 "PopupDialog.h" 
    32: 
    33: const char szPopupDialogName[] = "BBPopupDialog"; // Window class etc.
    34: 
    35: extern PopupDialog* pPopupDialog;
    36: extern Settings* pSettings;
    37: extern BImage* pBImage;
    38: 
    39: int popupDialogMessageSubscription[] = { BB_RECONFIGURE, BB_POPUPMESSAGE, 0 };
    40: 
    41: //===========================================================================
    42: 
    43: PopupDialog::PopupDialog(HINSTANCE hInstance)
    44: {
    45:     hPopupDialogWnd = NULL;
    46:     hBlackboxWnd = GetBBWnd();
    47:     hPopupDialogInstance = hInstance;
    48:     cachedBackground = CreateCompatibleDC(NULL);
    49:     message.title[0] = message.body[0] = '\0';
    50:     PopupDialogHidden = true;
    51:     PopupTypeYesNo = false;
    52:     dynamicFontHeight = 15;
    53: 
    54:     //====================
    55: 
    56:     // Get size and position for our window...
    57:     GetDimensions();
    58: 
    59:     //====================
    60: 
    61:     // Register window class...
    62:     WNDCLASS wc;
    63:     ZeroMemory(&wc, sizeof(wc));
    64:     wc.hInstance = hPopupDialogInstance;        // hInstance
    65:     wc.lpfnWndProc = PopupDialogProc;           // Window procedure
    66:     wc.lpszClassName = szPopupDialogName;       // Window class name
    67:     wc.hbrBackground = NULL;                    // No class background brush
    68:     wc.style = CS_DBLCLKS;                      // Class styles (e.g. accept doubleclicks)
    69:     wc.hCursor = LoadCursor(NULL, IDC_ARROW);   // Always display the regular arrow cursor
    70: 
    71:     if (!RegisterClass(&wc))
    72:     {
    73:         MessageBox(0, "Error registering popup dialog window class!", szPopupDialogName, MB_OK | MB_ICONERROR | MB_TOPMOST);
    74:         Log("PopupDialog: Error registering window class!", NULL);
    75:         return;
    76:     }
    77: 
    78:     // Create popup dialog window...
    79:     hPopupDialogWnd = CreateWindowEx(
    80:         WS_EX_TOOLWINDOW | WS_EX_ACCEPTFILES | WS_EX_NOACTIVATE | WS_EX_LAYERED,    // window style
    81:         szPopupDialogName,                                                          // window class
    82:         NULL,                                                                       // window name
    83:         WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,                               // window parameters
    84:         PopupDialogX,                                                               // x position
    85:         PopupDialogY,                                                               // y position
    86:         PopupDialogWidth,                                                           // window width
    87:         PopupDialogHeight,                                                          // window height
    88:         NULL, //pDesktop->hDesktopWnd,                                              // owner window
    89:         NULL,                                                                       // no menu
    90:         hPopupDialogInstance,                                                       // hInstance
    91:         NULL                                                                        // no window creation data
    92:     );
    93: 
    94:     if (!hPopupDialogWnd)
    95:     {
    96:         UnregisterClass(szPopupDialogName, hPopupDialogInstance); // Unregister window class
    97:         MessageBox(0, "Error creating popup dialog window!", szPopupDialogName, MB_OK | MB_ICONERROR | MB_TOPMOST);
    98:         Log("PopupDialog: Error creating window!", NULL);
    99:         return;
   100:     }
   101: 
   102:     ShowWindow(hPopupDialogWnd, SW_HIDE);
   103:     MakeSticky(hPopupDialogWnd); // Make the popup dialog window sticky... (i.e. shown regardless of the current workspace)
   104: 
   105:     //====================
   106: 
   107:     // Subscribe to Blackbox messages applicable to popup dialogs...
   108:     SendMessage(hBlackboxWnd, BB_REGISTERMESSAGE, (WPARAM)hPopupDialogWnd, (LPARAM)popupDialogMessageSubscription);
   109: }
   110: 
   111: //===========================================================================
   112: 
   113: PopupDialog::~PopupDialog()
   114: {
   115:     // Unsubscribe to previously subscribed Blackbox messages...
   116:     SendMessage(hBlackboxWnd, BB_UNREGISTERMESSAGE, (WPARAM)hPopupDialogWnd, (LPARAM)popupDialogMessageSubscription);
   117: 
   118:     if (hPopupDialogWnd) DestroyWindow(hPopupDialogWnd); // Destroy window....
   119:     UnregisterClass(szPopupDialogName, hPopupDialogInstance); // Unregister window class...
   120:     if (cachedBackground) DeleteDC(cachedBackground); // Delete the cached gradient...
   121: }
   122: 
   123: //===========================================================================
   124: 
   125: LRESULT CALLBACK PopupDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
   126: {
   127: 
   128:     switch (message)
   129:     {
   130:         //====================
   131: 
   132:         case BB_POPUPMESSAGE:
   133:         {
   134:             if ((wParam == NULL) && (lParam == NULL))
   135:             {
   136:                 ShowWindow(pPopupDialog->hPopupDialogWnd, SW_HIDE);
   137:                 pPopupDialog->PopupDialogHidden = true;
   138:             }
   139:             else
   140:             {
   141: //              strncpy(pPopupDialog->message.title, (LPCSTR)wParam, sizeof(pPopupDialog->message.title));
   142: //              strncpy(pPopupDialog->message.body, (LPCSTR)lParam, sizeof(pPopupDialog->message.body));
   143:                 strncpy_s(pPopupDialog->message.title, sizeofArray(pPopupDialog->message.title), (LPCSTR)wParam, _TRUNCATE);
   144:                 strncpy_s(pPopupDialog->message.body, sizeofArray(pPopupDialog->message.body), (LPCSTR)lParam, _TRUNCATE);
   145:                 pPopupDialog->message.title[sizeof(pPopupDialog->message.title)-1] = '\0';
   146:                 pPopupDialog->message.body[sizeof(pPopupDialog->message.body)-1] = '\0';
   147: 
   148:                 if (!_strnicmp(pPopupDialog->message.body, "@xoblite", 8) || !_strnicmp(pPopupDialog->message.body, "@GoToURL", 8)) pPopupDialog->PopupTypeYesNo = true;
   149:                 else pPopupDialog->PopupTypeYesNo = false;
   150: 
   151:                 pPopupDialog->PopupDialogHidden = false;
   152:                 pPopupDialog->UpdatePopupDialog();
   153: 
   154:                 ShowWindow(pPopupDialog->hPopupDialogWnd, SW_SHOWNOACTIVATE);
   155: //              if (pPopupDialog->PopupTypeYesNo) SetWindowPos(pPopupDialog->hPopupDialogWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER);
   156: //              else
   157: //              {
   158: //                  SetWindowPos(pPopupDialog->hPopupDialogWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER);
   159: //                  SetWindowPos(pPopupDialog->hPopupDialogWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER);
   160: //              }
   161:                 SetWindowPos(pPopupDialog->hPopupDialogWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER);
   162: 
   163:                 if (pPopupDialog->PopupTypeYesNo) SetForegroundWindow(pPopupDialog->hPopupDialogWnd); // Move the focus to the popup dialog window to allow Yes/No selection via the keyboard... (see WM_KEYDOWN below)
   164:             }
   165:         }
   166:         break;
   167: 
   168:         //===========================================================================
   169: 
   170:         case BB_RECONFIGURE:
   171:         case WM_DISPLAYCHANGE:
   172:         {
   173:             if (!pPopupDialog->PopupDialogHidden) pPopupDialog->UpdatePopupDialog();
   174:         }
   175:         break;
   176: 
   177:         case WM_SETTINGCHANGE:
   178:         {
   179:             if (!pPopupDialog->PopupDialogHidden)
   180:             {
   181:                 // Update the [centered] popup dialog window's position if the work area changes...
   182:                 // (e.g. if the Explorer taskbar is moved to another screen edge)
   183:                 if (wParam == SPI_SETWORKAREA) pPopupDialog->UpdatePopupDialog();
   184:             }
   185:             return 0;
   186:         }
   187: 
   188:         //====================
   189: 
   190:         case WM_CLOSE:
   191:             return 0;
   192: 
   193:         //====================
   194: 
   195:         case WM_NCHITTEST:
   196:         {
   197:             RECT r;
   198:             GetWindowRect(hwnd, &r);
   199:             int relativeY = HIWORD(lParam) - r.top; // Get y position relative to the window... (nb. lParam x/y position is relative to the *screen*)
   200:             if (relativeY <= pPopupDialog->PopupDialogTitleHeight) return HTCAPTION;
   201:             else return HTCLIENT;
   202:         }
   203:         break;
   204: 
   205:         //====================
   206: 
   207:         case WM_LBUTTONUP:
   208:         {
   209:             if (pPopupDialog->PopupTypeYesNo)
   210:             {
   211:                 POINT pt;
   212:                 pt.x = LOWORD(lParam);
   213:                 pt.y = HIWORD(lParam);
   214: 
   215:                 // Did the user click the "Yes" button?
   216:                 if (PtInRect(&pPopupDialog->popupYesRect, pt))
   217:                 {
   218:                     ShowWindow(pPopupDialog->hPopupDialogWnd, SW_HIDE);
   219:                     pPopupDialog->PopupDialogHidden = true;
   220: //                  PlaySoundFX(SFX_TASKBAR_MINIMIZE);
   221:                     BBSmartExecute(pPopupDialog->message.body);
   222:                     break;
   223:                 }
   224:                 else if (PtInRect(&pPopupDialog->popupNoRect, pt))
   225:                 {
   226:                     ShowWindow(pPopupDialog->hPopupDialogWnd, SW_HIDE);
   227:                     pPopupDialog->PopupDialogHidden = true;
   228: //                  PlaySoundFX(SFX_TASKBAR_MINIMIZE);
   229:                     break;
   230:                 }
   231:             }
   232:             else
   233:             {
   234:                 ShowWindow(pPopupDialog->hPopupDialogWnd, SW_HIDE);
   235:                 pPopupDialog->PopupDialogHidden = true;
   236:                 PlaySoundFX(SFX_TASKBAR_MINIMIZE);
   237:             }
   238:         }
   239:         break;
   240: 
   241:         //====================
   242: 
   243:         case WM_KEYDOWN:
   244:         {
   245:             if (pPopupDialog->PopupTypeYesNo)
   246:             {
   247:                 if (wParam == VK_RETURN) // Return key -> Equivalent to clicking the "Yes" button...
   248:                 {
   249:                     ShowWindow(pPopupDialog->hPopupDialogWnd, SW_HIDE);
   250:                     pPopupDialog->PopupDialogHidden = true;
   251: //                  PlaySoundFX(SFX_TASKBAR_MINIMIZE);
   252:                     BBSmartExecute(pPopupDialog->message.body);
   253:                 }
   254:                 else if ((wParam == VK_ESCAPE) || (wParam == VK_DELETE))  // Escape of Delete key -> Equivalent to clicking the "No" button...
   255:                 {
   256:                     ShowWindow(pPopupDialog->hPopupDialogWnd, SW_HIDE);
   257:                     pPopupDialog->PopupDialogHidden = true;
   258: //                  PlaySoundFX(SFX_TASKBAR_MINIMIZE);
   259:                 }
   260:             }
   261: 
   262:             return 0;
   263:         }
   264:         break;
   265: 
   266:         //====================
   267: 
   268:         default:
   269:             return DefWindowProc(hwnd, message, wParam, lParam);
   270: 
   271:         //====================
   272:     }
   273:     return 0;
   274: }
   275: 
   276: //===========================================================================
   277: // Function: UpdatePopupDialog
   278: // Purpose: ...
   279: //===========================================================================
   280: 
   281: void PopupDialog::UpdatePopupDialog()
   282: {
   283:     if (PopupDialogHidden) return;
   284: 
   285:     // Fetch the new size and position parameters for our window...
   286:     GetDimensions();
   287: 
   288:     RECT r;
   289:     SetRect(&r, 0, 0, PopupDialogWidth, PopupDialogHeight);
   290: 
   291:     HDC hdc = GetWindowDC(hPopupDialogWnd);
   292:     HBITMAP bufbmp = CreateCompatibleBitmap(hdc, PopupDialogWidth, PopupDialogHeight);
   293:     DeleteObject(SelectObject(cachedBackground, bufbmp));
   294:     ReleaseDC(hPopupDialogWnd, hdc);
   295: 
   296:     //====================
   297: 
   298:     // Draw popup dialog window background... (title + body as applicable)
   299:     if (PopupTypeYesNo)
   300:     {
   301:         SetRect(&r, 0, 0, PopupDialogWidth, PopupDialogHeight);
   302:         HBRUSH brush = CreateSolidBrush(0x000000);
   303:         FillRect(cachedBackground, &r, brush);
   304:         DeleteObject(brush);
   305:     }
   306:     else
   307:     {
   308:         SetRect(&r, 0, 0, PopupDialogWidth, PopupDialogHeight);
   309:         HBRUSH brush = CreateSolidBrush(0xffffff);
   310:         FillRect(cachedBackground, &r, brush);
   311:         DeleteObject(brush);
   312: 
   313:         SetRect(&r, 0, 0, PopupDialogWidth, PopupDialogTitleHeight);
   314:         brush = CreateSolidBrush(0x000000);
   315:         FillRect(cachedBackground, &r, brush);
   316:         DeleteObject(brush);
   317:     }
   318: 
   319:     //====================
   320: 
   321:     // Create the popup dialog window font... (nb. always using "Segoe UI" for consistency)
   322:     HFONT font = CreateFont(dynamicFontHeight*pSettings->scalingFactorHiDPI, 0, 0, 0, FW_NORMAL, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Segoe UI");
   323:     HGDIOBJ oldfont = SelectObject(cachedBackground, font);
   324:     SetBkMode(cachedBackground, TRANSPARENT);
   325: 
   326:     if (PopupTypeYesNo) // -> Popup dialog window with a question + yes/no buttons; executing a specified bro@m upon answering yes, abort on answering no...
   327:     {
   328:         // Draw message...
   329: //      DrawTextWithEffects(cachedBackground, popupTitleTextRect, message.title, DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_WORD_ELLIPSIS, 0xffffff, false, 0, false, 0, 0, 0);
   330:         DrawTextWithEffects(cachedBackground, popupTitleTextRect, message.title, DT_CENTER | DT_TOP | DT_NOPREFIX | DT_WORD_ELLIPSIS, 0xffffff, false, 0, false, 0, 0, 0);
   331: 
   332:         // Draw yes/no butttons...
   333:         int buttonBorderWidth = 2;
   334:         if (pSettings->scalingFactorHiDPI > 1) buttonBorderWidth = 5 * (pSettings->scalingFactorHiDPI - 1);
   335:         MakeGradient(cachedBackground, popupYesRect, B_SOLID, 0x000000, 0, false, 0, 0, 0, 0x008800, buttonBorderWidth);
   336:         DrawTextWithEffects(cachedBackground, popupYesRect, "Yes", DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE, 0xffffff, false, 0, false, 0, 0, 0);
   337:         MakeGradient(cachedBackground, popupNoRect, B_SOLID, 0x000000, 0, false, 0, 0, 0, 0x000088, buttonBorderWidth);
   338:         DrawTextWithEffects(cachedBackground, popupNoRect, "No", DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE, 0xffffff, false, 0, false, 0, 0, 0);
   339: 
   340:     }
   341:     else // -> Popup dialog window showing information in a regular black text on white background message body... (e.g. About xoblite, About the current style, etc)
   342:     {
   343:         // Draw message title...
   344:         DrawTextWithEffects(cachedBackground, popupTitleTextRect, message.title, DT_LEFT | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_WORD_ELLIPSIS, 0xffffff, false, 0, false, 0, 0, 0);
   345:         // Draw message body...
   346:         DrawTextWithEffects(cachedBackground, popupBodyTextRect, message.body, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_WORD_ELLIPSIS, 0x000000, false, 0, false, 0, 0, 0);
   347:     }
   348: 
   349:     DeleteObject(SelectObject(cachedBackground, oldfont));
   350: 
   351:     //====================
   352: 
   353:     // Are we showing the "About xoblite..." popup dialog window?
   354:     if (IsInString(message.title, "About xoblite..."))
   355:     {
   356:         // Draw xoblite icon rightmost on the window title bar...
   357:         HICON popupIcon;
   358:         RECT iconRect;
   359:         int titleVerticalCenter = PopupDialogTitleHeight / 2;
   360: 
   361:         iconRect.left = PopupDialogWidth - popupIconSize - (5*pSettings->scalingFactorHiDPI);
   362:         iconRect.top = titleVerticalCenter - (popupIconSize / 2);
   363:         iconRect.bottom = iconRect.top + popupIconSize;
   364:         iconRect.right = iconRect.left + popupIconSize;
   365: 
   366:         popupIcon = LoadIcon(hPopupDialogInstance, MAKEINTRESOURCE(IDI_XOBLITE));
   367:         DrawSatHueIcon(cachedBackground, iconRect, popupIcon, popupIconSize, 255, 0);
   368:         DeleteObject(popupIcon);
   369:     }
   370: 
   371:     //====================
   372: 
   373:     // Apply per pixel alpha, creating e.g. nice rounded corners... 8)
   374:     SetRect(&r, 0, 0, PopupDialogWidth, PopupDialogHeight);
   375:     AlphaRect(cachedBackground, r, 255); // 210?
   376:     AlphaCorner(cachedBackground, r, CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT, 0, 0, 0, 0, 0, 255); // 210?
   377:     SetRect(&r, 0, 0, PopupDialogWidth, PopupDialogTitleHeight);
   378:     AlphaRect(cachedBackground, r, 255);
   379:     AlphaCorner(cachedBackground, r, CORNER_TOPLEFT | CORNER_TOPRIGHT, 0, 0, 0, 0, 0, 255);
   380:     SetRect(&r, 0, 0, PopupDialogWidth, PopupDialogHeight);
   381:     AlphaApply(cachedBackground, r);
   382: 
   383:     //====================
   384: 
   385:     if (bufbmp) DeleteObject(bufbmp);
   386: 
   387:     POINT pt;
   388:     pt.x = PopupDialogX, pt.y = PopupDialogY;
   389:     POINT ptSrc;
   390:     ptSrc.x = 0, ptSrc.y = 0;
   391: 
   392:     BLENDFUNCTION bf;
   393:     bf.BlendOp = AC_SRC_OVER;
   394:     bf.BlendFlags = 0;
   395:     bf.AlphaFormat = AC_SRC_ALPHA;
   396:     bf.SourceConstantAlpha = (unsigned char)255;
   397: 
   398:     SIZE windowSize = { PopupDialogWidth, PopupDialogHeight };
   399:     BOOL result = UpdateLayeredWindow(hPopupDialogWnd, NULL, &pt, &windowSize, cachedBackground, &ptSrc, 0, &bf, ULW_ALPHA);
   400: /*
   401:     if (!cachedBackground) SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_INFORMATION_MESSAGE, (LPARAM)"xoblite -> PopupDialog -> Invalid cachedBackground!");
   402: 
   403:     if (result == 0)
   404:     {
   405:         int error = GetLastError();
   406:         char msg[255];
   407:         sprintf_s(msg, sizeofArray(msg), "xoblite -> PopupDialog -> UpdateLayeredWindow failed! [error code %d]", error);
   408:         SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_ERROR_MESSAGE, (LPARAM)msg);
   409:     }
   410: */
   411: }
   412: 
   413: //===========================================================================
   414: // Function: GetDimensions
   415: // Purpose: ...
   416: //===========================================================================
   417: 
   418: void PopupDialog::GetDimensions()
   419: {
   420:     RECT workArea;
   421:     SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&workArea, SPIF_SENDCHANGE);
   422:     ScreenWidth = workArea.right - workArea.left;
   423:     ScreenHeight = workArea.bottom - workArea.top;
   424: 
   425:     //====================
   426: 
   427:     // Count the number of row of text in the message body...
   428:     int numberOfRowsOfText = 1;
   429:     if (PopupTypeYesNo)
   430:     {
   431:         for (unsigned int n=0; n < strnlen_s(message.title, sizeofArray(message.title)); n++)
   432:         {
   433:             if (message.title[n] == '\n') numberOfRowsOfText++;
   434:         }
   435:     }
   436:     else
   437:     {
   438:         for (unsigned int n=0; n< strnlen_s(message.body, sizeofArray(message.body)); n++)
   439:         {
   440:             if (message.body[n] == '\n') numberOfRowsOfText++;
   441:         }
   442:     }
   443: 
   444: //  char msg[255];
   445: //  sprintf_s(msg, sizeofArray(msg), "PopupDialog -> DEBUG -> numberOfRowsOfText: %d", numberOfRowsOfText);
   446: //  SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_INFORMATION_MESSAGE, (LPARAM)msg);
   447: 
   448:     //====================
   449: 
   450:     dynamicFontHeight = 15; // Default font size (before being multiplied by HiDPI scaling factor)
   451:     if (!PopupTypeYesNo)
   452:     {
   453:         // Adjust the font size dynamically based on a rough approximation (read: not an exact science here) of the resulting window size...
   454:         while (((1+numberOfRowsOfText) * (dynamicFontHeight*pSettings->scalingFactorHiDPI)) > ((ScreenHeight*80)/100)) dynamicFontHeight -= 1;
   455:         if (dynamicFontHeight < 5) dynamicFontHeight = 5; // Safeguard :)
   456:     }
   457: 
   458:     //====================
   459: 
   460:     // Calculate the window title height...
   461:     SIZE size;
   462:     HDC fonthdc = CreateDC("DISPLAY", NULL, NULL, NULL);
   463:     HFONT tempFont = CreateFont(dynamicFontHeight*pSettings->scalingFactorHiDPI, 0, 0, 0, FW_NORMAL, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Segoe UI");
   464:     HGDIOBJ oldFont = SelectObject(fonthdc, tempFont);
   465: 
   466:     GetTextExtentPoint32(fonthdc, "TQkgfp/", 8, &size);
   467:     PopupDialogTitleHeight = size.cy + (5*pSettings->scalingFactorHiDPI);
   468: 
   469:     // Choose the window title icon size depending on the height of the window title...
   470: //  if (PopupDialogTitleHeight >= 36) popupIconSize = 32;
   471: //  else popupIconSize = 16;
   472: 
   473:     // Set the window title icon size depending on the height of the window title...
   474:     popupIconSize = size.cy;
   475: 
   476:     //====================
   477: 
   478:     // Calculate window height...
   479:     if (PopupTypeYesNo)
   480:     {
   481:         PopupDialogYesNoHeight = (PopupDialogTitleHeight * 2);
   482: //      PopupDialogHeight = (size.cy * numberOfRowsOfText) + PopupDialogYesNoHeight + 30; // 30 pixels -> 10 pixels of padding at the top, bottom and in between message and yes/no buttons
   483:         PopupDialogHeight = (size.cy * numberOfRowsOfText) + PopupDialogYesNoHeight + (15*pSettings->scalingFactorHiDPI); // -> Extra pixels of padding at the top, bottom and in between message and yes/no buttons
   484:     }
   485: //  else PopupDialogHeight = (size.cy * (numberOfRowsOfText+1)) + 25; // Message title 1 row + body N rows + misc padding
   486:     else PopupDialogHeight = (size.cy * (1+numberOfRowsOfText)) + (15*pSettings->scalingFactorHiDPI); // Message title 1 row + Body N rows + Padding around them
   487: 
   488:     //====================
   489: 
   490:     // Calculate window width...
   491:     char* ptr = NULL;
   492:     char* ptr2 = NULL;
   493:     int maxWidth;
   494: 
   495:     if (PopupTypeYesNo)
   496:     {
   497:         ptr = message.title;
   498:         maxWidth = 0;
   499:     }
   500:     else
   501:     {
   502:         ptr = message.body;
   503:         GetTextExtentPoint32(fonthdc, message.title, strnlen_s(message.title, sizeofArray(message.title)), &size);
   504:         maxWidth = size.cx;
   505:     }
   506: 
   507:     char oneRowOfText[MAX_LINE_LENGTH];
   508:     while (numberOfRowsOfText > 0)
   509:     {
   510:         if (ptr[0] == '\0') break;
   511:         else if (ptr[0] != '\n')
   512:         {
   513:             ptr2 = strchr(ptr, '\n');
   514:             if (ptr2 == NULL)
   515:             {
   516:                 strcpy_s(oneRowOfText, sizeofArray(oneRowOfText), ptr);
   517: //              SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_INFORMATION_MESSAGE, (LPARAM)oneRowOfText);
   518:                 GetTextExtentPoint32(fonthdc, oneRowOfText, strnlen_s(oneRowOfText, sizeofArray(oneRowOfText)), &size);
   519:                 if (size.cx > maxWidth) maxWidth = size.cx;
   520:                 break;
   521:             }
   522:             else
   523:             {
   524: //              strncpy(oneRowOfText, ptr, ptr2 - ptr);
   525:                 strncpy_s(oneRowOfText, sizeofArray(oneRowOfText), ptr, ptr2-ptr);
   526:                 oneRowOfText[ptr2-ptr] = '\0';
   527: //              SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_INFORMATION_MESSAGE, (LPARAM)oneRowOfText);
   528:                 GetTextExtentPoint32(fonthdc, oneRowOfText, strnlen_s(oneRowOfText, sizeofArray(oneRowOfText)), &size);
   529:                 if (size.cx > maxWidth) maxWidth = size.cx;
   530:                 numberOfRowsOfText--;
   531:                 ptr = ptr2++;
   532:             }
   533:         }
   534:         else ptr++;
   535:     }
   536: 
   537: //  PopupDialogWidth = maxWidth + 30 + popupIconSize; // Max width (of title or body) + 10 pixels padding on each side + 16 or 32 pixels wide icon + 10 pixels padding to the right of the icon
   538:     PopupDialogWidth = maxWidth + (15*pSettings->scalingFactorHiDPI) + popupIconSize; // Padding + Max width of title or body text + Padding + 16 or 32 pixels wide icon + Padding
   539:     if ((pSettings->scalingFactorHiDPI > 1) && (PopupDialogWidth < (200* pSettings->scalingFactorHiDPI))) PopupDialogWidth = (200 * pSettings->scalingFactorHiDPI); // Simple failsafe
   540:     else if (PopupDialogWidth < 300) PopupDialogWidth = 300; // Simple failsafe
   541: 
   542:     //====================
   543: 
   544:     // Update the popup dialog window RECTs for later use...
   545:     int padding = 5 * pSettings->scalingFactorHiDPI;
   546:     if (PopupTypeYesNo)
   547:     {
   548:         int xcenter = PopupDialogWidth / 2;
   549: //      SetRect(&popupYesRect, 10, PopupDialogHeight - PopupDialogYesNoHeight - 10, xcenter - 5, PopupDialogHeight - 10);
   550: //      SetRect(&popupNoRect, xcenter + 5, PopupDialogHeight - PopupDialogYesNoHeight - 10, PopupDialogWidth - 10, PopupDialogHeight - 10);
   551: //      SetRect(&popupTitleTextRect, 10, 10, PopupDialogWidth - 10, popupYesRect.top - 10);
   552:         SetRect(&popupYesRect, padding, PopupDialogHeight - PopupDialogYesNoHeight - padding, xcenter - (padding/2), PopupDialogHeight - padding);
   553:         SetRect(&popupNoRect, xcenter + (padding/2), PopupDialogHeight - PopupDialogYesNoHeight - padding, PopupDialogWidth - padding, PopupDialogHeight - padding);
   554:         SetRect(&popupTitleTextRect, padding, padding, PopupDialogWidth - padding, popupYesRect.top - padding);
   555:     }
   556:     else
   557:     {
   558: //      SetRect(&popupTitleTextRect, 10, 0, PopupDialogWidth - 10, PopupDialogTitleHeight);
   559: //      SetRect(&popupBodyTextRect, 10, PopupDialogTitleHeight + 5, PopupDialogWidth - 10, PopupDialogHeight);
   560:         SetRect(&popupTitleTextRect, padding, 0, PopupDialogWidth-padding, PopupDialogTitleHeight);
   561:         SetRect(&popupBodyTextRect, padding, PopupDialogTitleHeight+(padding/2), PopupDialogWidth-padding, PopupDialogHeight);
   562:     }
   563: 
   564:     DeleteObject(SelectObject(fonthdc, oldFont));
   565:     DeleteDC(fonthdc);
   566: 
   567:     //====================
   568: 
   569:     PopupDialogX = workArea.left + (ScreenWidth / 2) - (PopupDialogWidth / 2);
   570:     PopupDialogY = workArea.top + (ScreenHeight / 2) - (PopupDialogHeight / 2);
   571: }
   572: 
   573: //===========================================================================
   574: