// Purpose: wxDataViewCtrl generic implementation
// Author: Robert Roebling
// Modified by: Francesco Montorsi, Guru Kathiresan, Bo Yang
-// Id: $Id$
// Copyright: (c) 1998 Robert Roebling
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For the generic implementation, both the leaf nodes and the nodes are sorted for
// fast search when needed
static wxDataViewModel* g_model;
-static int g_column = -2;
+
+// The column is either the index of the column to be used for sorting or one
+// of the special values in this enum:
+enum
+{
+ // Sort when we're thawed later.
+ SortColumn_OnThaw = -3,
+
+ // Don't sort at all.
+ SortColumn_None = -2,
+
+ // Sort using the model default sort order.
+ SortColumn_Default = -1
+};
+
+static int g_column = SortColumn_None;
static bool g_asending = true;
// ----------------------------------------------------------------------------
}
}
+void wxDataViewColumn::UnsetAsSortKey()
+{
+ m_sort = false;
+
+ if ( m_owner )
+ m_owner->SetSortingColumnIndex(wxNOT_FOUND);
+
+ UpdateDisplay();
+}
+
void wxDataViewColumn::SetSortOrder(bool ascending)
{
if ( !m_owner )
{
const unsigned idx = event.GetColumn();
- if ( SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK, idx) )
+ if ( SendEvent(wxEVT_DATAVIEW_COLUMN_HEADER_CLICK, idx) )
return;
// default handling for the column click is to sort by this column or
owner->OnColumnChange(idx);
- SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_SORTED, idx);
+ SendEvent(wxEVT_DATAVIEW_COLUMN_SORTED, idx);
}
void OnRClick(wxHeaderCtrlEvent& event)
{
- if ( !SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK,
+ if ( !SendEvent(wxEVT_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK,
event.GetColumn()) )
event.Skip();
}
UpdateDisplay();
}
+ // Override the base class method to resort if needed, i.e. if
+ // SortPrepare() was called -- and ignored -- while we were frozen.
+ virtual void DoThaw()
+ {
+ if ( g_column == SortColumn_OnThaw )
+ {
+ Resort();
+ g_column = SortColumn_None;
+ }
+
+ wxWindow::DoThaw();
+ }
+
void SortPrepare()
{
g_model = GetModel();
+
wxDataViewColumn* col = GetOwner()->GetSortingColumn();
if( !col )
{
if (g_model->HasDefaultCompare())
- g_column = -1;
+ {
+ // See below for the explanation of IsFrozen() test.
+ if ( IsFrozen() )
+ g_column = SortColumn_OnThaw;
+ else
+ g_column = SortColumn_Default;
+ }
else
- g_column = -2;
+ g_column = SortColumn_None;
g_asending = true;
return;
}
+
+ // Avoid sorting while the window is frozen, this allows to quickly add
+ // many items without resorting after each addition and only resort
+ // them all at once when the window is finally thawed, see above.
+ if ( IsFrozen() )
+ {
+ g_column = SortColumn_OnThaw;
+ return;
+ }
+
g_column = col->GetModelColumn();
g_asending = col->IsSortOrderAscending();
}
bool wxDataViewBitmapRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
{
if (m_bitmap.IsOk())
- dc->DrawBitmap( m_bitmap, cell.x, cell.y );
+ dc->DrawBitmap( m_bitmap, cell.x, cell.y, true /* use mask */ );
else if (m_icon.IsOk())
dc->DrawIcon( m_icon, cell.x, cell.y );
return true;
}
-bool wxDataViewToggleRenderer::WXActivateCell(const wxRect& WXUNUSED(cell),
+bool wxDataViewToggleRenderer::WXActivateCell(const wxRect& WXUNUSED(cellRect),
wxDataViewModel *model,
const wxDataViewItem& item,
unsigned int col,
{
if ( mouseEvent )
{
- // only react to clicks directly on the checkbox, not elsewhere in the same cell:
+ // Only react to clicks directly on the checkbox, not elsewhere in the
+ // same cell.
if ( !wxRect(GetSize()).Contains(mouseEvent->GetPosition()) )
return false;
}
wxDataViewModel *model = GetModel();
- wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE, m_owner->GetId() );
+ wxDataViewEvent event( wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE, m_owner->GetId() );
event.SetEventObject( m_owner );
event.SetItem( item );
event.SetModel( model );
wxDataViewModel *model = GetModel();
- wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE, m_owner->GetId() );
+ wxDataViewEvent event( wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE, m_owner->GetId() );
event.SetEventObject( m_owner );
event.SetItem( item );
event.SetModel( model );
wxCustomDataObject *obj = (wxCustomDataObject *) GetDropTarget()->GetDataObject();
- wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP, m_owner->GetId() );
+ wxDataViewEvent event( wxEVT_DATAVIEW_ITEM_DROP, m_owner->GetId() );
event.SetEventObject( m_owner );
event.SetItem( item );
event.SetModel( model );
// Send the event to wxDataViewCtrl itself.
wxWindow * const parent = GetParent();
- wxDataViewEvent cache_event(wxEVT_COMMAND_DATAVIEW_CACHE_HINT, parent->GetId());
+ wxDataViewEvent cache_event(wxEVT_DATAVIEW_CACHE_HINT, parent->GetId());
cache_event.SetEventObject(parent);
cache_event.SetCache(item_start, item_last - 1);
parent->ProcessWindowEvent(cache_event);
// Send event
wxWindow *parent = GetParent();
- wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId());
+ wxDataViewEvent le(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId());
le.SetEventObject(parent);
le.SetModel(GetModel());
le.SetItem(item);
bool wxDataViewMainWindow::ValueChanged( const wxDataViewItem & item, unsigned int model_column )
{
- int view_column = -1;
- unsigned int n_col = m_owner->GetColumnCount();
- for (unsigned i = 0; i < n_col; i++)
- {
- wxDataViewColumn *column = m_owner->GetColumn( i );
- if (column->GetModelColumn() == model_column)
- {
- view_column = (int) i;
- break;
- }
- }
- if (view_column == -1)
+ int view_column = m_owner->GetModelColumnIndex(model_column);
+ if ( view_column == wxNOT_FOUND )
return false;
// NOTE: to be valid, we cannot use e.g. INT_MAX - 1
// Send event
wxWindow *parent = GetParent();
- wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId());
+ wxDataViewEvent le(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId());
le.SetEventObject(parent);
le.SetModel(GetModel());
le.SetItem(item);
void wxDataViewMainWindow::SendSelectionChangedEvent( const wxDataViewItem& item)
{
wxWindow *parent = GetParent();
- wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, parent->GetId());
+ wxDataViewEvent le(wxEVT_DATAVIEW_SELECTION_CHANGED, parent->GetId());
le.SetEventObject(parent);
le.SetModel(GetModel());
if (!node->IsOpen())
{
- if ( !SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING, node->GetItem()) )
+ if ( !SendExpanderEvent(wxEVT_DATAVIEW_ITEM_EXPANDING, node->GetItem()) )
{
// Vetoed by the event handler.
return;
m_count = -1;
UpdateDisplay();
// Send the expanded event
- SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDED,node->GetItem());
+ SendExpanderEvent(wxEVT_DATAVIEW_ITEM_EXPANDED,node->GetItem());
}
}
if (node->IsOpen())
{
- if ( !SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING,node->GetItem()) )
+ if ( !SendExpanderEvent(wxEVT_DATAVIEW_ITEM_COLLAPSING,node->GetItem()) )
{
// Vetoed by the event handler.
return;
m_count = -1;
UpdateDisplay();
- SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSED,node->GetItem());
+ SendExpanderEvent(wxEVT_DATAVIEW_ITEM_COLLAPSED,node->GetItem());
}
}
}
else
{
- // Enter activates the item, i.e. sends wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED to
+ // Enter activates the item, i.e. sends wxEVT_DATAVIEW_ITEM_ACTIVATED to
// it. Only if that event is not handled do we activate column renderer (which
// is normally done by Space) or even inline editing.
const wxDataViewItem item = GetItemByRow(m_currentRow);
- wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED,
+ wxDataViewEvent le(wxEVT_DATAVIEW_ITEM_ACTIVATED,
parent->GetId());
le.SetItem(item);
le.SetEventObject(parent);
if (event.RightUp())
{
wxWindow *parent = GetParent();
- wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU, parent->GetId());
+ wxDataViewEvent le(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, parent->GetId());
le.SetEventObject(parent);
le.SetModel(model);
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)
+#if wxUSE_DRAG_AND_DROP
+ if (event.Dragging() || ((m_dragCount > 0) && event.Leaving()))
{
- event.Skip();
+ if (m_dragCount == 0)
+ {
+ // we have to report the raw, physical coords as we want to be
+ // able to call HitTest(event.m_pointDrag) from the user code to
+ // get the item being dragged
+ m_dragStart = event.GetPosition();
+ }
+
+ m_dragCount++;
+ if ((m_dragCount < 3) && (event.Leaving()))
+ m_dragCount = 3;
+ else if (m_dragCount != 3)
+ return;
+
+ if (event.LeftIsDown())
+ {
+ 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 itemDragged = GetItemByRow( drag_item_row );
+
+ // Notify cell about drag
+ wxDataViewEvent event( wxEVT_DATAVIEW_ITEM_BEGIN_DRAG, m_owner->GetId() );
+ event.SetEventObject( m_owner );
+ event.SetItem( itemDragged );
+ event.SetModel( model );
+ if (!m_owner->HandleWindowEvent( event ))
+ return;
+
+ if (!event.IsAllowed())
+ return;
+
+ wxDataObject *obj = event.GetDataObject();
+ if (!obj)
+ return;
+
+ wxDataViewDropSource drag( this, drag_item_row );
+ drag.SetData( *obj );
+ /* wxDragResult res = */ drag.DoDragDrop(event.GetDragFlags());
+ delete obj;
+ }
return;
}
+ else
+ {
+ m_dragCount = 0;
+ }
+#endif // wxUSE_DRAG_AND_DROP
- wxDataViewRenderer *cell = col->GetRenderer();
- if ((current >= GetRowCount()) || (x > GetEndOfLastCol()))
+ // Check if we clicked outside the item area.
+ if ((current >= GetRowCount()) || !col)
{
- // Unselect all if below the last row ?
+ // Follow Windows convention here: clicking either left or right (but
+ // not middle) button clears the existing selection.
+ if (m_owner && (event.LeftDown() || event.RightDown()))
+ {
+ if (!GetSelections().empty())
+ {
+ m_owner->UnselectAll();
+ SendSelectionChangedEvent(wxDataViewItem());
+ }
+ }
event.Skip();
return;
}
+ wxDataViewRenderer *cell = col->GetRenderer();
wxDataViewColumn* const
expander = GetExpanderColumnOrFirstOne(GetOwner());
}
}
-#if wxUSE_DRAG_AND_DROP
- if (event.Dragging())
- {
- if (m_dragCount == 0)
- {
- // we have to report the raw, physical coords as we want to be
- // able to call HitTest(event.m_pointDrag) from the user code to
- // get the item being dragged
- m_dragStart = event.GetPosition();
- }
-
- m_dragCount++;
-
- if (m_dragCount != 3)
- return;
-
- if (event.LeftIsDown())
- {
- 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 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( itemDragged );
- event.SetModel( model );
- if (!m_owner->HandleWindowEvent( event ))
- return;
-
- if (!event.IsAllowed())
- return;
-
- wxDataObject *obj = event.GetDataObject();
- if (!obj)
- return;
-
- wxDataViewDropSource drag( this, drag_item_row );
- drag.SetData( *obj );
- /* wxDragResult res = */ drag.DoDragDrop(event.GetDragFlags());
- delete obj;
- }
- return;
- }
- else
- {
- m_dragCount = 0;
- }
-#endif // wxUSE_DRAG_AND_DROP
-
bool simulateClick = false;
if (event.ButtonDClick())
else if ( current == m_lineLastClicked )
{
wxWindow *parent = GetParent();
- wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, parent->GetId());
+ wxDataViewEvent le(wxEVT_DATAVIEW_ITEM_ACTIVATED, parent->GetId());
le.SetItem( item );
le.SetColumn( col->GetModelColumn() );
le.SetDataViewColumn( col );
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
+ // not the entire space as given by the control but the one
// used by the renderer after calculation of alignment etc.
+ //
+ // Notice that this results in negative coordinates when clicking
+ // in the upper left corner of a centre-aligned cell which doesn't
+ // fill its column entirely so this is somewhat surprising, but we
+ // do it like this for compatibility with the native GTK+ version,
+ // see #12270.
// adjust the rectangle ourselves to account for the alignment
+ int align = cell->GetAlignment();
+ if ( align == wxDVR_DEFAULT_ALIGNMENT )
+ align = wxALIGN_CENTRE;
+
wxRect rectItem = cell_rect;
- const int align = cell->GetAlignment();
- if ( align != wxDVR_DEFAULT_ALIGNMENT )
+ const wxSize size = cell->GetSize();
+ if ( size.x >= 0 && size.x < cell_rect.width )
{
- const wxSize size = cell->GetSize();
-
- if ( size.x >= 0 && size.x < cell_rect.width )
- {
- if ( align & wxALIGN_CENTER_HORIZONTAL )
- rectItem.x += (cell_rect.width - size.x)/2;
- else if ( align & wxALIGN_RIGHT )
- rectItem.x += cell_rect.width - size.x;
- // else: wxALIGN_LEFT is the default
- }
+ if ( align & wxALIGN_CENTER_HORIZONTAL )
+ rectItem.x += (cell_rect.width - size.x)/2;
+ else if ( align & wxALIGN_RIGHT )
+ rectItem.x += cell_rect.width - size.x;
+ // else: wxALIGN_LEFT is the default
+ }
- if ( size.y >= 0 && size.y < cell_rect.height )
- {
- if ( align & wxALIGN_CENTER_VERTICAL )
- rectItem.y += (cell_rect.height - size.y)/2;
- else if ( align & wxALIGN_BOTTOM )
- rectItem.y += cell_rect.height - size.y;
- // else: wxALIGN_TOP is the default
- }
+ if ( size.y >= 0 && size.y < cell_rect.height )
+ {
+ if ( align & wxALIGN_CENTER_VERTICAL )
+ rectItem.y += (cell_rect.height - size.y)/2;
+ else if ( align & wxALIGN_BOTTOM )
+ rectItem.y += cell_rect.height - size.y;
+ // else: wxALIGN_TOP is the default
}
wxMouseEvent event2(event);
void wxDataViewCtrl::DoSetExpanderColumn()
{
+ wxDataViewColumn* column = GetExpanderColumn();
+ if ( column )
+ {
+ int index = GetColumnIndex(column);
+ if ( index != wxNOT_FOUND )
+ InvalidateColBestWidth(index);
+ }
+
m_clientArea->UpdateDisplay();
}
return wxNOT_FOUND;
}
+int wxDataViewCtrl::GetModelColumnIndex( unsigned int model_column ) const
+{
+ const int count = GetColumnCount();
+ for ( int index = 0; index < count; index++ )
+ {
+ wxDataViewColumn* column = GetColumn(index);
+ if ( column->GetModelColumn() == model_column )
+ return index;
+ }
+ return wxNOT_FOUND;
+}
+
unsigned int wxDataViewCtrl::GetBestColumnWidth(int idx) const
{
if ( m_colsBestWidths[idx].width != 0 )
wxDataViewMainWindow *clientArea,
wxDataViewRenderer *renderer,
const wxDataViewModel *model,
- unsigned column,
+ unsigned int model_column,
int expanderSize)
: m_width(0),
m_dvc(dvc),
m_clientArea(clientArea),
m_renderer(renderer),
m_model(model),
- m_column(column),
+ m_model_column(model_column),
m_expanderSize(expanderSize)
{
+ int index = dvc->GetModelColumnIndex( model_column );
+ wxDataViewColumn* column = index == wxNOT_FOUND ? NULL : dvc->GetColumn(index);
m_isExpanderCol =
!clientArea->IsList() &&
(column == 0 ||
- GetExpanderColumnOrFirstOne(const_cast<wxDataViewCtrl*>(dvc)) == dvc->GetColumnAt(column));
+ GetExpanderColumnOrFirstOne(const_cast<wxDataViewCtrl*>(dvc)) == column );
}
void UpdateWithWidth(int width)
item = m_clientArea->GetItemByRow(row);
}
- m_renderer->PrepareForItem(m_model, item, m_column);
+ m_renderer->PrepareForItem(m_model, item, m_model_column);
m_width = wxMax(m_width, m_renderer->GetSize().x + indent);
}
wxDataViewMainWindow *m_clientArea;
wxDataViewRenderer *m_renderer;
const wxDataViewModel *m_model;
- unsigned m_column;
+ unsigned m_model_column;
bool m_isExpanderCol;
int m_expanderSize;
};