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(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(wxSize size
)
1166 if (m_impl
->m_window
->GetWindowStyle() & wxDS_MANAGE_SCROLLBARS
)
1168 m_child
->SetSize(size
);
1169 wxSize best_size
= m_child
->GetBestSize();
1170 if (best_size
.GetWidth() < size
.GetWidth())
1172 best_size
.SetWidth(size
.GetWidth());
1174 if (best_size
.GetHeight() < size
.GetHeight())
1176 best_size
.SetHeight(size
.GetHeight());
1178 m_child
->SetSize(best_size
);
1180 int hpos
= m_hscroll
->GetThumbPosition();
1181 int vpos
= m_vscroll
->GetThumbPosition();
1187 if (hpos
> best_size
.GetWidth() - size
.GetWidth())
1188 hpos
= best_size
.GetWidth() - size
.GetWidth();
1189 if (vpos
> best_size
.GetHeight() - size
.GetHeight())
1190 vpos
= best_size
.GetHeight() - size
.GetHeight();
1192 m_hscroll
->SetScrollbar(hpos
, size
.GetWidth(),
1193 best_size
.GetWidth(), size
.GetWidth());
1194 m_vscroll
->SetScrollbar(vpos
, size
.GetHeight(),
1195 best_size
.GetHeight(), size
.GetHeight());
1197 // Umm, the scrollbars are doing something insane under GTK+ and subtracting
1198 // one from the position I pass in. This works around that.
1199 m_hscroll
->SetThumbPosition(hpos
+ hpos
- m_hscroll
->GetThumbPosition());
1200 m_vscroll
->SetThumbPosition(vpos
+ vpos
- m_vscroll
->GetThumbPosition());
1202 wxPoint pos
= m_child
->GetPosition();
1203 m_viewport
->ScrollWindow(-hpos
- pos
.x
, -vpos
- pos
.y
);
1207 m_child
->SetSize(size
);
1213 wxDynamicSashWindowLeaf::FindScrollBar(const wxWindow
*child
, int vert
) const
1215 if (m_child
== child
)
1217 return vert
? m_vscroll
: m_hscroll
;
1223 void wxDynamicSashWindowLeaf::OnSize(wxSizeEvent
&WXUNUSED(event
))
1225 m_impl
->m_container
->Refresh();
1226 ResizeChild(m_viewport
->GetSize());
1229 void wxDynamicSashWindowLeaf::OnPaint(wxPaintEvent
&WXUNUSED(event
))
1231 wxPaintDC
dc(m_impl
->m_container
);
1232 dc
.SetBackground(wxBrush(m_impl
->m_container
->GetBackgroundColour(), wxSOLID
));
1235 wxPen
highlight(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT
), 1, wxSOLID
);
1236 wxPen
shadow(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW
), 1, wxSOLID
);
1237 wxPen
black(*wxBLACK
, 1, wxSOLID
);
1239 wxSize size
= m_impl
->m_container
->GetSize();
1240 int w
= size
.GetWidth();
1241 int h
= size
.GetHeight();
1242 size
= m_hscroll
->GetSize();
1243 int sh
= size
.GetHeight();
1244 size
= m_vscroll
->GetSize();
1245 int sw
= size
.GetWidth();
1248 dc
.DrawLine(1, 1, 1, h
- 2);
1249 dc
.DrawLine(1, 1, w
- 2, 1);
1251 dc
.DrawLine(2, 2, 2, h
- 3);
1252 dc
.DrawLine(2, 2, w
- 3, 2);
1253 dc
.SetPen(highlight
);
1254 dc
.DrawLine(w
- 2, 2, w
- 2, h
- sh
- 2);
1255 dc
.DrawLine(w
- 2, h
- sh
- 2, w
- sw
- 2, h
- sh
- 2);
1256 dc
.DrawLine(w
- sw
- 2, h
- sh
- 2, w
- sw
- 2, h
- 2);
1257 dc
.DrawLine(w
- sw
- 2, h
- 2, 2, h
- 2);
1259 dc
.SetPen(highlight
);
1260 dc
.DrawLine(w
- sw
- 2, 8, w
- sw
- 2, 4);
1261 dc
.DrawLine(w
- sw
- 2, 4, w
- 5, 4);
1263 dc
.DrawLine(w
- 5, 4, w
- 5, 8);
1264 dc
.DrawLine(w
- 5, 8, w
- sw
- 2, 8);
1266 dc
.DrawLine(w
- 4, 3, w
- 4, 9);
1267 dc
.DrawLine(w
- 4, 9, w
- sw
- 3, 9);
1269 dc
.SetPen(highlight
);
1270 dc
.DrawLine(4, h
- 5, 4, h
- sh
- 2);
1271 dc
.DrawLine(4, h
- sh
- 2, 8, h
- sh
- 2);
1273 dc
.DrawLine(8, h
- sh
- 2, 8, h
- 5);
1274 dc
.DrawLine(8, h
- 5, 4, h
- 5);
1276 dc
.DrawLine(9, h
- sh
- 3, 9, h
- 4);
1277 dc
.DrawLine(9, h
- 4, 3, h
- 4);
1279 int cy
= (h
- sh
+ h
- 6) / 2 + 1;
1280 int cx
= (w
- sw
+ w
- 6) / 2 + 1;
1289 for (y
= sy
; y
< h
- 2; y
+= 4)
1291 for (x
= sx
; x
< w
- 2; x
+= 4)
1293 if (x
- cx
>= -(y
- cy
))
1295 dc
.SetPen(highlight
);
1298 dc
.DrawPoint(x
+ 1, y
+ 1);
1304 void wxDynamicSashWindowLeaf::OnScroll(wxScrollEvent
&WXUNUSED(event
))
1306 int nx
= -m_hscroll
->GetThumbPosition();
1307 int ny
= -m_vscroll
->GetThumbPosition();
1311 wxPoint pos
= m_child
->GetPosition();
1313 m_viewport
->ScrollWindow(nx
- pos
.x
, ny
- pos
.y
);
1317 void wxDynamicSashWindowLeaf::OnFocus(wxFocusEvent
&event
)
1319 if ( event
.GetEventObject() == m_hscroll
||
1320 event
.GetEventObject() == m_vscroll
)
1322 m_child
->SetFocus();
1327 void wxDynamicSashWindowLeaf::OnMouseMove(wxMouseEvent
&event
)
1329 if (m_impl
->m_dragging
)
1332 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1334 wxCursor
cursor(wxCURSOR_ARROW
);
1335 if (region
== DSR_HORIZONTAL_TAB
)
1337 cursor
= wxCursor(wxCURSOR_SIZENS
);
1339 else if (region
== DSR_VERTICAL_TAB
)
1341 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1343 else if ((region
== DSR_CORNER
) &&
1344 (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0)
1346 cursor
= wxCursor(wxCURSOR_SIZENWSE
);
1348 else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1349 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
)
1351 if (m_impl
->FindParent(region
))
1353 if (region
== DSR_LEFT_EDGE
|| region
== DSR_RIGHT_EDGE
)
1355 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1359 cursor
= wxCursor(wxCURSOR_SIZENS
);
1364 m_impl
->m_container
->SetCursor(cursor
);
1367 void wxDynamicSashWindowLeaf::OnLeave(wxMouseEvent
&WXUNUSED(event
))
1369 wxCursor
cursor(wxCURSOR_ARROW
);
1370 m_impl
->m_container
->SetCursor(cursor
);
1374 void wxDynamicSashWindowLeaf::OnPress(wxMouseEvent
&event
)
1376 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1378 if ((region
== DSR_CORNER
) && (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) == 0)
1381 if (region
== DSR_HORIZONTAL_TAB
|| region
== DSR_VERTICAL_TAB
|| region
== DSR_CORNER
)
1383 m_impl
->m_dragging
= region
;
1384 m_impl
->m_drag_x
= event
.m_x
;
1385 m_impl
->m_drag_y
= event
.m_y
;
1386 m_impl
->DrawSash(event
.m_x
, event
.m_y
);
1387 m_impl
->m_container
->CaptureMouse();
1389 else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1390 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
)
1392 wxDynamicSashWindowImpl
*parent
= m_impl
->FindParent(region
);
1399 m_impl
->m_container
->ClientToScreen(&x
, &y
);
1400 parent
->m_container
->ScreenToClient(&x
, &y
);
1402 parent
->m_dragging
= parent
->m_split
;
1403 parent
->m_drag_x
= x
;
1404 parent
->m_drag_y
= y
;
1405 parent
->DrawSash(x
, y
);
1406 parent
->m_container
->CaptureMouse();
1411 void wxDynamicSashWindowLeaf::OnRelease(wxMouseEvent
&WXUNUSED(event
))
1415 void wxDynamicSashWindowLeaf::OnReparent(wxEvent
&WXUNUSED(event
))
1419 m_child
->Reparent(m_viewport
);
1422 ResizeChild(m_viewport
->GetSize());
1425 // ============================================================================
1427 // ============================================================================
1429 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent()
1431 m_eventObject
= NULL
;
1432 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1435 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent(wxObject
*object
)
1437 m_eventObject
= object
;
1438 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1441 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashSplitEvent
, wxCommandEvent
)
1443 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent()
1445 m_eventObject
= NULL
;
1446 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1449 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent(wxObject
*object
)
1451 m_eventObject
= object
;
1452 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1455 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashUnifyEvent
, wxCommandEvent
)
1458 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent()
1460 m_eventObject
= NULL
;
1461 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1464 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(wxObject
*object
)
1466 m_eventObject
= object
;
1467 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1470 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
)
1475 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashReparentEvent
, wxEvent
)