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 "Desktop.h"
32:
33: #include <gdiplus.h>
34: using namespace Gdiplus;
35:
36: const char szDesktopName[] = "BBDesktop"; // Window class etc.
37:
38: extern Desktop* pDesktop;
39: extern Settings* pSettings;
40: extern MenuCommon* pMenuCommon;
41: extern Toolbar* pToolbar;
42: extern Taskbar* pTaskbar;
43: extern Workspaces* pWorkspaces;
44:
45: int desktopMessageSubscription[] = { BB_RECONFIGURE, BB_WINDOWSHADE, BB_WINDOWLOWER, BB_WINDOWRAISE, BB_WINDOWGROWHEIGHT, BB_WINDOWGROWWIDTH, 0 };
46:
47: //===========================================================================
48:
49: Desktop::Desktop(HINSTANCE hInstance)
50: {
51: hDesktopWnd = NULL;
52: hBlackboxWnd = GetBBWnd();
53: hDesktopInstance = hInstance;
54: cachedBackground = CreateCompatibleDC(NULL);
55: wallpaper.bitmap = NULL;
56:
57: CurrentTime[0] = CurrentWeekday[0] = CurrentDate[0] = CurrentGreeting[0] = '\0';
58:
59: fullscreenDetected = false;
60:
61: numberOfDisplayMonitors = GetSystemMetrics(SM_CMONITORS);
62:
63: //====================
64:
65: // Get size and position for the desktop window...
66: GetDimensions();
67: // ...and get the current time for the desktop clock...
68: GetClockText(true);
69: GetGreetingText(true);
70:
71: //====================
72:
73: // Register window class...
74: WNDCLASS wc;
75: ZeroMemory(&wc, sizeof(wc));
76: wc.hInstance = hDesktopInstance; // hInstance
77: wc.lpfnWndProc = DesktopWndProc; // window procedure
78: wc.lpszClassName = szDesktopName; // window class name
79: wc.hbrBackground = NULL; // no class background brush
80: wc.style = CS_DBLCLKS; // class styles (e.g. accept doubleclicks)
81: wc.hCursor = LoadCursor(NULL, IDC_ARROW); // always display the regular arrow cursor
82:
83: if (!RegisterClass(&wc))
84: {
85: MessageBox(0, "Error registering desktop window class!", szDesktopName, MB_OK | MB_ICONERROR | MB_TOPMOST);
86: Log("Desktop: Error registering window class", NULL);
87: return;
88: }
89:
90: // Create desktop window...
91: hDesktopWnd = CreateWindowEx(
92: WS_EX_TOOLWINDOW | WS_EX_ACCEPTFILES | WS_EX_NOACTIVATE | WS_EX_LAYERED, // window style
93: szDesktopName, // window class
94: NULL, // window name
95: WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, // window parameters
96: DesktopX, // x position
97: DesktopY, // y position
98: DesktopWidth, // window width
99: DesktopHeight, // window height
100: NULL, // owner window
101: NULL, // no menu
102: hDesktopInstance, // hInstance
103: NULL // no window creation data
104: );
105:
106: if (!hDesktopWnd)
107: {
108: UnregisterClass(szDesktopName, hDesktopInstance); // unregister window class
109: MessageBox(0, "Error creating desktop window!", szDesktopName, MB_OK | MB_ICONERROR | MB_TOPMOST);
110: Log("Desktop: Error creating window!", NULL);
111: return;
112: }
113:
114: //====================
115:
116: // Subscribe to Blackbox messages applicable to the desktop...
117: SendMessage(hBlackboxWnd, BB_REGISTERMESSAGE, (WPARAM)hDesktopWnd, (LPARAM)desktopMessageSubscription);
118:
119: // Make the desktop window sticky...
120: MakeSticky(hDesktopWnd);
121: // Move the desktop window to the bottom of the application z-order... (i.e. just above the Windows desktop in our case)
122: // SetWindowPos(hDesktopWnd, GetDesktopWindow(), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOOWNERZORDER);
123: SetWindowPos(hDesktopWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOOWNERZORDER);
124:
125: // Show+update the desktop window...
126: ShowWindow(hDesktopWnd, SW_SHOWNOACTIVATE);
127: UpdateDesktopWindow();
128:
129: // Set timer for the desktop clock update checks... (every 10 seconds, but only updating the desktop/clock if needed)
130: if (!SetTimer(hDesktopWnd, DESKTOP_UPDATE_TIMER, 10000, (TIMERPROC)NULL)) CurrentTime[0] = '\0';
131: }
132:
133: //===========================================================================
134:
135: Desktop::~Desktop()
136: {
137: KillTimer(hDesktopWnd, DESKTOP_UPDATE_TIMER);
138:
139: // Unsubscribe to previously subscribed Blackbox messages...
140: SendMessage(hBlackboxWnd, BB_UNREGISTERMESSAGE, (WPARAM)hDesktopWnd, (LPARAM)desktopMessageSubscription);
141:
142: if (hDesktopWnd) DestroyWindow(hDesktopWnd); // Destroy window...
143: UnregisterClass(szDesktopName, hDesktopInstance); // Unregister window class...
144:
145: if (wallpaper.bitmap != NULL) DeleteObject(wallpaper.bitmap); // Delete any cached bitmap...
146: if (cachedBackground != NULL) DeleteDC(cachedBackground); // Delete the cached gradient...
147: }
148:
149: //===========================================================================
150:
151: LRESULT CALLBACK DesktopWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
152: {
153:
154: switch (message)
155: {
156: //====================
157:
158: case BB_RECONFIGURE:
159: case WM_DISPLAYCHANGE:
160: {
161: // Update the desktop's dimensions if the screen resolution changes...
162: pDesktop->GetClockText(true);
163: pDesktop->GetGreetingText(true);
164: pDesktop->UpdateDesktopWindow();
165:
166: if (message == BB_RECONFIGURE) PlaySoundFX(SFX_RECONFIGURE);
167: else if (message == WM_DISPLAYCHANGE)
168: {
169: int tempMonitors = GetSystemMetrics(SM_CMONITORS);
170: if (pDesktop->numberOfDisplayMonitors != tempMonitors)
171: {
172: char msg[255];
173: if (tempMonitors > pDesktop->numberOfDisplayMonitors) strcpy_s(msg, sizeofArray(msg), "System -> Display monitor attached.");
174: else if (tempMonitors < pDesktop->numberOfDisplayMonitors) strcpy_s(msg, sizeofArray(msg), "System -> Display monitor removed.");
175: SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_REGULAR_MESSAGE, (LPARAM)msg);
176:
177: pDesktop->numberOfDisplayMonitors = tempMonitors;
178: }
179: }
180: break;
181: }
182:
183: case WM_SETTINGCHANGE:
184: {
185: if (wParam == SPI_SETWORKAREA)
186: {
187: // Update the desktop's dimensions and position if the work area changes...
188: // (e.g. if the Explorer taskbar is moved to another screen edge)
189: pDesktop->UpdateDesktopWindow();
190:
191: // ...and notify plugins that the desktop work area has changed...
192: SendMessage(GetBBWnd(), BB_REDRAWGUI, BBRG_DESKTOP, 0);
193: }
194: return 0;
195: }
196: /*
197: case WM_WINDOWPOSCHANGED:
198: {
199: // Move the desktop window back to the bottom of the z-order if its place in it changes...
200: // SetWindowPos(pDesktop->hDesktopWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
201: // SetWindowPos(pDesktop->hDesktopWnd, GetDesktopWindow(), 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
202: // SetWindowPos(pDesktop->hDesktopWnd, GetDesktopWindow(), pDesktop->DesktopX, pDesktop->DesktopX, pDesktop->DesktopWidth, pDesktop->DesktopHeight, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
203: // SetWindowPos(pDesktop->hDesktopWnd, GetDesktopWindow(), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_NOOWNERZORDER);
204: if (pSettings->debugLogging) SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_REGULAR_MESSAGE, (LPARAM)"WM_WINDOWPOSCHANGED");
205: // if (pSettings->debugLogging) Log("Desktop: WM_WINDOWPOSCHANGED", "");
206: return 0;
207: }
208: */
209: /*
210: case WM_GETMINMAXINFO:
211: {
212: // Not sure if this is really needed since we don't allow the desktop window to be moved/resized anyway, but just in case... ;)
213: MINMAXINFO* mmi = (MINMAXINFO*)lParam;
214: mmi->ptMaxPosition.x = pDesktop->DesktopX;
215: mmi->ptMaxPosition.y = pDesktop->DesktopY;
216: mmi->ptMaxSize.x = pDesktop->DesktopWidth;
217: mmi->ptMaxSize.y = pDesktop->DesktopHeight;
218: return 0;
219: }
220: */
221: //====================
222:
223: case WM_TIMER:
224: {
225: // Is it time to update the desktop clock/weekday/date?
226: if (!pSettings->desktopClockHidden || !pSettings->desktopWeekdayHidden || !pSettings->desktopDateHidden || !pSettings->desktopGreetingHidden)
227: {
228: if (pDesktop->GetClockText(false))
229: {
230: // Clock timestamp has changed; update the other widgets too, then the Desktop...
231: pDesktop->GetGreetingText(true);
232: pDesktop->UpdateDesktopWindow();
233: }
234: }
235: return 0;
236: }
237:
238: //====================
239:
240: case WM_CLOSE:
241: return 0;
242:
243: //====================
244:
245: case WM_MOUSEACTIVATE:
246: return MA_NOACTIVATE;
247: /*
248: case WM_ACTIVATE:
249: {
250: if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) SetActiveWindow(hwnd);
251: return 0;
252: }
253: break;
254: */
255: //====================
256:
257: case WM_NCHITTEST:
258: return HTCLIENT;
259:
260: case WM_LBUTTONDOWN:
261: case WM_LBUTTONUP:
262: // case WM_LBUTTONDBLCLK:
263: case WM_RBUTTONDOWN:
264: case WM_RBUTTONUP:
265: case WM_MBUTTONDOWN:
266: case WM_MBUTTONUP:
267: case WM_XBUTTONDOWN:
268: case WM_XBUTTONUP:
269: case WM_MOUSEWHEEL:
270: case WM_MOUSEHWHEEL:
271: case WM_DROPFILES:
272: {
273: // First we set focus to the main Blackbox window, in order to prevent the
274: // focus from going back to the previous task after e.g. the menu is closed...
275: SetForegroundWindow(GetBBWnd());
276: pDesktop->MouseAndDropHandler(pDesktop->hDesktopWnd, message, wParam, lParam);
277: return 0;
278: }
279: break;
280:
281: //====================
282:
283: case BB_WINDOWSHADE:
284: pDesktop->ShadeWindow();
285: break;
286: case BB_WINDOWGROWHEIGHT:
287: pDesktop->GrowWindowHeight();
288: break;
289: case BB_WINDOWGROWWIDTH:
290: pDesktop->GrowWindowWidth();
291: break;
292: case BB_WINDOWLOWER:
293: pDesktop->LowerWindow();
294: break;
295: case BB_WINDOWRAISE:
296: pDesktop->RaiseWindow();
297: break;
298:
299: //====================
300:
301: default:
302: return DefWindowProc(hwnd, message, wParam, lParam);
303:
304: //====================
305: }
306:
307: return 0;
308: }
309:
310: //===========================================================================
311: // Function: UpdateDesktopWindow
312: // Purpose: ...
313: //===========================================================================
314:
315: void Desktop::UpdateDesktopWindow()
316: {
317: if (fullscreenDetected) return;
318:
319: // Fetch the new size and position parameters for the desktop window...
320: GetDimensions();
321:
322: RECT r;
323: SetRect(&r, 0, 0, DesktopWidth, DesktopHeight);
324:
325: HDC hdc = GetWindowDC(hDesktopWnd);
326: HBITMAP bufbmp = CreateCompatibleBitmap(hdc, r.right - r.left, r.bottom - r.top);
327: DeleteObject(SelectObject(cachedBackground, bufbmp));
328: ReleaseDC(hDesktopWnd, hdc);
329:
330: HDC widgetDC = CreateCompatibleDC(cachedBackground);
331: HBRUSH blackBrush = CreateSolidBrush(0x000000);
332:
333: //====================
334:
335:
336: if (!pSettings->underExplorer && (wallpaper.bitmap != NULL))
337: {
338: SetRect(&r, 0, 0, DesktopWidth, DesktopHeight);
339: DrawWallpaper(cachedBackground);
340: AlphaRect(cachedBackground, r, 255);
341: }
342: else
343: {
344: // Draw desktop background...
345: FillRect(cachedBackground, &r, blackBrush);
346:
347: // Apply almost full transparency -> The desktop is still there and we can click it etc, but the regular Windows wallpaper can be seen!
348: SetRect(&r, 0, 0, DesktopWidth, DesktopHeight);
349: if (!pSettings->desktopBorderHidden)
350: {
351: AlphaRect(cachedBackground, r, 64);
352: InflateRect(&r, (-4 * pSettings->scalingFactorHiDPI), (-4 * pSettings->scalingFactorHiDPI));
353: }
354: AlphaRect(cachedBackground, r, 1);
355: // if (!pSettings->desktopBorderHidden) AlphaCorner(cachedBackground, r, CORNER_TOPLEFT | CORNER_TOPRIGHT | CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT, 0, 0, 0, 0, 64, 1);
356: // AlphaApply(cachedBackground, r);
357: }
358:
359: //====================
360:
361: if (!pSettings->desktopWorkspaceIndicatorHidden)
362: {
363: // Draw workspace indicators... (nb. their default position is near top center, but if the toolbar placement is top something we put them near bottom center instead)
364: char workspacePlacement[20] = "\0";
365: Placement workspaceIndicators;
366: workspaceIndicators.width = (pSettings->numberOfWorkspaces * 20 * pSettings->scalingFactorHiDPI) - (4 * pSettings->scalingFactorHiDPI);
367: workspaceIndicators.height = (9 * pSettings->scalingFactorHiDPI);
368: if (!pSettings->toolbarHidden && (pSettings->ToolbarPlacement.placement == PLACEMENT_TOP_LEFT ||
369: pSettings->ToolbarPlacement.placement == PLACEMENT_TOP_CENTER || pSettings->ToolbarPlacement.placement == PLACEMENT_TOP_RIGHT))
370: {
371: sprintf_s(workspacePlacement, sizeofArray(workspacePlacement), "Manual xC y-%d", r.top + (9*pSettings->scalingFactorHiDPI));
372: }
373: else
374: {
375: sprintf_s(workspacePlacement, sizeofArray(workspacePlacement), "Manual xC y%d", r.top + (9*pSettings->scalingFactorHiDPI));
376: }
377: pSettings->ParsePlacement(workspacePlacement, &workspaceIndicators);
378: pSettings->PositionFromPlacement(&workspaceIndicators);
379: // CheckWidgetPlacement(&workspaceIndicators);
380:
381: SetRect(&r, workspaceIndicators.x, workspaceIndicators.y, workspaceIndicators.x+(16*pSettings->scalingFactorHiDPI), workspaceIndicators.y+(9*pSettings->scalingFactorHiDPI));
382:
383: // HBRUSH brush = CreateSolidBrush(0xffffff);
384: HBRUSH brush = CreateSolidBrush(pSettings->desktopWidgetColor);
385: for (int i=0; i<pSettings->numberOfWorkspaces; i++)
386: {
387: FillRect(cachedBackground, &r, brush);
388: if (i == pSettings->currentWorkspace) AlphaRect(cachedBackground, r, 128);
389: else AlphaRect(cachedBackground, r, 64);
390: OffsetRect(&r, (20*pSettings->scalingFactorHiDPI), 0);
391: }
392: DeleteObject(brush);
393: }
394:
395: //====================
396:
397: // Draw weekday...?
398: if (!pSettings->desktopWeekdayHidden)
399: {
400: HFONT widgetFont;
401: HGDIOBJ oldFont;
402:
403: if (pSettings->desktopWeekdayFontDefined)
404: {
405: widgetFont = CreateFont((pSettings->desktopWeekdayFontHeight * pSettings->scalingFactorHiDPI), 0, 0, 0, pSettings->desktopWeekdayFontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, pSettings->desktopWeekdayFont);
406: }
407: else
408: {
409: int fontHeight = 30 * pSettings->scalingFactorHiDPI;
410: widgetFont = CreateFont(fontHeight, 0, 0, 0, FW_BOLD, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Segoe UI");
411: }
412:
413: oldFont = SelectObject(cachedBackground, widgetFont);
414: SetBkMode(cachedBackground, TRANSPARENT);
415: SIZE size;
416: GetTextExtentPoint32W(cachedBackground, CurrentWeekday, wcslen(CurrentWeekday), &size);
417: pSettings->desktopWeekdayPlacement.width = size.cx;
418: pSettings->desktopWeekdayPlacement.height = size.cy;
419: SelectObject(cachedBackground, oldFont);
420:
421: if (pSettings->desktopWeekdayPlacement.placement != PLACEMENT_DEFAULT)
422: {
423: pSettings->PositionFromPlacement(&pSettings->desktopWeekdayPlacement);
424: CheckWidgetPlacement(&pSettings->desktopWeekdayPlacement);
425: SetRect(&weekdayRect, pSettings->desktopWeekdayPlacement.x, pSettings->desktopWeekdayPlacement.y, pSettings->desktopWeekdayPlacement.x + pSettings->desktopWeekdayPlacement.width, pSettings->desktopWeekdayPlacement.y + pSettings->desktopWeekdayPlacement.height);
426: }
427: else // PLACEMENT_DEFAULT
428: {
429: weekdayRect.left = DesktopCenterX - (pSettings->desktopWeekdayPlacement.width / 2);
430: weekdayRect.right = weekdayRect.left + pSettings->desktopWeekdayPlacement.width;
431: if (pSettings->desktopWeekdayFontDefined) weekdayRect.top = 139;
432: else weekdayRect.top = 120;
433: weekdayRect.bottom = weekdayRect.top + pSettings->desktopWeekdayPlacement.height;
434: }
435:
436: HBITMAP widgetBitmap = CreateCompatibleBitmap(cachedBackground, pSettings->desktopWeekdayPlacement.width, pSettings->desktopWeekdayPlacement.height);
437: HGDIOBJ oldBitmap = SelectObject(widgetDC, widgetBitmap);
438:
439: oldFont = SelectObject(widgetDC, widgetFont);
440: SetBkMode(widgetDC, TRANSPARENT);
441: RECT widgetRect = { 0, 0, pSettings->desktopWeekdayPlacement.width, pSettings->desktopWeekdayPlacement.height };
442: FillRect(widgetDC, &widgetRect, blackBrush);
443: DrawTextWithEffectsUnicode(widgetDC, widgetRect, CurrentWeekday, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE, 0xffffff, false, 0, false, 0, 0, 0);
444: AlphaFromRGB(widgetDC, widgetRect, 0, 255, false, 0); // Note: The widget bitmap's per-pixel alpha is 0 by default -> We need to calculate this for AlphaBlend() below to work!
445: SelectObject(widgetDC, oldFont);
446:
447: BLENDFUNCTION bf;
448: ZeroMemory(&bf, sizeof(BLENDFUNCTION));
449: bf.BlendOp = AC_SRC_OVER;
450: bf.BlendFlags = 0;
451: bf.SourceConstantAlpha = pSettings->desktopWeekdayTransparencyAlpha;
452: bf.AlphaFormat = AC_SRC_ALPHA;
453: AlphaBlend(cachedBackground, weekdayRect.left, weekdayRect.top, pSettings->desktopWeekdayPlacement.width, pSettings->desktopWeekdayPlacement.height, widgetDC, 0, 0, pSettings->desktopWeekdayPlacement.width, pSettings->desktopWeekdayPlacement.height, bf);
454:
455: SelectObject(widgetDC, oldBitmap);
456: DeleteObject(widgetBitmap);
457: // DeleteObject(oldBitmap); // Not necessary (default stock object)
458: DeleteObject(widgetFont);
459: // DeleteObject(oldFont); // Not necessary (default stock object)
460: }
461:
462: //====================
463:
464: // Draw date...?
465: if (!pSettings->desktopDateHidden)
466: {
467: HFONT widgetFont;
468: HGDIOBJ oldFont;
469:
470: if (pSettings->desktopDateFontDefined)
471: {
472: widgetFont = CreateFont((pSettings->desktopDateFontHeight * pSettings->scalingFactorHiDPI), 0, 0, 0, pSettings->desktopDateFontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, pSettings->desktopDateFont);
473: }
474: else
475: {
476: int fontHeight = 30 * pSettings->scalingFactorHiDPI;
477: widgetFont = CreateFont(fontHeight, 0, 0, 0, FW_NORMAL, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Segoe UI");
478: }
479:
480: oldFont = SelectObject(cachedBackground, widgetFont);
481: SetBkMode(cachedBackground, TRANSPARENT);
482: SIZE size;
483: GetTextExtentPoint32W(cachedBackground, CurrentDate, wcslen(CurrentDate), &size);
484: pSettings->desktopDatePlacement.width = size.cx;
485: pSettings->desktopDatePlacement.height = size.cy;
486: SelectObject(cachedBackground, oldFont);
487:
488: if (pSettings->desktopDatePlacement.placement != PLACEMENT_DEFAULT)
489: {
490: pSettings->PositionFromPlacement(&pSettings->desktopDatePlacement);
491: CheckWidgetPlacement(&pSettings->desktopDatePlacement);
492: SetRect(&dateRect, pSettings->desktopDatePlacement.x, pSettings->desktopDatePlacement.y, pSettings->desktopDatePlacement.x + pSettings->desktopDatePlacement.width, pSettings->desktopDatePlacement.y + pSettings->desktopDatePlacement.height);
493: }
494: else // PLACEMENT_DEFAULT
495: {
496: dateRect.left = DesktopCenterX - (pSettings->desktopDatePlacement.width / 2);
497: dateRect.right = dateRect.left + pSettings->desktopDatePlacement.width;
498: if (pSettings->desktopDateFontDefined) dateRect.top = 239;
499: else dateRect.top = 220;
500: dateRect.bottom = dateRect.top + pSettings->desktopDatePlacement.height;
501: }
502:
503: HBITMAP widgetBitmap = CreateCompatibleBitmap(cachedBackground, pSettings->desktopDatePlacement.width, pSettings->desktopDatePlacement.height);
504: HGDIOBJ oldBitmap = SelectObject(widgetDC, widgetBitmap);
505:
506: oldFont = SelectObject(widgetDC, widgetFont);
507: SetBkMode(widgetDC, TRANSPARENT);
508: RECT widgetRect = { 0, 0, pSettings->desktopDatePlacement.width, pSettings->desktopDatePlacement.height };
509: FillRect(widgetDC, &widgetRect, blackBrush);
510: DrawTextWithEffectsUnicode(widgetDC, widgetRect, CurrentDate, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE, 0xffffff, false, 0, false, 0, 0, 0);
511: AlphaFromRGB(widgetDC, widgetRect, 0, 255, false, 0); // Note: The widget bitmap's per-pixel alpha is 0 by default -> We need to calculate this for AlphaBlend() below to work!
512: SelectObject(widgetDC, oldFont);
513:
514: BLENDFUNCTION bf;
515: ZeroMemory(&bf, sizeof(BLENDFUNCTION));
516: bf.BlendOp = AC_SRC_OVER;
517: bf.BlendFlags = 0;
518: bf.SourceConstantAlpha = pSettings->desktopDateTransparencyAlpha;
519: bf.AlphaFormat = AC_SRC_ALPHA;
520: AlphaBlend(cachedBackground, dateRect.left, dateRect.top, pSettings->desktopDatePlacement.width, pSettings->desktopDatePlacement.height, widgetDC, 0, 0, pSettings->desktopDatePlacement.width, pSettings->desktopDatePlacement.height, bf);
521:
522: SelectObject(widgetDC, oldBitmap);
523: DeleteObject(widgetBitmap);
524: // DeleteObject(oldBitmap); // Not necessary (default stock object)
525: DeleteObject(widgetFont);
526: // DeleteObject(oldFont); // Not necessary (default stock object)
527: }
528:
529: //====================
530:
531: // Draw clock...?
532: if (!pSettings->desktopClockHidden)
533: {
534: if (!_stricmp(pSettings->desktopClockType, "Regular")) // -> Regular 24-hour clock (HH:MM)
535: {
536: HFONT widgetFont;
537: HGDIOBJ oldFont;
538:
539: if (pSettings->desktopClockFontDefined)
540: {
541: widgetFont = CreateFont((pSettings->desktopClockFontHeight * pSettings->scalingFactorHiDPI), 0, 0, 0, pSettings->desktopClockFontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, pSettings->desktopClockFont);
542: }
543: else
544: {
545: int fontHeight = 180 * pSettings->scalingFactorHiDPI;
546: widgetFont = CreateFont(fontHeight, 0, 0, 0, FW_NORMAL, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Segoe UI");
547: }
548:
549: oldFont = SelectObject(cachedBackground, widgetFont);
550: SetBkMode(cachedBackground, TRANSPARENT);
551: SIZE size;
552: GetTextExtentPoint32W(cachedBackground, CurrentTime, wcslen(CurrentTime), &size);
553: pSettings->desktopClockPlacement.width = size.cx;
554: pSettings->desktopClockPlacement.height = size.cy;
555: SelectObject(cachedBackground, oldFont);
556:
557: if (pSettings->desktopClockPlacement.placement != PLACEMENT_DEFAULT)
558: {
559: pSettings->PositionFromPlacement(&pSettings->desktopClockPlacement);
560: CheckWidgetPlacement(&pSettings->desktopClockPlacement);
561: SetRect(&clockRect, pSettings->desktopClockPlacement.x, pSettings->desktopClockPlacement.y, pSettings->desktopClockPlacement.x + pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.y + pSettings->desktopClockPlacement.height);
562: }
563: else // PLACEMENT_DEFAULT
564: {
565: clockRect.left = DesktopCenterX - (pSettings->desktopClockPlacement.width / 2);
566: clockRect.right = clockRect.left + pSettings->desktopClockPlacement.width;
567: if (pSettings->desktopClockFontDefined) clockRect.top = 39;
568: else clockRect.top = 20;
569: clockRect.bottom = clockRect.top + pSettings->desktopClockPlacement.height;
570: }
571:
572: HBITMAP widgetBitmap = CreateCompatibleBitmap(cachedBackground, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height);
573: HGDIOBJ oldBitmap = SelectObject(widgetDC, widgetBitmap);
574:
575: oldFont = SelectObject(widgetDC, widgetFont);
576: SetBkMode(widgetDC, TRANSPARENT);
577: RECT widgetRect = { 0, 0, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height };
578: FillRect(widgetDC, &widgetRect, blackBrush);
579: DrawTextWithEffectsUnicode(widgetDC, widgetRect, CurrentTime, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE, 0xffffff, false, 0, false, 0, 0, 0);
580: AlphaFromRGB(widgetDC, widgetRect, 0, 255, false, 0); // Note: The widget bitmap's per-pixel alpha is 0 by default -> We need to calculate this for AlphaBlend() below to work!
581: SelectObject(widgetDC, oldFont);
582:
583: BLENDFUNCTION bf;
584: ZeroMemory(&bf, sizeof(BLENDFUNCTION));
585: bf.BlendOp = AC_SRC_OVER;
586: bf.BlendFlags = 0;
587: bf.SourceConstantAlpha = pSettings->desktopClockTransparencyAlpha;
588: bf.AlphaFormat = AC_SRC_ALPHA;
589: AlphaBlend(cachedBackground, clockRect.left, clockRect.top, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height, widgetDC, 0, 0, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height, bf);
590:
591: SelectObject(widgetDC, oldBitmap);
592: DeleteObject(widgetBitmap);
593: // DeleteObject(oldBitmap); // Not necessary (default stock object)
594: DeleteObject(widgetFont);
595: // DeleteObject(oldFont); // Not necessary (default stock object)
596: }
597:
598: //====================
599:
600: else if (!_stricmp(pSettings->desktopClockType, "Text")) // -> "Text clock" using simple 12 hour format, e.g. "TWENTY past EIGHT" (regardless of AM/PM)
601: {
602: char textMinutes[32] = "", textInBetween[32] = "", textHours[32] = "", textAfter[32] = "", textClock[128] = "";
603:
604: int relativeMinute = currentMinute;
605: if (currentMinute > 30) relativeMinute = 60 - currentMinute;
606:
607: if (relativeMinute < 30)
608: {
609: if (relativeMinute >= 20)
610: {
611: strcat_s(textMinutes, sizeofArray(textMinutes), "TWENTY");
612: switch (relativeMinute - 20)
613: {
614: case 9: { strcat_s(textMinutes, sizeofArray(textMinutes), " NINE"); break; }
615: case 8: { strcat_s(textMinutes, sizeofArray(textMinutes), " EIGHT"); break; }
616: case 7: { strcat_s(textMinutes, sizeofArray(textMinutes), " SEVEN"); break; }
617: case 6: { strcat_s(textMinutes, sizeofArray(textMinutes), " SIX"); break; }
618: case 5: { strcat_s(textMinutes, sizeofArray(textMinutes), " FIVE"); break; }
619: case 4: { strcat_s(textMinutes, sizeofArray(textMinutes), " FOUR"); break; }
620: case 3: { strcat_s(textMinutes, sizeofArray(textMinutes), " THREE"); break; }
621: case 2: { strcat_s(textMinutes, sizeofArray(textMinutes), " TWO"); break; }
622: case 1: { strcat_s(textMinutes, sizeofArray(textMinutes), " ONE"); break; }
623: case 0: { break; }
624: }
625: }
626: else if (relativeMinute >= 10)
627: {
628: switch (relativeMinute - 10)
629: {
630: case 9: { strcat_s(textMinutes, sizeofArray(textMinutes), "NINETEEN"); break; }
631: case 8: { strcat_s(textMinutes, sizeofArray(textMinutes), "EIGHTEEN"); break; }
632: case 7: { strcat_s(textMinutes, sizeofArray(textMinutes), "SEVENTEEN"); break; }
633: case 6: { strcat_s(textMinutes, sizeofArray(textMinutes), "SIXTEEN"); break; }
634: case 5: { strcat_s(textMinutes, sizeofArray(textMinutes), "QUARTER "); break; }
635: case 4: { strcat_s(textMinutes, sizeofArray(textMinutes), "FOURTEEN"); break; }
636: case 3: { strcat_s(textMinutes, sizeofArray(textMinutes), "THIRTEEN"); break; }
637: case 2: { strcat_s(textMinutes, sizeofArray(textMinutes), "TWELVE"); break; }
638: case 1: { strcat_s(textMinutes, sizeofArray(textMinutes), "ELEVEN"); break; }
639: case 0: { strcat_s(textMinutes, sizeofArray(textMinutes), "TEN"); break; }
640: }
641: }
642: else if (relativeMinute >= 0)
643: {
644: switch (relativeMinute)
645: {
646: case 9: { strcat_s(textMinutes, sizeofArray(textMinutes), "NINE"); break; }
647: case 8: { strcat_s(textMinutes, sizeofArray(textMinutes), "EIGHT"); break; }
648: case 7: { strcat_s(textMinutes, sizeofArray(textMinutes), "SEVEN"); break; }
649: case 6: { strcat_s(textMinutes, sizeofArray(textMinutes), "SIX"); break; }
650: case 5: { strcat_s(textMinutes, sizeofArray(textMinutes), "FIVE"); break; }
651: case 4: { strcat_s(textMinutes, sizeofArray(textMinutes), "FOUR"); break; }
652: case 3: { strcat_s(textMinutes, sizeofArray(textMinutes), "THREE"); break; }
653: case 2: { strcat_s(textMinutes, sizeofArray(textMinutes), "TWO"); break; }
654: case 1: { strcat_s(textMinutes, sizeofArray(textMinutes), "ONE"); break; }
655: }
656: }
657:
658: if (relativeMinute == 1) strcat_s(textInBetween, sizeofArray(textInBetween), " minute ");
659: else if ((relativeMinute > 1) && (relativeMinute != 15)) strcat_s(textInBetween, sizeofArray(textInBetween), " minutes ");
660:
661: if ((currentMinute > 0) && (currentMinute <= 30)) strcat_s(textInBetween, sizeofArray(textInBetween), "past ");
662: else if (relativeMinute > 0) strcat_s(textInBetween, sizeofArray(textInBetween), "to ");
663: }
664: else if (currentMinute == 30)
665: {
666: strcat_s(textMinutes, sizeofArray(textMinutes), "HALF");
667: strcat_s(textInBetween, sizeofArray(textInBetween), " past ");
668: }
669:
670: int relativeHour = currentHour;
671: if (currentMinute > 30) relativeHour += 1;
672: if (relativeHour >= 24) relativeHour -= 24;
673: switch (relativeHour)
674: {
675: case 12: { strcat_s(textHours, sizeofArray(textHours), "NOON"); break; }
676: case 11: case 23: { strcat_s(textHours, sizeofArray(textHours), "ELEVEN"); break; }
677: case 10: case 22: { strcat_s(textHours, sizeofArray(textHours), "TEN"); break; }
678: case 9: case 21: { strcat_s(textHours, sizeofArray(textHours), "NINE"); break; }
679: case 8: case 20: { strcat_s(textHours, sizeofArray(textHours), "EIGHT"); break; }
680: case 7: case 19: { strcat_s(textHours, sizeofArray(textHours), "SEVEN"); break; }
681: case 6: case 18: { strcat_s(textHours, sizeofArray(textHours), "SIX"); break; }
682: case 5: case 17: { strcat_s(textHours, sizeofArray(textHours), "FIVE"); break; }
683: case 4: case 16: { strcat_s(textHours, sizeofArray(textHours), "FOUR"); break; }
684: case 3: case 15: { strcat_s(textHours, sizeofArray(textHours), "THREE"); break; }
685: case 2: case 14: { strcat_s(textHours, sizeofArray(textHours), "TWO"); break; }
686: case 1: case 13: { strcat_s(textHours, sizeofArray(textHours), "ONE"); break; }
687: case 0: { strcat_s(textHours, sizeofArray(textHours), "MIDNIGHT"); break; }
688: }
689:
690: if ((currentMinute == 0) && (currentHour != 0) && (currentHour != 12)) strcat_s(textAfter, sizeofArray(textAfter), " o'clock");
691:
692: sprintf_s(textClock, sizeofArray(textClock), "%s%s%s%s", textMinutes, textInBetween, textHours, textAfter);
693:
694: HFONT widgetFont;
695: HGDIOBJ oldFont;
696:
697: if (pSettings->desktopClockFontDefined)
698: {
699: widgetFont = CreateFont((pSettings->desktopClockFontHeight * pSettings->scalingFactorHiDPI), 0, 0, 0, pSettings->desktopClockFontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, pSettings->desktopClockFont);
700: }
701: else
702: {
703: int fontHeight = 30 * pSettings->scalingFactorHiDPI;
704: widgetFont = CreateFont(fontHeight, 0, 0, 0, FW_BOLD, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Segoe UI");
705: }
706:
707: oldFont = SelectObject(cachedBackground, widgetFont);
708: SetBkMode(cachedBackground, TRANSPARENT);
709: SIZE size;
710: GetTextExtentPoint32(cachedBackground, textClock, strnlen_s(textClock, sizeofArray(textClock)), &size);
711: pSettings->desktopClockPlacement.width = size.cx;
712: pSettings->desktopClockPlacement.height = size.cy;
713: SelectObject(cachedBackground, oldFont);
714:
715: if (pSettings->desktopClockPlacement.placement != PLACEMENT_DEFAULT)
716: {
717: pSettings->PositionFromPlacement(&pSettings->desktopClockPlacement);
718: CheckWidgetPlacement(&pSettings->desktopClockPlacement);
719: SetRect(&clockRect, pSettings->desktopClockPlacement.x, pSettings->desktopClockPlacement.y, pSettings->desktopClockPlacement.x + pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.y + pSettings->desktopClockPlacement.height);
720: }
721: else // PLACEMENT_DEFAULT
722: {
723: clockRect.left = DesktopCenterX - (pSettings->desktopClockPlacement.width / 2);
724: clockRect.right = clockRect.left + pSettings->desktopClockPlacement.width;
725: // if (pSettings->doubleScaleHiDPI) clockRect.top = 32;
726: if (pSettings->scalingFactorHiDPI > 1) clockRect.top = 32;
727: else clockRect.top = 30;
728: clockRect.bottom = clockRect.top + pSettings->desktopClockPlacement.height;
729:
730: pSettings->desktopClockPlacement.x = clockRect.left; // ...we keep a copy of clockRect.left to be able to restore it later... (see below)
731: }
732:
733: HBITMAP widgetBitmap = CreateCompatibleBitmap(cachedBackground, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height);
734: HGDIOBJ oldBitmap = SelectObject(widgetDC, widgetBitmap);
735:
736: oldFont = SelectObject(widgetDC, widgetFont);
737: SetBkMode(widgetDC, TRANSPARENT);
738: RECT widgetRect = { 0, 0, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height };
739: FillRect(widgetDC, &widgetRect, blackBrush);
740:
741: if (strnlen_s(textMinutes, sizeofArray(textMinutes)) > 0)
742: {
743: DrawTextWithEffects(widgetDC, widgetRect, textMinutes, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE, 0xffffff, false, 0, false, 0, 0, 0);
744: GetTextExtentPoint32(widgetDC, textMinutes, strnlen_s(textMinutes, sizeofArray(textMinutes)), &size);
745: widgetRect.left += size.cx;
746:
747: DrawTextWithEffects(widgetDC, widgetRect, textInBetween, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE, 0x7f7f7f, false, 0, false, 0, 0, 0);
748: GetTextExtentPoint32(widgetDC, textInBetween, strnlen_s(textInBetween, sizeofArray(textInBetween)), &size);
749: widgetRect.left += size.cx;
750: }
751:
752: DrawTextWithEffects(widgetDC, widgetRect, textHours, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE, 0xffffff, false, 0, false, 0, 0, 0);
753:
754: if (strnlen_s(textAfter, sizeofArray(textAfter)) > 0)
755: {
756: GetTextExtentPoint32(widgetDC, textHours, strnlen_s(textHours, sizeofArray(textHours)), &size);
757: widgetRect.left += size.cx;
758: DrawTextWithEffects(widgetDC, widgetRect, textAfter, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE, 0x7f7f7f, false, 0, false, 0, 0, 0);
759: }
760:
761: widgetRect.left = 0;
762:
763: AlphaFromRGB(widgetDC, widgetRect, 0, 255, false, 0); // Note: The widget bitmap's per-pixel alpha is 0 by default -> We need to calculate this for AlphaBlend() below to work!
764: SelectObject(widgetDC, oldFont);
765:
766: BLENDFUNCTION bf;
767: ZeroMemory(&bf, sizeof(BLENDFUNCTION));
768: bf.BlendOp = AC_SRC_OVER;
769: bf.BlendFlags = 0;
770: bf.SourceConstantAlpha = pSettings->desktopClockTransparencyAlpha;
771: bf.AlphaFormat = AC_SRC_ALPHA;
772: AlphaBlend(cachedBackground, clockRect.left, clockRect.top, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height, widgetDC, 0, 0, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height, bf);
773:
774: SelectObject(widgetDC, oldBitmap);
775: DeleteObject(widgetBitmap);
776: // DeleteObject(oldBitmap); // Not necessary (default stock object)
777: DeleteObject(widgetFont);
778: // DeleteObject(oldFont); // Not necessary (default stock object)
779: }
780:
781: //====================
782:
783: else if (!_stricmp(pSettings->desktopClockType, "Binary")) // -> Bit clock
784: {
785: wchar_t temp;
786: int currentDigit;
787: int clockBitWidth, clockBitHeight;
788:
789: if (pSettings->desktopClockPlacement.placement != PLACEMENT_DEFAULT)
790: {
791: if (pSettings->doubleScaleHiDPI)
792: {
793: pSettings->desktopClockPlacement.width = 396;
794: pSettings->desktopClockPlacement.height = 228;
795: pSettings->PositionFromPlacement(&pSettings->desktopClockPlacement);
796: CheckWidgetPlacement(&pSettings->desktopClockPlacement);
797: clockBitWidth = 96;
798: clockBitHeight = 54;
799: }
800: else
801: {
802: pSettings->desktopClockPlacement.width = 204;
803: pSettings->desktopClockPlacement.height = 120;
804: pSettings->PositionFromPlacement(&pSettings->desktopClockPlacement);
805: CheckWidgetPlacement(&pSettings->desktopClockPlacement);
806: clockBitWidth = 48;
807: clockBitHeight = 27;
808: }
809: SetRect(&clockRect, pSettings->desktopClockPlacement.x, pSettings->desktopClockPlacement.y, pSettings->desktopClockPlacement.x + pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.y + pSettings->desktopClockPlacement.height);
810: }
811: else // PLACEMENT_DEFAULT
812: {
813: if (pSettings->doubleScaleHiDPI) SetRect(&clockRect, DesktopCenterX-198, 49, DesktopCenterX-102, 103);
814: else SetRect(&clockRect, DesktopCenterX-102, 49, DesktopCenterX-54, 76);
815: }
816:
817: HBITMAP widgetBitmap = CreateCompatibleBitmap(cachedBackground, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height);
818: HGDIOBJ oldBitmap = SelectObject(widgetDC, widgetBitmap);
819:
820: int mediumTransparencyAlpha = pSettings->desktopClockTransparencyAlpha / 2;
821: int lowTransparencyAlpha = pSettings->desktopClockTransparencyAlpha / 8;
822:
823: HBRUSH widgetHighBrush = CreateSolidBrush(0xffffff);
824: HBRUSH widgetMediumBrush = CreateSolidBrush(0x7f7f7f);
825: HBRUSH widgetLowBrush = CreateSolidBrush(0x1f1f1f);
826:
827: RECT widgetRect = { 0, 0, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height };
828: FillRect(widgetDC, &widgetRect, blackBrush);
829: SetRect(&widgetRect, 0, 0, clockBitWidth, clockBitHeight);
830:
831: for (int x=1; x<=4; x++)
832: {
833: switch (x)
834: {
835: case 1: { temp = CurrentTime[0]; break; }
836: case 2: { temp = CurrentTime[1]; break; }
837: case 3: { temp = CurrentTime[3]; break; }
838: case 4: { temp = CurrentTime[4]; break; }
839: default: { break; }
840: }
841: currentDigit = _wtoi(&temp);
842:
843: for (int y=1; y<=4; y++)
844: {
845: if ((x == 1) && ((y == 1) || (y == 2))) FillRect(widgetDC, &widgetRect, widgetLowBrush); // ...because the first digit can only be 0-2...
846: else
847: {
848: if ((y == 1) && (currentDigit & 8)) FillRect(widgetDC, &widgetRect, widgetHighBrush);
849: else if ((y == 2) && (currentDigit & 4)) FillRect(widgetDC, &widgetRect, widgetHighBrush);
850: else if ((y == 3) && (currentDigit & 2)) FillRect(widgetDC, &widgetRect, widgetHighBrush);
851: else if ((y == 4) && (currentDigit & 1)) FillRect(widgetDC, &widgetRect, widgetHighBrush);
852: else FillRect(widgetDC, &widgetRect, widgetMediumBrush);
853: }
854:
855: if (pSettings->doubleScaleHiDPI) OffsetRect(&widgetRect, 0, 58);
856: else OffsetRect(&widgetRect, 0, 31);
857: }
858:
859: if (pSettings->doubleScaleHiDPI) OffsetRect(&widgetRect, 100, -232);
860: else OffsetRect(&widgetRect, 52, -124);
861: }
862:
863: widgetRect = { 0, 0, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height };
864:
865: AlphaFromRGB(widgetDC, widgetRect, 0, 255, false, 0); // Note: The widget bitmap's per-pixel alpha is 0 by default -> We need to calculate this for AlphaBlend() below to work!
866:
867: BLENDFUNCTION bf;
868: ZeroMemory(&bf, sizeof(BLENDFUNCTION));
869: bf.BlendOp = AC_SRC_OVER;
870: bf.BlendFlags = 0;
871: bf.SourceConstantAlpha = pSettings->desktopClockTransparencyAlpha;
872: bf.AlphaFormat = AC_SRC_ALPHA;
873: AlphaBlend(cachedBackground, clockRect.left, clockRect.top, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height, widgetDC, 0, 0, pSettings->desktopClockPlacement.width, pSettings->desktopClockPlacement.height, bf);
874:
875: DeleteObject(widgetHighBrush);
876: DeleteObject(widgetMediumBrush);
877: DeleteObject(widgetLowBrush);
878: SelectObject(widgetDC, oldBitmap);
879: DeleteObject(widgetBitmap);
880: // DeleteObject(oldBitmap); // Not necessary (default stock object)
881: }
882: }
883:
884: //====================
885:
886: // Draw greeting...?
887: if (!pSettings->desktopGreetingHidden)
888: {
889: if (pSettings->desktopDisableGDIplusForWidgets) // -> Use regular GDI to draw the widget
890: {
891: HFONT widgetFont;
892: HGDIOBJ oldFont;
893:
894: if (pSettings->desktopGreetingFontDefined)
895: {
896: widgetFont = CreateFont((pSettings->desktopGreetingFontHeight * pSettings->scalingFactorHiDPI), 0, 0, 0, pSettings->desktopGreetingFontWeight, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, pSettings->desktopGreetingFont);
897: }
898: else
899: {
900: int fontHeight = 30 * pSettings->scalingFactorHiDPI;
901: widgetFont = CreateFont(fontHeight, 0, 0, 0, FW_NORMAL, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Segoe UI");
902: }
903:
904: oldFont = SelectObject(cachedBackground, widgetFont);
905: SetBkMode(cachedBackground, TRANSPARENT);
906: SIZE size;
907: GetTextExtentPoint32W(cachedBackground, CurrentGreeting, wcslen(CurrentGreeting), &size);
908: pSettings->desktopGreetingPlacement.width = size.cx;
909: pSettings->desktopGreetingPlacement.height = size.cy;
910: SelectObject(cachedBackground, oldFont);
911:
912: if (pSettings->desktopGreetingPlacement.placement != PLACEMENT_DEFAULT)
913: {
914: pSettings->PositionFromPlacement(&pSettings->desktopGreetingPlacement);
915: CheckWidgetPlacement(&pSettings->desktopGreetingPlacement);
916: SetRect(&greetingRect, pSettings->desktopGreetingPlacement.x, pSettings->desktopGreetingPlacement.y, pSettings->desktopGreetingPlacement.x + pSettings->desktopGreetingPlacement.width, pSettings->desktopGreetingPlacement.y + pSettings->desktopGreetingPlacement.height);
917: }
918: else // PLACEMENT_DEFAULT
919: {
920: greetingRect.left = DesktopCenterX - (pSettings->desktopGreetingPlacement.width / 2);
921: greetingRect.right = greetingRect.left + pSettings->desktopGreetingPlacement.width;
922: if (pSettings->desktopGreetingFontDefined) greetingRect.top = 339;
923: else greetingRect.top = 320;
924: greetingRect.bottom = greetingRect.top + pSettings->desktopGreetingPlacement.height;
925: }
926:
927: HBITMAP widgetBitmap = CreateCompatibleBitmap(cachedBackground, pSettings->desktopGreetingPlacement.width, pSettings->desktopGreetingPlacement.height);
928: HGDIOBJ oldBitmap = SelectObject(widgetDC, widgetBitmap);
929:
930: oldFont = SelectObject(widgetDC, widgetFont);
931: SetBkMode(widgetDC, TRANSPARENT);
932: RECT widgetRect = { 0, 0, pSettings->desktopGreetingPlacement.width, pSettings->desktopGreetingPlacement.height };
933: FillRect(widgetDC, &widgetRect, blackBrush);
934: DrawTextWithEffectsUnicode(widgetDC, widgetRect, CurrentGreeting, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE, 0xffffff, false, 0, false, 0, 0, 0);
935: AlphaFromRGB(widgetDC, widgetRect, 0, 255, false, 0); // Note: The widget bitmap's per-pixel alpha is 0 by default -> We need to calculate this for AlphaBlend() below to work!
936: SelectObject(widgetDC, oldFont);
937:
938: BLENDFUNCTION bf;
939: ZeroMemory(&bf, sizeof(BLENDFUNCTION));
940: bf.BlendOp = AC_SRC_OVER;
941: bf.BlendFlags = 0;
942: bf.SourceConstantAlpha = pSettings->desktopGreetingTransparencyAlpha;
943: bf.AlphaFormat = AC_SRC_ALPHA;
944: AlphaBlend(cachedBackground, greetingRect.left, greetingRect.top, pSettings->desktopGreetingPlacement.width, pSettings->desktopGreetingPlacement.height, widgetDC, 0, 0, pSettings->desktopGreetingPlacement.width, pSettings->desktopGreetingPlacement.height, bf);
945:
946: SelectObject(widgetDC, oldBitmap);
947: DeleteObject(widgetBitmap);
948: // DeleteObject(oldBitmap); // Not necessary (default stock object)
949: DeleteObject(widgetFont);
950: // DeleteObject(oldFont); // Not necessary (default stock object)
951: }
952: else // if (!pSettings->desktopDisableGDIplusForWidgets) -> Use GDI+ to draw the widget
953: {
954: Graphics graphics(cachedBackground);
955: graphics.SetCompositingMode(CompositingModeSourceOver);
956: graphics.SetCompositingQuality(CompositingQualityHighQuality);
957:
958: wchar_t fontFamilyWC[MAX_PATH];
959: MultiByteToWideChar(CP_ACP, 0, pSettings->desktopGreetingFont, (strnlen_s(pSettings->desktopGreetingFont, sizeofArray(pSettings->desktopGreetingFont))+1), fontFamilyWC, sizeofArray(fontFamilyWC));
960: FontFamily fontFamily(fontFamilyWC);
961: FontStyle fontStyle;
962: if (pSettings->desktopGreetingFontWeight == FW_BOLD) fontStyle = FontStyleBold;
963: else fontStyle = FontStyleRegular;
964: Font font(&fontFamily, (Gdiplus::REAL)(pSettings->desktopGreetingFontHeight*pSettings->scalingFactorHiDPI), fontStyle, UnitPixel);
965:
966: StringFormat stringFormat;
967: stringFormat.SetAlignment(StringAlignmentNear);
968: stringFormat.SetLineAlignment(StringAlignmentNear);
969: stringFormat.SetTrimming(StringTrimmingNone);
970:
971: PointF originPoint(0.0f, 0.0f);
972: RectF boundingRect;
973: graphics.MeasureString(CurrentGreeting, wcsnlen_s(CurrentGreeting, sizeofArray(CurrentGreeting)), &font, originPoint, &stringFormat, &boundingRect);
974: pSettings->desktopGreetingPlacement.width = (int)boundingRect.Width;
975: pSettings->desktopGreetingPlacement.height = (int)boundingRect.Height;
976:
977: if (pSettings->desktopGreetingPlacement.placement != PLACEMENT_DEFAULT)
978: {
979: pSettings->PositionFromPlacement(&pSettings->desktopGreetingPlacement);
980: CheckWidgetPlacement(&pSettings->desktopGreetingPlacement);
981: SetRect(&greetingRect, pSettings->desktopGreetingPlacement.x, pSettings->desktopGreetingPlacement.y, pSettings->desktopGreetingPlacement.x + pSettings->desktopGreetingPlacement.width, pSettings->desktopGreetingPlacement.y + pSettings->desktopGreetingPlacement.height);
982: }
983: else // PLACEMENT_DEFAULT
984: {
985: greetingRect.left = DesktopCenterX - (pSettings->desktopGreetingPlacement.width / 2);
986: greetingRect.right = greetingRect.left + pSettings->desktopGreetingPlacement.width;
987: if (pSettings->desktopGreetingFontDefined) greetingRect.top = 339;
988: else greetingRect.top = 320;
989: greetingRect.bottom = greetingRect.top + pSettings->desktopGreetingPlacement.height;
990: }
991:
992: PointF pointF((Gdiplus::REAL)pSettings->desktopGreetingPlacement.x, (Gdiplus::REAL)pSettings->desktopGreetingPlacement.y);
993: SolidBrush brush(Color(pSettings->desktopGreetingTransparencyAlpha, GetRValue(pSettings->desktopGreetingColor), GetGValue(pSettings->desktopGreetingColor), GetBValue(pSettings->desktopGreetingColor)));
994: graphics.SetTextRenderingHint(TextRenderingHintAntiAlias); // (Note: Tried TextRenderingHintClearTypeGridFit/TextRenderingHintAntiAliasGridFit/etc here but for some reason it looked really ugly, so sticking with TextRenderingHintAntiAlias for now.)
995: graphics.DrawString(CurrentGreeting, -1, &font, pointF, &brush);
996: }
997: }
998:
999: //====================
1000:
1001: if (pSettings->desktopDisableGDIplusForWidgets)
1002: {
1003: SetRect(&r, 0, 0, DesktopWidth, DesktopHeight);
1004: AlphaFromRGB(cachedBackground, r, 1, 255, true, pSettings->desktopWidgetColor);
1005: AlphaApply(cachedBackground, r);
1006: }
1007:
1008: //====================
1009:
1010: DeleteObject(blackBrush);
1011: DeleteDC(widgetDC);
1012: if (bufbmp) DeleteObject(bufbmp);
1013:
1014: POINT pt;
1015: pt.x = DesktopX, pt.y = DesktopY;
1016: POINT ptSrc;
1017: ptSrc.x = 0, ptSrc.y = 0;
1018:
1019: BLENDFUNCTION bf;
1020: bf.BlendOp = AC_SRC_OVER;
1021: bf.BlendFlags = 0;
1022: bf.AlphaFormat = AC_SRC_ALPHA;
1023: bf.SourceConstantAlpha = (unsigned char)255;
1024:
1025: SIZE windowSize = { DesktopWidth, DesktopHeight };
1026: BOOL result = UpdateLayeredWindow(hDesktopWnd, NULL, &pt, &windowSize, cachedBackground, &ptSrc, 0, &bf, ULW_ALPHA);
1027: /*
1028: if (!cachedBackground) SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_INFORMATION_MESSAGE, (LPARAM)"xoblite -> Desktop -> Invalid cachedBackground!");
1029:
1030: if (result == 0)
1031: {
1032: int error = GetLastError();
1033: char msg[255];
1034: sprintf_s(msg, sizeofArray(msg), "xoblite -> Desktop -> UpdateLayeredWindow failed! [error code %d]", error);
1035: SendMessage(GetBBWnd(), BB_CONSOLEMESSAGE, (WPARAM)CONSOLE_ERROR_MESSAGE, (LPARAM)msg);
1036: }
1037: else
1038: */
1039: {
1040: // Finally, we move the desktop window to the bottom of the regular z-order... (i.e. just above the Windows desktop in our case)
1041: SetWindowPos(hDesktopWnd, GetDesktopWindow(), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOOWNERZORDER);
1042: }
1043: }
1044:
1045: //===========================================================================
1046: // Function: GetDimensions
1047: // Purpose: Get the dimensions of the available desktop space ("work area")
1048: //===========================================================================
1049:
1050: void Desktop::GetDimensions()
1051: {
1052: /*
1053: if (!pSettings->explorerHidden)
1054: {
1055: RECT workArea;
1056: SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&workArea, SPIF_SENDCHANGE);
1057: DesktopWidth = workArea.right - workArea.left;
1058: DesktopHeight = workArea.bottom - workArea.top;
1059: DesktopX = workArea.left;
1060: DesktopY = workArea.top;
1061: }
1062: else
1063: */
1064: {
1065: DesktopWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
1066: DesktopHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
1067: DesktopX = GetSystemMetrics(SM_XVIRTUALSCREEN);
1068: DesktopY = GetSystemMetrics(SM_YVIRTUALSCREEN);
1069: }
1070:
1071: DesktopCenterX = DesktopX + (DesktopWidth / 2);
1072: DesktopCenterY = DesktopY + (DesktopHeight / 2);
1073: }
1074:
1075: //===========================================================================
1076: // Function: LoadDesktopWallpaper
1077: // Purpose: ...
1078: //===========================================================================
1079:
1080: void Desktop::LoadWallpaper(LPSTR imagePath)
1081: {
1082: if (!FileExists(imagePath)) return;
1083:
1084: if (wallpaper.bitmap != NULL) DeleteObject(wallpaper.bitmap);
1085:
1086: // Load the image using GDI+ to add support for JPG, PNG etc...
1087: // (nb. regular GDI LoadImage() only supports BMP images. GDI+ startup/shutdown is performed "globally" in Blackbox.cpp.)
1088: wchar_t imagePathWC[MAX_PATH];
1089: MultiByteToWideChar(CP_ACP, 0, imagePath, strlen(imagePath) + 1, imagePathWC, MAX_PATH);
1090: Bitmap* bitmap = new Bitmap(imagePathWC, FALSE);
1091:
1092: // ...fetch some of its properties...
1093: wallpaper.width = bitmap->GetWidth();
1094: wallpaper.height = bitmap->GetHeight();
1095: PixelFormat pf = bitmap->GetPixelFormat();
1096: if ((pf == PixelFormat32bppARGB) || (pf == PixelFormat32bppPARGB) || (pf == PixelFormat64bppARGB) || (pf == PixelFormat64bppPARGB)) wallpaper.argb = true;
1097: else wallpaper.argb = false;
1098:
1099: // ...and finally create our target GDI HBITMAP from the loaded GDI+ Bitmap...
1100: bitmap->GetHBITMAP(0, &wallpaper.bitmap);
1101:
1102: delete bitmap;
1103: }
1104:
1105: //===========================================================================
1106: // Function: DrawWallpaper
1107: // Purpose: ...
1108: //===========================================================================
1109:
1110: void Desktop::DrawWallpaper(HDC hdc)
1111: {
1112: if (wallpaper.bitmap == NULL) return;
1113:
1114: HDC buf = CreateCompatibleDC(hdc);
1115: DeleteObject(SelectObject(buf, wallpaper.bitmap));
1116: SetStretchBltMode(hdc, HALFTONE);
1117: StretchBlt(hdc, 0, 0, DesktopWidth, DesktopHeight, buf, 0, 0, wallpaper.width, wallpaper.height, SRCCOPY);
1118:
1119: DeleteDC(buf);
1120: }
1121:
1122: //===========================================================================
1123: // Function: CheckWidgetPlacement
1124: // Purpose: Adjusts desktop widget placements to account for various factors
1125: //===========================================================================
1126:
1127: void Desktop::CheckWidgetPlacement(Placement* widget)
1128: {
1129: // Adjust from screen relative to desktop relative coordinates...
1130: // (cf. e.g. Explorer taskbar along left or top screen edges)
1131: widget->x -= DesktopX;
1132: widget->y -= DesktopY;
1133:
1134: // Adjust for the semi-transparent desktop border... (if visible)
1135: if (pSettings->desktopBorderHidden) return;
1136:
1137: if ((widget->placement == PLACEMENT_TOP_LEFT)
1138: || (widget->placement == PLACEMENT_CENTER_LEFT)
1139: || (widget->placement == PLACEMENT_BOTTOM_LEFT))
1140: {
1141: widget->x += 20;
1142: }
1143:
1144: if ((widget->placement == PLACEMENT_TOP_RIGHT)
1145: || (widget->placement == PLACEMENT_CENTER_RIGHT)
1146: || (widget->placement == PLACEMENT_BOTTOM_RIGHT))
1147: {
1148: widget->x -= 20;
1149: }
1150:
1151: if ((widget->placement == PLACEMENT_TOP_LEFT)
1152: || (widget->placement == PLACEMENT_TOP_RIGHT))
1153: {
1154: widget->y += 20;
1155: }
1156:
1157: if ((widget->placement == PLACEMENT_TOP_CENTER) && !pSettings->desktopWorkspaceIndicatorHidden)
1158: {
1159: // Stay clear of the workspace indicators if they're visible...
1160: widget->y += 39;
1161: }
1162:
1163: if ((widget->placement == PLACEMENT_BOTTOM_LEFT)
1164: || (widget->placement == PLACEMENT_BOTTOM_CENTER)
1165: || (widget->placement == PLACEMENT_BOTTOM_RIGHT))
1166: {
1167: widget->y -= 20;
1168: }
1169: }
1170:
1171: /*
1172: //===========================================================================
1173: // Function: UpdatePosition
1174: // Purpose: ...
1175: //===========================================================================
1176:
1177: void Desktop::UpdatePosition()
1178: {
1179: // Fetch the new size and position parameters for our window...
1180: GetDimensions();
1181:
1182: // Resize the window and move it to its new position...
1183: // MoveWindow(hDesktopWnd, DesktopX, DesktopY, DesktopWidth, DesktopHeight, true);
1184: // SetWindowPos(hDesktopWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER);
1185:
1186: // Apply transparency depending on xoblite.rc setting...
1187: // SetTransparency(hDesktopWnd, pSettings->desktopTransparencyAlpha);
1188:
1189: // Update the desktop window...
1190: UpdateDesktopWindow();
1191: }
1192: */
1193:
1194: //===========================================================================
1195: // Function: MouseAndDropHandler
1196: // Purpose: Supporting function to DesktopWndProc() [see above]
1197: //===========================================================================
1198:
1199: LRESULT Desktop::MouseAndDropHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1200: {
1201: switch (uMsg)
1202: {
1203: //====================
1204:
1205: case WM_LBUTTONUP:
1206: {
1207: if (pMenuCommon)
1208: {
1209: // if (GetAsyncKeyState(VK_MENU) & 0x8000)
1210: // {
1211: // SendMessage(GetBBWnd(), BB_MENU, 0, 0); // Alt+LeftClick -> Main menu
1212: // PlaySoundFX(SFX_MENU_SHOW);
1213: // }
1214: // else
1215: {
1216: // First we set focus to the toolbar to prevent the focus from going back to the previous task after the menu is closed...
1217: // if (pSettings->followActive) SetForegroundWindow(pToolbar->hToolbarWnd);
1218: // if (pSettings->followActive) SetForegroundWindow(hDesktopWnd);
1219: // Move the desktop window to the bottom of the application z-order... (i.e. just above the Windows desktop in our case)
1220: // SetWindowPos(hDesktopWnd, GetDesktopWindow(), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOOWNERZORDER);
1221: SetWindowPos(hDesktopWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOOWNERZORDER);
1222:
1223: if (pMenuCommon) pMenuCommon->Hide();
1224: }
1225: }
1226: }
1227: break;
1228:
1229: case WM_LBUTTONDOWN: { } break;
1230:
1231: //====================
1232:
1233: case WM_RBUTTONUP:
1234: {
1235: // Has the user defined an Right Click command override?
1236: if (strnlen_s(pSettings->desktopRClickOverride, sizeofArray(pSettings->desktopRClickOverride)) > 0)
1237: {
1238: if (GetAsyncKeyState(VK_MENU) & 0x8000)
1239: {
1240: SendMessage(GetBBWnd(), BB_MENU, 0, 0); // Alt+Right Click -> Main menu
1241: PlaySoundFX(SFX_MENU_SHOW);
1242: }
1243: else BBSmartExecute(pSettings->desktopRClickOverride); // Right Click without modifiers -> User defined command override
1244: }
1245:
1246: // We otherwise provide a couple of Right Click + key modifier actions for people with only a *2* button mouse...
1247: else if (GetAsyncKeyState(VK_MENU) & 0x8000)
1248: {
1249: if ((GetAsyncKeyState(VK_CONTROL) & 0x8000) && (GetAsyncKeyState(VK_SHIFT) & 0x8000))
1250: {
1251: SendMessage(GetBBWnd(), BB_MENU, 2, 0); // Ctrl+Alt+Shift+RightClick -> Configuration menu
1252: PlaySoundFX(SFX_MENU_SHOW);
1253: }
1254: else if (GetAsyncKeyState(VK_CONTROL) & 0x8000) BBSmartExecute("@xoblite Random Wallpaper"); // Ctrl+Alt+RightClick -> Apply random wallpaper (note: matching Modifier+MidClick)
1255: else if (GetAsyncKeyState(VK_SHIFT) & 0x8000) BBSmartExecute("@xoblite Random Style"); // Shift+Alt+RightClick -> Apply random style (note: matching Modifier+MidClick)
1256: else
1257: {
1258: SendMessage(GetBBWnd(), BB_MENU, 5, 0); // Alt+RightClick -> Styles menu
1259: PlaySoundFX(SFX_MENU_SHOW);
1260: }
1261: }
1262: else if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
1263: {
1264: SendMessage(GetBBWnd(), BB_MENU, 6, 0); // Ctrl+RightClick -> Themes menu
1265: PlaySoundFX(SFX_MENU_SHOW);
1266: }
1267: else if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
1268: {
1269: SendMessage(GetBBWnd(), BB_MENU, 1, 0); // Shift+RightClick -> Workspaces menu
1270: PlaySoundFX(SFX_MENU_SHOW);
1271: // BBSmartExecute("@xoblite Random Wallpaper"); // Shift+RightClick -> Apply random wallpaper
1272: }
1273: else
1274: {
1275: SendMessage(GetBBWnd(), BB_MENU, 0, 0); // RightClick without modifiers -> Main menu
1276: PlaySoundFX(SFX_MENU_SHOW);
1277: }
1278: }
1279: break;
1280:
1281: case WM_RBUTTONDOWN: { } break;
1282:
1283: //====================
1284:
1285: case WM_MBUTTONUP:
1286: {
1287: // Has the user defined an Mid Click command override?
1288: if (strnlen_s(pSettings->desktopMClickOverride, sizeofArray(pSettings->desktopMClickOverride)) > 0)
1289: {
1290: if (GetAsyncKeyState(VK_MENU) & 0x8000)
1291: {
1292: SendMessage(GetBBWnd(), BB_MENU, 5, 0); // Alt+Mid Click -> Styles menu
1293: PlaySoundFX(SFX_MENU_SHOW);
1294: }
1295: else BBSmartExecute(pSettings->desktopMClickOverride); // Mid Click without modifiers -> User defined command override
1296: }
1297:
1298: // We otherwise provide a couple of Mid Click + key modifier actions for people with only a *3* button mouse...
1299: else if (GetAsyncKeyState(VK_MENU) & 0x8000)
1300: {
1301: if (GetAsyncKeyState(VK_CONTROL) & 0x8000) BBSmartExecute("@xoblite Random Wallpaper"); // Ctrl+Alt+MidClick -> Apply random wallpaper (note: matching Modifier+RightClick)
1302: else if (GetAsyncKeyState(VK_SHIFT) & 0x8000) BBSmartExecute("@xoblite Random Style"); // Shift+Alt+MidClick -> Apply random style (note: matching Modifier+RightClick)
1303: else
1304: {
1305: SendMessage(GetBBWnd(), BB_MENU, 6, 0); // Alt+MidClick -> Themes menu
1306: PlaySoundFX(SFX_MENU_SHOW);
1307: }
1308: }
1309: else
1310: {
1311: if (GetAsyncKeyState(VK_CONTROL) & 0x8000) SendMessage(GetBBWnd(), BB_MENU, 1, 0); // Ctrl+MidClick -> Workspaces menu
1312: else if (GetAsyncKeyState(VK_SHIFT) & 0x8000) SendMessage(GetBBWnd(), BB_MENU, 2, 0); // Shift+MidClick -> Configuration menu
1313: else SendMessage(GetBBWnd(), BB_MENU, 5, 0); // MidClick without modifiers -> Styles menu
1314: PlaySoundFX(SFX_MENU_SHOW);
1315: }
1316: }
1317: break;
1318:
1319: case WM_MBUTTONDOWN: { } break;
1320:
1321: //====================
1322:
1323: case WM_XBUTTONDOWN:
1324: {
1325: if (HIWORD(wParam) == XBUTTON1)
1326: {
1327: if (strnlen_s(pSettings->desktopX1ClickOverride, sizeofArray(pSettings->desktopX1ClickOverride)) > 0) // Has the user defined an X1 click command override?
1328: {
1329: if (GetAsyncKeyState(VK_MENU) & 0x8000)
1330: {
1331: SendMessage(GetBBWnd(), BB_MENU, 6, 0); // Alt+X1 Click -> Themes menu
1332: PlaySoundFX(SFX_MENU_SHOW);
1333: }
1334: else BBSmartExecute(pSettings->desktopX1ClickOverride); // X1 Click without modifiers -> User defined command override
1335: }
1336: else if (GetAsyncKeyState(VK_MENU) & 0x8000) BBSmartExecute("@xoblite Random Wallpaper"); // Alt+X1 Click -> Apply random wallpaper
1337: else
1338: {
1339: // if (GetAsyncKeyState(VK_CONTROL) & 0x8000) BBSmartExecute("@xoblite About"); // Placeholder, possibly for use defined command?
1340: if (GetAsyncKeyState(VK_SHIFT) & 0x8000) SendMessage(GetBBWnd(), BB_MENU, 2, 0); // Shift+X1 Click -> Configuration menu
1341: else SendMessage(GetBBWnd(), BB_MENU, 6, 0); // X1 Click without modifiers -> Themes menu
1342: PlaySoundFX(SFX_MENU_SHOW);
1343: }
1344: }
1345: else if (HIWORD(wParam) == XBUTTON2)
1346: {
1347: if (strnlen_s(pSettings->desktopX2ClickOverride, sizeofArray(pSettings->desktopX2ClickOverride)) > 0) // Has the user defined an X2 click command override?
1348: {
1349: if (GetAsyncKeyState(VK_MENU) & 0x8000)
1350: {
1351: SendMessage(GetBBWnd(), BB_MENU, 1, 0); // Alt+X2 Click -> Workspaces menu
1352: PlaySoundFX(SFX_MENU_SHOW);
1353: }
1354: else BBSmartExecute(pSettings->desktopX2ClickOverride); // X2 Click without modifiers -> User defined command override
1355: }
1356: else if (GetAsyncKeyState(VK_MENU) & 0x8000) BBSmartExecute("@xoblite Random Style"); // Alt+X2 Click -> Apply random style
1357: else
1358: {
1359: // if (GetAsyncKeyState(VK_CONTROL) & 0x8000) BBSmartExecute("@xoblite About"); // Placeholder, possibly for use defined command?
1360: if (GetAsyncKeyState(VK_SHIFT) & 0x8000) SendMessage(GetBBWnd(), BB_MENU, 2, 0); // Shift+X2 Click -> Configuration menu
1361: else SendMessage(GetBBWnd(), BB_MENU, 1, 0); // X2 Click without modifiers -> Workspaces menu
1362: PlaySoundFX(SFX_MENU_SHOW);
1363: }
1364: }
1365: }
1366: break;
1367:
1368: case WM_XBUTTONUP: { } break;
1369:
1370: //====================
1371:
1372: case WM_MOUSEWHEEL:
1373: {
1374: if (pSettings->mousewheelChanging)
1375: {
1376: if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) pWorkspaces->NextWorkspace(); // Mousewheel down -> Move to the next workspace...
1377: else pWorkspaces->PreviousWorkspace(); // Mousewheel up -> Move to the previous workspace...
1378: }
1379: }
1380: break;
1381:
1382: // case WM_MOUSEHWHEEL: { } break;
1383:
1384: //====================
1385:
1386: case WM_DROPFILES:
1387: {
1388: return ParseDropFiles(hWnd, wParam);
1389: }
1390: break;
1391:
1392: //====================
1393:
1394: default:
1395: return DefWindowProc(hWnd, uMsg, wParam, lParam);
1396:
1397: //====================
1398: }
1399: return 0;
1400: }
1401:
1402: //===========================================================================
1403:
1404: bool Desktop::GetClockText(bool forceUpdate)
1405: {
1406: time(&systemTime);
1407: localtime_s(&localTime, &systemTime);
1408: currentHour = localTime.tm_hour;
1409: currentMinute = localTime.tm_min;
1410:
1411: // Only update the desktop clock if the current minute has changed...
1412: if ((currentMinute != displayedMinute) || forceUpdate)
1413: {
1414: // Get the time string for the regular clock...
1415: if (IsInString(pSettings->toolbarClockFormat, "%I")) wcsftime(CurrentTime, sizeof(CurrentTime), L"%I:%M %p", &localTime); // 12-hour format including AM/PM indicator
1416: else wcsftime(CurrentTime, sizeof(CurrentTime), L"%H:%M", &localTime); // 24-hour format
1417: // Remember minute shown in display, now wait until minute changes before updating again...
1418: displayedMinute = currentMinute;
1419:
1420: // Get the weekday string...
1421: wcsftime(CurrentWeekday, sizeof(CurrentWeekday), L"%A", &localTime); // Day of the week as per the current locale (e.g. "Saturday")
1422:
1423: // Get the configurable date string... (nb. we need to convert the format string from ANSI to Unicode first)
1424: wchar_t desktopDateFormatInUnicode[MAX_LINE_LENGTH];
1425: MultiByteToWideChar(CP_UTF8, 0, pSettings->desktopDateFormat, strnlen_s(pSettings->desktopDateFormat, sizeofArray(pSettings->desktopDateFormat))+1, desktopDateFormatInUnicode, MAX_LINE_LENGTH);
1426: wcsftime(CurrentDate, sizeof(CurrentDate), desktopDateFormatInUnicode, &localTime); // Configurable date string (cf. the toolbar clock's format string and related options)
1427:
1428: if (!forceUpdate)
1429: {
1430: if (currentMinute == 0) PlaySoundFX(SFX_CLOCK_HOUR);
1431: else if (currentMinute == 30) PlaySoundFX(SFX_CLOCK_HALFHOUR);
1432: }
1433:
1434: return true;
1435: }
1436: else return false;
1437: }
1438:
1439: //===========================================================================
1440:
1441: bool Desktop::GetGreetingText(bool forceUpdate)
1442: {
1443: time(&systemTime);
1444: localtime_s(&localTime, &systemTime);
1445:
1446: if (localTime.tm_hour < 6) wcscpy_s(CurrentGreeting, sizeofArray(CurrentGreeting), pSettings->desktopGreetingNight); // 00-06 -> "Good night" (default)
1447: else if (localTime.tm_hour < 12) wcscpy_s(CurrentGreeting, sizeofArray(CurrentGreeting), pSettings->desktopGreetingMorning); // 06-12 -> "Good morning" (default)
1448: else if (localTime.tm_hour < 18) wcscpy_s(CurrentGreeting, sizeofArray(CurrentGreeting), pSettings->desktopGreetingAfternoon); // 12-18 -> "Good afternoon" (default)
1449: else wcscpy_s(CurrentGreeting, sizeofArray(CurrentGreeting), pSettings->desktopGreetingEvening); // 18-24 -> "Good evening" (default)
1450:
1451: return true;
1452: }
1453:
1454: //===========================================================================
1455: // Function: FullscreenDetected
1456: // Purpose: Hide the desktop (no updating) if an application goes fullscreen
1457: //===========================================================================
1458:
1459: void Desktop::FullscreenDetected(bool fullscreen)
1460: {
1461: if (fullscreen)
1462: {
1463: fullscreenDetected = true;
1464: ShowWindow(hDesktopWnd, SW_HIDE);
1465: }
1466: else
1467: {
1468: fullscreenDetected = false;
1469: ShowWindow(hDesktopWnd, SW_SHOWNOACTIVATE);
1470: UpdateDesktopWindow();
1471: }
1472: }
1473:
1474: //===========================================================================
1475: // Function: ShadeWindow
1476: // Purpose: Toggles a window shaded/unshaded
1477: // (see also supporting functions below)
1478: //===========================================================================
1479:
1480: void Desktop::ShadeWindow()
1481: {
1482: HWND currentWnd = pTaskbar->GetActiveWindow();
1483: BOOL isMaximized = IsZoomed(currentWnd);
1484:
1485: RECT r;
1486: GetWindowRect(currentWnd, &r);
1487: int titleBarHeight = GetSystemMetrics(SM_CYMIN);
1488: bool isUnshaded = (r.bottom-r.top > titleBarHeight);
1489:
1490: // i = IsFlaggedShaded(currentWnd);
1491: int i = (int)(GetProp(currentWnd, "BBNormalHeight") != NULL);
1492:
1493: if (isMaximized)
1494: {
1495: if (isUnshaded) WinShade(currentWnd, -1, isMaximized);
1496: else WinUnshade(currentWnd, -1, isMaximized);
1497: }
1498: else
1499: {
1500: if (i != -1) // Was the window found in our list?
1501: {
1502: if (isUnshaded) // Shade the window...
1503: {
1504: RemoveProp(currentWnd, "BBNormalHeight");
1505: WinShade(currentWnd, -1, isMaximized);
1506: }
1507: else // Unshade the window...
1508: {
1509: WinUnshade(currentWnd, i, isMaximized);
1510: }
1511: }
1512: else WinShade(currentWnd, i, isMaximized);
1513: }
1514: }
1515:
1516: //===========================================================================
1517: // Function: WinShade
1518: // Purpose: Shrink a window to only its titlebar,
1519: // while remembering its original height
1520: //===========================================================================
1521:
1522: void Desktop::WinShade(HWND hwnd, int listIndex, bool maximized)
1523: {
1524: if (listIndex != -1) return;
1525:
1526: RECT r;
1527: SetForegroundWindow(hwnd);
1528: GetWindowRect(hwnd, &r);
1529:
1530: // Remember the window's original height...
1531: if (!maximized) SetProp(hwnd, "BBNormalHeight", (HANDLE)(r.bottom - r.top));
1532: // ...and then shrink it vertically to only show its titlebar...
1533: SetWindowPos(hwnd, HWND_TOP, r.left, r.top, r.right-r.left, GetSystemMetrics(SM_CYMIN), SWP_NOOWNERZORDER | SWP_NOSENDCHANGING);
1534: }
1535:
1536: //===========================================================================
1537: // Function: WinUnshade
1538: // Purpose: Restores a previously shaded window to its original height
1539: //===========================================================================
1540:
1541: void Desktop::WinUnshade(HWND hwnd, int listIndex, bool maximized)
1542: {
1543: if (listIndex == -1 && !maximized) return;
1544:
1545: RECT r;
1546: int height;
1547: SetForegroundWindow(hwnd);
1548:
1549: if (maximized)
1550: {
1551: // This is a maximized window, so its restored height should be the height of the screen's work area...
1552: RECT workArea;
1553: SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&workArea, SPIF_SENDCHANGE);
1554: height = workArea.bottom - workArea.top;
1555: }
1556: else
1557: {
1558: // This is a non-maximized window, so we restore it to the original height previously saved...
1559: height = (long)GetProp(hwnd, "BBNormalHeight");
1560: RemoveProp(hwnd, "BBNormalHeight");
1561: }
1562:
1563: if (height == 0) return;
1564:
1565: GetWindowRect(hwnd, &r);
1566: MoveWindow(hwnd, r.left, r.top, r.right-r.left, height, true);
1567: InvalidateRect(hwnd, NULL, false);
1568: }
1569:
1570: //===========================================================================
1571: // Functions: GrowWindowHeight / GrowWindowWidth
1572: // Purpose: ...
1573: //===========================================================================
1574:
1575: void Desktop::GrowWindowHeight()
1576: {
1577: HWND currentWnd = pTaskbar->GetActiveWindow();
1578: RECT r, workArea;
1579: GetWindowRect(currentWnd, &r);
1580: SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&workArea, SPIF_SENDCHANGE);
1581: MoveWindow(currentWnd, r.left, 0, r.right-r.left, workArea.bottom-workArea.top, true);
1582: InvalidateRect(currentWnd, NULL, false);
1583: SetForegroundWindow(currentWnd);
1584: }
1585:
1586: //====================
1587:
1588: void Desktop::GrowWindowWidth()
1589: {
1590: HWND currentWnd = pTaskbar->GetActiveWindow();
1591: RECT r, workArea;
1592: GetWindowRect(currentWnd, &r);
1593: SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&workArea, SPIF_SENDCHANGE);
1594: MoveWindow(currentWnd, 0, r.top, workArea.right-workArea.left, r.bottom-r.top, true);
1595: GetWindowRect(currentWnd, &r);
1596: SetForegroundWindow(currentWnd);
1597: }
1598:
1599: //===========================================================================
1600: // Functions: LowerWindow / RaiseWindow
1601: // Purpose: ...
1602: //===========================================================================
1603:
1604: void Desktop::LowerWindow()
1605: {
1606: HWND currentWnd = pTaskbar->GetActiveWindow();
1607:
1608: // First we fetch the bottom window...
1609: HWND bottomWnd = GetWindow(hBlackboxWnd, GW_HWNDLAST);
1610: HWND hProgManWnd = FindWindow("Progman", "Program Manager");
1611: if (bottomWnd == hProgManWnd) bottomWnd = GetWindow(bottomWnd, GW_HWNDPREV);
1612:
1613: while (!IsWindowVisible(bottomWnd) || IsIconic(bottomWnd) || (GetWindowTextLength(bottomWnd) == 0))
1614: {
1615: bottomWnd = GetWindow(bottomWnd, GW_HWNDPREV);
1616: if (bottomWnd == hProgManWnd) bottomWnd = GetWindow(bottomWnd, GW_HWNDPREV);
1617: }
1618:
1619: // ...and then we put the current window behind that...
1620: if (IsWindow(currentWnd) && IsWindow(bottomWnd))
1621: {
1622: SetWindowPos(currentWnd, bottomWnd, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING);
1623: }
1624: }
1625:
1626: //====================
1627:
1628: void Desktop::RaiseWindow()
1629: {
1630: HWND currentWnd = pTaskbar->GetActiveWindow();
1631: // Raise the current window to the top...
1632: SetWindowPos(currentWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING);
1633: }
1634:
1635: //===========================================================================
1636: