1 /////////////////////////////////////////////////////////////////////////////
2 // Name: dynamicsash.cpp
3 // Purpose: A window which can be dynamically split to an arbitrary depth
4 // and later reunified through the user interface
5 // Author: Matt Kimball
9 // Copyright: (c) 2001 Matt Kimball
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
14 #pragma implementation "splittree.h"
17 // For compilers that support precompilation, includes "wx/wx.h".
18 #include "wx/wxprec.h"
24 // for all others, include the necessary headers (this file is usually all you
25 // need because it includes almost all "standard" wxWindows headers)
30 #include "wx/gizmos/dynamicsash.h"
33 wxDynamicSashWindow works by internally storing a tree of Implementation
34 objects (wxDynamicSsahWindowImpl) and Leaf objects
35 (wxDynamicSashWindowLeaf). The wxDynamicSashWindow has a pointer to one
36 implementation, and each implementation either has a pointer to a one
37 leaf (m_leaf) or a pointer to two children implementation objects
38 (m_child). The leaves each are responsible for drawing the frame and
39 decorations around one user-provided views and for responding to mouse
42 A resulting tree might look something like this:
46 +- wxDynamicSashWindowImpl
48 +- wxDynamicSashWindowLeaf
52 +- wxDynamicSashWindowImpl
54 +- wxDynamicSashWindowLeaf
58 +- wxDynamicSashWindowLeaf
62 Each time a split occurs, one of the implementation objects removes its
63 leaf, generates two new implementation object children, each with a new
64 leaf, and reparents the user view which was connected to its old leaf
65 to be one of the new leaf's user view, and sends a Split event to the
66 user view in the hopes that it will generate a new user view for the
69 When a unification ocurrs, an implementation object is replaced by one
70 of its children, and the tree of its other child is pruned.
72 One quirk is that the top-level implementation object (m_top) always
73 keeps a pointer to the implementation object where a new child is needed.
74 (m_add_child_target). This is so that when a new uesr view is added
75 to the hierarchy, AddChild() is able to reparent the new user view to
76 the correct implementation object's leaf.
80 #include <wx/dcmemory.h>
81 #include <wx/dcscreen.h>
82 #include <wx/layout.h>
83 #include <wx/scrolbar.h>
84 #include <wx/settings.h>
87 #define wxEVT_DYNAMIC_SASH_PRIVATE (wxEVT_DYNAMIC_SASH_BASE + 8)
88 #define wxEVT_DYNAMIC_SASH_REPARENT (wxEVT_DYNAMIC_SASH_PRIVATE + 1)
92 wxDynamicSashReparentEvent is generated by the AddChild() method of
93 wxDynamicSashWindow when it wants a Leaf to reparent a user view window
94 to its viewport at some time in the future. We can't reparent the window
95 immediately, because switching parents in AddChild() confuses the wxWindow
96 class. Instead, we queue up this event, and the window is actually
97 reparented the next time we process events in the idle loop.
99 class wxDynamicSashReparentEvent
: public wxEvent
{
101 wxDynamicSashReparentEvent();
102 wxDynamicSashReparentEvent(wxObject
*object
);
103 wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
);
105 virtual wxEvent
* Clone() const { return new wxDynamicSashReparentEvent(*this); }
107 DECLARE_DYNAMIC_CLASS(wxDynamicSashReparentEvent
);
111 enum DynamicSashRegion
{
123 class wxDynamicSashWindowImpl
: public wxEvtHandler
{
125 wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
);
126 ~wxDynamicSashWindowImpl();
129 void AddChild(wxWindow
*window
);
130 void DrawSash(int x
, int y
) const;
131 void ConstrainChildren(int px
, int py
);
132 void Split(int x
, int y
);
133 void Unify(int panel
);
134 void Resize(int x
, int y
);
135 wxDynamicSashWindowImpl
*FindParent(DynamicSashRegion side
) const;
136 wxDynamicSashWindowImpl
*FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
137 wxDynamicSashWindowImpl
*sash_b
) const;
138 wxWindow
*FindFrame() const;
139 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
141 void OnSize(wxSizeEvent
&event
);
142 void OnPaint(wxPaintEvent
&event
);
143 void OnMouseMove(wxMouseEvent
&event
);
144 void OnLeave(wxMouseEvent
&event
);
145 void OnPress(wxMouseEvent
&event
);
146 void OnRelease(wxMouseEvent
&event
);
148 wxDynamicSashWindow
*m_window
;
149 wxDynamicSashWindowImpl
*m_add_child_target
;
151 /* This is the window we are responsible for managing. Either of
152 leaf or our children are in this window. For the top level
153 implementation object, this is the same as m_window.
154 Otherwise it is a window we've created an will destroy when we
156 wxWindow
*m_container
;
158 wxDynamicSashWindowImpl
*m_parent
;
159 wxDynamicSashWindowImpl
*m_top
;
160 wxDynamicSashWindowImpl
*m_child
[2];
162 class wxDynamicSashWindowLeaf
*m_leaf
;
164 /* If the implementation is split horizontally or vertically, m_split
165 is set to DSR_HORIZONTAL_TAB or DSR_VERTICAL_TAB, respectively.
166 Otherwise it is set to DSR_NONE. */
167 DynamicSashRegion m_split
;
169 /* These are used to keep track of a sash as it is being dragged, for
170 drawing the user interface correctly. */
171 DynamicSashRegion m_dragging
;
172 int m_drag_x
, m_drag_y
;
175 class wxDynamicSashWindowLeaf
: public wxEvtHandler
{
177 wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
);
178 ~wxDynamicSashWindowLeaf();
181 void AddChild(wxWindow
*window
);
182 DynamicSashRegion
GetRegion(int x
, int y
);
183 void ResizeChild(wxSize size
);
184 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
186 void OnSize(wxSizeEvent
&event
);
187 void OnPaint(wxPaintEvent
&event
);
188 void OnScroll(wxScrollEvent
&event
);
189 void OnFocus(wxFocusEvent
&event
);
190 void OnMouseMove(wxMouseEvent
&event
);
191 void OnLeave(wxMouseEvent
&event
);
192 void OnPress(wxMouseEvent
&event
);
193 void OnRelease(wxMouseEvent
&event
);
194 void OnReparent(wxEvent
&event
);
196 wxDynamicSashWindowImpl
*m_impl
;
198 wxScrollBar
*m_vscroll
, *m_hscroll
;
200 /* m_child is the window provided to us by the application developer.
201 m_viewport is a window we've created, and it is the immediately
202 parent of m_child. We scroll m_child by moving it around within
204 wxWindow
*m_viewport
, *m_child
;
207 // wxDynamicSashWindow //////////////////////////////////////////////////////
209 wxDynamicSashWindow::wxDynamicSashWindow() {
213 wxDynamicSashWindow::wxDynamicSashWindow(wxWindow
*parent
, wxWindowID id
,
214 const wxPoint
& pos
, const wxSize
& size
,
215 long style
, const wxString
& name
) {
217 Create(parent
, id
, pos
, size
, style
, name
);
220 wxDynamicSashWindow::~wxDynamicSashWindow() {
221 SetEventHandler(this);
225 bool wxDynamicSashWindow::Create(wxWindow
*parent
, wxWindowID id
,
226 const wxPoint
& pos
, const wxSize
& size
,
227 long style
, const wxString
& name
) {
231 if (!wxWindow::Create(parent
, id
, pos
, size
, style
, name
))
234 m_impl
= new wxDynamicSashWindowImpl(this);
238 if (!m_impl
->Create()) {
247 void wxDynamicSashWindow::AddChild(wxWindowBase
*child
) {
248 wxWindow::AddChild(child
);
250 m_impl
->AddChild(wxDynamicCast(child
, wxWindow
));
253 wxScrollBar
*wxDynamicSashWindow::GetHScrollBar(const wxWindow
*child
) const {
254 return m_impl
->FindScrollBar(child
, 0);
257 wxScrollBar
*wxDynamicSashWindow::GetVScrollBar(const wxWindow
*child
) const {
258 return m_impl
->FindScrollBar(child
, 1);
261 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashWindow
, wxWindow
)
263 // wxDynamicSashWindowImpl //////////////////////////////////////////////////
265 wxDynamicSashWindowImpl::wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
) {
267 m_add_child_target
= this;
272 m_child
[0] = m_child
[1] = NULL
;
274 m_dragging
= DSR_NONE
;
278 wxDynamicSashWindowImpl::~wxDynamicSashWindowImpl() {
286 if (m_container
!= m_window
&& m_container
) {
287 m_container
->SetEventHandler(m_container
);
288 m_container
->Destroy();
292 bool wxDynamicSashWindowImpl::Create() {
294 m_container
= m_window
;
297 wxCursor
cursor(wxCURSOR_ARROW
);
298 m_container
->SetCursor(cursor
);
300 m_leaf
= new wxDynamicSashWindowLeaf(this);
304 if (!m_leaf
->Create()) {
310 m_container
->SetEventHandler(this);
312 Connect(-1, wxEVT_SIZE
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnSize
);
313 Connect(-1, wxEVT_PAINT
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnPaint
);
314 Connect(-1, wxEVT_MOTION
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnMouseMove
);
315 Connect(-1, wxEVT_ENTER_WINDOW
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnMouseMove
);
316 Connect(-1, wxEVT_LEAVE_WINDOW
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnLeave
);
317 Connect(-1, wxEVT_LEFT_DOWN
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnPress
);
318 Connect(-1, wxEVT_LEFT_UP
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnRelease
);
323 void wxDynamicSashWindowImpl::AddChild(wxWindow
*window
) {
324 if (m_add_child_target
&& m_add_child_target
->m_leaf
) {
325 m_add_child_target
->m_leaf
->AddChild(window
);
329 void wxDynamicSashWindowImpl::DrawSash(int x
, int y
) const {
333 dc
.StartDrawingOnTop(m_container
);
337 bdc
.SelectObject(bmp
);
338 bdc
.DrawRectangle(-1, -1, 10, 10);
339 for (i
= 0; i
< 8; i
++) {
340 for (j
= 0; j
< 8; j
++) {
349 dc
.SetLogicalFunction(wxXOR
);
351 if ((m_dragging
== DSR_CORNER
) &&
352 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0) {
356 m_container
->ClientToScreen(&cx
, &cy
);
357 m_container
->ClientToScreen(&x
, &y
);
359 if (cx
< x
&& cy
< y
) {
360 dc
.DrawRectangle(cx
- 2, cy
- 2, x
- cx
+ 4, 4);
361 dc
.DrawRectangle(x
- 2, cy
+ 2, 4, y
- cy
);
362 dc
.DrawRectangle(cx
- 2, cy
+ 2, 4, y
- cy
);
363 dc
.DrawRectangle(cx
+ 2, y
- 2, x
- cx
- 4, 4);
367 m_container
->GetClientSize(&body_w
, &body_h
);
378 if (m_dragging
== DSR_HORIZONTAL_TAB
)
383 m_container
->ClientToScreen(&x
, &y
);
386 w
= body_w
; h
= body_h
;
388 if (m_dragging
== DSR_HORIZONTAL_TAB
)
389 dc
.DrawRectangle(x
, y
- 2, w
, 4);
391 dc
.DrawRectangle(x
- 2, y
, 4, h
);
394 dc
.EndDrawingOnTop();
397 wxDynamicSashWindowImpl
*wxDynamicSashWindowImpl::FindParent(DynamicSashRegion side
) const {
398 if (m_parent
== NULL
) {
402 if (m_parent
->m_split
== DSR_HORIZONTAL_TAB
) {
403 if (side
== DSR_TOP_EDGE
&& m_parent
->m_child
[1] == this)
405 if (side
== DSR_BOTTOM_EDGE
&& m_parent
->m_child
[0] == this)
407 } else if (m_parent
->m_split
== DSR_VERTICAL_TAB
) {
408 if (side
== DSR_LEFT_EDGE
&& m_parent
->m_child
[1] == this)
410 if (side
== DSR_RIGHT_EDGE
&& m_parent
->m_child
[0] == this)
414 return m_parent
->FindParent(side
);
417 wxDynamicSashWindowImpl
*wxDynamicSashWindowImpl::FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
418 wxDynamicSashWindowImpl
*sash_b
) const {
420 win
= sash_a
->m_container
->GetParent();
421 while (win
&& !win
->IsTopLevel()) {
422 if (win
== sash_b
->m_container
) {
426 win
= win
->GetParent();
433 wxWindow
*wxDynamicSashWindowImpl::FindFrame() const {
436 win
= m_window
->GetParent();
437 while (win
&& !win
->IsTopLevel()) {
438 win
= win
->GetParent();
444 wxScrollBar
*wxDynamicSashWindowImpl::FindScrollBar(const wxWindow
*child
, int vert
) const {
445 if (m_child
[0] == NULL
&& m_leaf
== NULL
) {
450 return m_leaf
->FindScrollBar(child
, vert
);
452 wxScrollBar
*ret
= m_child
[0]->FindScrollBar(child
, vert
);
454 ret
= m_child
[1]->FindScrollBar(child
, vert
);
461 void wxDynamicSashWindowImpl::ConstrainChildren(int px
, int py
) {
462 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
463 layout
->left
.SameAs(m_container
, wxLeft
);
464 layout
->top
.SameAs(m_container
, wxTop
);
465 if (m_split
== DSR_HORIZONTAL_TAB
) {
466 layout
->right
.SameAs(m_container
, wxRight
);
467 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
469 layout
->bottom
.SameAs(m_container
, wxBottom
);
470 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
472 m_child
[0]->m_container
->SetConstraints(layout
);
474 layout
= new wxLayoutConstraints();
475 layout
->right
.SameAs(m_container
, wxRight
);
476 layout
->bottom
.SameAs(m_container
, wxBottom
);
477 if (m_split
== DSR_HORIZONTAL_TAB
) {
478 layout
->top
.Below(m_child
[0]->m_container
, 1);
479 layout
->left
.SameAs(m_container
, wxLeft
);
481 layout
->left
.RightOf(m_child
[0]->m_container
, 1);
482 layout
->top
.SameAs(m_container
, wxTop
);
484 m_child
[1]->m_container
->SetConstraints(layout
);
487 void wxDynamicSashWindowImpl::Unify(int panel
) {
493 if (m_child
[panel
]->m_leaf
) {
494 wxDynamicSashWindowImpl
*child
[2];
496 child
[0] = m_child
[0];
497 child
[1] = m_child
[1];
499 m_child
[0] = m_child
[1] = NULL
;
501 m_leaf
= new wxDynamicSashWindowLeaf(this);
503 m_leaf
->m_child
= child
[panel
]->m_leaf
->m_child
;
505 m_leaf
->m_vscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_vscroll
->GetThumbPosition(),
506 child
[panel
]->m_leaf
->m_vscroll
->GetThumbSize(),
507 child
[panel
]->m_leaf
->m_vscroll
->GetRange(),
508 child
[panel
]->m_leaf
->m_vscroll
->GetPageSize());
509 m_leaf
->m_hscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_hscroll
->GetThumbPosition(),
510 child
[panel
]->m_leaf
->m_hscroll
->GetThumbSize(),
511 child
[panel
]->m_leaf
->m_hscroll
->GetRange(),
512 child
[panel
]->m_leaf
->m_hscroll
->GetPageSize());
513 m_add_child_target
= NULL
;
514 wxDynamicSashReparentEvent
event(m_leaf
);
515 m_leaf
->ProcessEvent(event
);
522 wxDynamicSashUnifyEvent
unify(m_leaf
->m_child
);
523 m_leaf
->m_child
->ProcessEvent(unify
);
525 m_split
= m_child
[panel
]->m_split
;
527 delete m_child
[other
];
529 wxDynamicSashWindowImpl
*child_panel
= m_child
[panel
];
530 m_child
[0] = child_panel
->m_child
[0];
531 m_child
[1] = child_panel
->m_child
[1];
533 m_child
[0]->m_parent
= this;
534 m_child
[1]->m_parent
= this;
536 m_add_child_target
= NULL
;
537 m_child
[0]->m_container
->Reparent(m_container
);
538 m_child
[1]->m_container
->Reparent(m_container
);
540 child_panel
->m_child
[0] = child_panel
->m_child
[1] = NULL
;
543 wxSize size
= m_container
->GetSize();
544 wxSize child_size
= m_child
[0]->m_container
->GetSize();
546 ConstrainChildren(child_size
.GetWidth() * 100 / size
.GetWidth(),
547 child_size
.GetHeight() * 100 / size
.GetHeight());
549 m_container
->Layout();
553 void wxDynamicSashWindowImpl::Split(int px
, int py
) {
555 m_add_child_target
= NULL
;
557 m_child
[0] = new wxDynamicSashWindowImpl(m_window
);
558 m_child
[0]->m_container
= new wxWindow(m_container
, -1);
559 m_child
[0]->m_parent
= this;
560 m_child
[0]->m_top
= m_top
;
561 m_child
[0]->Create();
562 if (m_leaf
->m_child
) {
563 m_leaf
->m_child
->Reparent(m_container
);
564 m_child
[0]->AddChild(m_leaf
->m_child
);
567 m_child
[1] = new wxDynamicSashWindowImpl(m_window
);
568 m_child
[1]->m_container
= new wxWindow(m_container
, -1);
569 m_child
[1]->m_parent
= this;
570 m_child
[1]->m_top
= m_top
;
571 m_child
[1]->Create();
573 m_split
= m_dragging
;
574 ConstrainChildren(px
, py
);
576 m_top
->m_add_child_target
= m_child
[1];
577 wxDynamicSashSplitEvent
split(m_child
[0]->m_leaf
->m_child
);
578 m_child
[0]->m_leaf
->m_child
->ProcessEvent(split
);
580 m_child
[0]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
581 m_leaf
->m_vscroll
->GetThumbSize(),
582 m_leaf
->m_vscroll
->GetRange(),
583 m_leaf
->m_vscroll
->GetPageSize());
584 m_child
[0]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
585 m_leaf
->m_hscroll
->GetThumbSize(),
586 m_leaf
->m_hscroll
->GetRange(),
587 m_leaf
->m_hscroll
->GetPageSize());
588 m_child
[1]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
589 m_leaf
->m_vscroll
->GetThumbSize(),
590 m_leaf
->m_vscroll
->GetRange(),
591 m_leaf
->m_vscroll
->GetPageSize());
592 m_child
[1]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
593 m_leaf
->m_hscroll
->GetThumbSize(),
594 m_leaf
->m_hscroll
->GetRange(),
595 m_leaf
->m_hscroll
->GetPageSize());
599 m_container
->Layout();
603 /* This code is called when you finish resizing a view by dragging the
604 corner tab, but I think this implementation is lousy and will surprise
605 the user more often than it will do what they are trying to do. What
606 I really need to be doing here is do a rewrite such that *no* sashes
607 move except the ones immediately to the bottom and right of this window,
608 and handle the case where you resize a window larger than it's neighbors
609 by destroying the neighbors.
611 But this will do for now. */
612 void wxDynamicSashWindowImpl::Resize(int x
, int y
) {
613 wxDynamicSashWindowImpl
*h_parent
= FindParent(DSR_BOTTOM_EDGE
);
614 wxDynamicSashWindowImpl
*v_parent
= FindParent(DSR_RIGHT_EDGE
);
617 wxWindow
*frame
= FindFrame();
627 m_container
->ClientToScreen(NULL
, &y
);
628 h_parent
->m_container
->ScreenToClient(NULL
, &y
);
630 int py
= (int)((y
* 100) / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
633 wxDynamicSashWindowImpl
*ho_parent
= FindParent(DSR_TOP_EDGE
);
636 if (FindUpperParent(h_parent
, ho_parent
) == ho_parent
) {
639 py
= (int)((ho_parent
->m_child
[0]->m_container
->GetSize().GetHeight() * 100)
640 / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
641 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
642 h_parent
->m_container
, wxHeight
, py
);
644 h_parent
= ho_parent
;
650 } else if (py
> 90) {
653 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
654 h_parent
->m_container
, wxHeight
, py
);
655 h_parent
->m_container
->Layout();
659 h_parent
= FindParent(DSR_TOP_EDGE
);
662 int py
= (int)((y
* 100) /
663 (h_parent
->m_container
->GetSize().GetHeight() +
664 y
- m_container
->GetSize().GetHeight()) + 0.5);
674 wxSize size
= frame
->GetSize();
675 frame
->SetSize(size
.GetWidth(), size
.GetHeight() + y
- m_container
->GetSize().GetHeight());
680 m_container
->ClientToScreen(&x
, NULL
);
681 v_parent
->m_container
->ScreenToClient(&x
, NULL
);
683 int px
= (int)((x
* 100) / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
686 wxDynamicSashWindowImpl
*vo_parent
= FindParent(DSR_LEFT_EDGE
);
689 if (FindUpperParent(v_parent
, vo_parent
) == vo_parent
) {
692 px
= (int)((vo_parent
->m_child
[0]->m_container
->GetSize().GetWidth() * 100)
693 / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
694 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
695 v_parent
->m_container
, wxWidth
, px
);
697 v_parent
= vo_parent
;
703 } else if (px
> 90) {
706 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
707 v_parent
->m_container
, wxWidth
, px
);
708 v_parent
->m_container
->Layout();
712 v_parent
= FindParent(DSR_LEFT_EDGE
);
715 int px
= (int)((x
* 100) /
716 (v_parent
->m_container
->GetSize().GetWidth() +
717 x
- m_container
->GetSize().GetWidth()) + 0.5);
727 wxSize size
= frame
->GetSize();
728 frame
->SetSize(size
.GetWidth() + x
- m_container
->GetSize().GetWidth(), size
.GetHeight());
732 if (h_unify
!= -1 && v_unify
!= -1) {
733 wxDynamicSashWindowImpl
*parent
= FindUpperParent(h_parent
, v_parent
);
735 if (parent
== h_parent
) {
736 h_parent
->Unify(h_unify
);
738 v_parent
->Unify(v_unify
);
740 } else if (h_unify
!= -1) {
741 h_parent
->Unify(h_unify
);
742 } else if (v_unify
!= -1) {
743 v_parent
->Unify(v_unify
);
748 void wxDynamicSashWindowImpl::OnSize(wxSizeEvent
&event
) {
749 m_container
->Layout();
752 m_leaf
->OnSize(event
);
755 void wxDynamicSashWindowImpl::OnPaint(wxPaintEvent
&event
) {
757 m_leaf
->OnPaint(event
);
759 wxPaintDC
dc(m_container
);
760 dc
.SetBackground(wxBrush(m_container
->GetBackgroundColour(), wxSOLID
));
765 void wxDynamicSashWindowImpl::OnMouseMove(wxMouseEvent
&event
) {
767 DrawSash(m_drag_x
, m_drag_y
);
768 m_drag_x
= event
.m_x
; m_drag_y
= event
.m_y
;
769 DrawSash(m_drag_x
, m_drag_y
);
771 m_leaf
->OnMouseMove(event
);
775 void wxDynamicSashWindowImpl::OnLeave(wxMouseEvent
&event
) {
777 m_leaf
->OnLeave(event
);
781 void wxDynamicSashWindowImpl::OnPress(wxMouseEvent
&event
) {
783 m_leaf
->OnPress(event
);
785 m_dragging
= m_split
;
786 m_drag_x
= event
.m_x
;
787 m_drag_y
= event
.m_y
;
788 DrawSash(m_drag_x
, m_drag_y
);
789 m_container
->CaptureMouse();
793 void wxDynamicSashWindowImpl::OnRelease(wxMouseEvent
&event
) {
794 if ((m_dragging
== DSR_CORNER
) &&
795 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0) {
796 DrawSash(m_drag_x
, m_drag_y
);
797 m_container
->ReleaseMouse();
799 Resize(event
.m_x
, event
.m_y
);
801 m_dragging
= DSR_NONE
;
802 } else if (m_dragging
) {
803 DrawSash(m_drag_x
, m_drag_y
);
804 m_container
->ReleaseMouse();
806 wxSize size
= m_container
->GetSize();
807 int px
= (int)((event
.m_x
* 100) / size
.GetWidth() + 0.5);
808 int py
= (int)((event
.m_y
* 100) / size
.GetHeight() + 0.5);
810 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
>= 10 && py
<= 90)
811 || (m_dragging
== DSR_VERTICAL_TAB
&& px
>= 10 && px
<= 90)) {
812 if (m_child
[0] == NULL
) {
815 /* It would be nice if moving *this* sash didn't implicitly move
816 the sashes of our children (if any). But this will do. */
817 wxLayoutConstraints
*layout
= m_child
[0]->m_container
->GetConstraints();
818 if (m_split
== DSR_HORIZONTAL_TAB
) {
819 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
821 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
823 m_container
->Layout();
826 if (m_child
[0] != NULL
) {
827 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
<= 10)
828 || (m_dragging
== DSR_VERTICAL_TAB
&& px
<= 10)) {
836 wxCursor
cursor(wxCURSOR_ARROW
);
837 if (m_split
== DSR_HORIZONTAL_TAB
) {
838 cursor
= wxCursor(wxCURSOR_SIZENS
);
839 } else if (m_split
== DSR_VERTICAL_TAB
) {
840 cursor
= wxCursor(wxCURSOR_SIZEWE
);
842 m_container
->SetCursor(cursor
);
844 m_dragging
= DSR_NONE
;
846 m_leaf
->OnRelease(event
);
850 // wxDynamicSashWindowLeaf //////////////////////////////////////////////////
852 wxDynamicSashWindowLeaf::wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
) {
855 m_hscroll
= m_vscroll
= NULL
;
859 wxDynamicSashWindowLeaf::~wxDynamicSashWindowLeaf() {
860 m_hscroll
->SetEventHandler(m_hscroll
);
861 m_vscroll
->SetEventHandler(m_vscroll
);
862 m_viewport
->SetEventHandler(m_viewport
);
864 m_hscroll
->Destroy();
865 m_vscroll
->Destroy();
866 m_viewport
->Destroy();
869 bool wxDynamicSashWindowLeaf::Create() {
872 m_hscroll
= new wxScrollBar();
873 m_vscroll
= new wxScrollBar();
874 m_viewport
= new wxWindow();
876 if (!m_hscroll
|| !m_vscroll
|| !m_viewport
) {
880 wxDynamicSashWindowImpl
*add_child_target
= m_impl
->m_add_child_target
;
881 m_impl
->m_add_child_target
= NULL
;
882 success
= m_hscroll
->Create(m_impl
->m_container
, -1, wxDefaultPosition
, wxDefaultSize
,
884 success
= success
&& m_vscroll
->Create(m_impl
->m_container
, -1, wxDefaultPosition
, wxDefaultSize
,
886 success
= success
&& m_viewport
->Create(m_impl
->m_container
, -1);
887 m_impl
->m_add_child_target
= add_child_target
;
889 wxCursor
cursor(wxCURSOR_ARROW
);
890 m_hscroll
->SetCursor(cursor
);
891 m_vscroll
->SetCursor(cursor
);
892 m_viewport
->SetCursor(cursor
);
894 m_viewport
->SetEventHandler(this);
895 Connect(-1, wxEVT_DYNAMIC_SASH_REPARENT
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnReparent
);
897 if (m_impl
->m_window
->GetWindowStyle() & wxDS_MANAGE_SCROLLBARS
) {
898 m_hscroll
->SetEventHandler(this);
899 m_vscroll
->SetEventHandler(this);
901 Connect(-1, wxEVT_SET_FOCUS
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnFocus
);
902 Connect(-1, wxEVT_SCROLL_TOP
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
903 Connect(-1, wxEVT_SCROLL_BOTTOM
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
904 Connect(-1, wxEVT_SCROLL_LINEUP
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
905 Connect(-1, wxEVT_SCROLL_LINEDOWN
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
906 Connect(-1, wxEVT_SCROLL_PAGEUP
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
907 Connect(-1, wxEVT_SCROLL_PAGEDOWN
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
908 Connect(-1, wxEVT_SCROLL_THUMBTRACK
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
909 Connect(-1, wxEVT_SCROLL_THUMBRELEASE
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
912 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
915 wxSize size
= m_hscroll
->GetBestSize();
917 size
= m_hscroll
->GetSize();
920 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 10);
921 layout
->right
.LeftOf(m_vscroll
);
922 layout
->bottom
.SameAs(m_impl
->m_container
, wxBottom
, 3);
923 layout
->height
.Absolute(size
.GetHeight());
924 m_hscroll
->SetConstraints(layout
);
926 layout
= new wxLayoutConstraints();
929 size
= size
= m_vscroll
->GetBestSize();
931 size
= m_vscroll
->GetSize();
934 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 10);
935 layout
->bottom
.Above(m_hscroll
);
936 layout
->right
.SameAs(m_impl
->m_container
, wxRight
, 3);
937 layout
->width
.Absolute(size
.GetWidth());
938 m_vscroll
->SetConstraints(layout
);
940 layout
= new wxLayoutConstraints();
943 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 3);
944 layout
->right
.LeftOf(m_vscroll
);
945 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 3);
946 layout
->bottom
.Above(m_hscroll
);
947 m_viewport
->SetConstraints(layout
);
949 m_impl
->m_container
->Layout();
954 void wxDynamicSashWindowLeaf::AddChild(wxWindow
*window
) {
961 wxDynamicSashReparentEvent
event(this);
962 AddPendingEvent(event
);
965 DynamicSashRegion
wxDynamicSashWindowLeaf::GetRegion(int x
, int y
) {
966 wxSize size
= m_impl
->m_container
->GetSize();
967 int w
= size
.GetWidth();
968 int h
= size
.GetHeight();
969 size
= m_hscroll
->GetSize();
970 int sh
= size
.GetHeight();
971 size
= m_vscroll
->GetSize();
972 int sw
= size
.GetWidth();
974 if (x
>= w
- sw
- 3 && x
< w
&& y
>= h
- sh
- 3 && y
< h
)
976 if (x
>= 3 && x
< 10 && y
>= h
- sh
- 3 && y
< h
- 2)
977 return DSR_VERTICAL_TAB
;
978 if (x
>= w
- sw
- 3 && x
< w
- 2 && y
>= 3 && y
< 10)
979 return DSR_HORIZONTAL_TAB
;
981 return DSR_LEFT_EDGE
;
985 return DSR_RIGHT_EDGE
;
987 return DSR_BOTTOM_EDGE
;
992 void wxDynamicSashWindowLeaf::ResizeChild(wxSize size
) {
994 if (m_impl
->m_window
->GetWindowStyle() & wxDS_MANAGE_SCROLLBARS
) {
995 m_child
->SetSize(size
);
996 wxSize best_size
= m_child
->GetBestSize();
997 if (best_size
.GetWidth() < size
.GetWidth()) {
998 best_size
.SetWidth(size
.GetWidth());
1000 if (best_size
.GetHeight() < size
.GetHeight()) {
1001 best_size
.SetHeight(size
.GetHeight());
1003 m_child
->SetSize(best_size
);
1005 int hpos
= m_hscroll
->GetThumbPosition();
1006 int vpos
= m_vscroll
->GetThumbPosition();
1014 if (hpos
> best_size
.GetWidth() - size
.GetWidth()) {
1015 hpos
= best_size
.GetWidth() - size
.GetWidth();
1017 if (vpos
> best_size
.GetHeight() - size
.GetHeight()) {
1018 vpos
= best_size
.GetHeight() - size
.GetHeight();
1021 m_hscroll
->SetScrollbar(hpos
, size
.GetWidth(),
1022 best_size
.GetWidth(), size
.GetWidth());
1023 m_vscroll
->SetScrollbar(vpos
, size
.GetHeight(),
1024 best_size
.GetHeight(), size
.GetHeight());
1026 // Umm, the scrollbars are doing something insane under GTK+ and subtracting
1027 // one from the position I pass in. This works around that.
1028 m_hscroll
->SetThumbPosition(hpos
+ hpos
- m_hscroll
->GetThumbPosition());
1029 m_vscroll
->SetThumbPosition(vpos
+ vpos
- m_vscroll
->GetThumbPosition());
1031 wxPoint pos
= m_child
->GetPosition();
1032 m_viewport
->ScrollWindow(-hpos
- pos
.x
, -vpos
- pos
.y
);
1034 m_child
->SetSize(size
);
1039 wxScrollBar
*wxDynamicSashWindowLeaf::FindScrollBar(const wxWindow
*child
, int vert
) const {
1040 if (m_child
== child
) {
1051 void wxDynamicSashWindowLeaf::OnSize(wxSizeEvent
&event
) {
1052 m_impl
->m_container
->Refresh();
1053 ResizeChild(m_viewport
->GetSize());
1056 void wxDynamicSashWindowLeaf::OnPaint(wxPaintEvent
&event
) {
1057 wxPaintDC
dc(m_impl
->m_container
);
1058 dc
.SetBackground(wxBrush(m_impl
->m_container
->GetBackgroundColour(), wxSOLID
));
1061 wxPen
highlight(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_BTNHIGHLIGHT
), 1, wxSOLID
);
1062 wxPen
shadow(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_BTNSHADOW
), 1, wxSOLID
);
1063 wxPen
black(*wxBLACK
, 1, wxSOLID
);
1065 wxSize size
= m_impl
->m_container
->GetSize();
1066 int w
= size
.GetWidth();
1067 int h
= size
.GetHeight();
1068 size
= m_hscroll
->GetSize();
1069 int sh
= size
.GetHeight();
1070 size
= m_vscroll
->GetSize();
1071 int sw
= size
.GetWidth();
1074 dc
.DrawLine(1, 1, 1, h
- 2);
1075 dc
.DrawLine(1, 1, w
- 2, 1);
1077 dc
.DrawLine(2, 2, 2, h
- 3);
1078 dc
.DrawLine(2, 2, w
- 3, 2);
1079 dc
.SetPen(highlight
);
1080 dc
.DrawLine(w
- 2, 2, w
- 2, h
- sh
- 2);
1081 dc
.DrawLine(w
- 2, h
- sh
- 2, w
- sw
- 2, h
- sh
- 2);
1082 dc
.DrawLine(w
- sw
- 2, h
- sh
- 2, w
- sw
- 2, h
- 2);
1083 dc
.DrawLine(w
- sw
- 2, h
- 2, 2, h
- 2);
1085 dc
.SetPen(highlight
);
1086 dc
.DrawLine(w
- sw
- 2, 8, w
- sw
- 2, 4);
1087 dc
.DrawLine(w
- sw
- 2, 4, w
- 5, 4);
1089 dc
.DrawLine(w
- 5, 4, w
- 5, 8);
1090 dc
.DrawLine(w
- 5, 8, w
- sw
- 2, 8);
1092 dc
.DrawLine(w
- 4, 3, w
- 4, 9);
1093 dc
.DrawLine(w
- 4, 9, w
- sw
- 3, 9);
1095 dc
.SetPen(highlight
);
1096 dc
.DrawLine(4, h
- 5, 4, h
- sh
- 2);
1097 dc
.DrawLine(4, h
- sh
- 2, 8, h
- sh
- 2);
1099 dc
.DrawLine(8, h
- sh
- 2, 8, h
- 5);
1100 dc
.DrawLine(8, h
- 5, 4, h
- 5);
1102 dc
.DrawLine(9, h
- sh
- 3, 9, h
- 4);
1103 dc
.DrawLine(9, h
- 4, 3, h
- 4);
1105 int cy
= (h
- sh
+ h
- 6) / 2 + 1;
1106 int cx
= (w
- sw
+ w
- 6) / 2 + 1;
1115 for (y
= sy
; y
< h
- 2; y
+= 4) {
1116 for (x
= sx
; x
< w
- 2; x
+= 4) {
1117 if (x
- cx
>= -(y
- cy
)) {
1118 dc
.SetPen(highlight
);
1121 dc
.DrawPoint(x
+ 1, y
+ 1);
1127 void wxDynamicSashWindowLeaf::OnScroll(wxScrollEvent
&event
) {
1128 int nx
= -m_hscroll
->GetThumbPosition();
1129 int ny
= -m_vscroll
->GetThumbPosition();
1132 wxPoint pos
= m_child
->GetPosition();
1134 m_viewport
->ScrollWindow(nx
- pos
.x
, ny
- pos
.y
);
1138 void wxDynamicSashWindowLeaf::OnFocus(wxFocusEvent
&event
) {
1139 if (event
.m_eventObject
== m_hscroll
|| event
.m_eventObject
== m_vscroll
) {
1140 m_child
->SetFocus();
1145 void wxDynamicSashWindowLeaf::OnMouseMove(wxMouseEvent
&event
) {
1146 if (m_impl
->m_dragging
) {
1150 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1152 wxCursor
cursor(wxCURSOR_ARROW
);
1153 if (region
== DSR_HORIZONTAL_TAB
) {
1154 cursor
= wxCursor(wxCURSOR_SIZENS
);
1155 } else if (region
== DSR_VERTICAL_TAB
) {
1156 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1157 } else if ((region
== DSR_CORNER
) &&
1158 (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0) {
1159 cursor
= wxCursor(wxCURSOR_SIZENWSE
);
1160 } else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1161 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
) {
1162 if (m_impl
->FindParent(region
)) {
1163 if (region
== DSR_LEFT_EDGE
|| region
== DSR_RIGHT_EDGE
) {
1164 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1166 cursor
= wxCursor(wxCURSOR_SIZENS
);
1171 m_impl
->m_container
->SetCursor(cursor
);
1174 void wxDynamicSashWindowLeaf::OnLeave(wxMouseEvent
&event
) {
1175 wxCursor
cursor(wxCURSOR_ARROW
);
1176 m_impl
->m_container
->SetCursor(cursor
);
1180 void wxDynamicSashWindowLeaf::OnPress(wxMouseEvent
&event
) {
1181 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1183 if ((region
== DSR_CORNER
) && (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) == 0)
1186 if (region
== DSR_HORIZONTAL_TAB
|| region
== DSR_VERTICAL_TAB
|| region
== DSR_CORNER
) {
1187 m_impl
->m_dragging
= region
;
1188 m_impl
->m_drag_x
= event
.m_x
;
1189 m_impl
->m_drag_y
= event
.m_y
;
1190 m_impl
->DrawSash(event
.m_x
, event
.m_y
);
1191 m_impl
->m_container
->CaptureMouse();
1192 } else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1193 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
) {
1194 wxDynamicSashWindowImpl
*parent
= m_impl
->FindParent(region
);
1200 m_impl
->m_container
->ClientToScreen(&x
, &y
);
1201 parent
->m_container
->ScreenToClient(&x
, &y
);
1203 parent
->m_dragging
= parent
->m_split
;
1204 parent
->m_drag_x
= x
;
1205 parent
->m_drag_y
= y
;
1206 parent
->DrawSash(x
, y
);
1207 parent
->m_container
->CaptureMouse();
1212 void wxDynamicSashWindowLeaf::OnRelease(wxMouseEvent
&event
) {
1215 void wxDynamicSashWindowLeaf::OnReparent(wxEvent
&event
) {
1217 m_child
->Reparent(m_viewport
);
1220 ResizeChild(m_viewport
->GetSize());
1223 // wxDynamicSashSplitEvent //////////////////////////////////////////////////
1225 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent() {
1226 m_eventObject
= NULL
;
1227 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1230 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent(wxObject
*object
) {
1231 m_eventObject
= object
;
1232 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1235 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashSplitEvent
, wxCommandEvent
)
1237 // wxDynamicSashUnifyEvent //////////////////////////////////////////////////
1239 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent() {
1240 m_eventObject
= NULL
;
1241 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1244 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent(wxObject
*object
) {
1245 m_eventObject
= object
;
1246 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1249 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashUnifyEvent
, wxCommandEvent
)
1251 // wxDynamicSsahReparentEvent ///////////////////////////////////////////////
1253 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent() {
1254 m_eventObject
= NULL
;
1255 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1258 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(wxObject
*object
) {
1259 m_eventObject
= object
;
1260 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1263 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
)
1268 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashReparentEvent
, wxEvent
)
1270 /////////////////////////////////////////////////////////////////////////////