1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/generic/datavgen.cpp 
   3 // Purpose:     wxDataViewCtrl generic implementation 
   4 // Author:      Robert Roebling 
   5 // Modified by: Francesco Montorsi, Guru Kathiresan, Bo Yang 
   7 // Copyright:   (c) 1998 Robert Roebling 
   8 // Licence:     wxWindows licence 
   9 ///////////////////////////////////////////////////////////////////////////// 
  11 // For compilers that support precompilation, includes "wx.h". 
  12 #include "wx/wxprec.h" 
  18 #if wxUSE_DATAVIEWCTRL 
  20 #include "wx/dataview.h" 
  22 #ifdef wxUSE_GENERICDATAVIEWCTRL 
  26         #include "wx/msw/private.h" 
  27         #include "wx/msw/wrapwin.h" 
  28         #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly" 
  32     #include "wx/dcclient.h" 
  34     #include "wx/settings.h" 
  35     #include "wx/msgdlg.h" 
  36     #include "wx/dcscreen.h" 
  40 #include "wx/stockitem.h" 
  41 #include "wx/popupwin.h" 
  42 #include "wx/renderer.h" 
  43 #include "wx/dcbuffer.h" 
  46 #include "wx/listimpl.cpp" 
  47 #include "wx/imaglist.h" 
  48 #include "wx/headerctrl.h" 
  50 #include "wx/stopwatch.h" 
  51 #include "wx/weakref.h" 
  53 //----------------------------------------------------------------------------- 
  55 //----------------------------------------------------------------------------- 
  57 class wxDataViewColumn
; 
  58 class wxDataViewHeaderWindow
; 
  61 //----------------------------------------------------------------------------- 
  63 //----------------------------------------------------------------------------- 
  65 static const int SCROLL_UNIT_X 
= 15; 
  67 // the cell padding on the left/right 
  68 static const int PADDING_RIGHTLEFT 
= 3; 
  70 // the expander space margin 
  71 static const int EXPANDER_MARGIN 
= 4; 
  74 static const int EXPANDER_OFFSET 
= 4; 
  76 static const int EXPANDER_OFFSET 
= 1; 
  79 // Below is the compare stuff. 
  80 // For the generic implementation, both the leaf nodes and the nodes are sorted for 
  81 // fast search when needed 
  82 static wxDataViewModel
* g_model
; 
  83 static int g_column 
= -2; 
  84 static bool g_asending 
= true; 
  86 // ---------------------------------------------------------------------------- 
  88 // ---------------------------------------------------------------------------- 
  93 // Return the expander column or, if it is not set, the first column and also 
  94 // set it as the expander one for the future. 
  95 wxDataViewColumn
* GetExpanderColumnOrFirstOne(wxDataViewCtrl
* dataview
) 
  97     wxDataViewColumn
* expander 
= dataview
->GetExpanderColumn(); 
 100         // TODO-RTL: last column for RTL support 
 101         expander 
= dataview
->GetColumnAt( 0 ); 
 102         dataview
->SetExpanderColumn(expander
); 
 108 } // anonymous namespace 
 110 //----------------------------------------------------------------------------- 
 112 //----------------------------------------------------------------------------- 
 114 void wxDataViewColumn::Init(int width
, wxAlignment align
, int flags
) 
 121     m_sortAscending 
= true; 
 124 int wxDataViewColumn::GetWidth() const 
 128         case wxCOL_WIDTH_DEFAULT
: 
 129             return wxDVC_DEFAULT_WIDTH
; 
 131         case wxCOL_WIDTH_AUTOSIZE
: 
 132             wxCHECK_MSG( m_owner
, wxDVC_DEFAULT_WIDTH
, "no owner control" ); 
 133             return m_owner
->GetBestColumnWidth(m_owner
->GetColumnIndex(this)); 
 140 void wxDataViewColumn::UpdateDisplay() 
 144         int idx 
= m_owner
->GetColumnIndex( this ); 
 145         m_owner
->OnColumnChange( idx 
); 
 149 void wxDataViewColumn::SetSortOrder(bool ascending
) 
 154     // First unset the old sort column if any. 
 155     int oldSortKey 
= m_owner
->GetSortingColumnIndex(); 
 156     if ( oldSortKey 
!= wxNOT_FOUND 
) 
 158         m_owner
->GetColumn(oldSortKey
)->UnsetAsSortKey(); 
 161     // Now set this one as the new sort column. 
 162     const int idx 
= m_owner
->GetColumnIndex(this); 
 163     m_owner
->SetSortingColumnIndex(idx
); 
 166     m_sortAscending 
= ascending
; 
 168     // Call this directly instead of using UpdateDisplay() as we already have 
 169     // the column index, no need to look it up again. 
 170     m_owner
->OnColumnChange(idx
); 
 173 //----------------------------------------------------------------------------- 
 174 // wxDataViewHeaderWindow 
 175 //----------------------------------------------------------------------------- 
 177 class wxDataViewHeaderWindow 
: public wxHeaderCtrl
 
 180     wxDataViewHeaderWindow(wxDataViewCtrl 
*parent
) 
 181         : wxHeaderCtrl(parent
) 
 185     wxDataViewCtrl 
*GetOwner() const 
 186         { return static_cast<wxDataViewCtrl 
*>(GetParent()); } 
 189     // implement/override wxHeaderCtrl functions by forwarding them to the main 
 191     virtual const wxHeaderColumn
& GetColumn(unsigned int idx
) const 
 193         return *(GetOwner()->GetColumn(idx
)); 
 196     virtual bool UpdateColumnWidthToFit(unsigned int idx
, int widthTitle
) 
 198         wxDataViewCtrl 
* const owner 
= GetOwner(); 
 200         int widthContents 
= owner
->GetBestColumnWidth(idx
); 
 201         owner
->GetColumn(idx
)->SetWidth(wxMax(widthTitle
, widthContents
)); 
 202         owner
->OnColumnChange(idx
); 
 208     bool SendEvent(wxEventType type
, unsigned int n
) 
 210         wxDataViewCtrl 
* const owner 
= GetOwner(); 
 211         wxDataViewEvent 
event(type
, owner
->GetId()); 
 213         event
.SetEventObject(owner
); 
 215         event
.SetDataViewColumn(owner
->GetColumn(n
)); 
 216         event
.SetModel(owner
->GetModel()); 
 218         // for events created by wxDataViewHeaderWindow the 
 219         // row / value fields are not valid 
 220         return owner
->ProcessWindowEvent(event
); 
 223     void OnClick(wxHeaderCtrlEvent
& event
) 
 225         const unsigned idx 
= event
.GetColumn(); 
 227         if ( SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK
, idx
) ) 
 230         // default handling for the column click is to sort by this column or 
 231         // toggle its sort order 
 232         wxDataViewCtrl 
* const owner 
= GetOwner(); 
 233         wxDataViewColumn 
* const col 
= owner
->GetColumn(idx
); 
 234         if ( !col
->IsSortable() ) 
 236             // no default handling for non-sortable columns 
 241         if ( col
->IsSortKey() ) 
 243             // already using this column for sorting, just change the order 
 244             col
->ToggleSortOrder(); 
 246         else // not using this column for sorting yet 
 248             col
->SetSortOrder(true); 
 251         wxDataViewModel 
* const model 
= owner
->GetModel(); 
 255         owner
->OnColumnChange(idx
); 
 257         SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_SORTED
, idx
); 
 260     void OnRClick(wxHeaderCtrlEvent
& event
) 
 262         if ( !SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK
, 
 267     void OnResize(wxHeaderCtrlEvent
& event
) 
 269         wxDataViewCtrl 
* const owner 
= GetOwner(); 
 271         const unsigned col 
= event
.GetColumn(); 
 272         owner
->GetColumn(col
)->SetWidth(event
.GetWidth()); 
 273         GetOwner()->OnColumnChange(col
); 
 276     void OnEndReorder(wxHeaderCtrlEvent
& event
) 
 278         wxDataViewCtrl 
* const owner 
= GetOwner(); 
 279         owner
->ColumnMoved(owner
->GetColumn(event
.GetColumn()), 
 280                         event
.GetNewOrder()); 
 283     DECLARE_EVENT_TABLE() 
 284     wxDECLARE_NO_COPY_CLASS(wxDataViewHeaderWindow
); 
 287 BEGIN_EVENT_TABLE(wxDataViewHeaderWindow
, wxHeaderCtrl
) 
 288     EVT_HEADER_CLICK(wxID_ANY
, wxDataViewHeaderWindow::OnClick
) 
 289     EVT_HEADER_RIGHT_CLICK(wxID_ANY
, wxDataViewHeaderWindow::OnRClick
) 
 291     EVT_HEADER_RESIZING(wxID_ANY
, wxDataViewHeaderWindow::OnResize
) 
 292     EVT_HEADER_END_RESIZE(wxID_ANY
, wxDataViewHeaderWindow::OnResize
) 
 294     EVT_HEADER_END_REORDER(wxID_ANY
, wxDataViewHeaderWindow::OnEndReorder
) 
 297 //----------------------------------------------------------------------------- 
 298 // wxDataViewRenameTimer 
 299 //----------------------------------------------------------------------------- 
 301 class wxDataViewRenameTimer
: public wxTimer
 
 304     wxDataViewMainWindow 
*m_owner
; 
 307     wxDataViewRenameTimer( wxDataViewMainWindow 
*owner 
); 
 311 //----------------------------------------------------------------------------- 
 312 // wxDataViewTreeNode 
 313 //----------------------------------------------------------------------------- 
 315 class wxDataViewTreeNode
; 
 316 WX_DEFINE_ARRAY( wxDataViewTreeNode 
*, wxDataViewTreeNodes 
); 
 318 int LINKAGEMODE 
wxGenericTreeModelNodeCmp( wxDataViewTreeNode 
** node1
, 
 319                                            wxDataViewTreeNode 
** node2
); 
 321 class wxDataViewTreeNode
 
 324     wxDataViewTreeNode(wxDataViewTreeNode 
*parent
, const wxDataViewItem
& item
) 
 331     ~wxDataViewTreeNode() 
 335             wxDataViewTreeNodes
& nodes 
= m_branchData
->children
; 
 336             for ( wxDataViewTreeNodes::iterator i 
= nodes
.begin(); 
 347     static wxDataViewTreeNode
* CreateRootNode() 
 349         wxDataViewTreeNode 
*n 
= new wxDataViewTreeNode(NULL
, wxDataViewItem()); 
 350         n
->m_branchData 
= new BranchNodeData
; 
 351         n
->m_branchData
->open 
= true; 
 355     wxDataViewTreeNode 
* GetParent() const { return m_parent
; } 
 357     const wxDataViewTreeNodes
& GetChildNodes() const 
 359         wxASSERT( m_branchData 
!= NULL 
); 
 360         return m_branchData
->children
; 
 363     void InsertChild(wxDataViewTreeNode 
*node
, unsigned index
) 
 366             m_branchData 
= new BranchNodeData
; 
 368         m_branchData
->children
.Insert(node
, index
); 
 370         // TODO: insert into sorted array directly in O(log n) instead of resorting in O(n log n) 
 372             m_branchData
->children
.Sort( &wxGenericTreeModelNodeCmp 
); 
 375     void RemoveChild(wxDataViewTreeNode 
*node
) 
 377         wxCHECK_RET( m_branchData 
!= NULL
, "leaf node doesn't have children" ); 
 378         m_branchData
->children
.Remove(node
); 
 381     // returns position of child node for given item in children list or wxNOT_FOUND 
 382     int FindChildByItem(const wxDataViewItem
& item
) const 
 387         const wxDataViewTreeNodes
& nodes 
= m_branchData
->children
; 
 388         const int len 
= nodes
.size(); 
 389         for ( int i 
= 0; i 
< len
; i
++ ) 
 391             if ( nodes
[i
]->m_item 
== item 
) 
 397     const wxDataViewItem 
& GetItem() const { return m_item
; } 
 398     void SetItem( const wxDataViewItem 
& item 
) { m_item 
= item
; } 
 400     int GetIndentLevel() const 
 403         const wxDataViewTreeNode 
* node 
= this; 
 404         while( node
->GetParent()->GetParent() != NULL 
) 
 406             node 
= node
->GetParent(); 
 414         return m_branchData 
&& m_branchData
->open
; 
 419         // We do not allow the (invisible) root node to be collapsed because 
 420         // there is no way to expand it again. 
 424         wxCHECK_RET( m_branchData 
!= NULL
, "can't open leaf node" ); 
 428         const wxDataViewTreeNodes
& nodes 
= m_branchData
->children
; 
 429         const int len 
= nodes
.GetCount(); 
 430         for ( int i 
= 0;i 
< len
; i 
++) 
 431             sum 
+= 1 + nodes
[i
]->GetSubTreeCount(); 
 433         if (m_branchData
->open
) 
 435             ChangeSubTreeCount(-sum
); 
 436             m_branchData
->open 
= !m_branchData
->open
; 
 440             m_branchData
->open 
= !m_branchData
->open
; 
 441             ChangeSubTreeCount(+sum
); 
 445     // "HasChildren" property corresponds to model's IsContainer(). Note that it may be true 
 446     // even if GetChildNodes() is empty; see below. 
 447     bool HasChildren() const 
 449         return m_branchData 
!= NULL
; 
 452     void SetHasChildren(bool has
) 
 454         // The invisible root item always has children, so ignore any attempts 
 461             wxDELETE(m_branchData
); 
 463         else if ( m_branchData 
== NULL 
) 
 465             m_branchData 
= new BranchNodeData
; 
 469     int GetSubTreeCount() const 
 471         return m_branchData 
? m_branchData
->subTreeCount 
: 0; 
 474     void ChangeSubTreeCount( int num 
) 
 476         wxASSERT( m_branchData 
!= NULL 
); 
 478         if( !m_branchData
->open 
) 
 481         m_branchData
->subTreeCount 
+= num
; 
 482         wxASSERT( m_branchData
->subTreeCount 
>= 0 ); 
 485             m_parent
->ChangeSubTreeCount(num
); 
 495             wxDataViewTreeNodes
& nodes 
= m_branchData
->children
; 
 497             nodes
.Sort( &wxGenericTreeModelNodeCmp 
); 
 498             int len 
= nodes
.GetCount(); 
 499             for (int i 
= 0; i 
< len
; i 
++) 
 501                 if ( nodes
[i
]->HasChildren() ) 
 509     wxDataViewTreeNode  
*m_parent
; 
 511     // Corresponding model item. 
 512     wxDataViewItem       m_item
; 
 514     // Data specific to non-leaf (branch, inner) nodes. They are kept in a 
 515     // separate struct in order to conserve memory. 
 516     struct BranchNodeData
 
 524         // Child nodes. Note that this may be empty even if m_hasChildren in 
 525         // case this branch of the tree wasn't expanded and realized yet. 
 526         wxDataViewTreeNodes  children
; 
 528         // Is the branch node currently open (expanded)? 
 531         // Total count of expanded (i.e. visible with the help of some 
 532         // scrolling) items in the subtree, but excluding this node. I.e. it is 
 533         // 0 for leaves and is the number of rows the subtree occupies for 
 538     BranchNodeData 
*m_branchData
; 
 542 int LINKAGEMODE 
wxGenericTreeModelNodeCmp( wxDataViewTreeNode 
** node1
, 
 543                                            wxDataViewTreeNode 
** node2
) 
 545     return g_model
->Compare( (*node1
)->GetItem(), (*node2
)->GetItem(), g_column
, g_asending 
); 
 549 //----------------------------------------------------------------------------- 
 550 // wxDataViewMainWindow 
 551 //----------------------------------------------------------------------------- 
 553 WX_DEFINE_SORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection
); 
 555 class wxDataViewMainWindow
: public wxWindow
 
 558     wxDataViewMainWindow( wxDataViewCtrl 
*parent
, 
 560                             const wxPoint 
&pos 
= wxDefaultPosition
, 
 561                             const wxSize 
&size 
= wxDefaultSize
, 
 562                             const wxString 
&name 
= wxT("wxdataviewctrlmainwindow") ); 
 563     virtual ~wxDataViewMainWindow(); 
 565     bool IsList() const { return GetModel()->IsListModel(); } 
 566     bool IsVirtualList() const { return m_root 
== NULL
; } 
 568     // notifications from wxDataViewModel 
 569     bool ItemAdded( const wxDataViewItem 
&parent
, const wxDataViewItem 
&item 
); 
 570     bool ItemDeleted( const wxDataViewItem 
&parent
, const wxDataViewItem 
&item 
); 
 571     bool ItemChanged( const wxDataViewItem 
&item 
); 
 572     bool ValueChanged( const wxDataViewItem 
&item
, unsigned int model_column 
); 
 576         if (!IsVirtualList()) 
 586         g_model 
= GetModel(); 
 587         wxDataViewColumn
* col 
= GetOwner()->GetSortingColumn(); 
 590             if (g_model
->HasDefaultCompare()) 
 598         g_column 
= col
->GetModelColumn(); 
 599         g_asending 
= col
->IsSortOrderAscending(); 
 602     void SetOwner( wxDataViewCtrl
* owner 
) { m_owner 
= owner
; } 
 603     wxDataViewCtrl 
*GetOwner() { return m_owner
; } 
 604     const wxDataViewCtrl 
*GetOwner() const { return m_owner
; } 
 606     wxDataViewModel
* GetModel() { return GetOwner()->GetModel(); } 
 607     const wxDataViewModel
* GetModel() const { return GetOwner()->GetModel(); } 
 609 #if wxUSE_DRAG_AND_DROP 
 610     wxBitmap 
CreateItemBitmap( unsigned int row
, int &indent 
); 
 611 #endif // wxUSE_DRAG_AND_DROP 
 612     void OnPaint( wxPaintEvent 
&event 
); 
 613     void OnCharHook( wxKeyEvent 
&event 
); 
 614     void OnChar( wxKeyEvent 
&event 
); 
 615     void OnVerticalNavigation(int delta
, const wxKeyEvent
& event
); 
 618     void OnMouse( wxMouseEvent 
&event 
); 
 619     void OnSetFocus( wxFocusEvent 
&event 
); 
 620     void OnKillFocus( wxFocusEvent 
&event 
); 
 622     void UpdateDisplay(); 
 623     void RecalculateDisplay(); 
 624     void OnInternalIdle(); 
 626     void OnRenameTimer(); 
 628     void ScrollWindow( int dx
, int dy
, const wxRect 
*rect 
= NULL 
); 
 629     void ScrollTo( int rows
, int column 
); 
 631     unsigned GetCurrentRow() const { return m_currentRow
; } 
 632     bool HasCurrentRow() { return m_currentRow 
!= (unsigned int)-1; } 
 633     void ChangeCurrentRow( unsigned int row 
); 
 634     bool TryAdvanceCurrentColumn(wxDataViewTreeNode 
*node
, bool forward
); 
 636     wxDataViewColumn 
*GetCurrentColumn() const { return m_currentCol
; } 
 637     void ClearCurrentColumn() { m_currentCol 
= NULL
; } 
 639     bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE
); } 
 640     bool IsEmpty() { return GetRowCount() == 0; } 
 642     int GetCountPerPage() const; 
 643     int GetEndOfLastCol() const; 
 644     unsigned int GetFirstVisibleRow() const; 
 646     // I change this method to un const because in the tree view, 
 647     // the displaying number of the tree are changing along with the 
 648     // expanding/collapsing of the tree nodes 
 649     unsigned int GetLastVisibleRow(); 
 650     unsigned int GetRowCount() const; 
 652     const wxDataViewSelection
& GetSelections() const { return m_selection
; } 
 653     void SetSelections( const wxDataViewSelection 
& sel 
) 
 654         { m_selection 
= sel
; UpdateDisplay(); } 
 655     void Select( const wxArrayInt
& aSelections 
); 
 656     void SelectAllRows( bool on 
); 
 657     void SelectRow( unsigned int row
, bool on 
); 
 658     void SelectRows( unsigned int from
, unsigned int to
, bool on 
); 
 659     void ReverseRowSelection( unsigned int row 
); 
 660     bool IsRowSelected( unsigned int row 
); 
 661     void SendSelectionChangedEvent( const wxDataViewItem
& item
); 
 663     void RefreshRow( unsigned int row 
); 
 664     void RefreshRows( unsigned int from
, unsigned int to 
); 
 665     void RefreshRowsAfter( unsigned int firstRow 
); 
 667     // returns the colour to be used for drawing the rules 
 668     wxColour 
GetRuleColour() const 
 670         return wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT
); 
 673     wxRect 
GetLineRect( unsigned int row 
) const; 
 675     int GetLineStart( unsigned int row 
) const;  // row * m_lineHeight in fixed mode 
 676     int GetLineHeight( unsigned int row 
) const; // m_lineHeight in fixed mode 
 677     int GetLineAt( unsigned int y 
) const;       // y / m_lineHeight in fixed mode 
 679     void SetRowHeight( int lineHeight 
) { m_lineHeight 
= lineHeight
; } 
 680     int GetRowHeight() const { return m_lineHeight
; } 
 682     // Some useful functions for row and item mapping 
 683     wxDataViewItem 
GetItemByRow( unsigned int row 
) const; 
 684     int GetRowByItem( const wxDataViewItem 
& item 
) const; 
 686     wxDataViewTreeNode 
* GetTreeNodeByRow( unsigned int row 
) const; 
 687     // We did not need this temporarily 
 688     // wxDataViewTreeNode * GetTreeNodeByItem( const wxDataViewItem & item ); 
 690     // Methods for building the mapping tree 
 691     void BuildTree( wxDataViewModel  
* model 
); 
 693     void HitTest( const wxPoint 
& point
, wxDataViewItem 
& item
, wxDataViewColumn
* &column 
); 
 694     wxRect 
GetItemRect( const wxDataViewItem 
& item
, const wxDataViewColumn
* column 
); 
 696     void Expand( unsigned int row 
); 
 697     void Collapse( unsigned int row 
); 
 698     bool IsExpanded( unsigned int row 
) const; 
 699     bool HasChildren( unsigned int row 
) const; 
 701 #if wxUSE_DRAG_AND_DROP 
 702     bool EnableDragSource( const wxDataFormat 
&format 
); 
 703     bool EnableDropTarget( const wxDataFormat 
&format 
); 
 705     void RemoveDropHint(); 
 706     wxDragResult 
OnDragOver( wxDataFormat format
, wxCoord x
, wxCoord y
, wxDragResult def 
); 
 707     bool OnDrop( wxDataFormat format
, wxCoord x
, wxCoord y 
); 
 708     wxDragResult 
OnData( wxDataFormat format
, wxCoord x
, wxCoord y
, wxDragResult def 
); 
 710 #endif // wxUSE_DRAG_AND_DROP 
 712     void OnColumnsCountChanged(); 
 714     // Called by wxDataViewCtrl and our own OnRenameTimer() to start edit the 
 715     // specified item in the given column. 
 716     void StartEditing(const wxDataViewItem
& item
, const wxDataViewColumn
* col
); 
 719     int RecalculateCount() const; 
 721     // Return false only if the event was vetoed by its handler. 
 722     bool SendExpanderEvent(wxEventType type
, const wxDataViewItem
& item
); 
 724     wxDataViewTreeNode 
* FindNode( const wxDataViewItem 
& item 
); 
 726     wxDataViewColumn 
*FindColumnForEditing(const wxDataViewItem
& item
, wxDataViewCellMode mode
); 
 728     bool IsCellEditableInMode(const wxDataViewItem
& item
, const wxDataViewColumn 
*col
, wxDataViewCellMode mode
) const; 
 730     void DrawCellBackground( wxDataViewRenderer
* cell
, wxDC
& dc
, const wxRect
& rect 
); 
 733     wxDataViewCtrl             
*m_owner
; 
 737     wxDataViewColumn           
*m_currentCol
; 
 738     unsigned int                m_currentRow
; 
 739     wxDataViewSelection         m_selection
; 
 741     wxDataViewRenameTimer      
*m_renameTimer
; 
 746     bool                        m_currentColSetByKeyboard
; 
 748 #if wxUSE_DRAG_AND_DROP 
 753     wxDataFormat                m_dragFormat
; 
 756     wxDataFormat                m_dropFormat
; 
 758     unsigned int                m_dropHintLine
; 
 759 #endif // wxUSE_DRAG_AND_DROP 
 761     // for double click logic 
 762     unsigned int m_lineLastClicked
, 
 763         m_lineBeforeLastClicked
, 
 764         m_lineSelectSingleOnUp
; 
 766     // the pen used to draw horiz/vertical rules 
 769     // the pen used to draw the expander and the lines 
 772     // This is the tree structure of the model 
 773     wxDataViewTreeNode 
* m_root
; 
 776     // This is the tree node under the cursor 
 777     wxDataViewTreeNode 
* m_underMouse
; 
 779     // The control used for editing or NULL. 
 780     wxWeakRef
<wxWindow
> m_editorCtrl
; 
 782     // Id m_editorCtrl is non-NULL, pointer to the associated renderer. 
 783     wxDataViewRenderer
* m_editorRenderer
; 
 786     DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow
) 
 787     DECLARE_EVENT_TABLE() 
 790 // --------------------------------------------------------- 
 791 // wxGenericDataViewModelNotifier 
 792 // --------------------------------------------------------- 
 794 class wxGenericDataViewModelNotifier
: public wxDataViewModelNotifier
 
 797     wxGenericDataViewModelNotifier( wxDataViewMainWindow 
*mainWindow 
) 
 798         { m_mainWindow 
= mainWindow
; } 
 800     virtual bool ItemAdded( const wxDataViewItem 
& parent
, const wxDataViewItem 
& item 
) 
 801         { return m_mainWindow
->ItemAdded( parent 
, item 
); } 
 802     virtual bool ItemDeleted( const wxDataViewItem 
&parent
, const wxDataViewItem 
&item 
) 
 803         { return m_mainWindow
->ItemDeleted( parent
, item 
); } 
 804     virtual bool ItemChanged( const wxDataViewItem 
& item 
) 
 805         { return m_mainWindow
->ItemChanged(item
);  } 
 806     virtual bool ValueChanged( const wxDataViewItem 
& item 
, unsigned int col 
) 
 807         { return m_mainWindow
->ValueChanged( item
, col 
); } 
 808     virtual bool Cleared() 
 809         { return m_mainWindow
->Cleared(); } 
 810     virtual void Resort() 
 811         { m_mainWindow
->Resort(); } 
 813     wxDataViewMainWindow    
*m_mainWindow
; 
 816 // --------------------------------------------------------- 
 817 // wxDataViewRenderer 
 818 // --------------------------------------------------------- 
 820 IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer
, wxDataViewRendererBase
) 
 822 wxDataViewRenderer::wxDataViewRenderer( const wxString 
&varianttype
, 
 823                                         wxDataViewCellMode mode
, 
 825     wxDataViewCustomRendererBase( varianttype
, mode
, align 
) 
 829     m_ellipsizeMode 
= wxELLIPSIZE_MIDDLE
; 
 833 wxDataViewRenderer::~wxDataViewRenderer() 
 838 wxDC 
*wxDataViewRenderer::GetDC() 
 842         if (GetOwner() == NULL
) 
 844         if (GetOwner()->GetOwner() == NULL
) 
 846         m_dc 
= new wxClientDC( GetOwner()->GetOwner() ); 
 852 void wxDataViewRenderer::SetAlignment( int align 
) 
 857 int wxDataViewRenderer::GetAlignment() const 
 862 // --------------------------------------------------------- 
 863 // wxDataViewCustomRenderer 
 864 // --------------------------------------------------------- 
 866 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer
, wxDataViewRenderer
) 
 868 wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString 
&varianttype
, 
 869                         wxDataViewCellMode mode
, int align 
) : 
 870     wxDataViewRenderer( varianttype
, mode
, align 
) 
 874 // --------------------------------------------------------- 
 875 // wxDataViewTextRenderer 
 876 // --------------------------------------------------------- 
 878 IMPLEMENT_CLASS(wxDataViewTextRenderer
, wxDataViewRenderer
) 
 880 wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString 
&varianttype
, 
 881                                                 wxDataViewCellMode mode
, int align 
) : 
 882     wxDataViewRenderer( varianttype
, mode
, align 
) 
 886 bool wxDataViewTextRenderer::SetValue( const wxVariant 
&value 
) 
 888     m_text 
= value
.GetString(); 
 893 bool wxDataViewTextRenderer::GetValue( wxVariant
& WXUNUSED(value
) ) const 
 898 bool wxDataViewTextRenderer::HasEditorCtrl() const 
 903 wxWindow
* wxDataViewTextRenderer::CreateEditorCtrl( wxWindow 
*parent
, 
 904         wxRect labelRect
, const wxVariant 
&value 
) 
 906     wxTextCtrl
* ctrl 
= new wxTextCtrl( parent
, wxID_ANY
, value
, 
 907                                        wxPoint(labelRect
.x
,labelRect
.y
), 
 908                                        wxSize(labelRect
.width
,labelRect
.height
), 
 909                                        wxTE_PROCESS_ENTER 
); 
 911     // select the text in the control an place the cursor at the end 
 912     ctrl
->SetInsertionPointEnd(); 
 918 bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxWindow 
*editor
, wxVariant 
&value 
) 
 920     wxTextCtrl 
*text 
= (wxTextCtrl
*) editor
; 
 921     value 
= text
->GetValue(); 
 925 bool wxDataViewTextRenderer::Render(wxRect rect
, wxDC 
*dc
, int state
) 
 927     RenderText(m_text
, 0, rect
, dc
, state
); 
 931 wxSize 
wxDataViewTextRenderer::GetSize() const 
 934         return GetTextExtent(m_text
); 
 936         return wxSize(wxDVC_DEFAULT_RENDERER_SIZE
,wxDVC_DEFAULT_RENDERER_SIZE
); 
 939 // --------------------------------------------------------- 
 940 // wxDataViewBitmapRenderer 
 941 // --------------------------------------------------------- 
 943 IMPLEMENT_CLASS(wxDataViewBitmapRenderer
, wxDataViewRenderer
) 
 945 wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString 
&varianttype
, 
 946                                                     wxDataViewCellMode mode
, int align 
) : 
 947     wxDataViewRenderer( varianttype
, mode
, align 
) 
 951 bool wxDataViewBitmapRenderer::SetValue( const wxVariant 
&value 
) 
 953     if (value
.GetType() == wxT("wxBitmap")) 
 955     if (value
.GetType() == wxT("wxIcon")) 
 961 bool wxDataViewBitmapRenderer::GetValue( wxVariant
& WXUNUSED(value
) ) const 
 966 bool wxDataViewBitmapRenderer::Render( wxRect cell
, wxDC 
*dc
, int WXUNUSED(state
) ) 
 969         dc
->DrawBitmap( m_bitmap
, cell
.x
, cell
.y 
); 
 970     else if (m_icon
.IsOk()) 
 971         dc
->DrawIcon( m_icon
, cell
.x
, cell
.y 
); 
 976 wxSize 
wxDataViewBitmapRenderer::GetSize() const 
 979         return wxSize( m_bitmap
.GetWidth(), m_bitmap
.GetHeight() ); 
 980     else if (m_icon
.IsOk()) 
 981         return wxSize( m_icon
.GetWidth(), m_icon
.GetHeight() ); 
 983     return wxSize(wxDVC_DEFAULT_RENDERER_SIZE
,wxDVC_DEFAULT_RENDERER_SIZE
); 
 986 // --------------------------------------------------------- 
 987 // wxDataViewToggleRenderer 
 988 // --------------------------------------------------------- 
 990 IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer
, wxDataViewRenderer
) 
 992 wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString 
&varianttype
, 
 993                         wxDataViewCellMode mode
, int align 
) : 
 994     wxDataViewRenderer( varianttype
, mode
, align 
) 
 999 bool wxDataViewToggleRenderer::SetValue( const wxVariant 
&value 
) 
1001     m_toggle 
= value
.GetBool(); 
1006 bool wxDataViewToggleRenderer::GetValue( wxVariant 
&WXUNUSED(value
) ) const 
1011 bool wxDataViewToggleRenderer::Render( wxRect cell
, wxDC 
*dc
, int WXUNUSED(state
) ) 
1015         flags 
|= wxCONTROL_CHECKED
; 
1016     if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE 
|| 
1017         GetEnabled() == false) 
1018         flags 
|= wxCONTROL_DISABLED
; 
1020     // Ensure that the check boxes always have at least the minimal required 
1021     // size, otherwise DrawCheckBox() doesn't really work well. If this size is 
1022     // greater than the cell size, the checkbox will be truncated but this is a 
1024     wxSize size 
= cell
.GetSize(); 
1025     size
.IncTo(GetSize()); 
1028     wxRendererNative::Get().DrawCheckBox( 
1029             GetOwner()->GetOwner(), 
1037 bool wxDataViewToggleRenderer::WXActivateCell(const wxRect
& WXUNUSED(cell
), 
1038                                               wxDataViewModel 
*model
, 
1039                                               const wxDataViewItem
& item
, 
1041                                               const wxMouseEvent 
*mouseEvent
) 
1045         // only react to clicks directly on the checkbox, not elsewhere in the same cell: 
1046         if ( !wxRect(GetSize()).Contains(mouseEvent
->GetPosition()) ) 
1050     model
->ChangeValue(!m_toggle
, item
, col
); 
1054 wxSize 
wxDataViewToggleRenderer::GetSize() const 
1056     // the window parameter is not used by GetCheckBoxSize() so it's 
1057     // safe to pass NULL 
1058     return wxRendererNative::Get().GetCheckBoxSize(NULL
); 
1061 // --------------------------------------------------------- 
1062 // wxDataViewProgressRenderer 
1063 // --------------------------------------------------------- 
1065 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer
, wxDataViewRenderer
) 
1067 wxDataViewProgressRenderer::wxDataViewProgressRenderer( const wxString 
&label
, 
1068     const wxString 
&varianttype
, wxDataViewCellMode mode
, int align 
) : 
1069     wxDataViewRenderer( varianttype
, mode
, align 
) 
1075 bool wxDataViewProgressRenderer::SetValue( const wxVariant 
&value 
) 
1077     m_value 
= (long) value
; 
1079     if (m_value 
< 0) m_value 
= 0; 
1080     if (m_value 
> 100) m_value 
= 100; 
1085 bool wxDataViewProgressRenderer::GetValue( wxVariant 
&value 
) const 
1087     value 
= (long) m_value
; 
1092 wxDataViewProgressRenderer::Render(wxRect rect
, wxDC 
*dc
, int WXUNUSED(state
)) 
1094     // deflate the rect to leave a small border between bars in adjacent rows 
1095     wxRect bar 
= rect
.Deflate(0, 1); 
1097     dc
->SetBrush( *wxTRANSPARENT_BRUSH 
); 
1098     dc
->SetPen( *wxBLACK_PEN 
); 
1099     dc
->DrawRectangle( bar 
); 
1101     bar
.width 
= (int)(bar
.width 
* m_value 
/ 100.); 
1102     dc
->SetPen( *wxTRANSPARENT_PEN 
); 
1104     const wxDataViewItemAttr
& attr 
= GetAttr(); 
1105     dc
->SetBrush( attr
.HasColour() ? wxBrush(attr
.GetColour()) 
1107     dc
->DrawRectangle( bar 
); 
1112 wxSize 
wxDataViewProgressRenderer::GetSize() const 
1114     return wxSize(40,12); 
1117 // --------------------------------------------------------- 
1118 // wxDataViewIconTextRenderer 
1119 // --------------------------------------------------------- 
1121 IMPLEMENT_CLASS(wxDataViewIconTextRenderer
, wxDataViewRenderer
) 
1123 wxDataViewIconTextRenderer::wxDataViewIconTextRenderer( 
1124 const wxString 
&varianttype
, wxDataViewCellMode mode
, int align 
) : 
1125     wxDataViewRenderer( varianttype
, mode
, align 
) 
1128     SetAlignment(align
); 
1131 bool wxDataViewIconTextRenderer::SetValue( const wxVariant 
&value 
) 
1137 bool wxDataViewIconTextRenderer::GetValue( wxVariant
& WXUNUSED(value
) ) const 
1142 bool wxDataViewIconTextRenderer::Render(wxRect rect
, wxDC 
*dc
, int state
) 
1146     const wxIcon
& icon 
= m_value
.GetIcon(); 
1149         dc
->DrawIcon(icon
, rect
.x
, rect
.y 
+ (rect
.height 
- icon
.GetHeight())/2); 
1150         xoffset 
= icon
.GetWidth()+4; 
1153     RenderText(m_value
.GetText(), xoffset
, rect
, dc
, state
); 
1158 wxSize 
wxDataViewIconTextRenderer::GetSize() const 
1160     if (!m_value
.GetText().empty()) 
1162         wxSize size 
= GetTextExtent(m_value
.GetText()); 
1164         if (m_value
.GetIcon().IsOk()) 
1165             size
.x 
+= m_value
.GetIcon().GetWidth() + 4; 
1168     return wxSize(80,20); 
1171 wxWindow
* wxDataViewIconTextRenderer::CreateEditorCtrl(wxWindow 
*parent
, wxRect labelRect
, const wxVariant
& value
) 
1173     wxDataViewIconText iconText
; 
1176     wxString text 
= iconText
.GetText(); 
1178     // adjust the label rect to take the width of the icon into account 
1179     if (iconText
.GetIcon().IsOk()) 
1181         int w 
= iconText
.GetIcon().GetWidth() + 4; 
1183         labelRect
.width 
-= w
; 
1186     wxTextCtrl
* ctrl 
= new wxTextCtrl( parent
, wxID_ANY
, text
, 
1187                                        wxPoint(labelRect
.x
,labelRect
.y
), 
1188                                        wxSize(labelRect
.width
,labelRect
.height
), 
1189                                        wxTE_PROCESS_ENTER 
); 
1191     // select the text in the control an place the cursor at the end 
1192     ctrl
->SetInsertionPointEnd(); 
1198 bool wxDataViewIconTextRenderer::GetValueFromEditorCtrl( wxWindow 
*editor
, wxVariant
& value 
) 
1200     wxTextCtrl 
*text 
= (wxTextCtrl
*) editor
; 
1202     // The icon can't be edited so get its old value and reuse it. 
1204     wxDataViewColumn
* const col 
= GetOwner(); 
1205     GetView()->GetModel()->GetValue(valueOld
, m_item
, col
->GetModelColumn()); 
1207     wxDataViewIconText iconText
; 
1208     iconText 
<< valueOld
; 
1210     // But replace the text with the value entered by user. 
1211     iconText
.SetText(text
->GetValue()); 
1217 //----------------------------------------------------------------------------- 
1218 // wxDataViewDropTarget 
1219 //----------------------------------------------------------------------------- 
1221 #if wxUSE_DRAG_AND_DROP 
1223 class wxBitmapCanvas
: public wxWindow
 
1226     wxBitmapCanvas( wxWindow 
*parent
, const wxBitmap 
&bitmap
, const wxSize 
&size 
) : 
1227     wxWindow( parent
, wxID_ANY
, wxPoint(0,0), size 
) 
1230         Connect( wxEVT_PAINT
, wxPaintEventHandler(wxBitmapCanvas::OnPaint
) ); 
1233     void OnPaint( wxPaintEvent 
&WXUNUSED(event
) ) 
1236         dc
.DrawBitmap( m_bitmap
, 0, 0); 
1242 class wxDataViewDropSource
: public wxDropSource
 
1245     wxDataViewDropSource( wxDataViewMainWindow 
*win
, unsigned int row 
) : 
1253     ~wxDataViewDropSource() 
1258     virtual bool GiveFeedback( wxDragResult 
WXUNUSED(effect
) ) 
1260         wxPoint pos 
= wxGetMousePosition(); 
1264             int liney 
= m_win
->GetLineStart( m_row 
); 
1266             m_win
->GetOwner()->CalcUnscrolledPosition( 0, liney
, NULL
, &liney 
); 
1267             m_win
->ClientToScreen( &linex
, &liney 
); 
1268             m_dist_x 
= pos
.x 
- linex
; 
1269             m_dist_y 
= pos
.y 
- liney
; 
1272             wxBitmap ib 
= m_win
->CreateItemBitmap( m_row
, indent 
); 
1274             m_hint 
= new wxFrame( m_win
->GetParent(), wxID_ANY
, wxEmptyString
, 
1275                                         wxPoint(pos
.x 
- m_dist_x
, pos
.y 
+ 5 ), 
1277                                         wxFRAME_TOOL_WINDOW 
| 
1278                                         wxFRAME_FLOAT_ON_PARENT 
| 
1279                                         wxFRAME_NO_TASKBAR 
| 
1281             new wxBitmapCanvas( m_hint
, ib
, ib
.GetSize() ); 
1286             m_hint
->Move( pos
.x 
- m_dist_x
, pos
.y 
+ 5  ); 
1287             m_hint
->SetTransparent( 128 ); 
1293     wxDataViewMainWindow   
*m_win
; 
1296     int m_dist_x
,m_dist_y
; 
1300 class wxDataViewDropTarget
: public wxDropTarget
 
1303     wxDataViewDropTarget( wxDataObject 
*obj
, wxDataViewMainWindow 
*win 
) : 
1309     virtual wxDragResult 
OnDragOver( wxCoord x
, wxCoord y
, wxDragResult def 
) 
1311         wxDataFormat format 
= GetMatchingPair(); 
1312         if (format 
== wxDF_INVALID
) 
1314         return m_win
->OnDragOver( format
, x
, y
, def
); 
1317     virtual bool OnDrop( wxCoord x
, wxCoord y 
) 
1319         wxDataFormat format 
= GetMatchingPair(); 
1320         if (format 
== wxDF_INVALID
) 
1322         return m_win
->OnDrop( format
, x
, y 
); 
1325     virtual wxDragResult 
OnData( wxCoord x
, wxCoord y
, wxDragResult def 
) 
1327         wxDataFormat format 
= GetMatchingPair(); 
1328         if (format 
== wxDF_INVALID
) 
1332         return m_win
->OnData( format
, x
, y
, def 
); 
1335     virtual void OnLeave() 
1336         { m_win
->OnLeave(); } 
1338     wxDataViewMainWindow   
*m_win
; 
1341 #endif // wxUSE_DRAG_AND_DROP 
1343 //----------------------------------------------------------------------------- 
1344 // wxDataViewRenameTimer 
1345 //----------------------------------------------------------------------------- 
1347 wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow 
*owner 
) 
1352 void wxDataViewRenameTimer::Notify() 
1354     m_owner
->OnRenameTimer(); 
1357 //----------------------------------------------------------------------------- 
1358 // wxDataViewMainWindow 
1359 //----------------------------------------------------------------------------- 
1361 // The tree building helper, declared firstly 
1362 static void BuildTreeHelper( const wxDataViewModel 
* model
,  const wxDataViewItem 
& item
, 
1363                              wxDataViewTreeNode 
* node
); 
1365 int LINKAGEMODE 
wxDataViewSelectionCmp( unsigned int row1
, unsigned int row2 
) 
1367     if (row1 
> row2
) return 1; 
1368     if (row1 
== row2
) return 0; 
1372 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow
, wxWindow
) 
1374 BEGIN_EVENT_TABLE(wxDataViewMainWindow
,wxWindow
) 
1375     EVT_PAINT         (wxDataViewMainWindow::OnPaint
) 
1376     EVT_MOUSE_EVENTS  (wxDataViewMainWindow::OnMouse
) 
1377     EVT_SET_FOCUS     (wxDataViewMainWindow::OnSetFocus
) 
1378     EVT_KILL_FOCUS    (wxDataViewMainWindow::OnKillFocus
) 
1379     EVT_CHAR_HOOK     (wxDataViewMainWindow::OnCharHook
) 
1380     EVT_CHAR          (wxDataViewMainWindow::OnChar
) 
1383 wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl 
*parent
, wxWindowID id
, 
1384     const wxPoint 
&pos
, const wxSize 
&size
, const wxString 
&name 
) : 
1385     wxWindow( parent
, id
, pos
, size
, wxWANTS_CHARS
|wxBORDER_NONE
, name 
), 
1386     m_selection( wxDataViewSelectionCmp 
) 
1391     m_editorRenderer 
= NULL
; 
1393     m_lastOnSame 
= false; 
1394     m_renameTimer 
= new wxDataViewRenameTimer( this ); 
1396     // TODO: user better initial values/nothing selected 
1397     m_currentCol 
= NULL
; 
1398     m_currentColSetByKeyboard 
= false; 
1399     m_useCellFocus 
= false; 
1400     m_currentRow 
= (unsigned)-1; 
1403     // We would like to use the same line height that Explorer uses. This is 
1404     // different from standard ListView control since Vista. 
1405     if ( wxGetWinVersion() >= wxWinVersion_Vista 
) 
1406         m_lineHeight 
= wxMax(16, GetCharHeight()) + 6; // 16 = mini icon height 
1409         m_lineHeight 
= wxMax(16, GetCharHeight()) + 1; // 16 = mini icon height 
1411 #if wxUSE_DRAG_AND_DROP 
1413     m_dragStart 
= wxPoint(0,0); 
1415     m_dragEnabled 
= false; 
1416     m_dropEnabled 
= false; 
1418     m_dropHintLine 
= (unsigned int) -1; 
1419 #endif // wxUSE_DRAG_AND_DROP 
1421     m_lineLastClicked 
= (unsigned int) -1; 
1422     m_lineBeforeLastClicked 
= (unsigned int) -1; 
1423     m_lineSelectSingleOnUp 
= (unsigned int) -1; 
1427     SetBackgroundColour( *wxWHITE 
); 
1429     SetBackgroundStyle(wxBG_STYLE_CUSTOM
); 
1431     m_penRule 
= wxPen(GetRuleColour()); 
1433     // compose a pen whichcan draw black lines 
1434     // TODO: maybe there is something system colour to use 
1435     m_penExpander 
= wxPen(wxColour(0,0,0)); 
1437     m_root 
= wxDataViewTreeNode::CreateRootNode(); 
1439     // Make m_count = -1 will cause the class recaculate the real displaying number of rows. 
1441     m_underMouse 
= NULL
; 
1445 wxDataViewMainWindow::~wxDataViewMainWindow() 
1448     delete m_renameTimer
; 
1452 #if wxUSE_DRAG_AND_DROP 
1453 bool wxDataViewMainWindow::EnableDragSource( const wxDataFormat 
&format 
) 
1455     m_dragFormat 
= format
; 
1456     m_dragEnabled 
= format 
!= wxDF_INVALID
; 
1461 bool wxDataViewMainWindow::EnableDropTarget( const wxDataFormat 
&format 
) 
1463     m_dropFormat 
= format
; 
1464     m_dropEnabled 
= format 
!= wxDF_INVALID
; 
1467         SetDropTarget( new wxDataViewDropTarget( new wxCustomDataObject( format 
), this ) ); 
1472 void wxDataViewMainWindow::RemoveDropHint() 
1477             RefreshRow( m_dropHintLine 
); 
1478             m_dropHintLine 
= (unsigned int) -1; 
1482 wxDragResult 
wxDataViewMainWindow::OnDragOver( wxDataFormat format
, wxCoord x
, 
1483                                                wxCoord y
, wxDragResult def 
) 
1487     m_owner
->CalcUnscrolledPosition( xx
, yy
, &xx
, &yy 
); 
1488     unsigned int row 
= GetLineAt( yy 
); 
1490     if ((row 
>= GetRowCount()) || (xx 
> GetEndOfLastCol())) 
1496     wxDataViewItem item 
= GetItemByRow( row 
); 
1498     wxDataViewModel 
*model 
= GetModel(); 
1500     wxDataViewEvent 
event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE
, m_owner
->GetId() ); 
1501     event
.SetEventObject( m_owner 
); 
1502     event
.SetItem( item 
); 
1503     event
.SetModel( model 
); 
1504     event
.SetDataFormat( format 
); 
1505     event
.SetDropEffect( def 
); 
1506     if (!m_owner
->HandleWindowEvent( event 
)) 
1512     if (!event
.IsAllowed()) 
1519     if (m_dropHint 
&& (row 
!= m_dropHintLine
)) 
1520         RefreshRow( m_dropHintLine 
); 
1522     m_dropHintLine 
= row
; 
1528 bool wxDataViewMainWindow::OnDrop( wxDataFormat format
, wxCoord x
, wxCoord y 
) 
1534     m_owner
->CalcUnscrolledPosition( xx
, yy
, &xx
, &yy 
); 
1535     unsigned int row 
= GetLineAt( yy 
); 
1537     if ((row 
>= GetRowCount()) || (xx 
> GetEndOfLastCol())) 
1540     wxDataViewItem item 
= GetItemByRow( row 
); 
1542     wxDataViewModel 
*model 
= GetModel(); 
1544     wxDataViewEvent 
event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE
, m_owner
->GetId() ); 
1545     event
.SetEventObject( m_owner 
); 
1546     event
.SetItem( item 
); 
1547     event
.SetModel( model 
); 
1548     event
.SetDataFormat( format 
); 
1549     if (!m_owner
->HandleWindowEvent( event 
)) 
1552     if (!event
.IsAllowed()) 
1558 wxDragResult 
wxDataViewMainWindow::OnData( wxDataFormat format
, wxCoord x
, wxCoord y
, 
1563     m_owner
->CalcUnscrolledPosition( xx
, yy
, &xx
, &yy 
); 
1564     unsigned int row 
= GetLineAt( yy 
); 
1566     if ((row 
>= GetRowCount()) || (xx 
> GetEndOfLastCol())) 
1569     wxDataViewItem item 
= GetItemByRow( row 
); 
1571     wxDataViewModel 
*model 
= GetModel(); 
1573     wxCustomDataObject 
*obj 
= (wxCustomDataObject 
*) GetDropTarget()->GetDataObject(); 
1575     wxDataViewEvent 
event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP
, m_owner
->GetId() ); 
1576     event
.SetEventObject( m_owner 
); 
1577     event
.SetItem( item 
); 
1578     event
.SetModel( model 
); 
1579     event
.SetDataFormat( format 
); 
1580     event
.SetDataSize( obj
->GetSize() ); 
1581     event
.SetDataBuffer( obj
->GetData() ); 
1582     event
.SetDropEffect( def 
); 
1583     if (!m_owner
->HandleWindowEvent( event 
)) 
1586     if (!event
.IsAllowed()) 
1592 void wxDataViewMainWindow::OnLeave() 
1597 wxBitmap 
wxDataViewMainWindow::CreateItemBitmap( unsigned int row
, int &indent 
) 
1599     int height 
= GetLineHeight( row 
); 
1601     unsigned int cols 
= GetOwner()->GetColumnCount(); 
1603     for (col 
= 0; col 
< cols
; col
++) 
1605         wxDataViewColumn 
*column 
= GetOwner()->GetColumnAt(col
); 
1606         if (column
->IsHidden()) 
1607             continue;      // skip it! 
1608         width 
+= column
->GetWidth(); 
1614         wxDataViewTreeNode 
*node 
= GetTreeNodeByRow(row
); 
1615         indent 
= GetOwner()->GetIndent() * node
->GetIndentLevel(); 
1616         indent 
= indent 
+ m_lineHeight
; 
1617             // try to use the m_lineHeight as the expander space 
1621     wxBitmap 
bitmap( width
, height 
); 
1622     wxMemoryDC 
dc( bitmap 
); 
1623     dc
.SetFont( GetFont() ); 
1624     dc
.SetPen( *wxBLACK_PEN 
); 
1625     dc
.SetBrush( *wxWHITE_BRUSH 
); 
1626     dc
.DrawRectangle( 0,0,width
,height 
); 
1628     wxDataViewModel 
*model 
= m_owner
->GetModel(); 
1630     wxDataViewColumn 
* const 
1631         expander 
= GetExpanderColumnOrFirstOne(GetOwner()); 
1634     for (col 
= 0; col 
< cols
; col
++) 
1636         wxDataViewColumn 
*column 
= GetOwner()->GetColumnAt( col 
); 
1637         wxDataViewRenderer 
*cell 
= column
->GetRenderer(); 
1639         if (column
->IsHidden()) 
1640             continue;       // skip it! 
1642         width 
= column
->GetWidth(); 
1644         if (column 
== expander
) 
1647         wxDataViewItem item 
= GetItemByRow( row 
); 
1648         cell
->PrepareForItem(model
, item
, column
->GetModelColumn()); 
1650         wxRect 
item_rect(x
, 0, width
, height
); 
1651         item_rect
.Deflate(PADDING_RIGHTLEFT
, 0); 
1653         // dc.SetClippingRegion( item_rect ); 
1654         cell
->WXCallRender(item_rect
, &dc
, 0); 
1655         // dc.DestroyClippingRegion(); 
1663 #endif // wxUSE_DRAG_AND_DROP 
1666 // Draw focus rect for individual cell. Unlike native focus rect, we render 
1667 // this in foreground text color (typically white) to enhance contrast and 
1669 static void DrawSelectedCellFocusRect(wxDC
& dc
, const wxRect
& rect
) 
1671     // (This code is based on wxRendererGeneric::DrawFocusRect and modified.) 
1673     // draw the pixels manually because the "dots" in wxPen with wxDOT style 
1674     // may be short traits and not really dots 
1676     // note that to behave in the same manner as DrawRect(), we must exclude 
1677     // the bottom and right borders from the rectangle 
1678     wxCoord x1 
= rect
.GetLeft(), 
1680             x2 
= rect
.GetRight(), 
1681             y2 
= rect
.GetBottom(); 
1683     wxDCPenChanger 
pen(dc
, wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
)); 
1686     for ( z 
= x1 
+ 1; z 
< x2
; z 
+= 2 ) 
1687         dc
.DrawPoint(z
, rect
.GetTop()); 
1689     wxCoord shift 
= z 
== x2 
? 0 : 1; 
1690     for ( z 
= y1 
+ shift
; z 
< y2
; z 
+= 2 ) 
1691         dc
.DrawPoint(x2
, z
); 
1693     shift 
= z 
== y2 
? 0 : 1; 
1694     for ( z 
= x2 
- shift
; z 
> x1
; z 
-= 2 ) 
1695         dc
.DrawPoint(z
, y2
); 
1697     shift 
= z 
== x1 
? 0 : 1; 
1698     for ( z 
= y2 
- shift
; z 
> y1
; z 
-= 2 ) 
1699         dc
.DrawPoint(x1
, z
); 
1703 void wxDataViewMainWindow::OnPaint( wxPaintEvent 
&WXUNUSED(event
) ) 
1705     wxDataViewModel 
*model 
= GetModel(); 
1706     wxAutoBufferedPaintDC 
dc( this ); 
1708     dc
.SetBrush(GetOwner()->GetBackgroundColour()); 
1709     dc
.SetPen( *wxTRANSPARENT_PEN 
); 
1710     dc
.DrawRectangle(GetClientSize()); 
1714         // No items to draw. 
1719     GetOwner()->PrepareDC( dc 
); 
1720     dc
.SetFont( GetFont() ); 
1722     wxRect update 
= GetUpdateRegion().GetBox(); 
1723     m_owner
->CalcUnscrolledPosition( update
.x
, update
.y
, &update
.x
, &update
.y 
); 
1725     // compute which items needs to be redrawn 
1726     unsigned int item_start 
= GetLineAt( wxMax(0,update
.y
) ); 
1727     unsigned int item_count 
= 
1728         wxMin( (int)(  GetLineAt( wxMax(0,update
.y
+update
.height
) ) - item_start 
+ 1), 
1729             (int)(GetRowCount( ) - item_start
)); 
1730     unsigned int item_last 
= item_start 
+ item_count
; 
1732     // Send the event to wxDataViewCtrl itself. 
1733     wxWindow 
* const parent 
= GetParent(); 
1734     wxDataViewEvent 
cache_event(wxEVT_COMMAND_DATAVIEW_CACHE_HINT
, parent
->GetId()); 
1735     cache_event
.SetEventObject(parent
); 
1736     cache_event
.SetCache(item_start
, item_last 
- 1); 
1737     parent
->ProcessWindowEvent(cache_event
); 
1739     // compute which columns needs to be redrawn 
1740     unsigned int cols 
= GetOwner()->GetColumnCount(); 
1743         // we assume that we have at least one column below and painting an 
1744         // empty control is unnecessary anyhow 
1748     unsigned int col_start 
= 0; 
1749     unsigned int x_start
; 
1750     for (x_start 
= 0; col_start 
< cols
; col_start
++) 
1752         wxDataViewColumn 
*col 
= GetOwner()->GetColumnAt(col_start
); 
1753         if (col
->IsHidden()) 
1754             continue;      // skip it! 
1756         unsigned int w 
= col
->GetWidth(); 
1757         if (x_start
+w 
>= (unsigned int)update
.x
) 
1763     unsigned int col_last 
= col_start
; 
1764     unsigned int x_last 
= x_start
; 
1765     for (; col_last 
< cols
; col_last
++) 
1767         wxDataViewColumn 
*col 
= GetOwner()->GetColumnAt(col_last
); 
1768         if (col
->IsHidden()) 
1769             continue;      // skip it! 
1771         if (x_last 
> (unsigned int)update
.GetRight()) 
1774         x_last 
+= col
->GetWidth(); 
1777     // Draw background of alternate rows specially if required 
1778     if ( m_owner
->HasFlag(wxDV_ROW_LINES
) ) 
1780         wxColour altRowColour 
= m_owner
->m_alternateRowColour
; 
1781         if ( !altRowColour
.IsOk() ) 
1783             // Determine the alternate rows colour automatically from the 
1784             // background colour. 
1785             const wxColour bgColour 
= m_owner
->GetBackgroundColour(); 
1787             // Depending on the background, alternate row color 
1788             // will be 3% more dark or 50% brighter. 
1789             int alpha 
= bgColour
.GetRGB() > 0x808080 ? 97 : 150; 
1790             altRowColour 
= bgColour
.ChangeLightness(alpha
); 
1793         dc
.SetPen(*wxTRANSPARENT_PEN
); 
1794         dc
.SetBrush(wxBrush(altRowColour
)); 
1796         for (unsigned int item 
= item_start
; item 
< item_last
; item
++) 
1800                 dc
.DrawRectangle(x_start
, 
1802                                  GetClientSize().GetWidth(), 
1803                                  GetLineHeight(item
)); 
1808     // Draw horizontal rules if required 
1809     if ( m_owner
->HasFlag(wxDV_HORIZ_RULES
) ) 
1811         dc
.SetPen(m_penRule
); 
1812         dc
.SetBrush(*wxTRANSPARENT_BRUSH
); 
1814         for (unsigned int i 
= item_start
; i 
<= item_last
; i
++) 
1816             int y 
= GetLineStart( i 
); 
1817             dc
.DrawLine(x_start
, y
, x_last
, y
); 
1821     // Draw vertical rules if required 
1822     if ( m_owner
->HasFlag(wxDV_VERT_RULES
) ) 
1824         dc
.SetPen(m_penRule
); 
1825         dc
.SetBrush(*wxTRANSPARENT_BRUSH
); 
1827         // NB: Vertical rules are drawn in the last pixel of a column so that 
1828         //     they align perfectly with native MSW wxHeaderCtrl as well as for 
1829         //     consistency with MSW native list control. There's no vertical 
1830         //     rule at the most-left side of the control. 
1832         int x 
= x_start 
- 1; 
1833         for (unsigned int i 
= col_start
; i 
< col_last
; i
++) 
1835             wxDataViewColumn 
*col 
= GetOwner()->GetColumnAt(i
); 
1836             if (col
->IsHidden()) 
1837                 continue;       // skip it 
1839             x 
+= col
->GetWidth(); 
1841             dc
.DrawLine(x
, GetLineStart( item_start 
), 
1842                         x
, GetLineStart( item_last 
) ); 
1846     // redraw the background for the items which are selected/current 
1847     for (unsigned int item 
= item_start
; item 
< item_last
; item
++) 
1849         bool selected 
= m_selection
.Index( item 
) != wxNOT_FOUND
; 
1851         if (selected 
|| item 
== m_currentRow
) 
1853             wxRect 
rect( x_start
, GetLineStart( item 
), 
1854                          x_last 
- x_start
, GetLineHeight( item 
) ); 
1856             // draw selection and whole-item focus: 
1859                 int flags 
= wxCONTROL_SELECTED
; 
1861                     flags 
|= wxCONTROL_FOCUSED
; 
1863                 wxRendererNative::Get().DrawItemSelectionRect
 
1872             // draw keyboard focus rect if applicable 
1873             if ( item 
== m_currentRow 
&& m_hasFocus 
) 
1875                 bool renderColumnFocus 
= false; 
1877                 if ( m_useCellFocus 
&& m_currentCol 
&& m_currentColSetByKeyboard 
) 
1879                     renderColumnFocus 
= true; 
1881                     // If this is container node without columns, render full-row focus: 
1884                         wxDataViewTreeNode 
*node 
= GetTreeNodeByRow(item
); 
1885                         if ( node
->HasChildren() && !model
->HasContainerColumns(node
->GetItem()) ) 
1886                             renderColumnFocus 
= false; 
1890                 if ( renderColumnFocus 
) 
1892                     for ( unsigned int i 
= col_start
; i 
< col_last
; i
++ ) 
1894                         wxDataViewColumn 
*col 
= GetOwner()->GetColumnAt(i
); 
1895                         if ( col
->IsHidden() ) 
1898                         rect
.width 
= col
->GetWidth(); 
1900                         if ( col 
== m_currentCol 
) 
1902                             // make the rect more visible by adding a small 
1903                             // margin around it: 
1908                                 // DrawFocusRect() uses XOR and is all but 
1909                                 // invisible against dark-blue background. Use 
1910                                 // the same color used for selected text. 
1911                                 DrawSelectedCellFocusRect(dc
, rect
); 
1915                                 wxRendererNative::Get().DrawFocusRect
 
1926                         rect
.x 
+= rect
.width
; 
1931                     // render focus rectangle for the whole row 
1932                     wxRendererNative::Get().DrawFocusRect
 
1937                                             selected 
? (int)wxCONTROL_SELECTED 
: 0 
1944 #if wxUSE_DRAG_AND_DROP 
1947         wxRect 
rect( x_start
, GetLineStart( m_dropHintLine 
), 
1948                      x_last 
- x_start
, GetLineHeight( m_dropHintLine 
) ); 
1949         dc
.SetPen( *wxBLACK_PEN 
); 
1950         dc
.SetBrush( *wxTRANSPARENT_BRUSH 
); 
1951         dc
.DrawRectangle( rect 
); 
1953 #endif // wxUSE_DRAG_AND_DROP 
1955     wxDataViewColumn 
* const 
1956         expander 
= GetExpanderColumnOrFirstOne(GetOwner()); 
1958     // redraw all cells for all rows which must be repainted and all columns 
1960     cell_rect
.x 
= x_start
; 
1961     for (unsigned int i 
= col_start
; i 
< col_last
; i
++) 
1963         wxDataViewColumn 
*col 
= GetOwner()->GetColumnAt( i 
); 
1964         wxDataViewRenderer 
*cell 
= col
->GetRenderer(); 
1965         cell_rect
.width 
= col
->GetWidth(); 
1967         if ( col
->IsHidden() || cell_rect
.width 
<= 0 ) 
1968             continue;       // skip it! 
1970         for (unsigned int item 
= item_start
; item 
< item_last
; item
++) 
1972             // get the cell value and set it into the renderer 
1973             wxDataViewTreeNode 
*node 
= NULL
; 
1974             wxDataViewItem dataitem
; 
1976             if (!IsVirtualList()) 
1978                 node 
= GetTreeNodeByRow(item
); 
1982                 dataitem 
= node
->GetItem(); 
1984                 // Skip all columns of "container" rows except the expander 
1985                 // column itself unless HasContainerColumns() overrides this. 
1986                 if ( col 
!= expander 
&& 
1987                         model
->IsContainer(dataitem
) && 
1988                             !model
->HasContainerColumns(dataitem
) ) 
1993                 dataitem 
= wxDataViewItem( wxUIntToPtr(item
+1) ); 
1996             cell
->PrepareForItem(model
, dataitem
, col
->GetModelColumn()); 
1999             cell_rect
.y 
= GetLineStart( item 
); 
2000             cell_rect
.height 
= GetLineHeight( item 
); 
2002             // draw the background 
2003             bool selected 
= m_selection
.Index( item 
) != wxNOT_FOUND
; 
2005                 DrawCellBackground( cell
, dc
, cell_rect 
); 
2007             // deal with the expander 
2009             if ((!IsList()) && (col 
== expander
)) 
2011                 // Calculate the indent first 
2012                 indent 
= GetOwner()->GetIndent() * node
->GetIndentLevel(); 
2014                 // we reserve m_lineHeight of horizontal space for the expander 
2015                 // but leave EXPANDER_MARGIN around the expander itself 
2016                 int exp_x 
= cell_rect
.x 
+ indent 
+ EXPANDER_MARGIN
; 
2018                 indent 
+= m_lineHeight
; 
2020                 // draw expander if needed and visible 
2021                 if ( node
->HasChildren() && exp_x 
< cell_rect
.GetRight() ) 
2023                     dc
.SetPen( m_penExpander 
); 
2024                     dc
.SetBrush( wxNullBrush 
); 
2026                     int exp_size 
= m_lineHeight 
- 2*EXPANDER_MARGIN
; 
2027                     int exp_y 
= cell_rect
.y 
+ (cell_rect
.height 
- exp_size
)/2 
2028                                    + EXPANDER_MARGIN 
- EXPANDER_OFFSET
; 
2030                     const wxRect 
rect(exp_x
, exp_y
, exp_size
, exp_size
); 
2033                     if ( m_underMouse 
== node 
) 
2034                         flag 
|= wxCONTROL_CURRENT
; 
2035                     if ( node
->IsOpen() ) 
2036                         flag 
|= wxCONTROL_EXPANDED
; 
2038                     // ensure that we don't overflow the cell (which might 
2039                     // happen if the column is very narrow) 
2040                     wxDCClipper 
clip(dc
, cell_rect
); 
2042                     wxRendererNative::Get().DrawTreeItemButton( this, dc
, rect
, flag
); 
2045                 // force the expander column to left-center align 
2046                 cell
->SetAlignment( wxALIGN_CENTER_VERTICAL 
); 
2049             wxRect item_rect 
= cell_rect
; 
2050             item_rect
.Deflate(PADDING_RIGHTLEFT
, 0); 
2052             // account for the tree indent (harmless if we're not indented) 
2053             item_rect
.x 
+= indent
; 
2054             item_rect
.width 
-= indent
; 
2056             if ( item_rect
.width 
<= 0 ) 
2060             if (m_hasFocus 
&& selected
) 
2061                 state 
|= wxDATAVIEW_CELL_SELECTED
; 
2063             // TODO: it would be much more efficient to create a clipping 
2064             //       region for the entire column being rendered (in the OnPaint 
2065             //       of wxDataViewMainWindow) instead of a single clip region for 
2066             //       each cell. However it would mean that each renderer should 
2067             //       respect the given wxRect's top & bottom coords, eventually 
2068             //       violating only the left & right coords - however the user can 
2069             //       make its own renderer and thus we cannot be sure of that. 
2070             wxDCClipper 
clip(dc
, item_rect
); 
2072             cell
->WXCallRender(item_rect
, &dc
, state
); 
2075         cell_rect
.x 
+= cell_rect
.width
; 
2080 void wxDataViewMainWindow::DrawCellBackground( wxDataViewRenderer
* cell
, wxDC
& dc
, const wxRect
& rect 
) 
2082     wxRect 
rectBg( rect 
); 
2084     // don't overlap the horizontal rules 
2085     if ( m_owner
->HasFlag(wxDV_HORIZ_RULES
) ) 
2091     // don't overlap the vertical rules 
2092     if ( m_owner
->HasFlag(wxDV_VERT_RULES
) ) 
2098     cell
->RenderBackground(&dc
, rectBg
); 
2101 void wxDataViewMainWindow::OnRenameTimer() 
2103     // We have to call this here because changes may just have 
2104     // been made and no screen update taken place. 
2107         // TODO: use wxTheApp->SafeYieldFor(NULL, wxEVT_CATEGORY_UI) instead 
2108         //       (needs to be tested!) 
2112     wxDataViewItem item 
= GetItemByRow( m_currentRow 
); 
2114     StartEditing( item
, m_currentCol 
); 
2118 wxDataViewMainWindow::StartEditing(const wxDataViewItem
& item
, 
2119                                    const wxDataViewColumn
* col
) 
2121     wxDataViewRenderer
* renderer 
= col
->GetRenderer(); 
2122     if ( !IsCellEditableInMode(item
, col
, wxDATAVIEW_CELL_EDITABLE
) ) 
2125     const wxRect itemRect 
= GetItemRect(item
, col
); 
2126     if ( renderer
->StartEditing(item
, itemRect
) ) 
2128         // Save the renderer to be able to finish/cancel editing it later and 
2129         // save the control to be able to detect if we're still editing it. 
2130         m_editorRenderer 
= renderer
; 
2131         m_editorCtrl 
= renderer
->GetEditorCtrl(); 
2135 //----------------------------------------------------------------------------- 
2136 // Helper class for do operation on the tree node 
2137 //----------------------------------------------------------------------------- 
2142     virtual ~DoJob() { } 
2144     // The return value control how the tree-walker tranverse the tree 
2147         DONE
,          // Job done, stop traversing and return 
2148         SKIP_SUBTREE
,  // Ignore the current node's subtree and continue 
2149         CONTINUE       
// Job not done, continue 
2152     virtual int operator() ( wxDataViewTreeNode 
* node 
) = 0; 
2155 bool Walker( wxDataViewTreeNode 
* node
, DoJob 
& func 
) 
2157     wxCHECK_MSG( node
, false, "can't walk NULL node" ); 
2159     switch( func( node 
) ) 
2163         case DoJob::SKIP_SUBTREE
: 
2165         case DoJob::CONTINUE
: 
2169     if ( node
->HasChildren() ) 
2171         const wxDataViewTreeNodes
& nodes 
= node
->GetChildNodes(); 
2173         for ( wxDataViewTreeNodes::const_iterator i 
= nodes
.begin(); 
2177             if ( Walker(*i
, func
) ) 
2185 bool wxDataViewMainWindow::ItemAdded(const wxDataViewItem 
& parent
, const wxDataViewItem 
& item
) 
2187     if (IsVirtualList()) 
2189         wxDataViewVirtualListModel 
*list_model 
= 
2190             (wxDataViewVirtualListModel
*) GetModel(); 
2191         m_count 
= list_model
->GetCount(); 
2197         wxDataViewTreeNode 
*parentNode 
= FindNode(parent
); 
2202         wxDataViewItemArray modelSiblings
; 
2203         GetModel()->GetChildren(parent
, modelSiblings
); 
2204         const int modelSiblingsSize 
= modelSiblings
.size(); 
2206         int posInModel 
= modelSiblings
.Index(item
, /*fromEnd=*/true); 
2207         wxCHECK_MSG( posInModel 
!= wxNOT_FOUND
, false, "adding non-existent item?" ); 
2209         wxDataViewTreeNode 
*itemNode 
= new wxDataViewTreeNode(parentNode
, item
); 
2210         itemNode
->SetHasChildren(GetModel()->IsContainer(item
)); 
2212         parentNode
->SetHasChildren(true); 
2214         const wxDataViewTreeNodes
& nodeSiblings 
= parentNode
->GetChildNodes(); 
2215         const int nodeSiblingsSize 
= nodeSiblings
.size(); 
2219         if ( posInModel 
== modelSiblingsSize 
- 1 ) 
2221             nodePos 
= nodeSiblingsSize
; 
2223         else if ( modelSiblingsSize 
== nodeSiblingsSize 
+ 1 ) 
2225             // This is the simple case when our node tree already matches the 
2226             // model and only this one item is missing. 
2227             nodePos 
= posInModel
; 
2231             // It's possible that a larger discrepancy between the model and 
2232             // our realization exists. This can happen e.g. when adding a bunch 
2233             // of items to the model and then calling ItemsAdded() just once 
2234             // afterwards. In this case, we must find the right position by 
2235             // looking at sibling items. 
2237             // append to the end if we won't find a better position: 
2238             nodePos 
= nodeSiblingsSize
; 
2240             for ( int nextItemPos 
= posInModel 
+ 1; 
2241                   nextItemPos 
< modelSiblingsSize
; 
2244                 int nextNodePos 
= parentNode
->FindChildByItem(modelSiblings
[nextItemPos
]); 
2245                 if ( nextNodePos 
!= wxNOT_FOUND 
) 
2247                     nodePos 
= nextNodePos
; 
2253         parentNode
->ChangeSubTreeCount(+1); 
2254         parentNode
->InsertChild(itemNode
, nodePos
); 
2259     GetOwner()->InvalidateColBestWidths(); 
2265 bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem
& parent
, 
2266                                        const wxDataViewItem
& item
) 
2268     if (IsVirtualList()) 
2270         wxDataViewVirtualListModel 
*list_model 
= 
2271             (wxDataViewVirtualListModel
*) GetModel(); 
2272         m_count 
= list_model
->GetCount(); 
2274         if ( !m_selection
.empty() ) 
2276             const int row 
= GetRowByItem(item
); 
2278             int rowIndexInSelection 
= wxNOT_FOUND
; 
2280             const size_t selCount 
= m_selection
.size(); 
2281             for ( size_t i 
= 0; i 
< selCount
; i
++ ) 
2283                 if ( m_selection
[i
] == (unsigned)row 
) 
2284                     rowIndexInSelection 
= i
; 
2285                 else if ( m_selection
[i
] > (unsigned)row 
) 
2289             if ( rowIndexInSelection 
!= wxNOT_FOUND 
) 
2290                 m_selection
.RemoveAt(rowIndexInSelection
); 
2294     else // general case 
2296         wxDataViewTreeNode 
*parentNode 
= FindNode(parent
); 
2298         // Notice that it is possible that the item being deleted is not in the 
2299         // tree at all, for example we could be deleting a never shown (because 
2300         // collapsed) item in a tree model. So it's not an error if we don't know 
2301         // about this item, just return without doing anything then. 
2305         wxCHECK_MSG( parentNode
->HasChildren(), false, "parent node doesn't have children?" ); 
2306         const wxDataViewTreeNodes
& parentsChildren 
= parentNode
->GetChildNodes(); 
2308         // We can't use FindNode() to find 'item', because it was already 
2309         // removed from the model by the time ItemDeleted() is called, so we 
2310         // have to do it manually. We keep track of its position as well for 
2312         int itemPosInNode 
= 0; 
2313         wxDataViewTreeNode 
*itemNode 
= NULL
; 
2314         for ( wxDataViewTreeNodes::const_iterator i 
= parentsChildren
.begin(); 
2315               i 
!= parentsChildren
.end(); 
2316               ++i
, ++itemPosInNode 
) 
2318             if( (*i
)->GetItem() == item 
) 
2325         // If the parent wasn't expanded, it's possible that we didn't have a 
2326         // node corresponding to 'item' and so there's nothing left to do. 
2329             // If this was the last child to be removed, it's possible the parent 
2330             // node became a leaf. Let's ask the model about it. 
2331             if ( parentNode
->GetChildNodes().empty() ) 
2332                 parentNode
->SetHasChildren(GetModel()->IsContainer(parent
)); 
2337         // Delete the item from wxDataViewTreeNode representation: 
2338         const int itemsDeleted 
= 1 + itemNode
->GetSubTreeCount(); 
2340         parentNode
->RemoveChild(itemNode
); 
2342         parentNode
->ChangeSubTreeCount(-itemsDeleted
); 
2344         // Make the row number invalid and get a new valid one when user call GetRowCount 
2347         // If this was the last child to be removed, it's possible the parent 
2348         // node became a leaf. Let's ask the model about it. 
2349         if ( parentNode
->GetChildNodes().empty() ) 
2351             bool isContainer 
= GetModel()->IsContainer(parent
); 
2352             parentNode
->SetHasChildren(isContainer
); 
2355                 // If it's still a container, make sure we show "+" icon for it 
2356                 // and not "-" one as there is nothing to collapse any more. 
2357                 if ( parentNode
->IsOpen() ) 
2358                     parentNode
->ToggleOpen(); 
2362         // Update selection by removing 'item' and its entire children tree from the selection. 
2363         if ( !m_selection
.empty() ) 
2365             // we can't call GetRowByItem() on 'item', as it's already deleted, so compute it from 
2366             // the parent ('parentNode') and position in its list of children 
2368             if ( itemPosInNode 
== 0 ) 
2370                 // 1st child, row number is that of the parent parentNode + 1 
2371                 itemRow 
= GetRowByItem(parentNode
->GetItem()) + 1; 
2375                 // row number is that of the sibling above 'item' + its subtree if any + 1 
2376                 const wxDataViewTreeNode 
*siblingNode 
= parentNode
->GetChildNodes()[itemPosInNode 
- 1]; 
2378                 itemRow 
= GetRowByItem(siblingNode
->GetItem()) + 
2379                           siblingNode
->GetSubTreeCount() + 
2383             wxDataViewSelection 
newsel(wxDataViewSelectionCmp
); 
2385             const size_t numSelections 
= m_selection
.size(); 
2386             for ( size_t i 
= 0; i 
< numSelections
; ++i 
) 
2388                 const int s 
= m_selection
[i
]; 
2390                     newsel
.push_back(s
); 
2391                 else if ( s 
>= itemRow 
+ itemsDeleted 
) 
2392                     newsel
.push_back(s 
- itemsDeleted
); 
2393                 // else: deleted item, remove from selection 
2396             m_selection 
= newsel
; 
2400     // Change the current row to the last row if the current exceed the max row number 
2401     if ( m_currentRow 
>= GetRowCount() ) 
2402         ChangeCurrentRow(m_count 
- 1); 
2404     GetOwner()->InvalidateColBestWidths(); 
2410 bool wxDataViewMainWindow::ItemChanged(const wxDataViewItem 
& item
) 
2415     GetOwner()->InvalidateColBestWidths(); 
2418     wxWindow 
*parent 
= GetParent(); 
2419     wxDataViewEvent 
le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED
, parent
->GetId()); 
2420     le
.SetEventObject(parent
); 
2421     le
.SetModel(GetModel()); 
2423     parent
->ProcessWindowEvent(le
); 
2428 bool wxDataViewMainWindow::ValueChanged( const wxDataViewItem 
& item
, unsigned int model_column 
) 
2430     int view_column 
= -1; 
2431     unsigned int n_col 
= m_owner
->GetColumnCount(); 
2432     for (unsigned i 
= 0; i 
< n_col
; i
++) 
2434         wxDataViewColumn 
*column 
= m_owner
->GetColumn( i 
); 
2435         if (column
->GetModelColumn() == model_column
) 
2437             view_column 
= (int) i
; 
2441     if (view_column 
== -1) 
2444     // NOTE: to be valid, we cannot use e.g. INT_MAX - 1 
2445 /*#define MAX_VIRTUAL_WIDTH       100000 
2447     wxRect rect( 0, row*m_lineHeight, MAX_VIRTUAL_WIDTH, m_lineHeight ); 
2448     m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); 
2449     Refresh( true, &rect ); 
2456     GetOwner()->InvalidateColBestWidth(view_column
); 
2459     wxWindow 
*parent 
= GetParent(); 
2460     wxDataViewEvent 
le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED
, parent
->GetId()); 
2461     le
.SetEventObject(parent
); 
2462     le
.SetModel(GetModel()); 
2464     le
.SetColumn(view_column
); 
2465     le
.SetDataViewColumn(GetOwner()->GetColumn(view_column
)); 
2466     parent
->ProcessWindowEvent(le
); 
2471 bool wxDataViewMainWindow::Cleared() 
2474     m_selection
.Clear(); 
2475     m_currentRow 
= (unsigned)-1; 
2480         BuildTree( GetModel() ); 
2487     GetOwner()->InvalidateColBestWidths(); 
2493 void wxDataViewMainWindow::UpdateDisplay() 
2496     m_underMouse 
= NULL
; 
2499 void wxDataViewMainWindow::OnInternalIdle() 
2501     wxWindow::OnInternalIdle(); 
2505         RecalculateDisplay(); 
2510 void wxDataViewMainWindow::RecalculateDisplay() 
2512     wxDataViewModel 
*model 
= GetModel(); 
2519     int width 
= GetEndOfLastCol(); 
2520     int height 
= GetLineStart( GetRowCount() ); 
2522     SetVirtualSize( width
, height 
); 
2523     GetOwner()->SetScrollRate( 10, m_lineHeight 
); 
2528 void wxDataViewMainWindow::ScrollWindow( int dx
, int dy
, const wxRect 
*rect 
) 
2530     m_underMouse 
= NULL
; 
2532     wxWindow::ScrollWindow( dx
, dy
, rect 
); 
2534     if (GetOwner()->m_headerArea
) 
2535         GetOwner()->m_headerArea
->ScrollWindow( dx
, 0 ); 
2538 void wxDataViewMainWindow::ScrollTo( int rows
, int column 
) 
2540     m_underMouse 
= NULL
; 
2543     m_owner
->GetScrollPixelsPerUnit( &x
, &y 
); 
2544     int sy 
= GetLineStart( rows 
)/y
; 
2548         wxRect rect 
= GetClientRect(); 
2552         m_owner
->CalcUnscrolledPosition( rect
.x
, rect
.y
, &xx
, &yy 
); 
2553         for (x_start 
= 0; colnum 
< column
; colnum
++) 
2555             wxDataViewColumn 
*col 
= GetOwner()->GetColumnAt(colnum
); 
2556             if (col
->IsHidden()) 
2557                 continue;      // skip it! 
2559             w 
= col
->GetWidth(); 
2563         int x_end 
= x_start 
+ w
; 
2564         xe 
= xx 
+ rect
.width
; 
2567             sx 
= ( xx 
+ x_end 
- xe 
)/x
; 
2574     m_owner
->Scroll( sx
, sy 
); 
2577 int wxDataViewMainWindow::GetCountPerPage() const 
2579     wxSize size 
= GetClientSize(); 
2580     return size
.y 
/ m_lineHeight
; 
2583 int wxDataViewMainWindow::GetEndOfLastCol() const 
2587     for (i 
= 0; i 
< GetOwner()->GetColumnCount(); i
++) 
2589         const wxDataViewColumn 
*c 
= 
2590             const_cast<wxDataViewCtrl
*>(GetOwner())->GetColumnAt( i 
); 
2593             width 
+= c
->GetWidth(); 
2598 unsigned int wxDataViewMainWindow::GetFirstVisibleRow() const 
2602     m_owner
->CalcUnscrolledPosition( x
, y
, &x
, &y 
); 
2604     return GetLineAt( y 
); 
2607 unsigned int wxDataViewMainWindow::GetLastVisibleRow() 
2609     wxSize client_size 
= GetClientSize(); 
2610     m_owner
->CalcUnscrolledPosition( client_size
.x
, client_size
.y
, 
2611                                     &client_size
.x
, &client_size
.y 
); 
2613     // we should deal with the pixel here 
2614     unsigned int row 
= GetLineAt(client_size
.y
) - 1; 
2616     return wxMin( GetRowCount()-1, row 
); 
2619 unsigned int wxDataViewMainWindow::GetRowCount() const 
2621     if ( m_count 
== -1 ) 
2623         wxDataViewMainWindow
* const 
2624             self 
= const_cast<wxDataViewMainWindow
*>(this); 
2625         self
->m_count 
= RecalculateCount(); 
2626         self
->UpdateDisplay(); 
2631 void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row 
) 
2638 void wxDataViewMainWindow::SelectAllRows( bool on 
) 
2645         m_selection
.Clear(); 
2646         for (unsigned int i 
= 0; i 
< GetRowCount(); i
++) 
2647             m_selection
.Add( i 
); 
2652         unsigned int first_visible 
= GetFirstVisibleRow(); 
2653         unsigned int last_visible 
= GetLastVisibleRow(); 
2655         for (i 
= 0; i 
< m_selection
.GetCount(); i
++) 
2657             unsigned int row 
= m_selection
[i
]; 
2658             if ((row 
>= first_visible
) && (row 
<= last_visible
)) 
2661         m_selection
.Clear(); 
2665 void wxDataViewMainWindow::SelectRow( unsigned int row
, bool on 
) 
2667     if (m_selection
.Index( row 
) == wxNOT_FOUND
) 
2671             m_selection
.Add( row 
); 
2679             m_selection
.Remove( row 
); 
2685 void wxDataViewMainWindow::SelectRows( unsigned int from
, unsigned int to
, bool on 
) 
2689         unsigned int tmp 
= from
; 
2695     for (i 
= from
; i 
<= to
; i
++) 
2697         if (m_selection
.Index( i 
) == wxNOT_FOUND
) 
2700                 m_selection
.Add( i 
); 
2705                 m_selection
.Remove( i 
); 
2708     RefreshRows( from
, to 
); 
2711 void wxDataViewMainWindow::Select( const wxArrayInt
& aSelections 
) 
2713     for (size_t i
=0; i 
< aSelections
.GetCount(); i
++) 
2715         int n 
= aSelections
[i
]; 
2717         m_selection
.Add( n 
); 
2722 void wxDataViewMainWindow::ReverseRowSelection( unsigned int row 
) 
2724     if (m_selection
.Index( row 
) == wxNOT_FOUND
) 
2725         m_selection
.Add( row 
); 
2727         m_selection
.Remove( row 
); 
2731 bool wxDataViewMainWindow::IsRowSelected( unsigned int row 
) 
2733     return (m_selection
.Index( row 
) != wxNOT_FOUND
); 
2736 void wxDataViewMainWindow::SendSelectionChangedEvent( const wxDataViewItem
& item
) 
2738     wxWindow 
*parent 
= GetParent(); 
2739     wxDataViewEvent 
le(wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED
, parent
->GetId()); 
2741     le
.SetEventObject(parent
); 
2742     le
.SetModel(GetModel()); 
2745     parent
->ProcessWindowEvent(le
); 
2748 void wxDataViewMainWindow::RefreshRow( unsigned int row 
) 
2750     wxRect 
rect( 0, GetLineStart( row 
), GetEndOfLastCol(), GetLineHeight( row 
) ); 
2751     m_owner
->CalcScrolledPosition( rect
.x
, rect
.y
, &rect
.x
, &rect
.y 
); 
2753     wxSize client_size 
= GetClientSize(); 
2754     wxRect 
client_rect( 0, 0, client_size
.x
, client_size
.y 
); 
2755     wxRect intersect_rect 
= client_rect
.Intersect( rect 
); 
2756     if (intersect_rect
.width 
> 0) 
2757         Refresh( true, &intersect_rect 
); 
2760 void wxDataViewMainWindow::RefreshRows( unsigned int from
, unsigned int to 
) 
2764         unsigned int tmp 
= to
; 
2769     wxRect 
rect( 0, GetLineStart( from 
), GetEndOfLastCol(), GetLineStart( (to
-from
+1) ) ); 
2770     m_owner
->CalcScrolledPosition( rect
.x
, rect
.y
, &rect
.x
, &rect
.y 
); 
2772     wxSize client_size 
= GetClientSize(); 
2773     wxRect 
client_rect( 0, 0, client_size
.x
, client_size
.y 
); 
2774     wxRect intersect_rect 
= client_rect
.Intersect( rect 
); 
2775     if (intersect_rect
.width 
> 0) 
2776         Refresh( true, &intersect_rect 
); 
2779 void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow 
) 
2781     wxSize client_size 
= GetClientSize(); 
2782     int start 
= GetLineStart( firstRow 
); 
2783     m_owner
->CalcScrolledPosition( start
, 0, &start
, NULL 
); 
2784     if (start 
> client_size
.y
) return; 
2786     wxRect 
rect( 0, start
, client_size
.x
, client_size
.y 
- start 
); 
2788     Refresh( true, &rect 
); 
2791 wxRect 
wxDataViewMainWindow::GetLineRect( unsigned int row 
) const 
2795     rect
.y 
= GetLineStart( row 
); 
2796     rect
.width 
= GetEndOfLastCol(); 
2797     rect
.height 
= GetLineHeight( row 
); 
2802 int wxDataViewMainWindow::GetLineStart( unsigned int row 
) const 
2804     const wxDataViewModel 
*model 
= GetModel(); 
2806     if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT
) 
2808         // TODO make more efficient 
2813         for (r 
= 0; r 
< row
; r
++) 
2815             const wxDataViewTreeNode
* node 
= GetTreeNodeByRow(r
); 
2816             if (!node
) return start
; 
2818             wxDataViewItem item 
= node
->GetItem(); 
2820             unsigned int cols 
= GetOwner()->GetColumnCount(); 
2822             int height 
= m_lineHeight
; 
2823             for (col 
= 0; col 
< cols
; col
++) 
2825                 const wxDataViewColumn 
*column 
= GetOwner()->GetColumn(col
); 
2826                 if (column
->IsHidden()) 
2827                     continue;      // skip it! 
2830                     model
->IsContainer(item
) && 
2831                     !model
->HasContainerColumns(item
)) 
2832                     continue;      // skip it! 
2834                 wxDataViewRenderer 
*renderer 
= 
2835                     const_cast<wxDataViewRenderer
*>(column
->GetRenderer()); 
2836                 renderer
->PrepareForItem(model
, item
, column
->GetModelColumn()); 
2838                 height 
= wxMax( height
, renderer
->GetSize().y 
); 
2848         return row 
* m_lineHeight
; 
2852 int wxDataViewMainWindow::GetLineAt( unsigned int y 
) const 
2854     const wxDataViewModel 
*model 
= GetModel(); 
2856     // check for the easy case first 
2857     if ( !GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT
) ) 
2858         return y 
/ m_lineHeight
; 
2860     // TODO make more efficient 
2861     unsigned int row 
= 0; 
2862     unsigned int yy 
= 0; 
2865         const wxDataViewTreeNode
* node 
= GetTreeNodeByRow(row
); 
2868             // not really correct... 
2869             return row 
+ ((y
-yy
) / m_lineHeight
); 
2872         wxDataViewItem item 
= node
->GetItem(); 
2874         unsigned int cols 
= GetOwner()->GetColumnCount(); 
2876         int height 
= m_lineHeight
; 
2877         for (col 
= 0; col 
< cols
; col
++) 
2879             const wxDataViewColumn 
*column 
= GetOwner()->GetColumn(col
); 
2880             if (column
->IsHidden()) 
2881                 continue;      // skip it! 
2884                 model
->IsContainer(item
) && 
2885                 !model
->HasContainerColumns(item
)) 
2886                 continue;      // skip it! 
2888             wxDataViewRenderer 
*renderer 
= 
2889                 const_cast<wxDataViewRenderer
*>(column
->GetRenderer()); 
2890             renderer
->PrepareForItem(model
, item
, column
->GetModelColumn()); 
2892             height 
= wxMax( height
, renderer
->GetSize().y 
); 
2903 int wxDataViewMainWindow::GetLineHeight( unsigned int row 
) const 
2905     const wxDataViewModel 
*model 
= GetModel(); 
2907     if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT
) 
2909         wxASSERT( !IsVirtualList() ); 
2911         const wxDataViewTreeNode
* node 
= GetTreeNodeByRow(row
); 
2912         // wxASSERT( node ); 
2913         if (!node
) return m_lineHeight
; 
2915         wxDataViewItem item 
= node
->GetItem(); 
2917         int height 
= m_lineHeight
; 
2919         unsigned int cols 
= GetOwner()->GetColumnCount(); 
2921         for (col 
= 0; col 
< cols
; col
++) 
2923             const wxDataViewColumn 
*column 
= GetOwner()->GetColumn(col
); 
2924             if (column
->IsHidden()) 
2925                 continue;      // skip it! 
2928                 model
->IsContainer(item
) && 
2929                 !model
->HasContainerColumns(item
)) 
2930                 continue;      // skip it! 
2932             wxDataViewRenderer 
*renderer 
= 
2933                 const_cast<wxDataViewRenderer
*>(column
->GetRenderer()); 
2934             renderer
->PrepareForItem(model
, item
, column
->GetModelColumn()); 
2936             height 
= wxMax( height
, renderer
->GetSize().y 
); 
2943         return m_lineHeight
; 
2948 class RowToTreeNodeJob
: public DoJob
 
2951     RowToTreeNodeJob( unsigned int row 
, int current
, wxDataViewTreeNode 
* node 
) 
2954         this->current 
= current
; 
2959     virtual int operator() ( wxDataViewTreeNode 
* node 
) 
2962         if( current 
== static_cast<int>(row
)) 
2968         if( node
->GetSubTreeCount() + current 
< static_cast<int>(row
) ) 
2970             current 
+= node
->GetSubTreeCount(); 
2971             return  DoJob::SKIP_SUBTREE
; 
2977             // If the current node has only leaf children, we can find the 
2978             // desired node directly. This can speed up finding the node 
2979             // in some cases, and will have a very good effect for list views. 
2980             if ( node
->HasChildren() && 
2981                  (int)node
->GetChildNodes().size() == node
->GetSubTreeCount() ) 
2983                 const int index 
= static_cast<int>(row
) - current 
- 1; 
2984                 ret 
= node
->GetChildNodes()[index
]; 
2988             return DoJob::CONTINUE
; 
2992     wxDataViewTreeNode 
* GetResult() const 
2998     wxDataViewTreeNode 
* ret
; 
2999     wxDataViewTreeNode 
* parent
; 
3002 wxDataViewTreeNode 
* wxDataViewMainWindow::GetTreeNodeByRow(unsigned int row
) const 
3004     wxASSERT( !IsVirtualList() ); 
3006     if ( row 
== (unsigned)-1 ) 
3009     RowToTreeNodeJob 
job( row 
, -2, m_root 
); 
3010     Walker( m_root 
, job 
); 
3011     return job
.GetResult(); 
3014 wxDataViewItem 
wxDataViewMainWindow::GetItemByRow(unsigned int row
) const 
3016     wxDataViewItem item
; 
3017     if (IsVirtualList()) 
3019         if ( row 
< GetRowCount() ) 
3020             item 
= wxDataViewItem(wxUIntToPtr(row
+1)); 
3024         wxDataViewTreeNode 
*node 
= GetTreeNodeByRow(row
); 
3026             item 
= node
->GetItem(); 
3033 wxDataViewMainWindow::SendExpanderEvent(wxEventType type
, 
3034                                         const wxDataViewItem
& item
) 
3036     wxWindow 
*parent 
= GetParent(); 
3037     wxDataViewEvent 
le(type
, parent
->GetId()); 
3039     le
.SetEventObject(parent
); 
3040     le
.SetModel(GetModel()); 
3043     return !parent
->ProcessWindowEvent(le
) || le
.IsAllowed(); 
3046 bool wxDataViewMainWindow::IsExpanded( unsigned int row 
) const 
3051     wxDataViewTreeNode 
* node 
= GetTreeNodeByRow(row
); 
3055     if (!node
->HasChildren()) 
3058     return node
->IsOpen(); 
3061 bool wxDataViewMainWindow::HasChildren( unsigned int row 
) const 
3066     wxDataViewTreeNode 
* node 
= GetTreeNodeByRow(row
); 
3070     if (!node
->HasChildren()) 
3076 void wxDataViewMainWindow::Expand( unsigned int row 
) 
3081     wxDataViewTreeNode 
* node 
= GetTreeNodeByRow(row
); 
3085     if (!node
->HasChildren()) 
3088     if (!node
->IsOpen()) 
3090         if ( !SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING
, node
->GetItem()) ) 
3092             // Vetoed by the event handler. 
3098         // build the children of current node 
3099         if( node
->GetChildNodes().empty() ) 
3102             ::BuildTreeHelper(GetModel(), node
->GetItem(), node
); 
3105         // By expanding the node all row indices that are currently in the selection list 
3106         // and are greater than our node have become invalid. So we have to correct that now. 
3107         const unsigned rowAdjustment 
= node
->GetSubTreeCount(); 
3108         for(unsigned i
=0; i
<m_selection
.size(); ++i
) 
3110             const unsigned testRow 
= m_selection
[i
]; 
3111             // all rows above us are not affected, so skip them 
3115             m_selection
[i
] += rowAdjustment
; 
3118         if(m_currentRow 
> row
) 
3119             ChangeCurrentRow(m_currentRow 
+ rowAdjustment
); 
3123         // Send the expanded event 
3124         SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDED
,node
->GetItem()); 
3128 void wxDataViewMainWindow::Collapse(unsigned int row
) 
3133     wxDataViewTreeNode 
*node 
= GetTreeNodeByRow(row
); 
3137     if (!node
->HasChildren()) 
3142             if ( !SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING
,node
->GetItem()) ) 
3144                 // Vetoed by the event handler. 
3148             // Find out if there are selected items below the current node. 
3149             bool selectCollapsingRow 
= false; 
3150             const unsigned rowAdjustment 
= node
->GetSubTreeCount(); 
3151             unsigned maxRowToBeTested 
= row 
+ rowAdjustment
; 
3152             for(unsigned i
=0; i
<m_selection
.size(); ++i
) 
3154                 const unsigned testRow 
= m_selection
[i
]; 
3155                 if(testRow 
> row 
&& testRow 
<= maxRowToBeTested
) 
3157                     selectCollapsingRow 
= true; 
3158                     // get out as soon as we have found a node that is selected 
3165             // If the node to be closed has selected items the user won't see those any longer. 
3166             // We select the collapsing node in this case. 
3167             if(selectCollapsingRow
) 
3169                 SelectAllRows(false); 
3170                 ChangeCurrentRow(row
); 
3171                 SelectRow(row
, true); 
3172                 SendSelectionChangedEvent(GetItemByRow(row
)); 
3176                 // if there were no selected items below our node we still need to "fix" the 
3177                 // selection list to adjust for the changing of the row indices. 
3178                 // We actually do the opposite of what we are doing in Expand(). 
3179                 for(unsigned i
=0; i
<m_selection
.size(); ++i
) 
3181                     const unsigned testRow 
= m_selection
[i
]; 
3182                     // all rows above us are not affected, so skip them 
3186                     m_selection
[i
] -= rowAdjustment
; 
3189                 // if the "current row" is being collapsed away we change it to the current row ;-) 
3190                 if(m_currentRow 
> row 
&& m_currentRow 
<= maxRowToBeTested
) 
3191                     ChangeCurrentRow(row
); 
3192                 else if(m_currentRow 
> row
) 
3193                     ChangeCurrentRow(m_currentRow 
- rowAdjustment
); 
3198             SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSED
,node
->GetItem()); 
3202 wxDataViewTreeNode 
* wxDataViewMainWindow::FindNode( const wxDataViewItem 
& item 
) 
3204     const wxDataViewModel 
* model 
= GetModel(); 
3211     // Compose the parent-chain for the item we are looking for 
3212     wxVector
<wxDataViewItem
> parentChain
; 
3213     wxDataViewItem 
it( item 
); 
3216         parentChain
.push_back(it
); 
3217         it 
= model
->GetParent(it
); 
3220     // Find the item along the parent-chain. 
3221     // This algorithm is designed to speed up the node-finding method 
3222     wxDataViewTreeNode
* node 
= m_root
; 
3223     for( unsigned iter 
= parentChain
.size()-1; ; --iter 
) 
3225         if( node
->HasChildren() ) 
3227             if( node
->GetChildNodes().empty() ) 
3229                 // Even though the item is a container, it doesn't have any 
3230                 // child nodes in the control's representation yet. We have 
3231                 // to realize its subtree now. 
3233                 ::BuildTreeHelper(model
, node
->GetItem(), node
); 
3236             const wxDataViewTreeNodes
& nodes 
= node
->GetChildNodes(); 
3239             for (unsigned i 
= 0; i 
< nodes
.GetCount(); ++i
) 
3241                 wxDataViewTreeNode
* currentNode 
= nodes
[i
]; 
3242                 if (currentNode
->GetItem() == parentChain
[iter
]) 
3244                     if (currentNode
->GetItem() == item
) 
3264 void wxDataViewMainWindow::HitTest( const wxPoint 
& point
, wxDataViewItem 
& item
, 
3265                                     wxDataViewColumn
* &column 
) 
3267     wxDataViewColumn 
*col 
= NULL
; 
3268     unsigned int cols 
= GetOwner()->GetColumnCount(); 
3269     unsigned int colnum 
= 0; 
3271     m_owner
->CalcUnscrolledPosition( point
.x
, point
.y
, &x
, &y 
); 
3272     for (unsigned x_start 
= 0; colnum 
< cols
; colnum
++) 
3274         col 
= GetOwner()->GetColumnAt(colnum
); 
3275         if (col
->IsHidden()) 
3276             continue;      // skip it! 
3278         unsigned int w 
= col
->GetWidth(); 
3279         if (x_start
+w 
>= (unsigned int)x
) 
3286     item 
= GetItemByRow( GetLineAt( y 
) ); 
3289 wxRect 
wxDataViewMainWindow::GetItemRect( const wxDataViewItem 
& item
, 
3290                                           const wxDataViewColumn
* column 
) 
3295     unsigned int cols 
= GetOwner()->GetColumnCount(); 
3296     // If column is null the loop will compute the combined width of all columns. 
3297     // Otherwise, it will compute the x position of the column we are looking for. 
3298     for (unsigned int i 
= 0; i 
< cols
; i
++) 
3300         wxDataViewColumn
* col 
= GetOwner()->GetColumnAt( i 
); 
3305         if (col
->IsHidden()) 
3306             continue;      // skip it! 
3308         xpos 
+= col
->GetWidth(); 
3309         width 
+= col
->GetWidth(); 
3314         // If we have a column, we need can get its width directly. 
3315         if(column
->IsHidden()) 
3318             width 
= column
->GetWidth(); 
3323         // If we have no column, we reset the x position back to zero. 
3327     // we have to take an expander column into account and compute its indentation 
3328     // to get the correct x position where the actual text is 
3330     int row 
= GetRowByItem(item
); 
3332             (column 
== 0 || GetExpanderColumnOrFirstOne(GetOwner()) == column
) ) 
3334         wxDataViewTreeNode
* node 
= GetTreeNodeByRow(row
); 
3335         indent 
= GetOwner()->GetIndent() * node
->GetIndentLevel(); 
3336         indent 
= indent 
+ m_lineHeight
; // use m_lineHeight as the width of the expander 
3339     wxRect 
itemRect( xpos 
+ indent
, 
3340                      GetLineStart( row 
), 
3342                      GetLineHeight( row 
) ); 
3344     GetOwner()->CalcScrolledPosition(  itemRect
.x
,  itemRect
.y
, 
3345                                       &itemRect
.x
, &itemRect
.y 
); 
3350 int wxDataViewMainWindow::RecalculateCount() const 
3352     if (IsVirtualList()) 
3354         wxDataViewVirtualListModel 
*list_model 
= 
3355             (wxDataViewVirtualListModel
*) GetModel(); 
3357         return list_model
->GetCount(); 
3361         return m_root
->GetSubTreeCount(); 
3365 class ItemToRowJob 
: public DoJob
 
3368     ItemToRowJob(const wxDataViewItem
& item_
, wxVector
<wxDataViewItem
>::reverse_iterator iter
) 
3375     // Maybe binary search will help to speed up this process 
3376     virtual int operator() ( wxDataViewTreeNode 
* node
) 
3379         if( node
->GetItem() == item 
) 
3384         if( node
->GetItem() == *m_iter 
) 
3387             return DoJob::CONTINUE
; 
3391             ret 
+= node
->GetSubTreeCount(); 
3392             return DoJob::SKIP_SUBTREE
; 
3397     // the row number is begin from zero 
3398     int GetResult() const 
3402     wxVector
<wxDataViewItem
>::reverse_iterator m_iter
; 
3403     wxDataViewItem item
; 
3408 int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem 
& item
) const 
3410     const wxDataViewModel 
* model 
= GetModel(); 
3414     if (IsVirtualList()) 
3416         return wxPtrToUInt( item
.GetID() ) -1; 
3423         // Compose the parent-chain of the item we are looking for 
3424         wxVector
<wxDataViewItem
> parentChain
; 
3425         wxDataViewItem 
it( item 
); 
3428             parentChain
.push_back(it
); 
3429             it 
= model
->GetParent(it
); 
3432         // add an 'invalid' item to represent our 'invisible' root node 
3433         parentChain
.push_back(wxDataViewItem()); 
3435         // the parent chain was created by adding the deepest parent first. 
3436         // so if we want to start at the root node, we have to iterate backwards through the vector 
3437         ItemToRowJob 
job( item
, parentChain
.rbegin() ); 
3438         Walker( m_root
, job 
); 
3439         return job
.GetResult(); 
3443 static void BuildTreeHelper( const wxDataViewModel 
* model
,  const wxDataViewItem 
& item
, 
3444                              wxDataViewTreeNode 
* node
) 
3446     if( !model
->IsContainer( item 
) ) 
3449     wxDataViewItemArray children
; 
3450     unsigned int num 
= model
->GetChildren( item
, children
); 
3452     for ( unsigned int index 
= 0; index 
< num
; index
++ ) 
3454         wxDataViewTreeNode 
*n 
= new wxDataViewTreeNode(node
, children
[index
]); 
3456         if( model
->IsContainer(children
[index
]) ) 
3457             n
->SetHasChildren( true ); 
3459         node
->InsertChild(n
, index
); 
3462     wxASSERT( node
->IsOpen() ); 
3463     node
->ChangeSubTreeCount(+num
); 
3466 void wxDataViewMainWindow::BuildTree(wxDataViewModel 
* model
) 
3470     if (GetModel()->IsVirtualListModel()) 
3476     m_root 
= wxDataViewTreeNode::CreateRootNode(); 
3478     // First we define a invalid item to fetch the top-level elements 
3479     wxDataViewItem item
; 
3481     BuildTreeHelper( model
, item
, m_root
); 
3485 void wxDataViewMainWindow::DestroyTree() 
3487     if (!IsVirtualList()) 
3495 wxDataViewMainWindow::FindColumnForEditing(const wxDataViewItem
& item
, wxDataViewCellMode mode
) 
3497     // Edit the current column editable in 'mode'. If no column is focused 
3498     // (typically because the user has full row selected), try to find the 
3499     // first editable column (this would typically be a checkbox for 
3500     // wxDATAVIEW_CELL_ACTIVATABLE and we don't want to force the user to set 
3501     // focus on the checkbox column; or on the only editable text column). 
3503     wxDataViewColumn 
*candidate 
= m_currentCol
; 
3506          !IsCellEditableInMode(item
, candidate
, mode
) && 
3507          !m_currentColSetByKeyboard 
) 
3509         // If current column was set by mouse to something not editable (in 
3510         // 'mode') and the user pressed Space/F2 to edit it, treat the 
3511         // situation as if there was whole-row focus, because that's what is 
3512         // visually indicated and the mouse click could very well be targeted 
3513         // on the row rather than on an individual cell. 
3515         // But if it was done by keyboard, respect that even if the column 
3516         // isn't editable, because focus is visually on that column and editing 
3517         // something else would be surprising. 
3523         const unsigned cols 
= GetOwner()->GetColumnCount(); 
3524         for ( unsigned i 
= 0; i 
< cols
; i
++ ) 
3526             wxDataViewColumn 
*c 
= GetOwner()->GetColumnAt(i
); 
3527             if ( c
->IsHidden() ) 
3530             if ( IsCellEditableInMode(item
, c
, mode
) ) 
3538     // If on container item without columns, only the expander column 
3539     // may be directly editable: 
3541          GetOwner()->GetExpanderColumn() != candidate 
&& 
3542          GetModel()->IsContainer(item
) && 
3543          !GetModel()->HasContainerColumns(item
) ) 
3545         candidate 
= GetOwner()->GetExpanderColumn(); 
3551    if ( !IsCellEditableInMode(item
, candidate
, mode
) ) 
3557 bool wxDataViewMainWindow::IsCellEditableInMode(const wxDataViewItem
& item
, 
3558                                                 const wxDataViewColumn 
*col
, 
3559                                                 wxDataViewCellMode mode
) const 
3561     if ( col
->GetRenderer()->GetMode() != mode 
) 
3564     if ( !GetModel()->IsEnabled(item
, col
->GetModelColumn()) ) 
3570 void wxDataViewMainWindow::OnCharHook(wxKeyEvent
& event
) 
3574         // Handle any keys special for the in-place editor and return without 
3575         // calling Skip() below. 
3576         switch ( event
.GetKeyCode() ) 
3579                 m_editorRenderer
->CancelEditing(); 
3583                 m_editorRenderer
->FinishEditing(); 
3591 void wxDataViewMainWindow::OnChar( wxKeyEvent 
&event 
) 
3593     wxWindow 
* const parent 
= GetParent(); 
3595     // propagate the char event upwards 
3596     wxKeyEvent 
eventForParent(event
); 
3597     eventForParent
.SetEventObject(parent
); 
3598     if ( parent
->ProcessWindowEvent(eventForParent
) ) 
3601     if ( parent
->HandleAsNavigationKey(event
) ) 
3604     // no item -> nothing to do 
3605     if (!HasCurrentRow()) 
3611     // don't use m_linesPerPage directly as it might not be computed yet 
3612     const int pageSize 
= GetCountPerPage(); 
3613     wxCHECK_RET( pageSize
, wxT("should have non zero page size") ); 
3615     switch ( event
.GetKeyCode() ) 
3618             if ( event
.HasModifiers() ) 
3625                 // Enter activates the item, i.e. sends wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED to 
3626                 // it. Only if that event is not handled do we activate column renderer (which 
3627                 // is normally done by Space) or even inline editing. 
3629                 const wxDataViewItem item 
= GetItemByRow(m_currentRow
); 
3631                 wxDataViewEvent 
le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED
, 
3634                 le
.SetEventObject(parent
); 
3635                 le
.SetModel(GetModel()); 
3637                 if ( parent
->ProcessWindowEvent(le
) ) 
3639                 // else: fall through to WXK_SPACE handling 
3643             if ( event
.HasModifiers() ) 
3650                 // Space toggles activatable items or -- if not activatable -- 
3651                 // starts inline editing (this is normally done using F2 on 
3652                 // Windows, but Space is common everywhere else, so use it too 
3653                 // for greater cross-platform compatibility). 
3655                 const wxDataViewItem item 
= GetItemByRow(m_currentRow
); 
3657                 // Activate the current activatable column. If not column is focused (typically 
3658                 // because the user has full row selected), try to find the first activatable 
3659                 // column (this would typically be a checkbox and we don't want to force the user 
3660                 // to set focus on the checkbox column). 
3661                 wxDataViewColumn 
*activatableCol 
= FindColumnForEditing(item
, wxDATAVIEW_CELL_ACTIVATABLE
); 
3663                 if ( activatableCol 
) 
3665                     const unsigned colIdx 
= activatableCol
->GetModelColumn(); 
3666                     const wxRect cell_rect 
= GetOwner()->GetItemRect(item
, activatableCol
); 
3668                     wxDataViewRenderer 
*cell 
= activatableCol
->GetRenderer(); 
3669                     cell
->PrepareForItem(GetModel(), item
, colIdx
); 
3670                     cell
->WXActivateCell(cell_rect
, GetModel(), item
, colIdx
, NULL
); 
3674                 // else: fall through to WXK_F2 handling 
3678             if ( event
.HasModifiers() ) 
3685                 if( !m_selection
.empty() ) 
3687                     // Mimic Windows 7 behavior: edit the item that has focus 
3688                     // if it is selected and the first selected item if focus 
3689                     // is out of selection. 
3691                     if ( m_selection
.Index(m_currentRow
) != wxNOT_FOUND 
) 
3694                         sel 
= m_selection
[0]; 
3697                     const wxDataViewItem item 
= GetItemByRow(sel
); 
3699                     // Edit the current column. If no column is focused 
3700                     // (typically because the user has full row selected), try 
3701                     // to find the first editable column. 
3702                     wxDataViewColumn 
*editableCol 
= FindColumnForEditing(item
, wxDATAVIEW_CELL_EDITABLE
); 
3705                         GetOwner()->EditItem(item
, editableCol
); 
3711             OnVerticalNavigation( -1, event 
); 
3715             OnVerticalNavigation( +1, event 
); 
3717         // Add the process for tree expanding/collapsing 
3727             OnVerticalNavigation( +(int)GetRowCount(), event 
); 
3731             OnVerticalNavigation( -(int)GetRowCount(), event 
); 
3735             OnVerticalNavigation( -(pageSize 
- 1), event 
); 
3739             OnVerticalNavigation( +(pageSize 
- 1), event 
); 
3747 void wxDataViewMainWindow::OnVerticalNavigation(int delta
, const wxKeyEvent
& event
) 
3749     // if there is no selection, we cannot move it anywhere 
3750     if (!HasCurrentRow() || IsEmpty()) 
3753     int newRow 
= (int)m_currentRow 
+ delta
; 
3755     // let's keep the new row inside the allowed range 
3759     const int rowCount 
= (int)GetRowCount(); 
3760     if ( newRow 
>= rowCount 
) 
3761         newRow 
= rowCount 
- 1; 
3763     unsigned int oldCurrent 
= m_currentRow
; 
3764     unsigned int newCurrent 
= (unsigned int)newRow
; 
3766     // in single selection we just ignore Shift as we can't select several 
3768     if ( event
.ShiftDown() && !IsSingleSel() ) 
3770         RefreshRow( oldCurrent 
); 
3772         ChangeCurrentRow( newCurrent 
); 
3774         // select all the items between the old and the new one 
3775         if ( oldCurrent 
> newCurrent 
) 
3777             newCurrent 
= oldCurrent
; 
3778             oldCurrent 
= m_currentRow
; 
3781         SelectRows( oldCurrent
, newCurrent
, true ); 
3782         if (oldCurrent
!=newCurrent
) 
3783             SendSelectionChangedEvent(GetItemByRow(m_selection
[0])); 
3787         RefreshRow( oldCurrent 
); 
3789         // all previously selected items are unselected unless ctrl is held 
3790         if ( !event
.ControlDown() ) 
3791             SelectAllRows(false); 
3793         ChangeCurrentRow( newCurrent 
); 
3795         if ( !event
.ControlDown() ) 
3797             SelectRow( m_currentRow
, true ); 
3798             SendSelectionChangedEvent(GetItemByRow(m_currentRow
)); 
3801             RefreshRow( m_currentRow 
); 
3804     GetOwner()->EnsureVisible( m_currentRow
, -1 ); 
3807 void wxDataViewMainWindow::OnLeftKey() 
3811         TryAdvanceCurrentColumn(NULL
, /*forward=*/false); 
3815         wxDataViewTreeNode
* node 
= GetTreeNodeByRow(m_currentRow
); 
3819         if ( TryAdvanceCurrentColumn(node
, /*forward=*/false) ) 
3822         // Because TryAdvanceCurrentColumn() return false, we are at the first 
3823         // column or using whole-row selection. In this situation, we can use 
3824         // the standard TreeView handling of the left key. 
3825         if (node
->HasChildren() && node
->IsOpen()) 
3827             Collapse(m_currentRow
); 
3831             // if the node is already closed, we move the selection to its parent 
3832             wxDataViewTreeNode 
*parent_node 
= node
->GetParent(); 
3836                 int parent 
= GetRowByItem( parent_node
->GetItem() ); 
3839                     unsigned int row 
= m_currentRow
; 
3840                     SelectRow( row
, false); 
3841                     SelectRow( parent
, true ); 
3842                     ChangeCurrentRow( parent 
); 
3843                     GetOwner()->EnsureVisible( parent
, -1 ); 
3844                     SendSelectionChangedEvent( parent_node
->GetItem() ); 
3851 void wxDataViewMainWindow::OnRightKey() 
3855         TryAdvanceCurrentColumn(NULL
, /*forward=*/true); 
3859         wxDataViewTreeNode
* node 
= GetTreeNodeByRow(m_currentRow
); 
3863         if ( node
->HasChildren() ) 
3865             if ( !node
->IsOpen() ) 
3867                 Expand( m_currentRow 
); 
3871                 // if the node is already open, we move the selection to the first child 
3872                 unsigned int row 
= m_currentRow
; 
3873                 SelectRow( row
, false ); 
3874                 SelectRow( row 
+ 1, true ); 
3875                 ChangeCurrentRow( row 
+ 1 ); 
3876                 GetOwner()->EnsureVisible( row 
+ 1, -1 ); 
3877                 SendSelectionChangedEvent( GetItemByRow(row
+1) ); 
3882             TryAdvanceCurrentColumn(node
, /*forward=*/true); 
3887 bool wxDataViewMainWindow::TryAdvanceCurrentColumn(wxDataViewTreeNode 
*node
, bool forward
) 
3889     if ( GetOwner()->GetColumnCount() == 0 ) 
3892     if ( !m_useCellFocus 
) 
3897         // navigation shouldn't work in branch nodes without other columns: 
3898         if ( node
->HasChildren() && !GetModel()->HasContainerColumns(node
->GetItem()) ) 
3902     if ( m_currentCol 
== NULL 
|| !m_currentColSetByKeyboard 
) 
3906             m_currentCol 
= GetOwner()->GetColumnAt(1); 
3907             m_currentColSetByKeyboard 
= true; 
3908             RefreshRow(m_currentRow
); 
3915     int idx 
= GetOwner()->GetColumnIndex(m_currentCol
) + (forward 
? +1 : -1); 
3917     if ( idx 
>= (int)GetOwner()->GetColumnCount() ) 
3920     GetOwner()->EnsureVisible(m_currentRow
, idx
); 
3924         // We are going to the left of the second column. Reset to whole-row 
3925         // focus (which means first column would be edited). 
3926         m_currentCol 
= NULL
; 
3927         RefreshRow(m_currentRow
); 
3931     m_currentCol 
= GetOwner()->GetColumnAt(idx
); 
3932     m_currentColSetByKeyboard 
= true; 
3933     RefreshRow(m_currentRow
); 
3937 void wxDataViewMainWindow::OnMouse( wxMouseEvent 
&event 
) 
3939     if (event
.GetEventType() == wxEVT_MOUSEWHEEL
) 
3941         // let the base handle mouse wheel events. 
3946     if(event
.LeftDown()) 
3948         // Not skipping this event would prevent the system from setting focus 
3953     int x 
= event
.GetX(); 
3954     int y 
= event
.GetY(); 
3955     m_owner
->CalcUnscrolledPosition( x
, y
, &x
, &y 
); 
3956     wxDataViewColumn 
*col 
= NULL
; 
3959     unsigned int cols 
= GetOwner()->GetColumnCount(); 
3961     for (i 
= 0; i 
< cols
; i
++) 
3963         wxDataViewColumn 
*c 
= GetOwner()->GetColumnAt( i 
); 
3965             continue;      // skip it! 
3967         if (x 
< xpos 
+ c
->GetWidth()) 
3972         xpos 
+= c
->GetWidth(); 
3975     wxDataViewModel
* const model 
= GetModel(); 
3977     const unsigned int current 
= GetLineAt( y 
); 
3978     const wxDataViewItem item 
= GetItemByRow(current
); 
3980     // Handle right clicking here, before everything else as context menu 
3981     // events should be sent even when we click outside of any item, unlike all 
3983     if (event
.RightUp()) 
3985         wxWindow 
*parent 
= GetParent(); 
3986         wxDataViewEvent 
le(wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU
, parent
->GetId()); 
3987         le
.SetEventObject(parent
); 
3990         if ( item
.IsOk() && col 
) 
3993             le
.SetColumn( col
->GetModelColumn() ); 
3994             le
.SetDataViewColumn( col 
); 
3997             model
->GetValue( value
, item
, col
->GetModelColumn() ); 
4001         parent
->ProcessWindowEvent(le
); 
4011     wxDataViewRenderer 
*cell 
= col
->GetRenderer(); 
4012     if ((current 
>= GetRowCount()) || (x 
> GetEndOfLastCol())) 
4014         // Unselect all if below the last row ? 
4019     wxDataViewColumn
* const 
4020         expander 
= GetExpanderColumnOrFirstOne(GetOwner()); 
4022     // Test whether the mouse is hovering over the expander (a.k.a tree "+" 
4023     // button) and also determine the offset of the real cell start, skipping 
4024     // the indentation and the expander itself. 
4025     bool hoverOverExpander 
= false; 
4027     if ((!IsList()) && (expander 
== col
)) 
4029         wxDataViewTreeNode 
* node 
= GetTreeNodeByRow(current
); 
4031         int indent 
= node
->GetIndentLevel(); 
4032         itemOffset 
= GetOwner()->GetIndent()*indent
; 
4034         if ( node
->HasChildren() ) 
4036             // we make the rectangle we are looking in a bit bigger than the actual 
4037             // visual expander so the user can hit that little thing reliably 
4038             wxRect 
rect(itemOffset
, 
4039                         GetLineStart( current 
) + (GetLineHeight(current
) - m_lineHeight
)/2, 
4040                         m_lineHeight
, m_lineHeight
); 
4042             if( rect
.Contains(x
, y
) ) 
4044                 // So the mouse is over the expander 
4045                 hoverOverExpander 
= true; 
4046                 if (m_underMouse 
&& m_underMouse 
!= node
) 
4048                     // wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem())); 
4049                     RefreshRow(GetRowByItem(m_underMouse
->GetItem())); 
4051                 if (m_underMouse 
!= node
) 
4053                     // wxLogMessage("Do the row: %d", current); 
4054                     RefreshRow(current
); 
4056                 m_underMouse 
= node
; 
4060         // Account for the expander as well, even if this item doesn't have it, 
4061         // its parent does so it still counts for the offset. 
4062         itemOffset 
+= m_lineHeight
; 
4064     if (!hoverOverExpander
) 
4066         if (m_underMouse 
!= NULL
) 
4068             // wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem())); 
4069             RefreshRow(GetRowByItem(m_underMouse
->GetItem())); 
4070             m_underMouse 
= NULL
; 
4074 #if wxUSE_DRAG_AND_DROP 
4075     if (event
.Dragging()) 
4077         if (m_dragCount 
== 0) 
4079             // we have to report the raw, physical coords as we want to be 
4080             // able to call HitTest(event.m_pointDrag) from the user code to 
4081             // get the item being dragged 
4082             m_dragStart 
= event
.GetPosition(); 
4087         if (m_dragCount 
!= 3) 
4090         if (event
.LeftIsDown()) 
4092             m_owner
->CalcUnscrolledPosition( m_dragStart
.x
, m_dragStart
.y
, 
4093                                              &m_dragStart
.x
, &m_dragStart
.y 
); 
4094             unsigned int drag_item_row 
= GetLineAt( m_dragStart
.y 
); 
4095             wxDataViewItem itemDragged 
= GetItemByRow( drag_item_row 
); 
4097             // Notify cell about drag 
4098             wxDataViewEvent 
event( wxEVT_COMMAND_DATAVIEW_ITEM_BEGIN_DRAG
, m_owner
->GetId() ); 
4099             event
.SetEventObject( m_owner 
); 
4100             event
.SetItem( itemDragged 
); 
4101             event
.SetModel( model 
); 
4102             if (!m_owner
->HandleWindowEvent( event 
)) 
4105             if (!event
.IsAllowed()) 
4108             wxDataObject 
*obj 
= event
.GetDataObject(); 
4112             wxDataViewDropSource 
drag( this, drag_item_row 
); 
4113             drag
.SetData( *obj 
); 
4114             /* wxDragResult res = */ drag
.DoDragDrop(event
.GetDragFlags()); 
4123 #endif // wxUSE_DRAG_AND_DROP 
4125     bool simulateClick 
= false; 
4127     if (event
.ButtonDClick()) 
4129         m_renameTimer
->Stop(); 
4130         m_lastOnSame 
= false; 
4133     bool ignore_other_columns 
= 
4134         ((expander 
!= col
) && 
4135         (model
->IsContainer(item
)) && 
4136         (!model
->HasContainerColumns(item
))); 
4138     if (event
.LeftDClick()) 
4140         if(hoverOverExpander
) 
4142             // a double click on the expander will be converted into a "simulated" normal click 
4143             simulateClick 
= true; 
4145         else if ( current 
== m_lineLastClicked 
) 
4147             wxWindow 
*parent 
= GetParent(); 
4148             wxDataViewEvent 
le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED
, parent
->GetId()); 
4150             le
.SetColumn( col
->GetModelColumn() ); 
4151             le
.SetDataViewColumn( col 
); 
4152             le
.SetEventObject(parent
); 
4153             le
.SetModel(GetModel()); 
4155             parent
->ProcessWindowEvent(le
); 
4160             // The first click was on another item, so don't interpret this as 
4161             // a double click, but as a simple click instead 
4162             simulateClick 
= true; 
4166     if (event
.LeftUp() && !hoverOverExpander
) 
4168         if (m_lineSelectSingleOnUp 
!= (unsigned int)-1) 
4170             // select single line 
4171             SelectAllRows( false ); 
4172             SelectRow( m_lineSelectSingleOnUp
, true ); 
4173             SendSelectionChangedEvent( GetItemByRow(m_lineSelectSingleOnUp
) ); 
4176         // If the user click the expander, we do not do editing even if the column 
4177         // with expander are editable 
4178         if (m_lastOnSame 
&& !ignore_other_columns
) 
4180             if ((col 
== m_currentCol
) && (current 
== m_currentRow
) && 
4181                 IsCellEditableInMode(item
, col
, wxDATAVIEW_CELL_EDITABLE
) ) 
4183                 m_renameTimer
->Start( 100, true ); 
4187         m_lastOnSame 
= false; 
4188         m_lineSelectSingleOnUp 
= (unsigned int)-1; 
4190     else if(!event
.LeftUp()) 
4192         // This is necessary, because after a DnD operation in 
4193         // from and to ourself, the up event is swallowed by the 
4194         // DnD code. So on next non-up event (which means here and 
4195         // now) m_lineSelectSingleOnUp should be reset. 
4196         m_lineSelectSingleOnUp 
= (unsigned int)-1; 
4199     if (event
.RightDown()) 
4201         m_lineBeforeLastClicked 
= m_lineLastClicked
; 
4202         m_lineLastClicked 
= current
; 
4204         // If the item is already selected, do not update the selection. 
4205         // Multi-selections should not be cleared if a selected item is clicked. 
4206         if (!IsRowSelected(current
)) 
4208             SelectAllRows(false); 
4209             const unsigned oldCurrent 
= m_currentRow
; 
4210             ChangeCurrentRow(current
); 
4211             SelectRow(m_currentRow
,true); 
4212             RefreshRow(oldCurrent
); 
4213             SendSelectionChangedEvent(GetItemByRow( m_currentRow 
) ); 
4216     else if (event
.MiddleDown()) 
4220     if((event
.LeftDown() || simulateClick
) && hoverOverExpander
) 
4222         wxDataViewTreeNode
* node 
= GetTreeNodeByRow(current
); 
4224         // hoverOverExpander being true tells us that our node must be 
4225         // valid and have children. 
4226         // So we don't need any extra checks. 
4227         if( node
->IsOpen() ) 
4232     else if ((event
.LeftDown() || simulateClick
) && !hoverOverExpander
) 
4234         m_lineBeforeLastClicked 
= m_lineLastClicked
; 
4235         m_lineLastClicked 
= current
; 
4237         unsigned int oldCurrentRow 
= m_currentRow
; 
4238         bool oldWasSelected 
= IsRowSelected(m_currentRow
); 
4240         bool cmdModifierDown 
= event
.CmdDown(); 
4241         if ( IsSingleSel() || !(cmdModifierDown 
|| event
.ShiftDown()) ) 
4243             if ( IsSingleSel() || !IsRowSelected(current
) ) 
4245                 SelectAllRows( false ); 
4246                 ChangeCurrentRow(current
); 
4247                 SelectRow(m_currentRow
,true); 
4248                 SendSelectionChangedEvent(GetItemByRow( m_currentRow 
) ); 
4250             else // multi sel & current is highlighted & no mod keys 
4252                 m_lineSelectSingleOnUp 
= current
; 
4253                 ChangeCurrentRow(current
); // change focus 
4256         else // multi sel & either ctrl or shift is down 
4258             if (cmdModifierDown
) 
4260                 ChangeCurrentRow(current
); 
4261                 ReverseRowSelection(m_currentRow
); 
4262                 SendSelectionChangedEvent(GetItemByRow(m_currentRow
)); 
4264             else if (event
.ShiftDown()) 
4266                 ChangeCurrentRow(current
); 
4268                 unsigned int lineFrom 
= oldCurrentRow
, 
4271                 if ( lineTo 
< lineFrom 
) 
4274                     lineFrom 
= m_currentRow
; 
4277                 SelectRows(lineFrom
, lineTo
, true); 
4278                 SendSelectionChangedEvent(GetItemByRow(m_selection
[0]) ); 
4280             else // !ctrl, !shift 
4282                 // test in the enclosing if should make it impossible 
4283                 wxFAIL_MSG( wxT("how did we get here?") ); 
4287         if (m_currentRow 
!= oldCurrentRow
) 
4288             RefreshRow( oldCurrentRow 
); 
4290         wxDataViewColumn 
*oldCurrentCol 
= m_currentCol
; 
4292         // Update selection here... 
4294         m_currentColSetByKeyboard 
= false; 
4296         // This flag is used to decide whether we should start editing the item 
4297         // label. We do it if the user clicks twice (but not double clicks, 
4298         // i.e. simulateClick is false) on the same item but not if the click 
4299         // was used for something else already, e.g. selecting the item (so it 
4300         // must have been already selected) or giving the focus to the control 
4301         // (so it must have had focus already). 
4302         m_lastOnSame 
= !simulateClick 
&& ((col 
== oldCurrentCol
) && 
4303                         (current 
== oldCurrentRow
)) && oldWasSelected 
&& 
4306         // Call ActivateCell() after everything else as under GTK+ 
4307         if ( IsCellEditableInMode(item
, col
, wxDATAVIEW_CELL_ACTIVATABLE
) ) 
4309             // notify cell about click 
4310             cell
->PrepareForItem(model
, item
, col
->GetModelColumn()); 
4312             wxRect 
cell_rect( xpos 
+ itemOffset
, 
4313                               GetLineStart( current 
), 
4314                               col
->GetWidth() - itemOffset
, 
4315                               GetLineHeight( current 
) ); 
4317             // Report position relative to the cell's custom area, i.e. 
4318             // no the entire space as given by the control but the one 
4319             // used by the renderer after calculation of alignment etc. 
4321             // adjust the rectangle ourselves to account for the alignment 
4322             wxRect rectItem 
= cell_rect
; 
4323             const int align 
= cell
->GetAlignment(); 
4324             if ( align 
!= wxDVR_DEFAULT_ALIGNMENT 
) 
4326                 const wxSize size 
= cell
->GetSize(); 
4328                 if ( size
.x 
>= 0 && size
.x 
< cell_rect
.width 
) 
4330                     if ( align 
& wxALIGN_CENTER_HORIZONTAL 
) 
4331                         rectItem
.x 
+= (cell_rect
.width 
- size
.x
)/2; 
4332                     else if ( align 
& wxALIGN_RIGHT 
) 
4333                         rectItem
.x 
+= cell_rect
.width 
- size
.x
; 
4334                     // else: wxALIGN_LEFT is the default 
4337                 if ( size
.y 
>= 0 && size
.y 
< cell_rect
.height 
) 
4339                     if ( align 
& wxALIGN_CENTER_VERTICAL 
) 
4340                         rectItem
.y 
+= (cell_rect
.height 
- size
.y
)/2; 
4341                     else if ( align 
& wxALIGN_BOTTOM 
) 
4342                         rectItem
.y 
+= cell_rect
.height 
- size
.y
; 
4343                     // else: wxALIGN_TOP is the default 
4347             wxMouseEvent 
event2(event
); 
4348             event2
.m_x 
-= rectItem
.x
; 
4349             event2
.m_y 
-= rectItem
.y
; 
4350             m_owner
->CalcUnscrolledPosition(event2
.m_x
, event2
.m_y
, &event2
.m_x
, &event2
.m_y
); 
4352              /* ignore ret */ cell
->WXActivateCell
 
4357                                         col
->GetModelColumn(), 
4364 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent 
&event 
) 
4368     if (HasCurrentRow()) 
4374 void wxDataViewMainWindow::OnKillFocus( wxFocusEvent 
&event 
) 
4378     if (HasCurrentRow()) 
4384 void wxDataViewMainWindow::OnColumnsCountChanged() 
4386     int editableCount 
= 0; 
4388     const unsigned cols 
= GetOwner()->GetColumnCount(); 
4389     for ( unsigned i 
= 0; i 
< cols
; i
++ ) 
4391         wxDataViewColumn 
*c 
= GetOwner()->GetColumnAt(i
); 
4392         if ( c
->IsHidden() ) 
4394         if ( c
->GetRenderer()->GetMode() != wxDATAVIEW_CELL_INERT 
) 
4398     m_useCellFocus 
= (editableCount 
> 1); 
4403 //----------------------------------------------------------------------------- 
4405 //----------------------------------------------------------------------------- 
4407 WX_DEFINE_LIST(wxDataViewColumnList
) 
4409 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl
, wxDataViewCtrlBase
) 
4410 BEGIN_EVENT_TABLE(wxDataViewCtrl
, wxDataViewCtrlBase
) 
4411     EVT_SIZE(wxDataViewCtrl::OnSize
) 
4414 wxDataViewCtrl::~wxDataViewCtrl() 
4417         GetModel()->RemoveNotifier( m_notifier 
); 
4420     m_colsBestWidths
.clear(); 
4423 void wxDataViewCtrl::Init() 
4425     m_cols
.DeleteContents(true); 
4428     // No sorting column at start 
4429     m_sortingColumnIdx 
= wxNOT_FOUND
; 
4431     m_headerArea 
= NULL
; 
4433     m_colsDirty 
= false; 
4436 bool wxDataViewCtrl::Create(wxWindow 
*parent
, 
4441                             const wxValidator
& validator
, 
4442                             const wxString
& name
) 
4444 //    if ( (style & wxBORDER_MASK) == 0) 
4445 //        style |= wxBORDER_SUNKEN; 
4449     if (!wxControl::Create( parent
, id
, pos
, size
, 
4450                             style 
| wxScrolledWindowStyle
, validator
, name
)) 
4453     SetInitialSize(size
); 
4456     MacSetClipChildren( true ); 
4459     m_clientArea 
= new wxDataViewMainWindow( this, wxID_ANY 
); 
4461     // We use the cursor keys for moving the selection, not scrolling, so call 
4462     // this method to ensure wxScrollHelperEvtHandler doesn't catch all 
4463     // keyboard events forwarded to us from wxListMainWindow. 
4464     DisableKeyboardScrolling(); 
4466     if (HasFlag(wxDV_NO_HEADER
)) 
4467         m_headerArea 
= NULL
; 
4469         m_headerArea 
= new wxDataViewHeaderWindow(this); 
4471     SetTargetWindow( m_clientArea 
); 
4473     wxBoxSizer 
*sizer 
= new wxBoxSizer( wxVERTICAL 
); 
4475         sizer
->Add( m_headerArea
, 0, wxGROW 
); 
4476     sizer
->Add( m_clientArea
, 1, wxGROW 
); 
4482 wxBorder 
wxDataViewCtrl::GetDefaultBorder() const 
4484     return wxBORDER_THEME
; 
4488 WXLRESULT 
wxDataViewCtrl::MSWWindowProc(WXUINT nMsg
, 
4492     WXLRESULT rc 
= wxDataViewCtrlBase::MSWWindowProc(nMsg
, wParam
, lParam
); 
4495     // we need to process arrows ourselves for scrolling 
4496     if ( nMsg 
== WM_GETDLGCODE 
) 
4498         rc 
|= DLGC_WANTARROWS
; 
4506 wxSize 
wxDataViewCtrl::GetSizeAvailableForScrollTarget(const wxSize
& size
) 
4508     wxSize newsize 
= size
; 
4509     if (!HasFlag(wxDV_NO_HEADER
) && (m_headerArea
)) 
4510     newsize
.y 
-= m_headerArea
->GetSize().y
; 
4515 void wxDataViewCtrl::OnSize( wxSizeEvent 
&WXUNUSED(event
) ) 
4517     // We need to override OnSize so that our scrolled 
4518     // window a) does call Layout() to use sizers for 
4519     // positioning the controls but b) does not query 
4520     // the sizer for their size and use that for setting 
4521     // the scrollable area as set that ourselves by 
4522     // calling SetScrollbar() further down. 
4528     // We must redraw the headers if their height changed. Normally this 
4529     // shouldn't happen as the control shouldn't let itself be resized beneath 
4530     // its minimal height but avoid the display artefacts that appear if it 
4531     // does happen, e.g. because there is really not enough vertical space. 
4532     if ( !HasFlag(wxDV_NO_HEADER
) && m_headerArea 
&& 
4533             m_headerArea
->GetSize().y 
<= m_headerArea
->GetBestSize(). y 
) 
4535         m_headerArea
->Refresh(); 
4539 void wxDataViewCtrl::SetFocus() 
4542         m_clientArea
->SetFocus(); 
4545 bool wxDataViewCtrl::AssociateModel( wxDataViewModel 
*model 
) 
4547     if (!wxDataViewCtrlBase::AssociateModel( model 
)) 
4552         m_notifier 
= new wxGenericDataViewModelNotifier( m_clientArea 
); 
4553         model
->AddNotifier( m_notifier 
); 
4555     else if (m_notifier
) 
4557         m_notifier
->Cleared(); 
4561     m_clientArea
->DestroyTree(); 
4565         m_clientArea
->BuildTree(model
); 
4568     m_clientArea
->UpdateDisplay(); 
4573 #if wxUSE_DRAG_AND_DROP 
4575 bool wxDataViewCtrl::EnableDragSource( const wxDataFormat 
&format 
) 
4577     return m_clientArea
->EnableDragSource( format 
); 
4580 bool wxDataViewCtrl::EnableDropTarget( const wxDataFormat 
&format 
) 
4582     return m_clientArea
->EnableDropTarget( format 
); 
4585 #endif // wxUSE_DRAG_AND_DROP 
4587 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn 
*col 
) 
4589     if (!wxDataViewCtrlBase::AppendColumn(col
)) 
4592     m_cols
.Append( col 
); 
4593     m_colsBestWidths
.push_back(CachedColWidthInfo()); 
4594     OnColumnsCountChanged(); 
4598 bool wxDataViewCtrl::PrependColumn( wxDataViewColumn 
*col 
) 
4600     if (!wxDataViewCtrlBase::PrependColumn(col
)) 
4603     m_cols
.Insert( col 
); 
4604     m_colsBestWidths
.insert(m_colsBestWidths
.begin(), CachedColWidthInfo()); 
4605     OnColumnsCountChanged(); 
4609 bool wxDataViewCtrl::InsertColumn( unsigned int pos
, wxDataViewColumn 
*col 
) 
4611     if (!wxDataViewCtrlBase::InsertColumn(pos
,col
)) 
4614     m_cols
.Insert( pos
, col 
); 
4615     m_colsBestWidths
.insert(m_colsBestWidths
.begin() + pos
, CachedColWidthInfo()); 
4616     OnColumnsCountChanged(); 
4620 void wxDataViewCtrl::OnColumnChange(unsigned int idx
) 
4623         m_headerArea
->UpdateColumn(idx
); 
4625     m_clientArea
->UpdateDisplay(); 
4628 void wxDataViewCtrl::OnColumnsCountChanged() 
4631         m_headerArea
->SetColumnCount(GetColumnCount()); 
4633     m_clientArea
->OnColumnsCountChanged(); 
4636 void wxDataViewCtrl::DoSetExpanderColumn() 
4638     m_clientArea
->UpdateDisplay(); 
4641 void wxDataViewCtrl::DoSetIndent() 
4643     m_clientArea
->UpdateDisplay(); 
4646 unsigned int wxDataViewCtrl::GetColumnCount() const 
4648     return m_cols
.GetCount(); 
4651 bool wxDataViewCtrl::SetRowHeight( int lineHeight 
) 
4653     if ( !m_clientArea 
) 
4656     m_clientArea
->SetRowHeight(lineHeight
); 
4661 wxDataViewColumn
* wxDataViewCtrl::GetColumn( unsigned int idx 
) const 
4666 wxDataViewColumn 
*wxDataViewCtrl::GetColumnAt(unsigned int pos
) const 
4668     // columns can't be reordered if there is no header window which allows 
4670     const unsigned idx 
= m_headerArea 
? m_headerArea
->GetColumnsOrder()[pos
] 
4673     return GetColumn(idx
); 
4676 int wxDataViewCtrl::GetColumnIndex(const wxDataViewColumn 
*column
) const 
4678     const unsigned count 
= m_cols
.size(); 
4679     for ( unsigned n 
= 0; n 
< count
; n
++ ) 
4681         if ( m_cols
[n
] == column 
) 
4688 unsigned int wxDataViewCtrl::GetBestColumnWidth(int idx
) const 
4690     if ( m_colsBestWidths
[idx
].width 
!= 0 ) 
4691         return m_colsBestWidths
[idx
].width
; 
4693     const int count 
= m_clientArea
->GetRowCount(); 
4694     wxDataViewColumn 
*column 
= GetColumn(idx
); 
4695     wxDataViewRenderer 
*renderer 
= 
4696         const_cast<wxDataViewRenderer
*>(column
->GetRenderer()); 
4698     class MaxWidthCalculator
 
4701         MaxWidthCalculator(const wxDataViewCtrl 
*dvc
, 
4702                            wxDataViewMainWindow 
*clientArea
, 
4703                            wxDataViewRenderer 
*renderer
, 
4704                            const wxDataViewModel 
*model
, 
4709               m_clientArea(clientArea
), 
4710               m_renderer(renderer
), 
4713               m_expanderSize(expanderSize
) 
4717                 !clientArea
->IsList() && 
4719                  GetExpanderColumnOrFirstOne(const_cast<wxDataViewCtrl
*>(dvc
)) == dvc
->GetColumnAt(column
)); 
4722         void UpdateWithWidth(int width
) 
4724             m_width 
= wxMax(m_width
, width
); 
4727         void UpdateWithRow(int row
) 
4730             wxDataViewItem item
; 
4732             if ( m_isExpanderCol 
) 
4734                 wxDataViewTreeNode 
*node 
= m_clientArea
->GetTreeNodeByRow(row
); 
4735                 item 
= node
->GetItem(); 
4736                 indent 
= m_dvc
->GetIndent() * node
->GetIndentLevel() + m_expanderSize
; 
4740                 item 
= m_clientArea
->GetItemByRow(row
); 
4743             m_renderer
->PrepareForItem(m_model
, item
, m_column
); 
4744             m_width 
= wxMax(m_width
, m_renderer
->GetSize().x 
+ indent
); 
4747         int GetMaxWidth() const { return m_width
; } 
4751         const wxDataViewCtrl 
*m_dvc
; 
4752         wxDataViewMainWindow 
*m_clientArea
; 
4753         wxDataViewRenderer 
*m_renderer
; 
4754         const wxDataViewModel 
*m_model
; 
4756         bool m_isExpanderCol
; 
4760     MaxWidthCalculator 
calculator(this, m_clientArea
, renderer
, 
4761                                   GetModel(), column
->GetModelColumn(), 
4762                                   m_clientArea
->GetRowHeight()); 
4764     calculator
.UpdateWithWidth(column
->GetMinWidth()); 
4767         calculator
.UpdateWithWidth(m_headerArea
->GetColumnTitleWidth(*column
)); 
4769     // The code below deserves some explanation. For very large controls, we 
4770     // simply can't afford to calculate sizes for all items, it takes too 
4771     // long. So the best we can do is to check the first and the last N/2 
4772     // items in the control for some sufficiently large N and calculate best 
4773     // sizes from that. That can result in the calculated best width being too 
4774     // small for some outliers, but it's better to get slightly imperfect 
4775     // result than to wait several seconds after every update. To avoid highly 
4776     // visible miscalculations, we also include all currently visible items 
4777     // no matter what.  Finally, the value of N is determined dynamically by 
4778     // measuring how much time we spent on the determining item widths so far. 
4781     int top_part_end 
= count
; 
4782     static const long CALC_TIMEOUT 
= 20/*ms*/; 
4783     // don't call wxStopWatch::Time() too often 
4784     static const unsigned CALC_CHECK_FREQ 
= 100; 
4787     // use some hard-coded limit, that's the best we can do without timer 
4788     int top_part_end 
= wxMin(500, count
); 
4789 #endif // wxUSE_STOPWATCH/!wxUSE_STOPWATCH 
4793     for ( row 
= 0; row 
< top_part_end
; row
++ ) 
4796         if ( row 
% CALC_CHECK_FREQ 
== CALC_CHECK_FREQ
-1 && 
4797              timer
.Time() > CALC_TIMEOUT 
) 
4799 #endif // wxUSE_STOPWATCH 
4800         calculator
.UpdateWithRow(row
); 
4803     // row is the first unmeasured item now; that's our value of N/2 
4809         // add bottom N/2 items now: 
4810         const int bottom_part_start 
= wxMax(row
, count 
- row
); 
4811         for ( row 
= bottom_part_start
; row 
< count
; row
++ ) 
4813             calculator
.UpdateWithRow(row
); 
4816         // finally, include currently visible items in the calculation: 
4817         const wxPoint origin 
= CalcUnscrolledPosition(wxPoint(0, 0)); 
4818         int first_visible 
= m_clientArea
->GetLineAt(origin
.y
); 
4819         int last_visible 
= m_clientArea
->GetLineAt(origin
.y 
+ GetClientSize().y
); 
4821         first_visible 
= wxMax(first_visible
, top_part_end
); 
4822         last_visible 
= wxMin(bottom_part_start
, last_visible
); 
4824         for ( row 
= first_visible
; row 
< last_visible
; row
++ ) 
4826             calculator
.UpdateWithRow(row
); 
4829         wxLogTrace("dataview", 
4830                    "determined best size from %d top, %d bottom plus %d more visible items out of %d total", 
4832                    count 
- bottom_part_start
, 
4833                    wxMax(0, last_visible 
- first_visible
), 
4837     int max_width 
= calculator
.GetMaxWidth(); 
4838     if ( max_width 
> 0 ) 
4839         max_width 
+= 2 * PADDING_RIGHTLEFT
; 
4841     const_cast<wxDataViewCtrl
*>(this)->m_colsBestWidths
[idx
].width 
= max_width
; 
4845 void wxDataViewCtrl::ColumnMoved(wxDataViewColumn 
* WXUNUSED(col
), 
4846                                 unsigned int WXUNUSED(new_pos
)) 
4848     // do _not_ reorder m_cols elements here, they should always be in the 
4849     // order in which columns were added, we only display the columns in 
4851     m_clientArea
->UpdateDisplay(); 
4854 bool wxDataViewCtrl::DeleteColumn( wxDataViewColumn 
*column 
) 
4856     wxDataViewColumnList::compatibility_iterator ret 
= m_cols
.Find( column 
); 
4860     m_colsBestWidths
.erase(m_colsBestWidths
.begin() + GetColumnIndex(column
)); 
4863     if ( m_clientArea
->GetCurrentColumn() == column 
) 
4864         m_clientArea
->ClearCurrentColumn(); 
4866     OnColumnsCountChanged(); 
4871 bool wxDataViewCtrl::ClearColumns() 
4873     SetExpanderColumn(NULL
); 
4875     m_colsBestWidths
.clear(); 
4877     m_clientArea
->ClearCurrentColumn(); 
4879     OnColumnsCountChanged(); 
4884 void wxDataViewCtrl::InvalidateColBestWidth(int idx
) 
4886     m_colsBestWidths
[idx
].width 
= 0; 
4887     m_colsBestWidths
[idx
].dirty 
= true; 
4891 void wxDataViewCtrl::InvalidateColBestWidths() 
4893     // mark all columns as dirty: 
4894     m_colsBestWidths
.clear(); 
4895     m_colsBestWidths
.resize(m_cols
.size()); 
4899 void wxDataViewCtrl::UpdateColWidths() 
4901     m_colsDirty 
= false; 
4903     if ( !m_headerArea 
) 
4906     const unsigned len 
= m_colsBestWidths
.size(); 
4907     for ( unsigned i 
= 0; i 
< len
; i
++ ) 
4909         // Note that we have to have an explicit 'dirty' flag here instead of 
4910         // checking if the width==0, as is done in GetBestColumnWidth(). 
4912         // Testing width==0 wouldn't work correctly if some code called 
4913         // GetWidth() after col. width invalidation but before 
4914         // wxDataViewCtrl::UpdateColWidths() was called at idle time. This 
4915         // would result in the header's column width getting out of sync with 
4916         // the control itself. 
4917         if ( m_colsBestWidths
[i
].dirty 
) 
4919             m_headerArea
->UpdateColumn(i
); 
4920             m_colsBestWidths
[i
].dirty 
= false; 
4925 void wxDataViewCtrl::OnInternalIdle() 
4927     wxDataViewCtrlBase::OnInternalIdle(); 
4933 int wxDataViewCtrl::GetColumnPosition( const wxDataViewColumn 
*column 
) const 
4935     unsigned int len 
= GetColumnCount(); 
4936     for ( unsigned int i 
= 0; i 
< len
; i
++ ) 
4938         wxDataViewColumn 
* col 
= GetColumnAt(i
); 
4946 wxDataViewColumn 
*wxDataViewCtrl::GetSortingColumn() const 
4948     return m_sortingColumnIdx 
== wxNOT_FOUND 
? NULL
 
4949                                             : GetColumn(m_sortingColumnIdx
); 
4952 wxDataViewItem 
wxDataViewCtrl::DoGetCurrentItem() const 
4954     return GetItemByRow(m_clientArea
->GetCurrentRow()); 
4957 void wxDataViewCtrl::DoSetCurrentItem(const wxDataViewItem
& item
) 
4959     const int row 
= m_clientArea
->GetRowByItem(item
); 
4961     const unsigned oldCurrent 
= m_clientArea
->GetCurrentRow(); 
4962     if ( static_cast<unsigned>(row
) != oldCurrent 
) 
4964         m_clientArea
->ChangeCurrentRow(row
); 
4965         m_clientArea
->RefreshRow(oldCurrent
); 
4966         m_clientArea
->RefreshRow(row
); 
4970 wxDataViewColumn 
*wxDataViewCtrl::GetCurrentColumn() const 
4972     return m_clientArea
->GetCurrentColumn(); 
4975 int wxDataViewCtrl::GetSelectedItemsCount() const 
4977     return m_clientArea
->GetSelections().size(); 
4980 int wxDataViewCtrl::GetSelections( wxDataViewItemArray 
& sel 
) const 
4983     const wxDataViewSelection
& selections 
= m_clientArea
->GetSelections(); 
4985     const size_t len 
= selections
.size(); 
4986     for ( size_t i 
= 0; i 
< len
; i
++ ) 
4988         wxDataViewItem item 
= m_clientArea
->GetItemByRow(selections
[i
]); 
4995             wxFAIL_MSG( "invalid item in selection - bad internal state" ); 
5002 void wxDataViewCtrl::SetSelections( const wxDataViewItemArray 
& sel 
) 
5004     wxDataViewSelection 
selection(wxDataViewSelectionCmp
); 
5006     wxDataViewItem last_parent
; 
5008     int len 
= sel
.GetCount(); 
5009     for( int i 
= 0; i 
< len
; i 
++ ) 
5011         wxDataViewItem item 
= sel
[i
]; 
5012         wxDataViewItem parent 
= GetModel()->GetParent( item 
); 
5015             if (parent 
!= last_parent
) 
5016                 ExpandAncestors(item
); 
5019         last_parent 
= parent
; 
5020         int row 
= m_clientArea
->GetRowByItem( item 
); 
5022             selection
.Add( static_cast<unsigned int>(row
) ); 
5025     m_clientArea
->SetSelections( selection 
); 
5028 void wxDataViewCtrl::Select( const wxDataViewItem 
& item 
) 
5030     ExpandAncestors( item 
); 
5032     int row 
= m_clientArea
->GetRowByItem( item 
); 
5035         // Unselect all rows before select another in the single select mode 
5036         if (m_clientArea
->IsSingleSel()) 
5037             m_clientArea
->SelectAllRows(false); 
5039         m_clientArea
->SelectRow(row
, true); 
5041         // Also set focus to the selected item 
5042         m_clientArea
->ChangeCurrentRow( row 
); 
5046 void wxDataViewCtrl::Unselect( const wxDataViewItem 
& item 
) 
5048     int row 
= m_clientArea
->GetRowByItem( item 
); 
5050         m_clientArea
->SelectRow(row
, false); 
5053 bool wxDataViewCtrl::IsSelected( const wxDataViewItem 
& item 
) const 
5055     int row 
= m_clientArea
->GetRowByItem( item 
); 
5058         return m_clientArea
->IsRowSelected(row
); 
5063 void wxDataViewCtrl::SetAlternateRowColour(const wxColour
& colour
) 
5065     m_alternateRowColour 
= colour
; 
5068 void wxDataViewCtrl::SelectAll() 
5070     m_clientArea
->SelectAllRows(true); 
5073 void wxDataViewCtrl::UnselectAll() 
5075     m_clientArea
->SelectAllRows(false); 
5078 void wxDataViewCtrl::EnsureVisible( int row
, int column 
) 
5082     if( row 
> (int) m_clientArea
->GetRowCount() ) 
5083         row 
= m_clientArea
->GetRowCount(); 
5085     int first 
= m_clientArea
->GetFirstVisibleRow(); 
5086     int last 
= m_clientArea
->GetLastVisibleRow(); 
5088         m_clientArea
->ScrollTo( row
, column 
); 
5089     else if( row 
> last 
) 
5090         m_clientArea
->ScrollTo( row 
- last 
+ first
, column 
); 
5092         m_clientArea
->ScrollTo( first
, column 
); 
5095 void wxDataViewCtrl::EnsureVisible( const wxDataViewItem 
& item
, const wxDataViewColumn 
* column 
) 
5097     ExpandAncestors( item 
); 
5099     m_clientArea
->RecalculateDisplay(); 
5101     int row 
= m_clientArea
->GetRowByItem(item
); 
5104         if( column 
== NULL 
) 
5105             EnsureVisible(row
, -1); 
5107             EnsureVisible( row
, GetColumnIndex(column
) ); 
5112 void wxDataViewCtrl::HitTest( const wxPoint 
& point
, wxDataViewItem 
& item
, 
5113                               wxDataViewColumn
* &column 
) const 
5115     m_clientArea
->HitTest(point
, item
, column
); 
5118 wxRect 
wxDataViewCtrl::GetItemRect( const wxDataViewItem 
& item
, 
5119                                     const wxDataViewColumn
* column 
) const 
5121     return m_clientArea
->GetItemRect(item
, column
); 
5124 wxDataViewItem 
wxDataViewCtrl::GetItemByRow( unsigned int row 
) const 
5126     return m_clientArea
->GetItemByRow( row 
); 
5129 int wxDataViewCtrl::GetRowByItem( const wxDataViewItem 
& item 
) const 
5131     return m_clientArea
->GetRowByItem( item 
); 
5134 void wxDataViewCtrl::Expand( const wxDataViewItem 
& item 
) 
5136     ExpandAncestors( item 
); 
5138     int row 
= m_clientArea
->GetRowByItem( item 
); 
5141         m_clientArea
->Expand(row
); 
5142         InvalidateColBestWidths(); 
5146 void wxDataViewCtrl::Collapse( const wxDataViewItem 
& item 
) 
5148     int row 
= m_clientArea
->GetRowByItem( item 
); 
5151         m_clientArea
->Collapse(row
); 
5152         InvalidateColBestWidths(); 
5156 bool wxDataViewCtrl::IsExpanded( const wxDataViewItem 
& item 
) const 
5158     int row 
= m_clientArea
->GetRowByItem( item 
); 
5160         return m_clientArea
->IsExpanded(row
); 
5164 void wxDataViewCtrl::EditItem(const wxDataViewItem
& item
, const wxDataViewColumn 
*column
) 
5166     wxCHECK_RET( item
.IsOk(), "invalid item" ); 
5167     wxCHECK_RET( column
, "no column provided" ); 
5169     m_clientArea
->StartEditing(item
, column
); 
5172 #endif // !wxUSE_GENERICDATAVIEWCTRL 
5174 #endif // wxUSE_DATAVIEWCTRL