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