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
12 // This program is part of the eCos host tools.
14 // This program is free software; you can redistribute it and/or modify it
15 // under the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 of the License, or (at your option)
19 // This program is distributed in the hope that it will be useful, but WITHOUT
20 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
24 // You should have received a copy of the GNU General Public License along with
25 // this program; if not, write to the Free Software Foundation, Inc.,
26 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 /////////////////////////////////////////////////////////////////////////////
30 // ============================================================================
32 // ============================================================================
34 // ----------------------------------------------------------------------------
36 // ----------------------------------------------------------------------------
38 #pragma implementation "splittree.h"
41 // For compilers that support precompilation, includes "wx/wx.h".
45 #include "wx/msw/private.h"
52 #include "wx/generic/treectlg.h"
54 #include "splittree.h"
58 * wxRemotelyScrolledTreeCtrl
61 #if USE_GENERIC_TREECTRL
62 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl, wxGenericTreeCtrl)
64 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl, wxTreeCtrl)
67 #if USE_GENERIC_TREECTRL
68 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl, wxGenericTreeCtrl)
70 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl, wxTreeCtrl)
72 EVT_SIZE(wxRemotelyScrolledTreeCtrl::OnSize)
73 EVT_TREE_ITEM_EXPANDED(-1, wxRemotelyScrolledTreeCtrl::OnExpand)
74 EVT_TREE_ITEM_COLLAPSED(-1, wxRemotelyScrolledTreeCtrl::OnExpand)
75 EVT_SCROLLWIN(wxRemotelyScrolledTreeCtrl::OnScroll)
78 wxRemotelyScrolledTreeCtrl::wxRemotelyScrolledTreeCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pt,
79 const wxSize& sz, long style):
80 wxTreeCtrl(parent, id, pt, sz, style
81 #if wxVERSION_NUMBER > 2301
86 m_companionWindow = NULL;
89 wxRemotelyScrolledTreeCtrl::~wxRemotelyScrolledTreeCtrl()
93 void wxRemotelyScrolledTreeCtrl::HideVScrollbar()
96 if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
98 ::ShowScrollBar((HWND) GetHWND(), SB_VERT, FALSE);
103 // Implicit in overriding SetScrollbars
107 // Number of pixels per user unit (0 or -1 for no scrollbar)
108 // Length of virtual canvas in user units
109 // Length of page in user units
110 void wxRemotelyScrolledTreeCtrl::SetScrollbars(int pixelsPerUnitX, int pixelsPerUnitY,
111 int noUnitsX, int noUnitsY,
115 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
117 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
118 // Pass TRUE for noRefresh so that it doesn't
119 // draw part of the tree as if the scroll view is
120 // at zero vertically.
121 win->wxGenericTreeCtrl::SetScrollbars(pixelsPerUnitX, pixelsPerUnitY, noUnitsX, 0, xPos, 0, /* noRefresh */ TRUE);
123 ecScrolledWindow* scrolledWindow = GetScrolledWindow();
126 scrolledWindow->SetScrollbars(0, pixelsPerUnitY, 0, noUnitsY, 0, yPos, noRefresh);
131 // In case we're using the generic tree control.
132 int wxRemotelyScrolledTreeCtrl::GetScrollPos(int orient) const
134 ecScrolledWindow* scrolledWindow = GetScrolledWindow();
136 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
138 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
140 if (orient == wxHORIZONTAL)
141 return win->wxGenericTreeCtrl::GetScrollPos(orient);
144 return scrolledWindow->GetScrollPos(orient);
151 // In case we're using the generic tree control.
152 // Get the view start
153 void wxRemotelyScrolledTreeCtrl::GetViewStart(int *x, int *y) const
155 ecScrolledWindow* scrolledWindow = GetScrolledWindow();
157 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
160 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
162 win->wxGenericTreeCtrl::GetViewStart(& x1, & y1);
167 scrolledWindow->GetViewStart(& x2, & y2);
172 // x is wrong since the horizontal scrollbar is controlled by the
173 // tree control, but we probably don't need it.
174 scrolledWindow->GetViewStart(x, y);
178 // In case we're using the generic tree control.
179 void wxRemotelyScrolledTreeCtrl::PrepareDC(wxDC& dc)
181 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
183 ecScrolledWindow* scrolledWindow = GetScrolledWindow();
185 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
188 GetViewStart(& startX, & startY);
190 int xppu1, yppu1, xppu2, yppu2;
191 win->wxGenericTreeCtrl::GetScrollPixelsPerUnit(& xppu1, & yppu1);
192 scrolledWindow->GetScrollPixelsPerUnit(& xppu2, & yppu2);
194 dc.SetDeviceOrigin( -startX * xppu1, -startY * yppu2 );
195 // dc.SetUserScale( win->GetScaleX(), win->GetScaleY() );
199 // Scroll to the given line (in scroll units where each unit is
200 // the height of an item)
201 void wxRemotelyScrolledTreeCtrl::ScrollToLine(int posHoriz, int posVert)
204 if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
206 UINT sbCode = SB_THUMBPOSITION;
207 HWND vertScrollBar = 0;
208 MSWDefWindowProc((WXUINT) WM_VSCROLL, MAKELONG(sbCode, posVert), (WXHWND) vertScrollBar);
213 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
215 /* Doesn't work yet because scrolling is ignored by Scroll
217 wxScrolledWindow* scrolledWindow = GetScrolledWindow();
220 scrolledWindow->GetScrollPixelsPerUnit(& xppu, & yppu);
221 win->Scroll(-1, posVert*yppu);
227 void wxRemotelyScrolledTreeCtrl::OnSize(wxSizeEvent& event)
230 AdjustRemoteScrollbars();
234 void wxRemotelyScrolledTreeCtrl::OnExpand(wxTreeEvent& event)
236 AdjustRemoteScrollbars();
239 // If we don't have this, we get some bits of lines still remaining
240 if (event.GetEventType() == wxEVT_COMMAND_TREE_ITEM_COLLAPSED)
244 if (m_companionWindow)
245 m_companionWindow->GetEventHandler()->ProcessEvent(event);
248 // Adjust the containing wxScrolledWindow's scrollbars appropriately
249 void wxRemotelyScrolledTreeCtrl::AdjustRemoteScrollbars()
251 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
253 // This is for the generic tree control.
254 // It calls SetScrollbars which has been overridden
255 // to adjust the parent scrolled window vertical
257 ((wxGenericTreeCtrl*) this)->AdjustMyScrollbars();
262 // This is for the wxMSW tree control
263 ecScrolledWindow* scrolledWindow = GetScrolledWindow();
267 if (GetBoundingRect(GetRootItem(), itemRect))
269 // Actually, the real height seems to be 1 less than reported
270 // (e.g. 16 instead of 16)
271 int itemHeight = itemRect.GetHeight() - 1;
274 GetClientSize(&w, &h);
276 wxRect rect(0, 0, 0, 0);
279 double f = ((double) (rect.GetHeight()) / (double) itemHeight) ;
280 int treeViewHeight = (int) ceil(f);
282 int scrollPixelsPerLine = itemHeight;
283 int scrollPos = - (itemRect.y / itemHeight);
285 scrolledWindow->SetScrollbars(0, scrollPixelsPerLine, 0, treeViewHeight, 0, scrollPos);
287 // Ensure that when a scrollbar becomes hidden or visible,
288 // the contained window sizes are right.
289 // Problem: this is called too early (?)
290 wxSizeEvent event(scrolledWindow->GetSize(), scrolledWindow->GetId());
291 scrolledWindow->GetEventHandler()->ProcessEvent(event);
298 // Calculate the area that contains both rectangles
299 static wxRect CombineRectangles(const wxRect& rect1, const wxRect& rect2)
303 int right1 = rect1.GetRight();
304 int bottom1 = rect1.GetBottom();
305 int right2 = rect2.GetRight();
306 int bottom2 = rect2.GetBottom();
308 wxPoint topLeft = wxPoint(wxMin(rect1.x, rect2.x), wxMin(rect1.y, rect2.y));
309 wxPoint bottomRight = wxPoint(wxMax(right1, right2), wxMax(bottom1, bottom2));
311 rect.x = topLeft.x; rect.y = topLeft.y;
312 rect.SetRight(bottomRight.x);
313 rect.SetBottom(bottomRight.y);
319 // Calculate the tree overall size so we can set the scrollbar
322 // static int g_count = 0;
324 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(wxRect& rect)
327 CalcTreeSize(GetRootItem(), rect);
330 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(const wxTreeItemId& id, wxRect& rect)
332 // More efficient implementation would be to find the last item (but how?)
333 // Q: is the bounding rect relative to the top of the virtual tree workspace
334 // or the top of the window? How would we convert?
336 if (GetBoundingRect(id, itemSize))
338 rect = CombineRectangles(rect, itemSize);
342 wxTreeItemId childId = GetFirstChild(id, cookie);
345 CalcTreeSize(childId, rect);
346 childId = GetNextChild(childId, cookie);
350 // Find the scrolled window that contains this control
351 ecScrolledWindow* wxRemotelyScrolledTreeCtrl::GetScrolledWindow() const
353 wxWindow* parent = wxWindow::GetParent();
356 if (parent->IsKindOf(CLASSINFO(ecScrolledWindow)))
357 return (ecScrolledWindow*) parent;
358 parent = parent->GetParent();
363 void wxRemotelyScrolledTreeCtrl::OnScroll(wxScrollWinEvent& event)
365 int orient = event.GetOrientation();
366 if (orient == wxHORIZONTAL)
371 ecScrolledWindow* scrollWin = GetScrolledWindow();
376 scrollWin->GetViewStart(& x, & y);
382 * wxTreeCompanionWindow
384 * A window displaying values associated with tree control items.
387 IMPLEMENT_CLASS(wxTreeCompanionWindow, wxWindow)
389 BEGIN_EVENT_TABLE(wxTreeCompanionWindow, wxWindow)
390 EVT_PAINT(wxTreeCompanionWindow::OnPaint)
391 EVT_SCROLLWIN(wxTreeCompanionWindow::OnScroll)
392 EVT_TREE_ITEM_EXPANDED(-1, wxTreeCompanionWindow::OnExpand)
393 EVT_TREE_ITEM_COLLAPSED(-1, wxTreeCompanionWindow::OnExpand)
396 wxTreeCompanionWindow::wxTreeCompanionWindow(wxWindow* parent, wxWindowID id,
400 wxWindow(parent, id, pos, sz, style)
405 void wxTreeCompanionWindow::DrawItem(wxDC& dc, wxTreeItemId id, const wxRect& rect)
409 wxString text = m_treeCtrl->GetItemText(id);
410 dc.SetTextForeground(* wxBLACK);
411 dc.SetBackgroundMode(wxTRANSPARENT);
414 dc.GetTextExtent(text, & textW, & textH);
417 int y = rect.GetY() + wxMax(0, (rect.GetHeight() - textH) / 2);
419 dc.DrawText(text, x, y);
423 void wxTreeCompanionWindow::OnPaint(wxPaintEvent& event)
430 wxPen pen(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT), 1, wxSOLID);
432 dc.SetBrush(* wxTRANSPARENT_BRUSH);
433 wxFont font(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
436 wxSize clientSize = GetClientSize();
439 wxTreeItemId h, lastH;
440 for(h=m_treeCtrl->GetFirstVisibleItem();h;h=m_treeCtrl->GetNextVisible(h))
442 if (m_treeCtrl->GetBoundingRect(h, itemRect))
444 cy = itemRect.GetTop();
445 wxRect drawItemRect(0, cy, clientSize.x, itemRect.GetHeight());
449 // Draw the actual item
450 DrawItem(dc, h, drawItemRect);
451 dc.DrawLine(0, cy, clientSize.x, cy);
454 if (lastH.IsOk() && m_treeCtrl->GetBoundingRect(lastH, itemRect))
456 cy = itemRect.GetBottom();
457 dc.DrawLine(0, cy, clientSize.x, cy);
461 void wxTreeCompanionWindow::OnScroll(wxScrollWinEvent& event)
463 int orient = event.GetOrientation();
464 if (orient == wxHORIZONTAL)
472 // TODO: scroll the window physically instead of just refreshing.
476 void wxTreeCompanionWindow::OnExpand(wxTreeEvent& event)
478 // TODO: something more optimized than simply refresh the whole
479 // window when the tree is expanded/collapsed. Tricky.
484 * wxThinSplitterWindow
487 IMPLEMENT_CLASS(wxThinSplitterWindow, wxSplitterWindow)
489 BEGIN_EVENT_TABLE(wxThinSplitterWindow, wxSplitterWindow)
490 EVT_SIZE(wxThinSplitterWindow::OnSize)
491 // Not in older versions of wxWindows, unfortunately
493 EVT_SPLITTER_DOUBLECLICKED(-1, wxThinSplitterWindow::OnDoubleClickSash)
497 wxThinSplitterWindow::wxThinSplitterWindow(wxWindow* parent, wxWindowID id,
501 wxSplitterWindow(parent, id, pos, sz, style)
505 void wxThinSplitterWindow::SizeWindows()
507 // The client size may have changed inbetween
508 // the sizing of the first window and the sizing of
509 // the second. So repeat SizeWindows.
510 wxSplitterWindow::SizeWindows();
511 wxSplitterWindow::SizeWindows();
514 // Tests for x, y over sash
515 bool wxThinSplitterWindow::SashHitTest(int x, int y, int tolerance)
517 return wxSplitterWindow::SashHitTest(x, y, 4);
520 void wxThinSplitterWindow::DrawSash(wxDC& dc)
522 if ( m_sashPosition == 0 || !m_windowTwo)
524 if (GetWindowStyle() & wxSP_NOSASH)
528 GetClientSize(&w, &h);
530 if ( m_splitMode == wxSPLIT_VERTICAL )
532 dc.SetPen(* m_facePen);
533 dc.SetBrush(* m_faceBrush);
536 if ( (GetWindowStyleFlag() & wxSP_BORDER) != wxSP_BORDER && (GetWindowStyleFlag() & wxSP_3DBORDER) != wxSP_3DBORDER )
537 h1 += 1; // Not sure why this is necessary...
538 if ( (GetWindowStyleFlag() & wxSP_3DBORDER) == wxSP_3DBORDER)
542 dc.DrawRectangle(m_sashPosition, y1, m_sashSize, h1);
546 dc.SetPen(* m_facePen);
547 dc.SetBrush(* m_faceBrush);
550 if ( (GetWindowStyleFlag() & wxSP_BORDER) != wxSP_BORDER && (GetWindowStyleFlag() & wxSP_3DBORDER) != wxSP_3DBORDER )
552 if ( (GetWindowStyleFlag() & wxSP_3DBORDER) == wxSP_3DBORDER)
556 dc.DrawRectangle(x1, m_sashPosition, w1, m_sashSize);
559 dc.SetPen(wxNullPen);
560 dc.SetBrush(wxNullBrush);
563 void wxThinSplitterWindow::OnSize(wxSizeEvent& event)
565 wxSplitterWindow::OnSize(event);
569 * wxSplitterScrolledWindow
572 IMPLEMENT_CLASS(wxSplitterScrolledWindow, ecScrolledWindow)
574 BEGIN_EVENT_TABLE(wxSplitterScrolledWindow, ecScrolledWindow)
575 EVT_SCROLLWIN(wxSplitterScrolledWindow::OnScroll)
576 EVT_SIZE(wxSplitterScrolledWindow::OnSize)
579 wxSplitterScrolledWindow::wxSplitterScrolledWindow(wxWindow* parent, wxWindowID id,
583 ecScrolledWindow(parent, id, pos, sz, style)
587 void wxSplitterScrolledWindow::OnSize(wxSizeEvent& event)
589 wxSize sz = GetClientSize();
590 if (GetChildren().First())
592 ((wxWindow*) GetChildren().First()->Data())->SetSize(0, 0, sz.x, sz.y);
596 void wxSplitterScrolledWindow::OnScroll(wxScrollWinEvent& event)
598 // Ensure that events being propagated back up the window hierarchy
599 // don't cause an infinite loop
600 static bool inOnScroll = FALSE;
608 int orient = event.GetOrientation();
610 int nScrollInc = CalcScrollInc(event);
617 if (orient == wxHORIZONTAL)
623 int newPos = m_xScrollPosition + nScrollInc;
624 SetScrollPos(wxHORIZONTAL, newPos, TRUE );
629 int newPos = m_yScrollPosition + nScrollInc;
630 SetScrollPos(wxVERTICAL, newPos, TRUE );
633 if (orient == wxHORIZONTAL)
635 m_xScrollPosition += nScrollInc;
639 m_yScrollPosition += nScrollInc;
642 // Find targets in splitter window and send the event to them
643 wxNode* node = GetChildren().First();
646 wxWindow* child = (wxWindow*) node->Data();
647 if (child->IsKindOf(CLASSINFO(wxSplitterWindow)))
649 wxSplitterWindow* splitter = (wxSplitterWindow*) child;
650 if (splitter->GetWindow1())
651 splitter->GetWindow1()->ProcessEvent(event);
652 if (splitter->GetWindow2())
653 splitter->GetWindow2()->ProcessEvent(event);
660 m_targetWindow->MacUpdateImmediately() ;