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