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