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"
22 // for all others, include the necessary headers (this file is usually all you
23 // need because it includes almost all "standard" wxWidgets headers)
32 #include "wx/gizmos/dynamicsash.h"
35 const wxChar
* wxDynamicSashWindowNameStr
= wxT("dynamicSashWindow");
39 wxDynamicSashWindow works by internally storing a tree of Implementation
40 objects (wxDynamicSsahWindowImpl) and Leaf objects
41 (wxDynamicSashWindowLeaf). The wxDynamicSashWindow has a pointer to one
42 implementation, and each implementation either has a pointer to a one
43 leaf (m_leaf) or a pointer to two children implementation objects
44 (m_child). The leaves each are responsible for drawing the frame and
45 decorations around one user-provided views and for responding to mouse
48 A resulting tree might look something like this:
52 +- wxDynamicSashWindowImpl
54 +- wxDynamicSashWindowLeaf
58 +- wxDynamicSashWindowImpl
60 +- wxDynamicSashWindowLeaf
64 +- wxDynamicSashWindowLeaf
68 Each time a split occurs, one of the implementation objects removes its
69 leaf, generates two new implementation object children, each with a new
70 leaf, and reparents the user view which was connected to its old leaf
71 to be one of the new leaf's user view, and sends a Split event to the
72 user view in the hopes that it will generate a new user view for the
75 When a unification ocurrs, an implementation object is replaced by one
76 of its children, and the tree of its other child is pruned.
78 One quirk is that the top-level implementation object (m_top) always
79 keeps a pointer to the implementation object where a new child is needed.
80 (m_add_child_target). This is so that when a new uesr view is added
81 to the hierarchy, AddChild() is able to reparent the new user view to
82 the correct implementation object's leaf.
86 #include "wx/dcmemory.h"
87 #include "wx/dcscreen.h"
88 #include "wx/layout.h"
89 #include "wx/scrolbar.h"
90 #include "wx/settings.h"
93 const wxEventType wxEVT_DYNAMIC_SASH_SPLIT
= wxNewEventType();
94 const wxEventType wxEVT_DYNAMIC_SASH_UNIFY
= wxNewEventType();
95 const wxEventType wxEVT_DYNAMIC_SASH_REPARENT
= wxNewEventType();
97 enum DynamicSashRegion
111 wxDynamicSashReparentEvent is generated by the AddChild() method of
112 wxDynamicSashWindow when it wants a Leaf to reparent a user view window
113 to its viewport at some time in the future. We can't reparent the window
114 immediately, because switching parents in AddChild() confuses the wxWindow
115 class. Instead, we queue up this event, and the window is actually
116 reparented the next time we process events in the idle loop.
118 class wxDynamicSashReparentEvent
: public wxEvent
121 wxDynamicSashReparentEvent();
122 wxDynamicSashReparentEvent(wxObject
*object
);
123 wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
);
125 virtual wxEvent
* Clone() const { return new wxDynamicSashReparentEvent(*this); }
127 DECLARE_DYNAMIC_CLASS(wxDynamicSashReparentEvent
)
131 class wxDynamicSashWindowImpl
: public wxEvtHandler
134 wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
);
135 ~wxDynamicSashWindowImpl();
138 void AddChild(wxWindow
*window
);
139 void DrawSash(int x
, int y
) const;
140 void ConstrainChildren(int px
, int py
);
141 void Split(int x
, int y
);
142 void Unify(int panel
);
143 void Resize(int x
, int y
);
144 wxDynamicSashWindowImpl
*FindParent(DynamicSashRegion side
) const;
145 wxDynamicSashWindowImpl
*FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
146 wxDynamicSashWindowImpl
*sash_b
) const;
147 wxWindow
*FindFrame() const;
148 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
150 void OnSize(wxSizeEvent
&event
);
151 void OnPaint(wxPaintEvent
&event
);
152 void OnMouseMove(wxMouseEvent
&event
);
153 void OnLeave(wxMouseEvent
&event
);
154 void OnPress(wxMouseEvent
&event
);
155 void OnRelease(wxMouseEvent
&event
);
157 wxDynamicSashWindow
*m_window
;
158 wxDynamicSashWindowImpl
*m_add_child_target
;
160 /* This is the window we are responsible for managing. Either of
161 leaf or our children are in this window. For the top level
162 implementation object, this is the same as m_window.
163 Otherwise it is a window we've created an will destroy when we
165 wxWindow
*m_container
;
167 wxDynamicSashWindowImpl
*m_parent
;
168 wxDynamicSashWindowImpl
*m_top
;
169 wxDynamicSashWindowImpl
*m_child
[2];
171 class wxDynamicSashWindowLeaf
*m_leaf
;
173 /* If the implementation is split horizontally or vertically, m_split
174 is set to DSR_HORIZONTAL_TAB or DSR_VERTICAL_TAB, respectively.
175 Otherwise it is set to DSR_NONE. */
176 DynamicSashRegion m_split
;
178 /* These are used to keep track of a sash as it is being dragged, for
179 drawing the user interface correctly. */
180 DynamicSashRegion m_dragging
;
181 int m_drag_x
, m_drag_y
;
184 class wxDynamicSashWindowLeaf
: public wxEvtHandler
187 wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
);
188 ~wxDynamicSashWindowLeaf();
191 void AddChild(wxWindow
*window
);
192 DynamicSashRegion
GetRegion(int x
, int y
);
193 void ResizeChild(const wxSize
& size
);
194 wxScrollBar
*FindScrollBar(const wxWindow
*child
, int vert
) const;
196 void OnSize(wxSizeEvent
&event
);
197 void OnViewSize(wxSizeEvent
&event
);
198 void OnPaint(wxPaintEvent
&event
);
199 void OnScroll(wxScrollEvent
&event
);
200 void OnFocus(wxFocusEvent
&event
);
201 void OnMouseMove(wxMouseEvent
&event
);
202 void OnLeave(wxMouseEvent
&event
);
203 void OnPress(wxMouseEvent
&event
);
204 void OnRelease(wxMouseEvent
&event
);
205 void OnReparent(wxEvent
&event
);
207 wxDynamicSashWindowImpl
*m_impl
;
209 wxScrollBar
*m_vscroll
,
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
,
221 // ============================================================================
222 // wxDynamicSashWindow
223 // ============================================================================
225 wxDynamicSashWindow::wxDynamicSashWindow()
230 wxDynamicSashWindow::wxDynamicSashWindow(wxWindow
*parent
,
235 const wxString
& name
)
238 Create(parent
, id
, pos
, size
, style
, name
);
241 wxDynamicSashWindow::~wxDynamicSashWindow()
243 SetEventHandler(this);
247 bool wxDynamicSashWindow::Create(wxWindow
*parent
,
252 const wxString
& name
)
257 if (!wxWindow::Create(parent
, id
, pos
, size
, style
, name
))
260 m_impl
= new wxDynamicSashWindowImpl(this);
264 if (!m_impl
->Create())
274 void wxDynamicSashWindow::AddChild(wxWindowBase
*child
)
276 wxWindow::AddChild(child
);
278 m_impl
->AddChild(wxDynamicCast(child
, wxWindow
));
281 wxScrollBar
*wxDynamicSashWindow::GetHScrollBar(const wxWindow
*child
) const
283 return m_impl
->FindScrollBar(child
, 0);
286 wxScrollBar
*wxDynamicSashWindow::GetVScrollBar(const wxWindow
*child
) const
288 return m_impl
->FindScrollBar(child
, 1);
291 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashWindow
, wxWindow
)
294 // ============================================================================
295 // wxDynamicSashWindowImpl
296 // ============================================================================
298 wxDynamicSashWindowImpl::wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
)
301 m_add_child_target
= this;
309 m_dragging
= DSR_NONE
;
313 wxDynamicSashWindowImpl::~wxDynamicSashWindowImpl()
322 if (m_container
!= m_window
&& m_container
)
324 m_container
->SetEventHandler(m_container
);
325 m_container
->Destroy();
329 bool wxDynamicSashWindowImpl::Create()
332 m_container
= m_window
;
334 wxCursor
cursor(wxCURSOR_ARROW
);
335 m_container
->SetCursor(cursor
);
337 m_leaf
= new wxDynamicSashWindowLeaf(this);
341 if (!m_leaf
->Create())
348 m_container
->SetEventHandler(this);
350 Connect(wxEVT_SIZE
, wxSizeEventHandler(wxDynamicSashWindowImpl::OnSize
));
351 Connect(wxEVT_PAINT
, wxPaintEventHandler(wxDynamicSashWindowImpl::OnPaint
));
352 Connect(wxEVT_MOTION
,
353 wxMouseEventHandler(wxDynamicSashWindowImpl::OnMouseMove
));
354 Connect(wxEVT_ENTER_WINDOW
,
355 wxMouseEventHandler(wxDynamicSashWindowImpl::OnMouseMove
));
356 Connect(wxEVT_LEAVE_WINDOW
,
357 wxMouseEventHandler(wxDynamicSashWindowImpl::OnLeave
));
358 Connect(wxEVT_LEFT_DOWN
,
359 wxMouseEventHandler(wxDynamicSashWindowImpl::OnPress
));
360 Connect(wxEVT_LEFT_UP
,
361 wxMouseEventHandler(wxDynamicSashWindowImpl::OnRelease
));
366 void wxDynamicSashWindowImpl::AddChild(wxWindow
*window
)
368 if (m_add_child_target
&& m_add_child_target
->m_leaf
)
369 m_add_child_target
->m_leaf
->AddChild(window
);
372 void wxDynamicSashWindowImpl::DrawSash(int x
, int y
) const
377 dc
.StartDrawingOnTop(m_container
);
381 bdc
.SelectObject(bmp
);
382 bdc
.DrawRectangle(-1, -1, 10, 10);
383 for (i
= 0; i
< 8; i
++)
385 for (j
= 0; j
< 8; j
++)
394 dc
.SetLogicalFunction(wxXOR
);
396 if ((m_dragging
== DSR_CORNER
) &&
397 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0)
402 m_container
->ClientToScreen(&cx
, &cy
);
403 m_container
->ClientToScreen(&x
, &y
);
405 if (cx
< x
&& cy
< y
)
407 dc
.DrawRectangle(cx
- 2, cy
- 2, x
- cx
+ 4, 4);
408 dc
.DrawRectangle(x
- 2, cy
+ 2, 4, y
- cy
);
409 dc
.DrawRectangle(cx
- 2, cy
+ 2, 4, y
- cy
);
410 dc
.DrawRectangle(cx
+ 2, y
- 2, x
- cx
- 4, 4);
416 m_container
->GetClientSize(&body_w
, &body_h
);
427 if (m_dragging
== DSR_HORIZONTAL_TAB
)
432 m_container
->ClientToScreen(&x
, &y
);
435 w
= body_w
; h
= body_h
;
437 if (m_dragging
== DSR_HORIZONTAL_TAB
)
438 dc
.DrawRectangle(x
, y
- 2, w
, 4);
440 dc
.DrawRectangle(x
- 2, y
, 4, h
);
443 dc
.EndDrawingOnTop();
446 wxDynamicSashWindowImpl
*
447 wxDynamicSashWindowImpl::FindParent(DynamicSashRegion side
) const
449 if (m_parent
== NULL
)
452 if (m_parent
->m_split
== DSR_HORIZONTAL_TAB
)
454 if (side
== DSR_TOP_EDGE
&& m_parent
->m_child
[1] == this)
456 if (side
== DSR_BOTTOM_EDGE
&& m_parent
->m_child
[0] == this)
459 else if (m_parent
->m_split
== DSR_VERTICAL_TAB
)
461 if (side
== DSR_LEFT_EDGE
&& m_parent
->m_child
[1] == this)
463 if (side
== DSR_RIGHT_EDGE
&& m_parent
->m_child
[0] == this)
467 return m_parent
->FindParent(side
);
470 wxDynamicSashWindowImpl
*
471 wxDynamicSashWindowImpl::FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
472 wxDynamicSashWindowImpl
*sash_b
) const
475 win
= sash_a
->m_container
->GetParent();
476 while (win
&& !win
->IsTopLevel())
478 if (win
== sash_b
->m_container
)
481 win
= win
->GetParent();
488 wxWindow
*wxDynamicSashWindowImpl::FindFrame() const
492 win
= m_window
->GetParent();
493 while (win
&& !win
->IsTopLevel()
495 && ! wxIsKindOf(win
, wxMDIChildFrame
) // not top-level but still a frame
499 win
= win
->GetParent();
506 wxDynamicSashWindowImpl::FindScrollBar(const wxWindow
*child
, int vert
) const
508 if (m_child
[0] == NULL
&& m_leaf
== NULL
)
513 return m_leaf
->FindScrollBar(child
, vert
);
516 wxScrollBar
*ret
= m_child
[0]->FindScrollBar(child
, vert
);
518 ret
= m_child
[1]->FindScrollBar(child
, vert
);
523 void wxDynamicSashWindowImpl::ConstrainChildren(int px
, int py
)
525 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
526 layout
->left
.SameAs(m_container
, wxLeft
);
527 layout
->top
.SameAs(m_container
, wxTop
);
528 if (m_split
== DSR_HORIZONTAL_TAB
)
530 layout
->right
.SameAs(m_container
, wxRight
);
531 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
535 layout
->bottom
.SameAs(m_container
, wxBottom
);
536 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
538 m_child
[0]->m_container
->SetConstraints(layout
);
540 layout
= new wxLayoutConstraints();
541 layout
->right
.SameAs(m_container
, wxRight
);
542 layout
->bottom
.SameAs(m_container
, wxBottom
);
543 if (m_split
== DSR_HORIZONTAL_TAB
)
545 layout
->top
.Below(m_child
[0]->m_container
, 1);
546 layout
->left
.SameAs(m_container
, wxLeft
);
550 layout
->left
.RightOf(m_child
[0]->m_container
, 1);
551 layout
->top
.SameAs(m_container
, wxTop
);
553 m_child
[1]->m_container
->SetConstraints(layout
);
556 void wxDynamicSashWindowImpl::Unify(int panel
)
558 int other
= panel
== 0 ? 1 : 0;
560 if (m_child
[panel
]->m_leaf
)
562 wxDynamicSashWindowImpl
*child
[2];
564 child
[0] = m_child
[0];
565 child
[1] = m_child
[1];
567 m_child
[0] = m_child
[1] = NULL
;
569 m_leaf
= new wxDynamicSashWindowLeaf(this);
571 m_leaf
->m_child
= child
[panel
]->m_leaf
->m_child
;
573 m_leaf
->m_vscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_vscroll
->GetThumbPosition(),
574 child
[panel
]->m_leaf
->m_vscroll
->GetThumbSize(),
575 child
[panel
]->m_leaf
->m_vscroll
->GetRange(),
576 child
[panel
]->m_leaf
->m_vscroll
->GetPageSize());
577 m_leaf
->m_hscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_hscroll
->GetThumbPosition(),
578 child
[panel
]->m_leaf
->m_hscroll
->GetThumbSize(),
579 child
[panel
]->m_leaf
->m_hscroll
->GetRange(),
580 child
[panel
]->m_leaf
->m_hscroll
->GetPageSize());
581 m_add_child_target
= NULL
;
582 wxDynamicSashReparentEvent
event(m_leaf
);
583 m_leaf
->ProcessEvent(event
);
590 wxDynamicSashUnifyEvent
unify(m_leaf
->m_child
);
591 m_leaf
->m_child
->ProcessEvent(unify
);
595 m_split
= m_child
[panel
]->m_split
;
597 delete m_child
[other
];
599 wxDynamicSashWindowImpl
*child_panel
= m_child
[panel
];
600 m_child
[0] = child_panel
->m_child
[0];
601 m_child
[1] = child_panel
->m_child
[1];
603 m_child
[0]->m_parent
= this;
604 m_child
[1]->m_parent
= this;
606 m_add_child_target
= NULL
;
607 m_child
[0]->m_container
->Reparent(m_container
);
608 m_child
[1]->m_container
->Reparent(m_container
);
610 child_panel
->m_child
[0] = child_panel
->m_child
[1] = NULL
;
613 wxSize size
= m_container
->GetSize();
614 wxSize child_size
= m_child
[0]->m_container
->GetSize();
616 ConstrainChildren(child_size
.GetWidth() * 100 / size
.GetWidth(),
617 child_size
.GetHeight() * 100 / size
.GetHeight());
619 m_container
->Layout();
623 void wxDynamicSashWindowImpl::Split(int px
, int py
)
626 m_add_child_target
= NULL
;
628 m_child
[0] = new wxDynamicSashWindowImpl(m_window
);
629 m_child
[0]->m_container
= new wxWindow(m_container
, wxID_ANY
);
630 m_child
[0]->m_parent
= this;
631 m_child
[0]->m_top
= m_top
;
632 m_child
[0]->Create();
635 m_leaf
->m_child
->Reparent(m_container
);
636 m_child
[0]->AddChild(m_leaf
->m_child
);
639 m_child
[1] = new wxDynamicSashWindowImpl(m_window
);
640 m_child
[1]->m_container
= new wxWindow(m_container
, wxID_ANY
);
641 m_child
[1]->m_parent
= this;
642 m_child
[1]->m_top
= m_top
;
643 m_child
[1]->Create();
645 m_split
= m_dragging
;
646 ConstrainChildren(px
, py
);
648 m_top
->m_add_child_target
= m_child
[1];
649 wxDynamicSashSplitEvent
split(m_child
[0]->m_leaf
->m_child
);
650 m_child
[0]->m_leaf
->m_child
->ProcessEvent(split
);
652 m_child
[0]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
653 m_leaf
->m_vscroll
->GetThumbSize(),
654 m_leaf
->m_vscroll
->GetRange(),
655 m_leaf
->m_vscroll
->GetPageSize());
656 m_child
[0]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
657 m_leaf
->m_hscroll
->GetThumbSize(),
658 m_leaf
->m_hscroll
->GetRange(),
659 m_leaf
->m_hscroll
->GetPageSize());
660 m_child
[1]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
661 m_leaf
->m_vscroll
->GetThumbSize(),
662 m_leaf
->m_vscroll
->GetRange(),
663 m_leaf
->m_vscroll
->GetPageSize());
664 m_child
[1]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
665 m_leaf
->m_hscroll
->GetThumbSize(),
666 m_leaf
->m_hscroll
->GetRange(),
667 m_leaf
->m_hscroll
->GetPageSize());
671 m_container
->Layout();
675 /* This code is called when you finish resizing a view by dragging the
676 corner tab, but I think this implementation is lousy and will surprise
677 the user more often than it will do what they are trying to do. What
678 I really need to be doing here is do a rewrite such that *no* sashes
679 move except the ones immediately to the bottom and right of this window,
680 and handle the case where you resize a window larger than it's neighbors
681 by destroying the neighbors.
683 But this will do for now. */
684 void wxDynamicSashWindowImpl::Resize(int x
, int y
)
686 wxDynamicSashWindowImpl
*h_parent
= FindParent(DSR_BOTTOM_EDGE
);
687 wxDynamicSashWindowImpl
*v_parent
= FindParent(DSR_RIGHT_EDGE
);
690 wxWindow
*frame
= FindFrame();
699 m_container
->ClientToScreen(NULL
, &y
);
700 h_parent
->m_container
->ScreenToClient(NULL
, &y
);
702 int py
= (int)((y
* 100) / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
706 wxDynamicSashWindowImpl
*ho_parent
= FindParent(DSR_TOP_EDGE
);
710 if (FindUpperParent(h_parent
, ho_parent
) == ho_parent
)
716 py
= (int)((ho_parent
->m_child
[0]->m_container
->GetSize().GetHeight() * 100)
717 / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
718 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
719 h_parent
->m_container
, wxHeight
, py
);
721 h_parent
= ho_parent
;
736 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
737 h_parent
->m_container
, wxHeight
, py
);
738 h_parent
->m_container
->Layout();
744 h_parent
= FindParent(DSR_TOP_EDGE
);
748 int py
= (int)((y
* 100) /
749 (h_parent
->m_container
->GetSize().GetHeight() +
750 y
- m_container
->GetSize().GetHeight()) + 0.5);
762 wxSize size
= frame
->GetSize();
763 frame
->SetSize(size
.GetWidth(), size
.GetHeight() + y
- m_container
->GetSize().GetHeight());
769 m_container
->ClientToScreen(&x
, NULL
);
770 v_parent
->m_container
->ScreenToClient(&x
, NULL
);
772 int px
= (int)((x
* 100) / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
776 wxDynamicSashWindowImpl
*vo_parent
= FindParent(DSR_LEFT_EDGE
);
780 if (FindUpperParent(v_parent
, vo_parent
) == vo_parent
)
786 px
= (int)((vo_parent
->m_child
[0]->m_container
->GetSize().GetWidth() * 100)
787 / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
788 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
789 v_parent
->m_container
, wxWidth
, px
);
791 v_parent
= vo_parent
;
806 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
807 v_parent
->m_container
, wxWidth
, px
);
808 v_parent
->m_container
->Layout();
814 v_parent
= FindParent(DSR_LEFT_EDGE
);
818 int px
= (int)((x
* 100) /
819 (v_parent
->m_container
->GetSize().GetWidth() +
820 x
- m_container
->GetSize().GetWidth()) + 0.5);
832 wxSize size
= frame
->GetSize();
833 frame
->SetSize(size
.GetWidth() + x
- m_container
->GetSize().GetWidth(), size
.GetHeight());
837 if (h_unify
!= -1 && v_unify
!= -1)
839 wxDynamicSashWindowImpl
*parent
= FindUpperParent(h_parent
, v_parent
);
841 if (parent
== h_parent
)
843 h_parent
->Unify(h_unify
);
847 v_parent
->Unify(v_unify
);
850 else if (h_unify
!= -1)
852 h_parent
->Unify(h_unify
);
854 else if (v_unify
!= -1)
856 v_parent
->Unify(v_unify
);
861 void wxDynamicSashWindowImpl::OnSize(wxSizeEvent
&event
)
863 m_container
->Layout();
866 m_leaf
->OnSize(event
);
869 void wxDynamicSashWindowImpl::OnPaint(wxPaintEvent
&event
)
872 m_leaf
->OnPaint(event
);
875 wxPaintDC
dc(m_container
);
876 dc
.SetBackground(wxBrush(m_container
->GetBackgroundColour(), wxSOLID
));
881 void wxDynamicSashWindowImpl::OnMouseMove(wxMouseEvent
&event
)
885 DrawSash(m_drag_x
, m_drag_y
);
886 m_drag_x
= event
.m_x
; m_drag_y
= event
.m_y
;
887 DrawSash(m_drag_x
, m_drag_y
);
891 m_leaf
->OnMouseMove(event
);
895 void wxDynamicSashWindowImpl::OnLeave(wxMouseEvent
&event
)
898 m_leaf
->OnLeave(event
);
901 void wxDynamicSashWindowImpl::OnPress(wxMouseEvent
&event
)
905 m_leaf
->OnPress(event
);
909 m_dragging
= m_split
;
910 m_drag_x
= event
.m_x
;
911 m_drag_y
= event
.m_y
;
912 DrawSash(m_drag_x
, m_drag_y
);
913 m_container
->CaptureMouse();
917 void wxDynamicSashWindowImpl::OnRelease(wxMouseEvent
&event
)
919 if ((m_dragging
== DSR_CORNER
) &&
920 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0)
922 DrawSash(m_drag_x
, m_drag_y
);
923 m_container
->ReleaseMouse();
925 Resize(event
.m_x
, event
.m_y
);
927 m_dragging
= DSR_NONE
;
931 DrawSash(m_drag_x
, m_drag_y
);
932 m_container
->ReleaseMouse();
934 wxSize size
= m_container
->GetSize();
935 int px
= (int)((event
.m_x
* 100) / size
.GetWidth() + 0.5);
936 int py
= (int)((event
.m_y
* 100) / size
.GetHeight() + 0.5);
938 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
>= 10 && py
<= 90)
939 || (m_dragging
== DSR_VERTICAL_TAB
&& px
>= 10 && px
<= 90))
941 if (m_child
[0] == NULL
)
947 /* It would be nice if moving *this* sash didn't implicitly move
948 the sashes of our children (if any). But this will do. */
949 wxLayoutConstraints
*layout
= m_child
[0]->m_container
->GetConstraints();
950 if (m_split
== DSR_HORIZONTAL_TAB
)
952 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
956 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
958 m_container
->Layout();
963 if (m_child
[0] != NULL
)
965 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
<= 10)
966 || (m_dragging
== DSR_VERTICAL_TAB
&& px
<= 10))
978 if (m_split
== DSR_HORIZONTAL_TAB
)
979 cursor
= wxCursor(wxCURSOR_SIZENS
);
980 else if (m_split
== DSR_VERTICAL_TAB
)
981 cursor
= wxCursor(wxCURSOR_SIZEWE
);
983 cursor
= wxCursor(wxCURSOR_ARROW
);
985 m_container
->SetCursor(cursor
);
987 m_dragging
= DSR_NONE
;
991 m_leaf
->OnRelease(event
);
995 // ============================================================================
996 // wxDynamicSashWindowLeaf
997 // ============================================================================
999 wxDynamicSashWindowLeaf::wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
)
1009 wxDynamicSashWindowLeaf::~wxDynamicSashWindowLeaf()
1011 m_hscroll
->SetEventHandler(m_hscroll
);
1012 m_vscroll
->SetEventHandler(m_vscroll
);
1014 m_hscroll
->Destroy();
1015 m_vscroll
->Destroy();
1016 m_viewport
->Destroy();
1019 bool wxDynamicSashWindowLeaf::Create()
1021 m_hscroll
= new wxScrollBar();
1022 m_vscroll
= new wxScrollBar();
1023 m_viewport
= new wxWindow();
1025 wxDynamicSashWindowImpl
*add_child_target
= m_impl
->m_add_child_target
;
1026 m_impl
->m_add_child_target
= NULL
;
1028 bool success
= m_hscroll
->Create(m_impl
->m_container
, wxID_ANY
,
1029 wxDefaultPosition
, wxDefaultSize
,
1032 success
= m_vscroll
->Create(m_impl
->m_container
, wxID_ANY
,
1033 wxDefaultPosition
, wxDefaultSize
,
1036 success
= m_viewport
->Create(m_impl
->m_container
, wxID_ANY
);
1040 m_impl
->m_add_child_target
= add_child_target
;
1042 wxCursor
cursor(wxCURSOR_ARROW
);
1043 m_hscroll
->SetCursor(cursor
);
1044 m_vscroll
->SetCursor(cursor
);
1045 m_viewport
->SetCursor(cursor
);
1047 // the viewport must resize its child when it is itself resized, but it's
1048 // more convenient to do it in our own method instead of deriving a new
1049 // class just for this: this is why we pass this as last Connect() argument
1050 m_viewport
->Connect(wxEVT_SIZE
,
1051 wxSizeEventHandler(wxDynamicSashWindowLeaf::OnViewSize
),
1054 Connect(wxEVT_DYNAMIC_SASH_REPARENT
,
1055 wxEventHandler(wxDynamicSashWindowLeaf::OnReparent
));
1057 if (m_impl
->m_window
->GetWindowStyle() & wxDS_MANAGE_SCROLLBARS
)
1059 m_hscroll
->SetEventHandler(this);
1060 m_vscroll
->SetEventHandler(this);
1062 Connect(wxEVT_SET_FOCUS
,
1063 wxFocusEventHandler(wxDynamicSashWindowLeaf::OnFocus
));
1064 Connect(wxEVT_SCROLL_TOP
,
1065 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1066 Connect(wxEVT_SCROLL_BOTTOM
,
1067 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1068 Connect(wxEVT_SCROLL_LINEUP
,
1069 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1070 Connect(wxEVT_SCROLL_LINEDOWN
,
1071 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1072 Connect(wxEVT_SCROLL_PAGEUP
,
1073 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1074 Connect(wxEVT_SCROLL_PAGEDOWN
,
1075 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1076 Connect(wxEVT_SCROLL_THUMBTRACK
,
1077 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1078 Connect(wxEVT_SCROLL_THUMBRELEASE
,
1079 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1082 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
1086 wxSize size
= m_hscroll
->GetBestSize();
1088 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 10);
1089 layout
->right
.LeftOf(m_vscroll
);
1090 layout
->bottom
.SameAs(m_impl
->m_container
, wxBottom
, 3);
1091 layout
->height
.Absolute(size
.GetHeight());
1092 m_hscroll
->SetConstraints(layout
);
1094 layout
= new wxLayoutConstraints();
1098 size
= m_vscroll
->GetBestSize();
1100 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 10);
1101 layout
->bottom
.Above(m_hscroll
);
1102 layout
->right
.SameAs(m_impl
->m_container
, wxRight
, 3);
1103 layout
->width
.Absolute(size
.GetWidth());
1104 m_vscroll
->SetConstraints(layout
);
1106 layout
= new wxLayoutConstraints();
1109 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 3);
1110 layout
->right
.LeftOf(m_vscroll
);
1111 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 3);
1112 layout
->bottom
.Above(m_hscroll
);
1113 m_viewport
->SetConstraints(layout
);
1115 m_impl
->m_container
->Layout();
1120 void wxDynamicSashWindowLeaf::AddChild(wxWindow
*window
)
1127 wxDynamicSashReparentEvent
event(this);
1128 AddPendingEvent(event
);
1131 DynamicSashRegion
wxDynamicSashWindowLeaf::GetRegion(int x
, int y
)
1133 wxSize size
= m_impl
->m_container
->GetSize();
1134 int w
= size
.GetWidth();
1135 int h
= size
.GetHeight();
1136 size
= m_hscroll
->GetSize();
1137 int sh
= size
.GetHeight();
1138 size
= m_vscroll
->GetSize();
1139 int sw
= size
.GetWidth();
1141 if (x
>= w
- sw
- 3 && x
< w
&& y
>= h
- sh
- 3 && y
< h
)
1143 if (x
>= 3 && x
< 10 && y
>= h
- sh
- 3 && y
< h
- 2)
1144 return DSR_VERTICAL_TAB
;
1145 if (x
>= w
- sw
- 3 && x
< w
- 2 && y
>= 3 && y
< 10)
1146 return DSR_HORIZONTAL_TAB
;
1148 return DSR_LEFT_EDGE
;
1150 return DSR_TOP_EDGE
;
1152 return DSR_RIGHT_EDGE
;
1154 return DSR_BOTTOM_EDGE
;
1159 void wxDynamicSashWindowLeaf::ResizeChild(const wxSize
& size
)
1163 if (m_impl
->m_window
->HasFlag(wxDS_MANAGE_SCROLLBARS
))
1165 wxSize best_size
= m_child
->GetBestSize();
1166 if (best_size
.GetWidth() < size
.GetWidth())
1167 best_size
.SetWidth(size
.GetWidth());
1168 if (best_size
.GetHeight() < size
.GetHeight())
1169 best_size
.SetHeight(size
.GetHeight());
1170 m_child
->SetSize(best_size
);
1172 int hpos
= m_hscroll
->GetThumbPosition();
1173 int vpos
= m_vscroll
->GetThumbPosition();
1179 if (hpos
> best_size
.GetWidth() - size
.GetWidth())
1180 hpos
= best_size
.GetWidth() - size
.GetWidth();
1181 if (vpos
> best_size
.GetHeight() - size
.GetHeight())
1182 vpos
= best_size
.GetHeight() - size
.GetHeight();
1184 m_hscroll
->SetScrollbar(hpos
, size
.GetWidth(),
1185 best_size
.GetWidth(), size
.GetWidth());
1186 m_vscroll
->SetScrollbar(vpos
, size
.GetHeight(),
1187 best_size
.GetHeight(), size
.GetHeight());
1189 // Umm, the scrollbars are doing something insane under GTK+ and subtracting
1190 // one from the position I pass in. This works around that.
1191 m_hscroll
->SetThumbPosition(hpos
+ hpos
- m_hscroll
->GetThumbPosition());
1192 m_vscroll
->SetThumbPosition(vpos
+ vpos
- m_vscroll
->GetThumbPosition());
1194 wxPoint pos
= m_child
->GetPosition();
1195 m_viewport
->ScrollWindow(-hpos
- pos
.x
, -vpos
- pos
.y
);
1197 else // !wxDS_MANAGE_SCROLLBARS
1199 m_child
->SetSize(size
);
1205 wxDynamicSashWindowLeaf::FindScrollBar(const wxWindow
*child
, int vert
) const
1207 if (m_child
== child
)
1209 return vert
? m_vscroll
: m_hscroll
;
1215 void wxDynamicSashWindowLeaf::OnSize(wxSizeEvent
&WXUNUSED(event
))
1217 m_impl
->m_container
->Refresh();
1220 void wxDynamicSashWindowLeaf::OnViewSize(wxSizeEvent
&WXUNUSED(event
))
1223 ResizeChild(m_viewport
->GetSize());
1226 void wxDynamicSashWindowLeaf::OnPaint(wxPaintEvent
&WXUNUSED(event
))
1228 wxPaintDC
dc(m_impl
->m_container
);
1229 dc
.SetBackground(wxBrush(m_impl
->m_container
->GetBackgroundColour(), wxSOLID
));
1232 wxPen
highlight(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT
), 1, wxSOLID
);
1233 wxPen
shadow(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW
), 1, wxSOLID
);
1234 wxPen
black(*wxBLACK
, 1, wxSOLID
);
1236 wxSize size
= m_impl
->m_container
->GetSize();
1237 int w
= size
.GetWidth();
1238 int h
= size
.GetHeight();
1239 size
= m_hscroll
->GetSize();
1240 int sh
= size
.GetHeight();
1241 size
= m_vscroll
->GetSize();
1242 int sw
= size
.GetWidth();
1245 dc
.DrawLine(1, 1, 1, h
- 2);
1246 dc
.DrawLine(1, 1, w
- 2, 1);
1248 dc
.DrawLine(2, 2, 2, h
- 3);
1249 dc
.DrawLine(2, 2, w
- 3, 2);
1250 dc
.SetPen(highlight
);
1251 dc
.DrawLine(w
- 2, 2, w
- 2, h
- sh
- 2);
1252 dc
.DrawLine(w
- 2, h
- sh
- 2, w
- sw
- 2, h
- sh
- 2);
1253 dc
.DrawLine(w
- sw
- 2, h
- sh
- 2, w
- sw
- 2, h
- 2);
1254 dc
.DrawLine(w
- sw
- 2, h
- 2, 2, h
- 2);
1256 dc
.SetPen(highlight
);
1257 dc
.DrawLine(w
- sw
- 2, 8, w
- sw
- 2, 4);
1258 dc
.DrawLine(w
- sw
- 2, 4, w
- 5, 4);
1260 dc
.DrawLine(w
- 5, 4, w
- 5, 8);
1261 dc
.DrawLine(w
- 5, 8, w
- sw
- 2, 8);
1263 dc
.DrawLine(w
- 4, 3, w
- 4, 9);
1264 dc
.DrawLine(w
- 4, 9, w
- sw
- 3, 9);
1266 dc
.SetPen(highlight
);
1267 dc
.DrawLine(4, h
- 5, 4, h
- sh
- 2);
1268 dc
.DrawLine(4, h
- sh
- 2, 8, h
- sh
- 2);
1270 dc
.DrawLine(8, h
- sh
- 2, 8, h
- 5);
1271 dc
.DrawLine(8, h
- 5, 4, h
- 5);
1273 dc
.DrawLine(9, h
- sh
- 3, 9, h
- 4);
1274 dc
.DrawLine(9, h
- 4, 3, h
- 4);
1276 int cy
= (h
- sh
+ h
- 6) / 2 + 1;
1277 int cx
= (w
- sw
+ w
- 6) / 2 + 1;
1286 for (y
= sy
; y
< h
- 2; y
+= 4)
1288 for (x
= sx
; x
< w
- 2; x
+= 4)
1290 if (x
- cx
>= -(y
- cy
))
1292 dc
.SetPen(highlight
);
1295 dc
.DrawPoint(x
+ 1, y
+ 1);
1301 void wxDynamicSashWindowLeaf::OnScroll(wxScrollEvent
&WXUNUSED(event
))
1303 int nx
= -m_hscroll
->GetThumbPosition();
1304 int ny
= -m_vscroll
->GetThumbPosition();
1308 wxPoint pos
= m_child
->GetPosition();
1310 m_viewport
->ScrollWindow(nx
- pos
.x
, ny
- pos
.y
);
1314 void wxDynamicSashWindowLeaf::OnFocus(wxFocusEvent
&event
)
1316 if ( event
.GetEventObject() == m_hscroll
||
1317 event
.GetEventObject() == m_vscroll
)
1319 m_child
->SetFocus();
1324 void wxDynamicSashWindowLeaf::OnMouseMove(wxMouseEvent
&event
)
1326 if (m_impl
->m_dragging
)
1329 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1331 wxCursor
cursor(wxCURSOR_ARROW
);
1332 if (region
== DSR_HORIZONTAL_TAB
)
1334 cursor
= wxCursor(wxCURSOR_SIZENS
);
1336 else if (region
== DSR_VERTICAL_TAB
)
1338 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1340 else if ((region
== DSR_CORNER
) &&
1341 (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0)
1343 cursor
= wxCursor(wxCURSOR_SIZENWSE
);
1345 else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1346 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
)
1348 if (m_impl
->FindParent(region
))
1350 if (region
== DSR_LEFT_EDGE
|| region
== DSR_RIGHT_EDGE
)
1352 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1356 cursor
= wxCursor(wxCURSOR_SIZENS
);
1361 m_impl
->m_container
->SetCursor(cursor
);
1364 void wxDynamicSashWindowLeaf::OnLeave(wxMouseEvent
&WXUNUSED(event
))
1366 wxCursor
cursor(wxCURSOR_ARROW
);
1367 m_impl
->m_container
->SetCursor(cursor
);
1371 void wxDynamicSashWindowLeaf::OnPress(wxMouseEvent
&event
)
1373 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1375 if ((region
== DSR_CORNER
) && (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) == 0)
1378 if (region
== DSR_HORIZONTAL_TAB
|| region
== DSR_VERTICAL_TAB
|| region
== DSR_CORNER
)
1380 m_impl
->m_dragging
= region
;
1381 m_impl
->m_drag_x
= event
.m_x
;
1382 m_impl
->m_drag_y
= event
.m_y
;
1383 m_impl
->DrawSash(event
.m_x
, event
.m_y
);
1384 m_impl
->m_container
->CaptureMouse();
1386 else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1387 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
)
1389 wxDynamicSashWindowImpl
*parent
= m_impl
->FindParent(region
);
1396 m_impl
->m_container
->ClientToScreen(&x
, &y
);
1397 parent
->m_container
->ScreenToClient(&x
, &y
);
1399 parent
->m_dragging
= parent
->m_split
;
1400 parent
->m_drag_x
= x
;
1401 parent
->m_drag_y
= y
;
1402 parent
->DrawSash(x
, y
);
1403 parent
->m_container
->CaptureMouse();
1408 void wxDynamicSashWindowLeaf::OnRelease(wxMouseEvent
&WXUNUSED(event
))
1412 void wxDynamicSashWindowLeaf::OnReparent(wxEvent
&WXUNUSED(event
))
1416 m_child
->Reparent(m_viewport
);
1419 ResizeChild(m_viewport
->GetSize());
1422 // ============================================================================
1424 // ============================================================================
1426 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent()
1428 m_eventObject
= NULL
;
1429 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1432 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent(wxObject
*object
)
1434 m_eventObject
= object
;
1435 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1438 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashSplitEvent
, wxCommandEvent
)
1440 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent()
1442 m_eventObject
= NULL
;
1443 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1446 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent(wxObject
*object
)
1448 m_eventObject
= object
;
1449 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1452 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashUnifyEvent
, wxCommandEvent
)
1455 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent()
1457 m_eventObject
= NULL
;
1458 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1461 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(wxObject
*object
)
1463 m_eventObject
= object
;
1464 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1467 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
)
1472 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashReparentEvent
, wxEvent
)
1474 #endif // !wxUSE_MDI