#endif // wxUSE_DRAG_AND_DROP
+ // ------- sorting support
+
+ // wxGrid doesn't support sorting on its own but it can indicate the sort
+ // order in the column header (currently only if native header control is
+ // used though)
+
+ // return the column currently displaying the sort indicator or wxNOT_FOUND
+ // if none
+ int GetSortingColumn() const { return m_sortCol; }
+
+ // return true if this column is currently used for sorting
+ bool IsSortingBy(int col) const { return GetSortingColumn() == col; }
+
+ // return the current sorting order (on GetSortingColumn()): true for
+ // ascending sort and false for descending; it doesn't make sense to call
+ // it if GetSortingColumn() returns wxNOT_FOUND
+ bool IsSortOrderAscending() const { return m_sortIsAscending; }
+
+ // set the sorting column (or unsets any existing one if wxNOT_FOUND) and
+ // the order in which to sort
+ void SetSortingColumn(int col, bool ascending = true);
+
+ // unset any existing sorting column
+ void UnsetSortingColumn() { SetSortingColumn(wxNOT_FOUND); }
+
#ifdef WXWIN_COMPATIBILITY_2_8
// ------ For compatibility with previous wxGrid only...
//
wxArrayInt m_colWidths;
wxArrayInt m_colRights;
+ int m_sortCol;
+ bool m_sortIsAscending;
+
bool m_useNativeHeader,
m_nativeColumnLabels;
// common part of Clip{Horz,Vert}GridLines
void DoClipGridLines(bool& var, bool clip);
+ // update the sorting indicator shown in the specified column (whose index
+ // must be valid)
+ //
+ // this will use GetSortingColumn() and IsSortOrderAscending() to determine
+ // the sorting indicator to effectively show
+ void UpdateColumnSortingIndicator(int col);
+
// return the position (not index) of the column at the given logical pixel
// position
void ProcessColLabelMouseEvent(wxMouseEvent& event);
void ProcessCornerLabelMouseEvent(wxMouseEvent& event);
+ void DoColHeaderClick(int col);
+
void DoStartResizeCol(int col);
void DoUpdateResizeCol(int x);
void DoUpdateResizeColWidth(int w);
extern WXDLLIMPEXP_ADV const wxEventType wxEVT_GRID_EDITOR_CREATED;
extern WXDLLIMPEXP_ADV const wxEventType wxEVT_GRID_CELL_BEGIN_DRAG;
extern WXDLLIMPEXP_ADV const wxEventType wxEVT_GRID_COL_MOVE;
+extern WXDLLIMPEXP_ADV const wxEventType wxEVT_GRID_COL_SORT;
typedef void (wxEvtHandler::*wxGridEventFunction)(wxGridEvent&);
#define EVT_GRID_CMD_ROW_SIZE(id, fn) wx__DECLARE_GRIDSIZEEVT(ROW_SIZE, id, fn)
#define EVT_GRID_CMD_COL_SIZE(id, fn) wx__DECLARE_GRIDSIZEEVT(COL_SIZE, id, fn)
#define EVT_GRID_CMD_COL_MOVE(id, fn) wx__DECLARE_GRIDEVT(COL_MOVE, id, fn)
+#define EVT_GRID_CMD_COL_SORT(id, fn) wx__DECLARE_GRIDEVT(COL_SORT, id, fn)
#define EVT_GRID_CMD_RANGE_SELECT(id, fn) wx__DECLARE_GRIDRANGESELEVT(RANGE_SELECT, id, fn)
#define EVT_GRID_CMD_CELL_CHANGE(id, fn) wx__DECLARE_GRIDEVT(CELL_CHANGE, id, fn)
#define EVT_GRID_CMD_SELECT_CELL(id, fn) wx__DECLARE_GRIDEVT(SELECT_CELL, id, fn)
#define EVT_GRID_ROW_SIZE(fn) EVT_GRID_CMD_ROW_SIZE(wxID_ANY, fn)
#define EVT_GRID_COL_SIZE(fn) EVT_GRID_CMD_COL_SIZE(wxID_ANY, fn)
#define EVT_GRID_COL_MOVE(fn) EVT_GRID_CMD_COL_MOVE(wxID_ANY, fn)
+#define EVT_GRID_COL_SORT(fn) EVT_GRID_CMD_COL_SORT(wxID_ANY, fn)
#define EVT_GRID_RANGE_SELECT(fn) EVT_GRID_CMD_RANGE_SELECT(wxID_ANY, fn)
#define EVT_GRID_CELL_CHANGE(fn) EVT_GRID_CMD_CELL_CHANGE(wxID_ANY, fn)
#define EVT_GRID_SELECT_CELL(fn) EVT_GRID_CMD_SELECT_CELL(wxID_ANY, fn)
called for this row.
*/
int GetRowMinimalHeight(int col) const;
+
+
+ /**
+ @name Sorting support.
+
+ wxGrid doesn't provide any support for sorting the data but it does
+ generate events allowing the user code to sort it and supports
+ displaying the sort indicator in the column used for sorting.
+
+ To use wxGrid sorting support you need to handle wxEVT_GRID_COL_SORT
+ event (and not veto it) and resort the data displayed in the grid. The
+ grid will automatically update the sorting indicator on the column
+ which was clicked.
+
+ You can also call the functions in this section directly to update the
+ sorting indicator. Once again, they don't do anything with the grid
+ data, it remains your responsibility to actually sort it appropriately.
+ */
+ //@{
+
+ /**
+ Return the column in which the sorting indicator is currently
+ displayed.
+
+ Returns @c wxNOT_FOUND if sorting indicator is not currently displayed
+ at all.
+
+ @see SetSortingColumn()
+ */
+ int GetSortingColumn() const;
+
+ /**
+ Return @true if this column is currently used for sorting.
+
+ @see GetSortingColumn()
+ */
+ bool IsSortingBy(int col) const;
+
+ /**
+ Return @true if the current sorting order is ascending or @false if it
+ is descending.
+
+ It only makes sense to call this function if GetSortingColumn() returns
+ a valid column index and not @c wxNOT_FOUND.
+
+ @see SetSortingColumn()
+ */
+ bool IsSortOrderAscending() const;
+
+ /**
+ Set the column to display the sorting indicator in and its direction.
+
+ @param col
+ The column to display the sorting indicator in or @c wxNOT_FOUND to
+ remove any currently displayed sorting indicator.
+ @param ascending
+ If @true, display the ascending sort indicator, otherwise display
+ the descending sort indicator.
+
+ @see GetSortingColumn(), IsSortOrderAscending()
+ */
+ void SetSortingColumn(int col, bool ascending = true);
+
+ /**
+ Remove any currently shown sorting indicator.
+
+ This is equivalent to calling SetSortingColumn() with @c wxNOT_FOUND
+ first argument.
+ */
+ void UnsetSortingColumn();
+ //@}
};
proceed in which case wxGrid::SetColPos() is used to reorder the
columns display order without affecting the use of the column indices
otherwise.
+
This event macro corresponds to @c wxEVT_GRID_COL_MOVE event type.
+ @event{EVT_GRID_COL_SORT(func)}
+ This event is generated when a column is clicked by the user and its
+ name is explained by the fact that the custom reaction to a click on a
+ column is to sort the grid contents by this column. However the grid
+ itself has no special support for sorting and it's up to the handler of
+ this event to update the associated table. But if the event is handled
+ (and not vetoed) the grid supposes that the table was indeed resorted
+ and updates the column to indicate the new sort order and refreshes
+ itself.
+
+ This event macro corresponds to @c wxEVT_GRID_COL_SORT event type.
@endEventTable
@library{wxadv}
ROW_MAX = 3
};
+ TabularGridTable() { m_sortOrder = NULL; }
+
virtual int GetNumberRows() { return ROW_MAX; }
virtual int GetNumberCols() { return COL_MAX; }
virtual wxString GetValue(int row, int col)
{
- // notice that column parameter here always refers to the internal
- // column index, independently of its position on the screen
- static const char *filedata[][COL_MAX] =
+ if ( m_sortOrder )
+ row = m_sortOrder[row];
+
+ switch ( col )
{
- { "autoexec", "bat", "412", "Apr 17 2004" },
- { "boot", "ini", "604", "May 27 2006" },
- { "io", "sys", "40774", "May 31 1994" },
- };
- wxCOMPILE_TIME_ASSERT( WXSIZEOF(filedata) == ROW_MAX, Mismatch );
+ case COL_NAME:
+ case COL_EXT:
+ return GetNameOrExt(row, col);
- return filedata[row][col];
+ case COL_SIZE:
+ return wxString::Format("%lu", GetSize(row));
+
+ case COL_DATE:
+ return GetDate(row).FormatDate();
+
+ case COL_MAX:
+ default:
+ wxFAIL_MSG( "unknown column" );
+ }
+
+ return wxString();
}
virtual void SetValue(int, int, const wxString&)
virtual wxString GetColLabelValue(int col)
{
+ // notice that column parameter here always refers to the internal
+ // column index, independently of its position on the screen
static const char *labels[] = { "Name", "Extension", "Size", "Date" };
- wxCOMPILE_TIME_ASSERT( WXSIZEOF(labels) == COL_MAX, Mismatch );
+ wxCOMPILE_TIME_ASSERT( WXSIZEOF(labels) == COL_MAX, LabelsMismatch );
return labels[col];
}
{
wxFAIL_MSG( "shouldn't be called" );
}
+
+ void Sort(int col, bool ascending)
+ {
+ // we hardcode all sorting orders for simplicity here
+ static int sortOrders[COL_MAX][2][ROW_MAX] =
+ {
+ // descending ascending
+ { { 2, 1, 0 }, { 0, 1, 2 } },
+ { { 2, 1, 0 }, { 0, 1, 2 } },
+ { { 2, 1, 0 }, { 0, 1, 2 } },
+ { { 1, 0, 2 }, { 2, 0, 1 } },
+ };
+
+ m_sortOrder = col == wxNOT_FOUND ? NULL : sortOrders[col][ascending];
+ }
+
+private:
+ wxString GetNameOrExt(int row, int col) const
+ {
+ static const char *
+ names[] = { "autoexec.bat", "boot.ini", "io.sys" };
+ wxCOMPILE_TIME_ASSERT( WXSIZEOF(names) == ROW_MAX, NamesMismatch );
+
+ const wxString s(names[row]);
+ return col == COL_NAME ? s.BeforeFirst('.') : s.AfterLast('.');
+ }
+
+ unsigned long GetSize(int row) const
+ {
+ static const unsigned long
+ sizes[] = { 412, 604, 40774 };
+ wxCOMPILE_TIME_ASSERT( WXSIZEOF(sizes) == ROW_MAX, SizesMismatch );
+
+ return sizes[row];
+ }
+
+ wxDateTime GetDate(int row) const
+ {
+ static const char *
+ dates[] = { "2004-04-17", "2006-05-27", "1994-05-31" };
+ wxCOMPILE_TIME_ASSERT( WXSIZEOF(dates) == ROW_MAX, DatesMismatch );
+
+ wxDateTime dt;
+ dt.ParseISODate(dates[row]);
+ return dt;
+ }
+
+ int *m_sortOrder;
};
// specialized text control for column indexes entry
UpdateOrder();
}
+ void OnGridColSort(wxGridEvent& event)
+ {
+ const int col = event.GetCol();
+ m_table->Sort(col, !(m_grid->IsSortingBy(col) &&
+ m_grid->IsSortOrderAscending()));
+ }
+
void OnGridColMove(wxGridEvent& event)
{
// can't update it yet as the order hasn't been changed, so do it a bit
// controls
wxGrid *m_grid;
+ TabularGridTable *m_table;
wxCheckBox *m_chkUseNative,
*m_chkDrawNative,
*m_chkShowRowLabels,
EVT_BUTTON(wxID_APPLY, TabularGridFrame::OnMoveColumn)
+ EVT_GRID_COL_SORT(TabularGridFrame::OnGridColSort)
EVT_GRID_COL_MOVE(TabularGridFrame::OnGridColMove)
EVT_IDLE(TabularGridFrame::OnIdle)
wxPanel * const panel = new wxPanel(this);
// create and initialize the grid with the specified data
+ m_table = new TabularGridTable;
m_grid = new wxGrid(panel, wxID_ANY,
wxDefaultPosition, wxDefaultSize,
wxBORDER_STATIC | wxWANTS_CHARS);
- m_grid->SetTable(new TabularGridTable, true, wxGrid::wxGridSelectRows);
+ m_grid->SetTable(m_table, true, wxGrid::wxGridSelectRows);
m_grid->EnableDragColMove();
m_grid->UseNativeColHeader();
{
new TabularGridFrame;
}
+
+bool GridApp::OnInit()
+{
+ new TabularGridFrame();
+
+ return true;
+}
DEFINE_EVENT_TYPE(wxEVT_GRID_ROW_SIZE)
DEFINE_EVENT_TYPE(wxEVT_GRID_COL_SIZE)
DEFINE_EVENT_TYPE(wxEVT_GRID_COL_MOVE)
+DEFINE_EVENT_TYPE(wxEVT_GRID_COL_SORT)
DEFINE_EVENT_TYPE(wxEVT_GRID_RANGE_SELECT)
DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_CHANGE)
DEFINE_EVENT_TYPE(wxEVT_GRID_SELECT_CELL)
return flags;
}
- // TODO: currently there is no support for sorting
- virtual bool IsSortKey() const { return false; }
- virtual bool IsSortOrderAscending() const { return false; }
+ virtual bool IsSortKey() const
+ {
+ return m_grid->IsSortingBy(m_col);
+ }
+
+ virtual bool IsSortOrderAscending() const
+ {
+ return m_grid->IsSortOrderAscending();
+ }
private:
// these really should be const but are not because the column needs to be
// override to implement column auto sizing
virtual bool UpdateColumnWidthToFit(unsigned int idx, int widthTitle)
{
+ // TODO: currently grid doesn't support computing the column best width
+ // from its contents so we just use the best label width as is
GetOwner()->SetColSize(idx, widthTitle);
return true;
// event handlers forwarding wxHeaderCtrl events to wxGrid
+ void OnClick(wxHeaderCtrlEvent& event)
+ {
+ GetOwner()->DoColHeaderClick(event.GetColumn());
+ }
+
void OnBeginResize(wxHeaderCtrlEvent& event)
{
GetOwner()->DoStartResizeCol(event.GetColumn());
};
BEGIN_EVENT_TABLE(wxGridHeaderCtrl, wxHeaderCtrl)
+ EVT_HEADER_CLICK(wxID_ANY, wxGridHeaderCtrl::OnClick)
+
EVT_HEADER_BEGIN_RESIZE(wxID_ANY, wxGridHeaderCtrl::OnBeginResize)
EVT_HEADER_RESIZING(wxID_ANY, wxGridHeaderCtrl::OnResizing)
EVT_HEADER_END_RESIZE(wxID_ANY, wxGridHeaderCtrl::OnEndResize)
m_isDragging = false;
m_startDragPos = wxDefaultPosition;
+ m_sortCol = wxNOT_FOUND;
+ m_sortIsAscending = true;
+
m_useNativeHeader =
m_nativeColumnLabels = false;
}
}
+void wxGrid::UpdateColumnSortingIndicator(int col)
+{
+ wxCHECK_RET( col != wxNOT_FOUND, "invalid column index" );
+
+ if ( m_useNativeHeader )
+ GetColHeader()->UpdateColumn(col);
+ else if ( m_nativeColumnLabels )
+ m_colWindow->Refresh();
+ //else: sorting indicator display not yet implemented in grid version
+}
+
+void wxGrid::SetSortingColumn(int col, bool ascending)
+{
+ if ( col == m_sortCol )
+ {
+ // we are already using this column for sorting (or not sorting at all)
+ // but we might still change the sorting order, check for it
+ if ( m_sortCol != wxNOT_FOUND && ascending != m_sortIsAscending )
+ {
+ m_sortIsAscending = ascending;
+
+ UpdateColumnSortingIndicator(m_sortCol);
+ }
+ }
+ else // we're changing the column used for sorting
+ {
+ const int sortColOld = m_sortCol;
+
+ // change it before updating the column as we want GetSortingColumn()
+ // to return the correct new value
+ m_sortCol = col;
+
+ if ( sortColOld != wxNOT_FOUND )
+ UpdateColumnSortingIndicator(sortColOld);
+
+ if ( m_sortCol != wxNOT_FOUND )
+ {
+ m_sortIsAscending = ascending;
+ UpdateColumnSortingIndicator(m_sortCol);
+ }
+ }
+}
+
+void wxGrid::DoColHeaderClick(int col)
+{
+ // we consider that the grid was resorted if this event is processed and
+ // not vetoed
+ if ( SendEvent(wxEVT_GRID_COL_SORT, -1, col) == 1 )
+ {
+ SetSortingColumn(col, IsSortingBy(col) ? !m_sortIsAscending : true);
+ Refresh();
+ }
+}
+
void wxGrid::DoStartResizeCol(int col)
{
m_dragRowOrCol = col;
void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
{
- int x, y, col;
+ int x, y;
wxPoint pos( event.GetPosition() );
CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
+ int col = XToCol(x);
if ( event.Dragging() )
{
if (!m_isDragging)
m_isDragging = true;
GetColLabelWindow()->CaptureMouse();
- if ( m_cursorMode == WXGRID_CURSOR_MOVE_COL )
- DoStartMoveCol(XToCol(x));
+ if ( m_cursorMode == WXGRID_CURSOR_MOVE_COL && col != -1 )
+ DoStartMoveCol(col);
}
if ( event.LeftIsDown() )
case WXGRID_CURSOR_SELECT_COL:
{
- if ( (col = XToCol( x )) >= 0 )
+ if ( col != -1 )
{
if ( m_selection )
m_selection->SelectCol(col, event);
//
if ( XToEdgeOfCol(x) < 0 )
{
- col = XToCol(x);
if ( col >= 0 &&
!SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, col, event ) )
{
//
if ( event.LeftDClick() )
{
- col = XToEdgeOfCol(x);
- if ( col < 0 )
+ const int colEdge = XToEdgeOfCol(x);
+ if ( colEdge == -1 )
{
- col = XToCol(x);
if ( col >= 0 &&
! SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, col, event ) )
{
else
{
// adjust column width depending on label text
- AutoSizeColLabelSize( col );
+ AutoSizeColLabelSize( colEdge );
ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow());
m_dragLastPos = -1;
break;
case WXGRID_CURSOR_MOVE_COL:
- if ( m_dragLastPos == -1 )
+ if ( m_dragLastPos == -1 || col == m_dragRowOrCol )
{
- // The user clicked on the column but didn't actually drag
+ // the column didn't actually move anywhere
+ if ( col != -1 )
+ DoColHeaderClick(col);
m_colWindow->Refresh(); // "unpress" the column
}
else
case WXGRID_CURSOR_SELECT_CELL:
case WXGRID_CURSOR_RESIZE_ROW:
case WXGRID_CURSOR_SELECT_ROW:
- // nothing to do (?)
+ if ( col != -1 )
+ DoColHeaderClick(col);
break;
}
//
else if ( event.RightDown() )
{
- col = XToCol(x);
if ( col >= 0 &&
!SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, col, event ) )
{
//
else if ( event.RightDClick() )
{
- col = XToCol(x);
if ( col >= 0 &&
!SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, col, event ) )
{
if ( m_nativeColumnLabels )
{
- wxRendererNative::Get().DrawHeaderButton(GetColLabelWindow(), dc, rect, 0);
+ wxRendererNative::Get().DrawHeaderButton
+ (
+ GetColLabelWindow(),
+ dc,
+ rect,
+ 0,
+ IsSortingBy(col)
+ ? IsSortOrderAscending()
+ ? wxHDR_SORT_ICON_UP
+ : wxHDR_SORT_ICON_DOWN
+ : wxHDR_SORT_ICON_NONE
+ );
}
else
{