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