[UXTHEME] Greatly reduce the number of times we open the theme data for the non clien...
[reactos.git] / reactos / dll / win32 / uxtheme / nonclient.c
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 HFONT hMenuFont = NULL;
12 HFONT hMenuFontBold = NULL;
13
14 void InitMenuFont(VOID)
15 {
16 NONCLIENTMETRICS ncm;
17
18 ncm.cbSize = sizeof(NONCLIENTMETRICS);
19
20 if (!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
21 {
22 return;
23 }
24
25 hMenuFont = CreateFontIndirect(&ncm.lfMenuFont);
26
27 ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
28 hMenuFontBold = CreateFontIndirect(&ncm.lfMenuFont);
29 }
30
31 static BOOL
32 IsWindowActive(HWND hWnd, DWORD ExStyle)
33 {
34 BOOL ret;
35
36 if (ExStyle & WS_EX_MDICHILD)
37 {
38 ret = IsChild(GetForegroundWindow(), hWnd);
39 if (ret)
40 ret = (hWnd == (HWND)SendMessageW(GetParent(hWnd), WM_MDIGETACTIVE, 0, 0));
41 }
42 else
43 {
44 ret = (GetForegroundWindow() == hWnd);
45 }
46
47 return ret;
48 }
49
50 BOOL
51 IsScrollBarVisible(HWND hWnd, INT hBar)
52 {
53 SCROLLBARINFO sbi = {sizeof(SCROLLBARINFO)};
54 if(!GetScrollBarInfo(hWnd, hBar, &sbi))
55 return FALSE;
56
57 return !(sbi.rgstate[0] & STATE_SYSTEM_OFFSCREEN);
58 }
59
60 static BOOL
61 UserHasWindowEdge(DWORD Style, DWORD ExStyle)
62 {
63 if (Style & WS_MINIMIZE)
64 return TRUE;
65 if (ExStyle & WS_EX_DLGMODALFRAME)
66 return TRUE;
67 if (ExStyle & WS_EX_STATICEDGE)
68 return FALSE;
69 if (Style & WS_THICKFRAME)
70 return TRUE;
71 Style &= WS_CAPTION;
72 if (Style == WS_DLGFRAME || Style == WS_CAPTION)
73 return TRUE;
74 return FALSE;
75 }
76
77 static HICON
78 UserGetWindowIcon(PDRAW_CONTEXT pcontext)
79 {
80 HICON hIcon = NULL;
81
82 SendMessageTimeout(pcontext->hWnd, WM_GETICON, ICON_SMALL2, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
83
84 if (!hIcon)
85 SendMessageTimeout(pcontext->hWnd, WM_GETICON, ICON_SMALL, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
86
87 if (!hIcon)
88 SendMessageTimeout(pcontext->hWnd, WM_GETICON, ICON_BIG, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon);
89
90 if (!hIcon)
91 hIcon = (HICON)GetClassLong(pcontext->hWnd, GCL_HICONSM);
92
93 if (!hIcon)
94 hIcon = (HICON)GetClassLong(pcontext->hWnd, GCL_HICON);
95
96 // See also win32ss/user/ntuser/nonclient.c!NC_IconForWindow
97 if (!hIcon && !(pcontext->wi.dwExStyle & WS_EX_DLGMODALFRAME))
98 hIcon = LoadIconW(NULL, (LPCWSTR)IDI_WINLOGO);
99
100 return hIcon;
101 }
102
103 HRESULT WINAPI ThemeDrawCaptionText(PDRAW_CONTEXT pcontext, RECT* pRect, int iPartId, int iStateId)
104 {
105 HRESULT hr;
106 HFONT hFont = NULL;
107 HGDIOBJ oldFont = NULL;
108 LOGFONTW logfont;
109 COLORREF textColor;
110 COLORREF oldTextColor;
111
112 WCHAR buffer[50];
113 WCHAR *pszText = buffer;
114 INT len;
115
116 len = InternalGetWindowText(pcontext->hWnd, NULL, 0);
117 if (!len)
118 return S_OK;
119
120 len++; /* From now on this is the size of the buffer so include the null */
121
122 if (len > ARRAYSIZE(buffer))
123 {
124 pszText = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
125 if (!pszText)
126 return E_OUTOFMEMORY;
127 }
128
129 InternalGetWindowText(pcontext->hWnd, pszText, len);
130
131 hr = GetThemeSysFont(0,TMT_CAPTIONFONT,&logfont);
132 if(SUCCEEDED(hr))
133 hFont = CreateFontIndirectW(&logfont);
134
135 if(hFont)
136 oldFont = SelectObject(pcontext->hDC, hFont);
137
138 if (!pcontext->Active)
139 textColor = GetSysColor(COLOR_INACTIVECAPTIONTEXT);
140 else
141 textColor = GetSysColor(COLOR_CAPTIONTEXT);
142
143 oldTextColor = SetTextColor(pcontext->hDC, textColor);
144 DrawThemeText(pcontext->theme,
145 pcontext->hDC,
146 iPartId,
147 iStateId,
148 pszText,
149 len - 1,
150 DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS,
151 0,
152 pRect);
153 SetTextColor(pcontext->hDC, oldTextColor);
154
155 if (hFont)
156 {
157 SelectObject(pcontext->hDC, oldFont);
158 DeleteObject(hFont);
159 }
160 if (pszText != buffer)
161 {
162 HeapFree(GetProcessHeap(), 0, pszText);
163 }
164 return S_OK;
165 }
166
167 void
168 ThemeInitDrawContext(PDRAW_CONTEXT pcontext,
169 HWND hWnd,
170 HRGN hRgn)
171 {
172 pcontext->wi.cbSize = sizeof(pcontext->wi);
173 GetWindowInfo(hWnd, &pcontext->wi);
174 pcontext->hWnd = hWnd;
175 pcontext->Active = IsWindowActive(hWnd, pcontext->wi.dwExStyle);
176 pcontext->theme = GetNCCaptionTheme(hWnd, pcontext->wi.dwStyle);
177 pcontext->scrolltheme = GetNCScrollbarTheme(hWnd, pcontext->wi.dwStyle);
178
179 pcontext->CaptionHeight = pcontext->wi.cyWindowBorders;
180 pcontext->CaptionHeight += GetSystemMetrics(pcontext->wi.dwExStyle & WS_EX_TOOLWINDOW ? SM_CYSMCAPTION : SM_CYCAPTION );
181
182 if(hRgn <= (HRGN)1)
183 {
184 hRgn = CreateRectRgnIndirect(&pcontext->wi.rcWindow);
185 }
186 pcontext->hRgn = hRgn;
187
188 pcontext->hDC = GetDCEx(hWnd, hRgn, DCX_WINDOW | DCX_INTERSECTRGN | DCX_USESTYLE | DCX_KEEPCLIPRGN);
189 }
190
191 void
192 ThemeCleanupDrawContext(PDRAW_CONTEXT pcontext)
193 {
194 ReleaseDC(pcontext->hWnd ,pcontext->hDC);
195
196 if(pcontext->hRgn != NULL)
197 {
198 DeleteObject(pcontext->hRgn);
199 }
200 }
201
202 static void
203 ThemeStartBufferedPaint(PDRAW_CONTEXT pcontext, int cx, int cy)
204 {
205 HBITMAP hbmp;
206
207 pcontext->hDCScreen = pcontext->hDC;
208 pcontext->hDC = CreateCompatibleDC(pcontext->hDCScreen);
209 hbmp = CreateCompatibleBitmap(pcontext->hDCScreen, cx, cy);
210 pcontext->hbmpOld = (HBITMAP)SelectObject(pcontext->hDC, hbmp);
211 }
212
213 static void
214 ThemeEndBufferedPaint(PDRAW_CONTEXT pcontext, int x, int y, int cx, int cy)
215 {
216 HBITMAP hbmp;
217 BitBlt(pcontext->hDCScreen, 0, 0, cx, cy, pcontext->hDC, x, y, SRCCOPY);
218 hbmp = (HBITMAP) SelectObject(pcontext->hDC, pcontext->hbmpOld);
219 DeleteObject(pcontext->hDC);
220 DeleteObject(hbmp);
221
222 pcontext->hDC = pcontext->hDCScreen;
223 }
224
225 static void
226 ThemeDrawCaptionButton(PDRAW_CONTEXT pcontext,
227 RECT* prcCurrent,
228 CAPTIONBUTTON buttonId,
229 INT iStateId)
230 {
231 RECT rcPart;
232 INT ButtonWidth, ButtonHeight, iPartId;
233 SIZE ButtonSize;
234
235 switch(buttonId)
236 {
237 case CLOSEBUTTON:
238 iPartId = pcontext->wi.dwExStyle & WS_EX_TOOLWINDOW ? WP_SMALLCLOSEBUTTON : WP_CLOSEBUTTON;
239 break;
240
241 case MAXBUTTON:
242 if (!(pcontext->wi.dwStyle & WS_MAXIMIZEBOX))
243 {
244 if (!(pcontext->wi.dwStyle & WS_MINIMIZEBOX))
245 return;
246 else
247 iStateId = BUTTON_DISABLED;
248 }
249
250 iPartId = pcontext->wi.dwStyle & WS_MAXIMIZE ? WP_RESTOREBUTTON : WP_MAXBUTTON;
251 break;
252
253 case MINBUTTON:
254 if (!(pcontext->wi.dwStyle & WS_MINIMIZEBOX))
255 {
256 if (!(pcontext->wi.dwStyle & WS_MAXIMIZEBOX))
257 return;
258 else
259 iStateId = BUTTON_DISABLED;
260 }
261
262 iPartId = pcontext->wi.dwStyle & WS_MINIMIZE ? WP_RESTOREBUTTON : WP_MINBUTTON;
263 break;
264
265 default:
266 //FIXME: Implement Help Button
267 return;
268 }
269
270 GetThemePartSize(pcontext->theme, pcontext->hDC, iPartId, 0, NULL, TS_MIN, &ButtonSize);
271
272 ButtonHeight = GetSystemMetrics( pcontext->wi.dwExStyle & WS_EX_TOOLWINDOW ? SM_CYSMSIZE : SM_CYSIZE);
273 ButtonWidth = MulDiv(ButtonSize.cx, ButtonHeight, ButtonSize.cy);
274
275 ButtonHeight -= 4;
276 ButtonWidth -= 4;
277
278 /* Calculate the position */
279 rcPart.top = prcCurrent->top;
280 rcPart.right = prcCurrent->right;
281 rcPart.bottom = rcPart.top + ButtonHeight ;
282 rcPart.left = rcPart.right - ButtonWidth ;
283 prcCurrent->right -= ButtonWidth + BUTTON_GAP_SIZE;
284
285 DrawThemeBackground(pcontext->theme, pcontext->hDC, iPartId, iStateId, &rcPart, NULL);
286 }
287
288 static DWORD
289 ThemeGetButtonState(DWORD htCurrect, DWORD htHot, DWORD htDown, BOOL Active)
290 {
291 if (htHot == htCurrect)
292 return BUTTON_HOT;
293 if (!Active)
294 return BUTTON_INACTIVE;
295 if (htDown == htCurrect)
296 return BUTTON_PRESSED;
297
298 return BUTTON_NORMAL;
299 }
300
301 /* Used only from mouse event handlers */
302 static void
303 ThemeDrawCaptionButtons(PDRAW_CONTEXT pcontext, DWORD htHot, DWORD htDown)
304 {
305 RECT rcCurrent;
306
307 /* Calculate the area of the caption */
308 rcCurrent.top = rcCurrent.left = 0;
309 rcCurrent.right = pcontext->wi.rcWindow.right - pcontext->wi.rcWindow.left;
310 rcCurrent.bottom = pcontext->CaptionHeight;
311
312 /* Add a padding around the objects of the caption */
313 InflateRect(&rcCurrent, -(int)pcontext->wi.cyWindowBorders-BUTTON_GAP_SIZE,
314 -(int)pcontext->wi.cyWindowBorders-BUTTON_GAP_SIZE);
315
316 /* Draw the buttons */
317 ThemeDrawCaptionButton(pcontext, &rcCurrent, CLOSEBUTTON,
318 ThemeGetButtonState(HTCLOSE, htHot, htDown, pcontext->Active));
319 ThemeDrawCaptionButton(pcontext, &rcCurrent, MAXBUTTON,
320 ThemeGetButtonState(HTMAXBUTTON, htHot, htDown, pcontext->Active));
321 ThemeDrawCaptionButton(pcontext, &rcCurrent, MINBUTTON,
322 ThemeGetButtonState(HTMINBUTTON, htHot, htDown, pcontext->Active));
323 ThemeDrawCaptionButton(pcontext, &rcCurrent, HELPBUTTON,
324 ThemeGetButtonState(HTHELP, htHot, htDown, pcontext->Active));
325 }
326
327 /* Used from WM_NCPAINT and WM_NCACTIVATE handlers */
328 static void
329 ThemeDrawCaption(PDRAW_CONTEXT pcontext, RECT* prcCurrent)
330 {
331 RECT rcPart;
332 int iPart, iState;
333 HICON hIcon;
334
335 // See also win32ss/user/ntuser/nonclient.c!UserDrawCaptionBar
336 // and win32ss/user/ntuser/nonclient.c!UserDrawCaption
337 if ((pcontext->wi.dwStyle & WS_SYSMENU) && !(pcontext->wi.dwExStyle & WS_EX_TOOLWINDOW))
338 hIcon = UserGetWindowIcon(pcontext);
339 else
340 hIcon = NULL;
341
342 /* Get the caption part and state id */
343 if (pcontext->wi.dwStyle & WS_MINIMIZE)
344 iPart = WP_MINCAPTION;
345 else if (pcontext->wi.dwExStyle & WS_EX_TOOLWINDOW)
346 iPart = WP_SMALLCAPTION;
347 else if (pcontext->wi.dwStyle & WS_MAXIMIZE)
348 iPart = WP_MAXCAPTION;
349 else
350 iPart = WP_CAPTION;
351
352 iState = pcontext->Active ? FS_ACTIVE : FS_INACTIVE;
353
354 /* Draw the caption background */
355 rcPart = *prcCurrent;
356 rcPart.bottom = rcPart.top + pcontext->CaptionHeight;
357 prcCurrent->top = rcPart.bottom;
358 DrawThemeBackground(pcontext->theme, pcontext->hDC,iPart,iState,&rcPart,NULL);
359
360 /* Add a padding around the objects of the caption */
361 InflateRect(&rcPart, -(int)pcontext->wi.cyWindowBorders-BUTTON_GAP_SIZE,
362 -(int)pcontext->wi.cyWindowBorders-BUTTON_GAP_SIZE);
363
364 /* Draw the caption buttons */
365 if (pcontext->wi.dwStyle & WS_SYSMENU)
366 {
367 iState = pcontext->Active ? BUTTON_NORMAL : BUTTON_INACTIVE;
368
369 ThemeDrawCaptionButton(pcontext, &rcPart, CLOSEBUTTON, iState);
370 ThemeDrawCaptionButton(pcontext, &rcPart, MAXBUTTON, iState);
371 ThemeDrawCaptionButton(pcontext, &rcPart, MINBUTTON, iState);
372 ThemeDrawCaptionButton(pcontext, &rcPart, HELPBUTTON, iState);
373 }
374
375 rcPart.top += 3 ;
376
377 /* Draw the icon */
378 if (hIcon)
379 {
380 int IconHeight = GetSystemMetrics(SM_CYSMICON);
381 int IconWidth = GetSystemMetrics(SM_CXSMICON);
382 DrawIconEx(pcontext->hDC, rcPart.left, rcPart.top , hIcon, IconWidth, IconHeight, 0, NULL, DI_NORMAL);
383 rcPart.left += IconWidth + 4;
384 }
385
386 rcPart.right -= 4;
387
388 /* Draw the caption */
389 ThemeDrawCaptionText(pcontext, &rcPart, iPart, iState);
390 }
391
392 static void
393 ThemeDrawBorders(PDRAW_CONTEXT pcontext, RECT* prcCurrent)
394 {
395 RECT rcPart;
396 int iState = pcontext->Active ? FS_ACTIVE : FS_INACTIVE;
397
398 /* Draw the bottom border */
399 rcPart = *prcCurrent;
400 rcPart.top = rcPart.bottom - pcontext->wi.cyWindowBorders;
401 prcCurrent->bottom = rcPart.top;
402 DrawThemeBackground(pcontext->theme, pcontext->hDC, WP_FRAMEBOTTOM, iState, &rcPart, NULL);
403
404 /* Draw the left border */
405 rcPart = *prcCurrent;
406 rcPart.right = rcPart.left + pcontext->wi.cxWindowBorders ;
407 prcCurrent->left = rcPart.right;
408 DrawThemeBackground(pcontext->theme, pcontext->hDC,WP_FRAMELEFT, iState, &rcPart, NULL);
409
410 /* Draw the right border */
411 rcPart = *prcCurrent;
412 rcPart.left = rcPart.right - pcontext->wi.cxWindowBorders;
413 prcCurrent->right = rcPart.left;
414 DrawThemeBackground(pcontext->theme, pcontext->hDC,WP_FRAMERIGHT, iState, &rcPart, NULL);
415 }
416
417 static void
418 DrawClassicFrame(PDRAW_CONTEXT context, RECT* prcCurrent)
419 {
420 /* Draw outer edge */
421 if (UserHasWindowEdge(context->wi.dwStyle, context->wi.dwExStyle))
422 {
423 DrawEdge(context->hDC, prcCurrent, EDGE_RAISED, BF_RECT | BF_ADJUST);
424 }
425 else if (context->wi.dwExStyle & WS_EX_STATICEDGE)
426 {
427 DrawEdge(context->hDC, prcCurrent, BDR_SUNKENINNER, BF_RECT | BF_ADJUST | BF_FLAT);
428 }
429
430 /* Firstly the "thick" frame */
431 if ((context->wi.dwStyle & WS_THICKFRAME) && !(context->wi.dwStyle & WS_MINIMIZE))
432 {
433 INT Width =
434 (GetSystemMetrics(SM_CXFRAME) - GetSystemMetrics(SM_CXDLGFRAME)) *
435 GetSystemMetrics(SM_CXBORDER);
436 INT Height =
437 (GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYDLGFRAME)) *
438 GetSystemMetrics(SM_CYBORDER);
439
440 SelectObject(context->hDC, GetSysColorBrush(
441 context->Active ? COLOR_ACTIVEBORDER : COLOR_INACTIVEBORDER));
442
443 /* Draw frame */
444 PatBlt(context->hDC, prcCurrent->left, prcCurrent->top,
445 prcCurrent->right - prcCurrent->left, Height, PATCOPY);
446 PatBlt(context->hDC, prcCurrent->left, prcCurrent->top,
447 Width, prcCurrent->bottom - prcCurrent->top, PATCOPY);
448 PatBlt(context->hDC, prcCurrent->left, prcCurrent->bottom - 1,
449 prcCurrent->right - prcCurrent->left, -Height, PATCOPY);
450 PatBlt(context->hDC, prcCurrent->right - 1, prcCurrent->top,
451 -Width, prcCurrent->bottom - prcCurrent->top, PATCOPY);
452
453 InflateRect(prcCurrent, -Width, -Height);
454 }
455
456 /* Now the other bit of the frame */
457 if (context->wi.dwStyle & (WS_DLGFRAME | WS_BORDER) || (context->wi.dwExStyle & WS_EX_DLGMODALFRAME))
458 {
459 INT Width = GetSystemMetrics(SM_CXBORDER);
460 INT Height = GetSystemMetrics(SM_CYBORDER);
461
462 SelectObject(context->hDC, GetSysColorBrush(
463 (context->wi.dwExStyle & (WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE)) ? COLOR_3DFACE :
464 (context->wi.dwExStyle & WS_EX_STATICEDGE) ? COLOR_WINDOWFRAME :
465 (context->wi.dwStyle & (WS_DLGFRAME | WS_THICKFRAME)) ? COLOR_3DFACE :
466 COLOR_WINDOWFRAME));
467
468 /* Draw frame */
469 PatBlt(context->hDC, prcCurrent->left, prcCurrent->top,
470 prcCurrent->right - prcCurrent->left, Height, PATCOPY);
471 PatBlt(context->hDC, prcCurrent->left, prcCurrent->top,
472 Width, prcCurrent->bottom - prcCurrent->top, PATCOPY);
473 PatBlt(context->hDC, prcCurrent->left, prcCurrent->bottom - 1,
474 prcCurrent->right - prcCurrent->left, -Height, PATCOPY);
475 PatBlt(context->hDC, prcCurrent->right - 1, prcCurrent->top,
476 -Width, prcCurrent->bottom - prcCurrent->top, PATCOPY);
477
478 InflateRect(prcCurrent, -Width, -Height);
479 }
480 }
481
482 static void ThemeDrawMenuBar(PDRAW_CONTEXT pcontext, RECT* prcCurrent)
483 {
484 /* Let the window manager paint the menu */
485 prcCurrent->top += PaintMenuBar(pcontext->hWnd,
486 pcontext->hDC,
487 pcontext->wi.cxWindowBorders,
488 pcontext->wi.cxWindowBorders,
489 prcCurrent->top,
490 pcontext->Active);
491 }
492
493 static void ThemeDrawScrollBarsGrip(PDRAW_CONTEXT pcontext, RECT* prcCurrent)
494 {
495 RECT rcPart;
496 HWND hwndParent;
497 RECT ParentClientRect;
498 DWORD ParentStyle;
499
500 rcPart = *prcCurrent;
501
502 if (pcontext->wi.dwExStyle & WS_EX_LEFTSCROLLBAR)
503 rcPart.right = rcPart.left + GetSystemMetrics(SM_CXVSCROLL);
504 else
505 rcPart.left = rcPart.right - GetSystemMetrics(SM_CXVSCROLL);
506
507 rcPart.top = rcPart.bottom - GetSystemMetrics(SM_CYHSCROLL);
508
509 FillRect(pcontext->hDC, &rcPart, GetSysColorBrush(COLOR_BTNFACE));
510
511 hwndParent = GetParent(pcontext->hWnd);
512 GetClientRect(hwndParent, &ParentClientRect);
513 ParentStyle = GetWindowLongW(hwndParent, GWL_STYLE);
514
515 if (HASSIZEGRIP(pcontext->wi.dwStyle, pcontext->wi.dwExStyle, ParentStyle, pcontext->wi.rcWindow, ParentClientRect))
516 {
517 int iState;
518 if (pcontext->wi.dwExStyle & WS_EX_LEFTSCROLLBAR)
519 iState = pcontext->wi.dwExStyle & WS_EX_LEFTSCROLLBAR;
520 else
521 iState = SZB_RIGHTALIGN;
522 DrawThemeBackground(pcontext->scrolltheme, pcontext->hDC, SBP_SIZEBOX, iState, &rcPart, NULL);
523 }
524 }
525
526 static void
527 ThemePaintWindow(PDRAW_CONTEXT pcontext, RECT* prcCurrent, BOOL bDoDoubleBuffering)
528 {
529 if(!(pcontext->wi.dwStyle & WS_VISIBLE))
530 return;
531
532 if((pcontext->wi.dwStyle & WS_CAPTION)==WS_CAPTION)
533 {
534 if (bDoDoubleBuffering)
535 ThemeStartBufferedPaint(pcontext, prcCurrent->right, pcontext->CaptionHeight);
536 ThemeDrawCaption(pcontext, prcCurrent);
537 if (bDoDoubleBuffering)
538 ThemeEndBufferedPaint(pcontext, 0, 0, prcCurrent->right, pcontext->CaptionHeight);
539 ThemeDrawBorders(pcontext, prcCurrent);
540 }
541 else
542 {
543 DrawClassicFrame(pcontext, prcCurrent);
544 }
545
546 if(pcontext->wi.dwStyle & WS_MINIMIZE)
547 return;
548
549 if(HAS_MENU(pcontext->hWnd, pcontext->wi.dwStyle))
550 ThemeDrawMenuBar(pcontext, prcCurrent);
551
552 if (pcontext->wi.dwExStyle & WS_EX_CLIENTEDGE)
553 DrawEdge(pcontext->hDC, prcCurrent, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
554
555 if((pcontext->wi.dwStyle & WS_HSCROLL) && IsScrollBarVisible(pcontext->hWnd, OBJID_HSCROLL))
556 ThemeDrawScrollBar(pcontext, SB_HORZ , NULL);
557
558 if((pcontext->wi.dwStyle & WS_VSCROLL) && IsScrollBarVisible(pcontext->hWnd, OBJID_VSCROLL))
559 ThemeDrawScrollBar(pcontext, SB_VERT, NULL);
560
561 if((pcontext->wi.dwStyle & (WS_HSCROLL|WS_VSCROLL)) == (WS_HSCROLL|WS_VSCROLL) &&
562 IsScrollBarVisible(pcontext->hWnd, OBJID_HSCROLL) &&
563 IsScrollBarVisible(pcontext->hWnd, OBJID_VSCROLL))
564 {
565 ThemeDrawScrollBarsGrip(pcontext, prcCurrent);
566 }
567 }
568
569 /*
570 * Message handlers
571 */
572
573 static LRESULT
574 ThemeHandleNCPaint(HWND hWnd, HRGN hRgn)
575 {
576 DRAW_CONTEXT context;
577 RECT rcCurrent;
578
579 ThemeInitDrawContext(&context, hWnd, hRgn);
580
581 rcCurrent = context.wi.rcWindow;
582 OffsetRect( &rcCurrent, -context.wi.rcWindow.left, -context.wi.rcWindow.top);
583
584 ThemePaintWindow(&context, &rcCurrent, TRUE);
585 ThemeCleanupDrawContext(&context);
586
587 return 0;
588 }
589
590 static LRESULT
591 ThemeHandleNcMouseMove(HWND hWnd, DWORD ht, POINT* pt)
592 {
593 DRAW_CONTEXT context;
594 TRACKMOUSEEVENT tme;
595 DWORD style;
596 PWND_DATA pwndData;
597
598 /* First of all check if we have something to do here */
599 style = GetWindowLongW(hWnd, GWL_STYLE);
600 if((style & (WS_CAPTION|WS_HSCROLL|WS_VSCROLL))==0)
601 return 0;
602
603 /* Get theme data for this window */
604 pwndData = ThemeGetWndData(hWnd);
605 if (pwndData == NULL)
606 return 0;
607
608 /* Begin tracking in the non client area if we are not tracking yet */
609 tme.cbSize = sizeof(TRACKMOUSEEVENT);
610 tme.dwFlags = TME_QUERY;
611 tme.hwndTrack = hWnd;
612 TrackMouseEvent(&tme);
613 if (tme.dwFlags != (TME_LEAVE | TME_NONCLIENT))
614 {
615 tme.hwndTrack = hWnd;
616 tme.dwFlags = TME_LEAVE | TME_NONCLIENT;
617 TrackMouseEvent(&tme);
618 }
619
620 ThemeInitDrawContext(&context, hWnd, 0);
621 if (context.wi.dwStyle & WS_SYSMENU)
622 {
623 if (HT_ISBUTTON(ht) || HT_ISBUTTON(pwndData->lastHitTest))
624 ThemeDrawCaptionButtons(&context, ht, 0);
625 }
626
627 if (context.wi.dwStyle & WS_HSCROLL)
628 {
629 if (ht == HTHSCROLL || pwndData->lastHitTest == HTHSCROLL)
630 ThemeDrawScrollBar(&context, SB_HORZ , ht == HTHSCROLL ? pt : NULL);
631 }
632
633 if (context.wi.dwStyle & WS_VSCROLL)
634 {
635 if (ht == HTVSCROLL || pwndData->lastHitTest == HTVSCROLL)
636 ThemeDrawScrollBar(&context, SB_VERT, ht == HTVSCROLL ? pt : NULL);
637 }
638 ThemeCleanupDrawContext(&context);
639
640 pwndData->lastHitTest = ht;
641
642 return 0;
643 }
644
645 static LRESULT
646 ThemeHandleNcMouseLeave(HWND hWnd)
647 {
648 DRAW_CONTEXT context;
649 DWORD style;
650 PWND_DATA pwndData;
651
652 /* First of all check if we have something to do here */
653 style = GetWindowLongW(hWnd, GWL_STYLE);
654 if((style & (WS_CAPTION|WS_HSCROLL|WS_VSCROLL))==0)
655 return 0;
656
657 /* Get theme data for this window */
658 pwndData = ThemeGetWndData(hWnd);
659 if (pwndData == NULL)
660 return 0;
661
662 ThemeInitDrawContext(&context, hWnd, 0);
663 if (context.wi.dwStyle & WS_SYSMENU && HT_ISBUTTON(pwndData->lastHitTest))
664 ThemeDrawCaptionButtons(&context, 0, 0);
665
666 if (context.wi.dwStyle & WS_HSCROLL && pwndData->lastHitTest == HTHSCROLL)
667 ThemeDrawScrollBar(&context, SB_HORZ, NULL);
668
669 if (context.wi.dwStyle & WS_VSCROLL && pwndData->lastHitTest == HTVSCROLL)
670 ThemeDrawScrollBar(&context, SB_VERT, NULL);
671
672 ThemeCleanupDrawContext(&context);
673
674 pwndData->lastHitTest = HTNOWHERE;
675
676 return 0;
677 }
678
679 static VOID
680 ThemeHandleButton(HWND hWnd, WPARAM wParam)
681 {
682 MSG Msg;
683 BOOL Pressed = TRUE;
684 WPARAM SCMsg, ht;
685 ULONG Style;
686 DRAW_CONTEXT context;
687 PWND_DATA pwndData;
688
689 Style = GetWindowLongW(hWnd, GWL_STYLE);
690 if (!((Style & WS_CAPTION) && (Style & WS_SYSMENU)))
691 return ;
692
693 switch (wParam)
694 {
695 case HTCLOSE:
696 SCMsg = SC_CLOSE;
697 break;
698 case HTMINBUTTON:
699 if (!(Style & WS_MINIMIZEBOX))
700 return;
701 SCMsg = ((Style & WS_MINIMIZE) ? SC_RESTORE : SC_MINIMIZE);
702 break;
703 case HTMAXBUTTON:
704 if (!(Style & WS_MAXIMIZEBOX))
705 return;
706 SCMsg = ((Style & WS_MAXIMIZE) ? SC_RESTORE : SC_MAXIMIZE);
707 break;
708 default :
709 return;
710 }
711
712 /* Get theme data for this window */
713 pwndData = ThemeGetWndData(hWnd);
714 if (pwndData == NULL)
715 return;
716
717 ThemeInitDrawContext(&context, hWnd, 0);
718 ThemeDrawCaptionButtons(&context, 0, wParam);
719 pwndData->lastHitTest = wParam;
720
721 SetCapture(hWnd);
722
723 ht = wParam;
724
725 for (;;)
726 {
727 if (GetMessageW(&Msg, 0, WM_MOUSEFIRST, WM_MOUSELAST) <= 0)
728 break;
729
730 if (Msg.message == WM_LBUTTONUP)
731 break;
732
733 if (Msg.message != WM_MOUSEMOVE)
734 continue;
735
736 ht = SendMessage(hWnd, WM_NCHITTEST, 0, MAKELPARAM(Msg.pt.x, Msg.pt.y));
737 Pressed = (ht == wParam);
738
739 /* Only draw the buttons if the hit test changed */
740 if (ht != pwndData->lastHitTest &&
741 (HT_ISBUTTON(ht) || HT_ISBUTTON(pwndData->lastHitTest)))
742 {
743 ThemeDrawCaptionButtons(&context, 0, Pressed ? wParam: 0);
744 pwndData->lastHitTest = ht;
745 }
746 }
747
748 ThemeDrawCaptionButtons(&context, ht, 0);
749 ThemeCleanupDrawContext(&context);
750
751 ReleaseCapture();
752
753 if (Pressed)
754 SendMessageW(hWnd, WM_SYSCOMMAND, SCMsg, 0);
755 }
756
757
758 static LRESULT
759 DefWndNCHitTest(HWND hWnd, POINT Point)
760 {
761 RECT WindowRect;
762 POINT ClientPoint;
763 WINDOWINFO wi;
764
765 wi.cbSize = sizeof(wi);
766 GetWindowInfo(hWnd, &wi);
767
768 if (!PtInRect(&wi.rcWindow, Point))
769 {
770 return HTNOWHERE;
771 }
772 WindowRect = wi.rcWindow;
773
774 if (UserHasWindowEdge(wi.dwStyle, wi.dwExStyle))
775 {
776 LONG XSize, YSize;
777
778 InflateRect(&WindowRect, -(int)wi.cxWindowBorders, -(int)wi.cyWindowBorders);
779 XSize = GetSystemMetrics(SM_CXSIZE) * GetSystemMetrics(SM_CXBORDER);
780 YSize = GetSystemMetrics(SM_CYSIZE) * GetSystemMetrics(SM_CYBORDER);
781 if (!PtInRect(&WindowRect, Point))
782 {
783 BOOL ThickFrame;
784
785 ThickFrame = (wi.dwStyle & WS_THICKFRAME);
786 if (Point.y < WindowRect.top)
787 {
788 if(wi.dwStyle & WS_MINIMIZE)
789 return HTCAPTION;
790 if(!ThickFrame)
791 return HTBORDER;
792 if (Point.x < (WindowRect.left + XSize))
793 return HTTOPLEFT;
794 if (Point.x >= (WindowRect.right - XSize))
795 return HTTOPRIGHT;
796 return HTTOP;
797 }
798 if (Point.y >= WindowRect.bottom)
799 {
800 if(wi.dwStyle & WS_MINIMIZE)
801 return HTCAPTION;
802 if(!ThickFrame)
803 return HTBORDER;
804 if (Point.x < (WindowRect.left + XSize))
805 return HTBOTTOMLEFT;
806 if (Point.x >= (WindowRect.right - XSize))
807 return HTBOTTOMRIGHT;
808 return HTBOTTOM;
809 }
810 if (Point.x < WindowRect.left)
811 {
812 if(wi.dwStyle & WS_MINIMIZE)
813 return HTCAPTION;
814 if(!ThickFrame)
815 return HTBORDER;
816 if (Point.y < (WindowRect.top + YSize))
817 return HTTOPLEFT;
818 if (Point.y >= (WindowRect.bottom - YSize))
819 return HTBOTTOMLEFT;
820 return HTLEFT;
821 }
822 if (Point.x >= WindowRect.right)
823 {
824 if(wi.dwStyle & WS_MINIMIZE)
825 return HTCAPTION;
826 if(!ThickFrame)
827 return HTBORDER;
828 if (Point.y < (WindowRect.top + YSize))
829 return HTTOPRIGHT;
830 if (Point.y >= (WindowRect.bottom - YSize))
831 return HTBOTTOMRIGHT;
832 return HTRIGHT;
833 }
834 }
835 }
836 else
837 {
838 if (wi.dwExStyle & WS_EX_STATICEDGE)
839 InflateRect(&WindowRect, -GetSystemMetrics(SM_CXBORDER),
840 -GetSystemMetrics(SM_CYBORDER));
841 if (!PtInRect(&WindowRect, Point))
842 return HTBORDER;
843 }
844
845 if ((wi.dwStyle & WS_CAPTION) == WS_CAPTION)
846 {
847 if (wi.dwExStyle & WS_EX_TOOLWINDOW)
848 WindowRect.top += GetSystemMetrics(SM_CYSMCAPTION);
849 else
850 WindowRect.top += GetSystemMetrics(SM_CYCAPTION);
851
852 if (!PtInRect(&WindowRect, Point))
853 {
854 INT ButtonWidth;
855
856 if (wi.dwExStyle & WS_EX_TOOLWINDOW)
857 ButtonWidth = GetSystemMetrics(SM_CXSMSIZE);
858 else
859 ButtonWidth = GetSystemMetrics(SM_CXSIZE);
860
861 ButtonWidth -= 4;
862 ButtonWidth += BUTTON_GAP_SIZE;
863
864 if (wi.dwStyle & WS_SYSMENU)
865 {
866 if (wi.dwExStyle & WS_EX_TOOLWINDOW)
867 {
868 WindowRect.right -= ButtonWidth;
869 }
870 else
871 {
872 // if(!(wi.dwExStyle & WS_EX_DLGMODALFRAME))
873 // FIXME: The real test should check whether there is
874 // an icon for the system window, and if so, do the
875 // rect.left increase.
876 // See win32ss/user/user32/windows/nonclient.c!DefWndNCHitTest
877 // and win32ss/user/ntuser/nonclient.c!GetNCHitEx which does
878 // the test better.
879 WindowRect.left += ButtonWidth;
880 WindowRect.right -= ButtonWidth;
881 }
882 }
883 if (Point.x < WindowRect.left)
884 return HTSYSMENU;
885 if (WindowRect.right <= Point.x)
886 return HTCLOSE;
887 if (wi.dwStyle & WS_MAXIMIZEBOX || wi.dwStyle & WS_MINIMIZEBOX)
888 WindowRect.right -= ButtonWidth;
889 if (Point.x >= WindowRect.right)
890 return HTMAXBUTTON;
891 if (wi.dwStyle & WS_MINIMIZEBOX)
892 WindowRect.right -= ButtonWidth;
893 if (Point.x >= WindowRect.right)
894 return HTMINBUTTON;
895 return HTCAPTION;
896 }
897 }
898
899 if(!(wi.dwStyle & WS_MINIMIZE))
900 {
901 HMENU menu;
902
903 ClientPoint = Point;
904 ScreenToClient(hWnd, &ClientPoint);
905 GetClientRect(hWnd, &wi.rcClient);
906
907 if (PtInRect(&wi.rcClient, ClientPoint))
908 {
909 return HTCLIENT;
910 }
911
912 if ((menu = GetMenu(hWnd)) && !(wi.dwStyle & WS_CHILD))
913 {
914 if (Point.x > 0 && Point.x < WindowRect.right && ClientPoint.y < 0)
915 return HTMENU;
916 }
917
918 if (wi.dwExStyle & WS_EX_CLIENTEDGE)
919 {
920 InflateRect(&WindowRect, -2 * GetSystemMetrics(SM_CXBORDER),
921 -2 * GetSystemMetrics(SM_CYBORDER));
922 }
923
924 if ((wi.dwStyle & WS_VSCROLL) && (wi.dwStyle & WS_HSCROLL) &&
925 (WindowRect.bottom - WindowRect.top) > GetSystemMetrics(SM_CYHSCROLL))
926 {
927 RECT ParentRect, TempRect = WindowRect, TempRect2 = WindowRect;
928 HWND Parent = GetParent(hWnd);
929
930 TempRect.bottom -= GetSystemMetrics(SM_CYHSCROLL);
931 if ((wi.dwExStyle & WS_EX_LEFTSCROLLBAR) != 0)
932 TempRect.right = TempRect.left + GetSystemMetrics(SM_CXVSCROLL);
933 else
934 TempRect.left = TempRect.right - GetSystemMetrics(SM_CXVSCROLL);
935 if (PtInRect(&TempRect, Point))
936 return HTVSCROLL;
937
938 TempRect2.top = TempRect2.bottom - GetSystemMetrics(SM_CYHSCROLL);
939 if ((wi.dwExStyle & WS_EX_LEFTSCROLLBAR) != 0)
940 TempRect2.left += GetSystemMetrics(SM_CXVSCROLL);
941 else
942 TempRect2.right -= GetSystemMetrics(SM_CXVSCROLL);
943 if (PtInRect(&TempRect2, Point))
944 return HTHSCROLL;
945
946 TempRect.top = TempRect2.top;
947 TempRect.bottom = TempRect2.bottom;
948 if(Parent)
949 GetClientRect(Parent, &ParentRect);
950 if (PtInRect(&TempRect, Point) && HASSIZEGRIP(wi.dwStyle, wi.dwExStyle,
951 GetWindowLongW(Parent, GWL_STYLE), wi.rcWindow, ParentRect))
952 {
953 if ((wi.dwExStyle & WS_EX_LEFTSCROLLBAR) != 0)
954 return HTBOTTOMLEFT;
955 else
956 return HTBOTTOMRIGHT;
957 }
958 }
959 else
960 {
961 if (wi.dwStyle & WS_VSCROLL)
962 {
963 RECT TempRect = WindowRect;
964
965 if ((wi.dwExStyle & WS_EX_LEFTSCROLLBAR) != 0)
966 TempRect.right = TempRect.left + GetSystemMetrics(SM_CXVSCROLL);
967 else
968 TempRect.left = TempRect.right - GetSystemMetrics(SM_CXVSCROLL);
969 if (PtInRect(&TempRect, Point))
970 return HTVSCROLL;
971 }
972 else if (wi.dwStyle & WS_HSCROLL)
973 {
974 RECT TempRect = WindowRect;
975 TempRect.top = TempRect.bottom - GetSystemMetrics(SM_CYHSCROLL);
976 if (PtInRect(&TempRect, Point))
977 return HTHSCROLL;
978 }
979 }
980 }
981
982 return HTNOWHERE;
983 }
984
985 LRESULT CALLBACK
986 ThemeWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, WNDPROC DefWndProc)
987 {
988 switch(Msg)
989 {
990 case WM_NCPAINT:
991 return ThemeHandleNCPaint(hWnd, (HRGN)wParam);
992 //
993 // WM_NCUAHDRAWCAPTION : wParam are DC_* flags.
994 //
995 case WM_NCUAHDRAWCAPTION:
996 //
997 // WM_NCUAHDRAWFRAME : wParam is HDC, lParam are DC_ACTIVE and or DC_REDRAWHUNGWND.
998 //
999 case WM_NCUAHDRAWFRAME:
1000 case WM_NCACTIVATE:
1001
1002 if ((GetWindowLongW(hWnd, GWL_STYLE) & WS_CAPTION) != WS_CAPTION)
1003 return TRUE;
1004
1005 ThemeHandleNCPaint(hWnd, (HRGN)1);
1006 return TRUE;
1007 case WM_NCMOUSEMOVE:
1008 {
1009 POINT Point;
1010 Point.x = GET_X_LPARAM(lParam);
1011 Point.y = GET_Y_LPARAM(lParam);
1012 return ThemeHandleNcMouseMove(hWnd, wParam, &Point);
1013 }
1014 case WM_NCMOUSELEAVE:
1015 return ThemeHandleNcMouseLeave(hWnd);
1016 case WM_NCLBUTTONDOWN:
1017 switch (wParam)
1018 {
1019 case HTMINBUTTON:
1020 case HTMAXBUTTON:
1021 case HTCLOSE:
1022 {
1023 ThemeHandleButton(hWnd, wParam);
1024 return 0;
1025 }
1026 default:
1027 return DefWndProc(hWnd, Msg, wParam, lParam);
1028 }
1029 case WM_NCHITTEST:
1030 {
1031 POINT Point;
1032 Point.x = GET_X_LPARAM(lParam);
1033 Point.y = GET_Y_LPARAM(lParam);
1034 return DefWndNCHitTest(hWnd, Point);
1035 }
1036 case WM_SYSCOMMAND:
1037 {
1038 if((wParam & 0xfff0) == SC_VSCROLL ||
1039 (wParam & 0xfff0) == SC_HSCROLL)
1040 {
1041 POINT Pt;
1042 Pt.x = (short)LOWORD(lParam);
1043 Pt.y = (short)HIWORD(lParam);
1044 NC_TrackScrollBar(hWnd, wParam, Pt);
1045 return 0;
1046 }
1047 else
1048 {
1049 return DefWndProc(hWnd, Msg, wParam, lParam);
1050 }
1051 }
1052 default:
1053 return DefWndProc(hWnd, Msg, wParam, lParam);
1054 }
1055 }
1056
1057 HRESULT WINAPI DrawNCPreview(HDC hDC,
1058 DWORD DNCP_Flag,
1059 LPRECT prcPreview,
1060 LPCWSTR pszThemeFileName,
1061 LPCWSTR pszColorName,
1062 LPCWSTR pszSizeName,
1063 PNONCLIENTMETRICSW pncMetrics,
1064 COLORREF* lpaRgbValues)
1065 {
1066 WNDCLASSEXW DummyPreviewWindowClass;
1067 HWND hwndDummy;
1068 HRESULT hres;
1069 HTHEMEFILE hThemeFile;
1070 DRAW_CONTEXT context;
1071 RECT rcCurrent;
1072
1073 /* FIXME: We also need to implement drawing the rest of the preview windows
1074 * and make use of the ncmetrics and colors passed as parameters */
1075
1076 /* Create a dummy window that will be used to trick the paint funtions */
1077 memset(&DummyPreviewWindowClass, 0, sizeof(DummyPreviewWindowClass));
1078 DummyPreviewWindowClass.cbSize = sizeof(DummyPreviewWindowClass);
1079 DummyPreviewWindowClass.lpszClassName = L"DummyPreviewWindowClass";
1080 DummyPreviewWindowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
1081 DummyPreviewWindowClass.hInstance = hDllInst;
1082 DummyPreviewWindowClass.lpfnWndProc = DefWindowProcW;
1083 if (!RegisterClassExW(&DummyPreviewWindowClass))
1084 return E_FAIL;
1085
1086 hwndDummy = CreateWindowExW(0, L"DummyPreviewWindowClass", L"Active window", WS_OVERLAPPEDWINDOW,30,30,300,150,0,0,hDllInst,NULL);
1087 if (!hwndDummy)
1088 return E_FAIL;
1089
1090 hres = OpenThemeFile(pszThemeFileName, pszColorName, pszSizeName, &hThemeFile,0);
1091 if (FAILED(hres))
1092 return hres;
1093
1094 /* Initialize the special draw context for the preview */
1095 context.hDC = hDC;
1096 context.hWnd = hwndDummy;
1097 context.theme = OpenThemeDataFromFile(hThemeFile, hwndDummy, L"WINDOW", 0);
1098 if (!context.theme)
1099 return E_FAIL;
1100 context.scrolltheme = OpenThemeDataFromFile(hThemeFile, hwndDummy, L"SCROLLBAR", 0);
1101 if (!context.scrolltheme)
1102 return E_FAIL;
1103 context.Active = TRUE;
1104 context.wi.cbSize = sizeof(context.wi);
1105 if (!GetWindowInfo(hwndDummy, &context.wi))
1106 return E_FAIL;
1107 context.wi.dwStyle |= WS_VISIBLE;
1108 context.CaptionHeight = context.wi.cyWindowBorders;
1109 context.CaptionHeight += GetSystemMetrics(context.wi.dwExStyle & WS_EX_TOOLWINDOW ? SM_CYSMCAPTION : SM_CYCAPTION );
1110 context.hRgn = CreateRectRgnIndirect(&context.wi.rcWindow);
1111
1112 /* Paint the window on the preview hDC */
1113 rcCurrent = context.wi.rcWindow;
1114 ThemePaintWindow(&context, &rcCurrent, FALSE);
1115
1116 context.hDC = NULL;
1117 CloseThemeData (context.theme);
1118 CloseThemeData (context.scrolltheme);
1119 ThemeCleanupDrawContext(&context);
1120
1121 /* Cleanup */
1122 DestroyWindow(hwndDummy);
1123 UnregisterClassW(L"DummyPreviewWindowClass", hDllInst);
1124
1125 return S_OK;
1126 }