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 wxVERSON_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 win->wxGenericTreeCtrl::SetScrollbars(pixelsPerUnitX, 0, noUnitsX, 0, xPos, 0, noRefresh);
120 ecScrolledWindow* scrolledWindow = GetScrolledWindow();
123 scrolledWindow->SetScrollbars(0, pixelsPerUnitY, 0, noUnitsY, 0, yPos, noRefresh);
128 // In case we're using the generic tree control.
129 int wxRemotelyScrolledTreeCtrl::GetScrollPos(int orient) const
131 ecScrolledWindow* scrolledWindow = GetScrolledWindow();
133 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
135 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
137 if (orient == wxHORIZONTAL)
138 return win->wxGenericTreeCtrl::GetScrollPos(orient);
141 return scrolledWindow->GetScrollPos(orient);
148 // In case we're using the generic tree control.
149 // Get the view start
150 void wxRemotelyScrolledTreeCtrl::GetViewStart(int *x, int *y) const
152 ecScrolledWindow* scrolledWindow = GetScrolledWindow();
154 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
157 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
159 win->wxGenericTreeCtrl::GetViewStart(& x1, & y1);
164 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 void wxRemotelyScrolledTreeCtrl::PrepareDC(wxDC& dc)
178 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
180 ecScrolledWindow* scrolledWindow = GetScrolledWindow();
182 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
185 GetViewStart(& startX, & startY);
187 int xppu1, yppu1, xppu2, yppu2;
188 win->wxGenericTreeCtrl::GetScrollPixelsPerUnit(& xppu1, & yppu1);
189 scrolledWindow->GetScrollPixelsPerUnit(& xppu2, & yppu2);
191 dc.SetDeviceOrigin( -startX * xppu1, -startY * yppu2 );
192 // dc.SetUserScale( win->GetScaleX(), win->GetScaleY() );
196 // Scroll to the given line (in scroll units where each unit is
197 // the height of an item)
198 void wxRemotelyScrolledTreeCtrl::ScrollToLine(int posHoriz, int posVert)
201 if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
203 UINT sbCode = SB_THUMBPOSITION;
204 HWND vertScrollBar = 0;
205 MSWDefWindowProc((WXUINT) WM_VSCROLL, MAKELONG(sbCode, posVert), (WXHWND) vertScrollBar);
210 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
212 /* Doesn't work yet because scrolling is ignored by Scroll
214 wxScrolledWindow* scrolledWindow = GetScrolledWindow();
217 scrolledWindow->GetScrollPixelsPerUnit(& xppu, & yppu);
218 win->Scroll(-1, posVert*yppu);
224 void wxRemotelyScrolledTreeCtrl::OnSize(wxSizeEvent& event)
227 AdjustRemoteScrollbars();
231 void wxRemotelyScrolledTreeCtrl::OnExpand(wxTreeEvent& event)
233 AdjustRemoteScrollbars();
236 // If we don't have this, we get some bits of lines still remaining
237 if (event.GetEventType() == wxEVT_COMMAND_TREE_ITEM_COLLAPSED)
241 if (m_companionWindow)
242 m_companionWindow->GetEventHandler()->ProcessEvent(event);
245 // Adjust the containing wxScrolledWindow's scrollbars appropriately
246 void wxRemotelyScrolledTreeCtrl::AdjustRemoteScrollbars()
248 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
250 // This is for the generic tree control.
251 // It calls SetScrollbars which has been overridden
252 // to adjust the parent scrolled window vertical
254 ((wxGenericTreeCtrl*) this)->AdjustMyScrollbars();
259 // This is for the wxMSW tree control
260 ecScrolledWindow* scrolledWindow = GetScrolledWindow();
264 if (GetBoundingRect(GetRootItem(), itemRect))
266 // Actually, the real height seems to be 1 less than reported
267 // (e.g. 16 instead of 16)
268 int itemHeight = itemRect.GetHeight() - 1;
271 GetClientSize(&w, &h);
273 wxRect rect(0, 0, 0, 0);
276 double f = ((double) (rect.GetHeight()) / (double) itemHeight) ;
277 int treeViewHeight = (int) ceil(f);
279 int scrollPixelsPerLine = itemHeight;
280 int scrollPos = - (itemRect.y / itemHeight);
282 scrolledWindow->SetScrollbars(0, scrollPixelsPerLine, 0, treeViewHeight, 0, scrollPos);
284 // Ensure that when a scrollbar becomes hidden or visible,
285 // the contained window sizes are right.
286 // Problem: this is called too early (?)
287 wxSizeEvent event(scrolledWindow->GetSize(), scrolledWindow->GetId());
288 scrolledWindow->GetEventHandler()->ProcessEvent(event);
295 // Calculate the area that contains both rectangles
296 static wxRect CombineRectangles(const wxRect& rect1, const wxRect& rect2)
300 int right1 = rect1.GetRight();
301 int bottom1 = rect1.GetBottom();
302 int right2 = rect2.GetRight();
303 int bottom2 = rect2.GetBottom();
305 wxPoint topLeft = wxPoint(wxMin(rect1.x, rect2.x), wxMin(rect1.y, rect2.y));
306 wxPoint bottomRight = wxPoint(wxMax(right1, right2), wxMax(bottom1, bottom2));
308 rect.x = topLeft.x; rect.y = topLeft.y;
309 rect.SetRight(bottomRight.x);
310 rect.SetBottom(bottomRight.y);
316 // Calculate the tree overall size so we can set the scrollbar
319 // static int g_count = 0;
321 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(wxRect& rect)
324 CalcTreeSize(GetRootItem(), rect);
327 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(const wxTreeItemId& id, wxRect& rect)
329 // More efficient implementation would be to find the last item (but how?)
330 // Q: is the bounding rect relative to the top of the virtual tree workspace
331 // or the top of the window? How would we convert?
333 if (GetBoundingRect(id, itemSize))
335 rect = CombineRectangles(rect, itemSize);
339 wxTreeItemId childId = GetFirstChild(id, cookie);
342 CalcTreeSize(childId, rect);
343 childId = GetNextChild(childId, cookie);
347 // Find the scrolled window that contains this control
348 ecScrolledWindow* wxRemotelyScrolledTreeCtrl::GetScrolledWindow() const
350 wxWindow* parent = wxWindow::GetParent();
353 if (parent->IsKindOf(CLASSINFO(ecScrolledWindow)))
354 return (ecScrolledWindow*) parent;
355 parent = parent->GetParent();
360 void wxRemotelyScrolledTreeCtrl::OnScroll(wxScrollWinEvent& event)
362 int orient = event.GetOrientation();
363 if (orient == wxHORIZONTAL)
368 ecScrolledWindow* scrollWin = GetScrolledWindow();
373 scrollWin->GetViewStart(& x, & y);
379 * wxTreeCompanionWindow
381 * A window displaying values associated with tree control items.
384 IMPLEMENT_CLASS(wxTreeCompanionWindow, wxWindow)
386 BEGIN_EVENT_TABLE(wxTreeCompanionWindow, wxWindow)
387 EVT_PAINT(wxTreeCompanionWindow::OnPaint)
388 EVT_SCROLLWIN(wxTreeCompanionWindow::OnScroll)
389 EVT_TREE_ITEM_EXPANDED(-1, wxTreeCompanionWindow::OnExpand)
390 EVT_TREE_ITEM_COLLAPSED(-1, wxTreeCompanionWindow::OnExpand)
393 wxTreeCompanionWindow::wxTreeCompanionWindow(wxWindow* parent, wxWindowID id,
397 wxWindow(parent, id, pos, sz, style)
402 void wxTreeCompanionWindow::DrawItem(wxDC& dc, wxTreeItemId id, const wxRect& rect)
406 wxString text = m_treeCtrl->GetItemText(id);
407 dc.SetTextForeground(* wxBLACK);
408 dc.SetBackgroundMode(wxTRANSPARENT);
411 dc.GetTextExtent(text, & textW, & textH);
414 int y = rect.GetY() + wxMax(0, (rect.GetHeight() - textH) / 2);
416 dc.DrawText(text, x, y);
420 void wxTreeCompanionWindow::OnPaint(wxPaintEvent& event)
427 wxPen pen(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT), 1, wxSOLID);
429 dc.SetBrush(* wxTRANSPARENT_BRUSH);
430 wxFont font(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
433 wxSize clientSize = GetClientSize();
436 wxTreeItemId h, lastH;
437 for(h=m_treeCtrl->GetFirstVisibleItem();h;h=m_treeCtrl->GetNextVisible(h))
439 if (m_treeCtrl->GetBoundingRect(h, itemRect))
441 cy = itemRect.GetTop();
442 wxRect drawItemRect(0, cy, clientSize.x, itemRect.GetHeight());
446 // Draw the actual item
447 DrawItem(dc, h, drawItemRect);
448 dc.DrawLine(0, cy, clientSize.x, cy);
451 if (lastH.IsOk() && m_treeCtrl->GetBoundingRect(lastH, itemRect))
453 cy = itemRect.GetBottom();
454 dc.DrawLine(0, cy, clientSize.x, cy);
458 void wxTreeCompanionWindow::OnScroll(wxScrollWinEvent& event)
460 int orient = event.GetOrientation();
461 if (orient == wxHORIZONTAL)
469 // TODO: scroll the window physically instead of just refreshing.
473 void wxTreeCompanionWindow::OnExpand(wxTreeEvent& event)
475 // TODO: something more optimized than simply refresh the whole
476 // window when the tree is expanded/collapsed. Tricky.
481 * wxThinSplitterWindow
484 IMPLEMENT_CLASS(wxThinSplitterWindow, wxSplitterWindow)
486 BEGIN_EVENT_TABLE(wxThinSplitterWindow, wxSplitterWindow)
487 EVT_SIZE(wxThinSplitterWindow::OnSize)
488 // Not in older versions of wxWindows, unfortunately
490 EVT_SPLITTER_DOUBLECLICKED(-1, wxThinSplitterWindow::OnDoubleClickSash)
494 wxThinSplitterWindow::wxThinSplitterWindow(wxWindow* parent, wxWindowID id,
498 wxSplitterWindow(parent, id, pos, sz, style)
502 void wxThinSplitterWindow::SizeWindows()
504 // The client size may have changed inbetween
505 // the sizing of the first window and the sizing of
506 // the second. So repeat SizeWindows.
507 wxSplitterWindow::SizeWindows();
508 wxSplitterWindow::SizeWindows();
511 // Tests for x, y over sash
512 bool wxThinSplitterWindow::SashHitTest(int x, int y, int tolerance)
514 return wxSplitterWindow::SashHitTest(x, y, 4);
517 void wxThinSplitterWindow::DrawSash(wxDC& dc)
519 if ( m_sashPosition == 0 || !m_windowTwo)
521 if (GetWindowStyle() & wxSP_NOSASH)
525 GetClientSize(&w, &h);
527 if ( m_splitMode == wxSPLIT_VERTICAL )
529 dc.SetPen(* m_facePen);
530 dc.SetBrush(* m_faceBrush);
533 if ( (GetWindowStyleFlag() & wxSP_BORDER) != wxSP_BORDER && (GetWindowStyleFlag() & wxSP_3DBORDER) != wxSP_3DBORDER )
534 h1 += 1; // Not sure why this is necessary...
535 if ( (GetWindowStyleFlag() & wxSP_3DBORDER) == wxSP_3DBORDER)
539 dc.DrawRectangle(m_sashPosition, y1, m_sashSize, h1);
543 dc.SetPen(* m_facePen);
544 dc.SetBrush(* m_faceBrush);
547 if ( (GetWindowStyleFlag() & wxSP_BORDER) != wxSP_BORDER && (GetWindowStyleFlag() & wxSP_3DBORDER) != wxSP_3DBORDER )
549 if ( (GetWindowStyleFlag() & wxSP_3DBORDER) == wxSP_3DBORDER)
553 dc.DrawRectangle(x1, m_sashPosition, w1, m_sashSize);
556 dc.SetPen(wxNullPen);
557 dc.SetBrush(wxNullBrush);
560 void wxThinSplitterWindow::OnSize(wxSizeEvent& event)
562 wxSplitterWindow::OnSize(event);
566 * wxSplitterScrolledWindow
569 IMPLEMENT_CLASS(wxSplitterScrolledWindow, ecScrolledWindow)
571 BEGIN_EVENT_TABLE(wxSplitterScrolledWindow, ecScrolledWindow)
572 EVT_SCROLLWIN(wxSplitterScrolledWindow::OnScroll)
573 EVT_SIZE(wxSplitterScrolledWindow::OnSize)
576 wxSplitterScrolledWindow::wxSplitterScrolledWindow(wxWindow* parent, wxWindowID id,
580 ecScrolledWindow(parent, id, pos, sz, style)
584 void wxSplitterScrolledWindow::OnSize(wxSizeEvent& event)
586 wxSize sz = GetClientSize();
587 if (GetChildren().First())
589 ((wxWindow*) GetChildren().First()->Data())->SetSize(0, 0, sz.x, sz.y);
593 void wxSplitterScrolledWindow::OnScroll(wxScrollWinEvent& event)
595 // Ensure that events being propagated back up the window hierarchy
596 // don't cause an infinite loop
597 static bool inOnScroll = FALSE;
605 int orient = event.GetOrientation();
607 int nScrollInc = CalcScrollInc(event);
614 if (orient == wxHORIZONTAL)
620 int newPos = m_xScrollPosition + nScrollInc;
621 SetScrollPos(wxHORIZONTAL, newPos, TRUE );
626 int newPos = m_yScrollPosition + nScrollInc;
627 SetScrollPos(wxVERTICAL, newPos, TRUE );
630 if (orient == wxHORIZONTAL)
632 m_xScrollPosition += nScrollInc;
636 m_yScrollPosition += nScrollInc;
639 // Find targets in splitter window and send the event to them
640 wxNode* node = GetChildren().First();
643 wxWindow* child = (wxWindow*) node->Data();
644 if (child->IsKindOf(CLASSINFO(wxSplitterWindow)))
646 wxSplitterWindow* splitter = (wxSplitterWindow*) child;
647 if (splitter->GetWindow1())
648 splitter->GetWindow1()->ProcessEvent(event);
649 if (splitter->GetWindow2())
650 splitter->GetWindow2()->ProcessEvent(event);
657 m_targetWindow->MacUpdateImmediately() ;