///////////////////////////////////////////////////////////////////////////////
// Name:        src/univ/themes/win32.cpp
// Purpose:     wxUniversal theme implementing Win32-like LNF
// Author:      Vadim Zeitlin
// Created:     06.08.00
// Copyright:   (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
// Licence:     wxWindows licence
///////////////////////////////////////////////////////////////////////////////

// ===========================================================================
// declarations
// ===========================================================================

// ---------------------------------------------------------------------------
// headers
// ---------------------------------------------------------------------------

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"


#include "wx/univ/theme.h"

#if wxUSE_THEME_WIN32

#ifndef WX_PRECOMP
    #include "wx/timer.h"
    #include "wx/intl.h"
    #include "wx/dc.h"
    #include "wx/window.h"

    #include "wx/dcmemory.h"
    #include "wx/dcclient.h"

    #include "wx/button.h"
    #include "wx/bmpbuttn.h"
    #include "wx/listbox.h"
    #include "wx/checklst.h"
    #include "wx/combobox.h"
    #include "wx/scrolbar.h"
    #include "wx/slider.h"
    #include "wx/textctrl.h"
    #include "wx/toolbar.h"
    #include "wx/statusbr.h"

    #ifdef __WXMSW__
        // for COLOR_* constants
        #include "wx/msw/private.h"
    #endif
    #include "wx/menu.h"
    #include "wx/settings.h"
    #include "wx/toplevel.h"
    #include "wx/image.h"
#endif // WX_PRECOMP

#include "wx/notebook.h"
#include "wx/spinbutt.h"
#include "wx/artprov.h"
#include "wx/tglbtn.h"

#include "wx/univ/scrtimer.h"
#include "wx/univ/stdrend.h"
#include "wx/univ/inpcons.h"
#include "wx/univ/inphand.h"
#include "wx/univ/colschem.h"

// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------

static const int BORDER_THICKNESS = 2;

static const size_t NUM_STATUSBAR_GRIP_BANDS = 3;
static const size_t WIDTH_STATUSBAR_GRIP_BAND = 4;
static const size_t STATUSBAR_GRIP_SIZE =
    WIDTH_STATUSBAR_GRIP_BAND*NUM_STATUSBAR_GRIP_BANDS;

static const wxCoord SLIDER_MARGIN = 6; // margin around slider
static const wxCoord SLIDER_THUMB_LENGTH = 18;
static const wxCoord SLIDER_TICK_LENGTH = 6;

// wxWin32Renderer: draw the GUI elements in Win32 style
// ----------------------------------------------------------------------------

class wxWin32Renderer : public wxStdRenderer
{
public:
    // ctor
    wxWin32Renderer(const wxColourScheme *scheme);

    // reimplement the renderer methods which are different for this theme
    virtual void DrawLabel(wxDC& dc,
                           const wxString& label,
                           const wxRect& rect,
                           int flags = 0,
                           int alignment = wxALIGN_LEFT | wxALIGN_TOP,
                           int indexAccel = -1,
                           wxRect *rectBounds = nullptr);
    virtual void DrawButtonLabel(wxDC& dc,
                                 const wxString& label,
                                 const wxBitmap& image,
                                 const wxRect& rect,
                                 int flags = 0,
                                 int alignment = wxALIGN_LEFT | wxALIGN_TOP,
                                 int indexAccel = -1,
                                 wxRect *rectBounds = nullptr);
    virtual void DrawButtonBorder(wxDC& dc,
                                  const wxRect& rect,
                                  int flags = 0,
                                  wxRect *rectIn = nullptr);

    virtual void DrawArrow(wxDC& dc,
                           wxDirection dir,
                           const wxRect& rect,
                           int flags = 0);
    virtual void DrawScrollbarThumb(wxDC& dc,
                                    wxOrientation orient,
                                    const wxRect& rect,
                                    int flags = 0);
    virtual void DrawScrollbarShaft(wxDC& dc,
                                    wxOrientation orient,
                                    const wxRect& rect,
                                    int flags = 0);

#if wxUSE_TOOLBAR
    virtual void DrawToolBarButton(wxDC& dc,
                                   const wxString& label,
                                   const wxBitmap& bitmap,
                                   const wxRect& rect,
                                   int flags = 0,
                                   long style = 0,
                                   int tbarStyle = 0);
#endif // wxUSE_TOOLBAR

#if wxUSE_NOTEBOOK
    virtual void DrawTab(wxDC& dc,
                         const wxRect& rect,
                         wxDirection dir,
                         const wxString& label,
                         const wxBitmap& bitmap = wxNullBitmap,
                         int flags = 0,
                         int indexAccel = -1);
#endif // wxUSE_NOTEBOOK

#if wxUSE_SLIDER
    virtual void DrawSliderShaft(wxDC& dc,
                                 const wxRect& rect,
                                 int lenThumb,
                                 wxOrientation orient,
                                 int flags = 0,
                                 long style = 0,
                                 wxRect *rectShaft = nullptr);
    virtual void DrawSliderThumb(wxDC& dc,
                                 const wxRect& rect,
                                 wxOrientation orient,
                                 int flags = 0,
                                 long style = 0);
    virtual void DrawSliderTicks(wxDC& dc,
                                 const wxRect& rect,
                                 int lenThumb,
                                 wxOrientation orient,
                                 int start,
                                 int end,
                                 int step = 1,
                                 int flags = 0,
                                 long style = 0);
#endif // wxUSE_SLIDER

#if wxUSE_MENUS
    virtual void DrawMenuBarItem(wxDC& dc,
                                 const wxRect& rect,
                                 const wxString& label,
                                 int flags = 0,
                                 int indexAccel = -1);
    virtual void DrawMenuItem(wxDC& dc,
                              wxCoord y,
                              const wxMenuGeometryInfo& geometryInfo,
                              const wxString& label,
                              const wxString& accel,
                              const wxBitmap& bitmap = wxNullBitmap,
                              int flags = 0,
                              int indexAccel = -1);
    virtual void DrawMenuSeparator(wxDC& dc,
                                   wxCoord y,
                                   const wxMenuGeometryInfo& geomInfo);
#endif // wxUSE_MENUS

#if wxUSE_STATUSBAR
    virtual void DrawStatusField(wxDC& dc,
                                 const wxRect& rect,
                                 const wxString& label,
                                 int flags = 0, int style = 0);
#endif // wxUSE_STATUSBAR

    virtual void GetComboBitmaps(wxBitmap *bmpNormal,
                                 wxBitmap *bmpFocus,
                                 wxBitmap *bmpPressed,
                                 wxBitmap *bmpDisabled);

    virtual void AdjustSize(wxSize *size, const wxWindow *window);
    virtual bool AreScrollbarsInsideBorder() const;

    virtual wxSize GetScrollbarArrowSize() const
        { return m_sizeScrollbarArrow; }

    virtual wxSize GetCheckBitmapSize() const
        { return wxSize(13, 13); }
    virtual wxSize GetRadioBitmapSize() const
        { return wxSize(12, 12); }
    virtual wxCoord GetCheckItemMargin() const
        { return 0; }

#if wxUSE_TOOLBAR
    virtual wxSize GetToolBarButtonSize(wxCoord *separator) const
        { if ( separator ) *separator = 5; return wxSize(16, 15); }
    virtual wxSize GetToolBarMargin() const
        { return wxSize(4, 4); }
#endif // wxUSE_TOOLBAR

#if wxUSE_TEXTCTRL
    virtual wxRect GetTextTotalArea(const wxTextCtrl *text,
                                    const wxRect& rect) const;
    virtual wxRect GetTextClientArea(const wxTextCtrl *text,
                                     const wxRect& rect,
                                     wxCoord *extraSpaceBeyond) const;
#endif // wxUSE_TEXTCTRL

#if wxUSE_NOTEBOOK
    virtual wxSize GetTabIndent() const { return wxSize(2, 2); }
    virtual wxSize GetTabPadding() const { return wxSize(6, 5); }
#endif // wxUSE_NOTEBOOK

#if wxUSE_SLIDER

    virtual wxCoord GetSliderDim() const { return SLIDER_THUMB_LENGTH + 2*BORDER_THICKNESS; }
    virtual wxCoord GetSliderTickLen() const { return SLIDER_TICK_LENGTH; }
    virtual wxRect GetSliderShaftRect(const wxRect& rect,
                                      int lenThumb,
                                      wxOrientation orient,
                                      long style = 0) const;
    virtual wxSize GetSliderThumbSize(const wxRect& rect,
                                      int lenThumb,
                                      wxOrientation orient) const;
#endif // wxUSE_SLIDER

    virtual wxSize GetProgressBarStep() const { return wxSize(16, 32); }

#if wxUSE_MENUS
    virtual wxSize GetMenuBarItemSize(const wxSize& sizeText) const;
    virtual wxMenuGeometryInfo *GetMenuGeometry(wxWindow *win,
                                                const wxMenu& menu) const;
#endif // wxUSE_MENUS

protected:
    // overridden wxStdRenderer methods
    virtual void DrawFrameWithLabel(wxDC& dc,
                                    const wxString& label,
                                    const wxRect& rectFrame,
                                    const wxRect& rectText,
                                    int flags,
                                    int alignment,
                                    int indexAccel);

    virtual void DrawCheckItemBitmap(wxDC& dc,
                                     const wxBitmap& bitmap,
                                     const wxRect& rect,
                                     int flags);


    // draw the border used for scrollbar arrows
    void DrawArrowBorder(wxDC& dc, wxRect *rect, bool isPressed = false);

    // public DrawArrow()s helper
    void DrawArrow(wxDC& dc, const wxRect& rect,
                   ArrowDirection arrowDir, ArrowStyle arrowStyle);

    // DrawArrowButton is used by DrawScrollbar and DrawComboButton
    void DrawArrowButton(wxDC& dc, const wxRect& rect,
                         ArrowDirection arrowDir,
                         ArrowStyle arrowStyle);

    // draw a normal or transposed line (useful for using the same code fo both
    // horizontal and vertical widgets)
    void DrawLine(wxDC& dc,
                  wxCoord x1, wxCoord y1,
                  wxCoord x2, wxCoord y2,
                  bool transpose = false)
    {
        if ( transpose )
            dc.DrawLine(y1, x1, y2, x2);
        else
            dc.DrawLine(x1, y1, x2, y2);
    }

    // get the standard check/radio button bitmap
    wxBitmap GetIndicator(IndicatorType indType, int flags);
    virtual wxBitmap GetCheckBitmap(int flags)
        { return GetIndicator(IndicatorType_Check, flags); }
    virtual wxBitmap GetRadioBitmap(int flags)
        { return GetIndicator(IndicatorType_Radio, flags); }

    virtual wxBitmap GetFrameButtonBitmap(FrameButtonType type);

#if wxUSE_SLIDER
    // Fill the arguments with true or false if this slider has labels on
    // left/right side (or top/bottom for horizontal sliders) respectively
    static
    void GetSliderLabelsSides(wxOrientation orient, long style,
                              bool *left, bool *right)
    {
        // should we draw ticks at all?
        if ( !(style & wxSL_AUTOTICKS) )
        {
            *left =
            *right = false;
            return;
        }

        // should we draw them on both sides?
        if ( style & wxSL_BOTH )
        {
            *left =
            *right = true;
            return;
        }

        // we draw them on one side only, determine which one
        if ( ((style & wxSL_TOP) && (orient == wxHORIZONTAL)) ||
                ((style & wxSL_LEFT) && (orient == wxVERTICAL)) )
        {
            *left = true;
            *right = false;
        }
        else if ( ((style & wxSL_BOTTOM) && (orient == wxHORIZONTAL)) ||
                    ((style & wxSL_RIGHT) && (orient == wxVERTICAL)) )
        {
            *left = false;
            *right = true;
        }
        else
        {
            wxFAIL_MSG( "inconsistent wxSlider flags" );

            *left =
            *right = false;
        }
    }
#endif // wxUSE_SLIDER

private:
    // the sizing parameters (TODO make them changeable)
    wxSize m_sizeScrollbarArrow;

    // the checked and unchecked bitmaps for DrawCheckItemBitmap()
    wxBitmap m_bmpCheckBitmaps[IndicatorStatus_Max];

    // the bitmaps returned by GetIndicator()
    wxBitmap m_bmpIndicators[IndicatorType_Max]
                            [IndicatorState_MaxMenu]
                            [IndicatorStatus_Max];

    // titlebar icons:
    wxBitmap m_bmpFrameButtons[FrameButton_Max];

    // standard defaults for the above bitmaps
    static const char **ms_xpmChecked[IndicatorStatus_Max];
    static const char **ms_xpmIndicators[IndicatorType_Max]
                                        [IndicatorState_MaxMenu]
                                        [IndicatorStatus_Max];
    static const char **ms_xpmFrameButtons[FrameButton_Max];

    // first row is for the normal state, second - for the disabled
    wxBitmap m_bmpArrows[Arrow_StateMax][Arrow_Max];
};

// ----------------------------------------------------------------------------
// wxWin32InputHandler and derived classes: process the keyboard and mouse
// messages according to Windows standards
// ----------------------------------------------------------------------------

class wxWin32InputHandler : public wxInputHandler
{
public:
    wxWin32InputHandler() { }

    virtual bool HandleKey(wxInputConsumer *control,
                           const wxKeyEvent& event,
                           bool pressed);
    virtual bool HandleMouse(wxInputConsumer *control,
                             const wxMouseEvent& event);
};

#if wxUSE_SCROLLBAR
class wxWin32ScrollBarInputHandler : public wxStdScrollBarInputHandler
{
public:
    wxWin32ScrollBarInputHandler(wxRenderer *renderer,
                                 wxInputHandler *handler);

    virtual bool HandleMouse(wxInputConsumer *control,
                             const wxMouseEvent& event);
    virtual bool HandleMouseMove(wxInputConsumer *control,
                                 const wxMouseEvent& event);

    virtual bool OnScrollTimer(wxScrollBar *scrollbar,
                               const wxControlAction& action);

protected:
    virtual void Highlight(wxScrollBar * WXUNUSED(scrollbar),
                           bool WXUNUSED(doIt))
    {
        // we don't highlight anything
    }

    // the first and last event which caused the thumb to move
    wxMouseEvent m_eventStartDrag,
                 m_eventLastDrag;

    // have we paused the scrolling because the mouse moved?
    bool m_scrollPaused;

    // we remember the interval of the timer to be able to restart it
    int m_interval;
};
#endif // wxUSE_SCROLLBAR

#if wxUSE_CHECKBOX
class wxWin32CheckboxInputHandler : public wxStdInputHandler
{
public:
    wxWin32CheckboxInputHandler(wxInputHandler *handler)
        : wxStdInputHandler(handler) { }

    virtual bool HandleKey(wxInputConsumer *control,
                           const wxKeyEvent& event,
                           bool pressed);
};
#endif // wxUSE_CHECKBOX

#if wxUSE_TEXTCTRL
class wxWin32TextCtrlInputHandler : public wxStdInputHandler
{
public:
    wxWin32TextCtrlInputHandler(wxInputHandler *handler)
        : wxStdInputHandler(handler) { }

    virtual bool HandleKey(wxInputConsumer *control,
                           const wxKeyEvent& event,
                           bool pressed);
};
#endif // wxUSE_TEXTCTRL

class wxWin32StatusBarInputHandler : public wxStdInputHandler
{
public:
    wxWin32StatusBarInputHandler(wxInputHandler *handler);

    virtual bool HandleMouse(wxInputConsumer *consumer,
                             const wxMouseEvent& event);

    virtual bool HandleMouseMove(wxInputConsumer *consumer,
                                 const wxMouseEvent& event);

protected:
    // is the given point over the statusbar grip?
    bool IsOnGrip(wxWindow *statbar, const wxPoint& pt) const;

private:
    // the cursor we had replaced with the resize one
    wxCursor m_cursorOld;

    // was the mouse over the grip last time we checked?
    bool m_isOnGrip;
};

class wxWin32SystemMenuEvtHandler;

class wxWin32FrameInputHandler : public wxStdInputHandler
{
public:
    wxWin32FrameInputHandler(wxInputHandler *handler);
    virtual ~wxWin32FrameInputHandler();

    virtual bool HandleMouse(wxInputConsumer *control,
                             const wxMouseEvent& event);

    virtual bool HandleActivation(wxInputConsumer *consumer, bool activated);

#if wxUSE_MENUS
    void PopupSystemMenu(wxTopLevelWindow *window) const;
#endif // wxUSE_MENUS

private:
    // was the mouse over the grip last time we checked?
    wxWin32SystemMenuEvtHandler *m_menuHandler;
};

// ----------------------------------------------------------------------------
// wxWin32ColourScheme: uses (default) Win32 colours
// ----------------------------------------------------------------------------

class wxWin32ColourScheme : public wxColourScheme
{
public:
    virtual wxColour Get(StdColour col) const;
    virtual wxColour GetBackground(wxWindow *win) const;
};

// ----------------------------------------------------------------------------
// wxWin32ArtProvider
// ----------------------------------------------------------------------------

class wxWin32ArtProvider : public wxArtProvider
{
protected:
    virtual wxBitmap CreateBitmap(const wxArtID& id,
                                  const wxArtClient& client,
                                  const wxSize& size);
};

// ----------------------------------------------------------------------------
// wxWin32Theme
// ----------------------------------------------------------------------------

WX_DEFINE_ARRAY_PTR(wxInputHandler *, wxArrayHandlers);

class wxWin32Theme : public wxTheme
{
public:
    wxWin32Theme();
    virtual ~wxWin32Theme();

    virtual wxRenderer *GetRenderer();
    virtual wxArtProvider *GetArtProvider();
    virtual wxInputHandler *GetInputHandler(const wxString& control,
                                            wxInputConsumer *consumer);
    virtual wxColourScheme *GetColourScheme();

private:
    wxWin32Renderer *m_renderer;

    wxWin32ArtProvider *m_artProvider;

    // the names of the already created handlers and the handlers themselves
    // (these arrays are synchronized)
    wxSortedArrayString m_handlerNames;
    wxArrayHandlers m_handlers;

    wxWin32ColourScheme *m_scheme;

    WX_DECLARE_THEME(win32)
};

// ----------------------------------------------------------------------------
// standard bitmaps
// ----------------------------------------------------------------------------

// frame buttons bitmaps
static const char *frame_button_close_xpm[] = {
"12 10 2 1",
"         c None",
"X        c black",
"            ",
"  XX    XX  ",
"   XX  XX   ",
"    XXXX    ",
"     XX     ",
"    XXXX    ",
"   XX  XX   ",
"  XX    XX  ",
"            ",
"            "};

static const char *frame_button_help_xpm[] = {
"12 10 2 1",
"         c None",
"X        c #000000",
"    XXXX    ",
"   XX  XX   ",
"   XX  XX   ",
"      XX    ",
"     XX     ",
"     XX     ",
"            ",
"     XX     ",
"     XX     ",
"            "};

static const char *frame_button_maximize_xpm[] = {
"12 10 2 1",
"         c None",
"X        c #000000",
" XXXXXXXXX  ",
" XXXXXXXXX  ",
" X       X  ",
" X       X  ",
" X       X  ",
" X       X  ",
" X       X  ",
" X       X  ",
" XXXXXXXXX  ",
"            "};

static const char *frame_button_minimize_xpm[] = {
"12 10 2 1",
"         c None",
"X        c #000000",
"            ",
"            ",
"            ",
"            ",
"            ",
"            ",
"            ",
"  XXXXXX    ",
"  XXXXXX    ",
"            "};

static const char *frame_button_restore_xpm[] = {
"12 10 2 1",
"         c None",
"X        c #000000",
"   XXXXXX   ",
"   XXXXXX   ",
"   X    X   ",
" XXXXXX X   ",
" XXXXXX X   ",
" X    XXX   ",
" X    X     ",
" X    X     ",
" XXXXXX     ",
"            "};

const char **wxWin32Renderer::ms_xpmFrameButtons[FrameButton_Max] =
{
    frame_button_close_xpm,
    frame_button_minimize_xpm,
    frame_button_maximize_xpm,
    frame_button_restore_xpm,
    frame_button_help_xpm,
};

// menu bitmaps

static const char *checked_menu_xpm[] = {
/* columns rows colors chars-per-pixel */
"9 9 2 1",
"w c None",
"b c black",
/* pixels */
"wwwwwwwww",
"wwwwwwwbw",
"wwwwwwbbw",
"wbwwwbbbw",
"wbbwbbbww",
"wbbbbbwww",
"wwbbbwwww",
"wwwbwwwww",
"wwwwwwwww"
};

static const char *selected_checked_menu_xpm[] = {
/* columns rows colors chars-per-pixel */
"9 9 2 1",
"w c None",
"b c white",
/* pixels */
"wwwwwwwww",
"wwwwwwwbw",
"wwwwwwbbw",
"wbwwwbbbw",
"wbbwbbbww",
"wbbbbbwww",
"wwbbbwwww",
"wwwbwwwww",
"wwwwwwwww"
};

static const char *disabled_checked_menu_xpm[] = {
/* columns rows colors chars-per-pixel */
"9 9 3 1",
"w c None",
"b c #7f7f7f",
"W c #e0e0e0",
/* pixels */
"wwwwwwwww",
"wwwwwwwbw",
"wwwwwwbbW",
"wbwwwbbbW",
"wbbwbbbWW",
"wbbbbbWWw",
"wwbbbWWww",
"wwwbWWwww",
"wwwwWwwww"
};

static const char *selected_disabled_checked_menu_xpm[] = {
/* columns rows colors chars-per-pixel */
"9 9 2 1",
"w c None",
"b c #7f7f7f",
/* pixels */
"wwwwwwwww",
"wwwwwwwbw",
"wwwwwwbbw",
"wbwwwbbbw",
"wbbwbbbww",
"wbbbbbwww",
"wwbbbwwww",
"wwwbwwwww",
"wwwwwwwww"
};

// checkbox and radiobox bitmaps below

static const char *checked_xpm[] = {
/* columns rows colors chars-per-pixel */
"13 13 5 1",
"w c white",
"b c black",
"d c #7f7f7f",
"g c #c0c0c0",
"h c #e0e0e0",
/* pixels */
"ddddddddddddh",
"dbbbbbbbbbbgh",
"dbwwwwwwwwwgh",
"dbwwwwwwwbwgh",
"dbwwwwwwbbwgh",
"dbwbwwwbbbwgh",
"dbwbbwbbbwwgh",
"dbwbbbbbwwwgh",
"dbwwbbbwwwwgh",
"dbwwwbwwwwwgh",
"dbwwwwwwwwwgh",
"dgggggggggggh",
"hhhhhhhhhhhhh"
};

static const char *pressed_checked_xpm[] = {
/* columns rows colors chars-per-pixel */
"13 13 4 1",
"b c black",
"d c #7f7f7f",
"g c #c0c0c0",
"h c #e0e0e0",
/* pixels */
"ddddddddddddh",
"dbbbbbbbbbbgh",
"dbggggggggggh",
"dbgggggggbggh",
"dbggggggbbggh",
"dbgbgggbbbggh",
"dbgbbgbbbgggh",
"dbgbbbbbggggh",
"dbggbbbgggggh",
"dbgggbggggggh",
"dbggggggggggh",
"dgggggggggggh",
"hhhhhhhhhhhhh"
};

static const char *pressed_disabled_checked_xpm[] = {
/* columns rows colors chars-per-pixel */
"13 13 4 1",
"b c black",
"d c #7f7f7f",
"g c #c0c0c0",
"h c #e0e0e0",
/* pixels */
"ddddddddddddh",
"dbbbbbbbbbbgh",
"dbggggggggggh",
"dbgggggggdggh",
"dbggggggddggh",
"dbgdgggdddggh",
"dbgddgdddgggh",
"dbgdddddggggh",
"dbggdddgggggh",
"dbgggdggggggh",
"dbggggggggggh",
"dgggggggggggh",
"hhhhhhhhhhhhh"
};

static const char *checked_item_xpm[] = {
/* columns rows colors chars-per-pixel */
"13 13 3 1",
"w c white",
"b c black",
"d c #808080",
/* pixels */
"wwwwwwwwwwwww",
"wdddddddddddw",
"wdwwwwwwwwwdw",
"wdwwwwwwwbwdw",
"wdwwwwwwbbwdw",
"wdwbwwwbbbwdw",
"wdwbbwbbbwwdw",
"wdwbbbbbwwwdw",
"wdwwbbbwwwwdw",
"wdwwwbwwwwwdw",
"wdwwwwwwwwwdw",
"wdddddddddddw",
"wwwwwwwwwwwww"
};

static const char *unchecked_xpm[] = {
/* columns rows colors chars-per-pixel */
"13 13 5 1",
"w c white",
"b c black",
"d c #7f7f7f",
"g c #c0c0c0",
"h c #e0e0e0",
/* pixels */
"ddddddddddddh",
"dbbbbbbbbbbgh",
"dbwwwwwwwwwgh",
"dbwwwwwwwwwgh",
"dbwwwwwwwwwgh",
"dbwwwwwwwwwgh",
"dbwwwwwwwwwgh",
"dbwwwwwwwwwgh",
"dbwwwwwwwwwgh",
"dbwwwwwwwwwgh",
"dbwwwwwwwwwgh",
"dgggggggggggh",
"hhhhhhhhhhhhh"
};

static const char *pressed_unchecked_xpm[] = {
/* columns rows colors chars-per-pixel */
"13 13 4 1",
"b c black",
"d c #7f7f7f",
"g c #c0c0c0",
"h c #e0e0e0",
/* pixels */
"ddddddddddddh",
"dbbbbbbbbbbgh",
"dbggggggggggh",
"dbggggggggggh",
"dbggggggggggh",
"dbggggggggggh",
"dbggggggggggh",
"dbggggggggggh",
"dbggggggggggh",
"dbggggggggggh",
"dbggggggggggh",
"dbggggggggggh",
"hhhhhhhhhhhhh"
};

static const char *unchecked_item_xpm[] = {
/* columns rows colors chars-per-pixel */
"13 13 2 1",
"w c white",
"d c #808080",
/* pixels */
"wwwwwwwwwwwww",
"wdddddddddddw",
"wdwwwwwwwwwdw",
"wdwwwwwwwwwdw",
"wdwwwwwwwwwdw",
"wdwwwwwwwwwdw",
"wdwwwwwwwwwdw",
"wdwwwwwwwwwdw",
"wdwwwwwwwwwdw",
"wdwwwwwwwwwdw",
"wdwwwwwwwwwdw",
"wdddddddddddw",
"wwwwwwwwwwwww"
};

static const char *undetermined_xpm[] = {
/* columns rows colors chars-per-pixel */
"13 13 5 1",
"A c #030303",
"B c #838383",
"C c #C3C3C3",
"D c #FBFBFB",
"E c #DBDBDB",
/* pixels */
"BBBBBBBBBBBBD",
"BAAAAAAAAAAED",
"BACDCDCDCDCED",
"BADCDCDCDBDED",
"BACDCDCDBBCED",
"BADBDCEBBBDED",
"BACBBDBBBDCED",
"BADBBBBBDCDED",
"BACDBBBDCDCED",
"BADCDBDCDCDED",
"BACDCDCDCDCED",
"BEEEEEEEEEEED",
"DDDDDDDDDDDDD"
};

static const char *pressed_undetermined_xpm[] = {
/* columns rows colors chars-per-pixel */
"13 13 5 1",
"A c #040404",
"B c #848484",
"C c #C4C4C4",
"D c #FCFCFC",
"E c #DCDCDC",
/* pixels */
"BBBBBBBBBBBBD",
"BAAAAAAAAAAED",
"BACCCCCCCCCCD",
"BACCCCCCCACED",
"BACCCCCCAACED",
"BACACCCAAACED",
"BACAACAAACCED",
"BACAAAAACCCED",
"BACCAAACCCCCD",
"BACCCACCCCCED",
"BACCCCCCCCCED",
"BEEEEEEEEEEED",
"DDDDDDDDDDDDD"
};

static const char *checked_radio_xpm[] = {
/* columns rows colors chars-per-pixel */
"12 12 6 1",
"  c None",
"w c white",
"b c black",
"d c #7f7f7f",
"g c #c0c0c0",
"h c #e0e0e0",
/* pixels */
"    dddd    ",
"  ddbbbbdd  ",
" dbbwwwwbbh ",
" dbwwwwwwgh ",
"dbwwwbbwwwgh",
"dbwwbbbbwwgh",
"dbwwbbbbwwgh",
"dbwwwbbwwwgh",
" dbwwwwwwgh ",
" dggwwwwggh ",
"  hhgggghh  ",
"    hhhh    "
};

static const char *pressed_checked_radio_xpm[] = {
/* columns rows colors chars-per-pixel */
"12 12 6 1",
"  c None",
"w c white",
"b c black",
"d c #7f7f7f",
"g c #c0c0c0",
"h c #e0e0e0",
/* pixels */
"    dddd    ",
"  ddbbbbdd  ",
" dbbggggbbh ",
" dbgggggggh ",
"dbgggbbggggh",
"dbggbbbbgggh",
"dbggbbbbgggh",
"dbgggbbggggh",
" dbgggggggh ",
" dggggggggh ",
"  hhgggghh  ",
"    hhhh    "
};

static const char *pressed_disabled_checked_radio_xpm[] = {
/* columns rows colors chars-per-pixel */
"12 12 6 1",
"  c None",
"w c white",
"b c black",
"d c #7f7f7f",
"g c #c0c0c0",
"h c #e0e0e0",
/* pixels */
"    dddd    ",
"  ddbbbbdd  ",
" dbbggggbbh ",
" dbgggggggh ",
"dbgggddggggh",
"dbggddddgggh",
"dbggddddgggh",
"dbgggddggggh",
" dbgggggggh ",
" dggggggggh ",
"  hhgggghh  ",
"    hhhh    ",
};

static const char *unchecked_radio_xpm[] = {
/* columns rows colors chars-per-pixel */
"12 12 6 1",
"  c None",
"w c white",
"b c black",
"d c #7f7f7f",
"g c #c0c0c0",
"h c #e0e0e0",
/* pixels */
"    dddd    ",
"  ddbbbbdd  ",
" dbbwwwwbbh ",
" dbwwwwwwgh ",
"dbwwwwwwwwgh",
"dbwwwwwwwwgh",
"dbwwwwwwwwgh",
"dbwwwwwwwwgh",
" dbwwwwwwgh ",
" dggwwwwggh ",
"  hhgggghh  ",
"    hhhh    "
};

static const char *pressed_unchecked_radio_xpm[] = {
/* columns rows colors chars-per-pixel */
"12 12 6 1",
"  c None",
"w c white",
"b c black",
"d c #7f7f7f",
"g c #c0c0c0",
"h c #e0e0e0",
/* pixels */
"    dddd    ",
"  ddbbbbdd  ",
" dbbggggbbh ",
" dbgggggggh ",
"dbgggggggggh",
"dbgggggggggh",
"dbgggggggggh",
"dbgggggggggh",
" dbgggggggh ",
" dggggggggh ",
"  hhgggghh  ",
"    hhhh    "
};

const char **wxWin32Renderer::ms_xpmIndicators[IndicatorType_Max]
                                              [IndicatorState_MaxMenu]
                                              [IndicatorStatus_Max] =
{
    // checkboxes first
    {
        // normal state
        { checked_xpm, unchecked_xpm, undetermined_xpm },

        // pressed state
        { pressed_checked_xpm, pressed_unchecked_xpm, pressed_undetermined_xpm },

        // disabled state
        { pressed_disabled_checked_xpm, pressed_unchecked_xpm, pressed_disabled_checked_xpm },
    },

    // radio
    {
        // normal state
        { checked_radio_xpm, unchecked_radio_xpm, nullptr },

        // pressed state
        { pressed_checked_radio_xpm, pressed_unchecked_radio_xpm, nullptr },

        // disabled state
        { pressed_disabled_checked_radio_xpm, pressed_unchecked_radio_xpm, nullptr },
    },

    // menu
    {
        // normal state
        { checked_menu_xpm, nullptr, nullptr },

        // selected state
        { selected_checked_menu_xpm, nullptr, nullptr },

        // disabled state
        { disabled_checked_menu_xpm, nullptr, nullptr },

        // disabled selected state
        { selected_disabled_checked_menu_xpm, nullptr, nullptr },
    }
};

const char **wxWin32Renderer::ms_xpmChecked[IndicatorStatus_Max] =
{
    checked_item_xpm,
    unchecked_item_xpm
};

// ============================================================================
// implementation
// ============================================================================

WX_IMPLEMENT_THEME(wxWin32Theme, win32, wxTRANSLATE("Win32 theme"));

// ----------------------------------------------------------------------------
// wxWin32Theme
// ----------------------------------------------------------------------------

wxWin32Theme::wxWin32Theme()
{
    m_scheme = nullptr;
    m_renderer = nullptr;
    m_artProvider = nullptr;
}

wxWin32Theme::~wxWin32Theme()
{
    delete m_renderer;
    delete m_scheme;
    delete m_artProvider;
}

wxRenderer *wxWin32Theme::GetRenderer()
{
    if ( !m_renderer )
    {
        m_renderer = new wxWin32Renderer(GetColourScheme());
    }

    return m_renderer;
}

wxArtProvider *wxWin32Theme::GetArtProvider()
{
    if ( !m_artProvider )
    {
        m_artProvider = new wxWin32ArtProvider;
    }

    return m_artProvider;
}

wxInputHandler *
wxWin32Theme::GetInputHandler(const wxString& control,
                              wxInputConsumer *consumer)
{
    wxInputHandler *handler = nullptr;
    int n = m_handlerNames.Index(control);
    if ( n == wxNOT_FOUND )
    {
        static wxWin32InputHandler s_handlerDef;

        wxInputHandler * const
          handlerStd = consumer->DoGetStdInputHandler(&s_handlerDef);

        // create a new handler
        if ( control == wxINP_HANDLER_TOPLEVEL )
        {
            static wxWin32FrameInputHandler s_handler(handlerStd);

            handler = &s_handler;
        }
#if wxUSE_CHECKBOX
        else if ( control == wxINP_HANDLER_CHECKBOX )
        {
            static wxWin32CheckboxInputHandler s_handler(handlerStd);

            handler = &s_handler;
        }
#endif // wxUSE_CHECKBOX
#if wxUSE_SCROLLBAR
        else if ( control == wxINP_HANDLER_SCROLLBAR )
        {
            static wxWin32ScrollBarInputHandler
                s_handler(GetRenderer(), handlerStd);

            handler = &s_handler;
        }
#endif // wxUSE_SCROLLBAR
#if wxUSE_STATUSBAR
        else if ( control == wxINP_HANDLER_STATUSBAR )
        {
            static wxWin32StatusBarInputHandler s_handler(handlerStd);

            handler = &s_handler;
        }
#endif // wxUSE_STATUSBAR
#if wxUSE_TEXTCTRL
        else if ( control == wxINP_HANDLER_TEXTCTRL )
        {
            static wxWin32TextCtrlInputHandler s_handler(handlerStd);

            handler = &s_handler;
        }
#endif // wxUSE_TEXTCTRL
        else // no special handler for this control
        {
            handler = handlerStd;
        }

        n = m_handlerNames.Add(control);
        m_handlers.Insert(handler, n);
    }
    else // we already have it
    {
        handler = m_handlers[n];
    }

    return handler;
}

wxColourScheme *wxWin32Theme::GetColourScheme()
{
    if ( !m_scheme )
    {
        m_scheme = new wxWin32ColourScheme;
    }
    return m_scheme;
}

// ============================================================================
// wxWin32ColourScheme
// ============================================================================

wxColour wxWin32ColourScheme::GetBackground(wxWindow *win) const
{
    wxColour col;
    if ( win->UseBgCol() )
    {
        // use the user specified colour
        col = win->GetBackgroundColour();
    }

    if ( !win->ShouldInheritColours() )
    {
#if wxUSE_TEXTCTRL
        wxTextCtrl *text = wxDynamicCast(win, wxTextCtrl);
#endif // wxUSE_TEXTCTRL
#if wxUSE_LISTBOX
        wxListBox* listBox = wxDynamicCast(win, wxListBox);
#endif // wxUSE_LISTBOX

#if wxUSE_TEXTCTRL
        if ( text
#if wxUSE_LISTBOX
         || listBox
#endif
          )
        {
            if ( !win->IsEnabled() ) // not IsEditable()
                col = Get(CONTROL);
            else
            {
                if ( !col.IsOk() )
                {
                    // doesn't depend on the state
                    col = Get(WINDOW);
                }
            }
        }
#endif // wxUSE_TEXTCTRL

        if (!col.IsOk())
            col = Get(CONTROL); // Most controls should be this colour, not WINDOW
    }
    else
    {
        int flags = win->GetStateFlags();

        // the colour set by the user should be used for the normal state
        // and for the states for which we don't have any specific colours
        if ( !col.IsOk() || (flags & wxCONTROL_PRESSED) != 0 )
        {
#if wxUSE_SCROLLBAR
            if ( wxDynamicCast(win, wxScrollBar) )
                col = Get(flags & wxCONTROL_PRESSED ? SCROLLBAR_PRESSED
                                                    : SCROLLBAR);
            else
#endif // wxUSE_SCROLLBAR
                col = Get(CONTROL);
        }
    }

    return col;
}

wxColour wxWin32ColourScheme::Get(wxWin32ColourScheme::StdColour col) const
{
    switch ( col )
    {
        // use the system colours under Windows
#if defined(__WXMSW__)
        case WINDOW:            return wxColour(GetSysColor(COLOR_WINDOW));

        case CONTROL_PRESSED:
        case CONTROL_CURRENT:
        case CONTROL:           return wxColour(GetSysColor(COLOR_BTNFACE));

        case CONTROL_TEXT:      return wxColour(GetSysColor(COLOR_BTNTEXT));

#if defined(COLOR_3DLIGHT)
        case SCROLLBAR:         return wxColour(GetSysColor(COLOR_3DLIGHT));
#else
        case SCROLLBAR:         return wxColour(0xe0e0e0);
#endif
        case SCROLLBAR_PRESSED: return wxColour(GetSysColor(COLOR_BTNTEXT));

        case HIGHLIGHT:         return wxColour(GetSysColor(COLOR_HIGHLIGHT));
        case HIGHLIGHT_TEXT:    return wxColour(GetSysColor(COLOR_HIGHLIGHTTEXT));

#if defined(COLOR_3DDKSHADOW)
        case SHADOW_DARK:       return wxColour(GetSysColor(COLOR_3DDKSHADOW));
#else
        case SHADOW_DARK:       return wxColour(GetSysColor(COLOR_3DHADOW));
#endif

        case CONTROL_TEXT_DISABLED:
        case SHADOW_HIGHLIGHT:  return wxColour(GetSysColor(COLOR_BTNHIGHLIGHT));

        case SHADOW_IN:         return wxColour(GetSysColor(COLOR_BTNFACE));

        case CONTROL_TEXT_DISABLED_SHADOW:
        case SHADOW_OUT:        return wxColour(GetSysColor(COLOR_BTNSHADOW));

        case TITLEBAR:          return wxColour(GetSysColor(COLOR_INACTIVECAPTION));
        case TITLEBAR_ACTIVE:   return wxColour(GetSysColor(COLOR_ACTIVECAPTION));
        case TITLEBAR_TEXT:     return wxColour(GetSysColor(COLOR_INACTIVECAPTIONTEXT));
        case TITLEBAR_ACTIVE_TEXT: return wxColour(GetSysColor(COLOR_CAPTIONTEXT));

        case DESKTOP:           return wxColour(0x808000);
        case FRAME:             return wxColour(GetSysColor(COLOR_APPWORKSPACE));
#else // !__WXMSW__
        // use the standard Windows colours elsewhere
        case WINDOW:            return *wxWHITE;

        case CONTROL_PRESSED:
        case CONTROL_CURRENT:
        case CONTROL:           return wxColour(0xc0c0c0);

        case CONTROL_TEXT:      return *wxBLACK;

        case SCROLLBAR:         return wxColour(0xe0e0e0);
        case SCROLLBAR_PRESSED: return *wxBLACK;

        case HIGHLIGHT:         return wxColour(0x800000);
        case HIGHLIGHT_TEXT:    return wxColour(0xffffff);

        case SHADOW_DARK:       return *wxBLACK;

        case CONTROL_TEXT_DISABLED:return wxColour(0xe0e0e0);
        case SHADOW_HIGHLIGHT:  return wxColour(0xffffff);

        case SHADOW_IN:         return wxColour(0xc0c0c0);

        case CONTROL_TEXT_DISABLED_SHADOW:
        case SHADOW_OUT:        return wxColour(0x7f7f7f);

        case TITLEBAR:          return wxColour(0xaeaaae);
        case TITLEBAR_ACTIVE:   return wxColour(0x820300);
        case TITLEBAR_TEXT:     return wxColour(0xc0c0c0);
        case TITLEBAR_ACTIVE_TEXT:return *wxWHITE;

        case DESKTOP:           return wxColour(0x808000);
        case FRAME:             return wxColour(0x808080);
#endif // __WXMSW__

        case GAUGE:             return Get(HIGHLIGHT);

        case MAX:
        default:
            wxFAIL_MSG(wxT("invalid standard colour"));
            return *wxBLACK;
    }
}

// ============================================================================
// wxWin32Renderer
// ============================================================================

// ----------------------------------------------------------------------------
// construction
// ----------------------------------------------------------------------------

wxWin32Renderer::wxWin32Renderer(const wxColourScheme *scheme)
               : wxStdRenderer(scheme)
{
    // init data
    m_sizeScrollbarArrow = wxSize(16, 16);

    // init the arrow bitmaps
    static const size_t ARROW_WIDTH = 7;
    static const size_t ARROW_LENGTH = 4;

    wxMask *mask;
    wxMemoryDC dcNormal,
               dcDisabled,
               dcInverse;
    for ( size_t n = 0; n < Arrow_Max; n++ )
    {
        bool isVertical = n > Arrow_Right;
        int w, h;
        if ( isVertical )
        {
            w = ARROW_WIDTH;
            h = ARROW_LENGTH;
        }
        else
        {
            h = ARROW_WIDTH;
            w = ARROW_LENGTH;
        }

        // disabled arrow is larger because of the shadow
        m_bmpArrows[Arrow_Normal][n].Create(w, h);
        m_bmpArrows[Arrow_Disabled][n].Create(w + 1, h + 1);

        dcNormal.SelectObject(m_bmpArrows[Arrow_Normal][n]);
        dcDisabled.SelectObject(m_bmpArrows[Arrow_Disabled][n]);

        dcNormal.SetBackground(*wxWHITE_BRUSH);
        dcDisabled.SetBackground(*wxWHITE_BRUSH);
        dcNormal.Clear();
        dcDisabled.Clear();

        dcNormal.SetPen(m_penBlack);
        dcDisabled.SetPen(m_penDarkGrey);

        // calculate the position of the point of the arrow
        wxCoord x1, y1;
        if ( isVertical )
        {
            x1 = (ARROW_WIDTH - 1)/2;
            y1 = n == Arrow_Up ? 0 : ARROW_LENGTH - 1;
        }
        else // horizontal
        {
            x1 = n == Arrow_Left ? 0 : ARROW_LENGTH - 1;
            y1 = (ARROW_WIDTH - 1)/2;
        }

        wxCoord x2 = x1,
                y2 = y1;

        if ( isVertical )
            x2++;
        else
            y2++;

        for ( size_t i = 0; i < ARROW_LENGTH; i++ )
        {
            dcNormal.DrawLine(x1, y1, x2, y2);
            dcDisabled.DrawLine(x1, y1, x2, y2);

            if ( isVertical )
            {
                x1--;
                x2++;

                if ( n == Arrow_Up )
                {
                    y1++;
                    y2++;
                }
                else // down arrow
                {
                    y1--;
                    y2--;
                }
            }
            else // left or right arrow
            {
                y1--;
                y2++;

                if ( n == Arrow_Left )
                {
                    x1++;
                    x2++;
                }
                else
                {
                    x1--;
                    x2--;
                }
            }
        }

        // draw the shadow for the disabled one
        dcDisabled.SetPen(m_penHighlight);
        switch ( n )
        {
            case Arrow_Left:
                y1 += 2;
                dcDisabled.DrawLine(x1, y1, x2, y2);
                break;

            case Arrow_Right:
                x1 = ARROW_LENGTH - 1;
                y1 = (ARROW_WIDTH - 1)/2 + 1;
                x2 = 0;
                y2 = ARROW_WIDTH;
                dcDisabled.DrawLine(x1, y1, x2, y2);
                dcDisabled.DrawLine(++x1, y1, x2, ++y2);
                break;

            case Arrow_Up:
                x1 += 2;
                dcDisabled.DrawLine(x1, y1, x2, y2);
                break;

            case Arrow_Down:
                x1 = ARROW_WIDTH - 1;
                y1 = 1;
                x2 = (ARROW_WIDTH - 1)/2;
                y2 = ARROW_LENGTH;
                dcDisabled.DrawLine(x1, y1, x2, y2);
                dcDisabled.DrawLine(++x1, y1, x2, ++y2);
                break;

        }

        // create the inverted bitmap but only for the right arrow as we only
        // use it for the menus
        if ( n == Arrow_Right )
        {
            m_bmpArrows[Arrow_Inverted][n].Create(w, h);
            dcInverse.SelectObject(m_bmpArrows[Arrow_Inverted][n]);
            dcInverse.Clear();
            dcInverse.Blit(0, 0, w, h,
                          &dcNormal, 0, 0,
                          wxXOR);
            dcInverse.SelectObject(wxNullBitmap);

            mask = new wxMask(m_bmpArrows[Arrow_Inverted][n], *wxBLACK);
            m_bmpArrows[Arrow_Inverted][n].SetMask(mask);

            m_bmpArrows[Arrow_InvertedDisabled][n].Create(w, h);
            dcInverse.SelectObject(m_bmpArrows[Arrow_InvertedDisabled][n]);
            dcInverse.Clear();
            dcInverse.Blit(0, 0, w, h,
                          &dcDisabled, 0, 0,
                          wxXOR);
            dcInverse.SelectObject(wxNullBitmap);

            mask = new wxMask(m_bmpArrows[Arrow_InvertedDisabled][n], *wxBLACK);
            m_bmpArrows[Arrow_InvertedDisabled][n].SetMask(mask);
        }

        dcNormal.SelectObject(wxNullBitmap);
        dcDisabled.SelectObject(wxNullBitmap);

        mask = new wxMask(m_bmpArrows[Arrow_Normal][n], *wxWHITE);
        m_bmpArrows[Arrow_Normal][n].SetMask(mask);
        mask = new wxMask(m_bmpArrows[Arrow_Disabled][n], *wxWHITE);
        m_bmpArrows[Arrow_Disabled][n].SetMask(mask);

        m_bmpArrows[Arrow_Pressed][n] = m_bmpArrows[Arrow_Normal][n];
    }
}

bool wxWin32Renderer::AreScrollbarsInsideBorder() const
{
    return true;
}

// ----------------------------------------------------------------------------
// label
// ----------------------------------------------------------------------------

void wxWin32Renderer::DrawLabel(wxDC& dc,
                                const wxString& label,
                                const wxRect& rect,
                                int flags,
                                int alignment,
                                int indexAccel,
                                wxRect *rectBounds)
{
    // the underscores are not drawn for focused controls in wxMSW
    if ( flags & wxCONTROL_FOCUSED )
    {
        indexAccel = -1;
    }

    if ( flags & wxCONTROL_DISABLED )
    {
        // the combination of wxCONTROL_SELECTED and wxCONTROL_DISABLED
        // currently only can happen for a menu item and it seems that Windows
        // doesn't draw the shadow in this case, so we don't do it either
        if ( flags & wxCONTROL_SELECTED )
        {
            // just make the label text greyed out
            dc.SetTextForeground(m_penDarkGrey.GetColour());

            flags &= ~wxCONTROL_DISABLED;
        }
    }

    wxStdRenderer::DrawLabel(dc, label, rect, flags, alignment,
                             indexAccel, rectBounds);
}

void wxWin32Renderer::DrawFrameWithLabel(wxDC& dc,
                                         const wxString& label,
                                         const wxRect& rectFrame,
                                         const wxRect& rectText,
                                         int flags,
                                         int alignment,
                                         int indexAccel)
{
    wxString label2;
    label2 << wxT(' ') << label << wxT(' ');
    if ( indexAccel != -1 )
    {
        // adjust it as we prepended a space
        indexAccel++;
    }

    wxStdRenderer::DrawFrameWithLabel(dc, label2, rectFrame, rectText,
                                      flags, alignment, indexAccel);
}

void wxWin32Renderer::DrawButtonLabel(wxDC& dc,
                                      const wxString& label,
                                      const wxBitmap& image,
                                      const wxRect& rect,
                                      int flags,
                                      int alignment,
                                      int indexAccel,
                                      wxRect *rectBounds)
{
    // the underscores are not drawn for focused controls in wxMSW
    if ( flags & wxCONTROL_PRESSED )
    {
        indexAccel = -1;
    }

    wxStdRenderer::DrawButtonLabel(dc, label, image, rect, flags, alignment,
                                   indexAccel, rectBounds);
}

void wxWin32Renderer::DrawButtonBorder(wxDC& dc,
                                       const wxRect& rectTotal,
                                       int flags,
                                       wxRect *rectIn)
{
    wxRect rect = rectTotal;

    wxPen penOut(*wxBLACK);
    if ( flags & wxCONTROL_PRESSED )
    {
        // button pressed: draw a double border around it
        DrawRect(dc, &rect, penOut);
        DrawRect(dc, &rect, m_penDarkGrey);
    }
    else // button not pressed
    {
        if ( flags & (wxCONTROL_FOCUSED | wxCONTROL_ISDEFAULT) )
        {
            // button either default or focused (or both): add an extra border
            // around it
            DrawRect(dc, &rect, penOut);
        }

        // now draw a normal button border
        DrawRaisedBorder(dc, &rect);
    }

    if ( rectIn )
        *rectIn = rect;
}

// ----------------------------------------------------------------------------
// (check)listbox items
// ----------------------------------------------------------------------------

void wxWin32Renderer::DrawCheckItemBitmap(wxDC& dc,
                                          const wxBitmap& bitmap,
                                          const wxRect& rect,
                                          int flags)
{
    wxBitmap bmp;
    if ( bitmap.IsOk() )
    {
        bmp = bitmap;
    }
    else // use default bitmap
    {
        IndicatorStatus i = flags & wxCONTROL_CHECKED
                                ? IndicatorStatus_Checked
                                : IndicatorStatus_Unchecked;

        if ( !m_bmpCheckBitmaps[i].IsOk() )
        {
            m_bmpCheckBitmaps[i] = wxBitmap(ms_xpmChecked[i]);
        }

        bmp = m_bmpCheckBitmaps[i];
    }

    dc.DrawBitmap(bmp, rect.x, rect.y + (rect.height - bmp.GetHeight()) / 2 - 1,
                  true /* use mask */);
}

// ----------------------------------------------------------------------------
// check/radio buttons
// ----------------------------------------------------------------------------

wxBitmap wxWin32Renderer::GetIndicator(IndicatorType indType, int flags)
{
    IndicatorState indState;
    IndicatorStatus indStatus;
    GetIndicatorsFromFlags(flags, indState, indStatus);

    wxBitmap& bmp = m_bmpIndicators[indType][indState][indStatus];
    if ( !bmp.IsOk() )
    {
        const char **xpm = ms_xpmIndicators[indType][indState][indStatus];
        if ( xpm )
        {
            // create and cache it
            bmp = wxBitmap(xpm);
        }
    }

    return bmp;
}

// ----------------------------------------------------------------------------
// toolbar stuff
// ----------------------------------------------------------------------------

#if wxUSE_TOOLBAR
void wxWin32Renderer::DrawToolBarButton(wxDC& dc,
                                        const wxString& label,
                                        const wxBitmap& bitmap,
                                        const wxRect& rectOrig,
                                        int flags,
                                        long style,
                                        int tbarStyle)
{
    if (style == wxTOOL_STYLE_BUTTON)
    {
        wxRect rect = rectOrig;
        rect.Deflate(BORDER_THICKNESS);

        if ( flags & wxCONTROL_PRESSED )
        {
            DrawBorder(dc, wxBORDER_SUNKEN, rect, flags);
        }
        else if ( flags & wxCONTROL_CURRENT )
        {
            DrawBorder(dc, wxBORDER_RAISED, rect, flags);
        }

        if(tbarStyle & wxTB_TEXT)
        {
            if(tbarStyle & wxTB_HORIZONTAL)
            {
                dc.DrawLabel(label, bitmap, rect, wxALIGN_CENTRE);
            }
            else
            {
                dc.DrawLabel(label, bitmap, rect, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL);
            }
        }
        else
        {
            int xpoint = (rect.GetLeft() + rect.GetRight() + 1 - bitmap.GetWidth()) / 2;
            int ypoint = (rect.GetTop() + rect.GetBottom() + 1 - bitmap.GetHeight()) / 2;
            dc.DrawBitmap(bitmap, xpoint, ypoint, bitmap.GetMask() != nullptr);
        }
    }
    else if (style == wxTOOL_STYLE_SEPARATOR)
    {
        // leave a small gap around the line, also account for the toolbar
        // border itself
        if(rectOrig.height > rectOrig.width)
        {
            // horizontal
            DrawVerticalLine(dc, rectOrig.x + rectOrig.width/2,
                             rectOrig.y + 2*BORDER_THICKNESS,
                             rectOrig.GetBottom() - BORDER_THICKNESS);
        }
        else
        {
            // vertical
            DrawHorizontalLine(dc, rectOrig.y + rectOrig.height/2,
                         rectOrig.x + 2*BORDER_THICKNESS,
                         rectOrig.GetRight() - BORDER_THICKNESS);
        }
    }
    // don't draw wxTOOL_STYLE_CONTROL
}
#endif // wxUSE_TOOLBAR

// ----------------------------------------------------------------------------
// notebook
// ----------------------------------------------------------------------------

#if wxUSE_NOTEBOOK

void wxWin32Renderer::DrawTab(wxDC& dc,
                              const wxRect& rectOrig,
                              wxDirection dir,
                              const wxString& label,
                              const wxBitmap& bitmap,
                              int flags,
                              int indexAccel)
{
    #define SELECT_FOR_VERTICAL(X,Y) ( isVertical ? Y : X )
    #define REVERSE_FOR_VERTICAL(X,Y) \
        SELECT_FOR_VERTICAL(X,Y)      \
        ,                             \
        SELECT_FOR_VERTICAL(Y,X)

    wxRect rect = rectOrig;

    bool isVertical = ( dir == wxLEFT ) || ( dir == wxRIGHT );

    // the current tab is drawn indented (to the top for default case) and
    // bigger than the other ones
    const wxSize indent = GetTabIndent();
    if ( flags & wxCONTROL_SELECTED )
    {
        rect.Inflate( SELECT_FOR_VERTICAL( indent.x , 0),
                      SELECT_FOR_VERTICAL( 0, indent.y ));
        switch ( dir )
        {
            default:
                wxFAIL_MSG(wxT("invaild notebook tab orientation"));
                // fall through

            case wxTOP:
                rect.y -= indent.y;
                // fall through
            case wxBOTTOM:
                rect.height += indent.y;
                break;

            case wxLEFT:
                rect.x -= indent.x;
                // fall through
            case wxRIGHT:
                rect.width += indent.x;
                break;
        }
    }

    // draw the text, image and the focus around them (if necessary)
    wxRect rectLabel( REVERSE_FOR_VERTICAL(rect.x,rect.y),
                      REVERSE_FOR_VERTICAL(rect.width,rect.height)
                    );
    rectLabel.Deflate(1, 1);
    if ( isVertical )
    {
        // draw it horizontally into memory and rotate for screen
        wxMemoryDC dcMem;
        wxBitmap bitmapRotated,
                 bitmapMem( rectLabel.x + rectLabel.width,
                            rectLabel.y + rectLabel.height );
        dcMem.SelectObject(bitmapMem);
        dcMem.SetBackground(dc.GetBackground());
        dcMem.SetFont(dc.GetFont());
        dcMem.SetTextForeground(dc.GetTextForeground());
        dcMem.Clear();
        bitmapRotated =
#if wxUSE_IMAGE
                        wxBitmap( wxImage( bitmap.ConvertToImage() ).Rotate90(dir==wxLEFT) )
#else
                        bitmap
#endif // wxUSE_IMAGE
                        ;
        DrawButtonLabel(dcMem, label, bitmapRotated, rectLabel,
                        flags, wxALIGN_CENTRE, indexAccel);
        dcMem.SelectObject(wxNullBitmap);
        bitmapMem = bitmapMem.GetSubBitmap(rectLabel);
#if wxUSE_IMAGE
        bitmapMem = wxBitmap(wxImage(bitmapMem.ConvertToImage()).Rotate90(dir==wxRIGHT));
#endif // wxUSE_IMAGE
        dc.DrawBitmap(bitmapMem, rectLabel.y, rectLabel.x, false);
    }
    else
    {
        DrawButtonLabel(dc, label, bitmap, rectLabel,
                        flags, wxALIGN_CENTRE, indexAccel);
    }

    // now draw the tab border itself (maybe use DrawRoundedRectangle()?)
    static const wxCoord CUTOFF = 2; // radius of the rounded corner
    wxCoord x = SELECT_FOR_VERTICAL(rect.x,rect.y),
            y = SELECT_FOR_VERTICAL(rect.y,rect.x),
            x2 = SELECT_FOR_VERTICAL(rect.GetRight(),rect.GetBottom()),
            y2 = SELECT_FOR_VERTICAL(rect.GetBottom(),rect.GetRight());

    // FIXME: all this code will break if the tab indent or the border width,
    //        it is tied to the fact that both of them are equal to 2
    switch ( dir )
    {
        default:
            // default is top
        case wxLEFT:
            // left orientation looks like top but IsVertical makes x and y reversed
        case wxTOP:
            // top is not vertical so use coordinates in written order
            dc.SetPen(m_penHighlight);
            dc.DrawLine(REVERSE_FOR_VERTICAL(x, y2),
                        REVERSE_FOR_VERTICAL(x, y + CUTOFF));
            dc.DrawLine(REVERSE_FOR_VERTICAL(x, y + CUTOFF),
                        REVERSE_FOR_VERTICAL(x + CUTOFF, y));
            dc.DrawLine(REVERSE_FOR_VERTICAL(x + CUTOFF, y),
                        REVERSE_FOR_VERTICAL(x2 - CUTOFF + 1, y));

            dc.SetPen(m_penBlack);
            dc.DrawLine(REVERSE_FOR_VERTICAL(x2, y2),
                        REVERSE_FOR_VERTICAL(x2, y + CUTOFF));
            dc.DrawLine(REVERSE_FOR_VERTICAL(x2, y + CUTOFF),
                        REVERSE_FOR_VERTICAL(x2 - CUTOFF, y));

            dc.SetPen(m_penDarkGrey);
            dc.DrawLine(REVERSE_FOR_VERTICAL(x2 - 1, y2),
                        REVERSE_FOR_VERTICAL(x2 - 1, y + CUTOFF - 1));

            if ( flags & wxCONTROL_SELECTED )
            {
                dc.SetPen(m_penLightGrey);

                // overwrite the part of the border below this tab
                dc.DrawLine(REVERSE_FOR_VERTICAL(x + 1, y2 + 1),
                            REVERSE_FOR_VERTICAL(x2 - 1, y2 + 1));

                // and the shadow of the tab to the left of us
                dc.DrawLine(REVERSE_FOR_VERTICAL(x + 1, y + CUTOFF + 1),
                            REVERSE_FOR_VERTICAL(x + 1, y2 + 1));
            }
            break;

        case wxRIGHT:
            // right orientation looks like bottom but IsVertical makes x and y reversed
        case wxBOTTOM:
            // bottom is not vertical so use coordinates in written order
            dc.SetPen(m_penHighlight);
            // we need to continue one pixel further to overwrite the corner of
            // the border for the selected tab
            dc.DrawLine(REVERSE_FOR_VERTICAL(x, y - (flags & wxCONTROL_SELECTED ? 1 : 0)),
                        REVERSE_FOR_VERTICAL(x, y2 - CUTOFF));
            dc.DrawLine(REVERSE_FOR_VERTICAL(x, y2 - CUTOFF),
                        REVERSE_FOR_VERTICAL(x + CUTOFF, y2));

            dc.SetPen(m_penBlack);
            dc.DrawLine(REVERSE_FOR_VERTICAL(x + CUTOFF, y2),
                        REVERSE_FOR_VERTICAL(x2 - CUTOFF + 1, y2));
            dc.DrawLine(REVERSE_FOR_VERTICAL(x2, y),
                        REVERSE_FOR_VERTICAL(x2, y2 - CUTOFF));
            dc.DrawLine(REVERSE_FOR_VERTICAL(x2, y2 - CUTOFF),
                        REVERSE_FOR_VERTICAL(x2 - CUTOFF, y2));

            dc.SetPen(m_penDarkGrey);
            dc.DrawLine(REVERSE_FOR_VERTICAL(x + CUTOFF, y2 - 1),
                        REVERSE_FOR_VERTICAL(x2 - CUTOFF + 1, y2 - 1));
            dc.DrawLine(REVERSE_FOR_VERTICAL(x2 - 1, y),
                        REVERSE_FOR_VERTICAL(x2 - 1, y2 - CUTOFF + 1));

            if ( flags & wxCONTROL_SELECTED )
            {
                dc.SetPen(m_penLightGrey);

                // overwrite the part of the (double!) border above this tab
                dc.DrawLine(REVERSE_FOR_VERTICAL(x + 1, y - 1),
                            REVERSE_FOR_VERTICAL(x2 - 1, y - 1));
                dc.DrawLine(REVERSE_FOR_VERTICAL(x + 1, y - 2),
                            REVERSE_FOR_VERTICAL(x2 - 1, y - 2));

                // and the shadow of the tab to the left of us
                dc.DrawLine(REVERSE_FOR_VERTICAL(x + 1, y2 - CUTOFF),
                            REVERSE_FOR_VERTICAL(x + 1, y - 1));
            }
            break;
    }

    #undef SELECT_FOR_VERTICAL
    #undef REVERSE_FOR_VERTICAL
}

#endif // wxUSE_NOTEBOOK

#if wxUSE_SLIDER

// ----------------------------------------------------------------------------
// slider
// ----------------------------------------------------------------------------

wxSize
wxWin32Renderer::GetSliderThumbSize(const wxRect& WXUNUSED(rect),
                                    int lenThumb,
                                    wxOrientation orient) const
{
    wxSize size;
    wxCoord width  = wxMax (lenThumb, SLIDER_THUMB_LENGTH) / 2;
    wxCoord height = wxMax (lenThumb, SLIDER_THUMB_LENGTH);

    if (orient == wxHORIZONTAL)
    {
        size.x = width;
        size.y = height;
    }
    else
    { // == wxVERTICAL
        size.x = height;
        size.y = width;
    }

    return size;
}

wxRect wxWin32Renderer::GetSliderShaftRect(const wxRect& rectOrig,
                                           int lenThumb,
                                           wxOrientation orient,
                                           long style) const
{
    bool left, right;
    GetSliderLabelsSides(orient, style, &left, &right);

    wxRect rect = rectOrig;

    wxSize sizeThumb = GetSliderThumbSize (rect, lenThumb, orient);

    if (orient == wxHORIZONTAL)
    {
        rect.x += SLIDER_MARGIN;
        if (left && right)
        {
            rect.y += wxMax ((rect.height - 2*BORDER_THICKNESS) / 2, sizeThumb.y/2);
        }
        else if (left)
        {
            rect.y += wxMax ((rect.height - 2*BORDER_THICKNESS - sizeThumb.y/2), sizeThumb.y/2);
        }
        else
        {
            rect.y += sizeThumb.y/2;
        }
        rect.width -= 2*SLIDER_MARGIN;
        rect.height = 2*BORDER_THICKNESS;
    }
    else // == wxVERTICAL
    {
        rect.y += SLIDER_MARGIN;
        if (left && right)
        {
            rect.x += wxMax ((rect.width - 2*BORDER_THICKNESS) / 2, sizeThumb.x/2);
        }
        else if (left)
        {
            rect.x += wxMax ((rect.width - 2*BORDER_THICKNESS - sizeThumb.x/2), sizeThumb.x/2);
        }
        else
        {
            rect.x += sizeThumb.x/2;
        }
        rect.width = 2*BORDER_THICKNESS;
        rect.height -= 2*SLIDER_MARGIN;
    }

    return rect;
}

void wxWin32Renderer::DrawSliderShaft(wxDC& dc,
                                      const wxRect& rectOrig,
                                      int lenThumb,
                                      wxOrientation orient,
                                      int flags,
                                      long style,
                                      wxRect *rectShaft)
{
    /*    show shaft geometry

             shaft
        +-------------+
        |             |
        |     XXX     |  <-- x1
        |     XXX     |
        |     XXX     |
        |     XXX     |
        |     XXX     |  <-- x2
        |             |
        +-------------+

              ^ ^
              | |
             y1 y2
    */

    if (flags & wxCONTROL_FOCUSED)
        DrawFocusRect(nullptr, dc, rectOrig);

    wxRect rect = GetSliderShaftRect(rectOrig, lenThumb, orient, style);

    if (rectShaft) *rectShaft = rect;

    DrawSunkenBorder(dc, &rect);
}

void wxWin32Renderer::DrawSliderThumb(wxDC& dc,
                                      const wxRect& rect,
                                      wxOrientation orient,
                                      int flags,
                                      long style)
{
    /*    show thumb geometry

             H       <--- y1
           H H B
         H     H B
       H         H B <--- y3
       H         D B
       H         D B
       H         D B
       H         D B   where H is highlight colour
       H         D B         D    dark grey
       H         D B         B    black
       H         D B
       H         D B
       H         D B <--- y4
         H     D B
           H D B
             B       <--- y2

       ^     ^     ^
       |     |     |
       x1    x3    x2

       The interior of this shape is filled with the hatched brush if the thumb
       is pressed.
    */

    DrawBackground(dc, wxNullColour, rect, flags);

    bool left, right;
    GetSliderLabelsSides(orient, style, &left, &right);

    bool isVertical = orient == wxVERTICAL;

    wxCoord sizeArrow = (isVertical ? rect.height : rect.width) / 2;
    wxCoord c = ((isVertical ? rect.height : rect.width) - 2*sizeArrow);

    wxCoord x1, x2, x3, y1, y2, y3, y4;
    x1 = (isVertical ? rect.y : rect.x);
    x2 = (isVertical ? rect.GetBottom() : rect.GetRight());
    x3 = (x1-1+c) + sizeArrow;
    y1 = (isVertical ? rect.x : rect.y);
    y2 = (isVertical ? rect.GetRight() : rect.GetBottom());
    y3 = (left  ? (y1-1+c) + sizeArrow : y1);
    y4 = (right ? (y2+1-c) - sizeArrow : y2);

    dc.SetPen(m_penBlack);
    if (left)
    {
        DrawLine(dc, x3+1-c, y1, x2, y3, isVertical);
    }
    DrawLine(dc, x2, y3, x2, y4, isVertical);
    if (right)
    {
        DrawLine(dc, x3+1-c, y2, x2, y4, isVertical);
    }
    else
    {
        DrawLine(dc, x1, y2, x2, y2, isVertical);
    }

    dc.SetPen(m_penDarkGrey);
    DrawLine(dc, x2-1, y3+1, x2-1, y4-1, isVertical);
    if (right)
    {
        DrawLine(dc, x3+1-c, y2-1, x2-1, y4, isVertical);
    }
    else
    {
        DrawLine(dc, x1+1, y2-1, x2-1, y2-1, isVertical);
    }

    dc.SetPen(m_penHighlight);
    if (left)
    {
        DrawLine(dc, x1, y3, x3, y1, isVertical);
        DrawLine(dc, x3+1-c, y1+1, x2-1, y3, isVertical);
    }
    else
    {
        DrawLine(dc, x1, y1, x2, y1, isVertical);
    }
    DrawLine(dc, x1, y3, x1, y4, isVertical);
    if (right)
    {
        DrawLine(dc, x1, y4, x3+c, y2+c, isVertical);
    }

    if (flags & wxCONTROL_PRESSED)
    {
        // TODO: MSW fills the entire area inside, not just the rect
        wxRect rectInt = rect;
        if ( isVertical )
        {
            rectInt.SetLeft(y3);
            rectInt.SetRight(y4);
        }
        else
        {
            rectInt.SetTop(y3);
            rectInt.SetBottom(y4);
        }
        rectInt.Deflate(2);

        static const char *stipple_xpm[] = {
            /* columns rows colors chars-per-pixel */
            "2 2 2 1",
            "  c None",
            "w c white",
            /* pixels */
            "w ",
            " w",
        };
        dc.SetBrush(wxBitmap(stipple_xpm));

        dc.SetTextForeground(wxSCHEME_COLOUR(m_scheme, SHADOW_HIGHLIGHT));
        dc.SetTextBackground(wxSCHEME_COLOUR(m_scheme, CONTROL));
        dc.SetPen(*wxTRANSPARENT_PEN);
        dc.DrawRectangle(rectInt);
    }
}

void wxWin32Renderer::DrawSliderTicks(wxDC& dc,
                                      const wxRect& rect,
                                      int lenThumb,
                                      wxOrientation orient,
                                      int start,
                                      int end,
                                      int step,
                                      int WXUNUSED(flags),
                                      long style)
{
    /*    show ticks geometry

        left        right
        ticks shaft ticks
        ----   XX   ----  <-- x1
        ----   XX   ----
        ----   XX   ----
        ----   XX   ----  <-- x2

        ^  ^        ^  ^
        |  |        |  |
        y3 y1       y2 y4
    */

    // empty slider?
    if ( end == start )
        return;

    bool left, right;
    GetSliderLabelsSides(orient, style, &left, &right);

    bool isVertical = orient == wxVERTICAL;

    // default thumb size
    wxSize sizeThumb = GetSliderThumbSize (rect, 0, orient);
    wxCoord defaultLen = (isVertical ? sizeThumb.x : sizeThumb.y);

    // normal thumb size
    sizeThumb = GetSliderThumbSize (rect, lenThumb, orient);
    wxCoord widthThumb  = (isVertical ? sizeThumb.y : sizeThumb.x);

    wxRect rectShaft = GetSliderShaftRect (rect, lenThumb, orient, style);

    wxCoord x1, x2, y1, y2, y3, y4 , len;
    x1 = (isVertical ? rectShaft.y : rectShaft.x) + widthThumb/2;
    x2 = (isVertical ? rectShaft.GetBottom() : rectShaft.GetRight()) - widthThumb/2;
    y1 = (isVertical ? rectShaft.x : rectShaft.y) - defaultLen/2;
    y2 = (isVertical ? rectShaft.GetRight() : rectShaft.GetBottom()) + defaultLen/2;
    y3 = (isVertical ? rect.x : rect.y);
    y4 = (isVertical ? rect.GetRight() : rect.GetBottom());
    len = x2 - x1;

    dc.SetPen(m_penBlack);

    int range = end - start;
    for ( int n = 0; n < range; n += step )
    {
        wxCoord x = x1 + (len*n) / range;

        if (left && (y1 > y3))
        {
            DrawLine(dc, x, y1, x, y3, orient == wxVERTICAL);
        }
        if (right && (y4 > y2))
        {
            DrawLine(dc, x, y2, x, y4, orient == wxVERTICAL);
        }
    }
    // always draw the line at the end position
    if (left && (y1 > y3))
    {
        DrawLine(dc, x2, y1, x2, y3, orient == wxVERTICAL);
    }
    if (right && (y4 > y2))
    {
        DrawLine(dc, x2, y2, x2, y4, orient == wxVERTICAL);
    }
}

#endif // wxUSE_SLIDER

#if wxUSE_MENUS

// ----------------------------------------------------------------------------
// menu and menubar
// ----------------------------------------------------------------------------

// wxWin32MenuGeometryInfo: the wxMenuGeometryInfo used by wxWin32Renderer
class WXDLLEXPORT wxWin32MenuGeometryInfo : public wxMenuGeometryInfo
{
public:
    virtual wxSize GetSize() const { return m_size; }

    wxCoord GetLabelOffset() const { return m_ofsLabel; }
    wxCoord GetAccelOffset() const { return m_ofsAccel; }

    wxCoord GetItemHeight() const { return m_heightItem; }

private:
    // the total size of the menu
    wxSize m_size;

    // the offset of the start of the menu item label
    wxCoord m_ofsLabel;

    // the offset of the start of the accel label
    wxCoord m_ofsAccel;

    // the height of a normal (not separator) item
    wxCoord m_heightItem;

    friend wxMenuGeometryInfo *
        wxWin32Renderer::GetMenuGeometry(wxWindow *, const wxMenu&) const;
};

// FIXME: all constants are hardcoded but shouldn't be
static const wxCoord MENU_LEFT_MARGIN = 9;
static const wxCoord MENU_RIGHT_MARGIN = 18;
static const wxCoord MENU_VERT_MARGIN = 3;

// the margin around bitmap/check marks (on each side)
static const wxCoord MENU_BMP_MARGIN = 2;

// the margin between the labels and accel strings
static const wxCoord MENU_ACCEL_MARGIN = 8;

// the separator height in pixels: in fact, strangely enough, the real height
// is 2 but Windows adds one extra pixel in the bottom margin, so take it into
// account here
static const wxCoord MENU_SEPARATOR_HEIGHT = 3;

// the size of the standard checkmark bitmap
static const wxCoord MENU_CHECK_SIZE = 9;

void wxWin32Renderer::DrawMenuBarItem(wxDC& dc,
                                      const wxRect& rectOrig,
                                      const wxString& label,
                                      int flags,
                                      int indexAccel)
{
    wxRect rect = rectOrig;
    rect.height--;

    wxDCTextColourChanger colChanger(dc);

    if ( flags & wxCONTROL_SELECTED )
    {
        colChanger.Set(wxSCHEME_COLOUR(m_scheme, HIGHLIGHT_TEXT));

        const wxColour colBg = wxSCHEME_COLOUR(m_scheme, HIGHLIGHT);
        dc.SetBrush(colBg);
        dc.SetPen(colBg);
        dc.DrawRectangle(rect);
    }

    // don't draw the focus rect around menu bar items
    DrawLabel(dc, label, rect, flags & ~wxCONTROL_FOCUSED,
              wxALIGN_CENTRE, indexAccel);
}

void wxWin32Renderer::DrawMenuItem(wxDC& dc,
                                   wxCoord y,
                                   const wxMenuGeometryInfo& gi,
                                   const wxString& label,
                                   const wxString& accel,
                                   const wxBitmap& bitmap,
                                   int flags,
                                   int indexAccel)
{
    const wxWin32MenuGeometryInfo& geometryInfo =
        (const wxWin32MenuGeometryInfo&)gi;

    wxRect rect;
    rect.x = 0;
    rect.y = y;
    rect.width = geometryInfo.GetSize().x;
    rect.height = geometryInfo.GetItemHeight();

    // draw the selected item specially
    wxDCTextColourChanger colChanger(dc);
    if ( flags & wxCONTROL_SELECTED )
    {
        colChanger.Set(wxSCHEME_COLOUR(m_scheme, HIGHLIGHT_TEXT));

        const wxColour colBg = wxSCHEME_COLOUR(m_scheme, HIGHLIGHT);
        dc.SetBrush(colBg);
        dc.SetPen(colBg);
        dc.DrawRectangle(rect);
    }

    // draw the bitmap: use the bitmap provided or the standard checkmark for
    // the checkable items
    wxBitmap bmp = bitmap;
    if ( !bmp.IsOk() && (flags & wxCONTROL_CHECKED) )
    {
        bmp = GetIndicator(IndicatorType_Menu, flags);
    }

    if ( bmp.IsOk() )
    {
        rect.SetRight(geometryInfo.GetLabelOffset());
        wxControlRenderer::DrawBitmap(dc, bmp, rect);
    }

    // draw the label
    rect.x = geometryInfo.GetLabelOffset();
    rect.SetRight(geometryInfo.GetAccelOffset());

    DrawLabel(dc, label, rect, flags, wxALIGN_CENTRE_VERTICAL, indexAccel);

    // draw the accel string
    rect.x = geometryInfo.GetAccelOffset();
    rect.SetRight(geometryInfo.GetSize().x);

    // NB: no accel index here
    DrawLabel(dc, accel, rect, flags, wxALIGN_CENTRE_VERTICAL);

    // draw the submenu indicator
    if ( flags & wxCONTROL_ISSUBMENU )
    {
        rect.x = geometryInfo.GetSize().x - MENU_RIGHT_MARGIN;
        rect.width = MENU_RIGHT_MARGIN;

        ArrowStyle arrowStyle;
        if ( flags & wxCONTROL_DISABLED )
            arrowStyle = flags & wxCONTROL_SELECTED ? Arrow_InvertedDisabled
                                                    : Arrow_Disabled;
        else if ( flags & wxCONTROL_SELECTED )
            arrowStyle = Arrow_Inverted;
        else
            arrowStyle = Arrow_Normal;

        DrawArrow(dc, rect, Arrow_Right, arrowStyle);
    }
}

void wxWin32Renderer::DrawMenuSeparator(wxDC& dc,
                                        wxCoord y,
                                        const wxMenuGeometryInfo& geomInfo)
{
    DrawHorizontalLine(dc, y + MENU_VERT_MARGIN, 0, geomInfo.GetSize().x);
}

wxSize wxWin32Renderer::GetMenuBarItemSize(const wxSize& sizeText) const
{
    wxSize size = sizeText;

    // FIXME: menubar height is configurable under Windows
    size.x += 12;
    size.y += 6;

    return size;
}

wxMenuGeometryInfo *wxWin32Renderer::GetMenuGeometry(wxWindow *win,
                                                     const wxMenu& menu) const
{
    // prepare the dc: for now we draw all the items with the system font
    wxInfoDC dc(win);
    dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));

    // the height of a normal item
    wxCoord heightText = dc.GetCharHeight();

    // the total height
    wxCoord height = 0;

    // the max length of label and accel strings: the menu width is the sum of
    // them, even if they're for different items (as the accels should be
    // aligned)
    //
    // the max length of the bitmap is never 0 as Windows always leaves enough
    // space for a check mark indicator
    wxCoord widthLabelMax = 0,
            widthAccelMax = 0,
            widthBmpMax = MENU_LEFT_MARGIN;

    for ( wxMenuItemList::compatibility_iterator node = menu.GetMenuItems().GetFirst();
          node;
          node = node->GetNext() )
    {
        // height of this item
        wxCoord h;

        wxMenuItem *item = node->GetData();
        if ( item->IsSeparator() )
        {
            h = MENU_SEPARATOR_HEIGHT;
        }
        else // not separator
        {
            h = heightText;

            wxCoord widthLabel;
            dc.GetTextExtent(item->GetItemLabelText(), &widthLabel, nullptr);
            if ( widthLabel > widthLabelMax )
            {
                widthLabelMax = widthLabel;
            }

            wxCoord widthAccel;
            dc.GetTextExtent(item->GetAccelString(), &widthAccel, nullptr);
            if ( widthAccel > widthAccelMax )
            {
                widthAccelMax = widthAccel;
            }

            const wxBitmap& bmp = item->GetBitmap();
            if ( bmp.IsOk() )
            {
                wxCoord widthBmp = bmp.GetWidth();
                if ( widthBmp > widthBmpMax )
                    widthBmpMax = widthBmp;
            }
            //else if ( item->IsCheckable() ): no need to check for this as
            // MENU_LEFT_MARGIN is big enough to show the check mark
        }

        h += 2*MENU_VERT_MARGIN;

        // remember the item position and height
        item->SetGeometry(height, h);

        height += h;
    }

    // bundle the metrics into a struct and return it
    wxWin32MenuGeometryInfo *gi = new wxWin32MenuGeometryInfo;

    gi->m_ofsLabel = widthBmpMax + 2*MENU_BMP_MARGIN;
    gi->m_ofsAccel = gi->m_ofsLabel + widthLabelMax;
    if ( widthAccelMax > 0 )
    {
        // if we actually have any accesl, add a margin
        gi->m_ofsAccel += MENU_ACCEL_MARGIN;
    }

    gi->m_heightItem = heightText + 2*MENU_VERT_MARGIN;

    gi->m_size.x = gi->m_ofsAccel + widthAccelMax + MENU_RIGHT_MARGIN;
    gi->m_size.y = height;

    return gi;
}

#endif // wxUSE_MENUS

#if wxUSE_STATUSBAR

// ----------------------------------------------------------------------------
// status bar
// ----------------------------------------------------------------------------

void wxWin32Renderer::DrawStatusField(wxDC& dc,
                                      const wxRect& rect,
                                      const wxString& label,
                                      int flags,
                                      int style)
{
    wxRect rectIn;

    if ( flags & wxCONTROL_SIZEGRIP )
    {
        // draw the size grip: it is a normal rect except that in the lower
        // right corner we have several bands which may be used for dragging
        // the status bar corner
        //
        // each band consists of 4 stripes: m_penHighlight, double
        // m_penDarkGrey and transparent one
        wxCoord x2 = rect.GetRight(),
                y2 = rect.GetBottom();

        // draw the upper left part of the rect normally
        if (style != wxSB_FLAT)
        {
            if (style == wxSB_RAISED)
                dc.SetPen(m_penHighlight);
            else
                dc.SetPen(m_penDarkGrey);
            dc.DrawLine(rect.GetLeft(), rect.GetTop(), rect.GetLeft(), y2);
            dc.DrawLine(rect.GetLeft() + 1, rect.GetTop(), x2, rect.GetTop());
        }

        // draw the grey stripes of the grip
        size_t n;
        wxCoord ofs = WIDTH_STATUSBAR_GRIP_BAND - 1;
        for ( n = 0; n < NUM_STATUSBAR_GRIP_BANDS; n++, ofs += WIDTH_STATUSBAR_GRIP_BAND )
        {
            dc.DrawLine(x2 - ofs + 1, y2 - 1, x2, y2 - ofs);
            dc.DrawLine(x2 - ofs, y2 - 1, x2, y2 - ofs - 1);
        }

        // draw the white stripes
        dc.SetPen(m_penHighlight);
        ofs = WIDTH_STATUSBAR_GRIP_BAND + 1;
        for ( n = 0; n < NUM_STATUSBAR_GRIP_BANDS; n++, ofs += WIDTH_STATUSBAR_GRIP_BAND )
        {
            dc.DrawLine(x2 - ofs + 1, y2 - 1, x2, y2 - ofs);
        }

        // draw the remaining rect boundaries
        if (style != wxSB_FLAT)
        {
            if (style == wxSB_RAISED)
                dc.SetPen(m_penDarkGrey);
            else
                dc.SetPen(m_penHighlight);
            ofs -= WIDTH_STATUSBAR_GRIP_BAND;
            dc.DrawLine(x2, rect.GetTop(), x2, y2 - ofs + 1);
            dc.DrawLine(rect.GetLeft(), y2, x2 - ofs + 1, y2);
        }

        rectIn = rect;
        rectIn.Deflate(1);

        rectIn.width -= STATUSBAR_GRIP_SIZE;

        // this will prevent the standard version from drawing any borders
        style = wxSB_FLAT;
    }

    wxStdRenderer::DrawStatusField(dc, rect, label, flags, style);
}

#endif // wxUSE_STATUSBAR

// ----------------------------------------------------------------------------
// combobox
// ----------------------------------------------------------------------------

void wxWin32Renderer::GetComboBitmaps(wxBitmap *bmpNormal,
                                      wxBitmap * WXUNUSED(bmpFocus),
                                      wxBitmap *bmpPressed,
                                      wxBitmap *bmpDisabled)
{
    static const wxCoord widthCombo = 16;
    static const wxCoord heightCombo = 17;

    wxMemoryDC dcMem;

    if ( bmpNormal )
    {
        bmpNormal->Create(widthCombo, heightCombo);
        dcMem.SelectObject(*bmpNormal);
        DrawArrowButton(dcMem, wxRect(0, 0, widthCombo, heightCombo),
                        Arrow_Down, Arrow_Normal);
    }

    if ( bmpPressed )
    {
        bmpPressed->Create(widthCombo, heightCombo);
        dcMem.SelectObject(*bmpPressed);
        DrawArrowButton(dcMem, wxRect(0, 0, widthCombo, heightCombo),
                        Arrow_Down, Arrow_Pressed);
    }

    if ( bmpDisabled )
    {
        bmpDisabled->Create(widthCombo, heightCombo);
        dcMem.SelectObject(*bmpDisabled);
        DrawArrowButton(dcMem, wxRect(0, 0, widthCombo, heightCombo),
                        Arrow_Down, Arrow_Disabled);
    }
}

// ----------------------------------------------------------------------------
// scrollbar
// ----------------------------------------------------------------------------

void wxWin32Renderer::DrawArrowBorder(wxDC& dc, wxRect *rect, bool isPressed)
{
    if ( isPressed )
    {
        DrawRect(dc, rect, m_penDarkGrey);

        // the arrow is usually drawn inside border of width 2 and is offset by
        // another pixel in both directions when it's pressed - as the border
        // in this case is more narrow as well, we have to adjust rect like
        // this:
        rect->Inflate(-1);
        rect->x++;
        rect->y++;
    }
    else // !pressed
    {
        DrawAntiSunkenBorder(dc, rect);
    }
}

void wxWin32Renderer::DrawArrow(wxDC& dc,
                                wxDirection dir,
                                const wxRect& rect,
                                int flags)
{
    ArrowStyle arrowStyle;
    if ( flags & wxCONTROL_PRESSED )
    {
        // can't be pressed and disabled
        arrowStyle = Arrow_Pressed;
    }
    else
    {
        arrowStyle = flags & wxCONTROL_DISABLED ? Arrow_Disabled : Arrow_Normal;
    }

    DrawArrowButton(dc, rect, GetArrowDirection(dir), arrowStyle);
}

void wxWin32Renderer::DrawArrow(wxDC& dc,
                                const wxRect& rect,
                                ArrowDirection arrowDir,
                                ArrowStyle arrowStyle)
{
    const wxBitmap& bmp = m_bmpArrows[arrowStyle][arrowDir];

    // under Windows the arrows always have the same size so just centre it in
    // the provided rectangle
    wxCoord x = rect.x + (rect.width - bmp.GetWidth()) / 2,
            y = rect.y + (rect.height - bmp.GetHeight()) / 2;

    // Windows does it like this...
    if ( arrowDir == Arrow_Left )
        x--;

    // draw it
    dc.DrawBitmap(bmp, x, y, true /* use mask */);
}

void wxWin32Renderer::DrawArrowButton(wxDC& dc,
                                      const wxRect& rectAll,
                                      ArrowDirection arrowDir,
                                      ArrowStyle arrowStyle)
{
    wxRect rect = rectAll;
    DrawBackground(dc, wxSCHEME_COLOUR(m_scheme, CONTROL), rect);
    DrawArrowBorder(dc, &rect, arrowStyle == Arrow_Pressed);
    DrawArrow(dc, rect, arrowDir, arrowStyle);
}

void wxWin32Renderer::DrawScrollbarThumb(wxDC& dc,
                                         wxOrientation WXUNUSED(orient),
                                         const wxRect& rect,
                                         int WXUNUSED(flags))
{
    // we don't use the flags, the thumb never changes appearance
    wxRect rectThumb = rect;
    DrawArrowBorder(dc, &rectThumb);
    DrawBackground(dc, wxNullColour, rectThumb);
}

void wxWin32Renderer::DrawScrollbarShaft(wxDC& dc,
                                         wxOrientation WXUNUSED(orient),
                                         const wxRect& rectBar,
                                         int flags)
{
    wxColourScheme::StdColour col = flags & wxCONTROL_PRESSED
                                    ? wxColourScheme::SCROLLBAR_PRESSED
                                    : wxColourScheme::SCROLLBAR;
    DrawBackground(dc, m_scheme->Get(col), rectBar);
}

// ----------------------------------------------------------------------------
// standard icons
// ----------------------------------------------------------------------------

/* Copyright (c) Julian Smart */
static const char *error_xpm[]={
/* columns rows colors chars-per-pixel */
"32 32 70 1",
"- c #BF0101",
"b c #361F1F",
"& c #C08484",
"X c #BF3333",
"# c #C08181",
"% c #C01111",
"d c #C51515",
"s c #551818",
"O c #C07E7E",
": c #C00E0E",
"u c #E28A8A",
"2 c #C81F1F",
"8 c #FFFFFF",
"p c #E59494",
"< c #BB0101",
"y c #DA6A6A",
"A c #4C4C4C",
"9 c #F7DFDF",
"@ c #BF5353",
"w c #FAE9E9",
"F c #272727",
"5 c #D24A4A",
". c #C06363",
"n c #BF8282",
"7 c #F2C9C9",
"t c #C09292",
"M c #3E3E3E",
"x c #4D4D4D",
"4 c #CA2A2A",
"h c #E79F9F",
"* c #C05454",
"D c #711212",
"V c #737373",
"$ c #BF3232",
"N c #900B0B",
"6 c #BD0303",
"3 c #DF7F7F",
"K c #6F1212",
"C c #BD0000",
"m c #950909",
"P c #8A8A8A",
"j c #D75F5F",
"  c None",
"e c #F4D4D4",
"S c #BF2020",
"L c #747474",
"G c #842C2C",
"c c #ECB4B4",
"l c #2E2121",
"g c #BF7E7E",
"k c #9B0808",
"= c #BF0505",
"a c #B10303",
"q c #7E2020",
"1 c #642222",
"J c #676767",
"B c #322020",
"; c #C00303",
"i c #242424",
"o c #C00000",
"> c #BF1F1F",
", c #842B2B",
"f c #701212",
"0 c #BE0000",
"r c #960909",
"H c #686868",
"v c #BC0000",
"Z c #671414",
"+ c #C02020",
"z c #CD3535",
/* pixels */
"                                ",
"                                ",
"            .XoooOO             ",
"         .+ooooooooo@#          ",
"        $oooooooooooo%&         ",
"      *=-ooooooooooooo;:        ",
"     *oooooooooooooooooo>       ",
"     =ooooooooooooooooooo,      ",
"    $-ooooooooooooooooooo<1     ",
"   .oooooo2334ooo533oooooo6     ",
"   +ooooooo789oo2883oooooo0q    ",
"   oooooooo2w83o78eoooooooor    ",
"  toooooooooy88u884oooooooori   ",
"  Xooooooooooe888poooooooooas   ",
"  ooooooooooo4889doooooooooof   ",
"  ooooooooooo588w2oooooooooofi  ",
"  oooooooooodw8887oooooooooofi  ",
"  goooooooooh8w588jooooooookli  ",
"  tooooooooz885op8wdooooooorix  ",
"   oooooood98cood98cooooooori   ",
"   @oooooop8w2ooo5885ooooovbi   ",
"   n%ooooooooooooooooooooomiM   ",
"    &;oooooooooooooooooooNBiV   ",
"     :ooooooooooooooooooCZiA    ",
"     nSooooooooooooooooCDiF     ",
"      nG<oooooooooooooNZiiH     ",
"        160ooooooooovmBiFH      ",
"         nqrraoookrrbiiA        ",
"          nJisKKKliiiML         ",
"             nPiiix             ",
"                                ",
"                                "
};

/* Copyright (c) Julian Smart */
static const char *info_xpm[]={
/* columns rows colors chars-per-pixel */
"32 32 17 1",
"* c #A1A3FB",
"X c #FFFFFF",
"O c #191EF4",
"= c #777AF9",
": c #4D51F7",
"  c None",
"- c #2328F5",
"+ c #4247F6",
"; c #C1C2FC",
". c #C0C0C0",
"& c #E0E1FE",
"% c #242424",
"> c #2D32F5",
"o c #CBCCFD",
"# c #0309F3",
"@ c #8C8FFA",
"$ c #EAEBFE",
/* pixels */
"          .......               ",
"       ...XXXXXXX...            ",
"     ..XXXXXXXXXXXXX..          ",
"    .XXXXXXXXXXXXXXXXX.         ",
"   .XXXXXXXXoO+XXXXXXXX.        ",
"  .XXXXXXXXX@#OXXXXXXXXX.       ",
" .XXXXXXXXXX$@oXXXXXXXXXX.      ",
" .XXXXXXXXXXXXXXXXXXXXXXX.%     ",
" .XXXXXXXXX&*=-XXXXXXXXXX.%%    ",
".XXXXXXXXXX;:#>XXXXXXXXXXX.%    ",
".XXXXXXXXXXX;#+XXXXXXXXXXX.%    ",
".XXXXXXXXXXX;#+XXXXXXXXXXX.%%   ",
" .XXXXXXXXXX;#+XXXXXXXXXX.%%%   ",
" .XXXXXXXXXX;#+XXXXXXXXXX.%%%   ",
" .XXXXXXXXXX;#+XXXXXXXXXX.%%    ",
"  .XXXXXXXX*-##+XXXXXXXX.%%%    ",
"   .XXXXXXXXXXXXXXXXXXX.%%%%    ",
"    .XXXXXXXXXXXXXXXXX.%%%%     ",
"     ..XXXXXXXXXXXXX..%%%%      ",
"      %...XXXXXXXX..%%%%%       ",
"       %%%..XXXXXX.%%%%%        ",
"         %%%.XXXXX.%%%          ",
"            %.XXXX.%%           ",
"              .XXX.%%           ",
"               .XX.%%           ",
"                .X.%%           ",
"                 ..%%           ",
"                  .%%           ",
"                   %%           ",
"                    %           ",
"                                ",
"                                "
};

/* Copyright (c) Julian Smart */
static const char *question_xpm[]={
/* columns rows colors chars-per-pixel */
"32 32 16 1",
"O c #A3A3FF",
"X c #FFFFFF",
"% c #CACAFF",
"- c #4141FF",
"= c #6060FF",
"* c #2B2BFF",
"@ c #B5B5FF",
"  c None",
"# c #1616FF",
"+ c #8181FF",
"$ c #0000FF",
". c #C0C0C0",
"; c #5555FF",
": c #242424",
"o c #E7E7FF",
"& c #7575FF",
/* pixels */
"          .......               ",
"       ...XXXXXXX...            ",
"     ..XXXXXXXXXXXXX..          ",
"    .XXXXXXoO++@XXXXXX.         ",
"   .XXXXXXO#$$$$#%XXXXX.        ",
"  .XXXXXX@$$#&&#$#oXXXXX.       ",
" .XXXXXXX*$$%XX%$$=XXXXXX.      ",
" .XXXXXXX+-;XXXX$$-XXXXXX.:     ",
" .XXXXXXXXXXXXX+$$&XXXXXX.::    ",
".XXXXXXXXXXXXo;$$*oXXXXXXX.:    ",
".XXXXXXXXXXXo*$$*oXXXXXXXX.:    ",
".XXXXXXXXXXX+$$*oXXXXXXXXX.::   ",
" .XXXXXXXXXX-$$oXXXXXXXXX.:::   ",
" .XXXXXXXXXXX--XXXXXXXXXX.:::   ",
" .XXXXXXXXXXXXXXXXXXXXXXX.::    ",
"  .XXXXXXXXX-$$XXXXXXXXX.:::    ",
"   .XXXXXXXX-$$XXXXXXXX.::::    ",
"    .XXXXXXXO++XXXXXXX.::::     ",
"     ..XXXXXXXXXXXXX..::::      ",
"      :...XXXXXXXX..:::::       ",
"       :::..XXXXXX.:::::        ",
"         :::.XXXXX.:::          ",
"            :.XXXX.::           ",
"              .XXX.::           ",
"               .XX.::           ",
"                .X.::           ",
"                 ..::           ",
"                  .::           ",
"                   ::           ",
"                    :           ",
"                                ",
"                                "
};

/* Copyright (c) Julian Smart */
static const char *warning_xpm[]={
/* columns rows colors chars-per-pixel */
"32 32 9 1",
"@ c Black",
"o c #A6A800",
"+ c #8A8C00",
"$ c #B8BA00",
"  c None",
"O c #6E7000",
"X c #DCDF00",
". c #C00000",
"# c #373800",
/* pixels */
"                                ",
"                                ",
"                                ",
"                .               ",
"               ...              ",
"               ...              ",
"              .....             ",
"             ...X..             ",
"             ..XXX..            ",
"            ...XXX...           ",
"            ..XXXXX..           ",
"           ..XXXXXX...          ",
"          ...XXoO+XX..          ",
"          ..XXXO@#XXX..         ",
"         ..XXXXO@#XXX...        ",
"        ...XXXXO@#XXXX..        ",
"        ..XXXXXO@#XXXX...       ",
"       ...XXXXXo@OXXXXX..       ",
"      ...XXXXXXo@OXXXXXX..      ",
"      ..XXXXXXX$@OXXXXXX...     ",
"     ...XXXXXXXX@XXXXXXXX..     ",
"    ...XXXXXXXXXXXXXXXXXX...    ",
"    ..XXXXXXXXXXOXXXXXXXXX..    ",
"   ...XXXXXXXXXO@#XXXXXXXXX..   ",
"   ..XXXXXXXXXXX#XXXXXXXXXX...  ",
"  ...XXXXXXXXXXXXXXXXXXXXXXX..  ",
" ...XXXXXXXXXXXXXXXXXXXXXXXX... ",
" .............................. ",
" .............................. ",
"                                ",
"                                ",
"                                "
};


wxBitmap wxWin32ArtProvider::CreateBitmap(const wxArtID& id,
                                          const wxArtClient& WXUNUSED(client),
                                          const wxSize& WXUNUSED(size))
{
    if ( id == wxART_INFORMATION )
        return wxBitmap(info_xpm);
    if ( id == wxART_ERROR )
        return wxBitmap(error_xpm);
    if ( id == wxART_WARNING )
        return wxBitmap(warning_xpm);
    if ( id == wxART_QUESTION )
        return wxBitmap(question_xpm);
    return wxNullBitmap;
}


#if wxUSE_TEXTCTRL

// ----------------------------------------------------------------------------
// text control geometry
// ----------------------------------------------------------------------------

wxRect
wxWin32Renderer::GetTextTotalArea(const wxTextCtrl *text,
                                  const wxRect& rect) const
{
    wxRect rectTotal = wxStdRenderer::GetTextTotalArea(text, rect);

    // this is strange but it's what Windows does
    rectTotal.height++;

    return rectTotal;
}

wxRect
wxWin32Renderer::GetTextClientArea(const wxTextCtrl *text,
                                   const wxRect& rect,
                                   wxCoord *extraSpaceBeyond) const
{
    wxRect rectText = rect;

    // undo GetTextTotalArea()
    if ( rectText.height > 0 )
        rectText.height--;

    return wxStdRenderer::GetTextClientArea(text, rect, extraSpaceBeyond);
}

#endif // wxUSE_TEXTCTRL

// ----------------------------------------------------------------------------
// size adjustments
// ----------------------------------------------------------------------------

void wxWin32Renderer::AdjustSize(wxSize *size, const wxWindow *window)
{
#if wxUSE_SCROLLBAR
    if ( wxDynamicCast(window, wxScrollBar) )
    {
        /*
        Don't adjust the size for a scrollbar as its DoGetBestClientSize
        already has the correct size set. Any size changes here would get
        added to the best size, making the scrollbar larger.
        Also skip border width adjustments, they don't make sense for us.
        */
        return;
    }
#endif // wxUSE_SCROLLBAR

#if wxUSE_BMPBUTTON
    if ( wxDynamicCast(window, wxBitmapButton) )
    {
        // do nothing
    } else
#endif // wxUSE_BMPBUTTON
#if wxUSE_BUTTON || wxUSE_TOGGLEBTN
    if ( 0
#  if wxUSE_BUTTON
         || wxDynamicCast(window, wxButton)
#  endif // wxUSE_BUTTON
#  if wxUSE_TOGGLEBTN
         || wxDynamicCast(window, wxToggleButton)
#  endif // wxUSE_TOGGLEBTN
        )
    {
        if ( !(window->GetWindowStyle() & wxBU_EXACTFIT) )
        {
            // TODO: don't hardcode all this
            size->x += 3*window->GetCharWidth();

            wxCoord heightBtn = (11*(window->GetCharHeight() + 8))/10;
            if ( size->y < heightBtn - 8 )
                size->y = heightBtn;
            else
                size->y += 9;
        }

        // for compatibility with other ports, the buttons default size is never
        // less than the standard one, but not when display not PDAs.
        if (wxSystemSettings::GetScreenType() > wxSYS_SCREEN_PDA)
        {
            if ( !(window->GetWindowStyle() & wxBU_EXACTFIT) )
            {
                wxSize szDef = wxButton::GetDefaultSize();
                if ( size->x < szDef.x )
                    size->x = szDef.x;
            }
        }

        // no border width adjustments for buttons
        return;
    }
#endif // wxUSE_BUTTON || wxUSE_TOGGLEBTN

    wxStdRenderer::AdjustSize(size, window);
}

wxBitmap wxWin32Renderer::GetFrameButtonBitmap(FrameButtonType type)
{
    wxBitmap& bmp = m_bmpFrameButtons[type];
    if ( !bmp.IsOk() )
    {
        bmp = wxBitmap(ms_xpmFrameButtons[type]);
    }

    return bmp;
}

// ============================================================================
// wxInputHandler
// ============================================================================

// ----------------------------------------------------------------------------
// wxWin32InputHandler
// ----------------------------------------------------------------------------

bool wxWin32InputHandler::HandleKey(wxInputConsumer * WXUNUSED(control),
                                    const wxKeyEvent& WXUNUSED(event),
                                    bool WXUNUSED(pressed))
{
    return false;
}

bool wxWin32InputHandler::HandleMouse(wxInputConsumer *control,
                                      const wxMouseEvent& event)
{
    // clicking on the control gives it focus
    if ( event.ButtonDown() )
    {
        wxWindow * const win = control->GetInputWindow();

        if ( win->CanAcceptFocus() && wxWindow::FindFocus() != win )
        {
            win->SetFocus();

            return true;
        }
    }

    return false;
}

#if wxUSE_SCROLLBAR

// ----------------------------------------------------------------------------
// wxWin32ScrollBarInputHandler
// ----------------------------------------------------------------------------

wxWin32ScrollBarInputHandler::
wxWin32ScrollBarInputHandler(wxRenderer *renderer, wxInputHandler *handler)
        : wxStdScrollBarInputHandler(renderer, handler)
{
    m_scrollPaused = false;
    m_interval = 0;
}

bool wxWin32ScrollBarInputHandler::OnScrollTimer(wxScrollBar *scrollbar,
                                                 const wxControlAction& action)
{
    // stop if went beyond the position of the original click (this can only
    // happen when we scroll by pages)
    bool stop = false;
    if ( action == wxACTION_SCROLL_PAGE_DOWN )
    {
        stop = scrollbar->HitTestBar(m_ptStartScrolling) != wxHT_SCROLLBAR_BAR_2;
    }
    else if ( action == wxACTION_SCROLL_PAGE_UP )
    {
        stop = scrollbar->HitTestBar(m_ptStartScrolling) != wxHT_SCROLLBAR_BAR_1;
    }

    if ( stop )
    {
        StopScrolling(scrollbar);

        scrollbar->Refresh();

        return false;
    }

    return wxStdScrollBarInputHandler::OnScrollTimer(scrollbar, action);
}

bool wxWin32ScrollBarInputHandler::HandleMouse(wxInputConsumer *control,
                                               const wxMouseEvent& event)
{
    // remember the current state
    bool wasDraggingThumb = m_htLast == wxHT_SCROLLBAR_THUMB;

    // do process the message
    bool rc = wxStdScrollBarInputHandler::HandleMouse(control, event);

    // analyse the changes
    if ( !wasDraggingThumb && (m_htLast == wxHT_SCROLLBAR_THUMB) )
    {
        // we just started dragging the thumb, remember its initial position to
        // be able to restore it if the drag is cancelled later
        m_eventStartDrag = event;
    }

    return rc;
}

bool wxWin32ScrollBarInputHandler::HandleMouseMove(wxInputConsumer *control,
                                                   const wxMouseEvent& event)
{
    // we don't highlight scrollbar elements, so there is no need to process
    // mouse move events normally - only do it while mouse is captured (i.e.
    // when we're dragging the thumb or pressing on something)
    if ( !m_winCapture )
        return false;

    if ( event.Entering() )
    {
        // we're not interested in this at all
        return false;
    }

    wxScrollBar *scrollbar = wxStaticCast(control->GetInputWindow(), wxScrollBar);
    wxHitTest ht;
    if ( m_scrollPaused )
    {
        // check if the mouse returned to its original location

        if ( event.Leaving() )
        {
            // it surely didn't
            return false;
        }

        ht = scrollbar->HitTestBar(event.GetPosition());
        if ( ht == m_htLast )
        {
            // yes it did, resume scrolling
            m_scrollPaused = false;
            if ( m_timerScroll )
            {
                // we were scrolling by line/page, restart timer
                m_timerScroll->Start(m_interval);

                Press(scrollbar, true);
            }
            else // we were dragging the thumb
            {
                // restore its last location
                HandleThumbMove(scrollbar, m_eventLastDrag);
            }

            return true;
        }
    }
    else // normal case, scrolling hasn't been paused
    {
        // if we're scrolling the scrollbar because the arrow or the shaft was
        // pressed, check that the mouse stays on the same scrollbar element

#if 0
        // Always let thumb jump back if we leave the scrollbar
        if ( event.Moving() )
        {
            ht = scrollbar->HitTestBar(event.GetPosition());
        }
        else // event.Leaving()
        {
            ht = wxHT_NOWHERE;
        }
#else
        // Jump back only if we get far away from it
        wxPoint pos = event.GetPosition();
        if (scrollbar->HasFlag( wxVERTICAL ))
        {
            if (pos.x > -40 && pos.x < scrollbar->GetSize().x+40)
               pos.x = 5;
        }
        else
        {
            if (pos.y > -40 && pos.y < scrollbar->GetSize().y+40)
               pos.y = 5;
        }
        ht = scrollbar->HitTestBar(pos);
#endif

        // if we're dragging the thumb and the mouse stays in the scrollbar, it
        // is still ok - we only want to catch the case when the mouse leaves
        // the scrollbar here
        if ( m_htLast == wxHT_SCROLLBAR_THUMB && ht != wxHT_NOWHERE )
        {
            ht = wxHT_SCROLLBAR_THUMB;
        }

        if ( ht != m_htLast )
        {
            // what were we doing? 2 possibilities: either an arrow/shaft was
            // pressed in which case we have a timer and so we just stop it or
            // we were dragging the thumb
            if ( m_timerScroll )
            {
                // pause scrolling
                m_interval = m_timerScroll->GetInterval();
                m_timerScroll->Stop();
                m_scrollPaused = true;

                // unpress the arrow
                Press(scrollbar, false);
            }
            else // we were dragging the thumb
            {
                // remember the current thumb position to be able to restore it
                // if the mouse returns to it later
                m_eventLastDrag = event;

                // and restore the original position (before dragging) of the
                // thumb for now
                HandleThumbMove(scrollbar, m_eventStartDrag);
            }

            return true;
        }
    }

    return wxStdInputHandler::HandleMouseMove(control, event);
}

#endif // wxUSE_SCROLLBAR

#if wxUSE_CHECKBOX

// ----------------------------------------------------------------------------
// wxWin32CheckboxInputHandler
// ----------------------------------------------------------------------------

bool wxWin32CheckboxInputHandler::HandleKey(wxInputConsumer *control,
                                            const wxKeyEvent& event,
                                            bool pressed)
{
    if ( pressed )
    {
        wxControlAction action;
        int keycode = event.GetKeyCode();
        switch ( keycode )
        {
            case WXK_SPACE:
                action = wxACTION_CHECKBOX_TOGGLE;
                break;

            case WXK_SUBTRACT:
            case WXK_NUMPAD_SUBTRACT:
                action = wxACTION_CHECKBOX_CHECK;
                break;

            case WXK_ADD:
            case WXK_NUMPAD_ADD:
            case WXK_NUMPAD_EQUAL:
                action = wxACTION_CHECKBOX_CLEAR;
                break;
        }

        if ( !action.IsEmpty() )
        {
            control->PerformAction(action);

            return true;
        }
    }

    return false;
}

#endif // wxUSE_CHECKBOX

#if wxUSE_TEXTCTRL

// ----------------------------------------------------------------------------
// wxWin32TextCtrlInputHandler
// ----------------------------------------------------------------------------

bool wxWin32TextCtrlInputHandler::HandleKey(wxInputConsumer *control,
                                            const wxKeyEvent& event,
                                            bool pressed)
{
    // handle only MSW-specific text bindings here, the others are handled in
    // the base class
    if ( pressed )
    {
        int keycode = event.GetKeyCode();

        wxControlAction action;
        if ( keycode == WXK_DELETE && event.ShiftDown() )
        {
            action = wxACTION_TEXT_CUT;
        }
        else if ( keycode == WXK_INSERT )
        {
            if ( event.ControlDown() )
                action = wxACTION_TEXT_COPY;
            else if ( event.ShiftDown() )
                action = wxACTION_TEXT_PASTE;
        }

        if ( action != wxACTION_NONE )
        {
            control->PerformAction(action);

            return true;
        }
    }

    return wxStdInputHandler::HandleKey(control, event, pressed);
}

#endif // wxUSE_TEXTCTRL

#if wxUSE_STATUSBAR

// ----------------------------------------------------------------------------
// wxWin32StatusBarInputHandler
// ----------------------------------------------------------------------------

wxWin32StatusBarInputHandler::
wxWin32StatusBarInputHandler(wxInputHandler *handler)
    : wxStdInputHandler(handler)
{
    m_isOnGrip = false;
}

bool wxWin32StatusBarInputHandler::IsOnGrip(wxWindow *statbar,
                                            const wxPoint& pt) const
{
    if ( statbar->HasFlag(wxST_SIZEGRIP) &&
         statbar->GetParent()->HasFlag(wxRESIZE_BORDER) )
    {
        wxTopLevelWindow *
            parentTLW = wxDynamicCast(statbar->GetParent(), wxTopLevelWindow);

        wxCHECK_MSG( parentTLW, false,
                     wxT("the status bar should be a child of a TLW") );

        // a maximized window can't be resized anyhow
        if ( !parentTLW->IsMaximized() )
        {
            // VZ: I think that the standard Windows behaviour is to only
            //     show the resizing cursor when the mouse is on top of the
            //     grip itself but apparently different Windows versions behave
            //     differently (?) and it seems a better UI to allow resizing
            //     the status bar even when the mouse is above the grip
            wxSize sizeSbar = statbar->GetSize();

            int diff = sizeSbar.x - pt.x;
            return diff >= 0 && diff < (wxCoord)STATUSBAR_GRIP_SIZE;
        }
    }

    return false;
}

bool wxWin32StatusBarInputHandler::HandleMouse(wxInputConsumer *consumer,
                                               const wxMouseEvent& event)
{
    if ( event.Button(1) )
    {
        if ( event.ButtonDown(1) )
        {
            wxWindow *statbar = consumer->GetInputWindow();

            if ( IsOnGrip(statbar, event.GetPosition()) )
            {
                wxTopLevelWindow *tlw = wxDynamicCast(statbar->GetParent(),
                                                      wxTopLevelWindow);
                if ( tlw )
                {
                    tlw->PerformAction(wxACTION_TOPLEVEL_RESIZE,
                                       wxHT_TOPLEVEL_BORDER_SE);

                    statbar->SetCursor(m_cursorOld);

                    return true;
                }
            }
        }
    }

    return wxStdInputHandler::HandleMouse(consumer, event);
}

bool wxWin32StatusBarInputHandler::HandleMouseMove(wxInputConsumer *consumer,
                                                   const wxMouseEvent& event)
{
    wxWindow *statbar = consumer->GetInputWindow();

    bool isOnGrip = IsOnGrip(statbar, event.GetPosition());
    if ( isOnGrip != m_isOnGrip )
    {
        m_isOnGrip = isOnGrip;
        if ( isOnGrip )
        {
            m_cursorOld = statbar->GetCursor();
            statbar->SetCursor(wxCURSOR_SIZENWSE);
        }
        else
        {
            statbar->SetCursor(m_cursorOld);
        }
    }

    return wxStdInputHandler::HandleMouseMove(consumer, event);
}

#endif // wxUSE_STATUSBAR

// ----------------------------------------------------------------------------
// wxWin32FrameInputHandler
// ----------------------------------------------------------------------------

class wxWin32SystemMenuEvtHandler : public wxEvtHandler
{
public:
    wxWin32SystemMenuEvtHandler(wxWin32FrameInputHandler *handler);

    void Attach(wxInputConsumer *consumer);
    void Detach();

private:
    wxDECLARE_EVENT_TABLE();
    void OnSystemMenu(wxCommandEvent &event);
    void OnCloseFrame(wxCommandEvent &event);
    void OnClose(wxCloseEvent &event);

    wxWin32FrameInputHandler *m_inputHnd;
    wxTopLevelWindow         *m_wnd;
#if wxUSE_ACCEL
    wxAcceleratorTable        m_oldAccelTable;
#endif
};

wxWin32SystemMenuEvtHandler::
wxWin32SystemMenuEvtHandler(wxWin32FrameInputHandler *handler)
{
    m_inputHnd = handler;
    m_wnd = nullptr;
}

void wxWin32SystemMenuEvtHandler::Attach(wxInputConsumer *consumer)
{
    wxASSERT_MSG( m_wnd == nullptr, wxT("can't attach the handler twice!") );

    m_wnd = wxStaticCast(consumer->GetInputWindow(), wxTopLevelWindow);
    m_wnd->PushEventHandler(this);

#if wxUSE_ACCEL
    // VS: This code relies on using generic implementation of
    //     wxAcceleratorTable in wxUniv!
    wxAcceleratorTable table = *m_wnd->GetAcceleratorTable();
    m_oldAccelTable = table;
    table.Add(wxAcceleratorEntry(wxACCEL_ALT, WXK_SPACE, wxID_SYSTEM_MENU));
    table.Add(wxAcceleratorEntry(wxACCEL_ALT, WXK_F4, wxID_CLOSE_FRAME));
    m_wnd->SetAcceleratorTable(table);
#endif
}

void wxWin32SystemMenuEvtHandler::Detach()
{
    if ( m_wnd )
    {
#if wxUSE_ACCEL
        m_wnd->SetAcceleratorTable(m_oldAccelTable);
#endif
        m_wnd->RemoveEventHandler(this);
        m_wnd = nullptr;
    }
}

wxBEGIN_EVENT_TABLE(wxWin32SystemMenuEvtHandler, wxEvtHandler)
    EVT_MENU(wxID_SYSTEM_MENU, wxWin32SystemMenuEvtHandler::OnSystemMenu)
    EVT_MENU(wxID_CLOSE_FRAME, wxWin32SystemMenuEvtHandler::OnCloseFrame)
    EVT_CLOSE(wxWin32SystemMenuEvtHandler::OnClose)
wxEND_EVENT_TABLE()

void wxWin32SystemMenuEvtHandler::OnSystemMenu(wxCommandEvent &WXUNUSED(event))
{
#if wxUSE_ACCEL
    wxAcceleratorTable table = *m_wnd->GetAcceleratorTable();
    m_wnd->SetAcceleratorTable(wxNullAcceleratorTable);
#endif

#if wxUSE_MENUS
    m_inputHnd->PopupSystemMenu(m_wnd);
#endif // wxUSE_MENUS

#if wxUSE_ACCEL
    m_wnd->SetAcceleratorTable(table);
#endif
}

void wxWin32SystemMenuEvtHandler::OnCloseFrame(wxCommandEvent &WXUNUSED(event))
{
    m_wnd->PerformAction(wxACTION_TOPLEVEL_BUTTON_CLICK,
                         wxTOPLEVEL_BUTTON_CLOSE);
}

void wxWin32SystemMenuEvtHandler::OnClose(wxCloseEvent &event)
{
    m_wnd = nullptr;
    event.Skip();
}


wxWin32FrameInputHandler::wxWin32FrameInputHandler(wxInputHandler *handler)
                        : wxStdInputHandler(handler)
{
    m_menuHandler = new wxWin32SystemMenuEvtHandler(this);
}

wxWin32FrameInputHandler::~wxWin32FrameInputHandler()
{
    if ( m_menuHandler )
    {
        m_menuHandler->Detach();
        delete m_menuHandler;
    }
}

bool wxWin32FrameInputHandler::HandleMouse(wxInputConsumer *consumer,
                                           const wxMouseEvent& event)
{
    if ( event.LeftDClick() || event.LeftDown() || event.RightDown() )
    {
        wxTopLevelWindow *tlw =
            wxStaticCast(consumer->GetInputWindow(), wxTopLevelWindow);

        long hit = tlw->HitTest(event.GetPosition());

        if ( event.LeftDClick() && hit == wxHT_TOPLEVEL_TITLEBAR )
        {
            tlw->PerformAction(wxACTION_TOPLEVEL_BUTTON_CLICK,
                               tlw->IsMaximized() ? wxTOPLEVEL_BUTTON_RESTORE
                                                  : wxTOPLEVEL_BUTTON_MAXIMIZE);
            return true;
        }
        else if ( tlw->GetWindowStyle() & wxSYSTEM_MENU )
        {
            if ( (event.LeftDown() && hit == wxHT_TOPLEVEL_ICON) ||
                 (event.RightDown() &&
                      (hit == wxHT_TOPLEVEL_TITLEBAR ||
                       hit == wxHT_TOPLEVEL_ICON)) )
            {
#if wxUSE_MENUS
                PopupSystemMenu(tlw);
#endif // wxUSE_MENUS
                return true;
            }
        }
    }

    return wxStdInputHandler::HandleMouse(consumer, event);
}

#if wxUSE_MENUS

void wxWin32FrameInputHandler::PopupSystemMenu(wxTopLevelWindow *window) const
{
    wxMenu menu;

    if ( window->GetWindowStyle() & wxMAXIMIZE_BOX )
        menu.Append(wxID_RESTORE_FRAME , _("&Restore"));
    menu.Append(wxID_MOVE_FRAME , _("&Move"));
    if ( window->GetWindowStyle() & wxRESIZE_BORDER )
        menu.Append(wxID_RESIZE_FRAME , _("&Size"));
    if ( wxSystemSettings::HasFeature(wxSYS_CAN_ICONIZE_FRAME) )
        menu.Append(wxID_ICONIZE_FRAME , _("Mi&nimize"));
    if ( window->GetWindowStyle() & wxMAXIMIZE_BOX )
        menu.Append(wxID_MAXIMIZE_FRAME , _("Ma&ximize"));
    menu.AppendSeparator();
    menu.Append(wxID_CLOSE_FRAME, _("&Close") + wxT("\t") + _("Alt+") + wxT("F4"));

    if ( window->GetWindowStyle() & wxMAXIMIZE_BOX )
    {
        if ( window->IsMaximized() )
        {
            menu.Enable(wxID_MAXIMIZE_FRAME, false);
            menu.Enable(wxID_MOVE_FRAME, false);
            if ( window->GetWindowStyle() & wxRESIZE_BORDER )
                menu.Enable(wxID_RESIZE_FRAME, false);
        }
        else
            menu.Enable(wxID_RESTORE_FRAME, false);
    }

    window->PopupMenu(&menu, wxPoint(0, 0));
}

#endif // wxUSE_MENUS

bool wxWin32FrameInputHandler::HandleActivation(wxInputConsumer *consumer,
                                                bool activated)
{
    if ( consumer->GetInputWindow()->GetWindowStyle() & wxSYSTEM_MENU )
    {
        // always detach if active frame changed:
        m_menuHandler->Detach();

        if ( activated )
        {
            m_menuHandler->Attach(consumer);
        }
    }

    return wxStdInputHandler::HandleActivation(consumer, activated);
}

#endif // wxUSE_THEME_WIN32
