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 // ----------------------------------------------------------------------------
21 #if defined(__GNUG__) && !defined(__APPLE__)
22 #pragma implementation "splittree.h"
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" wxWidgets headers)
40 #include "wx/msw/winundef.h"
43 #include "wx/gizmos/splittree.h"
47 * wxRemotelyScrolledTreeCtrl
50 #if USE_GENERIC_TREECTRL
51 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl
, wxGenericTreeCtrl
)
53 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl
, wxTreeCtrl
)
56 #if USE_GENERIC_TREECTRL
57 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl
, wxGenericTreeCtrl
)
59 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl
, wxTreeCtrl
)
61 EVT_SIZE(wxRemotelyScrolledTreeCtrl::OnSize
)
62 EVT_TREE_ITEM_EXPANDED(-1, wxRemotelyScrolledTreeCtrl::OnExpand
)
63 EVT_TREE_ITEM_COLLAPSED(-1, wxRemotelyScrolledTreeCtrl::OnExpand
)
64 EVT_SCROLLWIN(wxRemotelyScrolledTreeCtrl::OnScroll
)
67 wxRemotelyScrolledTreeCtrl::wxRemotelyScrolledTreeCtrl(wxWindow
* parent
, wxWindowID id
, const wxPoint
& pt
,
68 const wxSize
& sz
, long style
):
69 wxTreeCtrl(parent
, id
, pt
, sz
, style
)
71 m_companionWindow
= NULL
;
74 wxRemotelyScrolledTreeCtrl::~wxRemotelyScrolledTreeCtrl()
78 void wxRemotelyScrolledTreeCtrl::HideVScrollbar()
80 #if defined(__WXMSW__) && USE_GENERIC_TREECTRL
81 if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
83 ::ShowScrollBar((HWND
) GetHWND(), SB_VERT
, FALSE
);
88 // Implicit in overriding SetScrollbars
92 // Number of pixels per user unit (0 or -1 for no scrollbar)
93 // Length of virtual canvas in user units
94 // Length of page in user units
95 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
96 void wxRemotelyScrolledTreeCtrl::SetScrollbars(int pixelsPerUnitX
, int pixelsPerUnitY
,
97 int noUnitsX
, int noUnitsY
,
101 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
103 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
104 win
->wxGenericTreeCtrl::SetScrollbars(pixelsPerUnitX
, 0, noUnitsX
, 0, xPos
, 0, noRefresh
);
106 wxScrolledWindow
* scrolledWindow
= GetScrolledWindow();
109 scrolledWindow
->SetScrollbars(0, pixelsPerUnitY
, 0, noUnitsY
, 0, yPos
, noRefresh
);
113 void wxRemotelyScrolledTreeCtrl::SetScrollbars(int WXUNUSED(pixelsPerUnitX
), int WXUNUSED(pixelsPerUnitY
),
114 int WXUNUSED(noUnitsX
), int WXUNUSED(noUnitsY
),
115 int WXUNUSED(xPos
), int WXUNUSED(yPos
),
116 bool WXUNUSED(noRefresh
))
121 // In case we're using the generic tree control.
122 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
123 int wxRemotelyScrolledTreeCtrl::GetScrollPos(int orient
) const
125 wxScrolledWindow
* scrolledWindow
= GetScrolledWindow();
127 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
129 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
131 if (orient
== wxHORIZONTAL
)
132 return win
->wxGenericTreeCtrl::GetScrollPos(orient
);
135 return scrolledWindow
->GetScrollPos(orient
);
139 int wxRemotelyScrolledTreeCtrl::GetScrollPos(int WXUNUSED(orient
)) const
146 // In case we're using the generic tree control.
147 // Get the view start
148 void wxRemotelyScrolledTreeCtrl::GetViewStart(int *x
, int *y
) const
150 wxScrolledWindow
* scrolledWindow
= GetScrolledWindow();
152 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
153 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
156 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
158 win
->wxGenericTreeCtrl::GetViewStart(& x1
, & y1
);
163 scrolledWindow
->GetViewStart(& x2
, & y2
);
169 // x is wrong since the horizontal scrollbar is controlled by the
170 // tree control, but we probably don't need it.
171 scrolledWindow
->GetViewStart(x
, y
);
175 // In case we're using the generic tree control.
176 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
177 void wxRemotelyScrolledTreeCtrl::PrepareDC(wxDC
& dc
)
179 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
181 wxScrolledWindow
* scrolledWindow
= GetScrolledWindow();
183 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
186 GetViewStart(& startX
, & startY
);
188 int xppu1
, yppu1
, xppu2
, yppu2
;
189 win
->wxGenericTreeCtrl::GetScrollPixelsPerUnit(& xppu1
, & yppu1
);
190 scrolledWindow
->GetScrollPixelsPerUnit(& xppu2
, & yppu2
);
192 dc
.SetDeviceOrigin( -startX
* xppu1
, -startY
* yppu2
);
193 // dc.SetUserScale( win->GetScaleX(), win->GetScaleY() );
196 void wxRemotelyScrolledTreeCtrl::PrepareDC(wxDC
& WXUNUSED(dc
))
201 // Scroll to the given line (in scroll units where each unit is
202 // the height of an item)
203 void wxRemotelyScrolledTreeCtrl::ScrollToLine(int WXUNUSED(posHoriz
), int posVert
)
206 #if USE_GENERIC_TREECTRL
207 if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
210 UINT sbCode
= SB_THUMBPOSITION
;
211 HWND vertScrollBar
= 0;
212 MSWDefWindowProc((WXUINT
) WM_VSCROLL
, MAKELONG(sbCode
, posVert
), (WXLPARAM
) vertScrollBar
);
214 #if USE_GENERIC_TREECTRL
218 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
220 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
222 /* Doesn't work yet because scrolling is ignored by Scroll
224 wxScrolledWindow* scrolledWindow = GetScrolledWindow();
227 scrolledWindow->GetScrollPixelsPerUnit(& xppu, & yppu);
228 win->Scroll(-1, posVert*yppu);
235 void wxRemotelyScrolledTreeCtrl::OnSize(wxSizeEvent
& event
)
238 AdjustRemoteScrollbars();
242 void wxRemotelyScrolledTreeCtrl::OnExpand(wxTreeEvent
& event
)
244 AdjustRemoteScrollbars();
247 // If we don't have this, we get some bits of lines still remaining
248 if (event
.GetEventType() == wxEVT_COMMAND_TREE_ITEM_COLLAPSED
)
252 if (m_companionWindow
)
253 m_companionWindow
->GetEventHandler()->ProcessEvent(event
);
256 // Adjust the containing wxScrolledWindow's scrollbars appropriately
257 void wxRemotelyScrolledTreeCtrl::AdjustRemoteScrollbars()
259 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
260 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
262 // This is for the generic tree control.
263 // It calls SetScrollbars which has been overridden
264 // to adjust the parent scrolled window vertical
266 ((wxGenericTreeCtrl
*) this)->AdjustMyScrollbars();
272 // This is for the wxMSW tree control
273 wxScrolledWindow
* scrolledWindow
= GetScrolledWindow();
277 if (GetBoundingRect(GetRootItem(), itemRect
))
279 // Actually, the real height seems to be 1 less than reported
280 // (e.g. 16 instead of 16)
281 int itemHeight
= itemRect
.GetHeight() - 1;
284 GetClientSize(&w
, &h
);
286 wxRect
rect(0, 0, 0, 0);
289 double f
= ((double) (rect
.GetHeight()) / (double) itemHeight
) ;
290 int treeViewHeight
= (int) ceil(f
);
292 int scrollPixelsPerLine
= itemHeight
;
293 int scrollPos
= - (itemRect
.y
/ itemHeight
);
295 scrolledWindow
->SetScrollbars(0, scrollPixelsPerLine
, 0, treeViewHeight
, 0, scrollPos
);
297 // Ensure that when a scrollbar becomes hidden or visible,
298 // the contained window sizes are right.
299 // Problem: this is called too early (?)
300 wxSizeEvent
event(scrolledWindow
->GetSize(), scrolledWindow
->GetId());
301 scrolledWindow
->GetEventHandler()->ProcessEvent(event
);
308 // Calculate the area that contains both rectangles
309 static wxRect
CombineRectangles(const wxRect
& rect1
, const wxRect
& rect2
)
313 int right1
= rect1
.GetRight();
314 int bottom1
= rect1
.GetBottom();
315 int right2
= rect2
.GetRight();
316 int bottom2
= rect2
.GetBottom();
318 wxPoint topLeft
= wxPoint(wxMin(rect1
.x
, rect2
.x
), wxMin(rect1
.y
, rect2
.y
));
319 wxPoint bottomRight
= wxPoint(wxMax(right1
, right2
), wxMax(bottom1
, bottom2
));
321 rect
.x
= topLeft
.x
; rect
.y
= topLeft
.y
;
322 rect
.SetRight(bottomRight
.x
);
323 rect
.SetBottom(bottomRight
.y
);
329 // Calculate the tree overall size so we can set the scrollbar
331 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(wxRect
& rect
)
333 CalcTreeSize(GetRootItem(), rect
);
336 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(const wxTreeItemId
& id
, wxRect
& rect
)
338 // More efficient implementation would be to find the last item (but how?)
339 // Q: is the bounding rect relative to the top of the virtual tree workspace
340 // or the top of the window? How would we convert?
342 if (GetBoundingRect(id
, itemSize
))
344 rect
= CombineRectangles(rect
, itemSize
);
348 wxTreeItemId childId
= GetFirstChild(id
, cookie
);
351 CalcTreeSize(childId
, rect
);
352 childId
= GetNextChild(childId
, cookie
);
356 // Find the scrolled window that contains this control
357 wxScrolledWindow
* wxRemotelyScrolledTreeCtrl::GetScrolledWindow() const
359 wxWindow
* parent
= wxWindow::GetParent();
362 if (parent
->IsKindOf(CLASSINFO(wxScrolledWindow
)))
363 return (wxScrolledWindow
*) parent
;
364 parent
= parent
->GetParent();
369 void wxRemotelyScrolledTreeCtrl::OnScroll(wxScrollWinEvent
& event
)
371 int orient
= event
.GetOrientation();
372 if (orient
== wxHORIZONTAL
)
377 wxScrolledWindow
* scrollWin
= GetScrolledWindow();
382 scrollWin
->GetViewStart(& x
, & y
);
388 * wxTreeCompanionWindow
390 * A window displaying values associated with tree control items.
393 IMPLEMENT_CLASS(wxTreeCompanionWindow
, wxWindow
)
395 BEGIN_EVENT_TABLE(wxTreeCompanionWindow
, wxWindow
)
396 EVT_PAINT(wxTreeCompanionWindow::OnPaint
)
397 EVT_SCROLLWIN(wxTreeCompanionWindow::OnScroll
)
398 EVT_TREE_ITEM_EXPANDED(-1, wxTreeCompanionWindow::OnExpand
)
399 EVT_TREE_ITEM_COLLAPSED(-1, wxTreeCompanionWindow::OnExpand
)
402 wxTreeCompanionWindow::wxTreeCompanionWindow(wxWindow
* parent
, wxWindowID id
,
406 wxWindow(parent
, id
, pos
, sz
, style
)
411 void wxTreeCompanionWindow::DrawItem(wxDC
& dc
, wxTreeItemId id
, const wxRect
& rect
)
417 wxString text
= m_treeCtrl
->GetItemText(id
);
418 dc
.SetTextForeground(* wxBLACK
);
419 dc
.SetBackgroundMode(wxTRANSPARENT
);
422 dc
.GetTextExtent(text
, & textW
, & textH
);
425 int y
= rect
.GetY() + wxMax(0, (rect
.GetHeight() - textH
) / 2);
427 dc
.DrawText(text
, x
, y
);
432 void wxTreeCompanionWindow::OnPaint(wxPaintEvent
& WXUNUSED(event
))
439 wxPen
pen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT
), 1, wxSOLID
);
441 dc
.SetBrush(* wxTRANSPARENT_BRUSH
);
442 wxFont
font(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
445 wxSize clientSize
= GetClientSize();
447 wxTreeItemId h
, lastH
;
448 for(h
=m_treeCtrl
->GetFirstVisibleItem();h
;h
=m_treeCtrl
->GetNextVisible(h
))
450 if (m_treeCtrl
->GetBoundingRect(h
, itemRect
))
452 int cy
= itemRect
.GetTop();
453 wxRect
drawItemRect(0, cy
, clientSize
.x
, itemRect
.GetHeight());
457 // Draw the actual item
458 DrawItem(dc
, h
, drawItemRect
);
459 dc
.DrawLine(0, cy
, clientSize
.x
, cy
);
462 if (lastH
.IsOk() && m_treeCtrl
->GetBoundingRect(lastH
, itemRect
))
464 int cy
= itemRect
.GetBottom();
465 dc
.DrawLine(0, cy
, clientSize
.x
, cy
);
469 void wxTreeCompanionWindow::OnScroll(wxScrollWinEvent
& event
)
471 int orient
= event
.GetOrientation();
472 if (orient
== wxHORIZONTAL
)
480 // TODO: scroll the window physically instead of just refreshing.
484 void wxTreeCompanionWindow::OnExpand(wxTreeEvent
& WXUNUSED(event
))
486 // TODO: something more optimized than simply refresh the whole
487 // window when the tree is expanded/collapsed. Tricky.
492 * wxThinSplitterWindow
495 IMPLEMENT_CLASS(wxThinSplitterWindow
, wxSplitterWindow
)
497 BEGIN_EVENT_TABLE(wxThinSplitterWindow
, wxSplitterWindow
)
498 EVT_SIZE(wxThinSplitterWindow::OnSize
)
501 wxThinSplitterWindow::wxThinSplitterWindow(wxWindow
* parent
, wxWindowID id
,
505 wxSplitterWindow(parent
, id
, pos
, sz
, style
)
509 wxThinSplitterWindow::~wxThinSplitterWindow()
513 void wxThinSplitterWindow::SizeWindows()
515 // The client size may have changed inbetween
516 // the sizing of the first window and the sizing of
517 // the second. So repeat SizeWindows.
518 wxSplitterWindow::SizeWindows();
519 wxSplitterWindow::SizeWindows();
522 // Tests for x, y over sash
523 bool wxThinSplitterWindow::SashHitTest(int x
, int y
, int WXUNUSED(tolerance
))
525 return wxSplitterWindow::SashHitTest(x
, y
, 4);
528 void wxThinSplitterWindow::DrawSash(wxDC
& dc
)
530 if ( GetSashPosition() == 0 || !m_windowTwo
)
532 if (GetWindowStyle() & wxSP_NOSASH
)
536 GetClientSize(&w
, &h
);
538 if ( m_splitMode
== wxSPLIT_VERTICAL
)
540 dc
.SetPen(* m_facePen
);
541 dc
.SetBrush(* m_faceBrush
);
544 if ( (GetWindowStyleFlag() & wxSP_BORDER
) != wxSP_BORDER
&& (GetWindowStyleFlag() & wxSP_3DBORDER
) != wxSP_3DBORDER
)
545 h1
+= 1; // Not sure why this is necessary...
546 if ( (GetWindowStyleFlag() & wxSP_3DBORDER
) == wxSP_3DBORDER
)
550 dc
.DrawRectangle(GetSashPosition(), y1
, GetSashSize(), h1
);
554 dc
.SetPen(* m_facePen
);
555 dc
.SetBrush(* m_faceBrush
);
558 if ( (GetWindowStyleFlag() & wxSP_BORDER
) != wxSP_BORDER
&& (GetWindowStyleFlag() & wxSP_3DBORDER
) != wxSP_3DBORDER
)
560 if ( (GetWindowStyleFlag() & wxSP_3DBORDER
) == wxSP_3DBORDER
)
564 dc
.DrawRectangle(x1
, GetSashPosition(), w1
, GetSashSize());
567 dc
.SetPen(wxNullPen
);
568 dc
.SetBrush(wxNullBrush
);
571 void wxThinSplitterWindow::OnSize(wxSizeEvent
& event
)
573 wxSplitterWindow::OnSize(event
);
577 * wxSplitterScrolledWindow
580 IMPLEMENT_CLASS(wxSplitterScrolledWindow
, wxScrolledWindow
)
582 BEGIN_EVENT_TABLE(wxSplitterScrolledWindow
, wxScrolledWindow
)
583 EVT_SCROLLWIN(wxSplitterScrolledWindow::OnScroll
)
584 EVT_SIZE(wxSplitterScrolledWindow::OnSize
)
587 wxSplitterScrolledWindow::wxSplitterScrolledWindow(wxWindow
* parent
, wxWindowID id
,
591 wxScrolledWindow(parent
, id
, pos
, sz
, style
)
595 void wxSplitterScrolledWindow::OnSize(wxSizeEvent
& WXUNUSED(event
))
597 wxSize sz
= GetClientSize();
598 if (GetChildren().GetFirst())
600 ((wxWindow
*) GetChildren().GetFirst()->GetData())->SetSize(0, 0, sz
.x
, sz
.y
);
604 void wxSplitterScrolledWindow::OnScroll(wxScrollWinEvent
& event
)
606 // Ensure that events being propagated back up the window hierarchy
607 // don't cause an infinite loop
608 static bool inOnScroll
= FALSE
;
616 int orient
= event
.GetOrientation();
618 int nScrollInc
= CalcScrollInc(event
);
625 if (orient
== wxHORIZONTAL
)
631 int newPos
= m_xScrollPosition
+ nScrollInc
;
632 SetScrollPos(wxHORIZONTAL
, newPos
, TRUE
);
637 int newPos
= m_yScrollPosition
+ nScrollInc
;
638 SetScrollPos(wxVERTICAL
, newPos
, TRUE
);
641 if (orient
== wxHORIZONTAL
)
643 m_xScrollPosition
+= nScrollInc
;
647 m_yScrollPosition
+= nScrollInc
;
650 // Find targets in splitter window and send the event to them
651 wxNode
* node
= (wxNode
*)GetChildren().GetFirst();
654 wxWindow
* child
= (wxWindow
*) node
->GetData();
655 if (child
->IsKindOf(CLASSINFO(wxSplitterWindow
)))
657 wxSplitterWindow
* splitter
= (wxSplitterWindow
*) child
;
658 if (splitter
->GetWindow1())
659 splitter
->GetWindow1()->ProcessEvent(event
);
660 if (splitter
->GetWindow2())
661 splitter
->GetWindow2()->ProcessEvent(event
);
664 node
= node
->GetNext();
668 m_targetWindow
->MacUpdateImmediately() ;