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
) {
338 m_container
->ClientToScreen(&cx
, &cy
);
339 m_container
->ClientToScreen(&x
, &y
);
341 if (cx
< x
&& cy
< y
) {
342 dc
.DrawRectangle(cx
- 2, cy
- 2, x
- cx
+ 4, 4);
343 dc
.DrawRectangle(x
- 2, cy
+ 2, 4, y
- cy
);
344 dc
.DrawRectangle(cx
- 2, cy
+ 2, 4, y
- cy
);
345 dc
.DrawRectangle(cx
+ 2, y
- 2, x
- cx
- 4, 4);
349 m_container
->GetClientSize(&body_w
, &body_h
);
360 if (m_dragging
== DSR_HORIZONTAL_TAB
)
365 m_container
->ClientToScreen(&x
, &y
);
368 w
= body_w
; h
= body_h
;
370 if (m_dragging
== DSR_HORIZONTAL_TAB
)
371 dc
.DrawRectangle(x
, y
- 2, w
, 4);
373 dc
.DrawRectangle(x
- 2, y
, 4, h
);
376 dc
.EndDrawingOnTop();
379 wxDynamicSashWindowImpl
*wxDynamicSashWindowImpl::FindParent(DynamicSashRegion side
) const {
380 if (m_parent
== NULL
) {
384 if (m_parent
->m_split
== DSR_HORIZONTAL_TAB
) {
385 if (side
== DSR_TOP_EDGE
&& m_parent
->m_child
[1] == this)
387 if (side
== DSR_BOTTOM_EDGE
&& m_parent
->m_child
[0] == this)
389 } else if (m_parent
->m_split
== DSR_VERTICAL_TAB
) {
390 if (side
== DSR_LEFT_EDGE
&& m_parent
->m_child
[1] == this)
392 if (side
== DSR_RIGHT_EDGE
&& m_parent
->m_child
[0] == this)
396 return m_parent
->FindParent(side
);
399 wxDynamicSashWindowImpl
*wxDynamicSashWindowImpl::FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
400 wxDynamicSashWindowImpl
*sash_b
) const {
402 win
= sash_a
->m_container
->GetParent();
403 while (win
&& !win
->IsTopLevel()) {
404 if (win
== sash_b
->m_container
) {
408 win
= win
->GetParent();
415 wxWindow
*wxDynamicSashWindowImpl::FindFrame() const {
418 win
= m_window
->GetParent();
419 while (win
&& !win
->IsTopLevel()) {
420 win
= win
->GetParent();
426 wxScrollBar
*wxDynamicSashWindowImpl::FindScrollBar(const wxWindow
*child
, int vert
) const {
427 if (m_child
[0] == NULL
&& m_leaf
== NULL
) {
432 return m_leaf
->FindScrollBar(child
, vert
);
434 wxScrollBar
*ret
= m_child
[0]->FindScrollBar(child
, vert
);
436 ret
= m_child
[1]->FindScrollBar(child
, vert
);
443 void wxDynamicSashWindowImpl::ConstrainChildren(int px
, int py
) {
444 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
445 layout
->left
.SameAs(m_container
, wxLeft
);
446 layout
->top
.SameAs(m_container
, wxTop
);
447 if (m_split
== DSR_HORIZONTAL_TAB
) {
448 layout
->right
.SameAs(m_container
, wxRight
);
449 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
451 layout
->bottom
.SameAs(m_container
, wxBottom
);
452 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
454 m_child
[0]->m_container
->SetConstraints(layout
);
456 layout
= new wxLayoutConstraints();
457 layout
->right
.SameAs(m_container
, wxRight
);
458 layout
->bottom
.SameAs(m_container
, wxBottom
);
459 if (m_split
== DSR_HORIZONTAL_TAB
) {
460 layout
->top
.Below(m_child
[0]->m_container
, 1);
461 layout
->left
.SameAs(m_container
, wxLeft
);
463 layout
->left
.RightOf(m_child
[0]->m_container
, 1);
464 layout
->top
.SameAs(m_container
, wxTop
);
466 m_child
[1]->m_container
->SetConstraints(layout
);
469 void wxDynamicSashWindowImpl::Unify(int panel
) {
475 if (m_child
[panel
]->m_leaf
) {
476 wxDynamicSashWindowImpl
*child
[2];
478 child
[0] = m_child
[0];
479 child
[1] = m_child
[1];
481 m_child
[0] = m_child
[1] = NULL
;
483 m_leaf
= new wxDynamicSashWindowLeaf(this);
485 m_leaf
->m_child
= child
[panel
]->m_leaf
->m_child
;
487 m_leaf
->m_vscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_vscroll
->GetThumbPosition(),
488 child
[panel
]->m_leaf
->m_vscroll
->GetThumbSize(),
489 child
[panel
]->m_leaf
->m_vscroll
->GetRange(),
490 child
[panel
]->m_leaf
->m_vscroll
->GetPageSize());
491 m_leaf
->m_hscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_hscroll
->GetThumbPosition(),
492 child
[panel
]->m_leaf
->m_hscroll
->GetThumbSize(),
493 child
[panel
]->m_leaf
->m_hscroll
->GetRange(),
494 child
[panel
]->m_leaf
->m_hscroll
->GetPageSize());
495 m_add_child_target
= NULL
;
496 wxDynamicSashReparentEvent
event(m_leaf
);
497 m_leaf
->ProcessEvent(event
);
504 wxDynamicSashUnifyEvent
unify(m_leaf
->m_child
);
505 m_leaf
->m_child
->ProcessEvent(unify
);
507 m_split
= m_child
[panel
]->m_split
;
509 delete m_child
[other
];
511 wxDynamicSashWindowImpl
*child_panel
= m_child
[panel
];
512 m_child
[0] = child_panel
->m_child
[0];
513 m_child
[1] = child_panel
->m_child
[1];
515 m_child
[0]->m_parent
= this;
516 m_child
[1]->m_parent
= this;
518 m_add_child_target
= NULL
;
519 m_child
[0]->m_container
->Reparent(m_container
);
520 m_child
[1]->m_container
->Reparent(m_container
);
522 child_panel
->m_child
[0] = child_panel
->m_child
[1] = NULL
;
525 wxSize size
= m_container
->GetSize();
526 wxSize child_size
= m_child
[0]->m_container
->GetSize();
528 ConstrainChildren(child_size
.GetWidth() * 100 / size
.GetWidth(),
529 child_size
.GetHeight() * 100 / size
.GetHeight());
531 m_container
->Layout();
535 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();
587 /* This code is called when you finish resizing a view by dragging the
588 corner tab, but I think this implementation is lousy and will surprise
589 the user more often than it will do what they are trying to do. What
590 I really need to be doing here is do a rewrite such that *no* sashes
591 move except the ones immediately to the bottom and right of this window,
592 and handle the case where you resize a window larger than it's neighbors
593 by destroying the neighbors.
595 But this will do for now. */
596 void wxDynamicSashWindowImpl::Resize(int x
, int y
) {
597 wxDynamicSashWindowImpl
*h_parent
= FindParent(DSR_BOTTOM_EDGE
);
598 wxDynamicSashWindowImpl
*v_parent
= FindParent(DSR_RIGHT_EDGE
);
601 wxWindow
*frame
= FindFrame();
611 m_container
->ClientToScreen(NULL
, &y
);
612 h_parent
->m_container
->ScreenToClient(NULL
, &y
);
614 int py
= (int)((y
* 100) / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
617 wxDynamicSashWindowImpl
*ho_parent
= FindParent(DSR_TOP_EDGE
);
620 if (FindUpperParent(h_parent
, ho_parent
) == ho_parent
) {
623 py
= (int)((ho_parent
->m_child
[0]->m_container
->GetSize().GetHeight() * 100)
624 / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
625 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
626 h_parent
->m_container
, wxHeight
, py
);
628 h_parent
= ho_parent
;
634 } else if (py
> 90) {
637 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
638 h_parent
->m_container
, wxHeight
, py
);
639 h_parent
->m_container
->Layout();
643 h_parent
= FindParent(DSR_TOP_EDGE
);
646 int py
= (int)((y
* 100) /
647 (h_parent
->m_container
->GetSize().GetHeight() +
648 y
- m_container
->GetSize().GetHeight()) + 0.5);
658 wxSize size
= frame
->GetSize();
659 frame
->SetSize(size
.GetWidth(), size
.GetHeight() + y
- m_container
->GetSize().GetHeight());
664 m_container
->ClientToScreen(&x
, NULL
);
665 v_parent
->m_container
->ScreenToClient(&x
, NULL
);
667 int px
= (int)((x
* 100) / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
670 wxDynamicSashWindowImpl
*vo_parent
= FindParent(DSR_LEFT_EDGE
);
673 if (FindUpperParent(v_parent
, vo_parent
) == vo_parent
) {
676 px
= (int)((vo_parent
->m_child
[0]->m_container
->GetSize().GetWidth() * 100)
677 / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
678 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
679 v_parent
->m_container
, wxWidth
, px
);
681 v_parent
= vo_parent
;
687 } else if (px
> 90) {
690 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
691 v_parent
->m_container
, wxWidth
, px
);
692 v_parent
->m_container
->Layout();
696 v_parent
= FindParent(DSR_LEFT_EDGE
);
699 int px
= (int)((x
* 100) /
700 (v_parent
->m_container
->GetSize().GetWidth() +
701 x
- m_container
->GetSize().GetWidth()) + 0.5);
711 wxSize size
= frame
->GetSize();
712 frame
->SetSize(size
.GetWidth() + x
- m_container
->GetSize().GetWidth(), size
.GetHeight());
716 if (h_unify
!= -1 && v_unify
!= -1) {
717 wxDynamicSashWindowImpl
*parent
= FindUpperParent(h_parent
, v_parent
);
719 if (parent
== h_parent
) {
720 h_parent
->Unify(h_unify
);
722 v_parent
->Unify(v_unify
);
724 } else if (h_unify
!= -1) {
725 h_parent
->Unify(h_unify
);
726 } else if (v_unify
!= -1) {
727 v_parent
->Unify(v_unify
);
732 void wxDynamicSashWindowImpl::OnSize(wxSizeEvent
&event
) {
733 m_container
->Layout();
736 m_leaf
->OnSize(event
);
739 void wxDynamicSashWindowImpl::OnPaint(wxPaintEvent
&event
) {
741 m_leaf
->OnPaint(event
);
743 wxPaintDC
dc(m_container
);
744 dc
.SetBackground(wxBrush(m_container
->GetBackgroundColour(), wxSOLID
));
749 void wxDynamicSashWindowImpl::OnMouseMove(wxMouseEvent
&event
) {
751 DrawSash(m_drag_x
, m_drag_y
);
752 m_drag_x
= event
.m_x
; m_drag_y
= event
.m_y
;
753 DrawSash(m_drag_x
, m_drag_y
);
755 m_leaf
->OnMouseMove(event
);
759 void wxDynamicSashWindowImpl::OnLeave(wxMouseEvent
&event
) {
761 m_leaf
->OnLeave(event
);
765 void wxDynamicSashWindowImpl::OnPress(wxMouseEvent
&event
) {
767 m_leaf
->OnPress(event
);
769 m_dragging
= m_split
;
770 m_drag_x
= event
.m_x
;
771 m_drag_y
= event
.m_y
;
772 DrawSash(m_drag_x
, m_drag_y
);
773 m_container
->CaptureMouse();
777 void wxDynamicSashWindowImpl::OnRelease(wxMouseEvent
&event
) {
778 if (m_dragging
== DSR_CORNER
) {
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() & wxMANAGE_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() & wxMANAGE_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);
1089 int cy
= (h
- sh
+ h
- 6) / 2 + 1;
1090 int cx
= (w
- sw
+ w
- 6) / 2 + 1;
1099 for (y
= sy
; y
< h
- 2; y
+= 4) {
1100 for (x
= sx
; x
< w
- 2; x
+= 4) {
1101 if (x
- cx
>= -(y
- cy
)) {
1102 dc
.SetPen(highlight
);
1105 dc
.DrawPoint(x
+ 1, y
+ 1);
1111 void wxDynamicSashWindowLeaf::OnScroll(wxScrollEvent
&event
) {
1112 int nx
= -m_hscroll
->GetThumbPosition();
1113 int ny
= -m_vscroll
->GetThumbPosition();
1116 wxPoint pos
= m_child
->GetPosition();
1118 m_viewport
->ScrollWindow(nx
- pos
.x
, ny
- pos
.y
);
1122 void wxDynamicSashWindowLeaf::OnFocus(wxFocusEvent
&event
) {
1123 if (event
.m_eventObject
== m_hscroll
|| event
.m_eventObject
== m_vscroll
) {
1124 m_child
->SetFocus();
1129 void wxDynamicSashWindowLeaf::OnMouseMove(wxMouseEvent
&event
) {
1130 if (m_impl
->m_dragging
) {
1134 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1136 wxCursor
cursor(wxCURSOR_ARROW
);
1137 if (region
== DSR_HORIZONTAL_TAB
) {
1138 cursor
= wxCursor(wxCURSOR_SIZENS
);
1139 } else if (region
== DSR_VERTICAL_TAB
) {
1140 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1141 } else if (region
== DSR_CORNER
) {
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_HORIZONTAL_TAB
|| region
== DSR_VERTICAL_TAB
|| region
== DSR_CORNER
) {
1167 m_impl
->m_dragging
= region
;
1168 m_impl
->m_drag_x
= event
.m_x
;
1169 m_impl
->m_drag_y
= event
.m_y
;
1170 m_impl
->DrawSash(event
.m_x
, event
.m_y
);
1171 m_impl
->m_container
->CaptureMouse();
1172 } else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1173 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
) {
1174 wxDynamicSashWindowImpl
*parent
= m_impl
->FindParent(region
);
1180 m_impl
->m_container
->ClientToScreen(&x
, &y
);
1181 parent
->m_container
->ScreenToClient(&x
, &y
);
1183 parent
->m_dragging
= parent
->m_split
;
1184 parent
->m_drag_x
= x
;
1185 parent
->m_drag_y
= y
;
1186 parent
->DrawSash(x
, y
);
1187 parent
->m_container
->CaptureMouse();
1192 void wxDynamicSashWindowLeaf::OnRelease(wxMouseEvent
&event
) {
1195 void wxDynamicSashWindowLeaf::OnReparent(wxEvent
&event
) {
1197 m_child
->Reparent(m_viewport
);
1200 ResizeChild(m_viewport
->GetSize());
1203 // wxDynamicSashSplitEvent //////////////////////////////////////////////////
1205 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent() {
1206 m_eventObject
= NULL
;
1207 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1210 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent(wxObject
*object
) {
1211 m_eventObject
= object
;
1212 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1215 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashSplitEvent
, wxCommandEvent
)
1217 // wxDynamicSashUnifyEvent //////////////////////////////////////////////////
1219 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent() {
1220 m_eventObject
= NULL
;
1221 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1224 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent(wxObject
*object
) {
1225 m_eventObject
= object
;
1226 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1229 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashUnifyEvent
, wxCommandEvent
)
1231 // wxDynamicSsahReparentEvent ///////////////////////////////////////////////
1233 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent() {
1234 m_eventObject
= NULL
;
1235 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1238 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(wxObject
*object
) {
1239 m_eventObject
= object
;
1240 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1243 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
)
1248 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashReparentEvent
, wxEvent
)
1250 /////////////////////////////////////////////////////////////////////////////