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
9 // Copyright: (c) 2001 Matt Kimball
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
14 #pragma implementation "splittree.h"
17 // For compilers that support precompilation, includes "wx/wx.h".
18 #include "wx/wxprec.h"
24 // for all others, include the necessary headers (this file is usually all you
25 // need because it includes almost all "standard" wxWidgets headers)
34 #include "wx/gizmos/dynamicsash.h"
37 const wxChar
* wxDynamicSashWindowNameStr
= wxT("dynamicSashWindow");
41 wxDynamicSashWindow works by internally storing a tree of Implementation
42 objects (wxDynamicSsahWindowImpl) and Leaf objects
43 (wxDynamicSashWindowLeaf). The wxDynamicSashWindow has a pointer to one
44 implementation, and each implementation either has a pointer to a one
45 leaf (m_leaf) or a pointer to two children implementation objects
46 (m_child). The leaves each are responsible for drawing the frame and
47 decorations around one user-provided views and for responding to mouse
50 A resulting tree might look something like this:
54 +- wxDynamicSashWindowImpl
56 +- wxDynamicSashWindowLeaf
60 +- wxDynamicSashWindowImpl
62 +- wxDynamicSashWindowLeaf
66 +- wxDynamicSashWindowLeaf
70 Each time a split occurs, one of the implementation objects removes its
71 leaf, generates two new implementation object children, each with a new
72 leaf, and reparents the user view which was connected to its old leaf
73 to be one of the new leaf's user view, and sends a Split event to the
74 user view in the hopes that it will generate a new user view for the
77 When a unification ocurrs, an implementation object is replaced by one
78 of its children, and the tree of its other child is pruned.
80 One quirk is that the top-level implementation object (m_top) always
81 keeps a pointer to the implementation object where a new child is needed.
82 (m_add_child_target). This is so that when a new uesr view is added
83 to the hierarchy, AddChild() is able to reparent the new user view to
84 the correct implementation object's leaf.
88 #include <wx/dcmemory.h>
89 #include <wx/dcscreen.h>
90 #include <wx/layout.h>
91 #include <wx/scrolbar.h>
92 #include <wx/settings.h>
95 #define wxEVT_DYNAMIC_SASH_PRIVATE (wxEVT_DYNAMIC_SASH_BASE + 8)
96 #define wxEVT_DYNAMIC_SASH_REPARENT (wxEVT_DYNAMIC_SASH_PRIVATE + 1)
100 wxDynamicSashReparentEvent is generated by the AddChild() method of
101 wxDynamicSashWindow when it wants a Leaf to reparent a user view window
102 to its viewport at some time in the future. We can't reparent the window
103 immediately, because switching parents in AddChild() confuses the wxWindow
104 class. Instead, we queue up this event, and the window is actually
105 reparented the next time we process events in the idle loop.
107 class wxDynamicSashReparentEvent
: public wxEvent
{
109 wxDynamicSashReparentEvent();
110 wxDynamicSashReparentEvent(wxObject
*object
);
111 wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
);
113 virtual wxEvent
* Clone() const { return new wxDynamicSashReparentEvent(*this); }
115 DECLARE_DYNAMIC_CLASS(wxDynamicSashReparentEvent
)
119 enum DynamicSashRegion
{
131 class wxDynamicSashWindowImpl
: public wxEvtHandler
{
133 wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
);
134 ~wxDynamicSashWindowImpl();
137 void AddChild(wxWindow
*window
);
138 void DrawSash(int x
, int y
) const;
139 void ConstrainChildren(int px
, int py
);
140 void Split(int x
, int y
);
141 void Unify(int panel
);
142 void Resize(int x
, int y
);
143 wxDynamicSashWindowImpl
*FindParent(DynamicSashRegion side
) const;
144 wxDynamicSashWindowImpl
*FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
145 wxDynamicSashWindowImpl
*sash_b
) const;
146 wxWindow
*FindFrame() const;
147 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
149 void OnSize(wxSizeEvent
&event
);
150 void OnPaint(wxPaintEvent
&event
);
151 void OnMouseMove(wxMouseEvent
&event
);
152 void OnLeave(wxMouseEvent
&event
);
153 void OnPress(wxMouseEvent
&event
);
154 void OnRelease(wxMouseEvent
&event
);
156 wxDynamicSashWindow
*m_window
;
157 wxDynamicSashWindowImpl
*m_add_child_target
;
159 /* This is the window we are responsible for managing. Either of
160 leaf or our children are in this window. For the top level
161 implementation object, this is the same as m_window.
162 Otherwise it is a window we've created an will destroy when we
164 wxWindow
*m_container
;
166 wxDynamicSashWindowImpl
*m_parent
;
167 wxDynamicSashWindowImpl
*m_top
;
168 wxDynamicSashWindowImpl
*m_child
[2];
170 class wxDynamicSashWindowLeaf
*m_leaf
;
172 /* If the implementation is split horizontally or vertically, m_split
173 is set to DSR_HORIZONTAL_TAB or DSR_VERTICAL_TAB, respectively.
174 Otherwise it is set to DSR_NONE. */
175 DynamicSashRegion m_split
;
177 /* These are used to keep track of a sash as it is being dragged, for
178 drawing the user interface correctly. */
179 DynamicSashRegion m_dragging
;
180 int m_drag_x
, m_drag_y
;
183 class wxDynamicSashWindowLeaf
: public wxEvtHandler
{
185 wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
);
186 ~wxDynamicSashWindowLeaf();
189 void AddChild(wxWindow
*window
);
190 DynamicSashRegion
GetRegion(int x
, int y
);
191 void ResizeChild(wxSize size
);
192 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
194 void OnSize(wxSizeEvent
&event
);
195 void OnPaint(wxPaintEvent
&event
);
196 void OnScroll(wxScrollEvent
&event
);
197 void OnFocus(wxFocusEvent
&event
);
198 void OnMouseMove(wxMouseEvent
&event
);
199 void OnLeave(wxMouseEvent
&event
);
200 void OnPress(wxMouseEvent
&event
);
201 void OnRelease(wxMouseEvent
&event
);
202 void OnReparent(wxEvent
&event
);
204 wxDynamicSashWindowImpl
*m_impl
;
206 wxScrollBar
*m_vscroll
, *m_hscroll
;
208 /* m_child is the window provided to us by the application developer.
209 m_viewport is a window we've created, and it is the immediately
210 parent of m_child. We scroll m_child by moving it around within
212 wxWindow
*m_viewport
, *m_child
;
215 // wxDynamicSashWindow //////////////////////////////////////////////////////
217 wxDynamicSashWindow::wxDynamicSashWindow() {
221 wxDynamicSashWindow::wxDynamicSashWindow(wxWindow
*parent
, wxWindowID id
,
222 const wxPoint
& pos
, const wxSize
& size
,
223 long style
, const wxString
& name
) {
225 Create(parent
, id
, pos
, size
, style
, name
);
228 wxDynamicSashWindow::~wxDynamicSashWindow() {
229 SetEventHandler(this);
233 bool wxDynamicSashWindow::Create(wxWindow
*parent
, wxWindowID id
,
234 const wxPoint
& pos
, const wxSize
& size
,
235 long style
, const wxString
& name
) {
239 if (!wxWindow::Create(parent
, id
, pos
, size
, style
, name
))
242 m_impl
= new wxDynamicSashWindowImpl(this);
246 if (!m_impl
->Create()) {
255 void wxDynamicSashWindow::AddChild(wxWindowBase
*child
) {
256 wxWindow::AddChild(child
);
258 m_impl
->AddChild(wxDynamicCast(child
, wxWindow
));
261 wxScrollBar
*wxDynamicSashWindow::GetHScrollBar(const wxWindow
*child
) const {
262 return m_impl
->FindScrollBar(child
, 0);
265 wxScrollBar
*wxDynamicSashWindow::GetVScrollBar(const wxWindow
*child
) const {
266 return m_impl
->FindScrollBar(child
, 1);
269 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashWindow
, wxWindow
)
271 // wxDynamicSashWindowImpl //////////////////////////////////////////////////
273 wxDynamicSashWindowImpl::wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
) {
275 m_add_child_target
= this;
280 m_child
[0] = m_child
[1] = NULL
;
282 m_dragging
= DSR_NONE
;
286 wxDynamicSashWindowImpl::~wxDynamicSashWindowImpl() {
294 if (m_container
!= m_window
&& m_container
) {
295 m_container
->SetEventHandler(m_container
);
296 m_container
->Destroy();
300 bool wxDynamicSashWindowImpl::Create() {
302 m_container
= m_window
;
305 wxCursor
cursor(wxCURSOR_ARROW
);
306 m_container
->SetCursor(cursor
);
308 m_leaf
= new wxDynamicSashWindowLeaf(this);
312 if (!m_leaf
->Create()) {
318 m_container
->SetEventHandler(this);
320 Connect(wxID_ANY
, wxEVT_SIZE
, (wxObjectEventFunction
)
322 (wxSizeEventFunction
)&wxDynamicSashWindowImpl::OnSize
);
323 Connect(wxID_ANY
, wxEVT_PAINT
, (wxObjectEventFunction
)
325 (wxPaintEventFunction
)&wxDynamicSashWindowImpl::OnPaint
);
326 Connect(wxID_ANY
, wxEVT_MOTION
, (wxObjectEventFunction
)
328 (wxMouseEventFunction
)&wxDynamicSashWindowImpl::OnMouseMove
);
329 Connect(wxID_ANY
, wxEVT_ENTER_WINDOW
, (wxObjectEventFunction
)
331 (wxMouseEventFunction
)&wxDynamicSashWindowImpl::OnMouseMove
);
332 Connect(wxID_ANY
, wxEVT_LEAVE_WINDOW
, (wxObjectEventFunction
)
334 (wxMouseEventFunction
)&wxDynamicSashWindowImpl::OnLeave
);
335 Connect(wxID_ANY
, wxEVT_LEFT_DOWN
, (wxObjectEventFunction
)
337 (wxMouseEventFunction
)&wxDynamicSashWindowImpl::OnPress
);
338 Connect(wxID_ANY
, wxEVT_LEFT_UP
, (wxObjectEventFunction
)
340 (wxMouseEventFunction
)&wxDynamicSashWindowImpl::OnRelease
);
345 void wxDynamicSashWindowImpl::AddChild(wxWindow
*window
) {
346 if (m_add_child_target
&& m_add_child_target
->m_leaf
) {
347 m_add_child_target
->m_leaf
->AddChild(window
);
351 void wxDynamicSashWindowImpl::DrawSash(int x
, int y
) const {
355 dc
.StartDrawingOnTop(m_container
);
359 bdc
.SelectObject(bmp
);
360 bdc
.DrawRectangle(-1, -1, 10, 10);
361 for (i
= 0; i
< 8; i
++) {
362 for (j
= 0; j
< 8; j
++) {
371 dc
.SetLogicalFunction(wxXOR
);
373 if ((m_dragging
== DSR_CORNER
) &&
374 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0) {
378 m_container
->ClientToScreen(&cx
, &cy
);
379 m_container
->ClientToScreen(&x
, &y
);
381 if (cx
< x
&& cy
< y
) {
382 dc
.DrawRectangle(cx
- 2, cy
- 2, x
- cx
+ 4, 4);
383 dc
.DrawRectangle(x
- 2, cy
+ 2, 4, y
- cy
);
384 dc
.DrawRectangle(cx
- 2, cy
+ 2, 4, y
- cy
);
385 dc
.DrawRectangle(cx
+ 2, y
- 2, x
- cx
- 4, 4);
389 m_container
->GetClientSize(&body_w
, &body_h
);
400 if (m_dragging
== DSR_HORIZONTAL_TAB
)
405 m_container
->ClientToScreen(&x
, &y
);
408 w
= body_w
; h
= body_h
;
410 if (m_dragging
== DSR_HORIZONTAL_TAB
)
411 dc
.DrawRectangle(x
, y
- 2, w
, 4);
413 dc
.DrawRectangle(x
- 2, y
, 4, h
);
416 dc
.EndDrawingOnTop();
419 wxDynamicSashWindowImpl
*wxDynamicSashWindowImpl::FindParent(DynamicSashRegion side
) const {
420 if (m_parent
== NULL
) {
424 if (m_parent
->m_split
== DSR_HORIZONTAL_TAB
) {
425 if (side
== DSR_TOP_EDGE
&& m_parent
->m_child
[1] == this)
427 if (side
== DSR_BOTTOM_EDGE
&& m_parent
->m_child
[0] == this)
429 } else if (m_parent
->m_split
== DSR_VERTICAL_TAB
) {
430 if (side
== DSR_LEFT_EDGE
&& m_parent
->m_child
[1] == this)
432 if (side
== DSR_RIGHT_EDGE
&& m_parent
->m_child
[0] == this)
436 return m_parent
->FindParent(side
);
439 wxDynamicSashWindowImpl
*wxDynamicSashWindowImpl::FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
440 wxDynamicSashWindowImpl
*sash_b
) const {
442 win
= sash_a
->m_container
->GetParent();
443 while (win
&& !win
->IsTopLevel()) {
444 if (win
== sash_b
->m_container
) {
448 win
= win
->GetParent();
455 wxWindow
*wxDynamicSashWindowImpl::FindFrame() const {
458 win
= m_window
->GetParent();
459 while (win
&& !win
->IsTopLevel()
461 && ! wxIsKindOf(win
, wxMDIChildFrame
) // not top-level but still a frame
464 win
= win
->GetParent();
470 wxScrollBar
*wxDynamicSashWindowImpl::FindScrollBar(const wxWindow
*child
, int vert
) const {
471 if (m_child
[0] == NULL
&& m_leaf
== NULL
) {
476 return m_leaf
->FindScrollBar(child
, vert
);
478 wxScrollBar
*ret
= m_child
[0]->FindScrollBar(child
, vert
);
480 ret
= m_child
[1]->FindScrollBar(child
, vert
);
487 void wxDynamicSashWindowImpl::ConstrainChildren(int px
, int py
) {
488 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
489 layout
->left
.SameAs(m_container
, wxLeft
);
490 layout
->top
.SameAs(m_container
, wxTop
);
491 if (m_split
== DSR_HORIZONTAL_TAB
) {
492 layout
->right
.SameAs(m_container
, wxRight
);
493 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
495 layout
->bottom
.SameAs(m_container
, wxBottom
);
496 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
498 m_child
[0]->m_container
->SetConstraints(layout
);
500 layout
= new wxLayoutConstraints();
501 layout
->right
.SameAs(m_container
, wxRight
);
502 layout
->bottom
.SameAs(m_container
, wxBottom
);
503 if (m_split
== DSR_HORIZONTAL_TAB
) {
504 layout
->top
.Below(m_child
[0]->m_container
, 1);
505 layout
->left
.SameAs(m_container
, wxLeft
);
507 layout
->left
.RightOf(m_child
[0]->m_container
, 1);
508 layout
->top
.SameAs(m_container
, wxTop
);
510 m_child
[1]->m_container
->SetConstraints(layout
);
513 void wxDynamicSashWindowImpl::Unify(int panel
) {
519 if (m_child
[panel
]->m_leaf
) {
520 wxDynamicSashWindowImpl
*child
[2];
522 child
[0] = m_child
[0];
523 child
[1] = m_child
[1];
525 m_child
[0] = m_child
[1] = NULL
;
527 m_leaf
= new wxDynamicSashWindowLeaf(this);
529 m_leaf
->m_child
= child
[panel
]->m_leaf
->m_child
;
531 m_leaf
->m_vscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_vscroll
->GetThumbPosition(),
532 child
[panel
]->m_leaf
->m_vscroll
->GetThumbSize(),
533 child
[panel
]->m_leaf
->m_vscroll
->GetRange(),
534 child
[panel
]->m_leaf
->m_vscroll
->GetPageSize());
535 m_leaf
->m_hscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_hscroll
->GetThumbPosition(),
536 child
[panel
]->m_leaf
->m_hscroll
->GetThumbSize(),
537 child
[panel
]->m_leaf
->m_hscroll
->GetRange(),
538 child
[panel
]->m_leaf
->m_hscroll
->GetPageSize());
539 m_add_child_target
= NULL
;
540 wxDynamicSashReparentEvent
event(m_leaf
);
541 m_leaf
->ProcessEvent(event
);
548 wxDynamicSashUnifyEvent
unify(m_leaf
->m_child
);
549 m_leaf
->m_child
->ProcessEvent(unify
);
551 m_split
= m_child
[panel
]->m_split
;
553 delete m_child
[other
];
555 wxDynamicSashWindowImpl
*child_panel
= m_child
[panel
];
556 m_child
[0] = child_panel
->m_child
[0];
557 m_child
[1] = child_panel
->m_child
[1];
559 m_child
[0]->m_parent
= this;
560 m_child
[1]->m_parent
= this;
562 m_add_child_target
= NULL
;
563 m_child
[0]->m_container
->Reparent(m_container
);
564 m_child
[1]->m_container
->Reparent(m_container
);
566 child_panel
->m_child
[0] = child_panel
->m_child
[1] = NULL
;
569 wxSize size
= m_container
->GetSize();
570 wxSize child_size
= m_child
[0]->m_container
->GetSize();
572 ConstrainChildren(child_size
.GetWidth() * 100 / size
.GetWidth(),
573 child_size
.GetHeight() * 100 / size
.GetHeight());
575 m_container
->Layout();
579 void wxDynamicSashWindowImpl::Split(int px
, int py
) {
581 m_add_child_target
= NULL
;
583 m_child
[0] = new wxDynamicSashWindowImpl(m_window
);
584 m_child
[0]->m_container
= new wxWindow(m_container
, wxID_ANY
);
585 m_child
[0]->m_parent
= this;
586 m_child
[0]->m_top
= m_top
;
587 m_child
[0]->Create();
588 if (m_leaf
->m_child
) {
589 m_leaf
->m_child
->Reparent(m_container
);
590 m_child
[0]->AddChild(m_leaf
->m_child
);
593 m_child
[1] = new wxDynamicSashWindowImpl(m_window
);
594 m_child
[1]->m_container
= new wxWindow(m_container
, wxID_ANY
);
595 m_child
[1]->m_parent
= this;
596 m_child
[1]->m_top
= m_top
;
597 m_child
[1]->Create();
599 m_split
= m_dragging
;
600 ConstrainChildren(px
, py
);
602 m_top
->m_add_child_target
= m_child
[1];
603 wxDynamicSashSplitEvent
split(m_child
[0]->m_leaf
->m_child
);
604 m_child
[0]->m_leaf
->m_child
->ProcessEvent(split
);
606 m_child
[0]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
607 m_leaf
->m_vscroll
->GetThumbSize(),
608 m_leaf
->m_vscroll
->GetRange(),
609 m_leaf
->m_vscroll
->GetPageSize());
610 m_child
[0]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
611 m_leaf
->m_hscroll
->GetThumbSize(),
612 m_leaf
->m_hscroll
->GetRange(),
613 m_leaf
->m_hscroll
->GetPageSize());
614 m_child
[1]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
615 m_leaf
->m_vscroll
->GetThumbSize(),
616 m_leaf
->m_vscroll
->GetRange(),
617 m_leaf
->m_vscroll
->GetPageSize());
618 m_child
[1]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
619 m_leaf
->m_hscroll
->GetThumbSize(),
620 m_leaf
->m_hscroll
->GetRange(),
621 m_leaf
->m_hscroll
->GetPageSize());
625 m_container
->Layout();
629 /* This code is called when you finish resizing a view by dragging the
630 corner tab, but I think this implementation is lousy and will surprise
631 the user more often than it will do what they are trying to do. What
632 I really need to be doing here is do a rewrite such that *no* sashes
633 move except the ones immediately to the bottom and right of this window,
634 and handle the case where you resize a window larger than it's neighbors
635 by destroying the neighbors.
637 But this will do for now. */
638 void wxDynamicSashWindowImpl::Resize(int x
, int y
) {
639 wxDynamicSashWindowImpl
*h_parent
= FindParent(DSR_BOTTOM_EDGE
);
640 wxDynamicSashWindowImpl
*v_parent
= FindParent(DSR_RIGHT_EDGE
);
643 wxWindow
*frame
= FindFrame();
653 m_container
->ClientToScreen(NULL
, &y
);
654 h_parent
->m_container
->ScreenToClient(NULL
, &y
);
656 int py
= (int)((y
* 100) / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
659 wxDynamicSashWindowImpl
*ho_parent
= FindParent(DSR_TOP_EDGE
);
662 if (FindUpperParent(h_parent
, ho_parent
) == ho_parent
) {
665 py
= (int)((ho_parent
->m_child
[0]->m_container
->GetSize().GetHeight() * 100)
666 / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
667 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
668 h_parent
->m_container
, wxHeight
, py
);
670 h_parent
= ho_parent
;
676 } else if (py
> 90) {
679 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
680 h_parent
->m_container
, wxHeight
, py
);
681 h_parent
->m_container
->Layout();
685 h_parent
= FindParent(DSR_TOP_EDGE
);
688 int py
= (int)((y
* 100) /
689 (h_parent
->m_container
->GetSize().GetHeight() +
690 y
- m_container
->GetSize().GetHeight()) + 0.5);
700 wxSize size
= frame
->GetSize();
701 frame
->SetSize(size
.GetWidth(), size
.GetHeight() + y
- m_container
->GetSize().GetHeight());
706 m_container
->ClientToScreen(&x
, NULL
);
707 v_parent
->m_container
->ScreenToClient(&x
, NULL
);
709 int px
= (int)((x
* 100) / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
712 wxDynamicSashWindowImpl
*vo_parent
= FindParent(DSR_LEFT_EDGE
);
715 if (FindUpperParent(v_parent
, vo_parent
) == vo_parent
) {
718 px
= (int)((vo_parent
->m_child
[0]->m_container
->GetSize().GetWidth() * 100)
719 / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
720 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
721 v_parent
->m_container
, wxWidth
, px
);
723 v_parent
= vo_parent
;
729 } else if (px
> 90) {
732 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
733 v_parent
->m_container
, wxWidth
, px
);
734 v_parent
->m_container
->Layout();
738 v_parent
= FindParent(DSR_LEFT_EDGE
);
741 int px
= (int)((x
* 100) /
742 (v_parent
->m_container
->GetSize().GetWidth() +
743 x
- m_container
->GetSize().GetWidth()) + 0.5);
753 wxSize size
= frame
->GetSize();
754 frame
->SetSize(size
.GetWidth() + x
- m_container
->GetSize().GetWidth(), size
.GetHeight());
758 if (h_unify
!= -1 && v_unify
!= -1) {
759 wxDynamicSashWindowImpl
*parent
= FindUpperParent(h_parent
, v_parent
);
761 if (parent
== h_parent
) {
762 h_parent
->Unify(h_unify
);
764 v_parent
->Unify(v_unify
);
766 } else if (h_unify
!= -1) {
767 h_parent
->Unify(h_unify
);
768 } else if (v_unify
!= -1) {
769 v_parent
->Unify(v_unify
);
774 void wxDynamicSashWindowImpl::OnSize(wxSizeEvent
&event
) {
775 m_container
->Layout();
778 m_leaf
->OnSize(event
);
781 void wxDynamicSashWindowImpl::OnPaint(wxPaintEvent
&event
) {
783 m_leaf
->OnPaint(event
);
785 wxPaintDC
dc(m_container
);
786 dc
.SetBackground(wxBrush(m_container
->GetBackgroundColour(), wxSOLID
));
791 void wxDynamicSashWindowImpl::OnMouseMove(wxMouseEvent
&event
) {
793 DrawSash(m_drag_x
, m_drag_y
);
794 m_drag_x
= event
.m_x
; m_drag_y
= event
.m_y
;
795 DrawSash(m_drag_x
, m_drag_y
);
797 m_leaf
->OnMouseMove(event
);
801 void wxDynamicSashWindowImpl::OnLeave(wxMouseEvent
&event
) {
803 m_leaf
->OnLeave(event
);
807 void wxDynamicSashWindowImpl::OnPress(wxMouseEvent
&event
) {
809 m_leaf
->OnPress(event
);
811 m_dragging
= m_split
;
812 m_drag_x
= event
.m_x
;
813 m_drag_y
= event
.m_y
;
814 DrawSash(m_drag_x
, m_drag_y
);
815 m_container
->CaptureMouse();
819 void wxDynamicSashWindowImpl::OnRelease(wxMouseEvent
&event
) {
820 if ((m_dragging
== DSR_CORNER
) &&
821 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0) {
822 DrawSash(m_drag_x
, m_drag_y
);
823 m_container
->ReleaseMouse();
825 Resize(event
.m_x
, event
.m_y
);
827 m_dragging
= DSR_NONE
;
828 } else if (m_dragging
) {
829 DrawSash(m_drag_x
, m_drag_y
);
830 m_container
->ReleaseMouse();
832 wxSize size
= m_container
->GetSize();
833 int px
= (int)((event
.m_x
* 100) / size
.GetWidth() + 0.5);
834 int py
= (int)((event
.m_y
* 100) / size
.GetHeight() + 0.5);
836 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
>= 10 && py
<= 90)
837 || (m_dragging
== DSR_VERTICAL_TAB
&& px
>= 10 && px
<= 90)) {
838 if (m_child
[0] == NULL
) {
841 /* It would be nice if moving *this* sash didn't implicitly move
842 the sashes of our children (if any). But this will do. */
843 wxLayoutConstraints
*layout
= m_child
[0]->m_container
->GetConstraints();
844 if (m_split
== DSR_HORIZONTAL_TAB
) {
845 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
847 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
849 m_container
->Layout();
852 if (m_child
[0] != NULL
) {
853 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
<= 10)
854 || (m_dragging
== DSR_VERTICAL_TAB
&& px
<= 10)) {
862 wxCursor
cursor(wxCURSOR_ARROW
);
863 if (m_split
== DSR_HORIZONTAL_TAB
) {
864 cursor
= wxCursor(wxCURSOR_SIZENS
);
865 } else if (m_split
== DSR_VERTICAL_TAB
) {
866 cursor
= wxCursor(wxCURSOR_SIZEWE
);
868 m_container
->SetCursor(cursor
);
870 m_dragging
= DSR_NONE
;
872 m_leaf
->OnRelease(event
);
876 // wxDynamicSashWindowLeaf //////////////////////////////////////////////////
878 wxDynamicSashWindowLeaf::wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
) {
881 m_hscroll
= m_vscroll
= NULL
;
885 wxDynamicSashWindowLeaf::~wxDynamicSashWindowLeaf() {
886 m_hscroll
->SetEventHandler(m_hscroll
);
887 m_vscroll
->SetEventHandler(m_vscroll
);
888 m_viewport
->SetEventHandler(m_viewport
);
890 m_hscroll
->Destroy();
891 m_vscroll
->Destroy();
892 m_viewport
->Destroy();
895 bool wxDynamicSashWindowLeaf::Create() {
898 m_hscroll
= new wxScrollBar();
899 m_vscroll
= new wxScrollBar();
900 m_viewport
= new wxWindow();
902 if (!m_hscroll
|| !m_vscroll
|| !m_viewport
) {
906 wxDynamicSashWindowImpl
*add_child_target
= m_impl
->m_add_child_target
;
907 m_impl
->m_add_child_target
= NULL
;
908 success
= m_hscroll
->Create(m_impl
->m_container
, wxID_ANY
, wxDefaultPosition
, wxDefaultSize
,
910 success
= success
&& m_vscroll
->Create(m_impl
->m_container
, wxID_ANY
, wxDefaultPosition
, wxDefaultSize
,
912 success
= success
&& m_viewport
->Create(m_impl
->m_container
, wxID_ANY
);
913 m_impl
->m_add_child_target
= add_child_target
;
915 wxCursor
cursor(wxCURSOR_ARROW
);
916 m_hscroll
->SetCursor(cursor
);
917 m_vscroll
->SetCursor(cursor
);
918 m_viewport
->SetCursor(cursor
);
920 m_viewport
->SetEventHandler(this);
921 Connect(wxID_ANY
, wxEVT_DYNAMIC_SASH_REPARENT
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnReparent
);
923 if (m_impl
->m_window
->GetWindowStyle() & wxDS_MANAGE_SCROLLBARS
) {
924 m_hscroll
->SetEventHandler(this);
925 m_vscroll
->SetEventHandler(this);
927 Connect(wxID_ANY
, wxEVT_SET_FOCUS
, (wxObjectEventFunction
)
929 (wxFocusEventFunction
)&wxDynamicSashWindowLeaf::OnFocus
);
930 Connect(wxID_ANY
, wxEVT_SCROLL_TOP
, (wxObjectEventFunction
)
932 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
933 Connect(wxID_ANY
, wxEVT_SCROLL_BOTTOM
, (wxObjectEventFunction
)
935 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
936 Connect(wxID_ANY
, wxEVT_SCROLL_LINEUP
, (wxObjectEventFunction
)
938 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
939 Connect(wxID_ANY
, wxEVT_SCROLL_LINEDOWN
, (wxObjectEventFunction
)
941 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
942 Connect(wxID_ANY
, wxEVT_SCROLL_PAGEUP
, (wxObjectEventFunction
)
944 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
945 Connect(wxID_ANY
, wxEVT_SCROLL_PAGEDOWN
, (wxObjectEventFunction
)
947 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
948 Connect(wxID_ANY
, wxEVT_SCROLL_THUMBTRACK
, (wxObjectEventFunction
)
950 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
951 Connect(wxID_ANY
, wxEVT_SCROLL_THUMBRELEASE
, (wxObjectEventFunction
)
953 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
956 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
959 wxSize size
= m_hscroll
->GetBestSize();
961 size
= m_hscroll
->GetSize();
964 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 10);
965 layout
->right
.LeftOf(m_vscroll
);
966 layout
->bottom
.SameAs(m_impl
->m_container
, wxBottom
, 3);
967 layout
->height
.Absolute(size
.GetHeight());
968 m_hscroll
->SetConstraints(layout
);
970 layout
= new wxLayoutConstraints();
973 size
= size
= m_vscroll
->GetBestSize();
975 size
= m_vscroll
->GetSize();
978 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 10);
979 layout
->bottom
.Above(m_hscroll
);
980 layout
->right
.SameAs(m_impl
->m_container
, wxRight
, 3);
981 layout
->width
.Absolute(size
.GetWidth());
982 m_vscroll
->SetConstraints(layout
);
984 layout
= new wxLayoutConstraints();
987 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 3);
988 layout
->right
.LeftOf(m_vscroll
);
989 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 3);
990 layout
->bottom
.Above(m_hscroll
);
991 m_viewport
->SetConstraints(layout
);
993 m_impl
->m_container
->Layout();
998 void wxDynamicSashWindowLeaf::AddChild(wxWindow
*window
) {
1005 wxDynamicSashReparentEvent
event(this);
1006 AddPendingEvent(event
);
1009 DynamicSashRegion
wxDynamicSashWindowLeaf::GetRegion(int x
, int y
) {
1010 wxSize size
= m_impl
->m_container
->GetSize();
1011 int w
= size
.GetWidth();
1012 int h
= size
.GetHeight();
1013 size
= m_hscroll
->GetSize();
1014 int sh
= size
.GetHeight();
1015 size
= m_vscroll
->GetSize();
1016 int sw
= size
.GetWidth();
1018 if (x
>= w
- sw
- 3 && x
< w
&& y
>= h
- sh
- 3 && y
< h
)
1020 if (x
>= 3 && x
< 10 && y
>= h
- sh
- 3 && y
< h
- 2)
1021 return DSR_VERTICAL_TAB
;
1022 if (x
>= w
- sw
- 3 && x
< w
- 2 && y
>= 3 && y
< 10)
1023 return DSR_HORIZONTAL_TAB
;
1025 return DSR_LEFT_EDGE
;
1027 return DSR_TOP_EDGE
;
1029 return DSR_RIGHT_EDGE
;
1031 return DSR_BOTTOM_EDGE
;
1036 void wxDynamicSashWindowLeaf::ResizeChild(wxSize size
) {
1038 if (m_impl
->m_window
->GetWindowStyle() & wxDS_MANAGE_SCROLLBARS
) {
1039 m_child
->SetSize(size
);
1040 wxSize best_size
= m_child
->GetBestSize();
1041 if (best_size
.GetWidth() < size
.GetWidth()) {
1042 best_size
.SetWidth(size
.GetWidth());
1044 if (best_size
.GetHeight() < size
.GetHeight()) {
1045 best_size
.SetHeight(size
.GetHeight());
1047 m_child
->SetSize(best_size
);
1049 int hpos
= m_hscroll
->GetThumbPosition();
1050 int vpos
= m_vscroll
->GetThumbPosition();
1058 if (hpos
> best_size
.GetWidth() - size
.GetWidth()) {
1059 hpos
= best_size
.GetWidth() - size
.GetWidth();
1061 if (vpos
> best_size
.GetHeight() - size
.GetHeight()) {
1062 vpos
= best_size
.GetHeight() - size
.GetHeight();
1065 m_hscroll
->SetScrollbar(hpos
, size
.GetWidth(),
1066 best_size
.GetWidth(), size
.GetWidth());
1067 m_vscroll
->SetScrollbar(vpos
, size
.GetHeight(),
1068 best_size
.GetHeight(), size
.GetHeight());
1070 // Umm, the scrollbars are doing something insane under GTK+ and subtracting
1071 // one from the position I pass in. This works around that.
1072 m_hscroll
->SetThumbPosition(hpos
+ hpos
- m_hscroll
->GetThumbPosition());
1073 m_vscroll
->SetThumbPosition(vpos
+ vpos
- m_vscroll
->GetThumbPosition());
1075 wxPoint pos
= m_child
->GetPosition();
1076 m_viewport
->ScrollWindow(-hpos
- pos
.x
, -vpos
- pos
.y
);
1078 m_child
->SetSize(size
);
1083 wxScrollBar
*wxDynamicSashWindowLeaf::FindScrollBar(const wxWindow
*child
, int vert
) const {
1084 if (m_child
== child
) {
1095 void wxDynamicSashWindowLeaf::OnSize(wxSizeEvent
&WXUNUSED(event
)) {
1096 m_impl
->m_container
->Refresh();
1097 ResizeChild(m_viewport
->GetSize());
1100 void wxDynamicSashWindowLeaf::OnPaint(wxPaintEvent
&WXUNUSED(event
)) {
1101 wxPaintDC
dc(m_impl
->m_container
);
1102 dc
.SetBackground(wxBrush(m_impl
->m_container
->GetBackgroundColour(), wxSOLID
));
1105 wxPen
highlight(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT
), 1, wxSOLID
);
1106 wxPen
shadow(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW
), 1, wxSOLID
);
1107 wxPen
black(*wxBLACK
, 1, wxSOLID
);
1109 wxSize size
= m_impl
->m_container
->GetSize();
1110 int w
= size
.GetWidth();
1111 int h
= size
.GetHeight();
1112 size
= m_hscroll
->GetSize();
1113 int sh
= size
.GetHeight();
1114 size
= m_vscroll
->GetSize();
1115 int sw
= size
.GetWidth();
1118 dc
.DrawLine(1, 1, 1, h
- 2);
1119 dc
.DrawLine(1, 1, w
- 2, 1);
1121 dc
.DrawLine(2, 2, 2, h
- 3);
1122 dc
.DrawLine(2, 2, w
- 3, 2);
1123 dc
.SetPen(highlight
);
1124 dc
.DrawLine(w
- 2, 2, w
- 2, h
- sh
- 2);
1125 dc
.DrawLine(w
- 2, h
- sh
- 2, w
- sw
- 2, h
- sh
- 2);
1126 dc
.DrawLine(w
- sw
- 2, h
- sh
- 2, w
- sw
- 2, h
- 2);
1127 dc
.DrawLine(w
- sw
- 2, h
- 2, 2, h
- 2);
1129 dc
.SetPen(highlight
);
1130 dc
.DrawLine(w
- sw
- 2, 8, w
- sw
- 2, 4);
1131 dc
.DrawLine(w
- sw
- 2, 4, w
- 5, 4);
1133 dc
.DrawLine(w
- 5, 4, w
- 5, 8);
1134 dc
.DrawLine(w
- 5, 8, w
- sw
- 2, 8);
1136 dc
.DrawLine(w
- 4, 3, w
- 4, 9);
1137 dc
.DrawLine(w
- 4, 9, w
- sw
- 3, 9);
1139 dc
.SetPen(highlight
);
1140 dc
.DrawLine(4, h
- 5, 4, h
- sh
- 2);
1141 dc
.DrawLine(4, h
- sh
- 2, 8, h
- sh
- 2);
1143 dc
.DrawLine(8, h
- sh
- 2, 8, h
- 5);
1144 dc
.DrawLine(8, h
- 5, 4, h
- 5);
1146 dc
.DrawLine(9, h
- sh
- 3, 9, h
- 4);
1147 dc
.DrawLine(9, h
- 4, 3, h
- 4);
1149 int cy
= (h
- sh
+ h
- 6) / 2 + 1;
1150 int cx
= (w
- sw
+ w
- 6) / 2 + 1;
1159 for (y
= sy
; y
< h
- 2; y
+= 4) {
1160 for (x
= sx
; x
< w
- 2; x
+= 4) {
1161 if (x
- cx
>= -(y
- cy
)) {
1162 dc
.SetPen(highlight
);
1165 dc
.DrawPoint(x
+ 1, y
+ 1);
1171 void wxDynamicSashWindowLeaf::OnScroll(wxScrollEvent
&WXUNUSED(event
)) {
1172 int nx
= -m_hscroll
->GetThumbPosition();
1173 int ny
= -m_vscroll
->GetThumbPosition();
1176 wxPoint pos
= m_child
->GetPosition();
1178 m_viewport
->ScrollWindow(nx
- pos
.x
, ny
- pos
.y
);
1182 void wxDynamicSashWindowLeaf::OnFocus(wxFocusEvent
&event
) {
1183 if (event
.GetEventObject() == m_hscroll
|| event
.GetEventObject() == m_vscroll
) {
1184 m_child
->SetFocus();
1189 void wxDynamicSashWindowLeaf::OnMouseMove(wxMouseEvent
&event
) {
1190 if (m_impl
->m_dragging
) {
1194 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1196 wxCursor
cursor(wxCURSOR_ARROW
);
1197 if (region
== DSR_HORIZONTAL_TAB
) {
1198 cursor
= wxCursor(wxCURSOR_SIZENS
);
1199 } else if (region
== DSR_VERTICAL_TAB
) {
1200 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1201 } else if ((region
== DSR_CORNER
) &&
1202 (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0) {
1203 cursor
= wxCursor(wxCURSOR_SIZENWSE
);
1204 } else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1205 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
) {
1206 if (m_impl
->FindParent(region
)) {
1207 if (region
== DSR_LEFT_EDGE
|| region
== DSR_RIGHT_EDGE
) {
1208 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1210 cursor
= wxCursor(wxCURSOR_SIZENS
);
1215 m_impl
->m_container
->SetCursor(cursor
);
1218 void wxDynamicSashWindowLeaf::OnLeave(wxMouseEvent
&WXUNUSED(event
)) {
1219 wxCursor
cursor(wxCURSOR_ARROW
);
1220 m_impl
->m_container
->SetCursor(cursor
);
1224 void wxDynamicSashWindowLeaf::OnPress(wxMouseEvent
&event
) {
1225 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1227 if ((region
== DSR_CORNER
) && (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) == 0)
1230 if (region
== DSR_HORIZONTAL_TAB
|| region
== DSR_VERTICAL_TAB
|| region
== DSR_CORNER
) {
1231 m_impl
->m_dragging
= region
;
1232 m_impl
->m_drag_x
= event
.m_x
;
1233 m_impl
->m_drag_y
= event
.m_y
;
1234 m_impl
->DrawSash(event
.m_x
, event
.m_y
);
1235 m_impl
->m_container
->CaptureMouse();
1236 } else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1237 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
) {
1238 wxDynamicSashWindowImpl
*parent
= m_impl
->FindParent(region
);
1244 m_impl
->m_container
->ClientToScreen(&x
, &y
);
1245 parent
->m_container
->ScreenToClient(&x
, &y
);
1247 parent
->m_dragging
= parent
->m_split
;
1248 parent
->m_drag_x
= x
;
1249 parent
->m_drag_y
= y
;
1250 parent
->DrawSash(x
, y
);
1251 parent
->m_container
->CaptureMouse();
1256 void wxDynamicSashWindowLeaf::OnRelease(wxMouseEvent
&WXUNUSED(event
)) {
1259 void wxDynamicSashWindowLeaf::OnReparent(wxEvent
&WXUNUSED(event
)) {
1261 m_child
->Reparent(m_viewport
);
1264 ResizeChild(m_viewport
->GetSize());
1267 // wxDynamicSashSplitEvent //////////////////////////////////////////////////
1269 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent() {
1270 m_eventObject
= NULL
;
1271 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1274 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent(wxObject
*object
) {
1275 m_eventObject
= object
;
1276 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1279 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashSplitEvent
, wxCommandEvent
)
1281 // wxDynamicSashUnifyEvent //////////////////////////////////////////////////
1283 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent() {
1284 m_eventObject
= NULL
;
1285 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1288 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent(wxObject
*object
) {
1289 m_eventObject
= object
;
1290 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1293 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashUnifyEvent
, wxCommandEvent
)
1295 // wxDynamicSsahReparentEvent ///////////////////////////////////////////////
1297 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent() {
1298 m_eventObject
= NULL
;
1299 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1302 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(wxObject
*object
) {
1303 m_eventObject
= object
;
1304 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1307 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
)
1312 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashReparentEvent
, wxEvent
)
1314 /////////////////////////////////////////////////////////////////////////////