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 DECLARE_DYNAMIC_CLASS(wxDynamicSashReparentEvent
);
91 enum DynamicSashRegion
{
103 class wxDynamicSashWindowImpl
: public wxEvtHandler
{
105 wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
);
106 ~wxDynamicSashWindowImpl();
109 void AddChild(wxWindow
*window
);
110 void DrawSash(int x
, int y
) const;
111 void ConstrainChildren(int px
, int py
);
112 void Split(int x
, int y
);
113 void Unify(int panel
);
114 void Resize(int x
, int y
);
115 wxDynamicSashWindowImpl
*FindParent(DynamicSashRegion side
) const;
116 wxDynamicSashWindowImpl
*FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
117 wxDynamicSashWindowImpl
*sash_b
) const;
118 wxWindow
*FindFrame() const;
119 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
121 void OnSize(wxSizeEvent
&event
);
122 void OnPaint(wxPaintEvent
&event
);
123 void OnMouseMove(wxMouseEvent
&event
);
124 void OnLeave(wxMouseEvent
&event
);
125 void OnPress(wxMouseEvent
&event
);
126 void OnRelease(wxMouseEvent
&event
);
128 wxDynamicSashWindow
*m_window
;
129 wxDynamicSashWindowImpl
*m_add_child_target
;
131 /* This is the window we are responsible for managing. Either of
132 leaf or our children are in this window. For the top level
133 implementation object, this is the same as m_window.
134 Otherwise it is a window we've created an will destroy when we
136 wxWindow
*m_container
;
138 wxDynamicSashWindowImpl
*m_parent
;
139 wxDynamicSashWindowImpl
*m_top
;
140 wxDynamicSashWindowImpl
*m_child
[2];
142 class wxDynamicSashWindowLeaf
*m_leaf
;
144 /* If the implementation is split horizontally or vertically, m_split
145 is set to DSR_HORIZONTAL_TAB or DSR_VERTICAL_TAB, respectively.
146 Otherwise it is set to DSR_NONE. */
147 DynamicSashRegion m_split
;
149 /* These are used to keep track of a sash as it is being dragged, for
150 drawing the user interface correctly. */
151 DynamicSashRegion m_dragging
;
152 int m_drag_x
, m_drag_y
;
155 class wxDynamicSashWindowLeaf
: public wxEvtHandler
{
157 wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
);
158 ~wxDynamicSashWindowLeaf();
161 void AddChild(wxWindow
*window
);
162 DynamicSashRegion
GetRegion(int x
, int y
);
163 void ResizeChild(wxSize size
);
164 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
166 void OnSize(wxSizeEvent
&event
);
167 void OnPaint(wxPaintEvent
&event
);
168 void OnScroll(wxScrollEvent
&event
);
169 void OnFocus(wxFocusEvent
&event
);
170 void OnMouseMove(wxMouseEvent
&event
);
171 void OnLeave(wxMouseEvent
&event
);
172 void OnPress(wxMouseEvent
&event
);
173 void OnRelease(wxMouseEvent
&event
);
174 void OnReparent(wxEvent
&event
);
176 wxDynamicSashWindowImpl
*m_impl
;
178 wxScrollBar
*m_vscroll
, *m_hscroll
;
180 /* m_child is the window provided to us by the application developer.
181 m_viewport is a window we've created, and it is the immediately
182 parent of m_child. We scroll m_child by moving it around within
184 wxWindow
*m_viewport
, *m_child
;
187 // wxDynamicSashWindow //////////////////////////////////////////////////////
189 wxDynamicSashWindow::wxDynamicSashWindow() {
193 wxDynamicSashWindow::wxDynamicSashWindow(wxWindow
*parent
, wxWindowID id
,
194 const wxPoint
& pos
, const wxSize
& size
,
195 long style
, const wxString
& name
) {
197 Create(parent
, id
, pos
, size
, style
, name
);
200 wxDynamicSashWindow::~wxDynamicSashWindow() {
201 SetEventHandler(this);
205 bool wxDynamicSashWindow::Create(wxWindow
*parent
, wxWindowID id
,
206 const wxPoint
& pos
, const wxSize
& size
,
207 long style
, const wxString
& name
) {
211 if (!wxWindow::Create(parent
, id
, pos
, size
, style
, name
))
214 m_impl
= new wxDynamicSashWindowImpl(this);
218 if (!m_impl
->Create()) {
227 void wxDynamicSashWindow::AddChild(wxWindowBase
*child
) {
228 wxWindow::AddChild(child
);
230 m_impl
->AddChild(wxDynamicCast(child
, wxWindow
));
233 wxScrollBar
*wxDynamicSashWindow::GetHScrollBar(const wxWindow
*child
) const {
234 return m_impl
->FindScrollBar(child
, 0);
237 wxScrollBar
*wxDynamicSashWindow::GetVScrollBar(const wxWindow
*child
) const {
238 return m_impl
->FindScrollBar(child
, 1);
241 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashWindow
, wxWindow
)
243 // wxDynamicSashWindowImpl //////////////////////////////////////////////////
245 wxDynamicSashWindowImpl::wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
) {
247 m_add_child_target
= this;
252 m_child
[0] = m_child
[1] = NULL
;
254 m_dragging
= DSR_NONE
;
258 wxDynamicSashWindowImpl::~wxDynamicSashWindowImpl() {
266 if (m_container
!= m_window
&& m_container
) {
267 m_container
->SetEventHandler(m_container
);
268 m_container
->Destroy();
272 bool wxDynamicSashWindowImpl::Create() {
274 m_container
= m_window
;
277 wxCursor
cursor(wxCURSOR_ARROW
);
278 m_container
->SetCursor(cursor
);
280 m_leaf
= new wxDynamicSashWindowLeaf(this);
284 if (!m_leaf
->Create()) {
290 m_container
->SetEventHandler(this);
292 Connect(-1, wxEVT_SIZE
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnSize
);
293 Connect(-1, wxEVT_PAINT
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnPaint
);
294 Connect(-1, wxEVT_MOTION
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnMouseMove
);
295 Connect(-1, wxEVT_ENTER_WINDOW
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnMouseMove
);
296 Connect(-1, wxEVT_LEAVE_WINDOW
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnLeave
);
297 Connect(-1, wxEVT_LEFT_DOWN
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnPress
);
298 Connect(-1, wxEVT_LEFT_UP
, (wxObjectEventFunction
)&wxDynamicSashWindowImpl::OnRelease
);
303 void wxDynamicSashWindowImpl::AddChild(wxWindow
*window
) {
304 if (m_add_child_target
&& m_add_child_target
->m_leaf
) {
305 m_add_child_target
->m_leaf
->AddChild(window
);
309 void wxDynamicSashWindowImpl::DrawSash(int x
, int y
) const {
313 dc
.StartDrawingOnTop(m_container
);
317 bdc
.SelectObject(bmp
);
318 bdc
.DrawRectangle(-1, -1, 10, 10);
319 for (i
= 0; i
< 8; i
++) {
320 for (j
= 0; j
< 8; j
++) {
329 dc
.SetLogicalFunction(wxXOR
);
331 if (m_dragging
== DSR_CORNER
) {
335 m_container
->ClientToScreen(&cx
, &cy
);
336 m_container
->ClientToScreen(&x
, &y
);
338 if (cx
< x
&& cy
< y
) {
339 dc
.DrawRectangle(cx
- 2, cy
- 2, x
- cx
+ 4, 4);
340 dc
.DrawRectangle(x
- 2, cy
+ 2, 4, y
- cy
);
341 dc
.DrawRectangle(cx
- 2, cy
+ 2, 4, y
- cy
);
342 dc
.DrawRectangle(cx
+ 2, y
- 2, x
- cx
- 4, 4);
346 m_container
->GetClientSize(&body_w
, &body_h
);
357 if (m_dragging
== DSR_HORIZONTAL_TAB
)
362 m_container
->ClientToScreen(&x
, &y
);
365 w
= body_w
; h
= body_h
;
367 if (m_dragging
== DSR_HORIZONTAL_TAB
)
368 dc
.DrawRectangle(x
, y
- 2, w
, 4);
370 dc
.DrawRectangle(x
- 2, y
, 4, h
);
373 dc
.EndDrawingOnTop();
376 wxDynamicSashWindowImpl
*wxDynamicSashWindowImpl::FindParent(DynamicSashRegion side
) const {
377 if (m_parent
== NULL
) {
381 if (m_parent
->m_split
== DSR_HORIZONTAL_TAB
) {
382 if (side
== DSR_TOP_EDGE
&& m_parent
->m_child
[1] == this)
384 if (side
== DSR_BOTTOM_EDGE
&& m_parent
->m_child
[0] == this)
386 } else if (m_parent
->m_split
== DSR_VERTICAL_TAB
) {
387 if (side
== DSR_LEFT_EDGE
&& m_parent
->m_child
[1] == this)
389 if (side
== DSR_RIGHT_EDGE
&& m_parent
->m_child
[0] == this)
393 return m_parent
->FindParent(side
);
396 wxDynamicSashWindowImpl
*wxDynamicSashWindowImpl::FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
397 wxDynamicSashWindowImpl
*sash_b
) const {
399 win
= sash_a
->m_container
->GetParent();
400 while (win
&& !win
->IsTopLevel()) {
401 if (win
== sash_b
->m_container
) {
405 win
= win
->GetParent();
412 wxWindow
*wxDynamicSashWindowImpl::FindFrame() const {
415 win
= m_window
->GetParent();
416 while (win
&& !win
->IsTopLevel()) {
417 win
= win
->GetParent();
423 wxScrollBar
*wxDynamicSashWindowImpl::FindScrollBar(const wxWindow
*child
, int vert
) const {
424 if (m_child
[0] == NULL
&& m_leaf
== NULL
) {
429 return m_leaf
->FindScrollBar(child
, vert
);
431 wxScrollBar
*ret
= m_child
[0]->FindScrollBar(child
, vert
);
433 ret
= m_child
[1]->FindScrollBar(child
, vert
);
440 void wxDynamicSashWindowImpl::ConstrainChildren(int px
, int py
) {
441 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
442 layout
->left
.SameAs(m_container
, wxLeft
);
443 layout
->top
.SameAs(m_container
, wxTop
);
444 if (m_split
== DSR_HORIZONTAL_TAB
) {
445 layout
->right
.SameAs(m_container
, wxRight
);
446 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
448 layout
->bottom
.SameAs(m_container
, wxBottom
);
449 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
451 m_child
[0]->m_container
->SetConstraints(layout
);
453 layout
= new wxLayoutConstraints();
454 layout
->right
.SameAs(m_container
, wxRight
);
455 layout
->bottom
.SameAs(m_container
, wxBottom
);
456 if (m_split
== DSR_HORIZONTAL_TAB
) {
457 layout
->top
.Below(m_child
[0]->m_container
, 1);
458 layout
->left
.SameAs(m_container
, wxLeft
);
460 layout
->left
.RightOf(m_child
[0]->m_container
, 1);
461 layout
->top
.SameAs(m_container
, wxTop
);
463 m_child
[1]->m_container
->SetConstraints(layout
);
466 void wxDynamicSashWindowImpl::Unify(int panel
) {
472 if (m_child
[panel
]->m_leaf
) {
473 wxDynamicSashWindowImpl
*child
[2];
475 child
[0] = m_child
[0];
476 child
[1] = m_child
[1];
478 m_child
[0] = m_child
[1] = NULL
;
480 m_leaf
= new wxDynamicSashWindowLeaf(this);
482 m_leaf
->m_child
= child
[panel
]->m_leaf
->m_child
;
484 m_leaf
->m_vscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_vscroll
->GetThumbPosition(),
485 child
[panel
]->m_leaf
->m_vscroll
->GetThumbSize(),
486 child
[panel
]->m_leaf
->m_vscroll
->GetRange(),
487 child
[panel
]->m_leaf
->m_vscroll
->GetPageSize());
488 m_leaf
->m_hscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_hscroll
->GetThumbPosition(),
489 child
[panel
]->m_leaf
->m_hscroll
->GetThumbSize(),
490 child
[panel
]->m_leaf
->m_hscroll
->GetRange(),
491 child
[panel
]->m_leaf
->m_hscroll
->GetPageSize());
492 m_add_child_target
= NULL
;
493 wxDynamicSashReparentEvent
event(m_leaf
);
494 m_leaf
->ProcessEvent(event
);
501 wxDynamicSashUnifyEvent
unify(m_leaf
->m_child
);
502 m_leaf
->m_child
->ProcessEvent(unify
);
504 m_split
= m_child
[panel
]->m_split
;
506 delete m_child
[other
];
508 wxDynamicSashWindowImpl
*child_panel
= m_child
[panel
];
509 m_child
[0] = child_panel
->m_child
[0];
510 m_child
[1] = child_panel
->m_child
[1];
512 m_child
[0]->m_parent
= this;
513 m_child
[1]->m_parent
= this;
515 m_add_child_target
= NULL
;
516 m_child
[0]->m_container
->Reparent(m_container
);
517 m_child
[1]->m_container
->Reparent(m_container
);
519 child_panel
->m_child
[0] = child_panel
->m_child
[1] = NULL
;
522 wxSize size
= m_container
->GetSize();
523 wxSize child_size
= m_child
[0]->m_container
->GetSize();
525 ConstrainChildren(child_size
.GetWidth() * 100 / size
.GetWidth(),
526 child_size
.GetHeight() * 100 / size
.GetHeight());
528 m_container
->Layout();
532 void wxDynamicSashWindowImpl::Split(int px
, int py
) {
535 m_add_child_target
= NULL
;
537 m_child
[0] = new wxDynamicSashWindowImpl(m_window
);
538 m_child
[0]->m_container
= new wxWindow(m_container
, -1);
539 m_child
[0]->m_parent
= this;
540 m_child
[0]->m_top
= m_top
;
541 m_child
[0]->Create();
542 if (m_leaf
->m_child
) {
543 m_leaf
->m_child
->Reparent(m_container
);
544 m_child
[0]->AddChild(m_leaf
->m_child
);
547 m_child
[1] = new wxDynamicSashWindowImpl(m_window
);
548 m_child
[1]->m_container
= new wxWindow(m_container
, -1);
549 m_child
[1]->m_parent
= this;
550 m_child
[1]->m_top
= m_top
;
551 m_child
[1]->Create();
553 m_split
= m_dragging
;
554 ConstrainChildren(px
, py
);
556 m_top
->m_add_child_target
= m_child
[1];
557 wxDynamicSashSplitEvent
split(m_child
[0]->m_leaf
->m_child
);
558 m_child
[0]->m_leaf
->m_child
->ProcessEvent(split
);
560 m_child
[0]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
561 m_leaf
->m_vscroll
->GetThumbSize(),
562 m_leaf
->m_vscroll
->GetRange(),
563 m_leaf
->m_vscroll
->GetPageSize());
564 m_child
[0]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
565 m_leaf
->m_hscroll
->GetThumbSize(),
566 m_leaf
->m_hscroll
->GetRange(),
567 m_leaf
->m_hscroll
->GetPageSize());
568 m_child
[1]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
569 m_leaf
->m_vscroll
->GetThumbSize(),
570 m_leaf
->m_vscroll
->GetRange(),
571 m_leaf
->m_vscroll
->GetPageSize());
572 m_child
[1]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
573 m_leaf
->m_hscroll
->GetThumbSize(),
574 m_leaf
->m_hscroll
->GetRange(),
575 m_leaf
->m_hscroll
->GetPageSize());
579 m_container
->Layout();
584 /* This code is called when you finish resizing a view by dragging the
585 corner tab, but I think this implementation is lousy and will surprise
586 the user more often than it will do what they are trying to do. What
587 I really need to be doing here is do a rewrite such that *no* sashes
588 move except the ones immediately to the bottom and right of this window,
589 and handle the case where you resize a window larger than it's neighbors
590 by destroying the neighbors.
592 But this will do for now. */
593 void wxDynamicSashWindowImpl::Resize(int x
, int y
) {
594 wxDynamicSashWindowImpl
*h_parent
= FindParent(DSR_BOTTOM_EDGE
);
595 wxDynamicSashWindowImpl
*v_parent
= FindParent(DSR_RIGHT_EDGE
);
598 wxWindow
*frame
= FindFrame();
608 m_container
->ClientToScreen(NULL
, &y
);
609 h_parent
->m_container
->ScreenToClient(NULL
, &y
);
611 int py
= (int)((y
* 100) / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
614 wxDynamicSashWindowImpl
*ho_parent
= FindParent(DSR_TOP_EDGE
);
617 if (FindUpperParent(h_parent
, ho_parent
) == ho_parent
) {
620 py
= (int)((ho_parent
->m_child
[0]->m_container
->GetSize().GetHeight() * 100)
621 / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
622 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
623 h_parent
->m_container
, wxHeight
, py
);
625 h_parent
= ho_parent
;
631 } else if (py
> 90) {
634 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
635 h_parent
->m_container
, wxHeight
, py
);
636 h_parent
->m_container
->Layout();
640 h_parent
= FindParent(DSR_TOP_EDGE
);
643 int py
= (int)((y
* 100) /
644 (h_parent
->m_container
->GetSize().GetHeight() +
645 y
- m_container
->GetSize().GetHeight()) + 0.5);
655 wxSize size
= frame
->GetSize();
656 frame
->SetSize(size
.GetWidth(), size
.GetHeight() + y
- m_container
->GetSize().GetHeight());
661 m_container
->ClientToScreen(&x
, NULL
);
662 v_parent
->m_container
->ScreenToClient(&x
, NULL
);
664 int px
= (int)((x
* 100) / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
667 wxDynamicSashWindowImpl
*vo_parent
= FindParent(DSR_LEFT_EDGE
);
670 if (FindUpperParent(v_parent
, vo_parent
) == vo_parent
) {
673 px
= (int)((vo_parent
->m_child
[0]->m_container
->GetSize().GetWidth() * 100)
674 / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
675 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
676 v_parent
->m_container
, wxWidth
, px
);
678 v_parent
= vo_parent
;
684 } else if (px
> 90) {
687 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
688 v_parent
->m_container
, wxWidth
, px
);
689 v_parent
->m_container
->Layout();
693 v_parent
= FindParent(DSR_LEFT_EDGE
);
696 int px
= (int)((x
* 100) /
697 (v_parent
->m_container
->GetSize().GetWidth() +
698 x
- m_container
->GetSize().GetWidth()) + 0.5);
708 wxSize size
= frame
->GetSize();
709 frame
->SetSize(size
.GetWidth() + x
- m_container
->GetSize().GetWidth(), size
.GetHeight());
713 if (h_unify
!= -1 && v_unify
!= -1) {
714 wxDynamicSashWindowImpl
*parent
= FindUpperParent(h_parent
, v_parent
);
716 if (parent
== h_parent
) {
717 h_parent
->Unify(h_unify
);
719 v_parent
->Unify(v_unify
);
721 } else if (h_unify
!= -1) {
722 h_parent
->Unify(h_unify
);
723 } else if (v_unify
!= -1) {
724 v_parent
->Unify(v_unify
);
729 void wxDynamicSashWindowImpl::OnSize(wxSizeEvent
&event
) {
730 m_container
->Layout();
733 m_leaf
->OnSize(event
);
736 void wxDynamicSashWindowImpl::OnPaint(wxPaintEvent
&event
) {
738 m_leaf
->OnPaint(event
);
740 wxPaintDC
dc(m_container
);
741 dc
.SetBackground(wxBrush(m_container
->GetBackgroundColour(), wxSOLID
));
746 void wxDynamicSashWindowImpl::OnMouseMove(wxMouseEvent
&event
) {
748 DrawSash(m_drag_x
, m_drag_y
);
749 m_drag_x
= event
.m_x
; m_drag_y
= event
.m_y
;
750 DrawSash(m_drag_x
, m_drag_y
);
752 m_leaf
->OnMouseMove(event
);
756 void wxDynamicSashWindowImpl::OnLeave(wxMouseEvent
&event
) {
758 m_leaf
->OnLeave(event
);
762 void wxDynamicSashWindowImpl::OnPress(wxMouseEvent
&event
) {
764 m_leaf
->OnPress(event
);
766 m_dragging
= m_split
;
767 m_drag_x
= event
.m_x
;
768 m_drag_y
= event
.m_y
;
769 DrawSash(m_drag_x
, m_drag_y
);
770 m_container
->CaptureMouse();
774 void wxDynamicSashWindowImpl::OnRelease(wxMouseEvent
&event
) {
775 if (m_dragging
== DSR_CORNER
) {
776 DrawSash(m_drag_x
, m_drag_y
);
777 m_container
->ReleaseMouse();
779 Resize(event
.m_x
, event
.m_y
);
781 m_dragging
= DSR_NONE
;
782 } else if (m_dragging
) {
783 DrawSash(m_drag_x
, m_drag_y
);
784 m_container
->ReleaseMouse();
786 wxSize size
= m_container
->GetSize();
787 int px
= (int)((event
.m_x
* 100) / size
.GetWidth() + 0.5);
788 int py
= (int)((event
.m_y
* 100) / size
.GetHeight() + 0.5);
790 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
>= 10 && py
<= 90)
791 || (m_dragging
== DSR_VERTICAL_TAB
&& px
>= 10 && px
<= 90)) {
792 if (m_child
[0] == NULL
) {
795 /* It would be nice if moving *this* sash didn't implicitly move
796 the sashes of our children (if any). But this will do. */
797 wxLayoutConstraints
*layout
= m_child
[0]->m_container
->GetConstraints();
798 if (m_split
== DSR_HORIZONTAL_TAB
) {
799 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
801 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
803 m_container
->Layout();
806 if (m_child
[0] != NULL
) {
807 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
<= 10)
808 || (m_dragging
== DSR_VERTICAL_TAB
&& px
<= 10)) {
816 wxCursor
cursor(wxCURSOR_ARROW
);
817 if (m_split
== DSR_HORIZONTAL_TAB
) {
818 cursor
= wxCursor(wxCURSOR_SIZENS
);
819 } else if (m_split
== DSR_VERTICAL_TAB
) {
820 cursor
= wxCursor(wxCURSOR_SIZEWE
);
822 m_container
->SetCursor(cursor
);
824 m_dragging
= DSR_NONE
;
826 m_leaf
->OnRelease(event
);
830 // wxDynamicSashWindowLeaf //////////////////////////////////////////////////
832 wxDynamicSashWindowLeaf::wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
) {
835 m_hscroll
= m_vscroll
= NULL
;
839 wxDynamicSashWindowLeaf::~wxDynamicSashWindowLeaf() {
840 m_hscroll
->SetEventHandler(m_hscroll
);
841 m_vscroll
->SetEventHandler(m_vscroll
);
842 m_viewport
->SetEventHandler(m_viewport
);
844 m_hscroll
->Destroy();
845 m_vscroll
->Destroy();
846 m_viewport
->Destroy();
849 bool wxDynamicSashWindowLeaf::Create() {
852 m_hscroll
= new wxScrollBar();
853 m_vscroll
= new wxScrollBar();
854 m_viewport
= new wxWindow();
856 if (!m_hscroll
|| !m_vscroll
|| !m_viewport
) {
860 wxDynamicSashWindowImpl
*add_child_target
= m_impl
->m_add_child_target
;
861 m_impl
->m_add_child_target
= NULL
;
862 success
= m_hscroll
->Create(m_impl
->m_container
, -1, wxDefaultPosition
, wxDefaultSize
,
864 success
= success
&& m_vscroll
->Create(m_impl
->m_container
, -1, wxDefaultPosition
, wxDefaultSize
,
866 success
= success
&& m_viewport
->Create(m_impl
->m_container
, -1);
867 m_impl
->m_add_child_target
= add_child_target
;
869 wxCursor
cursor(wxCURSOR_ARROW
);
870 m_hscroll
->SetCursor(cursor
);
871 m_vscroll
->SetCursor(cursor
);
872 m_viewport
->SetCursor(cursor
);
874 m_viewport
->SetEventHandler(this);
875 Connect(-1, wxEVT_DYNAMIC_SASH_REPARENT
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnReparent
);
877 if (m_impl
->m_window
->GetWindowStyle() & wxMANAGE_SCROLLBARS
) {
878 m_hscroll
->SetEventHandler(this);
879 m_vscroll
->SetEventHandler(this);
881 Connect(-1, wxEVT_SET_FOCUS
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnFocus
);
882 Connect(-1, wxEVT_SCROLL_TOP
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
883 Connect(-1, wxEVT_SCROLL_BOTTOM
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
884 Connect(-1, wxEVT_SCROLL_LINEUP
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
885 Connect(-1, wxEVT_SCROLL_LINEDOWN
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
886 Connect(-1, wxEVT_SCROLL_PAGEUP
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
887 Connect(-1, wxEVT_SCROLL_PAGEDOWN
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
888 Connect(-1, wxEVT_SCROLL_THUMBTRACK
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
889 Connect(-1, wxEVT_SCROLL_THUMBRELEASE
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
892 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
895 wxSize size
= m_hscroll
->GetBestSize();
897 size
= m_hscroll
->GetSize();
900 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 10);
901 layout
->right
.LeftOf(m_vscroll
);
902 layout
->bottom
.SameAs(m_impl
->m_container
, wxBottom
, 3);
903 layout
->height
.Absolute(size
.GetHeight());
904 m_hscroll
->SetConstraints(layout
);
906 layout
= new wxLayoutConstraints();
909 size
= size
= m_vscroll
->GetBestSize();
911 size
= m_vscroll
->GetSize();
914 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 10);
915 layout
->bottom
.Above(m_hscroll
);
916 layout
->right
.SameAs(m_impl
->m_container
, wxRight
, 3);
917 layout
->width
.Absolute(size
.GetWidth());
918 m_vscroll
->SetConstraints(layout
);
920 layout
= new wxLayoutConstraints();
923 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 3);
924 layout
->right
.LeftOf(m_vscroll
);
925 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 3);
926 layout
->bottom
.Above(m_hscroll
);
927 m_viewport
->SetConstraints(layout
);
929 m_impl
->m_container
->Layout();
934 void wxDynamicSashWindowLeaf::AddChild(wxWindow
*window
) {
941 wxDynamicSashReparentEvent
event(this);
942 AddPendingEvent(event
);
945 DynamicSashRegion
wxDynamicSashWindowLeaf::GetRegion(int x
, int y
) {
946 wxSize size
= m_impl
->m_container
->GetSize();
947 int w
= size
.GetWidth();
948 int h
= size
.GetHeight();
949 size
= m_hscroll
->GetSize();
950 int sh
= size
.GetHeight();
951 size
= m_vscroll
->GetSize();
952 int sw
= size
.GetWidth();
954 if (x
>= w
- sw
- 3 && x
< w
&& y
>= h
- sh
- 3 && y
< h
)
956 if (x
>= 3 && x
< 10 && y
>= h
- sh
- 3 && y
< h
- 2)
957 return DSR_VERTICAL_TAB
;
958 if (x
>= w
- sw
- 3 && x
< w
- 2 && y
>= 3 && y
< 10)
959 return DSR_HORIZONTAL_TAB
;
961 return DSR_LEFT_EDGE
;
965 return DSR_RIGHT_EDGE
;
967 return DSR_BOTTOM_EDGE
;
972 void wxDynamicSashWindowLeaf::ResizeChild(wxSize size
) {
974 if (m_impl
->m_window
->GetWindowStyle() & wxMANAGE_SCROLLBARS
) {
975 m_child
->SetSize(size
);
976 wxSize best_size
= m_child
->GetBestSize();
977 if (best_size
.GetWidth() < size
.GetWidth()) {
978 best_size
.SetWidth(size
.GetWidth());
980 if (best_size
.GetHeight() < size
.GetHeight()) {
981 best_size
.SetHeight(size
.GetHeight());
983 m_child
->SetSize(best_size
);
985 int hpos
= m_hscroll
->GetThumbPosition();
986 int vpos
= m_vscroll
->GetThumbPosition();
994 if (hpos
> best_size
.GetWidth() - size
.GetWidth()) {
995 hpos
= best_size
.GetWidth() - size
.GetWidth();
997 if (vpos
> best_size
.GetHeight() - size
.GetHeight()) {
998 vpos
= best_size
.GetHeight() - size
.GetHeight();
1001 m_hscroll
->SetScrollbar(hpos
, size
.GetWidth(),
1002 best_size
.GetWidth(), size
.GetWidth());
1003 m_vscroll
->SetScrollbar(vpos
, size
.GetHeight(),
1004 best_size
.GetHeight(), size
.GetHeight());
1006 // Umm, the scrollbars are doing something insane under GTK+ and subtracting
1007 // one from the position I pass in. This works around that.
1008 m_hscroll
->SetThumbPosition(hpos
+ hpos
- m_hscroll
->GetThumbPosition());
1009 m_vscroll
->SetThumbPosition(vpos
+ vpos
- m_vscroll
->GetThumbPosition());
1011 wxPoint pos
= m_child
->GetPosition();
1012 m_viewport
->ScrollWindow(-hpos
- pos
.x
, -vpos
- pos
.y
);
1014 m_child
->SetSize(size
);
1019 wxScrollBar
*wxDynamicSashWindowLeaf::FindScrollBar(const wxWindow
*child
, int vert
) const {
1020 if (m_child
== child
) {
1031 void wxDynamicSashWindowLeaf::OnSize(wxSizeEvent
&event
) {
1032 m_impl
->m_container
->Refresh();
1033 ResizeChild(m_viewport
->GetSize());
1036 void wxDynamicSashWindowLeaf::OnPaint(wxPaintEvent
&event
) {
1037 wxPaintDC
dc(m_impl
->m_container
);
1038 dc
.SetBackground(wxBrush(m_impl
->m_container
->GetBackgroundColour(), wxSOLID
));
1041 wxPen
highlight(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_BTNHIGHLIGHT
), 1, wxSOLID
);
1042 wxPen
shadow(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_BTNSHADOW
), 1, wxSOLID
);
1043 wxPen
black(*wxBLACK
, 1, wxSOLID
);
1045 wxSize size
= m_impl
->m_container
->GetSize();
1046 int w
= size
.GetWidth();
1047 int h
= size
.GetHeight();
1048 size
= m_hscroll
->GetSize();
1049 int sh
= size
.GetHeight();
1050 size
= m_vscroll
->GetSize();
1051 int sw
= size
.GetWidth();
1054 dc
.DrawLine(1, 1, 1, h
- 2);
1055 dc
.DrawLine(1, 1, w
- 2, 1);
1057 dc
.DrawLine(2, 2, 2, h
- 3);
1058 dc
.DrawLine(2, 2, w
- 3, 2);
1059 dc
.SetPen(highlight
);
1060 dc
.DrawLine(w
- 2, 2, w
- 2, h
- sh
- 2);
1061 dc
.DrawLine(w
- 2, h
- sh
- 2, w
- sw
- 2, h
- sh
- 2);
1062 dc
.DrawLine(w
- sw
- 2, h
- sh
- 2, w
- sw
- 2, h
- 2);
1063 dc
.DrawLine(w
- sw
- 2, h
- 2, 2, h
- 2);
1065 dc
.SetPen(highlight
);
1066 dc
.DrawLine(w
- sw
- 2, 8, w
- sw
- 2, 4);
1067 dc
.DrawLine(w
- sw
- 2, 4, w
- 5, 4);
1069 dc
.DrawLine(w
- 5, 4, w
- 5, 8);
1070 dc
.DrawLine(w
- 5, 8, w
- sw
- 2, 8);
1072 dc
.DrawLine(w
- 4, 3, w
- 4, 9);
1073 dc
.DrawLine(w
- 4, 9, w
- sw
- 3, 9);
1075 dc
.SetPen(highlight
);
1076 dc
.DrawLine(4, h
- 5, 4, h
- sh
- 2);
1077 dc
.DrawLine(4, h
- sh
- 2, 8, h
- sh
- 2);
1079 dc
.DrawLine(8, h
- sh
- 2, 8, h
- 5);
1080 dc
.DrawLine(8, h
- 5, 4, h
- 5);
1082 dc
.DrawLine(9, h
- sh
- 3, 9, h
- 4);
1083 dc
.DrawLine(9, h
- 4, 3, h
- 4);
1086 int cy
= (h
- sh
+ h
- 6) / 2 + 1;
1087 int cx
= (w
- sw
+ w
- 6) / 2 + 1;
1096 for (y
= sy
; y
< h
- 2; y
+= 4) {
1097 for (x
= sx
; x
< w
- 2; x
+= 4) {
1098 if (x
- cx
>= -(y
- cy
)) {
1099 dc
.SetPen(highlight
);
1102 dc
.DrawPoint(x
+ 1, y
+ 1);
1108 void wxDynamicSashWindowLeaf::OnScroll(wxScrollEvent
&event
) {
1109 int nx
= -m_hscroll
->GetThumbPosition();
1110 int ny
= -m_vscroll
->GetThumbPosition();
1113 wxPoint pos
= m_child
->GetPosition();
1115 m_viewport
->ScrollWindow(nx
- pos
.x
, ny
- pos
.y
);
1119 void wxDynamicSashWindowLeaf::OnFocus(wxFocusEvent
&event
) {
1120 if (event
.m_eventObject
== m_hscroll
|| event
.m_eventObject
== m_vscroll
) {
1121 m_child
->SetFocus();
1126 void wxDynamicSashWindowLeaf::OnMouseMove(wxMouseEvent
&event
) {
1127 if (m_impl
->m_dragging
) {
1131 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1133 wxCursor
cursor(wxCURSOR_ARROW
);
1134 if (region
== DSR_HORIZONTAL_TAB
) {
1135 cursor
= wxCursor(wxCURSOR_SIZENS
);
1136 } else if (region
== DSR_VERTICAL_TAB
) {
1137 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1138 } else if (region
== DSR_CORNER
) {
1139 cursor
= wxCursor(wxCURSOR_SIZENWSE
);
1140 } else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1141 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
) {
1142 if (m_impl
->FindParent(region
)) {
1143 if (region
== DSR_LEFT_EDGE
|| region
== DSR_RIGHT_EDGE
) {
1144 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1146 cursor
= wxCursor(wxCURSOR_SIZENS
);
1151 m_impl
->m_container
->SetCursor(cursor
);
1154 void wxDynamicSashWindowLeaf::OnLeave(wxMouseEvent
&event
) {
1155 wxCursor
cursor(wxCURSOR_ARROW
);
1156 m_impl
->m_container
->SetCursor(cursor
);
1160 void wxDynamicSashWindowLeaf::OnPress(wxMouseEvent
&event
) {
1161 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1163 if (region
== DSR_HORIZONTAL_TAB
|| region
== DSR_VERTICAL_TAB
|| region
== DSR_CORNER
) {
1164 m_impl
->m_dragging
= region
;
1165 m_impl
->m_drag_x
= event
.m_x
;
1166 m_impl
->m_drag_y
= event
.m_y
;
1167 m_impl
->DrawSash(event
.m_x
, event
.m_y
);
1168 m_impl
->m_container
->CaptureMouse();
1169 } else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1170 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
) {
1171 wxDynamicSashWindowImpl
*parent
= m_impl
->FindParent(region
);
1177 m_impl
->m_container
->ClientToScreen(&x
, &y
);
1178 parent
->m_container
->ScreenToClient(&x
, &y
);
1180 parent
->m_dragging
= parent
->m_split
;
1181 parent
->m_drag_x
= x
;
1182 parent
->m_drag_y
= y
;
1183 parent
->DrawSash(x
, y
);
1184 parent
->m_container
->CaptureMouse();
1189 void wxDynamicSashWindowLeaf::OnRelease(wxMouseEvent
&event
) {
1192 void wxDynamicSashWindowLeaf::OnReparent(wxEvent
&event
) {
1194 m_child
->Reparent(m_viewport
);
1197 ResizeChild(m_viewport
->GetSize());
1200 // wxDynamicSashSplitEvent //////////////////////////////////////////////////
1202 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent() {
1203 m_eventObject
= NULL
;
1204 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1207 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent(wxObject
*object
) {
1208 m_eventObject
= object
;
1209 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1212 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashSplitEvent
, wxCommandEvent
)
1214 // wxDynamicSashUnifyEvent //////////////////////////////////////////////////
1216 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent() {
1217 m_eventObject
= NULL
;
1218 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1221 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent(wxObject
*object
) {
1222 m_eventObject
= object
;
1223 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1226 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashUnifyEvent
, wxCommandEvent
)
1228 // wxDynamicSsahReparentEvent ///////////////////////////////////////////////
1230 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent() {
1231 m_eventObject
= NULL
;
1232 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1235 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(wxObject
*object
) {
1236 m_eventObject
= object
;
1237 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1240 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashReparentEvent
, wxEvent
)
1242 /////////////////////////////////////////////////////////////////////////////