1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Classes to achieve a remotely-scrolled tree in a splitter
4 // window that can be scrolled by a scrolled window higher in the
6 // Author: Julian Smart
10 // Copyright: (c) Julian Smart
11 // Licence: wxWindows licence
12 /////////////////////////////////////////////////////////////////////////////
14 // ============================================================================
16 // ============================================================================
18 // ----------------------------------------------------------------------------
20 // ----------------------------------------------------------------------------
22 #pragma interface "splittree.cpp"
25 // For compilers that support precompilation, includes "wx/wx.h".
26 #include "wx/wxprec.h"
32 // for all others, include the necessary headers (this file is usually all you
33 // need because it includes almost all "standard" wxWindows headers)
38 #include "wx/generic/treectlg.h"
40 #include "wx/gizmos/splittree.h"
43 * wxRemotelyScrolledTreeCtrl
46 #if USE_GENERIC_TREECTRL
47 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl
, wxGenericTreeCtrl
)
49 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl
, wxTreeCtrl
)
52 #if USE_GENERIC_TREECTRL
53 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl
, wxGenericTreeCtrl
)
55 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl
, wxTreeCtrl
)
57 EVT_SIZE(wxRemotelyScrolledTreeCtrl::OnSize
)
58 EVT_PAINT(wxRemotelyScrolledTreeCtrl::OnPaint
)
59 EVT_TREE_ITEM_EXPANDED(-1, wxRemotelyScrolledTreeCtrl::OnExpand
)
60 EVT_TREE_ITEM_COLLAPSED(-1, wxRemotelyScrolledTreeCtrl::OnExpand
)
61 EVT_SCROLLWIN(wxRemotelyScrolledTreeCtrl::OnScroll
)
64 wxRemotelyScrolledTreeCtrl::wxRemotelyScrolledTreeCtrl(wxWindow
* parent
, wxWindowID id
, const wxPoint
& pt
,
65 const wxSize
& sz
, long style
):
66 wxTreeCtrl(parent
, id
, pt
, sz
, style
)
70 wxRemotelyScrolledTreeCtrl::~wxRemotelyScrolledTreeCtrl()
74 void wxRemotelyScrolledTreeCtrl::HideVScrollbar()
77 if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
79 ::ShowScrollBar((HWND
) GetHWND(), SB_VERT
, FALSE
);
84 // Implicit in overriding SetScrollbars
88 // Number of pixels per user unit (0 or -1 for no scrollbar)
89 // Length of virtual canvas in user units
90 // Length of page in user units
91 void wxRemotelyScrolledTreeCtrl::SetScrollbars(int pixelsPerUnitX
, int pixelsPerUnitY
,
92 int noUnitsX
, int noUnitsY
,
96 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
98 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
99 win
->wxGenericTreeCtrl::SetScrollbars(pixelsPerUnitX
, 0, noUnitsX
, 0, xPos
, 0, noRefresh
);
101 wxScrolledWindow
* scrolledWindow
= GetScrolledWindow();
104 scrolledWindow
->SetScrollbars(0, pixelsPerUnitY
, 0, noUnitsY
, 0, yPos
, noRefresh
);
109 // In case we're using the generic tree control.
110 // Get the view start
111 void wxRemotelyScrolledTreeCtrl::GetViewStart(int *x
, int *y
) const
113 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
115 wxScrolledWindow
* scrolledWindow
= GetScrolledWindow();
117 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
119 win
->wxGenericTreeCtrl::GetViewStart(& x1
, & y1
);
124 scrolledWindow
->GetViewStart(& x2
, & y2
);
129 // In case we're using the generic tree control.
130 void wxRemotelyScrolledTreeCtrl::PrepareDC(wxDC
& dc
)
132 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
134 wxScrolledWindow
* scrolledWindow
= GetScrolledWindow();
136 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
139 GetViewStart(& startX
, & startY
);
141 int xppu1
, yppu1
, xppu2
, yppu2
;
142 win
->wxGenericTreeCtrl::GetScrollPixelsPerUnit(& xppu1
, & yppu1
);
143 scrolledWindow
->GetScrollPixelsPerUnit(& xppu2
, & yppu2
);
145 dc
.SetDeviceOrigin( -startX
* xppu1
, -startY
* yppu2
);
146 dc
.SetUserScale( win
->GetScaleX(), win
->GetScaleY() );
150 // Scroll to the given line (in scroll units where each unit is
151 // the height of an item)
152 void wxRemotelyScrolledTreeCtrl::ScrollToLine(int posHoriz
, int posVert
)
155 if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
157 UINT sbCode
= SB_THUMBPOSITION
;
158 HWND vertScrollBar
= 0;
159 MSWDefWindowProc((WXUINT
) WM_VSCROLL
, MAKELONG(sbCode
, posVert
), (WXHWND
) vertScrollBar
);
164 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
166 /* Doesn't work yet because scrolling is ignored by Scroll
168 wxScrolledWindow* scrolledWindow = GetScrolledWindow();
171 scrolledWindow->GetScrollPixelsPerUnit(& xppu, & yppu);
172 win->Scroll(-1, posVert*yppu);
178 void wxRemotelyScrolledTreeCtrl::OnSize(wxSizeEvent
& event
)
181 AdjustRemoteScrollbars();
185 void wxRemotelyScrolledTreeCtrl::OnExpand(wxTreeEvent
& event
)
187 AdjustRemoteScrollbars();
190 // If we don't have this, we get some bits of lines still remaining
191 if (event
.GetEventType() == wxEVT_COMMAND_TREE_ITEM_COLLAPSED
)
195 // Adjust the containing wxScrolledWindow's scrollbars appropriately
196 void wxRemotelyScrolledTreeCtrl::AdjustRemoteScrollbars()
198 // WILL THIS BE DONE AUTOMATICALLY BY THE GENERIC TREE CONTROL?
201 Problem with remote-scrolling the generic tree control. It relies
202 on PrepareDC for adjusting the device origin, which in turn takes
203 values from wxScrolledWindow: which we've turned off in order to use
204 a different scrollbar :-( So we could override PrepareDC and use
205 the _other_ scrolled window's position instead.
206 Note also ViewStart would need to be overridden.
207 Plus, wxGenericTreeCtrl::OnPaint will reset the device origin.
211 // Assumption: wxGenericTreeCtrl will adjust the scrollbars automatically,
212 // since it'll call SetScrollbars and we've defined this to Do The Right Thing.
213 if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
216 wxScrolledWindow
* scrolledWindow
= GetScrolledWindow();
220 if (GetBoundingRect(GetRootItem(), itemRect
))
222 int itemHeight
= itemRect
.GetHeight();
225 GetClientSize(&w
, &h
);
227 wxRect
rect(0, 0, 0, 0);
229 int treeViewHeight
= rect
.GetHeight()/itemHeight
;
231 int scrollPixelsPerLine
= itemHeight
;
232 int scrollPos
= - (itemRect
.y
/ itemHeight
);
234 scrolledWindow
->SetScrollbars(0, scrollPixelsPerLine
, 0, treeViewHeight
, 0, scrollPos
);
236 // Ensure that when a scrollbar becomes hidden or visible,
237 // the contained window sizes are right.
238 // Problem: this is called too early (?)
239 wxSizeEvent
event(scrolledWindow
->GetSize(), scrolledWindow
->GetId());
240 scrolledWindow
->GetEventHandler()->ProcessEvent(event
);
246 // Calculate the area that contains both rectangles
247 static wxRect
CombineRectangles(const wxRect
& rect1
, const wxRect
& rect2
)
251 int right1
= rect1
.GetRight();
252 int bottom1
= rect1
.GetBottom();
253 int right2
= rect2
.GetRight();
254 int bottom2
= rect2
.GetBottom();
256 wxPoint topLeft
= wxPoint(wxMin(rect1
.x
, rect2
.x
), wxMin(rect1
.y
, rect2
.y
));
257 wxPoint bottomRight
= wxPoint(wxMax(right1
, right2
), wxMax(bottom1
, bottom2
));
259 rect
.x
= topLeft
.x
; rect
.y
= topLeft
.y
;
260 rect
.SetRight(bottomRight
.x
);
261 rect
.SetBottom(bottomRight
.y
);
267 // Calculate the tree overall size so we can set the scrollbar
269 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(wxRect
& rect
)
271 CalcTreeSize(GetRootItem(), rect
);
274 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(wxTreeItemId
& id
, wxRect
& rect
)
276 // TODO: implement GetFirst/NextVisibleItem
277 // for wxGenericTreeCtrl, plus GetBoundingRect.
279 // More efficient implementation would be to find the last item (but how?)
280 // Q: is the bounding rect relative to the top of the virtual tree workspace
281 // or the top of the window? How would we convert?
283 if (GetBoundingRect(id
, itemSize
))
285 rect
= CombineRectangles(rect
, itemSize
);
289 wxTreeItemId childId
= GetFirstChild(id
, cookie
);
292 CalcTreeSize(childId
, rect
);
293 childId
= GetNextChild(childId
, cookie
);
297 // Find the scrolled window that contains this control
298 wxScrolledWindow
* wxRemotelyScrolledTreeCtrl::GetScrolledWindow() const
300 wxWindow
* parent
= wxWindow::GetParent();
303 if (parent
->IsKindOf(CLASSINFO(wxScrolledWindow
)))
304 return (wxScrolledWindow
*) parent
;
305 parent
= parent
->GetParent();
310 void wxRemotelyScrolledTreeCtrl::OnPaint(wxPaintEvent
& event
)
314 wxTreeCtrl::OnPaint(event
);
316 // Reset the device origin since it may have been set
317 dc
.SetDeviceOrigin(0, 0);
319 wxSize sz
= GetClientSize();
321 wxPen
pen(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT
), 1, wxSOLID
);
323 dc
.SetBrush(* wxTRANSPARENT_BRUSH
);
326 if (GetBoundingRect(GetRootItem(), itemRect
))
328 int itemHeight
= itemRect
.GetHeight();
329 wxRect rcClient
= GetRect();
332 for(h
=GetFirstVisibleItem();h
;h
=GetNextVisible(h
))
334 dc
.DrawLine(rcClient
.x
, cy
, rcClient
.x
+ rcClient
.width
, cy
);
337 dc
.DrawLine(rcClient
.x
, cy
, rcClient
.x
+ rcClient
.width
, cy
);
341 void wxRemotelyScrolledTreeCtrl::OnScroll(wxScrollWinEvent
& event
)
343 int orient
= event
.GetOrientation();
344 if (orient
== wxHORIZONTAL
)
346 // Don't 'skip' or we'd get into infinite recursion
349 wxScrolledWindow
* scrollWin
= GetScrolledWindow();
354 scrollWin
->GetViewStart(& x
, & y
);
360 * wxThinSplitterWindow
363 IMPLEMENT_CLASS(wxThinSplitterWindow
, wxSplitterWindow
)
365 BEGIN_EVENT_TABLE(wxThinSplitterWindow
, wxSplitterWindow
)
366 EVT_SIZE(wxThinSplitterWindow::OnSize
)
369 wxThinSplitterWindow::wxThinSplitterWindow(wxWindow
* parent
, wxWindowID id
,
373 wxSplitterWindow(parent
, id
, pos
, sz
, style
)
377 void wxThinSplitterWindow::SizeWindows()
379 // The client size may have changed inbetween
380 // the sizing of the first window and the sizing of
381 // the second. So repeat SizeWindows.
382 wxSplitterWindow::SizeWindows();
383 wxSplitterWindow::SizeWindows();
386 // Tests for x, y over sash
387 bool wxThinSplitterWindow::SashHitTest(int x
, int y
, int tolerance
)
389 return wxSplitterWindow::SashHitTest(x
, y
, 4);
392 void wxThinSplitterWindow::DrawSash(wxDC
& dc
)
394 if ( m_sashPosition
== 0 || !m_windowTwo
)
396 if (GetWindowStyle() & wxSP_NOSASH
)
400 GetClientSize(&w
, &h
);
402 if ( m_splitMode
== wxSPLIT_VERTICAL
)
404 dc
.SetPen(* m_facePen
);
405 dc
.SetBrush(* m_faceBrush
);
408 if ( (GetWindowStyleFlag() & wxSP_BORDER
) != wxSP_BORDER
&& (GetWindowStyleFlag() & wxSP_3DBORDER
) != wxSP_3DBORDER
)
409 h1
+= 1; // Not sure why this is necessary...
410 if ( (GetWindowStyleFlag() & wxSP_3DBORDER
) == wxSP_3DBORDER
)
414 dc
.DrawRectangle(m_sashPosition
, y1
, m_sashSize
, h1
);
418 dc
.SetPen(* m_facePen
);
419 dc
.SetBrush(* m_faceBrush
);
422 if ( (GetWindowStyleFlag() & wxSP_BORDER
) != wxSP_BORDER
&& (GetWindowStyleFlag() & wxSP_3DBORDER
) != wxSP_3DBORDER
)
424 if ( (GetWindowStyleFlag() & wxSP_3DBORDER
) == wxSP_3DBORDER
)
428 dc
.DrawRectangle(x1
, m_sashPosition
, w1
, m_sashSize
);
431 dc
.SetPen(wxNullPen
);
432 dc
.SetBrush(wxNullBrush
);
435 void wxThinSplitterWindow::OnSize(wxSizeEvent
& event
)
437 wxSplitterWindow::OnSize(event
);
441 * wxSplitterScrolledWindow
444 IMPLEMENT_CLASS(wxSplitterScrolledWindow
, wxScrolledWindow
)
446 BEGIN_EVENT_TABLE(wxSplitterScrolledWindow
, wxScrolledWindow
)
447 EVT_SCROLLWIN(wxSplitterScrolledWindow::OnScroll
)
448 EVT_SIZE(wxSplitterScrolledWindow::OnSize
)
451 wxSplitterScrolledWindow::wxSplitterScrolledWindow(wxWindow
* parent
, wxWindowID id
,
455 wxScrolledWindow(parent
, id
, pos
, sz
, style
)
459 void wxSplitterScrolledWindow::OnSize(wxSizeEvent
& event
)
461 wxSize sz
= GetClientSize();
462 if (GetChildren().First())
464 ((wxWindow
*) GetChildren().First()->Data())->SetSize(0, 0, sz
.x
, sz
.y
);
468 void wxSplitterScrolledWindow::OnScroll(wxScrollWinEvent
& event
)
470 // Ensure that events being propagated back up the window hierarchy
471 // don't cause an infinite loop
472 static bool inOnScroll
= FALSE
;
477 int orient
= event
.GetOrientation();
479 int nScrollInc
= CalcScrollInc(event
);
486 if (orient
== wxHORIZONTAL
)
488 int newPos
= m_xScrollPosition
+ nScrollInc
;
489 SetScrollPos(wxHORIZONTAL
, newPos
, TRUE
);
493 int newPos
= m_yScrollPosition
+ nScrollInc
;
494 SetScrollPos(wxVERTICAL
, newPos
, TRUE
);
497 if (orient
== wxHORIZONTAL
)
499 m_xScrollPosition
+= nScrollInc
;
503 m_yScrollPosition
+= nScrollInc
;
506 // Find targets in splitter window and send the event to them
507 wxNode
* node
= GetChildren().First();
510 wxWindow
* child
= (wxWindow
*) node
->Data();
511 if (child
->IsKindOf(CLASSINFO(wxSplitterWindow
)))
513 wxSplitterWindow
* splitter
= (wxSplitterWindow
*) child
;
514 if (splitter
->GetWindow1())
515 splitter
->GetWindow1()->ProcessEvent(event
);
516 if (splitter
->GetWindow2())
517 splitter
->GetWindow2()->ProcessEvent(event
);
524 m_targetWindow
->MacUpdateImmediately() ;