1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/renderg.cpp
3 // Purpose: generic implementation of wxRendererNative (for any platform)
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9 // License: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // for compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
27 #include "wx/renderer.h"
30 #include "wx/string.h"
32 #include "wx/settings.h"
33 #include "wx/gdicmn.h"
34 #include "wx/module.h"
37 #include "wx/splitter.h"
38 #include "wx/dcmirror.h"
41 #include "wx/osx/private.h"
44 // ----------------------------------------------------------------------------
45 // wxRendererGeneric: our wxRendererNative implementation
46 // ----------------------------------------------------------------------------
48 class WXDLLEXPORT wxRendererGeneric
: public wxRendererNative
53 virtual int DrawHeaderButton(wxWindow
*win
,
57 wxHeaderSortIconType sortArrow
= wxHDR_SORT_ICON_NONE
,
58 wxHeaderButtonParams
* params
= NULL
);
60 virtual int DrawHeaderButtonContents(wxWindow
*win
,
64 wxHeaderSortIconType sortArrow
= wxHDR_SORT_ICON_NONE
,
65 wxHeaderButtonParams
* params
= NULL
);
67 virtual int GetHeaderButtonHeight(wxWindow
*win
);
69 virtual void DrawTreeItemButton(wxWindow
*win
,
74 virtual void DrawSplitterBorder(wxWindow
*win
,
79 virtual void DrawSplitterSash(wxWindow
*win
,
86 virtual void DrawComboBoxDropButton(wxWindow
*win
,
91 virtual void DrawDropArrow(wxWindow
*win
,
96 virtual void DrawCheckBox(wxWindow
*win
,
101 virtual wxSize
GetCheckBoxSize(wxWindow
*win
);
103 virtual void DrawPushButton(wxWindow
*win
,
108 virtual void DrawItemSelectionRect(wxWindow
*win
,
113 virtual void DrawFocusRect(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
= 0);
115 virtual void DrawChoice(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
=0);
117 virtual void DrawComboBox(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
=0);
119 virtual void DrawTextCtrl(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
=0);
121 virtual void DrawRadioButton(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
=0);
123 virtual wxSplitterRenderParams
GetSplitterParams(const wxWindow
*win
);
125 virtual wxRendererVersion
GetVersion() const
127 return wxRendererVersion(wxRendererVersion::Current_Version
,
128 wxRendererVersion::Current_Age
);
132 // Cleanup by deleting standard renderer
133 static void Cleanup();
135 // Get the generic object
136 static wxRendererGeneric
* DoGetGeneric();
139 // draw the rectange using the first pen for the left and top sides and
140 // the second one for the bottom and right ones
141 void DrawShadedRect(wxDC
& dc
, wxRect
*rect
,
142 const wxPen
& pen1
, const wxPen
& pen2
);
150 static wxRendererGeneric
* sm_rendererGeneric
;
153 // ============================================================================
154 // wxRendererGeneric implementation
155 // ============================================================================
157 // Get the generic object
158 wxRendererGeneric
* wxRendererGeneric::DoGetGeneric()
160 if (!sm_rendererGeneric
)
161 sm_rendererGeneric
= new wxRendererGeneric
;
162 return sm_rendererGeneric
;
165 // ----------------------------------------------------------------------------
166 // wxRendererGeneric creation
167 // ----------------------------------------------------------------------------
170 wxRendererNative
& wxRendererNative::GetGeneric()
172 return * wxRendererGeneric::DoGetGeneric();
175 void wxRendererGeneric::Cleanup()
177 if (sm_rendererGeneric
)
178 delete sm_rendererGeneric
;
180 sm_rendererGeneric
= NULL
;
183 wxRendererGeneric
* wxRendererGeneric::sm_rendererGeneric
= NULL
;
185 wxRendererGeneric::wxRendererGeneric()
186 : m_penBlack(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW
)),
187 m_penDarkGrey(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW
)),
188 m_penLightGrey(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
)),
189 m_penHighlight(wxSystemSettings::GetColour(wxSYS_COLOUR_3DHIGHLIGHT
))
193 // ----------------------------------------------------------------------------
194 // wxRendererGeneric helpers
195 // ----------------------------------------------------------------------------
198 wxRendererGeneric::DrawShadedRect(wxDC
& dc
,
203 // draw the rectangle
205 dc
.DrawLine(rect
->GetLeft(), rect
->GetTop(),
206 rect
->GetLeft(), rect
->GetBottom());
207 dc
.DrawLine(rect
->GetLeft() + 1, rect
->GetTop(),
208 rect
->GetRight(), rect
->GetTop());
210 dc
.DrawLine(rect
->GetRight(), rect
->GetTop(),
211 rect
->GetRight(), rect
->GetBottom());
212 dc
.DrawLine(rect
->GetLeft(), rect
->GetBottom(),
213 rect
->GetRight() + 1, rect
->GetBottom());
219 // ----------------------------------------------------------------------------
220 // tree/list ctrl drawing
221 // ----------------------------------------------------------------------------
224 wxRendererGeneric::DrawHeaderButton(wxWindow
* win
,
228 wxHeaderSortIconType sortArrow
,
229 wxHeaderButtonParams
* params
)
231 const wxCoord x
= rect
.x
,
236 dc
.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
)));
237 dc
.SetPen(*wxTRANSPARENT_PEN
);
238 dc
.DrawRectangle(rect
);
240 dc
.SetBrush(*wxTRANSPARENT_BRUSH
);
242 dc
.SetPen(m_penBlack
);
243 dc
.DrawLine( x
+w
-1, y
, x
+w
-1, y
+h
); // right (outer)
244 dc
.DrawLine( x
, y
+h
-1, x
+w
, y
+h
-1 ); // bottom (outer)
246 dc
.SetPen(m_penDarkGrey
);
247 dc
.DrawLine( x
+w
-2, y
+1, x
+w
-2, y
+h
-1 ); // right (inner)
248 dc
.DrawLine( x
+1, y
+h
-2, x
+w
-1, y
+h
-2 ); // bottom (inner)
250 dc
.SetPen(m_penHighlight
);
251 dc
.DrawLine( x
, y
, x
, y
+h
-1 ); // left (outer)
252 dc
.DrawLine( x
, y
, x
+w
-1, y
); // top (outer)
254 return DrawHeaderButtonContents(win
, dc
, rect
, flags
, sortArrow
, params
);
259 wxRendererGeneric::DrawHeaderButtonContents(wxWindow
*win
,
263 wxHeaderSortIconType sortArrow
,
264 wxHeaderButtonParams
* params
)
268 // Mark this item as selected. For the generic version we'll just draw an
270 if ( flags
& wxCONTROL_SELECTED
)
272 // draw a line at the bottom of the header button, overlaying the
273 // native hot-tracking line (on XP)
274 const int penwidth
= 3;
275 int y
= rect
.y
+ rect
.height
+ 1 - penwidth
;
276 wxColour c
= (params
&& params
->m_selectionColour
.Ok()) ?
277 params
->m_selectionColour
: wxColour(0x66, 0x66, 0x66);
278 wxPen
pen(c
, penwidth
);
279 pen
.SetCap(wxCAP_BUTT
);
281 dc
.DrawLine(rect
.x
, y
, rect
.x
+ rect
.width
, y
);
284 // Draw an up or down arrow
286 if (sortArrow
!= wxHDR_SORT_ICON_NONE
)
290 // make a rect for the arrow
293 ar
.y
+= (rect
.height
- ar
.height
)/2;
294 ar
.x
= ar
.x
+ rect
.width
- 3*ar
.width
/2;
295 arrowSpace
= 3*ar
.width
/2; // space to preserve when drawing the label
298 if ( sortArrow
& wxHDR_SORT_ICON_UP
)
300 triPt
[0].x
= ar
.width
/ 2;
302 triPt
[1].x
= ar
.width
;
303 triPt
[1].y
= ar
.height
;
305 triPt
[2].y
= ar
.height
;
311 triPt
[1].x
= ar
.width
;
313 triPt
[2].x
= ar
.width
/ 2;
314 triPt
[2].y
= ar
.height
;
317 wxColour c
= (params
&& params
->m_arrowColour
.Ok()) ?
318 params
->m_arrowColour
: wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW
);
320 dc
.SetBrush(wxBrush(c
));
321 dc
.DrawPolygon( 3, triPt
, ar
.x
, ar
.y
);
323 labelWidth
+= arrowSpace
;
325 const int margin
= 5; // number of pixels to reserve on either side of the label
329 if ( params
&& params
->m_labelBitmap
.Ok() )
330 bmpWidth
= params
->m_labelBitmap
.GetWidth() + 2;
332 labelWidth
+= bmpWidth
+ 2*margin
;
334 // Draw a label if one is given
335 if ( params
&& !params
->m_labelText
.empty() )
337 wxFont font
= params
->m_labelFont
.Ok() ?
338 params
->m_labelFont
: win
->GetFont();
339 wxColour clr
= params
->m_labelColour
.Ok() ?
340 params
->m_labelColour
: win
->GetForegroundColour();
342 wxString
label( params
->m_labelText
);
345 dc
.SetTextForeground(clr
);
346 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
348 int tw
, th
, td
, x
, y
;
349 dc
.GetTextExtent( label
, &tw
, &th
, &td
);
351 y
= rect
.y
+ wxMax(0, (rect
.height
- (th
+td
)) / 2);
353 y
+= 2; // No idea why.
356 // truncate and add an ellipsis (...) if the text is too wide.
357 int targetWidth
= rect
.width
- arrowSpace
- bmpWidth
- 2*margin
;
358 if ( tw
> targetWidth
)
361 dc
.GetTextExtent( wxT("..."), &ellipsisWidth
, NULL
);
363 label
.Truncate( label
.length() - 1 );
364 dc
.GetTextExtent( label
, &tw
, &th
);
365 } while (tw
+ ellipsisWidth
> targetWidth
&& label
.length() );
366 label
.append( wxT("...") );
370 switch (params
->m_labelAlignment
)
377 x
= rect
.x
+ wxMax(0, (rect
.width
- arrowSpace
- tw
- bmpWidth
)/2);
380 x
= rect
.x
+ wxMax(0, rect
.width
- arrowSpace
- margin
- tw
- bmpWidth
);
384 dc
.DrawText(label
, x
, y
);
388 // draw the bitmap if there is one
389 if ( params
&& params
->m_labelBitmap
.Ok() )
392 w
= params
->m_labelBitmap
.GetWidth();
393 h
= params
->m_labelBitmap
.GetHeight();
395 y
= rect
.y
+ wxMax(1, (rect
.height
- h
) / 2);
397 // if there is a text label, then put the bitmap at the end of the label
402 // otherwise use the alignment flags
405 switch (params
->m_labelAlignment
)
412 x
= rect
.x
+ wxMax(1, (rect
.width
- arrowSpace
- w
)/2);
415 x
= rect
.x
+ wxMax(1, rect
.width
- arrowSpace
- margin
- w
);
419 dc
.DrawBitmap(params
->m_labelBitmap
, x
, y
, true);
425 int wxRendererGeneric::GetHeaderButtonHeight(wxWindow
*win
)
427 // Copied and adapted from src/generic/listctrl.cpp
428 const int HEADER_OFFSET_Y
= 1;
429 const int EXTRA_HEIGHT
= 4;
433 win
->GetTextExtent(wxT("Hg"), &w
, &h
, &d
);
435 return h
+ d
+ 2 * HEADER_OFFSET_Y
+ EXTRA_HEIGHT
;
439 // draw the plus or minus sign
441 wxRendererGeneric::DrawTreeItemButton(wxWindow
* WXUNUSED(win
),
447 wxDCPenChanger
penChanger(dc
, *wxGREY_PEN
);
448 wxDCBrushChanger
brushChanger(dc
, *wxWHITE_BRUSH
);
450 dc
.DrawRectangle(rect
);
453 const wxCoord xMiddle
= rect
.x
+ rect
.width
/2;
454 const wxCoord yMiddle
= rect
.y
+ rect
.height
/2;
456 // half of the length of the horz lines in "-" and "+"
457 const wxCoord halfWidth
= rect
.width
/2 - 2;
458 dc
.SetPen(*wxBLACK_PEN
);
459 dc
.DrawLine(xMiddle
- halfWidth
, yMiddle
,
460 xMiddle
+ halfWidth
+ 1, yMiddle
);
462 if ( !(flags
& wxCONTROL_EXPANDED
) )
465 const wxCoord halfHeight
= rect
.height
/2 - 2;
466 dc
.DrawLine(xMiddle
, yMiddle
- halfHeight
,
467 xMiddle
, yMiddle
+ halfHeight
+ 1);
471 // ----------------------------------------------------------------------------
473 // ----------------------------------------------------------------------------
475 wxSplitterRenderParams
476 wxRendererGeneric::GetSplitterParams(const wxWindow
*win
)
482 if ( win
->HasFlag(wxSP_3DSASH
) )
484 else if ( win
->HasFlag(wxSP_NOSASH
) )
489 if ( win
->HasFlag(wxSP_3DBORDER
) )
494 return wxSplitterRenderParams(sashWidth
, border
, false);
498 wxRendererGeneric::DrawSplitterBorder(wxWindow
*win
,
500 const wxRect
& rectOrig
,
503 if ( win
->HasFlag(wxSP_3DBORDER
) )
505 wxRect rect
= rectOrig
;
506 DrawShadedRect(dc
, &rect
, m_penDarkGrey
, m_penHighlight
);
507 DrawShadedRect(dc
, &rect
, m_penBlack
, m_penLightGrey
);
512 wxRendererGeneric::DrawSplitterSash(wxWindow
*win
,
514 const wxSize
& sizeReal
,
516 wxOrientation orient
,
519 // to avoid duplicating the same code for horizontal and vertical sashes,
520 // simply mirror the DC instead if needed (i.e. if horz splitter)
521 wxMirrorDC
dc(dcReal
, orient
!= wxVERTICAL
);
522 wxSize size
= dc
.Reflect(sizeReal
);
525 // we draw a Win32-like grey sash with possible 3D border here:
527 // ---- this is position
532 // GWGGGDB where G is light grey (face)
533 // GWGGGDB W white (light)
534 // GWGGGDB D dark grey (shadow)
535 // GWGGGDB B black (dark shadow)
537 // GWGGGDB and lower letters are our border (already drawn)
541 // only the middle 3 columns are drawn unless wxSP_3D is specified
543 const wxCoord h
= size
.y
;
546 // If we're drawing the border, draw the sash 3d lines shorter
547 if ( win
->HasFlag(wxSP_3DBORDER
) )
552 dc
.SetPen(*wxTRANSPARENT_PEN
);
553 dc
.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
)));
555 if ( win
->HasFlag(wxSP_3DSASH
) )
558 dc
.DrawRectangle(position
+ 2, 0, 3, h
);
560 dc
.SetPen(m_penLightGrey
);
561 dc
.DrawLine(position
, offset
, position
, h
- offset
);
563 dc
.SetPen(m_penHighlight
);
564 dc
.DrawLine(position
+ 1, 0, position
+ 1, h
);
566 dc
.SetPen(m_penDarkGrey
);
567 dc
.DrawLine(position
+ 5, 0, position
+ 5, h
);
569 dc
.SetPen(m_penBlack
);
570 dc
.DrawLine(position
+ 6, offset
, position
+ 6, h
- offset
);
575 dc
.DrawRectangle(position
, 0, 3, h
);
579 // ----------------------------------------------------------------------------
581 // ----------------------------------------------------------------------------
584 wxRendererGeneric::DrawComboBoxDropButton(wxWindow
*win
,
589 DrawPushButton(win
,dc
,rect
,flags
);
590 DrawDropArrow(win
,dc
,rect
,flags
);
594 wxRendererGeneric::DrawDropArrow(wxWindow
*win
,
599 // This generic implementation should be good
600 // enough for Windows platforms (including XP).
602 int arrowHalf
= rect
.width
/5;
603 int rectMid
= rect
.width
/ 2;
604 int arrowTopY
= (rect
.height
/2) - (arrowHalf
/2);
606 // This should always result in arrow with odd width.
609 wxPoint(rectMid
- arrowHalf
, arrowTopY
),
610 wxPoint(rectMid
+ arrowHalf
, arrowTopY
),
611 wxPoint(rectMid
, arrowTopY
+ arrowHalf
)
613 dc
.SetBrush(wxBrush(win
->GetForegroundColour()));
614 dc
.SetPen(wxPen(win
->GetForegroundColour()));
615 dc
.DrawPolygon(WXSIZEOF(pt
), pt
, rect
.x
, rect
.y
);
619 wxRendererGeneric::DrawCheckBox(wxWindow
*WXUNUSED(win
),
624 dc
.SetPen(*(flags
& wxCONTROL_DISABLED
? wxGREY_PEN
: wxBLACK_PEN
));
625 dc
.SetBrush( *wxTRANSPARENT_BRUSH
);
626 dc
.DrawRectangle(rect
);
628 if ( flags
& wxCONTROL_CHECKED
)
630 dc
.DrawCheckMark(rect
.Deflate(2, 2));
634 wxSize
wxRendererGeneric::GetCheckBoxSize(wxWindow
*WXUNUSED(win
))
636 return wxSize(16, 16);
640 wxRendererGeneric::DrawPushButton(wxWindow
*win
,
645 // Don't try anything too fancy. It'll just turn out looking
646 // out-of-place on most platforms.
647 wxColour bgCol
= flags
& wxCONTROL_DISABLED
?
648 wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE
) :
649 win
->GetBackgroundColour();
650 dc
.SetBrush(wxBrush(bgCol
));
651 dc
.SetPen(wxPen(bgCol
));
652 dc
.DrawRectangle(rect
);
657 wxRendererGeneric::DrawItemSelectionRect(wxWindow
* win
,
662 wxRendererGeneric::DrawItemSelectionRect(wxWindow
* WXUNUSED(win
),
669 if ( flags
& wxCONTROL_SELECTED
)
671 if ( flags
& wxCONTROL_FOCUSED
)
673 brush
= wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
677 brush
= wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW
));
682 brush
= *wxTRANSPARENT_BRUSH
;
686 if ((flags
& wxCONTROL_CURRENT
) && (flags
& wxCONTROL_FOCUSED
)
687 #if defined( __WXMAC__ ) && !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON
688 && IsControlActive( (ControlRef
)win
->GetHandle() )
691 dc
.SetPen( *wxBLACK_PEN
);
693 dc
.SetPen( *wxTRANSPARENT_PEN
);
695 dc
.DrawRectangle( rect
);
699 wxRendererGeneric::DrawFocusRect(wxWindow
* WXUNUSED(win
), wxDC
& dc
, const wxRect
& rect
, int WXUNUSED(flags
))
701 // draw the pixels manually because the "dots" in wxPen with wxDOT style
702 // may be short traits and not really dots
704 // note that to behave in the same manner as DrawRect(), we must exclude
705 // the bottom and right borders from the rectangle
706 wxCoord x1
= rect
.GetLeft(),
708 x2
= rect
.GetRight(),
709 y2
= rect
.GetBottom();
711 dc
.SetPen(m_penBlack
);
714 dc
.SetLogicalFunction(wxCOPY
);
716 // this seems to be closer than what Windows does than wxINVERT although
717 // I'm still not sure if it's correct
718 dc
.SetLogicalFunction(wxAND_REVERSE
);
722 for ( z
= x1
+ 1; z
< x2
; z
+= 2 )
723 dc
.DrawPoint(z
, rect
.GetTop());
725 wxCoord shift
= z
== x2
? 0 : 1;
726 for ( z
= y1
+ shift
; z
< y2
; z
+= 2 )
729 shift
= z
== y2
? 0 : 1;
730 for ( z
= x2
- shift
; z
> x1
; z
-= 2 )
733 shift
= z
== x1
? 0 : 1;
734 for ( z
= y2
- shift
; z
> y1
; z
-= 2 )
737 dc
.SetLogicalFunction(wxCOPY
);
740 void wxRendererGeneric::DrawChoice(wxWindow
* WXUNUSED(win
), wxDC
& WXUNUSED(dc
),
741 const wxRect
& WXUNUSED(rect
), int WXUNUSED(flags
))
746 void wxRendererGeneric::DrawComboBox(wxWindow
* WXUNUSED(win
), wxDC
& WXUNUSED(dc
),
747 const wxRect
& WXUNUSED(rect
), int WXUNUSED(flags
))
752 void wxRendererGeneric::DrawRadioButton(wxWindow
* WXUNUSED(win
), wxDC
& WXUNUSED(dc
),
753 const wxRect
& WXUNUSED(rect
), int WXUNUSED(flags
))
758 void wxRendererGeneric::DrawTextCtrl(wxWindow
* WXUNUSED(win
), wxDC
& WXUNUSED(dc
),
759 const wxRect
& WXUNUSED(rect
), int WXUNUSED(flags
))
767 // ----------------------------------------------------------------------------
768 // A module to allow cleanup of generic renderer.
769 // ----------------------------------------------------------------------------
771 class wxGenericRendererModule
: public wxModule
773 DECLARE_DYNAMIC_CLASS(wxGenericRendererModule
)
775 wxGenericRendererModule() {}
776 bool OnInit() { return true; }
777 void OnExit() { wxRendererGeneric::Cleanup(); }
780 IMPLEMENT_DYNAMIC_CLASS(wxGenericRendererModule
, wxModule
)