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 OnViewSize(wxSizeEvent
&event
);
200 void OnPaint(wxPaintEvent
&event
);
201 void OnScroll(wxScrollEvent
&event
);
202 void OnFocus(wxFocusEvent
&event
);
203 void OnMouseMove(wxMouseEvent
&event
);
204 void OnLeave(wxMouseEvent
&event
);
205 void OnPress(wxMouseEvent
&event
);
206 void OnRelease(wxMouseEvent
&event
);
207 void OnReparent(wxEvent
&event
);
209 wxDynamicSashWindowImpl
*m_impl
;
211 wxScrollBar
*m_vscroll
,
214 /* m_child is the window provided to us by the application developer.
215 m_viewport is a window we've created, and it is the immediately
216 parent of m_child. We scroll m_child by moving it around within
218 wxWindow
*m_viewport
,
223 // ============================================================================
224 // wxDynamicSashWindow
225 // ============================================================================
227 wxDynamicSashWindow::wxDynamicSashWindow()
232 wxDynamicSashWindow::wxDynamicSashWindow(wxWindow
*parent
,
237 const wxString
& name
)
240 Create(parent
, id
, pos
, size
, style
, name
);
243 wxDynamicSashWindow::~wxDynamicSashWindow()
245 SetEventHandler(this);
249 bool wxDynamicSashWindow::Create(wxWindow
*parent
,
254 const wxString
& name
)
259 if (!wxWindow::Create(parent
, id
, pos
, size
, style
, name
))
262 m_impl
= new wxDynamicSashWindowImpl(this);
266 if (!m_impl
->Create())
276 void wxDynamicSashWindow::AddChild(wxWindowBase
*child
)
278 wxWindow::AddChild(child
);
280 m_impl
->AddChild(wxDynamicCast(child
, wxWindow
));
283 wxScrollBar
*wxDynamicSashWindow::GetHScrollBar(const wxWindow
*child
) const
285 return m_impl
->FindScrollBar(child
, 0);
288 wxScrollBar
*wxDynamicSashWindow::GetVScrollBar(const wxWindow
*child
) const
290 return m_impl
->FindScrollBar(child
, 1);
293 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashWindow
, wxWindow
)
296 // ============================================================================
297 // wxDynamicSashWindowImpl
298 // ============================================================================
300 wxDynamicSashWindowImpl::wxDynamicSashWindowImpl(wxDynamicSashWindow
*window
)
303 m_add_child_target
= this;
311 m_dragging
= DSR_NONE
;
315 wxDynamicSashWindowImpl::~wxDynamicSashWindowImpl()
324 if (m_container
!= m_window
&& m_container
)
326 m_container
->SetEventHandler(m_container
);
327 m_container
->Destroy();
331 bool wxDynamicSashWindowImpl::Create()
334 m_container
= m_window
;
336 wxCursor
cursor(wxCURSOR_ARROW
);
337 m_container
->SetCursor(cursor
);
339 m_leaf
= new wxDynamicSashWindowLeaf(this);
343 if (!m_leaf
->Create())
350 m_container
->SetEventHandler(this);
352 Connect(wxEVT_SIZE
, wxSizeEventHandler(wxDynamicSashWindowImpl::OnSize
));
353 Connect(wxEVT_PAINT
, wxPaintEventHandler(wxDynamicSashWindowImpl::OnPaint
));
354 Connect(wxEVT_MOTION
,
355 wxMouseEventHandler(wxDynamicSashWindowImpl::OnMouseMove
));
356 Connect(wxEVT_ENTER_WINDOW
,
357 wxMouseEventHandler(wxDynamicSashWindowImpl::OnMouseMove
));
358 Connect(wxEVT_LEAVE_WINDOW
,
359 wxMouseEventHandler(wxDynamicSashWindowImpl::OnLeave
));
360 Connect(wxEVT_LEFT_DOWN
,
361 wxMouseEventHandler(wxDynamicSashWindowImpl::OnPress
));
362 Connect(wxEVT_LEFT_UP
,
363 wxMouseEventHandler(wxDynamicSashWindowImpl::OnRelease
));
368 void wxDynamicSashWindowImpl::AddChild(wxWindow
*window
)
370 if (m_add_child_target
&& m_add_child_target
->m_leaf
)
371 m_add_child_target
->m_leaf
->AddChild(window
);
374 void wxDynamicSashWindowImpl::DrawSash(int x
, int y
) const
379 dc
.StartDrawingOnTop(m_container
);
383 bdc
.SelectObject(bmp
);
384 bdc
.DrawRectangle(-1, -1, 10, 10);
385 for (i
= 0; i
< 8; i
++)
387 for (j
= 0; j
< 8; j
++)
396 dc
.SetLogicalFunction(wxXOR
);
398 if ((m_dragging
== DSR_CORNER
) &&
399 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0)
404 m_container
->ClientToScreen(&cx
, &cy
);
405 m_container
->ClientToScreen(&x
, &y
);
407 if (cx
< x
&& cy
< y
)
409 dc
.DrawRectangle(cx
- 2, cy
- 2, x
- cx
+ 4, 4);
410 dc
.DrawRectangle(x
- 2, cy
+ 2, 4, y
- cy
);
411 dc
.DrawRectangle(cx
- 2, cy
+ 2, 4, y
- cy
);
412 dc
.DrawRectangle(cx
+ 2, y
- 2, x
- cx
- 4, 4);
418 m_container
->GetClientSize(&body_w
, &body_h
);
429 if (m_dragging
== DSR_HORIZONTAL_TAB
)
434 m_container
->ClientToScreen(&x
, &y
);
437 w
= body_w
; h
= body_h
;
439 if (m_dragging
== DSR_HORIZONTAL_TAB
)
440 dc
.DrawRectangle(x
, y
- 2, w
, 4);
442 dc
.DrawRectangle(x
- 2, y
, 4, h
);
445 dc
.EndDrawingOnTop();
448 wxDynamicSashWindowImpl
*
449 wxDynamicSashWindowImpl::FindParent(DynamicSashRegion side
) const
451 if (m_parent
== NULL
)
454 if (m_parent
->m_split
== DSR_HORIZONTAL_TAB
)
456 if (side
== DSR_TOP_EDGE
&& m_parent
->m_child
[1] == this)
458 if (side
== DSR_BOTTOM_EDGE
&& m_parent
->m_child
[0] == this)
461 else if (m_parent
->m_split
== DSR_VERTICAL_TAB
)
463 if (side
== DSR_LEFT_EDGE
&& m_parent
->m_child
[1] == this)
465 if (side
== DSR_RIGHT_EDGE
&& m_parent
->m_child
[0] == this)
469 return m_parent
->FindParent(side
);
472 wxDynamicSashWindowImpl
*
473 wxDynamicSashWindowImpl::FindUpperParent(wxDynamicSashWindowImpl
*sash_a
,
474 wxDynamicSashWindowImpl
*sash_b
) const
477 win
= sash_a
->m_container
->GetParent();
478 while (win
&& !win
->IsTopLevel())
480 if (win
== sash_b
->m_container
)
483 win
= win
->GetParent();
490 wxWindow
*wxDynamicSashWindowImpl::FindFrame() const
494 win
= m_window
->GetParent();
495 while (win
&& !win
->IsTopLevel()
497 && ! wxIsKindOf(win
, wxMDIChildFrame
) // not top-level but still a frame
501 win
= win
->GetParent();
508 wxDynamicSashWindowImpl::FindScrollBar(const wxWindow
*child
, int vert
) const
510 if (m_child
[0] == NULL
&& m_leaf
== NULL
)
515 return m_leaf
->FindScrollBar(child
, vert
);
518 wxScrollBar
*ret
= m_child
[0]->FindScrollBar(child
, vert
);
520 ret
= m_child
[1]->FindScrollBar(child
, vert
);
525 void wxDynamicSashWindowImpl::ConstrainChildren(int px
, int py
)
527 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
528 layout
->left
.SameAs(m_container
, wxLeft
);
529 layout
->top
.SameAs(m_container
, wxTop
);
530 if (m_split
== DSR_HORIZONTAL_TAB
)
532 layout
->right
.SameAs(m_container
, wxRight
);
533 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
537 layout
->bottom
.SameAs(m_container
, wxBottom
);
538 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
540 m_child
[0]->m_container
->SetConstraints(layout
);
542 layout
= new wxLayoutConstraints();
543 layout
->right
.SameAs(m_container
, wxRight
);
544 layout
->bottom
.SameAs(m_container
, wxBottom
);
545 if (m_split
== DSR_HORIZONTAL_TAB
)
547 layout
->top
.Below(m_child
[0]->m_container
, 1);
548 layout
->left
.SameAs(m_container
, wxLeft
);
552 layout
->left
.RightOf(m_child
[0]->m_container
, 1);
553 layout
->top
.SameAs(m_container
, wxTop
);
555 m_child
[1]->m_container
->SetConstraints(layout
);
558 void wxDynamicSashWindowImpl::Unify(int panel
)
560 int other
= panel
== 0 ? 1 : 0;
562 if (m_child
[panel
]->m_leaf
)
564 wxDynamicSashWindowImpl
*child
[2];
566 child
[0] = m_child
[0];
567 child
[1] = m_child
[1];
569 m_child
[0] = m_child
[1] = NULL
;
571 m_leaf
= new wxDynamicSashWindowLeaf(this);
573 m_leaf
->m_child
= child
[panel
]->m_leaf
->m_child
;
575 m_leaf
->m_vscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_vscroll
->GetThumbPosition(),
576 child
[panel
]->m_leaf
->m_vscroll
->GetThumbSize(),
577 child
[panel
]->m_leaf
->m_vscroll
->GetRange(),
578 child
[panel
]->m_leaf
->m_vscroll
->GetPageSize());
579 m_leaf
->m_hscroll
->SetScrollbar(child
[panel
]->m_leaf
->m_hscroll
->GetThumbPosition(),
580 child
[panel
]->m_leaf
->m_hscroll
->GetThumbSize(),
581 child
[panel
]->m_leaf
->m_hscroll
->GetRange(),
582 child
[panel
]->m_leaf
->m_hscroll
->GetPageSize());
583 m_add_child_target
= NULL
;
584 wxDynamicSashReparentEvent
event(m_leaf
);
585 m_leaf
->ProcessEvent(event
);
592 wxDynamicSashUnifyEvent
unify(m_leaf
->m_child
);
593 m_leaf
->m_child
->ProcessEvent(unify
);
597 m_split
= m_child
[panel
]->m_split
;
599 delete m_child
[other
];
601 wxDynamicSashWindowImpl
*child_panel
= m_child
[panel
];
602 m_child
[0] = child_panel
->m_child
[0];
603 m_child
[1] = child_panel
->m_child
[1];
605 m_child
[0]->m_parent
= this;
606 m_child
[1]->m_parent
= this;
608 m_add_child_target
= NULL
;
609 m_child
[0]->m_container
->Reparent(m_container
);
610 m_child
[1]->m_container
->Reparent(m_container
);
612 child_panel
->m_child
[0] = child_panel
->m_child
[1] = NULL
;
615 wxSize size
= m_container
->GetSize();
616 wxSize child_size
= m_child
[0]->m_container
->GetSize();
618 ConstrainChildren(child_size
.GetWidth() * 100 / size
.GetWidth(),
619 child_size
.GetHeight() * 100 / size
.GetHeight());
621 m_container
->Layout();
625 void wxDynamicSashWindowImpl::Split(int px
, int py
)
628 m_add_child_target
= NULL
;
630 m_child
[0] = new wxDynamicSashWindowImpl(m_window
);
631 m_child
[0]->m_container
= new wxWindow(m_container
, wxID_ANY
);
632 m_child
[0]->m_parent
= this;
633 m_child
[0]->m_top
= m_top
;
634 m_child
[0]->Create();
637 m_leaf
->m_child
->Reparent(m_container
);
638 m_child
[0]->AddChild(m_leaf
->m_child
);
641 m_child
[1] = new wxDynamicSashWindowImpl(m_window
);
642 m_child
[1]->m_container
= new wxWindow(m_container
, wxID_ANY
);
643 m_child
[1]->m_parent
= this;
644 m_child
[1]->m_top
= m_top
;
645 m_child
[1]->Create();
647 m_split
= m_dragging
;
648 ConstrainChildren(px
, py
);
650 m_top
->m_add_child_target
= m_child
[1];
651 wxDynamicSashSplitEvent
split(m_child
[0]->m_leaf
->m_child
);
652 m_child
[0]->m_leaf
->m_child
->ProcessEvent(split
);
654 m_child
[0]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
655 m_leaf
->m_vscroll
->GetThumbSize(),
656 m_leaf
->m_vscroll
->GetRange(),
657 m_leaf
->m_vscroll
->GetPageSize());
658 m_child
[0]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
659 m_leaf
->m_hscroll
->GetThumbSize(),
660 m_leaf
->m_hscroll
->GetRange(),
661 m_leaf
->m_hscroll
->GetPageSize());
662 m_child
[1]->m_leaf
->m_vscroll
->SetScrollbar(m_leaf
->m_vscroll
->GetThumbPosition(),
663 m_leaf
->m_vscroll
->GetThumbSize(),
664 m_leaf
->m_vscroll
->GetRange(),
665 m_leaf
->m_vscroll
->GetPageSize());
666 m_child
[1]->m_leaf
->m_hscroll
->SetScrollbar(m_leaf
->m_hscroll
->GetThumbPosition(),
667 m_leaf
->m_hscroll
->GetThumbSize(),
668 m_leaf
->m_hscroll
->GetRange(),
669 m_leaf
->m_hscroll
->GetPageSize());
673 m_container
->Layout();
677 /* This code is called when you finish resizing a view by dragging the
678 corner tab, but I think this implementation is lousy and will surprise
679 the user more often than it will do what they are trying to do. What
680 I really need to be doing here is do a rewrite such that *no* sashes
681 move except the ones immediately to the bottom and right of this window,
682 and handle the case where you resize a window larger than it's neighbors
683 by destroying the neighbors.
685 But this will do for now. */
686 void wxDynamicSashWindowImpl::Resize(int x
, int y
)
688 wxDynamicSashWindowImpl
*h_parent
= FindParent(DSR_BOTTOM_EDGE
);
689 wxDynamicSashWindowImpl
*v_parent
= FindParent(DSR_RIGHT_EDGE
);
692 wxWindow
*frame
= FindFrame();
701 m_container
->ClientToScreen(NULL
, &y
);
702 h_parent
->m_container
->ScreenToClient(NULL
, &y
);
704 int py
= (int)((y
* 100) / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
708 wxDynamicSashWindowImpl
*ho_parent
= FindParent(DSR_TOP_EDGE
);
712 if (FindUpperParent(h_parent
, ho_parent
) == ho_parent
)
718 py
= (int)((ho_parent
->m_child
[0]->m_container
->GetSize().GetHeight() * 100)
719 / h_parent
->m_container
->GetSize().GetHeight() + 0.5);
720 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
721 h_parent
->m_container
, wxHeight
, py
);
723 h_parent
= ho_parent
;
738 h_parent
->m_child
[0]->m_container
->GetConstraints()->height
.PercentOf(
739 h_parent
->m_container
, wxHeight
, py
);
740 h_parent
->m_container
->Layout();
746 h_parent
= FindParent(DSR_TOP_EDGE
);
750 int py
= (int)((y
* 100) /
751 (h_parent
->m_container
->GetSize().GetHeight() +
752 y
- m_container
->GetSize().GetHeight()) + 0.5);
764 wxSize size
= frame
->GetSize();
765 frame
->SetSize(size
.GetWidth(), size
.GetHeight() + y
- m_container
->GetSize().GetHeight());
771 m_container
->ClientToScreen(&x
, NULL
);
772 v_parent
->m_container
->ScreenToClient(&x
, NULL
);
774 int px
= (int)((x
* 100) / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
778 wxDynamicSashWindowImpl
*vo_parent
= FindParent(DSR_LEFT_EDGE
);
782 if (FindUpperParent(v_parent
, vo_parent
) == vo_parent
)
788 px
= (int)((vo_parent
->m_child
[0]->m_container
->GetSize().GetWidth() * 100)
789 / v_parent
->m_container
->GetSize().GetWidth() + 0.5);
790 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
791 v_parent
->m_container
, wxWidth
, px
);
793 v_parent
= vo_parent
;
808 v_parent
->m_child
[0]->m_container
->GetConstraints()->width
.PercentOf(
809 v_parent
->m_container
, wxWidth
, px
);
810 v_parent
->m_container
->Layout();
816 v_parent
= FindParent(DSR_LEFT_EDGE
);
820 int px
= (int)((x
* 100) /
821 (v_parent
->m_container
->GetSize().GetWidth() +
822 x
- m_container
->GetSize().GetWidth()) + 0.5);
834 wxSize size
= frame
->GetSize();
835 frame
->SetSize(size
.GetWidth() + x
- m_container
->GetSize().GetWidth(), size
.GetHeight());
839 if (h_unify
!= -1 && v_unify
!= -1)
841 wxDynamicSashWindowImpl
*parent
= FindUpperParent(h_parent
, v_parent
);
843 if (parent
== h_parent
)
845 h_parent
->Unify(h_unify
);
849 v_parent
->Unify(v_unify
);
852 else if (h_unify
!= -1)
854 h_parent
->Unify(h_unify
);
856 else if (v_unify
!= -1)
858 v_parent
->Unify(v_unify
);
863 void wxDynamicSashWindowImpl::OnSize(wxSizeEvent
&event
)
865 m_container
->Layout();
868 m_leaf
->OnSize(event
);
871 void wxDynamicSashWindowImpl::OnPaint(wxPaintEvent
&event
)
874 m_leaf
->OnPaint(event
);
877 wxPaintDC
dc(m_container
);
878 dc
.SetBackground(wxBrush(m_container
->GetBackgroundColour(), wxSOLID
));
883 void wxDynamicSashWindowImpl::OnMouseMove(wxMouseEvent
&event
)
887 DrawSash(m_drag_x
, m_drag_y
);
888 m_drag_x
= event
.m_x
; m_drag_y
= event
.m_y
;
889 DrawSash(m_drag_x
, m_drag_y
);
893 m_leaf
->OnMouseMove(event
);
897 void wxDynamicSashWindowImpl::OnLeave(wxMouseEvent
&event
)
900 m_leaf
->OnLeave(event
);
903 void wxDynamicSashWindowImpl::OnPress(wxMouseEvent
&event
)
907 m_leaf
->OnPress(event
);
911 m_dragging
= m_split
;
912 m_drag_x
= event
.m_x
;
913 m_drag_y
= event
.m_y
;
914 DrawSash(m_drag_x
, m_drag_y
);
915 m_container
->CaptureMouse();
919 void wxDynamicSashWindowImpl::OnRelease(wxMouseEvent
&event
)
921 if ((m_dragging
== DSR_CORNER
) &&
922 (m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0)
924 DrawSash(m_drag_x
, m_drag_y
);
925 m_container
->ReleaseMouse();
927 Resize(event
.m_x
, event
.m_y
);
929 m_dragging
= DSR_NONE
;
933 DrawSash(m_drag_x
, m_drag_y
);
934 m_container
->ReleaseMouse();
936 wxSize size
= m_container
->GetSize();
937 int px
= (int)((event
.m_x
* 100) / size
.GetWidth() + 0.5);
938 int py
= (int)((event
.m_y
* 100) / size
.GetHeight() + 0.5);
940 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
>= 10 && py
<= 90)
941 || (m_dragging
== DSR_VERTICAL_TAB
&& px
>= 10 && px
<= 90))
943 if (m_child
[0] == NULL
)
949 /* It would be nice if moving *this* sash didn't implicitly move
950 the sashes of our children (if any). But this will do. */
951 wxLayoutConstraints
*layout
= m_child
[0]->m_container
->GetConstraints();
952 if (m_split
== DSR_HORIZONTAL_TAB
)
954 layout
->height
.PercentOf(m_container
, wxHeight
, py
);
958 layout
->width
.PercentOf(m_container
, wxWidth
, px
);
960 m_container
->Layout();
965 if (m_child
[0] != NULL
)
967 if ((m_dragging
== DSR_HORIZONTAL_TAB
&& py
<= 10)
968 || (m_dragging
== DSR_VERTICAL_TAB
&& px
<= 10))
980 if (m_split
== DSR_HORIZONTAL_TAB
)
981 cursor
= wxCursor(wxCURSOR_SIZENS
);
982 else if (m_split
== DSR_VERTICAL_TAB
)
983 cursor
= wxCursor(wxCURSOR_SIZEWE
);
985 cursor
= wxCursor(wxCURSOR_ARROW
);
987 m_container
->SetCursor(cursor
);
989 m_dragging
= DSR_NONE
;
993 m_leaf
->OnRelease(event
);
997 // ============================================================================
998 // wxDynamicSashWindowLeaf
999 // ============================================================================
1001 wxDynamicSashWindowLeaf::wxDynamicSashWindowLeaf(wxDynamicSashWindowImpl
*impl
)
1011 wxDynamicSashWindowLeaf::~wxDynamicSashWindowLeaf()
1013 m_hscroll
->SetEventHandler(m_hscroll
);
1014 m_vscroll
->SetEventHandler(m_vscroll
);
1016 m_hscroll
->Destroy();
1017 m_vscroll
->Destroy();
1018 m_viewport
->Destroy();
1021 bool wxDynamicSashWindowLeaf::Create()
1023 m_hscroll
= new wxScrollBar();
1024 m_vscroll
= new wxScrollBar();
1025 m_viewport
= new wxWindow();
1027 wxDynamicSashWindowImpl
*add_child_target
= m_impl
->m_add_child_target
;
1028 m_impl
->m_add_child_target
= NULL
;
1030 bool success
= m_hscroll
->Create(m_impl
->m_container
, wxID_ANY
,
1031 wxDefaultPosition
, wxDefaultSize
,
1034 success
= m_vscroll
->Create(m_impl
->m_container
, wxID_ANY
,
1035 wxDefaultPosition
, wxDefaultSize
,
1038 success
= m_viewport
->Create(m_impl
->m_container
, wxID_ANY
);
1042 m_impl
->m_add_child_target
= add_child_target
;
1044 wxCursor
cursor(wxCURSOR_ARROW
);
1045 m_hscroll
->SetCursor(cursor
);
1046 m_vscroll
->SetCursor(cursor
);
1047 m_viewport
->SetCursor(cursor
);
1049 // the viewport must resize its child when it is itself resized, but it's
1050 // more convenient to do it in our own method instead of deriving a new
1051 // class just for this: this is why we pass this as last Connect() argument
1052 m_viewport
->Connect(wxEVT_SIZE
,
1053 wxSizeEventHandler(wxDynamicSashWindowLeaf::OnViewSize
),
1056 Connect(wxEVT_DYNAMIC_SASH_REPARENT
,
1057 wxEventHandler(wxDynamicSashWindowLeaf::OnReparent
));
1059 if (m_impl
->m_window
->GetWindowStyle() & wxDS_MANAGE_SCROLLBARS
)
1061 m_hscroll
->SetEventHandler(this);
1062 m_vscroll
->SetEventHandler(this);
1064 Connect(wxEVT_SET_FOCUS
,
1065 wxFocusEventHandler(wxDynamicSashWindowLeaf::OnFocus
));
1066 Connect(wxEVT_SCROLL_TOP
,
1067 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1068 Connect(wxEVT_SCROLL_BOTTOM
,
1069 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1070 Connect(wxEVT_SCROLL_LINEUP
,
1071 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1072 Connect(wxEVT_SCROLL_LINEDOWN
,
1073 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1074 Connect(wxEVT_SCROLL_PAGEUP
,
1075 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1076 Connect(wxEVT_SCROLL_PAGEDOWN
,
1077 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1078 Connect(wxEVT_SCROLL_THUMBTRACK
,
1079 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1080 Connect(wxEVT_SCROLL_THUMBRELEASE
,
1081 wxScrollEventHandler(wxDynamicSashWindowLeaf::OnScroll
));
1084 wxLayoutConstraints
*layout
= new wxLayoutConstraints();
1088 wxSize size
= m_hscroll
->GetBestSize();
1090 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 10);
1091 layout
->right
.LeftOf(m_vscroll
);
1092 layout
->bottom
.SameAs(m_impl
->m_container
, wxBottom
, 3);
1093 layout
->height
.Absolute(size
.GetHeight());
1094 m_hscroll
->SetConstraints(layout
);
1096 layout
= new wxLayoutConstraints();
1100 size
= m_vscroll
->GetBestSize();
1102 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 10);
1103 layout
->bottom
.Above(m_hscroll
);
1104 layout
->right
.SameAs(m_impl
->m_container
, wxRight
, 3);
1105 layout
->width
.Absolute(size
.GetWidth());
1106 m_vscroll
->SetConstraints(layout
);
1108 layout
= new wxLayoutConstraints();
1111 layout
->left
.SameAs(m_impl
->m_container
, wxLeft
, 3);
1112 layout
->right
.LeftOf(m_vscroll
);
1113 layout
->top
.SameAs(m_impl
->m_container
, wxTop
, 3);
1114 layout
->bottom
.Above(m_hscroll
);
1115 m_viewport
->SetConstraints(layout
);
1117 m_impl
->m_container
->Layout();
1122 void wxDynamicSashWindowLeaf::AddChild(wxWindow
*window
)
1129 wxDynamicSashReparentEvent
event(this);
1130 AddPendingEvent(event
);
1133 DynamicSashRegion
wxDynamicSashWindowLeaf::GetRegion(int x
, int y
)
1135 wxSize size
= m_impl
->m_container
->GetSize();
1136 int w
= size
.GetWidth();
1137 int h
= size
.GetHeight();
1138 size
= m_hscroll
->GetSize();
1139 int sh
= size
.GetHeight();
1140 size
= m_vscroll
->GetSize();
1141 int sw
= size
.GetWidth();
1143 if (x
>= w
- sw
- 3 && x
< w
&& y
>= h
- sh
- 3 && y
< h
)
1145 if (x
>= 3 && x
< 10 && y
>= h
- sh
- 3 && y
< h
- 2)
1146 return DSR_VERTICAL_TAB
;
1147 if (x
>= w
- sw
- 3 && x
< w
- 2 && y
>= 3 && y
< 10)
1148 return DSR_HORIZONTAL_TAB
;
1150 return DSR_LEFT_EDGE
;
1152 return DSR_TOP_EDGE
;
1154 return DSR_RIGHT_EDGE
;
1156 return DSR_BOTTOM_EDGE
;
1161 void wxDynamicSashWindowLeaf::ResizeChild(const wxSize
& size
)
1165 if (m_impl
->m_window
->HasFlag(wxDS_MANAGE_SCROLLBARS
))
1167 wxSize best_size
= m_child
->GetBestSize();
1168 if (best_size
.GetWidth() < size
.GetWidth())
1169 best_size
.SetWidth(size
.GetWidth());
1170 if (best_size
.GetHeight() < size
.GetHeight())
1171 best_size
.SetHeight(size
.GetHeight());
1172 m_child
->SetSize(best_size
);
1174 int hpos
= m_hscroll
->GetThumbPosition();
1175 int vpos
= m_vscroll
->GetThumbPosition();
1181 if (hpos
> best_size
.GetWidth() - size
.GetWidth())
1182 hpos
= best_size
.GetWidth() - size
.GetWidth();
1183 if (vpos
> best_size
.GetHeight() - size
.GetHeight())
1184 vpos
= best_size
.GetHeight() - size
.GetHeight();
1186 m_hscroll
->SetScrollbar(hpos
, size
.GetWidth(),
1187 best_size
.GetWidth(), size
.GetWidth());
1188 m_vscroll
->SetScrollbar(vpos
, size
.GetHeight(),
1189 best_size
.GetHeight(), size
.GetHeight());
1191 // Umm, the scrollbars are doing something insane under GTK+ and subtracting
1192 // one from the position I pass in. This works around that.
1193 m_hscroll
->SetThumbPosition(hpos
+ hpos
- m_hscroll
->GetThumbPosition());
1194 m_vscroll
->SetThumbPosition(vpos
+ vpos
- m_vscroll
->GetThumbPosition());
1196 wxPoint pos
= m_child
->GetPosition();
1197 m_viewport
->ScrollWindow(-hpos
- pos
.x
, -vpos
- pos
.y
);
1199 else // !wxDS_MANAGE_SCROLLBARS
1201 m_child
->SetSize(size
);
1207 wxDynamicSashWindowLeaf::FindScrollBar(const wxWindow
*child
, int vert
) const
1209 if (m_child
== child
)
1211 return vert
? m_vscroll
: m_hscroll
;
1217 void wxDynamicSashWindowLeaf::OnSize(wxSizeEvent
&WXUNUSED(event
))
1219 m_impl
->m_container
->Refresh();
1222 void wxDynamicSashWindowLeaf::OnViewSize(wxSizeEvent
&WXUNUSED(event
))
1225 ResizeChild(m_viewport
->GetSize());
1228 void wxDynamicSashWindowLeaf::OnPaint(wxPaintEvent
&WXUNUSED(event
))
1230 wxPaintDC
dc(m_impl
->m_container
);
1231 dc
.SetBackground(wxBrush(m_impl
->m_container
->GetBackgroundColour(), wxSOLID
));
1234 wxPen
highlight(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT
), 1, wxSOLID
);
1235 wxPen
shadow(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW
), 1, wxSOLID
);
1236 wxPen
black(*wxBLACK
, 1, wxSOLID
);
1238 wxSize size
= m_impl
->m_container
->GetSize();
1239 int w
= size
.GetWidth();
1240 int h
= size
.GetHeight();
1241 size
= m_hscroll
->GetSize();
1242 int sh
= size
.GetHeight();
1243 size
= m_vscroll
->GetSize();
1244 int sw
= size
.GetWidth();
1247 dc
.DrawLine(1, 1, 1, h
- 2);
1248 dc
.DrawLine(1, 1, w
- 2, 1);
1250 dc
.DrawLine(2, 2, 2, h
- 3);
1251 dc
.DrawLine(2, 2, w
- 3, 2);
1252 dc
.SetPen(highlight
);
1253 dc
.DrawLine(w
- 2, 2, w
- 2, h
- sh
- 2);
1254 dc
.DrawLine(w
- 2, h
- sh
- 2, w
- sw
- 2, h
- sh
- 2);
1255 dc
.DrawLine(w
- sw
- 2, h
- sh
- 2, w
- sw
- 2, h
- 2);
1256 dc
.DrawLine(w
- sw
- 2, h
- 2, 2, h
- 2);
1258 dc
.SetPen(highlight
);
1259 dc
.DrawLine(w
- sw
- 2, 8, w
- sw
- 2, 4);
1260 dc
.DrawLine(w
- sw
- 2, 4, w
- 5, 4);
1262 dc
.DrawLine(w
- 5, 4, w
- 5, 8);
1263 dc
.DrawLine(w
- 5, 8, w
- sw
- 2, 8);
1265 dc
.DrawLine(w
- 4, 3, w
- 4, 9);
1266 dc
.DrawLine(w
- 4, 9, w
- sw
- 3, 9);
1268 dc
.SetPen(highlight
);
1269 dc
.DrawLine(4, h
- 5, 4, h
- sh
- 2);
1270 dc
.DrawLine(4, h
- sh
- 2, 8, h
- sh
- 2);
1272 dc
.DrawLine(8, h
- sh
- 2, 8, h
- 5);
1273 dc
.DrawLine(8, h
- 5, 4, h
- 5);
1275 dc
.DrawLine(9, h
- sh
- 3, 9, h
- 4);
1276 dc
.DrawLine(9, h
- 4, 3, h
- 4);
1278 int cy
= (h
- sh
+ h
- 6) / 2 + 1;
1279 int cx
= (w
- sw
+ w
- 6) / 2 + 1;
1288 for (y
= sy
; y
< h
- 2; y
+= 4)
1290 for (x
= sx
; x
< w
- 2; x
+= 4)
1292 if (x
- cx
>= -(y
- cy
))
1294 dc
.SetPen(highlight
);
1297 dc
.DrawPoint(x
+ 1, y
+ 1);
1303 void wxDynamicSashWindowLeaf::OnScroll(wxScrollEvent
&WXUNUSED(event
))
1305 int nx
= -m_hscroll
->GetThumbPosition();
1306 int ny
= -m_vscroll
->GetThumbPosition();
1310 wxPoint pos
= m_child
->GetPosition();
1312 m_viewport
->ScrollWindow(nx
- pos
.x
, ny
- pos
.y
);
1316 void wxDynamicSashWindowLeaf::OnFocus(wxFocusEvent
&event
)
1318 if ( event
.GetEventObject() == m_hscroll
||
1319 event
.GetEventObject() == m_vscroll
)
1321 m_child
->SetFocus();
1326 void wxDynamicSashWindowLeaf::OnMouseMove(wxMouseEvent
&event
)
1328 if (m_impl
->m_dragging
)
1331 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1333 wxCursor
cursor(wxCURSOR_ARROW
);
1334 if (region
== DSR_HORIZONTAL_TAB
)
1336 cursor
= wxCursor(wxCURSOR_SIZENS
);
1338 else if (region
== DSR_VERTICAL_TAB
)
1340 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1342 else if ((region
== DSR_CORNER
) &&
1343 (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) != 0)
1345 cursor
= wxCursor(wxCURSOR_SIZENWSE
);
1347 else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1348 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
)
1350 if (m_impl
->FindParent(region
))
1352 if (region
== DSR_LEFT_EDGE
|| region
== DSR_RIGHT_EDGE
)
1354 cursor
= wxCursor(wxCURSOR_SIZEWE
);
1358 cursor
= wxCursor(wxCURSOR_SIZENS
);
1363 m_impl
->m_container
->SetCursor(cursor
);
1366 void wxDynamicSashWindowLeaf::OnLeave(wxMouseEvent
&WXUNUSED(event
))
1368 wxCursor
cursor(wxCURSOR_ARROW
);
1369 m_impl
->m_container
->SetCursor(cursor
);
1373 void wxDynamicSashWindowLeaf::OnPress(wxMouseEvent
&event
)
1375 DynamicSashRegion region
= GetRegion(event
.m_x
, event
.m_y
);
1377 if ((region
== DSR_CORNER
) && (m_impl
->m_window
->GetWindowStyle() & wxDS_DRAG_CORNER
) == 0)
1380 if (region
== DSR_HORIZONTAL_TAB
|| region
== DSR_VERTICAL_TAB
|| region
== DSR_CORNER
)
1382 m_impl
->m_dragging
= region
;
1383 m_impl
->m_drag_x
= event
.m_x
;
1384 m_impl
->m_drag_y
= event
.m_y
;
1385 m_impl
->DrawSash(event
.m_x
, event
.m_y
);
1386 m_impl
->m_container
->CaptureMouse();
1388 else if (region
== DSR_LEFT_EDGE
|| region
== DSR_TOP_EDGE
1389 || region
== DSR_RIGHT_EDGE
|| region
== DSR_BOTTOM_EDGE
)
1391 wxDynamicSashWindowImpl
*parent
= m_impl
->FindParent(region
);
1398 m_impl
->m_container
->ClientToScreen(&x
, &y
);
1399 parent
->m_container
->ScreenToClient(&x
, &y
);
1401 parent
->m_dragging
= parent
->m_split
;
1402 parent
->m_drag_x
= x
;
1403 parent
->m_drag_y
= y
;
1404 parent
->DrawSash(x
, y
);
1405 parent
->m_container
->CaptureMouse();
1410 void wxDynamicSashWindowLeaf::OnRelease(wxMouseEvent
&WXUNUSED(event
))
1414 void wxDynamicSashWindowLeaf::OnReparent(wxEvent
&WXUNUSED(event
))
1418 m_child
->Reparent(m_viewport
);
1421 ResizeChild(m_viewport
->GetSize());
1424 // ============================================================================
1426 // ============================================================================
1428 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent()
1430 m_eventObject
= NULL
;
1431 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1434 wxDynamicSashSplitEvent::wxDynamicSashSplitEvent(wxObject
*object
)
1436 m_eventObject
= object
;
1437 m_eventType
= wxEVT_DYNAMIC_SASH_SPLIT
;
1440 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashSplitEvent
, wxCommandEvent
)
1442 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent()
1444 m_eventObject
= NULL
;
1445 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1448 wxDynamicSashUnifyEvent::wxDynamicSashUnifyEvent(wxObject
*object
)
1450 m_eventObject
= object
;
1451 m_eventType
= wxEVT_DYNAMIC_SASH_UNIFY
;
1454 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashUnifyEvent
, wxCommandEvent
)
1457 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent()
1459 m_eventObject
= NULL
;
1460 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1463 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(wxObject
*object
)
1465 m_eventObject
= object
;
1466 m_eventType
= wxEVT_DYNAMIC_SASH_REPARENT
;
1469 wxDynamicSashReparentEvent::wxDynamicSashReparentEvent(const wxDynamicSashReparentEvent
& evt
)
1474 IMPLEMENT_DYNAMIC_CLASS(wxDynamicSashReparentEvent
, wxEvent
)