1///////////////////////////////////////////////////////////////////////////////
2// Name:        src/univ/window.cpp
3// Purpose:     implementation of extra wxWindow methods for wxUniv port
4// Author:      Vadim Zeitlin
5// Modified by:
6// Created:     06.08.00
7// RCS-ID:      $Id: winuniv.cpp 46437 2007-06-13 04:35:23Z SC $
8// Copyright:   (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
9// Licence:     wxWindows licence
10///////////////////////////////////////////////////////////////////////////////
11
12// ===========================================================================
13// declarations
14// ===========================================================================
15
16// ---------------------------------------------------------------------------
17// headers
18// ---------------------------------------------------------------------------
19
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24    #pragma hdrstop
25#endif
26
27#include "wx/window.h"
28
29#ifndef WX_PRECOMP
30    #include "wx/app.h"
31    #include "wx/dcclient.h"
32    #include "wx/dcmemory.h"
33    #include "wx/event.h"
34    #include "wx/scrolbar.h"
35    #include "wx/menu.h"
36    #include "wx/frame.h"
37    #include "wx/log.h"
38#endif // WX_PRECOMP
39
40#include "wx/univ/colschem.h"
41#include "wx/univ/renderer.h"
42#include "wx/univ/theme.h"
43
44#if wxUSE_CARET
45    #include "wx/caret.h"
46#endif // wxUSE_CARET
47
48// turn Refresh() debugging on/off
49#define WXDEBUG_REFRESH
50
51#ifndef __WXDEBUG__
52    #undef WXDEBUG_REFRESH
53#endif
54
55#if defined(WXDEBUG_REFRESH) && defined(__WXMSW__) && !defined(__WXMICROWIN__)
56#include "wx/msw/private.h"
57#endif
58
59// ============================================================================
60// implementation
61// ============================================================================
62
63// ----------------------------------------------------------------------------
64// event tables
65// ----------------------------------------------------------------------------
66
67// we can't use wxWindowNative here as it won't be expanded inside the macro
68#if defined(__WXMSW__)
69    IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowMSW)
70#elif defined(__WXGTK__)
71    IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowGTK)
72#elif defined(__WXMGL__)
73    IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowMGL)
74#elif defined(__WXDFB__)
75    IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowDFB)
76#elif defined(__WXX11__)
77    IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowX11)
78#elif defined(__WXPM__)
79    IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowOS2)
80#elif defined(__WXMAC__)
81    IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowMac)
82#endif
83
84BEGIN_EVENT_TABLE(wxWindow, wxWindowNative)
85    EVT_SIZE(wxWindow::OnSize)
86
87#if wxUSE_ACCEL || wxUSE_MENUS
88    EVT_KEY_DOWN(wxWindow::OnKeyDown)
89#endif // wxUSE_ACCEL
90
91#if wxUSE_MENUS
92    EVT_CHAR(wxWindow::OnChar)
93    EVT_KEY_UP(wxWindow::OnKeyUp)
94#endif // wxUSE_MENUS
95
96    EVT_PAINT(wxWindow::OnPaint)
97    EVT_NC_PAINT(wxWindow::OnNcPaint)
98    EVT_ERASE_BACKGROUND(wxWindow::OnErase)
99END_EVENT_TABLE()
100
101// ----------------------------------------------------------------------------
102// creation
103// ----------------------------------------------------------------------------
104
105void wxWindow::Init()
106{
107#if wxUSE_SCROLLBAR
108    m_scrollbarVert =
109    m_scrollbarHorz = (wxScrollBar *)NULL;
110#endif // wxUSE_SCROLLBAR
111
112    m_isCurrent = false;
113
114    m_renderer = wxTheme::Get()->GetRenderer();
115
116    m_oldSize.x = wxDefaultCoord;
117    m_oldSize.y = wxDefaultCoord;
118}
119
120bool wxWindow::Create(wxWindow *parent,
121                      wxWindowID id,
122                      const wxPoint& pos,
123                      const wxSize& size,
124                      long style,
125                      const wxString& name)
126{
127    long actualStyle = style;
128
129    // we add wxCLIP_CHILDREN to get the same ("natural") behaviour under MSW
130    // as under the other platforms
131    actualStyle |= wxCLIP_CHILDREN;
132
133    actualStyle &= ~wxVSCROLL;
134    actualStyle &= ~wxHSCROLL;
135
136#ifdef __WXMSW__
137    // without this, borders (non-client areas in general) are not repainted
138    // correctly when resizing; apparently, native NC areas are fully repainted
139    // even without this style by MSW, but wxUniv implements client area
140    // itself, so it doesn't work correctly for us
141    //
142    // FIXME: this is very expensive, we need to fix the (commented-out) code
143    //        in OnSize() instead
144    actualStyle |= wxFULL_REPAINT_ON_RESIZE;
145#endif
146
147    if ( !wxWindowNative::Create(parent, id, pos, size, actualStyle, name) )
148        return false;
149
150    // Set full style again, including those we didn't want present
151    // when calling the base window Create().
152    wxWindowBase::SetWindowStyleFlag(style);
153
154    // if we allow or should always have a vertical scrollbar, make it
155    if ( style & wxVSCROLL || style & wxALWAYS_SHOW_SB )
156    {
157#if wxUSE_TWO_WINDOWS
158        SetInsertIntoMain( true );
159#endif
160#if wxUSE_SCROLLBAR
161        m_scrollbarVert = new wxScrollBar(this, wxID_ANY,
162                                          wxDefaultPosition, wxDefaultSize,
163                                          wxSB_VERTICAL);
164#endif // wxUSE_SCROLLBAR
165#if wxUSE_TWO_WINDOWS
166        SetInsertIntoMain( false );
167#endif
168    }
169
170    // if we should allow a horizontal scrollbar, make it
171    if ( style & wxHSCROLL )
172    {
173#if wxUSE_TWO_WINDOWS
174        SetInsertIntoMain( true );
175#endif
176#if wxUSE_SCROLLBAR
177        m_scrollbarHorz = new wxScrollBar(this, wxID_ANY,
178                                          wxDefaultPosition, wxDefaultSize,
179                                          wxSB_HORIZONTAL);
180#endif // wxUSE_SCROLLBAR
181#if wxUSE_TWO_WINDOWS
182        SetInsertIntoMain( false );
183#endif
184    }
185
186#if wxUSE_SCROLLBAR
187    if (m_scrollbarHorz || m_scrollbarVert)
188    {
189        // position it/them
190        PositionScrollbars();
191    }
192#endif // wxUSE_SCROLLBAR
193
194    return true;
195}
196
197wxWindow::~wxWindow()
198{
199    m_isBeingDeleted = true;
200
201#if wxUSE_SCROLLBAR
202    // clear pointers to scrollbar before deleting the children: they are
203    // children and so will be deleted by DestroyChildren() call below and if
204    // any code using the scrollbars would be called in the process or from
205    // ~wxWindowBase, the app would crash:
206    m_scrollbarVert = m_scrollbarHorz = NULL;
207#endif
208
209    // we have to destroy our children before we're destroyed because our
210    // children suppose that we're of type wxWindow, not just wxWindowNative,
211    // and so bad things may happen if they're deleted from the base class dtor
212    // as by then we're not a wxWindow any longer and wxUniv-specific virtual
213    // functions can't be called
214    DestroyChildren();
215}
216
217// ----------------------------------------------------------------------------
218// background pixmap
219// ----------------------------------------------------------------------------
220
221void wxWindow::SetBackground(const wxBitmap& bitmap,
222                              int alignment,
223                              wxStretch stretch)
224{
225    m_bitmapBg = bitmap;
226    m_alignBgBitmap = alignment;
227    m_stretchBgBitmap = stretch;
228}
229
230const wxBitmap& wxWindow::GetBackgroundBitmap(int *alignment,
231                                               wxStretch *stretch) const
232{
233    if ( m_bitmapBg.Ok() )
234    {
235        if ( alignment )
236            *alignment = m_alignBgBitmap;
237        if ( stretch )
238            *stretch = m_stretchBgBitmap;
239    }
240
241    return m_bitmapBg;
242}
243
244// ----------------------------------------------------------------------------
245// painting
246// ----------------------------------------------------------------------------
247
248// the event handlers executed when the window must be repainted
249void wxWindow::OnNcPaint(wxNcPaintEvent& WXUNUSED(event))
250{
251    if ( m_renderer )
252    {
253        // get the window rect
254        wxRect rect(GetSize());
255
256#if wxUSE_SCROLLBAR
257        // if the scrollbars are outside the border, we must adjust the rect to
258        // exclude them
259        if ( !m_renderer->AreScrollbarsInsideBorder() )
260        {
261            wxScrollBar *scrollbar = GetScrollbar(wxVERTICAL);
262            if ( scrollbar )
263                rect.width -= scrollbar->GetSize().x;
264
265            scrollbar = GetScrollbar(wxHORIZONTAL);
266            if ( scrollbar )
267                rect.height -= scrollbar->GetSize().y;
268        }
269#endif // wxUSE_SCROLLBAR
270
271        // get the DC and draw the border on it
272        wxWindowDC dc(this);
273        DoDrawBorder(dc, rect);
274    }
275}
276
277void wxWindow::OnPaint(wxPaintEvent& event)
278{
279    if ( !m_renderer )
280    {
281        // it is a native control which paints itself
282        event.Skip();
283    }
284    else
285    {
286        // get the DC to use and create renderer on it
287        wxPaintDC dc(this);
288        wxControlRenderer renderer(this, dc, m_renderer);
289
290        // draw the control
291        DoDraw(&renderer);
292    }
293}
294
295// the event handler executed when the window background must be painted
296void wxWindow::OnErase(wxEraseEvent& event)
297{
298    if ( !m_renderer )
299    {
300        event.Skip();
301
302        return;
303    }
304
305    DoDrawBackground(*event.GetDC());
306
307#if wxUSE_SCROLLBAR
308    // if we have both scrollbars, we also have a square in the corner between
309    // them which we must paint
310    if ( m_scrollbarVert && m_scrollbarHorz )
311    {
312        wxSize size = GetSize();
313        wxRect rectClient = GetClientRect(),
314               rectBorder = m_renderer->GetBorderDimensions(GetBorder());
315
316        wxRect rectCorner;
317        rectCorner.x = rectClient.GetRight() + 1;
318        rectCorner.y = rectClient.GetBottom() + 1;
319        rectCorner.SetRight(size.x - rectBorder.width);
320        rectCorner.SetBottom(size.y - rectBorder.height);
321
322        if ( GetUpdateRegion().Contains(rectCorner) )
323        {
324            m_renderer->DrawScrollCorner(*event.GetDC(), rectCorner);
325        }
326    }
327#endif // wxUSE_SCROLLBAR
328}
329
330bool wxWindow::DoDrawBackground(wxDC& dc)
331{
332    wxRect rect;
333
334    wxSize size = GetSize();  // Why not GetClientSize() ?
335    rect.x = 0;
336    rect.y = 0;
337    rect.width = size.x;
338    rect.height = size.y;
339
340    wxWindow * const parent = GetParent();
341    if ( HasTransparentBackground() && !UseBgCol() && parent )
342    {
343        wxASSERT( !IsTopLevel() );
344
345        wxPoint pos = GetPosition();
346
347        AdjustForParentClientOrigin( pos.x, pos.y, 0 );
348
349        // Adjust DC logical origin
350        wxCoord org_x, org_y, x, y;
351        dc.GetLogicalOrigin( &org_x, &org_y );
352        x = org_x + pos.x;
353        y = org_y + pos.y;
354        dc.SetLogicalOrigin( x, y );
355
356        // Adjust draw rect
357        rect.x = pos.x;
358        rect.y = pos.y;
359
360        // Let parent draw the background
361        parent->EraseBackground( dc, rect );
362
363        // Restore DC logical origin
364        dc.SetLogicalOrigin( org_x, org_y );
365    }
366    else
367    {
368        // Draw background ourselves
369        EraseBackground( dc, rect );
370    }
371
372    return true;
373}
374
375void wxWindow::EraseBackground(wxDC& dc, const wxRect& rect)
376{
377    if ( GetBackgroundBitmap().Ok() )
378    {
379        // Get the bitmap and the flags
380        int alignment;
381        wxStretch stretch;
382        wxBitmap bmp = GetBackgroundBitmap(&alignment, &stretch);
383        wxControlRenderer::DrawBitmap(dc, bmp, rect, alignment, stretch);
384    }
385    else
386    {
387        // Just fill it with bg colour if no bitmap
388
389        m_renderer->DrawBackground(dc, wxTHEME_BG_COLOUR(this),
390                                   rect, GetStateFlags());
391    }
392}
393
394void wxWindow::DoDrawBorder(wxDC& dc, const wxRect& rect)
395{
396    // draw outline unless the update region is enitrely inside it in which
397    // case we don't need to do it
398#if 0 // doesn't seem to work, why?
399    if ( wxRegion(rect).Contains(GetUpdateRegion().GetBox()) != wxInRegion )
400#endif
401    {
402        m_renderer->DrawBorder(dc, GetBorder(), rect, GetStateFlags());
403    }
404}
405
406void wxWindow::DoDraw(wxControlRenderer * WXUNUSED(renderer))
407{
408}
409
410void wxWindow::Refresh(bool eraseBackground, const wxRect *rect)
411{
412    wxRect rectClient; // the same rectangle in client coordinates
413    wxPoint origin = GetClientAreaOrigin();
414
415    wxSize size = GetClientSize();
416
417    if ( rect )
418    {
419        // the rectangle passed as argument is in client coordinates
420        rectClient = *rect;
421
422        // don't refresh anything beyond the client area (scrollbars for
423        // example)
424        if ( rectClient.GetRight() > size.x )
425            rectClient.SetRight(size.x);
426        if ( rectClient.GetBottom() > size.y )
427            rectClient.SetBottom(size.y);
428
429    }
430    else // refresh the entire client area
431    {
432        // x,y is already set to 0 by default
433        rectClient.SetSize(size);
434    }
435
436    // convert refresh rectangle to window coordinates:
437    wxRect rectWin(rectClient);
438    rectWin.Offset(origin);
439
440    // debugging helper
441#ifdef WXDEBUG_REFRESH
442    static bool s_refreshDebug = false;
443    if ( s_refreshDebug )
444    {
445        wxWindowDC dc(this);
446        dc.SetBrush(*wxCYAN_BRUSH);
447        dc.SetPen(*wxTRANSPARENT_PEN);
448        dc.DrawRectangle(rectWin);
449
450        // under Unix we use "--sync" X option for this
451        #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
452            ::GdiFlush();
453            ::Sleep(200);
454        #endif // __WXMSW__
455    }
456#endif // WXDEBUG_REFRESH
457
458    wxWindowNative::Refresh(eraseBackground, &rectWin);
459
460    // Refresh all sub controls if any.
461    wxWindowList& children = GetChildren();
462    for ( wxWindowList::iterator i = children.begin(); i != children.end(); ++i )
463    {
464        wxWindow *child = *i;
465        // only refresh subcontrols if they are visible:
466        if ( child->IsTopLevel() || !child->IsShown() || child->IsFrozen() )
467            continue;
468
469        // ...and when the subcontrols are in the update region:
470        wxRect childrect(child->GetRect());
471        childrect.Intersect(rectClient);
472        if ( childrect.IsEmpty() )
473            continue;
474
475        // refresh the subcontrol now:
476        childrect.Offset(-child->GetPosition());
477        // NB: We must call wxWindowNative version because we need to refresh
478        //     the entire control, not just its client area, and this is why we
479        //     don't account for child client area origin here neither. Also
480        //     note that we don't pass eraseBackground to the child, but use
481        //     true instead: this is because we can't be sure that
482        //     eraseBackground=false is safe for children as well and not only
483        //     for the parent.
484        child->wxWindowNative::Refresh(eraseBackground, &childrect);
485    }
486}
487
488// ----------------------------------------------------------------------------
489// state flags
490// ----------------------------------------------------------------------------
491
492bool wxWindow::Enable(bool enable)
493{
494    if ( !wxWindowNative::Enable(enable) )
495        return false;
496
497    // disabled window can't keep focus
498    if ( FindFocus() == this && GetParent() != NULL )
499    {
500        GetParent()->SetFocus();
501    }
502
503    if ( m_renderer )
504    {
505        // a window with renderer is drawn by ourselves and it has to be
506        // refreshed to reflect its new status
507        Refresh();
508    }
509
510    return true;
511}
512
513bool wxWindow::IsFocused() const
514{
515    return FindFocus() == this;
516}
517
518bool wxWindow::IsPressed() const
519{
520    return false;
521}
522
523bool wxWindow::IsDefault() const
524{
525    return false;
526}
527
528bool wxWindow::IsCurrent() const
529{
530    return m_isCurrent;
531}
532
533bool wxWindow::SetCurrent(bool doit)
534{
535    if ( doit == m_isCurrent )
536        return false;
537
538    m_isCurrent = doit;
539
540    if ( CanBeHighlighted() )
541        Refresh();
542
543    return true;
544}
545
546int wxWindow::GetStateFlags() const
547{
548    int flags = 0;
549    if ( !IsEnabled() )
550        flags |= wxCONTROL_DISABLED;
551
552    // the following states are only possible if our application is active - if
553    // it is not, even our default/focused controls shouldn't appear as such
554    if ( wxTheApp->IsActive() )
555    {
556        if ( IsCurrent() )
557            flags |= wxCONTROL_CURRENT;
558        if ( IsFocused() )
559            flags |= wxCONTROL_FOCUSED;
560        if ( IsPressed() )
561            flags |= wxCONTROL_PRESSED;
562        if ( IsDefault() )
563            flags |= wxCONTROL_ISDEFAULT;
564    }
565
566    return flags;
567}
568
569// ----------------------------------------------------------------------------
570// size
571// ----------------------------------------------------------------------------
572
573void wxWindow::OnSize(wxSizeEvent& event)
574{
575    event.Skip();
576
577#if wxUSE_SCROLLBAR
578    if ( m_scrollbarVert || m_scrollbarHorz )
579    {
580        PositionScrollbars();
581    }
582#endif // wxUSE_SCROLLBAR
583
584#if 0   // ndef __WXMSW__
585    // Refresh the area (strip) previously occupied by the border
586
587    if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) && IsShown() )
588    {
589        // This code assumes that wxSizeEvent.GetSize() returns
590        // the area of the entire window, not just the client
591        // area.
592        wxSize newSize = event.GetSize();
593
594        if (m_oldSize.x == wxDefaultCoord && m_oldSize.y == wxDefaultCoord)
595        {
596            m_oldSize = newSize;
597            return;
598        }
599
600        if (HasFlag( wxSIMPLE_BORDER ))
601        {
602            if (newSize.y > m_oldSize.y)
603            {
604                wxRect rect;
605                rect.x = 0;
606                rect.width = m_oldSize.x;
607                rect.y = m_oldSize.y-2;
608                rect.height = 1;
609                Refresh( true, &rect );
610            }
611            else if (newSize.y < m_oldSize.y)
612            {
613                wxRect rect;
614                rect.y = newSize.y;
615                rect.x = 0;
616                rect.height = 1;
617                rect.width = newSize.x;
618                wxWindowNative::Refresh( true, &rect );
619            }
620
621            if (newSize.x > m_oldSize.x)
622            {
623                wxRect rect;
624                rect.y = 0;
625                rect.height = m_oldSize.y;
626                rect.x = m_oldSize.x-2;
627                rect.width = 1;
628                Refresh( true, &rect );
629            }
630            else if (newSize.x < m_oldSize.x)
631            {
632                wxRect rect;
633                rect.x = newSize.x;
634                rect.y = 0;
635                rect.width = 1;
636                rect.height = newSize.y;
637                wxWindowNative::Refresh( true, &rect );
638            }
639        }
640        else
641        if (HasFlag( wxSUNKEN_BORDER ) || HasFlag( wxRAISED_BORDER ))
642        {
643            if (newSize.y > m_oldSize.y)
644            {
645                wxRect rect;
646                rect.x = 0;
647                rect.width = m_oldSize.x;
648                rect.y = m_oldSize.y-4;
649                rect.height = 2;
650                Refresh( true, &rect );
651            }
652            else if (newSize.y < m_oldSize.y)
653            {
654                wxRect rect;
655                rect.y = newSize.y;
656                rect.x = 0;
657                rect.height = 2;
658                rect.width = newSize.x;
659                wxWindowNative::Refresh( true, &rect );
660            }
661
662            if (newSize.x > m_oldSize.x)
663            {
664                wxRect rect;
665                rect.y = 0;
666                rect.height = m_oldSize.y;
667                rect.x = m_oldSize.x-4;
668                rect.width = 2;
669                Refresh( true, &rect );
670            }
671            else if (newSize.x < m_oldSize.x)
672            {
673                wxRect rect;
674                rect.x = newSize.x;
675                rect.y = 0;
676                rect.width = 2;
677                rect.height = newSize.y;
678                wxWindowNative::Refresh( true, &rect );
679            }
680        }
681
682        m_oldSize = newSize;
683    }
684#endif
685}
686
687wxSize wxWindow::DoGetBestSize() const
688{
689    return AdjustSize(DoGetBestClientSize());
690}
691
692wxSize wxWindow::DoGetBestClientSize() const
693{
694    return wxWindowNative::DoGetBestSize();
695}
696
697wxSize wxWindow::AdjustSize(const wxSize& size) const
698{
699    wxSize sz = size;
700    if ( m_renderer )
701        m_renderer->AdjustSize(&sz, this);
702    return sz;
703}
704
705wxPoint wxWindow::GetClientAreaOrigin() const
706{
707    wxPoint pt = wxWindowBase::GetClientAreaOrigin();
708
709#if wxUSE_TWO_WINDOWS
710#else
711    if ( m_renderer )
712        pt += m_renderer->GetBorderDimensions(GetBorder()).GetPosition();
713#endif
714
715    return pt;
716}
717
718void wxWindow::DoGetClientSize(int *width, int *height) const
719{
720    // if it is a native window, we assume it handles the scrollbars itself
721    // too - and if it doesn't, there is not much we can do
722    if ( !m_renderer )
723    {
724        wxWindowNative::DoGetClientSize(width, height);
725
726        return;
727    }
728
729    int w, h;
730    wxWindowNative::DoGetClientSize(&w, &h);
731
732    // we assume that the scrollbars are positioned correctly (by a previous
733    // call to PositionScrollbars()) here
734
735    wxRect rectBorder;
736    if ( m_renderer )
737        rectBorder = m_renderer->GetBorderDimensions(GetBorder());
738
739    if ( width )
740    {
741#if wxUSE_SCROLLBAR
742        // in any case, take account of the scrollbar
743        if ( m_scrollbarVert )
744            w -= m_scrollbarVert->GetSize().x;
745#endif // wxUSE_SCROLLBAR
746
747        // account for the left and right borders
748        *width = w - rectBorder.x - rectBorder.width;
749
750        // we shouldn't return invalid width
751        if ( *width < 0 )
752            *width = 0;
753    }
754
755    if ( height )
756    {
757#if wxUSE_SCROLLBAR
758        if ( m_scrollbarHorz )
759            h -= m_scrollbarHorz->GetSize().y;
760#endif // wxUSE_SCROLLBAR
761
762        *height = h - rectBorder.y - rectBorder.height;
763
764        // we shouldn't return invalid height
765        if ( *height < 0 )
766            *height = 0;
767    }
768}
769
770void wxWindow::DoSetClientSize(int width, int height)
771{
772    // take into account the borders
773    wxRect rectBorder = m_renderer->GetBorderDimensions(GetBorder());
774    width += rectBorder.x;
775    height += rectBorder.y;
776
777    // and the scrollbars (as they may be offset into the border, use the
778    // scrollbar position, not size - this supposes that PositionScrollbars()
779    // had been called before)
780    wxSize size = GetSize();
781#if wxUSE_SCROLLBAR
782    if ( m_scrollbarVert )
783        width += size.x - m_scrollbarVert->GetPosition().x;
784#endif // wxUSE_SCROLLBAR
785    width += rectBorder.width;
786
787#if wxUSE_SCROLLBAR
788    if ( m_scrollbarHorz )
789        height += size.y - m_scrollbarHorz->GetPosition().y;
790#endif // wxUSE_SCROLLBAR
791    height += rectBorder.height;
792
793    wxWindowNative::DoSetClientSize(width, height);
794}
795
796wxHitTest wxWindow::DoHitTest(wxCoord x, wxCoord y) const
797{
798    wxHitTest ht = wxWindowNative::DoHitTest(x, y);
799
800#if wxUSE_SCROLLBAR
801    if ( ht == wxHT_WINDOW_INSIDE )
802    {
803        if ( m_scrollbarVert && x >= m_scrollbarVert->GetPosition().x )
804        {
805            // it can still be changed below because it may also be the corner
806            ht = wxHT_WINDOW_VERT_SCROLLBAR;
807        }
808
809        if ( m_scrollbarHorz && y >= m_scrollbarHorz->GetPosition().y )
810        {
811            ht = ht == wxHT_WINDOW_VERT_SCROLLBAR ? wxHT_WINDOW_CORNER
812                                                  : wxHT_WINDOW_HORZ_SCROLLBAR;
813        }
814    }
815#endif // wxUSE_SCROLLBAR
816
817    return ht;
818}
819
820// ----------------------------------------------------------------------------
821// scrolling: we implement it entirely ourselves except for ScrollWindow()
822// function which is supposed to be (efficiently) implemented by the native
823// window class
824// ----------------------------------------------------------------------------
825
826void wxWindow::RefreshScrollbars()
827{
828#if wxUSE_SCROLLBAR
829    if ( m_scrollbarHorz )
830        m_scrollbarHorz->Refresh();
831
832    if ( m_scrollbarVert )
833        m_scrollbarVert->Refresh();
834#endif // wxUSE_SCROLLBAR
835}
836
837void wxWindow::PositionScrollbars()
838{
839#if wxUSE_SCROLLBAR
840    // do not use GetClientSize/Rect as it relies on the scrollbars being
841    // correctly positioned
842
843    wxSize size = GetSize();
844    wxBorder border = GetBorder();
845    wxRect rectBorder = m_renderer->GetBorderDimensions(border);
846    bool inside = m_renderer->AreScrollbarsInsideBorder();
847
848    int height = m_scrollbarHorz ? m_scrollbarHorz->GetSize().y : 0;
849    int width = m_scrollbarVert ? m_scrollbarVert->GetSize().x : 0;
850
851    wxRect rectBar;
852    if ( m_scrollbarVert )
853    {
854        rectBar.x = size.x - width;
855        if ( inside )
856           rectBar.x -= rectBorder.width;
857        rectBar.width = width;
858        rectBar.y = 0;
859        if ( inside )
860            rectBar.y += rectBorder.y;
861        rectBar.height = size.y - height;
862        if ( inside )
863            rectBar.height -= rectBorder.y + rectBorder.height;
864
865        m_scrollbarVert->SetSize(rectBar, wxSIZE_NO_ADJUSTMENTS);
866    }
867
868    if ( m_scrollbarHorz )
869    {
870        rectBar.y = size.y - height;
871        if ( inside )
872            rectBar.y -= rectBorder.height;
873        rectBar.height = height;
874        rectBar.x = 0;
875        if ( inside )
876            rectBar.x += rectBorder.x;
877        rectBar.width = size.x - width;
878        if ( inside )
879            rectBar.width -= rectBorder.x + rectBorder.width;
880
881        m_scrollbarHorz->SetSize(rectBar, wxSIZE_NO_ADJUSTMENTS);
882    }
883
884    RefreshScrollbars();
885#endif // wxUSE_SCROLLBAR
886}
887
888void wxWindow::SetScrollbar(int orient,
889                            int pos,
890                            int pageSize,
891                            int range,
892                            bool refresh)
893{
894#if wxUSE_SCROLLBAR
895    wxASSERT_MSG( pageSize <= range,
896                    _T("page size can't be greater than range") );
897
898    bool hasClientSizeChanged = false;
899    wxScrollBar *scrollbar = GetScrollbar(orient);
900    if ( range && (pageSize < range) )
901    {
902        if ( !scrollbar )
903        {
904            // create it
905#if wxUSE_TWO_WINDOWS
906            SetInsertIntoMain( true );
907#endif
908            scrollbar = new wxScrollBar(this, wxID_ANY,
909                                        wxDefaultPosition, wxDefaultSize,
910                                        orient & wxVERTICAL ? wxSB_VERTICAL
911                                                            : wxSB_HORIZONTAL);
912#if wxUSE_TWO_WINDOWS
913            SetInsertIntoMain( false );
914#endif
915            if ( orient & wxVERTICAL )
916                m_scrollbarVert = scrollbar;
917            else
918                m_scrollbarHorz = scrollbar;
919
920            // the client area diminished as we created a scrollbar
921            hasClientSizeChanged = true;
922
923            PositionScrollbars();
924        }
925        else if ( GetWindowStyle() & wxALWAYS_SHOW_SB )
926        {
927            // we might have disabled it before
928            scrollbar->Enable();
929        }
930
931        scrollbar->SetScrollbar(pos, pageSize, range, pageSize, refresh);
932    }
933    else // no range means no scrollbar
934    {
935        if ( scrollbar )
936        {
937            // wxALWAYS_SHOW_SB only applies to the vertical scrollbar
938            if ( (orient & wxVERTICAL) && (GetWindowStyle() & wxALWAYS_SHOW_SB) )
939            {
940                // just disable the scrollbar
941                scrollbar->SetScrollbar(pos, pageSize, range, pageSize, refresh);
942                scrollbar->Disable();
943            }
944            else // really remove the scrollbar
945            {
946                delete scrollbar;
947
948                if ( orient & wxVERTICAL )
949                    m_scrollbarVert = NULL;
950                else
951                    m_scrollbarHorz = NULL;
952
953                // the client area increased as we removed a scrollbar
954                hasClientSizeChanged = true;
955
956                // the size of the remaining scrollbar must be adjusted
957                if ( m_scrollbarHorz || m_scrollbarVert )
958                {
959                    PositionScrollbars();
960                }
961            }
962        }
963    }
964
965    // give the window a chance to relayout
966    if ( hasClientSizeChanged )
967    {
968#if wxUSE_TWO_WINDOWS
969        wxWindowNative::SetSize( GetSize() );
970#else
971        wxSizeEvent event(GetSize());
972        (void)GetEventHandler()->ProcessEvent(event);
973#endif
974    }
975#else
976    wxUnusedVar(orient);
977    wxUnusedVar(pos);
978    wxUnusedVar(pageSize);
979    wxUnusedVar(range);
980    wxUnusedVar(refresh);
981#endif // wxUSE_SCROLLBAR
982}
983
984void wxWindow::SetScrollPos(int orient, int pos, bool WXUNUSED(refresh))
985{
986#if wxUSE_SCROLLBAR
987    wxScrollBar *scrollbar = GetScrollbar(orient);
988
989    if (scrollbar)
990        scrollbar->SetThumbPosition(pos);
991
992    // VZ: I think we can safely ignore this as we always refresh it
993    //     automatically whenever the value chanegs
994#if 0
995    if ( refresh )
996        Refresh();
997#endif
998#else
999    wxUnusedVar(orient);
1000    wxUnusedVar(pos);
1001#endif // wxUSE_SCROLLBAR
1002}
1003
1004int wxWindow::GetScrollPos(int orient) const
1005{
1006#if wxUSE_SCROLLBAR
1007    wxScrollBar *scrollbar = GetScrollbar(orient);
1008    return scrollbar ? scrollbar->GetThumbPosition() : 0;
1009#else
1010    wxUnusedVar(orient);
1011    return 0;
1012#endif // wxUSE_SCROLLBAR
1013}
1014
1015int wxWindow::GetScrollThumb(int orient) const
1016{
1017#if wxUSE_SCROLLBAR
1018    wxScrollBar *scrollbar = GetScrollbar(orient);
1019    return scrollbar ? scrollbar->GetThumbSize() : 0;
1020#else
1021    wxUnusedVar(orient);
1022    return 0;
1023#endif // wxUSE_SCROLLBAR
1024}
1025
1026int wxWindow::GetScrollRange(int orient) const
1027{
1028#if wxUSE_SCROLLBAR
1029    wxScrollBar *scrollbar = GetScrollbar(orient);
1030    return scrollbar ? scrollbar->GetRange() : 0;
1031#else
1032    wxUnusedVar(orient);
1033    return 0;
1034#endif // wxUSE_SCROLLBAR
1035}
1036
1037void wxWindow::ScrollWindow(int dx, int dy, const wxRect *rect)
1038{
1039    // use native scrolling when available and do it in generic way
1040    // otherwise:
1041#ifdef __WXX11__
1042
1043    wxWindowNative::ScrollWindow(dx, dy, rect);
1044
1045#else // !wxX11
1046
1047    // before scrolling it, ensure that we don't have any unpainted areas
1048    Update();
1049
1050    wxRect r;
1051
1052    if ( dx )
1053    {
1054        r = ScrollNoRefresh(dx, 0, rect);
1055        Refresh(true /* erase bkgnd */, &r);
1056    }
1057
1058    if ( dy )
1059    {
1060        r = ScrollNoRefresh(0, dy, rect);
1061        Refresh(true /* erase bkgnd */, &r);
1062    }
1063
1064    // scroll children accordingly:
1065    wxPoint offset(dx, dy);
1066
1067    for (wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
1068         node; node = node->GetNext())
1069    {
1070        wxWindow *child = node->GetData();
1071#if wxUSE_SCROLLBAR
1072        if ( child == m_scrollbarVert || child == m_scrollbarHorz )
1073            continue;
1074#endif // wxUSE_SCROLLBAR
1075
1076        // VS: Scrolling children has non-trivial semantics. If rect=NULL then
1077        //     it is easy: we scroll all children. Otherwise it gets
1078        //     complicated:
1079        //       1. if scrolling in one direction only, scroll only
1080        //          those children that intersect shaft defined by the rectangle
1081        //          and scrolling direction
1082        //       2. if scrolling in both axes, scroll all children
1083
1084        bool shouldMove = false;
1085
1086        if ( rect && (dx * dy == 0 /* moving in only one of x, y axis */) )
1087        {
1088            wxRect childRect = child->GetRect();
1089            if ( dx == 0 && (childRect.GetLeft() <= rect->GetRight() ||
1090                             childRect.GetRight() >= rect->GetLeft()) )
1091            {
1092                shouldMove = true;
1093            }
1094            else if ( dy == 0 && (childRect.GetTop() <= rect->GetBottom() ||
1095                                  childRect.GetBottom() >= rect->GetTop()) )
1096            {
1097                shouldMove = true;
1098            }
1099            // else: child outside of scrolling shaft, don't move
1100        }
1101        else // scrolling in both axes or rect=NULL
1102        {
1103            shouldMove = true;
1104        }
1105
1106        if ( shouldMove )
1107            child->Move(child->GetPosition() + offset, wxSIZE_ALLOW_MINUS_ONE);
1108    }
1109#endif // wxX11/!wxX11
1110}
1111
1112wxRect wxWindow::ScrollNoRefresh(int dx, int dy, const wxRect *rectTotal)
1113{
1114    wxASSERT_MSG( !dx || !dy, _T("can't be used for diag scrolling") );
1115
1116    // the rect to refresh (which we will calculate)
1117    wxRect rect;
1118
1119    if ( !dx && !dy )
1120    {
1121        // nothing to do
1122        return rect;
1123    }
1124
1125    // calculate the part of the window which we can just redraw in the new
1126    // location
1127    wxSize sizeTotal = rectTotal ? rectTotal->GetSize() : GetClientSize();
1128
1129    wxLogTrace(_T("scroll"), _T("rect is %dx%d, scroll by %d, %d"),
1130               sizeTotal.x, sizeTotal.y, dx, dy);
1131
1132    // the initial and end point of the region we move in client coords
1133    wxPoint ptSource, ptDest;
1134    if ( rectTotal )
1135    {
1136        ptSource = rectTotal->GetPosition();
1137        ptDest = rectTotal->GetPosition();
1138    }
1139
1140    // the size of this region
1141    wxSize size;
1142    size.x = sizeTotal.x - abs(dx);
1143    size.y = sizeTotal.y - abs(dy);
1144    if ( size.x <= 0 || size.y <= 0 )
1145    {
1146        // just redraw everything as nothing of the displayed image will stay
1147        wxLogTrace(_T("scroll"), _T("refreshing everything"));
1148
1149        rect = rectTotal ? *rectTotal : wxRect(0, 0, sizeTotal.x, sizeTotal.y);
1150    }
1151    else // move the part which doesn't change to the new location
1152    {
1153        // note that when we scroll the canvas in some direction we move the
1154        // block which doesn't need to be refreshed in the opposite direction
1155
1156        if ( dx < 0 )
1157        {
1158            // scroll to the right, move to the left
1159            ptSource.x -= dx;
1160        }
1161        else
1162        {
1163            // scroll to the left, move to the right
1164            ptDest.x += dx;
1165        }
1166
1167        if ( dy < 0 )
1168        {
1169            // scroll down, move up
1170            ptSource.y -= dy;
1171        }
1172        else
1173        {
1174            // scroll up, move down
1175            ptDest.y += dy;
1176        }
1177
1178#if wxUSE_CARET
1179        // we need to hide the caret before moving or it will erase itself at
1180        // the wrong (old) location
1181        wxCaret *caret = GetCaret();
1182        if ( caret )
1183            caret->Hide();
1184#endif // wxUSE_CARET
1185
1186        // do move
1187        wxClientDC dc(this);
1188        wxBitmap bmp(size.x, size.y);
1189        wxMemoryDC dcMem;
1190        dcMem.SelectObject(bmp);
1191
1192        dcMem.Blit(wxPoint(0,0), size, &dc, ptSource
1193#if defined(__WXGTK__) && !defined(wxHAS_WORKING_GTK_DC_BLIT)
1194                + GetClientAreaOrigin()
1195#endif // broken wxGTK wxDC::Blit
1196                  );
1197        dc.Blit(ptDest, size, &dcMem, wxPoint(0,0));
1198
1199        wxLogTrace(_T("scroll"),
1200                   _T("Blit: (%d, %d) of size %dx%d -> (%d, %d)"),
1201                   ptSource.x, ptSource.y,
1202                   size.x, size.y,
1203                   ptDest.x, ptDest.y);
1204
1205        // and now repaint the uncovered area
1206
1207        // FIXME: We repaint the intersection of these rectangles twice - is
1208        //        it bad? I don't think so as it is rare to scroll the window
1209        //        diagonally anyhow and so adding extra logic to compute
1210        //        rectangle intersection is probably not worth the effort
1211
1212        rect.x = ptSource.x;
1213        rect.y = ptSource.y;
1214
1215        if ( dx )
1216        {
1217            if ( dx < 0 )
1218            {
1219                // refresh the area along the right border
1220                rect.x += size.x + dx;
1221                rect.width = -dx;
1222            }
1223            else
1224            {
1225                // refresh the area along the left border
1226                rect.width = dx;
1227            }
1228
1229            rect.height = sizeTotal.y;
1230
1231            wxLogTrace(_T("scroll"), _T("refreshing (%d, %d)-(%d, %d)"),
1232                       rect.x, rect.y,
1233                       rect.GetRight() + 1, rect.GetBottom() + 1);
1234        }
1235
1236        if ( dy )
1237        {
1238            if ( dy < 0 )
1239            {
1240                // refresh the area along the bottom border
1241                rect.y += size.y + dy;
1242                rect.height = -dy;
1243            }
1244            else
1245            {
1246                // refresh the area along the top border
1247                rect.height = dy;
1248            }
1249
1250            rect.width = sizeTotal.x;
1251
1252            wxLogTrace(_T("scroll"), _T("refreshing (%d, %d)-(%d, %d)"),
1253                       rect.x, rect.y,
1254                       rect.GetRight() + 1, rect.GetBottom() + 1);
1255        }
1256
1257#if wxUSE_CARET
1258        if ( caret )
1259            caret->Show();
1260#endif // wxUSE_CARET
1261    }
1262
1263    return rect;
1264}
1265
1266// ----------------------------------------------------------------------------
1267// accelerators and menu hot keys
1268// ----------------------------------------------------------------------------
1269
1270#if wxUSE_MENUS
1271    // the last window over which Alt was pressed (used by OnKeyUp)
1272    wxWindow *wxWindow::ms_winLastAltPress = NULL;
1273#endif // wxUSE_MENUS
1274
1275#if wxUSE_ACCEL || wxUSE_MENUS
1276
1277void wxWindow::OnKeyDown(wxKeyEvent& event)
1278{
1279#if wxUSE_MENUS
1280    int key = event.GetKeyCode();
1281    if ( !event.ControlDown() && (key == WXK_ALT || key == WXK_F10) )
1282    {
1283        ms_winLastAltPress = this;
1284
1285        // it can't be an accel anyhow
1286        return;
1287    }
1288
1289    ms_winLastAltPress = NULL;
1290#endif // wxUSE_MENUS
1291
1292#if wxUSE_ACCEL
1293    for ( wxWindow *win = this; win; win = win->GetParent() )
1294    {
1295        int command = win->GetAcceleratorTable()->GetCommand(event);
1296        if ( command != -1 )
1297        {
1298            wxCommandEvent eventCmd(wxEVT_COMMAND_MENU_SELECTED, command);
1299            if ( win->GetEventHandler()->ProcessEvent(eventCmd) )
1300            {
1301                // skip "event.Skip()" below
1302                return;
1303            }
1304        }
1305
1306        if ( win->IsTopLevel() )
1307        {
1308            // try the frame menu bar
1309#if wxUSE_MENUS
1310            wxFrame *frame = wxDynamicCast(win, wxFrame);
1311            if ( frame )
1312            {
1313                wxMenuBar *menubar = frame->GetMenuBar();
1314                if ( menubar && menubar->ProcessAccelEvent(event) )
1315                {
1316                    // skip "event.Skip()" below
1317                    return;
1318                }
1319            }
1320#endif // wxUSE_MENUS
1321
1322#if wxUSE_BUTTON
1323            // if it wasn't in a menu, try to find a button
1324            if ( command != -1 )
1325            {
1326                wxWindow* child = win->FindWindow(command);
1327                if ( child && wxDynamicCast(child, wxButton) )
1328                {
1329                    wxCommandEvent eventCmd(wxEVT_COMMAND_BUTTON_CLICKED, command);
1330                    eventCmd.SetEventObject(child);
1331                    if ( child->GetEventHandler()->ProcessEvent(eventCmd) )
1332                    {
1333                        // skip "event.Skip()" below
1334                        return;
1335                    }
1336                }
1337            }
1338#endif // wxUSE_BUTTON
1339
1340            // don't propagate accels from the child frame to the parent one
1341            break;
1342        }
1343    }
1344#endif // wxUSE_ACCEL
1345
1346    event.Skip();
1347}
1348
1349#endif // wxUSE_ACCEL
1350
1351#if wxUSE_MENUS
1352
1353wxMenuBar *wxWindow::GetParentFrameMenuBar() const
1354{
1355    for ( const wxWindow *win = this; win; win = win->GetParent() )
1356    {
1357        if ( win->IsTopLevel() )
1358        {
1359            wxFrame *frame = wxDynamicCast(win, wxFrame);
1360            if ( frame )
1361            {
1362                return frame->GetMenuBar();
1363            }
1364
1365            // don't look further - we don't want to return the menubar of the
1366            // parent frame
1367            break;
1368        }
1369    }
1370
1371    return NULL;
1372}
1373
1374void wxWindow::OnChar(wxKeyEvent& event)
1375{
1376    if ( event.AltDown() && !event.ControlDown() )
1377    {
1378        int key = event.GetKeyCode();
1379
1380        wxMenuBar *menubar = GetParentFrameMenuBar();
1381        if ( menubar )
1382        {
1383            int item = menubar->FindNextItemForAccel(-1, key);
1384            if ( item != -1 )
1385            {
1386                menubar->PopupMenu((size_t)item);
1387
1388                // skip "event.Skip()" below
1389                return;
1390            }
1391        }
1392    }
1393
1394    event.Skip();
1395}
1396
1397void wxWindow::OnKeyUp(wxKeyEvent& event)
1398{
1399    int key = event.GetKeyCode();
1400    if ( !event.HasModifiers() && (key == WXK_ALT || key == WXK_F10) )
1401    {
1402        // only process Alt release specially if there were no other key
1403        // presses since Alt had been pressed and if both events happened in
1404        // the same window
1405        if ( ms_winLastAltPress == this )
1406        {
1407            wxMenuBar *menubar = GetParentFrameMenuBar();
1408            if ( menubar && this != menubar )
1409            {
1410                menubar->SelectMenu(0);
1411            }
1412        }
1413    }
1414    else
1415    {
1416        event.Skip();
1417    }
1418
1419    // in any case reset it
1420    ms_winLastAltPress = NULL;
1421}
1422
1423#endif // wxUSE_MENUS
1424
1425// ----------------------------------------------------------------------------
1426// MSW-specific section
1427// ----------------------------------------------------------------------------
1428
1429#ifdef __WXMSW__
1430
1431#include "wx/msw/private.h"
1432
1433WXLRESULT wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
1434{
1435    if ( message == WM_NCHITTEST )
1436    {
1437        // the windows which contain the other windows should let the mouse
1438        // events through, otherwise a window inside a static box would
1439        // never get any events at all
1440        if ( IsStaticBox() )
1441        {
1442            return HTTRANSPARENT;
1443        }
1444    }
1445
1446    return wxWindowNative::MSWWindowProc(message, wParam, lParam);
1447}
1448
1449#endif // __WXMSW__
1450