diff --git a/include/wx/generic/grid.h b/include/wx/generic/grid.h index 96d20f6d2c..754deb5460 100644 --- a/include/wx/generic/grid.h +++ b/include/wx/generic/grid.h @@ -233,6 +233,130 @@ public: // Smart pointer to wxGridCellRenderer, calling DecRef() on it automatically. typedef wxObjectDataPtr 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(m_event); + } + + // Can be called for objects with Mouse origin only. + const wxMouseEvent& GetMouseEvent() const + { + wxASSERT( m_origin == Mouse ); + + return *static_cast(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(); @@ -2908,13 +3058,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(); diff --git a/interface/wx/grid.h b/interface/wx/grid.h index dc8c3081e6..112365d672 100644 --- a/interface/wx/grid.h +++ b/interface/wx/grid.h @@ -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,12 @@ 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. + @library{wxcore} @category{grid} @@ -627,6 +724,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: /** diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index 13e89994b6..397c05bcd5 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -4664,7 +4664,7 @@ wxGrid::DoGridCellLeftUp(wxMouseEvent& event, { ClearSelection(); - if ( DoEnableCellEditControl() ) + if ( DoEnableCellEditControl(wxGridActivationSource::From(event)) ) GetCurrentCellEditorPtr()->StartingClick(); m_waitForSlowClick = false; @@ -5969,7 +5969,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 @@ -7149,7 +7150,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 +7159,20 @@ void wxGrid::EnableCellEditControl( bool enable ) } } -bool wxGrid::DoEnableCellEditControl() +bool wxGrid::DoEnableCellEditControl(const wxGridActivationSource& actSource) { if ( SendEvent(wxEVT_GRID_EDITOR_SHOWN) == -1 ) return false; - m_cellEditCtrlEnabled = true; + 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); - DoShowCellEditControl(); + return false; + } return true; } @@ -7217,16 +7224,57 @@ 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. + if ( SendEvent(wxEVT_GRID_CELL_CHANGING, res.GetNewValue()) != -1 ) + { + 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) == -1 ) + { + wxFAIL_MSG( "Vetoing wxEVT_GRID_CELL_CHANGED is ignored" ); + } + } + 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 +7299,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 +7394,8 @@ void wxGrid::DoShowCellEditControl() editor->BeginEdit(row, col, this); editor->SetCellAttr(NULL); + + return true; } void wxGrid::HideCellEditControl()