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: