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
,
213 /* m_child is the window provided to us by the application developer.
214 m_viewport is a window we've created, and it is the immediately
215 parent of m_child. We scroll m_child by moving it around within
217 wxWindow
*m_viewport
,
222 // ============================================================================
223 // wxDynamicSashWindow
224 // ============================================================================
226 wxDynamicSashWindow::wxDynamicSashWindow()
231 wxDynamicSashWindow::wxDynamicSashWindow(wxWindow
*parent
,
236 const wxString
& name
)
239 Create(parent
, id
, pos
, size
, style
, name
);
242 wxDynamicSashWindow::~wxDynamicSashWindow()
244 SetEventHandler(this);
248 bool wxDynamicSashWindow::Create(wxWindow
*parent
,
253 const wxString
& name
)
258 if (!wxWindow::Create(parent
, id
, pos
, size
, style
, name
))
261 m_impl
= new wxDynamicSashWindowImpl(this);
265 if (!m_impl
->Create())
275 void wxDynamicSashWindow::AddChild(wxWindowBase
*child
)
277 wxWindow::AddChild(child
);
279 m_impl
->AddChild(wxDynamicCast(child
, wxWindow
));
282 wxScrollBar
*wxDynamicSashWindow::GetHScrollBar(const wxWindow
*child
) const
284 return m_impl
->FindScrollBar(child
, 0);
287 wxScrollBar
*wxDynamicSashWindow::GetVScrollBar(const wxWindow
*child
) const
289 return m_impl
->FindScrollBar(child
, 1);
292 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashWindow
, wxWindow
)
295 // ============================================================================
296 // wxDynamicSashWindowImpl
297 // ============================================================================
299 wxDynamicSashWindowImpl::wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
)
302 m_add_child_target
= this;
310 m_dragging
= DSR_NONE
;
314 wxDynamicSashWindowImpl::~wxDynamicSashWindowImpl()
323 if (m_container
!= m_window
&& m_container
)
325 m_container
->SetEventHandler(m_container
);
326 m_container
->Destroy();
330 bool wxDynamicSashWindowImpl::Create()
333 m_container
= m_window
;
335 wxCursor
cursor(wxCURSOR_ARROW
);
336 m_container
->SetCursor(cursor
);
338 m_leaf
= new wxDynamicSashWindowLeaf(this);
342 if (!m_leaf
->Create())
349 m_container
->SetEventHandler(this);
351 Connect(wxEVT_SIZE
, wxSizeEventHandler(wxDynamicSashWindowImpl::OnSize
));
352 Connect(wxEVT_PAINT
, wxPaintEventHandler(wxDynamicSashWindowImpl::OnPaint
));
353 Connect(wxEVT_MOTION
,
354 wxMouseEventHandler(wxDynamicSashWindowImpl::OnMouseMove
));
355 Connect(wxEVT_ENTER_WINDOW
,
356 wxMouseEventHandler(wxDynamicSashWindowImpl::OnMouseMove
));
357 Connect(wxEVT_LEAVE_WINDOW
,
358 wxMouseEventHandler(wxDynamicSashWindowImpl::OnLeave
));
359 Connect(wxEVT_LEFT_DOWN
,
360 wxMouseEventHandler(wxDynamicSashWindowImpl::OnPress
));
361 Connect(wxEVT_LEFT_UP
,
362 wxMouseEventHandler(wxDynamicSashWindowImpl::OnRelease
));
367 void wxDynamicSashWindowImpl::AddChild(wxWindow
*window
)
369 if (m_add_child_target
&& m_add_child_target
->m_leaf
)
370 m_add_child_target
->m_leaf
->AddChild(window
);
373 void wxDynamicSashWindowImpl::DrawSash(int x
, int y
) const
378 dc
.StartDrawingOnTop(m_container
);
382 bdc
.SelectObject(bmp
);
383 bdc
.DrawRectangle(-1, -1, 10, 10);
384 for (i
= 0; i
< 8; i
++)
386 for (j
= 0; j
< 8; j
++)
395 dc
.SetLogicalFunction(wxXOR
);
397 if ((m_dragging
== DSR_CORNER
) &&
398 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0)
403 m_container
->ClientToScreen(&cx
, &cy
);
404 m_container
->ClientToScreen(&x
, &y
);
406 if (cx
< x
&& cy
< y
)
408 dc
.DrawRectangle(cx
- 2, cy
- 2, x
- cx
+ 4, 4);
409 dc
.DrawRectangle(x
- 2, cy
+ 2, 4, y
- cy
);
410 dc
.DrawRectangle(cx
- 2, cy
+ 2, 4, y
- cy
);
411 dc
.DrawRectangle(cx
+ 2, y
- 2, x
- cx
- 4, 4);
417 m_container
->GetClientSize(&body_w
, &body_h
);
428 if (m_dragging
== DSR_HORIZONTAL_TAB
)
433 m_container
->ClientToScreen(&x
, &y
);
436 w
= body_w
; h
= body_h
;
438 if (m_dragging
== DSR_HORIZONTAL_TAB
)
439 dc
.DrawRectangle(x
, y
- 2, w
, 4);
441 dc
.DrawRectangle(x
- 2, y
, 4, h
);
444 dc
.EndDrawingOnTop();
447 wxDynamicSashWindowImpl
*
448 wxDynamicSashWindowImpl::FindParent(DynamicSashRegion side
) const
450 if (m_parent
== NULL
)
453 if (m_parent
->m_split
== DSR_HORIZONTAL_TAB
)
455 if (side
== DSR_TOP_EDGE
&& m_parent
->m_child
[1] == this)
457 if (side
== DSR_BOTTOM_EDGE
&& m_parent
->m_child
[0] == this)
460 else if (m_parent
->m_split
== DSR_VERTICAL_TAB
)
462 if (side
== DSR_LEFT_EDGE
&& m_parent
->m_child
[1] == this)
464 if (side
== DSR_RIGHT_EDGE
&& m_parent
->m_child
[0] == this)
468 return m_parent
->FindParent(side
);
471 wxDynamicSashWindowImpl
*
472 wxDynamicSashWindowImpl::FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
473 wxDynamicSashWindowImpl
*sash_b
) const
476 win
= sash_a
->m_container
->GetParent();
477 while (win
&& !win
->IsTopLevel())
479 if (win
== sash_b
->m_container
)
482 win
= win
->GetParent();
489 wxWindow
*wxDynamicSashWindowImpl::FindFrame() const
493 win
= m_window
->GetParent();
494 while (win
&& !win
->IsTopLevel()
496 && ! wxIsKindOf(win
, wxMDIChildFrame
) // not top-level but still a frame
500 win
= win
->GetParent();
507 wxDynamicSashWindowImpl::FindScrollBar(const wxWindow
*child
, int vert
) const
509 if (m_child
[0] == NULL
&& m_leaf
== NULL
)
514 return m_leaf
->FindScrollBar(child
, vert
);
517 wxScrollBar
*ret
= m_child
[0]->FindScrollBar(child
, vert
);
519 ret
= m_child
[1]->FindScrollBar(child
, vert
);
524 void wxDynamicSashWindowImpl::ConstrainChildren(int px
, int py
)
526 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
527 layout
->left
.SameAs(m_container
, wxLeft
);
528 layout
->top
.SameAs(m_container
, wxTop
);
529 if (m_split
== DSR_HORIZONTAL_TAB
)
531 layout
->right
.SameAs(m_container
, wxRight
);
532 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
536 layout
->bottom
.SameAs(m_container
, wxBottom
);
537 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
539 m_child
[0]->m_container
->SetConstraints(layout
);
541 layout
= new wxLayoutConstraints();
542 layout
->right
.SameAs(m_container
, wxRight
);
543 layout
->bottom
.SameAs(m_container
, wxBottom
);
544 if (m_split
== DSR_HORIZONTAL_TAB
)
546 layout
->top
.Below(m_child
[0]->m_container
, 1);
547 layout
->left
.SameAs(m_container
, wxLeft
);
551 layout
->left
.RightOf(m_child
[0]->m_container
, 1);
552 layout
->top
.SameAs(m_container
, wxTop
);
554 m_child
[1]->m_container
->SetConstraints(layout
);
557 void wxDynamicSashWindowImpl::Unify(int panel
)
559 int other
= panel
== 0 ? 1 : 0;
561 if (m_child
[panel
]->m_leaf
)
563 wxDynamicSashWindowImpl
*child
[2];
565 child
[0] = m_child
[0];
566 child
[1] = m_child
[1];
568 m_child
[0] = m_child
[1] = NULL
;
570 m_leaf
= new wxDynamicSashWindowLeaf(this);
572 m_leaf
->m_child
= child
[panel
]->m_leaf
->m_child
;
574 m_leaf
->m_vscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_vscroll
->GetThumbPosition(),
575 child
[panel
]->m_leaf
->m_vscroll
->GetThumbSize(),
576 child
[panel
]->m_leaf
->m_vscroll
->GetRange(),
577 child
[panel
]->m_leaf
->m_vscroll
->GetPageSize());
578 m_leaf
->m_hscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_hscroll
->GetThumbPosition(),
579 child
[panel
]->m_leaf
->m_hscroll
->GetThumbSize(),
580 child
[panel
]->m_leaf
->m_hscroll
->GetRange(),
581 child
[panel
]->m_leaf
->m_hscroll
->GetPageSize());
582 m_add_child_target
= NULL
;
583 wxDynamicSashReparentEvent
event(m_leaf
);
584 m_leaf
->ProcessEvent(event
);
591 wxDynamicSashUnifyEvent
unify(m_leaf
->m_child
);
592 m_leaf
->m_child
->ProcessEvent(unify
);
596 m_split
= m_child
[panel
]->m_split
;
598 delete m_child
[other
];
600 wxDynamicSashWindowImpl
*child_panel
= m_child
[panel
];
601 m_child
[0] = child_panel
->m_child
[0];
602 m_child
[1] = child_panel
->m_child
[1];
604 m_child
[0]->m_parent
= this;
605 m_child
[1]->m_parent
= this;
607 m_add_child_target
= NULL
;
608 m_child
[0]->m_container
->Reparent(m_container
);
609 m_child
[1]->m_container
->Reparent(m_container
);
611 child_panel
->m_child
[0] = child_panel
->m_child
[1] = NULL
;
614 wxSize size
= m_container
->GetSize();
615 wxSize child_size
= m_child
[0]->m_container
->GetSize();
617 ConstrainChildren(child_size
.GetWidth() * 100 / size
.GetWidth(),
618 child_size
.GetHeight() * 100 / size
.GetHeight());
620 m_container
->Layout();
624 void wxDynamicSashWindowImpl::Split(int px
, int py
)
627 m_add_child_target
= NULL
;
629 m_child
[0] = new wxDynamicSashWindowImpl(m_window
);
630 m_child
[0]->m_container
= new wxWindow(m_container
, wxID_ANY
);
631 m_child
[0]->m_parent
= this;
632 m_child
[0]->m_top
= m_top
;
633 m_child
[0]->Create();
636 m_leaf
->m_child
->Reparent(m_container
);
637 m_child
[0]->AddChild(m_leaf
->m_child
);
640 m_child
[1] = new wxDynamicSashWindowImpl(m_window
);
641 m_child
[1]->m_container
= new wxWindow(m_container
, wxID_ANY
);
642 m_child
[1]->m_parent
= this;
643 m_child
[1]->m_top
= m_top
;
644 m_child
[1]->Create();
646 m_split
= m_dragging
;
647 ConstrainChildren(px
, py
);
649 m_top
->m_add_child_target
= m_child
[1];
650 wxDynamicSashSplitEvent
split(m_child
[0]->m_leaf
->m_child
);
651 m_child
[0]->m_leaf
->m_child
->ProcessEvent(split
);
653 m_child
[0]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
654 m_leaf
->m_vscroll
->GetThumbSize(),
655 m_leaf
->m_vscroll
->GetRange(),
656 m_leaf
->m_vscroll
->GetPageSize());
657 m_child
[0]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
658 m_leaf
->m_hscroll
->GetThumbSize(),
659 m_leaf
->m_hscroll
->GetRange(),
660 m_leaf
->m_hscroll
->GetPageSize());
661 m_child
[1]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
662 m_leaf
->m_vscroll
->GetThumbSize(),
663 m_leaf
->m_vscroll
->GetRange(),
664 m_leaf
->m_vscroll
->GetPageSize());
665 m_child
[1]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
666 m_leaf
->m_hscroll
->GetThumbSize(),
667 m_leaf
->m_hscroll
->GetRange(),
668 m_leaf
->m_hscroll
->GetPageSize());
672 m_container
->Layout();
676 /* This code is called when you finish resizing a view by dragging the
677 corner tab, but I think this implementation is lousy and will surprise
678 the user more often than it will do what they are trying to do. What
679 I really need to be doing here is do a rewrite such that *no* sashes
680 move except the ones immediately to the bottom and right of this window,
681 and handle the case where you resize a window larger than it's neighbors
682 by destroying the neighbors.
684 But this will do for now. */
685 void wxDynamicSashWindowImpl::Resize(int x
, int y
)
687 wxDynamicSashWindowImpl
*h_parent
= FindParent(DSR_BOTTOM_EDGE
);
688 wxDynamicSashWindowImpl
*v_parent
= FindParent(DSR_RIGHT_EDGE
);
691 wxWindow
*frame
= FindFrame();
700 m_container
->ClientToScreen(NULL
, &y
);
701 h_parent
->m_container
->ScreenToClient(NULL
, &y
);
703 int py
= (int)((y
* 100) / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
707 wxDynamicSashWindowImpl
*ho_parent
= FindParent(DSR_TOP_EDGE
);
711 if (FindUpperParent(h_parent
, ho_parent
) == ho_parent
)
717 py
= (int)((ho_parent
->m_child
[0]->m_container
->GetSize().GetHeight() * 100)
718 / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
719 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
720 h_parent
->m_container
, wxHeight
, py
);
722 h_parent
= ho_parent
;
737 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
738 h_parent
->m_container
, wxHeight
, py
);
739 h_parent
->m_container
->Layout();
745 h_parent
= FindParent(DSR_TOP_EDGE
);
749 int py
= (int)((y
* 100) /
750 (h_parent
->m_container
->GetSize().GetHeight() +
751 y
- m_container
->GetSize().GetHeight()) + 0.5);
763 wxSize size
= frame
->GetSize();
764 frame
->SetSize(size
.GetWidth(), size
.GetHeight() + y
- m_container
->GetSize().GetHeight());
770 m_container
->ClientToScreen(&x
, NULL
);
771 v_parent
->m_container
->ScreenToClient(&x
, NULL
);
773 int px
= (int)((x
* 100) / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
777 wxDynamicSashWindowImpl
*vo_parent
= FindParent(DSR_LEFT_EDGE
);
781 if (FindUpperParent(v_parent
, vo_parent
) == vo_parent
)
787 px
= (int)((vo_parent
->m_child
[0]->m_container
->GetSize().GetWidth() * 100)
788 / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
789 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
790 v_parent
->m_container
, wxWidth
, px
);
792 v_parent
= vo_parent
;
807 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
808 v_parent
->m_container
, wxWidth
, px
);
809 v_parent
->m_container
->Layout();
815 v_parent
= FindParent(DSR_LEFT_EDGE
);
819 int px
= (int)((x
* 100) /
820 (v_parent
->m_container
->GetSize().GetWidth() +
821 x
- m_container
->GetSize().GetWidth()) + 0.5);
833 wxSize size
= frame
->GetSize();
834 frame
->SetSize(size
.GetWidth() + x
- m_container
->GetSize().GetWidth(), size
.GetHeight());
838 if (h_unify
!= -1 && v_unify
!= -1)
840 wxDynamicSashWindowImpl
*parent
= FindUpperParent(h_parent
, v_parent
);
842 if (parent
== h_parent
)
844 h_parent
->Unify(h_unify
);
848 v_parent
->Unify(v_unify
);
851 else if (h_unify
!= -1)
853 h_parent
->Unify(h_unify
);
855 else if (v_unify
!= -1)
857 v_parent
->Unify(v_unify
);
862 void wxDynamicSashWindowImpl::OnSize(wxSizeEvent
&event
)
864 m_container
->Layout();
867 m_leaf
->OnSize(event
);
870 void wxDynamicSashWindowImpl::OnPaint(wxPaintEvent
&event
)
873 m_leaf
->OnPaint(event
);
876 wxPaintDC
dc(m_container
);
877 dc
.SetBackground(wxBrush(m_container
->GetBackgroundColour(), wxSOLID
));
882 void wxDynamicSashWindowImpl::OnMouseMove(wxMouseEvent
&event
)
886 DrawSash(m_drag_x
, m_drag_y
);
887 m_drag_x
= event
.m_x
; m_drag_y
= event
.m_y
;
888 DrawSash(m_drag_x
, m_drag_y
);
892 m_leaf
->OnMouseMove(event
);
896 void wxDynamicSashWindowImpl::OnLeave(wxMouseEvent
&event
)
899 m_leaf
->OnLeave(event
);
902 void wxDynamicSashWindowImpl::OnPress(wxMouseEvent
&event
)
906 m_leaf
->OnPress(event
);
910 m_dragging
= m_split
;
911 m_drag_x
= event
.m_x
;
912 m_drag_y
= event
.m_y
;
913 DrawSash(m_drag_x
, m_drag_y
);
914 m_container
->CaptureMouse();
918 void wxDynamicSashWindowImpl::OnRelease(wxMouseEvent
&event
)
920 if ((m_dragging
== DSR_CORNER
) &&
921 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0)
923 DrawSash(m_drag_x
, m_drag_y
);
924 m_container
->ReleaseMouse();
926 Resize(event
.m_x
, event
.m_y
);
928 m_dragging
= DSR_NONE
;
932 DrawSash(m_drag_x
, m_drag_y
);
933 m_container
->ReleaseMouse();
935 wxSize size
= m_container
->GetSize();
936 int px
= (int)((event
.m_x
* 100) / size
.GetWidth() + 0.5);
937 int py
= (int)((event
.m_y
* 100) / size
.GetHeight() + 0.5);
939 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
>= 10 && py
<= 90)
940 || (m_dragging
== DSR_VERTICAL_TAB
&& px
>= 10 && px
<= 90))
942 if (m_child
[0] == NULL
)
948 /* It would be nice if moving *this* sash didn't implicitly move
949 the sashes of our children (if any). But this will do. */
950 wxLayoutConstraints
*layout
= m_child
[0]->m_container
->GetConstraints();
951 if (m_split
== DSR_HORIZONTAL_TAB
)
953 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
957 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
959 m_container
->Layout();
964 if (m_child
[0] != NULL
)
966 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
<= 10)
967 || (m_dragging
== DSR_VERTICAL_TAB
&& px
<= 10))
979 if (m_split
== DSR_HORIZONTAL_TAB
)
980 cursor
= wxCursor(wxCURSOR_SIZENS
);
981 else if (m_split
== DSR_VERTICAL_TAB
)
982 cursor
= wxCursor(wxCURSOR_SIZEWE
);
984 cursor
= wxCursor(wxCURSOR_ARROW
);
986 m_container
->SetCursor(cursor
);
988 m_dragging
= DSR_NONE
;
992 m_leaf
->OnRelease(event
);
996 // ============================================================================
997 // wxDynamicSashWindowLeaf
998 // ============================================================================
1000 wxDynamicSashWindowLeaf::wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
)
1004 m_hscroll
= m_vscroll
= NULL
;
1008 wxDynamicSashWindowLeaf::~wxDynamicSashWindowLeaf()
1010 m_hscroll
->SetEventHandler(m_hscroll
);
1011 m_vscroll
->SetEventHandler(m_vscroll
);
1012 m_viewport
->SetEventHandler(m_viewport
);
1014 m_hscroll
->Destroy();
1015 m_vscroll
->Destroy();
1016 m_viewport
->Destroy();
1019 bool wxDynamicSashWindowLeaf::Create()
1023 m_hscroll
= new wxScrollBar();
1024 m_vscroll
= new wxScrollBar();
1025 m_viewport
= new wxWindow();
1027 if (!m_hscroll
|| !m_vscroll
|| !m_viewport
)
1032 wxDynamicSashWindowImpl
*add_child_target
= m_impl
->m_add_child_target
;
1033 m_impl
->m_add_child_target
= NULL
;
1034 success
= m_hscroll
->Create(m_impl
->m_container
, wxID_ANY
, wxDefaultPosition
, wxDefaultSize
,
1036 success
= success
&& m_vscroll
->Create(m_impl
->m_container
, wxID_ANY
, wxDefaultPosition
, wxDefaultSize
,
1038 success
= success
&& m_viewport
->Create(m_impl
->m_container
, wxID_ANY
);
1039 m_impl
->m_add_child_target
= add_child_target
;
1041 wxCursor
cursor(wxCURSOR_ARROW
);
1042 m_hscroll
->SetCursor(cursor
);
1043 m_vscroll
->SetCursor(cursor
);
1044 m_viewport
->SetCursor(cursor
);
1046 m_viewport
->SetEventHandler(this);
1047 Connect(wxEVT_DYNAMIC_SASH_REPARENT
,
1048 wxEventHandler(wxDynamicSashWindowLeaf::OnReparent
));
1050 if (m_impl
->m_window
->GetWindowStyle() & wxDS_MANAGE_SCROLLBARS
)
1052 m_hscroll
->SetEventHandler(this);
1053 m_vscroll
->SetEventHandler(this);
1055 Connect(wxEVT_SET_FOCUS
,
1056 wxFocusEventHandler(wxDynamicSashWindowLeaf::OnFocus
));
1057 Connect(wxEVT_SCROLL_TOP
,
1058 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1059 Connect(wxEVT_SCROLL_BOTTOM
,
1060 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1061 Connect(wxEVT_SCROLL_LINEUP
,
1062 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1063 Connect(wxEVT_SCROLL_LINEDOWN
,
1064 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1065 Connect(wxEVT_SCROLL_PAGEUP
,
1066 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1067 Connect(wxEVT_SCROLL_PAGEDOWN
,
1068 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1069 Connect(wxEVT_SCROLL_THUMBTRACK
,
1070 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1071 Connect(wxEVT_SCROLL_THUMBRELEASE
,
1072 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1075 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
1079 wxSize size
= m_hscroll
->GetBestSize();
1081 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 10);
1082 layout
->right
.LeftOf(m_vscroll
);
1083 layout
->bottom
.SameAs(m_impl
->m_container
, wxBottom
, 3);
1084 layout
->height
.Absolute(size
.GetHeight());
1085 m_hscroll
->SetConstraints(layout
);
1087 layout
= new wxLayoutConstraints();
1091 size
= m_vscroll
->GetBestSize();
1093 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 10);
1094 layout
->bottom
.Above(m_hscroll
);
1095 layout
->right
.SameAs(m_impl
->m_container
, wxRight
, 3);
1096 layout
->width
.Absolute(size
.GetWidth());
1097 m_vscroll
->SetConstraints(layout
);
1099 layout
= new wxLayoutConstraints();
1102 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 3);
1103 layout
->right
.LeftOf(m_vscroll
);
1104 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 3);
1105 layout
->bottom
.Above(m_hscroll
);
1106 m_viewport
->SetConstraints(layout
);
1108 m_impl
->m_container
->Layout();
1113 void wxDynamicSashWindowLeaf::AddChild(wxWindow
*window
)
1120 wxDynamicSashReparentEvent
event(this);
1121 AddPendingEvent(event
);
1124 DynamicSashRegion
wxDynamicSashWindowLeaf::GetRegion(int x
, int y
)
1126 wxSize size
= m_impl
->m_container
->GetSize();
1127 int w
= size
.GetWidth();
1128 int h
= size
.GetHeight();
1129 size
= m_hscroll
->GetSize();
1130 int sh
= size
.GetHeight();
1131 size
= m_vscroll
->GetSize();
1132 int sw
= size
.GetWidth();
1134 if (x
>= w
- sw
- 3 && x
< w
&& y
>= h
- sh
- 3 && y
< h
)
1136 if (x
>= 3 && x
< 10 && y
>= h
- sh
- 3 && y
< h
- 2)
1137 return DSR_VERTICAL_TAB
;
1138 if (x
>= w
- sw
- 3 && x
< w
- 2 && y
>= 3 && y
< 10)
1139 return DSR_HORIZONTAL_TAB
;
1141 return DSR_LEFT_EDGE
;
1143 return DSR_TOP_EDGE
;
1145 return DSR_RIGHT_EDGE
;
1147 return DSR_BOTTOM_EDGE
;
1152 void wxDynamicSashWindowLeaf::ResizeChild(const wxSize
& size
)
1156 if (m_impl
->m_window
->HasFlag(wxDS_MANAGE_SCROLLBARS
))
1158 wxSize best_size
= m_child
->GetBestSize();
1159 if (best_size
.GetWidth() < size
.GetWidth())
1160 best_size
.SetWidth(size
.GetWidth());
1161 if (best_size
.GetHeight() < size
.GetHeight())
1162 best_size
.SetHeight(size
.GetHeight());
1163 m_child
->SetSize(best_size
);
1165 int hpos
= m_hscroll
->GetThumbPosition();
1166 int vpos
= m_vscroll
->GetThumbPosition();
1172 if (hpos
> best_size
.GetWidth() - size
.GetWidth())
1173 hpos
= best_size
.GetWidth() - size
.GetWidth();
1174 if (vpos
> best_size
.GetHeight() - size
.GetHeight())
1175 vpos
= best_size
.GetHeight() - size
.GetHeight();
1177 m_hscroll
->SetScrollbar(hpos
, size
.GetWidth(),
1178 best_size
.GetWidth(), size
.GetWidth());
1179 m_vscroll
->SetScrollbar(vpos
, size
.GetHeight(),
1180 best_size
.GetHeight(), size
.GetHeight());
1182 // Umm, the scrollbars are doing something insane under GTK+ and subtracting
1183 // one from the position I pass in. This works around that.
1184 m_hscroll
->SetThumbPosition(hpos
+ hpos
- m_hscroll
->GetThumbPosition());
1185 m_vscroll
->SetThumbPosition(vpos
+ vpos
- m_vscroll
->GetThumbPosition());
1187 wxPoint pos
= m_child
->GetPosition();
1188 m_viewport
->ScrollWindow(-hpos
- pos
.x
, -vpos
- pos
.y
);
1190 else // !wxDS_MANAGE_SCROLLBARS
1192 m_child
->SetSize(size
);
1198 wxDynamicSashWindowLeaf::FindScrollBar(const wxWindow
*child
, int vert
) const
1200 if (m_child
== child
)
1202 return vert
? m_vscroll
: m_hscroll
;
1208 void wxDynamicSashWindowLeaf::OnSize(wxSizeEvent
&WXUNUSED(event
))
1210 m_impl
->m_container
->Refresh();
1211 ResizeChild(m_viewport
->GetSize());
1214 void wxDynamicSashWindowLeaf::OnPaint(wxPaintEvent
&WXUNUSED(event
))
1216 wxPaintDC
dc(m_impl
->m_container
);
1217 dc
.SetBackground(wxBrush(m_impl
->m_container
->GetBackgroundColour(), wxSOLID
));
1220 wxPen
highlight(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT
), 1, wxSOLID
);
1221 wxPen
shadow(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW
), 1, wxSOLID
);
1222 wxPen
black(*wxBLACK
, 1, wxSOLID
);
1224 wxSize size
= m_impl
->m_container
->GetSize();
1225 int w
= size
.GetWidth();
1226 int h
= size
.GetHeight();
1227 size
= m_hscroll
->GetSize();
1228 int sh
= size
.GetHeight();
1229 size
= m_vscroll
->GetSize();
1230 int sw
= size
.GetWidth();
1233 dc
.DrawLine(1, 1, 1, h
- 2);
1234 dc
.DrawLine(1, 1, w
- 2, 1);
1236 dc
.DrawLine(2, 2, 2, h
- 3);
1237 dc
.DrawLine(2, 2, w
- 3, 2);
1238 dc
.SetPen(highlight
);
1239 dc
.DrawLine(w
- 2, 2, w
- 2, h
- sh
- 2);
1240 dc
.DrawLine(w
- 2, h
- sh
- 2, w
- sw
- 2, h
- sh
- 2);
1241 dc
.DrawLine(w
- sw
- 2, h
- sh
- 2, w
- sw
- 2, h
- 2);
1242 dc
.DrawLine(w
- sw
- 2, h
- 2, 2, h
- 2);
1244 dc
.SetPen(highlight
);
1245 dc
.DrawLine(w
- sw
- 2, 8, w
- sw
- 2, 4);
1246 dc
.DrawLine(w
- sw
- 2, 4, w
- 5, 4);
1248 dc
.DrawLine(w
- 5, 4, w
- 5, 8);
1249 dc
.DrawLine(w
- 5, 8, w
- sw
- 2, 8);
1251 dc
.DrawLine(w
- 4, 3, w
- 4, 9);
1252 dc
.DrawLine(w
- 4, 9, w
- sw
- 3, 9);
1254 dc
.SetPen(highlight
);
1255 dc
.DrawLine(4, h
- 5, 4, h
- sh
- 2);
1256 dc
.DrawLine(4, h
- sh
- 2, 8, h
- sh
- 2);
1258 dc
.DrawLine(8, h
- sh
- 2, 8, h
- 5);
1259 dc
.DrawLine(8, h
- 5, 4, h
- 5);
1261 dc
.DrawLine(9, h
- sh
- 3, 9, h
- 4);
1262 dc
.DrawLine(9, h
- 4, 3, h
- 4);
1264 int cy
= (h
- sh
+ h
- 6) / 2 + 1;
1265 int cx
= (w
- sw
+ w
- 6) / 2 + 1;
1274 for (y
= sy
; y
< h
- 2; y
+= 4)
1276 for (x
= sx
; x
< w
- 2; x
+= 4)
1278 if (x
- cx
>= -(y
- cy
))
1280 dc
.SetPen(highlight
);
1283 dc
.DrawPoint(x
+ 1, y
+ 1);
1289 void wxDynamicSashWindowLeaf::OnScroll(wxScrollEvent
&WXUNUSED(event
))
1291 int nx
= -m_hscroll
->GetThumbPosition();
1292 int ny
= -m_vscroll
->GetThumbPosition();
1296 wxPoint pos
= m_child
->GetPosition();
1298 m_viewport
->ScrollWindow(nx
- pos
.x
, ny
- pos
.y
);
1302 void wxDynamicSashWindowLeaf::OnFocus(wxFocusEvent
&event
)
1304 if ( event
.GetEventObject() == m_hscroll
||
1305 event
.GetEventObject() == m_vscroll
)
1307 m_child
->SetFocus();
1312 void wxDynamicSashWindowLeaf::OnMouseMove(wxMouseEvent
&event
)
1314 if (m_impl
->m_dragging
)
1317 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1319 wxCursor
cursor(wxCURSOR_ARROW
);
1320 if (region
== DSR_HORIZONTAL_TAB
)
1322 cursor
= wxCursor(wxCURSOR_SIZENS
);
1324 else if (region
== DSR_VERTICAL_TAB
)
1326 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1328 else if ((region
== DSR_CORNER
) &&
1329 (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0)
1331 cursor
= wxCursor(wxCURSOR_SIZENWSE
);
1333 else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1334 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
)
1336 if (m_impl
->FindParent(region
))
1338 if (region
== DSR_LEFT_EDGE
|| region
== DSR_RIGHT_EDGE
)
1340 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1344 cursor
= wxCursor(wxCURSOR_SIZENS
);
1349 m_impl
->m_container
->SetCursor(cursor
);
1352 void wxDynamicSashWindowLeaf::OnLeave(wxMouseEvent
&WXUNUSED(event
))
1354 wxCursor
cursor(wxCURSOR_ARROW
);
1355 m_impl
->m_container
->SetCursor(cursor
);
1359 void wxDynamicSashWindowLeaf::OnPress(wxMouseEvent
&event
)
1361 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1363 if ((region
== DSR_CORNER
) && (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) == 0)
1366 if (region
== DSR_HORIZONTAL_TAB
|| region
== DSR_VERTICAL_TAB
|| region
== DSR_CORNER
)
1368 m_impl
->m_dragging
= region
;
1369 m_impl
->m_drag_x
= event
.m_x
;
1370 m_impl
->m_drag_y
= event
.m_y
;
1371 m_impl
->DrawSash(event
.m_x
, event
.m_y
);
1372 m_impl
->m_container
->CaptureMouse();
1374 else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1375 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
)
1377 wxDynamicSashWindowImpl
*parent
= m_impl
->FindParent(region
);
1384 m_impl
->m_container
->ClientToScreen(&x
, &y
);
1385 parent
->m_container
->ScreenToClient(&x
, &y
);
1387 parent
->m_dragging
= parent
->m_split
;
1388 parent
->m_drag_x
= x
;
1389 parent
->m_drag_y
= y
;
1390 parent
->DrawSash(x
, y
);
1391 parent
->m_container
->CaptureMouse();
1396 void wxDynamicSashWindowLeaf::OnRelease(wxMouseEvent
&WXUNUSED(event
))
1400 void wxDynamicSashWindowLeaf::OnReparent(wxEvent
&WXUNUSED(event
))
1404 m_child
->Reparent(m_viewport
);
1407 ResizeChild(m_viewport
->GetSize());
1410 // ============================================================================
1412 // ============================================================================
1414 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent()
1416 m_eventObject
= NULL
;
1417 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1420 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent(wxObject
*object
)
1422 m_eventObject
= object
;
1423 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1426 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashSplitEvent
, wxCommandEvent
)
1428 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent()
1430 m_eventObject
= NULL
;
1431 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1434 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent(wxObject
*object
)
1436 m_eventObject
= object
;
1437 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1440 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashUnifyEvent
, wxCommandEvent
)
1443 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent()
1445 m_eventObject
= NULL
;
1446 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1449 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(wxObject
*object
)
1451 m_eventObject
= object
;
1452 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1455 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
)
1460 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashReparentEvent
, wxEvent
)