Merge branch 'grid-activatable-editors'

Add support for activatable editors in wxGrid and make
wxGridCellBoolEditor activatable.

Also a couple of editor-related bug fixes.

See https://github.com/wxWidgets/wxWidgets/pull/1917
This commit is contained in:
Vadim Zeitlin 2020-07-01 14:37:44 +02:00
commit 5a0723223d
6 changed files with 749 additions and 101 deletions

View File

@ -233,6 +233,130 @@ public:
// Smart pointer to wxGridCellRenderer, calling DecRef() on it automatically.
typedef wxObjectDataPtr<wxGridCellRenderer> wxGridCellRendererPtr;
// ----------------------------------------------------------------------------
// Helper classes used by wxGridCellEditor::TryActivate() and DoActivate().
// ----------------------------------------------------------------------------
// This class represents a source of cell activation, which may be either a
// user event (mouse or keyboard) or the program itself.
//
// Note that objects of this class are supposed to be ephemeral and so store
// pointers to the events specified when creating them, which are supposed to
// have life-time greater than that of the objects of this class.
class wxGridActivationSource
{
public:
enum Origin
{
Program,
Key,
Mouse
};
// Factory functions, only used by the library itself.
static wxGridActivationSource FromProgram()
{
return wxGridActivationSource(Program, NULL);
}
static wxGridActivationSource From(const wxKeyEvent& event)
{
return wxGridActivationSource(Key, &event);
}
static wxGridActivationSource From(const wxMouseEvent& event)
{
return wxGridActivationSource(Mouse, &event);
}
// Accessors allowing to retrieve information about the source.
// Can be called for any object.
Origin GetOrigin() const { return m_origin; }
// Can be called for objects with Key origin only.
const wxKeyEvent& GetKeyEvent() const
{
wxASSERT( m_origin == Key );
return *static_cast<const wxKeyEvent*>(m_event);
}
// Can be called for objects with Mouse origin only.
const wxMouseEvent& GetMouseEvent() const
{
wxASSERT( m_origin == Mouse );
return *static_cast<const wxMouseEvent*>(m_event);
}
private:
wxGridActivationSource(Origin origin, const wxEvent* event)
: m_origin(origin),
m_event(event)
{
}
const Origin m_origin;
const wxEvent* const m_event;
};
// This class represents the result of TryActivate(), which may be either
// absence of any action (if activating wouldn't change the value anyhow),
// attempt to change the value to the specified one or just start normal
// editing, which is the default for the editors not supporting activation.
class wxGridActivationResult
{
public:
enum Action
{
Ignore,
Change,
ShowEditor
};
// Factory functions, only used by the library itself.
static wxGridActivationResult DoNothing()
{
return wxGridActivationResult(Ignore);
}
static wxGridActivationResult DoChange(const wxString& newval)
{
return wxGridActivationResult(Change, newval);
}
static wxGridActivationResult DoEdit()
{
return wxGridActivationResult(ShowEditor);
}
// Accessors allowing to retrieve information about the result.
// Can be called for any object.
Action GetAction() const { return m_action; }
// Can be called for objects with Change action type only.
const wxString& GetNewValue() const
{
wxASSERT( m_action == Change );
return m_newval;
}
private:
explicit
wxGridActivationResult(Action action, const wxString& newval = wxString())
: m_action(action),
m_newval(newval)
{
}
const Action m_action;
const wxString m_newval;
};
// ----------------------------------------------------------------------------
// wxGridCellEditor: This class is responsible for providing and manipulating
// the in-place edit controls for the grid. Instances of wxGridCellEditor
@ -335,6 +459,32 @@ public:
wxControl* GetControl() { return wxDynamicCast(m_control, wxControl); }
void SetControl(wxControl* control) { m_control = control; }
// Support for "activatable" editors: those change the value of the cell
// immediately, instead of creating an editor control and waiting for user
// input.
//
// See wxGridCellBoolEditor for an example of such editor.
// Override this function to return "Change" activation result from it to
// show that the editor supports activation. DoActivate() will be called if
// the cell changing event is not vetoed.
virtual
wxGridActivationResult
TryActivate(int WXUNUSED(row), int WXUNUSED(col),
wxGrid* WXUNUSED(grid),
const wxGridActivationSource& WXUNUSED(actSource))
{
return wxGridActivationResult::DoEdit();
}
virtual
void
DoActivate(int WXUNUSED(row), int WXUNUSED(col), wxGrid* WXUNUSED(grid))
{
wxFAIL_MSG( "Must be overridden if TryActivate() is overridden" );
}
protected:
// the dtor is private because only DecRef() can delete us
virtual ~wxGridCellEditor();
@ -373,6 +523,34 @@ protected:
// Smart pointer to wxGridCellEditor, calling DecRef() on it automatically.
typedef wxObjectDataPtr<wxGridCellEditor> wxGridCellEditorPtr;
// Base class for editors that can be only activated and not edited normally.
class wxGridCellActivatableEditor : public wxGridCellEditor
{
public:
// In this class these methods must be overridden.
virtual wxGridActivationResult
TryActivate(int row, int col, wxGrid* grid,
const wxGridActivationSource& actSource) = 0;
virtual void DoActivate(int row, int col, wxGrid* grid) = 0;
// All the other methods that normally must be implemented in an editor are
// defined as just stubs below, as they should be never called.
virtual void Create(wxWindow*, wxWindowID, wxEvtHandler*) wxOVERRIDE
{ wxFAIL; }
virtual void BeginEdit(int, int, wxGrid*) wxOVERRIDE
{ wxFAIL; }
virtual bool EndEdit(int, int, const wxGrid*,
const wxString&, wxString*) wxOVERRIDE
{ wxFAIL; return false; }
virtual void ApplyEdit(int, int, wxGrid*) wxOVERRIDE
{ wxFAIL; }
virtual void Reset() wxOVERRIDE
{ wxFAIL; }
virtual wxString GetValue() const wxOVERRIDE
{ wxFAIL; return wxString(); }
};
// ----------------------------------------------------------------------------
// wxGridHeaderRenderer and company: like wxGridCellRenderer but for headers
// ----------------------------------------------------------------------------
@ -2609,28 +2787,34 @@ protected:
bool Redimension( wxGridTableMessage& );
// Send the given grid event and return -1 if it was vetoed or, as a
// special exception, if an event for a particular cell resulted in this
// cell being deleted, 1 if it was processed (but not vetoed) and 0 if it
// wasn't processed.
int DoSendEvent(wxGridEvent& gridEvt);
enum EventResult
{
Event_Vetoed = -1,
Event_Unhandled,
Event_Handled,
Event_CellDeleted // Event handler deleted the cell.
};
// Send the given grid event and returns one of the event handling results
// defined above.
EventResult DoSendEvent(wxGridEvent& gridEvt);
// Generate an event of the given type and call DoSendEvent().
int SendEvent(wxEventType evtType,
EventResult SendEvent(wxEventType evtType,
int row, int col,
const wxMouseEvent& e);
int SendEvent(wxEventType evtType,
EventResult SendEvent(wxEventType evtType,
const wxGridCellCoords& coords,
const wxMouseEvent& e)
{ return SendEvent(evtType, coords.GetRow(), coords.GetCol(), e); }
int SendEvent(wxEventType evtType,
EventResult SendEvent(wxEventType evtType,
int row, int col,
const wxString& s = wxString());
int SendEvent(wxEventType evtType,
EventResult SendEvent(wxEventType evtType,
const wxGridCellCoords& coords,
const wxString& s = wxString())
{ return SendEvent(evtType, coords.GetRow(), coords.GetCol(), s); }
int SendEvent(wxEventType evtType, const wxString& s = wxString())
EventResult SendEvent(wxEventType evtType, const wxString& s = wxString())
{ return SendEvent(evtType, m_currentCellCoords, s); }
// send wxEVT_GRID_{ROW,COL}_SIZE or wxEVT_GRID_COL_AUTO_SIZE, return true
@ -2908,13 +3092,17 @@ private:
}
// Show/hide the cell editor for the current cell unconditionally.
void DoShowCellEditControl();
// Return false if the editor was activated instead of being shown and also
// sets m_cellEditCtrlEnabled to true when it returns true as a side effect.
bool DoShowCellEditControl(const wxGridActivationSource& actSource);
void DoHideCellEditControl();
// Unconditionally try showing the editor for the current cell.
//
// Returns false if the user code vetoed wxEVT_GRID_EDITOR_SHOWN.
bool DoEnableCellEditControl();
// Returns false if the user code vetoed wxEVT_GRID_EDITOR_SHOWN or if the
// editor was simply activated and won't be permanently shown.
bool DoEnableCellEditControl(const wxGridActivationSource& actSource);
// Unconditionally disable (accepting the changes) the editor.
void DoDisableCellEditControl();

View File

@ -238,6 +238,11 @@ class WXDLLIMPEXP_ADV wxGridCellBoolEditor : public wxGridCellEditor
public:
wxGridCellBoolEditor() { }
virtual wxGridActivationResult
TryActivate(int row, int col, wxGrid* grid,
const wxGridActivationSource& actSource) wxOVERRIDE;
virtual void DoActivate(int row, int col, wxGrid* grid) wxOVERRIDE;
virtual void Create(wxWindow* parent,
wxWindowID id,
wxEvtHandler* evtHandler) wxOVERRIDE;
@ -275,6 +280,15 @@ protected:
wxCheckBox *CBox() const { return (wxCheckBox *)m_control; }
private:
// These functions modify or use m_value.
void SetValueFromGrid(int row, int col, wxGrid* grid);
void SetGridFromValue(int row, int col, wxGrid* grid) const;
wxString GetStringValue() const { return GetStringValue(m_value); }
static
wxString GetStringValue(bool value) { return ms_stringValues[value]; }
bool m_value;
static wxString ms_stringValues[2];

View File

@ -447,6 +447,97 @@ public:
};
/**
Represents a source of cell activation, which may be either a user event
(mouse or keyboard) or the program itself.
An object of this class is passed to wxGridCellEditor::TryActivate() by the
library and the code overriding this method may use its GetOrigin() method
to determine how exactly the cell is being activated.
@since 3.1.4
*/
class wxGridActivationSource
{
public:
/// Result of GetOrigin().
enum Origin
{
/// Activated due to an explicit wxGrid::EnableCellEditControl() call.
Program,
/// Activated due to the user pressing a key, see GetKeyEvent().
Key,
/// Activated due to the user clicking on a cell, see GetMouseEvent().
Mouse
};
/// Get the origin of the activation.
Origin GetOrigin() const;
/**
Get the key event corresponding to the key press activating the cell.
This method can be called for objects with Key origin only, use
GetOrigin() to check for this first.
*/
const wxKeyEvent& GetKeyEvent() const;
/**
Get the mouse event corresponding to the click activating the cell.
This method can be called for objects with Mouse origin only, use
GetOrigin() to check for this first.
*/
const wxMouseEvent& GetMouseEvent() const;
};
/**
Represents the result of wxGridCellEditor::TryActivate().
Editors overriding wxGridCellEditor::TryActivate() must use one of
DoNothing(), DoChange() or DoEdit() methods to return an object of this
type corresponding to the desired action.
@since 3.1.4
*/
class wxGridActivationResult
{
public:
/**
Indicate that nothing should be done and the cell shouldn't be edited
at all.
Note that this is different from DoEdit() and may be useful when the
value of the cell wouldn't change if it were activated anyhow, e.g.
because the key or mouse event carried by wxGridActivationSource would
leave the cell value unchanged.
*/
static wxGridActivationResult DoNothing();
/**
Indicate that activating the cell is possible and would change its
value to the given one.
This is the method to call for activatable editors, using it will
result in changing the value of the cell to @a newval without showing
the editor control at all.
Note that the change may still be vetoed by wxEVT_GRID_CELL_CHANGING
handler.
*/
static wxGridActivationResult DoChange(const wxString& newval);
/**
Indicate that the editor control should be shown and the cell should be
edited normally.
This is the default return value of wxGridCellEditor::TryActivate().
*/
static wxGridActivationResult DoEdit();
};
/**
@class wxGridCellEditor
@ -456,6 +547,15 @@ public:
the cell attributes for individual cells, rows, columns, or even for the
entire grid.
Normally wxGridCellEditor shows some UI control allowing the user to edit
the cell, but starting with wxWidgets 3.1.4 it's also possible to define
"activatable" cell editors, that change the value of the cell directly when
it's activated (typically by pressing Space key or clicking on it), see
TryActivate() method. Note that when implementing an editor which is always
activatable, i.e. never shows any in-place editor, it is more convenient to
derive its class from wxGridCellActivatableEditor than from wxGridCellEditor
itself.
@library{wxcore}
@category{grid}
@ -627,6 +727,41 @@ public:
void SetControl(wxControl* control);
/**
Function allowing to create an "activatable" editor.
As explained in this class description, activatable editors don't show
any edit control but change the cell value directly, when it is
activated (by any way described by wxGridActivationSource).
To create such editor, this method must be overridden to return
wxGridActivationResult::DoChange() passing it the new value of the
cell. If the change is not vetoed by wxEVT_GRID_CELL_CHANGING handler,
DoActivate() will be called to actually change the value, so it must be
overridden as well if TryActivate() is overridden.
By default, wxGridActivationResult::DoEdit() is returned, meaning that
this is a normal editor, using an edit control for changing the cell
value.
@since 3.1.4
*/
virtual wxGridActivationResult
TryActivate(int row, int col, wxGrid* grid,
const wxGridActivationSource& actSource);
/**
Function which must be overridden for "activatable" editors.
If TryActivate() is overridden to return "change" action, this function
will be called to actually apply this change. Note that it is not
passed the value to apply, as it is assumed that the editor class
stores this value as a member variable anyhow.
@since 3.1.4
*/
virtual void DoActivate(int row, int col, wxGrid* grid);
protected:
/**
@ -635,6 +770,34 @@ protected:
virtual ~wxGridCellEditor();
};
/**
Base class for activatable editors.
Inheriting from this class makes it simpler to implement editors that
support only activation, but not in-place editing, as they only need to
implement TryActivate(), DoActivate() and Clone() methods, but not all the
other pure virtual methods of wxGridCellEditor.
@since 3.1.4
*/
class wxGridCellActivatableEditor : public wxGridCellEditor
{
public:
/**
Same method as in wxGridCellEditor, but pure virtual.
Note that the implementation of this method must never return
wxGridActivationResult::DoEdit() for the editors inheriting from this
class, as it doesn't support normal editing.
*/
virtual wxGridActivationResult
TryActivate(int row, int col, wxGrid* grid,
const wxGridActivationSource& actSource) = 0;
/// Same method as in wxGridCellEditor, but pure virtual.
virtual void DoActivate(int row, int col, wxGrid* grid) = 0;
};
/**
Smart pointer wrapping wxGridCellEditor.

View File

@ -121,6 +121,135 @@ private:
wxDECLARE_NO_COPY_CLASS(CustomColumnHeadersProvider);
};
// ----------------------------------------------------------------------------
// Custom wxGrid renderer and editor for showing stars as used for rating
// ----------------------------------------------------------------------------
// Max number of stars shown by MyGridStarRenderer.
static const int MAX_STARS = 5;
// Helper function returning the number between 0 and MAX_STARS corresponding
// to the value of the cell.
static int GetStarValue(wxGrid& grid, int row, int col)
{
unsigned long n = 0;
if ( !grid.GetCellValue(row, col).ToULong(&n) || n > MAX_STARS )
n = 0;
return static_cast<int>(n);
}
// Another helper returning the string containing the appropriate number of
// black and white stars.
static wxString GetStarString(int numBlackStars)
{
const wxUniChar BLACK_STAR = 0x2605;
const wxUniChar WHITE_STAR = 0x2606;
return wxString(BLACK_STAR, numBlackStars) +
wxString(WHITE_STAR, MAX_STARS - numBlackStars);
}
// Renders the value of the cell, which is supposed to be a number between 1
// and 5, as a sequence of that number of black stars followed by the number of
// white stars needed to have 5 stars in total.
class MyGridStarRenderer : public wxGridCellRenderer
{
public:
virtual void Draw(wxGrid& grid,
wxGridCellAttr& attr,
wxDC& dc,
const wxRect& rect,
int row, int col,
bool isSelected) wxOVERRIDE
{
wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
grid.DrawTextRectangle(dc, GetStarString(GetStarValue(grid, row, col)),
rect, attr);
}
virtual wxSize GetBestSize(wxGrid& WXUNUSED(grid),
wxGridCellAttr& attr,
wxDC& dc,
int WXUNUSED(row),
int WXUNUSED(col)) wxOVERRIDE
{
dc.SetFont(attr.GetFont());
return dc.GetTextExtent(GetStarString(MAX_STARS));
}
virtual wxGridCellRenderer *Clone() const wxOVERRIDE
{
return new MyGridStarRenderer();
}
};
// Activatable editor cycling the number of stars on each activation.
class MyGridStarEditor : public wxGridCellActivatableEditor
{
public:
virtual wxGridActivationResult
TryActivate(int row, int col, wxGrid* grid,
const wxGridActivationSource& actSource) wxOVERRIDE
{
int numStars = -1;
switch ( actSource.GetOrigin() )
{
case wxGridActivationSource::Program:
// It isn't really possible to programmatically start editing a
// cell using this editor.
return wxGridActivationResult::DoNothing();
case wxGridActivationSource::Key:
switch ( actSource.GetKeyEvent().GetKeyCode() )
{
case '0': numStars = 0; break;
case '1': numStars = 1; break;
case '2': numStars = 2; break;
case '3': numStars = 3; break;
case '4': numStars = 4; break;
case '5': numStars = 5; break;
case ' ':
// Use space key to cycle over the values.
break;
default:
return wxGridActivationResult::DoNothing();
}
break;
case wxGridActivationSource::Mouse:
// Ideally we should use the mouse event position to determine
// on which star the user clicked, but for now keep it simple
// and just cycle through the star value.
break;
}
if ( numStars == -1 )
numStars = (GetStarValue(*grid, row, col) + 1) % (MAX_STARS + 1);
m_value.Printf("%d", numStars);
return wxGridActivationResult::DoChange(m_value);
}
virtual void DoActivate(int row, int col, wxGrid* grid) wxOVERRIDE
{
grid->SetCellValue(row, col, m_value);
}
virtual wxGridCellEditor *Clone() const
{
return new MyGridStarEditor();
}
private:
wxString m_value;
};
// ----------------------------------------------------------------------------
// wxWin macros
// ----------------------------------------------------------------------------
@ -517,6 +646,10 @@ GridFrame::GridFrame()
grid->SetCellAlignment(4, 4, wxALIGN_CENTRE, wxALIGN_CENTRE);
grid->SetCellRenderer(4, 4, new MyGridCellRenderer);
grid->SetCellValue(4, 5, "3");
grid->SetCellRenderer(4, 5, new MyGridStarRenderer);
grid->SetCellEditor(4, 5, new MyGridStarEditor);
grid->SetCellRenderer(3, 0, new wxGridCellBoolRenderer);
grid->SetCellEditor(3, 0, new wxGridCellBoolEditor);
grid->SetCellBackgroundColour(3, 0, wxColour(255, 127, 127));
@ -534,7 +667,7 @@ GridFrame::GridFrame()
grid->SetRowAttr(5, attr);
grid->SetCellValue(2, 4, "a wider column");
grid->SetColSize(4, 3*grid->GetDefaultColLabelSize()/2);
grid->SetColSize(4, 3*grid->GetDefaultColSize()/2);
grid->SetColMinimalWidth(4, grid->GetColSize(4));
grid->SetCellTextColour(5, 8, *wxGREEN);

View File

@ -3716,7 +3716,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event, wxGridRowLabelWindo
{
row = YToRow(pos.y);
if ( row >= 0 &&
!SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, row, -1, event ) )
SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, row, -1, event ) == Event_Unhandled )
{
// Check if row selection is possible and allowed, before doing
// anything else, including changing the cursor mode to "select
@ -3792,7 +3792,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event, wxGridRowLabelWindo
{
row = YToRow(pos.y);
if ( row >=0 &&
!SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, row, -1, event ) )
SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, row, -1, event ) == Event_Unhandled )
{
// no default action at the moment
}
@ -3816,7 +3816,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event, wxGridRowLabelWindo
{
row = YToRow(pos.y);
if ( row < 0 ||
!SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, row, -1, event ) )
SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, row, -1, event ) == Event_Unhandled )
{
// no default action at the moment
event.Skip();
@ -3829,7 +3829,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event, wxGridRowLabelWindo
{
row = YToRow(pos.y);
if ( row < 0 ||
!SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, row, -1, event ) )
SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, row, -1, event ) == Event_Unhandled )
{
// no default action at the moment
event.Skip();
@ -3954,7 +3954,7 @@ 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 )
if ( SendEvent(wxEVT_GRID_COL_SORT, -1, col) == Event_Handled )
{
SetSortingColumn(col, IsSortingBy(col) ? !m_sortIsAscending : true);
Refresh();
@ -4112,7 +4112,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindo
else // not a request to start resizing
{
if ( col >= 0 &&
!SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, col, event ) )
SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, col, event ) == Event_Unhandled )
{
if ( m_canDragColMove )
{
@ -4187,7 +4187,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindo
if ( colEdge == -1 )
{
if ( col >= 0 &&
! SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, col, event ) )
SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, col, event ) == Event_Unhandled )
{
// no default action at the moment
}
@ -4268,7 +4268,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindo
else if ( event.RightDown() )
{
if ( col < 0 ||
!SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, col, event ) )
SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, col, event ) == Event_Unhandled )
{
// no default action at the moment
event.Skip();
@ -4280,7 +4280,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindo
else if ( event.RightDClick() )
{
if ( col < 0 ||
!SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, col, event ) )
SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, col, event ) == Event_Unhandled )
{
// no default action at the moment
event.Skip();
@ -4322,7 +4322,7 @@ void wxGrid::ProcessCornerLabelMouseEvent( wxMouseEvent& event )
// indicate corner label by having both row and
// col args == -1
//
if ( !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, -1, event ) )
if ( SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, -1, event ) == Event_Unhandled )
{
SelectAll();
}
@ -4333,7 +4333,7 @@ void wxGrid::ProcessCornerLabelMouseEvent( wxMouseEvent& event )
}
else if ( event.RightDown() )
{
if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, -1, event ) )
if ( SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, -1, event ) == Event_Unhandled )
{
// no default action at the moment
event.Skip();
@ -4341,7 +4341,7 @@ void wxGrid::ProcessCornerLabelMouseEvent( wxMouseEvent& event )
}
else if ( event.RightDClick() )
{
if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, -1, event ) )
if ( SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, -1, event ) == Event_Unhandled )
{
// no default action at the moment
event.Skip();
@ -4494,7 +4494,7 @@ wxGrid::DoGridCellDrag(wxMouseEvent& event,
if ( CanDragCell() )
{
// if event is handled by user code, no further processing
return SendEvent(wxEVT_GRID_CELL_BEGIN_DRAG, coords, event) == 0;
return SendEvent(wxEVT_GRID_CELL_BEGIN_DRAG, coords, event) == Event_Unhandled;
}
break;
@ -4543,7 +4543,7 @@ wxGrid::DoGridCellLeftDown(wxMouseEvent& event,
const wxGridCellCoords& coords,
const wxPoint& pos)
{
if ( SendEvent(wxEVT_GRID_CELL_LEFT_CLICK, coords, event) )
if ( SendEvent(wxEVT_GRID_CELL_LEFT_CLICK, coords, event) != Event_Unhandled )
{
// event handled by user code, no need to do anything here
return;
@ -4644,7 +4644,7 @@ wxGrid::DoGridCellLeftDClick(wxMouseEvent& event,
{
if ( XToEdgeOfCol(pos.x) < 0 && YToEdgeOfRow(pos.y) < 0 )
{
if ( !SendEvent(wxEVT_GRID_CELL_LEFT_DCLICK, coords, event) )
if ( SendEvent(wxEVT_GRID_CELL_LEFT_DCLICK, coords, event) == Event_Unhandled )
{
// we want double click to select a cell and start editing
// (i.e. to behave in same way as sequence of two slow clicks):
@ -4664,7 +4664,7 @@ wxGrid::DoGridCellLeftUp(wxMouseEvent& event,
{
ClearSelection();
if ( DoEnableCellEditControl() )
if ( DoEnableCellEditControl(wxGridActivationSource::From(event)) )
GetCurrentCellEditorPtr()->StartingClick();
m_waitForSlowClick = false;
@ -4835,9 +4835,9 @@ void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event, wxGridWindow *eventG
else if ( event.LeftDClick() )
handled = (DoGridCellLeftDClick(event, coords, pos), true);
else if ( event.RightDown() )
handled = SendEvent(wxEVT_GRID_CELL_RIGHT_CLICK, coords, event) != 0;
handled = SendEvent(wxEVT_GRID_CELL_RIGHT_CLICK, coords, event) != Event_Unhandled;
else if ( event.RightDClick() )
handled = SendEvent(wxEVT_GRID_CELL_RIGHT_DCLICK, coords, event) != 0;
handled = SendEvent(wxEVT_GRID_CELL_RIGHT_DCLICK, coords, event) != Event_Unhandled;
}
}
else if ( event.Moving() )
@ -4949,9 +4949,8 @@ void wxGrid::DoEndMoveCol(int pos)
{
wxASSERT_MSG( m_dragMoveCol != -1, "no matching DoStartMoveCol?" );
if ( SendEvent(wxEVT_GRID_COL_MOVE, -1, m_dragMoveCol) != -1 )
if ( SendEvent(wxEVT_GRID_COL_MOVE, -1, m_dragMoveCol) != Event_Vetoed )
SetColPos(m_dragMoveCol, pos);
//else: vetoed by user
m_dragMoveCol = -1;
}
@ -5285,31 +5284,27 @@ wxGrid::SendGridSizeEvent(wxEventType type,
return ProcessWindowEvent(gridEvt);
}
// Process the event and return
// -1 if the event was vetoed or if event cell was deleted
// +1 if the event was processed (but not vetoed)
// 0 if the event wasn't handled
int wxGrid::DoSendEvent(wxGridEvent& gridEvt)
wxGrid::EventResult wxGrid::DoSendEvent(wxGridEvent& gridEvt)
{
const bool claimed = ProcessWindowEvent(gridEvt);
// A Veto'd event may not be `claimed' so test this first
if ( !gridEvt.IsAllowed() )
return -1;
return Event_Vetoed;
// We also return -1 if the event cell was deleted, as this allows to have
// checks in several functions that generate an event and then proceed
// doing something by default with the selected cell: this shouldn't be
// done if the user-defined handler deleted this cell.
// Detect the special case in which the event cell was deleted, as this
// allows to have checks in several functions that generate an event and
// then proceed doing something by default with the selected cell: this
// shouldn't be done if the user-defined handler deleted this cell.
if ( gridEvt.GetRow() >= GetNumberRows() ||
gridEvt.GetCol() >= GetNumberCols() )
return -1;
return Event_CellDeleted;
return claimed ? 1 : 0;
return claimed ? Event_Handled : Event_Unhandled;
}
// Generate a grid event based on a mouse event and call DoSendEvent() with it.
int
wxGrid::EventResult
wxGrid::SendEvent(wxEventType type,
int row, int col,
const wxMouseEvent& mouseEv)
@ -5362,7 +5357,7 @@ wxGrid::SendEvent(wxEventType type,
// Generate a grid event of specified type, return value same as above
//
int
wxGrid::EventResult
wxGrid::SendEvent(wxEventType type, int row, int col, const wxString& s)
{
wxGridEvent gridEvt( GetId(), type, this, row, col );
@ -5969,7 +5964,8 @@ void wxGrid::OnChar( wxKeyEvent& event )
// ensure cell is visble
MakeCellVisible(m_currentCellCoords);
if ( DoEnableCellEditControl() && !specialEditKey )
if ( DoEnableCellEditControl(wxGridActivationSource::From(event))
&& !specialEditKey )
editor->StartingKey(event);
}
else
@ -6048,10 +6044,18 @@ void wxGrid::DoGridProcessTab(wxKeyboardState& kbdState)
bool wxGrid::SetCurrentCell( const wxGridCellCoords& coords )
{
if ( SendEvent(wxEVT_GRID_SELECT_CELL, coords) == -1 )
switch ( SendEvent(wxEVT_GRID_SELECT_CELL, coords) )
{
// the event has been vetoed - do nothing
return false;
case Event_Vetoed:
case Event_CellDeleted:
// We shouldn't do anything if the event was vetoed and can't do
// anything if the cell doesn't exist any longer.
return false;
case Event_Unhandled:
case Event_Handled:
// But it doesn't matter here if the event was skipped or not.
break;
}
wxGridWindow *currentGridWindow = CellToGridWindow(coords);
@ -7149,7 +7153,7 @@ void wxGrid::EnableCellEditControl( bool enable )
// this should be checked by the caller!
wxCHECK_RET( CanEnableCellControl(), wxT("can't enable editing for this cell!") );
DoEnableCellEditControl();
DoEnableCellEditControl(wxGridActivationSource::FromProgram());
}
else
{
@ -7158,14 +7162,31 @@ void wxGrid::EnableCellEditControl( bool enable )
}
}
bool wxGrid::DoEnableCellEditControl()
bool wxGrid::DoEnableCellEditControl(const wxGridActivationSource& actSource)
{
if ( SendEvent(wxEVT_GRID_EDITOR_SHOWN) == -1 )
switch ( SendEvent(wxEVT_GRID_EDITOR_SHOWN) )
{
case Event_Vetoed:
case Event_CellDeleted:
// We shouldn't do anything if the event was vetoed and can't do
// anything if the cell doesn't exist any longer.
return false;
case Event_Unhandled:
case Event_Handled:
// But it doesn't matter here if the event was skipped or not.
break;
}
if ( !DoShowCellEditControl(actSource) )
{
// We have to send the HIDDEN event matching the SHOWN one above as the
// user code may reasonably expect always getting them in pairs, so do
// it even if the editor hadn't really been shown at all.
SendEvent(wxEVT_GRID_EDITOR_HIDDEN);
return false;
m_cellEditCtrlEnabled = true;
DoShowCellEditControl();
}
return true;
}
@ -7217,16 +7238,64 @@ void wxGrid::ShowCellEditControl()
return;
}
DoShowCellEditControl();
DoShowCellEditControl(wxGridActivationSource::FromProgram());
}
}
void wxGrid::DoShowCellEditControl()
bool wxGrid::DoShowCellEditControl(const wxGridActivationSource& actSource)
{
wxRect rect = CellToRect( m_currentCellCoords );
int row = m_currentCellCoords.GetRow();
int col = m_currentCellCoords.GetCol();
wxGridCellAttrPtr attr = GetCellAttrPtr(row, col);
wxGridCellEditorPtr editor = attr->GetEditorPtr(this, row, col);
const wxGridActivationResult&
res = editor->TryActivate(row, col, this, actSource);
switch ( res.GetAction() )
{
case wxGridActivationResult::Change:
// This is somewhat similar to what DoSaveEditControlValue() does.
// but we don't allow vetoing CHANGED event here as this code is
// new and shouldn't have to support this obsolete usage.
switch ( SendEvent(wxEVT_GRID_CELL_CHANGING, res.GetNewValue()) )
{
case Event_Vetoed:
case Event_CellDeleted:
break;
case Event_Unhandled:
case Event_Handled:
const wxString& oldval = GetCellValue(m_currentCellCoords);
editor->DoActivate(row, col, this);
// Show the new cell value.
RefreshBlock(m_currentCellCoords, m_currentCellCoords);
if ( SendEvent(wxEVT_GRID_CELL_CHANGED, oldval) == Event_Vetoed )
{
wxFAIL_MSG( "Vetoing wxEVT_GRID_CELL_CHANGED is ignored" );
}
break;
}
wxFALLTHROUGH;
case wxGridActivationResult::Ignore:
// In any case, don't start editing normally.
return false;
case wxGridActivationResult::ShowEditor:
// Continue normally.
break;
}
// It's not enabled just yet, but will be soon, and we need to set it
// before generating any events in case their user-defined handlers decide
// to call EnableCellEditControl() to avoid reentrancy problems.
m_cellEditCtrlEnabled = true;
wxGridWindow *gridWindow = CellToGridWindow(row, col);
// if this is part of a multicell, find owner (topleft)
@ -7251,8 +7320,6 @@ void wxGrid::DoShowCellEditControl()
rect.Deflate(1, 1);
#endif
wxGridCellAttrPtr attr = GetCellAttrPtr(row, col);
wxGridCellEditorPtr editor = attr->GetEditorPtr(this, row, col);
if ( !editor->IsCreated() )
{
editor->Create(gridWindow, wxID_ANY,
@ -7348,6 +7415,8 @@ void wxGrid::DoShowCellEditControl()
editor->BeginEdit(row, col, this);
editor->SetCellAttr(NULL);
return true;
}
void wxGrid::HideCellEditControl()
@ -7443,21 +7512,32 @@ void wxGrid::DoSaveEditControlValue()
wxGridCellEditorPtr editor = GetCurrentCellEditorPtr();
wxString newval;
bool changed = editor->EndEdit(row, col, this, oldval, &newval);
if ( !editor->EndEdit(row, col, this, oldval, &newval) )
return;
if ( changed && SendEvent(wxEVT_GRID_CELL_CHANGING, newval) != -1 )
switch ( SendEvent(wxEVT_GRID_CELL_CHANGING, newval) )
{
editor->ApplyEdit(row, col, this);
case Event_Vetoed:
case Event_CellDeleted:
break;
// for compatibility reasons dating back to wx 2.8 when this event
// was called wxEVT_GRID_CELL_CHANGE and wxEVT_GRID_CELL_CHANGING
// didn't exist we allow vetoing this one too
if ( SendEvent(wxEVT_GRID_CELL_CHANGED, oldval) == -1 )
{
// Event has been vetoed, set the data back.
SetCellValue(m_currentCellCoords, oldval);
case Event_Unhandled:
case Event_Handled:
editor->ApplyEdit(row, col, this);
// for compatibility reasons dating back to wx 2.8 when this event
// was called wxEVT_GRID_CELL_CHANGE and wxEVT_GRID_CELL_CHANGING
// didn't exist we allow vetoing this one too
if ( SendEvent(wxEVT_GRID_CELL_CHANGED, oldval) == Event_Vetoed )
{
// Event has been vetoed, set the data back.
//
// Note that we must use row and col here, which are sure to
// not have been changed, while m_currentCellCoords could have
// been changed by the event handler.
SetCellValue(row, col, oldval);
}
}
}
}
void wxGrid::OnHideEditor(wxCommandEvent& WXUNUSED(event))

View File

@ -671,6 +671,8 @@ void wxGridCellTextEditor::SetParameters(const wxString& params)
void wxGridCellTextEditor::SetValidator(const wxValidator& validator)
{
m_validator.reset(static_cast<wxValidator*>(validator.Clone()));
if ( m_validator && IsCreated() )
Text()->SetValidator(*m_validator);
}
#endif
@ -1247,6 +1249,61 @@ bool wxGridCellFloatEditor::IsAcceptedKey(wxKeyEvent& event)
// the default values for GetValue()
wxString wxGridCellBoolEditor::ms_stringValues[2] = { wxT(""), wxT("1") };
wxGridActivationResult
wxGridCellBoolEditor::TryActivate(int row, int col, wxGrid* grid,
const wxGridActivationSource& actSource)
{
SetValueFromGrid(row, col, grid);
switch ( actSource.GetOrigin() )
{
case wxGridActivationSource::Program:
// It's not really clear what should happen in this case, so for
// now show the editor interactively to avoid making the choice.
return wxGridActivationResult::DoEdit();
case wxGridActivationSource::Mouse:
m_value = !m_value;
return wxGridActivationResult::DoChange(GetStringValue());
case wxGridActivationSource::Key:
switch ( actSource.GetKeyEvent().GetKeyCode() )
{
// Handle F2 as space here because we must handle it somehow,
// because pressing it always starts editing in wxGrid, and
// it's not really clear what else could it do.
case WXK_F2:
case WXK_SPACE:
m_value = !m_value;
break;
case '+':
if ( m_value )
return wxGridActivationResult::DoNothing();
m_value = true;
break;
case '-':
if ( !m_value )
return wxGridActivationResult::DoNothing();
m_value = false;
break;
}
return wxGridActivationResult::DoChange(GetStringValue());
}
wxFAIL_MSG( "unknown activation source origin" );
return wxGridActivationResult::DoNothing();
}
void wxGridCellBoolEditor::DoActivate(int row, int col, wxGrid* grid)
{
SetGridFromValue(row, col, grid);
}
void wxGridCellBoolEditor::Create(wxWindow* parent,
wxWindowID id,
wxEvtHandler* evtHandler)
@ -1312,27 +1369,7 @@ void wxGridCellBoolEditor::BeginEdit(int row, int col, wxGrid* grid)
wxASSERT_MSG(m_control,
wxT("The wxGridCellEditor must be created first!"));
if (grid->GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL))
{
m_value = grid->GetTable()->GetValueAsBool(row, col);
}
else
{
wxString cellval( grid->GetTable()->GetValue(row, col) );
if ( cellval == ms_stringValues[false] )
m_value = false;
else if ( cellval == ms_stringValues[true] )
m_value = true;
else
{
// do not try to be smart here and convert it to true or false
// because we'll still overwrite it with something different and
// this risks to be very surprising for the user code, let them
// know about it
wxFAIL_MSG( wxT("invalid value for a cell with bool editor!") );
}
}
SetValueFromGrid(row, col, grid);
CBox()->SetValue(m_value);
CBox()->SetFocus();
@ -1358,11 +1395,7 @@ bool wxGridCellBoolEditor::EndEdit(int WXUNUSED(row),
void wxGridCellBoolEditor::ApplyEdit(int row, int col, wxGrid* grid)
{
wxGridTableBase * const table = grid->GetTable();
if ( table->CanSetValueAs(row, col, wxGRID_VALUE_BOOL) )
table->SetValueAsBool(row, col, m_value);
else
table->SetValue(row, col, GetValue());
SetGridFromValue(row, col, grid);
}
void wxGridCellBoolEditor::Reset()
@ -1416,7 +1449,7 @@ void wxGridCellBoolEditor::StartingKey(wxKeyEvent& event)
wxString wxGridCellBoolEditor::GetValue() const
{
return ms_stringValues[CBox()->GetValue()];
return GetStringValue(CBox()->GetValue());
}
/* static */ void
@ -1433,6 +1466,43 @@ wxGridCellBoolEditor::IsTrueValue(const wxString& value)
return value == ms_stringValues[true];
}
void wxGridCellBoolEditor::SetValueFromGrid(int row, int col, wxGrid* grid)
{
if (grid->GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL))
{
m_value = grid->GetTable()->GetValueAsBool(row, col);
}
else
{
wxString cellval( grid->GetTable()->GetValue(row, col) );
if ( cellval == ms_stringValues[false] )
m_value = false;
else if ( cellval == ms_stringValues[true] )
m_value = true;
else
{
// do not try to be smart here and convert it to true or false
// because we'll still overwrite it with something different and
// this risks to be very surprising for the user code, let them
// know about it
wxFAIL_MSG( wxT("invalid value for a cell with bool editor!") );
// Still need to initialize it to something.
m_value = false;
}
}
}
void wxGridCellBoolEditor::SetGridFromValue(int row, int col, wxGrid* grid) const
{
wxGridTableBase * const table = grid->GetTable();
if ( table->CanSetValueAs(row, col, wxGRID_VALUE_BOOL) )
table->SetValueAsBool(row, col, m_value);
else
table->SetValue(row, col, GetStringValue());
}
#endif // wxUSE_CHECKBOX
#if wxUSE_COMBOBOX