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