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
);
87 virtual wxEvent
* Clone() const { return NULL
; }
89 DECLARE_DYNAMIC_CLASS(wxDynamicSashReparentEvent
);
93 enum DynamicSashRegion
{
105 class wxDynamicSashWindowImpl
: public wxEvtHandler
{
107 wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
);
108 ~wxDynamicSashWindowImpl();
111 void AddChild(wxWindow
*window
);
112 void DrawSash(int x
, int y
) const;
113 void ConstrainChildren(int px
, int py
);
114 void Split(int x
, int y
);
115 void Unify(int panel
);
116 void Resize(int x
, int y
);
117 wxDynamicSashWindowImpl
*FindParent(DynamicSashRegion side
) const;
118 wxDynamicSashWindowImpl
*FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
119 wxDynamicSashWindowImpl
*sash_b
) const;
120 wxWindow
*FindFrame() const;
121 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
123 void OnSize(wxSizeEvent
&event
);
124 void OnPaint(wxPaintEvent
&event
);
125 void OnMouseMove(wxMouseEvent
&event
);
126 void OnLeave(wxMouseEvent
&event
);
127 void OnPress(wxMouseEvent
&event
);
128 void OnRelease(wxMouseEvent
&event
);
130 wxDynamicSashWindow
*m_window
;
131 wxDynamicSashWindowImpl
*m_add_child_target
;
133 /* This is the window we are responsible for managing. Either of
134 leaf or our children are in this window. For the top level
135 implementation object, this is the same as m_window.
136 Otherwise it is a window we've created an will destroy when we
138 wxWindow
*m_container
;
140 wxDynamicSashWindowImpl
*m_parent
;
141 wxDynamicSashWindowImpl
*m_top
;
142 wxDynamicSashWindowImpl
*m_child
[2];
144 class wxDynamicSashWindowLeaf
*m_leaf
;
146 /* If the implementation is split horizontally or vertically, m_split
147 is set to DSR_HORIZONTAL_TAB or DSR_VERTICAL_TAB, respectively.
148 Otherwise it is set to DSR_NONE. */
149 DynamicSashRegion m_split
;
151 /* These are used to keep track of a sash as it is being dragged, for
152 drawing the user interface correctly. */
153 DynamicSashRegion m_dragging
;
154 int m_drag_x
, m_drag_y
;
157 class wxDynamicSashWindowLeaf
: public wxEvtHandler
{
159 wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
);
160 ~wxDynamicSashWindowLeaf();
163 void AddChild(wxWindow
*window
);
164 DynamicSashRegion
GetRegion(int x
, int y
);
165 void ResizeChild(wxSize size
);
166 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
168 void OnSize(wxSizeEvent
&event
);
169 void OnPaint(wxPaintEvent
&event
);
170 void OnScroll(wxScrollEvent
&event
);
171 void OnFocus(wxFocusEvent
&event
);
172 void OnMouseMove(wxMouseEvent
&event
);
173 void OnLeave(wxMouseEvent
&event
);
174 void OnPress(wxMouseEvent
&event
);
175 void OnRelease(wxMouseEvent
&event
);
176 void OnReparent(wxEvent
&event
);
178 wxDynamicSashWindowImpl
*m_impl
;
180 wxScrollBar
*m_vscroll
, *m_hscroll
;
182 /* m_child is the window provided to us by the application developer.
183 m_viewport is a window we've created, and it is the immediately
184 parent of m_child. We scroll m_child by moving it around within
186 wxWindow
*m_viewport
, *m_child
;
189 // wxDynamicSashWindow //////////////////////////////////////////////////////
191 wxDynamicSashWindow::wxDynamicSashWindow() {
195 wxDynamicSashWindow::wxDynamicSashWindow(wxWindow
*parent
, wxWindowID id
,
196 const wxPoint
& pos
, const wxSize
& size
,
197 long style
, const wxString
& name
) {
199 Create(parent
, id
, pos
, size
, style
, name
);
202 wxDynamicSashWindow::~wxDynamicSashWindow() {
203 SetEventHandler(this);
207 bool wxDynamicSashWindow::Create(wxWindow
*parent
, wxWindowID id
,
208 const wxPoint
& pos
, const wxSize
& size
,
209 long style
, const wxString
& name
) {
213 if (!wxWindow::Create(parent
, id
, pos
, size
, style
, name
))
216 m_impl
= new wxDynamicSashWindowImpl(this);
220 if (!m_impl
->Create()) {
229 void wxDynamicSashWindow::AddChild(wxWindowBase
*child
) {
230 wxWindow::AddChild(child
);
232 m_impl
->AddChild(wxDynamicCast(child
, wxWindow
));
235 wxScrollBar
*wxDynamicSashWindow::GetHScrollBar(const wxWindow
*child
) const {
236 return m_impl
->FindScrollBar(child
, 0);
239 wxScrollBar
*wxDynamicSashWindow::GetVScrollBar(const wxWindow
*child
) const {
240 return m_impl
->FindScrollBar(child
, 1);
243 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashWindow
, wxWindow
)
245 // wxDynamicSashWindowImpl //////////////////////////////////////////////////
247 wxDynamicSashWindowImpl::wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
) {
249 m_add_child_target
= this;
254 m_child
[0] = m_child
[1] = NULL
;
256 m_dragging
= DSR_NONE
;
260 wxDynamicSashWindowImpl::~wxDynamicSashWindowImpl() {
268 if (m_container
!= m_window
&& m_container
) {
269 m_container
->SetEventHandler(m_container
);
270 m_container
->Destroy();
274 bool wxDynamicSashWindowImpl::Create() {
276 m_container
= m_window
;
279 wxCursor
cursor(wxCURSOR_ARROW
);
280 m_container
->SetCursor(cursor
);
282 m_leaf
= new wxDynamicSashWindowLeaf(this);
286 if (!m_leaf
->Create()) {
292 m_container
->SetEventHandler(this);
294 Connect(-1, wxEVT_SIZE
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnSize
);
295 Connect(-1, wxEVT_PAINT
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnPaint
);
296 Connect(-1, wxEVT_MOTION
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnMouseMove
);
297 Connect(-1, wxEVT_ENTER_WINDOW
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnMouseMove
);
298 Connect(-1, wxEVT_LEAVE_WINDOW
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnLeave
);
299 Connect(-1, wxEVT_LEFT_DOWN
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnPress
);
300 Connect(-1, wxEVT_LEFT_UP
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnRelease
);
305 void wxDynamicSashWindowImpl::AddChild(wxWindow
*window
) {
306 if (m_add_child_target
&& m_add_child_target
->m_leaf
) {
307 m_add_child_target
->m_leaf
->AddChild(window
);
311 void wxDynamicSashWindowImpl::DrawSash(int x
, int y
) const {
315 dc
.StartDrawingOnTop(m_container
);
319 bdc
.SelectObject(bmp
);
320 bdc
.DrawRectangle(-1, -1, 10, 10);
321 for (i
= 0; i
< 8; i
++) {
322 for (j
= 0; j
< 8; j
++) {
331 dc
.SetLogicalFunction(wxXOR
);
333 if (m_dragging
== DSR_CORNER
) {
337 m_container
->ClientToScreen(&cx
, &cy
);
338 m_container
->ClientToScreen(&x
, &y
);
340 if (cx
< x
&& cy
< y
) {
341 dc
.DrawRectangle(cx
- 2, cy
- 2, x
- cx
+ 4, 4);
342 dc
.DrawRectangle(x
- 2, cy
+ 2, 4, y
- cy
);
343 dc
.DrawRectangle(cx
- 2, cy
+ 2, 4, y
- cy
);
344 dc
.DrawRectangle(cx
+ 2, y
- 2, x
- cx
- 4, 4);
348 m_container
->GetClientSize(&body_w
, &body_h
);
359 if (m_dragging
== DSR_HORIZONTAL_TAB
)
364 m_container
->ClientToScreen(&x
, &y
);
367 w
= body_w
; h
= body_h
;
369 if (m_dragging
== DSR_HORIZONTAL_TAB
)
370 dc
.DrawRectangle(x
, y
- 2, w
, 4);
372 dc
.DrawRectangle(x
- 2, y
, 4, h
);
375 dc
.EndDrawingOnTop();
378 wxDynamicSashWindowImpl
*wxDynamicSashWindowImpl::FindParent(DynamicSashRegion side
) const {
379 if (m_parent
== NULL
) {
383 if (m_parent
->m_split
== DSR_HORIZONTAL_TAB
) {
384 if (side
== DSR_TOP_EDGE
&& m_parent
->m_child
[1] == this)
386 if (side
== DSR_BOTTOM_EDGE
&& m_parent
->m_child
[0] == this)
388 } else if (m_parent
->m_split
== DSR_VERTICAL_TAB
) {
389 if (side
== DSR_LEFT_EDGE
&& m_parent
->m_child
[1] == this)
391 if (side
== DSR_RIGHT_EDGE
&& m_parent
->m_child
[0] == this)
395 return m_parent
->FindParent(side
);
398 wxDynamicSashWindowImpl
*wxDynamicSashWindowImpl::FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
399 wxDynamicSashWindowImpl
*sash_b
) const {
401 win
= sash_a
->m_container
->GetParent();
402 while (win
&& !win
->IsTopLevel()) {
403 if (win
== sash_b
->m_container
) {
407 win
= win
->GetParent();
414 wxWindow
*wxDynamicSashWindowImpl::FindFrame() const {
417 win
= m_window
->GetParent();
418 while (win
&& !win
->IsTopLevel()) {
419 win
= win
->GetParent();
425 wxScrollBar
*wxDynamicSashWindowImpl::FindScrollBar(const wxWindow
*child
, int vert
) const {
426 if (m_child
[0] == NULL
&& m_leaf
== NULL
) {
431 return m_leaf
->FindScrollBar(child
, vert
);
433 wxScrollBar
*ret
= m_child
[0]->FindScrollBar(child
, vert
);
435 ret
= m_child
[1]->FindScrollBar(child
, vert
);
442 void wxDynamicSashWindowImpl::ConstrainChildren(int px
, int py
) {
443 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
444 layout
->left
.SameAs(m_container
, wxLeft
);
445 layout
->top
.SameAs(m_container
, wxTop
);
446 if (m_split
== DSR_HORIZONTAL_TAB
) {
447 layout
->right
.SameAs(m_container
, wxRight
);
448 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
450 layout
->bottom
.SameAs(m_container
, wxBottom
);
451 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
453 m_child
[0]->m_container
->SetConstraints(layout
);
455 layout
= new wxLayoutConstraints();
456 layout
->right
.SameAs(m_container
, wxRight
);
457 layout
->bottom
.SameAs(m_container
, wxBottom
);
458 if (m_split
== DSR_HORIZONTAL_TAB
) {
459 layout
->top
.Below(m_child
[0]->m_container
, 1);
460 layout
->left
.SameAs(m_container
, wxLeft
);
462 layout
->left
.RightOf(m_child
[0]->m_container
, 1);
463 layout
->top
.SameAs(m_container
, wxTop
);
465 m_child
[1]->m_container
->SetConstraints(layout
);
468 void wxDynamicSashWindowImpl::Unify(int panel
) {
474 if (m_child
[panel
]->m_leaf
) {
475 wxDynamicSashWindowImpl
*child
[2];
477 child
[0] = m_child
[0];
478 child
[1] = m_child
[1];
480 m_child
[0] = m_child
[1] = NULL
;
482 m_leaf
= new wxDynamicSashWindowLeaf(this);
484 m_leaf
->m_child
= child
[panel
]->m_leaf
->m_child
;
486 m_leaf
->m_vscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_vscroll
->GetThumbPosition(),
487 child
[panel
]->m_leaf
->m_vscroll
->GetThumbSize(),
488 child
[panel
]->m_leaf
->m_vscroll
->GetRange(),
489 child
[panel
]->m_leaf
->m_vscroll
->GetPageSize());
490 m_leaf
->m_hscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_hscroll
->GetThumbPosition(),
491 child
[panel
]->m_leaf
->m_hscroll
->GetThumbSize(),
492 child
[panel
]->m_leaf
->m_hscroll
->GetRange(),
493 child
[panel
]->m_leaf
->m_hscroll
->GetPageSize());
494 m_add_child_target
= NULL
;
495 wxDynamicSashReparentEvent
event(m_leaf
);
496 m_leaf
->ProcessEvent(event
);
503 wxDynamicSashUnifyEvent
unify(m_leaf
->m_child
);
504 m_leaf
->m_child
->ProcessEvent(unify
);
506 m_split
= m_child
[panel
]->m_split
;
508 delete m_child
[other
];
510 wxDynamicSashWindowImpl
*child_panel
= m_child
[panel
];
511 m_child
[0] = child_panel
->m_child
[0];
512 m_child
[1] = child_panel
->m_child
[1];
514 m_child
[0]->m_parent
= this;
515 m_child
[1]->m_parent
= this;
517 m_add_child_target
= NULL
;
518 m_child
[0]->m_container
->Reparent(m_container
);
519 m_child
[1]->m_container
->Reparent(m_container
);
521 child_panel
->m_child
[0] = child_panel
->m_child
[1] = NULL
;
524 wxSize size
= m_container
->GetSize();
525 wxSize child_size
= m_child
[0]->m_container
->GetSize();
527 ConstrainChildren(child_size
.GetWidth() * 100 / size
.GetWidth(),
528 child_size
.GetHeight() * 100 / size
.GetHeight());
530 m_container
->Layout();
534 void wxDynamicSashWindowImpl::Split(int px
, int py
) {
537 m_add_child_target
= NULL
;
539 m_child
[0] = new wxDynamicSashWindowImpl(m_window
);
540 m_child
[0]->m_container
= new wxWindow(m_container
, -1);
541 m_child
[0]->m_parent
= this;
542 m_child
[0]->m_top
= m_top
;
543 m_child
[0]->Create();
544 if (m_leaf
->m_child
) {
545 m_leaf
->m_child
->Reparent(m_container
);
546 m_child
[0]->AddChild(m_leaf
->m_child
);
549 m_child
[1] = new wxDynamicSashWindowImpl(m_window
);
550 m_child
[1]->m_container
= new wxWindow(m_container
, -1);
551 m_child
[1]->m_parent
= this;
552 m_child
[1]->m_top
= m_top
;
553 m_child
[1]->Create();
555 m_split
= m_dragging
;
556 ConstrainChildren(px
, py
);
558 m_top
->m_add_child_target
= m_child
[1];
559 wxDynamicSashSplitEvent
split(m_child
[0]->m_leaf
->m_child
);
560 m_child
[0]->m_leaf
->m_child
->ProcessEvent(split
);
562 m_child
[0]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
563 m_leaf
->m_vscroll
->GetThumbSize(),
564 m_leaf
->m_vscroll
->GetRange(),
565 m_leaf
->m_vscroll
->GetPageSize());
566 m_child
[0]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
567 m_leaf
->m_hscroll
->GetThumbSize(),
568 m_leaf
->m_hscroll
->GetRange(),
569 m_leaf
->m_hscroll
->GetPageSize());
570 m_child
[1]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
571 m_leaf
->m_vscroll
->GetThumbSize(),
572 m_leaf
->m_vscroll
->GetRange(),
573 m_leaf
->m_vscroll
->GetPageSize());
574 m_child
[1]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
575 m_leaf
->m_hscroll
->GetThumbSize(),
576 m_leaf
->m_hscroll
->GetRange(),
577 m_leaf
->m_hscroll
->GetPageSize());
581 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 DrawSash(m_drag_x
, m_drag_y
);
779 m_container
->ReleaseMouse();
781 Resize(event
.m_x
, event
.m_y
);
783 m_dragging
= DSR_NONE
;
784 } else if (m_dragging
) {
785 DrawSash(m_drag_x
, m_drag_y
);
786 m_container
->ReleaseMouse();
788 wxSize size
= m_container
->GetSize();
789 int px
= (int)((event
.m_x
* 100) / size
.GetWidth() + 0.5);
790 int py
= (int)((event
.m_y
* 100) / size
.GetHeight() + 0.5);
792 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
>= 10 && py
<= 90)
793 || (m_dragging
== DSR_VERTICAL_TAB
&& px
>= 10 && px
<= 90)) {
794 if (m_child
[0] == NULL
) {
797 /* It would be nice if moving *this* sash didn't implicitly move
798 the sashes of our children (if any). But this will do. */
799 wxLayoutConstraints
*layout
= m_child
[0]->m_container
->GetConstraints();
800 if (m_split
== DSR_HORIZONTAL_TAB
) {
801 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
803 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
805 m_container
->Layout();
808 if (m_child
[0] != NULL
) {
809 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
<= 10)
810 || (m_dragging
== DSR_VERTICAL_TAB
&& px
<= 10)) {
818 wxCursor
cursor(wxCURSOR_ARROW
);
819 if (m_split
== DSR_HORIZONTAL_TAB
) {
820 cursor
= wxCursor(wxCURSOR_SIZENS
);
821 } else if (m_split
== DSR_VERTICAL_TAB
) {
822 cursor
= wxCursor(wxCURSOR_SIZEWE
);
824 m_container
->SetCursor(cursor
);
826 m_dragging
= DSR_NONE
;
828 m_leaf
->OnRelease(event
);
832 // wxDynamicSashWindowLeaf //////////////////////////////////////////////////
834 wxDynamicSashWindowLeaf::wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
) {
837 m_hscroll
= m_vscroll
= NULL
;
841 wxDynamicSashWindowLeaf::~wxDynamicSashWindowLeaf() {
842 m_hscroll
->SetEventHandler(m_hscroll
);
843 m_vscroll
->SetEventHandler(m_vscroll
);
844 m_viewport
->SetEventHandler(m_viewport
);
846 m_hscroll
->Destroy();
847 m_vscroll
->Destroy();
848 m_viewport
->Destroy();
851 bool wxDynamicSashWindowLeaf::Create() {
854 m_hscroll
= new wxScrollBar();
855 m_vscroll
= new wxScrollBar();
856 m_viewport
= new wxWindow();
858 if (!m_hscroll
|| !m_vscroll
|| !m_viewport
) {
862 wxDynamicSashWindowImpl
*add_child_target
= m_impl
->m_add_child_target
;
863 m_impl
->m_add_child_target
= NULL
;
864 success
= m_hscroll
->Create(m_impl
->m_container
, -1, wxDefaultPosition
, wxDefaultSize
,
866 success
= success
&& m_vscroll
->Create(m_impl
->m_container
, -1, wxDefaultPosition
, wxDefaultSize
,
868 success
= success
&& m_viewport
->Create(m_impl
->m_container
, -1);
869 m_impl
->m_add_child_target
= add_child_target
;
871 wxCursor
cursor(wxCURSOR_ARROW
);
872 m_hscroll
->SetCursor(cursor
);
873 m_vscroll
->SetCursor(cursor
);
874 m_viewport
->SetCursor(cursor
);
876 m_viewport
->SetEventHandler(this);
877 Connect(-1, wxEVT_DYNAMIC_SASH_REPARENT
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnReparent
);
879 if (m_impl
->m_window
->GetWindowStyle() & wxMANAGE_SCROLLBARS
) {
880 m_hscroll
->SetEventHandler(this);
881 m_vscroll
->SetEventHandler(this);
883 Connect(-1, wxEVT_SET_FOCUS
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnFocus
);
884 Connect(-1, wxEVT_SCROLL_TOP
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
885 Connect(-1, wxEVT_SCROLL_BOTTOM
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
886 Connect(-1, wxEVT_SCROLL_LINEUP
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
887 Connect(-1, wxEVT_SCROLL_LINEDOWN
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
888 Connect(-1, wxEVT_SCROLL_PAGEUP
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
889 Connect(-1, wxEVT_SCROLL_PAGEDOWN
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
890 Connect(-1, wxEVT_SCROLL_THUMBTRACK
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
891 Connect(-1, wxEVT_SCROLL_THUMBRELEASE
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
894 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
897 wxSize size
= m_hscroll
->GetBestSize();
899 size
= m_hscroll
->GetSize();
902 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 10);
903 layout
->right
.LeftOf(m_vscroll
);
904 layout
->bottom
.SameAs(m_impl
->m_container
, wxBottom
, 3);
905 layout
->height
.Absolute(size
.GetHeight());
906 m_hscroll
->SetConstraints(layout
);
908 layout
= new wxLayoutConstraints();
911 size
= size
= m_vscroll
->GetBestSize();
913 size
= m_vscroll
->GetSize();
916 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 10);
917 layout
->bottom
.Above(m_hscroll
);
918 layout
->right
.SameAs(m_impl
->m_container
, wxRight
, 3);
919 layout
->width
.Absolute(size
.GetWidth());
920 m_vscroll
->SetConstraints(layout
);
922 layout
= new wxLayoutConstraints();
925 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 3);
926 layout
->right
.LeftOf(m_vscroll
);
927 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 3);
928 layout
->bottom
.Above(m_hscroll
);
929 m_viewport
->SetConstraints(layout
);
931 m_impl
->m_container
->Layout();
936 void wxDynamicSashWindowLeaf::AddChild(wxWindow
*window
) {
943 wxDynamicSashReparentEvent
event(this);
944 AddPendingEvent(event
);
947 DynamicSashRegion
wxDynamicSashWindowLeaf::GetRegion(int x
, int y
) {
948 wxSize size
= m_impl
->m_container
->GetSize();
949 int w
= size
.GetWidth();
950 int h
= size
.GetHeight();
951 size
= m_hscroll
->GetSize();
952 int sh
= size
.GetHeight();
953 size
= m_vscroll
->GetSize();
954 int sw
= size
.GetWidth();
956 if (x
>= w
- sw
- 3 && x
< w
&& y
>= h
- sh
- 3 && y
< h
)
958 if (x
>= 3 && x
< 10 && y
>= h
- sh
- 3 && y
< h
- 2)
959 return DSR_VERTICAL_TAB
;
960 if (x
>= w
- sw
- 3 && x
< w
- 2 && y
>= 3 && y
< 10)
961 return DSR_HORIZONTAL_TAB
;
963 return DSR_LEFT_EDGE
;
967 return DSR_RIGHT_EDGE
;
969 return DSR_BOTTOM_EDGE
;
974 void wxDynamicSashWindowLeaf::ResizeChild(wxSize size
) {
976 if (m_impl
->m_window
->GetWindowStyle() & wxMANAGE_SCROLLBARS
) {
977 m_child
->SetSize(size
);
978 wxSize best_size
= m_child
->GetBestSize();
979 if (best_size
.GetWidth() < size
.GetWidth()) {
980 best_size
.SetWidth(size
.GetWidth());
982 if (best_size
.GetHeight() < size
.GetHeight()) {
983 best_size
.SetHeight(size
.GetHeight());
985 m_child
->SetSize(best_size
);
987 int hpos
= m_hscroll
->GetThumbPosition();
988 int vpos
= m_vscroll
->GetThumbPosition();
996 if (hpos
> best_size
.GetWidth() - size
.GetWidth()) {
997 hpos
= best_size
.GetWidth() - size
.GetWidth();
999 if (vpos
> best_size
.GetHeight() - size
.GetHeight()) {
1000 vpos
= best_size
.GetHeight() - size
.GetHeight();
1003 m_hscroll
->SetScrollbar(hpos
, size
.GetWidth(),
1004 best_size
.GetWidth(), size
.GetWidth());
1005 m_vscroll
->SetScrollbar(vpos
, size
.GetHeight(),
1006 best_size
.GetHeight(), size
.GetHeight());
1008 // Umm, the scrollbars are doing something insane under GTK+ and subtracting
1009 // one from the position I pass in. This works around that.
1010 m_hscroll
->SetThumbPosition(hpos
+ hpos
- m_hscroll
->GetThumbPosition());
1011 m_vscroll
->SetThumbPosition(vpos
+ vpos
- m_vscroll
->GetThumbPosition());
1013 wxPoint pos
= m_child
->GetPosition();
1014 m_viewport
->ScrollWindow(-hpos
- pos
.x
, -vpos
- pos
.y
);
1016 m_child
->SetSize(size
);
1021 wxScrollBar
*wxDynamicSashWindowLeaf::FindScrollBar(const wxWindow
*child
, int vert
) const {
1022 if (m_child
== child
) {
1033 void wxDynamicSashWindowLeaf::OnSize(wxSizeEvent
&event
) {
1034 m_impl
->m_container
->Refresh();
1035 ResizeChild(m_viewport
->GetSize());
1038 void wxDynamicSashWindowLeaf::OnPaint(wxPaintEvent
&event
) {
1039 wxPaintDC
dc(m_impl
->m_container
);
1040 dc
.SetBackground(wxBrush(m_impl
->m_container
->GetBackgroundColour(), wxSOLID
));
1043 wxPen
highlight(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_BTNHIGHLIGHT
), 1, wxSOLID
);
1044 wxPen
shadow(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_BTNSHADOW
), 1, wxSOLID
);
1045 wxPen
black(*wxBLACK
, 1, wxSOLID
);
1047 wxSize size
= m_impl
->m_container
->GetSize();
1048 int w
= size
.GetWidth();
1049 int h
= size
.GetHeight();
1050 size
= m_hscroll
->GetSize();
1051 int sh
= size
.GetHeight();
1052 size
= m_vscroll
->GetSize();
1053 int sw
= size
.GetWidth();
1056 dc
.DrawLine(1, 1, 1, h
- 2);
1057 dc
.DrawLine(1, 1, w
- 2, 1);
1059 dc
.DrawLine(2, 2, 2, h
- 3);
1060 dc
.DrawLine(2, 2, w
- 3, 2);
1061 dc
.SetPen(highlight
);
1062 dc
.DrawLine(w
- 2, 2, w
- 2, h
- sh
- 2);
1063 dc
.DrawLine(w
- 2, h
- sh
- 2, w
- sw
- 2, h
- sh
- 2);
1064 dc
.DrawLine(w
- sw
- 2, h
- sh
- 2, w
- sw
- 2, h
- 2);
1065 dc
.DrawLine(w
- sw
- 2, h
- 2, 2, h
- 2);
1067 dc
.SetPen(highlight
);
1068 dc
.DrawLine(w
- sw
- 2, 8, w
- sw
- 2, 4);
1069 dc
.DrawLine(w
- sw
- 2, 4, w
- 5, 4);
1071 dc
.DrawLine(w
- 5, 4, w
- 5, 8);
1072 dc
.DrawLine(w
- 5, 8, w
- sw
- 2, 8);
1074 dc
.DrawLine(w
- 4, 3, w
- 4, 9);
1075 dc
.DrawLine(w
- 4, 9, w
- sw
- 3, 9);
1077 dc
.SetPen(highlight
);
1078 dc
.DrawLine(4, h
- 5, 4, h
- sh
- 2);
1079 dc
.DrawLine(4, h
- sh
- 2, 8, h
- sh
- 2);
1081 dc
.DrawLine(8, h
- sh
- 2, 8, h
- 5);
1082 dc
.DrawLine(8, h
- 5, 4, h
- 5);
1084 dc
.DrawLine(9, h
- sh
- 3, 9, h
- 4);
1085 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 cursor
= wxCursor(wxCURSOR_SIZENWSE
);
1142 } else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1143 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
) {
1144 if (m_impl
->FindParent(region
)) {
1145 if (region
== DSR_LEFT_EDGE
|| region
== DSR_RIGHT_EDGE
) {
1146 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1148 cursor
= wxCursor(wxCURSOR_SIZENS
);
1153 m_impl
->m_container
->SetCursor(cursor
);
1156 void wxDynamicSashWindowLeaf::OnLeave(wxMouseEvent
&event
) {
1157 wxCursor
cursor(wxCURSOR_ARROW
);
1158 m_impl
->m_container
->SetCursor(cursor
);
1162 void wxDynamicSashWindowLeaf::OnPress(wxMouseEvent
&event
) {
1163 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1165 if (region
== DSR_HORIZONTAL_TAB
|| region
== DSR_VERTICAL_TAB
|| region
== DSR_CORNER
) {
1166 m_impl
->m_dragging
= region
;
1167 m_impl
->m_drag_x
= event
.m_x
;
1168 m_impl
->m_drag_y
= event
.m_y
;
1169 m_impl
->DrawSash(event
.m_x
, event
.m_y
);
1170 m_impl
->m_container
->CaptureMouse();
1171 } else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1172 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
) {
1173 wxDynamicSashWindowImpl
*parent
= m_impl
->FindParent(region
);
1179 m_impl
->m_container
->ClientToScreen(&x
, &y
);
1180 parent
->m_container
->ScreenToClient(&x
, &y
);
1182 parent
->m_dragging
= parent
->m_split
;
1183 parent
->m_drag_x
= x
;
1184 parent
->m_drag_y
= y
;
1185 parent
->DrawSash(x
, y
);
1186 parent
->m_container
->CaptureMouse();
1191 void wxDynamicSashWindowLeaf::OnRelease(wxMouseEvent
&event
) {
1194 void wxDynamicSashWindowLeaf::OnReparent(wxEvent
&event
) {
1196 m_child
->Reparent(m_viewport
);
1199 ResizeChild(m_viewport
->GetSize());
1202 // wxDynamicSashSplitEvent //////////////////////////////////////////////////
1204 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent() {
1205 m_eventObject
= NULL
;
1206 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1209 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent(wxObject
*object
) {
1210 m_eventObject
= object
;
1211 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1214 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashSplitEvent
, wxCommandEvent
)
1216 // wxDynamicSashUnifyEvent //////////////////////////////////////////////////
1218 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent() {
1219 m_eventObject
= NULL
;
1220 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1223 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent(wxObject
*object
) {
1224 m_eventObject
= object
;
1225 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1228 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashUnifyEvent
, wxCommandEvent
)
1230 // wxDynamicSsahReparentEvent ///////////////////////////////////////////////
1232 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent() {
1233 m_eventObject
= NULL
;
1234 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1237 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(wxObject
*object
) {
1238 m_eventObject
= object
;
1239 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1242 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashReparentEvent
, wxEvent
)
1244 /////////////////////////////////////////////////////////////////////////////