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 // For compilers that support precompilation, includes "wx/wx.h".
23 #include "wx/wxprec.h"
29 // for all others, include the necessary headers (this file is usually all you
30 // need because it includes almost all "standard" wxWidgets headers)
37 #include "wx/msw/winundef.h"
40 #include "wx/gizmos/splittree.h"
44 * wxRemotelyScrolledTreeCtrl
47 #if USE_GENERIC_TREECTRL
48 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl
, wxGenericTreeCtrl
)
50 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl
, wxTreeCtrl
)
53 #if USE_GENERIC_TREECTRL
54 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl
, wxGenericTreeCtrl
)
56 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl
, wxTreeCtrl
)
58 EVT_SIZE(wxRemotelyScrolledTreeCtrl::OnSize
)
59 EVT_PAINT(wxRemotelyScrolledTreeCtrl::OnPaint
)
60 EVT_TREE_ITEM_EXPANDED(wxID_ANY
, wxRemotelyScrolledTreeCtrl::OnExpand
)
61 EVT_TREE_ITEM_COLLAPSED(wxID_ANY
, wxRemotelyScrolledTreeCtrl::OnExpand
)
62 EVT_SCROLLWIN(wxRemotelyScrolledTreeCtrl::OnScroll
)
65 wxRemotelyScrolledTreeCtrl::wxRemotelyScrolledTreeCtrl(
66 wxWindow
* parent
, wxWindowID id
, const wxPoint
& pt
,
67 const wxSize
& sz
, long style
)
68 : wxTreeCtrl(parent
, id
, pt
, sz
, style
& ~wxTR_ROW_LINES
)
70 m_companionWindow
= NULL
;
72 // We draw the row lines ourself so they match what's done
73 // by the companion window. That is why the flag is turned
74 // off above, so wxGenericTreeCtrl doesn't draw them in a
76 m_drawRowLines
= (style
& wxTR_ROW_LINES
) != 0;
79 wxRemotelyScrolledTreeCtrl::~wxRemotelyScrolledTreeCtrl()
83 void wxRemotelyScrolledTreeCtrl::HideVScrollbar()
85 #if defined(__WXMSW__)
86 #if USE_GENERIC_TREECTRL
87 if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
90 ::ShowScrollBar((HWND
) GetHWND(), SB_VERT
, false);
92 #if USE_GENERIC_TREECTRL
95 // Implicit in overriding SetScrollbars
101 // Number of pixels per user unit (0 or -1 for no scrollbar)
102 // Length of virtual canvas in user units
103 // Length of page in user units
104 void wxRemotelyScrolledTreeCtrl::SetScrollbars(
105 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
106 int pixelsPerUnitX
, int pixelsPerUnitY
,
107 int noUnitsX
, int noUnitsY
,
111 int WXUNUSED(pixelsPerUnitX
), int WXUNUSED(pixelsPerUnitY
),
112 int WXUNUSED(noUnitsX
), int WXUNUSED(noUnitsY
),
113 int WXUNUSED(xPos
), int WXUNUSED(yPos
),
114 bool WXUNUSED(noRefresh
)
118 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
119 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
121 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
122 win
->wxGenericTreeCtrl::SetScrollbars(pixelsPerUnitX
, pixelsPerUnitY
, noUnitsX
, 0, xPos
, 0, /* noRefresh */ true);
124 wxScrolledWindow
* scrolledWindow
= GetScrolledWindow();
127 scrolledWindow
->SetScrollbars(0, pixelsPerUnitY
, 0, noUnitsY
, 0, yPos
, noRefresh
);
133 // In case we're using the generic tree control.
134 int wxRemotelyScrolledTreeCtrl::GetScrollPos(
135 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
143 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
144 // this condition fixes extsitence of warning but
145 wxScrolledWindow
* scrolledWindow
=
146 // but GetScrolledWindow is still executed in case internally does something
150 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
151 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
153 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
155 if (orient
== wxHORIZONTAL
)
156 return win
->wxGenericTreeCtrl::GetScrollPos(orient
);
159 return scrolledWindow
->GetScrollPos(orient
);
167 // In case we're using the generic tree control.
168 // Get the view start
169 void wxRemotelyScrolledTreeCtrl::GetViewStart(int *x
, int *y
) const
171 wxScrolledWindow
* scrolledWindow
= GetScrolledWindow();
173 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
174 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
177 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
179 win
->wxGenericTreeCtrl::GetViewStart(& x1
, & y1
);
184 scrolledWindow
->GetViewStart(& x2
, & y2
);
190 // x is wrong since the horizontal scrollbar is controlled by the
191 // tree control, but we probably don't need it.
192 scrolledWindow
->GetViewStart(x
, y
);
196 // In case we're using the generic tree control.
197 void wxRemotelyScrolledTreeCtrl::PrepareDC(
198 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
205 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
206 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
208 wxScrolledWindow
* scrolledWindow
= GetScrolledWindow();
210 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
213 GetViewStart(& startX
, & startY
);
215 int xppu1
, yppu1
, xppu2
, yppu2
;
216 win
->wxGenericTreeCtrl::GetScrollPixelsPerUnit(& xppu1
, & yppu1
);
217 scrolledWindow
->GetScrollPixelsPerUnit(& xppu2
, & yppu2
);
219 dc
.SetDeviceOrigin( -startX
* xppu1
, -startY
* yppu2
);
220 // dc.SetUserScale( win->GetScaleX(), win->GetScaleY() );
225 // Scroll to the given line (in scroll units where each unit is
226 // the height of an item)
227 void wxRemotelyScrolledTreeCtrl::ScrollToLine(int WXUNUSED(posHoriz
), int posVert
)
230 #if USE_GENERIC_TREECTRL
231 if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
232 #endif // USE_GENERIC_TREECTRL
234 UINT sbCode
= SB_THUMBPOSITION
;
235 HWND vertScrollBar
= 0;
236 MSWDefWindowProc((WXUINT
) WM_VSCROLL
, MAKELONG(sbCode
, posVert
), (WXLPARAM
) vertScrollBar
);
238 #if USE_GENERIC_TREECTRL
240 #endif // USE_GENERIC_TREECTRL
242 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
244 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
246 /* Doesn't work yet because scrolling is ignored by Scroll
248 wxScrolledWindow* scrolledWindow = GetScrolledWindow();
251 scrolledWindow->GetScrollPixelsPerUnit(& xppu, & yppu);
252 win->Scroll(-1, posVert*yppu);
256 #endif // USE_GENERIC_TREECTRL || !defined(__WXMSW__)
257 wxUnusedVar(posVert
);
260 void wxRemotelyScrolledTreeCtrl::OnSize(wxSizeEvent
& event
)
263 AdjustRemoteScrollbars();
267 void wxRemotelyScrolledTreeCtrl::OnExpand(wxTreeEvent
& event
)
269 AdjustRemoteScrollbars();
272 // If we don't have this, we get some bits of lines still remaining
273 if (event
.GetEventType() == wxEVT_COMMAND_TREE_ITEM_COLLAPSED
)
277 if (m_companionWindow
)
278 m_companionWindow
->GetEventHandler()->ProcessEvent(event
);
281 void wxRemotelyScrolledTreeCtrl::OnPaint(wxPaintEvent
& event
)
285 wxTreeCtrl::OnPaint(event
);
287 if (! m_drawRowLines
)
290 // Reset the device origin since it may have been set
291 dc
.SetDeviceOrigin(0, 0);
293 wxPen
pen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT
), 1, wxSOLID
);
295 dc
.SetBrush(* wxTRANSPARENT_BRUSH
);
297 wxSize clientSize
= GetClientSize();
299 wxTreeItemId h
, lastH
;
300 for (h
=GetFirstVisibleItem();
304 if (GetBoundingRect(h
, itemRect
))
306 int cy
= itemRect
.GetTop();
307 dc
.DrawLine(0, cy
, clientSize
.x
, cy
);
313 if (lastH
.IsOk() && GetBoundingRect(lastH
, itemRect
))
315 int cy
= itemRect
.GetBottom();
316 dc
.DrawLine(0, cy
, clientSize
.x
, cy
);
321 // Adjust the containing wxScrolledWindow's scrollbars appropriately
322 void wxRemotelyScrolledTreeCtrl::AdjustRemoteScrollbars()
324 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
325 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
327 // This is for the generic tree control.
328 // It calls SetScrollbars which has been overridden
329 // to adjust the parent scrolled window vertical
331 ((wxGenericTreeCtrl
*) this)->AdjustMyScrollbars();
337 // This is for the wxMSW tree control
338 wxScrolledWindow
* scrolledWindow
= GetScrolledWindow();
342 if (GetBoundingRect(GetFirstVisibleItem(), itemRect
))
344 // Actually, the real height seems to be 1 less than reported
345 // (e.g. 16 instead of 16)
346 int itemHeight
= itemRect
.GetHeight() - 1;
349 GetClientSize(&w
, &h
);
351 wxRect
rect(0, 0, 0, 0);
354 double f
= ((double) (rect
.GetHeight()) / (double) itemHeight
) ;
355 int treeViewHeight
= (int) ceil(f
);
357 int scrollPixelsPerLine
= itemHeight
;
358 int scrollPos
= - (itemRect
.y
/ itemHeight
);
360 scrolledWindow
->SetScrollbars(0, scrollPixelsPerLine
, 0, treeViewHeight
, 0, scrollPos
);
362 // Ensure that when a scrollbar becomes hidden or visible,
363 // the contained window sizes are right.
364 // Problem: this is called too early (?)
365 wxSizeEvent
event(scrolledWindow
->GetSize(), scrolledWindow
->GetId());
366 scrolledWindow
->GetEventHandler()->ProcessEvent(event
);
373 // Calculate the area that contains both rectangles
374 static wxRect
CombineRectangles(const wxRect
& rect1
, const wxRect
& rect2
)
378 int right1
= rect1
.GetRight();
379 int bottom1
= rect1
.GetBottom();
380 int right2
= rect2
.GetRight();
381 int bottom2
= rect2
.GetBottom();
383 wxPoint topLeft
= wxPoint(wxMin(rect1
.x
, rect2
.x
), wxMin(rect1
.y
, rect2
.y
));
384 wxPoint bottomRight
= wxPoint(wxMax(right1
, right2
), wxMax(bottom1
, bottom2
));
386 rect
.x
= topLeft
.x
; rect
.y
= topLeft
.y
;
387 rect
.SetRight(bottomRight
.x
);
388 rect
.SetBottom(bottomRight
.y
);
394 // Calculate the tree overall size so we can set the scrollbar
396 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(wxRect
& rect
)
398 CalcTreeSize(GetRootItem(), rect
);
401 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(const wxTreeItemId
& id
, wxRect
& rect
)
403 // More efficient implementation would be to find the last item (but how?)
404 // Q: is the bounding rect relative to the top of the virtual tree workspace
405 // or the top of the window? How would we convert?
407 if (GetBoundingRect(id
, itemSize
))
409 rect
= CombineRectangles(rect
, itemSize
);
412 wxTreeItemIdValue cookie
;
413 wxTreeItemId childId
= GetFirstChild(id
, cookie
);
416 CalcTreeSize(childId
, rect
);
417 childId
= GetNextChild(childId
, cookie
);
421 // Find the scrolled window that contains this control
422 wxScrolledWindow
* wxRemotelyScrolledTreeCtrl::GetScrolledWindow() const
424 wxWindow
* parent
= wxWindow::GetParent();
427 if (parent
->IsKindOf(CLASSINFO(wxScrolledWindow
)))
428 return (wxScrolledWindow
*) parent
;
429 parent
= parent
->GetParent();
434 void wxRemotelyScrolledTreeCtrl::OnScroll(wxScrollWinEvent
& event
)
436 int orient
= event
.GetOrientation();
437 if (orient
== wxHORIZONTAL
)
442 wxScrolledWindow
* scrollWin
= GetScrolledWindow();
447 scrollWin
->GetViewStart(& x
, & y
);
453 * wxTreeCompanionWindow
455 * A window displaying values associated with tree control items.
458 IMPLEMENT_CLASS(wxTreeCompanionWindow
, wxWindow
)
460 BEGIN_EVENT_TABLE(wxTreeCompanionWindow
, wxWindow
)
461 EVT_PAINT(wxTreeCompanionWindow::OnPaint
)
462 EVT_SCROLLWIN(wxTreeCompanionWindow::OnScroll
)
463 EVT_TREE_ITEM_EXPANDED(-1, wxTreeCompanionWindow::OnExpand
)
464 EVT_TREE_ITEM_COLLAPSED(-1, wxTreeCompanionWindow::OnExpand
)
467 wxTreeCompanionWindow::wxTreeCompanionWindow(wxWindow
* parent
, wxWindowID id
,
471 wxWindow(parent
, id
, pos
, sz
, style
)
476 void wxTreeCompanionWindow::DrawItem(wxDC
& dc
, wxTreeItemId id
, const wxRect
& rect
)
482 wxString text
= m_treeCtrl
->GetItemText(id
);
483 dc
.SetTextForeground(* wxBLACK
);
484 dc
.SetBackgroundMode(wxTRANSPARENT
);
487 dc
.GetTextExtent(text
, & textW
, & textH
);
490 int y
= rect
.GetY() + wxMax(0, (rect
.GetHeight() - textH
) / 2);
492 dc
.DrawText(text
, x
, y
);
497 void wxTreeCompanionWindow::OnPaint(wxPaintEvent
& WXUNUSED(event
))
504 wxPen
pen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT
), 1, wxSOLID
);
506 dc
.SetBrush(* wxTRANSPARENT_BRUSH
);
507 wxFont
font(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
510 wxSize clientSize
= GetClientSize();
512 wxTreeItemId h
, lastH
;
513 for (h
=m_treeCtrl
->GetFirstVisibleItem();
515 h
=m_treeCtrl
->GetNextVisible(h
))
517 if (m_treeCtrl
->GetBoundingRect(h
, itemRect
))
519 int cy
= itemRect
.GetTop();
520 wxRect
drawItemRect(0, cy
, clientSize
.x
, itemRect
.GetHeight());
524 // Draw the actual item
525 DrawItem(dc
, h
, drawItemRect
);
526 dc
.DrawLine(0, cy
, clientSize
.x
, cy
);
528 if (! m_treeCtrl
->IsVisible(h
))
531 if (lastH
.IsOk() && m_treeCtrl
->GetBoundingRect(lastH
, itemRect
))
533 int cy
= itemRect
.GetBottom();
534 dc
.DrawLine(0, cy
, clientSize
.x
, cy
);
538 void wxTreeCompanionWindow::OnScroll(wxScrollWinEvent
& event
)
540 int orient
= event
.GetOrientation();
541 if (orient
== wxHORIZONTAL
)
549 // TODO: scroll the window physically instead of just refreshing.
553 void wxTreeCompanionWindow::OnExpand(wxTreeEvent
& WXUNUSED(event
))
555 // TODO: something more optimized than simply refresh the whole
556 // window when the tree is expanded/collapsed. Tricky.
561 * wxThinSplitterWindow
564 IMPLEMENT_CLASS(wxThinSplitterWindow
, wxSplitterWindow
)
566 BEGIN_EVENT_TABLE(wxThinSplitterWindow
, wxSplitterWindow
)
567 EVT_SIZE(wxThinSplitterWindow::OnSize
)
570 wxThinSplitterWindow::wxThinSplitterWindow(wxWindow
* parent
, wxWindowID id
,
574 wxSplitterWindow(parent
, id
, pos
, sz
, style
)
576 wxColour
faceColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
577 m_facePen
= new wxPen(faceColour
, 1, wxSOLID
);
578 m_faceBrush
= new wxBrush(faceColour
, wxSOLID
);
581 wxThinSplitterWindow::~wxThinSplitterWindow()
588 void wxThinSplitterWindow::SizeWindows()
590 // The client size may have changed inbetween
591 // the sizing of the first window and the sizing of
592 // the second. So repeat SizeWindows.
593 wxSplitterWindow::SizeWindows();
594 wxSplitterWindow::SizeWindows();
597 // Tests for x, y over sash
598 bool wxThinSplitterWindow::SashHitTest(int x
, int y
, int WXUNUSED(tolerance
))
600 return wxSplitterWindow::SashHitTest(x
, y
, 4);
603 void wxThinSplitterWindow::DrawSash(wxDC
& dc
)
605 if ( m_sashPosition
== 0 || !m_windowTwo
)
607 if (GetWindowStyle() & wxSP_NOSASH
)
611 GetClientSize(&w
, &h
);
613 if ( m_splitMode
== wxSPLIT_VERTICAL
)
615 dc
.SetPen(* m_facePen
);
616 dc
.SetBrush(* m_faceBrush
);
619 if ( (GetWindowStyleFlag() & wxSP_BORDER
) != wxSP_BORDER
&& (GetWindowStyleFlag() & wxSP_3DBORDER
) != wxSP_3DBORDER
)
620 h1
+= 1; // Not sure why this is necessary...
621 if ( (GetWindowStyleFlag() & wxSP_3DBORDER
) == wxSP_3DBORDER
)
625 dc
.DrawRectangle(m_sashPosition
, y1
, GetSashSize(), h1
);
629 dc
.SetPen(* m_facePen
);
630 dc
.SetBrush(* m_faceBrush
);
633 if ( (GetWindowStyleFlag() & wxSP_BORDER
) != wxSP_BORDER
&& (GetWindowStyleFlag() & wxSP_3DBORDER
) != wxSP_3DBORDER
)
635 if ( (GetWindowStyleFlag() & wxSP_3DBORDER
) == wxSP_3DBORDER
)
639 dc
.DrawRectangle(x1
, m_sashPosition
, w1
, GetSashSize());
642 dc
.SetPen(wxNullPen
);
643 dc
.SetBrush(wxNullBrush
);
646 void wxThinSplitterWindow::OnSize(wxSizeEvent
& event
)
648 wxSplitterWindow::OnSize(event
);
652 * wxSplitterScrolledWindow
655 IMPLEMENT_CLASS(wxSplitterScrolledWindow
, wxScrolledWindow
)
657 BEGIN_EVENT_TABLE(wxSplitterScrolledWindow
, wxScrolledWindow
)
658 EVT_SCROLLWIN(wxSplitterScrolledWindow::OnScroll
)
659 EVT_SIZE(wxSplitterScrolledWindow::OnSize
)
662 wxSplitterScrolledWindow::wxSplitterScrolledWindow(wxWindow
* parent
, wxWindowID id
,
666 wxScrolledWindow(parent
, id
, pos
, sz
, style
)
670 void wxSplitterScrolledWindow::OnSize(wxSizeEvent
& WXUNUSED(event
))
672 wxSize sz
= GetClientSize();
673 if (GetChildren().GetFirst())
675 ((wxWindow
*) GetChildren().GetFirst()->GetData())->SetSize(0, 0, sz
.x
, sz
.y
);
679 void wxSplitterScrolledWindow::OnScroll(wxScrollWinEvent
& event
)
681 // Ensure that events being propagated back up the window hierarchy
682 // don't cause an infinite loop
683 static bool inOnScroll
= false;
691 int orient
= event
.GetOrientation();
693 int nScrollInc
= CalcScrollInc(event
);
700 if (orient
== wxHORIZONTAL
)
706 int newPos
= m_xScrollPosition
+ nScrollInc
;
707 SetScrollPos(wxHORIZONTAL
, newPos
, true );
712 int newPos
= m_yScrollPosition
+ nScrollInc
;
713 SetScrollPos(wxVERTICAL
, newPos
, true );
716 if (orient
== wxHORIZONTAL
)
718 m_xScrollPosition
+= nScrollInc
;
722 m_yScrollPosition
+= nScrollInc
;
725 // Find targets in splitter window and send the event to them
726 wxWindowList::compatibility_iterator node
= GetChildren().GetFirst();
729 wxWindow
* child
= (wxWindow
*) node
->GetData();
730 if (child
->IsKindOf(CLASSINFO(wxSplitterWindow
)))
732 wxSplitterWindow
* splitter
= (wxSplitterWindow
*) child
;
733 if (splitter
->GetWindow1())
734 splitter
->GetWindow1()->ProcessEvent(event
);
735 if (splitter
->GetWindow2())
736 splitter
->GetWindow2()->ProcessEvent(event
);
739 node
= node
->GetNext();
742 m_targetWindow
->Update() ;