/* * Action.hpp * * Created on: Dec 8, 2009 * Author: crueger */ #ifndef ACTION_HPP_ #define ACTION_HPP_ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include #include #include /** Used in .def files in paramdefaults define to set that no default value exists. * We define NOPARAM_DEFAULT here, as it is used in .def files and needs to be present * before these are included. */ #define NOPARAM_DEFAULT BOOST_PP_NIL /** Nicely visible short-hand for push a status message * */ #ifndef STATUS #define STATUS(msg) pushStatus(msg) #endif // forward declaration template class Registry; namespace MoleCuilder { class ActionHistory; class ActionQueue; class ActionRegistry; class ActionSequence; } class ActionSequenceTest; class Dialog; #include "Actions/ActionParameters.hpp" #include "Actions/ActionState.hpp" #include "Actions/ActionTrait.hpp" namespace MoleCuilder { /** Actions are Command patterns to allow for undoing and redoing. * * Each specific Action derives from this class to implement a certain functionality. * * Actions describe something that has to be done. * Actions can be passed around, stored, performed and undone (Command-Pattern: * http://en.wikipedia.org/wiki/Command_pattern). * * Unique to each Action is its ActionTrait, i.e. the options it requires * to perform a certain function. E.g. in order to execute a "add atom" Action * we need to know a position and an element. These options have certain * properties, see \ref OptionTrait and \ref ActionTrait wherein these are stored. * Essentially, each option is stored as an \ref OptionTrait instance and * contains the token, default value, a description, the type, ... * * ActionTrait itself is also an OptionTrait because the command token may actually * coincide with an option-token. E.g. this allows "...--add-atom 3" to mean * both execute the action under token "add-atom" and that the option "add-atom" * (the new atom's \ref element number) should contain 3. * * \ref ActionTrait contains a map of all associated options. With this in the cstor * we register not only the Action with the \ref ActionRegistry but also each of * its \link options OptionTrait \endlink with the \ref OptionRegistry. * * The token of the option is unique, but two Action's may share the same token if: * -# they have the same default value * -# they have the same type * * This requirement is easy because if you need to store some option of another * type, simply think of a new suitable name for it. * * The actual value, i.e. "3" in the "add-atom" example, is taken from the * ValueStorage, see \ref Dialog for how this is possible. * * \note There is a unit test that checks on the consistency of all registered * options, also in "--enable-debug"-mode assertions will check that an option * has not been registered before under another type. * */ class Action { //!> grant ActionQueue access to undo() and redo() friend class ActionHistory; //!> grant ActionQueue access to call() friend class ActionQueue; //!> grant ActionRegistry access to cstors (for ActionRegistry::fillRegistry()) friend class ActionRegistry; //!> grant ActionSequence access to Action's private stuff friend class ActionSequence; //!> grant all Registry templates access to cstor and dstor (for Registry::cleanup()) template friend class ::Registry; //!> TextMenu needs to instantiate some specific Actions, grant access to cstor friend class TextMenu; // some unit tests on Actions friend class ::ActionSequenceTest; public: enum QueryOptions {Interactive, NonInteractive}; /** Destructor for class Action. * * Needs to be public as clone() is a public function. * */ virtual ~Action(); protected: /** * Standard constructor of Action Base class * * All Actions need to have a name. The second flag indicates, whether the action should * be registered with the ActionRegistry. If the Action is registered the name of the * Action needs to be unique for all Actions that are registered. * * \note NO reference for \a _Traits as we do have to copy it, otherwise _Traits would have * to be present throughout the program's run. * * \param Traits information class to this action */ Action(const ActionTrait &_Traits); /** * This method is used to call an action. The basic operations for the Action * are carried out and if necessary/possible the Action is added to the History * to allow for undo of this action. * * If the call needs to undone you have to use the History, to avoid destroying * invariants used by the History. * * Note that this call can be Interactive (i.e. a dialog will ask the user for * necessary information) and NonInteractive (i.e. the information will have to * be present already within the ValueStorage class or else a MissingArgumentException * is thrown) */ void call(); public: /** * This method provides a flag that indicates if an undo mechanism is implemented * for this Action. If this is true, and this action was called last, you can * use the History to undo this action. */ virtual bool canUndo()=0; /** * This method provides a flag, that indicates if the Action changes the state of * the application in a way that needs to be undone for the History to work. * * If this is false the Action will not be added to the History upon calling. However * Actions called before this one will still be available for undo. */ virtual bool shouldUndo()=0; /** * Indicates whether the Action can do it's work at the moment. If this * is false calling the action will result in a no-op. */ virtual bool isActive() const; /** * Returns the name of the Action. */ const std::string getName() const; /** * returns a detailed help message. */ const std::string help() const; /** Clones the Action. * */ virtual Action* clone(enum QueryOptions flag = Interactive) const=0; /** Prepares the Action's parameters. * */ virtual void prepare(enum QueryOptions flag = Interactive); /** Prints what would be necessary to add the Action from the Command Line Interface. * * \param ost output stream to print to */ virtual void outputAsCLI(std::ostream &ost) const=0; /** Prints what would be necessary to add the Action from a Python script * * \param ost output stream to print to * \param prefix package prefix to be used */ virtual void outputAsPython(std::ostream &ost, const std::string &prefix) const=0; /** Sets the option defined by \a _token to \a _value for this action. * * This is needed when constructing MakroActions. * * \param _token token of the option * \param _value new value */ virtual void setOptionValue(const std::string &_token, const std::string &_value)=0; /** * Traits resemble all necessary information that "surrounds" an action, such as * its name (for ActionRegistry and as ref from string to instance and vice versa), * which menu, which position, what parameters, their types, if it is itself a * parameter and so on ... * * Note that is important that we do not use a reference here. We want to copy the * information in the Action's constructor and have it contained herein. Hence, we * also have our own copy constructor for ActionTrait. Information should be * encapsulated in the Action, no more references to the outside than absolutely * necessary. */ const ActionTrait Traits; protected: /** Removes the static entities Action::success and Action::failure. * This is only to be called on the program's exit, i.e. in cleanUp(), * as these static entities are used throughout all Actions. */ static void removeStaticStateEntities(); /** Creates the static entities Action::success and Action::failure. * This is only to be called by ActionHistory. */ static void createStaticStateEntities(); /** * This method is called by the History, when an undo is performed. It is * provided with the corresponding state produced by the performCall or * performRedo method and needs to provide a state that can be used for redo. */ ActionState::ptr undo(ActionState::ptr); /** * This method is called by the History, when a redo is performed. It is * provided with the corresponding state produced by the undo method and * needs to produce a State that can then be used for another undo. */ ActionState::ptr redo(ActionState::ptr); /** * This special state can be used to indicate that the Action was successful * without providing a special state. Use this if your Action does not need * a specialized state. */ static ActionState::ptr success; /** * This special state can be returned, to indicate that the action could not do it's * work, was aborted by the user etc. If you return this state make sure to transactionize * your Actions and unroll the complete transaction before this is returned. */ static ActionState::ptr failure; /** * This creates the dialog requesting the information needed for this action from the user * via means of the user interface. */ Dialog * createDialog(); /** Virtual function that starts the timer. * */ virtual void startTimer() const {}; /** Virtual function that ends the timer. * */ virtual void endTimer() const {}; /** Function pass-through for ActionQueue::insertAction(). * * This pass-through is present to allow each derived Action access to private * ActionQueue::insertAction() which is not possible otherwise as friendship * is not inherited. * */ static void insertAction(Action *_action, enum Action::QueryOptions state); /** Proxy function to grant all derived Actions access to * ActionQueue::pushStatus(). * * \param _msg status message to push */ void pushStatus(const std::string& _msg); private: /** * This is called internally before the action is processed. This adds necessary queries * to a given dialog to obtain parameters for the user for processing the action accordingly. * The dialog will be given to the user before Action::performCall() is initiated, values * are transfered via ValueStorage. */ virtual Dialog * fillDialog(Dialog*)=0; /** * This is called internally when the call is being done. Implement this method to do the actual * work of the Action. Implement this in your Derived classes. Needs to return a state that can be * used to undo the action. */ virtual ActionState::ptr performCall()=0; /** * This is called internally when the undo process is chosen. This Method should use the state * produced by the performCall method to return the state of the application to the state * it had before the Action. */ virtual ActionState::ptr performUndo(ActionState::ptr)=0; /** * This is called internally when the redo process is chosen. This method shoudl use the state * produced by the performUndo method to return the application to the state it should have after * the action. * * Often this method can be implement to re-use the performCall method. However if user interaction * or further parameters are needed, those should be taken from the state and not query the user * again. */ virtual ActionState::ptr performRedo(ActionState::ptr)=0; }; } #endif /* ACTION_HPP_ */