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 const wxChar
* wxDynamicSashWindowNameStr
= wxT("dynamicSashWindow");
37 wxDynamicSashWindow works by internally storing a tree of Implementation
38 objects (wxDynamicSsahWindowImpl) and Leaf objects
39 (wxDynamicSashWindowLeaf). The wxDynamicSashWindow has a pointer to one
40 implementation, and each implementation either has a pointer to a one
41 leaf (m_leaf) or a pointer to two children implementation objects
42 (m_child). The leaves each are responsible for drawing the frame and
43 decorations around one user-provided views and for responding to mouse
46 A resulting tree might look something like this:
50 +- wxDynamicSashWindowImpl
52 +- wxDynamicSashWindowLeaf
56 +- wxDynamicSashWindowImpl
58 +- wxDynamicSashWindowLeaf
62 +- wxDynamicSashWindowLeaf
66 Each time a split occurs, one of the implementation objects removes its
67 leaf, generates two new implementation object children, each with a new
68 leaf, and reparents the user view which was connected to its old leaf
69 to be one of the new leaf's user view, and sends a Split event to the
70 user view in the hopes that it will generate a new user view for the
73 When a unification ocurrs, an implementation object is replaced by one
74 of its children, and the tree of its other child is pruned.
76 One quirk is that the top-level implementation object (m_top) always
77 keeps a pointer to the implementation object where a new child is needed.
78 (m_add_child_target). This is so that when a new uesr view is added
79 to the hierarchy, AddChild() is able to reparent the new user view to
80 the correct implementation object's leaf.
84 #include <wx/dcmemory.h>
85 #include <wx/dcscreen.h>
86 #include <wx/layout.h>
87 #include <wx/scrolbar.h>
88 #include <wx/settings.h>
91 #define wxEVT_DYNAMIC_SASH_PRIVATE (wxEVT_DYNAMIC_SASH_BASE + 8)
92 #define wxEVT_DYNAMIC_SASH_REPARENT (wxEVT_DYNAMIC_SASH_PRIVATE + 1)
96 wxDynamicSashReparentEvent is generated by the AddChild() method of
97 wxDynamicSashWindow when it wants a Leaf to reparent a user view window
98 to its viewport at some time in the future. We can't reparent the window
99 immediately, because switching parents in AddChild() confuses the wxWindow
100 class. Instead, we queue up this event, and the window is actually
101 reparented the next time we process events in the idle loop.
103 class wxDynamicSashReparentEvent
: public wxEvent
{
105 wxDynamicSashReparentEvent();
106 wxDynamicSashReparentEvent(wxObject
*object
);
107 wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
);
109 virtual wxEvent
* Clone() const { return new wxDynamicSashReparentEvent(*this); }
111 DECLARE_DYNAMIC_CLASS(wxDynamicSashReparentEvent
);
115 enum DynamicSashRegion
{
127 class wxDynamicSashWindowImpl
: public wxEvtHandler
{
129 wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
);
130 ~wxDynamicSashWindowImpl();
133 void AddChild(wxWindow
*window
);
134 void DrawSash(int x
, int y
) const;
135 void ConstrainChildren(int px
, int py
);
136 void Split(int x
, int y
);
137 void Unify(int panel
);
138 void Resize(int x
, int y
);
139 wxDynamicSashWindowImpl
*FindParent(DynamicSashRegion side
) const;
140 wxDynamicSashWindowImpl
*FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
141 wxDynamicSashWindowImpl
*sash_b
) const;
142 wxWindow
*FindFrame() const;
143 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
145 void OnSize(wxSizeEvent
&event
);
146 void OnPaint(wxPaintEvent
&event
);
147 void OnMouseMove(wxMouseEvent
&event
);
148 void OnLeave(wxMouseEvent
&event
);
149 void OnPress(wxMouseEvent
&event
);
150 void OnRelease(wxMouseEvent
&event
);
152 wxDynamicSashWindow
*m_window
;
153 wxDynamicSashWindowImpl
*m_add_child_target
;
155 /* This is the window we are responsible for managing. Either of
156 leaf or our children are in this window. For the top level
157 implementation object, this is the same as m_window.
158 Otherwise it is a window we've created an will destroy when we
160 wxWindow
*m_container
;
162 wxDynamicSashWindowImpl
*m_parent
;
163 wxDynamicSashWindowImpl
*m_top
;
164 wxDynamicSashWindowImpl
*m_child
[2];
166 class wxDynamicSashWindowLeaf
*m_leaf
;
168 /* If the implementation is split horizontally or vertically, m_split
169 is set to DSR_HORIZONTAL_TAB or DSR_VERTICAL_TAB, respectively.
170 Otherwise it is set to DSR_NONE. */
171 DynamicSashRegion m_split
;
173 /* These are used to keep track of a sash as it is being dragged, for
174 drawing the user interface correctly. */
175 DynamicSashRegion m_dragging
;
176 int m_drag_x
, m_drag_y
;
179 class wxDynamicSashWindowLeaf
: public wxEvtHandler
{
181 wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
);
182 ~wxDynamicSashWindowLeaf();
185 void AddChild(wxWindow
*window
);
186 DynamicSashRegion
GetRegion(int x
, int y
);
187 void ResizeChild(wxSize size
);
188 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
190 void OnSize(wxSizeEvent
&event
);
191 void OnPaint(wxPaintEvent
&event
);
192 void OnScroll(wxScrollEvent
&event
);
193 void OnFocus(wxFocusEvent
&event
);
194 void OnMouseMove(wxMouseEvent
&event
);
195 void OnLeave(wxMouseEvent
&event
);
196 void OnPress(wxMouseEvent
&event
);
197 void OnRelease(wxMouseEvent
&event
);
198 void OnReparent(wxEvent
&event
);
200 wxDynamicSashWindowImpl
*m_impl
;
202 wxScrollBar
*m_vscroll
, *m_hscroll
;
204 /* m_child is the window provided to us by the application developer.
205 m_viewport is a window we've created, and it is the immediately
206 parent of m_child. We scroll m_child by moving it around within
208 wxWindow
*m_viewport
, *m_child
;
211 // wxDynamicSashWindow //////////////////////////////////////////////////////
213 wxDynamicSashWindow::wxDynamicSashWindow() {
217 wxDynamicSashWindow::wxDynamicSashWindow(wxWindow
*parent
, wxWindowID id
,
218 const wxPoint
& pos
, const wxSize
& size
,
219 long style
, const wxString
& name
) {
221 Create(parent
, id
, pos
, size
, style
, name
);
224 wxDynamicSashWindow::~wxDynamicSashWindow() {
225 SetEventHandler(this);
229 bool wxDynamicSashWindow::Create(wxWindow
*parent
, wxWindowID id
,
230 const wxPoint
& pos
, const wxSize
& size
,
231 long style
, const wxString
& name
) {
235 if (!wxWindow::Create(parent
, id
, pos
, size
, style
, name
))
238 m_impl
= new wxDynamicSashWindowImpl(this);
242 if (!m_impl
->Create()) {
251 void wxDynamicSashWindow::AddChild(wxWindowBase
*child
) {
252 wxWindow::AddChild(child
);
254 m_impl
->AddChild(wxDynamicCast(child
, wxWindow
));
257 wxScrollBar
*wxDynamicSashWindow::GetHScrollBar(const wxWindow
*child
) const {
258 return m_impl
->FindScrollBar(child
, 0);
261 wxScrollBar
*wxDynamicSashWindow::GetVScrollBar(const wxWindow
*child
) const {
262 return m_impl
->FindScrollBar(child
, 1);
265 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashWindow
, wxWindow
)
267 // wxDynamicSashWindowImpl //////////////////////////////////////////////////
269 wxDynamicSashWindowImpl::wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
) {
271 m_add_child_target
= this;
276 m_child
[0] = m_child
[1] = NULL
;
278 m_dragging
= DSR_NONE
;
282 wxDynamicSashWindowImpl::~wxDynamicSashWindowImpl() {
290 if (m_container
!= m_window
&& m_container
) {
291 m_container
->SetEventHandler(m_container
);
292 m_container
->Destroy();
296 bool wxDynamicSashWindowImpl::Create() {
298 m_container
= m_window
;
301 wxCursor
cursor(wxCURSOR_ARROW
);
302 m_container
->SetCursor(cursor
);
304 m_leaf
= new wxDynamicSashWindowLeaf(this);
308 if (!m_leaf
->Create()) {
314 m_container
->SetEventHandler(this);
316 Connect(-1, wxEVT_SIZE
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnSize
);
317 Connect(-1, wxEVT_PAINT
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnPaint
);
318 Connect(-1, wxEVT_MOTION
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnMouseMove
);
319 Connect(-1, wxEVT_ENTER_WINDOW
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnMouseMove
);
320 Connect(-1, wxEVT_LEAVE_WINDOW
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnLeave
);
321 Connect(-1, wxEVT_LEFT_DOWN
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnPress
);
322 Connect(-1, wxEVT_LEFT_UP
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnRelease
);
327 void wxDynamicSashWindowImpl::AddChild(wxWindow
*window
) {
328 if (m_add_child_target
&& m_add_child_target
->m_leaf
) {
329 m_add_child_target
->m_leaf
->AddChild(window
);
333 void wxDynamicSashWindowImpl::DrawSash(int x
, int y
) const {
337 dc
.StartDrawingOnTop(m_container
);
341 bdc
.SelectObject(bmp
);
342 bdc
.DrawRectangle(-1, -1, 10, 10);
343 for (i
= 0; i
< 8; i
++) {
344 for (j
= 0; j
< 8; j
++) {
353 dc
.SetLogicalFunction(wxXOR
);
355 if ((m_dragging
== DSR_CORNER
) &&
356 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0) {
360 m_container
->ClientToScreen(&cx
, &cy
);
361 m_container
->ClientToScreen(&x
, &y
);
363 if (cx
< x
&& cy
< y
) {
364 dc
.DrawRectangle(cx
- 2, cy
- 2, x
- cx
+ 4, 4);
365 dc
.DrawRectangle(x
- 2, cy
+ 2, 4, y
- cy
);
366 dc
.DrawRectangle(cx
- 2, cy
+ 2, 4, y
- cy
);
367 dc
.DrawRectangle(cx
+ 2, y
- 2, x
- cx
- 4, 4);
371 m_container
->GetClientSize(&body_w
, &body_h
);
382 if (m_dragging
== DSR_HORIZONTAL_TAB
)
387 m_container
->ClientToScreen(&x
, &y
);
390 w
= body_w
; h
= body_h
;
392 if (m_dragging
== DSR_HORIZONTAL_TAB
)
393 dc
.DrawRectangle(x
, y
- 2, w
, 4);
395 dc
.DrawRectangle(x
- 2, y
, 4, h
);
398 dc
.EndDrawingOnTop();
401 wxDynamicSashWindowImpl
*wxDynamicSashWindowImpl::FindParent(DynamicSashRegion side
) const {
402 if (m_parent
== NULL
) {
406 if (m_parent
->m_split
== DSR_HORIZONTAL_TAB
) {
407 if (side
== DSR_TOP_EDGE
&& m_parent
->m_child
[1] == this)
409 if (side
== DSR_BOTTOM_EDGE
&& m_parent
->m_child
[0] == this)
411 } else if (m_parent
->m_split
== DSR_VERTICAL_TAB
) {
412 if (side
== DSR_LEFT_EDGE
&& m_parent
->m_child
[1] == this)
414 if (side
== DSR_RIGHT_EDGE
&& m_parent
->m_child
[0] == this)
418 return m_parent
->FindParent(side
);
421 wxDynamicSashWindowImpl
*wxDynamicSashWindowImpl::FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
422 wxDynamicSashWindowImpl
*sash_b
) const {
424 win
= sash_a
->m_container
->GetParent();
425 while (win
&& !win
->IsTopLevel()) {
426 if (win
== sash_b
->m_container
) {
430 win
= win
->GetParent();
437 wxWindow
*wxDynamicSashWindowImpl::FindFrame() const {
440 win
= m_window
->GetParent();
441 while (win
&& !win
->IsTopLevel()) {
442 win
= win
->GetParent();
448 wxScrollBar
*wxDynamicSashWindowImpl::FindScrollBar(const wxWindow
*child
, int vert
) const {
449 if (m_child
[0] == NULL
&& m_leaf
== NULL
) {
454 return m_leaf
->FindScrollBar(child
, vert
);
456 wxScrollBar
*ret
= m_child
[0]->FindScrollBar(child
, vert
);
458 ret
= m_child
[1]->FindScrollBar(child
, vert
);
465 void wxDynamicSashWindowImpl::ConstrainChildren(int px
, int py
) {
466 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
467 layout
->left
.SameAs(m_container
, wxLeft
);
468 layout
->top
.SameAs(m_container
, wxTop
);
469 if (m_split
== DSR_HORIZONTAL_TAB
) {
470 layout
->right
.SameAs(m_container
, wxRight
);
471 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
473 layout
->bottom
.SameAs(m_container
, wxBottom
);
474 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
476 m_child
[0]->m_container
->SetConstraints(layout
);
478 layout
= new wxLayoutConstraints();
479 layout
->right
.SameAs(m_container
, wxRight
);
480 layout
->bottom
.SameAs(m_container
, wxBottom
);
481 if (m_split
== DSR_HORIZONTAL_TAB
) {
482 layout
->top
.Below(m_child
[0]->m_container
, 1);
483 layout
->left
.SameAs(m_container
, wxLeft
);
485 layout
->left
.RightOf(m_child
[0]->m_container
, 1);
486 layout
->top
.SameAs(m_container
, wxTop
);
488 m_child
[1]->m_container
->SetConstraints(layout
);
491 void wxDynamicSashWindowImpl::Unify(int panel
) {
497 if (m_child
[panel
]->m_leaf
) {
498 wxDynamicSashWindowImpl
*child
[2];
500 child
[0] = m_child
[0];
501 child
[1] = m_child
[1];
503 m_child
[0] = m_child
[1] = NULL
;
505 m_leaf
= new wxDynamicSashWindowLeaf(this);
507 m_leaf
->m_child
= child
[panel
]->m_leaf
->m_child
;
509 m_leaf
->m_vscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_vscroll
->GetThumbPosition(),
510 child
[panel
]->m_leaf
->m_vscroll
->GetThumbSize(),
511 child
[panel
]->m_leaf
->m_vscroll
->GetRange(),
512 child
[panel
]->m_leaf
->m_vscroll
->GetPageSize());
513 m_leaf
->m_hscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_hscroll
->GetThumbPosition(),
514 child
[panel
]->m_leaf
->m_hscroll
->GetThumbSize(),
515 child
[panel
]->m_leaf
->m_hscroll
->GetRange(),
516 child
[panel
]->m_leaf
->m_hscroll
->GetPageSize());
517 m_add_child_target
= NULL
;
518 wxDynamicSashReparentEvent
event(m_leaf
);
519 m_leaf
->ProcessEvent(event
);
526 wxDynamicSashUnifyEvent
unify(m_leaf
->m_child
);
527 m_leaf
->m_child
->ProcessEvent(unify
);
529 m_split
= m_child
[panel
]->m_split
;
531 delete m_child
[other
];
533 wxDynamicSashWindowImpl
*child_panel
= m_child
[panel
];
534 m_child
[0] = child_panel
->m_child
[0];
535 m_child
[1] = child_panel
->m_child
[1];
537 m_child
[0]->m_parent
= this;
538 m_child
[1]->m_parent
= this;
540 m_add_child_target
= NULL
;
541 m_child
[0]->m_container
->Reparent(m_container
);
542 m_child
[1]->m_container
->Reparent(m_container
);
544 child_panel
->m_child
[0] = child_panel
->m_child
[1] = NULL
;
547 wxSize size
= m_container
->GetSize();
548 wxSize child_size
= m_child
[0]->m_container
->GetSize();
550 ConstrainChildren(child_size
.GetWidth() * 100 / size
.GetWidth(),
551 child_size
.GetHeight() * 100 / size
.GetHeight());
553 m_container
->Layout();
557 void wxDynamicSashWindowImpl::Split(int px
, int py
) {
559 m_add_child_target
= NULL
;
561 m_child
[0] = new wxDynamicSashWindowImpl(m_window
);
562 m_child
[0]->m_container
= new wxWindow(m_container
, -1);
563 m_child
[0]->m_parent
= this;
564 m_child
[0]->m_top
= m_top
;
565 m_child
[0]->Create();
566 if (m_leaf
->m_child
) {
567 m_leaf
->m_child
->Reparent(m_container
);
568 m_child
[0]->AddChild(m_leaf
->m_child
);
571 m_child
[1] = new wxDynamicSashWindowImpl(m_window
);
572 m_child
[1]->m_container
= new wxWindow(m_container
, -1);
573 m_child
[1]->m_parent
= this;
574 m_child
[1]->m_top
= m_top
;
575 m_child
[1]->Create();
577 m_split
= m_dragging
;
578 ConstrainChildren(px
, py
);
580 m_top
->m_add_child_target
= m_child
[1];
581 wxDynamicSashSplitEvent
split(m_child
[0]->m_leaf
->m_child
);
582 m_child
[0]->m_leaf
->m_child
->ProcessEvent(split
);
584 m_child
[0]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
585 m_leaf
->m_vscroll
->GetThumbSize(),
586 m_leaf
->m_vscroll
->GetRange(),
587 m_leaf
->m_vscroll
->GetPageSize());
588 m_child
[0]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
589 m_leaf
->m_hscroll
->GetThumbSize(),
590 m_leaf
->m_hscroll
->GetRange(),
591 m_leaf
->m_hscroll
->GetPageSize());
592 m_child
[1]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
593 m_leaf
->m_vscroll
->GetThumbSize(),
594 m_leaf
->m_vscroll
->GetRange(),
595 m_leaf
->m_vscroll
->GetPageSize());
596 m_child
[1]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
597 m_leaf
->m_hscroll
->GetThumbSize(),
598 m_leaf
->m_hscroll
->GetRange(),
599 m_leaf
->m_hscroll
->GetPageSize());
603 m_container
->Layout();
607 /* This code is called when you finish resizing a view by dragging the
608 corner tab, but I think this implementation is lousy and will surprise
609 the user more often than it will do what they are trying to do. What
610 I really need to be doing here is do a rewrite such that *no* sashes
611 move except the ones immediately to the bottom and right of this window,
612 and handle the case where you resize a window larger than it's neighbors
613 by destroying the neighbors.
615 But this will do for now. */
616 void wxDynamicSashWindowImpl::Resize(int x
, int y
) {
617 wxDynamicSashWindowImpl
*h_parent
= FindParent(DSR_BOTTOM_EDGE
);
618 wxDynamicSashWindowImpl
*v_parent
= FindParent(DSR_RIGHT_EDGE
);
621 wxWindow
*frame
= FindFrame();
631 m_container
->ClientToScreen(NULL
, &y
);
632 h_parent
->m_container
->ScreenToClient(NULL
, &y
);
634 int py
= (int)((y
* 100) / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
637 wxDynamicSashWindowImpl
*ho_parent
= FindParent(DSR_TOP_EDGE
);
640 if (FindUpperParent(h_parent
, ho_parent
) == ho_parent
) {
643 py
= (int)((ho_parent
->m_child
[0]->m_container
->GetSize().GetHeight() * 100)
644 / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
645 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
646 h_parent
->m_container
, wxHeight
, py
);
648 h_parent
= ho_parent
;
654 } else if (py
> 90) {
657 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
658 h_parent
->m_container
, wxHeight
, py
);
659 h_parent
->m_container
->Layout();
663 h_parent
= FindParent(DSR_TOP_EDGE
);
666 int py
= (int)((y
* 100) /
667 (h_parent
->m_container
->GetSize().GetHeight() +
668 y
- m_container
->GetSize().GetHeight()) + 0.5);
678 wxSize size
= frame
->GetSize();
679 frame
->SetSize(size
.GetWidth(), size
.GetHeight() + y
- m_container
->GetSize().GetHeight());
684 m_container
->ClientToScreen(&x
, NULL
);
685 v_parent
->m_container
->ScreenToClient(&x
, NULL
);
687 int px
= (int)((x
* 100) / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
690 wxDynamicSashWindowImpl
*vo_parent
= FindParent(DSR_LEFT_EDGE
);
693 if (FindUpperParent(v_parent
, vo_parent
) == vo_parent
) {
696 px
= (int)((vo_parent
->m_child
[0]->m_container
->GetSize().GetWidth() * 100)
697 / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
698 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
699 v_parent
->m_container
, wxWidth
, px
);
701 v_parent
= vo_parent
;
707 } else if (px
> 90) {
710 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
711 v_parent
->m_container
, wxWidth
, px
);
712 v_parent
->m_container
->Layout();
716 v_parent
= FindParent(DSR_LEFT_EDGE
);
719 int px
= (int)((x
* 100) /
720 (v_parent
->m_container
->GetSize().GetWidth() +
721 x
- m_container
->GetSize().GetWidth()) + 0.5);
731 wxSize size
= frame
->GetSize();
732 frame
->SetSize(size
.GetWidth() + x
- m_container
->GetSize().GetWidth(), size
.GetHeight());
736 if (h_unify
!= -1 && v_unify
!= -1) {
737 wxDynamicSashWindowImpl
*parent
= FindUpperParent(h_parent
, v_parent
);
739 if (parent
== h_parent
) {
740 h_parent
->Unify(h_unify
);
742 v_parent
->Unify(v_unify
);
744 } else if (h_unify
!= -1) {
745 h_parent
->Unify(h_unify
);
746 } else if (v_unify
!= -1) {
747 v_parent
->Unify(v_unify
);
752 void wxDynamicSashWindowImpl::OnSize(wxSizeEvent
&event
) {
753 m_container
->Layout();
756 m_leaf
->OnSize(event
);
759 void wxDynamicSashWindowImpl::OnPaint(wxPaintEvent
&event
) {
761 m_leaf
->OnPaint(event
);
763 wxPaintDC
dc(m_container
);
764 dc
.SetBackground(wxBrush(m_container
->GetBackgroundColour(), wxSOLID
));
769 void wxDynamicSashWindowImpl::OnMouseMove(wxMouseEvent
&event
) {
771 DrawSash(m_drag_x
, m_drag_y
);
772 m_drag_x
= event
.m_x
; m_drag_y
= event
.m_y
;
773 DrawSash(m_drag_x
, m_drag_y
);
775 m_leaf
->OnMouseMove(event
);
779 void wxDynamicSashWindowImpl::OnLeave(wxMouseEvent
&event
) {
781 m_leaf
->OnLeave(event
);
785 void wxDynamicSashWindowImpl::OnPress(wxMouseEvent
&event
) {
787 m_leaf
->OnPress(event
);
789 m_dragging
= m_split
;
790 m_drag_x
= event
.m_x
;
791 m_drag_y
= event
.m_y
;
792 DrawSash(m_drag_x
, m_drag_y
);
793 m_container
->CaptureMouse();
797 void wxDynamicSashWindowImpl::OnRelease(wxMouseEvent
&event
) {
798 if ((m_dragging
== DSR_CORNER
) &&
799 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0) {
800 DrawSash(m_drag_x
, m_drag_y
);
801 m_container
->ReleaseMouse();
803 Resize(event
.m_x
, event
.m_y
);
805 m_dragging
= DSR_NONE
;
806 } else if (m_dragging
) {
807 DrawSash(m_drag_x
, m_drag_y
);
808 m_container
->ReleaseMouse();
810 wxSize size
= m_container
->GetSize();
811 int px
= (int)((event
.m_x
* 100) / size
.GetWidth() + 0.5);
812 int py
= (int)((event
.m_y
* 100) / size
.GetHeight() + 0.5);
814 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
>= 10 && py
<= 90)
815 || (m_dragging
== DSR_VERTICAL_TAB
&& px
>= 10 && px
<= 90)) {
816 if (m_child
[0] == NULL
) {
819 /* It would be nice if moving *this* sash didn't implicitly move
820 the sashes of our children (if any). But this will do. */
821 wxLayoutConstraints
*layout
= m_child
[0]->m_container
->GetConstraints();
822 if (m_split
== DSR_HORIZONTAL_TAB
) {
823 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
825 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
827 m_container
->Layout();
830 if (m_child
[0] != NULL
) {
831 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
<= 10)
832 || (m_dragging
== DSR_VERTICAL_TAB
&& px
<= 10)) {
840 wxCursor
cursor(wxCURSOR_ARROW
);
841 if (m_split
== DSR_HORIZONTAL_TAB
) {
842 cursor
= wxCursor(wxCURSOR_SIZENS
);
843 } else if (m_split
== DSR_VERTICAL_TAB
) {
844 cursor
= wxCursor(wxCURSOR_SIZEWE
);
846 m_container
->SetCursor(cursor
);
848 m_dragging
= DSR_NONE
;
850 m_leaf
->OnRelease(event
);
854 // wxDynamicSashWindowLeaf //////////////////////////////////////////////////
856 wxDynamicSashWindowLeaf::wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
) {
859 m_hscroll
= m_vscroll
= NULL
;
863 wxDynamicSashWindowLeaf::~wxDynamicSashWindowLeaf() {
864 m_hscroll
->SetEventHandler(m_hscroll
);
865 m_vscroll
->SetEventHandler(m_vscroll
);
866 m_viewport
->SetEventHandler(m_viewport
);
868 m_hscroll
->Destroy();
869 m_vscroll
->Destroy();
870 m_viewport
->Destroy();
873 bool wxDynamicSashWindowLeaf::Create() {
876 m_hscroll
= new wxScrollBar();
877 m_vscroll
= new wxScrollBar();
878 m_viewport
= new wxWindow();
880 if (!m_hscroll
|| !m_vscroll
|| !m_viewport
) {
884 wxDynamicSashWindowImpl
*add_child_target
= m_impl
->m_add_child_target
;
885 m_impl
->m_add_child_target
= NULL
;
886 success
= m_hscroll
->Create(m_impl
->m_container
, -1, wxDefaultPosition
, wxDefaultSize
,
888 success
= success
&& m_vscroll
->Create(m_impl
->m_container
, -1, wxDefaultPosition
, wxDefaultSize
,
890 success
= success
&& m_viewport
->Create(m_impl
->m_container
, -1);
891 m_impl
->m_add_child_target
= add_child_target
;
893 wxCursor
cursor(wxCURSOR_ARROW
);
894 m_hscroll
->SetCursor(cursor
);
895 m_vscroll
->SetCursor(cursor
);
896 m_viewport
->SetCursor(cursor
);
898 m_viewport
->SetEventHandler(this);
899 Connect(-1, wxEVT_DYNAMIC_SASH_REPARENT
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnReparent
);
901 if (m_impl
->m_window
->GetWindowStyle() & wxDS_MANAGE_SCROLLBARS
) {
902 m_hscroll
->SetEventHandler(this);
903 m_vscroll
->SetEventHandler(this);
905 Connect(-1, wxEVT_SET_FOCUS
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnFocus
);
906 Connect(-1, wxEVT_SCROLL_TOP
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
907 Connect(-1, wxEVT_SCROLL_BOTTOM
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
908 Connect(-1, wxEVT_SCROLL_LINEUP
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
909 Connect(-1, wxEVT_SCROLL_LINEDOWN
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
910 Connect(-1, wxEVT_SCROLL_PAGEUP
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
911 Connect(-1, wxEVT_SCROLL_PAGEDOWN
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
912 Connect(-1, wxEVT_SCROLL_THUMBTRACK
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
913 Connect(-1, wxEVT_SCROLL_THUMBRELEASE
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
916 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
919 wxSize size
= m_hscroll
->GetBestSize();
921 size
= m_hscroll
->GetSize();
924 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 10);
925 layout
->right
.LeftOf(m_vscroll
);
926 layout
->bottom
.SameAs(m_impl
->m_container
, wxBottom
, 3);
927 layout
->height
.Absolute(size
.GetHeight());
928 m_hscroll
->SetConstraints(layout
);
930 layout
= new wxLayoutConstraints();
933 size
= size
= m_vscroll
->GetBestSize();
935 size
= m_vscroll
->GetSize();
938 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 10);
939 layout
->bottom
.Above(m_hscroll
);
940 layout
->right
.SameAs(m_impl
->m_container
, wxRight
, 3);
941 layout
->width
.Absolute(size
.GetWidth());
942 m_vscroll
->SetConstraints(layout
);
944 layout
= new wxLayoutConstraints();
947 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 3);
948 layout
->right
.LeftOf(m_vscroll
);
949 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 3);
950 layout
->bottom
.Above(m_hscroll
);
951 m_viewport
->SetConstraints(layout
);
953 m_impl
->m_container
->Layout();
958 void wxDynamicSashWindowLeaf::AddChild(wxWindow
*window
) {
965 wxDynamicSashReparentEvent
event(this);
966 AddPendingEvent(event
);
969 DynamicSashRegion
wxDynamicSashWindowLeaf::GetRegion(int x
, int y
) {
970 wxSize size
= m_impl
->m_container
->GetSize();
971 int w
= size
.GetWidth();
972 int h
= size
.GetHeight();
973 size
= m_hscroll
->GetSize();
974 int sh
= size
.GetHeight();
975 size
= m_vscroll
->GetSize();
976 int sw
= size
.GetWidth();
978 if (x
>= w
- sw
- 3 && x
< w
&& y
>= h
- sh
- 3 && y
< h
)
980 if (x
>= 3 && x
< 10 && y
>= h
- sh
- 3 && y
< h
- 2)
981 return DSR_VERTICAL_TAB
;
982 if (x
>= w
- sw
- 3 && x
< w
- 2 && y
>= 3 && y
< 10)
983 return DSR_HORIZONTAL_TAB
;
985 return DSR_LEFT_EDGE
;
989 return DSR_RIGHT_EDGE
;
991 return DSR_BOTTOM_EDGE
;
996 void wxDynamicSashWindowLeaf::ResizeChild(wxSize size
) {
998 if (m_impl
->m_window
->GetWindowStyle() & wxDS_MANAGE_SCROLLBARS
) {
999 m_child
->SetSize(size
);
1000 wxSize best_size
= m_child
->GetBestSize();
1001 if (best_size
.GetWidth() < size
.GetWidth()) {
1002 best_size
.SetWidth(size
.GetWidth());
1004 if (best_size
.GetHeight() < size
.GetHeight()) {
1005 best_size
.SetHeight(size
.GetHeight());
1007 m_child
->SetSize(best_size
);
1009 int hpos
= m_hscroll
->GetThumbPosition();
1010 int vpos
= m_vscroll
->GetThumbPosition();
1018 if (hpos
> best_size
.GetWidth() - size
.GetWidth()) {
1019 hpos
= best_size
.GetWidth() - size
.GetWidth();
1021 if (vpos
> best_size
.GetHeight() - size
.GetHeight()) {
1022 vpos
= best_size
.GetHeight() - size
.GetHeight();
1025 m_hscroll
->SetScrollbar(hpos
, size
.GetWidth(),
1026 best_size
.GetWidth(), size
.GetWidth());
1027 m_vscroll
->SetScrollbar(vpos
, size
.GetHeight(),
1028 best_size
.GetHeight(), size
.GetHeight());
1030 // Umm, the scrollbars are doing something insane under GTK+ and subtracting
1031 // one from the position I pass in. This works around that.
1032 m_hscroll
->SetThumbPosition(hpos
+ hpos
- m_hscroll
->GetThumbPosition());
1033 m_vscroll
->SetThumbPosition(vpos
+ vpos
- m_vscroll
->GetThumbPosition());
1035 wxPoint pos
= m_child
->GetPosition();
1036 m_viewport
->ScrollWindow(-hpos
- pos
.x
, -vpos
- pos
.y
);
1038 m_child
->SetSize(size
);
1043 wxScrollBar
*wxDynamicSashWindowLeaf::FindScrollBar(const wxWindow
*child
, int vert
) const {
1044 if (m_child
== child
) {
1055 void wxDynamicSashWindowLeaf::OnSize(wxSizeEvent
&event
) {
1056 m_impl
->m_container
->Refresh();
1057 ResizeChild(m_viewport
->GetSize());
1060 void wxDynamicSashWindowLeaf::OnPaint(wxPaintEvent
&event
) {
1061 wxPaintDC
dc(m_impl
->m_container
);
1062 dc
.SetBackground(wxBrush(m_impl
->m_container
->GetBackgroundColour(), wxSOLID
));
1065 wxPen
highlight(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT
), 1, wxSOLID
);
1066 wxPen
shadow(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW
), 1, wxSOLID
);
1067 wxPen
black(*wxBLACK
, 1, wxSOLID
);
1069 wxSize size
= m_impl
->m_container
->GetSize();
1070 int w
= size
.GetWidth();
1071 int h
= size
.GetHeight();
1072 size
= m_hscroll
->GetSize();
1073 int sh
= size
.GetHeight();
1074 size
= m_vscroll
->GetSize();
1075 int sw
= size
.GetWidth();
1078 dc
.DrawLine(1, 1, 1, h
- 2);
1079 dc
.DrawLine(1, 1, w
- 2, 1);
1081 dc
.DrawLine(2, 2, 2, h
- 3);
1082 dc
.DrawLine(2, 2, w
- 3, 2);
1083 dc
.SetPen(highlight
);
1084 dc
.DrawLine(w
- 2, 2, w
- 2, h
- sh
- 2);
1085 dc
.DrawLine(w
- 2, h
- sh
- 2, w
- sw
- 2, h
- sh
- 2);
1086 dc
.DrawLine(w
- sw
- 2, h
- sh
- 2, w
- sw
- 2, h
- 2);
1087 dc
.DrawLine(w
- sw
- 2, h
- 2, 2, h
- 2);
1089 dc
.SetPen(highlight
);
1090 dc
.DrawLine(w
- sw
- 2, 8, w
- sw
- 2, 4);
1091 dc
.DrawLine(w
- sw
- 2, 4, w
- 5, 4);
1093 dc
.DrawLine(w
- 5, 4, w
- 5, 8);
1094 dc
.DrawLine(w
- 5, 8, w
- sw
- 2, 8);
1096 dc
.DrawLine(w
- 4, 3, w
- 4, 9);
1097 dc
.DrawLine(w
- 4, 9, w
- sw
- 3, 9);
1099 dc
.SetPen(highlight
);
1100 dc
.DrawLine(4, h
- 5, 4, h
- sh
- 2);
1101 dc
.DrawLine(4, h
- sh
- 2, 8, h
- sh
- 2);
1103 dc
.DrawLine(8, h
- sh
- 2, 8, h
- 5);
1104 dc
.DrawLine(8, h
- 5, 4, h
- 5);
1106 dc
.DrawLine(9, h
- sh
- 3, 9, h
- 4);
1107 dc
.DrawLine(9, h
- 4, 3, h
- 4);
1109 int cy
= (h
- sh
+ h
- 6) / 2 + 1;
1110 int cx
= (w
- sw
+ w
- 6) / 2 + 1;
1119 for (y
= sy
; y
< h
- 2; y
+= 4) {
1120 for (x
= sx
; x
< w
- 2; x
+= 4) {
1121 if (x
- cx
>= -(y
- cy
)) {
1122 dc
.SetPen(highlight
);
1125 dc
.DrawPoint(x
+ 1, y
+ 1);
1131 void wxDynamicSashWindowLeaf::OnScroll(wxScrollEvent
&event
) {
1132 int nx
= -m_hscroll
->GetThumbPosition();
1133 int ny
= -m_vscroll
->GetThumbPosition();
1136 wxPoint pos
= m_child
->GetPosition();
1138 m_viewport
->ScrollWindow(nx
- pos
.x
, ny
- pos
.y
);
1142 void wxDynamicSashWindowLeaf::OnFocus(wxFocusEvent
&event
) {
1143 if (event
.m_eventObject
== m_hscroll
|| event
.m_eventObject
== m_vscroll
) {
1144 m_child
->SetFocus();
1149 void wxDynamicSashWindowLeaf::OnMouseMove(wxMouseEvent
&event
) {
1150 if (m_impl
->m_dragging
) {
1154 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1156 wxCursor
cursor(wxCURSOR_ARROW
);
1157 if (region
== DSR_HORIZONTAL_TAB
) {
1158 cursor
= wxCursor(wxCURSOR_SIZENS
);
1159 } else if (region
== DSR_VERTICAL_TAB
) {
1160 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1161 } else if ((region
== DSR_CORNER
) &&
1162 (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0) {
1163 cursor
= wxCursor(wxCURSOR_SIZENWSE
);
1164 } else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1165 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
) {
1166 if (m_impl
->FindParent(region
)) {
1167 if (region
== DSR_LEFT_EDGE
|| region
== DSR_RIGHT_EDGE
) {
1168 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1170 cursor
= wxCursor(wxCURSOR_SIZENS
);
1175 m_impl
->m_container
->SetCursor(cursor
);
1178 void wxDynamicSashWindowLeaf::OnLeave(wxMouseEvent
&event
) {
1179 wxCursor
cursor(wxCURSOR_ARROW
);
1180 m_impl
->m_container
->SetCursor(cursor
);
1184 void wxDynamicSashWindowLeaf::OnPress(wxMouseEvent
&event
) {
1185 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1187 if ((region
== DSR_CORNER
) && (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) == 0)
1190 if (region
== DSR_HORIZONTAL_TAB
|| region
== DSR_VERTICAL_TAB
|| region
== DSR_CORNER
) {
1191 m_impl
->m_dragging
= region
;
1192 m_impl
->m_drag_x
= event
.m_x
;
1193 m_impl
->m_drag_y
= event
.m_y
;
1194 m_impl
->DrawSash(event
.m_x
, event
.m_y
);
1195 m_impl
->m_container
->CaptureMouse();
1196 } else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1197 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
) {
1198 wxDynamicSashWindowImpl
*parent
= m_impl
->FindParent(region
);
1204 m_impl
->m_container
->ClientToScreen(&x
, &y
);
1205 parent
->m_container
->ScreenToClient(&x
, &y
);
1207 parent
->m_dragging
= parent
->m_split
;
1208 parent
->m_drag_x
= x
;
1209 parent
->m_drag_y
= y
;
1210 parent
->DrawSash(x
, y
);
1211 parent
->m_container
->CaptureMouse();
1216 void wxDynamicSashWindowLeaf::OnRelease(wxMouseEvent
&event
) {
1219 void wxDynamicSashWindowLeaf::OnReparent(wxEvent
&event
) {
1221 m_child
->Reparent(m_viewport
);
1224 ResizeChild(m_viewport
->GetSize());
1227 // wxDynamicSashSplitEvent //////////////////////////////////////////////////
1229 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent() {
1230 m_eventObject
= NULL
;
1231 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1234 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent(wxObject
*object
) {
1235 m_eventObject
= object
;
1236 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1239 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashSplitEvent
, wxCommandEvent
)
1241 // wxDynamicSashUnifyEvent //////////////////////////////////////////////////
1243 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent() {
1244 m_eventObject
= NULL
;
1245 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1248 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent(wxObject
*object
) {
1249 m_eventObject
= object
;
1250 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1253 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashUnifyEvent
, wxCommandEvent
)
1255 // wxDynamicSsahReparentEvent ///////////////////////////////////////////////
1257 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent() {
1258 m_eventObject
= NULL
;
1259 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1262 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(wxObject
*object
) {
1263 m_eventObject
= object
;
1264 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1267 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
)
1272 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashReparentEvent
, wxEvent
)
1274 /////////////////////////////////////////////////////////////////////////////