///////////////////////////////////////////////////////////////////////////////
// Name:        src/univ/winuniv.cpp
// Purpose:     implementation of extra wxWindow methods for wxUniv port
// 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/window.h"

#ifndef WX_PRECOMP
    #include "wx/app.h"
    #include "wx/dcclient.h"
    #include "wx/dcmemory.h"
    #include "wx/event.h"
    #include "wx/scrolbar.h"
    #include "wx/menu.h"
    #include "wx/frame.h"
    #include "wx/log.h"
    #include "wx/button.h"
#endif // WX_PRECOMP

#include "wx/univ/colschem.h"
#include "wx/univ/renderer.h"
#include "wx/univ/theme.h"


#if wxUSE_CARET
    #include "wx/caret.h"
#endif // wxUSE_CARET

#if wxDEBUG_LEVEL >= 2
    // turn Refresh() debugging on/off
    #define WXDEBUG_REFRESH
#endif

#if defined(WXDEBUG_REFRESH) && defined(__WXMSW__)
#include "wx/msw/private.h"
#endif

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

#if wxUSE_SCROLLBAR
// ----------------------------------------------------------------------------
// scrollbars class
// ----------------------------------------------------------------------------

// This is scrollbar class used to implement wxWindow's "built-in" scrollbars;
// unlike the standard wxScrollBar class, this one is positioned outside of its
// parent's client area
class wxWindowScrollBar : public wxScrollBar
{
public:
    wxWindowScrollBar(wxWindow *parent,
                      wxWindowID id,
                      const wxPoint& pos = wxDefaultPosition,
                      const wxSize& size = wxDefaultSize,
                      long style = wxSB_HORIZONTAL)
        : wxScrollBar(parent, id, pos, size, style)
    {
    }

    virtual bool CanBeOutsideClientArea() const { return true; }
};
#endif // wxUSE_SCROLLBAR


// ----------------------------------------------------------------------------
// event tables
// ----------------------------------------------------------------------------

// we can't use wxWindowNative here as it won't be expanded inside the macro
#if defined(__WXMSW__)
    wxIMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowMSW);
#elif defined(__WXGTK__)
    wxIMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowGTK);
#elif defined(__WXOSX__)
    wxIMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowMac);
#elif defined(__WXDFB__)
    wxIMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowDFB);
#elif defined(__WXX11__)
    wxIMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowX11);
#endif

wxBEGIN_EVENT_TABLE(wxWindow, wxWindowNative)
    EVT_SIZE(wxWindow::OnSize)

#if wxUSE_ACCEL || wxUSE_MENUS
    EVT_KEY_DOWN(wxWindow::OnKeyDown)
#endif // wxUSE_ACCEL

#if wxUSE_MENUS
    EVT_CHAR(wxWindow::OnChar)
    EVT_KEY_UP(wxWindow::OnKeyUp)
#endif // wxUSE_MENUS

    EVT_PAINT(wxWindow::OnPaint)
    EVT_NC_PAINT(wxWindow::OnNcPaint)
    EVT_ERASE_BACKGROUND(wxWindow::OnErase)
wxEND_EVENT_TABLE()

// ----------------------------------------------------------------------------
// creation
// ----------------------------------------------------------------------------

void wxWindow::Init()
{
#if wxUSE_SCROLLBAR
    m_scrollbarVert =
    m_scrollbarHorz = nullptr;
#endif // wxUSE_SCROLLBAR

    m_isCurrent = false;

    m_renderer = wxTheme::Get()->GetRenderer();

    m_oldSize.x = wxDefaultCoord;
    m_oldSize.y = wxDefaultCoord;
}

bool wxWindow::Create(wxWindow *parent,
                      wxWindowID id,
                      const wxPoint& pos,
                      const wxSize& size,
                      long style,
                      const wxString& name)
{
    // Get default border
    wxBorder border = GetBorder(style);
    style &= ~wxBORDER_MASK;
    style |= border;

    long actualStyle = style;

    // we add wxCLIP_CHILDREN to get the same ("natural") behaviour under MSW
    // as under the other platforms
    actualStyle |= wxCLIP_CHILDREN;

    actualStyle &= ~wxVSCROLL;
    actualStyle &= ~wxHSCROLL;

#ifdef __WXMSW__
    // without this, borders (non-client areas in general) are not repainted
    // correctly when resizing; apparently, native NC areas are fully repainted
    // even without this style by MSW, but wxUniv implements client area
    // itself, so it doesn't work correctly for us
    //
    // FIXME: this is very expensive, we need to fix the (commented-out) code
    //        in OnSize() instead
    actualStyle |= wxFULL_REPAINT_ON_RESIZE;
#endif

    if ( !wxWindowNative::Create(parent, id, pos, size, actualStyle, name) )
        return false;

    // Set full style again, including those we didn't want present
    // when calling the base window Create().
    wxWindowBase::SetWindowStyleFlag(style);

    // if we allow or should always have a vertical scrollbar, make it
    if ( style & wxVSCROLL || style & wxALWAYS_SHOW_SB )
    {
#if wxUSE_TWO_WINDOWS
        SetInsertIntoMain( true );
#endif
#if wxUSE_SCROLLBAR
        m_scrollbarVert = new wxWindowScrollBar(this, wxID_ANY,
                                                wxDefaultPosition, wxDefaultSize,
                                                wxSB_VERTICAL);
#endif // wxUSE_SCROLLBAR
#if wxUSE_TWO_WINDOWS
        SetInsertIntoMain( false );
#endif
    }

    // if we should allow a horizontal scrollbar, make it
    if ( style & wxHSCROLL )
    {
#if wxUSE_TWO_WINDOWS
        SetInsertIntoMain( true );
#endif
#if wxUSE_SCROLLBAR
        m_scrollbarHorz = new wxWindowScrollBar(this, wxID_ANY,
                                                wxDefaultPosition, wxDefaultSize,
                                                wxSB_HORIZONTAL);
#endif // wxUSE_SCROLLBAR
#if wxUSE_TWO_WINDOWS
        SetInsertIntoMain( false );
#endif
    }

#if wxUSE_SCROLLBAR
    if (m_scrollbarHorz || m_scrollbarVert)
    {
        // position it/them
        PositionScrollbars();
    }
#endif // wxUSE_SCROLLBAR

    return true;
}

wxWindow::~wxWindow()
{
    SendDestroyEvent();

#if wxUSE_SCROLLBAR
    // clear pointers to scrollbar before deleting the children: they are
    // children and so will be deleted by DestroyChildren() call below and if
    // any code using the scrollbars would be called in the process or from
    // ~wxWindowBase, the app would crash:
    m_scrollbarVert = m_scrollbarHorz = nullptr;
#endif

    // we have to destroy our children before we're destroyed because our
    // children suppose that we're of type wxWindow, not just wxWindowNative,
    // and so bad things may happen if they're deleted from the base class dtor
    // as by then we're not a wxWindow any longer and wxUniv-specific virtual
    // functions can't be called
    DestroyChildren();
}

// ----------------------------------------------------------------------------
// background pixmap
// ----------------------------------------------------------------------------

void wxWindow::SetBackground(const wxBitmap& bitmap,
                              int alignment,
                              wxStretch stretch)
{
    m_bitmapBg = bitmap;
    m_alignBgBitmap = alignment;
    m_stretchBgBitmap = stretch;
}

const wxBitmap& wxWindow::GetBackgroundBitmap(int *alignment,
                                               wxStretch *stretch) const
{
    if ( m_bitmapBg.IsOk() )
    {
        if ( alignment )
            *alignment = m_alignBgBitmap;
        if ( stretch )
            *stretch = m_stretchBgBitmap;
    }

    return m_bitmapBg;
}

// ----------------------------------------------------------------------------
// painting
// ----------------------------------------------------------------------------

// the event handlers executed when the window must be repainted
void wxWindow::OnNcPaint(wxNcPaintEvent& WXUNUSED(event))
{
    if ( m_renderer )
    {
        // get the window rect
        wxRect rect(GetSize());

#if wxUSE_SCROLLBAR
        // if the scrollbars are outside the border, we must adjust the rect to
        // exclude them
        if ( !m_renderer->AreScrollbarsInsideBorder() )
        {
            wxScrollBar *scrollbar = GetScrollbar(wxVERTICAL);
            if ( scrollbar )
                rect.width -= scrollbar->GetSize().x;

            scrollbar = GetScrollbar(wxHORIZONTAL);
            if ( scrollbar )
                rect.height -= scrollbar->GetSize().y;
        }
#endif // wxUSE_SCROLLBAR

        // get the DC and draw the border on it
        wxWindowDC dc(this);
        DoDrawBorder(dc, rect);
    }
}

void wxWindow::OnPaint(wxPaintEvent& event)
{
    if ( !m_renderer )
    {
        // it is a native control which paints itself
        event.Skip();
    }
    else
    {
        // get the DC to use and create renderer on it
        wxPaintDC dc(this);
        wxControlRenderer renderer(this, dc, m_renderer);

        // draw the control
        DoDraw(&renderer);
    }
}

// the event handler executed when the window background must be painted
void wxWindow::OnErase(wxEraseEvent& event)
{
    if ( !m_renderer )
    {
        event.Skip();

        return;
    }

    DoDrawBackground(*event.GetDC());

#if wxUSE_SCROLLBAR
    // if we have both scrollbars, we also have a square in the corner between
    // them which we must paint
    if ( m_scrollbarVert && m_scrollbarHorz )
    {
        wxSize size = GetSize();
        wxRect rectClient = GetClientRect(),
               rectBorder = m_renderer->GetBorderDimensions(GetBorder());

        wxRect rectCorner;
        rectCorner.x = rectClient.GetRight() + 1;
        rectCorner.y = rectClient.GetBottom() + 1;
        rectCorner.SetRight(size.x - rectBorder.width);
        rectCorner.SetBottom(size.y - rectBorder.height);

        if ( GetUpdateRegion().Contains(rectCorner) )
        {
            m_renderer->DrawScrollCorner(*event.GetDC(), rectCorner);
        }
    }
#endif // wxUSE_SCROLLBAR
}

bool wxWindow::DoDrawBackground(wxDC& dc)
{
    wxRect rect;

    wxSize size = GetSize();  // Why not GetClientSize() ?
    rect.x = 0;
    rect.y = 0;
    rect.width = size.x;
    rect.height = size.y;

    wxWindow * const parent = GetParent();
    if ( HasTransparentBackground() && !UseBgCol() && parent )
    {
        // DirectFB paints the parent first, then its child windows, so by
        // the time this code is called, parent's background was already
        // drawn and there's no point in (imperfectly!) duplicating the work
        // here:
#ifndef __WXDFB__
        wxASSERT( !IsTopLevel() );

        wxPoint pos = GetPosition();

        AdjustForParentClientOrigin( pos.x, pos.y, 0 );

        // Adjust DC logical origin
        wxCoord org_x, org_y, x, y;
        dc.GetLogicalOrigin( &org_x, &org_y );
        x = org_x + pos.x;
        y = org_y + pos.y;
        dc.SetLogicalOrigin( x, y );

        // Adjust draw rect
        rect.x = pos.x;
        rect.y = pos.y;

        // Let parent draw the background
        parent->EraseBackground( dc, rect );

        // Restore DC logical origin
        dc.SetLogicalOrigin( org_x, org_y );
#endif // !__WXDFB__
    }
    else
    {
        // Draw background ourselves
        EraseBackground( dc, rect );
    }

    return true;
}

void wxWindow::EraseBackground(wxDC& dc, const wxRect& rect)
{
    if ( GetBackgroundBitmap().IsOk() )
    {
        // Get the bitmap and the flags
        int alignment;
        wxStretch stretch;
        wxBitmap bmp = GetBackgroundBitmap(&alignment, &stretch);
        wxControlRenderer::DrawBitmap(dc, bmp, rect, alignment, stretch);
    }
    else
    {
        // Just fill it with bg colour if no bitmap

        m_renderer->DrawBackground(dc, wxTHEME_BG_COLOUR(this),
                                   rect, GetStateFlags());
    }
}

void wxWindow::DoDrawBorder(wxDC& dc, const wxRect& rect)
{
    // draw outline unless the update region is enitrely inside it in which
    // case we don't need to do it
#if 0 // doesn't seem to work, why?
    if ( wxRegion(rect).Contains(GetUpdateRegion().GetBox()) != wxInRegion )
#endif
    {
        m_renderer->DrawBorder(dc, GetBorder(), rect, GetStateFlags());
    }
}

void wxWindow::DoDraw(wxControlRenderer * WXUNUSED(renderer))
{
}

void wxWindow::Refresh(bool eraseBackground, const wxRect *rect)
{
    wxRect rectClient; // the same rectangle in client coordinates
    wxPoint origin = GetClientAreaOrigin();

    wxSize size = GetClientSize();

    if ( rect )
    {
        // the rectangle passed as argument is in client coordinates
        rectClient = *rect;

        // don't refresh anything beyond the client area (scrollbars for
        // example)
        if ( rectClient.GetRight() > size.x )
            rectClient.SetRight(size.x);
        if ( rectClient.GetBottom() > size.y )
            rectClient.SetBottom(size.y);

    }
    else // refresh the entire client area
    {
        // x,y is already set to 0 by default
        rectClient.SetSize(size);
    }

    // convert refresh rectangle to window coordinates:
    wxRect rectWin(rectClient);
    rectWin.Offset(origin);

    // debugging helper
#ifdef WXDEBUG_REFRESH
    static bool s_refreshDebug = false;
    if ( s_refreshDebug )
    {
        wxWindowDC dc(this);
        dc.SetBrush(*wxCYAN_BRUSH);
        dc.SetPen(*wxTRANSPARENT_PEN);
        dc.DrawRectangle(rectWin);

        // under Unix we use "--sync" X option for this
        #if defined(__WXMSW__)
            ::GdiFlush();
            ::Sleep(200);
        #endif // __WXMSW__
    }
#endif // WXDEBUG_REFRESH

    wxWindowNative::Refresh(eraseBackground, &rectWin);

    // Refresh all sub controls if any.
    wxWindowList& children = GetChildren();
    for ( wxWindowList::iterator i = children.begin(); i != children.end(); ++i )
    {
        wxWindow *child = *i;
        // only refresh subcontrols if they are visible:
        if ( child->IsTopLevel() || !child->IsShown() || child->IsFrozen() )
            continue;

        // ...and when the subcontrols are in the update region:
        wxRect childrect(child->GetRect());
        childrect.Intersect(rectClient);
        if ( childrect.IsEmpty() )
            continue;

        // refresh the subcontrol now:
        childrect.Offset(-child->GetPosition());
        // NB: We must call wxWindowNative version because we need to refresh
        //     the entire control, not just its client area, and this is why we
        //     don't account for child client area origin here either. Also
        //     note that we don't pass eraseBackground to the child, but use
        //     true instead: this is because we can't be sure that
        //     eraseBackground=false is safe for children as well and not only
        //     for the parent.
        child->wxWindowNative::Refresh(eraseBackground, &childrect);
    }
}

// ----------------------------------------------------------------------------
// state flags
// ----------------------------------------------------------------------------

bool wxWindow::Enable(bool enable)
{
    if ( !wxWindowNative::Enable(enable) )
        return false;

    // disabled window can't keep focus
    if ( FindFocus() == this && GetParent() != nullptr )
    {
        GetParent()->SetFocus();
    }

    if ( m_renderer )
    {
        // a window with renderer is drawn by ourselves and it has to be
        // refreshed to reflect its new status
        Refresh();
    }

    return true;
}

bool wxWindow::IsFocused() const
{
    return FindFocus() == this;
}

bool wxWindow::IsPressed() const
{
    return false;
}

bool wxWindow::IsDefault() const
{
    return false;
}

bool wxWindow::IsCurrent() const
{
    return m_isCurrent;
}

bool wxWindow::WXMakeCurrent(bool doit)
{
    if ( doit == m_isCurrent )
        return false;

    m_isCurrent = doit;

    if ( CanBeHighlighted() )
        Refresh();

    return true;
}

int wxWindow::GetStateFlags() const
{
    int flags = 0;
    if ( !IsEnabled() )
        flags |= wxCONTROL_DISABLED;

    // the following states are only possible if our application is active - if
    // it is not, even our default/focused controls shouldn't appear as such
    if ( wxTheApp->IsActive() )
    {
        if ( IsCurrent() )
            flags |= wxCONTROL_CURRENT;
        if ( IsFocused() )
            flags |= wxCONTROL_FOCUSED;
        if ( IsPressed() )
            flags |= wxCONTROL_PRESSED;
        if ( IsDefault() )
            flags |= wxCONTROL_ISDEFAULT;
    }

    return flags;
}

// ----------------------------------------------------------------------------
// size
// ----------------------------------------------------------------------------

void wxWindow::OnSize(wxSizeEvent& event)
{
    event.Skip();

#if wxUSE_SCROLLBAR
    if ( m_scrollbarVert || m_scrollbarHorz )
    {
        PositionScrollbars();
    }
#endif // wxUSE_SCROLLBAR

#if 0   // ndef __WXMSW__
    // Refresh the area (strip) previously occupied by the border

    if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) && IsShown() )
    {
        // This code assumes that wxSizeEvent.GetSize() returns
        // the area of the entire window, not just the client
        // area.
        wxSize newSize = event.GetSize();

        if (m_oldSize.x == wxDefaultCoord && m_oldSize.y == wxDefaultCoord)
        {
            m_oldSize = newSize;
            return;
        }

        if (HasFlag( wxSIMPLE_BORDER ))
        {
            if (newSize.y > m_oldSize.y)
            {
                wxRect rect;
                rect.x = 0;
                rect.width = m_oldSize.x;
                rect.y = m_oldSize.y-2;
                rect.height = 1;
                Refresh( true, &rect );
            }
            else if (newSize.y < m_oldSize.y)
            {
                wxRect rect;
                rect.y = newSize.y;
                rect.x = 0;
                rect.height = 1;
                rect.width = newSize.x;
                wxWindowNative::Refresh( true, &rect );
            }

            if (newSize.x > m_oldSize.x)
            {
                wxRect rect;
                rect.y = 0;
                rect.height = m_oldSize.y;
                rect.x = m_oldSize.x-2;
                rect.width = 1;
                Refresh( true, &rect );
            }
            else if (newSize.x < m_oldSize.x)
            {
                wxRect rect;
                rect.x = newSize.x;
                rect.y = 0;
                rect.width = 1;
                rect.height = newSize.y;
                wxWindowNative::Refresh( true, &rect );
            }
        }
        else
        if (HasFlag( wxSUNKEN_BORDER ) || HasFlag( wxRAISED_BORDER ) || HasFlag( wxBORDER_THEME ))
        {
            if (newSize.y > m_oldSize.y)
            {
                wxRect rect;
                rect.x = 0;
                rect.width = m_oldSize.x;
                rect.y = m_oldSize.y-4;
                rect.height = 2;
                Refresh( true, &rect );
            }
            else if (newSize.y < m_oldSize.y)
            {
                wxRect rect;
                rect.y = newSize.y;
                rect.x = 0;
                rect.height = 2;
                rect.width = newSize.x;
                wxWindowNative::Refresh( true, &rect );
            }

            if (newSize.x > m_oldSize.x)
            {
                wxRect rect;
                rect.y = 0;
                rect.height = m_oldSize.y;
                rect.x = m_oldSize.x-4;
                rect.width = 2;
                Refresh( true, &rect );
            }
            else if (newSize.x < m_oldSize.x)
            {
                wxRect rect;
                rect.x = newSize.x;
                rect.y = 0;
                rect.width = 2;
                rect.height = newSize.y;
                wxWindowNative::Refresh( true, &rect );
            }
        }

        m_oldSize = newSize;
    }
#endif
}

wxSize wxWindow::GetWindowBorderSize() const
{
    return AdjustSize(wxSize(0, 0));
}

wxSize wxWindow::AdjustSize(const wxSize& size) const
{
    wxSize sz = size;
    if ( m_renderer )
        m_renderer->AdjustSize(&sz, this);
    return sz;
}

wxPoint wxWindow::GetClientAreaOrigin() const
{
    wxPoint pt = wxWindowBase::GetClientAreaOrigin();

#if wxUSE_TWO_WINDOWS
#else
    if ( m_renderer )
        pt += m_renderer->GetBorderDimensions(GetBorder()).GetPosition();
#endif

    return pt;
}

void wxWindow::DoGetClientSize(int *width, int *height) const
{
    // if it is a native window, we assume it handles the scrollbars itself
    // too - and if it doesn't, there is not much we can do
    if ( !m_renderer )
    {
        wxWindowNative::DoGetClientSize(width, height);

        return;
    }

    int w, h;
    wxWindowNative::DoGetClientSize(&w, &h);

    // we assume that the scrollbars are positioned correctly (by a previous
    // call to PositionScrollbars()) here

    wxRect rectBorder;
    if ( m_renderer )
        rectBorder = m_renderer->GetBorderDimensions(GetBorder());

    if ( width )
    {
#if wxUSE_SCROLLBAR
        // in any case, take account of the scrollbar
        if ( m_scrollbarVert )
            w -= m_scrollbarVert->GetSize().x;
#endif // wxUSE_SCROLLBAR

        // account for the left and right borders
        *width = w - rectBorder.x - rectBorder.width;

        // we shouldn't return invalid width
        if ( *width < 0 )
            *width = 0;
    }

    if ( height )
    {
#if wxUSE_SCROLLBAR
        if ( m_scrollbarHorz )
            h -= m_scrollbarHorz->GetSize().y;
#endif // wxUSE_SCROLLBAR

        *height = h - rectBorder.y - rectBorder.height;

        // we shouldn't return invalid height
        if ( *height < 0 )
            *height = 0;
    }
}

void wxWindow::DoSetClientSize(int width, int height)
{
    // take into account the borders
    wxRect rectBorder = m_renderer->GetBorderDimensions(GetBorder());
    width += rectBorder.x;
    height += rectBorder.y;

    // and the scrollbars (as they may be offset into the border, use the
    // scrollbar position, not size - this supposes that PositionScrollbars()
    // had been called before)
    wxSize size = GetSize();
#if wxUSE_SCROLLBAR
    if ( m_scrollbarVert )
        width += size.x - m_scrollbarVert->GetPosition().x;
#endif // wxUSE_SCROLLBAR
    width += rectBorder.width;

#if wxUSE_SCROLLBAR
    if ( m_scrollbarHorz )
        height += size.y - m_scrollbarHorz->GetPosition().y;
#endif // wxUSE_SCROLLBAR
    height += rectBorder.height;

    wxWindowNative::DoSetClientSize(width, height);
}

wxHitTest wxWindow::DoHitTest(wxCoord x, wxCoord y) const
{
    wxHitTest ht = wxWindowNative::DoHitTest(x, y);

#if wxUSE_SCROLLBAR
    if ( ht == wxHT_WINDOW_INSIDE )
    {
        if ( m_scrollbarVert && x >= m_scrollbarVert->GetPosition().x )
        {
            // it can still be changed below because it may also be the corner
            ht = wxHT_WINDOW_VERT_SCROLLBAR;
        }

        if ( m_scrollbarHorz && y >= m_scrollbarHorz->GetPosition().y )
        {
            ht = ht == wxHT_WINDOW_VERT_SCROLLBAR ? wxHT_WINDOW_CORNER
                                                  : wxHT_WINDOW_HORZ_SCROLLBAR;
        }
    }
#endif // wxUSE_SCROLLBAR

    return ht;
}

// ----------------------------------------------------------------------------
// scrolling: we implement it entirely ourselves except for ScrollWindow()
// function which is supposed to be (efficiently) implemented by the native
// window class
// ----------------------------------------------------------------------------

void wxWindow::RefreshScrollbars()
{
#if wxUSE_SCROLLBAR
    if ( m_scrollbarHorz )
        m_scrollbarHorz->Refresh();

    if ( m_scrollbarVert )
        m_scrollbarVert->Refresh();
#endif // wxUSE_SCROLLBAR
}

void wxWindow::PositionScrollbars()
{
#if wxUSE_SCROLLBAR
    // do not use GetClientSize/Rect as it relies on the scrollbars being
    // correctly positioned

    wxSize size = GetSize();
    wxBorder border = GetBorder();
    wxRect rectBorder = m_renderer->GetBorderDimensions(border);
    bool inside = m_renderer->AreScrollbarsInsideBorder();

    int height = m_scrollbarHorz ? m_scrollbarHorz->GetSize().y : 0;
    int width = m_scrollbarVert ? m_scrollbarVert->GetSize().x : 0;

    wxRect rectBar;
    if ( m_scrollbarVert )
    {
        rectBar.x = size.x - width;
        if ( inside )
           rectBar.x -= rectBorder.width;
        rectBar.width = width;
        rectBar.y = 0;
        if ( inside )
            rectBar.y += rectBorder.y;
        rectBar.height = size.y - height;
        if ( inside )
            rectBar.height -= rectBorder.y + rectBorder.height;

        m_scrollbarVert->SetSize(rectBar, wxSIZE_NO_ADJUSTMENTS);
    }

    if ( m_scrollbarHorz )
    {
        rectBar.y = size.y - height;
        if ( inside )
            rectBar.y -= rectBorder.height;
        rectBar.height = height;
        rectBar.x = 0;
        if ( inside )
            rectBar.x += rectBorder.x;
        rectBar.width = size.x - width;
        if ( inside )
            rectBar.width -= rectBorder.x + rectBorder.width;

        m_scrollbarHorz->SetSize(rectBar, wxSIZE_NO_ADJUSTMENTS);
    }

    RefreshScrollbars();
#endif // wxUSE_SCROLLBAR
}

void wxWindow::SetScrollbar(int orient,
                            int pos,
                            int pageSize,
                            int range,
                            bool refresh)
{
#if wxUSE_SCROLLBAR
    wxASSERT_MSG( (range == -1 || pageSize <= range),
                    wxT("page size can't be greater than range") );

    bool hasClientSizeChanged = false;
    wxScrollBar *scrollbar = GetScrollbar(orient);
    if ( range && (pageSize < range) )
    {
        if ( !scrollbar )
        {
            // create it
#if wxUSE_TWO_WINDOWS
            SetInsertIntoMain( true );
#endif
            scrollbar = new wxWindowScrollBar(this, wxID_ANY,
                                              wxDefaultPosition, wxDefaultSize,
                                              orient & wxVERTICAL ? wxSB_VERTICAL
                                                                  : wxSB_HORIZONTAL);
#if wxUSE_TWO_WINDOWS
            SetInsertIntoMain( false );
#endif
            if ( orient & wxVERTICAL )
                m_scrollbarVert = scrollbar;
            else
                m_scrollbarHorz = scrollbar;

            // the client area diminished as we created a scrollbar
            hasClientSizeChanged = true;

            PositionScrollbars();
        }
        else if ( GetWindowStyle() & wxALWAYS_SHOW_SB )
        {
            // we might have disabled it before
            scrollbar->Enable();
        }

        scrollbar->SetScrollbar(pos, pageSize, range, pageSize, refresh);
    }
    else // no range means no scrollbar
    {
        if ( scrollbar )
        {
            // wxALWAYS_SHOW_SB only applies to the vertical scrollbar
            if ( range == -1 || ((orient & wxVERTICAL) && (GetWindowStyle() & wxALWAYS_SHOW_SB)) )
            {
                // just disable the scrollbar
                scrollbar->SetScrollbar(pos, pageSize, range == -1 ? 0 : range, pageSize, refresh);
                scrollbar->Disable();
            }
            else // really remove the scrollbar
            {
                delete scrollbar;

                if ( orient & wxVERTICAL )
                    m_scrollbarVert = nullptr;
                else
                    m_scrollbarHorz = nullptr;

                // the client area increased as we removed a scrollbar
                hasClientSizeChanged = true;

                // the size of the remaining scrollbar must be adjusted
                if ( m_scrollbarHorz || m_scrollbarVert )
                {
                    PositionScrollbars();
                }
            }
        }
    }

    // give the window a chance to relayout
    if ( hasClientSizeChanged )
    {
#if wxUSE_TWO_WINDOWS
        wxWindowNative::SetSize( GetSize() );
#else
        wxSizeEvent event(GetSize());
        (void)GetEventHandler()->ProcessEvent(event);
#endif
    }
#else
    wxUnusedVar(orient);
    wxUnusedVar(pos);
    wxUnusedVar(pageSize);
    wxUnusedVar(range);
    wxUnusedVar(refresh);
#endif // wxUSE_SCROLLBAR
}

void wxWindow::SetScrollPos(int orient, int pos, bool WXUNUSED(refresh))
{
#if wxUSE_SCROLLBAR
    wxScrollBar *scrollbar = GetScrollbar(orient);

    if (scrollbar)
        scrollbar->SetThumbPosition(pos);

    // VZ: I think we can safely ignore this as we always refresh it
    //     automatically whenever the value chanegs
#if 0
    if ( refresh )
        Refresh();
#endif
#else
    wxUnusedVar(orient);
    wxUnusedVar(pos);
#endif // wxUSE_SCROLLBAR
}

int wxWindow::GetScrollPos(int orient) const
{
#if wxUSE_SCROLLBAR
    wxScrollBar *scrollbar = GetScrollbar(orient);
    return scrollbar ? scrollbar->GetThumbPosition() : 0;
#else
    wxUnusedVar(orient);
    return 0;
#endif // wxUSE_SCROLLBAR
}

int wxWindow::GetScrollThumb(int orient) const
{
#if wxUSE_SCROLLBAR
    wxScrollBar *scrollbar = GetScrollbar(orient);
    return scrollbar ? scrollbar->GetThumbSize() : 0;
#else
    wxUnusedVar(orient);
    return 0;
#endif // wxUSE_SCROLLBAR
}

int wxWindow::GetScrollRange(int orient) const
{
#if wxUSE_SCROLLBAR
    wxScrollBar *scrollbar = GetScrollbar(orient);
    return scrollbar ? scrollbar->GetRange() : 0;
#else
    wxUnusedVar(orient);
    return 0;
#endif // wxUSE_SCROLLBAR
}

void wxWindow::ScrollWindow(int dx, int dy, const wxRect *rect)
{
    // use native scrolling when available and do it in generic way
    // otherwise:
#ifdef __WXX11__

    wxWindowNative::ScrollWindow(dx, dy, rect);

#else // !wxX11

    // before scrolling it, ensure that we don't have any unpainted areas
    Update();

    wxRect r;

    if ( dx )
    {
        r = ScrollNoRefresh(dx, 0, rect);
        Refresh(true /* erase bkgnd */, &r);
    }

    if ( dy )
    {
        r = ScrollNoRefresh(0, dy, rect);
        Refresh(true /* erase bkgnd */, &r);
    }

    // scroll children accordingly:
    wxPoint offset(dx, dy);

    for (wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
         node; node = node->GetNext())
    {
        wxWindow *child = node->GetData();
#if wxUSE_SCROLLBAR
        if ( child == m_scrollbarVert || child == m_scrollbarHorz )
            continue;
#endif // wxUSE_SCROLLBAR

        // VS: Scrolling children has non-trivial semantics. If rect=nullptr then
        //     it is easy: we scroll all children. Otherwise it gets
        //     complicated:
        //       1. if scrolling in one direction only, scroll only
        //          those children that intersect shaft defined by the rectangle
        //          and scrolling direction
        //       2. if scrolling in both axes, scroll all children

        bool shouldMove = false;

        if ( rect && (dx * dy == 0 /* moving in only one of x, y axis */) )
        {
            wxRect childRect = child->GetRect();
            if ( dx == 0 && (childRect.GetLeft() <= rect->GetRight() ||
                             childRect.GetRight() >= rect->GetLeft()) )
            {
                shouldMove = true;
            }
            else if ( dy == 0 && (childRect.GetTop() <= rect->GetBottom() ||
                                  childRect.GetBottom() >= rect->GetTop()) )
            {
                shouldMove = true;
            }
            // else: child outside of scrolling shaft, don't move
        }
        else // scrolling in both axes or rect=nullptr
        {
            shouldMove = true;
        }

        if ( shouldMove )
            child->Move(child->GetPosition() + offset, wxSIZE_ALLOW_MINUS_ONE);
    }
#endif // wxX11/!wxX11
}

wxRect wxWindow::ScrollNoRefresh(int dx, int dy, const wxRect *rectTotal)
{
    wxASSERT_MSG( !dx || !dy, wxT("can't be used for diag scrolling") );

    // the rect to refresh (which we will calculate)
    wxRect rect;

    if ( !dx && !dy )
    {
        // nothing to do
        return rect;
    }

    // calculate the part of the window which we can just redraw in the new
    // location
    wxSize sizeTotal = rectTotal ? rectTotal->GetSize() : GetClientSize();

    wxLogTrace(wxT("scroll"), wxT("rect is %dx%d, scroll by %d, %d"),
               sizeTotal.x, sizeTotal.y, dx, dy);

    // the initial and end point of the region we move in client coords
    wxPoint ptSource, ptDest;
    if ( rectTotal )
    {
        ptSource = rectTotal->GetPosition();
        ptDest = rectTotal->GetPosition();
    }

    // the size of this region
    wxSize size;
    size.x = sizeTotal.x - abs(dx);
    size.y = sizeTotal.y - abs(dy);
    if ( size.x <= 0 || size.y <= 0 )
    {
        // just redraw everything as nothing of the displayed image will stay
        wxLogTrace(wxT("scroll"), wxT("refreshing everything"));

        rect = rectTotal ? *rectTotal : wxRect(0, 0, sizeTotal.x, sizeTotal.y);
    }
    else // move the part which doesn't change to the new location
    {
        // note that when we scroll the canvas in some direction we move the
        // block which doesn't need to be refreshed in the opposite direction

        if ( dx < 0 )
        {
            // scroll to the right, move to the left
            ptSource.x -= dx;
        }
        else
        {
            // scroll to the left, move to the right
            ptDest.x += dx;
        }

        if ( dy < 0 )
        {
            // scroll down, move up
            ptSource.y -= dy;
        }
        else
        {
            // scroll up, move down
            ptDest.y += dy;
        }

#if wxUSE_CARET
        // we need to hide the caret before moving or it will erase itself at
        // the wrong (old) location
        wxCaret *caret = GetCaret();
        if ( caret )
            caret->Hide();
#endif // wxUSE_CARET

        // do move
        wxClientDC dc(this);
        wxBitmap bmp(size.x, size.y);
        wxMemoryDC dcMem;
        dcMem.SelectObject(bmp);

        dcMem.Blit(wxPoint(0,0), size, &dc, ptSource
#if defined(__WXGTK__) && !defined(wxHAS_WORKING_GTK_DC_BLIT)
                + GetClientAreaOrigin()
#endif // broken wxGTK wxDC::Blit
                  );
        dc.Blit(ptDest, size, &dcMem, wxPoint(0,0));

        wxLogTrace(wxT("scroll"),
                   wxT("Blit: (%d, %d) of size %dx%d -> (%d, %d)"),
                   ptSource.x, ptSource.y,
                   size.x, size.y,
                   ptDest.x, ptDest.y);

        // and now repaint the uncovered area

        // FIXME: We repaint the intersection of these rectangles twice - is
        //        it bad? I don't think so as it is rare to scroll the window
        //        diagonally anyhow and so adding extra logic to compute
        //        rectangle intersection is probably not worth the effort

        rect.x = ptSource.x;
        rect.y = ptSource.y;

        if ( dx )
        {
            if ( dx < 0 )
            {
                // refresh the area along the right border
                rect.x += size.x + dx;
                rect.width = -dx;
            }
            else
            {
                // refresh the area along the left border
                rect.width = dx;
            }

            rect.height = sizeTotal.y;

            wxLogTrace(wxT("scroll"), wxT("refreshing (%d, %d)-(%d, %d)"),
                       rect.x, rect.y,
                       rect.GetRight() + 1, rect.GetBottom() + 1);
        }

        if ( dy )
        {
            if ( dy < 0 )
            {
                // refresh the area along the bottom border
                rect.y += size.y + dy;
                rect.height = -dy;
            }
            else
            {
                // refresh the area along the top border
                rect.height = dy;
            }

            rect.width = sizeTotal.x;

            wxLogTrace(wxT("scroll"), wxT("refreshing (%d, %d)-(%d, %d)"),
                       rect.x, rect.y,
                       rect.GetRight() + 1, rect.GetBottom() + 1);
        }

#if wxUSE_CARET
        if ( caret )
            caret->Show();
#endif // wxUSE_CARET
    }

    return rect;
}

// ----------------------------------------------------------------------------
// accelerators and menu hot keys
// ----------------------------------------------------------------------------

#if wxUSE_MENUS
    // the last window over which Alt was pressed (used by OnKeyUp)
    wxWindow *wxWindow::ms_winLastAltPress = nullptr;
#endif // wxUSE_MENUS

#if wxUSE_ACCEL || wxUSE_MENUS

void wxWindow::OnKeyDown(wxKeyEvent& event)
{
#if wxUSE_MENUS
    int key = event.GetKeyCode();
    if ( !event.ControlDown() && (key == WXK_ALT || key == WXK_F10) )
    {
        ms_winLastAltPress = this;

        // it can't be an accel anyhow
        return;
    }

    ms_winLastAltPress = nullptr;
#endif // wxUSE_MENUS

#if wxUSE_ACCEL
    for ( wxWindow *win = this; win; win = win->GetParent() )
    {
        int command = win->GetAcceleratorTable()->GetCommand(event);
        if ( command != -1 )
        {
            wxCommandEvent eventCmd(wxEVT_MENU, command);
            if ( win->GetEventHandler()->ProcessEvent(eventCmd) )
            {
                // skip "event.Skip()" below
                return;
            }
        }

        if ( win->IsTopLevel() )
        {
            // try the frame menu bar
#if wxUSE_MENUS
            wxFrame *frame = wxDynamicCast(win, wxFrame);
            if ( frame )
            {
                wxMenuBar *menubar = frame->GetMenuBar();
                if ( menubar && menubar->ProcessAccelEvent(event) )
                {
                    // skip "event.Skip()" below
                    return;
                }
            }
#endif // wxUSE_MENUS

#if wxUSE_BUTTON
            // if it wasn't in a menu, try to find a button
            if ( command != -1 )
            {
                wxWindow* child = win->FindWindow(command);
                if ( child && wxDynamicCast(child, wxButton) )
                {
                    wxCommandEvent eventCmd(wxEVT_BUTTON, command);
                    eventCmd.SetEventObject(child);
                    if ( child->GetEventHandler()->ProcessEvent(eventCmd) )
                    {
                        // skip "event.Skip()" below
                        return;
                    }
                }
            }
#endif // wxUSE_BUTTON

            // don't propagate accels from the child frame to the parent one
            break;
        }
    }
#endif // wxUSE_ACCEL

    event.Skip();
}

#endif // wxUSE_ACCEL

#if wxUSE_MENUS

wxMenuBar *wxWindow::GetParentFrameMenuBar() const
{
    for ( const wxWindow *win = this; win; win = win->GetParent() )
    {
        if ( win->IsTopLevel() )
        {
            wxFrame *frame = wxDynamicCast(win, wxFrame);
            if ( frame )
            {
                return frame->GetMenuBar();
            }

            // don't look further - we don't want to return the menubar of the
            // parent frame
            break;
        }
    }

    return nullptr;
}

void wxWindow::OnChar(wxKeyEvent& event)
{
    if ( event.AltDown() && !event.ControlDown() )
    {
        int key = event.GetKeyCode();

        wxMenuBar *menubar = GetParentFrameMenuBar();
        if ( menubar )
        {
            int item = menubar->FindNextItemForAccel(-1, key);
            if ( item != -1 )
            {
                menubar->PopupMenu((size_t)item);

                // skip "event.Skip()" below
                return;
            }
        }
    }

    // if Return was pressed, see if there's a default button to activate
    if ( !event.HasModifiers() && event.GetKeyCode() == WXK_RETURN )
    {
        wxTopLevelWindow *
            tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
        if ( tlw )
        {
            wxButton *btn = wxDynamicCast(tlw->GetDefaultItem(), wxButton);
            if ( btn )
            {
                wxCommandEvent evt(wxEVT_BUTTON, btn->GetId());
                evt.SetEventObject(btn);
                btn->Command(evt);
                return;
            }
        }
    }


    event.Skip();
}

void wxWindow::OnKeyUp(wxKeyEvent& event)
{
    int key = event.GetKeyCode();
    if ( !event.HasModifiers() && (key == WXK_ALT || key == WXK_F10) )
    {
        // only process Alt release specially if there were no other key
        // presses since Alt had been pressed and if both events happened in
        // the same window
        if ( ms_winLastAltPress == this )
        {
            wxMenuBar *menubar = GetParentFrameMenuBar();
            if ( menubar && this != menubar )
            {
                menubar->SelectMenu(0);
            }
        }
    }
    else
    {
        event.Skip();
    }

    // in any case reset it
    ms_winLastAltPress = nullptr;
}

#endif // wxUSE_MENUS

// ----------------------------------------------------------------------------
// MSW-specific section
// ----------------------------------------------------------------------------

#ifdef __WXMSW__

#include "wx/msw/private.h"

WXLRESULT wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
{
    if ( message == WM_NCHITTEST )
    {
        // the windows which contain the other windows should let the mouse
        // events through, otherwise a window inside a static box would
        // never get any events at all
        if ( IsStaticBox() )
        {
            return HTTRANSPARENT;
        }
    }

    return wxWindowNative::MSWWindowProc(message, wParam, lParam);
}

#endif // __WXMSW__
