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) 
  40 #include "wx/msw/winundef.h" 
  43 #include "wx/gizmos/splittree.h" 
  47  * wxRemotelyScrolledTreeCtrl 
  50 #if USE_GENERIC_TREECTRL 
  51 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl
, wxGenericTreeCtrl
) 
  53 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl
, wxTreeCtrl
) 
  56 #if USE_GENERIC_TREECTRL 
  57 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl
, wxGenericTreeCtrl
) 
  59 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl
, wxTreeCtrl
) 
  61         EVT_SIZE(wxRemotelyScrolledTreeCtrl::OnSize
) 
  62         EVT_PAINT(wxRemotelyScrolledTreeCtrl::OnPaint
) 
  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() 
  81 #if defined(__WXMSW__) 
  82 #if USE_GENERIC_TREECTRL 
  83     if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl
))) 
  86         ::ShowScrollBar((HWND
) GetHWND(), SB_VERT
, FALSE
); 
  88 #if USE_GENERIC_TREECTRL 
  91         // Implicit in overriding SetScrollbars 
  97 // Number of pixels per user unit (0 or -1 for no scrollbar) 
  98 // Length of virtual canvas in user units 
  99 // Length of page in user units 
 100 void wxRemotelyScrolledTreeCtrl::SetScrollbars(int pixelsPerUnitX
, int pixelsPerUnitY
, 
 101                              int noUnitsX
, int noUnitsY
, 
 105 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__) 
 106     if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
))) 
 108         wxGenericTreeCtrl
* win 
= (wxGenericTreeCtrl
*) this; 
 109         win
->wxGenericTreeCtrl::SetScrollbars(pixelsPerUnitX
, 0, noUnitsX
, 0, xPos
, 0, noRefresh
); 
 111             wxScrolledWindow
* scrolledWindow 
= GetScrolledWindow(); 
 114             scrolledWindow
->SetScrollbars(0, pixelsPerUnitY
, 0, noUnitsY
, 0, yPos
, noRefresh
); 
 120 // In case we're using the generic tree control. 
 121 int wxRemotelyScrolledTreeCtrl::GetScrollPos(int orient
) const 
 123     wxScrolledWindow
* scrolledWindow 
= GetScrolledWindow(); 
 125 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__) 
 126     if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
))) 
 128         wxGenericTreeCtrl
* win 
= (wxGenericTreeCtrl
*) this; 
 130         if (orient 
== wxHORIZONTAL
) 
 131             return win
->wxGenericTreeCtrl::GetScrollPos(orient
); 
 134             return scrolledWindow
->GetScrollPos(orient
); 
 142 // In case we're using the generic tree control. 
 143 // Get the view start 
 144 void wxRemotelyScrolledTreeCtrl::GetViewStart(int *x
, int *y
) const 
 146     wxScrolledWindow
* scrolledWindow 
= GetScrolledWindow(); 
 148 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__) 
 149     if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
))) 
 152         wxGenericTreeCtrl
* win 
= (wxGenericTreeCtrl
*) this; 
 154         win
->wxGenericTreeCtrl::GetViewStart(& x1
, & y1
); 
 159         scrolledWindow
->GetViewStart(& x2
, & y2
); 
 165                 // x is wrong since the horizontal scrollbar is controlled by the 
 166                 // tree control, but we probably don't need it. 
 167         scrolledWindow
->GetViewStart(x
, y
); 
 171 // In case we're using the generic tree control. 
 172 void wxRemotelyScrolledTreeCtrl::PrepareDC(wxDC
& dc
) 
 174 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__) 
 175     if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
))) 
 177             wxScrolledWindow
* scrolledWindow 
= GetScrolledWindow(); 
 179         wxGenericTreeCtrl
* win 
= (wxGenericTreeCtrl
*) this; 
 182         GetViewStart(& startX
, & startY
); 
 184         int xppu1
, yppu1
, xppu2
, yppu2
; 
 185         win
->wxGenericTreeCtrl::GetScrollPixelsPerUnit(& xppu1
, & yppu1
); 
 186         scrolledWindow
->GetScrollPixelsPerUnit(& xppu2
, & yppu2
); 
 188         dc
.SetDeviceOrigin( -startX 
* xppu1
, -startY 
* yppu2 
); 
 189         // dc.SetUserScale( win->GetScaleX(), win->GetScaleY() ); 
 194 // Scroll to the given line (in scroll units where each unit is 
 195 // the height of an item) 
 196 void wxRemotelyScrolledTreeCtrl::ScrollToLine(int posHoriz
, int posVert
) 
 199 #if USE_GENERIC_TREECTRL 
 200     if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl
))) 
 203             UINT sbCode 
= SB_THUMBPOSITION
; 
 204             HWND vertScrollBar 
= 0; 
 205             MSWDefWindowProc((WXUINT
) WM_VSCROLL
, MAKELONG(sbCode
, posVert
), (WXHWND
) vertScrollBar
); 
 207 #if USE_GENERIC_TREECTRL 
 211 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__) 
 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); 
 228 void wxRemotelyScrolledTreeCtrl::OnSize(wxSizeEvent
& event
) 
 231         AdjustRemoteScrollbars(); 
 235 void wxRemotelyScrolledTreeCtrl::OnExpand(wxTreeEvent
& event
) 
 237         AdjustRemoteScrollbars(); 
 240     // If we don't have this, we get some bits of lines still remaining 
 241     if (event
.GetEventType() == wxEVT_COMMAND_TREE_ITEM_COLLAPSED
) 
 245         if (m_companionWindow
) 
 246                 m_companionWindow
->GetEventHandler()->ProcessEvent(event
); 
 249 void wxRemotelyScrolledTreeCtrl::OnPaint(wxPaintEvent
& event
) 
 253         wxTreeCtrl::OnPaint(event
); 
 255     // The generic tree already knows how to draw lines 
 257     if ((GetWindowStyle() & wxTR_ROW_LINES
) == 0) 
 260     // Reset the device origin since it may have been set 
 261     dc
.SetDeviceOrigin(0, 0); 
 263         wxPen 
pen(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT
), 1, wxSOLID
); 
 265         dc
.SetBrush(* wxTRANSPARENT_BRUSH
); 
 267     wxSize clientSize 
= GetClientSize(); 
 270         wxTreeItemId h
, lastH
; 
 271         for(h
=GetFirstVisibleItem();h
;h
=GetNextVisible(h
)) 
 273                 if (GetBoundingRect(h
, itemRect
)) 
 275                         cy 
= itemRect
.GetTop(); 
 276                         dc
.DrawLine(0, cy
, clientSize
.x
, cy
); 
 280         if (GetBoundingRect(lastH
, itemRect
)) 
 282                 cy 
= itemRect
.GetBottom(); 
 283                 dc
.DrawLine(0, cy
, clientSize
.x
, cy
); 
 289 // Adjust the containing wxScrolledWindow's scrollbars appropriately 
 290 void wxRemotelyScrolledTreeCtrl::AdjustRemoteScrollbars() 
 292 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__) 
 293     if (IsKindOf(CLASSINFO(wxGenericTreeCtrl
))) 
 295                 // This is for the generic tree control. 
 296                 // It calls SetScrollbars which has been overridden 
 297                 // to adjust the parent scrolled window vertical 
 299                 ((wxGenericTreeCtrl
*) this)->AdjustMyScrollbars(); 
 305                 // This is for the wxMSW tree control 
 306                 wxScrolledWindow
* scrolledWindow 
= GetScrolledWindow(); 
 310                         if (GetBoundingRect(GetRootItem(), itemRect
)) 
 312                 // Actually, the real height seems to be 1 less than reported 
 313                 // (e.g. 16 instead of 16) 
 314                 int itemHeight 
= itemRect
.GetHeight() - 1; 
 317                                 GetClientSize(&w
, &h
); 
 319                                 wxRect 
rect(0, 0, 0, 0); 
 322                 double f 
= ((double) (rect
.GetHeight()) / (double) itemHeight
)  ; 
 323                 int treeViewHeight 
= (int) ceil(f
); 
 325                                 int scrollPixelsPerLine 
= itemHeight
; 
 326                                 int scrollPos 
= - (itemRect
.y 
/ itemHeight
); 
 328                                 scrolledWindow
->SetScrollbars(0, scrollPixelsPerLine
, 0, treeViewHeight
, 0, scrollPos
); 
 330                                 // Ensure that when a scrollbar becomes hidden or visible, 
 331                                 // the contained window sizes are right. 
 332                                 // Problem: this is called too early (?) 
 333                                 wxSizeEvent 
event(scrolledWindow
->GetSize(), scrolledWindow
->GetId()); 
 334                                 scrolledWindow
->GetEventHandler()->ProcessEvent(event
); 
 341 // Calculate the area that contains both rectangles 
 342 static wxRect 
CombineRectangles(const wxRect
& rect1
, const wxRect
& rect2
) 
 346     int right1 
= rect1
.GetRight(); 
 347     int bottom1 
= rect1
.GetBottom(); 
 348     int right2 
= rect2
.GetRight(); 
 349     int bottom2 
= rect2
.GetBottom(); 
 351     wxPoint topLeft 
= wxPoint(wxMin(rect1
.x
, rect2
.x
), wxMin(rect1
.y
, rect2
.y
)); 
 352     wxPoint bottomRight 
= wxPoint(wxMax(right1
, right2
), wxMax(bottom1
, bottom2
)); 
 354     rect
.x 
= topLeft
.x
; rect
.y 
= topLeft
.y
; 
 355     rect
.SetRight(bottomRight
.x
); 
 356     rect
.SetBottom(bottomRight
.y
); 
 362 // Calculate the tree overall size so we can set the scrollbar 
 364 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(wxRect
& rect
) 
 366         CalcTreeSize(GetRootItem(), rect
); 
 369 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(const wxTreeItemId
& id
, wxRect
& rect
) 
 371         // More efficient implementation would be to find the last item (but how?) 
 372         // Q: is the bounding rect relative to the top of the virtual tree workspace 
 373         // or the top of the window? How would we convert? 
 375         if (GetBoundingRect(id
, itemSize
)) 
 377                 rect 
= CombineRectangles(rect
, itemSize
); 
 381         wxTreeItemId childId 
= GetFirstChild(id
, cookie
); 
 384                 CalcTreeSize(childId
, rect
); 
 385                 childId 
= GetNextChild(childId
, cookie
); 
 389 // Find the scrolled window that contains this control 
 390 wxScrolledWindow
* wxRemotelyScrolledTreeCtrl::GetScrolledWindow() const 
 392         wxWindow
* parent 
= wxWindow::GetParent(); 
 395                 if (parent
->IsKindOf(CLASSINFO(wxScrolledWindow
))) 
 396                         return (wxScrolledWindow
*) parent
; 
 397                 parent 
= parent
->GetParent(); 
 402 void wxRemotelyScrolledTreeCtrl::OnScroll(wxScrollWinEvent
& event
) 
 404     int orient 
= event
.GetOrientation(); 
 405     if (orient 
== wxHORIZONTAL
) 
 410     wxScrolledWindow
* scrollWin 
= GetScrolledWindow(); 
 415     scrollWin
->GetViewStart(& x
, & y
); 
 421  * wxTreeCompanionWindow 
 423  * A window displaying values associated with tree control items. 
 426 IMPLEMENT_CLASS(wxTreeCompanionWindow
, wxWindow
) 
 428 BEGIN_EVENT_TABLE(wxTreeCompanionWindow
, wxWindow
) 
 429         EVT_PAINT(wxTreeCompanionWindow::OnPaint
) 
 430         EVT_SCROLLWIN(wxTreeCompanionWindow::OnScroll
) 
 431         EVT_TREE_ITEM_EXPANDED(-1, wxTreeCompanionWindow::OnExpand
) 
 432         EVT_TREE_ITEM_COLLAPSED(-1, wxTreeCompanionWindow::OnExpand
) 
 435 wxTreeCompanionWindow::wxTreeCompanionWindow(wxWindow
* parent
, wxWindowID id
, 
 439         wxWindow(parent
, id
, pos
, sz
, style
) 
 444 void wxTreeCompanionWindow::DrawItem(wxDC
& dc
, wxTreeItemId id
, const wxRect
& rect
) 
 450                 wxString text 
= m_treeCtrl
->GetItemText(id
); 
 451                 dc
.SetTextForeground(* wxBLACK
); 
 452                 dc
.SetBackgroundMode(wxTRANSPARENT
); 
 455                 dc
.GetTextExtent(text
, & textW
, & textH
); 
 458                 int y 
= rect
.GetY() + wxMax(0, (rect
.GetHeight() - textH
) / 2); 
 460                 dc
.DrawText(text
, x
, y
); 
 465 void wxTreeCompanionWindow::OnPaint(wxPaintEvent
& event
) 
 472         wxPen 
pen(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT
), 1, wxSOLID
); 
 474         dc
.SetBrush(* wxTRANSPARENT_BRUSH
); 
 475         wxFont 
font(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT
)); 
 478     wxSize clientSize 
= GetClientSize(); 
 481         wxTreeItemId h
, lastH
; 
 482         for(h
=m_treeCtrl
->GetFirstVisibleItem();h
;h
=m_treeCtrl
->GetNextVisible(h
)) 
 484                 if (m_treeCtrl
->GetBoundingRect(h
, itemRect
)) 
 486                         cy 
= itemRect
.GetTop(); 
 487                         wxRect 
drawItemRect(0, cy
, clientSize
.x
, itemRect
.GetHeight()); 
 491                         // Draw the actual item 
 492                         DrawItem(dc
, h
, drawItemRect
); 
 493                         dc
.DrawLine(0, cy
, clientSize
.x
, cy
); 
 496         if (lastH
.IsOk() && m_treeCtrl
->GetBoundingRect(lastH
, itemRect
)) 
 498                 cy 
= itemRect
.GetBottom(); 
 499                 dc
.DrawLine(0, cy
, clientSize
.x
, cy
); 
 503 void wxTreeCompanionWindow::OnScroll(wxScrollWinEvent
& event
) 
 505     int orient 
= event
.GetOrientation(); 
 506     if (orient 
== wxHORIZONTAL
) 
 514         // TODO: scroll the window physically instead of just refreshing. 
 518 void wxTreeCompanionWindow::OnExpand(wxTreeEvent
& event
) 
 520         // TODO: something more optimized than simply refresh the whole 
 521         // window when the tree is expanded/collapsed. Tricky. 
 526  * wxThinSplitterWindow 
 529 IMPLEMENT_CLASS(wxThinSplitterWindow
, wxSplitterWindow
) 
 531 BEGIN_EVENT_TABLE(wxThinSplitterWindow
, wxSplitterWindow
) 
 532         EVT_SIZE(wxThinSplitterWindow::OnSize
) 
 535 wxThinSplitterWindow::wxThinSplitterWindow(wxWindow
* parent
, wxWindowID id
, 
 539       wxSplitterWindow(parent
, id
, pos
, sz
, style
) 
 543 void wxThinSplitterWindow::SizeWindows() 
 545         // The client size may have changed inbetween 
 546         // the sizing of the first window and the sizing of 
 547         // the second. So repeat SizeWindows. 
 548     wxSplitterWindow::SizeWindows(); 
 549     wxSplitterWindow::SizeWindows(); 
 552 // Tests for x, y over sash 
 553 bool wxThinSplitterWindow::SashHitTest(int x
, int y
, int tolerance
) 
 555         return wxSplitterWindow::SashHitTest(x
, y
, 4); 
 558 void wxThinSplitterWindow::DrawSash(wxDC
& dc
) 
 560     if ( m_sashPosition 
== 0 || !m_windowTwo
) 
 562     if (GetWindowStyle() & wxSP_NOSASH
) 
 566     GetClientSize(&w
, &h
); 
 568         if ( m_splitMode 
== wxSPLIT_VERTICAL 
) 
 570                 dc
.SetPen(* m_facePen
); 
 571                 dc
.SetBrush(* m_faceBrush
); 
 574                 if ( (GetWindowStyleFlag() & wxSP_BORDER
) != wxSP_BORDER 
&& (GetWindowStyleFlag() & wxSP_3DBORDER
) != wxSP_3DBORDER 
) 
 575                         h1 
+= 1; // Not sure why this is necessary... 
 576                 if ( (GetWindowStyleFlag() & wxSP_3DBORDER
) == wxSP_3DBORDER
) 
 580                 dc
.DrawRectangle(m_sashPosition
, y1
, m_sashSize
, h1
); 
 584                 dc
.SetPen(* m_facePen
); 
 585                 dc
.SetBrush(* m_faceBrush
); 
 588                 if ( (GetWindowStyleFlag() & wxSP_BORDER
) != wxSP_BORDER 
&& (GetWindowStyleFlag() & wxSP_3DBORDER
) != wxSP_3DBORDER 
) 
 590                 if ( (GetWindowStyleFlag() & wxSP_3DBORDER
) == wxSP_3DBORDER
) 
 594                 dc
.DrawRectangle(x1
, m_sashPosition
, w1
, m_sashSize
); 
 597     dc
.SetPen(wxNullPen
); 
 598     dc
.SetBrush(wxNullBrush
); 
 601 void wxThinSplitterWindow::OnSize(wxSizeEvent
& event
) 
 603         wxSplitterWindow::OnSize(event
); 
 607  * wxSplitterScrolledWindow 
 610 IMPLEMENT_CLASS(wxSplitterScrolledWindow
, wxScrolledWindow
) 
 612 BEGIN_EVENT_TABLE(wxSplitterScrolledWindow
, wxScrolledWindow
) 
 613         EVT_SCROLLWIN(wxSplitterScrolledWindow::OnScroll
) 
 614         EVT_SIZE(wxSplitterScrolledWindow::OnSize
) 
 617 wxSplitterScrolledWindow::wxSplitterScrolledWindow(wxWindow
* parent
, wxWindowID id
, 
 621       wxScrolledWindow(parent
, id
, pos
, sz
, style
) 
 625 void wxSplitterScrolledWindow::OnSize(wxSizeEvent
& event
) 
 627         wxSize sz 
= GetClientSize(); 
 628         if (GetChildren().First()) 
 630                 ((wxWindow
*) GetChildren().First()->Data())->SetSize(0, 0, sz
.x
, sz
.y
); 
 634 void wxSplitterScrolledWindow::OnScroll(wxScrollWinEvent
& event
) 
 636     // Ensure that events being propagated back up the window hierarchy 
 637     // don't cause an infinite loop 
 638     static bool inOnScroll 
= FALSE
; 
 646     int orient 
= event
.GetOrientation(); 
 648     int nScrollInc 
= CalcScrollInc(event
); 
 655     if (orient 
== wxHORIZONTAL
) 
 661         int newPos 
= m_xScrollPosition 
+ nScrollInc
; 
 662         SetScrollPos(wxHORIZONTAL
, newPos
, TRUE 
); 
 667         int newPos 
= m_yScrollPosition 
+ nScrollInc
; 
 668         SetScrollPos(wxVERTICAL
, newPos
, TRUE 
); 
 671     if (orient 
== wxHORIZONTAL
) 
 673         m_xScrollPosition 
+= nScrollInc
; 
 677         m_yScrollPosition 
+= nScrollInc
; 
 680     // Find targets in splitter window and send the event to them 
 681     wxNode
* node 
= GetChildren().First(); 
 684         wxWindow
* child 
= (wxWindow
*) node
->Data(); 
 685         if (child
->IsKindOf(CLASSINFO(wxSplitterWindow
))) 
 687             wxSplitterWindow
* splitter 
= (wxSplitterWindow
*) child
; 
 688             if (splitter
->GetWindow1()) 
 689                 splitter
->GetWindow1()->ProcessEvent(event
); 
 690             if (splitter
->GetWindow2()) 
 691                 splitter
->GetWindow2()->ProcessEvent(event
); 
 698     m_targetWindow
->MacUpdateImmediately() ;