+ return rect;
+}
+
+int wxDataViewMainWindow::GetLineStart( unsigned int row ) const
+{
+ const wxDataViewModel *model = GetOwner()->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();
+
+ 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;
+ 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!
+
+ wxVariant value;
+ model->GetValue( value, item, column->GetModelColumn() );
+
+ wxDataViewRenderer *renderer =
+ const_cast<wxDataViewRenderer*>(column->GetRenderer());
+ renderer->SetValue( value );
+ 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 = GetOwner()->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();
+
+ 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;
+ 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!
+
+ wxVariant value;
+ model->GetValue( value, item, column->GetModelColumn() );
+
+ wxDataViewRenderer *renderer =
+ const_cast<wxDataViewRenderer*>(column->GetRenderer());
+ renderer->SetValue( value );
+ height = wxMax( height, renderer->GetSize().y );
+ }
+
+ yy += height;
+ if (y < yy)
+ return row;
+
+ row++;
+ }
+}
+
+int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const
+{
+ const wxDataViewModel *model = GetOwner()->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();
+
+ 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();
+ 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!
+
+ wxVariant value;
+ model->GetValue( value, item, column->GetModelColumn() );
+
+ wxDataViewRenderer *renderer =
+ const_cast<wxDataViewRenderer*>(column->GetRenderer());
+ renderer->SetValue( value );
+ height = wxMax( height, renderer->GetSize().y );
+ }
+
+ return height;
+ }
+ else
+ {
+ return m_lineHeight;
+ }
+}
+
+class RowToItemJob: 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<int>(row))
+ {
+ ret = node->GetItem();
+ return DoJob::OK;
+ }
+
+ if( node->GetSubTreeCount() + current < static_cast<int>(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<int>(row) - current - 1;
+ ret = node->GetChildren().Item( index );
+ return DoJob::OK;
+ }
+ return DoJob::CONT;
+ }
+ }
+
+ virtual int operator() ( void * n )
+ {
+ current ++;
+ if( current == static_cast<int>(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 (!m_root)
+ {
+ return wxDataViewItem( wxUIntToPtr(row) );
+ }
+ 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 )
+ {
+ this->row = row;
+ this->current = current;
+ ret = NULL;
+ parent = node;
+ }
+ virtual ~RowToTreeNodeJob(){ }
+
+ virtual int operator() ( wxDataViewTreeNode * node )
+ {
+ current ++;
+ if( current == static_cast<int>(row))
+ {
+ ret = node;
+ return DoJob::OK;
+ }
+
+ if( node->GetSubTreeCount() + current < static_cast<int>(row) )
+ {
+ current += node->GetSubTreeCount();
+ return DoJob::IGR;
+ }
+ 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)
+ {
+ int index = static_cast<int>(row) - current - 1;
+ void * n = node->GetChildren().Item( index );
+ ret = new wxDataViewTreeNode( parent );
+ ret->SetItem( wxDataViewItem( n ));
+ ret->SetHasChildren(false);
+ return DoJob::OK;
+ }
+ return DoJob::CONT;
+ }
+ }
+
+ virtual int operator() ( void * n )
+ {
+ current ++;
+ if( current == static_cast<int>(row))
+ {
+ ret = new wxDataViewTreeNode( parent );
+ ret->SetItem( wxDataViewItem( n ));
+ ret->SetHasChildren(false);
+ return DoJob::OK;
+ }
+
+ return DoJob::CONT;
+ }
+
+ 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();
+}
+
+wxDataViewEvent wxDataViewMainWindow::SendExpanderEvent( wxEventType type,
+ const wxDataViewItem & item )
+{
+ wxWindow *parent = GetParent();
+ wxDataViewEvent le(type, parent->GetId());
+
+ le.SetEventObject(parent);
+ le.SetModel(GetOwner()->GetModel());
+ le.SetItem( item );
+
+ parent->GetEventHandler()->ProcessEvent(le);
+ return le;
+}
+
+bool wxDataViewMainWindow::IsExpanded( unsigned int row ) const
+{
+ if (IsVirtualList())
+ return false;
+
+ wxDataViewTreeNode * node = GetTreeNodeByRow(row);
+ if (!node)
+ return false;
+
+ if (!node->HasChildren())
+ {
+ delete node;
+ return false;
+ }
+
+ return node->IsOpen();
+}
+
+bool wxDataViewMainWindow::HasChildren( unsigned int row ) const
+{
+ if (IsVirtualList())
+ return false;
+
+ wxDataViewTreeNode * node = GetTreeNodeByRow(row);
+ if (!node)
+ return false;
+
+ if (!node->HasChildren())
+ {
+ delete node;
+ return false;
+ }
+
+ return true;
+}
+
+void wxDataViewMainWindow::Expand( unsigned int row )
+{
+ if (IsVirtualList())
+ return;
+
+ wxDataViewTreeNode * node = GetTreeNodeByRow(row);
+ if (!node)
+ 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() )
+ return;
+
+ node->ToggleOpen();
+
+ // build the children of current node
+ if( node->GetChildrenNumber() == 0 )
+ {
+ SortPrepare();
+ ::BuildTreeHelper(GetOwner()->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 (IsVirtualList())
+ return;
+
+ wxDataViewTreeNode *node = GetTreeNodeByRow(row);
+ if (!node)
+ return;
+
+ if (!node->HasChildren())
+ {
+ delete node;
+ return;
+ }
+
+ if (node->IsOpen())
+ {
+ wxDataViewEvent e =
+ SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING,node->GetItem());
+ if( e.GetSkipped() )
+ 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 )
+{
+ wxDataViewModel * model = GetOwner()->GetModel();
+ if( model == NULL )
+ return NULL;
+
+ if (!item.IsOk())
+ return m_root;
+
+ // Compose the a parent-chain of the finding item
+ ItemList list;
+ list.DeleteContents( true );
+ wxDataViewItem it( item );
+ while( it.IsOk() )
+ {
+ wxDataViewItem * pItem = new wxDataViewItem( it );
+ list.Insert( pItem );
+ 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( ItemList::const_iterator iter = list.begin(); iter !=list.end(); iter++ )
+ {
+ if( node->HasChildren() )
+ {
+ if( node->GetChildrenNumber() == 0 )
+ {
+ SortPrepare();
+ ::BuildTreeHelper(model, node->GetItem(), node);
+ }
+
+ wxDataViewTreeNodes nodes = node->GetNodes();
+ unsigned int i;
+ bool found = false;
+
+ for (i = 0; i < nodes.GetCount(); i ++)
+ {
+ if (nodes[i]->GetItem() == (**iter))
+ {
+ if (nodes[i]->GetItem() == item)
+ return nodes[i];
+
+ node = nodes[i];
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return NULL;
+ }
+ else
+ return NULL;
+ }
+ 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 (!IsVirtualList() && (column == 0 || GetOwner()->GetExpanderColumn() == 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,
+ GetLineStart( row ),
+ width - indent,
+ GetLineHeight( row ) );
+
+ GetOwner()->CalcScrolledPosition( itemRect.x, itemRect.y,
+ &itemRect.x, &itemRect.y );
+
+ return itemRect;
+}
+
+int wxDataViewMainWindow::RecalculateCount()
+{
+ if (!m_root)
+ {
+ wxDataViewIndexListModel *list_model =
+ (wxDataViewIndexListModel*) GetOwner()->GetModel();
+#ifndef __WXMAC__
+ return list_model->GetLastIndex() + 1;
+#else
+ return list_model->GetLastIndex() - 1;
+#endif
+ }
+ else
+ {
+ return m_root->GetSubTreeCount();
+ }
+}
+
+class ItemToRowJob : public DoJob
+{
+public:
+ ItemToRowJob(const wxDataViewItem& item_, ItemList::const_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::OK;
+ }
+
+ if( node->GetItem() == **m_iter )
+ {
+ m_iter++;
+ return DoJob::CONT;
+ }
+ else
+ {
+ ret += node->GetSubTreeCount();
+ return DoJob::IGR;
+ }
+
+ }
+
+ 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; }
+
+private:
+ ItemList::const_iterator m_iter;
+ wxDataViewItem item;
+ int ret;
+
+};
+
+int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem & item) const
+{
+ const wxDataViewModel * model = GetOwner()->GetModel();
+ if( model == NULL )
+ return -1;
+
+ if (!m_root)
+ {
+ return wxPtrToUInt( item.GetID() );
+ }
+ else
+ {
+ if( !item.IsOk() )
+ return -1;
+
+ // Compose the a parent-chain of the finding item
+ ItemList list;
+ wxDataViewItem * pItem;
+ list.DeleteContents( true );
+ wxDataViewItem it( item );
+ while( it.IsOk() )
+ {
+ pItem = new wxDataViewItem( it );
+ list.Insert( pItem );
+ it = model->GetParent( it );
+ }
+ pItem = new wxDataViewItem( );
+ list.Insert( pItem );
+
+ ItemToRowJob job( item, list.begin() );
+ Walker(m_root , job );
+ return job.GetResult();
+ }
+}
+
+static void BuildTreeHelper( wxDataViewModel * model, wxDataViewItem & item,
+ wxDataViewTreeNode * node)
+{
+ if( !model->IsContainer( item ) )
+ return;
+
+ wxDataViewItemArray children;
+ unsigned int num = model->GetChildren( item, children);
+
+ unsigned int index = 0;
+ while( index < num )
+ {
+ if( model->IsContainer( children[index] ) )
+ {
+ wxDataViewTreeNode * n = new wxDataViewTreeNode( node );
+ n->SetItem(children[index]);
+ n->SetHasChildren( true );
+ node->AddNode( n );
+ }
+ else
+ {
+ node->AddLeaf( children[index].GetID() );
+ }
+ index ++;
+ }
+ node->SetSubTreeCount( num );
+ wxDataViewTreeNode * n = node->GetParent();
+ if( n != NULL)
+ n->ChangeSubTreeCount(num);
+
+}
+
+void wxDataViewMainWindow::BuildTree(wxDataViewModel * model)
+{
+ DestroyTree();
+
+ if (GetOwner()->GetModel()->IsVirtualListModel())
+ {
+ m_count = -1;
+ return;
+ }
+
+ m_root = new wxDataViewTreeNode( NULL );
+ m_root->SetHasChildren(true);
+
+ // First we define a invalid item to fetch the top-level elements
+ wxDataViewItem item;
+ SortPrepare();
+ BuildTreeHelper( model, item, m_root);
+ 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;
+ }
+}
+
+void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
+{
+ if ( GetParent()->HandleAsNavigationKey(event) )
+ return;
+
+ // no item -> nothing to do
+ if (!HasCurrentRow())
+ {