1///////////////////////////////////////////////////////////////////////////////
2// Name:        src/univ/themes/metal.cpp
3// Purpose:     wxUniversal theme implementing Win32-like LNF
4// Author:      Vadim Zeitlin, Robert Roebling
5// Modified by:
6// Created:     06.08.00
7// RCS-ID:      $Id: metal.cpp 42455 2006-10-26 15:33:10Z VS $
8// Copyright:   (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
9// Licence:     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#include "wx/univ/theme.h"
28
29#if wxUSE_THEME_METAL
30
31#ifndef WX_PRECOMP
32    #include "wx/timer.h"
33    #include "wx/intl.h"
34    #include "wx/dc.h"
35    #include "wx/window.h"
36
37    #include "wx/dcmemory.h"
38
39    #include "wx/button.h"
40    #include "wx/listbox.h"
41    #include "wx/checklst.h"
42    #include "wx/combobox.h"
43    #include "wx/scrolbar.h"
44    #include "wx/slider.h"
45    #include "wx/textctrl.h"
46    #include "wx/toolbar.h"
47
48    #include "wx/menu.h"
49    #include "wx/settings.h"
50    #include "wx/toplevel.h"
51#endif // WX_PRECOMP
52
53#include "wx/notebook.h"
54#include "wx/spinbutt.h"
55#include "wx/artprov.h"
56
57#include "wx/univ/scrtimer.h"
58#include "wx/univ/renderer.h"
59#include "wx/univ/inpcons.h"
60#include "wx/univ/inphand.h"
61#include "wx/univ/colschem.h"
62
63// ----------------------------------------------------------------------------
64// wxMetalRenderer: draw the GUI elements in Metal style
65// ----------------------------------------------------------------------------
66
67class wxMetalRenderer : public wxDelegateRenderer
68{
69    // FIXME cut'n'paste from Win32
70    enum wxArrowDirection
71    {
72        Arrow_Left,
73        Arrow_Right,
74        Arrow_Up,
75        Arrow_Down,
76        Arrow_Max
77    };
78
79    enum wxArrowStyle
80    {
81        Arrow_Normal,
82        Arrow_Disabled,
83        Arrow_Pressed,
84        Arrow_Inverted,
85        Arrow_InvertedDisabled,
86        Arrow_StateMax
87    };
88public:
89    wxMetalRenderer(wxRenderer *renderer, wxColourScheme* scheme);
90
91    virtual void DrawButtonSurface(wxDC& dc,
92                                   const wxColour& WXUNUSED(col),
93                                   const wxRect& rect,
94                                   int WXUNUSED(flags))
95        { DrawMetal(dc, rect); }
96
97    virtual void DrawScrollbarThumb(wxDC& dc,
98                                    wxOrientation orient,
99                                    const wxRect& rect,
100                                    int flags);
101
102    virtual void DrawScrollbarShaft(wxDC& dc,
103                                    wxOrientation orient,
104                                    const wxRect& rectBar,
105                                    int flags);
106
107    virtual void GetComboBitmaps(wxBitmap *bmpNormal,
108                                 wxBitmap *bmpFocus,
109                                 wxBitmap *bmpPressed,
110                                 wxBitmap *bmpDisabled);
111
112    virtual void DrawArrow(wxDC& dc,
113                           wxDirection dir,
114                           const wxRect& rect,
115                           int flags = 0);
116protected:
117    void DrawArrowButton(wxDC& dc,
118                         const wxRect& rectAll,
119                         wxArrowDirection arrowDir,
120                         wxArrowStyle arrowStyle);
121
122    void DrawRect(wxDC& dc, wxRect *rect, const wxPen& pen);
123
124    void DrawShadedRect(wxDC& dc, wxRect *rect,
125                        const wxPen& pen1, const wxPen& pen2);
126
127    void DrawArrowBorder(wxDC& dc, wxRect *rect, bool isPressed = false);
128
129    void DrawArrow(wxDC& dc, const wxRect& rect,
130                   wxArrowDirection arrowDir, wxArrowStyle arrowStyle);
131
132    void DrawMetal(wxDC &dc, const wxRect &rect );
133private:
134    wxPen m_penBlack,
135          m_penDarkGrey,
136          m_penLightGrey,
137          m_penHighlight;
138
139    wxBitmap m_bmpArrows[Arrow_StateMax][Arrow_Max];
140};
141
142// ----------------------------------------------------------------------------
143// wxMetalTheme
144// ----------------------------------------------------------------------------
145
146class wxMetalTheme : public wxDelegateTheme
147{
148public:
149    wxMetalTheme() : wxDelegateTheme(_T("win32")), m_renderer(NULL) {}
150    ~wxMetalTheme() { delete m_renderer; }
151
152protected:
153    virtual wxRenderer *GetRenderer()
154    {
155        if ( !m_renderer )
156        {
157            m_renderer = new wxMetalRenderer(m_theme->GetRenderer(),
158                                             GetColourScheme());
159        }
160
161        return m_renderer;
162    }
163
164    wxRenderer *m_renderer;
165
166    WX_DECLARE_THEME(Metal)
167};
168
169WX_IMPLEMENT_THEME(wxMetalTheme, Metal, wxTRANSLATE("Metal theme"));
170
171
172// ============================================================================
173// implementation
174// ============================================================================
175
176// ----------------------------------------------------------------------------
177// wxMetalRenderer
178// ----------------------------------------------------------------------------
179
180wxMetalRenderer::wxMetalRenderer(wxRenderer *renderer, wxColourScheme *scheme)
181               : wxDelegateRenderer(renderer)
182{
183    // init colours and pens
184    m_penBlack = wxPen(wxSCHEME_COLOUR(scheme, SHADOW_DARK), 0, wxSOLID);
185    m_penDarkGrey = wxPen(wxSCHEME_COLOUR(scheme, SHADOW_OUT), 0, wxSOLID);
186    m_penLightGrey = wxPen(wxSCHEME_COLOUR(scheme, SHADOW_IN), 0, wxSOLID);
187    m_penHighlight = wxPen(wxSCHEME_COLOUR(scheme, SHADOW_HIGHLIGHT), 0, wxSOLID);
188
189    // init the arrow bitmaps
190    static const size_t ARROW_WIDTH = 7;
191    static const size_t ARROW_LENGTH = 4;
192
193    wxMask *mask;
194    wxMemoryDC dcNormal,
195               dcDisabled,
196               dcInverse;
197    for ( size_t n = 0; n < Arrow_Max; n++ )
198    {
199        bool isVertical = n > Arrow_Right;
200        int w, h;
201        if ( isVertical )
202        {
203            w = ARROW_WIDTH;
204            h = ARROW_LENGTH;
205        }
206        else
207        {
208            h = ARROW_WIDTH;
209            w = ARROW_LENGTH;
210        }
211
212        // disabled arrow is larger because of the shadow
213        m_bmpArrows[Arrow_Normal][n].Create(w, h);
214        m_bmpArrows[Arrow_Disabled][n].Create(w + 1, h + 1);
215
216        dcNormal.SelectObject(m_bmpArrows[Arrow_Normal][n]);
217        dcDisabled.SelectObject(m_bmpArrows[Arrow_Disabled][n]);
218
219        dcNormal.SetBackground(*wxWHITE_BRUSH);
220        dcDisabled.SetBackground(*wxWHITE_BRUSH);
221        dcNormal.Clear();
222        dcDisabled.Clear();
223
224        dcNormal.SetPen(m_penBlack);
225        dcDisabled.SetPen(m_penDarkGrey);
226
227        // calculate the position of the point of the arrow
228        wxCoord x1, y1;
229        if ( isVertical )
230        {
231            x1 = (ARROW_WIDTH - 1)/2;
232            y1 = n == Arrow_Up ? 0 : ARROW_LENGTH - 1;
233        }
234        else // horizontal
235        {
236            x1 = n == Arrow_Left ? 0 : ARROW_LENGTH - 1;
237            y1 = (ARROW_WIDTH - 1)/2;
238        }
239
240        wxCoord x2 = x1,
241                y2 = y1;
242
243        if ( isVertical )
244            x2++;
245        else
246            y2++;
247
248        for ( size_t i = 0; i < ARROW_LENGTH; i++ )
249        {
250            dcNormal.DrawLine(x1, y1, x2, y2);
251            dcDisabled.DrawLine(x1, y1, x2, y2);
252
253            if ( isVertical )
254            {
255                x1--;
256                x2++;
257
258                if ( n == Arrow_Up )
259                {
260                    y1++;
261                    y2++;
262                }
263                else // down arrow
264                {
265                    y1--;
266                    y2--;
267                }
268            }
269            else // left or right arrow
270            {
271                y1--;
272                y2++;
273
274                if ( n == Arrow_Left )
275                {
276                    x1++;
277                    x2++;
278                }
279                else
280                {
281                    x1--;
282                    x2--;
283                }
284            }
285        }
286
287        // draw the shadow for the disabled one
288        dcDisabled.SetPen(m_penHighlight);
289        switch ( n )
290        {
291            case Arrow_Left:
292                y1 += 2;
293                dcDisabled.DrawLine(x1, y1, x2, y2);
294                break;
295
296            case Arrow_Right:
297                x1 = ARROW_LENGTH - 1;
298                y1 = (ARROW_WIDTH - 1)/2 + 1;
299                x2 = 0;
300                y2 = ARROW_WIDTH;
301                dcDisabled.DrawLine(x1, y1, x2, y2);
302                dcDisabled.DrawLine(++x1, y1, x2, ++y2);
303                break;
304
305            case Arrow_Up:
306                x1 += 2;
307                dcDisabled.DrawLine(x1, y1, x2, y2);
308                break;
309
310            case Arrow_Down:
311                x1 = ARROW_WIDTH - 1;
312                y1 = 1;
313                x2 = (ARROW_WIDTH - 1)/2;
314                y2 = ARROW_LENGTH;
315                dcDisabled.DrawLine(x1, y1, x2, y2);
316                dcDisabled.DrawLine(++x1, y1, x2, ++y2);
317                break;
318
319        }
320
321        // create the inverted bitmap but only for the right arrow as we only
322        // use it for the menus
323        if ( n == Arrow_Right )
324        {
325            m_bmpArrows[Arrow_Inverted][n].Create(w, h);
326            dcInverse.SelectObject(m_bmpArrows[Arrow_Inverted][n]);
327            dcInverse.Clear();
328            dcInverse.Blit(0, 0, w, h,
329                          &dcNormal, 0, 0,
330                          wxXOR);
331            dcInverse.SelectObject(wxNullBitmap);
332
333            mask = new wxMask(m_bmpArrows[Arrow_Inverted][n], *wxBLACK);
334            m_bmpArrows[Arrow_Inverted][n].SetMask(mask);
335
336            m_bmpArrows[Arrow_InvertedDisabled][n].Create(w, h);
337            dcInverse.SelectObject(m_bmpArrows[Arrow_InvertedDisabled][n]);
338            dcInverse.Clear();
339            dcInverse.Blit(0, 0, w, h,
340                          &dcDisabled, 0, 0,
341                          wxXOR);
342            dcInverse.SelectObject(wxNullBitmap);
343
344            mask = new wxMask(m_bmpArrows[Arrow_InvertedDisabled][n], *wxBLACK);
345            m_bmpArrows[Arrow_InvertedDisabled][n].SetMask(mask);
346        }
347
348        dcNormal.SelectObject(wxNullBitmap);
349        dcDisabled.SelectObject(wxNullBitmap);
350
351        mask = new wxMask(m_bmpArrows[Arrow_Normal][n], *wxWHITE);
352        m_bmpArrows[Arrow_Normal][n].SetMask(mask);
353        mask = new wxMask(m_bmpArrows[Arrow_Disabled][n], *wxWHITE);
354        m_bmpArrows[Arrow_Disabled][n].SetMask(mask);
355
356        m_bmpArrows[Arrow_Pressed][n] = m_bmpArrows[Arrow_Normal][n];
357    }
358}
359
360void wxMetalRenderer::DrawScrollbarThumb(wxDC& dc,
361                                         wxOrientation WXUNUSED(orient),
362                                         const wxRect& rect,
363                                         int WXUNUSED(flags))
364{
365    // we don't use the flags, the thumb never changes appearance
366    wxRect rectThumb = rect;
367    DrawArrowBorder(dc, &rectThumb);
368    DrawMetal(dc, rectThumb);
369}
370
371void wxMetalRenderer::DrawScrollbarShaft(wxDC& dc,
372                                         wxOrientation WXUNUSED(orient),
373                                         const wxRect& rectBar,
374                                         int WXUNUSED(flags))
375{
376    DrawMetal(dc, rectBar);
377}
378
379void wxMetalRenderer::GetComboBitmaps(wxBitmap *bmpNormal,
380                                      wxBitmap * WXUNUSED(bmpFocus),
381                                      wxBitmap *bmpPressed,
382                                      wxBitmap *bmpDisabled)
383{
384    static const wxCoord widthCombo = 16;
385    static const wxCoord heightCombo = 17;
386
387    wxMemoryDC dcMem;
388
389    if ( bmpNormal )
390    {
391        bmpNormal->Create(widthCombo, heightCombo);
392        dcMem.SelectObject(*bmpNormal);
393        DrawArrowButton(dcMem, wxRect(0, 0, widthCombo, heightCombo),
394                        Arrow_Down, Arrow_Normal);
395    }
396
397    if ( bmpPressed )
398    {
399        bmpPressed->Create(widthCombo, heightCombo);
400        dcMem.SelectObject(*bmpPressed);
401        DrawArrowButton(dcMem, wxRect(0, 0, widthCombo, heightCombo),
402                        Arrow_Down, Arrow_Pressed);
403    }
404
405    if ( bmpDisabled )
406    {
407        bmpDisabled->Create(widthCombo, heightCombo);
408        dcMem.SelectObject(*bmpDisabled);
409        DrawArrowButton(dcMem, wxRect(0, 0, widthCombo, heightCombo),
410                        Arrow_Down, Arrow_Disabled);
411    }
412}
413
414void wxMetalRenderer::DrawArrow(wxDC& dc,
415                                wxDirection dir,
416                                const wxRect& rect,
417                                int flags)
418{
419    // get the bitmap for this arrow
420    wxArrowDirection arrowDir;
421    switch ( dir )
422    {
423        case wxLEFT:    arrowDir = Arrow_Left; break;
424        case wxRIGHT:   arrowDir = Arrow_Right; break;
425        case wxUP:      arrowDir = Arrow_Up; break;
426        case wxDOWN:    arrowDir = Arrow_Down; break;
427
428        default:
429            wxFAIL_MSG(_T("unknown arrow direction"));
430            return;
431    }
432
433    wxArrowStyle arrowStyle;
434    if ( flags & wxCONTROL_PRESSED )
435    {
436        // can't be pressed and disabled
437        arrowStyle = Arrow_Pressed;
438    }
439    else
440    {
441        arrowStyle = flags & wxCONTROL_DISABLED ? Arrow_Disabled : Arrow_Normal;
442    }
443
444    DrawArrowButton(dc, rect, arrowDir, arrowStyle);
445}
446
447//
448// protected functions
449//
450
451void wxMetalRenderer::DrawArrowButton(wxDC& dc,
452                                      const wxRect& rectAll,
453                                      wxArrowDirection arrowDir,
454                                      wxArrowStyle arrowStyle)
455{
456    wxRect rect = rectAll;
457    DrawMetal( dc, rect );
458    DrawArrowBorder(dc, &rect, arrowStyle == Arrow_Pressed);
459    DrawArrow(dc, rect, arrowDir, arrowStyle);
460}
461
462void wxMetalRenderer::DrawRect(wxDC& dc, wxRect *rect, const wxPen& pen)
463{
464    // draw
465    dc.SetPen(pen);
466    dc.SetBrush(*wxTRANSPARENT_BRUSH);
467    dc.DrawRectangle(*rect);
468
469    // adjust the rect
470    rect->Inflate(-1);
471}
472
473void wxMetalRenderer::DrawShadedRect(wxDC& dc, wxRect *rect,
474                                     const wxPen& pen1, const wxPen& pen2)
475{
476    // draw the rectangle
477    dc.SetPen(pen1);
478    dc.DrawLine(rect->GetLeft(), rect->GetTop(),
479                rect->GetLeft(), rect->GetBottom());
480    dc.DrawLine(rect->GetLeft() + 1, rect->GetTop(),
481                rect->GetRight(), rect->GetTop());
482    dc.SetPen(pen2);
483    dc.DrawLine(rect->GetRight(), rect->GetTop(),
484                rect->GetRight(), rect->GetBottom());
485    dc.DrawLine(rect->GetLeft(), rect->GetBottom(),
486                rect->GetRight() + 1, rect->GetBottom());
487
488    // adjust the rect
489    rect->Inflate(-1);
490}
491
492void wxMetalRenderer::DrawArrowBorder(wxDC& dc, wxRect *rect, bool isPressed)
493{
494    if ( isPressed )
495    {
496        DrawRect(dc, rect, m_penDarkGrey);
497
498        // the arrow is usually drawn inside border of width 2 and is offset by
499        // another pixel in both directions when it's pressed - as the border
500        // in this case is more narrow as well, we have to adjust rect like
501        // this:
502        rect->Inflate(-1);
503        rect->x++;
504        rect->y++;
505    }
506    else
507    {
508        DrawShadedRect(dc, rect, m_penLightGrey, m_penBlack);
509        DrawShadedRect(dc, rect, m_penHighlight, m_penDarkGrey);
510    }
511}
512
513void wxMetalRenderer::DrawArrow(wxDC& dc,
514                                const wxRect& rect,
515                                wxArrowDirection arrowDir,
516                                wxArrowStyle arrowStyle)
517{
518    const wxBitmap& bmp = m_bmpArrows[arrowStyle][arrowDir];
519
520    // under Windows the arrows always have the same size so just centre it in
521    // the provided rectangle
522    wxCoord x = rect.x + (rect.width - bmp.GetWidth()) / 2,
523            y = rect.y + (rect.height - bmp.GetHeight()) / 2;
524
525    // Windows does it like this...
526    if ( arrowDir == Arrow_Left )
527        x--;
528
529    // draw it
530    dc.DrawBitmap(bmp, x, y, true /* use mask */);
531}
532
533// ----------------------------------------------------------------------------
534// metal gradient
535// ----------------------------------------------------------------------------
536
537void wxMetalRenderer::DrawMetal(wxDC &dc, const wxRect &rect )
538{
539    dc.SetPen(*wxTRANSPARENT_PEN);
540    for (int y = rect.y; y < rect.height+rect.y; y++)
541    {
542       unsigned char intens = (unsigned char)(230 + 80 * (rect.y-y) / rect.height);
543       dc.SetBrush( wxBrush( wxColour(intens,intens,intens), wxSOLID ) );
544       dc.DrawRectangle( rect.x, y, rect.width, 1 );
545    }
546}
547
548#endif // wxUSE_THEME_METAL
549