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