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