1/////////////////////////////////////////////////////////////////////////////
2// Name:        dynamicsash.cpp
3// Purpose:     A window which can be dynamically split to an arbitrary depth
4//              and later reunified through the user interface
5// Author:      Matt Kimball
6// Modified by:
7// Created:     7/15/2001
8// RCS-ID:      $Id: dynamicsash.cpp 35650 2005-09-23 12:56:45Z MR $
9// Copyright:   (c) 2001 Matt Kimball
10// Licence:     wxWindows licence
11/////////////////////////////////////////////////////////////////////////////
12
13// For compilers that support precompilation, includes "wx/wx.h".
14#include "wx/wxprec.h"
15
16#ifdef __BORLANDC__
17    #pragma hdrstop
18#endif
19
20// for all others, include the necessary headers (this file is usually all you
21// need because it includes almost all "standard" wxWidgets headers)
22#ifndef WX_PRECOMP
23    #include "wx/wx.h"
24#endif
25
26#ifdef __WXMSW__
27#include "wx/mdi.h"
28#endif
29
30#include "wx/gizmos/dynamicsash.h"
31
32
33const wxChar* wxDynamicSashWindowNameStr = wxT("dynamicSashWindow");
34
35
36/*
37    wxDynamicSashWindow works by internally storing a tree of Implementation
38    objects (wxDynamicSsahWindowImpl) and Leaf objects
39    (wxDynamicSashWindowLeaf).  The wxDynamicSashWindow has a pointer to one
40    implementation, and each implementation either has a pointer to a one
41    leaf (m_leaf) or a pointer to two children implementation objects
42    (m_child).  The leaves each are responsible for drawing the frame and
43    decorations around one user-provided views and for responding to mouse
44    and scrollbar events.
45
46    A resulting tree might look something like this:
47
48    wxDynamicSashWindow
49     |
50     +- wxDynamicSashWindowImpl
51         |
52         +- wxDynamicSashWindowLeaf
53         |   |
54         |   +- user view window
55         |
56         +- wxDynamicSashWindowImpl
57             |
58             +- wxDynamicSashWindowLeaf
59             |   |
60             |   +- user view window
61             |
62             +- wxDynamicSashWindowLeaf
63                 |
64                 +- user view window
65
66    Each time a split occurs, one of the implementation objects removes its
67    leaf, generates two new implementation object children, each with a new
68    leaf, and reparents the user view which was connected to its old leaf
69    to be one of the new leaf's user view, and sends a Split event to the
70    user view in the hopes that it will generate a new user view for the
71    other new leaf.
72
73    When a unification ocurrs, an implementation object is replaced by one
74    of its children, and the tree of its other child is pruned.
75
76    One quirk is that the top-level implementation object (m_top) always
77    keeps a pointer to the implementation object where a new child is needed.
78    (m_add_child_target).  This is so that when a new uesr view is added
79    to the hierarchy, AddChild() is able to reparent the new user view to
80    the correct implementation object's leaf.
81
82*/
83
84#include "wx/dcmemory.h"
85#include "wx/dcscreen.h"
86#include "wx/layout.h"
87#include "wx/scrolbar.h"
88#include "wx/settings.h"
89
90
91const wxEventType wxEVT_DYNAMIC_SASH_SPLIT = wxNewEventType();
92const wxEventType wxEVT_DYNAMIC_SASH_UNIFY = wxNewEventType();
93const wxEventType wxEVT_DYNAMIC_SASH_REPARENT = wxNewEventType();
94
95enum DynamicSashRegion
96{
97    DSR_NONE,
98    DSR_VERTICAL_TAB,
99    DSR_HORIZONTAL_TAB,
100    DSR_CORNER,
101    DSR_LEFT_EDGE,
102    DSR_TOP_EDGE,
103    DSR_RIGHT_EDGE,
104    DSR_BOTTOM_EDGE
105};
106
107
108/*
109    wxDynamicSashReparentEvent is generated by the AddChild() method of
110    wxDynamicSashWindow when it wants a Leaf to reparent a user view window
111    to its viewport at some time in the future.  We can't reparent the window
112    immediately, because switching parents in AddChild() confuses the wxWindow
113    class.  Instead, we queue up this event, and the window is actually
114    reparented the next time we process events in the idle loop.
115*/
116class wxDynamicSashReparentEvent : public wxEvent
117{
118public:
119    wxDynamicSashReparentEvent();
120    wxDynamicSashReparentEvent(wxObject *object);
121    wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent& evt);
122
123    virtual wxEvent* Clone() const { return new wxDynamicSashReparentEvent(*this); }
124
125    DECLARE_DYNAMIC_CLASS(wxDynamicSashReparentEvent)
126};
127
128
129class wxDynamicSashWindowImpl : public wxEvtHandler
130{
131public:
132    wxDynamicSashWindowImpl(wxDynamicSashWindow *window);
133    ~wxDynamicSashWindowImpl();
134
135    bool Create();
136    void AddChild(wxWindow *window);
137    void DrawSash(int x, int y) const;
138    void ConstrainChildren(int px, int py);
139    void Split(int x, int y);
140    void Unify(int panel);
141    void Resize(int x, int y);
142    wxDynamicSashWindowImpl *FindParent(DynamicSashRegion side) const;
143    wxDynamicSashWindowImpl *FindUpperParent(wxDynamicSashWindowImpl *sash_a,
144                                             wxDynamicSashWindowImpl *sash_b) const;
145    wxWindow *FindFrame() const;
146    wxScrollBar *FindScrollBar(const wxWindow *child, int vert) const;
147
148    void OnSize(wxSizeEvent &event);
149    void OnPaint(wxPaintEvent &event);
150    void OnMouseMove(wxMouseEvent &event);
151    void OnLeave(wxMouseEvent &event);
152    void OnPress(wxMouseEvent &event);
153    void OnRelease(wxMouseEvent &event);
154
155    wxDynamicSashWindow *m_window;
156    wxDynamicSashWindowImpl *m_add_child_target;
157
158    /*  This is the window we are responsible for managing.  Either of
159        leaf or our children are in this window.  For the top level
160        implementation object, this is the same as m_window.
161        Otherwise it is a window we've created an will destroy when we
162        are deleted.  */
163    wxWindow *m_container;
164
165    wxDynamicSashWindowImpl *m_parent;
166    wxDynamicSashWindowImpl *m_top;
167    wxDynamicSashWindowImpl *m_child[2];
168
169    class wxDynamicSashWindowLeaf *m_leaf;
170
171    /*  If the implementation is split horizontally or vertically, m_split
172        is set to DSR_HORIZONTAL_TAB or DSR_VERTICAL_TAB, respectively.
173        Otherwise it is set to DSR_NONE.  */
174    DynamicSashRegion m_split;
175
176    /*  These are used to keep track of a sash as it is being dragged, for
177        drawing the user interface correctly.  */
178    DynamicSashRegion m_dragging;
179    int m_drag_x, m_drag_y;
180};
181
182class wxDynamicSashWindowLeaf : public wxEvtHandler
183{
184public:
185    wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl *impl);
186    ~wxDynamicSashWindowLeaf();
187
188    bool Create();
189    void AddChild(wxWindow *window);
190    DynamicSashRegion GetRegion(int x, int y);
191    void ResizeChild(const wxSize& size);
192    wxScrollBar *FindScrollBar(const wxWindow *child, int vert) const;
193
194    void OnSize(wxSizeEvent &event);
195    void OnViewSize(wxSizeEvent &event);
196    void OnPaint(wxPaintEvent &event);
197    void OnScroll(wxScrollEvent &event);
198    void OnFocus(wxFocusEvent &event);
199    void OnMouseMove(wxMouseEvent &event);
200    void OnLeave(wxMouseEvent &event);
201    void OnPress(wxMouseEvent &event);
202    void OnRelease(wxMouseEvent &event);
203    void OnReparent(wxEvent &event);
204
205    wxDynamicSashWindowImpl *m_impl;
206
207    wxScrollBar *m_vscroll,
208                *m_hscroll;
209
210    /*  m_child is the window provided to us by the application developer.
211        m_viewport is a window we've created, and it is the immediately
212        parent of m_child.  We scroll m_child by moving it around within
213        m_viewport.  */
214    wxWindow *m_viewport,
215             *m_child;
216};
217
218
219// ============================================================================
220// wxDynamicSashWindow
221// ============================================================================
222
223wxDynamicSashWindow::wxDynamicSashWindow()
224{
225    m_impl = NULL;
226}
227
228wxDynamicSashWindow::wxDynamicSashWindow(wxWindow *parent,
229                                         wxWindowID id,
230                                         const wxPoint& pos,
231                                         const wxSize& size,
232                                         long style,
233                                         const wxString& name)
234{
235    m_impl = NULL;
236    Create(parent, id, pos, size, style, name);
237}
238
239wxDynamicSashWindow::~wxDynamicSashWindow()
240{
241    SetEventHandler(this);
242    delete m_impl;
243}
244
245bool wxDynamicSashWindow::Create(wxWindow *parent,
246                                 wxWindowID id,
247                                 const wxPoint& pos,
248                                 const wxSize& size,
249                                 long style,
250                                 const wxString& name)
251{
252    if (m_impl)
253        return false;
254
255    if (!wxWindow::Create(parent, id, pos, size, style, name))
256        return false;
257
258    m_impl = new wxDynamicSashWindowImpl(this);
259    if (!m_impl)
260        return false;
261
262    if (!m_impl->Create())
263    {
264        delete m_impl;
265        m_impl = NULL;
266        return false;
267    }
268
269    return true;
270}
271
272void wxDynamicSashWindow::AddChild(wxWindowBase *child)
273{
274    wxWindow::AddChild(child);
275
276    m_impl->AddChild(wxDynamicCast(child, wxWindow));
277}
278
279wxScrollBar *wxDynamicSashWindow::GetHScrollBar(const wxWindow *child) const
280{
281    return m_impl->FindScrollBar(child, 0);
282}
283
284wxScrollBar *wxDynamicSashWindow::GetVScrollBar(const wxWindow *child) const
285{
286    return m_impl->FindScrollBar(child, 1);
287}
288
289IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashWindow, wxWindow)
290
291
292// ============================================================================
293// wxDynamicSashWindowImpl
294// ============================================================================
295
296wxDynamicSashWindowImpl::wxDynamicSashWindowImpl(wxDynamicSashWindow *window)
297{
298    m_window = window;
299    m_add_child_target = this;
300
301    m_container = NULL;
302    m_parent = NULL;
303    m_top = this;
304    m_child[0] =
305    m_child[1] = NULL;
306    m_leaf = NULL;
307    m_dragging = DSR_NONE;
308    m_split = DSR_NONE;
309}
310
311wxDynamicSashWindowImpl::~wxDynamicSashWindowImpl()
312{
313    delete m_leaf;
314    delete m_child[0];
315    m_child[0] = NULL;
316    delete m_child[1];
317    m_child[1] = NULL;
318    m_leaf = NULL;
319
320    if (m_container != m_window && m_container)
321    {
322        m_container->SetEventHandler(m_container);
323        m_container->Destroy();
324    }
325}
326
327bool wxDynamicSashWindowImpl::Create()
328{
329    if (!m_container)
330        m_container = m_window;
331
332    wxCursor cursor(wxCURSOR_ARROW);
333    m_container->SetCursor(cursor);
334
335    m_leaf = new wxDynamicSashWindowLeaf(this);
336    if (!m_leaf)
337        return false;
338
339    if (!m_leaf->Create())
340    {
341        delete m_leaf;
342        m_leaf = NULL;
343        return false;
344    }
345
346    m_container->SetEventHandler(this);
347
348    Connect(wxEVT_SIZE, wxSizeEventHandler(wxDynamicSashWindowImpl::OnSize));
349    Connect(wxEVT_PAINT, wxPaintEventHandler(wxDynamicSashWindowImpl::OnPaint));
350    Connect(wxEVT_MOTION,
351            wxMouseEventHandler(wxDynamicSashWindowImpl::OnMouseMove));
352    Connect(wxEVT_ENTER_WINDOW,
353            wxMouseEventHandler(wxDynamicSashWindowImpl::OnMouseMove));
354    Connect(wxEVT_LEAVE_WINDOW,
355            wxMouseEventHandler(wxDynamicSashWindowImpl::OnLeave));
356    Connect(wxEVT_LEFT_DOWN,
357            wxMouseEventHandler(wxDynamicSashWindowImpl::OnPress));
358    Connect(wxEVT_LEFT_UP,
359            wxMouseEventHandler(wxDynamicSashWindowImpl::OnRelease));
360
361    return true;
362}
363
364void wxDynamicSashWindowImpl::AddChild(wxWindow *window)
365{
366    if (m_add_child_target && m_add_child_target->m_leaf)
367        m_add_child_target->m_leaf->AddChild(window);
368}
369
370void wxDynamicSashWindowImpl::DrawSash(int x, int y) const
371{
372    int i, j;
373
374    wxScreenDC dc;
375    dc.StartDrawingOnTop(m_container);
376
377    wxBitmap bmp(8, 8);
378    wxMemoryDC bdc;
379    bdc.SelectObject(bmp);
380    bdc.DrawRectangle(-1, -1, 10, 10);
381    for (i = 0; i < 8; i++)
382    {
383        for (j = 0; j < 8; j++)
384        {
385            if ((i + j) & 1)
386                bdc.DrawPoint(i, j);
387        }
388    }
389
390    wxBrush brush(bmp);
391    dc.SetBrush(brush);
392    dc.SetLogicalFunction(wxXOR);
393
394    if ((m_dragging == DSR_CORNER) &&
395        (m_window->GetWindowStyle() & wxDS_DRAG_CORNER) != 0)
396    {
397        int cx = 0;
398        int cy = 0;
399
400        m_container->ClientToScreen(&cx, &cy);
401        m_container->ClientToScreen(&x, &y);
402
403        if (cx < x && cy < y)
404        {
405            dc.DrawRectangle(cx - 2, cy - 2, x - cx + 4, 4);
406            dc.DrawRectangle(x - 2, cy + 2, 4, y - cy);
407            dc.DrawRectangle(cx - 2, cy + 2, 4, y - cy);
408            dc.DrawRectangle(cx + 2, y - 2, x - cx - 4, 4);
409        }
410    }
411    else
412    {
413        int body_w, body_h;
414        m_container->GetClientSize(&body_w, &body_h);
415
416        if (y < 0)
417            y = 0;
418        if (y > body_h)
419            y = body_h;
420        if (x < 0)
421            x = 0;
422        if (x > body_w)
423            x = body_w;
424
425        if (m_dragging == DSR_HORIZONTAL_TAB)
426            x = 0;
427        else
428            y = 0;
429
430        m_container->ClientToScreen(&x, &y);
431
432        int w, h;
433        w = body_w;  h = body_h;
434
435        if (m_dragging == DSR_HORIZONTAL_TAB)
436            dc.DrawRectangle(x, y - 2, w, 4);
437        else
438            dc.DrawRectangle(x - 2, y, 4, h);
439    }
440
441    dc.EndDrawingOnTop();
442}
443
444wxDynamicSashWindowImpl *
445wxDynamicSashWindowImpl::FindParent(DynamicSashRegion side) const
446{
447    if (m_parent == NULL)
448        return NULL;
449
450    if (m_parent->m_split == DSR_HORIZONTAL_TAB)
451    {
452        if (side == DSR_TOP_EDGE && m_parent->m_child[1] == this)
453            return m_parent;
454        if (side == DSR_BOTTOM_EDGE && m_parent->m_child[0] == this)
455            return m_parent;
456    }
457    else if (m_parent->m_split == DSR_VERTICAL_TAB)
458    {
459        if (side == DSR_LEFT_EDGE && m_parent->m_child[1] == this)
460            return m_parent;
461        if (side == DSR_RIGHT_EDGE && m_parent->m_child[0] == this)
462            return m_parent;
463    }
464
465    return m_parent->FindParent(side);
466}
467
468wxDynamicSashWindowImpl *
469wxDynamicSashWindowImpl::FindUpperParent(wxDynamicSashWindowImpl *sash_a,
470                                         wxDynamicSashWindowImpl *sash_b) const
471{
472    wxWindow *win;
473    win = sash_a->m_container->GetParent();
474    while (win && !win->IsTopLevel())
475    {
476        if (win == sash_b->m_container)
477            return sash_b;
478
479        win = win->GetParent();
480    }
481
482    return sash_a;
483}
484
485
486wxWindow *wxDynamicSashWindowImpl::FindFrame() const
487{
488    wxWindow *win;
489
490    win = m_window->GetParent();
491    while (win && !win->IsTopLevel()
492#ifdef __WXMSW__
493           && ! wxIsKindOf(win, wxMDIChildFrame)  // not top-level but still a frame
494#endif
495        )
496    {
497        win = win->GetParent();
498    }
499
500    return win;
501}
502
503wxScrollBar *
504wxDynamicSashWindowImpl::FindScrollBar(const wxWindow *child, int vert) const
505{
506    if (m_child[0] == NULL && m_leaf == NULL)
507        return NULL;
508
509    if (!m_child[0])
510    {
511        return m_leaf->FindScrollBar(child, vert);
512    }
513
514    wxScrollBar *ret = m_child[0]->FindScrollBar(child, vert);
515    if (!ret)
516        ret = m_child[1]->FindScrollBar(child, vert);
517
518    return ret;
519}
520
521void wxDynamicSashWindowImpl::ConstrainChildren(int px, int py)
522{
523    wxLayoutConstraints *layout = new wxLayoutConstraints();
524    layout->left.SameAs(m_container, wxLeft);
525    layout->top.SameAs(m_container, wxTop);
526    if (m_split == DSR_HORIZONTAL_TAB)
527    {
528        layout->right.SameAs(m_container, wxRight);
529        layout->height.PercentOf(m_container, wxHeight, py);
530    }
531    else
532    {
533        layout->bottom.SameAs(m_container, wxBottom);
534        layout->width.PercentOf(m_container, wxWidth, px);
535    }
536    m_child[0]->m_container->SetConstraints(layout);
537
538    layout = new wxLayoutConstraints();
539    layout->right.SameAs(m_container, wxRight);
540    layout->bottom.SameAs(m_container, wxBottom);
541    if (m_split == DSR_HORIZONTAL_TAB)
542    {
543        layout->top.Below(m_child[0]->m_container, 1);
544        layout->left.SameAs(m_container, wxLeft);
545    }
546    else
547    {
548        layout->left.RightOf(m_child[0]->m_container, 1);
549        layout->top.SameAs(m_container, wxTop);
550    }
551    m_child[1]->m_container->SetConstraints(layout);
552}
553
554void wxDynamicSashWindowImpl::Unify(int panel)
555{
556    int other = panel == 0 ? 1 : 0;
557
558    if (m_child[panel]->m_leaf)
559    {
560        wxDynamicSashWindowImpl *child[2];
561
562        child[0] = m_child[0];
563        child[1] = m_child[1];
564
565        m_child[0] = m_child[1] = NULL;
566
567        m_leaf = new wxDynamicSashWindowLeaf(this);
568        m_leaf->Create();
569        m_leaf->m_child = child[panel]->m_leaf->m_child;
570
571        m_leaf->m_vscroll->SetScrollbar(child[panel]->m_leaf->m_vscroll->GetThumbPosition(),
572                                        child[panel]->m_leaf->m_vscroll->GetThumbSize(),
573                                        child[panel]->m_leaf->m_vscroll->GetRange(),
574                                        child[panel]->m_leaf->m_vscroll->GetPageSize());
575        m_leaf->m_hscroll->SetScrollbar(child[panel]->m_leaf->m_hscroll->GetThumbPosition(),
576                                        child[panel]->m_leaf->m_hscroll->GetThumbSize(),
577                                        child[panel]->m_leaf->m_hscroll->GetRange(),
578                                        child[panel]->m_leaf->m_hscroll->GetPageSize());
579        m_add_child_target = NULL;
580        wxDynamicSashReparentEvent event(m_leaf);
581        m_leaf->ProcessEvent(event);
582
583        delete child[0];
584        delete child[1];
585
586        m_split = DSR_NONE;
587
588        wxDynamicSashUnifyEvent unify(m_leaf->m_child);
589        m_leaf->m_child->ProcessEvent(unify);
590    }
591    else
592    {
593        m_split = m_child[panel]->m_split;
594
595        delete m_child[other];
596
597        wxDynamicSashWindowImpl *child_panel = m_child[panel];
598        m_child[0] = child_panel->m_child[0];
599        m_child[1] = child_panel->m_child[1];
600
601        m_child[0]->m_parent = this;
602        m_child[1]->m_parent = this;
603
604        m_add_child_target = NULL;
605        m_child[0]->m_container->Reparent(m_container);
606        m_child[1]->m_container->Reparent(m_container);
607
608        child_panel->m_child[0] = child_panel->m_child[1] = NULL;
609        delete child_panel;
610
611        wxSize size = m_container->GetSize();
612        wxSize child_size = m_child[0]->m_container->GetSize();
613
614        ConstrainChildren(child_size.GetWidth() * 100 / size.GetWidth(),
615                            child_size.GetHeight() * 100 / size.GetHeight());
616
617        m_container->Layout();
618    }
619}
620
621void wxDynamicSashWindowImpl::Split(int px, int py)
622{
623
624    m_add_child_target = NULL;
625
626    m_child[0] = new wxDynamicSashWindowImpl(m_window);
627    m_child[0]->m_container = new wxWindow(m_container, wxID_ANY);
628    m_child[0]->m_parent = this;
629    m_child[0]->m_top = m_top;
630    m_child[0]->Create();
631    if (m_leaf->m_child)
632    {
633        m_leaf->m_child->Reparent(m_container);
634        m_child[0]->AddChild(m_leaf->m_child);
635    }
636
637    m_child[1] = new wxDynamicSashWindowImpl(m_window);
638    m_child[1]->m_container = new wxWindow(m_container, wxID_ANY);
639    m_child[1]->m_parent = this;
640    m_child[1]->m_top = m_top;
641    m_child[1]->Create();
642
643    m_split = m_dragging;
644    ConstrainChildren(px, py);
645
646    m_top->m_add_child_target = m_child[1];
647    wxDynamicSashSplitEvent split(m_child[0]->m_leaf->m_child);
648    m_child[0]->m_leaf->m_child->ProcessEvent(split);
649
650    m_child[0]->m_leaf->m_vscroll->SetScrollbar(m_leaf->m_vscroll->GetThumbPosition(),
651                                                m_leaf->m_vscroll->GetThumbSize(),
652                                                m_leaf->m_vscroll->GetRange(),
653                                                m_leaf->m_vscroll->GetPageSize());
654    m_child[0]->m_leaf->m_hscroll->SetScrollbar(m_leaf->m_hscroll->GetThumbPosition(),
655                                                m_leaf->m_hscroll->GetThumbSize(),
656                                                m_leaf->m_hscroll->GetRange(),
657                                                m_leaf->m_hscroll->GetPageSize());
658    m_child[1]->m_leaf->m_vscroll->SetScrollbar(m_leaf->m_vscroll->GetThumbPosition(),
659                                                m_leaf->m_vscroll->GetThumbSize(),
660                                                m_leaf->m_vscroll->GetRange(),
661                                                m_leaf->m_vscroll->GetPageSize());
662    m_child[1]->m_leaf->m_hscroll->SetScrollbar(m_leaf->m_hscroll->GetThumbPosition(),
663                                                m_leaf->m_hscroll->GetThumbSize(),
664                                                m_leaf->m_hscroll->GetRange(),
665                                                m_leaf->m_hscroll->GetPageSize());
666    delete m_leaf;
667    m_leaf = NULL;
668
669    m_container->Layout();
670}
671
672
673/*  This code is called when you finish resizing a view by dragging the
674    corner tab, but I think this implementation is lousy and will surprise
675    the user more often than it will do what they are trying to do.  What
676    I really need to be doing here is do a rewrite such that *no* sashes
677    move except the ones immediately to the bottom and right of this window,
678    and handle the case where you resize a window larger than it's neighbors
679    by destroying the neighbors.
680
681    But this will do for now.  */
682void wxDynamicSashWindowImpl::Resize(int x, int y)
683{
684    wxDynamicSashWindowImpl *h_parent = FindParent(DSR_BOTTOM_EDGE);
685    wxDynamicSashWindowImpl *v_parent = FindParent(DSR_RIGHT_EDGE);
686    int h_unify = -1;
687    int v_unify = -1;
688    wxWindow *frame = FindFrame();
689
690    if (x < 0)
691        x = 0;
692    if (y < 0)
693        y = 0;
694
695    if (h_parent)
696    {
697        m_container->ClientToScreen(NULL, &y);
698        h_parent->m_container->ScreenToClient(NULL, &y);
699
700        int py = (int)((y * 100) / h_parent->m_container->GetSize().GetHeight() + 0.5);
701
702        if (py < 10)
703        {
704            wxDynamicSashWindowImpl *ho_parent = FindParent(DSR_TOP_EDGE);
705
706            if (ho_parent)
707            {
708                if (FindUpperParent(h_parent, ho_parent) == ho_parent)
709                {
710                    h_unify = 1;
711                }
712                else
713                {
714                    py = (int)((ho_parent->m_child[0]->m_container->GetSize().GetHeight() * 100)
715                                / h_parent->m_container->GetSize().GetHeight() + 0.5);
716                    h_parent->m_child[0]->m_container->GetConstraints()->height.PercentOf(
717                            h_parent->m_container, wxHeight, py);
718
719                    h_parent = ho_parent;
720                    h_unify = 0;
721                }
722            }
723            else
724            {
725                h_unify = 1;
726            }
727        }
728        else if (py > 90)
729        {
730            h_unify = 0;
731        }
732        else
733        {
734            h_parent->m_child[0]->m_container->GetConstraints()->height.PercentOf(
735                    h_parent->m_container, wxHeight, py);
736            h_parent->m_container->Layout();
737        }
738    }
739    else
740    {
741        int do_resize = 1;
742        h_parent = FindParent(DSR_TOP_EDGE);
743
744        if (h_parent)
745        {
746            int py = (int)((y * 100) /
747                        (h_parent->m_container->GetSize().GetHeight() +
748                                y - m_container->GetSize().GetHeight()) + 0.5);
749
750            if (py < 10)
751                h_unify = 0;
752        }
753        else if (y < 64)
754        {
755            do_resize = 0;
756        }
757
758        if (do_resize)
759        {
760            wxSize size = frame->GetSize();
761            frame->SetSize(size.GetWidth(), size.GetHeight() + y - m_container->GetSize().GetHeight());
762        }
763    }
764
765    if (v_parent)
766    {
767        m_container->ClientToScreen(&x, NULL);
768        v_parent->m_container->ScreenToClient(&x, NULL);
769
770        int px = (int)((x * 100) / v_parent->m_container->GetSize().GetWidth() + 0.5);
771
772        if (px < 10)
773        {
774            wxDynamicSashWindowImpl *vo_parent = FindParent(DSR_LEFT_EDGE);
775
776            if (vo_parent)
777            {
778                if (FindUpperParent(v_parent, vo_parent) == vo_parent)
779                {
780                    v_unify = 1;
781                }
782                else
783                {
784                    px = (int)((vo_parent->m_child[0]->m_container->GetSize().GetWidth() * 100)
785                                / v_parent->m_container->GetSize().GetWidth() + 0.5);
786                    v_parent->m_child[0]->m_container->GetConstraints()->width.PercentOf(
787                            v_parent->m_container, wxWidth, px);
788
789                    v_parent = vo_parent;
790                    v_unify = 0;
791                }
792            }
793            else
794            {
795                v_unify = 1;
796            }
797        }
798        else if (px > 90)
799        {
800            v_unify = 0;
801        }
802        else
803        {
804            v_parent->m_child[0]->m_container->GetConstraints()->width.PercentOf(
805                    v_parent->m_container, wxWidth, px);
806            v_parent->m_container->Layout();
807        }
808    }
809    else
810    {
811        int do_resize = 1;
812        v_parent = FindParent(DSR_LEFT_EDGE);
813
814        if (v_parent)
815        {
816            int px = (int)((x * 100) /
817                        (v_parent->m_container->GetSize().GetWidth() +
818                                x - m_container->GetSize().GetWidth()) + 0.5);
819
820            if (px < 10)
821                v_unify = 0;
822        }
823        else if (x < 64)
824        {
825            do_resize = 0;
826        }
827
828        if (do_resize)
829        {
830            wxSize size = frame->GetSize();
831            frame->SetSize(size.GetWidth() + x - m_container->GetSize().GetWidth(), size.GetHeight());
832        }
833    }
834
835    if (h_unify != -1 && v_unify != -1)
836    {
837        wxDynamicSashWindowImpl *parent = FindUpperParent(h_parent, v_parent);
838
839        if (parent == h_parent)
840        {
841            h_parent->Unify(h_unify);
842        }
843        else
844        {
845            v_parent->Unify(v_unify);
846        }
847    }
848    else if (h_unify != -1)
849    {
850        h_parent->Unify(h_unify);
851    }
852    else if (v_unify != -1)
853    {
854        v_parent->Unify(v_unify);
855    }
856}
857
858
859void wxDynamicSashWindowImpl::OnSize(wxSizeEvent &event)
860{
861    m_container->Layout();
862
863    if (m_leaf)
864        m_leaf->OnSize(event);
865}
866
867void wxDynamicSashWindowImpl::OnPaint(wxPaintEvent &event)
868{
869    if (m_leaf)
870        m_leaf->OnPaint(event);
871    else
872    {
873        wxPaintDC dc(m_container);
874        dc.SetBackground(wxBrush(m_container->GetBackgroundColour(), wxSOLID));
875        dc.Clear();
876    }
877}
878
879void wxDynamicSashWindowImpl::OnMouseMove(wxMouseEvent &event)
880{
881    if (m_dragging)
882    {
883        DrawSash(m_drag_x, m_drag_y);
884        m_drag_x = event.m_x;  m_drag_y = event.m_y;
885        DrawSash(m_drag_x, m_drag_y);
886    }
887    else if (m_leaf)
888    {
889        m_leaf->OnMouseMove(event);
890    }
891}
892
893void wxDynamicSashWindowImpl::OnLeave(wxMouseEvent &event)
894{
895    if (m_leaf)
896        m_leaf->OnLeave(event);
897}
898
899void wxDynamicSashWindowImpl::OnPress(wxMouseEvent &event)
900{
901    if (m_leaf)
902    {
903        m_leaf->OnPress(event);
904    }
905    else
906    {
907        m_dragging = m_split;
908        m_drag_x = event.m_x;
909        m_drag_y = event.m_y;
910        DrawSash(m_drag_x, m_drag_y);
911        m_container->CaptureMouse();
912    }
913}
914
915void wxDynamicSashWindowImpl::OnRelease(wxMouseEvent &event)
916{
917    if ((m_dragging == DSR_CORNER) &&
918        (m_window->GetWindowStyle() & wxDS_DRAG_CORNER) != 0)
919    {
920        DrawSash(m_drag_x, m_drag_y);
921        m_container->ReleaseMouse();
922
923        Resize(event.m_x, event.m_y);
924
925        m_dragging = DSR_NONE;
926    }
927    else if (m_dragging)
928    {
929        DrawSash(m_drag_x, m_drag_y);
930        m_container->ReleaseMouse();
931
932        wxSize size = m_container->GetSize();
933        int px = (int)((event.m_x * 100) / size.GetWidth() + 0.5);
934        int py = (int)((event.m_y * 100) / size.GetHeight() + 0.5);
935
936        if ((m_dragging == DSR_HORIZONTAL_TAB && py >= 10 && py <= 90)
937                    || (m_dragging == DSR_VERTICAL_TAB && px >= 10 && px <= 90))
938        {
939            if (m_child[0] == NULL)
940            {
941                Split(px, py);
942            }
943            else
944            {
945                /*  It would be nice if moving *this* sash didn't implicitly move
946                    the sashes of our children (if any).  But this will do.  */
947                wxLayoutConstraints *layout = m_child[0]->m_container->GetConstraints();
948                if (m_split == DSR_HORIZONTAL_TAB)
949                {
950                    layout->height.PercentOf(m_container, wxHeight, py);
951                }
952                else
953                {
954                    layout->width.PercentOf(m_container, wxWidth, px);
955                }
956                m_container->Layout();
957            }
958        }
959        else
960        {
961            if (m_child[0] != NULL)
962            {
963                if ((m_dragging == DSR_HORIZONTAL_TAB && py <= 10)
964                        || (m_dragging == DSR_VERTICAL_TAB && px <= 10))
965                {
966                    Unify(1);
967                }
968                else
969                {
970                    Unify(0);
971                }
972            }
973        }
974
975        wxCursor cursor;
976        if (m_split == DSR_HORIZONTAL_TAB)
977            cursor = wxCursor(wxCURSOR_SIZENS);
978        else if (m_split == DSR_VERTICAL_TAB)
979            cursor = wxCursor(wxCURSOR_SIZEWE);
980        else
981            cursor = wxCursor(wxCURSOR_ARROW);
982
983        m_container->SetCursor(cursor);
984
985        m_dragging = DSR_NONE;
986    }
987    else if (m_leaf)
988    {
989        m_leaf->OnRelease(event);
990    }
991}
992
993// ============================================================================
994// wxDynamicSashWindowLeaf
995// ============================================================================
996
997wxDynamicSashWindowLeaf::wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl *impl)
998{
999    m_impl = impl;
1000
1001    m_hscroll =
1002    m_vscroll = NULL;
1003
1004    m_child = NULL;
1005}
1006
1007wxDynamicSashWindowLeaf::~wxDynamicSashWindowLeaf()
1008{
1009    m_hscroll->SetEventHandler(m_hscroll);
1010    m_vscroll->SetEventHandler(m_vscroll);
1011
1012    m_hscroll->Destroy();
1013    m_vscroll->Destroy();
1014    m_viewport->Destroy();
1015}
1016
1017bool wxDynamicSashWindowLeaf::Create()
1018{
1019    m_hscroll = new wxScrollBar();
1020    m_vscroll = new wxScrollBar();
1021    m_viewport = new wxWindow();
1022
1023    wxDynamicSashWindowImpl *add_child_target = m_impl->m_add_child_target;
1024    m_impl->m_add_child_target = NULL;
1025
1026    bool success = m_hscroll->Create(m_impl->m_container, wxID_ANY,
1027                                     wxDefaultPosition, wxDefaultSize,
1028                                     wxSB_HORIZONTAL);
1029    if ( success )
1030        success = m_vscroll->Create(m_impl->m_container, wxID_ANY,
1031                                    wxDefaultPosition, wxDefaultSize,
1032                                    wxSB_VERTICAL);
1033    if ( success )
1034        success = m_viewport->Create(m_impl->m_container, wxID_ANY);
1035    if ( !success )
1036        return false;
1037
1038    m_impl->m_add_child_target = add_child_target;
1039
1040    wxCursor cursor(wxCURSOR_ARROW);
1041    m_hscroll->SetCursor(cursor);
1042    m_vscroll->SetCursor(cursor);
1043    m_viewport->SetCursor(cursor);
1044
1045    // the viewport must resize its child when it is itself resized, but it's
1046    // more convenient to do it in our own method instead of deriving a new
1047    // class just for this: this is why we pass this as last Connect() argument
1048    m_viewport->Connect(wxEVT_SIZE,
1049                        wxSizeEventHandler(wxDynamicSashWindowLeaf::OnViewSize),
1050                        NULL, this);
1051
1052    Connect(wxEVT_DYNAMIC_SASH_REPARENT,
1053            wxEventHandler(wxDynamicSashWindowLeaf::OnReparent));
1054
1055    if (m_impl->m_window->GetWindowStyle() & wxDS_MANAGE_SCROLLBARS)
1056    {
1057        m_hscroll->SetEventHandler(this);
1058        m_vscroll->SetEventHandler(this);
1059
1060        Connect(wxEVT_SET_FOCUS,
1061                wxFocusEventHandler(wxDynamicSashWindowLeaf::OnFocus));
1062        Connect(wxEVT_SCROLL_TOP,
1063                wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll));
1064        Connect(wxEVT_SCROLL_BOTTOM,
1065                wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll));
1066        Connect(wxEVT_SCROLL_LINEUP,
1067                wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll));
1068        Connect(wxEVT_SCROLL_LINEDOWN,
1069                wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll));
1070        Connect(wxEVT_SCROLL_PAGEUP,
1071                wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll));
1072        Connect(wxEVT_SCROLL_PAGEDOWN,
1073                wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll));
1074        Connect(wxEVT_SCROLL_THUMBTRACK,
1075                wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll));
1076        Connect(wxEVT_SCROLL_THUMBRELEASE,
1077                wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll));
1078    }
1079
1080    wxLayoutConstraints *layout = new wxLayoutConstraints();
1081    if (!layout)
1082        return false;
1083
1084    wxSize size = m_hscroll->GetBestSize();
1085
1086    layout->left.SameAs(m_impl->m_container, wxLeft, 10);
1087    layout->right.LeftOf(m_vscroll);
1088    layout->bottom.SameAs(m_impl->m_container, wxBottom, 3);
1089    layout->height.Absolute(size.GetHeight());
1090    m_hscroll->SetConstraints(layout);
1091
1092    layout = new wxLayoutConstraints();
1093    if (!layout)
1094        return false;
1095
1096    size = m_vscroll->GetBestSize();
1097
1098    layout->top.SameAs(m_impl->m_container, wxTop, 10);
1099    layout->bottom.Above(m_hscroll);
1100    layout->right.SameAs(m_impl->m_container, wxRight, 3);
1101    layout->width.Absolute(size.GetWidth());
1102    m_vscroll->SetConstraints(layout);
1103
1104    layout = new wxLayoutConstraints();
1105    if (!layout)
1106        return false;
1107    layout->left.SameAs(m_impl->m_container, wxLeft, 3);
1108    layout->right.LeftOf(m_vscroll);
1109    layout->top.SameAs(m_impl->m_container, wxTop, 3);
1110    layout->bottom.Above(m_hscroll);
1111    m_viewport->SetConstraints(layout);
1112
1113    m_impl->m_container->Layout();
1114
1115    return true;
1116}
1117
1118void wxDynamicSashWindowLeaf::AddChild(wxWindow *window)
1119{
1120    if (m_child)
1121        m_child->Destroy();
1122
1123    m_child = window;
1124
1125    wxDynamicSashReparentEvent event(this);
1126    AddPendingEvent(event);
1127}
1128
1129DynamicSashRegion wxDynamicSashWindowLeaf::GetRegion(int x, int y)
1130{
1131    wxSize size = m_impl->m_container->GetSize();
1132    int w = size.GetWidth();
1133    int h = size.GetHeight();
1134    size = m_hscroll->GetSize();
1135    int sh = size.GetHeight();
1136    size = m_vscroll->GetSize();
1137    int sw = size.GetWidth();
1138
1139    if (x >= w - sw - 3 && x < w && y >= h - sh - 3 && y < h)
1140        return DSR_CORNER;
1141    if (x >= 3 && x < 10 && y >= h - sh - 3 && y < h - 2)
1142        return DSR_VERTICAL_TAB;
1143    if (x >= w - sw - 3 && x < w - 2 && y >= 3 && y < 10)
1144        return DSR_HORIZONTAL_TAB;
1145    if (x < 3)
1146        return DSR_LEFT_EDGE;
1147    if (y < 3)
1148        return DSR_TOP_EDGE;
1149    if (x >= w - 2)
1150        return DSR_RIGHT_EDGE;
1151    if (y >= h - 2)
1152        return DSR_BOTTOM_EDGE;
1153
1154    return DSR_NONE;
1155}
1156
1157void wxDynamicSashWindowLeaf::ResizeChild(const wxSize& size)
1158{
1159    if (m_child)
1160    {
1161        if (m_impl->m_window->HasFlag(wxDS_MANAGE_SCROLLBARS))
1162        {
1163            wxSize best_size = m_child->GetBestSize();
1164            if (best_size.GetWidth() < size.GetWidth())
1165                best_size.SetWidth(size.GetWidth());
1166            if (best_size.GetHeight() < size.GetHeight())
1167                best_size.SetHeight(size.GetHeight());
1168            m_child->SetSize(best_size);
1169
1170            int hpos = m_hscroll->GetThumbPosition();
1171            int vpos = m_vscroll->GetThumbPosition();
1172
1173            if (hpos < 0)
1174                hpos = 0;
1175            if (vpos < 0)
1176                vpos = 0;
1177            if (hpos > best_size.GetWidth() - size.GetWidth())
1178                hpos = best_size.GetWidth() - size.GetWidth();
1179            if (vpos > best_size.GetHeight() - size.GetHeight())
1180                vpos = best_size.GetHeight() - size.GetHeight();
1181
1182            m_hscroll->SetScrollbar(hpos, size.GetWidth(),
1183                                    best_size.GetWidth(), size.GetWidth());
1184            m_vscroll->SetScrollbar(vpos, size.GetHeight(),
1185                                    best_size.GetHeight(), size.GetHeight());
1186
1187            //  Umm, the scrollbars are doing something insane under GTK+ and subtracting
1188            //  one from the position I pass in.  This works around that.
1189            m_hscroll->SetThumbPosition(hpos + hpos - m_hscroll->GetThumbPosition());
1190            m_vscroll->SetThumbPosition(vpos + vpos - m_vscroll->GetThumbPosition());
1191
1192            wxPoint pos = m_child->GetPosition();
1193            m_viewport->ScrollWindow(-hpos - pos.x, -vpos - pos.y);
1194        }
1195        else // !wxDS_MANAGE_SCROLLBARS
1196        {
1197            m_child->SetSize(size);
1198        }
1199    }
1200}
1201
1202wxScrollBar *
1203wxDynamicSashWindowLeaf::FindScrollBar(const wxWindow *child, int vert) const
1204{
1205    if (m_child == child)
1206    {
1207        return vert ? m_vscroll : m_hscroll;
1208    }
1209
1210    return NULL;
1211}
1212
1213void wxDynamicSashWindowLeaf::OnSize(wxSizeEvent &WXUNUSED(event))
1214{
1215    m_impl->m_container->Refresh();
1216}
1217
1218void wxDynamicSashWindowLeaf::OnViewSize(wxSizeEvent &WXUNUSED(event))
1219{
1220    if ( m_viewport )
1221        ResizeChild(m_viewport->GetSize());
1222}
1223
1224void wxDynamicSashWindowLeaf::OnPaint(wxPaintEvent &WXUNUSED(event))
1225{
1226    wxPaintDC dc(m_impl->m_container);
1227    dc.SetBackground(wxBrush(m_impl->m_container->GetBackgroundColour(), wxSOLID));
1228    dc.Clear();
1229
1230    wxPen highlight(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT), 1, wxSOLID);
1231    wxPen shadow(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW), 1, wxSOLID);
1232    wxPen black(*wxBLACK, 1, wxSOLID);
1233
1234    wxSize size = m_impl->m_container->GetSize();
1235    int w = size.GetWidth();
1236    int h = size.GetHeight();
1237    size = m_hscroll->GetSize();
1238    int sh = size.GetHeight();
1239    size = m_vscroll->GetSize();
1240    int sw = size.GetWidth();
1241
1242    dc.SetPen(shadow);
1243    dc.DrawLine(1, 1, 1, h - 2);
1244    dc.DrawLine(1, 1, w - 2, 1);
1245    dc.SetPen(black);
1246    dc.DrawLine(2, 2, 2, h - 3);
1247    dc.DrawLine(2, 2, w - 3, 2);
1248    dc.SetPen(highlight);
1249    dc.DrawLine(w - 2, 2, w - 2, h - sh - 2);
1250    dc.DrawLine(w - 2, h - sh - 2, w - sw - 2, h - sh - 2);
1251    dc.DrawLine(w - sw - 2, h - sh - 2, w - sw - 2, h - 2);
1252    dc.DrawLine(w - sw - 2, h - 2, 2, h - 2);
1253
1254    dc.SetPen(highlight);
1255    dc.DrawLine(w - sw - 2, 8, w - sw - 2, 4);
1256    dc.DrawLine(w - sw - 2, 4, w - 5, 4);
1257    dc.SetPen(shadow);
1258    dc.DrawLine(w - 5, 4, w - 5, 8);
1259    dc.DrawLine(w - 5, 8, w - sw - 2, 8);
1260    dc.SetPen(black);
1261    dc.DrawLine(w - 4, 3, w - 4, 9);
1262    dc.DrawLine(w - 4, 9, w - sw - 3, 9);
1263
1264    dc.SetPen(highlight);
1265    dc.DrawLine(4, h - 5, 4, h - sh - 2);
1266    dc.DrawLine(4, h - sh - 2, 8, h - sh - 2);
1267    dc.SetPen(shadow);
1268    dc.DrawLine(8, h - sh - 2, 8, h - 5);
1269    dc.DrawLine(8, h - 5, 4, h - 5);
1270    dc.SetPen(black);
1271    dc.DrawLine(9, h - sh - 3, 9, h - 4);
1272    dc.DrawLine(9, h - 4, 3, h - 4);
1273
1274    int cy = (h - sh + h - 6) / 2 + 1;
1275    int cx = (w - sw + w - 6) / 2 + 1;
1276    int sy = cy;
1277    while (sy > h - sh)
1278        sy -= 4;
1279    int sx = cx;
1280    while (sx > w - sw)
1281        sx -= 4;
1282
1283    int x, y;
1284    for (y = sy; y < h - 2; y += 4)
1285    {
1286        for (x = sx; x < w - 2; x += 4)
1287        {
1288            if (x - cx >= -(y - cy))
1289            {
1290                dc.SetPen(highlight);
1291                dc.DrawPoint(x, y);
1292                dc.SetPen(shadow);
1293                dc.DrawPoint(x + 1, y + 1);
1294            }
1295        }
1296    }
1297}
1298
1299void wxDynamicSashWindowLeaf::OnScroll(wxScrollEvent &WXUNUSED(event))
1300{
1301    int nx = -m_hscroll->GetThumbPosition();
1302    int ny = -m_vscroll->GetThumbPosition();
1303
1304    if (m_child)
1305    {
1306        wxPoint pos = m_child->GetPosition();
1307
1308        m_viewport->ScrollWindow(nx - pos.x, ny - pos.y);
1309    }
1310}
1311
1312void wxDynamicSashWindowLeaf::OnFocus(wxFocusEvent &event)
1313{
1314    if ( event.GetEventObject() == m_hscroll ||
1315            event.GetEventObject() == m_vscroll )
1316    {
1317        m_child->SetFocus();
1318    }
1319}
1320
1321
1322void wxDynamicSashWindowLeaf::OnMouseMove(wxMouseEvent &event)
1323{
1324    if (m_impl->m_dragging)
1325        return;
1326
1327    DynamicSashRegion region = GetRegion(event.m_x, event.m_y);
1328
1329    wxCursor cursor(wxCURSOR_ARROW);
1330    if (region == DSR_HORIZONTAL_TAB)
1331    {
1332        cursor = wxCursor(wxCURSOR_SIZENS);
1333    }
1334    else if (region == DSR_VERTICAL_TAB)
1335    {
1336        cursor = wxCursor(wxCURSOR_SIZEWE);
1337    }
1338    else if ((region == DSR_CORNER) &&
1339               (m_impl->m_window->GetWindowStyle() & wxDS_DRAG_CORNER) != 0)
1340    {
1341        cursor = wxCursor(wxCURSOR_SIZENWSE);
1342    }
1343    else if (region == DSR_LEFT_EDGE || region == DSR_TOP_EDGE
1344                || region == DSR_RIGHT_EDGE || region == DSR_BOTTOM_EDGE)
1345    {
1346        if (m_impl->FindParent(region))
1347        {
1348            if (region == DSR_LEFT_EDGE || region == DSR_RIGHT_EDGE)
1349            {
1350                cursor = wxCursor(wxCURSOR_SIZEWE);
1351            }
1352            else
1353            {
1354                cursor = wxCursor(wxCURSOR_SIZENS);
1355            }
1356        }
1357    }
1358
1359    m_impl->m_container->SetCursor(cursor);
1360}
1361
1362void wxDynamicSashWindowLeaf::OnLeave(wxMouseEvent &WXUNUSED(event))
1363{
1364    wxCursor cursor(wxCURSOR_ARROW);
1365    m_impl->m_container->SetCursor(cursor);
1366}
1367
1368
1369void wxDynamicSashWindowLeaf::OnPress(wxMouseEvent &event)
1370{
1371    DynamicSashRegion region = GetRegion(event.m_x, event.m_y);
1372
1373    if ((region == DSR_CORNER) && (m_impl->m_window->GetWindowStyle() & wxDS_DRAG_CORNER) == 0)
1374        return;
1375
1376    if (region == DSR_HORIZONTAL_TAB || region == DSR_VERTICAL_TAB || region == DSR_CORNER)
1377    {
1378        m_impl->m_dragging = region;
1379        m_impl->m_drag_x = event.m_x;
1380        m_impl->m_drag_y = event.m_y;
1381        m_impl->DrawSash(event.m_x, event.m_y);
1382        m_impl->m_container->CaptureMouse();
1383    }
1384    else if (region == DSR_LEFT_EDGE || region == DSR_TOP_EDGE
1385                || region == DSR_RIGHT_EDGE || region == DSR_BOTTOM_EDGE)
1386    {
1387        wxDynamicSashWindowImpl *parent = m_impl->FindParent(region);
1388
1389        if (parent)
1390        {
1391            int x = event.m_x;
1392            int y = event.m_y;
1393
1394            m_impl->m_container->ClientToScreen(&x, &y);
1395            parent->m_container->ScreenToClient(&x, &y);
1396
1397            parent->m_dragging = parent->m_split;
1398            parent->m_drag_x = x;
1399            parent->m_drag_y = y;
1400            parent->DrawSash(x, y);
1401            parent->m_container->CaptureMouse();
1402        }
1403    }
1404}
1405
1406void wxDynamicSashWindowLeaf::OnRelease(wxMouseEvent &WXUNUSED(event))
1407{
1408}
1409
1410void wxDynamicSashWindowLeaf::OnReparent(wxEvent &WXUNUSED(event))
1411{
1412    if (m_child)
1413    {
1414        m_child->Reparent(m_viewport);
1415    }
1416
1417    ResizeChild(m_viewport->GetSize());
1418}
1419
1420// ============================================================================
1421// events
1422// ============================================================================
1423
1424wxDynamicSashSplitEvent::wxDynamicSashSplitEvent()
1425{
1426    m_eventObject = NULL;
1427    m_eventType = wxEVT_DYNAMIC_SASH_SPLIT;
1428}
1429
1430wxDynamicSashSplitEvent::wxDynamicSashSplitEvent(wxObject *object)
1431{
1432    m_eventObject = object;
1433    m_eventType = wxEVT_DYNAMIC_SASH_SPLIT;
1434}
1435
1436IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashSplitEvent, wxCommandEvent)
1437
1438wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent()
1439{
1440    m_eventObject = NULL;
1441    m_eventType = wxEVT_DYNAMIC_SASH_UNIFY;
1442}
1443
1444wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent(wxObject *object)
1445{
1446    m_eventObject = object;
1447    m_eventType = wxEVT_DYNAMIC_SASH_UNIFY;
1448}
1449
1450IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashUnifyEvent, wxCommandEvent)
1451
1452
1453wxDynamicSashReparentEvent::wxDynamicSashReparentEvent()
1454{
1455    m_eventObject = NULL;
1456    m_eventType = wxEVT_DYNAMIC_SASH_REPARENT;
1457}
1458
1459wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(wxObject *object)
1460{
1461    m_eventObject = object;
1462    m_eventType = wxEVT_DYNAMIC_SASH_REPARENT;
1463}
1464
1465wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent& evt)
1466    : wxEvent(evt)
1467{
1468}
1469
1470IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashReparentEvent, wxEvent)
1471
1472