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" 
  40 #include "wx/gizmos/splittree.h" 
  43  * wxRemotelyScrolledTreeCtrl 
  46 #if USE_GENERIC_TREECTRL 
  47 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl
, wxGenericTreeCtrl
) 
  49 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl
, wxTreeCtrl
) 
  52 #if USE_GENERIC_TREECTRL 
  53 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl
, wxGenericTreeCtrl
) 
  55 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl
, wxTreeCtrl
) 
  57         EVT_SIZE(wxRemotelyScrolledTreeCtrl::OnSize
) 
  58         EVT_TREE_ITEM_EXPANDED(-1, wxRemotelyScrolledTreeCtrl::OnExpand
) 
  59         EVT_TREE_ITEM_COLLAPSED(-1, wxRemotelyScrolledTreeCtrl::OnExpand
) 
  60         EVT_SCROLLWIN(wxRemotelyScrolledTreeCtrl::OnScroll
) 
  63 wxRemotelyScrolledTreeCtrl::wxRemotelyScrolledTreeCtrl(wxWindow
* parent
, wxWindowID id
, const wxPoint
& pt
, 
  64         const wxSize
& sz
, long style
): 
  65         wxTreeCtrl(parent
, id
, pt
, sz
, style
) 
  67         m_companionWindow 
= NULL
; 
  70 wxRemotelyScrolledTreeCtrl::~wxRemotelyScrolledTreeCtrl() 
  74 void wxRemotelyScrolledTreeCtrl::HideVScrollbar() 
  77     if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl
))) 
  79         ::ShowScrollBar((HWND
) GetHWND(), SB_VERT
, FALSE
); 
  84         // Implicit in overriding SetScrollbars 
  88 // Number of pixels per user unit (0 or -1 for no scrollbar) 
  89 // Length of virtual canvas in user units 
  90 // Length of page in user units 
  91 void wxRemotelyScrolledTreeCtrl::SetScrollbars(int pixelsPerUnitX
, int pixelsPerUnitY
, 
  92                              int noUnitsX
, int noUnitsY
, 
  96     if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
))) 
  98         wxGenericTreeCtrl
* win 
= (wxGenericTreeCtrl
*) this; 
  99         win
->wxGenericTreeCtrl::SetScrollbars(pixelsPerUnitX
, 0, noUnitsX
, 0, xPos
, 0, noRefresh
); 
 101             wxScrolledWindow
* scrolledWindow 
= GetScrolledWindow(); 
 104             scrolledWindow
->SetScrollbars(0, pixelsPerUnitY
, 0, noUnitsY
, 0, yPos
, noRefresh
); 
 109 // In case we're using the generic tree control. 
 110 int wxRemotelyScrolledTreeCtrl::GetScrollPos(int orient
) const 
 112     wxScrolledWindow
* scrolledWindow 
= GetScrolledWindow(); 
 114     if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
))) 
 116         wxGenericTreeCtrl
* win 
= (wxGenericTreeCtrl
*) this; 
 118         if (orient 
== wxHORIZONTAL
) 
 119             return win
->wxGenericTreeCtrl::GetScrollPos(orient
); 
 122             return scrolledWindow
->GetScrollPos(orient
); 
 129 // In case we're using the generic tree control. 
 130 // Get the view start 
 131 void wxRemotelyScrolledTreeCtrl::GetViewStart(int *x
, int *y
) const 
 133     wxScrolledWindow
* scrolledWindow 
= GetScrolledWindow(); 
 135     if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
))) 
 138         wxGenericTreeCtrl
* win 
= (wxGenericTreeCtrl
*) this; 
 140         win
->wxGenericTreeCtrl::GetViewStart(& x1
, & y1
); 
 145         scrolledWindow
->GetViewStart(& x2
, & y2
); 
 150                 // x is wrong since the horizontal scrollbar is controlled by the 
 151                 // tree control, but we probably don't need it. 
 152         scrolledWindow
->GetViewStart(x
, y
); 
 156 // In case we're using the generic tree control. 
 157 void wxRemotelyScrolledTreeCtrl::PrepareDC(wxDC
& dc
) 
 159     if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
))) 
 161             wxScrolledWindow
* scrolledWindow 
= GetScrolledWindow(); 
 163         wxGenericTreeCtrl
* win 
= (wxGenericTreeCtrl
*) this; 
 166         GetViewStart(& startX
, & startY
); 
 168         int xppu1
, yppu1
, xppu2
, yppu2
; 
 169         win
->wxGenericTreeCtrl::GetScrollPixelsPerUnit(& xppu1
, & yppu1
); 
 170         scrolledWindow
->GetScrollPixelsPerUnit(& xppu2
, & yppu2
); 
 172         dc
.SetDeviceOrigin( -startX 
* xppu1
, -startY 
* yppu2 
); 
 173         dc
.SetUserScale( win
->GetScaleX(), win
->GetScaleY() ); 
 177 // Scroll to the given line (in scroll units where each unit is 
 178 // the height of an item) 
 179 void wxRemotelyScrolledTreeCtrl::ScrollToLine(int posHoriz
, int posVert
) 
 182     if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl
))) 
 184             UINT sbCode 
= SB_THUMBPOSITION
; 
 185             HWND vertScrollBar 
= 0; 
 186             MSWDefWindowProc((WXUINT
) WM_VSCROLL
, MAKELONG(sbCode
, posVert
), (WXHWND
) vertScrollBar
); 
 191         wxGenericTreeCtrl
* win 
= (wxGenericTreeCtrl
*) this; 
 193                 /* Doesn't work yet because scrolling is ignored by Scroll 
 195             wxScrolledWindow* scrolledWindow = GetScrolledWindow(); 
 198                         scrolledWindow->GetScrollPixelsPerUnit(& xppu, & yppu); 
 199                         win->Scroll(-1, posVert*yppu); 
 205 void wxRemotelyScrolledTreeCtrl::OnSize(wxSizeEvent
& event
) 
 208         AdjustRemoteScrollbars(); 
 212 void wxRemotelyScrolledTreeCtrl::OnExpand(wxTreeEvent
& event
) 
 214         AdjustRemoteScrollbars(); 
 217     // If we don't have this, we get some bits of lines still remaining 
 218     if (event
.GetEventType() == wxEVT_COMMAND_TREE_ITEM_COLLAPSED
) 
 222         if (m_companionWindow
) 
 223                 m_companionWindow
->GetEventHandler()->ProcessEvent(event
); 
 226 // Adjust the containing wxScrolledWindow's scrollbars appropriately 
 227 void wxRemotelyScrolledTreeCtrl::AdjustRemoteScrollbars() 
 229     if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
))) 
 231                 // This is for the generic tree control. 
 232                 // It calls SetScrollbars which has been overridden 
 233                 // to adjust the parent scrolled window vertical 
 235                 ((wxGenericTreeCtrl
*) this)->AdjustMyScrollbars(); 
 240                 // This is for the wxMSW tree control 
 241                 wxScrolledWindow
* scrolledWindow 
= GetScrolledWindow(); 
 245                         if (GetBoundingRect(GetRootItem(), itemRect
)) 
 247                                 int itemHeight 
= itemRect
.GetHeight(); 
 250                                 GetClientSize(&w
, &h
); 
 252                                 wxRect 
rect(0, 0, 0, 0); 
 254                                 int treeViewHeight 
= rect
.GetHeight()/itemHeight
; 
 256                                 int scrollPixelsPerLine 
= itemHeight
; 
 257                                 int scrollPos 
= - (itemRect
.y 
/ itemHeight
); 
 259                                 scrolledWindow
->SetScrollbars(0, scrollPixelsPerLine
, 0, treeViewHeight
, 0, scrollPos
); 
 261                                 // Ensure that when a scrollbar becomes hidden or visible, 
 262                                 // the contained window sizes are right. 
 263                                 // Problem: this is called too early (?) 
 264                                 wxSizeEvent 
event(scrolledWindow
->GetSize(), scrolledWindow
->GetId()); 
 265                                 scrolledWindow
->GetEventHandler()->ProcessEvent(event
); 
 272 // Calculate the area that contains both rectangles 
 273 static wxRect 
CombineRectangles(const wxRect
& rect1
, const wxRect
& rect2
) 
 277     int right1 
= rect1
.GetRight(); 
 278     int bottom1 
= rect1
.GetBottom(); 
 279     int right2 
= rect2
.GetRight(); 
 280     int bottom2 
= rect2
.GetBottom(); 
 282     wxPoint topLeft 
= wxPoint(wxMin(rect1
.x
, rect2
.x
), wxMin(rect1
.y
, rect2
.y
)); 
 283     wxPoint bottomRight 
= wxPoint(wxMax(right1
, right2
), wxMax(bottom1
, bottom2
)); 
 285     rect
.x 
= topLeft
.x
; rect
.y 
= topLeft
.y
; 
 286     rect
.SetRight(bottomRight
.x
); 
 287     rect
.SetBottom(bottomRight
.y
); 
 293 // Calculate the tree overall size so we can set the scrollbar 
 295 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(wxRect
& rect
) 
 297         CalcTreeSize(GetRootItem(), rect
); 
 300 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(const wxTreeItemId
& id
, wxRect
& rect
) 
 302         // TODO: implement GetFirst/NextVisibleItem 
 303         // for wxGenericTreeCtrl, plus GetBoundingRect. 
 305         // More efficient implementation would be to find the last item (but how?) 
 306         // Q: is the bounding rect relative to the top of the virtual tree workspace 
 307         // or the top of the window? How would we convert? 
 309         if (GetBoundingRect(id
, itemSize
)) 
 311                 rect 
= CombineRectangles(rect
, itemSize
); 
 315         wxTreeItemId childId 
= GetFirstChild(id
, cookie
); 
 318                 CalcTreeSize(childId
, rect
); 
 319                 childId 
= GetNextChild(childId
, cookie
); 
 323 // Find the scrolled window that contains this control 
 324 wxScrolledWindow
* wxRemotelyScrolledTreeCtrl::GetScrolledWindow() const 
 326         wxWindow
* parent 
= wxWindow::GetParent(); 
 329                 if (parent
->IsKindOf(CLASSINFO(wxScrolledWindow
))) 
 330                         return (wxScrolledWindow
*) parent
; 
 331                 parent 
= parent
->GetParent(); 
 336 void wxRemotelyScrolledTreeCtrl::OnScroll(wxScrollWinEvent
& event
) 
 338     int orient 
= event
.GetOrientation(); 
 339     if (orient 
== wxHORIZONTAL
) 
 344     wxScrolledWindow
* scrollWin 
= GetScrolledWindow(); 
 349     scrollWin
->GetViewStart(& x
, & y
); 
 355  * wxTreeCompanionWindow 
 357  * A window displaying values associated with tree control items. 
 360 IMPLEMENT_CLASS(wxTreeCompanionWindow
, wxWindow
) 
 362 BEGIN_EVENT_TABLE(wxTreeCompanionWindow
, wxWindow
) 
 363         EVT_PAINT(wxTreeCompanionWindow::OnPaint
) 
 364         EVT_SCROLLWIN(wxTreeCompanionWindow::OnScroll
) 
 365         EVT_TREE_ITEM_EXPANDED(-1, wxTreeCompanionWindow::OnExpand
) 
 366         EVT_TREE_ITEM_COLLAPSED(-1, wxTreeCompanionWindow::OnExpand
) 
 369 wxTreeCompanionWindow::wxTreeCompanionWindow(wxWindow
* parent
, wxWindowID id
, 
 373         wxWindow(parent
, id
, pos
, sz
, style
) 
 378 void wxTreeCompanionWindow::DrawItem(wxDC
& dc
, wxTreeItemId id
, const wxRect
& rect
) 
 384                 wxString text 
= m_treeCtrl
->GetItemText(id
); 
 385                 dc
.SetTextForeground(* wxBLACK
); 
 386                 dc
.SetBackgroundMode(wxTRANSPARENT
); 
 389                 dc
.GetTextExtent(text
, & textW
, & textH
); 
 392                 int y 
= rect
.GetY() + wxMax(0, (rect
.GetHeight() - textH
) / 2); 
 394                 dc
.DrawText(text
, x
, y
); 
 399 void wxTreeCompanionWindow::OnPaint(wxPaintEvent
& event
) 
 406         wxPen 
pen(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT
), 1, wxSOLID
); 
 408         dc
.SetBrush(* wxTRANSPARENT_BRUSH
); 
 409         wxFont 
font(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT
)); 
 412     wxSize clientSize 
= GetClientSize(); 
 415         wxTreeItemId h
, lastH
; 
 416         for(h
=m_treeCtrl
->GetFirstVisibleItem();h
;h
=m_treeCtrl
->GetNextVisible(h
)) 
 418                 if (m_treeCtrl
->GetBoundingRect(h
, itemRect
)) 
 420                         cy 
= itemRect
.GetTop(); 
 421                         wxRect 
drawItemRect(0, cy
, clientSize
.x
, itemRect
.GetHeight()); 
 425                         // Draw the actual item 
 426                         DrawItem(dc
, h
, drawItemRect
); 
 427                         dc
.DrawLine(0, cy
, clientSize
.x
, cy
); 
 430         if (m_treeCtrl
->GetBoundingRect(lastH
, itemRect
)) 
 432                 cy 
= itemRect
.GetBottom(); 
 433                 dc
.DrawLine(0, cy
, clientSize
.x
, cy
); 
 437 void wxTreeCompanionWindow::OnScroll(wxScrollWinEvent
& event
) 
 439     int orient 
= event
.GetOrientation(); 
 440     if (orient 
== wxHORIZONTAL
) 
 448         // TODO: scroll the window physically instead of just refreshing. 
 452 void wxTreeCompanionWindow::OnExpand(wxTreeEvent
& event
) 
 454         // TODO: something more optimized than simply refresh the whole 
 455         // window when the tree is expanded/collapsed. Tricky. 
 460  * wxThinSplitterWindow 
 463 IMPLEMENT_CLASS(wxThinSplitterWindow
, wxSplitterWindow
) 
 465 BEGIN_EVENT_TABLE(wxThinSplitterWindow
, wxSplitterWindow
) 
 466         EVT_SIZE(wxThinSplitterWindow::OnSize
) 
 469 wxThinSplitterWindow::wxThinSplitterWindow(wxWindow
* parent
, wxWindowID id
, 
 473       wxSplitterWindow(parent
, id
, pos
, sz
, style
) 
 477 void wxThinSplitterWindow::SizeWindows() 
 479         // The client size may have changed inbetween 
 480         // the sizing of the first window and the sizing of 
 481         // the second. So repeat SizeWindows. 
 482     wxSplitterWindow::SizeWindows(); 
 483     wxSplitterWindow::SizeWindows(); 
 486 // Tests for x, y over sash 
 487 bool wxThinSplitterWindow::SashHitTest(int x
, int y
, int tolerance
) 
 489         return wxSplitterWindow::SashHitTest(x
, y
, 4); 
 492 void wxThinSplitterWindow::DrawSash(wxDC
& dc
) 
 494     if ( m_sashPosition 
== 0 || !m_windowTwo
) 
 496     if (GetWindowStyle() & wxSP_NOSASH
) 
 500     GetClientSize(&w
, &h
); 
 502         if ( m_splitMode 
== wxSPLIT_VERTICAL 
) 
 504                 dc
.SetPen(* m_facePen
); 
 505                 dc
.SetBrush(* m_faceBrush
); 
 508                 if ( (GetWindowStyleFlag() & wxSP_BORDER
) != wxSP_BORDER 
&& (GetWindowStyleFlag() & wxSP_3DBORDER
) != wxSP_3DBORDER 
) 
 509                         h1 
+= 1; // Not sure why this is necessary... 
 510                 if ( (GetWindowStyleFlag() & wxSP_3DBORDER
) == wxSP_3DBORDER
) 
 514                 dc
.DrawRectangle(m_sashPosition
, y1
, m_sashSize
, h1
); 
 518                 dc
.SetPen(* m_facePen
); 
 519                 dc
.SetBrush(* m_faceBrush
); 
 522                 if ( (GetWindowStyleFlag() & wxSP_BORDER
) != wxSP_BORDER 
&& (GetWindowStyleFlag() & wxSP_3DBORDER
) != wxSP_3DBORDER 
) 
 524                 if ( (GetWindowStyleFlag() & wxSP_3DBORDER
) == wxSP_3DBORDER
) 
 528                 dc
.DrawRectangle(x1
, m_sashPosition
, w1
, m_sashSize
); 
 531     dc
.SetPen(wxNullPen
); 
 532     dc
.SetBrush(wxNullBrush
); 
 535 void wxThinSplitterWindow::OnSize(wxSizeEvent
& event
) 
 537         wxSplitterWindow::OnSize(event
); 
 541  * wxSplitterScrolledWindow 
 544 IMPLEMENT_CLASS(wxSplitterScrolledWindow
, wxScrolledWindow
) 
 546 BEGIN_EVENT_TABLE(wxSplitterScrolledWindow
, wxScrolledWindow
) 
 547         EVT_SCROLLWIN(wxSplitterScrolledWindow::OnScroll
) 
 548         EVT_SIZE(wxSplitterScrolledWindow::OnSize
) 
 551 wxSplitterScrolledWindow::wxSplitterScrolledWindow(wxWindow
* parent
, wxWindowID id
, 
 555       wxScrolledWindow(parent
, id
, pos
, sz
, style
) 
 559 void wxSplitterScrolledWindow::OnSize(wxSizeEvent
& event
) 
 561         wxSize sz 
= GetClientSize(); 
 562         if (GetChildren().First()) 
 564                 ((wxWindow
*) GetChildren().First()->Data())->SetSize(0, 0, sz
.x
, sz
.y
); 
 568 void wxSplitterScrolledWindow::OnScroll(wxScrollWinEvent
& event
) 
 570     // Ensure that events being propagated back up the window hierarchy 
 571     // don't cause an infinite loop 
 572     static bool inOnScroll 
= FALSE
; 
 577     int orient 
= event
.GetOrientation(); 
 579     int nScrollInc 
= CalcScrollInc(event
); 
 586     if (orient 
== wxHORIZONTAL
) 
 592         int newPos 
= m_xScrollPosition 
+ nScrollInc
; 
 593         SetScrollPos(wxHORIZONTAL
, newPos
, TRUE 
); 
 598         int newPos 
= m_yScrollPosition 
+ nScrollInc
; 
 599         SetScrollPos(wxVERTICAL
, newPos
, TRUE 
); 
 602     if (orient 
== wxHORIZONTAL
) 
 604         m_xScrollPosition 
+= nScrollInc
; 
 608         m_yScrollPosition 
+= nScrollInc
; 
 611     // Find targets in splitter window and send the event to them 
 612     wxNode
* node 
= GetChildren().First(); 
 615         wxWindow
* child 
= (wxWindow
*) node
->Data(); 
 616         if (child
->IsKindOf(CLASSINFO(wxSplitterWindow
))) 
 618             wxSplitterWindow
* splitter 
= (wxSplitterWindow
*) child
; 
 619             if (splitter
->GetWindow1()) 
 620                 splitter
->GetWindow1()->ProcessEvent(event
); 
 621             if (splitter
->GetWindow2()) 
 622                 splitter
->GetWindow2()->ProcessEvent(event
); 
 629     m_targetWindow
->MacUpdateImmediately() ;