xref: /reactos/dll/win32/uxtheme/nonclient.c (revision c4f42b5a)
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