X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/99f44d9704f7a45fb4ee627336798eae4c941a1e..276f3938c4ae6420a05ad2a7667fdf79261c33d6:/src/generic/datavgen.cpp diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index 84520f5cce..f9a43c7075 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -83,6 +83,30 @@ static wxDataViewModel* g_model; static int g_column = -2; static bool g_asending = true; +// ---------------------------------------------------------------------------- +// helper functions +// ---------------------------------------------------------------------------- + +namespace +{ + +// Return the expander column or, if it is not set, the first column and also +// set it as the expander one for the future. +wxDataViewColumn* GetExpanderColumnOrFirstOne(wxDataViewCtrl* dataview) +{ + wxDataViewColumn* expander = dataview->GetExpanderColumn(); + if (!expander) + { + // TODO-RTL: last column for RTL support + expander = dataview->GetColumnAt( 0 ); + dataview->SetExpanderColumn(expander); + } + + return expander; +} + +} // anonymous namespace + //----------------------------------------------------------------------------- // wxDataViewColumn //----------------------------------------------------------------------------- @@ -122,6 +146,30 @@ void wxDataViewColumn::UpdateDisplay() } } +void wxDataViewColumn::SetSortOrder(bool ascending) +{ + if ( !m_owner ) + return; + + // First unset the old sort column if any. + int oldSortKey = m_owner->GetSortingColumnIndex(); + if ( oldSortKey != wxNOT_FOUND ) + { + m_owner->GetColumn(oldSortKey)->UnsetAsSortKey(); + } + + // Now set this one as the new sort column. + const int idx = m_owner->GetColumnIndex(this); + m_owner->SetSortingColumnIndex(idx); + + m_sort = true; + m_sortAscending = ascending; + + // Call this directly instead of using UpdateDisplay() as we already have + // the column index, no need to look it up again. + m_owner->OnColumnChange(idx); +} + //----------------------------------------------------------------------------- // wxDataViewHeaderWindow //----------------------------------------------------------------------------- @@ -169,7 +217,7 @@ private: // for events created by wxDataViewHeaderWindow the // row / value fields are not valid - return owner->GetEventHandler()->ProcessEvent(event); + return owner->ProcessWindowEvent(event); } void OnClick(wxHeaderCtrlEvent& event) @@ -197,16 +245,7 @@ private: } else // not using this column for sorting yet { - // first unset the old sort column if any - int oldSortKey = owner->GetSortingColumnIndex(); - if ( oldSortKey != wxNOT_FOUND ) - { - owner->GetColumn(oldSortKey)->UnsetAsSortKey(); - owner->OnColumnChange(oldSortKey); - } - - owner->SetSortingColumnIndex(idx); - col->SetAsSortKey(); + col->SetSortOrder(true); } wxDataViewModel * const model = owner->GetModel(); @@ -214,6 +253,8 @@ private: model->Resort(); owner->OnColumnChange(idx); + + SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_SORTED, idx); } void OnRClick(wxHeaderCtrlEvent& event) @@ -273,57 +314,73 @@ public: class wxDataViewTreeNode; WX_DEFINE_ARRAY( wxDataViewTreeNode *, wxDataViewTreeNodes ); -WX_DEFINE_ARRAY( void* , wxDataViewTreeLeaves); int LINKAGEMODE wxGenericTreeModelNodeCmp( wxDataViewTreeNode ** node1, wxDataViewTreeNode ** node2); -int LINKAGEMODE wxGenericTreeModelItemCmp( void ** id1, void ** id2); class wxDataViewTreeNode { public: - wxDataViewTreeNode( wxDataViewTreeNode * parent = NULL ) + wxDataViewTreeNode(wxDataViewTreeNode *parent, const wxDataViewItem& item) + : m_item(item), + m_parent(parent), + m_branchData(NULL) { - m_parent = parent; - if (!parent) - m_open = true; - else - m_open = false; - m_hasChildren = false; - m_subTreeCount = 0; } ~wxDataViewTreeNode() { + if ( m_branchData ) + { + wxDataViewTreeNodes& nodes = m_branchData->children; + for ( wxDataViewTreeNodes::iterator i = nodes.begin(); + i != nodes.end(); + ++i ) + { + delete *i; + } + + delete m_branchData; + } + } + + static wxDataViewTreeNode* CreateRootNode() + { + wxDataViewTreeNode *n = new wxDataViewTreeNode(NULL, wxDataViewItem()); + n->SetHasChildren(true); + n->m_branchData->open = true; + return n; } wxDataViewTreeNode * GetParent() const { return m_parent; } - void SetParent( wxDataViewTreeNode * parent ) { m_parent = parent; } - wxDataViewTreeNodes & GetNodes() { return m_nodes; } - wxDataViewTreeLeaves & GetChildren() { return m_leaves; } - void AddNode( wxDataViewTreeNode * node ) + const wxDataViewTreeNodes& GetChildNodes() const { - m_leaves.Add( node->GetItem().GetID() ); - if (g_column >= -1) - m_leaves.Sort( &wxGenericTreeModelItemCmp ); - m_nodes.Add( node ); - if (g_column >= -1) - m_nodes.Sort( &wxGenericTreeModelNodeCmp ); + wxASSERT( m_branchData != NULL ); + return m_branchData->children; } - void AddLeaf( void * leaf ) + + void InsertChild(wxDataViewTreeNode *node, unsigned index) { - m_leaves.Add( leaf ); + if ( !m_branchData ) + m_branchData = new BranchNodeData; + + m_branchData->children.Insert(node, index); + + // TODO: insert into sorted array directly in O(log n) instead of resorting in O(n log n) if (g_column >= -1) - m_leaves.Sort( &wxGenericTreeModelItemCmp ); + m_branchData->children.Sort( &wxGenericTreeModelNodeCmp ); + } + + void RemoveChild(wxDataViewTreeNode *node) + { + wxCHECK_RET( m_branchData != NULL, "leaf node doesn't have children" ); + m_branchData->children.Remove(node); } - wxDataViewItem & GetItem() { return m_item; } const wxDataViewItem & GetItem() const { return m_item; } void SetItem( const wxDataViewItem & item ) { m_item = item; } - unsigned int GetChildrenNumber() const { return m_leaves.GetCount(); } - unsigned int GetNodeNumber() const { return m_nodes.GetCount(); } int GetIndentLevel() const { int ret = 0; @@ -338,84 +395,136 @@ public: bool IsOpen() const { - return m_open; + return m_branchData && m_branchData->open; } void ToggleOpen() { - int len = m_nodes.GetCount(); + wxCHECK_RET( m_branchData != NULL, "can't open leaf node" ); + int sum = 0; + + const wxDataViewTreeNodes& nodes = m_branchData->children; + const int len = nodes.GetCount(); for ( int i = 0;i < len; i ++) - sum += m_nodes[i]->GetSubTreeCount(); + sum += 1 + nodes[i]->GetSubTreeCount(); - sum += m_leaves.GetCount(); - if (m_open) + if (m_branchData->open) { ChangeSubTreeCount(-sum); - m_open = !m_open; + m_branchData->open = !m_branchData->open; } else { - m_open = !m_open; - ChangeSubTreeCount(sum); + m_branchData->open = !m_branchData->open; + ChangeSubTreeCount(+sum); + } + } + + // "HasChildren" property corresponds to model's IsContainer(). Note that it may be true + // even if GetChildNodes() is empty; see below. + bool HasChildren() const + { + return m_branchData != NULL; + } + + void SetHasChildren(bool has) + { + if ( !has ) + { + wxDELETE(m_branchData); + } + else if ( m_branchData == NULL ) + { + m_branchData = new BranchNodeData; } } - bool HasChildren() const { return m_hasChildren; } - void SetHasChildren( bool has ){ m_hasChildren = has; } - void SetSubTreeCount( int num ) { m_subTreeCount = num; } - int GetSubTreeCount() const { return m_subTreeCount; } + int GetSubTreeCount() const + { + return m_branchData ? m_branchData->subTreeCount : 0; + } + void ChangeSubTreeCount( int num ) { - if( !m_open ) + wxASSERT( m_branchData != NULL ); + + if( !m_branchData->open ) return; - m_subTreeCount += num; + + m_branchData->subTreeCount += num; + wxASSERT( m_branchData->subTreeCount >= 0 ); + if( m_parent ) m_parent->ChangeSubTreeCount(num); } void Resort() { + if ( !m_branchData ) + return; + if (g_column >= -1) { - m_nodes.Sort( &wxGenericTreeModelNodeCmp ); - int len = m_nodes.GetCount(); + wxDataViewTreeNodes& nodes = m_branchData->children; + + nodes.Sort( &wxGenericTreeModelNodeCmp ); + int len = nodes.GetCount(); for (int i = 0; i < len; i ++) - m_nodes[i]->Resort(); - m_leaves.Sort( &wxGenericTreeModelItemCmp ); + { + if ( nodes[i]->HasChildren() ) + nodes[i]->Resort(); + } } } + private: wxDataViewTreeNode *m_parent; - wxDataViewTreeNodes m_nodes; - wxDataViewTreeLeaves m_leaves; + + // Corresponding model item. wxDataViewItem m_item; - bool m_open; - bool m_hasChildren; - int m_subTreeCount; + + // Data specific to non-leaf (branch, inner) nodes. They are kept in a + // separate struct in order to conserve memory. + struct BranchNodeData + { + BranchNodeData() + : open(false), + subTreeCount(0) + { + } + + // Child nodes. Note that this may be empty even if m_hasChildren in + // case this branch of the tree wasn't expanded and realized yet. + wxDataViewTreeNodes children; + + // Is the branch node currently open (expanded)? + bool open; + + // Total count of expanded (i.e. visible with the help of some + // scrolling) items in the subtree, but excluding this node. I.e. it is + // 0 for leaves and is the number of rows the subtree occupies for + // branch nodes. + int subTreeCount; + }; + + BranchNodeData *m_branchData; }; + int LINKAGEMODE wxGenericTreeModelNodeCmp( wxDataViewTreeNode ** node1, wxDataViewTreeNode ** node2) { return g_model->Compare( (*node1)->GetItem(), (*node2)->GetItem(), g_column, g_asending ); } -int LINKAGEMODE wxGenericTreeModelItemCmp( void ** id1, void ** id2) -{ - return g_model->Compare( *id1, *id2, g_column, g_asending ); -} - //----------------------------------------------------------------------------- // wxDataViewMainWindow //----------------------------------------------------------------------------- -WX_DEFINE_SORTED_USER_EXPORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection, - WXDLLIMPEXP_ADV); -WX_DECLARE_LIST(wxDataViewItem, ItemList); -WX_DEFINE_LIST(ItemList) +WX_DEFINE_SORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection); class wxDataViewMainWindow: public wxWindow { @@ -427,7 +536,7 @@ public: const wxString &name = wxT("wxdataviewctrlmainwindow") ); virtual ~wxDataViewMainWindow(); - bool IsList() const { return GetOwner()->GetModel()->IsListModel(); } + bool IsList() const { return GetModel()->IsListModel(); } bool IsVirtualList() const { return m_root == NULL; } // notifications from wxDataViewModel @@ -448,7 +557,7 @@ public: void SortPrepare() { - g_model = GetOwner()->GetModel(); + g_model = GetModel(); wxDataViewColumn* col = GetOwner()->GetSortingColumn(); if( !col ) { @@ -468,12 +577,17 @@ public: wxDataViewCtrl *GetOwner() { return m_owner; } const wxDataViewCtrl *GetOwner() const { return m_owner; } + wxDataViewModel* GetModel() { return GetOwner()->GetModel(); } + const wxDataViewModel* GetModel() const { return GetOwner()->GetModel(); } + #if wxUSE_DRAG_AND_DROP wxBitmap CreateItemBitmap( unsigned int row, int &indent ); #endif // wxUSE_DRAG_AND_DROP void OnPaint( wxPaintEvent &event ); - void OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event); void OnChar( wxKeyEvent &event ); + void OnVerticalNavigation(unsigned int newCurrent, const wxKeyEvent& event); + void OnLeftKey(); + void OnRightKey(); void OnMouse( wxMouseEvent &event ); void OnSetFocus( wxFocusEvent &event ); void OnKillFocus( wxFocusEvent &event ); @@ -504,8 +618,7 @@ public: unsigned int GetLastVisibleRow(); unsigned int GetRowCount(); - wxDataViewItem GetSelection() const; - wxDataViewSelection GetSelections(){ return m_selection; } + const wxDataViewSelection& GetSelections() const { return m_selection; } void SetSelections( const wxDataViewSelection & sel ) { m_selection = sel; UpdateDisplay(); } void Select( const wxArrayInt& aSelections ); @@ -532,6 +645,8 @@ public: int GetLineHeight( unsigned int row ) const; // m_lineHeight in fixed mode int GetLineAt( unsigned int y ) const; // y / m_lineHeight in fixed mode + void SetRowHeight( int lineHeight ) { m_lineHeight = lineHeight; } + // Some useful functions for row and item mapping wxDataViewItem GetItemByRow( unsigned int row ) const; int GetRowByItem( const wxDataViewItem & item ) const; @@ -565,7 +680,8 @@ private: int RecalculateCount(); - wxDataViewEvent SendExpanderEvent( wxEventType type, const wxDataViewItem & item ); + // Return false only if the event was vetoed by its handler. + bool SendExpanderEvent(wxEventType type, const wxDataViewItem& item); wxDataViewTreeNode * FindNode( const wxDataViewItem & item ); @@ -732,12 +848,13 @@ bool wxDataViewTextRenderer::HasEditorCtrl() const return true; } -wxControl* wxDataViewTextRenderer::CreateEditorCtrl( wxWindow *parent, +wxWindow* wxDataViewTextRenderer::CreateEditorCtrl( wxWindow *parent, wxRect labelRect, const wxVariant &value ) { wxTextCtrl* ctrl = new wxTextCtrl( parent, wxID_ANY, value, wxPoint(labelRect.x,labelRect.y), - wxSize(labelRect.width,labelRect.height) ); + wxSize(labelRect.width,labelRect.height), + wxTE_PROCESS_ENTER ); // select the text in the control an place the cursor at the end ctrl->SetInsertionPointEnd(); @@ -746,7 +863,7 @@ wxControl* wxDataViewTextRenderer::CreateEditorCtrl( wxWindow *parent, return ctrl; } -bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxControl *editor, wxVariant &value ) +bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxWindow *editor, wxVariant &value ) { wxTextCtrl *text = (wxTextCtrl*) editor; value = text->GetValue(); @@ -796,9 +913,9 @@ bool wxDataViewBitmapRenderer::GetValue( wxVariant& WXUNUSED(value) ) const bool wxDataViewBitmapRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) ) { - if (m_bitmap.Ok()) + if (m_bitmap.IsOk()) dc->DrawBitmap( m_bitmap, cell.x, cell.y ); - else if (m_icon.Ok()) + else if (m_icon.IsOk()) dc->DrawIcon( m_icon, cell.x, cell.y ); return true; @@ -806,9 +923,9 @@ bool wxDataViewBitmapRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state wxSize wxDataViewBitmapRenderer::GetSize() const { - if (m_bitmap.Ok()) + if (m_bitmap.IsOk()) return wxSize( m_bitmap.GetWidth(), m_bitmap.GetHeight() ); - else if (m_icon.Ok()) + else if (m_icon.IsOk()) return wxSize( m_icon.GetWidth(), m_icon.GetHeight() ); return wxSize(wxDVC_DEFAULT_RENDERER_SIZE,wxDVC_DEFAULT_RENDERER_SIZE); @@ -863,11 +980,23 @@ bool wxDataViewToggleRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state return true; } -bool wxDataViewToggleRenderer::WXOnLeftClick(const wxPoint& WXUNUSED(cursor), - const wxRect& WXUNUSED(cell), +bool wxDataViewToggleRenderer::WXOnLeftClick(const wxPoint& cursor, + const wxRect& cell, wxDataViewModel *model, const wxDataViewItem& item, unsigned int col) +{ + // only react to clicks directly on the checkbox, not elsewhere in the same cell: + if (!wxRect(GetSize()).Contains(cursor)) + return false; + + return WXOnActivate(cell, model, item, col); +} + +bool wxDataViewToggleRenderer::WXOnActivate(const wxRect& WXUNUSED(cell), + wxDataViewModel *model, + const wxDataViewItem& item, + unsigned int col) { if (model->IsEnabled(item, col)) { @@ -1102,7 +1231,7 @@ wxSize wxDataViewIconTextRenderer::GetSize() const return wxSize(80,20); } -wxControl* wxDataViewIconTextRenderer::CreateEditorCtrl(wxWindow *parent, wxRect labelRect, const wxVariant& value) +wxWindow* wxDataViewIconTextRenderer::CreateEditorCtrl(wxWindow *parent, wxRect labelRect, const wxVariant& value) { wxDataViewIconText iconText; iconText << value; @@ -1119,7 +1248,8 @@ wxControl* wxDataViewIconTextRenderer::CreateEditorCtrl(wxWindow *parent, wxRect wxTextCtrl* ctrl = new wxTextCtrl( parent, wxID_ANY, text, wxPoint(labelRect.x,labelRect.y), - wxSize(labelRect.width,labelRect.height) ); + wxSize(labelRect.width,labelRect.height), + wxTE_PROCESS_ENTER ); // select the text in the control an place the cursor at the end ctrl->SetInsertionPointEnd(); @@ -1128,7 +1258,7 @@ wxControl* wxDataViewIconTextRenderer::CreateEditorCtrl(wxWindow *parent, wxRect return ctrl; } -bool wxDataViewIconTextRenderer::GetValueFromEditorCtrl( wxControl *editor, wxVariant& value ) +bool wxDataViewIconTextRenderer::GetValueFromEditorCtrl( wxWindow *editor, wxVariant& value ) { wxTextCtrl *text = (wxTextCtrl*) editor; @@ -1345,8 +1475,7 @@ wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID i // TODO: maybe there is something system colour to use m_penExpander = wxPen(wxColour(0,0,0)); - m_root = new wxDataViewTreeNode( NULL ); - m_root->SetHasChildren(true); + m_root = wxDataViewTreeNode::CreateRootNode(); // Make m_count = -1 will cause the class recaculate the real displaying number of rows. m_count = -1; @@ -1407,7 +1536,7 @@ wxDragResult wxDataViewMainWindow::OnDragOver( wxDataFormat format, wxCoord x, wxDataViewItem item = GetItemByRow( row ); - wxDataViewModel *model = GetOwner()->GetModel(); + wxDataViewModel *model = GetModel(); wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE, m_owner->GetId() ); event.SetEventObject( m_owner ); @@ -1450,7 +1579,7 @@ bool wxDataViewMainWindow::OnDrop( wxDataFormat format, wxCoord x, wxCoord y ) wxDataViewItem item = GetItemByRow( row ); - wxDataViewModel *model = GetOwner()->GetModel(); + wxDataViewModel *model = GetModel(); wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE, m_owner->GetId() ); event.SetEventObject( m_owner ); @@ -1479,7 +1608,7 @@ wxDragResult wxDataViewMainWindow::OnData( wxDataFormat format, wxCoord x, wxCoo wxDataViewItem item = GetItemByRow( row ); - wxDataViewModel *model = GetOwner()->GetModel(); + wxDataViewModel *model = GetModel(); wxCustomDataObject *obj = (wxCustomDataObject *) GetDropTarget()->GetDataObject(); @@ -1525,9 +1654,6 @@ wxBitmap wxDataViewMainWindow::CreateItemBitmap( unsigned int row, int &indent ) indent = GetOwner()->GetIndent() * node->GetIndentLevel(); indent = indent + m_lineHeight; // try to use the m_lineHeight as the expander space - - if(!node->HasChildren()) - delete node; } width -= indent; @@ -1540,14 +1666,8 @@ wxBitmap wxDataViewMainWindow::CreateItemBitmap( unsigned int row, int &indent ) wxDataViewModel *model = m_owner->GetModel(); - wxDataViewColumn *expander = GetOwner()->GetExpanderColumn(); - if (!expander) - { - // TODO-RTL: last column for RTL support - expander = GetOwner()->GetColumnAt( 0 ); - GetOwner()->SetExpanderColumn(expander); - } - + wxDataViewColumn * const + expander = GetExpanderColumnOrFirstOne(GetOwner()); int x = 0; for (col = 0; col < cols; col++) @@ -1584,7 +1704,7 @@ wxBitmap wxDataViewMainWindow::CreateItemBitmap( unsigned int row, int &indent ) void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) { - wxDataViewModel *model = GetOwner()->GetModel(); + wxDataViewModel *model = GetModel(); wxAutoBufferedPaintDC dc( this ); #ifdef __WXMSW__ @@ -1593,6 +1713,12 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) dc.DrawRectangle(GetClientSize()); #endif + if ( IsEmpty() ) + { + // No items to draw. + return; + } + // prepare the DC GetOwner()->PrepareDC( dc ); dc.SetFont( GetFont() ); @@ -1725,13 +1851,8 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) } #endif // wxUSE_DRAG_AND_DROP - wxDataViewColumn *expander = GetOwner()->GetExpanderColumn(); - if (!expander) - { - // TODO-RTL: last column for RTL support - expander = GetOwner()->GetColumnAt( 0 ); - GetOwner()->SetExpanderColumn(expander); - } + wxDataViewColumn * const + expander = GetExpanderColumnOrFirstOne(GetOwner()); // redraw all cells for all rows which must be repainted and all columns wxRect cell_rect; @@ -1759,8 +1880,11 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) dataitem = node->GetItem(); - if ((i > 0) && model->IsContainer(dataitem) && - !model->HasContainerColumns(dataitem)) + // Skip all columns of "container" rows except the expander + // column itself unless HasContainerColumns() overrides this. + if ( col != expander && + model->IsContainer(dataitem) && + !model->HasContainerColumns(dataitem) ) continue; } else @@ -1815,12 +1939,6 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) // force the expander column to left-center align cell->SetAlignment( wxALIGN_CENTER_VERTICAL ); } - if (node && !node->HasChildren()) - { - // Yes, if the node does not have any child, it must be a leaf which - // mean that it is a temporarily created by GetTreeNodeByRow - wxDELETE(node); - } wxRect item_rect = cell_rect; item_rect.Deflate(PADDING_RIGHTLEFT, 0); @@ -1880,183 +1998,214 @@ public: virtual ~DoJob() { } // The return value control how the tree-walker tranverse the tree - // 0: Job done, stop tranverse and return - // 1: Ignore the current node's subtree and continue - // 2: Job not done, continue - enum { OK = 0 , IGR = 1, CONT = 2 }; + enum + { + DONE, // Job done, stop traversing and return + SKIP_SUBTREE, // Ignore the current node's subtree and continue + CONTINUE // Job not done, continue + }; + virtual int operator() ( wxDataViewTreeNode * node ) = 0; - virtual int operator() ( void * n ) = 0; }; bool Walker( wxDataViewTreeNode * node, DoJob & func ) { - if( node==NULL ) - return false; + wxCHECK_MSG( node, false, "can't walk NULL node" ); switch( func( node ) ) { - case DoJob::OK : + case DoJob::DONE: return true; - case DoJob::IGR: + case DoJob::SKIP_SUBTREE: return false; - case DoJob::CONT: - default: - ; + case DoJob::CONTINUE: + break; } - const wxDataViewTreeNodes& nodes = node->GetNodes(); - const wxDataViewTreeLeaves& leaves = node->GetChildren(); - - int len_nodes = nodes.GetCount(); - int len = leaves.GetCount(); - int i = 0, nodes_i = 0; - - for(; i < len; i ++ ) + if ( node->HasChildren() ) { - void * n = leaves[i]; - if( nodes_i < len_nodes && n == nodes[nodes_i]->GetItem().GetID() ) - { - wxDataViewTreeNode * nd = nodes[nodes_i]; - nodes_i++; + const wxDataViewTreeNodes& nodes = node->GetChildNodes(); - if( Walker( nd , func ) ) + for ( wxDataViewTreeNodes::const_iterator i = nodes.begin(); + i != nodes.end(); + ++i ) + { + if ( Walker(*i, func) ) return true; - } - else - switch( func( n ) ) - { - case DoJob::OK : - return true; - case DoJob::IGR: - continue; - case DoJob::CONT: - default: - ; - } } + return false; } bool wxDataViewMainWindow::ItemAdded(const wxDataViewItem & parent, const wxDataViewItem & item) { - GetOwner()->InvalidateColBestWidths(); - if (IsVirtualList()) { wxDataViewVirtualListModel *list_model = - (wxDataViewVirtualListModel*) GetOwner()->GetModel(); + (wxDataViewVirtualListModel*) GetModel(); m_count = list_model->GetCount(); - UpdateDisplay(); - return true; } + else + { + SortPrepare(); - SortPrepare(); + wxDataViewTreeNode *parentNode = FindNode(parent); + + if ( !parentNode ) + return false; - wxDataViewTreeNode * node; - node = FindNode(parent); + wxDataViewItemArray siblings; + GetModel()->GetChildren(parent, siblings); + int itemPos = siblings.Index(item, /*fromEnd=*/true); + wxCHECK_MSG( itemPos != wxNOT_FOUND, false, "adding non-existent item?" ); - if( node == NULL ) - return false; + wxDataViewTreeNode *itemNode = new wxDataViewTreeNode(parentNode, item); + itemNode->SetHasChildren(GetModel()->IsContainer(item)); - node->SetHasChildren( true ); + parentNode->SetHasChildren(true); + parentNode->InsertChild(itemNode, itemPos); + parentNode->ChangeSubTreeCount(+1); - if( g_model->IsContainer( item ) ) - { - wxDataViewTreeNode * newnode = new wxDataViewTreeNode( node ); - newnode->SetItem(item); - newnode->SetHasChildren( true ); - node->AddNode( newnode); + m_count = -1; } - else - node->AddLeaf( item.GetID() ); - - node->ChangeSubTreeCount(1); - m_count = -1; + GetOwner()->InvalidateColBestWidths(); UpdateDisplay(); return true; } -static void DestroyTreeHelper( wxDataViewTreeNode * node); - bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem& parent, const wxDataViewItem& item) { - GetOwner()->InvalidateColBestWidths(); - if (IsVirtualList()) { wxDataViewVirtualListModel *list_model = - (wxDataViewVirtualListModel*) GetOwner()->GetModel(); + (wxDataViewVirtualListModel*) GetModel(); m_count = list_model->GetCount(); - if( m_currentRow > GetRowCount() ) - m_currentRow = m_count - 1; + if ( !m_selection.empty() ) + { + const int row = GetRowByItem(item); - // TODO: why empty the entire selection? - m_selection.Empty(); + int rowIndexInSelection = wxNOT_FOUND; - UpdateDisplay(); + const size_t selCount = m_selection.size(); + for ( size_t i = 0; i < selCount; i++ ) + { + if ( m_selection[i] == (unsigned)row ) + rowIndexInSelection = i; + else if ( m_selection[i] > (unsigned)row ) + m_selection[i]--; + } + + if ( rowIndexInSelection != wxNOT_FOUND ) + m_selection.RemoveAt(rowIndexInSelection); + } - return true; } + else // general case + { + wxDataViewTreeNode *parentNode = FindNode(parent); - wxDataViewTreeNode * node = FindNode(parent); + // Notice that it is possible that the item being deleted is not in the + // tree at all, for example we could be deleting a never shown (because + // collapsed) item in a tree model. So it's not an error if we don't know + // about this item, just return without doing anything then. + if ( !parentNode ) + return false; - // Notice that it is possible that the item being deleted is not in the - // tree at all, for example we could be deleting a never shown (because - // collapsed) item in a tree model. So it's not an error if we don't know - // about this item, just return without doing anything then. - if ( !node || node->GetChildren().Index(item.GetID()) == wxNOT_FOUND ) - return false; + wxCHECK_MSG( parentNode->HasChildren(), false, "parent node doesn't have children?" ); + const wxDataViewTreeNodes& parentsChildren = parentNode->GetChildNodes(); + + // We can't use FindNode() to find 'item', because it was already + // removed from the model by the time ItemDeleted() is called, so we + // have to do it manually. We keep track of its position as well for + // later use. + int itemPosInNode = 0; + wxDataViewTreeNode *itemNode = NULL; + for ( wxDataViewTreeNodes::const_iterator i = parentsChildren.begin(); + i != parentsChildren.end(); + ++i, ++itemPosInNode ) + { + if( (*i)->GetItem() == item ) + { + itemNode = *i; + break; + } + } - int sub = -1; - node->GetChildren().Remove( item.GetID() ); - // Manipolate selection - if( m_selection.GetCount() > 1 ) - { - m_selection.Empty(); - } - bool isContainer = false; - wxDataViewTreeNodes nds = node->GetNodes(); - for (size_t i = 0; i < nds.GetCount(); i ++) - { - if (nds[i]->GetItem() == item) + // If the parent wasn't expanded, it's possible that we didn't have a + // node corresponding to 'item' and so there's nothing left to do. + if ( !itemNode ) { - isContainer = true; - break; + // If this was the last child to be removed, it's possible the parent + // node became a leaf. Let's ask the model about it. + if ( parentNode->GetChildNodes().empty() ) + parentNode->SetHasChildren(GetModel()->IsContainer(parent)); + + return false; } - } - if( isContainer ) - { - wxDataViewTreeNode * n = NULL; - wxDataViewTreeNodes nodes = node->GetNodes(); - int len = nodes.GetCount(); - for( int i = 0; i < len; i ++) + + // Delete the item from wxDataViewTreeNode representation: + const int itemsDeleted = 1 + itemNode->GetSubTreeCount(); + + parentNode->RemoveChild(itemNode); + delete itemNode; + parentNode->ChangeSubTreeCount(-itemsDeleted); + + // Make the row number invalid and get a new valid one when user call GetRowCount + m_count = -1; + + // If this was the last child to be removed, it's possible the parent + // node became a leaf. Let's ask the model about it. + if ( parentNode->GetChildNodes().empty() ) + parentNode->SetHasChildren(GetModel()->IsContainer(parent)); + + // Update selection by removing 'item' and its entire children tree from the selection. + if ( !m_selection.empty() ) { - if( nodes[i]->GetItem() == item ) + // we can't call GetRowByItem() on 'item', as it's already deleted, so compute it from + // the parent ('parentNode') and position in its list of children + int itemRow; + if ( itemPosInNode == 0 ) { - n = nodes[i]; - break; + // 1st child, row number is that of the parent parentNode + 1 + itemRow = GetRowByItem(parentNode->GetItem()) + 1; } - } + else + { + // row number is that of the sibling above 'item' + its subtree if any + 1 + const wxDataViewTreeNode *siblingNode = parentNode->GetChildNodes()[itemPosInNode - 1]; - wxCHECK_MSG( n != NULL, false, "item not found" ); + itemRow = GetRowByItem(siblingNode->GetItem()) + + siblingNode->GetSubTreeCount() + + 1; + } + + wxDataViewSelection newsel(wxDataViewSelectionCmp); + + const size_t numSelections = m_selection.size(); + for ( size_t i = 0; i < numSelections; ++i ) + { + const int s = m_selection[i]; + if ( s < itemRow ) + newsel.push_back(s); + else if ( s >= itemRow + itemsDeleted ) + newsel.push_back(s - itemsDeleted); + // else: deleted item, remove from selection + } - node->GetNodes().Remove( n ); - sub -= n->GetSubTreeCount(); - ::DestroyTreeHelper(n); + m_selection = newsel; + } } - // Make the row number invalid and get a new valid one when user call GetRowCount - m_count = -1; - node->ChangeSubTreeCount(sub); // Change the current row to the last row if the current exceed the max row number if( m_currentRow > GetRowCount() ) - m_currentRow = m_count - 1; + ChangeCurrentRow(m_count - 1); + GetOwner()->InvalidateColBestWidths(); UpdateDisplay(); return true; @@ -2064,18 +2213,18 @@ bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem& parent, bool wxDataViewMainWindow::ItemChanged(const wxDataViewItem & item) { - GetOwner()->InvalidateColBestWidths(); - SortPrepare(); g_model->Resort(); + GetOwner()->InvalidateColBestWidths(); + // Send event wxWindow *parent = GetParent(); wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId()); le.SetEventObject(parent); - le.SetModel(GetOwner()->GetModel()); + le.SetModel(GetModel()); le.SetItem(item); - parent->GetEventHandler()->ProcessEvent(le); + parent->ProcessWindowEvent(le); return true; } @@ -2096,8 +2245,6 @@ bool wxDataViewMainWindow::ValueChanged( const wxDataViewItem & item, unsigned i if (view_column == -1) return false; - GetOwner()->InvalidateColBestWidth(view_column); - // NOTE: to be valid, we cannot use e.g. INT_MAX - 1 /*#define MAX_VIRTUAL_WIDTH 100000 @@ -2110,29 +2257,30 @@ bool wxDataViewMainWindow::ValueChanged( const wxDataViewItem & item, unsigned i SortPrepare(); g_model->Resort(); + GetOwner()->InvalidateColBestWidth(view_column); + // Send event wxWindow *parent = GetParent(); wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId()); le.SetEventObject(parent); - le.SetModel(GetOwner()->GetModel()); + le.SetModel(GetModel()); le.SetItem(item); le.SetColumn(view_column); le.SetDataViewColumn(GetOwner()->GetColumn(view_column)); - parent->GetEventHandler()->ProcessEvent(le); + parent->ProcessWindowEvent(le); return true; } bool wxDataViewMainWindow::Cleared() { - GetOwner()->InvalidateColBestWidths(); - DestroyTree(); m_selection.Clear(); SortPrepare(); - BuildTree( GetOwner()->GetModel() ); + BuildTree( GetModel() ); + GetOwner()->InvalidateColBestWidths(); UpdateDisplay(); return true; @@ -2157,7 +2305,7 @@ void wxDataViewMainWindow::OnInternalIdle() void wxDataViewMainWindow::RecalculateDisplay() { - wxDataViewModel *model = GetOwner()->GetModel(); + wxDataViewModel *model = GetModel(); if (!model) { Refresh(); @@ -2385,10 +2533,10 @@ void wxDataViewMainWindow::SendSelectionChangedEvent( const wxDataViewItem& item wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, parent->GetId()); le.SetEventObject(parent); - le.SetModel(GetOwner()->GetModel()); + le.SetModel(GetModel()); le.SetItem( item ); - parent->GetEventHandler()->ProcessEvent(le); + parent->ProcessWindowEvent(le); } void wxDataViewMainWindow::RefreshRow( unsigned int row ) @@ -2434,58 +2582,6 @@ void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow ) Refresh( true, &rect ); } -void wxDataViewMainWindow::OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event) -{ - wxCHECK_RET( newCurrent < GetRowCount(), - wxT("invalid item index in OnArrowChar()") ); - - // if there is no selection, we cannot move it anywhere - if (!HasCurrentRow()) - return; - - unsigned int oldCurrent = m_currentRow; - - // in single selection we just ignore Shift as we can't select several - // items anyhow - if ( event.ShiftDown() && !IsSingleSel() ) - { - RefreshRow( oldCurrent ); - - ChangeCurrentRow( newCurrent ); - - // select all the items between the old and the new one - if ( oldCurrent > newCurrent ) - { - newCurrent = oldCurrent; - oldCurrent = m_currentRow; - } - - SelectRows( oldCurrent, newCurrent, true ); - if (oldCurrent!=newCurrent) - SendSelectionChangedEvent(GetItemByRow(m_selection[0])); - } - else // !shift - { - RefreshRow( oldCurrent ); - - // all previously selected items are unselected unless ctrl is held - if ( !event.ControlDown() ) - SelectAllRows(false); - - ChangeCurrentRow( newCurrent ); - - if ( !event.ControlDown() ) - { - SelectRow( m_currentRow, true ); - SendSelectionChangedEvent(GetItemByRow(m_currentRow)); - } - else - RefreshRow( m_currentRow ); - } - - GetOwner()->EnsureVisible( m_currentRow, -1 ); -} - wxRect wxDataViewMainWindow::GetLineRect( unsigned int row ) const { wxRect rect; @@ -2499,7 +2595,7 @@ wxRect wxDataViewMainWindow::GetLineRect( unsigned int row ) const int wxDataViewMainWindow::GetLineStart( unsigned int row ) const { - const wxDataViewModel *model = GetOwner()->GetModel(); + const wxDataViewModel *model = GetModel(); if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT) { @@ -2515,13 +2611,6 @@ int wxDataViewMainWindow::GetLineStart( unsigned int row ) const wxDataViewItem item = node->GetItem(); - if (node && !node->HasChildren()) - { - // Yes, if the node does not have any child, it must be a leaf which - // mean that it is a temporarily created by GetTreeNodeByRow - wxDELETE(node); - } - unsigned int cols = GetOwner()->GetColumnCount(); unsigned int col; int height = m_lineHeight; @@ -2556,7 +2645,7 @@ int wxDataViewMainWindow::GetLineStart( unsigned int row ) const int wxDataViewMainWindow::GetLineAt( unsigned int y ) const { - const wxDataViewModel *model = GetOwner()->GetModel(); + const wxDataViewModel *model = GetModel(); // check for the easy case first if ( !GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT) ) @@ -2576,13 +2665,6 @@ int wxDataViewMainWindow::GetLineAt( unsigned int y ) const wxDataViewItem item = node->GetItem(); - if (node && !node->HasChildren()) - { - // Yes, if the node does not have any child, it must be a leaf which - // mean that it is a temporarily created by GetTreeNodeByRow - wxDELETE(node); - } - unsigned int cols = GetOwner()->GetColumnCount(); unsigned int col; int height = m_lineHeight; @@ -2614,7 +2696,7 @@ int wxDataViewMainWindow::GetLineAt( unsigned int y ) const int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const { - const wxDataViewModel *model = GetOwner()->GetModel(); + const wxDataViewModel *model = GetModel(); if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT) { @@ -2626,13 +2708,6 @@ int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const wxDataViewItem item = node->GetItem(); - if (node && !node->HasChildren()) - { - // Yes, if the node does not have any child, it must be a leaf which - // mean that it is a temporarily created by GetTreeNodeByRow - wxDELETE(node); - } - int height = m_lineHeight; unsigned int cols = GetOwner()->GetColumnCount(); @@ -2663,88 +2738,17 @@ int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const } } -class RowToItemJob: public DoJob + +class RowToTreeNodeJob: public DoJob { public: - RowToItemJob( unsigned int row , int current ) - { this->row = row; this->current = current; } - virtual ~RowToItemJob() {} - - virtual int operator() ( wxDataViewTreeNode * node ) - { - current ++; - if( current == static_cast(row)) - { - ret = node->GetItem(); - return DoJob::OK; - } - - if( node->GetSubTreeCount() + current < static_cast(row) ) - { - current += node->GetSubTreeCount(); - return DoJob::IGR; - } - else - { - // If the current has no child node, we can find the desired item of the row - // number directly. - // This if can speed up finding in some case, and will has a very good effect - // when it comes to list view - if( node->GetNodes().GetCount() == 0) - { - int index = static_cast(row) - current - 1; - ret = node->GetChildren().Item( index ); - return DoJob::OK; - } - return DoJob::CONT; - } - } - - virtual int operator() ( void * n ) - { - current ++; - if( current == static_cast(row)) - { - ret = wxDataViewItem( n ); - return DoJob::OK; - } - return DoJob::CONT; - } - - wxDataViewItem GetResult() const - { return ret; } - -private: - unsigned int row; - int current; - wxDataViewItem ret; -}; - -wxDataViewItem wxDataViewMainWindow::GetItemByRow(unsigned int row) const -{ - if (IsVirtualList()) - { - return wxDataViewItem( wxUIntToPtr(row+1) ); - } - else - { - RowToItemJob job( row, -2 ); - Walker( m_root , job ); - return job.GetResult(); - } -} - -class RowToTreeNodeJob: public DoJob -{ -public: - RowToTreeNodeJob( unsigned int row , int current, wxDataViewTreeNode * node ) + RowToTreeNodeJob( unsigned int row , int current, wxDataViewTreeNode * node ) { this->row = row; this->current = current; ret = NULL; parent = node; } - virtual ~RowToTreeNodeJob(){ } virtual int operator() ( wxDataViewTreeNode * node ) { @@ -2752,47 +2756,31 @@ public: if( current == static_cast(row)) { ret = node; - return DoJob::OK; + return DoJob::DONE; } if( node->GetSubTreeCount() + current < static_cast(row) ) { current += node->GetSubTreeCount(); - return DoJob::IGR; + return DoJob::SKIP_SUBTREE; } else { parent = node; - // If the current node has no children, we can find the desired item of the - // row number directly. - // This if can speed up finding in some case, and will have a very good - // effect for list views. - if( node->GetNodes().GetCount() == 0) + // If the current node has only leaf children, we can find the + // desired node directly. This can speed up finding the node + // in some cases, and will have a very good effect for list views. + if ( node->HasChildren() && + (int)node->GetChildNodes().size() == node->GetSubTreeCount() ) { - int index = static_cast(row) - current - 1; - void * n = node->GetChildren().Item( index ); - ret = new wxDataViewTreeNode( parent ); - ret->SetItem( wxDataViewItem( n )); - ret->SetHasChildren(false); - return DoJob::OK; + const int index = static_cast(row) - current - 1; + ret = node->GetChildNodes()[index]; + return DoJob::DONE; } - return DoJob::CONT; - } - } - virtual int operator() ( void * n ) - { - current ++; - if( current == static_cast(row)) - { - ret = new wxDataViewTreeNode( parent ); - ret->SetItem( wxDataViewItem( n )); - ret->SetHasChildren(false); - return DoJob::OK; + return DoJob::CONTINUE; } - - return DoJob::CONT; } wxDataViewTreeNode * GetResult() const @@ -2814,18 +2802,31 @@ wxDataViewTreeNode * wxDataViewMainWindow::GetTreeNodeByRow(unsigned int row) co return job.GetResult(); } -wxDataViewEvent wxDataViewMainWindow::SendExpanderEvent( wxEventType type, - const wxDataViewItem & item ) +wxDataViewItem wxDataViewMainWindow::GetItemByRow(unsigned int row) const +{ + if (IsVirtualList()) + { + return wxDataViewItem( wxUIntToPtr(row+1) ); + } + else + { + wxDataViewTreeNode *node = GetTreeNodeByRow(row); + return node ? node->GetItem() : wxDataViewItem(); + } +} + +bool +wxDataViewMainWindow::SendExpanderEvent(wxEventType type, + const wxDataViewItem& item) { wxWindow *parent = GetParent(); wxDataViewEvent le(type, parent->GetId()); le.SetEventObject(parent); - le.SetModel(GetOwner()->GetModel()); + le.SetModel(GetModel()); le.SetItem( item ); - parent->GetEventHandler()->ProcessEvent(le); - return le; + return !parent->ProcessWindowEvent(le) || le.IsAllowed(); } bool wxDataViewMainWindow::IsExpanded( unsigned int row ) const @@ -2838,10 +2839,7 @@ bool wxDataViewMainWindow::IsExpanded( unsigned int row ) const return false; if (!node->HasChildren()) - { - delete node; return false; - } return node->IsOpen(); } @@ -2856,10 +2854,7 @@ bool wxDataViewMainWindow::HasChildren( unsigned int row ) const return false; if (!node->HasChildren()) - { - delete node; return false; - } return true; } @@ -2874,27 +2869,23 @@ void wxDataViewMainWindow::Expand( unsigned int row ) return; if (!node->HasChildren()) - { - delete node; return; - } if (!node->IsOpen()) { - wxDataViewEvent e = - SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING, node->GetItem()); - - // Check if the user prevent expanding - if( e.GetSkipped() ) + if ( !SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING, node->GetItem()) ) + { + // Vetoed by the event handler. return; + } node->ToggleOpen(); // build the children of current node - if( node->GetChildrenNumber() == 0 ) + if( node->GetChildNodes().empty() ) { SortPrepare(); - ::BuildTreeHelper(GetOwner()->GetModel(), node->GetItem(), node); + ::BuildTreeHelper(GetModel(), node->GetItem(), node); } // By expanding the node all row indices that are currently in the selection list @@ -2930,17 +2921,15 @@ void wxDataViewMainWindow::Collapse(unsigned int row) return; if (!node->HasChildren()) - { - delete node; return; - } if (node->IsOpen()) { - wxDataViewEvent e = - SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING,node->GetItem()); - if( e.GetSkipped() ) + if ( !SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING,node->GetItem()) ) + { + // Vetoed by the event handler. return; + } // Find out if there are selected items below the current node. bool selectCollapsingRow = false; @@ -2998,7 +2987,7 @@ void wxDataViewMainWindow::Collapse(unsigned int row) wxDataViewTreeNode * wxDataViewMainWindow::FindNode( const wxDataViewItem & item ) { - const wxDataViewModel * model = GetOwner()->GetModel(); + const wxDataViewModel * model = GetModel(); if( model == NULL ) return NULL; @@ -3017,17 +3006,20 @@ wxDataViewTreeNode * wxDataViewMainWindow::FindNode( const wxDataViewItem & item // Find the item along the parent-chain. // This algorithm is designed to speed up the node-finding method wxDataViewTreeNode* node = m_root; - for( unsigned iter = parentChain.size()-1; iter>=0; --iter ) + for( unsigned iter = parentChain.size()-1; ; --iter ) { if( node->HasChildren() ) { - if( node->GetChildrenNumber() == 0 ) + if( node->GetChildNodes().empty() ) { + // Even though the item is a container, it doesn't have any + // child nodes in the control's representation yet. We have + // to realize its subtree now. SortPrepare(); ::BuildTreeHelper(model, node->GetItem(), node); } - const wxDataViewTreeNodes& nodes = node->GetNodes(); + const wxDataViewTreeNodes& nodes = node->GetChildNodes(); bool found = false; for (unsigned i = 0; i < nodes.GetCount(); ++i) @@ -3048,6 +3040,9 @@ wxDataViewTreeNode * wxDataViewMainWindow::FindNode( const wxDataViewItem & item } else return NULL; + + if ( !iter ) + break; } return NULL; } @@ -3119,14 +3114,12 @@ wxRect wxDataViewMainWindow::GetItemRect( const wxDataViewItem & item, // to get the correct x position where the actual text is int indent = 0; int row = GetRowByItem(item); - if (!IsList() && (column == 0 || GetOwner()->GetExpanderColumn() == column) ) + if (!IsList() && + (column == 0 || GetExpanderColumnOrFirstOne(GetOwner()) == column) ) { wxDataViewTreeNode* node = GetTreeNodeByRow(row); indent = GetOwner()->GetIndent() * node->GetIndentLevel(); indent = indent + m_lineHeight; // use m_lineHeight as the width of the expander - - if(!node->HasChildren()) - delete node; } wxRect itemRect( xpos + indent, @@ -3145,7 +3138,7 @@ int wxDataViewMainWindow::RecalculateCount() if (IsVirtualList()) { wxDataViewVirtualListModel *list_model = - (wxDataViewVirtualListModel*) GetOwner()->GetModel(); + (wxDataViewVirtualListModel*) GetModel(); return list_model->GetCount(); } @@ -3171,30 +3164,22 @@ public: ret ++; if( node->GetItem() == item ) { - return DoJob::OK; + return DoJob::DONE; } if( node->GetItem() == *m_iter ) { m_iter++; - return DoJob::CONT; + return DoJob::CONTINUE; } else { ret += node->GetSubTreeCount(); - return DoJob::IGR; + return DoJob::SKIP_SUBTREE; } } - virtual int operator() ( void * n ) - { - ret ++; - if( n == item.GetID() ) - return DoJob::OK; - return DoJob::CONT; - } - // the row number is begin from zero int GetResult() const { return ret -1; } @@ -3208,7 +3193,7 @@ private: int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem & item) const { - const wxDataViewModel * model = GetOwner()->GetModel(); + const wxDataViewModel * model = GetModel(); if( model == NULL ) return -1; @@ -3250,41 +3235,31 @@ static void BuildTreeHelper( const wxDataViewModel * model, const wxDataViewIte wxDataViewItemArray children; unsigned int num = model->GetChildren( item, children); - unsigned int index = 0; - while( index < num ) + for ( unsigned int index = 0; index < num; index++ ) { - if( model->IsContainer( children[index] ) ) - { - wxDataViewTreeNode * n = new wxDataViewTreeNode( node ); - n->SetItem(children[index]); + wxDataViewTreeNode *n = new wxDataViewTreeNode(node, children[index]); + + if( model->IsContainer(children[index]) ) n->SetHasChildren( true ); - node->AddNode( n ); - } - else - { - node->AddLeaf( children[index].GetID() ); - } - index ++; + + node->InsertChild(n, index); } - node->SetSubTreeCount( num ); - wxDataViewTreeNode * n = node->GetParent(); - if( n != NULL) - n->ChangeSubTreeCount(num); + wxASSERT( node->IsOpen() ); + node->ChangeSubTreeCount(+num); } void wxDataViewMainWindow::BuildTree(wxDataViewModel * model) { DestroyTree(); - if (GetOwner()->GetModel()->IsVirtualListModel()) + if (GetModel()->IsVirtualListModel()) { m_count = -1; return; } - m_root = new wxDataViewTreeNode( NULL ); - m_root->SetHasChildren(true); + m_root = wxDataViewTreeNode::CreateRootNode(); // First we define a invalid item to fetch the top-level elements wxDataViewItem item; @@ -3293,25 +3268,12 @@ void wxDataViewMainWindow::BuildTree(wxDataViewModel * model) m_count = -1; } -static void DestroyTreeHelper( wxDataViewTreeNode * node ) -{ - if( node->GetNodeNumber() != 0 ) - { - int len = node->GetNodeNumber(); - wxDataViewTreeNodes& nodes = node->GetNodes(); - for (int i = 0; i < len; i++) - DestroyTreeHelper(nodes[i]); - } - delete node; -} - void wxDataViewMainWindow::DestroyTree() { if (!IsVirtualList()) { - ::DestroyTreeHelper(m_root); - m_count = 0; - m_root = NULL; + wxDELETE(m_root); + m_count = 0; } } @@ -3343,86 +3305,82 @@ void wxDataViewMainWindow::OnChar( wxKeyEvent &event ) { case WXK_RETURN: { + // Enter activates the item, i.e. sends wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED to + // it. Only if that event is not handled do we activate column renderer (which + // is normally done by Space). + + const wxDataViewItem item = GetItemByRow(m_currentRow); + wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, parent->GetId()); - le.SetItem( GetItemByRow(m_currentRow) ); + le.SetItem(item); le.SetEventObject(parent); - le.SetModel(GetOwner()->GetModel()); + le.SetModel(GetModel()); + + if ( parent->ProcessWindowEvent(le) ) + break; + // else: fall through to WXK_SPACE handling + } + + case WXK_SPACE: + { + // Activate the first activatable column if there is any: + wxDataViewColumn *activatableCol = NULL; + + const unsigned cols = GetOwner()->GetColumnCount(); + for ( unsigned i = 0; i < cols; i++ ) + { + wxDataViewColumn *c = GetOwner()->GetColumnAt(i); + if ( c->IsHidden() ) + continue; + if ( c->GetRenderer()->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE ) + { + activatableCol = c; + break; + } + } - parent->GetEventHandler()->ProcessEvent(le); + if ( activatableCol ) + { + const wxDataViewItem item = GetItemByRow(m_currentRow); + + const unsigned colIdx = activatableCol->GetModelColumn(); + const wxRect cell_rect = GetOwner()->GetItemRect(item, activatableCol); + + wxDataViewRenderer *cell = activatableCol->GetRenderer(); + cell->PrepareForItem(GetModel(), item, colIdx); + cell->WXOnActivate(cell_rect, GetModel(), item, colIdx); + } } break; case WXK_UP: if ( m_currentRow > 0 ) - OnArrowChar( m_currentRow - 1, event ); + OnVerticalNavigation( m_currentRow - 1, event ); break; case WXK_DOWN: - if ( m_currentRow < GetRowCount() - 1 ) - OnArrowChar( m_currentRow + 1, event ); + if ( m_currentRow + 1 < GetRowCount() ) + OnVerticalNavigation( m_currentRow + 1, event ); break; // Add the process for tree expanding/collapsing case WXK_LEFT: - { - if (IsList()) - break; - - wxDataViewTreeNode* node = GetTreeNodeByRow(m_currentRow); - if (!node) - break; - - if (node->HasChildren() && node->IsOpen()) - { - Collapse(m_currentRow); - } - else // if the node is already closed we move the selection to its parent - { - wxDataViewTreeNode *parent_node = node->GetParent(); - - if(!node->HasChildren()) - delete node; - - if (parent_node) - { - int parent = GetRowByItem( parent_node->GetItem() ); - if ( parent >= 0 ) - { - unsigned int row = m_currentRow; - SelectRow( row, false); - SelectRow( parent, true ); - ChangeCurrentRow( parent ); - GetOwner()->EnsureVisible( parent, -1 ); - SendSelectionChangedEvent( parent_node->GetItem() ); - } - } - } + OnLeftKey(); break; - } + case WXK_RIGHT: - { - if (!IsExpanded( m_currentRow )) - Expand( m_currentRow ); - else - { - unsigned int row = m_currentRow; - SelectRow( row, false ); - SelectRow( row + 1, true ); - ChangeCurrentRow( row + 1 ); - GetOwner()->EnsureVisible( row + 1, -1 ); - SendSelectionChangedEvent( GetItemByRow(row+1) ); - } + OnRightKey(); break; - } + case WXK_END: { if (!IsEmpty()) - OnArrowChar( GetRowCount() - 1, event ); + OnVerticalNavigation( GetRowCount() - 1, event ); break; } case WXK_HOME: if (!IsEmpty()) - OnArrowChar( 0, event ); + OnVerticalNavigation( 0, event ); break; case WXK_PAGEUP: @@ -3432,7 +3390,7 @@ void wxDataViewMainWindow::OnChar( wxKeyEvent &event ) if (index < 0) index = 0; - OnArrowChar( index, event ); + OnVerticalNavigation( index, event ); } break; @@ -3444,13 +3402,13 @@ void wxDataViewMainWindow::OnChar( wxKeyEvent &event ) if ( index >= count ) index = count - 1; - OnArrowChar( index, event ); + OnVerticalNavigation( index, event ); } break; case WXK_F2: { - if(m_selection.size() == 1) + if( !m_selection.empty() ) { // TODO: we need to revise that when we have a concept for a 'current column' GetOwner()->StartEditor(GetItemByRow(m_selection[0]), 0); @@ -3463,6 +3421,106 @@ void wxDataViewMainWindow::OnChar( wxKeyEvent &event ) } } +void wxDataViewMainWindow::OnVerticalNavigation(unsigned int newCurrent, const wxKeyEvent& event) +{ + wxCHECK_RET( newCurrent < GetRowCount(), + wxT("invalid item index in OnVerticalNavigation()") ); + + // if there is no selection, we cannot move it anywhere + if (!HasCurrentRow()) + return; + + unsigned int oldCurrent = m_currentRow; + + // in single selection we just ignore Shift as we can't select several + // items anyhow + if ( event.ShiftDown() && !IsSingleSel() ) + { + RefreshRow( oldCurrent ); + + ChangeCurrentRow( newCurrent ); + + // select all the items between the old and the new one + if ( oldCurrent > newCurrent ) + { + newCurrent = oldCurrent; + oldCurrent = m_currentRow; + } + + SelectRows( oldCurrent, newCurrent, true ); + if (oldCurrent!=newCurrent) + SendSelectionChangedEvent(GetItemByRow(m_selection[0])); + } + else // !shift + { + RefreshRow( oldCurrent ); + + // all previously selected items are unselected unless ctrl is held + if ( !event.ControlDown() ) + SelectAllRows(false); + + ChangeCurrentRow( newCurrent ); + + if ( !event.ControlDown() ) + { + SelectRow( m_currentRow, true ); + SendSelectionChangedEvent(GetItemByRow(m_currentRow)); + } + else + RefreshRow( m_currentRow ); + } + + GetOwner()->EnsureVisible( m_currentRow, -1 ); +} + +void wxDataViewMainWindow::OnLeftKey() +{ + if (IsList()) + return; + + wxDataViewTreeNode* node = GetTreeNodeByRow(m_currentRow); + if (!node) + return; + + if (node->HasChildren() && node->IsOpen()) + { + Collapse(m_currentRow); + } + else // if the node is already closed we move the selection to its parent + { + wxDataViewTreeNode *parent_node = node->GetParent(); + + if (parent_node) + { + int parent = GetRowByItem( parent_node->GetItem() ); + if ( parent >= 0 ) + { + unsigned int row = m_currentRow; + SelectRow( row, false); + SelectRow( parent, true ); + ChangeCurrentRow( parent ); + GetOwner()->EnsureVisible( parent, -1 ); + SendSelectionChangedEvent( parent_node->GetItem() ); + } + } + } +} + +void wxDataViewMainWindow::OnRightKey() +{ + if (!IsExpanded( m_currentRow )) + Expand( m_currentRow ); + else + { + unsigned int row = m_currentRow; + SelectRow( row, false ); + SelectRow( row + 1, true ); + ChangeCurrentRow( row + 1 ); + GetOwner()->EnsureVisible( row + 1, -1 ); + SendSelectionChangedEvent( GetItemByRow(row+1) ); + } +} + void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) { if (event.GetEventType() == wxEVT_MOUSEWHEEL) @@ -3497,6 +3555,37 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) } xpos += c->GetWidth(); } + + wxDataViewModel* const model = GetModel(); + + const unsigned int current = GetLineAt( y ); + const wxDataViewItem item = GetItemByRow(current); + + // Handle right clicking here, before everything else as context menu + // events should be sent even when we click outside of any item, unlike all + // the other ones. + if (event.RightUp()) + { + wxWindow *parent = GetParent(); + wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU, parent->GetId()); + le.SetEventObject(parent); + le.SetModel(model); + + if ( item.IsOk() && col ) + { + le.SetItem( item ); + le.SetColumn( col->GetModelColumn() ); + le.SetDataViewColumn( col ); + + wxVariant value; + model->GetValue( value, item, col->GetModelColumn() ); + le.SetValue(value); + } + + parent->ProcessWindowEvent(le); + return; + } + if (!col) { event.Skip(); @@ -3504,7 +3593,6 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) } wxDataViewRenderer *cell = col->GetRenderer(); - unsigned int current = GetLineAt( y ); if ((current >= GetRowCount()) || (x > GetEndOfLastCol())) { // Unselect all if below the last row ? @@ -3512,21 +3600,29 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) return; } - // Test whether the mouse is hovered on the tree item button + wxDataViewColumn* const + expander = GetExpanderColumnOrFirstOne(GetOwner()); + + // Test whether the mouse is hovering over the expander (a.k.a tree "+" + // button) and also determine the offset of the real cell start, skipping + // the indentation and the expander itself. bool hoverOverExpander = false; - if ((!IsList()) && (GetOwner()->GetExpanderColumn() == col)) + int itemOffset = 0; + if ((!IsList()) && (expander == col)) { wxDataViewTreeNode * node = GetTreeNodeByRow(current); - if( node!=NULL && node->HasChildren() ) - { - int indent = node->GetIndentLevel(); - indent = GetOwner()->GetIndent()*indent; + int indent = node->GetIndentLevel(); + itemOffset = GetOwner()->GetIndent()*indent; + + if ( node->HasChildren() ) + { // we make the rectangle we are looking in a bit bigger than the actual // visual expander so the user can hit that little thing reliably - wxRect rect( xpos + indent, + wxRect rect(itemOffset, GetLineStart( current ) + (GetLineHeight(current) - m_lineHeight)/2, m_lineHeight, m_lineHeight); + if( rect.Contains(x, y) ) { // So the mouse is over the expander @@ -3544,8 +3640,10 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) m_underMouse = node; } } - if (node!=NULL && !node->HasChildren()) - delete node; + + // Account for the expander as well, even if this item doesn't have it, + // its parent does so it still counts for the offset. + itemOffset += m_lineHeight; } if (!hoverOverExpander) { @@ -3557,8 +3655,6 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) } } - wxDataViewModel *model = GetOwner()->GetModel(); - #if wxUSE_DRAG_AND_DROP if (event.Dragging()) { @@ -3580,12 +3676,12 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) m_owner->CalcUnscrolledPosition( m_dragStart.x, m_dragStart.y, &m_dragStart.x, &m_dragStart.y ); unsigned int drag_item_row = GetLineAt( m_dragStart.y ); - wxDataViewItem item = GetItemByRow( drag_item_row ); + wxDataViewItem itemDragged = GetItemByRow( drag_item_row ); // Notify cell about drag wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_BEGIN_DRAG, m_owner->GetId() ); event.SetEventObject( m_owner ); - event.SetItem( item ); + event.SetItem( itemDragged ); event.SetModel( model ); if (!m_owner->HandleWindowEvent( event )) return; @@ -3618,9 +3714,8 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) m_lastOnSame = false; } - wxDataViewItem item = GetItemByRow(current); bool ignore_other_columns = - ((GetOwner()->GetExpanderColumn() != col) && + ((expander != col) && (model->IsContainer(item)) && (!model->HasContainerColumns(item))); @@ -3633,6 +3728,8 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) } else if ( current == m_lineLastClicked ) { + bool activated = false; + if ((!ignore_other_columns) && (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE)) { const unsigned colIdx = col->GetModelColumn(); @@ -3641,9 +3738,10 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) wxRect cell_rect( xpos, GetLineStart( current ), col->GetWidth(), GetLineHeight( current ) ); - cell->WXOnActivate( cell_rect, model, item, colIdx ); + activated = cell->WXOnActivate( cell_rect, model, item, colIdx ); } - else + + if ( !activated ) { wxWindow *parent = GetParent(); wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, parent->GetId()); @@ -3651,9 +3749,9 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) le.SetColumn( col->GetModelColumn() ); le.SetDataViewColumn( col ); le.SetEventObject(parent); - le.SetModel(GetOwner()->GetModel()); + le.SetModel(GetModel()); - parent->GetEventHandler()->ProcessEvent(le); + parent->ProcessWindowEvent(le); } return; } @@ -3708,25 +3806,13 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) if (!IsRowSelected(current)) { SelectAllRows(false); + const unsigned oldCurrent = m_currentRow; ChangeCurrentRow(current); SelectRow(m_currentRow,true); + RefreshRow(oldCurrent); SendSelectionChangedEvent(GetItemByRow( m_currentRow ) ); } } - else if (event.RightUp()) - { - wxVariant value; - model->GetValue( value, item, col->GetModelColumn() ); - wxWindow *parent = GetParent(); - wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU, parent->GetId()); - le.SetItem( item ); - le.SetColumn( col->GetModelColumn() ); - le.SetDataViewColumn( col ); - le.SetEventObject(parent); - le.SetModel(GetOwner()->GetModel()); - le.SetValue(value); - parent->GetEventHandler()->ProcessEvent(le); - } else if (event.MiddleDown()) { } @@ -3815,8 +3901,10 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) // notify cell about click cell->PrepareForItem(model, item, col->GetModelColumn()); - wxRect cell_rect( xpos, GetLineStart( current ), - col->GetWidth(), GetLineHeight( current ) ); + wxRect cell_rect( xpos + itemOffset, + GetLineStart( current ), + col->GetWidth() - itemOffset, + GetLineHeight( current ) ); // Report position relative to the cell's custom area, i.e. // no the entire space as given by the control but the one @@ -3880,14 +3968,6 @@ void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event ) event.Skip(); } -wxDataViewItem wxDataViewMainWindow::GetSelection() const -{ - if( m_selection.GetCount() != 1 ) - return wxDataViewItem(); - - return GetItemByRow( m_selection.Item(0)); -} - //----------------------------------------------------------------------------- // wxDataViewCtrl //----------------------------------------------------------------------------- @@ -3917,6 +3997,8 @@ void wxDataViewCtrl::Init() m_sortingColumnIdx = wxNOT_FOUND; m_headerArea = NULL; + + m_colsDirty = false; } bool wxDataViewCtrl::Create(wxWindow *parent, @@ -4010,6 +4092,16 @@ void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) ) Layout(); AdjustScrollbars(); + + // We must redraw the headers if their height changed. Normally this + // shouldn't happen as the control shouldn't let itself be resized beneath + // its minimal height but avoid the display artefacts that appear if it + // does happen, e.g. because there is really not enough vertical space. + if ( !HasFlag(wxDV_NO_HEADER) && m_headerArea && + m_headerArea->GetSize().y <= m_headerArea->GetBestSize(). y ) + { + m_headerArea->Refresh(); + } } void wxDataViewCtrl::SetFocus() @@ -4114,6 +4206,16 @@ unsigned int wxDataViewCtrl::GetColumnCount() const return m_cols.GetCount(); } +bool wxDataViewCtrl::SetRowHeight( int lineHeight ) +{ + if ( !m_clientArea ) + return false; + + m_clientArea->SetRowHeight(lineHeight); + + return true; +} + wxDataViewColumn* wxDataViewCtrl::GetColumn( unsigned int idx ) const { return m_cols[idx]; @@ -4300,6 +4402,7 @@ bool wxDataViewCtrl::DeleteColumn( wxDataViewColumn *column ) bool wxDataViewCtrl::ClearColumns() { + SetExpanderColumn(NULL); m_cols.Clear(); m_colsBestWidths.clear(); OnColumnsCountChanged(); @@ -4309,24 +4412,40 @@ bool wxDataViewCtrl::ClearColumns() void wxDataViewCtrl::InvalidateColBestWidth(int idx) { m_colsBestWidths[idx] = 0; - - if ( m_headerArea ) - m_headerArea->UpdateColumn(idx); + m_colsDirty = true; } void wxDataViewCtrl::InvalidateColBestWidths() { m_colsBestWidths.clear(); m_colsBestWidths.resize(m_cols.size()); + m_colsDirty = true; +} - if ( m_headerArea ) +void wxDataViewCtrl::UpdateColWidths() +{ + if ( !m_headerArea ) + return; + + const unsigned len = m_colsBestWidths.size(); + for ( unsigned i = 0; i < len; i++ ) { - const unsigned cols = m_headerArea->GetColumnCount(); - for ( unsigned i = 0; i < cols; i++ ) + if ( m_colsBestWidths[i] == 0 ) m_headerArea->UpdateColumn(i); } } +void wxDataViewCtrl::OnInternalIdle() +{ + wxDataViewCtrlBase::OnInternalIdle(); + + if ( m_colsDirty ) + { + m_colsDirty = false; + UpdateColWidths(); + } +} + int wxDataViewCtrl::GetColumnPosition( const wxDataViewColumn *column ) const { #if 1 @@ -4384,23 +4503,31 @@ void wxDataViewCtrl::DoSetCurrentItem(const wxDataViewItem& item) } } -// Selection code with wxDataViewItem as parameters -wxDataViewItem wxDataViewCtrl::GetSelection() const +int wxDataViewCtrl::GetSelectedItemsCount() const { - return m_clientArea->GetSelection(); + return m_clientArea->GetSelections().size(); } int wxDataViewCtrl::GetSelections( wxDataViewItemArray & sel ) const { sel.Empty(); - wxDataViewSelection selection = m_clientArea->GetSelections(); - int len = selection.GetCount(); - for( int i = 0; i < len; i ++) + const wxDataViewSelection& selections = m_clientArea->GetSelections(); + + const size_t len = selections.size(); + for ( size_t i = 0; i < len; i++ ) { - unsigned int row = selection[i]; - sel.Add( m_clientArea->GetItemByRow( row ) ); + wxDataViewItem item = m_clientArea->GetItemByRow(selections[i]); + if ( item.IsOk() ) + { + sel.Add(item); + } + else + { + wxFAIL_MSG( "invalid item in selection - bad internal state" ); + } } - return len; + + return sel.size(); } void wxDataViewCtrl::SetSelections( const wxDataViewItemArray & sel ) @@ -4464,73 +4591,6 @@ bool wxDataViewCtrl::IsSelected( const wxDataViewItem & item ) const return false; } -// Selection code with row number as parameter -int wxDataViewCtrl::GetSelections( wxArrayInt & sel ) const -{ - sel.Empty(); - wxDataViewSelection selection = m_clientArea->GetSelections(); - int len = selection.GetCount(); - for( int i = 0; i < len; i ++) - { - unsigned int row = selection[i]; - sel.Add( row ); - } - return len; -} - -void wxDataViewCtrl::SetSelections( const wxArrayInt & sel ) -{ - wxDataViewSelection selection(wxDataViewSelectionCmp); - int len = sel.GetCount(); - for( int i = 0; i < len; i ++ ) - { - int row = sel[i]; - if( row >= 0 ) - selection.Add( static_cast(row) ); - } - m_clientArea->SetSelections( selection ); -} - -void wxDataViewCtrl::Select( int row ) -{ - if( row >= 0 ) - { - if (m_clientArea->IsSingleSel()) - m_clientArea->SelectAllRows(false); - m_clientArea->SelectRow( row, true ); - } -} - -void wxDataViewCtrl::Unselect( int row ) -{ - if( row >= 0 ) - m_clientArea->SelectRow(row, false); -} - -bool wxDataViewCtrl::IsSelected( int row ) const -{ - if( row >= 0 ) - return m_clientArea->IsRowSelected(row); - return false; -} - -void wxDataViewCtrl::SelectRange( int from, int to ) -{ - wxArrayInt sel; - for( int i = from; i < to; i ++ ) - sel.Add( i ); - m_clientArea->Select(sel); -} - -void wxDataViewCtrl::UnselectRange( int from, int to ) -{ - wxDataViewSelection sel = m_clientArea->GetSelections(); - for( int i = from; i < to; i ++ ) - if( sel.Index( i ) != wxNOT_FOUND ) - sel.Remove( i ); - m_clientArea->SetSelections(sel); -} - void wxDataViewCtrl::SelectAll() { m_clientArea->SelectAllRows(true); @@ -4627,10 +4687,12 @@ void wxDataViewCtrl::StartEditor( const wxDataViewItem & item, unsigned int colu if (!col) return; - wxRect itemRect = GetItemRect(item, col); wxDataViewRenderer* renderer = col->GetRenderer(); - if (renderer->GetMode() == wxDATAVIEW_CELL_EDITABLE) - renderer->StartEditing(item, itemRect); + if (renderer->GetMode() != wxDATAVIEW_CELL_EDITABLE) + return; + + const wxRect itemRect = GetItemRect(item, col); + renderer->StartEditing(item, itemRect); } #endif // !wxUSE_GENERICDATAVIEWCTRL