1/////////////////////////////////////////////////////////////////////////////// 2// Name: src/msw/renderer.cpp 3// Purpose: implementation of wxRendererNative for Windows 4// Author: Vadim Zeitlin 5// Modified by: 6// Created: 20.07.2003 7// RCS-ID: $Id: renderer.cpp 53524 2008-05-10 00:05:20Z RD $ 8// Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org> 9// License: wxWindows licence 10/////////////////////////////////////////////////////////////////////////////// 11 12// ============================================================================ 13// declarations 14// ============================================================================ 15 16// ---------------------------------------------------------------------------- 17// headers 18// ---------------------------------------------------------------------------- 19 20// for compilers that support precompilation, includes "wx.h". 21#include "wx/wxprec.h" 22 23#ifdef __BORLANDC__ 24 #pragma hdrstop 25#endif 26 27#ifndef WX_PRECOMP 28 #include "wx/string.h" 29 #include "wx/window.h" 30 #include "wx/dc.h" 31 #include "wx/settings.h" 32#endif //WX_PRECOMP 33 34#include "wx/splitter.h" 35#include "wx/renderer.h" 36#include "wx/msw/private.h" 37#include "wx/msw/uxtheme.h" 38 39#if wxUSE_GRAPHICS_CONTEXT 40// TODO remove this dependency (gdiplus needs the macros) 41#ifndef max 42#define max(a,b) (((a) > (b)) ? (a) : (b)) 43#endif 44 45#ifndef min 46#define min(a,b) (((a) < (b)) ? (a) : (b)) 47#endif 48 49#include "gdiplus.h" 50using namespace Gdiplus; 51#endif 52 53// tmschema.h is in Win32 Platform SDK and might not be available with earlier 54// compilers 55#ifndef CP_DROPDOWNBUTTON 56 #define BP_PUSHBUTTON 1 57 #define BP_RADIOBUTTON 2 58 #define BP_CHECKBOX 3 59 #define RBS_UNCHECKEDNORMAL 1 60 #define RBS_CHECKEDNORMAL (RBS_UNCHECKEDNORMAL + 4) 61 #define RBS_MIXEDNORMAL (RBS_CHECKEDNORMAL + 4) 62 #define CBS_UNCHECKEDNORMAL 1 63 #define CBS_CHECKEDNORMAL (CBS_UNCHECKEDNORMAL + 4) 64 #define CBS_MIXEDNORMAL (CBS_CHECKEDNORMAL + 4) 65 66 #define PBS_NORMAL 1 67 #define PBS_HOT 2 68 #define PBS_PRESSED 3 69 #define PBS_DISABLED 4 70 #define PBS_DEFAULTED 5 71 72 #define CP_DROPDOWNBUTTON 1 73 74 #define CBXS_NORMAL 1 75 #define CBXS_HOT 2 76 #define CBXS_PRESSED 3 77 #define CBXS_DISABLED 4 78 79 #define TVP_GLYPH 2 80 81 #define GLPS_CLOSED 1 82 #define GLPS_OPENED 2 83 84 #define HP_HEADERITEM 1 85 86 #define HIS_NORMAL 1 87 #define HIS_HOT 2 88 #define HIS_PRESSED 3 89 90 #define TMT_HEIGHT 2417 91 92 #define HP_HEADERSORTARROW 4 93 #define HSAS_SORTEDUP 1 94 #define HSAS_SORTEDDOWN 2 95 96 #define EP_EDITTEXT 1 97 #define ETS_NORMAL 1 98 #define ETS_HOT 2 99 #define ETS_SELECTED 3 100 #define ETS_DISABLED 4 101 #define ETS_FOCUSED 5 102 #define ETS_READONLY 6 103 #define ETS_ASSIST 7 104 #define TMT_FILLCOLOR 3802 105 #define TMT_TEXTCOLOR 3803 106 #define TMT_BORDERCOLOR 3801 107 #define TMT_EDGEFILLCOLOR 3808 108#endif 109 110#if defined(__WXWINCE__) && !defined(DFCS_FLAT) 111 #define DFCS_FLAT 0 112#endif 113 114 115// ---------------------------------------------------------------------------- 116// If the DC is a wxGCDC then pull out the HDC from the GraphicsContext when 117// it is needed, and handle the Release when done. 118 119class GraphicsHDC 120{ 121public: 122 GraphicsHDC(wxDC* dc) 123 { 124#if wxUSE_GRAPHICS_CONTEXT 125 m_graphics = NULL; 126 wxGCDC* gcdc = wxDynamicCast(dc, wxGCDC); 127 if (gcdc) { 128 m_graphics = (Graphics*)gcdc->GetGraphicsContext()->GetNativeContext(); 129 m_hdc = m_graphics->GetHDC(); 130 } 131 else 132#endif 133 m_hdc = GetHdcOf(*dc); 134 } 135 136 ~GraphicsHDC() 137 { 138#if wxUSE_GRAPHICS_CONTEXT 139 if (m_graphics) 140 m_graphics->ReleaseHDC(m_hdc); 141#endif 142 } 143 144 operator HDC() const { return m_hdc; } 145 146private: 147 HDC m_hdc; 148#if wxUSE_GRAPHICS_CONTEXT 149 Graphics* m_graphics; 150#endif 151}; 152 153// ---------------------------------------------------------------------------- 154// wxRendererMSW: wxRendererNative implementation for "old" Win32 systems 155// ---------------------------------------------------------------------------- 156 157class WXDLLEXPORT wxRendererMSW : public wxDelegateRendererNative 158{ 159public: 160 wxRendererMSW() { } 161 162 static wxRendererNative& Get(); 163 164 virtual void DrawComboBoxDropButton(wxWindow *win, 165 wxDC& dc, 166 const wxRect& rect, 167 int flags = 0); 168 169 virtual void DrawPushButton(wxWindow *win, 170 wxDC& dc, 171 const wxRect& rect, 172 int flags = 0); 173 174private: 175 DECLARE_NO_COPY_CLASS(wxRendererMSW) 176}; 177 178// ---------------------------------------------------------------------------- 179// wxRendererXP: wxRendererNative implementation for Windows XP and later 180// ---------------------------------------------------------------------------- 181 182#if wxUSE_UXTHEME 183 184class WXDLLEXPORT wxRendererXP : public wxDelegateRendererNative 185{ 186public: 187 wxRendererXP() : wxDelegateRendererNative(wxRendererMSW::Get()) { } 188 189 static wxRendererNative& Get(); 190 191 virtual int DrawHeaderButton(wxWindow *win, 192 wxDC& dc, 193 const wxRect& rect, 194 int flags = 0, 195 wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE, 196 wxHeaderButtonParams* params = NULL); 197 virtual int GetHeaderButtonHeight(wxWindow *win); 198 199 virtual void DrawTreeItemButton(wxWindow *win, 200 wxDC& dc, 201 const wxRect& rect, 202 int flags = 0); 203 virtual void DrawSplitterBorder(wxWindow *win, 204 wxDC& dc, 205 const wxRect& rect, 206 int flags = 0); 207 virtual void DrawSplitterSash(wxWindow *win, 208 wxDC& dc, 209 const wxSize& size, 210 wxCoord position, 211 wxOrientation orient, 212 int flags = 0); 213 virtual void DrawComboBoxDropButton(wxWindow *win, 214 wxDC& dc, 215 const wxRect& rect, 216 int flags = 0); 217 virtual void DrawCheckBox(wxWindow *win, 218 wxDC& dc, 219 const wxRect& rect, 220 int flags = 0); 221 222 virtual void DrawPushButton(wxWindow *win, 223 wxDC& dc, 224 const wxRect& rect, 225 int flags = 0); 226 227 virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win); 228private: 229 DECLARE_NO_COPY_CLASS(wxRendererXP) 230}; 231 232#endif // wxUSE_UXTHEME 233 234// ============================================================================ 235// wxRendererNative and wxRendererMSW implementation 236// ============================================================================ 237 238/* static */ 239wxRendererNative& wxRendererNative::GetDefault() 240{ 241#if wxUSE_UXTHEME 242 wxUxThemeEngine *themeEngine = wxUxThemeEngine::Get(); 243 if ( themeEngine && themeEngine->IsAppThemed() ) 244 return wxRendererXP::Get(); 245#endif // wxUSE_UXTHEME 246 247 return wxRendererMSW::Get(); 248} 249 250/* static */ 251wxRendererNative& wxRendererMSW::Get() 252{ 253 static wxRendererMSW s_rendererMSW; 254 255 return s_rendererMSW; 256} 257 258void 259wxRendererMSW::DrawComboBoxDropButton(wxWindow * WXUNUSED(win), 260 wxDC& dc, 261 const wxRect& rect, 262 int flags) 263{ 264 RECT r; 265 r.left = rect.GetLeft(); 266 r.top = rect.GetTop(); 267 r.bottom = rect.y + rect.height; 268 r.right = rect.x + rect.width; 269 270 int style = DFCS_SCROLLCOMBOBOX; 271 if ( flags & wxCONTROL_DISABLED ) 272 style |= DFCS_INACTIVE; 273 if ( flags & wxCONTROL_PRESSED ) 274 style |= DFCS_PUSHED | DFCS_FLAT; 275 276 ::DrawFrameControl(GraphicsHDC(&dc), &r, DFC_SCROLL, style); 277} 278 279void 280wxRendererMSW::DrawPushButton(wxWindow * WXUNUSED(win), 281 wxDC& dc, 282 const wxRect& rectOrig, 283 int flags) 284{ 285 wxRect rect(rectOrig); 286 287 int style = DFCS_BUTTONPUSH; 288 if ( flags & wxCONTROL_DISABLED ) 289 style |= DFCS_INACTIVE; 290 if ( flags & wxCONTROL_PRESSED ) 291 style |= DFCS_PUSHED | DFCS_FLAT; 292 if ( flags & wxCONTROL_ISDEFAULT ) 293 { 294 // DrawFrameControl() doesn't seem to support default buttons so we 295 // have to draw the border ourselves 296 wxDCPenChanger pen(dc, *wxBLACK_PEN); 297 wxDCBrushChanger brush(dc, *wxTRANSPARENT_BRUSH); 298 dc.DrawRectangle(rect); 299 rect.Deflate(1); 300 } 301 302 RECT rc; 303 wxCopyRectToRECT(rect, rc); 304 305 ::DrawFrameControl(GraphicsHDC(&dc), &rc, DFC_BUTTON, style); 306} 307 308// ============================================================================ 309// wxRendererXP implementation 310// ============================================================================ 311 312#if wxUSE_UXTHEME 313 314/* static */ 315wxRendererNative& wxRendererXP::Get() 316{ 317 static wxRendererXP s_rendererXP; 318 319 return s_rendererXP; 320} 321 322// NOTE: There is no guarantee that the button drawn fills the entire rect (XP 323// default theme, for example), so the caller should have cleared button's 324// background before this call. This is quite likely a wxMSW-specific thing. 325void 326wxRendererXP::DrawComboBoxDropButton(wxWindow * win, 327 wxDC& dc, 328 const wxRect& rect, 329 int flags) 330{ 331 wxUxThemeHandle hTheme(win, L"COMBOBOX"); 332 if ( !hTheme ) 333 { 334 m_rendererNative.DrawComboBoxDropButton(win, dc, rect, flags); 335 return; 336 } 337 338 RECT r; 339 wxCopyRectToRECT(rect, r); 340 341 int state; 342 if ( flags & wxCONTROL_PRESSED ) 343 state = CBXS_PRESSED; 344 else if ( flags & wxCONTROL_CURRENT ) 345 state = CBXS_HOT; 346 else if ( flags & wxCONTROL_DISABLED ) 347 state = CBXS_DISABLED; 348 else 349 state = CBXS_NORMAL; 350 351 wxUxThemeEngine::Get()->DrawThemeBackground 352 ( 353 hTheme, 354 GraphicsHDC(&dc), 355 CP_DROPDOWNBUTTON, 356 state, 357 &r, 358 NULL 359 ); 360 361} 362 363int 364wxRendererXP::DrawHeaderButton(wxWindow *win, 365 wxDC& dc, 366 const wxRect& rect, 367 int flags, 368 wxHeaderSortIconType sortArrow, 369 wxHeaderButtonParams* params) 370{ 371 wxUxThemeHandle hTheme(win, L"HEADER"); 372 if ( !hTheme ) 373 { 374 return m_rendererNative.DrawHeaderButton(win, dc, rect, flags, sortArrow, params); 375 } 376 377 RECT r; 378 wxCopyRectToRECT(rect, r); 379 380 int state; 381 if ( flags & wxCONTROL_PRESSED ) 382 state = HIS_PRESSED; 383 else if ( flags & wxCONTROL_CURRENT ) 384 state = HIS_HOT; 385 else 386 state = HIS_NORMAL; 387 wxUxThemeEngine::Get()->DrawThemeBackground 388 ( 389 hTheme, 390 GraphicsHDC(&dc), 391 HP_HEADERITEM, 392 state, 393 &r, 394 NULL 395 ); 396 397 // NOTE: Using the theme to draw HP_HEADERSORTARROW doesn't do anything. 398 // Why? If this can be fixed then draw the sort arrows using the theme 399 // and then clear those flags before calling DrawHeaderButtonContents. 400 401 // Add any extras that are specified in flags and params 402 return DrawHeaderButtonContents(win, dc, rect, flags, sortArrow, params); 403} 404 405 406int 407wxRendererXP::GetHeaderButtonHeight(wxWindow *win) 408{ 409 wxUxThemeHandle hTheme(win, L"HEADER"); 410 if ( !hTheme ) 411 { 412 return m_rendererNative.GetHeaderButtonHeight(win); 413 } 414 415 HRESULT hr; 416 int value = -1; 417 418 hr = wxUxThemeEngine::Get()->GetThemeMetric( hTheme, 419 NULL, 420 HP_HEADERITEM, 421 HIS_NORMAL, 422 TMT_HEIGHT, 423 &value ); 424 if ( hr == S_OK ) 425 return value; 426 else 427 return 20; 428} 429 430 431void 432wxRendererXP::DrawTreeItemButton(wxWindow *win, 433 wxDC& dc, 434 const wxRect& rect, 435 int flags) 436{ 437 wxUxThemeHandle hTheme(win, L"TREEVIEW"); 438 if ( !hTheme ) 439 { 440 m_rendererNative.DrawTreeItemButton(win, dc, rect, flags); 441 return; 442 } 443 444 RECT r; 445 wxCopyRectToRECT(rect, r); 446 447 int state = flags & wxCONTROL_EXPANDED ? GLPS_OPENED : GLPS_CLOSED; 448 wxUxThemeEngine::Get()->DrawThemeBackground 449 ( 450 hTheme, 451 GraphicsHDC(&dc), 452 TVP_GLYPH, 453 state, 454 &r, 455 NULL 456 ); 457} 458 459void 460wxRendererXP::DrawCheckBox(wxWindow *win, 461 wxDC& dc, 462 const wxRect& rect, 463 int flags) 464{ 465 wxUxThemeHandle hTheme(win, L"BUTTON"); 466 if ( !hTheme ) 467 { 468 m_rendererNative.DrawCheckBox(win, dc, rect, flags); 469 return; 470 } 471 472 RECT r; 473 wxCopyRectToRECT(rect, r); 474 475 int state; 476 if ( flags & wxCONTROL_CHECKED ) 477 state = CBS_CHECKEDNORMAL; 478 else if ( flags & wxCONTROL_UNDETERMINED ) 479 state = CBS_MIXEDNORMAL; 480 else 481 state = CBS_UNCHECKEDNORMAL; 482 483 // CBS_XXX is followed by CBX_XXXGOT, then CBS_XXXPRESSED and DISABLED 484 if ( flags & wxCONTROL_CURRENT ) 485 state += 1; 486 else if ( flags & wxCONTROL_PRESSED ) 487 state += 2; 488 else if ( flags & wxCONTROL_DISABLED ) 489 state += 3; 490 491 wxUxThemeEngine::Get()->DrawThemeBackground 492 ( 493 hTheme, 494 GraphicsHDC(&dc), 495 BP_CHECKBOX, 496 state, 497 &r, 498 NULL 499 ); 500} 501 502 503 504void 505wxRendererXP::DrawPushButton(wxWindow * win, 506 wxDC& dc, 507 const wxRect& rect, 508 int flags) 509{ 510 wxUxThemeHandle hTheme(win, L"BUTTON"); 511 if ( !hTheme ) 512 { 513 m_rendererNative.DrawPushButton(win, dc, rect, flags); 514 return; 515 } 516 517 RECT r; 518 wxCopyRectToRECT(rect, r); 519 520 int state; 521 if ( flags & wxCONTROL_PRESSED ) 522 state = PBS_PRESSED; 523 else if ( flags & wxCONTROL_CURRENT ) 524 state = PBS_HOT; 525 else if ( flags & wxCONTROL_DISABLED ) 526 state = PBS_DISABLED; 527 else if ( flags & wxCONTROL_ISDEFAULT ) 528 state = PBS_DEFAULTED; 529 else 530 state = PBS_NORMAL; 531 532 wxUxThemeEngine::Get()->DrawThemeBackground 533 ( 534 hTheme, 535 GraphicsHDC(&dc), 536 BP_PUSHBUTTON, 537 state, 538 &r, 539 NULL 540 ); 541} 542 543// ---------------------------------------------------------------------------- 544// splitter drawing 545// ---------------------------------------------------------------------------- 546 547// the width of the sash: this is the same as used by Explorer... 548static const wxCoord SASH_WIDTH = 4; 549 550wxSplitterRenderParams 551wxRendererXP::GetSplitterParams(const wxWindow * win) 552{ 553 if ( win->HasFlag(wxSP_NO_XP_THEME) ) 554 return m_rendererNative.GetSplitterParams(win); 555 else 556 return wxSplitterRenderParams(SASH_WIDTH, 0, false); 557} 558 559void 560wxRendererXP::DrawSplitterBorder(wxWindow * win, 561 wxDC& dc, 562 const wxRect& rect, 563 int flags) 564{ 565 if ( win->HasFlag(wxSP_NO_XP_THEME) ) 566 { 567 m_rendererNative.DrawSplitterBorder(win, dc, rect, flags); 568 } 569} 570 571void 572wxRendererXP::DrawSplitterSash(wxWindow *win, 573 wxDC& dc, 574 const wxSize& size, 575 wxCoord position, 576 wxOrientation orient, 577 int flags) 578{ 579 if ( !win->HasFlag(wxSP_NO_XP_THEME) ) 580 { 581 dc.SetPen(*wxTRANSPARENT_PEN); 582 dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE))); 583 if ( orient == wxVERTICAL ) 584 { 585 dc.DrawRectangle(position, 0, SASH_WIDTH, size.y); 586 } 587 else // wxHORIZONTAL 588 { 589 dc.DrawRectangle(0, position, size.x, SASH_WIDTH); 590 } 591 592 return; 593 } 594 595 m_rendererNative.DrawSplitterSash(win, dc, size, position, orient, flags); 596} 597 598// ---------------------------------------------------------------------------- 599// Other renderer functions to be merged in to wxRenderer class in 2.9, but 600// they are standalone functions here to protect the ABI. 601// ---------------------------------------------------------------------------- 602 603// Uses the theme to draw the border and fill for something like a wxTextCtrl 604void wxRenderer_DrawTextCtrl(wxWindow* win, wxDC& dc, const wxRect& rect, int flags) 605{ 606 wxColour fill; 607 wxColour bdr; 608 COLORREF cref; 609 610#if wxUSE_UXTHEME 611 wxUxThemeHandle hTheme(win, L"EDIT"); 612 if (hTheme) 613 { 614 wxUxThemeEngine::Get()->GetThemeColor(hTheme, EP_EDITTEXT, 615 ETS_NORMAL, TMT_FILLCOLOR, &cref); 616 fill = wxRGBToColour(cref); 617 618 int etsState; 619 if ( flags & wxCONTROL_DISABLED ) 620 etsState = ETS_DISABLED; 621 else 622 etsState = ETS_NORMAL; 623 624 wxUxThemeEngine::Get()->GetThemeColor(hTheme, EP_EDITTEXT, 625 etsState, TMT_BORDERCOLOR, &cref); 626 bdr = wxRGBToColour(cref); 627 } 628 else 629#endif 630 { 631 fill = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); 632 bdr = *wxBLACK; 633 } 634 635 dc.SetPen( bdr ); 636 dc.SetBrush( fill ); 637 dc.DrawRectangle(rect); 638} 639 640 641// Draw the equivallent of a wxComboBox 642void wxRenderer_DrawComboBox(wxWindow* win, wxDC& dc, const wxRect& rect, int flags) 643{ 644 // Draw the main part of the control same as TextCtrl 645 wxRenderer_DrawTextCtrl(win, dc, rect, flags); 646 647 // Draw the button inside the border, on the right side 648 wxRect br(rect); 649 br.height -= 2; 650 br.x += br.width - br.height - 1; 651 br.width = br.height; 652 br.y += 1; 653 654 wxRendererNative::Get().DrawComboBoxDropButton(win, dc, br, flags); 655} 656 657 658void wxRenderer_DrawChoice(wxWindow* win, wxDC& dc, 659 const wxRect& rect, int flags) 660{ 661 wxRenderer_DrawComboBox(win, dc, rect, flags); 662} 663 664 665// Draw a themed radio button 666void wxRenderer_DrawRadioButton(wxWindow* win, wxDC& dc, const wxRect& rect, int flags) 667{ 668#if wxUSE_UXTHEME 669 wxUxThemeHandle hTheme(win, L"BUTTON"); 670 if ( !hTheme ) 671#endif 672 { 673 // ??? m_rendererNative.DrawRadioButton(win, dc, rect, flags); 674 return; 675 } 676 677#if wxUSE_UXTHEME 678 RECT r; 679 wxCopyRectToRECT(rect, r); 680 681 int state; 682 if ( flags & wxCONTROL_CHECKED ) 683 state = RBS_CHECKEDNORMAL; 684 else if ( flags & wxCONTROL_UNDETERMINED ) 685 state = RBS_MIXEDNORMAL; 686 else 687 state = RBS_UNCHECKEDNORMAL; 688 689 // RBS_XXX is followed by RBX_XXXGOT, then RBS_XXXPRESSED and DISABLED 690 if ( flags & wxCONTROL_CURRENT ) 691 state += 1; 692 else if ( flags & wxCONTROL_PRESSED ) 693 state += 2; 694 else if ( flags & wxCONTROL_DISABLED ) 695 state += 3; 696 697 wxUxThemeEngine::Get()->DrawThemeBackground 698 ( 699 hTheme, 700 GraphicsHDC(&dc), 701 BP_RADIOBUTTON, 702 state, 703 &r, 704 NULL 705 ); 706#endif 707} 708 709#endif // wxUSE_UXTHEME 710