/* Command - Key abstraction for proc/edit operations and UNDO management Copyright (C) Lumiera.org 2009, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *****************************************************/ /** @file command.cpp ** //TODO ** ** @see command.hpp ** @see command-registry.hpp ** */ #include "lib/util.hpp" #include "lib/error.hpp" #include "include/logging.h" #include "proc/control/command.hpp" #include "proc/control/command-def.hpp" #include "proc/control/command-impl.hpp" #include "proc/control/command-registry.hpp" #include "proc/control/handling-pattern.hpp" //#include "proc/mobject/mobject-ref.hpp" //#include "proc/mobject/mobject.hpp" //#include "proc/mobject/placement.hpp" #include #include #include using namespace lumiera; using std::ostringstream; using std::string; using boost::format; using boost::str; using util::cStr; namespace control { LUMIERA_ERROR_DEFINE (INVALID_COMMAND, "Unknown or insufficiently defined command"); LUMIERA_ERROR_DEFINE (DUPLICATE_COMMAND, "Attempt to redefine an already existing command definition"); LUMIERA_ERROR_DEFINE (INVALID_ARGUMENTS, "Arguments provided for binding doesn't match stored command function parameters"); LUMIERA_ERROR_DEFINE (UNBOUND_ARGUMENTS, "Command mutation functor not yet usable, because arguments aren't bound"); LUMIERA_ERROR_DEFINE (MISSING_MEMENTO, "Undo functor not yet usable, because no undo state has been captured"); /** storage for the singleton factory used to access CommandRegistry */ lumiera::Singleton CommandRegistry::instance; Command::~Command() { } /** */ Command Command::get (Symbol cmdID) { Command cmd = CommandRegistry::instance().queryIndex (cmdID); static format fmt("Command definition \"%s\" not found"); if (!cmd) throw error::Invalid(str(fmt % cmdID), LUMIERA_ERROR_INVALID_COMMAND); return cmd; } /** @todo this is a "nice-to-have"; it would allow to call a function as a command, * almost as if it was a normal function. But this function needs to be defined * as a command previously, together with a suitable UNDO function. Moreover * this would probably require to build an additional index; * thus this feature is unimplemented for the time being. */ Command Command::get (FuncPtr funcP) { UNIMPLEMENTED ("find a command definition which was based on the given function (ptr)"); } Command Command::fetchDef (Symbol cmdID) { CommandRegistry& registry = CommandRegistry::instance(); Command cmd = registry.queryIndex (cmdID); if (cmd) return cmd; // create an empty definition, later to be activated Command newDefinition; return registry.track (cmdID, newDefinition); } // return new or currently registered cmd... /** @internal to be invoked by CommandDef * @throw std::bad_alloc, in which case * CommandRegistry::killCommandImpl is invoked */ Command& Command::activate (CommandImpl* implFrame) { static format fmt("Command \"%s\" already defined"); REQUIRE (implFrame); if (this->isValid()) throw error::Logic (str (fmt % *this), LUMIERA_ERROR_DUPLICATE_COMMAND); _Handle::activate (implFrame, CommandRegistry::killCommandImpl); INFO (command, "Command \"%s\" defined OK", cStr(*this)); return *this; } CommandDef Command::storeDef (Symbol newCmdID) { UNIMPLEMENTED ("create a new definition & prototype based on this command"); } bool Command::remove (Symbol cmdID) { UNIMPLEMENTED ("de-register a single command instance"); } bool Command::undef (Symbol cmdID) { UNIMPLEMENTED ("completely drop a command definition, together with all dependent instances"); } /** @return the number of command \em definitions currently registered */ size_t Command::definition_count() { return CommandRegistry::instance().index_size(); } /** @return number distinguishable registered command \em instances */ size_t Command::instance_count() { return CommandRegistry::instance().instance_count(); } /** is this a valid command definition frame? especially * - the prototype command is initialised properly * - there is a command definition registered for our command ID * - and the registered command uses the same underlying command impl * record than our prototype */ bool CommandDef::isValid() const { if (prototype_) { Command cmd = CommandRegistry::instance().queryIndex (this->id_); return cmd.isValid() && (prototype_ == cmd); } return false; } bool Command::canExec() const { return isValid() && impl().canExec(); } bool Command::canUndo() const { return isValid() && impl().canUndo(); } /** diagnostics: shows the commandID, if any, * and the degree of definition of this command */ Command::operator string() const { ostringstream repr; repr << "Command"; ////////////////////////////////////////////////////////////////////TODO do we need no-throw guarantee here? Symbol id = CommandRegistry::instance().findDefinition (*this); if (id) repr << "(\""<