#pragma hdrstop
#endif
+#if wxUSE_TREELISTCTRL
+
#ifndef WX_PRECOMP
#include "wx/dc.h"
#endif // WX_PRECOMP
}
}
+ void OnDeleteColumn(unsigned col, unsigned numColumns)
+ {
+ wxASSERT_MSG( col, "Shouldn't be called for the first column" );
+
+ if ( !m_columnsTexts )
+ return;
+
+ wxScopedArray<wxString> oldTexts(m_columnsTexts);
+ m_columnsTexts = new wxString[numColumns - 2];
+ for ( unsigned n = 1, m = 1; n < numColumns - 1; n++, m++ )
+ {
+ if ( n == col )
+ {
+ n--;
+ }
+ else // Not the deleted column.
+ {
+ m_columnsTexts[n - 1] = oldTexts[m - 1];
+ }
+ }
+ }
+
+ void OnClearColumns()
+ {
+ if ( m_columnsTexts )
+ {
+ delete [] m_columnsTexts;
+ m_columnsTexts = NULL;
+ }
+ }
+
// Functions for modifying the tree.
// Methods called by wxTreeListCtrl.
void InsertColumn(unsigned col);
+ void DeleteColumn(unsigned col);
+ void ClearColumns();
Node* InsertItem(Node* parent,
Node* previous,
virtual unsigned GetChildren(const wxDataViewItem& item,
wxDataViewItemArray& children) const;
virtual bool IsListModel() const { return m_isFlat; }
+ virtual int Compare(const wxDataViewItem& item1,
+ const wxDataViewItem& item2,
+ unsigned col,
+ bool ascending) const;
private:
// The control we're associated with.
}
// Event handlers toggling the items checkbox if it was clicked.
- virtual bool Activate(const wxRect& WXUNUSED(cell),
- wxDataViewModel* model,
- const wxDataViewItem& item,
- unsigned int WXUNUSED(col))
- {
- static_cast<wxTreeListModel*>(model)->ToggleItem(item);
- return true;
- }
-
- virtual bool LeftClick(const wxPoint& pos,
- const wxRect& WXUNUSED(cell),
- wxDataViewModel* model,
- const wxDataViewItem& item,
- unsigned int WXUNUSED(col))
+ virtual bool ActivateCell(const wxRect& WXUNUSED(cell),
+ wxDataViewModel *model,
+ const wxDataViewItem & item,
+ unsigned int WXUNUSED(col),
+ const wxMouseEvent *mouseEvent)
{
- if ( !wxRect(GetCheckSize()).Contains(pos) )
- return false;
+ if ( mouseEvent )
+ {
+ if ( !wxRect(GetCheckSize()).Contains(mouseEvent->GetPosition()) )
+ return false;
+ }
static_cast<wxTreeListModel*>(model)->ToggleItem(item);
return true;
}
}
+void wxTreeListModel::DeleteColumn(unsigned col)
+{
+ wxCHECK_RET( col < m_numColumns, "Invalid column index" );
+
+ // Update all the items to remove the text for the non first columns.
+ if ( col > 0 )
+ {
+ for ( Node* node = m_root->GetChild(); node; node = node->NextInTree() )
+ {
+ node->OnDeleteColumn(col, m_numColumns);
+ }
+ }
+
+ m_numColumns--;
+}
+
+void wxTreeListModel::ClearColumns()
+{
+ m_numColumns = 0;
+
+ for ( Node* node = m_root->GetChild(); node; node = node->NextInTree() )
+ {
+ node->OnClearColumns();
+ }
+}
+
wxTreeListModelNode*
wxTreeListModel::InsertItem(Node* parent,
Node* previous,
wxScopedPtr<Node>
newItem(new Node(parent, text, imageClosed, imageOpened, data));
+ // FIXME-VC6: This compiler refuses to compare "Node* previous" with
+ // wxTLI_XXX without some help.
+ const wxTreeListItem previousItem(previous);
+
// If we have no children at all, then inserting as last child is the same
// as inserting as the first one so check for it here too.
- if ( previous == wxTLI_FIRST ||
- (previous == wxTLI_LAST && !parent->GetChild()) )
+ if ( previousItem == wxTLI_FIRST ||
+ (previousItem == wxTLI_LAST && !parent->GetChild()) )
{
parent->InsertChild(newItem.get());
}
else // Not the first item, find the previous one.
{
- if ( previous == wxTLI_LAST )
+ if ( previousItem == wxTLI_LAST )
{
previous = parent->GetChild();
// empty string we can return reference to.
wxCHECK_MSG( item, m_root->m_text, "Invalid item" );
- return col == 0 ? item->m_text : item->GetColumnText(col);
+ // Notice that asking for the text of a column of an item that doesn't have
+ // any column texts is not an error so we simply return an empty string in
+ // this case.
+ return col == 0 ? item->m_text
+ : item->HasColumnsTexts() ? item->GetColumnText(col)
+ : m_root->m_text;
}
void wxTreeListModel::SetItemText(Node* item, unsigned col, const wxString& text)
return numChildren;
}
+int
+wxTreeListModel::Compare(const wxDataViewItem& item1,
+ const wxDataViewItem& item2,
+ unsigned col,
+ bool ascending) const
+{
+ // Compare using default alphabetical order if no custom comparator.
+ wxTreeListItemComparator* const comp = m_treelist->m_comparator;
+ if ( !comp )
+ return wxDataViewModel::Compare(item1, item2, col, ascending);
+
+ // Forward comparison to the comparator:
+ int result = comp->Compare(m_treelist, col, FromDVI(item1), FromDVI(item2));
+
+ // And adjust by the sort order if necessary.
+ if ( !ascending )
+ result = -result;
+
+ return result;
+}
+
// ============================================================================
// wxTreeListCtrl implementation
// ============================================================================
EVT_DATAVIEW_ITEM_EXPANDED(wxID_ANY, wxTreeListCtrl::OnItemExpanded)
EVT_DATAVIEW_ITEM_ACTIVATED(wxID_ANY, wxTreeListCtrl::OnItemActivated)
EVT_DATAVIEW_ITEM_CONTEXT_MENU(wxID_ANY, wxTreeListCtrl::OnItemContextMenu)
+ EVT_DATAVIEW_COLUMN_SORTED(wxID_ANY, wxTreeListCtrl::OnColumnSorted)
EVT_SIZE(wxTreeListCtrl::OnSize)
END_EVENT_TABLE()
{
m_view = NULL;
m_model = NULL;
+ m_comparator = NULL;
}
bool wxTreeListCtrl::Create(wxWindow* parent,
m_model->DecRef();
}
+wxWindowList wxTreeListCtrl::GetCompositeWindowParts() const
+{
+ wxWindowList parts;
+ parts.push_back(m_view);
+ return parts;
+}
+
// ----------------------------------------------------------------------------
// Columns
// ----------------------------------------------------------------------------
{
wxCHECK_MSG( col < GetColumnCount(), false, "Invalid column index" );
- return m_view->DeleteColumn(m_view->GetColumn(col));
+ if ( !m_view->DeleteColumn(m_view->GetColumn(col)) )
+ return false;
+
+ m_model->DeleteColumn(col);
+
+ return true;
}
void wxTreeListCtrl::ClearColumns()
{
- if ( m_view )
- m_view->ClearColumns();
+ // Don't assert here, clearing columns of the control before it's created
+ // can be considered valid (just useless).
+ if ( !m_model )
+ return;
+
+ m_view->ClearColumns();
+
+ m_model->ClearColumns();
}
void wxTreeListCtrl::SetColumnWidth(unsigned col, int width)
wxDataViewColumn* const column = m_view->GetColumn(col);
wxCHECK_RET( column, "No such column?" );
- return column->SetWidth(width);
+ column->SetWidth(width);
}
int wxTreeListCtrl::GetColumnWidth(unsigned col) const
return true;
}
+// ----------------------------------------------------------------------------
+// Sorting
+// ----------------------------------------------------------------------------
+
+void wxTreeListCtrl::SetSortColumn(unsigned col, bool ascendingOrder)
+{
+ wxCHECK_RET( col < m_view->GetColumnCount(), "Invalid column index" );
+
+ m_view->GetColumn(col)->SetSortOrder(ascendingOrder);
+}
+
+bool wxTreeListCtrl::GetSortColumn(unsigned* col, bool* ascendingOrder)
+{
+ const unsigned numColumns = m_view->GetColumnCount();
+ for ( unsigned n = 0; n < numColumns; n++ )
+ {
+ wxDataViewColumn* const column = m_view->GetColumn(n);
+ if ( column->IsSortKey() )
+ {
+ if ( col )
+ *col = n;
+
+ if ( ascendingOrder )
+ *ascendingOrder = column->IsSortOrderAscending();
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void wxTreeListCtrl::SetItemComparator(wxTreeListItemComparator* comparator)
+{
+ m_comparator = comparator;
+}
+
// ----------------------------------------------------------------------------
// Events
// ----------------------------------------------------------------------------
-void wxTreeListCtrl::SendEvent(wxEventType evt, wxDataViewEvent& eventDV)
+void wxTreeListCtrl::SendItemEvent(wxEventType evt, wxDataViewEvent& eventDV)
{
wxTreeListEvent eventTL(evt, this, m_model->FromDVI(eventDV.GetItem()));
}
}
+void wxTreeListCtrl::SendColumnEvent(wxEventType evt, wxDataViewEvent& eventDV)
+{
+ wxTreeListEvent eventTL(evt, this, wxTreeListItem());
+ eventTL.SetColumn(eventDV.GetColumn());
+
+ if ( !ProcessWindowEvent(eventTL) )
+ {
+ eventDV.Skip();
+ return;
+ }
+
+ if ( !eventTL.IsAllowed() )
+ {
+ eventDV.Veto();
+ }
+}
+
void
wxTreeListCtrl::OnItemToggled(wxTreeListItem item, wxCheckBoxState stateOld)
{
void wxTreeListCtrl::OnSelectionChanged(wxDataViewEvent& event)
{
- SendEvent(wxEVT_COMMAND_TREELIST_SELECTION_CHANGED, event);
+ SendItemEvent(wxEVT_COMMAND_TREELIST_SELECTION_CHANGED, event);
}
void wxTreeListCtrl::OnItemExpanding(wxDataViewEvent& event)
{
- SendEvent(wxEVT_COMMAND_TREELIST_ITEM_EXPANDING, event);
+ SendItemEvent(wxEVT_COMMAND_TREELIST_ITEM_EXPANDING, event);
}
void wxTreeListCtrl::OnItemExpanded(wxDataViewEvent& event)
{
- SendEvent(wxEVT_COMMAND_TREELIST_ITEM_EXPANDED, event);
+ SendItemEvent(wxEVT_COMMAND_TREELIST_ITEM_EXPANDED, event);
}
void wxTreeListCtrl::OnItemActivated(wxDataViewEvent& event)
{
- SendEvent(wxEVT_COMMAND_TREELIST_ITEM_ACTIVATED, event);
+ SendItemEvent(wxEVT_COMMAND_TREELIST_ITEM_ACTIVATED, event);
}
void wxTreeListCtrl::OnItemContextMenu(wxDataViewEvent& event)
{
- SendEvent(wxEVT_COMMAND_TREELIST_ITEM_CONTEXT_MENU, event);
+ SendItemEvent(wxEVT_COMMAND_TREELIST_ITEM_CONTEXT_MENU, event);
+}
+
+void wxTreeListCtrl::OnColumnSorted(wxDataViewEvent& event)
+{
+ SendColumnEvent(wxEVT_COMMAND_TREELIST_COLUMN_SORTED, event);
}
// ----------------------------------------------------------------------------
const wxRect rect = GetClientRect();
m_view->SetSize(rect);
- // Resize the first column to take the remaining available space, if
- // any.
+#ifdef wxHAS_GENERIC_DATAVIEWCTRL
+ // The generic implementation doesn't refresh itself immediately which
+ // is annoying during "live resizing", so do it forcefully here to
+ // ensure that the items are re-laid out and the focus rectangle is
+ // redrawn correctly (instead of leaving traces) while our size is
+ // being changed.
+ wxWindow* const view = GetView();
+ view->Refresh();
+ view->Update();
+#endif // wxHAS_GENERIC_DATAVIEWCTRL
+
+ // Resize the first column to take the remaining available space.
const unsigned numColumns = GetColumnCount();
if ( !numColumns )
return;
// There is a bug in generic wxDataViewCtrl: if the column width sums
// up to the total size, horizontal scrollbar (unnecessarily) appears,
- // so subtract 10 pixels to ensure this doesn't happen.
- int remainingWidth = rect.width - 10;
+ // so subtract a bit to ensure this doesn't happen.
+ int remainingWidth = rect.width - 5;
for ( unsigned n = 1; n < GetColumnCount(); n++ )
{
remainingWidth -= GetColumnWidth(n);
- if ( remainingWidth < 0 )
- break;
+ if ( remainingWidth <= 0 )
+ {
+ // There is not enough space, as we're not going to give the
+ // first column negative width anyhow, just don't do anything.
+ return;
+ }
}
- // We don't decrease the width of the first column, even if we had
- // increased it ourselves, because we want to avoid changing its size
- // if the user resized it. We might want to remember if this was the
- // case or if we only ever adjusted it automatically in the future.
- if ( remainingWidth > GetColumnWidth(0) )
- SetColumnWidth(0, remainingWidth);
+ SetColumnWidth(0, remainingWidth);
}
}
+wxWindow* wxTreeListCtrl::GetView() const
+{
+#ifdef wxHAS_GENERIC_DATAVIEWCTRL
+ return m_view->GetMainWindow();
+#else
+ return m_view;
+#endif
+}
+
// ============================================================================
// wxTreeListEvent implementation
// ============================================================================
-wxIMPLEMENT_ABSTRACT_CLASS(wxTreeListEvent, wxNotifyEvent)
+wxIMPLEMENT_DYNAMIC_CLASS(wxTreeListEvent, wxNotifyEvent)
#define wxDEFINE_TREELIST_EVENT(name) \
wxDEFINE_EVENT(wxEVT_COMMAND_TREELIST_##name, wxTreeListEvent)
wxDEFINE_TREELIST_EVENT(ITEM_CHECKED);
wxDEFINE_TREELIST_EVENT(ITEM_ACTIVATED);
wxDEFINE_TREELIST_EVENT(ITEM_CONTEXT_MENU);
+wxDEFINE_TREELIST_EVENT(COLUMN_SORTED);
#undef wxDEFINE_TREELIST_EVENT
+
+#endif // wxUSE_TREELISTCTRL