1///////////////////////////////////////////////////////////////////////////////
2// Name:        src/generic/renderg.cpp
3// Purpose:     generic implementation of wxRendererNative (for any platform)
4// Author:      Vadim Zeitlin
5// Modified by:
6// Created:     20.07.2003
7// RCS-ID:      $Id: renderg.cpp 45498 2007-04-16 13:03:05Z VZ $
8// Copyright:   (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9// License:     wxWindows license
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#include "wx/renderer.h"
28
29#ifndef WX_PRECOMP
30    #include "wx/string.h"
31    #include "wx/dc.h"
32    #include "wx/settings.h"
33    #include "wx/gdicmn.h"
34    #include "wx/module.h"
35#endif //WX_PRECOMP
36
37#include "wx/splitter.h"
38#include "wx/dcmirror.h"
39
40// ----------------------------------------------------------------------------
41// wxRendererGeneric: our wxRendererNative implementation
42// ----------------------------------------------------------------------------
43
44class WXDLLEXPORT wxRendererGeneric : public wxRendererNative
45{
46public:
47    wxRendererGeneric();
48
49    virtual int  DrawHeaderButton(wxWindow *win,
50                                  wxDC& dc,
51                                  const wxRect& rect,
52                                  int flags = 0,
53                                  wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE,
54                                  wxHeaderButtonParams* params = NULL);
55
56    virtual int  DrawHeaderButtonContents(wxWindow *win,
57                                          wxDC& dc,
58                                          const wxRect& rect,
59                                          int flags = 0,
60                                          wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE,
61                                          wxHeaderButtonParams* params = NULL);
62
63    virtual int GetHeaderButtonHeight(wxWindow *win);
64
65    virtual void DrawTreeItemButton(wxWindow *win,
66                                    wxDC& dc,
67                                    const wxRect& rect,
68                                    int flags = 0);
69
70    virtual void DrawSplitterBorder(wxWindow *win,
71                                    wxDC& dc,
72                                    const wxRect& rect,
73                                    int flags = 0);
74
75    virtual void DrawSplitterSash(wxWindow *win,
76                                  wxDC& dc,
77                                  const wxSize& size,
78                                  wxCoord position,
79                                  wxOrientation orient,
80                                  int flags = 0);
81
82    virtual void DrawComboBoxDropButton(wxWindow *win,
83                                        wxDC& dc,
84                                        const wxRect& rect,
85                                        int flags = 0);
86
87    virtual void DrawDropArrow(wxWindow *win,
88                               wxDC& dc,
89                               const wxRect& rect,
90                               int flags = 0);
91
92    virtual void DrawCheckBox(wxWindow *win,
93                              wxDC& dc,
94                              const wxRect& rect,
95                              int flags = 0);
96
97    virtual void DrawPushButton(wxWindow *win,
98                                wxDC& dc,
99                                const wxRect& rect,
100                                int flags = 0);
101
102    virtual void DrawItemSelectionRect(wxWindow *win,
103                                       wxDC& dc,
104                                       const wxRect& rect,
105                                       int flags = 0);
106
107    virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win);
108
109    virtual wxRendererVersion GetVersion() const
110    {
111        return wxRendererVersion(wxRendererVersion::Current_Version,
112                                 wxRendererVersion::Current_Age);
113    }
114
115
116    // Cleanup by deleting standard renderer
117    static void Cleanup();
118
119    // Get the generic object
120    static wxRendererGeneric* DoGetGeneric();
121
122protected:
123    // draw the rectange using the first pen for the left and top sides and
124    // the second one for the bottom and right ones
125    void DrawShadedRect(wxDC& dc, wxRect *rect,
126                        const wxPen& pen1, const wxPen& pen2);
127
128    // the standard pens
129    wxPen m_penBlack,
130          m_penDarkGrey,
131          m_penLightGrey,
132          m_penHighlight;
133
134    static wxRendererGeneric* sm_rendererGeneric;
135};
136
137// ============================================================================
138// wxRendererGeneric implementation
139// ============================================================================
140
141// Get the generic object
142wxRendererGeneric* wxRendererGeneric::DoGetGeneric()
143{
144    if (!sm_rendererGeneric)
145        sm_rendererGeneric = new wxRendererGeneric;
146    return sm_rendererGeneric;
147}
148
149// ----------------------------------------------------------------------------
150// wxRendererGeneric creation
151// ----------------------------------------------------------------------------
152
153/* static */
154wxRendererNative& wxRendererNative::GetGeneric()
155{
156    return * wxRendererGeneric::DoGetGeneric();
157}
158
159void wxRendererGeneric::Cleanup()
160{
161    if (sm_rendererGeneric)
162        delete sm_rendererGeneric;
163
164    sm_rendererGeneric = NULL;
165}
166
167wxRendererGeneric* wxRendererGeneric::sm_rendererGeneric = NULL;
168
169wxRendererGeneric::wxRendererGeneric()
170    : m_penBlack(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW)),
171      m_penDarkGrey(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW)),
172      m_penLightGrey(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE)),
173      m_penHighlight(wxSystemSettings::GetColour(wxSYS_COLOUR_3DHIGHLIGHT))
174{
175}
176
177// ----------------------------------------------------------------------------
178// wxRendererGeneric helpers
179// ----------------------------------------------------------------------------
180
181void
182wxRendererGeneric::DrawShadedRect(wxDC& dc,
183                                  wxRect *rect,
184                                  const wxPen& pen1,
185                                  const wxPen& pen2)
186{
187    // draw the rectangle
188    dc.SetPen(pen1);
189    dc.DrawLine(rect->GetLeft(), rect->GetTop(),
190                rect->GetLeft(), rect->GetBottom());
191    dc.DrawLine(rect->GetLeft() + 1, rect->GetTop(),
192                rect->GetRight(), rect->GetTop());
193    dc.SetPen(pen2);
194    dc.DrawLine(rect->GetRight(), rect->GetTop(),
195                rect->GetRight(), rect->GetBottom());
196    dc.DrawLine(rect->GetLeft(), rect->GetBottom(),
197                rect->GetRight() + 1, rect->GetBottom());
198
199    // adjust the rect
200    rect->Inflate(-1);
201}
202
203// ----------------------------------------------------------------------------
204// tree/list ctrl drawing
205// ----------------------------------------------------------------------------
206
207int
208wxRendererGeneric::DrawHeaderButton(wxWindow* win,
209                                    wxDC& dc,
210                                    const wxRect& rect,
211                                    int flags,
212                                    wxHeaderSortIconType sortArrow,
213                                    wxHeaderButtonParams* params)
214{
215    const int CORNER = 1;
216
217    const wxCoord x = rect.x,
218                  y = rect.y,
219                  w = rect.width,
220                  h = rect.height;
221
222    dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE)));
223    dc.SetPen(*wxTRANSPARENT_PEN);
224    dc.DrawRectangle(rect);
225
226    dc.SetBrush(*wxTRANSPARENT_BRUSH);
227
228    dc.SetPen(m_penBlack);
229    dc.DrawLine( x+w-CORNER+1, y, x+w, y+h );  // right (outer)
230    dc.DrawRectangle( x, y+h, w+1, 1 );        // bottom (outer)
231
232    dc.SetPen(m_penDarkGrey);
233    dc.DrawLine( x+w-CORNER, y, x+w-1, y+h );  // right (inner)
234    dc.DrawRectangle( x+1, y+h-1, w-2, 1 );    // bottom (inner)
235
236    dc.SetPen(m_penHighlight);
237    dc.DrawRectangle( x, y, w-CORNER+1, 1 );   // top (outer)
238    dc.DrawRectangle( x, y, 1, h );            // left (outer)
239    dc.DrawLine( x, y+h-1, x+1, y+h-1 );
240    dc.DrawLine( x+w-1, y, x+w-1, y+1 );
241
242    return DrawHeaderButtonContents(win, dc, rect, flags, sortArrow, params);
243}
244
245
246int
247wxRendererGeneric::DrawHeaderButtonContents(wxWindow *win,
248                                            wxDC& dc,
249                                            const wxRect& rect,
250                                            int flags,
251                                            wxHeaderSortIconType sortArrow,
252                                            wxHeaderButtonParams* params)
253{
254    int labelWidth = 0;
255
256    // Mark this item as selected.  For the generic version we'll just draw an
257    // underline
258    if ( flags & wxCONTROL_SELECTED )
259    {
260        // draw a line at the bottom of the header button, overlaying the
261        // native hot-tracking line (on XP)
262        const int penwidth = 3;
263        int y = rect.y + rect.height + 1 - penwidth;
264        wxColour c = (params && params->m_selectionColour.Ok()) ?
265            params->m_selectionColour : wxColour(0x66, 0x66, 0x66);
266        wxPen pen(c, penwidth);
267        pen.SetCap(wxCAP_BUTT);
268        dc.SetPen(pen);
269        dc.DrawLine(rect.x, y, rect.x + rect.width, y);
270    }
271
272    // Draw an up or down arrow
273    int arrowSpace = 0;
274    if (sortArrow != wxHDR_SORT_ICON_NONE )
275    {
276        wxRect ar = rect;
277
278        // make a rect for the arrow
279        ar.height = 4;
280        ar.width = 8;
281        ar.y += (rect.height - ar.height)/2;
282        ar.x = ar.x + rect.width - 3*ar.width/2;
283        arrowSpace = 3*ar.width/2; // space to preserve when drawing the label
284
285        wxPoint triPt[3];
286        if ( sortArrow & wxHDR_SORT_ICON_UP )
287        {
288            triPt[0].x = ar.width / 2;
289            triPt[0].y = 0;
290            triPt[1].x = ar.width;
291            triPt[1].y = ar.height;
292            triPt[2].x = 0;
293            triPt[2].y = ar.height;
294        }
295        else
296        {
297            triPt[0].x = 0;
298            triPt[0].y = 0;
299            triPt[1].x = ar.width;
300            triPt[1].y = 0;
301            triPt[2].x = ar.width / 2;
302            triPt[2].y = ar.height;
303        }
304
305        wxColour c = (params && params->m_arrowColour.Ok()) ?
306            params->m_arrowColour : wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW);
307        dc.SetPen(wxPen(c));
308        dc.SetBrush(wxBrush(c));
309        dc.DrawPolygon( 3, triPt, ar.x, ar.y);
310    }
311    labelWidth += arrowSpace;
312
313    const int margin = 5;   // number of pixels to reserve on either side of the label
314    int bmpWidth = 0;
315    int txtEnd = 0;
316
317    if ( params && params->m_labelBitmap.Ok() )
318        bmpWidth = params->m_labelBitmap.GetWidth() + 2;
319
320    labelWidth += bmpWidth + 2*margin;
321
322    // Draw a label if one is given
323    if ( params && !params->m_labelText.empty() )
324    {
325        wxFont font  = params->m_labelFont.Ok() ?
326            params->m_labelFont : win->GetFont();
327        wxColour clr = params->m_labelColour.Ok() ?
328            params->m_labelColour : win->GetForegroundColour();
329
330        wxString label( params->m_labelText );
331
332        dc.SetFont(font);
333        dc.SetTextForeground(clr);
334        dc.SetBackgroundMode(wxTRANSPARENT);
335
336        int tw, th, td, x, y;
337        dc.GetTextExtent( label, &tw, &th, &td);
338        labelWidth += tw;
339        y = rect.y + wxMax(0, (rect.height - (th+td)) / 2);
340
341        // truncate and add an ellipsis (...) if the text is too wide.
342        int targetWidth = rect.width - arrowSpace - bmpWidth - 2*margin;
343        if ( tw > targetWidth )
344        {
345            int ellipsisWidth;
346            dc.GetTextExtent( wxT("..."), &ellipsisWidth, NULL);
347            do {
348                label.Truncate( label.length() - 1 );
349                dc.GetTextExtent( label, &tw, &th);
350            } while (tw + ellipsisWidth > targetWidth && label.length() );
351            label.append( wxT("...") );
352            tw += ellipsisWidth;
353        }
354
355        switch (params->m_labelAlignment)
356        {
357            default:
358            case wxALIGN_LEFT:
359                x = rect.x + margin;
360                break;
361            case wxALIGN_CENTER:
362                x = rect.x + wxMax(0, (rect.width - arrowSpace  - tw - bmpWidth)/2);
363                break;
364            case wxALIGN_RIGHT:
365                x = rect.x + wxMax(0, rect.width - arrowSpace - margin - tw - bmpWidth);
366                break;
367        }
368
369        dc.DrawText(label, x, y);
370        txtEnd = x + tw + 2;
371    }
372
373    // draw the bitmap if there is one
374    if ( params && params->m_labelBitmap.Ok() )
375    {
376        int w, h, x, y;
377        w = params->m_labelBitmap.GetWidth();
378        h = params->m_labelBitmap.GetHeight();
379
380        y = rect.y + wxMax(1, (rect.height - h) / 2);
381
382        // if there is a text label, then put the bitmap at the end of the label
383        if ( txtEnd != 0 )
384        {
385            x = txtEnd;
386        }
387        // otherwise use the alignment flags
388        else
389        {
390            switch (params->m_labelAlignment)
391            {
392                default:
393                case wxALIGN_LEFT:
394                    x = rect.x + margin;
395                    break;
396                case wxALIGN_CENTER:
397                    x = rect.x + wxMax(1, (rect.width - arrowSpace - w)/2);
398                    break;
399                case wxALIGN_RIGHT:
400                    x = rect.x + wxMax(1, rect.width - arrowSpace - margin - w);
401                    break;
402            }
403        }
404        dc.DrawBitmap(params->m_labelBitmap, x, y, true);
405    }
406    return labelWidth;
407}
408
409
410int wxRendererGeneric::GetHeaderButtonHeight(wxWindow *win)
411{
412    // Copied and adapted from src/generic/listctrl.cpp
413    const int HEADER_OFFSET_Y = 1;
414    const int EXTRA_HEIGHT = 4;
415
416    int w=0, h=14, d=0;
417    if (win)
418        win->GetTextExtent(wxT("Hg"), &w, &h, &d);
419
420    return h + d + 2 * HEADER_OFFSET_Y + EXTRA_HEIGHT;
421}
422
423
424// draw the plus or minus sign
425void
426wxRendererGeneric::DrawTreeItemButton(wxWindow * WXUNUSED(win),
427                                      wxDC& dc,
428                                      const wxRect& rect,
429                                      int flags)
430{
431    // store settings
432    wxDCPenChanger penChanger(dc, *wxGREY_PEN);
433    wxDCBrushChanger brushChanger(dc, *wxWHITE_BRUSH);
434
435    dc.DrawRectangle(rect);
436
437    // black lines
438    const wxCoord xMiddle = rect.x + rect.width/2;
439    const wxCoord yMiddle = rect.y + rect.height/2;
440
441    // half of the length of the horz lines in "-" and "+"
442    const wxCoord halfWidth = rect.width/2 - 2;
443    dc.SetPen(*wxBLACK_PEN);
444    dc.DrawLine(xMiddle - halfWidth, yMiddle,
445                xMiddle + halfWidth + 1, yMiddle);
446
447    if ( !(flags & wxCONTROL_EXPANDED) )
448    {
449        // turn "-" into "+"
450        const wxCoord halfHeight = rect.height/2 - 2;
451        dc.DrawLine(xMiddle, yMiddle - halfHeight,
452                    xMiddle, yMiddle + halfHeight + 1);
453    }
454}
455
456// ----------------------------------------------------------------------------
457// sash drawing
458// ----------------------------------------------------------------------------
459
460wxSplitterRenderParams
461wxRendererGeneric::GetSplitterParams(const wxWindow *win)
462{
463    // see below
464    wxCoord sashWidth,
465            border;
466
467    if ( win->HasFlag(wxSP_3DSASH) )
468        sashWidth = 7;
469    else if ( win->HasFlag(wxSP_NOSASH) )
470        sashWidth = 0;
471    else // no 3D effect
472        sashWidth = 3;
473
474    if ( win->HasFlag(wxSP_3DBORDER) )
475        border = 2;
476    else // no 3D effect
477        border = 0;
478
479    return wxSplitterRenderParams(sashWidth, border, false);
480}
481
482void
483wxRendererGeneric::DrawSplitterBorder(wxWindow *win,
484                                      wxDC& dc,
485                                      const wxRect& rectOrig,
486                                      int WXUNUSED(falgs))
487{
488    if ( win->HasFlag(wxSP_3DBORDER) )
489    {
490        wxRect rect = rectOrig;
491        DrawShadedRect(dc, &rect, m_penDarkGrey, m_penHighlight);
492        DrawShadedRect(dc, &rect, m_penBlack, m_penLightGrey);
493    }
494}
495
496void
497wxRendererGeneric::DrawSplitterSash(wxWindow *win,
498                                    wxDC& dcReal,
499                                    const wxSize& sizeReal,
500                                    wxCoord position,
501                                    wxOrientation orient,
502                                    int WXUNUSED(flags))
503{
504    // to avoid duplicating the same code for horizontal and vertical sashes,
505    // simply mirror the DC instead if needed (i.e. if horz splitter)
506    wxMirrorDC dc(dcReal, orient != wxVERTICAL);
507    wxSize size = dc.Reflect(sizeReal);
508
509
510    // we draw a Win32-like grey sash with possible 3D border here:
511    //
512    //   ---- this is position
513    //  /
514    // v
515    // dWGGGDd
516    // GWGGGDB
517    // GWGGGDB  where G is light grey (face)
518    // GWGGGDB        W    white      (light)
519    // GWGGGDB        D    dark grey  (shadow)
520    // GWGGGDB        B    black      (dark shadow)
521    // GWGGGDB
522    // GWGGGDB  and lower letters are our border (already drawn)
523    // GWGGGDB
524    // wWGGGDd
525    //
526    // only the middle 3 columns are drawn unless wxSP_3D is specified
527
528    const wxCoord h = size.y;
529    wxCoord offset = 0;
530
531    // If we're drawing the border, draw the sash 3d lines shorter
532    if ( win->HasFlag(wxSP_3DBORDER) )
533    {
534        offset = 1;
535    }
536
537    dc.SetPen(*wxTRANSPARENT_PEN);
538    dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE)));
539
540    if ( win->HasFlag(wxSP_3DSASH) )
541    {
542        // Draw the 3D sash
543        dc.DrawRectangle(position + 2, 0, 3, h);
544
545        dc.SetPen(m_penLightGrey);
546        dc.DrawLine(position, offset, position, h - offset);
547
548        dc.SetPen(m_penHighlight);
549        dc.DrawLine(position + 1, 0, position + 1, h);
550
551        dc.SetPen(m_penDarkGrey);
552        dc.DrawLine(position + 5, 0, position + 5, h);
553
554        dc.SetPen(m_penBlack);
555        dc.DrawLine(position + 6, offset, position + 6, h - offset);
556    }
557    else
558    {
559        // Draw a flat sash
560        dc.DrawRectangle(position, 0, 3, h);
561    }
562}
563
564// ----------------------------------------------------------------------------
565// button drawing
566// ----------------------------------------------------------------------------
567
568void
569wxRendererGeneric::DrawComboBoxDropButton(wxWindow *win,
570                                          wxDC& dc,
571                                          const wxRect& rect,
572                                          int flags)
573{
574    DrawPushButton(win,dc,rect,flags);
575    DrawDropArrow(win,dc,rect,flags);
576}
577
578void
579wxRendererGeneric::DrawDropArrow(wxWindow *win,
580                                 wxDC& dc,
581                                 const wxRect& rect,
582                                 int WXUNUSED(flags))
583{
584    // This generic implementation should be good
585    // enough for Windows platforms (including XP).
586
587    int arrowHalf = rect.width/5;
588    int rectMid = rect.width / 2;
589    int arrowTopY = (rect.height/2) - (arrowHalf/2);
590
591    // This should always result in arrow with odd width.
592    wxPoint pt[] =
593    {
594        wxPoint(rectMid - arrowHalf, arrowTopY),
595        wxPoint(rectMid + arrowHalf, arrowTopY),
596        wxPoint(rectMid, arrowTopY + arrowHalf)
597    };
598    dc.SetBrush(wxBrush(win->GetForegroundColour()));
599    dc.SetPen(wxPen(win->GetForegroundColour()));
600    dc.DrawPolygon(WXSIZEOF(pt), pt, rect.x, rect.y);
601}
602
603void
604wxRendererGeneric::DrawCheckBox(wxWindow *WXUNUSED(win),
605                                wxDC& dc,
606                                const wxRect& rect,
607                                int flags)
608{
609    dc.SetPen(*(flags & wxCONTROL_DISABLED ? wxGREY_PEN : wxBLACK_PEN));
610    dc.SetBrush( *wxTRANSPARENT_BRUSH );
611    dc.DrawRectangle(rect);
612
613    if ( flags & wxCONTROL_CHECKED )
614    {
615        dc.DrawCheckMark(rect.Deflate(2, 2));
616    }
617}
618
619void
620wxRendererGeneric::DrawPushButton(wxWindow *win,
621                                  wxDC& dc,
622                                  const wxRect& rect,
623                                  int flags)
624{
625    // Don't try anything too fancy. It'll just turn out looking
626    // out-of-place on most platforms.
627    wxColour bgCol = flags & wxCONTROL_DISABLED ?
628                        wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE) :
629                        win->GetBackgroundColour();
630    dc.SetBrush(wxBrush(bgCol));
631    dc.SetPen(wxPen(bgCol));
632    dc.DrawRectangle(rect);
633}
634
635void
636wxRendererGeneric::DrawItemSelectionRect(wxWindow * WXUNUSED(win),
637                                         wxDC& dc,
638                                         const wxRect& rect,
639                                         int flags)
640{
641    wxBrush brush;
642    if ( flags & wxCONTROL_SELECTED )
643    {
644        if ( flags & wxCONTROL_FOCUSED )
645        {
646            brush = wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
647        }
648        else // !focused
649        {
650            brush = wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW));
651        }
652    }
653    else // !selected
654    {
655        brush = *wxTRANSPARENT_BRUSH;
656    }
657
658    dc.SetBrush(brush);
659    dc.SetPen(flags & wxCONTROL_CURRENT ? *wxBLACK_PEN : *wxTRANSPARENT_PEN);
660
661    dc.DrawRectangle( rect );
662}
663
664
665// ----------------------------------------------------------------------------
666// A module to allow cleanup of generic renderer.
667// ----------------------------------------------------------------------------
668
669class wxGenericRendererModule: public wxModule
670{
671DECLARE_DYNAMIC_CLASS(wxGenericRendererModule)
672public:
673    wxGenericRendererModule() {}
674    bool OnInit() { return true; }
675    void OnExit() { wxRendererGeneric::Cleanup(); }
676};
677
678IMPLEMENT_DYNAMIC_CLASS(wxGenericRendererModule, wxModule)
679