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)
30 #include "wx/gizmos/dynamicsash.h"
33 const wxChar
* wxDynamicSashWindowNameStr
= wxT("dynamicSashWindow");
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
46 A resulting tree might look something like this:
50 +- wxDynamicSashWindowImpl
52 +- wxDynamicSashWindowLeaf
56 +- wxDynamicSashWindowImpl
58 +- wxDynamicSashWindowLeaf
62 +- wxDynamicSashWindowLeaf
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
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.
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.
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>
91 #define wxEVT_DYNAMIC_SASH_PRIVATE (wxEVT_DYNAMIC_SASH_BASE + 8)
92 #define wxEVT_DYNAMIC_SASH_REPARENT (wxEVT_DYNAMIC_SASH_PRIVATE + 1)
96 wxDynamicSashReparentEvent is generated by the AddChild() method of
97 wxDynamicSashWindow when it wants a Leaf to reparent a user view window
98 to its viewport at some time in the future. We can't reparent the window
99 immediately, because switching parents in AddChild() confuses the wxWindow
100 class. Instead, we queue up this event, and the window is actually
101 reparented the next time we process events in the idle loop.
103 class wxDynamicSashReparentEvent
: public wxEvent
{
105 wxDynamicSashReparentEvent();
106 wxDynamicSashReparentEvent(wxObject
*object
);
107 wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
);
109 virtual wxEvent
* Clone() const { return new wxDynamicSashReparentEvent(*this); }
111 DECLARE_DYNAMIC_CLASS(wxDynamicSashReparentEvent
);
115 enum DynamicSashRegion
{
127 class wxDynamicSashWindowImpl
: public wxEvtHandler
{
129 wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
);
130 ~wxDynamicSashWindowImpl();
133 void AddChild(wxWindow
*window
);
134 void DrawSash(int x
, int y
) const;
135 void ConstrainChildren(int px
, int py
);
136 void Split(int x
, int y
);
137 void Unify(int panel
);
138 void Resize(int x
, int y
);
139 wxDynamicSashWindowImpl
*FindParent(DynamicSashRegion side
) const;
140 wxDynamicSashWindowImpl
*FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
141 wxDynamicSashWindowImpl
*sash_b
) const;
142 wxWindow
*FindFrame() const;
143 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
145 void OnSize(wxSizeEvent
&event
);
146 void OnPaint(wxPaintEvent
&event
);
147 void OnMouseMove(wxMouseEvent
&event
);
148 void OnLeave(wxMouseEvent
&event
);
149 void OnPress(wxMouseEvent
&event
);
150 void OnRelease(wxMouseEvent
&event
);
152 wxDynamicSashWindow
*m_window
;
153 wxDynamicSashWindowImpl
*m_add_child_target
;
155 /* This is the window we are responsible for managing. Either of
156 leaf or our children are in this window. For the top level
157 implementation object, this is the same as m_window.
158 Otherwise it is a window we've created an will destroy when we
160 wxWindow
*m_container
;
162 wxDynamicSashWindowImpl
*m_parent
;
163 wxDynamicSashWindowImpl
*m_top
;
164 wxDynamicSashWindowImpl
*m_child
[2];
166 class wxDynamicSashWindowLeaf
*m_leaf
;
168 /* If the implementation is split horizontally or vertically, m_split
169 is set to DSR_HORIZONTAL_TAB or DSR_VERTICAL_TAB, respectively.
170 Otherwise it is set to DSR_NONE. */
171 DynamicSashRegion m_split
;
173 /* These are used to keep track of a sash as it is being dragged, for
174 drawing the user interface correctly. */
175 DynamicSashRegion m_dragging
;
176 int m_drag_x
, m_drag_y
;
179 class wxDynamicSashWindowLeaf
: public wxEvtHandler
{
181 wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
);
182 ~wxDynamicSashWindowLeaf();
185 void AddChild(wxWindow
*window
);
186 DynamicSashRegion
GetRegion(int x
, int y
);
187 void ResizeChild(wxSize size
);
188 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
190 void OnSize(wxSizeEvent
&event
);
191 void OnPaint(wxPaintEvent
&event
);
192 void OnScroll(wxScrollEvent
&event
);
193 void OnFocus(wxFocusEvent
&event
);
194 void OnMouseMove(wxMouseEvent
&event
);
195 void OnLeave(wxMouseEvent
&event
);
196 void OnPress(wxMouseEvent
&event
);
197 void OnRelease(wxMouseEvent
&event
);
198 void OnReparent(wxEvent
&event
);
200 wxDynamicSashWindowImpl
*m_impl
;
202 wxScrollBar
*m_vscroll
, *m_hscroll
;
204 /* m_child is the window provided to us by the application developer.
205 m_viewport is a window we've created, and it is the immediately
206 parent of m_child. We scroll m_child by moving it around within
208 wxWindow
*m_viewport
, *m_child
;
211 // wxDynamicSashWindow //////////////////////////////////////////////////////
213 wxDynamicSashWindow::wxDynamicSashWindow() {
217 wxDynamicSashWindow::wxDynamicSashWindow(wxWindow
*parent
, wxWindowID id
,
218 const wxPoint
& pos
, const wxSize
& size
,
219 long style
, const wxString
& name
) {
221 Create(parent
, id
, pos
, size
, style
, name
);
224 wxDynamicSashWindow::~wxDynamicSashWindow() {
225 SetEventHandler(this);
229 bool wxDynamicSashWindow::Create(wxWindow
*parent
, wxWindowID id
,
230 const wxPoint
& pos
, const wxSize
& size
,
231 long style
, const wxString
& name
) {
235 if (!wxWindow::Create(parent
, id
, pos
, size
, style
, name
))
238 m_impl
= new wxDynamicSashWindowImpl(this);
242 if (!m_impl
->Create()) {
251 void wxDynamicSashWindow::AddChild(wxWindowBase
*child
) {
252 wxWindow::AddChild(child
);
254 m_impl
->AddChild(wxDynamicCast(child
, wxWindow
));
257 wxScrollBar
*wxDynamicSashWindow::GetHScrollBar(const wxWindow
*child
) const {
258 return m_impl
->FindScrollBar(child
, 0);
261 wxScrollBar
*wxDynamicSashWindow::GetVScrollBar(const wxWindow
*child
) const {
262 return m_impl
->FindScrollBar(child
, 1);
265 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashWindow
, wxWindow
)
267 // wxDynamicSashWindowImpl //////////////////////////////////////////////////
269 wxDynamicSashWindowImpl::wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
) {
271 m_add_child_target
= this;
276 m_child
[0] = m_child
[1] = NULL
;
278 m_dragging
= DSR_NONE
;
282 wxDynamicSashWindowImpl::~wxDynamicSashWindowImpl() {
290 if (m_container
!= m_window
&& m_container
) {
291 m_container
->SetEventHandler(m_container
);
292 m_container
->Destroy();
296 bool wxDynamicSashWindowImpl::Create() {
298 m_container
= m_window
;
301 wxCursor
cursor(wxCURSOR_ARROW
);
302 m_container
->SetCursor(cursor
);
304 m_leaf
= new wxDynamicSashWindowLeaf(this);
308 if (!m_leaf
->Create()) {
314 m_container
->SetEventHandler(this);
316 Connect(wxID_ANY
, wxEVT_SIZE
, (wxObjectEventFunction
)
318 (wxSizeEventFunction
)&wxDynamicSashWindowImpl::OnSize
);
319 Connect(wxID_ANY
, wxEVT_PAINT
, (wxObjectEventFunction
)
321 (wxPaintEventFunction
)&wxDynamicSashWindowImpl::OnPaint
);
322 Connect(wxID_ANY
, wxEVT_MOTION
, (wxObjectEventFunction
)
324 (wxMouseEventFunction
)&wxDynamicSashWindowImpl::OnMouseMove
);
325 Connect(wxID_ANY
, wxEVT_ENTER_WINDOW
, (wxObjectEventFunction
)
327 (wxMouseEventFunction
)&wxDynamicSashWindowImpl::OnMouseMove
);
328 Connect(wxID_ANY
, wxEVT_LEAVE_WINDOW
, (wxObjectEventFunction
)
330 (wxMouseEventFunction
)&wxDynamicSashWindowImpl::OnLeave
);
331 Connect(wxID_ANY
, wxEVT_LEFT_DOWN
, (wxObjectEventFunction
)
333 (wxMouseEventFunction
)&wxDynamicSashWindowImpl::OnPress
);
334 Connect(wxID_ANY
, wxEVT_LEFT_UP
, (wxObjectEventFunction
)
336 (wxMouseEventFunction
)&wxDynamicSashWindowImpl::OnRelease
);
341 void wxDynamicSashWindowImpl::AddChild(wxWindow
*window
) {
342 if (m_add_child_target
&& m_add_child_target
->m_leaf
) {
343 m_add_child_target
->m_leaf
->AddChild(window
);
347 void wxDynamicSashWindowImpl::DrawSash(int x
, int y
) const {
351 dc
.StartDrawingOnTop(m_container
);
355 bdc
.SelectObject(bmp
);
356 bdc
.DrawRectangle(-1, -1, 10, 10);
357 for (i
= 0; i
< 8; i
++) {
358 for (j
= 0; j
< 8; j
++) {
367 dc
.SetLogicalFunction(wxXOR
);
369 if ((m_dragging
== DSR_CORNER
) &&
370 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0) {
374 m_container
->ClientToScreen(&cx
, &cy
);
375 m_container
->ClientToScreen(&x
, &y
);
377 if (cx
< x
&& cy
< y
) {
378 dc
.DrawRectangle(cx
- 2, cy
- 2, x
- cx
+ 4, 4);
379 dc
.DrawRectangle(x
- 2, cy
+ 2, 4, y
- cy
);
380 dc
.DrawRectangle(cx
- 2, cy
+ 2, 4, y
- cy
);
381 dc
.DrawRectangle(cx
+ 2, y
- 2, x
- cx
- 4, 4);
385 m_container
->GetClientSize(&body_w
, &body_h
);
396 if (m_dragging
== DSR_HORIZONTAL_TAB
)
401 m_container
->ClientToScreen(&x
, &y
);
404 w
= body_w
; h
= body_h
;
406 if (m_dragging
== DSR_HORIZONTAL_TAB
)
407 dc
.DrawRectangle(x
, y
- 2, w
, 4);
409 dc
.DrawRectangle(x
- 2, y
, 4, h
);
412 dc
.EndDrawingOnTop();
415 wxDynamicSashWindowImpl
*wxDynamicSashWindowImpl::FindParent(DynamicSashRegion side
) const {
416 if (m_parent
== NULL
) {
420 if (m_parent
->m_split
== DSR_HORIZONTAL_TAB
) {
421 if (side
== DSR_TOP_EDGE
&& m_parent
->m_child
[1] == this)
423 if (side
== DSR_BOTTOM_EDGE
&& m_parent
->m_child
[0] == this)
425 } else if (m_parent
->m_split
== DSR_VERTICAL_TAB
) {
426 if (side
== DSR_LEFT_EDGE
&& m_parent
->m_child
[1] == this)
428 if (side
== DSR_RIGHT_EDGE
&& m_parent
->m_child
[0] == this)
432 return m_parent
->FindParent(side
);
435 wxDynamicSashWindowImpl
*wxDynamicSashWindowImpl::FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
436 wxDynamicSashWindowImpl
*sash_b
) const {
438 win
= sash_a
->m_container
->GetParent();
439 while (win
&& !win
->IsTopLevel()) {
440 if (win
== sash_b
->m_container
) {
444 win
= win
->GetParent();
451 wxWindow
*wxDynamicSashWindowImpl::FindFrame() const {
454 win
= m_window
->GetParent();
455 while (win
&& !win
->IsTopLevel()) {
456 win
= win
->GetParent();
462 wxScrollBar
*wxDynamicSashWindowImpl::FindScrollBar(const wxWindow
*child
, int vert
) const {
463 if (m_child
[0] == NULL
&& m_leaf
== NULL
) {
468 return m_leaf
->FindScrollBar(child
, vert
);
470 wxScrollBar
*ret
= m_child
[0]->FindScrollBar(child
, vert
);
472 ret
= m_child
[1]->FindScrollBar(child
, vert
);
479 void wxDynamicSashWindowImpl::ConstrainChildren(int px
, int py
) {
480 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
481 layout
->left
.SameAs(m_container
, wxLeft
);
482 layout
->top
.SameAs(m_container
, wxTop
);
483 if (m_split
== DSR_HORIZONTAL_TAB
) {
484 layout
->right
.SameAs(m_container
, wxRight
);
485 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
487 layout
->bottom
.SameAs(m_container
, wxBottom
);
488 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
490 m_child
[0]->m_container
->SetConstraints(layout
);
492 layout
= new wxLayoutConstraints();
493 layout
->right
.SameAs(m_container
, wxRight
);
494 layout
->bottom
.SameAs(m_container
, wxBottom
);
495 if (m_split
== DSR_HORIZONTAL_TAB
) {
496 layout
->top
.Below(m_child
[0]->m_container
, 1);
497 layout
->left
.SameAs(m_container
, wxLeft
);
499 layout
->left
.RightOf(m_child
[0]->m_container
, 1);
500 layout
->top
.SameAs(m_container
, wxTop
);
502 m_child
[1]->m_container
->SetConstraints(layout
);
505 void wxDynamicSashWindowImpl::Unify(int panel
) {
511 if (m_child
[panel
]->m_leaf
) {
512 wxDynamicSashWindowImpl
*child
[2];
514 child
[0] = m_child
[0];
515 child
[1] = m_child
[1];
517 m_child
[0] = m_child
[1] = NULL
;
519 m_leaf
= new wxDynamicSashWindowLeaf(this);
521 m_leaf
->m_child
= child
[panel
]->m_leaf
->m_child
;
523 m_leaf
->m_vscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_vscroll
->GetThumbPosition(),
524 child
[panel
]->m_leaf
->m_vscroll
->GetThumbSize(),
525 child
[panel
]->m_leaf
->m_vscroll
->GetRange(),
526 child
[panel
]->m_leaf
->m_vscroll
->GetPageSize());
527 m_leaf
->m_hscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_hscroll
->GetThumbPosition(),
528 child
[panel
]->m_leaf
->m_hscroll
->GetThumbSize(),
529 child
[panel
]->m_leaf
->m_hscroll
->GetRange(),
530 child
[panel
]->m_leaf
->m_hscroll
->GetPageSize());
531 m_add_child_target
= NULL
;
532 wxDynamicSashReparentEvent
event(m_leaf
);
533 m_leaf
->ProcessEvent(event
);
540 wxDynamicSashUnifyEvent
unify(m_leaf
->m_child
);
541 m_leaf
->m_child
->ProcessEvent(unify
);
543 m_split
= m_child
[panel
]->m_split
;
545 delete m_child
[other
];
547 wxDynamicSashWindowImpl
*child_panel
= m_child
[panel
];
548 m_child
[0] = child_panel
->m_child
[0];
549 m_child
[1] = child_panel
->m_child
[1];
551 m_child
[0]->m_parent
= this;
552 m_child
[1]->m_parent
= this;
554 m_add_child_target
= NULL
;
555 m_child
[0]->m_container
->Reparent(m_container
);
556 m_child
[1]->m_container
->Reparent(m_container
);
558 child_panel
->m_child
[0] = child_panel
->m_child
[1] = NULL
;
561 wxSize size
= m_container
->GetSize();
562 wxSize child_size
= m_child
[0]->m_container
->GetSize();
564 ConstrainChildren(child_size
.GetWidth() * 100 / size
.GetWidth(),
565 child_size
.GetHeight() * 100 / size
.GetHeight());
567 m_container
->Layout();
571 void wxDynamicSashWindowImpl::Split(int px
, int py
) {
573 m_add_child_target
= NULL
;
575 m_child
[0] = new wxDynamicSashWindowImpl(m_window
);
576 m_child
[0]->m_container
= new wxWindow(m_container
, wxID_ANY
);
577 m_child
[0]->m_parent
= this;
578 m_child
[0]->m_top
= m_top
;
579 m_child
[0]->Create();
580 if (m_leaf
->m_child
) {
581 m_leaf
->m_child
->Reparent(m_container
);
582 m_child
[0]->AddChild(m_leaf
->m_child
);
585 m_child
[1] = new wxDynamicSashWindowImpl(m_window
);
586 m_child
[1]->m_container
= new wxWindow(m_container
, wxID_ANY
);
587 m_child
[1]->m_parent
= this;
588 m_child
[1]->m_top
= m_top
;
589 m_child
[1]->Create();
591 m_split
= m_dragging
;
592 ConstrainChildren(px
, py
);
594 m_top
->m_add_child_target
= m_child
[1];
595 wxDynamicSashSplitEvent
split(m_child
[0]->m_leaf
->m_child
);
596 m_child
[0]->m_leaf
->m_child
->ProcessEvent(split
);
598 m_child
[0]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
599 m_leaf
->m_vscroll
->GetThumbSize(),
600 m_leaf
->m_vscroll
->GetRange(),
601 m_leaf
->m_vscroll
->GetPageSize());
602 m_child
[0]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
603 m_leaf
->m_hscroll
->GetThumbSize(),
604 m_leaf
->m_hscroll
->GetRange(),
605 m_leaf
->m_hscroll
->GetPageSize());
606 m_child
[1]->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
[1]->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());
617 m_container
->Layout();
621 /* This code is called when you finish resizing a view by dragging the
622 corner tab, but I think this implementation is lousy and will surprise
623 the user more often than it will do what they are trying to do. What
624 I really need to be doing here is do a rewrite such that *no* sashes
625 move except the ones immediately to the bottom and right of this window,
626 and handle the case where you resize a window larger than it's neighbors
627 by destroying the neighbors.
629 But this will do for now. */
630 void wxDynamicSashWindowImpl::Resize(int x
, int y
) {
631 wxDynamicSashWindowImpl
*h_parent
= FindParent(DSR_BOTTOM_EDGE
);
632 wxDynamicSashWindowImpl
*v_parent
= FindParent(DSR_RIGHT_EDGE
);
635 wxWindow
*frame
= FindFrame();
645 m_container
->ClientToScreen(NULL
, &y
);
646 h_parent
->m_container
->ScreenToClient(NULL
, &y
);
648 int py
= (int)((y
* 100) / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
651 wxDynamicSashWindowImpl
*ho_parent
= FindParent(DSR_TOP_EDGE
);
654 if (FindUpperParent(h_parent
, ho_parent
) == ho_parent
) {
657 py
= (int)((ho_parent
->m_child
[0]->m_container
->GetSize().GetHeight() * 100)
658 / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
659 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
660 h_parent
->m_container
, wxHeight
, py
);
662 h_parent
= ho_parent
;
668 } else if (py
> 90) {
671 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
672 h_parent
->m_container
, wxHeight
, py
);
673 h_parent
->m_container
->Layout();
677 h_parent
= FindParent(DSR_TOP_EDGE
);
680 int py
= (int)((y
* 100) /
681 (h_parent
->m_container
->GetSize().GetHeight() +
682 y
- m_container
->GetSize().GetHeight()) + 0.5);
692 wxSize size
= frame
->GetSize();
693 frame
->SetSize(size
.GetWidth(), size
.GetHeight() + y
- m_container
->GetSize().GetHeight());
698 m_container
->ClientToScreen(&x
, NULL
);
699 v_parent
->m_container
->ScreenToClient(&x
, NULL
);
701 int px
= (int)((x
* 100) / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
704 wxDynamicSashWindowImpl
*vo_parent
= FindParent(DSR_LEFT_EDGE
);
707 if (FindUpperParent(v_parent
, vo_parent
) == vo_parent
) {
710 px
= (int)((vo_parent
->m_child
[0]->m_container
->GetSize().GetWidth() * 100)
711 / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
712 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
713 v_parent
->m_container
, wxWidth
, px
);
715 v_parent
= vo_parent
;
721 } else if (px
> 90) {
724 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
725 v_parent
->m_container
, wxWidth
, px
);
726 v_parent
->m_container
->Layout();
730 v_parent
= FindParent(DSR_LEFT_EDGE
);
733 int px
= (int)((x
* 100) /
734 (v_parent
->m_container
->GetSize().GetWidth() +
735 x
- m_container
->GetSize().GetWidth()) + 0.5);
745 wxSize size
= frame
->GetSize();
746 frame
->SetSize(size
.GetWidth() + x
- m_container
->GetSize().GetWidth(), size
.GetHeight());
750 if (h_unify
!= -1 && v_unify
!= -1) {
751 wxDynamicSashWindowImpl
*parent
= FindUpperParent(h_parent
, v_parent
);
753 if (parent
== h_parent
) {
754 h_parent
->Unify(h_unify
);
756 v_parent
->Unify(v_unify
);
758 } else if (h_unify
!= -1) {
759 h_parent
->Unify(h_unify
);
760 } else if (v_unify
!= -1) {
761 v_parent
->Unify(v_unify
);
766 void wxDynamicSashWindowImpl::OnSize(wxSizeEvent
&event
) {
767 m_container
->Layout();
770 m_leaf
->OnSize(event
);
773 void wxDynamicSashWindowImpl::OnPaint(wxPaintEvent
&event
) {
775 m_leaf
->OnPaint(event
);
777 wxPaintDC
dc(m_container
);
778 dc
.SetBackground(wxBrush(m_container
->GetBackgroundColour(), wxSOLID
));
783 void wxDynamicSashWindowImpl::OnMouseMove(wxMouseEvent
&event
) {
785 DrawSash(m_drag_x
, m_drag_y
);
786 m_drag_x
= event
.m_x
; m_drag_y
= event
.m_y
;
787 DrawSash(m_drag_x
, m_drag_y
);
789 m_leaf
->OnMouseMove(event
);
793 void wxDynamicSashWindowImpl::OnLeave(wxMouseEvent
&event
) {
795 m_leaf
->OnLeave(event
);
799 void wxDynamicSashWindowImpl::OnPress(wxMouseEvent
&event
) {
801 m_leaf
->OnPress(event
);
803 m_dragging
= m_split
;
804 m_drag_x
= event
.m_x
;
805 m_drag_y
= event
.m_y
;
806 DrawSash(m_drag_x
, m_drag_y
);
807 m_container
->CaptureMouse();
811 void wxDynamicSashWindowImpl::OnRelease(wxMouseEvent
&event
) {
812 if ((m_dragging
== DSR_CORNER
) &&
813 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0) {
814 DrawSash(m_drag_x
, m_drag_y
);
815 m_container
->ReleaseMouse();
817 Resize(event
.m_x
, event
.m_y
);
819 m_dragging
= DSR_NONE
;
820 } else if (m_dragging
) {
821 DrawSash(m_drag_x
, m_drag_y
);
822 m_container
->ReleaseMouse();
824 wxSize size
= m_container
->GetSize();
825 int px
= (int)((event
.m_x
* 100) / size
.GetWidth() + 0.5);
826 int py
= (int)((event
.m_y
* 100) / size
.GetHeight() + 0.5);
828 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
>= 10 && py
<= 90)
829 || (m_dragging
== DSR_VERTICAL_TAB
&& px
>= 10 && px
<= 90)) {
830 if (m_child
[0] == NULL
) {
833 /* It would be nice if moving *this* sash didn't implicitly move
834 the sashes of our children (if any). But this will do. */
835 wxLayoutConstraints
*layout
= m_child
[0]->m_container
->GetConstraints();
836 if (m_split
== DSR_HORIZONTAL_TAB
) {
837 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
839 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
841 m_container
->Layout();
844 if (m_child
[0] != NULL
) {
845 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
<= 10)
846 || (m_dragging
== DSR_VERTICAL_TAB
&& px
<= 10)) {
854 wxCursor
cursor(wxCURSOR_ARROW
);
855 if (m_split
== DSR_HORIZONTAL_TAB
) {
856 cursor
= wxCursor(wxCURSOR_SIZENS
);
857 } else if (m_split
== DSR_VERTICAL_TAB
) {
858 cursor
= wxCursor(wxCURSOR_SIZEWE
);
860 m_container
->SetCursor(cursor
);
862 m_dragging
= DSR_NONE
;
864 m_leaf
->OnRelease(event
);
868 // wxDynamicSashWindowLeaf //////////////////////////////////////////////////
870 wxDynamicSashWindowLeaf::wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
) {
873 m_hscroll
= m_vscroll
= NULL
;
877 wxDynamicSashWindowLeaf::~wxDynamicSashWindowLeaf() {
878 m_hscroll
->SetEventHandler(m_hscroll
);
879 m_vscroll
->SetEventHandler(m_vscroll
);
880 m_viewport
->SetEventHandler(m_viewport
);
882 m_hscroll
->Destroy();
883 m_vscroll
->Destroy();
884 m_viewport
->Destroy();
887 bool wxDynamicSashWindowLeaf::Create() {
890 m_hscroll
= new wxScrollBar();
891 m_vscroll
= new wxScrollBar();
892 m_viewport
= new wxWindow();
894 if (!m_hscroll
|| !m_vscroll
|| !m_viewport
) {
898 wxDynamicSashWindowImpl
*add_child_target
= m_impl
->m_add_child_target
;
899 m_impl
->m_add_child_target
= NULL
;
900 success
= m_hscroll
->Create(m_impl
->m_container
, wxID_ANY
, wxDefaultPosition
, wxDefaultSize
,
902 success
= success
&& m_vscroll
->Create(m_impl
->m_container
, wxID_ANY
, wxDefaultPosition
, wxDefaultSize
,
904 success
= success
&& m_viewport
->Create(m_impl
->m_container
, wxID_ANY
);
905 m_impl
->m_add_child_target
= add_child_target
;
907 wxCursor
cursor(wxCURSOR_ARROW
);
908 m_hscroll
->SetCursor(cursor
);
909 m_vscroll
->SetCursor(cursor
);
910 m_viewport
->SetCursor(cursor
);
912 m_viewport
->SetEventHandler(this);
913 Connect(wxID_ANY
, wxEVT_DYNAMIC_SASH_REPARENT
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnReparent
);
915 if (m_impl
->m_window
->GetWindowStyle() & wxDS_MANAGE_SCROLLBARS
) {
916 m_hscroll
->SetEventHandler(this);
917 m_vscroll
->SetEventHandler(this);
919 Connect(wxID_ANY
, wxEVT_SET_FOCUS
, (wxObjectEventFunction
)
921 (wxFocusEventFunction
)&wxDynamicSashWindowLeaf::OnFocus
);
922 Connect(wxID_ANY
, wxEVT_SCROLL_TOP
, (wxObjectEventFunction
)
924 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
925 Connect(wxID_ANY
, wxEVT_SCROLL_BOTTOM
, (wxObjectEventFunction
)
927 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
928 Connect(wxID_ANY
, wxEVT_SCROLL_LINEUP
, (wxObjectEventFunction
)
930 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
931 Connect(wxID_ANY
, wxEVT_SCROLL_LINEDOWN
, (wxObjectEventFunction
)
933 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
934 Connect(wxID_ANY
, wxEVT_SCROLL_PAGEUP
, (wxObjectEventFunction
)
936 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
937 Connect(wxID_ANY
, wxEVT_SCROLL_PAGEDOWN
, (wxObjectEventFunction
)
939 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
940 Connect(wxID_ANY
, wxEVT_SCROLL_THUMBTRACK
, (wxObjectEventFunction
)
942 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
943 Connect(wxID_ANY
, wxEVT_SCROLL_THUMBRELEASE
, (wxObjectEventFunction
)
945 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
948 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
951 wxSize size
= m_hscroll
->GetBestSize();
953 size
= m_hscroll
->GetSize();
956 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 10);
957 layout
->right
.LeftOf(m_vscroll
);
958 layout
->bottom
.SameAs(m_impl
->m_container
, wxBottom
, 3);
959 layout
->height
.Absolute(size
.GetHeight());
960 m_hscroll
->SetConstraints(layout
);
962 layout
= new wxLayoutConstraints();
965 size
= size
= m_vscroll
->GetBestSize();
967 size
= m_vscroll
->GetSize();
970 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 10);
971 layout
->bottom
.Above(m_hscroll
);
972 layout
->right
.SameAs(m_impl
->m_container
, wxRight
, 3);
973 layout
->width
.Absolute(size
.GetWidth());
974 m_vscroll
->SetConstraints(layout
);
976 layout
= new wxLayoutConstraints();
979 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 3);
980 layout
->right
.LeftOf(m_vscroll
);
981 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 3);
982 layout
->bottom
.Above(m_hscroll
);
983 m_viewport
->SetConstraints(layout
);
985 m_impl
->m_container
->Layout();
990 void wxDynamicSashWindowLeaf::AddChild(wxWindow
*window
) {
997 wxDynamicSashReparentEvent
event(this);
998 AddPendingEvent(event
);
1001 DynamicSashRegion
wxDynamicSashWindowLeaf::GetRegion(int x
, int y
) {
1002 wxSize size
= m_impl
->m_container
->GetSize();
1003 int w
= size
.GetWidth();
1004 int h
= size
.GetHeight();
1005 size
= m_hscroll
->GetSize();
1006 int sh
= size
.GetHeight();
1007 size
= m_vscroll
->GetSize();
1008 int sw
= size
.GetWidth();
1010 if (x
>= w
- sw
- 3 && x
< w
&& y
>= h
- sh
- 3 && y
< h
)
1012 if (x
>= 3 && x
< 10 && y
>= h
- sh
- 3 && y
< h
- 2)
1013 return DSR_VERTICAL_TAB
;
1014 if (x
>= w
- sw
- 3 && x
< w
- 2 && y
>= 3 && y
< 10)
1015 return DSR_HORIZONTAL_TAB
;
1017 return DSR_LEFT_EDGE
;
1019 return DSR_TOP_EDGE
;
1021 return DSR_RIGHT_EDGE
;
1023 return DSR_BOTTOM_EDGE
;
1028 void wxDynamicSashWindowLeaf::ResizeChild(wxSize size
) {
1030 if (m_impl
->m_window
->GetWindowStyle() & wxDS_MANAGE_SCROLLBARS
) {
1031 m_child
->SetSize(size
);
1032 wxSize best_size
= m_child
->GetBestSize();
1033 if (best_size
.GetWidth() < size
.GetWidth()) {
1034 best_size
.SetWidth(size
.GetWidth());
1036 if (best_size
.GetHeight() < size
.GetHeight()) {
1037 best_size
.SetHeight(size
.GetHeight());
1039 m_child
->SetSize(best_size
);
1041 int hpos
= m_hscroll
->GetThumbPosition();
1042 int vpos
= m_vscroll
->GetThumbPosition();
1050 if (hpos
> best_size
.GetWidth() - size
.GetWidth()) {
1051 hpos
= best_size
.GetWidth() - size
.GetWidth();
1053 if (vpos
> best_size
.GetHeight() - size
.GetHeight()) {
1054 vpos
= best_size
.GetHeight() - size
.GetHeight();
1057 m_hscroll
->SetScrollbar(hpos
, size
.GetWidth(),
1058 best_size
.GetWidth(), size
.GetWidth());
1059 m_vscroll
->SetScrollbar(vpos
, size
.GetHeight(),
1060 best_size
.GetHeight(), size
.GetHeight());
1062 // Umm, the scrollbars are doing something insane under GTK+ and subtracting
1063 // one from the position I pass in. This works around that.
1064 m_hscroll
->SetThumbPosition(hpos
+ hpos
- m_hscroll
->GetThumbPosition());
1065 m_vscroll
->SetThumbPosition(vpos
+ vpos
- m_vscroll
->GetThumbPosition());
1067 wxPoint pos
= m_child
->GetPosition();
1068 m_viewport
->ScrollWindow(-hpos
- pos
.x
, -vpos
- pos
.y
);
1070 m_child
->SetSize(size
);
1075 wxScrollBar
*wxDynamicSashWindowLeaf::FindScrollBar(const wxWindow
*child
, int vert
) const {
1076 if (m_child
== child
) {
1087 void wxDynamicSashWindowLeaf::OnSize(wxSizeEvent
&WXUNUSED(event
)) {
1088 m_impl
->m_container
->Refresh();
1089 ResizeChild(m_viewport
->GetSize());
1092 void wxDynamicSashWindowLeaf::OnPaint(wxPaintEvent
&WXUNUSED(event
)) {
1093 wxPaintDC
dc(m_impl
->m_container
);
1094 dc
.SetBackground(wxBrush(m_impl
->m_container
->GetBackgroundColour(), wxSOLID
));
1097 wxPen
highlight(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT
), 1, wxSOLID
);
1098 wxPen
shadow(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW
), 1, wxSOLID
);
1099 wxPen
black(*wxBLACK
, 1, wxSOLID
);
1101 wxSize size
= m_impl
->m_container
->GetSize();
1102 int w
= size
.GetWidth();
1103 int h
= size
.GetHeight();
1104 size
= m_hscroll
->GetSize();
1105 int sh
= size
.GetHeight();
1106 size
= m_vscroll
->GetSize();
1107 int sw
= size
.GetWidth();
1110 dc
.DrawLine(1, 1, 1, h
- 2);
1111 dc
.DrawLine(1, 1, w
- 2, 1);
1113 dc
.DrawLine(2, 2, 2, h
- 3);
1114 dc
.DrawLine(2, 2, w
- 3, 2);
1115 dc
.SetPen(highlight
);
1116 dc
.DrawLine(w
- 2, 2, w
- 2, h
- sh
- 2);
1117 dc
.DrawLine(w
- 2, h
- sh
- 2, w
- sw
- 2, h
- sh
- 2);
1118 dc
.DrawLine(w
- sw
- 2, h
- sh
- 2, w
- sw
- 2, h
- 2);
1119 dc
.DrawLine(w
- sw
- 2, h
- 2, 2, h
- 2);
1121 dc
.SetPen(highlight
);
1122 dc
.DrawLine(w
- sw
- 2, 8, w
- sw
- 2, 4);
1123 dc
.DrawLine(w
- sw
- 2, 4, w
- 5, 4);
1125 dc
.DrawLine(w
- 5, 4, w
- 5, 8);
1126 dc
.DrawLine(w
- 5, 8, w
- sw
- 2, 8);
1128 dc
.DrawLine(w
- 4, 3, w
- 4, 9);
1129 dc
.DrawLine(w
- 4, 9, w
- sw
- 3, 9);
1131 dc
.SetPen(highlight
);
1132 dc
.DrawLine(4, h
- 5, 4, h
- sh
- 2);
1133 dc
.DrawLine(4, h
- sh
- 2, 8, h
- sh
- 2);
1135 dc
.DrawLine(8, h
- sh
- 2, 8, h
- 5);
1136 dc
.DrawLine(8, h
- 5, 4, h
- 5);
1138 dc
.DrawLine(9, h
- sh
- 3, 9, h
- 4);
1139 dc
.DrawLine(9, h
- 4, 3, h
- 4);
1141 int cy
= (h
- sh
+ h
- 6) / 2 + 1;
1142 int cx
= (w
- sw
+ w
- 6) / 2 + 1;
1151 for (y
= sy
; y
< h
- 2; y
+= 4) {
1152 for (x
= sx
; x
< w
- 2; x
+= 4) {
1153 if (x
- cx
>= -(y
- cy
)) {
1154 dc
.SetPen(highlight
);
1157 dc
.DrawPoint(x
+ 1, y
+ 1);
1163 void wxDynamicSashWindowLeaf::OnScroll(wxScrollEvent
&WXUNUSED(event
)) {
1164 int nx
= -m_hscroll
->GetThumbPosition();
1165 int ny
= -m_vscroll
->GetThumbPosition();
1168 wxPoint pos
= m_child
->GetPosition();
1170 m_viewport
->ScrollWindow(nx
- pos
.x
, ny
- pos
.y
);
1174 void wxDynamicSashWindowLeaf::OnFocus(wxFocusEvent
&event
) {
1175 if (event
.m_eventObject
== m_hscroll
|| event
.m_eventObject
== m_vscroll
) {
1176 m_child
->SetFocus();
1181 void wxDynamicSashWindowLeaf::OnMouseMove(wxMouseEvent
&event
) {
1182 if (m_impl
->m_dragging
) {
1186 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1188 wxCursor
cursor(wxCURSOR_ARROW
);
1189 if (region
== DSR_HORIZONTAL_TAB
) {
1190 cursor
= wxCursor(wxCURSOR_SIZENS
);
1191 } else if (region
== DSR_VERTICAL_TAB
) {
1192 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1193 } else if ((region
== DSR_CORNER
) &&
1194 (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0) {
1195 cursor
= wxCursor(wxCURSOR_SIZENWSE
);
1196 } else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1197 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
) {
1198 if (m_impl
->FindParent(region
)) {
1199 if (region
== DSR_LEFT_EDGE
|| region
== DSR_RIGHT_EDGE
) {
1200 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1202 cursor
= wxCursor(wxCURSOR_SIZENS
);
1207 m_impl
->m_container
->SetCursor(cursor
);
1210 void wxDynamicSashWindowLeaf::OnLeave(wxMouseEvent
&WXUNUSED(event
)) {
1211 wxCursor
cursor(wxCURSOR_ARROW
);
1212 m_impl
->m_container
->SetCursor(cursor
);
1216 void wxDynamicSashWindowLeaf::OnPress(wxMouseEvent
&event
) {
1217 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1219 if ((region
== DSR_CORNER
) && (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) == 0)
1222 if (region
== DSR_HORIZONTAL_TAB
|| region
== DSR_VERTICAL_TAB
|| region
== DSR_CORNER
) {
1223 m_impl
->m_dragging
= region
;
1224 m_impl
->m_drag_x
= event
.m_x
;
1225 m_impl
->m_drag_y
= event
.m_y
;
1226 m_impl
->DrawSash(event
.m_x
, event
.m_y
);
1227 m_impl
->m_container
->CaptureMouse();
1228 } else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1229 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
) {
1230 wxDynamicSashWindowImpl
*parent
= m_impl
->FindParent(region
);
1236 m_impl
->m_container
->ClientToScreen(&x
, &y
);
1237 parent
->m_container
->ScreenToClient(&x
, &y
);
1239 parent
->m_dragging
= parent
->m_split
;
1240 parent
->m_drag_x
= x
;
1241 parent
->m_drag_y
= y
;
1242 parent
->DrawSash(x
, y
);
1243 parent
->m_container
->CaptureMouse();
1248 void wxDynamicSashWindowLeaf::OnRelease(wxMouseEvent
&WXUNUSED(event
)) {
1251 void wxDynamicSashWindowLeaf::OnReparent(wxEvent
&WXUNUSED(event
)) {
1253 m_child
->Reparent(m_viewport
);
1256 ResizeChild(m_viewport
->GetSize());
1259 // wxDynamicSashSplitEvent //////////////////////////////////////////////////
1261 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent() {
1262 m_eventObject
= NULL
;
1263 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1266 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent(wxObject
*object
) {
1267 m_eventObject
= object
;
1268 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1271 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashSplitEvent
, wxCommandEvent
)
1273 // wxDynamicSashUnifyEvent //////////////////////////////////////////////////
1275 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent() {
1276 m_eventObject
= NULL
;
1277 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1280 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent(wxObject
*object
) {
1281 m_eventObject
= object
;
1282 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1285 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashUnifyEvent
, wxCommandEvent
)
1287 // wxDynamicSsahReparentEvent ///////////////////////////////////////////////
1289 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent() {
1290 m_eventObject
= NULL
;
1291 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1294 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(wxObject
*object
) {
1295 m_eventObject
= object
;
1296 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1299 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
)
1304 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashReparentEvent
, wxEvent
)
1306 /////////////////////////////////////////////////////////////////////////////