add support for sorting to grid columns

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@57323 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin 2008-12-14 00:02:30 +00:00
parent 2b1f9fa062
commit 11393d2900
4 changed files with 316 additions and 30 deletions

View File

@ -1749,6 +1749,31 @@ public:
#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...
//
@ -1998,6 +2023,9 @@ protected:
wxArrayInt m_colWidths;
wxArrayInt m_colRights;
int m_sortCol;
bool m_sortIsAscending;
bool m_useNativeHeader,
m_nativeColumnLabels;
@ -2244,6 +2272,13 @@ private:
// 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
@ -2289,6 +2324,8 @@ private:
void ProcessColLabelMouseEvent(wxMouseEvent& event);
void ProcessCornerLabelMouseEvent(wxMouseEvent& event);
void DoColHeaderClick(int col);
void DoStartResizeCol(int col);
void DoUpdateResizeCol(int x);
void DoUpdateResizeColWidth(int w);
@ -2608,6 +2645,7 @@ extern WXDLLIMPEXP_ADV const wxEventType wxEVT_GRID_EDITOR_HIDDEN;
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&);
@ -2650,6 +2688,7 @@ typedef void (wxEvtHandler::*wxGridEditorCreatedEventFunction)(wxGridEditorCreat
#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)
@ -2671,6 +2710,7 @@ typedef void (wxEvtHandler::*wxGridEditorCreatedEventFunction)(wxGridEditorCreat
#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)

View File

@ -3056,6 +3056,77 @@ protected:
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();
//@}
};
@ -3183,7 +3254,19 @@ public:
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}

View File

@ -1565,22 +1565,34 @@ public:
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] =
{
{ "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 );
if ( m_sortOrder )
row = m_sortOrder[row];
return filedata[row][col];
switch ( col )
{
case COL_NAME:
case COL_EXT:
return GetNameOrExt(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&)
@ -1590,8 +1602,10 @@ public:
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];
}
@ -1600,6 +1614,54 @@ public:
{
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
@ -1690,6 +1752,13 @@ private:
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
@ -1721,6 +1790,7 @@ private:
// controls
wxGrid *m_grid;
TabularGridTable *m_table;
wxCheckBox *m_chkUseNative,
*m_chkDrawNative,
*m_chkShowRowLabels,
@ -1753,6 +1823,7 @@ BEGIN_EVENT_TABLE(TabularGridFrame, wxFrame)
EVT_BUTTON(wxID_APPLY, TabularGridFrame::OnMoveColumn)
EVT_GRID_COL_SORT(TabularGridFrame::OnGridColSort)
EVT_GRID_COL_MOVE(TabularGridFrame::OnGridColMove)
EVT_IDLE(TabularGridFrame::OnIdle)
@ -1766,10 +1837,11 @@ TabularGridFrame::TabularGridFrame()
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();
@ -1841,3 +1913,10 @@ void GridFrame::OnTabularTable(wxCommandEvent&)
{
new TabularGridFrame;
}
bool GridApp::OnInit()
{
new TabularGridFrame();
return true;
}

View File

@ -146,6 +146,7 @@ DEFINE_EVENT_TYPE(wxEVT_GRID_LABEL_RIGHT_DCLICK)
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)
@ -192,9 +193,15 @@ public:
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
@ -249,6 +256,8 @@ private:
// 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;
@ -256,6 +265,11 @@ private:
// event handlers forwarding wxHeaderCtrl events to wxGrid
void OnClick(wxHeaderCtrlEvent& event)
{
GetOwner()->DoColHeaderClick(event.GetColumn());
}
void OnBeginResize(wxHeaderCtrlEvent& event)
{
GetOwner()->DoStartResizeCol(event.GetColumn());
@ -292,6 +306,8 @@ private:
};
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)
@ -4884,6 +4900,9 @@ void wxGrid::Init()
m_isDragging = false;
m_startDragPos = wxDefaultPosition;
m_sortCol = wxNOT_FOUND;
m_sortIsAscending = true;
m_useNativeHeader =
m_nativeColumnLabels = false;
@ -5849,6 +5868,60 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event )
}
}
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;
@ -5882,10 +5955,11 @@ void wxGrid::DoUpdateResizeColWidth(int w)
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)
@ -5893,8 +5967,8 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
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() )
@ -5907,7 +5981,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
case WXGRID_CURSOR_SELECT_COL:
{
if ( (col = XToCol( x )) >= 0 )
if ( col != -1 )
{
if ( m_selection )
m_selection->SelectCol(col, event);
@ -6005,7 +6079,6 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
//
if ( XToEdgeOfCol(x) < 0 )
{
col = XToCol(x);
if ( col >= 0 &&
!SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, col, event ) )
{
@ -6059,10 +6132,9 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& 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 ) )
{
@ -6072,7 +6144,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
else
{
// adjust column width depending on label text
AutoSizeColLabelSize( col );
AutoSizeColLabelSize( colEdge );
ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow());
m_dragLastPos = -1;
@ -6090,9 +6162,11 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
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
@ -6105,7 +6179,8 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
case WXGRID_CURSOR_SELECT_CELL:
case WXGRID_CURSOR_RESIZE_ROW:
case WXGRID_CURSOR_SELECT_ROW:
// nothing to do (?)
if ( col != -1 )
DoColHeaderClick(col);
break;
}
@ -6117,7 +6192,6 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
//
else if ( event.RightDown() )
{
col = XToCol(x);
if ( col >= 0 &&
!SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, col, event ) )
{
@ -6129,7 +6203,6 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
//
else if ( event.RightDClick() )
{
col = XToCol(x);
if ( col >= 0 &&
!SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, col, event ) )
{
@ -8185,7 +8258,18 @@ void wxGrid::DrawColLabel(wxDC& dc, int col)
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
{