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 "Dock.h" 
    32: 
    33: // const char szDockName[] = "BBDock";
    34: const char szDockName[] = "BBSlit"; // Window class etc. (nb. this [likely?] needs to remain "BBSlit" for backwards compatibility reasons)
    35: 
    36: extern Dock *pDock;
    37: extern BImage *pBImage;
    38: extern Settings *pSettings;
    39: extern MenuCommon *pMenuCommon;
    40: extern PluginManager *pPluginManager;
    41: extern Taskbar *pTaskbar;
    42: 
    43: int dockMessageSubscription[] = { BB_TOGGLEDOCK, BB_RECONFIGURE, 0 };
    44: 
    45: //===========================================================================
    46: 
    47: Dock::Dock(HINSTANCE hInstance)
    48: {
    49:     hDockWnd = NULL;
    50:     hBlackboxWnd = GetBBWnd();
    51:     hDockInstance = hInstance;
    52: 
    53:     cachedBackground = CreateCompatibleDC(NULL);
    54:     cachedBackgroundExists = false;
    55: 
    56:     reconfigureLock = positioningInProgress = false;
    57:     DockWidth = DockHeight = DockX = DockY = 10;
    58: 
    59:     fullscreenDetected = false;
    60: 
    61:     //====================
    62: 
    63:     // Register window class...
    64:     WNDCLASS wc;
    65:     ZeroMemory(&wc,sizeof(wc));
    66:     wc.hInstance = hDockInstance;       
    67:     wc.lpfnWndProc = DockWndProc;               // window procedure
    68:     wc.lpszClassName = szDockName;              // window class name
    69:     wc.hbrBackground = NULL;                    // no class background brush
    70:     wc.style = CS_DBLCLKS;                      // class styles (e.g. to accept doubleclicks)
    71:     wc.hCursor = LoadCursor(NULL, IDC_ARROW);   // always display the regular arrow cursor
    72: 
    73:     if (!RegisterClass(&wc))
    74:     {
    75:         MessageBox(0, "Error registering dock window class!", szDockName, MB_OK | MB_ICONERROR | MB_TOPMOST);
    76:         Log("Dock", "Error registering window class!");
    77:         return;
    78:     }
    79: 
    80:     // Create dock window...
    81:     hDockWnd = CreateWindowEx(
    82:         WS_EX_TOOLWINDOW | WS_EX_ACCEPTFILES,           // window style
    83:         szDockName,                                     // window class
    84:         NULL,                                           // window name
    85:         WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,   // window parameters
    86:         DockX,                                          // x position
    87:         DockY,                                          // y position
    88:         0,                                              // window width
    89:         0,                                              // window height
    90:         hBlackboxWnd,                                   // owner window
    91:         NULL,                                           // no menu
    92:         hDockInstance,                                  // hInstance
    93:         NULL                                            // no window creation data
    94:     );
    95: 
    96:     if (!hDockWnd)
    97:     {                          
    98:         UnregisterClass(szDockName, hDockInstance); // unregister window class
    99:         MessageBox(0, "Error creating dock window!", szDockName, MB_OK | MB_ICONERROR | MB_TOPMOST);
   100:         Log("Dock", "Error creating window!");
   101:         return;
   102:     }
   103: 
   104:     //====================
   105: 
   106:     // Subscribe to Blackbox messages applicable to the dock...
   107:     SendMessage(hBlackboxWnd, BB_REGISTERMESSAGE, (WPARAM)hDockWnd, (LPARAM)dockMessageSubscription);
   108: 
   109:     // Make the dock window sticky...
   110:     MakeSticky(hDockWnd);
   111: 
   112:     // After a short locking interval to allow plugins to dock first,
   113:     // we show+update or hide the dock window as applicable...
   114:     // (nb. here this includes setting its z-order, window transparency, etc)
   115:     ReconfigureLock(true);
   116: }
   117: 
   118: //===========================================================================
   119: 
   120: Dock::~Dock()
   121: {
   122:     KillTimer(hDockWnd, DOCK_RECONFIGURE_LOCK_TIMER);
   123: 
   124:     // Unsubscribe to previously subscribed Blackbox messages...
   125:     SendMessage(hBlackboxWnd, BB_UNREGISTERMESSAGE, (WPARAM)hDockWnd, (LPARAM)dockMessageSubscription);
   126: 
   127:     DestroyWindow(hDockWnd); // Destroy window...
   128:     UnregisterClass(szDockName, hDockInstance); // Unregister window class...
   129: 
   130:     DeleteDC(cachedBackground); // Delete cached bitmaps...
   131: }
   132: 
   133: //===========================================================================
   134: 
   135: LRESULT CALLBACK DockWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
   136: {
   137:     switch (message)
   138:     {
   139:         //====================
   140: 
   141:         case WM_PAINT:
   142:         {
   143:             PAINTSTRUCT ps;
   144:             HDC hdc = BeginPaint(pDock->hDockWnd, &ps);
   145: 
   146:             // If we have not yet created a cached background, let's do that...
   147:             if (!pDock->cachedBackgroundExists)
   148:             {
   149: 
   150:                 HBITMAP tempBitmap = CreateCompatibleBitmap(hdc, pDock->DockWidth, pDock->DockHeight);
   151:                 DeleteObject(SelectObject(pDock->cachedBackground, tempBitmap));
   152: 
   153:                 // Draw dock background... (nb. as per the current style+settings if the dock is not empty, highly transparent black with white --- tag if the dock is empty)
   154:                 RECT r;
   155:                 GetClientRect(hwnd, &r);
   156:                 if (pDock->DockPlugins.size() > 0)
   157:                 {
   158:                     if (pSettings->Dock->type == B_IMAGEFROMFILE) DrawImageIntoRect(pDock->cachedBackground, r, pSettings->Dock, false, true);
   159:                     else MakeGradientSuper(pDock->cachedBackground, r, pSettings->Dock->type, pSettings->Dock->Color1, pSettings->Dock->Color2, pSettings->Dock->Color3, pSettings->Dock->Color4, pSettings->Dock->Color5, pSettings->Dock->Color6, pSettings->Dock->Color7, pSettings->Dock->Color8, pSettings->Dock->interlaced, pSettings->Dock->bevelstyle, pSettings->Dock->bevelposition, pSettings->bevelWidth, pSettings->Dock->borderColor, pSettings->Dock->borderWidth);
   160: 
   161:                     if (!pSettings->DockBorder->parentRelative && (pSettings->Dock->borderWidth > 0))
   162:                     {
   163:                         CreateBorderSuper(pDock->cachedBackground, r, pSettings->DockBorder->type, pSettings->DockBorder->Color1, pSettings->DockBorder->Color2, pSettings->DockBorder->Color3, pSettings->DockBorder->Color4, pSettings->DockBorder->Color5, pSettings->DockBorder->Color6, pSettings->DockBorder->Color7, pSettings->DockBorder->Color8, pSettings->Dock->borderWidth);
   164:                     }
   165:                 }
   166:                 else
   167:                 {
   168:                     MakeGradientSuper(pDock->cachedBackground, r, B_SOLID, 0x000000, 0, 0, 0, 0, 0, 0, 0, false, BEVEL_FLAT, 0, 0, 0, 0);
   169:                     HFONT font = CreateFont(pSettings->Dock->FontHeight, 0, 0, 0, pSettings->Dock->FontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, pSettings->Dock->Font);
   170:                     HGDIOBJ oldfont = SelectObject(pDock->cachedBackground, font);
   171:                     SetBkMode(pDock->cachedBackground, TRANSPARENT);
   172:                     DrawTextWithEffects(pDock->cachedBackground, r, "---", DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_WORD_ELLIPSIS, 0xffffff, false, 0, false, 0, 0, 0);
   173:                     DeleteObject(SelectObject(pDock->cachedBackground, oldfont));
   174:                 }
   175: 
   176:                 DeleteObject(tempBitmap);
   177: 
   178:                 // Set the "cached background created" indicator...
   179:                 pDock->cachedBackgroundExists = true;
   180:             }
   181: 
   182:             // Copy the cached background into the dock window HDC...
   183:             BitBlt(hdc, ps.rcPaint.left, ps.rcPaint.top, (ps.rcPaint.right - ps.rcPaint.left), (ps.rcPaint.bottom - ps.rcPaint.top), pDock->cachedBackground, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
   184: 
   185:             EndPaint(pDock->hDockWnd, &ps);
   186:         }
   187:         break;
   188: 
   189:         //====================
   190: 
   191:         case WM_CLOSE:
   192:             return 0;
   193: 
   194:         //====================
   195: 
   196:         case BB_RECONFIGURE:
   197:         {
   198:             // NOTE: When only need to do this if the dock is *empty*, since any docked plugins
   199:             // will send SLIT_UPDATE which will in turn invalidate the parent dock window...
   200:             if (pDock->DockPlugins.size() == 0) pDock->UpdateDockWindow();
   201:         }
   202:         break;
   203: 
   204:         //====================
   205: 
   206:         case WM_DISPLAYCHANGE:
   207:         {
   208:             // Update the dock's position if the screen resolution changes...
   209:             pDock->UpdatePosition();
   210:         }
   211:         break;
   212: 
   213:         case WM_SETTINGCHANGE:
   214:         {
   215:             // Update the dock's position if the work area changes...
   216:             // (e.g. if the Explorer taskbar is moved to another screen edge)
   217:             if (wParam == SPI_SETWORKAREA) pDock->UpdatePosition();
   218:             return 0;
   219:         }
   220: 
   221:         //====================
   222: 
   223:         case DOCK_ADD: // -> A plugin is being docked... (added to the dock)
   224:         {
   225:             if (IsWindow((HWND)lParam))
   226:             {
   227:                 dockPluginItem plugin;
   228:                 plugin.hwndPlugin = (HWND)lParam;
   229:                 pDock->DockPlugins.push_back(plugin);
   230: 
   231:                 SetWindowLongPtr((HWND)lParam, GWL_STYLE, (GetWindowLongPtr((HWND)lParam, GWL_STYLE) & ~WS_POPUP) | WS_CHILD);
   232:                 SetParent((HWND)lParam, pDock->hDockWnd);
   233:             }
   234: 
   235:             pDock->UpdateDockWindow();
   236:         }
   237:         break;
   238: 
   239:         //====================
   240: 
   241:         case DOCK_REMOVE: // -> A plugin is being undocked... (removed from the dock)
   242:         {
   243:             vector<dockPluginItem>::iterator dockPlugin;
   244: 
   245:             for (dockPlugin = pDock->DockPlugins.begin(); dockPlugin != pDock->DockPlugins.end(); dockPlugin++)
   246:             {
   247:                 if (dockPlugin->hwndPlugin == (HWND)lParam)
   248:                 {
   249:                     if (IsWindow((HWND)lParam))
   250:                     {
   251:                         SetWindowLongPtr((HWND)lParam, GWL_STYLE, (GetWindowLongPtr((HWND)lParam, GWL_STYLE) & ~WS_CHILD) | WS_POPUP);
   252:                         SetParent((HWND)lParam, NULL);
   253:                     }
   254: 
   255:                     pDock->DockPlugins.erase(dockPlugin);
   256: 
   257:                     break;
   258:                 }
   259:             }
   260: 
   261:             pDock->UpdateDockWindow();
   262:         }
   263:         break;
   264: 
   265:         //====================
   266: 
   267:         case DOCK_UPDATE: // -> Update the dock window...
   268:         {
   269:             pDock->UpdateDockWindow();
   270:         }
   271:         break;
   272: 
   273:         //====================
   274: 
   275:         case BB_TOGGLEDOCK: // -> Toggle the dock shown/hidden...
   276:         {
   277:             if (!pSettings->dockHidden) pSettings->dockHidden = true;
   278:             else pSettings->dockHidden = false;
   279:             pDock->UpdateDockWindow();
   280:             PlaySoundFX(SFX_TOGGLE_ELEMENT);
   281:             WriteBool(pSettings->xobrcFile, "xoblite.dock.hidden:", pSettings->dockHidden);
   282:         }
   283:         break;
   284: 
   285:         //====================
   286: 
   287:         case WM_NCHITTEST:
   288:         {
   289:             POINT p;
   290:             GetCursorPos(&p);
   291:             RECT r;
   292:             GetWindowRect(pDock->hDockWnd, &r);
   293:             pDock->DockX = r.left;
   294:             pDock->DockY = r.top;
   295: 
   296:             p.x = p.x - pDock->DockX;
   297:             p.y = p.y - pDock->DockY;
   298: 
   299:             // Allow mouse drag manual positioning if we're not hovering over
   300:             // a child (plugin) window, and the control key is being held down...
   301:             if (ChildWindowFromPoint(pDock->hDockWnd, p) == pDock->hDockWnd)
   302:             {
   303:                 if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
   304:                 {
   305:                     pDock->positioningInProgress = true;
   306:                     return HTCAPTION;
   307:                 }
   308:             }
   309: 
   310:             return HTCLIENT;
   311:         }
   312: 
   313:         //====================
   314: 
   315:         case WM_LBUTTONUP:
   316:         {
   317:             if (pMenuCommon) pMenuCommon->Hide();
   318:             return 0;
   319:         }
   320: 
   321:         case WM_LBUTTONDOWN: {} break;
   322: 
   323:         //====================
   324: 
   325:         case WM_RBUTTONUP:
   326:         case WM_NCRBUTTONUP:
   327:         {
   328:             if (GetAsyncKeyState(VK_CONTROL) & 0x8000) SendMessage(GetBBWnd(), BB_MENU, 0, 0); // Ctrl+RightClick -> Main menu
   329:             else if (GetAsyncKeyState(VK_MENU) & 0x8000) SendMessage(GetBBWnd(), BB_MENU, 0, 0); // Alt+RightClick -> Main menu
   330:             else SendMessage(GetBBWnd(), BB_MENU, 2, 0); // RightClick without modifiers -> Configuration menu
   331:             return 0;
   332:         }
   333: 
   334:         case WM_RBUTTONDOWN:
   335:         case WM_NCRBUTTONDOWN: {} break;
   336: 
   337:         //====================
   338: 
   339:         case WM_MBUTTONUP:
   340:         case WM_NCMBUTTONUP:
   341:         {
   342:             if (GetAsyncKeyState(VK_CONTROL) & 0x8000) PostMessage(GetBBWnd(), BB_TOGGLEPLUGINS, 0, 0); // Ctrl+MidClick -> Toggle plugins
   343:             else if (GetAsyncKeyState(VK_MENU) & 0x8000) PostMessage(GetBBWnd(), BB_TOGGLEPLUGINS, 0, 0); // Alt+MidClick -> Toggle plugins
   344:             else PostMessage(GetBBWnd(), BB_TOGGLETOOLBAR, 0, 0); // MidClick without modifiers -> Toggle toolbar
   345:         }
   346:         break;
   347: 
   348:         case WM_MBUTTONDOWN:
   349:         case WM_NCMBUTTONDOWN: {} break;
   350: 
   351:         //====================
   352: 
   353:         case WM_XBUTTONDOWN:
   354:         {
   355:             if (HIWORD(wParam) == XBUTTON1) // X1 Click without modifiers -> Toggle dock orientation (horizontal/vertical)
   356:             {
   357:                 if (pSettings->dockVertical) pSettings->dockVertical = false;
   358:                 else pSettings->dockVertical = true;
   359:                 pDock->UpdateDockWindow();
   360:                 WriteBool(pSettings->xobrcFile, "xoblite.dock.vertical:", pSettings->dockVertical);
   361:             }
   362:         }
   363:         break;
   364: 
   365:         //====================
   366: 
   367:         case WM_MOUSEWHEEL:
   368:         case WM_MOUSEHWHEEL:
   369:         {
   370:             // Forward any mousewheel messages to the
   371:             // child plugin window beneath the mouse cursor...
   372:             POINT p;
   373:             GetCursorPos(&p);
   374:             p.x = p.x - pDock->DockX;
   375:             p.y = p.y - pDock->DockY;
   376:             HWND pluginWnd = ChildWindowFromPoint(pDock->hDockWnd, p);
   377:             if ((pluginWnd != NULL) && (pluginWnd != pDock->hDockWnd)) PostMessage(pluginWnd, message, (WPARAM)wParam, (LPARAM)lParam);
   378:         }
   379:         break;
   380: 
   381:         //====================
   382: 
   383:         case WM_DROPFILES:
   384:         {
   385:             return ParseDropFiles(hwnd, wParam); // -> Common handling of drag'n'drop events for all core UI elements
   386:         }
   387:         break;
   388: 
   389:         //====================
   390: 
   391:         case WM_WINDOWPOSCHANGING:
   392:         {
   393:             if (pDock->positioningInProgress)
   394:             {
   395:                 // Snap window to screen edges when in manual positioning mode?
   396:                 if ((pSettings->DockPlacement.placement == PLACEMENT_MANUAL) && pSettings->dockSnapToEdges)
   397:                 {
   398:                     if (IsWindowVisible(hwnd)) SnapWindowToEdge((WINDOWPOS*)lParam, pSettings->edgeSnapThreshold, true);
   399:                 }
   400:             }
   401:             return 0;
   402:         }
   403: 
   404:         //====================
   405: 
   406:         case WM_EXITSIZEMOVE:
   407:         {
   408:             if (pDock->positioningInProgress)
   409:             {
   410:                 pDock->positioningInProgress = false;
   411: 
   412:                 RECT r;
   413:                 GetWindowRect(pDock->hDockWnd, &r);
   414:                 pSettings->DockPlacement.placement = PLACEMENT_MANUAL;
   415:                 pSettings->DockPlacement.rcx = pDock->DockX = r.left;
   416:                 pSettings->DockPlacement.rcy = pDock->DockY = r.top;
   417: 
   418:                 pSettings->WritePlacement(&pSettings->DockPlacement);
   419:             }
   420: 
   421:             return 0;
   422:         }
   423: 
   424:         //====================
   425: 
   426:         case WM_TIMER:
   427:         {
   428:             if (wParam == DOCK_RECONFIGURE_LOCK_TIMER)
   429:             {
   430:                 pDock->ReconfigureLock(false);
   431:                 return 0;
   432:             }
   433:         }
   434:         break;
   435: 
   436:         //====================
   437: 
   438:         default:
   439:             return DefWindowProc(hwnd,message,wParam,lParam);
   440: 
   441:     //====================
   442:     }
   443:     return 0;
   444: }
   445: 
   446: //===========================================================================
   447: 
   448: void Dock::UpdateDockWindow()
   449: {
   450:     if (fullscreenDetected) return;
   451: 
   452:     if (reconfigureLock) return; // Is the reconfigure lock enabled?
   453: 
   454:     //====================
   455: 
   456:     if (pSettings->dockHidden) // The dock is currently hidden, so no need to update its window...
   457:     {
   458:         DockWidth = DockHeight = 10; // Safeguard
   459:         ShowWindow(hDockWnd, SW_HIDE);
   460:     }
   461: 
   462:     //====================
   463: 
   464:     else // The dock is currently visible, so we need to update its window...
   465:     {
   466:         if (DockPlugins.size() > 0) // Any plugins currently docked?
   467:         {
   468:             RECT r = {0, 0, 0, 0};
   469:             int maxwidth = 0, maxheight = 0;
   470: 
   471:             //====================
   472: 
   473:             // Scan through the plugin list and calculate
   474:             // the maximum plugin width and height...
   475:             for (plugin = DockPlugins.begin(); plugin != DockPlugins.end(); plugin++)
   476:             {
   477:                 if (IsWindow(plugin->hwndPlugin)) // Check if the window is valid...
   478:                 {
   479:                     GetWindowRect(plugin->hwndPlugin, &r);
   480: 
   481:                     plugin->width = r.right - r.left;
   482:                     plugin->height = r.bottom - r.top;
   483: 
   484:                     if (plugin->width > maxwidth) maxwidth = plugin->width;
   485:                     if (plugin->height > maxheight) maxheight = plugin->height;
   486:                 }
   487:                 else // ...if not, we remove it from the list...
   488:                 {
   489:                     DockPlugins.erase(plugin);
   490:                     plugin--;
   491:                 }
   492:             }
   493: 
   494:             //====================
   495: 
   496:             int padding = pSettings->Dock->marginWidth;
   497:             if (padding < 2) padding = 2;
   498:             int xpad, ypad;
   499:             int offset = pSettings->Dock->borderWidth + padding + 1;
   500:             int tempX = offset;
   501:             int tempY = offset;
   502:             int totalGroupWidth = 0, totalGroupHeight = 0;
   503:             int maxwidthWithPadding = maxwidth + padding;
   504:             int maxheightWithPadding = maxheight + padding;
   505:             int maxwidthInGroup = 0, maxheightInGroup = 0;
   506: 
   507:             //====================
   508: 
   509:             // "Puzzle positioning" == Automatic positioning of plugins within the dock...
   510:             if (pSettings->dockPuzzlePositioning)
   511:             {
   512:                 firstInGroup = lastInGroup = DockPlugins.begin();
   513: 
   514:                 for (plugin = DockPlugins.begin(); plugin != DockPlugins.end(); plugin++)
   515:                 {
   516:                     if (pSettings->dockVertical) // Vertical dock alignment
   517:                     {
   518:                         totalGroupWidth = tempX + plugin->width + padding;
   519: 
   520:                         if (totalGroupWidth > maxwidthWithPadding)
   521:                         {
   522:                             if (firstInGroup->width != maxwidth)
   523:                             {
   524:                                 xpad = ((maxwidth - tempX) / 2) + pSettings->Dock->borderWidth + padding;
   525: 
   526:                                 for (member = firstInGroup; member <= lastInGroup; member++)
   527:                                 {
   528:                                     if (member->height == maxheightInGroup) ypad = 0;
   529:                                     else ypad = (maxheightInGroup - member->height) / 2;
   530: 
   531:                                     member->x += xpad;
   532:                                     member->y += ypad;
   533:                                 }
   534:                             }
   535: 
   536:                             tempX = offset;
   537:                             if (plugin != DockPlugins.begin()) tempY = tempY + maxheightInGroup + padding;
   538:                             totalGroupWidth = offset + plugin->width + padding;
   539:                             totalGroupHeight = offset + plugin->height + padding;
   540: 
   541:                             maxwidthInGroup = maxheightInGroup = 0;
   542:                             firstInGroup = plugin;
   543:                         }
   544:                     }
   545:                     else // Horizontal dock alignment
   546:                     {
   547:                         totalGroupHeight = tempY + plugin->height + padding;
   548: 
   549:                         if (totalGroupHeight > maxheightWithPadding)
   550:                         {
   551:                             if (firstInGroup->height != maxheight)
   552:                             {
   553:                                 ypad = ((maxheight - tempY) / 2) + pSettings->Dock->borderWidth + padding;
   554: 
   555:                                 for (member = firstInGroup; member <= lastInGroup; member++)
   556:                                 {
   557:                                     if (member->width == maxwidthInGroup) xpad = 0;
   558:                                     else xpad = (maxwidthInGroup - member->width) / 2;
   559: 
   560:                                     member->x += xpad;
   561:                                     member->y += ypad;
   562:                                 }
   563:                             }
   564: 
   565:                             if (plugin != DockPlugins.begin()) tempX = tempX + maxwidthInGroup + padding;
   566:                             tempY = offset;
   567:                             totalGroupWidth = offset + plugin->width + padding;
   568:                             totalGroupHeight = offset + plugin->height + padding;
   569: 
   570:                             maxwidthInGroup = maxheightInGroup = 0;
   571:                             firstInGroup = plugin;
   572:                         }
   573:                     }
   574: 
   575:                     plugin->x = tempX;
   576:                     plugin->y = tempY;
   577:                     lastInGroup = plugin;
   578:                     if (plugin->width > maxwidthInGroup) maxwidthInGroup = plugin->width;
   579:                     if (plugin->height > maxheightInGroup) maxheightInGroup = plugin->height;
   580: 
   581:                     if (pSettings->dockVertical) tempX = tempX + plugin->width + padding;
   582:                     else tempY = tempY + plugin->height + padding;
   583:                 }
   584: 
   585:                 // Finish alignment for the last group...
   586:                 if (pSettings->dockVertical)
   587:                 {
   588:                     xpad = (maxwidth - tempX) / 2 + pSettings->Dock->borderWidth + padding;
   589: 
   590:                     for (member = firstInGroup; member <= lastInGroup; member++)
   591:                     {
   592:                         if (member->height == maxheightInGroup) ypad = 0;
   593:                         else ypad = (maxheightInGroup - member->height) / 2;
   594: 
   595:                         if (member->width != maxwidth) member->x += xpad;
   596:                         member->y += ypad;
   597:                     }
   598:                 }
   599:                 else
   600:                 {
   601:                     ypad = (maxheight - tempY) / 2 + pSettings->Dock->borderWidth + padding;
   602: 
   603:                     for (member = firstInGroup; member <= lastInGroup; member++)
   604:                     {
   605:                         if (member->width == maxwidthInGroup) xpad = 0;
   606:                         else xpad = (maxwidthInGroup - member->width) / 2;
   607: 
   608:                         member->x += xpad;
   609:                         if (member->height != maxheight) member->y += ypad;
   610:                     }
   611:                 }
   612: 
   613:                 // Finally, we re-position the plugins...
   614:                 for (plugin = DockPlugins.begin(); plugin != DockPlugins.end(); plugin++)
   615:                 {
   616:                     SetWindowPos(plugin->hwndPlugin, NULL, plugin->x, plugin->y, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
   617:                 }
   618:             }
   619: 
   620:             //====================
   621:             
   622:             // Normal positioning == Plugins are placed in a single horizontal row / vertical column...
   623:             else
   624:             {
   625:                 for (plugin = DockPlugins.begin(); plugin != DockPlugins.end(); plugin++)
   626:                 {
   627:                     if (pSettings->dockVertical)
   628:                     {
   629:                         // Calculate necessary padding to get a centered x position...
   630:                         if (plugin->width == maxwidth) xpad = 0;
   631:                         else xpad = (maxwidth - plugin->width) / 2;
   632:                         SetWindowPos(plugin->hwndPlugin, NULL, (tempX + xpad), tempY, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
   633:                         tempY = tempY + plugin->height + padding;
   634:                     }
   635:                     else
   636:                     {
   637:                         // Calculate necessary padding to get a centered y position...
   638:                         if (plugin->height == maxheight) ypad = 0;
   639:                         else ypad = (maxheight - plugin->height) / 2;
   640:                         SetWindowPos(plugin->hwndPlugin, NULL, tempX, (tempY + ypad), 0, 0, SWP_NOZORDER | SWP_NOSIZE);
   641:                         tempX = tempX + plugin->width + padding;
   642:                     }
   643:                 }
   644:             }
   645: 
   646:             //====================
   647: 
   648:             // Finally, we resize the dimensions of the dock to fit all the plugins...
   649:             if (pSettings->dockVertical)
   650:             {
   651:                 DockWidth = maxwidth + (offset * 2);
   652:                 if (pSettings->dockPuzzlePositioning) DockHeight = tempY + maxheightInGroup + offset;
   653:                 else DockHeight = tempY - padding + offset;
   654:             }
   655:             else
   656:             {
   657:                 if (pSettings->dockPuzzlePositioning) DockWidth = tempX + maxwidthInGroup + offset;
   658:                 else DockWidth = tempX - padding + offset;
   659:                 DockHeight = maxheight + (offset * 2);
   660:             }
   661:         }
   662: 
   663:         //====================
   664: 
   665:         else // No plugins are currently docked, so we'll display a <Dock> tag instead; let's make room for it...
   666:         {
   667:             SIZE size;
   668:             HDC fonthdc = CreateDC("DISPLAY", NULL, NULL, NULL);
   669:             HFONT font = CreateFont(pSettings->Dock->FontHeight, 0, 0, 0, pSettings->Dock->FontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, pSettings->Dock->Font);
   670:             HGDIOBJ oldfont = SelectObject(fonthdc, font);
   671:             GetTextExtentPoint32(fonthdc, "---", strlen("---"), &size);
   672:             DockWidth = size.cx + pSettings->Dock->FontHeight + (pSettings->Dock->marginWidth * 2) + (pSettings->Dock->borderWidth * 2);
   673:             DockHeight = size.cy + (pSettings->Dock->marginWidth * 2) + (pSettings->Dock->borderWidth * 2);
   674:             DeleteObject(SelectObject(fonthdc, oldfont));
   675:             DeleteDC(fonthdc);
   676:         }
   677: 
   678:         //====================
   679: 
   680:         ShowWindow(hDockWnd, SW_SHOWNOACTIVATE);
   681:         UpdatePosition();
   682:     }
   683: }
   684: 
   685: //===========================================================================
   686: 
   687: void Dock::UpdatePosition()
   688: {
   689:     // Update the dock window position...
   690:     pSettings->DockPlacement.width = DockWidth;
   691:     pSettings->DockPlacement.height = DockHeight;
   692:     pSettings->PositionFromPlacement(&pSettings->DockPlacement);
   693:     DockX = pSettings->DockPlacement.x;
   694:     DockY = pSettings->DockPlacement.y;
   695: 
   696:     // Force re-rendering of the background bitmap...
   697:     cachedBackgroundExists = false;
   698: 
   699:     // Resize the dock window, move it to its new position, and request a WM_PAINT repaint...
   700:     // (nb. the dock window is *not* a DWM layered window like most other xoblite core windows)
   701:     MoveWindow(hDockWnd, DockX, DockY, DockWidth, DockHeight, false);
   702:     InvalidateRect(hDockWnd, NULL, false);
   703: 
   704:     // Set the dock window transparency... (nb. the dock window is *not* a DWM layered window and hence only supports window transparency, not per pixel alpha etc)
   705: //  SetTransparency(hDockWnd, pSettings->dockTransparencyAlpha);
   706:     if (DockPlugins.size() > 0) SetTransparency(hDockWnd, pSettings->dockTransparencyAlpha);
   707:     else SetTransparency(hDockWnd, (pSettings->dockTransparencyAlpha / 4));
   708: 
   709:     // Set the dock window z-order position... (regular or always on top)
   710:     if (pSettings->dockOnTop) SetWindowPos(hDockWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
   711:     else SetWindowPos(hDockWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
   712: 
   713:     // Finally, we ask all docked plugins to repaint their windows (i.e. the child windows of the dock window) too...
   714:     for (plugin = DockPlugins.begin(); plugin != DockPlugins.end(); plugin++)
   715:     {
   716:         if (IsWindow(plugin->hwndPlugin)) InvalidateRect(plugin->hwndPlugin, NULL, false);
   717:     }
   718: }
   719: 
   720: //===========================================================================
   721: 
   722: void Dock::ReconfigureLock(bool enable)
   723: {
   724:     if (enable)
   725:     {
   726:         if (!SetTimer(hDockWnd, DOCK_RECONFIGURE_LOCK_TIMER, 300, (TIMERPROC)NULL))
   727:         {
   728:             MessageBox(GetBBWnd(), "Error creating reconfigure lock timer!", szDockName, MB_OK | MB_ICONERROR | MB_TOPMOST);
   729:             Log("Dock", "Error creating reconfigure lock timer!");
   730:             return;
   731:         }
   732:         reconfigureLock = true;
   733:         ShowWindow(hDockWnd, SW_HIDE); // Hide the window to "hide" any display artifacts while updating all plugins...
   734:     }
   735:     else
   736:     {
   737:         KillTimer(pDock->hDockWnd, DOCK_RECONFIGURE_LOCK_TIMER);
   738:         pDock->reconfigureLock = false;
   739:         pDock->UpdateDockWindow();
   740:     }
   741: }
   742: 
   743: //===========================================================================
   744: 
   745: void Dock::ToggleAlwaysOnTop()
   746: {
   747:     if (!pSettings->dockOnTop)
   748:     {
   749:         pSettings->dockOnTop = true;
   750:         SetWindowPos(hDockWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
   751:     }
   752:     else
   753:     {
   754:         pSettings->dockOnTop = false;
   755:         SetWindowPos(hDockWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
   756:     }
   757: 
   758:     WriteBool(pSettings->xobrcFile, "xoblite.dock.onTop:", pSettings->dockOnTop);
   759: }
   760: 
   761: //===========================================================================
   762: 
   763: bool Dock::CheckIfMouseHover()
   764: {
   765:     POINT mousepos;
   766:     GetCursorPos(&mousepos);
   767:     if (mousepos.x >= DockX && mousepos.x <= (DockX + DockWidth))
   768:     {
   769:         if (mousepos.y >= DockY && mousepos.y <= (DockY + DockHeight)) return true;
   770:     }
   771: 
   772:     return false;
   773: }
   774: 
   775: //===========================================================================
   776: // Function: FullscreenDetected
   777: // Purpose: Hide the dock (no updating) if an application goes fullscreen
   778: //===========================================================================
   779: 
   780: void Dock::FullscreenDetected(bool fullscreen)
   781: {
   782:     if (fullscreen)
   783:     {
   784:         fullscreenDetected = true;
   785:         ShowWindow(hDockWnd, SW_HIDE);
   786:     }
   787:     else
   788:     {
   789:         fullscreenDetected = false;
   790:         if (!pSettings->dockHidden) ShowWindow(hDockWnd, SW_SHOWNOACTIVATE);
   791:         UpdateDockWindow();
   792:     }
   793: }
   794: 
   795: //===========================================================================
   796: