1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS uxtheme.dll
4 * FILE: dll/win32/uxtheme/nonclient.c
5 * PURPOSE: uxtheme non client area management
6 * PROGRAMMER: Giannis Adamopoulos
7 */
8
9 #include "uxthemep.h"
10
11 #define NC_PREVIEW_MSGBOX_HALF_WIDTH 75
12 #define NC_PREVIEW_MSGBOX_OFFSET_X -29
13 #define NC_PREVIEW_MSGBOX_OFFSET_Y 71
14
15 static BOOL
IsWindowActive(HWND hWnd,DWORD ExStyle)16 IsWindowActive(HWND hWnd, DWORD ExStyle)
17 {
18 BOOL ret;
19
20 if (ExStyle & WS_EX_MDICHILD)
21 {
22 ret = IsChild(GetForegroundWindow(), hWnd);
23 if (ret)
24 ret = (hWnd == (HWND)SendMessageW(GetParent(hWnd), WM_MDIGETACTIVE, 0, 0));
25 }
26 else
27 {
28 ret = (GetForegroundWindow() == hWnd);
29 }
30
31 return ret;
32 }
33
34 BOOL
IsScrollBarVisible(HWND hWnd,INT hBar)35 IsScrollBarVisible(HWND hWnd, INT hBar)
36 {
37 SCROLLBARINFO sbi = {sizeof(SCROLLBARINFO)};
38
39 if (!GetScrollBarInfo(hWnd, hBar, &sbi))
40 return FALSE;
41
42 return !(sbi.rgstate[0] & STATE_SYSTEM_OFFSCREEN);
43 }
44
45 static BOOL
UserHasWindowEdge(DWORD Style,DWORD ExStyle)46 UserHasWindowEdge(DWORD Style, DWORD ExStyle)
47 {
48 if (Style & WS_MINIMIZE)
49 return TRUE;
50 if (ExStyle & WS_EX_DLGMODALFRAME)
51 return TRUE;
52 if (ExStyle & WS_EX_STATICEDGE)
53 return FALSE;
54 if (Style & WS_THICKFRAME)
55 return TRUE;
56 Style &= WS_CAPTION;
57 if (Style == WS_DLGFRAME || Style == WS_CAPTION)
58 return TRUE;
59 return FALSE;
60 }
61
62 static HICON
UserGetWindowIcon(PDRAW_CONTEXT pcontext)63 UserGetWindowIcon(PDRAW_CONTEXT pcontext)
64 {
65 HICON hIcon = NULL;
66
67 SendMessageTimeout(pcontext->hWnd, WM_GETICON, ICON_SMALL2, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
68
69 if (!hIcon)
70 SendMessageTimeout(pcontext->hWnd, WM_GETICON, ICON_SMALL, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
71
72 if (!hIcon)
73 SendMessageTimeout(pcontext->hWnd, WM_GETICON, ICON_BIG, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
74
75 if (!hIcon)
76 hIcon = (HICON)GetClassLongPtr(pcontext->hWnd, GCLP_HICONSM);
77
78 if (!hIcon)
79 hIcon = (HICON)GetClassLongPtr(pcontext->hWnd, GCLP_HICON);
80
81 // See also win32ss/user/ntuser/nonclient.c!NC_IconForWindow
82 if (!hIcon && !(pcontext->wi.dwExStyle & WS_EX_DLGMODALFRAME))
83 hIcon = LoadIconW(NULL, (LPCWSTR)IDI_WINLOGO);
84
85 return hIcon;
86 }
87
ThemeDrawCaptionText(PDRAW_CONTEXT pcontext,RECT * pRect,int iPartId,int iStateId)88 HRESULT WINAPI ThemeDrawCaptionText(PDRAW_CONTEXT pcontext, RECT* pRect, int iPartId, int iStateId)
89 {
90 HRESULT hr;
91 HFONT hFont = NULL;
92 HGDIOBJ oldFont = NULL;
93 LOGFONTW logfont;
94 COLORREF textColor;
95 COLORREF oldTextColor;
96 int align = CA_LEFT;
97 int drawStyles = DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS;
98
99 WCHAR buffer[50];
100 WCHAR *pszText = buffer;
101 INT len;
102
103 len = InternalGetWindowText(pcontext->hWnd, NULL, 0);
104 if (!len)
105 return S_OK;
106
107 len++; /* From now on this is the size of the buffer so include the null */
108
109 if (len > ARRAYSIZE(buffer))
110 {
111 pszText = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
112 if (!pszText)
113 return E_OUTOFMEMORY;
114 }
115
116 InternalGetWindowText(pcontext->hWnd, pszText, len);
117
118 hr = GetThemeSysFont(pcontext->theme, TMT_CAPTIONFONT, &logfont);
119 if (SUCCEEDED(hr))
120 hFont = CreateFontIndirectW(&logfont);
121
122 if (hFont)
123 oldFont = SelectObject(pcontext->hDC, hFont);
124
125 textColor = GetThemeSysColor(pcontext->theme, pcontext->Active ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT);
126
127 GetThemeEnumValue(pcontext->theme, iPartId, iStateId, TMT_CONTENTALIGNMENT, &align);
128 if (align == CA_CENTER)
129 drawStyles |= DT_CENTER;
130 else if (align == CA_RIGHT)
131 drawStyles |= DT_RIGHT;
132
133 oldTextColor = SetTextColor(pcontext->hDC, textColor);
134 DrawThemeText(pcontext->theme,
135 pcontext->hDC,
136 iPartId,
137 iStateId,
138 pszText,
139 len - 1,
140 drawStyles,
141 0,
142 pRect);
143 SetTextColor(pcontext->hDC, oldTextColor);
144
145 if (hFont)
146 {
147 SelectObject(pcontext->hDC, oldFont);
148 DeleteObject(hFont);
149 }
150 if (pszText != buffer)
151 {
152 HeapFree(GetProcessHeap(), 0, pszText);
153 }
154 return S_OK;
155 }
156
157 void
ThemeInitDrawContext(PDRAW_CONTEXT pcontext,HWND hWnd,HRGN hRgn)158 ThemeInitDrawContext(PDRAW_CONTEXT pcontext,
159 HWND hWnd,
160 HRGN hRgn)
161 {
162 pcontext->wi.cbSize = sizeof(pcontext->wi);
163 GetWindowInfo(hWnd, &pcontext->wi);
164 pcontext->hWnd = hWnd;
165 pcontext->Active = IsWindowActive(hWnd, pcontext->wi.dwExStyle);
166 pcontext->theme = GetNCCaptionTheme(hWnd, pcontext->wi.dwStyle);
167 pcontext->scrolltheme = GetNCScrollbarTheme(hWnd, pcontext->wi.dwStyle);
168
169 pcontext->CaptionHeight = pcontext->wi.cyWindowBorders;
170 pcontext->CaptionHeight += GetSystemMetrics(pcontext->wi.dwExStyle & WS_EX_TOOLWINDOW ? SM_CYSMCAPTION : SM_CYCAPTION );
171
172 if (hRgn <= (HRGN)1)
173 {
174 hRgn = CreateRectRgnIndirect(&pcontext->wi.rcWindow);
175 }
176 pcontext->hRgn = hRgn;
177
178 pcontext->hDC = GetDCEx(hWnd, hRgn, DCX_WINDOW | DCX_INTERSECTRGN | DCX_USESTYLE | DCX_KEEPCLIPRGN);
179 }
180
181 void
ThemeCleanupDrawContext(PDRAW_CONTEXT pcontext)182 ThemeCleanupDrawContext(PDRAW_CONTEXT pcontext)
183 {
184 ReleaseDC(pcontext->hWnd ,pcontext->hDC);
185
186 if (pcontext->hRgn != NULL)
187 {
188 DeleteObject(pcontext->hRgn);
189 }
190 }
191
192 static void
ThemeStartBufferedPaint(PDRAW_CONTEXT pcontext,int cx,int cy)193 ThemeStartBufferedPaint(PDRAW_CONTEXT pcontext, int cx, int cy)
194 {
195 HBITMAP hbmp;
196
197 pcontext->hDCScreen = pcontext->hDC;
198 pcontext->hDC = CreateCompatibleDC(pcontext->hDCScreen);
199 hbmp = CreateCompatibleBitmap(pcontext->hDCScreen, cx, cy);
200 pcontext->hbmpOld = (HBITMAP)SelectObject(pcontext->hDC, hbmp);
201 }
202
203 static void
ThemeEndBufferedPaint(PDRAW_CONTEXT pcontext,int x,int y,int cx,int cy)204 ThemeEndBufferedPaint(PDRAW_CONTEXT pcontext, int x, int y, int cx, int cy)
205 {
206 HBITMAP hbmp;
207 BitBlt(pcontext->hDCScreen, 0, 0, cx, cy, pcontext->hDC, x, y, SRCCOPY);
208 hbmp = (HBITMAP) SelectObject(pcontext->hDC, pcontext->hbmpOld);
209 DeleteObject(pcontext->hDC);
210 DeleteObject(hbmp);
211
212 pcontext->hDC = pcontext->hDCScreen;
213 }
214
ThemeCalculateCaptionButtonsPosEx(WINDOWINFO * wi,HWND hWnd,HTHEME htheme,INT buttonHeight)215 static void ThemeCalculateCaptionButtonsPosEx(WINDOWINFO* wi, HWND hWnd, HTHEME htheme, INT buttonHeight)
216 {
217 PWND_DATA pwndData;
218 DWORD style;
219 INT captionBtnWidth, captionBtnHeight, iPartId, i;
220 RECT rcCurrent;
221 SIZE ButtonSize;
222
223 /* First of all check if we have something to do here */
224 style = GetWindowLongW(hWnd, GWL_STYLE);
225 if ((style & (WS_CAPTION | WS_SYSMENU)) != (WS_CAPTION | WS_SYSMENU))
226 return;
227
228 /* Get theme data for this window */
229 pwndData = ThemeGetWndData(hWnd);
230 if (pwndData == NULL)
231 return;
232
233 if (!htheme)
234 {
235 htheme = GetNCCaptionTheme(hWnd, style);
236 if (!htheme)
237 return;
238 }
239
240 /* Calculate the area of the caption */
241 rcCurrent.top = rcCurrent.left = 0;
242 rcCurrent.right = wi->rcWindow.right - wi->rcWindow.left;
243 rcCurrent.bottom = wi->rcWindow.bottom - wi->rcWindow.top;
244
245 /* Add a padding around the objects of the caption */
246 InflateRect(&rcCurrent, -(int)wi->cyWindowBorders-BUTTON_GAP_SIZE,
247 -(int)wi->cyWindowBorders-BUTTON_GAP_SIZE);
248
249 iPartId = wi->dwExStyle & WS_EX_TOOLWINDOW ? WP_SMALLCLOSEBUTTON : WP_CLOSEBUTTON;
250
251 GetThemePartSize(htheme, NULL, iPartId, 0, NULL, TS_MIN, &ButtonSize);
252
253 captionBtnWidth = MulDiv(ButtonSize.cx, buttonHeight, ButtonSize.cy);
254
255 captionBtnHeight = buttonHeight - 4;
256 captionBtnWidth -= 4;
257
258 for (i = CLOSEBUTTON; i <= HELPBUTTON; i++)
259 {
260 SetRect(&pwndData->rcCaptionButtons[i],
261 rcCurrent.right - captionBtnWidth,
262 rcCurrent.top,
263 rcCurrent.right,
264 rcCurrent.top + captionBtnHeight);
265
266 rcCurrent.right -= captionBtnWidth + BUTTON_GAP_SIZE;
267 }
268 }
269
ThemeCalculateCaptionButtonsPos(HWND hWnd,HTHEME htheme)270 void ThemeCalculateCaptionButtonsPos(HWND hWnd, HTHEME htheme)
271 {
272 INT btnHeight;
273 WINDOWINFO wi = {sizeof(wi)};
274
275 if (!GetWindowInfo(hWnd, &wi))
276 return;
277 btnHeight = GetSystemMetrics(wi.dwExStyle & WS_EX_TOOLWINDOW ? SM_CYSMSIZE : SM_CYSIZE);
278
279 ThemeCalculateCaptionButtonsPosEx(&wi, hWnd, htheme, btnHeight);
280 }
281
282 static void
ThemeDrawCaptionButton(PDRAW_CONTEXT pcontext,RECT * prcCurrent,CAPTIONBUTTON buttonId,INT iStateId)283 ThemeDrawCaptionButton(PDRAW_CONTEXT pcontext,
284 RECT* prcCurrent,
285 CAPTIONBUTTON buttonId,
286 INT iStateId)
287 {
288 INT iPartId;
289 HMENU SysMenu;
290 UINT MenuState;
291 PWND_DATA pwndData = ThemeGetWndData(pcontext->hWnd);
292 if (!pwndData)
293 return;
294
295 switch(buttonId)
296 {
297 case CLOSEBUTTON:
298 SysMenu = GetSystemMenu(pcontext->hWnd, FALSE);
299 MenuState = GetMenuState(SysMenu, SC_CLOSE, MF_BYCOMMAND);
300 if (!(pcontext->wi.dwStyle & WS_SYSMENU) || (MenuState & (MF_GRAYED | MF_DISABLED)) || (GetClassLongPtrW(pcontext->hWnd, GCL_STYLE) & CS_NOCLOSE))
301 {
302 iStateId = (pcontext->Active ? BUTTON_DISABLED : BUTTON_INACTIVE_DISABLED);
303 }
304
305 iPartId = pcontext->wi.dwExStyle & WS_EX_TOOLWINDOW ? WP_SMALLCLOSEBUTTON : WP_CLOSEBUTTON;
306 break;
307
308 case MAXBUTTON:
309 if (!(pcontext->wi.dwStyle & WS_MAXIMIZEBOX))
310 {
311 if (!(pcontext->wi.dwStyle & WS_MINIMIZEBOX))
312 return;
313 else
314 iStateId = (pcontext->Active ? BUTTON_DISABLED : BUTTON_INACTIVE_DISABLED);
315 }
316
317 iPartId = pcontext->wi.dwStyle & WS_MAXIMIZE ? WP_RESTOREBUTTON : WP_MAXBUTTON;
318 break;
319
320 case MINBUTTON:
321 if (!(pcontext->wi.dwStyle & WS_MINIMIZEBOX))
322 {
323 if (!(pcontext->wi.dwStyle & WS_MAXIMIZEBOX))
324 return;
325 else
326 iStateId = (pcontext->Active ? BUTTON_DISABLED : BUTTON_INACTIVE_DISABLED);
327 }
328
329 iPartId = pcontext->wi.dwStyle & WS_MINIMIZE ? WP_RESTOREBUTTON : WP_MINBUTTON;
330 break;
331
332 default:
333 //FIXME: Implement Help Button
334 return;
335 }
336
337 if (prcCurrent)
338 prcCurrent->right = pwndData->rcCaptionButtons[buttonId].left;
339
340 DrawThemeBackground(pcontext->theme, pcontext->hDC, iPartId, iStateId, &pwndData->rcCaptionButtons[buttonId], NULL);
341 }
342
343 static DWORD
ThemeGetButtonState(DWORD htCurrect,DWORD htHot,DWORD htDown,BOOL Active)344 ThemeGetButtonState(DWORD htCurrect, DWORD htHot, DWORD htDown, BOOL Active)
345 {
346 if (htHot == htCurrect)
347 return (Active ? BUTTON_HOT : BUTTON_INACTIVE_HOT);
348 if (htDown == htCurrect)
349 return (Active ? BUTTON_PRESSED : BUTTON_INACTIVE_PRESSED);
350
351 return (Active ? BUTTON_NORMAL : BUTTON_INACTIVE);
352 }
353
354 /* Used only from mouse event handlers */
355 static void
ThemeDrawCaptionButtons(PDRAW_CONTEXT pcontext,DWORD htHot,DWORD htDown)356 ThemeDrawCaptionButtons(PDRAW_CONTEXT pcontext, DWORD htHot, DWORD htDown)
357 {
358 /* Draw the buttons */
359 ThemeDrawCaptionButton(pcontext, NULL, CLOSEBUTTON,
360 ThemeGetButtonState(HTCLOSE, htHot, htDown, pcontext->Active));
361 ThemeDrawCaptionButton(pcontext, NULL, MAXBUTTON,
362 ThemeGetButtonState(HTMAXBUTTON, htHot, htDown, pcontext->Active));
363 ThemeDrawCaptionButton(pcontext, NULL, MINBUTTON,
364 ThemeGetButtonState(HTMINBUTTON, htHot, htDown, pcontext->Active));
365 ThemeDrawCaptionButton(pcontext, NULL, HELPBUTTON,
366 ThemeGetButtonState(HTHELP, htHot, htDown, pcontext->Active));
367 }
368
369 /* Used from WM_NCPAINT and WM_NCACTIVATE handlers */
370 static void
ThemeDrawCaption(PDRAW_CONTEXT pcontext,RECT * prcCurrent)371 ThemeDrawCaption(PDRAW_CONTEXT pcontext, RECT* prcCurrent)
372 {
373 RECT rcPart;
374 int iPart, iState;
375 HICON hIcon;
376
377 // See also win32ss/user/ntuser/nonclient.c!UserDrawCaptionBar
378 // and win32ss/user/ntuser/nonclient.c!UserDrawCaption
379 if ((pcontext->wi.dwStyle & WS_SYSMENU) && !(pcontext->wi.dwExStyle & WS_EX_TOOLWINDOW))
380 hIcon = UserGetWindowIcon(pcontext);
381 else
382 hIcon = NULL;
383
384 /* Get the caption part and state id */
385 if (pcontext->wi.dwStyle & WS_MINIMIZE)
386 iPart = WP_MINCAPTION;
387 else if (pcontext->wi.dwExStyle & WS_EX_TOOLWINDOW)
388 iPart = WP_SMALLCAPTION;
389 else if (pcontext->wi.dwStyle & WS_MAXIMIZE)
390 iPart = WP_MAXCAPTION;
391 else
392 iPart = WP_CAPTION;
393
394 iState = pcontext->Active ? FS_ACTIVE : FS_INACTIVE;
395
396 /* Draw the caption background */
397 rcPart = *prcCurrent;
398 rcPart.bottom = rcPart.top + pcontext->CaptionHeight;
399 prcCurrent->top = rcPart.bottom;
400 DrawThemeBackground(pcontext->theme, pcontext->hDC,iPart,iState,&rcPart,NULL);
401
402 /* Add a padding around the objects of the caption */
403 InflateRect(&rcPart, -(int)pcontext->wi.cyWindowBorders-BUTTON_GAP_SIZE,
404 -(int)pcontext->wi.cyWindowBorders-BUTTON_GAP_SIZE);
405
406 /* Draw the caption buttons */
407 if (pcontext->wi.dwStyle & WS_SYSMENU)
408 {
409 iState = pcontext->Active ? BUTTON_NORMAL : BUTTON_INACTIVE;
410
411 ThemeDrawCaptionButton(pcontext, &rcPart, CLOSEBUTTON, iState);
412 ThemeDrawCaptionButton(pcontext, &rcPart, MAXBUTTON, iState);
413 ThemeDrawCaptionButton(pcontext, &rcPart, MINBUTTON, iState);
414 ThemeDrawCaptionButton(pcontext, &rcPart, HELPBUTTON, iState);
415 }
416
417 rcPart.top += 3 ;
418
419 /* Draw the icon */
420 if (hIcon)
421 {
422 int IconHeight = GetSystemMetrics(SM_CYSMICON);
423 int IconWidth = GetSystemMetrics(SM_CXSMICON);
424 DrawIconEx(pcontext->hDC, rcPart.left, rcPart.top , hIcon, IconWidth, IconHeight, 0, NULL, DI_NORMAL);
425 rcPart.left += IconWidth + 4;
426 }
427
428 rcPart.right -= 4;
429
430 /* Draw the caption */
431 ThemeDrawCaptionText(pcontext, &rcPart, iPart, iState);
432 }
433
434 static void
ThemeDrawBorders(PDRAW_CONTEXT pcontext,RECT * prcCurrent)435 ThemeDrawBorders(PDRAW_CONTEXT pcontext, RECT* prcCurrent)
436 {
437 RECT rcPart;
438 int iState = pcontext->Active ? FS_ACTIVE : FS_INACTIVE;
439
440 /* Draw the bottom border */
441 rcPart = *prcCurrent;
442 rcPart.top = rcPart.bottom - pcontext->wi.cyWindowBorders;
443 prcCurrent->bottom = rcPart.top;
444 DrawThemeBackground(pcontext->theme, pcontext->hDC, WP_FRAMEBOTTOM, iState, &rcPart, NULL);
445
446 /* Draw the left border */
447 rcPart = *prcCurrent;
448 rcPart.right = rcPart.left + pcontext->wi.cxWindowBorders ;
449 prcCurrent->left = rcPart.right;
450 DrawThemeBackground(pcontext->theme, pcontext->hDC,WP_FRAMELEFT, iState, &rcPart, NULL);
451
452 /* Draw the right border */
453 rcPart = *prcCurrent;
454 rcPart.left = rcPart.right - pcontext->wi.cxWindowBorders;
455 prcCurrent->right = rcPart.left;
456 DrawThemeBackground(pcontext->theme, pcontext->hDC,WP_FRAMERIGHT, iState, &rcPart, NULL);
457 }
458
459 static void
DrawClassicFrame(PDRAW_CONTEXT context,RECT * prcCurrent)460 DrawClassicFrame(PDRAW_CONTEXT context, RECT* prcCurrent)
461 {
462 /* Draw outer edge */
463 if (UserHasWindowEdge(context->wi.dwStyle, context->wi.dwExStyle))
464 {
465 DrawEdge(context->hDC, prcCurrent, EDGE_RAISED, BF_RECT | BF_ADJUST);
466 }
467 else if (context->wi.dwExStyle & WS_EX_STATICEDGE)
468 {
469 DrawEdge(context->hDC, prcCurrent, BDR_SUNKENINNER, BF_RECT | BF_ADJUST | BF_FLAT);
470 }
471
472 /* Firstly the "thick" frame */
473 if ((context->wi.dwStyle & WS_THICKFRAME) && !(context->wi.dwStyle & WS_MINIMIZE))
474 {
475 INT Width =
476 (GetSystemMetrics(SM_CXFRAME) - GetSystemMetrics(SM_CXDLGFRAME)) *
477 GetSystemMetrics(SM_CXBORDER);
478 INT Height =
479 (GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYDLGFRAME)) *
480 GetSystemMetrics(SM_CYBORDER);
481
482 SelectObject(context->hDC, GetSysColorBrush(
483 context->Active ? COLOR_ACTIVEBORDER : COLOR_INACTIVEBORDER));
484
485 /* Draw frame */
486 PatBlt(context->hDC, prcCurrent->left, prcCurrent->top,
487 prcCurrent->right - prcCurrent->left, Height, PATCOPY);
488 PatBlt(context->hDC, prcCurrent->left, prcCurrent->top,
489 Width, prcCurrent->bottom - prcCurrent->top, PATCOPY);
490 PatBlt(context->hDC, prcCurrent->left, prcCurrent->bottom - 1,
491 prcCurrent->right - prcCurrent->left, -Height, PATCOPY);
492 PatBlt(context->hDC, prcCurrent->right - 1, prcCurrent->top,
493 -Width, prcCurrent->bottom - prcCurrent->top, PATCOPY);
494
495 InflateRect(prcCurrent, -Width, -Height);
496 }
497
498 /* Now the other bit of the frame */
499 if (context->wi.dwStyle & (WS_DLGFRAME | WS_BORDER) || (context->wi.dwExStyle & WS_EX_DLGMODALFRAME))
500 {
501 INT Width = GetSystemMetrics(SM_CXBORDER);
502 INT Height = GetSystemMetrics(SM_CYBORDER);
503
504 SelectObject(context->hDC, GetSysColorBrush(
505 (context->wi.dwExStyle & (WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE)) ? COLOR_3DFACE :
506 (context->wi.dwExStyle & WS_EX_STATICEDGE) ? COLOR_WINDOWFRAME :
507 (context->wi.dwStyle & (WS_DLGFRAME | WS_THICKFRAME)) ? COLOR_3DFACE :
508 COLOR_WINDOWFRAME));
509
510 /* Draw frame */
511 PatBlt(context->hDC, prcCurrent->left, prcCurrent->top,
512 prcCurrent->right - prcCurrent->left, Height, PATCOPY);
513 PatBlt(context->hDC, prcCurrent->left, prcCurrent->top,
514 Width, prcCurrent->bottom - prcCurrent->top, PATCOPY);
515 PatBlt(context->hDC, prcCurrent->left, prcCurrent->bottom - 1,
516 prcCurrent->right - prcCurrent->left, -Height, PATCOPY);
517 PatBlt(context->hDC, prcCurrent->right - 1, prcCurrent->top,
518 -Width, prcCurrent->bottom - prcCurrent->top, PATCOPY);
519
520 InflateRect(prcCurrent, -Width, -Height);
521 }
522 }
523
ThemeDrawMenuBar(PDRAW_CONTEXT pcontext,RECT * prcCurrent)524 static void ThemeDrawMenuBar(PDRAW_CONTEXT pcontext, RECT* prcCurrent)
525 {
526 /* Let the window manager paint the menu */
527 prcCurrent->top += PaintMenuBar(pcontext->hWnd,
528 pcontext->hDC,
529 pcontext->wi.cxWindowBorders,
530 pcontext->wi.cxWindowBorders,
531 prcCurrent->top,
532 pcontext->Active);
533 }
534
ThemeDrawScrollBarsGrip(PDRAW_CONTEXT pcontext,RECT * prcCurrent)535 static void ThemeDrawScrollBarsGrip(PDRAW_CONTEXT pcontext, RECT* prcCurrent)
536 {
537 RECT rcPart;
538 HWND hwndParent;
539 RECT ParentClientRect;
540 DWORD ParentStyle;
541
542 rcPart = *prcCurrent;
543
544 if (pcontext->wi.dwExStyle & WS_EX_LEFTSCROLLBAR)
545 rcPart.right = rcPart.left + GetSystemMetrics(SM_CXVSCROLL);
546 else
547 rcPart.left = rcPart.right - GetSystemMetrics(SM_CXVSCROLL);
548
549 rcPart.top = rcPart.bottom - GetSystemMetrics(SM_CYHSCROLL);
550
551 FillRect(pcontext->hDC, &rcPart, GetSysColorBrush(COLOR_BTNFACE));
552
553 hwndParent = GetParent(pcontext->hWnd);
554 GetClientRect(hwndParent, &ParentClientRect);
555 ParentStyle = GetWindowLongW(hwndParent, GWL_STYLE);
556
557 if (HASSIZEGRIP(pcontext->wi.dwStyle, pcontext->wi.dwExStyle, ParentStyle, pcontext->wi.rcWindow, ParentClientRect))
558 {
559 int iState;
560 if (pcontext->wi.dwExStyle & WS_EX_LEFTSCROLLBAR)
561 iState = pcontext->wi.dwExStyle & WS_EX_LEFTSCROLLBAR;
562 else
563 iState = SZB_RIGHTALIGN;
564 DrawThemeBackground(pcontext->scrolltheme, pcontext->hDC, SBP_SIZEBOX, iState, &rcPart, NULL);
565 }
566 }
567
568 static void
ThemePaintWindow(PDRAW_CONTEXT pcontext,RECT * prcCurrent,BOOL bDoDoubleBuffering)569 ThemePaintWindow(PDRAW_CONTEXT pcontext, RECT* prcCurrent, BOOL bDoDoubleBuffering)
570 {
571 if (!(pcontext->wi.dwStyle & WS_VISIBLE))
572 return;
573
574 if ((pcontext->wi.dwStyle & WS_CAPTION)==WS_CAPTION)
575 {
576 if (bDoDoubleBuffering)
577 ThemeStartBufferedPaint(pcontext, prcCurrent->right, pcontext->CaptionHeight);
578 ThemeDrawCaption(pcontext, prcCurrent);
579 if (bDoDoubleBuffering)
580 ThemeEndBufferedPaint(pcontext, 0, 0, prcCurrent->right, pcontext->CaptionHeight);
581 ThemeDrawBorders(pcontext, prcCurrent);
582 }
583 else
584 {
585 DrawClassicFrame(pcontext, prcCurrent);
586 }
587
588 if (pcontext->wi.dwStyle & WS_MINIMIZE)
589 return;
590
591 if (HAS_MENU(pcontext->hWnd, pcontext->wi.dwStyle))
592 ThemeDrawMenuBar(pcontext, prcCurrent);
593
594 if (pcontext->wi.dwExStyle & WS_EX_CLIENTEDGE)
595 DrawEdge(pcontext->hDC, prcCurrent, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
596
597 if ((pcontext->wi.dwStyle & WS_HSCROLL) && IsScrollBarVisible(pcontext->hWnd, OBJID_HSCROLL))
598 ThemeDrawScrollBar(pcontext, SB_HORZ , NULL);
599
600 if ((pcontext->wi.dwStyle & WS_VSCROLL) && IsScrollBarVisible(pcontext->hWnd, OBJID_VSCROLL))
601 ThemeDrawScrollBar(pcontext, SB_VERT, NULL);
602
603 if ((pcontext->wi.dwStyle & (WS_HSCROLL|WS_VSCROLL)) == (WS_HSCROLL|WS_VSCROLL) &&
604 IsScrollBarVisible(pcontext->hWnd, OBJID_HSCROLL) &&
605 IsScrollBarVisible(pcontext->hWnd, OBJID_VSCROLL))
606 {
607 ThemeDrawScrollBarsGrip(pcontext, prcCurrent);
608 }
609 }
610
611 /*
612 * Message handlers
613 */
614
615 static LRESULT
ThemeHandleNCPaint(HWND hWnd,HRGN hRgn)616 ThemeHandleNCPaint(HWND hWnd, HRGN hRgn)
617 {
618 DRAW_CONTEXT context;
619 RECT rcCurrent;
620
621 ThemeInitDrawContext(&context, hWnd, hRgn);
622
623 rcCurrent = context.wi.rcWindow;
624 OffsetRect( &rcCurrent, -context.wi.rcWindow.left, -context.wi.rcWindow.top);
625
626 ThemePaintWindow(&context, &rcCurrent, TRUE);
627 ThemeCleanupDrawContext(&context);
628
629 return 0;
630 }
631
632 static LRESULT
ThemeHandleNcMouseMove(HWND hWnd,DWORD ht,POINT * pt)633 ThemeHandleNcMouseMove(HWND hWnd, DWORD ht, POINT* pt)
634 {
635 DRAW_CONTEXT context;
636 TRACKMOUSEEVENT tme;
637 DWORD style;
638 PWND_DATA pwndData;
639
640 /* First of all check if we have something to do here */
641 style = GetWindowLongW(hWnd, GWL_STYLE);
642 if ((style & (WS_CAPTION|WS_HSCROLL|WS_VSCROLL))==0)
643 return 0;
644
645 /* Get theme data for this window */
646 pwndData = ThemeGetWndData(hWnd);
647 if (pwndData == NULL)
648 return 0;
649
650 /* Begin tracking in the non client area if we are not tracking yet */
651 tme.cbSize = sizeof(TRACKMOUSEEVENT);
652 tme.dwFlags = TME_QUERY;
653 tme.hwndTrack = hWnd;
654 TrackMouseEvent(&tme);
655 if (tme.dwFlags != (TME_LEAVE | TME_NONCLIENT))
656 {
657 tme.hwndTrack = hWnd;
658 tme.dwFlags = TME_LEAVE | TME_NONCLIENT;
659 TrackMouseEvent(&tme);
660 }
661
662 ThemeInitDrawContext(&context, hWnd, 0);
663 if (context.wi.dwStyle & WS_SYSMENU)
664 {
665 if (HT_ISBUTTON(ht) || HT_ISBUTTON(pwndData->lastHitTest))
666 ThemeDrawCaptionButtons(&context, ht, 0);
667 }
668
669 if (context.wi.dwStyle & WS_HSCROLL)
670 {
671 if (ht == HTHSCROLL || pwndData->lastHitTest == HTHSCROLL)
672 ThemeDrawScrollBar(&context, SB_HORZ , ht == HTHSCROLL ? pt : NULL);
673 }
674
675 if (context.wi.dwStyle & WS_VSCROLL)
676 {
677 if (ht == HTVSCROLL || pwndData->lastHitTest == HTVSCROLL)
678 ThemeDrawScrollBar(&context, SB_VERT, ht == HTVSCROLL ? pt : NULL);
679 }
680 ThemeCleanupDrawContext(&context);
681
682 pwndData->lastHitTest = ht;
683
684 return 0;
685 }
686
687 static LRESULT
ThemeHandleNcMouseLeave(HWND hWnd)688 ThemeHandleNcMouseLeave(HWND hWnd)
689 {
690 DRAW_CONTEXT context;
691 DWORD style;
692 PWND_DATA pwndData;
693
694 /* First of all check if we have something to do here */
695 style = GetWindowLongW(hWnd, GWL_STYLE);
696 if ((style & (WS_CAPTION|WS_HSCROLL|WS_VSCROLL))==0)
697 return 0;
698
699 /* Get theme data for this window */
700 pwndData = ThemeGetWndData(hWnd);
701 if (pwndData == NULL)
702 return 0;
703
704 ThemeInitDrawContext(&context, hWnd, 0);
705 if (context.wi.dwStyle & WS_SYSMENU && HT_ISBUTTON(pwndData->lastHitTest))
706 ThemeDrawCaptionButtons(&context, 0, 0);
707
708 if (context.wi.dwStyle & WS_HSCROLL && pwndData->lastHitTest == HTHSCROLL)
709 ThemeDrawScrollBar(&context, SB_HORZ, NULL);
710
711 if (context.wi.dwStyle & WS_VSCROLL && pwndData->lastHitTest == HTVSCROLL)
712 ThemeDrawScrollBar(&context, SB_VERT, NULL);
713
714 ThemeCleanupDrawContext(&context);
715
716 pwndData->lastHitTest = HTNOWHERE;
717
718 return 0;
719 }
720
721 static VOID
ThemeHandleButton(HWND hWnd,WPARAM wParam)722 ThemeHandleButton(HWND hWnd, WPARAM wParam)
723 {
724 MSG Msg;
725 BOOL Pressed = TRUE;
726 WPARAM SCMsg, ht;
727 ULONG Style;
728 DRAW_CONTEXT context;
729 PWND_DATA pwndData;
730
731 Style = GetWindowLongW(hWnd, GWL_STYLE);
732 if (!((Style & WS_CAPTION) && (Style & WS_SYSMENU)))
733 return ;
734
735 switch (wParam)
736 {
737 case HTCLOSE:
738 SCMsg = SC_CLOSE;
739 break;
740 case HTMINBUTTON:
741 if (!(Style & WS_MINIMIZEBOX))
742 return;
743 SCMsg = ((Style & WS_MINIMIZE) ? SC_RESTORE : SC_MINIMIZE);
744 break;
745 case HTMAXBUTTON:
746 if (!(Style & WS_MAXIMIZEBOX))
747 return;
748 SCMsg = ((Style & WS_MAXIMIZE) ? SC_RESTORE : SC_MAXIMIZE);
749 break;
750 default :
751 return;
752 }
753
754 /* Get theme data for this window */
755 pwndData = ThemeGetWndData(hWnd);
756 if (pwndData == NULL)
757 return;
758
759 ThemeInitDrawContext(&context, hWnd, 0);
760 ThemeDrawCaptionButtons(&context, 0, wParam);
761 pwndData->lastHitTest = wParam;
762
763 SetCapture(hWnd);
764
765 ht = wParam;
766
767 for (;;)
768 {
769 if (GetMessageW(&Msg, 0, WM_MOUSEFIRST, WM_MOUSELAST) <= 0)
770 break;
771
772 if (Msg.message == WM_LBUTTONUP)
773 break;
774
775 if (Msg.message != WM_MOUSEMOVE)
776 continue;
777
778 ht = SendMessage(hWnd, WM_NCHITTEST, 0, MAKELPARAM(Msg.pt.x, Msg.pt.y));
779 Pressed = (ht == wParam);
780
781 /* Only draw the buttons if the hit test changed */
782 if (ht != pwndData->lastHitTest &&
783 (HT_ISBUTTON(ht) || HT_ISBUTTON(pwndData->lastHitTest)))
784 {
785 ThemeDrawCaptionButtons(&context, 0, Pressed ? wParam: 0);
786 pwndData->lastHitTest = ht;
787 }
788 }
789
790 ThemeDrawCaptionButtons(&context, ht, 0);
791 ThemeCleanupDrawContext(&context);
792
793 ReleaseCapture();
794
795 if (Pressed)
796 SendMessageW(hWnd, WM_SYSCOMMAND, SCMsg, 0);
797 }
798
799 static LRESULT
DefWndNCHitTest(HWND hWnd,POINT Point)800 DefWndNCHitTest(HWND hWnd, POINT Point)
801 {
802 RECT WindowRect;
803 POINT ClientPoint;
804 WINDOWINFO wi;
805
806 wi.cbSize = sizeof(wi);
807 GetWindowInfo(hWnd, &wi);
808
809 if (!PtInRect(&wi.rcWindow, Point))
810 {
811 return HTNOWHERE;
812 }
813 WindowRect = wi.rcWindow;
814
815 if (UserHasWindowEdge(wi.dwStyle, wi.dwExStyle))
816 {
817 LONG XSize, YSize;
818
819 InflateRect(&WindowRect, -(int)wi.cxWindowBorders, -(int)wi.cyWindowBorders);
820 XSize = GetSystemMetrics(SM_CXSIZE) * GetSystemMetrics(SM_CXBORDER);
821 YSize = GetSystemMetrics(SM_CYSIZE) * GetSystemMetrics(SM_CYBORDER);
822 if (!PtInRect(&WindowRect, Point))
823 {
824 BOOL ThickFrame;
825
826 ThickFrame = (wi.dwStyle & WS_THICKFRAME);
827 if (Point.y < WindowRect.top)
828 {
829 if (wi.dwStyle & WS_MINIMIZE)
830 return HTCAPTION;
831 if (!ThickFrame)
832 return HTBORDER;
833 if (Point.x < (WindowRect.left + XSize))
834 return HTTOPLEFT;
835 if (Point.x >= (WindowRect.right - XSize))
836 return HTTOPRIGHT;
837 return HTTOP;
838 }
839 if (Point.y >= WindowRect.bottom)
840 {
841 if (wi.dwStyle & WS_MINIMIZE)
842 return HTCAPTION;
843 if (!ThickFrame)
844 return HTBORDER;
845 if (Point.x < (WindowRect.left + XSize))
846 return HTBOTTOMLEFT;
847 if (Point.x >= (WindowRect.right - XSize))
848 return HTBOTTOMRIGHT;
849 return HTBOTTOM;
850 }
851 if (Point.x < WindowRect.left)
852 {
853 if (wi.dwStyle & WS_MINIMIZE)
854 return HTCAPTION;
855 if (!ThickFrame)
856 return HTBORDER;
857 if (Point.y < (WindowRect.top + YSize))
858 return HTTOPLEFT;
859 if (Point.y >= (WindowRect.bottom - YSize))
860 return HTBOTTOMLEFT;
861 return HTLEFT;
862 }
863 if (Point.x >= WindowRect.right)
864 {
865 if (wi.dwStyle & WS_MINIMIZE)
866 return HTCAPTION;
867 if (!ThickFrame)
868 return HTBORDER;
869 if (Point.y < (WindowRect.top + YSize))
870 return HTTOPRIGHT;
871 if (Point.y >= (WindowRect.bottom - YSize))
872 return HTBOTTOMRIGHT;
873 return HTRIGHT;
874 }
875 }
876 }
877 else
878 {
879 if (wi.dwExStyle & WS_EX_STATICEDGE)
880 InflateRect(&WindowRect, -GetSystemMetrics(SM_CXBORDER),
881 -GetSystemMetrics(SM_CYBORDER));
882 if (!PtInRect(&WindowRect, Point))
883 return HTBORDER;
884 }
885
886 if ((wi.dwStyle & WS_CAPTION) == WS_CAPTION)
887 {
888 if (wi.dwExStyle & WS_EX_TOOLWINDOW)
889 WindowRect.top += GetSystemMetrics(SM_CYSMCAPTION);
890 else
891 WindowRect.top += GetSystemMetrics(SM_CYCAPTION);
892
893 if (!PtInRect(&WindowRect, Point))
894 {
895 if (wi.dwStyle & WS_SYSMENU)
896 {
897 PWND_DATA pwndData = ThemeGetWndData(hWnd);
898
899 if (!(wi.dwExStyle & WS_EX_TOOLWINDOW))
900 {
901 // if (!(wi.dwExStyle & WS_EX_DLGMODALFRAME))
902 // FIXME: The real test should check whether there is
903 // an icon for the system window, and if so, do the
904 // rect.left increase.
905 // See win32ss/user/user32/windows/nonclient.c!DefWndNCHitTest
906 // and win32ss/user/ntuser/nonclient.c!GetNCHitEx which does
907 // the test better.
908 WindowRect.left += GetSystemMetrics(SM_CXSMICON);
909 }
910
911 if (pwndData)
912 {
913 POINT pt = {Point.x - wi.rcWindow.left, Point.y - wi.rcWindow.top};
914 if (PtInRect(&pwndData->rcCaptionButtons[CLOSEBUTTON], pt))
915 return HTCLOSE;
916 if (PtInRect(&pwndData->rcCaptionButtons[MAXBUTTON], pt))
917 return HTMAXBUTTON;
918 if (PtInRect(&pwndData->rcCaptionButtons[MINBUTTON], pt))
919 return HTMINBUTTON;
920 }
921 }
922 if (Point.x < WindowRect.left)
923 return HTSYSMENU;
924 return HTCAPTION;
925 }
926 }
927
928 if (!(wi.dwStyle & WS_MINIMIZE))
929 {
930 HMENU menu;
931
932 ClientPoint = Point;
933 ScreenToClient(hWnd, &ClientPoint);
934 GetClientRect(hWnd, &wi.rcClient);
935
936 if (PtInRect(&wi.rcClient, ClientPoint))
937 {
938 return HTCLIENT;
939 }
940
941 if ((menu = GetMenu(hWnd)) && !(wi.dwStyle & WS_CHILD))
942 {
943 if (Point.x > 0 && Point.x < WindowRect.right && ClientPoint.y < 0)
944 return HTMENU;
945 }
946
947 if (wi.dwExStyle & WS_EX_CLIENTEDGE)
948 {
949 InflateRect(&WindowRect, -2 * GetSystemMetrics(SM_CXBORDER),
950 -2 * GetSystemMetrics(SM_CYBORDER));
951 }
952
953 if ((wi.dwStyle & WS_VSCROLL) && (wi.dwStyle & WS_HSCROLL) &&
954 (WindowRect.bottom - WindowRect.top) > GetSystemMetrics(SM_CYHSCROLL))
955 {
956 RECT ParentRect, TempRect = WindowRect, TempRect2 = WindowRect;
957 HWND Parent = GetParent(hWnd);
958
959 TempRect.bottom -= GetSystemMetrics(SM_CYHSCROLL);
960 if ((wi.dwExStyle & WS_EX_LEFTSCROLLBAR) != 0)
961 TempRect.right = TempRect.left + GetSystemMetrics(SM_CXVSCROLL);
962 else
963 TempRect.left = TempRect.right - GetSystemMetrics(SM_CXVSCROLL);
964 if (PtInRect(&TempRect, Point))
965 return HTVSCROLL;
966
967 TempRect2.top = TempRect2.bottom - GetSystemMetrics(SM_CYHSCROLL);
968 if ((wi.dwExStyle & WS_EX_LEFTSCROLLBAR) != 0)
969 TempRect2.left += GetSystemMetrics(SM_CXVSCROLL);
970 else
971 TempRect2.right -= GetSystemMetrics(SM_CXVSCROLL);
972 if (PtInRect(&TempRect2, Point))
973 return HTHSCROLL;
974
975 TempRect.top = TempRect2.top;
976 TempRect.bottom = TempRect2.bottom;
977 if (Parent)
978 GetClientRect(Parent, &ParentRect);
979 if (PtInRect(&TempRect, Point) && HASSIZEGRIP(wi.dwStyle, wi.dwExStyle,
980 GetWindowLongW(Parent, GWL_STYLE), wi.rcWindow, ParentRect))
981 {
982 if ((wi.dwExStyle & WS_EX_LEFTSCROLLBAR) != 0)
983 return HTBOTTOMLEFT;
984 else
985 return HTBOTTOMRIGHT;
986 }
987 }
988 else
989 {
990 if (wi.dwStyle & WS_VSCROLL)
991 {
992 RECT TempRect = WindowRect;
993
994 if ((wi.dwExStyle & WS_EX_LEFTSCROLLBAR) != 0)
995 TempRect.right = TempRect.left + GetSystemMetrics(SM_CXVSCROLL);
996 else
997 TempRect.left = TempRect.right - GetSystemMetrics(SM_CXVSCROLL);
998 if (PtInRect(&TempRect, Point))
999 return HTVSCROLL;
1000 }
1001 else if (wi.dwStyle & WS_HSCROLL)
1002 {
1003 RECT TempRect = WindowRect;
1004 TempRect.top = TempRect.bottom - GetSystemMetrics(SM_CYHSCROLL);
1005 if (PtInRect(&TempRect, Point))
1006 return HTHSCROLL;
1007 }
1008 }
1009 }
1010
1011 return HTNOWHERE;
1012 }
1013
1014 LRESULT CALLBACK
ThemeWndProc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam,WNDPROC DefWndProc)1015 ThemeWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, WNDPROC DefWndProc)
1016 {
1017 switch(Msg)
1018 {
1019 case WM_NCPAINT:
1020 return ThemeHandleNCPaint(hWnd, (HRGN)wParam);
1021 //
1022 // WM_NCUAHDRAWCAPTION : wParam are DC_* flags.
1023 //
1024 case WM_NCUAHDRAWCAPTION:
1025 //
1026 // WM_NCUAHDRAWFRAME : wParam is HDC, lParam are DC_ACTIVE and or DC_REDRAWHUNGWND.
1027 //
1028 case WM_NCUAHDRAWFRAME:
1029 case WM_NCACTIVATE:
1030
1031 if ((GetWindowLongW(hWnd, GWL_STYLE) & WS_CAPTION) != WS_CAPTION)
1032 return TRUE;
1033
1034 ThemeHandleNCPaint(hWnd, (HRGN)1);
1035 return TRUE;
1036 case WM_NCMOUSEMOVE:
1037 {
1038 POINT Point;
1039 Point.x = GET_X_LPARAM(lParam);
1040 Point.y = GET_Y_LPARAM(lParam);
1041 return ThemeHandleNcMouseMove(hWnd, wParam, &Point);
1042 }
1043 case WM_NCMOUSELEAVE:
1044 return ThemeHandleNcMouseLeave(hWnd);
1045 case WM_NCLBUTTONDOWN:
1046 switch (wParam)
1047 {
1048 case HTMINBUTTON:
1049 case HTMAXBUTTON:
1050 case HTCLOSE:
1051 {
1052 ThemeHandleButton(hWnd, wParam);
1053 return 0;
1054 }
1055 default:
1056 return DefWndProc(hWnd, Msg, wParam, lParam);
1057 }
1058 case WM_NCHITTEST:
1059 {
1060 POINT Point;
1061 Point.x = GET_X_LPARAM(lParam);
1062 Point.y = GET_Y_LPARAM(lParam);
1063 return DefWndNCHitTest(hWnd, Point);
1064 }
1065 case WM_SYSCOMMAND:
1066 {
1067 if ((wParam & 0xfff0) == SC_VSCROLL ||
1068 (wParam & 0xfff0) == SC_HSCROLL)
1069 {
1070 POINT Pt;
1071 Pt.x = (short)LOWORD(lParam);
1072 Pt.y = (short)HIWORD(lParam);
1073 NC_TrackScrollBar(hWnd, wParam, Pt);
1074 return 0;
1075 }
1076 else
1077 {
1078 return DefWndProc(hWnd, Msg, wParam, lParam);
1079 }
1080 }
1081 default:
1082 return DefWndProc(hWnd, Msg, wParam, lParam);
1083 }
1084 }
1085
1086 static
1087 void
DrawWindowForNCPreview(_In_ HDC hDC,_In_ PDRAW_CONTEXT pcontext,_In_ INT left,_In_ INT top,_In_ INT right,_In_ INT bottom,_In_ INT clientAreaColor,_Out_opt_ LPRECT prcClient)1088 DrawWindowForNCPreview(
1089 _In_ HDC hDC,
1090 _In_ PDRAW_CONTEXT pcontext,
1091 _In_ INT left,
1092 _In_ INT top,
1093 _In_ INT right,
1094 _In_ INT bottom,
1095 _In_ INT clientAreaColor,
1096 _Out_opt_ LPRECT prcClient)
1097 {
1098 if (!hDC)
1099 return;
1100
1101 if (!pcontext)
1102 return;
1103
1104 DWORD dwStyle = pcontext->wi.dwStyle;
1105 DWORD dwExStyle = pcontext->wi.dwExStyle;
1106 pcontext->CaptionHeight = pcontext->wi.cyWindowBorders + GetThemeSysSize(pcontext->theme, dwExStyle & WS_EX_TOOLWINDOW ? SM_CYSMSIZE : SM_CYSIZE);
1107 /* FIXME: still need to use ncmetrics from parameters for window border width */
1108
1109 RECT rcWindowPrev = { pcontext->wi.rcWindow.left, pcontext->wi.rcWindow.top, pcontext->wi.rcWindow.right, pcontext->wi.rcWindow.bottom };
1110 RECT rcClientPrev = { pcontext->wi.rcClient.left, pcontext->wi.rcClient.top, pcontext->wi.rcClient.right, pcontext->wi.rcClient.bottom };
1111 SetWindowPos(pcontext->hWnd, NULL, left, top, right - left, bottom - top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_DRAWFRAME | SWP_NOCOPYBITS);
1112 RECT rcWindowNew = { left, top, right, bottom };
1113 pcontext->wi.rcWindow = rcWindowNew;
1114
1115 BOOL hasVScrollBar = dwStyle & WS_VSCROLL;
1116 if (hasVScrollBar)
1117 {
1118 SCROLLINFO dummyScrollInfo;
1119 EnableScrollBar(pcontext->hWnd, SB_VERT, ESB_ENABLE_BOTH);
1120
1121 dummyScrollInfo.cbSize = sizeof(dummyScrollInfo);
1122 dummyScrollInfo.fMask = SIF_DISABLENOSCROLL | SIF_POS | SIF_RANGE;
1123 dummyScrollInfo.nMin = 0;
1124 dummyScrollInfo.nMax = rcWindowNew.bottom - rcWindowNew.top;
1125 dummyScrollInfo.nPos = 0;
1126 SetScrollInfo(pcontext->hWnd, SB_VERT, &dummyScrollInfo, TRUE);
1127 }
1128
1129 SetViewportOrgEx(hDC, rcWindowNew.left, rcWindowNew.top, NULL);
1130
1131 INT offsetX = -rcWindowNew.left;
1132 INT offsetY = -rcWindowNew.top;
1133 OffsetRect(&rcWindowNew, offsetX, offsetY);
1134 ThemeCalculateCaptionButtonsPosEx(&pcontext->wi, pcontext->hWnd, pcontext->theme, pcontext->CaptionHeight - pcontext->wi.cyWindowBorders);
1135
1136 INT leftBorderInset = pcontext->wi.cxWindowBorders;
1137 INT titleBarInset = pcontext->CaptionHeight; // + pcontext->wi.cyWindowBorders;
1138 INT rightBorderInset = pcontext->wi.cxWindowBorders;
1139 INT bottomBorderInset = pcontext->wi.cyWindowBorders;
1140
1141 RECT rcClientNew;
1142 if (GetWindowRect(pcontext->hWnd, &rcClientNew))
1143 {
1144 rcClientNew.left += leftBorderInset;
1145 rcClientNew.top += titleBarInset;
1146 rcClientNew.right -= rightBorderInset;
1147 rcClientNew.bottom -= bottomBorderInset;
1148 }
1149 pcontext->wi.rcClient = rcClientNew;
1150
1151 pcontext->wi.dwStyle &= ~(WS_HSCROLL | WS_VSCROLL);
1152 ThemePaintWindow(pcontext, &rcWindowNew, FALSE);
1153 pcontext->wi.dwStyle = dwStyle;
1154
1155 if (hasVScrollBar && IsScrollBarVisible(pcontext->hWnd, OBJID_VSCROLL))
1156 {
1157 SCROLLBARINFO sbi;
1158 sbi.cbSize = sizeof(sbi);
1159 GetScrollBarInfo(pcontext->hWnd, OBJID_VSCROLL, &sbi);
1160 INT scWidth = sbi.rcScrollBar.right - sbi.rcScrollBar.left;
1161
1162 sbi.rcScrollBar.right = rcClientNew.right;
1163 rcClientNew.right -= scWidth;
1164 sbi.rcScrollBar.left = rcClientNew.right;
1165
1166 sbi.rcScrollBar.top = rcClientNew.top;
1167 sbi.rcScrollBar.bottom = rcClientNew.bottom;
1168
1169 ThemeDrawScrollBarEx(pcontext, SB_VERT, &sbi, NULL);
1170 }
1171 pcontext->wi.rcClient = rcClientNew;
1172
1173 OffsetRect(&rcClientNew, -pcontext->wi.rcWindow.left, -pcontext->wi.rcWindow.top);
1174
1175 HBRUSH hbrWindow = GetThemeSysColorBrush(pcontext->theme, clientAreaColor);
1176 FillRect(hDC, &rcClientNew, hbrWindow);
1177 DeleteObject(hbrWindow);
1178
1179 pcontext->wi.rcWindow = rcWindowPrev;
1180 pcontext->wi.rcClient = rcClientPrev;
1181
1182 SetViewportOrgEx(hDC, 0, 0, NULL);
1183 if (prcClient != NULL)
1184 {
1185 prcClient->left = rcClientNew.left;
1186 prcClient->top = rcClientNew.top;
1187 prcClient->right = rcClientNew.right;
1188 prcClient->bottom = rcClientNew.bottom;
1189 OffsetRect(prcClient, -offsetX, -offsetY);
1190 }
1191 }
1192
1193 VOID
SetWindowResourceText(_In_ HWND hwnd,_In_ UINT uID)1194 SetWindowResourceText(
1195 _In_ HWND hwnd,
1196 _In_ UINT uID)
1197 {
1198 LPWSTR lpszDestBuf = NULL, lpszResourceString = NULL;
1199 size_t iStrSize = 0;
1200
1201 /* When passing a zero-length buffer size, LoadString() returns
1202 * a read-only pointer buffer to the program's resource string. */
1203 iStrSize = LoadStringW(hDllInst, uID, (LPWSTR)&lpszResourceString, 0);
1204
1205 if (lpszResourceString && ((lpszDestBuf = HeapAlloc(GetProcessHeap(), 0, (iStrSize + 1) * sizeof(WCHAR))) != NULL))
1206 {
1207 wcsncpy(lpszDestBuf, lpszResourceString, iStrSize);
1208 lpszDestBuf[iStrSize] = UNICODE_NULL; // NULL-terminate the string
1209
1210 SetWindowTextW(hwnd, lpszDestBuf);
1211 HeapFree(GetProcessHeap(), 0, lpszDestBuf);
1212 }
1213 }
1214
DrawNCPreview(HDC hDC,DWORD DNCP_Flag,LPRECT prcPreview,LPCWSTR pszThemeFileName,LPCWSTR pszColorName,LPCWSTR pszSizeName,PNONCLIENTMETRICSW pncMetrics,COLORREF * lpaRgbValues)1215 HRESULT WINAPI DrawNCPreview(HDC hDC,
1216 DWORD DNCP_Flag,
1217 LPRECT prcPreview,
1218 LPCWSTR pszThemeFileName,
1219 LPCWSTR pszColorName,
1220 LPCWSTR pszSizeName,
1221 PNONCLIENTMETRICSW pncMetrics,
1222 COLORREF* lpaRgbValues)
1223 {
1224 WNDCLASSEXW DummyPreviewWindowClass;
1225 HWND hwndDummy;
1226 HRESULT hres;
1227 HTHEMEFILE hThemeFile;
1228 DRAW_CONTEXT context;
1229 LPWSTR szText;
1230 int len;
1231
1232 /* Create a dummy window that will be used to trick the paint funtions */
1233 memset(&DummyPreviewWindowClass, 0, sizeof(DummyPreviewWindowClass));
1234 DummyPreviewWindowClass.cbSize = sizeof(DummyPreviewWindowClass);
1235 DummyPreviewWindowClass.lpszClassName = L"DummyPreviewWindowClass";
1236 DummyPreviewWindowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
1237 DummyPreviewWindowClass.hInstance = hDllInst;
1238 DummyPreviewWindowClass.lpfnWndProc = DefWindowProcW;
1239 if (!RegisterClassExW(&DummyPreviewWindowClass))
1240 return E_FAIL;
1241
1242 hwndDummy = CreateWindowExW(WS_EX_DLGMODALFRAME, L"DummyPreviewWindowClass", NULL, WS_OVERLAPPEDWINDOW | WS_VSCROLL, 30, 30, 300, 150, 0, 0, hDllInst, NULL);
1243 if (!hwndDummy)
1244 return E_FAIL;
1245
1246 hres = OpenThemeFile(pszThemeFileName, pszColorName, pszSizeName, &hThemeFile,0);
1247 if (FAILED(hres))
1248 return hres;
1249
1250 /* Initialize the special draw context for the preview */
1251 context.hDC = hDC;
1252 context.hWnd = hwndDummy;
1253 context.theme = OpenThemeDataFromFile(hThemeFile, hwndDummy, L"WINDOW", 0);
1254 if (!context.theme)
1255 return E_FAIL;
1256 context.scrolltheme = OpenThemeDataFromFile(hThemeFile, hwndDummy, L"SCROLLBAR", 0);
1257 if (!context.scrolltheme)
1258 return E_FAIL;
1259 context.wi.cbSize = sizeof(context.wi);
1260 if (!GetWindowInfo(hwndDummy, &context.wi))
1261 return E_FAIL;
1262 context.wi.dwStyle |= WS_VISIBLE;
1263
1264 context.hRgn = CreateRectRgnIndirect(&context.wi.rcWindow);
1265 RECT rcAdjPreview = { prcPreview->left, prcPreview->top, prcPreview->right, prcPreview->bottom };
1266 INT previewWidth = rcAdjPreview.right - rcAdjPreview.left;
1267 INT previewHeight = rcAdjPreview.bottom - rcAdjPreview.top;
1268
1269 /* Draw inactive preview window */
1270 context.Active = FALSE;
1271 SetWindowResourceText(hwndDummy, IDS_INACTIVEWIN);
1272 DrawWindowForNCPreview(hDC, &context, rcAdjPreview.left, rcAdjPreview.top, rcAdjPreview.right - 17, rcAdjPreview.bottom - 20, COLOR_WINDOW, NULL);
1273
1274 /* Draw active preview window */
1275 context.Active = TRUE;
1276 SetWindowResourceText(hwndDummy, IDS_ACTIVEWIN);
1277
1278 DWORD textDrawFlags = DT_NOPREFIX | DT_SINGLELINE | DT_WORDBREAK;
1279 RECT rcWindowClient;
1280 DrawWindowForNCPreview(hDC, &context, rcAdjPreview.left + 10, rcAdjPreview.top + 22, rcAdjPreview.right, rcAdjPreview.bottom, COLOR_WINDOW, &rcWindowClient);
1281 LOGFONTW lfText;
1282 HFONT textFont = NULL;
1283 if (SUCCEEDED(GetThemeSysFont(context.theme, TMT_MSGBOXFONT, &lfText)))
1284 textFont = CreateFontIndirectW(&lfText);
1285
1286 if (textFont)
1287 SelectFont(hDC, textFont);
1288
1289 HTHEME hBtnTheme = OpenThemeDataFromFile(hThemeFile, hwndDummy, L"BUTTON", OTD_NONCLIENT);
1290 len = LoadStringW(hDllInst, IDS_WINTEXT, (LPWSTR)&szText, 0);
1291 if (len > 0)
1292 {
1293 DTTOPTS dttOpts = { sizeof(dttOpts) };
1294 dttOpts.dwFlags = DTT_TEXTCOLOR;
1295 dttOpts.crText = GetThemeSysColor(context.theme, COLOR_WINDOWTEXT);
1296
1297 DrawThemeTextEx(hBtnTheme, hDC, BP_PUSHBUTTON, PBS_DEFAULTED, szText, len, DT_LEFT | DT_TOP | textDrawFlags, &rcWindowClient, &dttOpts);
1298 }
1299
1300 /* Draw preview dialog window */
1301 SetWindowResourceText(hwndDummy, IDS_MESSAGEBOX);
1302 DWORD dwStyleNew = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_DLGFRAME;
1303 SetWindowLongPtr(hwndDummy, GWL_STYLE, dwStyleNew);
1304
1305 if (!GetWindowInfo(hwndDummy, &context.wi))
1306 return E_FAIL;
1307
1308 context.wi.dwStyle = WS_VISIBLE | dwStyleNew;
1309
1310 INT msgBoxHCenter = rcAdjPreview.left + (previewWidth / 2);
1311 INT msgBoxVCenter = rcAdjPreview.top + (previewHeight / 2);
1312
1313 DrawWindowForNCPreview(hDC, &context, msgBoxHCenter - NC_PREVIEW_MSGBOX_HALF_WIDTH, msgBoxVCenter + NC_PREVIEW_MSGBOX_OFFSET_X, msgBoxHCenter + NC_PREVIEW_MSGBOX_HALF_WIDTH, msgBoxVCenter + NC_PREVIEW_MSGBOX_OFFSET_Y, COLOR_BTNFACE, &rcWindowClient);
1314
1315 /* Draw preview dialog button */
1316 if (hBtnTheme)
1317 {
1318 INT btnCenterH = rcWindowClient.left + ((rcWindowClient.right - rcWindowClient.left) / 2);
1319 INT btnCenterV = rcWindowClient.top + ((rcWindowClient.bottom - rcWindowClient.top) / 2);
1320 RECT rcBtn = {btnCenterH - 40, btnCenterV - 15, btnCenterH + 40, btnCenterV + 15};
1321 int btnPart = BP_PUSHBUTTON;
1322 int btnState = PBS_DEFAULTED;
1323 DrawThemeBackground(hBtnTheme, hDC, btnPart, btnState, &rcBtn, NULL);
1324 MARGINS btnContentMargins;
1325 if (GetThemeMargins(hBtnTheme, hDC, btnPart, btnState, TMT_CONTENTMARGINS, NULL, &btnContentMargins) == S_OK)
1326 {
1327 rcBtn.left += btnContentMargins.cxLeftWidth;
1328 rcBtn.top += btnContentMargins.cyTopHeight;
1329 rcBtn.right -= btnContentMargins.cxRightWidth;
1330 rcBtn.bottom -= btnContentMargins.cyBottomHeight;
1331 }
1332
1333 LOGFONTW lfBtn;
1334 if ((GetThemeFont(hBtnTheme, hDC, btnPart, btnState, TMT_FONT, &lfBtn) != S_OK) && textFont)
1335 SelectFont(hDC, textFont);
1336
1337 len = LoadStringW(hDllInst, IDS_OK, (LPWSTR)&szText, 0);
1338 if (len > 0)
1339 DrawThemeText(hBtnTheme, hDC, btnPart, btnState, szText, len, DT_CENTER | DT_VCENTER | textDrawFlags, 0, &rcBtn);
1340 CloseThemeData(hBtnTheme);
1341 }
1342
1343 context.hDC = NULL;
1344 CloseThemeData (context.theme);
1345 CloseThemeData (context.scrolltheme);
1346 ThemeCleanupDrawContext(&context);
1347
1348 /* Cleanup */
1349 DestroyWindow(hwndDummy);
1350 UnregisterClassW(L"DummyPreviewWindowClass", hDllInst);
1351
1352 return S_OK;
1353 }
1354