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 /////////////////////////////////////////////////////////////////////////////
13 #include "wx/gizmos/dynamicsash.h"
16 wxDynamicSashWindow works by internally storing a tree of Implementation
17 objects (wxDynamicSsahWindowImpl) and Leaf objects
18 (wxDynamicSashWindowLeaf). The wxDynamicSashWindow has a pointer to one
19 implementation, and each implementation either has a pointer to a one
20 leaf (m_leaf) or a pointer to two children implementation objects
21 (m_child). The leaves each are responsible for drawing the frame and
22 decorations around one user-provided views and for responding to mouse
25 A resulting tree might look something like this:
29 +- wxDynamicSashWindowImpl
31 +- wxDynamicSashWindowLeaf
35 +- wxDynamicSashWindowImpl
37 +- wxDynamicSashWindowLeaf
41 +- wxDynamicSashWindowLeaf
45 Each time a split occurs, one of the implementation objects removes its
46 leaf, generates two new implementation object children, each with a new
47 leaf, and reparents the user view which was connected to its old leaf
48 to be one of the new leaf's user view, and sends a Split event to the
49 user view in the hopes that it will generate a new user view for the
52 When a unification ocurrs, an implementation object is replaced by one
53 of its children, and the tree of its other child is pruned.
55 One quirk is that the top-level implementation object (m_top) always
56 keeps a pointer to the implementation object where a new child is needed.
57 (m_add_child_target). This is so that when a new uesr view is added
58 to the hierarchy, AddChild() is able to reparent the new user view to
59 the correct implementation object's leaf.
63 #include <wx/dcmemory.h>
64 #include <wx/dcscreen.h>
65 #include <wx/layout.h>
66 #include <wx/scrolbar.h>
67 #include <wx/settings.h>
70 #define wxEVT_DYNAMIC_SASH_PRIVATE (wxEVT_DYNAMIC_SASH_BASE + 8)
71 #define wxEVT_DYNAMIC_SASH_REPARENT (wxEVT_DYNAMIC_SASH_PRIVATE + 1)
75 wxDynamicSashReparentEvent is generated by the AddChild() method of
76 wxDynamicSashWindow when it wants a Leaf to reparent a user view window
77 to its viewport at some time in the future. We can't reparent the window
78 immediately, because switching parents in AddChild() confuses the wxWindow
79 class. Instead, we queue up this event, and the window is actually
80 reparented the next time we process events in the idle loop.
82 class wxDynamicSashReparentEvent
: public wxEvent
{
84 wxDynamicSashReparentEvent();
85 wxDynamicSashReparentEvent(wxObject
*object
);
86 wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
);
88 virtual wxEvent
* Clone() const { return new wxDynamicSashReparentEvent(*this); }
90 DECLARE_DYNAMIC_CLASS(wxDynamicSashReparentEvent
);
94 enum DynamicSashRegion
{
106 class wxDynamicSashWindowImpl
: public wxEvtHandler
{
108 wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
);
109 ~wxDynamicSashWindowImpl();
112 void AddChild(wxWindow
*window
);
113 void DrawSash(int x
, int y
) const;
114 void ConstrainChildren(int px
, int py
);
115 void Split(int x
, int y
);
116 void Unify(int panel
);
117 void Resize(int x
, int y
);
118 wxDynamicSashWindowImpl
*FindParent(DynamicSashRegion side
) const;
119 wxDynamicSashWindowImpl
*FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
120 wxDynamicSashWindowImpl
*sash_b
) const;
121 wxWindow
*FindFrame() const;
122 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
124 void OnSize(wxSizeEvent
&event
);
125 void OnPaint(wxPaintEvent
&event
);
126 void OnMouseMove(wxMouseEvent
&event
);
127 void OnLeave(wxMouseEvent
&event
);
128 void OnPress(wxMouseEvent
&event
);
129 void OnRelease(wxMouseEvent
&event
);
131 wxDynamicSashWindow
*m_window
;
132 wxDynamicSashWindowImpl
*m_add_child_target
;
134 /* This is the window we are responsible for managing. Either of
135 leaf or our children are in this window. For the top level
136 implementation object, this is the same as m_window.
137 Otherwise it is a window we've created an will destroy when we
139 wxWindow
*m_container
;
141 wxDynamicSashWindowImpl
*m_parent
;
142 wxDynamicSashWindowImpl
*m_top
;
143 wxDynamicSashWindowImpl
*m_child
[2];
145 class wxDynamicSashWindowLeaf
*m_leaf
;
147 /* If the implementation is split horizontally or vertically, m_split
148 is set to DSR_HORIZONTAL_TAB or DSR_VERTICAL_TAB, respectively.
149 Otherwise it is set to DSR_NONE. */
150 DynamicSashRegion m_split
;
152 /* These are used to keep track of a sash as it is being dragged, for
153 drawing the user interface correctly. */
154 DynamicSashRegion m_dragging
;
155 int m_drag_x
, m_drag_y
;
158 class wxDynamicSashWindowLeaf
: public wxEvtHandler
{
160 wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
);
161 ~wxDynamicSashWindowLeaf();
164 void AddChild(wxWindow
*window
);
165 DynamicSashRegion
GetRegion(int x
, int y
);
166 void ResizeChild(wxSize size
);
167 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
169 void OnSize(wxSizeEvent
&event
);
170 void OnPaint(wxPaintEvent
&event
);
171 void OnScroll(wxScrollEvent
&event
);
172 void OnFocus(wxFocusEvent
&event
);
173 void OnMouseMove(wxMouseEvent
&event
);
174 void OnLeave(wxMouseEvent
&event
);
175 void OnPress(wxMouseEvent
&event
);
176 void OnRelease(wxMouseEvent
&event
);
177 void OnReparent(wxEvent
&event
);
179 wxDynamicSashWindowImpl
*m_impl
;
181 wxScrollBar
*m_vscroll
, *m_hscroll
;
183 /* m_child is the window provided to us by the application developer.
184 m_viewport is a window we've created, and it is the immediately
185 parent of m_child. We scroll m_child by moving it around within
187 wxWindow
*m_viewport
, *m_child
;
190 // wxDynamicSashWindow //////////////////////////////////////////////////////
192 wxDynamicSashWindow::wxDynamicSashWindow() {
196 wxDynamicSashWindow::wxDynamicSashWindow(wxWindow
*parent
, wxWindowID id
,
197 const wxPoint
& pos
, const wxSize
& size
,
198 long style
, const wxString
& name
) {
200 Create(parent
, id
, pos
, size
, style
, name
);
203 wxDynamicSashWindow::~wxDynamicSashWindow() {
204 SetEventHandler(this);
208 bool wxDynamicSashWindow::Create(wxWindow
*parent
, wxWindowID id
,
209 const wxPoint
& pos
, const wxSize
& size
,
210 long style
, const wxString
& name
) {
214 if (!wxWindow::Create(parent
, id
, pos
, size
, style
, name
))
217 m_impl
= new wxDynamicSashWindowImpl(this);
221 if (!m_impl
->Create()) {
230 void wxDynamicSashWindow::AddChild(wxWindowBase
*child
) {
231 wxWindow::AddChild(child
);
233 m_impl
->AddChild(wxDynamicCast(child
, wxWindow
));
236 wxScrollBar
*wxDynamicSashWindow::GetHScrollBar(const wxWindow
*child
) const {
237 return m_impl
->FindScrollBar(child
, 0);
240 wxScrollBar
*wxDynamicSashWindow::GetVScrollBar(const wxWindow
*child
) const {
241 return m_impl
->FindScrollBar(child
, 1);
244 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashWindow
, wxWindow
)
246 // wxDynamicSashWindowImpl //////////////////////////////////////////////////
248 wxDynamicSashWindowImpl::wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
) {
250 m_add_child_target
= this;
255 m_child
[0] = m_child
[1] = NULL
;
257 m_dragging
= DSR_NONE
;
261 wxDynamicSashWindowImpl::~wxDynamicSashWindowImpl() {
269 if (m_container
!= m_window
&& m_container
) {
270 m_container
->SetEventHandler(m_container
);
271 m_container
->Destroy();
275 bool wxDynamicSashWindowImpl::Create() {
277 m_container
= m_window
;
280 wxCursor
cursor(wxCURSOR_ARROW
);
281 m_container
->SetCursor(cursor
);
283 m_leaf
= new wxDynamicSashWindowLeaf(this);
287 if (!m_leaf
->Create()) {
293 m_container
->SetEventHandler(this);
295 Connect(-1, wxEVT_SIZE
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnSize
);
296 Connect(-1, wxEVT_PAINT
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnPaint
);
297 Connect(-1, wxEVT_MOTION
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnMouseMove
);
298 Connect(-1, wxEVT_ENTER_WINDOW
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnMouseMove
);
299 Connect(-1, wxEVT_LEAVE_WINDOW
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnLeave
);
300 Connect(-1, wxEVT_LEFT_DOWN
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnPress
);
301 Connect(-1, wxEVT_LEFT_UP
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnRelease
);
306 void wxDynamicSashWindowImpl::AddChild(wxWindow
*window
) {
307 if (m_add_child_target
&& m_add_child_target
->m_leaf
) {
308 m_add_child_target
->m_leaf
->AddChild(window
);
312 void wxDynamicSashWindowImpl::DrawSash(int x
, int y
) const {
316 dc
.StartDrawingOnTop(m_container
);
320 bdc
.SelectObject(bmp
);
321 bdc
.DrawRectangle(-1, -1, 10, 10);
322 for (i
= 0; i
< 8; i
++) {
323 for (j
= 0; j
< 8; j
++) {
332 dc
.SetLogicalFunction(wxXOR
);
334 if ((m_dragging
== DSR_CORNER
) &&
335 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0) {
339 m_container
->ClientToScreen(&cx
, &cy
);
340 m_container
->ClientToScreen(&x
, &y
);
342 if (cx
< x
&& cy
< y
) {
343 dc
.DrawRectangle(cx
- 2, cy
- 2, x
- cx
+ 4, 4);
344 dc
.DrawRectangle(x
- 2, cy
+ 2, 4, y
- cy
);
345 dc
.DrawRectangle(cx
- 2, cy
+ 2, 4, y
- cy
);
346 dc
.DrawRectangle(cx
+ 2, y
- 2, x
- cx
- 4, 4);
350 m_container
->GetClientSize(&body_w
, &body_h
);
361 if (m_dragging
== DSR_HORIZONTAL_TAB
)
366 m_container
->ClientToScreen(&x
, &y
);
369 w
= body_w
; h
= body_h
;
371 if (m_dragging
== DSR_HORIZONTAL_TAB
)
372 dc
.DrawRectangle(x
, y
- 2, w
, 4);
374 dc
.DrawRectangle(x
- 2, y
, 4, h
);
377 dc
.EndDrawingOnTop();
380 wxDynamicSashWindowImpl
*wxDynamicSashWindowImpl::FindParent(DynamicSashRegion side
) const {
381 if (m_parent
== NULL
) {
385 if (m_parent
->m_split
== DSR_HORIZONTAL_TAB
) {
386 if (side
== DSR_TOP_EDGE
&& m_parent
->m_child
[1] == this)
388 if (side
== DSR_BOTTOM_EDGE
&& m_parent
->m_child
[0] == this)
390 } else if (m_parent
->m_split
== DSR_VERTICAL_TAB
) {
391 if (side
== DSR_LEFT_EDGE
&& m_parent
->m_child
[1] == this)
393 if (side
== DSR_RIGHT_EDGE
&& m_parent
->m_child
[0] == this)
397 return m_parent
->FindParent(side
);
400 wxDynamicSashWindowImpl
*wxDynamicSashWindowImpl::FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
401 wxDynamicSashWindowImpl
*sash_b
) const {
403 win
= sash_a
->m_container
->GetParent();
404 while (win
&& !win
->IsTopLevel()) {
405 if (win
== sash_b
->m_container
) {
409 win
= win
->GetParent();
416 wxWindow
*wxDynamicSashWindowImpl::FindFrame() const {
419 win
= m_window
->GetParent();
420 while (win
&& !win
->IsTopLevel()) {
421 win
= win
->GetParent();
427 wxScrollBar
*wxDynamicSashWindowImpl::FindScrollBar(const wxWindow
*child
, int vert
) const {
428 if (m_child
[0] == NULL
&& m_leaf
== NULL
) {
433 return m_leaf
->FindScrollBar(child
, vert
);
435 wxScrollBar
*ret
= m_child
[0]->FindScrollBar(child
, vert
);
437 ret
= m_child
[1]->FindScrollBar(child
, vert
);
444 void wxDynamicSashWindowImpl::ConstrainChildren(int px
, int py
) {
445 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
446 layout
->left
.SameAs(m_container
, wxLeft
);
447 layout
->top
.SameAs(m_container
, wxTop
);
448 if (m_split
== DSR_HORIZONTAL_TAB
) {
449 layout
->right
.SameAs(m_container
, wxRight
);
450 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
452 layout
->bottom
.SameAs(m_container
, wxBottom
);
453 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
455 m_child
[0]->m_container
->SetConstraints(layout
);
457 layout
= new wxLayoutConstraints();
458 layout
->right
.SameAs(m_container
, wxRight
);
459 layout
->bottom
.SameAs(m_container
, wxBottom
);
460 if (m_split
== DSR_HORIZONTAL_TAB
) {
461 layout
->top
.Below(m_child
[0]->m_container
, 1);
462 layout
->left
.SameAs(m_container
, wxLeft
);
464 layout
->left
.RightOf(m_child
[0]->m_container
, 1);
465 layout
->top
.SameAs(m_container
, wxTop
);
467 m_child
[1]->m_container
->SetConstraints(layout
);
470 void wxDynamicSashWindowImpl::Unify(int panel
) {
476 if (m_child
[panel
]->m_leaf
) {
477 wxDynamicSashWindowImpl
*child
[2];
479 child
[0] = m_child
[0];
480 child
[1] = m_child
[1];
482 m_child
[0] = m_child
[1] = NULL
;
484 m_leaf
= new wxDynamicSashWindowLeaf(this);
486 m_leaf
->m_child
= child
[panel
]->m_leaf
->m_child
;
488 m_leaf
->m_vscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_vscroll
->GetThumbPosition(),
489 child
[panel
]->m_leaf
->m_vscroll
->GetThumbSize(),
490 child
[panel
]->m_leaf
->m_vscroll
->GetRange(),
491 child
[panel
]->m_leaf
->m_vscroll
->GetPageSize());
492 m_leaf
->m_hscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_hscroll
->GetThumbPosition(),
493 child
[panel
]->m_leaf
->m_hscroll
->GetThumbSize(),
494 child
[panel
]->m_leaf
->m_hscroll
->GetRange(),
495 child
[panel
]->m_leaf
->m_hscroll
->GetPageSize());
496 m_add_child_target
= NULL
;
497 wxDynamicSashReparentEvent
event(m_leaf
);
498 m_leaf
->ProcessEvent(event
);
505 wxDynamicSashUnifyEvent
unify(m_leaf
->m_child
);
506 m_leaf
->m_child
->ProcessEvent(unify
);
508 m_split
= m_child
[panel
]->m_split
;
510 delete m_child
[other
];
512 wxDynamicSashWindowImpl
*child_panel
= m_child
[panel
];
513 m_child
[0] = child_panel
->m_child
[0];
514 m_child
[1] = child_panel
->m_child
[1];
516 m_child
[0]->m_parent
= this;
517 m_child
[1]->m_parent
= this;
519 m_add_child_target
= NULL
;
520 m_child
[0]->m_container
->Reparent(m_container
);
521 m_child
[1]->m_container
->Reparent(m_container
);
523 child_panel
->m_child
[0] = child_panel
->m_child
[1] = NULL
;
526 wxSize size
= m_container
->GetSize();
527 wxSize child_size
= m_child
[0]->m_container
->GetSize();
529 ConstrainChildren(child_size
.GetWidth() * 100 / size
.GetWidth(),
530 child_size
.GetHeight() * 100 / size
.GetHeight());
532 m_container
->Layout();
536 void wxDynamicSashWindowImpl::Split(int px
, int py
) {
538 m_add_child_target
= NULL
;
540 m_child
[0] = new wxDynamicSashWindowImpl(m_window
);
541 m_child
[0]->m_container
= new wxWindow(m_container
, -1);
542 m_child
[0]->m_parent
= this;
543 m_child
[0]->m_top
= m_top
;
544 m_child
[0]->Create();
545 if (m_leaf
->m_child
) {
546 m_leaf
->m_child
->Reparent(m_container
);
547 m_child
[0]->AddChild(m_leaf
->m_child
);
550 m_child
[1] = new wxDynamicSashWindowImpl(m_window
);
551 m_child
[1]->m_container
= new wxWindow(m_container
, -1);
552 m_child
[1]->m_parent
= this;
553 m_child
[1]->m_top
= m_top
;
554 m_child
[1]->Create();
556 m_split
= m_dragging
;
557 ConstrainChildren(px
, py
);
559 m_top
->m_add_child_target
= m_child
[1];
560 wxDynamicSashSplitEvent
split(m_child
[0]->m_leaf
->m_child
);
561 m_child
[0]->m_leaf
->m_child
->ProcessEvent(split
);
563 m_child
[0]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
564 m_leaf
->m_vscroll
->GetThumbSize(),
565 m_leaf
->m_vscroll
->GetRange(),
566 m_leaf
->m_vscroll
->GetPageSize());
567 m_child
[0]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
568 m_leaf
->m_hscroll
->GetThumbSize(),
569 m_leaf
->m_hscroll
->GetRange(),
570 m_leaf
->m_hscroll
->GetPageSize());
571 m_child
[1]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
572 m_leaf
->m_vscroll
->GetThumbSize(),
573 m_leaf
->m_vscroll
->GetRange(),
574 m_leaf
->m_vscroll
->GetPageSize());
575 m_child
[1]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
576 m_leaf
->m_hscroll
->GetThumbSize(),
577 m_leaf
->m_hscroll
->GetRange(),
578 m_leaf
->m_hscroll
->GetPageSize());
582 m_container
->Layout();
586 /* This code is called when you finish resizing a view by dragging the
587 corner tab, but I think this implementation is lousy and will surprise
588 the user more often than it will do what they are trying to do. What
589 I really need to be doing here is do a rewrite such that *no* sashes
590 move except the ones immediately to the bottom and right of this window,
591 and handle the case where you resize a window larger than it's neighbors
592 by destroying the neighbors.
594 But this will do for now. */
595 void wxDynamicSashWindowImpl::Resize(int x
, int y
) {
596 wxDynamicSashWindowImpl
*h_parent
= FindParent(DSR_BOTTOM_EDGE
);
597 wxDynamicSashWindowImpl
*v_parent
= FindParent(DSR_RIGHT_EDGE
);
600 wxWindow
*frame
= FindFrame();
610 m_container
->ClientToScreen(NULL
, &y
);
611 h_parent
->m_container
->ScreenToClient(NULL
, &y
);
613 int py
= (int)((y
* 100) / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
616 wxDynamicSashWindowImpl
*ho_parent
= FindParent(DSR_TOP_EDGE
);
619 if (FindUpperParent(h_parent
, ho_parent
) == ho_parent
) {
622 py
= (int)((ho_parent
->m_child
[0]->m_container
->GetSize().GetHeight() * 100)
623 / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
624 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
625 h_parent
->m_container
, wxHeight
, py
);
627 h_parent
= ho_parent
;
633 } else if (py
> 90) {
636 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
637 h_parent
->m_container
, wxHeight
, py
);
638 h_parent
->m_container
->Layout();
642 h_parent
= FindParent(DSR_TOP_EDGE
);
645 int py
= (int)((y
* 100) /
646 (h_parent
->m_container
->GetSize().GetHeight() +
647 y
- m_container
->GetSize().GetHeight()) + 0.5);
657 wxSize size
= frame
->GetSize();
658 frame
->SetSize(size
.GetWidth(), size
.GetHeight() + y
- m_container
->GetSize().GetHeight());
663 m_container
->ClientToScreen(&x
, NULL
);
664 v_parent
->m_container
->ScreenToClient(&x
, NULL
);
666 int px
= (int)((x
* 100) / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
669 wxDynamicSashWindowImpl
*vo_parent
= FindParent(DSR_LEFT_EDGE
);
672 if (FindUpperParent(v_parent
, vo_parent
) == vo_parent
) {
675 px
= (int)((vo_parent
->m_child
[0]->m_container
->GetSize().GetWidth() * 100)
676 / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
677 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
678 v_parent
->m_container
, wxWidth
, px
);
680 v_parent
= vo_parent
;
686 } else if (px
> 90) {
689 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
690 v_parent
->m_container
, wxWidth
, px
);
691 v_parent
->m_container
->Layout();
695 v_parent
= FindParent(DSR_LEFT_EDGE
);
698 int px
= (int)((x
* 100) /
699 (v_parent
->m_container
->GetSize().GetWidth() +
700 x
- m_container
->GetSize().GetWidth()) + 0.5);
710 wxSize size
= frame
->GetSize();
711 frame
->SetSize(size
.GetWidth() + x
- m_container
->GetSize().GetWidth(), size
.GetHeight());
715 if (h_unify
!= -1 && v_unify
!= -1) {
716 wxDynamicSashWindowImpl
*parent
= FindUpperParent(h_parent
, v_parent
);
718 if (parent
== h_parent
) {
719 h_parent
->Unify(h_unify
);
721 v_parent
->Unify(v_unify
);
723 } else if (h_unify
!= -1) {
724 h_parent
->Unify(h_unify
);
725 } else if (v_unify
!= -1) {
726 v_parent
->Unify(v_unify
);
731 void wxDynamicSashWindowImpl::OnSize(wxSizeEvent
&event
) {
732 m_container
->Layout();
735 m_leaf
->OnSize(event
);
738 void wxDynamicSashWindowImpl::OnPaint(wxPaintEvent
&event
) {
740 m_leaf
->OnPaint(event
);
742 wxPaintDC
dc(m_container
);
743 dc
.SetBackground(wxBrush(m_container
->GetBackgroundColour(), wxSOLID
));
748 void wxDynamicSashWindowImpl::OnMouseMove(wxMouseEvent
&event
) {
750 DrawSash(m_drag_x
, m_drag_y
);
751 m_drag_x
= event
.m_x
; m_drag_y
= event
.m_y
;
752 DrawSash(m_drag_x
, m_drag_y
);
754 m_leaf
->OnMouseMove(event
);
758 void wxDynamicSashWindowImpl::OnLeave(wxMouseEvent
&event
) {
760 m_leaf
->OnLeave(event
);
764 void wxDynamicSashWindowImpl::OnPress(wxMouseEvent
&event
) {
766 m_leaf
->OnPress(event
);
768 m_dragging
= m_split
;
769 m_drag_x
= event
.m_x
;
770 m_drag_y
= event
.m_y
;
771 DrawSash(m_drag_x
, m_drag_y
);
772 m_container
->CaptureMouse();
776 void wxDynamicSashWindowImpl::OnRelease(wxMouseEvent
&event
) {
777 if ((m_dragging
== DSR_CORNER
) &&
778 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0) {
779 DrawSash(m_drag_x
, m_drag_y
);
780 m_container
->ReleaseMouse();
782 Resize(event
.m_x
, event
.m_y
);
784 m_dragging
= DSR_NONE
;
785 } else if (m_dragging
) {
786 DrawSash(m_drag_x
, m_drag_y
);
787 m_container
->ReleaseMouse();
789 wxSize size
= m_container
->GetSize();
790 int px
= (int)((event
.m_x
* 100) / size
.GetWidth() + 0.5);
791 int py
= (int)((event
.m_y
* 100) / size
.GetHeight() + 0.5);
793 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
>= 10 && py
<= 90)
794 || (m_dragging
== DSR_VERTICAL_TAB
&& px
>= 10 && px
<= 90)) {
795 if (m_child
[0] == NULL
) {
798 /* It would be nice if moving *this* sash didn't implicitly move
799 the sashes of our children (if any). But this will do. */
800 wxLayoutConstraints
*layout
= m_child
[0]->m_container
->GetConstraints();
801 if (m_split
== DSR_HORIZONTAL_TAB
) {
802 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
804 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
806 m_container
->Layout();
809 if (m_child
[0] != NULL
) {
810 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
<= 10)
811 || (m_dragging
== DSR_VERTICAL_TAB
&& px
<= 10)) {
819 wxCursor
cursor(wxCURSOR_ARROW
);
820 if (m_split
== DSR_HORIZONTAL_TAB
) {
821 cursor
= wxCursor(wxCURSOR_SIZENS
);
822 } else if (m_split
== DSR_VERTICAL_TAB
) {
823 cursor
= wxCursor(wxCURSOR_SIZEWE
);
825 m_container
->SetCursor(cursor
);
827 m_dragging
= DSR_NONE
;
829 m_leaf
->OnRelease(event
);
833 // wxDynamicSashWindowLeaf //////////////////////////////////////////////////
835 wxDynamicSashWindowLeaf::wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
) {
838 m_hscroll
= m_vscroll
= NULL
;
842 wxDynamicSashWindowLeaf::~wxDynamicSashWindowLeaf() {
843 m_hscroll
->SetEventHandler(m_hscroll
);
844 m_vscroll
->SetEventHandler(m_vscroll
);
845 m_viewport
->SetEventHandler(m_viewport
);
847 m_hscroll
->Destroy();
848 m_vscroll
->Destroy();
849 m_viewport
->Destroy();
852 bool wxDynamicSashWindowLeaf::Create() {
855 m_hscroll
= new wxScrollBar();
856 m_vscroll
= new wxScrollBar();
857 m_viewport
= new wxWindow();
859 if (!m_hscroll
|| !m_vscroll
|| !m_viewport
) {
863 wxDynamicSashWindowImpl
*add_child_target
= m_impl
->m_add_child_target
;
864 m_impl
->m_add_child_target
= NULL
;
865 success
= m_hscroll
->Create(m_impl
->m_container
, -1, wxDefaultPosition
, wxDefaultSize
,
867 success
= success
&& m_vscroll
->Create(m_impl
->m_container
, -1, wxDefaultPosition
, wxDefaultSize
,
869 success
= success
&& m_viewport
->Create(m_impl
->m_container
, -1);
870 m_impl
->m_add_child_target
= add_child_target
;
872 wxCursor
cursor(wxCURSOR_ARROW
);
873 m_hscroll
->SetCursor(cursor
);
874 m_vscroll
->SetCursor(cursor
);
875 m_viewport
->SetCursor(cursor
);
877 m_viewport
->SetEventHandler(this);
878 Connect(-1, wxEVT_DYNAMIC_SASH_REPARENT
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnReparent
);
880 if (m_impl
->m_window
->GetWindowStyle() & wxDS_MANAGE_SCROLLBARS
) {
881 m_hscroll
->SetEventHandler(this);
882 m_vscroll
->SetEventHandler(this);
884 Connect(-1, wxEVT_SET_FOCUS
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnFocus
);
885 Connect(-1, wxEVT_SCROLL_TOP
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
886 Connect(-1, wxEVT_SCROLL_BOTTOM
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
887 Connect(-1, wxEVT_SCROLL_LINEUP
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
888 Connect(-1, wxEVT_SCROLL_LINEDOWN
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
889 Connect(-1, wxEVT_SCROLL_PAGEUP
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
890 Connect(-1, wxEVT_SCROLL_PAGEDOWN
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
891 Connect(-1, wxEVT_SCROLL_THUMBTRACK
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
892 Connect(-1, wxEVT_SCROLL_THUMBRELEASE
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
895 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
898 wxSize size
= m_hscroll
->GetBestSize();
900 size
= m_hscroll
->GetSize();
903 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 10);
904 layout
->right
.LeftOf(m_vscroll
);
905 layout
->bottom
.SameAs(m_impl
->m_container
, wxBottom
, 3);
906 layout
->height
.Absolute(size
.GetHeight());
907 m_hscroll
->SetConstraints(layout
);
909 layout
= new wxLayoutConstraints();
912 size
= size
= m_vscroll
->GetBestSize();
914 size
= m_vscroll
->GetSize();
917 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 10);
918 layout
->bottom
.Above(m_hscroll
);
919 layout
->right
.SameAs(m_impl
->m_container
, wxRight
, 3);
920 layout
->width
.Absolute(size
.GetWidth());
921 m_vscroll
->SetConstraints(layout
);
923 layout
= new wxLayoutConstraints();
926 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 3);
927 layout
->right
.LeftOf(m_vscroll
);
928 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 3);
929 layout
->bottom
.Above(m_hscroll
);
930 m_viewport
->SetConstraints(layout
);
932 m_impl
->m_container
->Layout();
937 void wxDynamicSashWindowLeaf::AddChild(wxWindow
*window
) {
944 wxDynamicSashReparentEvent
event(this);
945 AddPendingEvent(event
);
948 DynamicSashRegion
wxDynamicSashWindowLeaf::GetRegion(int x
, int y
) {
949 wxSize size
= m_impl
->m_container
->GetSize();
950 int w
= size
.GetWidth();
951 int h
= size
.GetHeight();
952 size
= m_hscroll
->GetSize();
953 int sh
= size
.GetHeight();
954 size
= m_vscroll
->GetSize();
955 int sw
= size
.GetWidth();
957 if (x
>= w
- sw
- 3 && x
< w
&& y
>= h
- sh
- 3 && y
< h
)
959 if (x
>= 3 && x
< 10 && y
>= h
- sh
- 3 && y
< h
- 2)
960 return DSR_VERTICAL_TAB
;
961 if (x
>= w
- sw
- 3 && x
< w
- 2 && y
>= 3 && y
< 10)
962 return DSR_HORIZONTAL_TAB
;
964 return DSR_LEFT_EDGE
;
968 return DSR_RIGHT_EDGE
;
970 return DSR_BOTTOM_EDGE
;
975 void wxDynamicSashWindowLeaf::ResizeChild(wxSize size
) {
977 if (m_impl
->m_window
->GetWindowStyle() & wxDS_MANAGE_SCROLLBARS
) {
978 m_child
->SetSize(size
);
979 wxSize best_size
= m_child
->GetBestSize();
980 if (best_size
.GetWidth() < size
.GetWidth()) {
981 best_size
.SetWidth(size
.GetWidth());
983 if (best_size
.GetHeight() < size
.GetHeight()) {
984 best_size
.SetHeight(size
.GetHeight());
986 m_child
->SetSize(best_size
);
988 int hpos
= m_hscroll
->GetThumbPosition();
989 int vpos
= m_vscroll
->GetThumbPosition();
997 if (hpos
> best_size
.GetWidth() - size
.GetWidth()) {
998 hpos
= best_size
.GetWidth() - size
.GetWidth();
1000 if (vpos
> best_size
.GetHeight() - size
.GetHeight()) {
1001 vpos
= best_size
.GetHeight() - size
.GetHeight();
1004 m_hscroll
->SetScrollbar(hpos
, size
.GetWidth(),
1005 best_size
.GetWidth(), size
.GetWidth());
1006 m_vscroll
->SetScrollbar(vpos
, size
.GetHeight(),
1007 best_size
.GetHeight(), size
.GetHeight());
1009 // Umm, the scrollbars are doing something insane under GTK+ and subtracting
1010 // one from the position I pass in. This works around that.
1011 m_hscroll
->SetThumbPosition(hpos
+ hpos
- m_hscroll
->GetThumbPosition());
1012 m_vscroll
->SetThumbPosition(vpos
+ vpos
- m_vscroll
->GetThumbPosition());
1014 wxPoint pos
= m_child
->GetPosition();
1015 m_viewport
->ScrollWindow(-hpos
- pos
.x
, -vpos
- pos
.y
);
1017 m_child
->SetSize(size
);
1022 wxScrollBar
*wxDynamicSashWindowLeaf::FindScrollBar(const wxWindow
*child
, int vert
) const {
1023 if (m_child
== child
) {
1034 void wxDynamicSashWindowLeaf::OnSize(wxSizeEvent
&event
) {
1035 m_impl
->m_container
->Refresh();
1036 ResizeChild(m_viewport
->GetSize());
1039 void wxDynamicSashWindowLeaf::OnPaint(wxPaintEvent
&event
) {
1040 wxPaintDC
dc(m_impl
->m_container
);
1041 dc
.SetBackground(wxBrush(m_impl
->m_container
->GetBackgroundColour(), wxSOLID
));
1044 wxPen
highlight(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_BTNHIGHLIGHT
), 1, wxSOLID
);
1045 wxPen
shadow(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_BTNSHADOW
), 1, wxSOLID
);
1046 wxPen
black(*wxBLACK
, 1, wxSOLID
);
1048 wxSize size
= m_impl
->m_container
->GetSize();
1049 int w
= size
.GetWidth();
1050 int h
= size
.GetHeight();
1051 size
= m_hscroll
->GetSize();
1052 int sh
= size
.GetHeight();
1053 size
= m_vscroll
->GetSize();
1054 int sw
= size
.GetWidth();
1057 dc
.DrawLine(1, 1, 1, h
- 2);
1058 dc
.DrawLine(1, 1, w
- 2, 1);
1060 dc
.DrawLine(2, 2, 2, h
- 3);
1061 dc
.DrawLine(2, 2, w
- 3, 2);
1062 dc
.SetPen(highlight
);
1063 dc
.DrawLine(w
- 2, 2, w
- 2, h
- sh
- 2);
1064 dc
.DrawLine(w
- 2, h
- sh
- 2, w
- sw
- 2, h
- sh
- 2);
1065 dc
.DrawLine(w
- sw
- 2, h
- sh
- 2, w
- sw
- 2, h
- 2);
1066 dc
.DrawLine(w
- sw
- 2, h
- 2, 2, h
- 2);
1068 dc
.SetPen(highlight
);
1069 dc
.DrawLine(w
- sw
- 2, 8, w
- sw
- 2, 4);
1070 dc
.DrawLine(w
- sw
- 2, 4, w
- 5, 4);
1072 dc
.DrawLine(w
- 5, 4, w
- 5, 8);
1073 dc
.DrawLine(w
- 5, 8, w
- sw
- 2, 8);
1075 dc
.DrawLine(w
- 4, 3, w
- 4, 9);
1076 dc
.DrawLine(w
- 4, 9, w
- sw
- 3, 9);
1078 dc
.SetPen(highlight
);
1079 dc
.DrawLine(4, h
- 5, 4, h
- sh
- 2);
1080 dc
.DrawLine(4, h
- sh
- 2, 8, h
- sh
- 2);
1082 dc
.DrawLine(8, h
- sh
- 2, 8, h
- 5);
1083 dc
.DrawLine(8, h
- 5, 4, h
- 5);
1085 dc
.DrawLine(9, h
- sh
- 3, 9, h
- 4);
1086 dc
.DrawLine(9, h
- 4, 3, h
- 4);
1088 int cy
= (h
- sh
+ h
- 6) / 2 + 1;
1089 int cx
= (w
- sw
+ w
- 6) / 2 + 1;
1098 for (y
= sy
; y
< h
- 2; y
+= 4) {
1099 for (x
= sx
; x
< w
- 2; x
+= 4) {
1100 if (x
- cx
>= -(y
- cy
)) {
1101 dc
.SetPen(highlight
);
1104 dc
.DrawPoint(x
+ 1, y
+ 1);
1110 void wxDynamicSashWindowLeaf::OnScroll(wxScrollEvent
&event
) {
1111 int nx
= -m_hscroll
->GetThumbPosition();
1112 int ny
= -m_vscroll
->GetThumbPosition();
1115 wxPoint pos
= m_child
->GetPosition();
1117 m_viewport
->ScrollWindow(nx
- pos
.x
, ny
- pos
.y
);
1121 void wxDynamicSashWindowLeaf::OnFocus(wxFocusEvent
&event
) {
1122 if (event
.m_eventObject
== m_hscroll
|| event
.m_eventObject
== m_vscroll
) {
1123 m_child
->SetFocus();
1128 void wxDynamicSashWindowLeaf::OnMouseMove(wxMouseEvent
&event
) {
1129 if (m_impl
->m_dragging
) {
1133 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1135 wxCursor
cursor(wxCURSOR_ARROW
);
1136 if (region
== DSR_HORIZONTAL_TAB
) {
1137 cursor
= wxCursor(wxCURSOR_SIZENS
);
1138 } else if (region
== DSR_VERTICAL_TAB
) {
1139 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1140 } else if ((region
== DSR_CORNER
) &&
1141 (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0) {
1142 cursor
= wxCursor(wxCURSOR_SIZENWSE
);
1143 } else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1144 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
) {
1145 if (m_impl
->FindParent(region
)) {
1146 if (region
== DSR_LEFT_EDGE
|| region
== DSR_RIGHT_EDGE
) {
1147 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1149 cursor
= wxCursor(wxCURSOR_SIZENS
);
1154 m_impl
->m_container
->SetCursor(cursor
);
1157 void wxDynamicSashWindowLeaf::OnLeave(wxMouseEvent
&event
) {
1158 wxCursor
cursor(wxCURSOR_ARROW
);
1159 m_impl
->m_container
->SetCursor(cursor
);
1163 void wxDynamicSashWindowLeaf::OnPress(wxMouseEvent
&event
) {
1164 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1166 if ((region
== DSR_CORNER
) && (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) == 0)
1169 if (region
== DSR_HORIZONTAL_TAB
|| region
== DSR_VERTICAL_TAB
|| region
== DSR_CORNER
) {
1170 m_impl
->m_dragging
= region
;
1171 m_impl
->m_drag_x
= event
.m_x
;
1172 m_impl
->m_drag_y
= event
.m_y
;
1173 m_impl
->DrawSash(event
.m_x
, event
.m_y
);
1174 m_impl
->m_container
->CaptureMouse();
1175 } else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1176 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
) {
1177 wxDynamicSashWindowImpl
*parent
= m_impl
->FindParent(region
);
1183 m_impl
->m_container
->ClientToScreen(&x
, &y
);
1184 parent
->m_container
->ScreenToClient(&x
, &y
);
1186 parent
->m_dragging
= parent
->m_split
;
1187 parent
->m_drag_x
= x
;
1188 parent
->m_drag_y
= y
;
1189 parent
->DrawSash(x
, y
);
1190 parent
->m_container
->CaptureMouse();
1195 void wxDynamicSashWindowLeaf::OnRelease(wxMouseEvent
&event
) {
1198 void wxDynamicSashWindowLeaf::OnReparent(wxEvent
&event
) {
1200 m_child
->Reparent(m_viewport
);
1203 ResizeChild(m_viewport
->GetSize());
1206 // wxDynamicSashSplitEvent //////////////////////////////////////////////////
1208 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent() {
1209 m_eventObject
= NULL
;
1210 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1213 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent(wxObject
*object
) {
1214 m_eventObject
= object
;
1215 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1218 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashSplitEvent
, wxCommandEvent
)
1220 // wxDynamicSashUnifyEvent //////////////////////////////////////////////////
1222 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent() {
1223 m_eventObject
= NULL
;
1224 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1227 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent(wxObject
*object
) {
1228 m_eventObject
= object
;
1229 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1232 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashUnifyEvent
, wxCommandEvent
)
1234 // wxDynamicSsahReparentEvent ///////////////////////////////////////////////
1236 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent() {
1237 m_eventObject
= NULL
;
1238 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1241 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(wxObject
*object
) {
1242 m_eventObject
= object
;
1243 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1246 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
)
1251 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashReparentEvent
, wxEvent
)
1253 /////////////////////////////////////////////////////////////////////////////