+bool wxDataViewMainWindow::IsRowSelected( unsigned int row )
+{
+ return (m_selection.Index( row ) != wxNOT_FOUND);
+}
+
+void wxDataViewMainWindow::SendSelectionChangedEvent( const wxDataViewItem& item)
+{
+ wxWindow *parent = GetParent();
+ wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, parent->GetId());
+
+ le.SetEventObject(parent);
+ le.SetModel(GetModel());
+ le.SetItem( item );
+
+ parent->ProcessWindowEvent(le);
+}
+
+void wxDataViewMainWindow::RefreshRow( unsigned int row )
+{
+ wxRect rect( 0, GetLineStart( row ), GetEndOfLastCol(), GetLineHeight( row ) );
+ m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
+
+ wxSize client_size = GetClientSize();
+ wxRect client_rect( 0, 0, client_size.x, client_size.y );
+ wxRect intersect_rect = client_rect.Intersect( rect );
+ if (intersect_rect.width > 0)
+ Refresh( true, &intersect_rect );
+}
+
+void wxDataViewMainWindow::RefreshRows( unsigned int from, unsigned int to )
+{
+ if (from > to)
+ {
+ unsigned int tmp = to;
+ to = from;
+ from = tmp;
+ }
+
+ wxRect rect( 0, GetLineStart( from ), GetEndOfLastCol(), GetLineStart( (to-from+1) ) );
+ m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
+
+ wxSize client_size = GetClientSize();
+ wxRect client_rect( 0, 0, client_size.x, client_size.y );
+ wxRect intersect_rect = client_rect.Intersect( rect );
+ if (intersect_rect.width > 0)
+ Refresh( true, &intersect_rect );
+}
+
+void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow )
+{
+ wxSize client_size = GetClientSize();
+ int start = GetLineStart( firstRow );
+ m_owner->CalcScrolledPosition( start, 0, &start, NULL );
+ if (start > client_size.y) return;
+
+ wxRect rect( 0, start, client_size.x, client_size.y - start );
+
+ Refresh( true, &rect );
+}
+
+wxRect wxDataViewMainWindow::GetLineRect( unsigned int row ) const
+{
+ wxRect rect;
+ rect.x = 0;
+ rect.y = GetLineStart( row );
+ rect.width = GetEndOfLastCol();
+ rect.height = GetLineHeight( row );
+
+ return rect;
+}
+
+int wxDataViewMainWindow::GetLineStart( unsigned int row ) const
+{
+ const wxDataViewModel *model = GetModel();
+
+ if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT)
+ {
+ // TODO make more efficient
+
+ int start = 0;
+
+ unsigned int r;
+ for (r = 0; r < row; r++)
+ {
+ const wxDataViewTreeNode* node = GetTreeNodeByRow(r);
+ if (!node) return start;
+
+ wxDataViewItem item = node->GetItem();
+
+ unsigned int cols = GetOwner()->GetColumnCount();
+ unsigned int col;
+ int height = m_lineHeight;
+ for (col = 0; col < cols; col++)
+ {
+ const wxDataViewColumn *column = GetOwner()->GetColumn(col);
+ if (column->IsHidden())
+ continue; // skip it!
+
+ if ((col != 0) &&
+ model->IsContainer(item) &&
+ !model->HasContainerColumns(item))
+ continue; // skip it!
+
+ wxDataViewRenderer *renderer =
+ const_cast<wxDataViewRenderer*>(column->GetRenderer());
+ renderer->PrepareForItem(model, item, column->GetModelColumn());
+
+ height = wxMax( height, renderer->GetSize().y );
+ }
+
+ start += height;
+ }
+
+ return start;
+ }
+ else
+ {
+ return row * m_lineHeight;
+ }
+}
+
+int wxDataViewMainWindow::GetLineAt( unsigned int y ) const
+{
+ const wxDataViewModel *model = GetModel();
+
+ // check for the easy case first
+ if ( !GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT) )
+ return y / m_lineHeight;
+
+ // TODO make more efficient
+ unsigned int row = 0;
+ unsigned int yy = 0;
+ for (;;)
+ {
+ const wxDataViewTreeNode* node = GetTreeNodeByRow(row);
+ if (!node)
+ {
+ // not really correct...
+ return row + ((y-yy) / m_lineHeight);
+ }
+
+ wxDataViewItem item = node->GetItem();
+
+ unsigned int cols = GetOwner()->GetColumnCount();
+ unsigned int col;
+ int height = m_lineHeight;
+ for (col = 0; col < cols; col++)
+ {
+ const wxDataViewColumn *column = GetOwner()->GetColumn(col);
+ if (column->IsHidden())
+ continue; // skip it!
+
+ if ((col != 0) &&
+ model->IsContainer(item) &&
+ !model->HasContainerColumns(item))
+ continue; // skip it!
+
+ wxDataViewRenderer *renderer =
+ const_cast<wxDataViewRenderer*>(column->GetRenderer());
+ renderer->PrepareForItem(model, item, column->GetModelColumn());
+
+ height = wxMax( height, renderer->GetSize().y );
+ }
+
+ yy += height;
+ if (y < yy)
+ return row;
+
+ row++;
+ }
+}
+
+int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const
+{
+ const wxDataViewModel *model = GetModel();
+
+ if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT)
+ {
+ wxASSERT( !IsVirtualList() );
+
+ const wxDataViewTreeNode* node = GetTreeNodeByRow(row);
+ // wxASSERT( node );
+ if (!node) return m_lineHeight;
+
+ wxDataViewItem item = node->GetItem();
+
+ int height = m_lineHeight;
+
+ unsigned int cols = GetOwner()->GetColumnCount();
+ unsigned int col;
+ for (col = 0; col < cols; col++)
+ {
+ const wxDataViewColumn *column = GetOwner()->GetColumn(col);
+ if (column->IsHidden())
+ continue; // skip it!
+
+ if ((col != 0) &&
+ model->IsContainer(item) &&
+ !model->HasContainerColumns(item))
+ continue; // skip it!
+
+ wxDataViewRenderer *renderer =
+ const_cast<wxDataViewRenderer*>(column->GetRenderer());
+ renderer->PrepareForItem(model, item, column->GetModelColumn());
+
+ height = wxMax( height, renderer->GetSize().y );
+ }
+
+ return height;
+ }
+ else
+ {
+ return m_lineHeight;
+ }
+}
+
+
+class RowToTreeNodeJob: public DoJob
+{
+public:
+ RowToTreeNodeJob( unsigned int row , int current, wxDataViewTreeNode * node )
+ {
+ this->row = row;
+ this->current = current;
+ ret = NULL;
+ parent = node;
+ }
+
+ virtual int operator() ( wxDataViewTreeNode * node )
+ {
+ current ++;
+ if( current == static_cast<int>(row))
+ {
+ ret = node;
+ return DoJob::DONE;
+ }
+
+ if( node->GetSubTreeCount() + current < static_cast<int>(row) )
+ {
+ current += node->GetSubTreeCount();
+ return DoJob::SKIP_SUBTREE;
+ }
+ else
+ {
+ parent = node;
+
+ // 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() )
+ {
+ const int index = static_cast<int>(row) - current - 1;
+ ret = node->GetChildNodes()[index];
+ return DoJob::DONE;
+ }
+
+ return DoJob::CONTINUE;
+ }
+ }
+
+ wxDataViewTreeNode * GetResult() const
+ { return ret; }
+
+private:
+ unsigned int row;
+ int current;
+ wxDataViewTreeNode * ret;
+ wxDataViewTreeNode * parent;
+};
+
+wxDataViewTreeNode * wxDataViewMainWindow::GetTreeNodeByRow(unsigned int row) const
+{
+ wxASSERT( !IsVirtualList() );
+
+ RowToTreeNodeJob job( row , -2, m_root );
+ Walker( m_root , job );
+ return job.GetResult();
+}
+
+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(GetModel());
+ le.SetItem( item );
+
+ return !parent->ProcessWindowEvent(le) || le.IsAllowed();
+}
+
+bool wxDataViewMainWindow::IsExpanded( unsigned int row ) const
+{
+ if (IsList())
+ return false;
+
+ wxDataViewTreeNode * node = GetTreeNodeByRow(row);
+ if (!node)
+ return false;
+
+ if (!node->HasChildren())
+ return false;
+
+ return node->IsOpen();
+}
+
+bool wxDataViewMainWindow::HasChildren( unsigned int row ) const
+{
+ if (IsList())
+ return false;
+
+ wxDataViewTreeNode * node = GetTreeNodeByRow(row);
+ if (!node)
+ return false;
+
+ if (!node->HasChildren())
+ return false;
+
+ return true;
+}
+
+void wxDataViewMainWindow::Expand( unsigned int row )
+{
+ if (IsList())
+ return;
+
+ wxDataViewTreeNode * node = GetTreeNodeByRow(row);
+ if (!node)
+ return;
+
+ if (!node->HasChildren())
+ return;
+
+ if (!node->IsOpen())
+ {
+ 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->GetChildNodes().empty() )
+ {
+ SortPrepare();
+ ::BuildTreeHelper(GetModel(), node->GetItem(), node);
+ }
+
+ // By expanding the node all row indices that are currently in the selection list
+ // and are greater than our node have become invalid. So we have to correct that now.
+ const unsigned rowAdjustment = node->GetSubTreeCount();
+ for(unsigned i=0; i<m_selection.size(); ++i)
+ {
+ const unsigned testRow = m_selection[i];
+ // all rows above us are not affected, so skip them
+ if(testRow <= row)
+ continue;
+
+ m_selection[i] += rowAdjustment;
+ }
+
+ if(m_currentRow > row)
+ ChangeCurrentRow(m_currentRow + rowAdjustment);
+
+ m_count = -1;
+ UpdateDisplay();
+ // Send the expanded event
+ SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDED,node->GetItem());
+ }
+}
+
+void wxDataViewMainWindow::Collapse(unsigned int row)
+{
+ if (IsList())
+ return;
+
+ wxDataViewTreeNode *node = GetTreeNodeByRow(row);
+ if (!node)
+ return;
+
+ if (!node->HasChildren())
+ return;
+
+ if (node->IsOpen())
+ {
+ 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;
+ const unsigned rowAdjustment = node->GetSubTreeCount();
+ unsigned maxRowToBeTested = row + rowAdjustment;
+ for(unsigned i=0; i<m_selection.size(); ++i)
+ {
+ const unsigned testRow = m_selection[i];
+ if(testRow > row && testRow <= maxRowToBeTested)
+ {
+ selectCollapsingRow = true;
+ // get out as soon as we have found a node that is selected
+ break;
+ }
+ }
+
+ node->ToggleOpen();
+
+ // If the node to be closed has selected items the user won't see those any longer.
+ // We select the collapsing node in this case.
+ if(selectCollapsingRow)
+ {
+ SelectAllRows(false);
+ ChangeCurrentRow(row);
+ SelectRow(row, true);
+ SendSelectionChangedEvent(GetItemByRow(row));
+ }
+ else
+ {
+ // if there were no selected items below our node we still need to "fix" the
+ // selection list to adjust for the changing of the row indices.
+ // We actually do the opposite of what we are doing in Expand().
+ for(unsigned i=0; i<m_selection.size(); ++i)
+ {
+ const unsigned testRow = m_selection[i];
+ // all rows above us are not affected, so skip them
+ if(testRow <= row)
+ continue;
+
+ m_selection[i] -= rowAdjustment;
+ }
+
+ // if the "current row" is being collapsed away we change it to the current row ;-)
+ if(m_currentRow > row && m_currentRow <= maxRowToBeTested)
+ ChangeCurrentRow(row);
+ else if(m_currentRow > row)
+ ChangeCurrentRow(m_currentRow - rowAdjustment);
+ }
+
+ m_count = -1;
+ UpdateDisplay();
+ SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSED,node->GetItem());
+ }
+}
+
+wxDataViewTreeNode * wxDataViewMainWindow::FindNode( const wxDataViewItem & item )
+{
+ const wxDataViewModel * model = GetModel();
+ if( model == NULL )
+ return NULL;
+
+ if (!item.IsOk())
+ return m_root;
+
+ // Compose the parent-chain for the item we are looking for
+ wxVector<wxDataViewItem> parentChain;
+ wxDataViewItem it( item );
+ while( it.IsOk() )
+ {
+ parentChain.push_back(it);
+ it = model->GetParent(it);
+ }
+
+ // 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 )
+ {
+ if( node->HasChildren() )
+ {
+ 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->GetChildNodes();
+ bool found = false;
+
+ for (unsigned i = 0; i < nodes.GetCount(); ++i)
+ {
+ wxDataViewTreeNode* currentNode = nodes[i];
+ if (currentNode->GetItem() == parentChain[iter])
+ {
+ if (currentNode->GetItem() == item)
+ return currentNode;
+
+ node = currentNode;
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return NULL;
+ }
+ else
+ return NULL;
+
+ if ( !iter )
+ break;
+ }
+ return NULL;
+}
+
+void wxDataViewMainWindow::HitTest( const wxPoint & point, wxDataViewItem & item,
+ wxDataViewColumn* &column )
+{
+ wxDataViewColumn *col = NULL;
+ unsigned int cols = GetOwner()->GetColumnCount();
+ unsigned int colnum = 0;
+ int x, y;
+ m_owner->CalcUnscrolledPosition( point.x, point.y, &x, &y );
+ for (unsigned x_start = 0; colnum < cols; colnum++)
+ {
+ col = GetOwner()->GetColumnAt(colnum);
+ if (col->IsHidden())
+ continue; // skip it!
+
+ unsigned int w = col->GetWidth();
+ if (x_start+w >= (unsigned int)x)
+ break;
+
+ x_start += w;
+ }
+
+ column = col;
+ item = GetItemByRow( GetLineAt( y ) );
+}
+
+wxRect wxDataViewMainWindow::GetItemRect( const wxDataViewItem & item,
+ const wxDataViewColumn* column )
+{
+ int xpos = 0;
+ int width = 0;
+
+ unsigned int cols = GetOwner()->GetColumnCount();
+ // If column is null the loop will compute the combined width of all columns.
+ // Otherwise, it will compute the x position of the column we are looking for.
+ for (unsigned int i = 0; i < cols; i++)
+ {
+ wxDataViewColumn* col = GetOwner()->GetColumnAt( i );
+
+ if (col == column)
+ break;
+
+ if (col->IsHidden())
+ continue; // skip it!
+
+ xpos += col->GetWidth();
+ width += col->GetWidth();
+ }
+
+ if(column != 0)
+ {
+ // If we have a column, we need can get its width directly.
+ if(column->IsHidden())
+ width = 0;
+ else
+ width = column->GetWidth();
+
+ }
+ else
+ {
+ // If we have no column, we reset the x position back to zero.
+ xpos = 0;
+ }
+
+ // we have to take an expander column into account and compute its indentation
+ // to get the correct x position where the actual text is
+ int indent = 0;
+ int row = GetRowByItem(item);
+ 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
+ }
+
+ wxRect itemRect( xpos + indent,
+ GetLineStart( row ),
+ width - indent,
+ GetLineHeight( row ) );
+
+ GetOwner()->CalcScrolledPosition( itemRect.x, itemRect.y,
+ &itemRect.x, &itemRect.y );
+
+ return itemRect;
+}
+
+int wxDataViewMainWindow::RecalculateCount()
+{
+ if (IsVirtualList())
+ {
+ wxDataViewVirtualListModel *list_model =
+ (wxDataViewVirtualListModel*) GetModel();
+
+ return list_model->GetCount();
+ }
+ else
+ {
+ return m_root->GetSubTreeCount();
+ }
+}
+
+class ItemToRowJob : public DoJob
+{
+public:
+ ItemToRowJob(const wxDataViewItem& item_, wxVector<wxDataViewItem>::reverse_iterator iter)
+ : m_iter(iter),
+ item(item_)
+ {
+ ret = -1;
+ }
+
+ // Maybe binary search will help to speed up this process
+ virtual int operator() ( wxDataViewTreeNode * node)
+ {
+ ret ++;
+ if( node->GetItem() == item )
+ {
+ return DoJob::DONE;
+ }
+
+ if( node->GetItem() == *m_iter )
+ {
+ m_iter++;
+ return DoJob::CONTINUE;
+ }
+ else
+ {
+ ret += node->GetSubTreeCount();
+ return DoJob::SKIP_SUBTREE;
+ }
+
+ }
+
+ // the row number is begin from zero
+ int GetResult() const
+ { return ret -1; }
+
+private:
+ wxVector<wxDataViewItem>::reverse_iterator m_iter;
+ wxDataViewItem item;
+ int ret;
+
+};
+
+int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem & item) const
+{
+ const wxDataViewModel * model = GetModel();
+ if( model == NULL )
+ return -1;
+
+ if (IsVirtualList())
+ {
+ return wxPtrToUInt( item.GetID() ) -1;
+ }
+ else
+ {
+ if( !item.IsOk() )
+ return -1;
+
+ // Compose the parent-chain of the item we are looking for
+ wxVector<wxDataViewItem> parentChain;
+ wxDataViewItem it( item );
+ while( it.IsOk() )
+ {
+ parentChain.push_back(it);
+ it = model->GetParent(it);
+ }
+
+ // add an 'invalid' item to represent our 'invisible' root node
+ parentChain.push_back(wxDataViewItem());
+
+ // the parent chain was created by adding the deepest parent first.
+ // so if we want to start at the root node, we have to iterate backwards through the vector
+ ItemToRowJob job( item, parentChain.rbegin() );
+ Walker( m_root, job );
+ return job.GetResult();
+ }
+}
+
+static void BuildTreeHelper( const wxDataViewModel * model, const wxDataViewItem & item,
+ wxDataViewTreeNode * node)
+{
+ if( !model->IsContainer( item ) )
+ return;
+
+ wxDataViewItemArray children;
+ unsigned int num = model->GetChildren( item, children);
+
+ for ( unsigned int index = 0; index < num; index++ )
+ {
+ wxDataViewTreeNode *n = new wxDataViewTreeNode(node, children[index]);
+
+ if( model->IsContainer(children[index]) )
+ n->SetHasChildren( true );
+
+ node->InsertChild(n, index);
+ }
+
+ wxASSERT( node->IsOpen() );
+ node->ChangeSubTreeCount(+num);
+}
+
+void wxDataViewMainWindow::BuildTree(wxDataViewModel * model)
+{
+ DestroyTree();
+
+ if (GetModel()->IsVirtualListModel())
+ {
+ m_count = -1;
+ return;
+ }
+
+ m_root = wxDataViewTreeNode::CreateRootNode();
+
+ // First we define a invalid item to fetch the top-level elements
+ wxDataViewItem item;
+ SortPrepare();
+ BuildTreeHelper( model, item, m_root);
+ m_count = -1;
+}
+
+void wxDataViewMainWindow::DestroyTree()
+{
+ if (!IsVirtualList())
+ {
+ wxDELETE(m_root);
+ m_count = 0;
+ }
+}
+
+wxDataViewColumn*
+wxDataViewMainWindow::FindColumnForEditing(const wxDataViewItem& item, wxDataViewCellMode mode)
+{
+ // Edit the current column editable in 'mode'. If no column is focused
+ // (typically because the user has full row selected), try to find the
+ // first editable column (this would typically be a checkbox for
+ // wxDATAVIEW_CELL_ACTIVATABLE and we don't want to force the user to set
+ // focus on the checkbox column; or on the only editable text column).
+
+ wxDataViewColumn *candidate = m_currentCol;
+
+ if ( candidate &&
+ !IsCellEditableInMode(item, candidate, mode) &&
+ !m_currentColSetByKeyboard )
+ {
+ // If current column was set by mouse to something not editable (in
+ // 'mode') and the user pressed Space/F2 to edit it, treat the
+ // situation as if there was whole-row focus, because that's what is
+ // visually indicated and the mouse click could very well be targeted
+ // on the row rather than on an individual cell.
+ //
+ // But if it was done by keyboard, respect that even if the column
+ // isn't editable, because focus is visually on that column and editing
+ // something else would be surprising.
+ candidate = NULL;
+ }
+
+ if ( !candidate )
+ {
+ const unsigned cols = GetOwner()->GetColumnCount();
+ for ( unsigned i = 0; i < cols; i++ )
+ {
+ wxDataViewColumn *c = GetOwner()->GetColumnAt(i);
+ if ( c->IsHidden() )
+ continue;
+
+ if ( IsCellEditableInMode(item, c, mode) )
+ {
+ candidate = c;
+ break;
+ }
+ }
+ }
+
+ // If on container item without columns, only the expander column
+ // may be directly editable:
+ if ( candidate &&
+ GetOwner()->GetExpanderColumn() != candidate &&
+ GetModel()->IsContainer(item) &&
+ !GetModel()->HasContainerColumns(item) )
+ {
+ candidate = GetOwner()->GetExpanderColumn();
+ }
+
+ if ( !candidate )
+ return NULL;
+
+ if ( !IsCellEditableInMode(item, candidate, mode) )
+ return NULL;
+
+ return candidate;
+}
+
+bool wxDataViewMainWindow::IsCellEditableInMode(const wxDataViewItem& item,
+ const wxDataViewColumn *col,
+ wxDataViewCellMode mode) const
+{
+ if ( col->GetRenderer()->GetMode() != mode )
+ return false;
+
+ if ( !GetModel()->IsEnabled(item, col->GetModelColumn()) )
+ return false;
+
+ return true;
+}
+
+void wxDataViewMainWindow::OnCharHook(wxKeyEvent& event)
+{
+ if ( m_editorCtrl )
+ {
+ // Handle any keys special for the in-place editor and return without
+ // calling Skip() below.
+ switch ( event.GetKeyCode() )
+ {
+ case WXK_ESCAPE:
+ m_editorRenderer->CancelEditing();
+ return;
+
+ case WXK_RETURN:
+ m_editorRenderer->FinishEditing();
+ return;
+ }
+ }
+
+ event.Skip();
+}
+
+void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
+{
+ wxWindow * const parent = GetParent();
+
+ // propagate the char event upwards
+ wxKeyEvent eventForParent(event);
+ eventForParent.SetEventObject(parent);
+ if ( parent->ProcessWindowEvent(eventForParent) )
+ return;
+
+ if ( parent->HandleAsNavigationKey(event) )
+ return;
+
+ // no item -> nothing to do
+ if (!HasCurrentRow())
+ {
+ event.Skip();
+ return;
+ }
+
+ // don't use m_linesPerPage directly as it might not be computed yet
+ const int pageSize = GetCountPerPage();
+ wxCHECK_RET( pageSize, wxT("should have non zero page size") );
+
+ switch ( event.GetKeyCode() )
+ {
+ case WXK_RETURN:
+ if ( !event.HasModifiers() )
+ {
+ // 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) or even inline editing.
+
+ const wxDataViewItem item = GetItemByRow(m_currentRow);
+
+ wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED,
+ parent->GetId());
+ le.SetItem(item);
+ le.SetEventObject(parent);
+ le.SetModel(GetModel());
+
+ if ( parent->ProcessWindowEvent(le) )
+ break;
+ // else: fall through to WXK_SPACE handling
+ }
+
+ case WXK_SPACE:
+ if ( !event.HasModifiers() )
+ {
+ // Space toggles activatable items or -- if not activatable --
+ // starts inline editing (this is normally done using F2 on
+ // Windows, but Space is common everywhere else, so use it too
+ // for greater cross-platform compatibility).
+
+ const wxDataViewItem item = GetItemByRow(m_currentRow);
+
+ // Activate the current activatable column. If not column is focused (typically
+ // because the user has full row selected), try to find the first activatable
+ // column (this would typically be a checkbox and we don't want to force the user
+ // to set focus on the checkbox column).
+ wxDataViewColumn *activatableCol = FindColumnForEditing(item, wxDATAVIEW_CELL_ACTIVATABLE);
+
+ if ( activatableCol )
+ {
+ const unsigned colIdx = activatableCol->GetModelColumn();
+ const wxRect cell_rect = GetOwner()->GetItemRect(item, activatableCol);
+
+ wxDataViewRenderer *cell = activatableCol->GetRenderer();
+ cell->PrepareForItem(GetModel(), item, colIdx);
+ cell->WXActivateCell(cell_rect, GetModel(), item, colIdx, NULL);
+
+ break;
+ }
+ // else: fall through to WXK_F2 handling
+ }
+
+ case WXK_F2:
+ if ( !event.HasModifiers() )
+ {
+ if( !m_selection.empty() )
+ {
+ // Mimic Windows 7 behavior: edit the item that has focus
+ // if it is selected and the first selected item if focus
+ // is out of selection.
+ int sel;
+ if ( m_selection.Index(m_currentRow) != wxNOT_FOUND )
+ sel = m_currentRow;
+ else
+ sel = m_selection[0];
+