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 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" wxWindows headers)
38 #include "wx/generic/treectlg.h"
42 #include "wx/msw/winundef.h"
45 #include "splittree.h"
48 * wxRemotelyScrolledTreeCtrl
51 #if USE_GENERIC_TREECTRL
52 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl
, wxGenericTreeCtrl
)
54 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl
, wxTreeCtrl
)
57 #if USE_GENERIC_TREECTRL
58 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl
, wxGenericTreeCtrl
)
60 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl
, wxTreeCtrl
)
62 EVT_SIZE(wxRemotelyScrolledTreeCtrl::OnSize
)
63 EVT_TREE_ITEM_EXPANDED(-1, wxRemotelyScrolledTreeCtrl::OnExpand
)
64 EVT_TREE_ITEM_COLLAPSED(-1, wxRemotelyScrolledTreeCtrl::OnExpand
)
65 EVT_SCROLLWIN(wxRemotelyScrolledTreeCtrl::OnScroll
)
68 wxRemotelyScrolledTreeCtrl::wxRemotelyScrolledTreeCtrl(wxWindow
* parent
, wxWindowID id
, const wxPoint
& pt
,
69 const wxSize
& sz
, long style
):
70 wxTreeCtrl(parent
, id
, pt
, sz
, style
)
72 m_companionWindow
= NULL
;
75 wxRemotelyScrolledTreeCtrl::~wxRemotelyScrolledTreeCtrl()
79 void wxRemotelyScrolledTreeCtrl::HideVScrollbar()
82 if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
84 ::ShowScrollBar((HWND
) GetHWND(), SB_VERT
, FALSE
);
89 // Implicit in overriding SetScrollbars
93 // Number of pixels per user unit (0 or -1 for no scrollbar)
94 // Length of virtual canvas in user units
95 // Length of page in user units
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
);
114 // In case we're using the generic tree control.
115 int wxRemotelyScrolledTreeCtrl::GetScrollPos(int orient
) const
117 wxScrolledWindow
* scrolledWindow
= GetScrolledWindow();
119 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
121 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
123 if (orient
== wxHORIZONTAL
)
124 return win
->wxGenericTreeCtrl::GetScrollPos(orient
);
127 return scrolledWindow
->GetScrollPos(orient
);
134 // In case we're using the generic tree control.
135 // Get the view start
136 void wxRemotelyScrolledTreeCtrl::GetViewStart(int *x
, int *y
) const
138 wxScrolledWindow
* scrolledWindow
= GetScrolledWindow();
140 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
143 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
145 win
->wxGenericTreeCtrl::GetViewStart(& x1
, & y1
);
150 scrolledWindow
->GetViewStart(& x2
, & y2
);
155 // x is wrong since the horizontal scrollbar is controlled by the
156 // tree control, but we probably don't need it.
157 scrolledWindow
->GetViewStart(x
, y
);
161 // In case we're using the generic tree control.
162 void wxRemotelyScrolledTreeCtrl::PrepareDC(wxDC
& dc
)
164 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
166 wxScrolledWindow
* scrolledWindow
= GetScrolledWindow();
168 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
171 GetViewStart(& startX
, & startY
);
173 int xppu1
, yppu1
, xppu2
, yppu2
;
174 win
->wxGenericTreeCtrl::GetScrollPixelsPerUnit(& xppu1
, & yppu1
);
175 scrolledWindow
->GetScrollPixelsPerUnit(& xppu2
, & yppu2
);
177 dc
.SetDeviceOrigin( -startX
* xppu1
, -startY
* yppu2
);
178 //dc.SetUserScale( win->GetScaleX(), win->GetScaleY() );
182 // Scroll to the given line (in scroll units where each unit is
183 // the height of an item)
184 void wxRemotelyScrolledTreeCtrl::ScrollToLine(int posHoriz
, int posVert
)
187 if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
189 UINT sbCode
= SB_THUMBPOSITION
;
190 HWND vertScrollBar
= 0;
191 MSWDefWindowProc((WXUINT
) WM_VSCROLL
, MAKELONG(sbCode
, posVert
), (WXHWND
) vertScrollBar
);
196 wxGenericTreeCtrl
* win
= (wxGenericTreeCtrl
*) this;
198 /* Doesn't work yet because scrolling is ignored by Scroll
200 wxScrolledWindow* scrolledWindow = GetScrolledWindow();
203 scrolledWindow->GetScrollPixelsPerUnit(& xppu, & yppu);
204 win->Scroll(-1, posVert*yppu);
210 void wxRemotelyScrolledTreeCtrl::OnSize(wxSizeEvent
& event
)
213 AdjustRemoteScrollbars();
217 void wxRemotelyScrolledTreeCtrl::OnExpand(wxTreeEvent
& event
)
219 AdjustRemoteScrollbars();
222 // If we don't have this, we get some bits of lines still remaining
223 if (event
.GetEventType() == wxEVT_COMMAND_TREE_ITEM_COLLAPSED
)
227 if (m_companionWindow
)
228 m_companionWindow
->GetEventHandler()->ProcessEvent(event
);
231 // Adjust the containing wxScrolledWindow's scrollbars appropriately
232 void wxRemotelyScrolledTreeCtrl::AdjustRemoteScrollbars()
234 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
)))
236 // This is for the generic tree control.
237 // It calls SetScrollbars which has been overridden
238 // to adjust the parent scrolled window vertical
240 ((wxGenericTreeCtrl
*) this)->AdjustMyScrollbars();
245 // This is for the wxMSW tree control
246 wxScrolledWindow
* scrolledWindow
= GetScrolledWindow();
250 if (GetBoundingRect(GetRootItem(), itemRect
))
252 int itemHeight
= itemRect
.GetHeight();
255 GetClientSize(&w
, &h
);
257 wxRect
rect(0, 0, 0, 0);
259 int treeViewHeight
= rect
.GetHeight()/itemHeight
;
261 int scrollPixelsPerLine
= itemHeight
;
262 int scrollPos
= - (itemRect
.y
/ itemHeight
);
264 scrolledWindow
->SetScrollbars(0, scrollPixelsPerLine
, 0, treeViewHeight
, 0, scrollPos
);
266 // Ensure that when a scrollbar becomes hidden or visible,
267 // the contained window sizes are right.
268 // Problem: this is called too early (?)
269 wxSizeEvent
event(scrolledWindow
->GetSize(), scrolledWindow
->GetId());
270 scrolledWindow
->GetEventHandler()->ProcessEvent(event
);
277 // Calculate the area that contains both rectangles
278 static wxRect
CombineRectangles(const wxRect
& rect1
, const wxRect
& rect2
)
282 int right1
= rect1
.GetRight();
283 int bottom1
= rect1
.GetBottom();
284 int right2
= rect2
.GetRight();
285 int bottom2
= rect2
.GetBottom();
287 wxPoint topLeft
= wxPoint(wxMin(rect1
.x
, rect2
.x
), wxMin(rect1
.y
, rect2
.y
));
288 wxPoint bottomRight
= wxPoint(wxMax(right1
, right2
), wxMax(bottom1
, bottom2
));
290 rect
.x
= topLeft
.x
; rect
.y
= topLeft
.y
;
291 rect
.SetRight(bottomRight
.x
);
292 rect
.SetBottom(bottomRight
.y
);
298 // Calculate the tree overall size so we can set the scrollbar
300 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(wxRect
& rect
)
302 CalcTreeSize(GetRootItem(), rect
);
305 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(const wxTreeItemId
& id
, wxRect
& rect
)
307 // TODO: implement GetFirst/NextVisibleItem
308 // for wxGenericTreeCtrl, plus GetBoundingRect.
310 // More efficient implementation would be to find the last item (but how?)
311 // Q: is the bounding rect relative to the top of the virtual tree workspace
312 // or the top of the window? How would we convert?
314 if (GetBoundingRect(id
, itemSize
))
316 rect
= CombineRectangles(rect
, itemSize
);
320 wxTreeItemId childId
= GetFirstChild(id
, cookie
);
323 CalcTreeSize(childId
, rect
);
324 childId
= GetNextChild(childId
, cookie
);
328 // Find the scrolled window that contains this control
329 wxScrolledWindow
* wxRemotelyScrolledTreeCtrl::GetScrolledWindow() const
331 wxWindow
* parent
= wxWindow::GetParent();
334 if (parent
->IsKindOf(CLASSINFO(wxScrolledWindow
)))
335 return (wxScrolledWindow
*) parent
;
336 parent
= parent
->GetParent();
341 void wxRemotelyScrolledTreeCtrl::OnScroll(wxScrollWinEvent
& event
)
343 int orient
= event
.GetOrientation();
344 if (orient
== wxHORIZONTAL
)
349 wxScrolledWindow
* scrollWin
= GetScrolledWindow();
354 scrollWin
->GetViewStart(& x
, & y
);
360 * wxTreeCompanionWindow
362 * A window displaying values associated with tree control items.
365 IMPLEMENT_CLASS(wxTreeCompanionWindow
, wxWindow
)
367 BEGIN_EVENT_TABLE(wxTreeCompanionWindow
, wxWindow
)
368 EVT_PAINT(wxTreeCompanionWindow::OnPaint
)
369 EVT_SCROLLWIN(wxTreeCompanionWindow::OnScroll
)
370 EVT_TREE_ITEM_EXPANDED(-1, wxTreeCompanionWindow::OnExpand
)
371 EVT_TREE_ITEM_COLLAPSED(-1, wxTreeCompanionWindow::OnExpand
)
374 wxTreeCompanionWindow::wxTreeCompanionWindow(wxWindow
* parent
, wxWindowID id
,
378 wxWindow(parent
, id
, pos
, sz
, style
)
383 void wxTreeCompanionWindow::DrawItem(wxDC
& dc
, wxTreeItemId id
, const wxRect
& rect
)
389 wxString text
= m_treeCtrl
->GetItemText(id
);
390 dc
.SetTextForeground(* wxBLACK
);
391 dc
.SetBackgroundMode(wxTRANSPARENT
);
394 dc
.GetTextExtent(text
, & textW
, & textH
);
397 int y
= rect
.GetY() + wxMax(0, (rect
.GetHeight() - textH
) / 2);
399 dc
.DrawText(text
, x
, y
);
404 void wxTreeCompanionWindow::OnPaint(wxPaintEvent
& event
)
411 wxPen
pen(wxColour(_T("BLACK")), 1, wxSOLID
);
413 dc
.SetBrush(* wxTRANSPARENT_BRUSH
);
414 wxFont
font(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT
));
417 wxSize clientSize
= GetClientSize();
420 wxTreeItemId h
, lastH
;
421 for(h
=m_treeCtrl
->GetFirstVisibleItem();h
;h
=m_treeCtrl
->GetNextVisible(h
))
423 if (m_treeCtrl
->GetBoundingRect(h
, itemRect
))
425 cy
= itemRect
.GetTop();
426 wxRect
drawItemRect(0, cy
, clientSize
.x
, itemRect
.GetHeight());
430 // Draw the actual item
431 DrawItem(dc
, h
, drawItemRect
);
432 dc
.DrawLine(0, cy
, clientSize
.x
, cy
);
435 if (lastH
.IsOk() && m_treeCtrl
->GetBoundingRect(lastH
, itemRect
))
437 cy
= itemRect
.GetBottom();
438 dc
.DrawLine(0, cy
, clientSize
.x
, cy
);
442 void wxTreeCompanionWindow::OnScroll(wxScrollWinEvent
& event
)
444 int orient
= event
.GetOrientation();
445 if (orient
== wxHORIZONTAL
)
453 // TODO: scroll the window physically instead of just refreshing.
457 void wxTreeCompanionWindow::OnExpand(wxTreeEvent
& event
)
459 // TODO: something more optimized than simply refresh the whole
460 // window when the tree is expanded/collapsed. Tricky.
465 * wxThinSplitterWindow
468 IMPLEMENT_CLASS(wxThinSplitterWindow
, wxSplitterWindow
)
470 BEGIN_EVENT_TABLE(wxThinSplitterWindow
, wxSplitterWindow
)
471 EVT_SIZE(wxThinSplitterWindow::OnSize
)
474 wxThinSplitterWindow::wxThinSplitterWindow(wxWindow
* parent
, wxWindowID id
,
478 wxSplitterWindow(parent
, id
, pos
, sz
, style
)
482 void wxThinSplitterWindow::SizeWindows()
484 // The client size may have changed inbetween
485 // the sizing of the first window and the sizing of
486 // the second. So repeat SizeWindows.
487 wxSplitterWindow::SizeWindows();
488 wxSplitterWindow::SizeWindows();
491 // Tests for x, y over sash
492 bool wxThinSplitterWindow::SashHitTest(int x
, int y
, int tolerance
)
494 return wxSplitterWindow::SashHitTest(x
, y
, 4);
497 void wxThinSplitterWindow::DrawSash(wxDC
& dc
)
499 if ( m_sashPosition
== 0 || !m_windowTwo
)
501 if (GetWindowStyle() & wxSP_NOSASH
)
505 GetClientSize(&w
, &h
);
507 if ( m_splitMode
== wxSPLIT_VERTICAL
)
509 dc
.SetPen(* m_facePen
);
510 dc
.SetBrush(* m_faceBrush
);
513 if ( (GetWindowStyleFlag() & wxSP_BORDER
) != wxSP_BORDER
&& (GetWindowStyleFlag() & wxSP_3DBORDER
) != wxSP_3DBORDER
)
514 h1
+= 1; // Not sure why this is necessary...
515 if ( (GetWindowStyleFlag() & wxSP_3DBORDER
) == wxSP_3DBORDER
)
519 dc
.DrawRectangle(m_sashPosition
, y1
, m_sashSize
, h1
);
523 dc
.SetPen(* m_facePen
);
524 dc
.SetBrush(* m_faceBrush
);
527 if ( (GetWindowStyleFlag() & wxSP_BORDER
) != wxSP_BORDER
&& (GetWindowStyleFlag() & wxSP_3DBORDER
) != wxSP_3DBORDER
)
529 if ( (GetWindowStyleFlag() & wxSP_3DBORDER
) == wxSP_3DBORDER
)
533 dc
.DrawRectangle(x1
, m_sashPosition
, w1
, m_sashSize
);
536 dc
.SetPen(wxNullPen
);
537 dc
.SetBrush(wxNullBrush
);
540 void wxThinSplitterWindow::OnSize(wxSizeEvent
& event
)
542 wxSplitterWindow::OnSize(event
);
546 * wxSplitterScrolledWindow
549 IMPLEMENT_CLASS(wxSplitterScrolledWindow
, wxScrolledWindow
)
551 BEGIN_EVENT_TABLE(wxSplitterScrolledWindow
, wxScrolledWindow
)
552 EVT_SCROLLWIN(wxSplitterScrolledWindow::OnScroll
)
553 EVT_SIZE(wxSplitterScrolledWindow::OnSize
)
556 wxSplitterScrolledWindow::wxSplitterScrolledWindow(wxWindow
* parent
, wxWindowID id
,
560 wxScrolledWindow(parent
, id
, pos
, sz
, style
)
564 void wxSplitterScrolledWindow::OnSize(wxSizeEvent
& event
)
566 wxSize sz
= GetClientSize();
567 if (GetChildren().First())
569 ((wxWindow
*) GetChildren().First()->Data())->SetSize(0, 0, sz
.x
, sz
.y
);
573 void wxSplitterScrolledWindow::OnScroll(wxScrollWinEvent
& event
)
575 // Ensure that events being propagated back up the window hierarchy
576 // don't cause an infinite loop
577 static bool inOnScroll
= FALSE
;
582 int orient
= event
.GetOrientation();
584 int nScrollInc
= 16;// FIXME CalcScrollInc(event);
591 if (orient
== wxHORIZONTAL
)
597 int newPos
= m_xScrollPosition
+ nScrollInc
;
598 SetScrollPos(wxHORIZONTAL
, newPos
, TRUE
);
603 int newPos
= m_yScrollPosition
+ nScrollInc
;
604 SetScrollPos(wxVERTICAL
, newPos
, TRUE
);
607 if (orient
== wxHORIZONTAL
)
609 m_xScrollPosition
+= nScrollInc
;
613 m_yScrollPosition
+= nScrollInc
;
616 // Find targets in splitter window and send the event to them
617 wxNode
* node
= GetChildren().First();
620 wxWindow
* child
= (wxWindow
*) node
->Data();
621 if (child
->IsKindOf(CLASSINFO(wxSplitterWindow
)))
623 wxSplitterWindow
* splitter
= (wxSplitterWindow
*) child
;
624 if (splitter
->GetWindow1())
625 splitter
->GetWindow1()->ProcessEvent(event
);
626 if (splitter
->GetWindow2())
627 splitter
->GetWindow2()->ProcessEvent(event
);
634 m_targetWindow
->MacUpdateImmediately() ;