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