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" wxWidgets headers)
34 #include "wx/gizmos/dynamicsash.h"
37 const wxChar
* wxDynamicSashWindowNameStr
= wxT("dynamicSashWindow");
41 wxDynamicSashWindow works by internally storing a tree of Implementation
42 objects (wxDynamicSsahWindowImpl) and Leaf objects
43 (wxDynamicSashWindowLeaf). The wxDynamicSashWindow has a pointer to one
44 implementation, and each implementation either has a pointer to a one
45 leaf (m_leaf) or a pointer to two children implementation objects
46 (m_child). The leaves each are responsible for drawing the frame and
47 decorations around one user-provided views and for responding to mouse
50 A resulting tree might look something like this:
54 +- wxDynamicSashWindowImpl
56 +- wxDynamicSashWindowLeaf
60 +- wxDynamicSashWindowImpl
62 +- wxDynamicSashWindowLeaf
66 +- wxDynamicSashWindowLeaf
70 Each time a split occurs, one of the implementation objects removes its
71 leaf, generates two new implementation object children, each with a new
72 leaf, and reparents the user view which was connected to its old leaf
73 to be one of the new leaf's user view, and sends a Split event to the
74 user view in the hopes that it will generate a new user view for the
77 When a unification ocurrs, an implementation object is replaced by one
78 of its children, and the tree of its other child is pruned.
80 One quirk is that the top-level implementation object (m_top) always
81 keeps a pointer to the implementation object where a new child is needed.
82 (m_add_child_target). This is so that when a new uesr view is added
83 to the hierarchy, AddChild() is able to reparent the new user view to
84 the correct implementation object's leaf.
88 #include "wx/dcmemory.h"
89 #include "wx/dcscreen.h"
90 #include "wx/layout.h"
91 #include "wx/scrolbar.h"
92 #include "wx/settings.h"
95 const wxEventType wxEVT_DYNAMIC_SASH_SPLIT
= wxNewEventType();
96 const wxEventType wxEVT_DYNAMIC_SASH_UNIFY
= wxNewEventType();
97 const wxEventType wxEVT_DYNAMIC_SASH_REPARENT
= wxNewEventType();
99 enum DynamicSashRegion
113 wxDynamicSashReparentEvent is generated by the AddChild() method of
114 wxDynamicSashWindow when it wants a Leaf to reparent a user view window
115 to its viewport at some time in the future. We can't reparent the window
116 immediately, because switching parents in AddChild() confuses the wxWindow
117 class. Instead, we queue up this event, and the window is actually
118 reparented the next time we process events in the idle loop.
120 class wxDynamicSashReparentEvent
: public wxEvent
123 wxDynamicSashReparentEvent();
124 wxDynamicSashReparentEvent(wxObject
*object
);
125 wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
);
127 virtual wxEvent
* Clone() const { return new wxDynamicSashReparentEvent(*this); }
129 DECLARE_DYNAMIC_CLASS(wxDynamicSashReparentEvent
)
133 class wxDynamicSashWindowImpl
: public wxEvtHandler
136 wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
);
137 ~wxDynamicSashWindowImpl();
140 void AddChild(wxWindow
*window
);
141 void DrawSash(int x
, int y
) const;
142 void ConstrainChildren(int px
, int py
);
143 void Split(int x
, int y
);
144 void Unify(int panel
);
145 void Resize(int x
, int y
);
146 wxDynamicSashWindowImpl
*FindParent(DynamicSashRegion side
) const;
147 wxDynamicSashWindowImpl
*FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
148 wxDynamicSashWindowImpl
*sash_b
) const;
149 wxWindow
*FindFrame() const;
150 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
152 void OnSize(wxSizeEvent
&event
);
153 void OnPaint(wxPaintEvent
&event
);
154 void OnMouseMove(wxMouseEvent
&event
);
155 void OnLeave(wxMouseEvent
&event
);
156 void OnPress(wxMouseEvent
&event
);
157 void OnRelease(wxMouseEvent
&event
);
159 wxDynamicSashWindow
*m_window
;
160 wxDynamicSashWindowImpl
*m_add_child_target
;
162 /* This is the window we are responsible for managing. Either of
163 leaf or our children are in this window. For the top level
164 implementation object, this is the same as m_window.
165 Otherwise it is a window we've created an will destroy when we
167 wxWindow
*m_container
;
169 wxDynamicSashWindowImpl
*m_parent
;
170 wxDynamicSashWindowImpl
*m_top
;
171 wxDynamicSashWindowImpl
*m_child
[2];
173 class wxDynamicSashWindowLeaf
*m_leaf
;
175 /* If the implementation is split horizontally or vertically, m_split
176 is set to DSR_HORIZONTAL_TAB or DSR_VERTICAL_TAB, respectively.
177 Otherwise it is set to DSR_NONE. */
178 DynamicSashRegion m_split
;
180 /* These are used to keep track of a sash as it is being dragged, for
181 drawing the user interface correctly. */
182 DynamicSashRegion m_dragging
;
183 int m_drag_x
, m_drag_y
;
186 class wxDynamicSashWindowLeaf
: public wxEvtHandler
189 wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
);
190 ~wxDynamicSashWindowLeaf();
193 void AddChild(wxWindow
*window
);
194 DynamicSashRegion
GetRegion(int x
, int y
);
195 void ResizeChild(const wxSize
& size
);
196 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
198 void OnSize(wxSizeEvent
&event
);
199 void OnPaint(wxPaintEvent
&event
);
200 void OnScroll(wxScrollEvent
&event
);
201 void OnFocus(wxFocusEvent
&event
);
202 void OnMouseMove(wxMouseEvent
&event
);
203 void OnLeave(wxMouseEvent
&event
);
204 void OnPress(wxMouseEvent
&event
);
205 void OnRelease(wxMouseEvent
&event
);
206 void OnReparent(wxEvent
&event
);
208 wxDynamicSashWindowImpl
*m_impl
;
210 wxScrollBar
*m_vscroll
, *m_hscroll
;
212 /* m_child is the window provided to us by the application developer.
213 m_viewport is a window we've created, and it is the immediately
214 parent of m_child. We scroll m_child by moving it around within
216 wxWindow
*m_viewport
, *m_child
;
220 // ============================================================================
221 // wxDynamicSashWindow
222 // ============================================================================
224 wxDynamicSashWindow::wxDynamicSashWindow()
229 wxDynamicSashWindow::wxDynamicSashWindow(wxWindow
*parent
,
234 const wxString
& name
)
237 Create(parent
, id
, pos
, size
, style
, name
);
240 wxDynamicSashWindow::~wxDynamicSashWindow()
242 SetEventHandler(this);
246 bool wxDynamicSashWindow::Create(wxWindow
*parent
,
251 const wxString
& name
)
256 if (!wxWindow::Create(parent
, id
, pos
, size
, style
, name
))
259 m_impl
= new wxDynamicSashWindowImpl(this);
263 if (!m_impl
->Create())
273 void wxDynamicSashWindow::AddChild(wxWindowBase
*child
)
275 wxWindow::AddChild(child
);
277 m_impl
->AddChild(wxDynamicCast(child
, wxWindow
));
280 wxScrollBar
*wxDynamicSashWindow::GetHScrollBar(const wxWindow
*child
) const
282 return m_impl
->FindScrollBar(child
, 0);
285 wxScrollBar
*wxDynamicSashWindow::GetVScrollBar(const wxWindow
*child
) const
287 return m_impl
->FindScrollBar(child
, 1);
290 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashWindow
, wxWindow
)
293 // ============================================================================
294 // wxDynamicSashWindowImpl
295 // ============================================================================
297 wxDynamicSashWindowImpl::wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
)
300 m_add_child_target
= this;
308 m_dragging
= DSR_NONE
;
312 wxDynamicSashWindowImpl::~wxDynamicSashWindowImpl()
321 if (m_container
!= m_window
&& m_container
)
323 m_container
->SetEventHandler(m_container
);
324 m_container
->Destroy();
328 bool wxDynamicSashWindowImpl::Create()
331 m_container
= m_window
;
333 wxCursor
cursor(wxCURSOR_ARROW
);
334 m_container
->SetCursor(cursor
);
336 m_leaf
= new wxDynamicSashWindowLeaf(this);
340 if (!m_leaf
->Create())
347 m_container
->SetEventHandler(this);
349 Connect(wxEVT_SIZE
, wxSizeEventHandler(wxDynamicSashWindowImpl::OnSize
));
350 Connect(wxEVT_PAINT
, wxPaintEventHandler(wxDynamicSashWindowImpl::OnPaint
));
351 Connect(wxEVT_MOTION
,
352 wxMouseEventHandler(wxDynamicSashWindowImpl::OnMouseMove
));
353 Connect(wxEVT_ENTER_WINDOW
,
354 wxMouseEventHandler(wxDynamicSashWindowImpl::OnMouseMove
));
355 Connect(wxEVT_LEAVE_WINDOW
,
356 wxMouseEventHandler(wxDynamicSashWindowImpl::OnLeave
));
357 Connect(wxEVT_LEFT_DOWN
,
358 wxMouseEventHandler(wxDynamicSashWindowImpl::OnPress
));
359 Connect(wxEVT_LEFT_UP
,
360 wxMouseEventHandler(wxDynamicSashWindowImpl::OnRelease
));
365 void wxDynamicSashWindowImpl::AddChild(wxWindow
*window
)
367 if (m_add_child_target
&& m_add_child_target
->m_leaf
)
368 m_add_child_target
->m_leaf
->AddChild(window
);
371 void wxDynamicSashWindowImpl::DrawSash(int x
, int y
) const
376 dc
.StartDrawingOnTop(m_container
);
380 bdc
.SelectObject(bmp
);
381 bdc
.DrawRectangle(-1, -1, 10, 10);
382 for (i
= 0; i
< 8; i
++)
384 for (j
= 0; j
< 8; j
++)
393 dc
.SetLogicalFunction(wxXOR
);
395 if ((m_dragging
== DSR_CORNER
) &&
396 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0)
401 m_container
->ClientToScreen(&cx
, &cy
);
402 m_container
->ClientToScreen(&x
, &y
);
404 if (cx
< x
&& cy
< y
)
406 dc
.DrawRectangle(cx
- 2, cy
- 2, x
- cx
+ 4, 4);
407 dc
.DrawRectangle(x
- 2, cy
+ 2, 4, y
- cy
);
408 dc
.DrawRectangle(cx
- 2, cy
+ 2, 4, y
- cy
);
409 dc
.DrawRectangle(cx
+ 2, y
- 2, x
- cx
- 4, 4);
415 m_container
->GetClientSize(&body_w
, &body_h
);
426 if (m_dragging
== DSR_HORIZONTAL_TAB
)
431 m_container
->ClientToScreen(&x
, &y
);
434 w
= body_w
; h
= body_h
;
436 if (m_dragging
== DSR_HORIZONTAL_TAB
)
437 dc
.DrawRectangle(x
, y
- 2, w
, 4);
439 dc
.DrawRectangle(x
- 2, y
, 4, h
);
442 dc
.EndDrawingOnTop();
445 wxDynamicSashWindowImpl
*
446 wxDynamicSashWindowImpl::FindParent(DynamicSashRegion side
) const
448 if (m_parent
== NULL
)
451 if (m_parent
->m_split
== DSR_HORIZONTAL_TAB
)
453 if (side
== DSR_TOP_EDGE
&& m_parent
->m_child
[1] == this)
455 if (side
== DSR_BOTTOM_EDGE
&& m_parent
->m_child
[0] == this)
458 else if (m_parent
->m_split
== DSR_VERTICAL_TAB
)
460 if (side
== DSR_LEFT_EDGE
&& m_parent
->m_child
[1] == this)
462 if (side
== DSR_RIGHT_EDGE
&& m_parent
->m_child
[0] == this)
466 return m_parent
->FindParent(side
);
469 wxDynamicSashWindowImpl
*
470 wxDynamicSashWindowImpl::FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
471 wxDynamicSashWindowImpl
*sash_b
) const
474 win
= sash_a
->m_container
->GetParent();
475 while (win
&& !win
->IsTopLevel())
477 if (win
== sash_b
->m_container
)
480 win
= win
->GetParent();
487 wxWindow
*wxDynamicSashWindowImpl::FindFrame() const
491 win
= m_window
->GetParent();
492 while (win
&& !win
->IsTopLevel()
494 && ! wxIsKindOf(win
, wxMDIChildFrame
) // not top-level but still a frame
498 win
= win
->GetParent();
505 wxDynamicSashWindowImpl::FindScrollBar(const wxWindow
*child
, int vert
) const
507 if (m_child
[0] == NULL
&& m_leaf
== NULL
)
512 return m_leaf
->FindScrollBar(child
, vert
);
515 wxScrollBar
*ret
= m_child
[0]->FindScrollBar(child
, vert
);
517 ret
= m_child
[1]->FindScrollBar(child
, vert
);
522 void wxDynamicSashWindowImpl::ConstrainChildren(int px
, int py
)
524 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
525 layout
->left
.SameAs(m_container
, wxLeft
);
526 layout
->top
.SameAs(m_container
, wxTop
);
527 if (m_split
== DSR_HORIZONTAL_TAB
)
529 layout
->right
.SameAs(m_container
, wxRight
);
530 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
534 layout
->bottom
.SameAs(m_container
, wxBottom
);
535 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
537 m_child
[0]->m_container
->SetConstraints(layout
);
539 layout
= new wxLayoutConstraints();
540 layout
->right
.SameAs(m_container
, wxRight
);
541 layout
->bottom
.SameAs(m_container
, wxBottom
);
542 if (m_split
== DSR_HORIZONTAL_TAB
)
544 layout
->top
.Below(m_child
[0]->m_container
, 1);
545 layout
->left
.SameAs(m_container
, wxLeft
);
549 layout
->left
.RightOf(m_child
[0]->m_container
, 1);
550 layout
->top
.SameAs(m_container
, wxTop
);
552 m_child
[1]->m_container
->SetConstraints(layout
);
555 void wxDynamicSashWindowImpl::Unify(int panel
)
557 int other
= panel
== 0 ? 1 : 0;
559 if (m_child
[panel
]->m_leaf
)
561 wxDynamicSashWindowImpl
*child
[2];
563 child
[0] = m_child
[0];
564 child
[1] = m_child
[1];
566 m_child
[0] = m_child
[1] = NULL
;
568 m_leaf
= new wxDynamicSashWindowLeaf(this);
570 m_leaf
->m_child
= child
[panel
]->m_leaf
->m_child
;
572 m_leaf
->m_vscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_vscroll
->GetThumbPosition(),
573 child
[panel
]->m_leaf
->m_vscroll
->GetThumbSize(),
574 child
[panel
]->m_leaf
->m_vscroll
->GetRange(),
575 child
[panel
]->m_leaf
->m_vscroll
->GetPageSize());
576 m_leaf
->m_hscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_hscroll
->GetThumbPosition(),
577 child
[panel
]->m_leaf
->m_hscroll
->GetThumbSize(),
578 child
[panel
]->m_leaf
->m_hscroll
->GetRange(),
579 child
[panel
]->m_leaf
->m_hscroll
->GetPageSize());
580 m_add_child_target
= NULL
;
581 wxDynamicSashReparentEvent
event(m_leaf
);
582 m_leaf
->ProcessEvent(event
);
589 wxDynamicSashUnifyEvent
unify(m_leaf
->m_child
);
590 m_leaf
->m_child
->ProcessEvent(unify
);
594 m_split
= m_child
[panel
]->m_split
;
596 delete m_child
[other
];
598 wxDynamicSashWindowImpl
*child_panel
= m_child
[panel
];
599 m_child
[0] = child_panel
->m_child
[0];
600 m_child
[1] = child_panel
->m_child
[1];
602 m_child
[0]->m_parent
= this;
603 m_child
[1]->m_parent
= this;
605 m_add_child_target
= NULL
;
606 m_child
[0]->m_container
->Reparent(m_container
);
607 m_child
[1]->m_container
->Reparent(m_container
);
609 child_panel
->m_child
[0] = child_panel
->m_child
[1] = NULL
;
612 wxSize size
= m_container
->GetSize();
613 wxSize child_size
= m_child
[0]->m_container
->GetSize();
615 ConstrainChildren(child_size
.GetWidth() * 100 / size
.GetWidth(),
616 child_size
.GetHeight() * 100 / size
.GetHeight());
618 m_container
->Layout();
622 void wxDynamicSashWindowImpl::Split(int px
, int py
)
625 m_add_child_target
= NULL
;
627 m_child
[0] = new wxDynamicSashWindowImpl(m_window
);
628 m_child
[0]->m_container
= new wxWindow(m_container
, wxID_ANY
);
629 m_child
[0]->m_parent
= this;
630 m_child
[0]->m_top
= m_top
;
631 m_child
[0]->Create();
634 m_leaf
->m_child
->Reparent(m_container
);
635 m_child
[0]->AddChild(m_leaf
->m_child
);
638 m_child
[1] = new wxDynamicSashWindowImpl(m_window
);
639 m_child
[1]->m_container
= new wxWindow(m_container
, wxID_ANY
);
640 m_child
[1]->m_parent
= this;
641 m_child
[1]->m_top
= m_top
;
642 m_child
[1]->Create();
644 m_split
= m_dragging
;
645 ConstrainChildren(px
, py
);
647 m_top
->m_add_child_target
= m_child
[1];
648 wxDynamicSashSplitEvent
split(m_child
[0]->m_leaf
->m_child
);
649 m_child
[0]->m_leaf
->m_child
->ProcessEvent(split
);
651 m_child
[0]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
652 m_leaf
->m_vscroll
->GetThumbSize(),
653 m_leaf
->m_vscroll
->GetRange(),
654 m_leaf
->m_vscroll
->GetPageSize());
655 m_child
[0]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
656 m_leaf
->m_hscroll
->GetThumbSize(),
657 m_leaf
->m_hscroll
->GetRange(),
658 m_leaf
->m_hscroll
->GetPageSize());
659 m_child
[1]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
660 m_leaf
->m_vscroll
->GetThumbSize(),
661 m_leaf
->m_vscroll
->GetRange(),
662 m_leaf
->m_vscroll
->GetPageSize());
663 m_child
[1]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
664 m_leaf
->m_hscroll
->GetThumbSize(),
665 m_leaf
->m_hscroll
->GetRange(),
666 m_leaf
->m_hscroll
->GetPageSize());
670 m_container
->Layout();
674 /* This code is called when you finish resizing a view by dragging the
675 corner tab, but I think this implementation is lousy and will surprise
676 the user more often than it will do what they are trying to do. What
677 I really need to be doing here is do a rewrite such that *no* sashes
678 move except the ones immediately to the bottom and right of this window,
679 and handle the case where you resize a window larger than it's neighbors
680 by destroying the neighbors.
682 But this will do for now. */
683 void wxDynamicSashWindowImpl::Resize(int x
, int y
)
685 wxDynamicSashWindowImpl
*h_parent
= FindParent(DSR_BOTTOM_EDGE
);
686 wxDynamicSashWindowImpl
*v_parent
= FindParent(DSR_RIGHT_EDGE
);
689 wxWindow
*frame
= FindFrame();
698 m_container
->ClientToScreen(NULL
, &y
);
699 h_parent
->m_container
->ScreenToClient(NULL
, &y
);
701 int py
= (int)((y
* 100) / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
705 wxDynamicSashWindowImpl
*ho_parent
= FindParent(DSR_TOP_EDGE
);
709 if (FindUpperParent(h_parent
, ho_parent
) == ho_parent
)
715 py
= (int)((ho_parent
->m_child
[0]->m_container
->GetSize().GetHeight() * 100)
716 / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
717 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
718 h_parent
->m_container
, wxHeight
, py
);
720 h_parent
= ho_parent
;
735 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
736 h_parent
->m_container
, wxHeight
, py
);
737 h_parent
->m_container
->Layout();
743 h_parent
= FindParent(DSR_TOP_EDGE
);
747 int py
= (int)((y
* 100) /
748 (h_parent
->m_container
->GetSize().GetHeight() +
749 y
- m_container
->GetSize().GetHeight()) + 0.5);
761 wxSize size
= frame
->GetSize();
762 frame
->SetSize(size
.GetWidth(), size
.GetHeight() + y
- m_container
->GetSize().GetHeight());
768 m_container
->ClientToScreen(&x
, NULL
);
769 v_parent
->m_container
->ScreenToClient(&x
, NULL
);
771 int px
= (int)((x
* 100) / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
775 wxDynamicSashWindowImpl
*vo_parent
= FindParent(DSR_LEFT_EDGE
);
779 if (FindUpperParent(v_parent
, vo_parent
) == vo_parent
)
785 px
= (int)((vo_parent
->m_child
[0]->m_container
->GetSize().GetWidth() * 100)
786 / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
787 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
788 v_parent
->m_container
, wxWidth
, px
);
790 v_parent
= vo_parent
;
805 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
806 v_parent
->m_container
, wxWidth
, px
);
807 v_parent
->m_container
->Layout();
813 v_parent
= FindParent(DSR_LEFT_EDGE
);
817 int px
= (int)((x
* 100) /
818 (v_parent
->m_container
->GetSize().GetWidth() +
819 x
- m_container
->GetSize().GetWidth()) + 0.5);
831 wxSize size
= frame
->GetSize();
832 frame
->SetSize(size
.GetWidth() + x
- m_container
->GetSize().GetWidth(), size
.GetHeight());
836 if (h_unify
!= -1 && v_unify
!= -1)
838 wxDynamicSashWindowImpl
*parent
= FindUpperParent(h_parent
, v_parent
);
840 if (parent
== h_parent
)
842 h_parent
->Unify(h_unify
);
846 v_parent
->Unify(v_unify
);
849 else if (h_unify
!= -1)
851 h_parent
->Unify(h_unify
);
853 else if (v_unify
!= -1)
855 v_parent
->Unify(v_unify
);
860 void wxDynamicSashWindowImpl::OnSize(wxSizeEvent
&event
)
862 m_container
->Layout();
865 m_leaf
->OnSize(event
);
868 void wxDynamicSashWindowImpl::OnPaint(wxPaintEvent
&event
)
871 m_leaf
->OnPaint(event
);
874 wxPaintDC
dc(m_container
);
875 dc
.SetBackground(wxBrush(m_container
->GetBackgroundColour(), wxSOLID
));
880 void wxDynamicSashWindowImpl::OnMouseMove(wxMouseEvent
&event
)
884 DrawSash(m_drag_x
, m_drag_y
);
885 m_drag_x
= event
.m_x
; m_drag_y
= event
.m_y
;
886 DrawSash(m_drag_x
, m_drag_y
);
890 m_leaf
->OnMouseMove(event
);
894 void wxDynamicSashWindowImpl::OnLeave(wxMouseEvent
&event
)
897 m_leaf
->OnLeave(event
);
900 void wxDynamicSashWindowImpl::OnPress(wxMouseEvent
&event
)
904 m_leaf
->OnPress(event
);
908 m_dragging
= m_split
;
909 m_drag_x
= event
.m_x
;
910 m_drag_y
= event
.m_y
;
911 DrawSash(m_drag_x
, m_drag_y
);
912 m_container
->CaptureMouse();
916 void wxDynamicSashWindowImpl::OnRelease(wxMouseEvent
&event
)
918 if ((m_dragging
== DSR_CORNER
) &&
919 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0)
921 DrawSash(m_drag_x
, m_drag_y
);
922 m_container
->ReleaseMouse();
924 Resize(event
.m_x
, event
.m_y
);
926 m_dragging
= DSR_NONE
;
930 DrawSash(m_drag_x
, m_drag_y
);
931 m_container
->ReleaseMouse();
933 wxSize size
= m_container
->GetSize();
934 int px
= (int)((event
.m_x
* 100) / size
.GetWidth() + 0.5);
935 int py
= (int)((event
.m_y
* 100) / size
.GetHeight() + 0.5);
937 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
>= 10 && py
<= 90)
938 || (m_dragging
== DSR_VERTICAL_TAB
&& px
>= 10 && px
<= 90))
940 if (m_child
[0] == NULL
)
946 /* It would be nice if moving *this* sash didn't implicitly move
947 the sashes of our children (if any). But this will do. */
948 wxLayoutConstraints
*layout
= m_child
[0]->m_container
->GetConstraints();
949 if (m_split
== DSR_HORIZONTAL_TAB
)
951 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
955 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
957 m_container
->Layout();
962 if (m_child
[0] != NULL
)
964 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
<= 10)
965 || (m_dragging
== DSR_VERTICAL_TAB
&& px
<= 10))
977 if (m_split
== DSR_HORIZONTAL_TAB
)
978 cursor
= wxCursor(wxCURSOR_SIZENS
);
979 else if (m_split
== DSR_VERTICAL_TAB
)
980 cursor
= wxCursor(wxCURSOR_SIZEWE
);
982 cursor
= wxCursor(wxCURSOR_ARROW
);
984 m_container
->SetCursor(cursor
);
986 m_dragging
= DSR_NONE
;
990 m_leaf
->OnRelease(event
);
994 // ============================================================================
995 // wxDynamicSashWindowLeaf
996 // ============================================================================
998 wxDynamicSashWindowLeaf::wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
)
1002 m_hscroll
= m_vscroll
= NULL
;
1006 wxDynamicSashWindowLeaf::~wxDynamicSashWindowLeaf()
1008 m_hscroll
->SetEventHandler(m_hscroll
);
1009 m_vscroll
->SetEventHandler(m_vscroll
);
1010 m_viewport
->SetEventHandler(m_viewport
);
1012 m_hscroll
->Destroy();
1013 m_vscroll
->Destroy();
1014 m_viewport
->Destroy();
1017 bool wxDynamicSashWindowLeaf::Create()
1021 m_hscroll
= new wxScrollBar();
1022 m_vscroll
= new wxScrollBar();
1023 m_viewport
= new wxWindow();
1025 if (!m_hscroll
|| !m_vscroll
|| !m_viewport
)
1030 wxDynamicSashWindowImpl
*add_child_target
= m_impl
->m_add_child_target
;
1031 m_impl
->m_add_child_target
= NULL
;
1032 success
= m_hscroll
->Create(m_impl
->m_container
, wxID_ANY
, wxDefaultPosition
, wxDefaultSize
,
1034 success
= success
&& m_vscroll
->Create(m_impl
->m_container
, wxID_ANY
, wxDefaultPosition
, wxDefaultSize
,
1036 success
= success
&& m_viewport
->Create(m_impl
->m_container
, wxID_ANY
);
1037 m_impl
->m_add_child_target
= add_child_target
;
1039 wxCursor
cursor(wxCURSOR_ARROW
);
1040 m_hscroll
->SetCursor(cursor
);
1041 m_vscroll
->SetCursor(cursor
);
1042 m_viewport
->SetCursor(cursor
);
1044 m_viewport
->SetEventHandler(this);
1045 Connect(wxID_ANY
, wxEVT_DYNAMIC_SASH_REPARENT
, (wxObjectEventFunction
)&wxDynamicSashWindowLeaf::OnReparent
);
1047 if (m_impl
->m_window
->GetWindowStyle() & wxDS_MANAGE_SCROLLBARS
)
1049 m_hscroll
->SetEventHandler(this);
1050 m_vscroll
->SetEventHandler(this);
1052 Connect(wxID_ANY
, wxEVT_SET_FOCUS
, (wxObjectEventFunction
)
1054 (wxFocusEventFunction
)&wxDynamicSashWindowLeaf::OnFocus
);
1055 Connect(wxID_ANY
, wxEVT_SCROLL_TOP
, (wxObjectEventFunction
)
1057 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
1058 Connect(wxID_ANY
, wxEVT_SCROLL_BOTTOM
, (wxObjectEventFunction
)
1060 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
1061 Connect(wxID_ANY
, wxEVT_SCROLL_LINEUP
, (wxObjectEventFunction
)
1063 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
1064 Connect(wxID_ANY
, wxEVT_SCROLL_LINEDOWN
, (wxObjectEventFunction
)
1066 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
1067 Connect(wxID_ANY
, wxEVT_SCROLL_PAGEUP
, (wxObjectEventFunction
)
1069 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
1070 Connect(wxID_ANY
, wxEVT_SCROLL_PAGEDOWN
, (wxObjectEventFunction
)
1072 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
1073 Connect(wxID_ANY
, wxEVT_SCROLL_THUMBTRACK
, (wxObjectEventFunction
)
1075 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
1076 Connect(wxID_ANY
, wxEVT_SCROLL_THUMBRELEASE
, (wxObjectEventFunction
)
1078 (wxScrollEventFunction
)&wxDynamicSashWindowLeaf::OnScroll
);
1081 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
1084 wxSize size
= m_hscroll
->GetBestSize();
1086 size
= m_hscroll
->GetSize();
1089 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 10);
1090 layout
->right
.LeftOf(m_vscroll
);
1091 layout
->bottom
.SameAs(m_impl
->m_container
, wxBottom
, 3);
1092 layout
->height
.Absolute(size
.GetHeight());
1093 m_hscroll
->SetConstraints(layout
);
1095 layout
= new wxLayoutConstraints();
1098 size
= size
= m_vscroll
->GetBestSize();
1100 size
= m_vscroll
->GetSize();
1103 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 10);
1104 layout
->bottom
.Above(m_hscroll
);
1105 layout
->right
.SameAs(m_impl
->m_container
, wxRight
, 3);
1106 layout
->width
.Absolute(size
.GetWidth());
1107 m_vscroll
->SetConstraints(layout
);
1109 layout
= new wxLayoutConstraints();
1112 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 3);
1113 layout
->right
.LeftOf(m_vscroll
);
1114 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 3);
1115 layout
->bottom
.Above(m_hscroll
);
1116 m_viewport
->SetConstraints(layout
);
1118 m_impl
->m_container
->Layout();
1123 void wxDynamicSashWindowLeaf::AddChild(wxWindow
*window
)
1130 wxDynamicSashReparentEvent
event(this);
1131 AddPendingEvent(event
);
1134 DynamicSashRegion
wxDynamicSashWindowLeaf::GetRegion(int x
, int y
)
1136 wxSize size
= m_impl
->m_container
->GetSize();
1137 int w
= size
.GetWidth();
1138 int h
= size
.GetHeight();
1139 size
= m_hscroll
->GetSize();
1140 int sh
= size
.GetHeight();
1141 size
= m_vscroll
->GetSize();
1142 int sw
= size
.GetWidth();
1144 if (x
>= w
- sw
- 3 && x
< w
&& y
>= h
- sh
- 3 && y
< h
)
1146 if (x
>= 3 && x
< 10 && y
>= h
- sh
- 3 && y
< h
- 2)
1147 return DSR_VERTICAL_TAB
;
1148 if (x
>= w
- sw
- 3 && x
< w
- 2 && y
>= 3 && y
< 10)
1149 return DSR_HORIZONTAL_TAB
;
1151 return DSR_LEFT_EDGE
;
1153 return DSR_TOP_EDGE
;
1155 return DSR_RIGHT_EDGE
;
1157 return DSR_BOTTOM_EDGE
;
1162 void wxDynamicSashWindowLeaf::ResizeChild(const wxSize
& size
)
1166 if (m_impl
->m_window
->HasFlag(wxDS_MANAGE_SCROLLBARS
))
1168 wxSize best_size
= m_child
->GetBestSize();
1169 if (best_size
.GetWidth() < size
.GetWidth())
1170 best_size
.SetWidth(size
.GetWidth());
1171 if (best_size
.GetHeight() < size
.GetHeight())
1172 best_size
.SetHeight(size
.GetHeight());
1173 m_child
->SetSize(best_size
);
1175 int hpos
= m_hscroll
->GetThumbPosition();
1176 int vpos
= m_vscroll
->GetThumbPosition();
1182 if (hpos
> best_size
.GetWidth() - size
.GetWidth())
1183 hpos
= best_size
.GetWidth() - size
.GetWidth();
1184 if (vpos
> best_size
.GetHeight() - size
.GetHeight())
1185 vpos
= best_size
.GetHeight() - size
.GetHeight();
1187 m_hscroll
->SetScrollbar(hpos
, size
.GetWidth(),
1188 best_size
.GetWidth(), size
.GetWidth());
1189 m_vscroll
->SetScrollbar(vpos
, size
.GetHeight(),
1190 best_size
.GetHeight(), size
.GetHeight());
1192 // Umm, the scrollbars are doing something insane under GTK+ and subtracting
1193 // one from the position I pass in. This works around that.
1194 m_hscroll
->SetThumbPosition(hpos
+ hpos
- m_hscroll
->GetThumbPosition());
1195 m_vscroll
->SetThumbPosition(vpos
+ vpos
- m_vscroll
->GetThumbPosition());
1197 wxPoint pos
= m_child
->GetPosition();
1198 m_viewport
->ScrollWindow(-hpos
- pos
.x
, -vpos
- pos
.y
);
1200 else // !wxDS_MANAGE_SCROLLBARS
1202 m_child
->SetSize(size
);
1208 wxDynamicSashWindowLeaf::FindScrollBar(const wxWindow
*child
, int vert
) const
1210 if (m_child
== child
)
1212 return vert
? m_vscroll
: m_hscroll
;
1218 void wxDynamicSashWindowLeaf::OnSize(wxSizeEvent
&WXUNUSED(event
))
1220 m_impl
->m_container
->Refresh();
1221 ResizeChild(m_viewport
->GetSize());
1224 void wxDynamicSashWindowLeaf::OnPaint(wxPaintEvent
&WXUNUSED(event
))
1226 wxPaintDC
dc(m_impl
->m_container
);
1227 dc
.SetBackground(wxBrush(m_impl
->m_container
->GetBackgroundColour(), wxSOLID
));
1230 wxPen
highlight(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT
), 1, wxSOLID
);
1231 wxPen
shadow(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW
), 1, wxSOLID
);
1232 wxPen
black(*wxBLACK
, 1, wxSOLID
);
1234 wxSize size
= m_impl
->m_container
->GetSize();
1235 int w
= size
.GetWidth();
1236 int h
= size
.GetHeight();
1237 size
= m_hscroll
->GetSize();
1238 int sh
= size
.GetHeight();
1239 size
= m_vscroll
->GetSize();
1240 int sw
= size
.GetWidth();
1243 dc
.DrawLine(1, 1, 1, h
- 2);
1244 dc
.DrawLine(1, 1, w
- 2, 1);
1246 dc
.DrawLine(2, 2, 2, h
- 3);
1247 dc
.DrawLine(2, 2, w
- 3, 2);
1248 dc
.SetPen(highlight
);
1249 dc
.DrawLine(w
- 2, 2, w
- 2, h
- sh
- 2);
1250 dc
.DrawLine(w
- 2, h
- sh
- 2, w
- sw
- 2, h
- sh
- 2);
1251 dc
.DrawLine(w
- sw
- 2, h
- sh
- 2, w
- sw
- 2, h
- 2);
1252 dc
.DrawLine(w
- sw
- 2, h
- 2, 2, h
- 2);
1254 dc
.SetPen(highlight
);
1255 dc
.DrawLine(w
- sw
- 2, 8, w
- sw
- 2, 4);
1256 dc
.DrawLine(w
- sw
- 2, 4, w
- 5, 4);
1258 dc
.DrawLine(w
- 5, 4, w
- 5, 8);
1259 dc
.DrawLine(w
- 5, 8, w
- sw
- 2, 8);
1261 dc
.DrawLine(w
- 4, 3, w
- 4, 9);
1262 dc
.DrawLine(w
- 4, 9, w
- sw
- 3, 9);
1264 dc
.SetPen(highlight
);
1265 dc
.DrawLine(4, h
- 5, 4, h
- sh
- 2);
1266 dc
.DrawLine(4, h
- sh
- 2, 8, h
- sh
- 2);
1268 dc
.DrawLine(8, h
- sh
- 2, 8, h
- 5);
1269 dc
.DrawLine(8, h
- 5, 4, h
- 5);
1271 dc
.DrawLine(9, h
- sh
- 3, 9, h
- 4);
1272 dc
.DrawLine(9, h
- 4, 3, h
- 4);
1274 int cy
= (h
- sh
+ h
- 6) / 2 + 1;
1275 int cx
= (w
- sw
+ w
- 6) / 2 + 1;
1284 for (y
= sy
; y
< h
- 2; y
+= 4)
1286 for (x
= sx
; x
< w
- 2; x
+= 4)
1288 if (x
- cx
>= -(y
- cy
))
1290 dc
.SetPen(highlight
);
1293 dc
.DrawPoint(x
+ 1, y
+ 1);
1299 void wxDynamicSashWindowLeaf::OnScroll(wxScrollEvent
&WXUNUSED(event
))
1301 int nx
= -m_hscroll
->GetThumbPosition();
1302 int ny
= -m_vscroll
->GetThumbPosition();
1306 wxPoint pos
= m_child
->GetPosition();
1308 m_viewport
->ScrollWindow(nx
- pos
.x
, ny
- pos
.y
);
1312 void wxDynamicSashWindowLeaf::OnFocus(wxFocusEvent
&event
)
1314 if ( event
.GetEventObject() == m_hscroll
||
1315 event
.GetEventObject() == m_vscroll
)
1317 m_child
->SetFocus();
1322 void wxDynamicSashWindowLeaf::OnMouseMove(wxMouseEvent
&event
)
1324 if (m_impl
->m_dragging
)
1327 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1329 wxCursor
cursor(wxCURSOR_ARROW
);
1330 if (region
== DSR_HORIZONTAL_TAB
)
1332 cursor
= wxCursor(wxCURSOR_SIZENS
);
1334 else if (region
== DSR_VERTICAL_TAB
)
1336 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1338 else if ((region
== DSR_CORNER
) &&
1339 (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0)
1341 cursor
= wxCursor(wxCURSOR_SIZENWSE
);
1343 else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1344 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
)
1346 if (m_impl
->FindParent(region
))
1348 if (region
== DSR_LEFT_EDGE
|| region
== DSR_RIGHT_EDGE
)
1350 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1354 cursor
= wxCursor(wxCURSOR_SIZENS
);
1359 m_impl
->m_container
->SetCursor(cursor
);
1362 void wxDynamicSashWindowLeaf::OnLeave(wxMouseEvent
&WXUNUSED(event
))
1364 wxCursor
cursor(wxCURSOR_ARROW
);
1365 m_impl
->m_container
->SetCursor(cursor
);
1369 void wxDynamicSashWindowLeaf::OnPress(wxMouseEvent
&event
)
1371 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1373 if ((region
== DSR_CORNER
) && (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) == 0)
1376 if (region
== DSR_HORIZONTAL_TAB
|| region
== DSR_VERTICAL_TAB
|| region
== DSR_CORNER
)
1378 m_impl
->m_dragging
= region
;
1379 m_impl
->m_drag_x
= event
.m_x
;
1380 m_impl
->m_drag_y
= event
.m_y
;
1381 m_impl
->DrawSash(event
.m_x
, event
.m_y
);
1382 m_impl
->m_container
->CaptureMouse();
1384 else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1385 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
)
1387 wxDynamicSashWindowImpl
*parent
= m_impl
->FindParent(region
);
1394 m_impl
->m_container
->ClientToScreen(&x
, &y
);
1395 parent
->m_container
->ScreenToClient(&x
, &y
);
1397 parent
->m_dragging
= parent
->m_split
;
1398 parent
->m_drag_x
= x
;
1399 parent
->m_drag_y
= y
;
1400 parent
->DrawSash(x
, y
);
1401 parent
->m_container
->CaptureMouse();
1406 void wxDynamicSashWindowLeaf::OnRelease(wxMouseEvent
&WXUNUSED(event
))
1410 void wxDynamicSashWindowLeaf::OnReparent(wxEvent
&WXUNUSED(event
))
1414 m_child
->Reparent(m_viewport
);
1417 ResizeChild(m_viewport
->GetSize());
1420 // ============================================================================
1422 // ============================================================================
1424 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent()
1426 m_eventObject
= NULL
;
1427 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1430 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent(wxObject
*object
)
1432 m_eventObject
= object
;
1433 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1436 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashSplitEvent
, wxCommandEvent
)
1438 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent()
1440 m_eventObject
= NULL
;
1441 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1444 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent(wxObject
*object
)
1446 m_eventObject
= object
;
1447 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1450 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashUnifyEvent
, wxCommandEvent
)
1453 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent()
1455 m_eventObject
= NULL
;
1456 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1459 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(wxObject
*object
)
1461 m_eventObject
= object
;
1462 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1465 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
)
1470 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashReparentEvent
, wxEvent
)