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
//-----------------------------------------------------------------------------
}
}
+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
//-----------------------------------------------------------------------------
// 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)
}
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();
model->Resort();
owner->OnColumnChange(idx);
+
+ SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_SORTED, idx);
}
void OnRClick(wxHeaderCtrlEvent& event)
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;
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
{
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
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 );
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 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;
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 );
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();
return ctrl;
}
-bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxControl *editor, wxVariant &value )
+bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxWindow *editor, wxVariant &value )
{
wxTextCtrl *text = (wxTextCtrl*) editor;
value = text->GetValue();
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;
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);
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))
{
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;
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();
return ctrl;
}
-bool wxDataViewIconTextRenderer::GetValueFromEditorCtrl( wxControl *editor, wxVariant& value )
+bool wxDataViewIconTextRenderer::GetValueFromEditorCtrl( wxWindow *editor, wxVariant& value )
{
wxTextCtrl *text = (wxTextCtrl*) editor;
// 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;
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++)
void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
{
- wxDataViewModel *model = GetOwner()->GetModel();
+ wxDataViewModel *model = GetModel();
wxAutoBufferedPaintDC dc( this );
#ifdef __WXMSW__
dc.DrawRectangle(GetClientSize());
#endif
+ if ( IsEmpty() )
+ {
+ // No items to draw.
+ return;
+ }
+
// prepare the DC
GetOwner()->PrepareDC( dc );
dc.SetFont( GetFont() );
}
#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;
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
// 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);
+
+ 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;
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;
}
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()->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;
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);
+ parent->ProcessWindowEvent(le);
}
void wxDataViewMainWindow::RefreshRow( unsigned int row )
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;
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
+
+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<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
-{
-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 )
{
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;
// 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)
}
else
return NULL;
+
+ if ( !iter )
+ break;
}
return NULL;
}
// 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,
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->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;
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;
}
}
{
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:
if (index < 0)
index = 0;
- OnArrowChar( index, event );
+ OnVerticalNavigation( index, event );
}
break;
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);
}
}
+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)
}
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();
}
wxDataViewRenderer *cell = col->GetRenderer();
- unsigned int current = GetLineAt( y );
if ((current >= GetRowCount()) || (x > GetEndOfLastCol()))
{
// Unselect all if below the last row ?
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
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)
{
}
}
- wxDataViewModel *model = GetOwner()->GetModel();
-
#if wxUSE_DRAG_AND_DROP
if (event.Dragging())
{
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;
m_lastOnSame = false;
}
- wxDataViewItem item = GetItemByRow(current);
bool ignore_other_columns =
- ((GetOwner()->GetExpanderColumn() != col) &&
+ ((expander != col) &&
(model->IsContainer(item)) &&
(!model->HasContainerColumns(item)));
}
else if ( current == m_lineLastClicked )
{
+ bool activated = false;
+
if ((!ignore_other_columns) && (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE))
{
const unsigned colIdx = col->GetModelColumn();
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());
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;
}
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())
{
}
// 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
event.Skip();
}
-wxDataViewItem wxDataViewMainWindow::GetSelection() const
-{
- if( m_selection.GetCount() != 1 )
- return wxDataViewItem();
-
- return GetItemByRow( m_selection.Item(0));
-}
-
//-----------------------------------------------------------------------------
// wxDataViewCtrl
//-----------------------------------------------------------------------------
m_sortingColumnIdx = wxNOT_FOUND;
m_headerArea = NULL;
+
+ m_colsDirty = false;
}
bool wxDataViewCtrl::Create(wxWindow *parent,
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()
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];
bool wxDataViewCtrl::ClearColumns()
{
+ SetExpanderColumn(NULL);
m_cols.Clear();
m_colsBestWidths.clear();
OnColumnsCountChanged();
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
}
}
-// 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);
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