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 // For compilers that support precompilation, includes "wx/wx.h".
14 #include "wx/wxprec.h"
20 // for all others, include the necessary headers (this file is usually all you
21 // need because it includes almost all "standard" wxWidgets 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 const wxEventType wxEVT_DYNAMIC_SASH_SPLIT
= wxNewEventType();
92 const wxEventType wxEVT_DYNAMIC_SASH_UNIFY
= wxNewEventType();
93 const wxEventType wxEVT_DYNAMIC_SASH_REPARENT
= wxNewEventType();
95 enum DynamicSashRegion
109 wxDynamicSashReparentEvent is generated by the AddChild() method of
110 wxDynamicSashWindow when it wants a Leaf to reparent a user view window
111 to its viewport at some time in the future. We can't reparent the window
112 immediately, because switching parents in AddChild() confuses the wxWindow
113 class. Instead, we queue up this event, and the window is actually
114 reparented the next time we process events in the idle loop.
116 class wxDynamicSashReparentEvent
: public wxEvent
119 wxDynamicSashReparentEvent();
120 wxDynamicSashReparentEvent(wxObject
*object
);
121 wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
);
123 virtual wxEvent
* Clone() const { return new wxDynamicSashReparentEvent(*this); }
125 DECLARE_DYNAMIC_CLASS(wxDynamicSashReparentEvent
)
129 class wxDynamicSashWindowImpl
: public wxEvtHandler
132 wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
);
133 ~wxDynamicSashWindowImpl();
136 void AddChild(wxWindow
*window
);
137 void DrawSash(int x
, int y
) const;
138 void ConstrainChildren(int px
, int py
);
139 void Split(int x
, int y
);
140 void Unify(int panel
);
141 void Resize(int x
, int y
);
142 wxDynamicSashWindowImpl
*FindParent(DynamicSashRegion side
) const;
143 wxDynamicSashWindowImpl
*FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
144 wxDynamicSashWindowImpl
*sash_b
) const;
145 wxWindow
*FindFrame() const;
146 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
148 void OnSize(wxSizeEvent
&event
);
149 void OnPaint(wxPaintEvent
&event
);
150 void OnMouseMove(wxMouseEvent
&event
);
151 void OnLeave(wxMouseEvent
&event
);
152 void OnPress(wxMouseEvent
&event
);
153 void OnRelease(wxMouseEvent
&event
);
155 wxDynamicSashWindow
*m_window
;
156 wxDynamicSashWindowImpl
*m_add_child_target
;
158 /* This is the window we are responsible for managing. Either of
159 leaf or our children are in this window. For the top level
160 implementation object, this is the same as m_window.
161 Otherwise it is a window we've created an will destroy when we
163 wxWindow
*m_container
;
165 wxDynamicSashWindowImpl
*m_parent
;
166 wxDynamicSashWindowImpl
*m_top
;
167 wxDynamicSashWindowImpl
*m_child
[2];
169 class wxDynamicSashWindowLeaf
*m_leaf
;
171 /* If the implementation is split horizontally or vertically, m_split
172 is set to DSR_HORIZONTAL_TAB or DSR_VERTICAL_TAB, respectively.
173 Otherwise it is set to DSR_NONE. */
174 DynamicSashRegion m_split
;
176 /* These are used to keep track of a sash as it is being dragged, for
177 drawing the user interface correctly. */
178 DynamicSashRegion m_dragging
;
179 int m_drag_x
, m_drag_y
;
182 class wxDynamicSashWindowLeaf
: public wxEvtHandler
185 wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
);
186 ~wxDynamicSashWindowLeaf();
189 void AddChild(wxWindow
*window
);
190 DynamicSashRegion
GetRegion(int x
, int y
);
191 void ResizeChild(const wxSize
& size
);
192 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
194 void OnSize(wxSizeEvent
&event
);
195 void OnViewSize(wxSizeEvent
&event
);
196 void OnPaint(wxPaintEvent
&event
);
197 void OnScroll(wxScrollEvent
&event
);
198 void OnFocus(wxFocusEvent
&event
);
199 void OnMouseMove(wxMouseEvent
&event
);
200 void OnLeave(wxMouseEvent
&event
);
201 void OnPress(wxMouseEvent
&event
);
202 void OnRelease(wxMouseEvent
&event
);
203 void OnReparent(wxEvent
&event
);
205 wxDynamicSashWindowImpl
*m_impl
;
207 wxScrollBar
*m_vscroll
,
210 /* m_child is the window provided to us by the application developer.
211 m_viewport is a window we've created, and it is the immediately
212 parent of m_child. We scroll m_child by moving it around within
214 wxWindow
*m_viewport
,
219 // ============================================================================
220 // wxDynamicSashWindow
221 // ============================================================================
223 wxDynamicSashWindow::wxDynamicSashWindow()
228 wxDynamicSashWindow::wxDynamicSashWindow(wxWindow
*parent
,
233 const wxString
& name
)
236 Create(parent
, id
, pos
, size
, style
, name
);
239 wxDynamicSashWindow::~wxDynamicSashWindow()
241 SetEventHandler(this);
245 bool wxDynamicSashWindow::Create(wxWindow
*parent
,
250 const wxString
& name
)
255 if (!wxWindow::Create(parent
, id
, pos
, size
, style
, name
))
258 m_impl
= new wxDynamicSashWindowImpl(this);
262 if (!m_impl
->Create())
272 void wxDynamicSashWindow::AddChild(wxWindowBase
*child
)
274 wxWindow::AddChild(child
);
276 m_impl
->AddChild(wxDynamicCast(child
, wxWindow
));
279 wxScrollBar
*wxDynamicSashWindow::GetHScrollBar(const wxWindow
*child
) const
281 return m_impl
->FindScrollBar(child
, 0);
284 wxScrollBar
*wxDynamicSashWindow::GetVScrollBar(const wxWindow
*child
) const
286 return m_impl
->FindScrollBar(child
, 1);
289 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashWindow
, wxWindow
)
292 // ============================================================================
293 // wxDynamicSashWindowImpl
294 // ============================================================================
296 wxDynamicSashWindowImpl::wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
)
299 m_add_child_target
= this;
307 m_dragging
= DSR_NONE
;
311 wxDynamicSashWindowImpl::~wxDynamicSashWindowImpl()
320 if (m_container
!= m_window
&& m_container
)
322 m_container
->SetEventHandler(m_container
);
323 m_container
->Destroy();
327 bool wxDynamicSashWindowImpl::Create()
330 m_container
= m_window
;
332 wxCursor
cursor(wxCURSOR_ARROW
);
333 m_container
->SetCursor(cursor
);
335 m_leaf
= new wxDynamicSashWindowLeaf(this);
339 if (!m_leaf
->Create())
346 m_container
->SetEventHandler(this);
348 Connect(wxEVT_SIZE
, wxSizeEventHandler(wxDynamicSashWindowImpl::OnSize
));
349 Connect(wxEVT_PAINT
, wxPaintEventHandler(wxDynamicSashWindowImpl::OnPaint
));
350 Connect(wxEVT_MOTION
,
351 wxMouseEventHandler(wxDynamicSashWindowImpl::OnMouseMove
));
352 Connect(wxEVT_ENTER_WINDOW
,
353 wxMouseEventHandler(wxDynamicSashWindowImpl::OnMouseMove
));
354 Connect(wxEVT_LEAVE_WINDOW
,
355 wxMouseEventHandler(wxDynamicSashWindowImpl::OnLeave
));
356 Connect(wxEVT_LEFT_DOWN
,
357 wxMouseEventHandler(wxDynamicSashWindowImpl::OnPress
));
358 Connect(wxEVT_LEFT_UP
,
359 wxMouseEventHandler(wxDynamicSashWindowImpl::OnRelease
));
364 void wxDynamicSashWindowImpl::AddChild(wxWindow
*window
)
366 if (m_add_child_target
&& m_add_child_target
->m_leaf
)
367 m_add_child_target
->m_leaf
->AddChild(window
);
370 void wxDynamicSashWindowImpl::DrawSash(int x
, int y
) const
375 dc
.StartDrawingOnTop(m_container
);
379 bdc
.SelectObject(bmp
);
380 bdc
.DrawRectangle(-1, -1, 10, 10);
381 for (i
= 0; i
< 8; i
++)
383 for (j
= 0; j
< 8; j
++)
392 dc
.SetLogicalFunction(wxXOR
);
394 if ((m_dragging
== DSR_CORNER
) &&
395 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0)
400 m_container
->ClientToScreen(&cx
, &cy
);
401 m_container
->ClientToScreen(&x
, &y
);
403 if (cx
< x
&& cy
< y
)
405 dc
.DrawRectangle(cx
- 2, cy
- 2, x
- cx
+ 4, 4);
406 dc
.DrawRectangle(x
- 2, cy
+ 2, 4, y
- cy
);
407 dc
.DrawRectangle(cx
- 2, cy
+ 2, 4, y
- cy
);
408 dc
.DrawRectangle(cx
+ 2, y
- 2, x
- cx
- 4, 4);
414 m_container
->GetClientSize(&body_w
, &body_h
);
425 if (m_dragging
== DSR_HORIZONTAL_TAB
)
430 m_container
->ClientToScreen(&x
, &y
);
433 w
= body_w
; h
= body_h
;
435 if (m_dragging
== DSR_HORIZONTAL_TAB
)
436 dc
.DrawRectangle(x
, y
- 2, w
, 4);
438 dc
.DrawRectangle(x
- 2, y
, 4, h
);
441 dc
.EndDrawingOnTop();
444 wxDynamicSashWindowImpl
*
445 wxDynamicSashWindowImpl::FindParent(DynamicSashRegion side
) const
447 if (m_parent
== NULL
)
450 if (m_parent
->m_split
== DSR_HORIZONTAL_TAB
)
452 if (side
== DSR_TOP_EDGE
&& m_parent
->m_child
[1] == this)
454 if (side
== DSR_BOTTOM_EDGE
&& m_parent
->m_child
[0] == this)
457 else if (m_parent
->m_split
== DSR_VERTICAL_TAB
)
459 if (side
== DSR_LEFT_EDGE
&& m_parent
->m_child
[1] == this)
461 if (side
== DSR_RIGHT_EDGE
&& m_parent
->m_child
[0] == this)
465 return m_parent
->FindParent(side
);
468 wxDynamicSashWindowImpl
*
469 wxDynamicSashWindowImpl::FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
470 wxDynamicSashWindowImpl
*sash_b
) const
473 win
= sash_a
->m_container
->GetParent();
474 while (win
&& !win
->IsTopLevel())
476 if (win
== sash_b
->m_container
)
479 win
= win
->GetParent();
486 wxWindow
*wxDynamicSashWindowImpl::FindFrame() const
490 win
= m_window
->GetParent();
491 while (win
&& !win
->IsTopLevel()
493 && ! wxIsKindOf(win
, wxMDIChildFrame
) // not top-level but still a frame
497 win
= win
->GetParent();
504 wxDynamicSashWindowImpl::FindScrollBar(const wxWindow
*child
, int vert
) const
506 if (m_child
[0] == NULL
&& m_leaf
== NULL
)
511 return m_leaf
->FindScrollBar(child
, vert
);
514 wxScrollBar
*ret
= m_child
[0]->FindScrollBar(child
, vert
);
516 ret
= m_child
[1]->FindScrollBar(child
, vert
);
521 void wxDynamicSashWindowImpl::ConstrainChildren(int px
, int py
)
523 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
524 layout
->left
.SameAs(m_container
, wxLeft
);
525 layout
->top
.SameAs(m_container
, wxTop
);
526 if (m_split
== DSR_HORIZONTAL_TAB
)
528 layout
->right
.SameAs(m_container
, wxRight
);
529 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
533 layout
->bottom
.SameAs(m_container
, wxBottom
);
534 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
536 m_child
[0]->m_container
->SetConstraints(layout
);
538 layout
= new wxLayoutConstraints();
539 layout
->right
.SameAs(m_container
, wxRight
);
540 layout
->bottom
.SameAs(m_container
, wxBottom
);
541 if (m_split
== DSR_HORIZONTAL_TAB
)
543 layout
->top
.Below(m_child
[0]->m_container
, 1);
544 layout
->left
.SameAs(m_container
, wxLeft
);
548 layout
->left
.RightOf(m_child
[0]->m_container
, 1);
549 layout
->top
.SameAs(m_container
, wxTop
);
551 m_child
[1]->m_container
->SetConstraints(layout
);
554 void wxDynamicSashWindowImpl::Unify(int panel
)
556 int other
= panel
== 0 ? 1 : 0;
558 if (m_child
[panel
]->m_leaf
)
560 wxDynamicSashWindowImpl
*child
[2];
562 child
[0] = m_child
[0];
563 child
[1] = m_child
[1];
565 m_child
[0] = m_child
[1] = NULL
;
567 m_leaf
= new wxDynamicSashWindowLeaf(this);
569 m_leaf
->m_child
= child
[panel
]->m_leaf
->m_child
;
571 m_leaf
->m_vscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_vscroll
->GetThumbPosition(),
572 child
[panel
]->m_leaf
->m_vscroll
->GetThumbSize(),
573 child
[panel
]->m_leaf
->m_vscroll
->GetRange(),
574 child
[panel
]->m_leaf
->m_vscroll
->GetPageSize());
575 m_leaf
->m_hscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_hscroll
->GetThumbPosition(),
576 child
[panel
]->m_leaf
->m_hscroll
->GetThumbSize(),
577 child
[panel
]->m_leaf
->m_hscroll
->GetRange(),
578 child
[panel
]->m_leaf
->m_hscroll
->GetPageSize());
579 m_add_child_target
= NULL
;
580 wxDynamicSashReparentEvent
event(m_leaf
);
581 m_leaf
->ProcessEvent(event
);
588 wxDynamicSashUnifyEvent
unify(m_leaf
->m_child
);
589 m_leaf
->m_child
->ProcessEvent(unify
);
593 m_split
= m_child
[panel
]->m_split
;
595 delete m_child
[other
];
597 wxDynamicSashWindowImpl
*child_panel
= m_child
[panel
];
598 m_child
[0] = child_panel
->m_child
[0];
599 m_child
[1] = child_panel
->m_child
[1];
601 m_child
[0]->m_parent
= this;
602 m_child
[1]->m_parent
= this;
604 m_add_child_target
= NULL
;
605 m_child
[0]->m_container
->Reparent(m_container
);
606 m_child
[1]->m_container
->Reparent(m_container
);
608 child_panel
->m_child
[0] = child_panel
->m_child
[1] = NULL
;
611 wxSize size
= m_container
->GetSize();
612 wxSize child_size
= m_child
[0]->m_container
->GetSize();
614 ConstrainChildren(child_size
.GetWidth() * 100 / size
.GetWidth(),
615 child_size
.GetHeight() * 100 / size
.GetHeight());
617 m_container
->Layout();
621 void wxDynamicSashWindowImpl::Split(int px
, int py
)
624 m_add_child_target
= NULL
;
626 m_child
[0] = new wxDynamicSashWindowImpl(m_window
);
627 m_child
[0]->m_container
= new wxWindow(m_container
, wxID_ANY
);
628 m_child
[0]->m_parent
= this;
629 m_child
[0]->m_top
= m_top
;
630 m_child
[0]->Create();
633 m_leaf
->m_child
->Reparent(m_container
);
634 m_child
[0]->AddChild(m_leaf
->m_child
);
637 m_child
[1] = new wxDynamicSashWindowImpl(m_window
);
638 m_child
[1]->m_container
= new wxWindow(m_container
, wxID_ANY
);
639 m_child
[1]->m_parent
= this;
640 m_child
[1]->m_top
= m_top
;
641 m_child
[1]->Create();
643 m_split
= m_dragging
;
644 ConstrainChildren(px
, py
);
646 m_top
->m_add_child_target
= m_child
[1];
647 wxDynamicSashSplitEvent
split(m_child
[0]->m_leaf
->m_child
);
648 m_child
[0]->m_leaf
->m_child
->ProcessEvent(split
);
650 m_child
[0]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
651 m_leaf
->m_vscroll
->GetThumbSize(),
652 m_leaf
->m_vscroll
->GetRange(),
653 m_leaf
->m_vscroll
->GetPageSize());
654 m_child
[0]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
655 m_leaf
->m_hscroll
->GetThumbSize(),
656 m_leaf
->m_hscroll
->GetRange(),
657 m_leaf
->m_hscroll
->GetPageSize());
658 m_child
[1]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
659 m_leaf
->m_vscroll
->GetThumbSize(),
660 m_leaf
->m_vscroll
->GetRange(),
661 m_leaf
->m_vscroll
->GetPageSize());
662 m_child
[1]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
663 m_leaf
->m_hscroll
->GetThumbSize(),
664 m_leaf
->m_hscroll
->GetRange(),
665 m_leaf
->m_hscroll
->GetPageSize());
669 m_container
->Layout();
673 /* This code is called when you finish resizing a view by dragging the
674 corner tab, but I think this implementation is lousy and will surprise
675 the user more often than it will do what they are trying to do. What
676 I really need to be doing here is do a rewrite such that *no* sashes
677 move except the ones immediately to the bottom and right of this window,
678 and handle the case where you resize a window larger than it's neighbors
679 by destroying the neighbors.
681 But this will do for now. */
682 void wxDynamicSashWindowImpl::Resize(int x
, int y
)
684 wxDynamicSashWindowImpl
*h_parent
= FindParent(DSR_BOTTOM_EDGE
);
685 wxDynamicSashWindowImpl
*v_parent
= FindParent(DSR_RIGHT_EDGE
);
688 wxWindow
*frame
= FindFrame();
697 m_container
->ClientToScreen(NULL
, &y
);
698 h_parent
->m_container
->ScreenToClient(NULL
, &y
);
700 int py
= (int)((y
* 100) / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
704 wxDynamicSashWindowImpl
*ho_parent
= FindParent(DSR_TOP_EDGE
);
708 if (FindUpperParent(h_parent
, ho_parent
) == ho_parent
)
714 py
= (int)((ho_parent
->m_child
[0]->m_container
->GetSize().GetHeight() * 100)
715 / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
716 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
717 h_parent
->m_container
, wxHeight
, py
);
719 h_parent
= ho_parent
;
734 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
735 h_parent
->m_container
, wxHeight
, py
);
736 h_parent
->m_container
->Layout();
742 h_parent
= FindParent(DSR_TOP_EDGE
);
746 int py
= (int)((y
* 100) /
747 (h_parent
->m_container
->GetSize().GetHeight() +
748 y
- m_container
->GetSize().GetHeight()) + 0.5);
760 wxSize size
= frame
->GetSize();
761 frame
->SetSize(size
.GetWidth(), size
.GetHeight() + y
- m_container
->GetSize().GetHeight());
767 m_container
->ClientToScreen(&x
, NULL
);
768 v_parent
->m_container
->ScreenToClient(&x
, NULL
);
770 int px
= (int)((x
* 100) / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
774 wxDynamicSashWindowImpl
*vo_parent
= FindParent(DSR_LEFT_EDGE
);
778 if (FindUpperParent(v_parent
, vo_parent
) == vo_parent
)
784 px
= (int)((vo_parent
->m_child
[0]->m_container
->GetSize().GetWidth() * 100)
785 / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
786 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
787 v_parent
->m_container
, wxWidth
, px
);
789 v_parent
= vo_parent
;
804 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
805 v_parent
->m_container
, wxWidth
, px
);
806 v_parent
->m_container
->Layout();
812 v_parent
= FindParent(DSR_LEFT_EDGE
);
816 int px
= (int)((x
* 100) /
817 (v_parent
->m_container
->GetSize().GetWidth() +
818 x
- m_container
->GetSize().GetWidth()) + 0.5);
830 wxSize size
= frame
->GetSize();
831 frame
->SetSize(size
.GetWidth() + x
- m_container
->GetSize().GetWidth(), size
.GetHeight());
835 if (h_unify
!= -1 && v_unify
!= -1)
837 wxDynamicSashWindowImpl
*parent
= FindUpperParent(h_parent
, v_parent
);
839 if (parent
== h_parent
)
841 h_parent
->Unify(h_unify
);
845 v_parent
->Unify(v_unify
);
848 else if (h_unify
!= -1)
850 h_parent
->Unify(h_unify
);
852 else if (v_unify
!= -1)
854 v_parent
->Unify(v_unify
);
859 void wxDynamicSashWindowImpl::OnSize(wxSizeEvent
&event
)
861 m_container
->Layout();
864 m_leaf
->OnSize(event
);
867 void wxDynamicSashWindowImpl::OnPaint(wxPaintEvent
&event
)
870 m_leaf
->OnPaint(event
);
873 wxPaintDC
dc(m_container
);
874 dc
.SetBackground(wxBrush(m_container
->GetBackgroundColour(), wxSOLID
));
879 void wxDynamicSashWindowImpl::OnMouseMove(wxMouseEvent
&event
)
883 DrawSash(m_drag_x
, m_drag_y
);
884 m_drag_x
= event
.m_x
; m_drag_y
= event
.m_y
;
885 DrawSash(m_drag_x
, m_drag_y
);
889 m_leaf
->OnMouseMove(event
);
893 void wxDynamicSashWindowImpl::OnLeave(wxMouseEvent
&event
)
896 m_leaf
->OnLeave(event
);
899 void wxDynamicSashWindowImpl::OnPress(wxMouseEvent
&event
)
903 m_leaf
->OnPress(event
);
907 m_dragging
= m_split
;
908 m_drag_x
= event
.m_x
;
909 m_drag_y
= event
.m_y
;
910 DrawSash(m_drag_x
, m_drag_y
);
911 m_container
->CaptureMouse();
915 void wxDynamicSashWindowImpl::OnRelease(wxMouseEvent
&event
)
917 if ((m_dragging
== DSR_CORNER
) &&
918 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0)
920 DrawSash(m_drag_x
, m_drag_y
);
921 m_container
->ReleaseMouse();
923 Resize(event
.m_x
, event
.m_y
);
925 m_dragging
= DSR_NONE
;
929 DrawSash(m_drag_x
, m_drag_y
);
930 m_container
->ReleaseMouse();
932 wxSize size
= m_container
->GetSize();
933 int px
= (int)((event
.m_x
* 100) / size
.GetWidth() + 0.5);
934 int py
= (int)((event
.m_y
* 100) / size
.GetHeight() + 0.5);
936 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
>= 10 && py
<= 90)
937 || (m_dragging
== DSR_VERTICAL_TAB
&& px
>= 10 && px
<= 90))
939 if (m_child
[0] == NULL
)
945 /* It would be nice if moving *this* sash didn't implicitly move
946 the sashes of our children (if any). But this will do. */
947 wxLayoutConstraints
*layout
= m_child
[0]->m_container
->GetConstraints();
948 if (m_split
== DSR_HORIZONTAL_TAB
)
950 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
954 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
956 m_container
->Layout();
961 if (m_child
[0] != NULL
)
963 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
<= 10)
964 || (m_dragging
== DSR_VERTICAL_TAB
&& px
<= 10))
976 if (m_split
== DSR_HORIZONTAL_TAB
)
977 cursor
= wxCursor(wxCURSOR_SIZENS
);
978 else if (m_split
== DSR_VERTICAL_TAB
)
979 cursor
= wxCursor(wxCURSOR_SIZEWE
);
981 cursor
= wxCursor(wxCURSOR_ARROW
);
983 m_container
->SetCursor(cursor
);
985 m_dragging
= DSR_NONE
;
989 m_leaf
->OnRelease(event
);
993 // ============================================================================
994 // wxDynamicSashWindowLeaf
995 // ============================================================================
997 wxDynamicSashWindowLeaf::wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
)
1007 wxDynamicSashWindowLeaf::~wxDynamicSashWindowLeaf()
1009 m_hscroll
->SetEventHandler(m_hscroll
);
1010 m_vscroll
->SetEventHandler(m_vscroll
);
1012 m_hscroll
->Destroy();
1013 m_vscroll
->Destroy();
1014 m_viewport
->Destroy();
1017 bool wxDynamicSashWindowLeaf::Create()
1019 m_hscroll
= new wxScrollBar();
1020 m_vscroll
= new wxScrollBar();
1021 m_viewport
= new wxWindow();
1023 wxDynamicSashWindowImpl
*add_child_target
= m_impl
->m_add_child_target
;
1024 m_impl
->m_add_child_target
= NULL
;
1026 bool success
= m_hscroll
->Create(m_impl
->m_container
, wxID_ANY
,
1027 wxDefaultPosition
, wxDefaultSize
,
1030 success
= m_vscroll
->Create(m_impl
->m_container
, wxID_ANY
,
1031 wxDefaultPosition
, wxDefaultSize
,
1034 success
= m_viewport
->Create(m_impl
->m_container
, wxID_ANY
);
1038 m_impl
->m_add_child_target
= add_child_target
;
1040 wxCursor
cursor(wxCURSOR_ARROW
);
1041 m_hscroll
->SetCursor(cursor
);
1042 m_vscroll
->SetCursor(cursor
);
1043 m_viewport
->SetCursor(cursor
);
1045 // the viewport must resize its child when it is itself resized, but it's
1046 // more convenient to do it in our own method instead of deriving a new
1047 // class just for this: this is why we pass this as last Connect() argument
1048 m_viewport
->Connect(wxEVT_SIZE
,
1049 wxSizeEventHandler(wxDynamicSashWindowLeaf::OnViewSize
),
1052 Connect(wxEVT_DYNAMIC_SASH_REPARENT
,
1053 wxEventHandler(wxDynamicSashWindowLeaf::OnReparent
));
1055 if (m_impl
->m_window
->GetWindowStyle() & wxDS_MANAGE_SCROLLBARS
)
1057 m_hscroll
->SetEventHandler(this);
1058 m_vscroll
->SetEventHandler(this);
1060 Connect(wxEVT_SET_FOCUS
,
1061 wxFocusEventHandler(wxDynamicSashWindowLeaf::OnFocus
));
1062 Connect(wxEVT_SCROLL_TOP
,
1063 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1064 Connect(wxEVT_SCROLL_BOTTOM
,
1065 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1066 Connect(wxEVT_SCROLL_LINEUP
,
1067 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1068 Connect(wxEVT_SCROLL_LINEDOWN
,
1069 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1070 Connect(wxEVT_SCROLL_PAGEUP
,
1071 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1072 Connect(wxEVT_SCROLL_PAGEDOWN
,
1073 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1074 Connect(wxEVT_SCROLL_THUMBTRACK
,
1075 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1076 Connect(wxEVT_SCROLL_THUMBRELEASE
,
1077 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1080 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
1084 wxSize size
= m_hscroll
->GetBestSize();
1086 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 10);
1087 layout
->right
.LeftOf(m_vscroll
);
1088 layout
->bottom
.SameAs(m_impl
->m_container
, wxBottom
, 3);
1089 layout
->height
.Absolute(size
.GetHeight());
1090 m_hscroll
->SetConstraints(layout
);
1092 layout
= new wxLayoutConstraints();
1096 size
= m_vscroll
->GetBestSize();
1098 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 10);
1099 layout
->bottom
.Above(m_hscroll
);
1100 layout
->right
.SameAs(m_impl
->m_container
, wxRight
, 3);
1101 layout
->width
.Absolute(size
.GetWidth());
1102 m_vscroll
->SetConstraints(layout
);
1104 layout
= new wxLayoutConstraints();
1107 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 3);
1108 layout
->right
.LeftOf(m_vscroll
);
1109 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 3);
1110 layout
->bottom
.Above(m_hscroll
);
1111 m_viewport
->SetConstraints(layout
);
1113 m_impl
->m_container
->Layout();
1118 void wxDynamicSashWindowLeaf::AddChild(wxWindow
*window
)
1125 wxDynamicSashReparentEvent
event(this);
1126 AddPendingEvent(event
);
1129 DynamicSashRegion
wxDynamicSashWindowLeaf::GetRegion(int x
, int y
)
1131 wxSize size
= m_impl
->m_container
->GetSize();
1132 int w
= size
.GetWidth();
1133 int h
= size
.GetHeight();
1134 size
= m_hscroll
->GetSize();
1135 int sh
= size
.GetHeight();
1136 size
= m_vscroll
->GetSize();
1137 int sw
= size
.GetWidth();
1139 if (x
>= w
- sw
- 3 && x
< w
&& y
>= h
- sh
- 3 && y
< h
)
1141 if (x
>= 3 && x
< 10 && y
>= h
- sh
- 3 && y
< h
- 2)
1142 return DSR_VERTICAL_TAB
;
1143 if (x
>= w
- sw
- 3 && x
< w
- 2 && y
>= 3 && y
< 10)
1144 return DSR_HORIZONTAL_TAB
;
1146 return DSR_LEFT_EDGE
;
1148 return DSR_TOP_EDGE
;
1150 return DSR_RIGHT_EDGE
;
1152 return DSR_BOTTOM_EDGE
;
1157 void wxDynamicSashWindowLeaf::ResizeChild(const wxSize
& size
)
1161 if (m_impl
->m_window
->HasFlag(wxDS_MANAGE_SCROLLBARS
))
1163 wxSize best_size
= m_child
->GetBestSize();
1164 if (best_size
.GetWidth() < size
.GetWidth())
1165 best_size
.SetWidth(size
.GetWidth());
1166 if (best_size
.GetHeight() < size
.GetHeight())
1167 best_size
.SetHeight(size
.GetHeight());
1168 m_child
->SetSize(best_size
);
1170 int hpos
= m_hscroll
->GetThumbPosition();
1171 int vpos
= m_vscroll
->GetThumbPosition();
1177 if (hpos
> best_size
.GetWidth() - size
.GetWidth())
1178 hpos
= best_size
.GetWidth() - size
.GetWidth();
1179 if (vpos
> best_size
.GetHeight() - size
.GetHeight())
1180 vpos
= best_size
.GetHeight() - size
.GetHeight();
1182 m_hscroll
->SetScrollbar(hpos
, size
.GetWidth(),
1183 best_size
.GetWidth(), size
.GetWidth());
1184 m_vscroll
->SetScrollbar(vpos
, size
.GetHeight(),
1185 best_size
.GetHeight(), size
.GetHeight());
1187 // Umm, the scrollbars are doing something insane under GTK+ and subtracting
1188 // one from the position I pass in. This works around that.
1189 m_hscroll
->SetThumbPosition(hpos
+ hpos
- m_hscroll
->GetThumbPosition());
1190 m_vscroll
->SetThumbPosition(vpos
+ vpos
- m_vscroll
->GetThumbPosition());
1192 wxPoint pos
= m_child
->GetPosition();
1193 m_viewport
->ScrollWindow(-hpos
- pos
.x
, -vpos
- pos
.y
);
1195 else // !wxDS_MANAGE_SCROLLBARS
1197 m_child
->SetSize(size
);
1203 wxDynamicSashWindowLeaf::FindScrollBar(const wxWindow
*child
, int vert
) const
1205 if (m_child
== child
)
1207 return vert
? m_vscroll
: m_hscroll
;
1213 void wxDynamicSashWindowLeaf::OnSize(wxSizeEvent
&WXUNUSED(event
))
1215 m_impl
->m_container
->Refresh();
1218 void wxDynamicSashWindowLeaf::OnViewSize(wxSizeEvent
&WXUNUSED(event
))
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
)