add wxRenderer::GetCheckBoxSize(); refactor wxGTK code to avoid duplication (#9642)
[wxWidgets.git] / src / msw / renderer.cpp
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$
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/scopeguard.h"
35 #include "wx/splitter.h"
36 #include "wx/renderer.h"
37 #include "wx/msw/private.h"
38 #include "wx/msw/dc.h"
39 #include "wx/msw/uxtheme.h"
40
41 // tmschema.h is in Win32 Platform SDK and might not be available with earlier
42 // compilers
43 #ifndef CP_DROPDOWNBUTTON
44 #define BP_PUSHBUTTON 1
45 #define BP_CHECKBOX 3
46 #define CBS_UNCHECKEDNORMAL 1
47 #define CBS_CHECKEDNORMAL (CBS_UNCHECKEDNORMAL + 4)
48 #define CBS_MIXEDNORMAL (CBS_CHECKEDNORMAL + 4)
49
50 #define PBS_NORMAL 1
51 #define PBS_HOT 2
52 #define PBS_PRESSED 3
53 #define PBS_DISABLED 4
54 #define PBS_DEFAULTED 5
55
56 #define CP_DROPDOWNBUTTON 1
57
58 #define CBXS_NORMAL 1
59 #define CBXS_HOT 2
60 #define CBXS_PRESSED 3
61 #define CBXS_DISABLED 4
62
63 #define TVP_GLYPH 2
64
65 #define GLPS_CLOSED 1
66 #define GLPS_OPENED 2
67
68 #define HP_HEADERITEM 1
69
70 #define HIS_NORMAL 1
71 #define HIS_HOT 2
72 #define HIS_PRESSED 3
73
74 #define TMT_HEIGHT 2417
75
76 #define HP_HEADERSORTARROW 4
77 #define HSAS_SORTEDUP 1
78 #define HSAS_SORTEDDOWN 2
79 #endif
80
81 #if defined(__WXWINCE__)
82 #ifndef DFCS_FLAT
83 #define DFCS_FLAT 0
84 #endif
85 #ifndef DFCS_MONO
86 #define DFCS_MONO 0
87 #endif
88 #endif
89
90 #ifndef DFCS_HOT
91 #define DFCS_HOT 0x1000
92 #endif
93
94 // ----------------------------------------------------------------------------
95 // wxRendererMSW: wxRendererNative implementation for "old" Win32 systems
96 // ----------------------------------------------------------------------------
97
98 class WXDLLEXPORT wxRendererMSW : public wxDelegateRendererNative
99 {
100 public:
101 wxRendererMSW() { }
102
103 static wxRendererNative& Get();
104
105 virtual void DrawComboBoxDropButton(wxWindow *win,
106 wxDC& dc,
107 const wxRect& rect,
108 int flags = 0);
109
110 virtual void DrawCheckBox(wxWindow *win,
111 wxDC& dc,
112 const wxRect& rect,
113 int flags = 0);
114
115 virtual void DrawPushButton(wxWindow *win,
116 wxDC& dc,
117 const wxRect& rect,
118 int flags = 0);
119
120 virtual void DrawFocusRect(wxWindow* win,
121 wxDC& dc,
122 const wxRect& rect,
123 int flags = 0);
124
125 virtual wxSize GetCheckBoxSize(wxWindow *win);
126
127 virtual int GetHeaderButtonHeight(wxWindow *win);
128
129 private:
130 DECLARE_NO_COPY_CLASS(wxRendererMSW)
131 };
132
133 // ----------------------------------------------------------------------------
134 // wxRendererXP: wxRendererNative implementation for Windows XP and later
135 // ----------------------------------------------------------------------------
136
137 #if wxUSE_UXTHEME
138
139 class WXDLLEXPORT wxRendererXP : public wxDelegateRendererNative
140 {
141 public:
142 wxRendererXP() : wxDelegateRendererNative(wxRendererMSW::Get()) { }
143
144 static wxRendererNative& Get();
145
146 virtual int DrawHeaderButton(wxWindow *win,
147 wxDC& dc,
148 const wxRect& rect,
149 int flags = 0,
150 wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE,
151 wxHeaderButtonParams* params = NULL);
152
153 virtual void DrawTreeItemButton(wxWindow *win,
154 wxDC& dc,
155 const wxRect& rect,
156 int flags = 0);
157 virtual void DrawSplitterBorder(wxWindow *win,
158 wxDC& dc,
159 const wxRect& rect,
160 int flags = 0);
161 virtual void DrawSplitterSash(wxWindow *win,
162 wxDC& dc,
163 const wxSize& size,
164 wxCoord position,
165 wxOrientation orient,
166 int flags = 0);
167 virtual void DrawComboBoxDropButton(wxWindow *win,
168 wxDC& dc,
169 const wxRect& rect,
170 int flags = 0);
171 virtual void DrawCheckBox(wxWindow *win,
172 wxDC& dc,
173 const wxRect& rect,
174 int flags = 0);
175
176 virtual void DrawPushButton(wxWindow *win,
177 wxDC& dc,
178 const wxRect& rect,
179 int flags = 0);
180
181 virtual void DrawItemSelectionRect(wxWindow *win,
182 wxDC& dc,
183 const wxRect& rect,
184 int flags = 0 );
185
186
187 virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win);
188 private:
189 DECLARE_NO_COPY_CLASS(wxRendererXP)
190 };
191
192 #endif // wxUSE_UXTHEME
193
194 // ============================================================================
195 // wxRendererNative and wxRendererMSW implementation
196 // ============================================================================
197
198 /* static */
199 wxRendererNative& wxRendererNative::GetDefault()
200 {
201 #if wxUSE_UXTHEME
202 wxUxThemeEngine *themeEngine = wxUxThemeEngine::Get();
203 if ( themeEngine && themeEngine->IsAppThemed() )
204 return wxRendererXP::Get();
205 #endif // wxUSE_UXTHEME
206
207 return wxRendererMSW::Get();
208 }
209
210 /* static */
211 wxRendererNative& wxRendererMSW::Get()
212 {
213 static wxRendererMSW s_rendererMSW;
214
215 return s_rendererMSW;
216 }
217
218 void
219 wxRendererMSW::DrawComboBoxDropButton(wxWindow * WXUNUSED(win),
220 wxDC& dc,
221 const wxRect& rect,
222 int flags)
223 {
224 RECT r;
225 wxCopyRectToRECT(rect, r);
226
227 int style = DFCS_SCROLLCOMBOBOX;
228 if ( flags & wxCONTROL_DISABLED )
229 style |= DFCS_INACTIVE;
230 if ( flags & wxCONTROL_PRESSED )
231 style |= DFCS_PUSHED | DFCS_FLAT;
232
233 ::DrawFrameControl(GetHdcOf(*((wxMSWDCImpl*)dc.GetImpl())), &r, DFC_SCROLL, style);
234 }
235
236 void
237 wxRendererMSW::DrawCheckBox(wxWindow * WXUNUSED(win),
238 wxDC& dc,
239 const wxRect& rect,
240 int flags)
241 {
242 RECT r;
243 wxCopyRectToRECT(rect, r);
244
245 int style = DFCS_BUTTONCHECK;
246 if ( flags & wxCONTROL_CHECKED )
247 style |= DFCS_CHECKED;
248 if ( flags & wxCONTROL_DISABLED )
249 style |= DFCS_INACTIVE;
250 if ( flags & wxCONTROL_FLAT )
251 style |= DFCS_MONO;
252 if ( flags & wxCONTROL_PRESSED )
253 style |= DFCS_PUSHED;
254 if ( flags & wxCONTROL_CURRENT )
255 style |= DFCS_HOT;
256
257 ::DrawFrameControl(GetHdcOf(*((wxMSWDCImpl*)dc.GetImpl())), &r, DFC_BUTTON, style);
258 }
259
260 void
261 wxRendererMSW::DrawPushButton(wxWindow * WXUNUSED(win),
262 wxDC& dc,
263 const wxRect& rectOrig,
264 int flags)
265 {
266 wxRect rect(rectOrig);
267
268 int style = DFCS_BUTTONPUSH;
269 if ( flags & wxCONTROL_DISABLED )
270 style |= DFCS_INACTIVE;
271 if ( flags & wxCONTROL_PRESSED )
272 style |= DFCS_PUSHED | DFCS_FLAT;
273 if ( flags & wxCONTROL_ISDEFAULT )
274 {
275 // DrawFrameControl() doesn't seem to support default buttons so we
276 // have to draw the border ourselves
277 wxDCPenChanger pen(dc, *wxBLACK_PEN);
278 wxDCBrushChanger brush(dc, *wxTRANSPARENT_BRUSH);
279 dc.DrawRectangle(rect);
280 rect.Deflate(1);
281 }
282
283 RECT rc;
284 wxCopyRectToRECT(rect, rc);
285
286 ::DrawFrameControl(GetHdcOf(*((wxMSWDCImpl*)dc.GetImpl())), &rc, DFC_BUTTON, style);
287 }
288
289 void wxRendererMSW::DrawFocusRect(wxWindow * WXUNUSED(win),
290 wxDC& dc,
291 const wxRect& rect,
292 int WXUNUSED(flags))
293 {
294 RECT rc;
295 wxCopyRectToRECT(rect, rc);
296
297 ::DrawFocusRect(GetHdcOf(*((wxMSWDCImpl*)dc.GetImpl())), &rc);
298 }
299
300 wxSize wxRendererMSW::GetCheckBoxSize(wxWindow * WXUNUSED(win))
301 {
302 return wxSize(::GetSystemMetrics(SM_CXMENUCHECK),
303 ::GetSystemMetrics(SM_CYMENUCHECK));
304 }
305
306 int wxRendererMSW::GetHeaderButtonHeight(wxWindow * WXUNUSED(win))
307 {
308 // some "reasonable" value returned in case of error, it doesn't really
309 // correspond to anything but it's better than returning 0
310 static const int DEFAULT_HEIGHT = 20;
311
312
313 // create a temporary header window just to get its geometry
314 HWND hwndHeader = ::CreateWindow(WC_HEADER, NULL, 0,
315 0, 0, 0, 0, NULL, NULL, NULL, NULL);
316 if ( !hwndHeader )
317 return DEFAULT_HEIGHT;
318
319 wxON_BLOCK_EXIT1( ::DestroyWindow, hwndHeader );
320
321 // initialize the struct filled with the values by Header_Layout()
322 RECT parentRect = { 0, 0, 100, 100 };
323 WINDOWPOS wp = { 0, 0, 0, 0, 0, 0, 0 };
324 HDLAYOUT hdl = { &parentRect, &wp };
325
326 return Header_Layout(hwndHeader, &hdl) ? wp.cy : DEFAULT_HEIGHT;
327 }
328
329 // ============================================================================
330 // wxRendererXP implementation
331 // ============================================================================
332
333 #if wxUSE_UXTHEME
334
335 /* static */
336 wxRendererNative& wxRendererXP::Get()
337 {
338 static wxRendererXP s_rendererXP;
339
340 return s_rendererXP;
341 }
342
343 // NOTE: There is no guarantee that the button drawn fills the entire rect (XP
344 // default theme, for example), so the caller should have cleared button's
345 // background before this call. This is quite likely a wxMSW-specific thing.
346 void
347 wxRendererXP::DrawComboBoxDropButton(wxWindow * win,
348 wxDC& dc,
349 const wxRect& rect,
350 int flags)
351 {
352 wxUxThemeHandle hTheme(win, L"COMBOBOX");
353 if ( !hTheme )
354 {
355 m_rendererNative.DrawComboBoxDropButton(win, dc, rect, flags);
356 return;
357 }
358
359 RECT r;
360 wxCopyRectToRECT(rect, r);
361
362 int state;
363 if ( flags & wxCONTROL_PRESSED )
364 state = CBXS_PRESSED;
365 else if ( flags & wxCONTROL_CURRENT )
366 state = CBXS_HOT;
367 else if ( flags & wxCONTROL_DISABLED )
368 state = CBXS_DISABLED;
369 else
370 state = CBXS_NORMAL;
371
372 wxUxThemeEngine::Get()->DrawThemeBackground
373 (
374 hTheme,
375 GetHdcOf(*((wxMSWDCImpl*)dc.GetImpl())),
376 CP_DROPDOWNBUTTON,
377 state,
378 &r,
379 NULL
380 );
381
382 }
383
384 int
385 wxRendererXP::DrawHeaderButton(wxWindow *win,
386 wxDC& dc,
387 const wxRect& rect,
388 int flags,
389 wxHeaderSortIconType sortArrow,
390 wxHeaderButtonParams* params)
391 {
392 wxUxThemeHandle hTheme(win, L"HEADER");
393 if ( !hTheme )
394 {
395 return m_rendererNative.DrawHeaderButton(win, dc, rect, flags, sortArrow, params);
396 }
397
398 RECT r;
399 wxCopyRectToRECT(rect, r);
400
401 int state;
402 if ( flags & wxCONTROL_PRESSED )
403 state = HIS_PRESSED;
404 else if ( flags & wxCONTROL_CURRENT )
405 state = HIS_HOT;
406 else
407 state = HIS_NORMAL;
408 wxUxThemeEngine::Get()->DrawThemeBackground
409 (
410 hTheme,
411 GetHdcOf(*((wxMSWDCImpl*)dc.GetImpl())),
412 HP_HEADERITEM,
413 state,
414 &r,
415 NULL
416 );
417
418 // NOTE: Using the theme to draw HP_HEADERSORTARROW doesn't do anything.
419 // Why? If this can be fixed then draw the sort arrows using the theme
420 // and then clear those flags before calling DrawHeaderButtonContents.
421
422 // Add any extras that are specified in flags and params
423 return DrawHeaderButtonContents(win, dc, rect, flags, sortArrow, params);
424 }
425
426
427 void
428 wxRendererXP::DrawTreeItemButton(wxWindow *win,
429 wxDC& dc,
430 const wxRect& rect,
431 int flags)
432 {
433 wxUxThemeHandle hTheme(win, L"TREEVIEW");
434 if ( !hTheme )
435 {
436 m_rendererNative.DrawTreeItemButton(win, dc, rect, flags);
437 return;
438 }
439
440 RECT r;
441 wxCopyRectToRECT(rect, r);
442
443 int state = flags & wxCONTROL_EXPANDED ? GLPS_OPENED : GLPS_CLOSED;
444 wxUxThemeEngine::Get()->DrawThemeBackground
445 (
446 hTheme,
447 GetHdcOf(*((wxMSWDCImpl*)dc.GetImpl())),
448 TVP_GLYPH,
449 state,
450 &r,
451 NULL
452 );
453 }
454
455 void
456 wxRendererXP::DrawCheckBox(wxWindow *win,
457 wxDC& dc,
458 const wxRect& rect,
459 int flags)
460 {
461 wxUxThemeHandle hTheme(win, L"BUTTON");
462 if ( !hTheme )
463 {
464 m_rendererNative.DrawCheckBox(win, dc, rect, flags);
465 return;
466 }
467
468 RECT r;
469 wxCopyRectToRECT(rect, r);
470
471 int state;
472 if ( flags & wxCONTROL_CHECKED )
473 state = CBS_CHECKEDNORMAL;
474 else if ( flags & wxCONTROL_UNDETERMINED )
475 state = CBS_MIXEDNORMAL;
476 else
477 state = CBS_UNCHECKEDNORMAL;
478
479 // CBS_XXX is followed by CBX_XXXHOT, then CBS_XXXPRESSED and DISABLED
480 enum
481 {
482 CBS_HOT_OFFSET = 1,
483 CBS_PRESSED_OFFSET = 2,
484 CBS_DISABLED_OFFSET = 3
485 };
486
487 if ( flags & wxCONTROL_DISABLED )
488 state += CBS_DISABLED_OFFSET;
489 else if ( flags & wxCONTROL_PRESSED )
490 state += CBS_PRESSED_OFFSET;
491 else if ( flags & wxCONTROL_CURRENT )
492 state += CBS_HOT_OFFSET;
493
494 wxUxThemeEngine::Get()->DrawThemeBackground
495 (
496 hTheme,
497 GetHdcOf(*((wxMSWDCImpl*)dc.GetImpl())),
498 BP_CHECKBOX,
499 state,
500 &r,
501 NULL
502 );
503 }
504
505 void
506 wxRendererXP::DrawPushButton(wxWindow * win,
507 wxDC& dc,
508 const wxRect& rect,
509 int flags)
510 {
511 wxUxThemeHandle hTheme(win, L"BUTTON");
512 if ( !hTheme )
513 {
514 m_rendererNative.DrawPushButton(win, dc, rect, flags);
515 return;
516 }
517
518 RECT r;
519 wxCopyRectToRECT(rect, r);
520
521 int state;
522 if ( flags & wxCONTROL_PRESSED )
523 state = PBS_PRESSED;
524 else if ( flags & wxCONTROL_CURRENT )
525 state = PBS_HOT;
526 else if ( flags & wxCONTROL_DISABLED )
527 state = PBS_DISABLED;
528 else if ( flags & wxCONTROL_ISDEFAULT )
529 state = PBS_DEFAULTED;
530 else
531 state = PBS_NORMAL;
532
533 wxUxThemeEngine::Get()->DrawThemeBackground
534 (
535 hTheme,
536 GetHdcOf(*((wxMSWDCImpl*)dc.GetImpl())),
537 BP_PUSHBUTTON,
538 state,
539 &r,
540 NULL
541 );
542
543 }
544
545 void
546 wxRendererXP::DrawItemSelectionRect(wxWindow *win,
547 wxDC& dc,
548 const wxRect& rect,
549 int flags)
550 {
551 wxBrush brush;
552 if ( flags & wxCONTROL_SELECTED )
553 {
554 if ( flags & wxCONTROL_FOCUSED )
555 {
556 brush = wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
557 }
558 else // !focused
559 {
560 brush = wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
561 }
562 }
563 else // !selected
564 {
565 brush = *wxTRANSPARENT_BRUSH;
566 }
567
568 dc.SetBrush(brush);
569 dc.SetPen(*wxTRANSPARENT_PEN);
570 dc.DrawRectangle( rect );
571
572 if ((flags & wxCONTROL_FOCUSED) && (flags & wxCONTROL_CURRENT))
573 DrawFocusRect( win, dc, rect, flags );
574 }
575
576
577
578 // ----------------------------------------------------------------------------
579 // splitter drawing
580 // ----------------------------------------------------------------------------
581
582 // the width of the sash: this is the same as used by Explorer...
583 static const wxCoord SASH_WIDTH = 4;
584
585 wxSplitterRenderParams
586 wxRendererXP::GetSplitterParams(const wxWindow * win)
587 {
588 if ( win->HasFlag(wxSP_NO_XP_THEME) )
589 return m_rendererNative.GetSplitterParams(win);
590 else
591 return wxSplitterRenderParams(SASH_WIDTH, 0, false);
592 }
593
594 void
595 wxRendererXP::DrawSplitterBorder(wxWindow * win,
596 wxDC& dc,
597 const wxRect& rect,
598 int flags)
599 {
600 if ( win->HasFlag(wxSP_NO_XP_THEME) )
601 {
602 m_rendererNative.DrawSplitterBorder(win, dc, rect, flags);
603 }
604 }
605
606 void
607 wxRendererXP::DrawSplitterSash(wxWindow *win,
608 wxDC& dc,
609 const wxSize& size,
610 wxCoord position,
611 wxOrientation orient,
612 int flags)
613 {
614 if ( !win->HasFlag(wxSP_NO_XP_THEME) )
615 {
616 dc.SetPen(*wxTRANSPARENT_PEN);
617 dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)));
618 if ( orient == wxVERTICAL )
619 {
620 dc.DrawRectangle(position, 0, SASH_WIDTH, size.y);
621 }
622 else // wxHORIZONTAL
623 {
624 dc.DrawRectangle(0, position, size.x, SASH_WIDTH);
625 }
626
627 return;
628 }
629
630 m_rendererNative.DrawSplitterSash(win, dc, size, position, orient, flags);
631 }
632
633 #endif // wxUSE_UXTHEME