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 AddChild( wxDataViewTreeNode * node )
{
- m_leaves.Add( leaf );
+ if ( !m_branchData )
+ m_branchData = new BranchNodeData;
+
+ m_branchData->children.Add( node );
+ // 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;
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);
}
}
- 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; }
+ // "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;
+ }
+ }
+
+ 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
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
void SortPrepare()
{
- g_model = GetOwner()->GetModel();
+ g_model = GetModel();
wxDataViewColumn* col = GetOwner()->GetSortingColumn();
if( !col )
{
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
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 );
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 );
{
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();
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();
// 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;
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 );
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 );
wxDataViewItem item = GetItemByRow( row );
- wxDataViewModel *model = GetOwner()->GetModel();
+ wxDataViewModel *model = GetModel();
wxCustomDataObject *obj = (wxCustomDataObject *) GetDropTarget()->GetDataObject();
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;
void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
{
- wxDataViewModel *model = GetOwner()->GetModel();
+ wxDataViewModel *model = GetModel();
wxAutoBufferedPaintDC dc( this );
#ifdef __WXMSW__
// 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);
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);
- wxDataViewTreeNode * node;
- node = FindNode(parent);
+ if ( !parentNode )
+ return false;
- if( node == NULL )
- return false;
+ wxDataViewTreeNode *itemNode = new wxDataViewTreeNode(parentNode, item);
+ itemNode->SetHasChildren(GetModel()->IsContainer(item));
- node->SetHasChildren( true );
+ parentNode->SetHasChildren(true);
+ parentNode->AddChild(itemNode);
+ 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()->UpdateColBestWidths();
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];
+
+ itemRow = GetRowByItem(siblingNode->GetItem()) +
+ siblingNode->GetSubTreeCount() +
+ 1;
}
- }
- wxCHECK_MSG( n != NULL, false, "item not found" );
+ wxDataViewSelection newsel(wxDataViewSelectionCmp);
- node->GetNodes().Remove( n );
- sub -= n->GetSubTreeCount();
- ::DestroyTreeHelper(n);
+ 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
+ }
+
+ 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()->UpdateColBestWidths();
UpdateDisplay();
return true;
bool wxDataViewMainWindow::ItemChanged(const wxDataViewItem & item)
{
- GetOwner()->InvalidateColBestWidths();
-
SortPrepare();
g_model->Resort();
+ GetOwner()->UpdateColBestWidths();
+
// 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);
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
SortPrepare();
g_model->Resort();
+ GetOwner()->UpdateColBestWidth(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));
bool wxDataViewMainWindow::Cleared()
{
- GetOwner()->InvalidateColBestWidths();
-
DestroyTree();
m_selection.Clear();
SortPrepare();
- BuildTree( GetOwner()->GetModel() );
+ BuildTree( GetModel() );
+ GetOwner()->UpdateColBestWidths();
UpdateDisplay();
return true;
void wxDataViewMainWindow::RecalculateDisplay()
{
- wxDataViewModel *model = GetOwner()->GetModel();
+ wxDataViewModel *model = GetModel();
if (!model)
{
Refresh();
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);
int wxDataViewMainWindow::GetLineStart( unsigned int row ) const
{
- const wxDataViewModel *model = GetOwner()->GetModel();
+ const wxDataViewModel *model = GetModel();
if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT)
{
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;
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) )
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;
int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const
{
- const wxDataViewModel *model = GetOwner()->GetModel();
+ const wxDataViewModel *model = GetModel();
if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT)
{
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();
}
}
-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 (IsVirtualList())
- {
- return wxDataViewItem( wxUIntToPtr(row+1) );
- }
- else
- {
- RowToItemJob job( row, -2 );
- Walker( m_root , job );
- return job.GetResult();
- }
-}
class RowToTreeNodeJob: public DoJob
{
ret = NULL;
parent = node;
}
- virtual ~RowToTreeNodeJob(){ }
virtual int operator() ( wxDataViewTreeNode * node )
{
if( current == static_cast<int>(row))
{
ret = node;
- return DoJob::OK;
+ return DoJob::DONE;
}
if( node->GetSubTreeCount() + current < static_cast<int>(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<int>(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<int>(row) - current - 1;
+ ret = node->GetChildNodes()[index];
+ return DoJob::DONE;
}
- 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::CONTINUE;
}
-
- return DoJob::CONT;
}
wxDataViewTreeNode * GetResult() const
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
return false;
if (!node->HasChildren())
- {
- delete node;
return false;
- }
return node->IsOpen();
}
return false;
if (!node->HasChildren())
- {
- delete node;
return false;
- }
return true;
}
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
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;
wxDataViewTreeNode * wxDataViewMainWindow::FindNode( const wxDataViewItem & item )
{
- const wxDataViewModel * model = GetOwner()->GetModel();
+ const wxDataViewModel * model = GetModel();
if( model == NULL )
return NULL;
{
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)
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,
if (IsVirtualList())
{
wxDataViewVirtualListModel *list_model =
- (wxDataViewVirtualListModel*) GetOwner()->GetModel();
+ (wxDataViewVirtualListModel*) GetModel();
return list_model->GetCount();
}
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; }
int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem & item) const
{
- const wxDataViewModel * model = GetOwner()->GetModel();
+ const wxDataViewModel * model = GetModel();
if( model == NULL )
return -1;
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->AddChild(n);
}
- 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;
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;
}
}
switch ( event.GetKeyCode() )
{
case WXK_RETURN:
+ case WXK_SPACE:
{
wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED,
parent->GetId());
le.SetItem( GetItemByRow(m_currentRow) );
le.SetEventObject(parent);
- le.SetModel(GetOwner()->GetModel());
+ le.SetModel(GetModel());
parent->GetEventHandler()->ProcessEvent(le);
}
{
wxDataViewTreeNode *parent_node = node->GetParent();
- if(!node->HasChildren())
- delete node;
-
if (parent_node)
{
int parent = GetRowByItem( parent_node->GetItem() );
return;
}
- // Test whether the mouse is hovered on the tree item button
+ // 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;
+ int expanderOffset = 0;
if ((!IsList()) && (GetOwner()->GetExpanderColumn() == col))
{
wxDataViewTreeNode * node = GetTreeNodeByRow(current);
wxRect rect( xpos + indent,
GetLineStart( current ) + (GetLineHeight(current) - m_lineHeight)/2,
m_lineHeight, m_lineHeight);
+
+ expanderOffset = indent + m_lineHeight;
+
if( rect.Contains(x, y) )
{
// So the mouse is over the expander
m_underMouse = node;
}
}
- if (node!=NULL && !node->HasChildren())
- delete node;
}
if (!hoverOverExpander)
{
}
}
- wxDataViewModel *model = GetOwner()->GetModel();
+ wxDataViewModel *model = GetModel();
#if wxUSE_DRAG_AND_DROP
if (event.Dragging())
le.SetColumn( col->GetModelColumn() );
le.SetDataViewColumn( col );
le.SetEventObject(parent);
- le.SetModel(GetOwner()->GetModel());
+ le.SetModel(GetModel());
parent->GetEventHandler()->ProcessEvent(le);
}
le.SetColumn( col->GetModelColumn() );
le.SetDataViewColumn( col );
le.SetEventObject(parent);
- le.SetModel(GetOwner()->GetModel());
+ le.SetModel(GetModel());
le.SetValue(value);
parent->GetEventHandler()->ProcessEvent(le);
}
// notify cell about click
cell->PrepareForItem(model, item, col->GetModelColumn());
- wxRect cell_rect( xpos, GetLineStart( current ),
- col->GetWidth(), GetLineHeight( current ) );
+ wxRect cell_rect( xpos + expanderOffset,
+ GetLineStart( current ),
+ col->GetWidth() - expanderOffset,
+ 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
event.Skip();
}
-wxDataViewItem wxDataViewMainWindow::GetSelection() const
-{
- if( m_selection.GetCount() != 1 )
- return wxDataViewItem();
-
- return GetItemByRow( m_selection.Item(0));
-}
-
//-----------------------------------------------------------------------------
// wxDataViewCtrl
//-----------------------------------------------------------------------------
return true;
}
-void wxDataViewCtrl::InvalidateColBestWidth(int idx)
+void wxDataViewCtrl::UpdateColBestWidth(int idx)
{
m_colsBestWidths[idx] = 0;
m_headerArea->UpdateColumn(idx);
}
-void wxDataViewCtrl::InvalidateColBestWidths()
+void wxDataViewCtrl::UpdateColBestWidths()
{
m_colsBestWidths.clear();
m_colsBestWidths.resize(m_cols.size());
}
}
-// 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 )
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<unsigned int>(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);