lumiera_/src/proc/control/command.cpp

311 lines
7.7 KiB
C++
Raw Normal View History

2009-06-08 04:46:07 +02:00
/*
Command - Key abstraction for proc/edit operations and UNDO management
Copyright (C) Lumiera.org
2009-07-27 02:38:53 +02:00
2009, Hermann Vosseler <Ichthyostega@web.de>
2009-06-08 04:46:07 +02:00
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
2009-07-21 04:49:00 +02:00
** @see command-registry.hpp
2009-06-08 04:46:07 +02:00
**
*/
2009-08-01 17:14:27 +02:00
#include "lib/util.hpp"
#include "lib/error.hpp"
2009-08-01 17:14:27 +02:00
#include "include/logging.h"
2009-06-08 04:46:07 +02:00
#include "proc/control/command.hpp"
2009-07-24 17:50:14 +02:00
#include "proc/control/command-def.hpp"
#include "proc/control/command-impl.hpp"
2009-07-21 04:49:00 +02:00
#include "proc/control/command-registry.hpp"
2009-07-24 17:50:14 +02:00
#include "proc/control/handling-pattern.hpp"
2009-06-08 04:46:07 +02:00
//#include "proc/mobject/mobject-ref.hpp"
//#include "proc/mobject/mobject.hpp"
//#include "proc/mobject/placement.hpp"
2009-08-01 17:14:27 +02:00
#include <boost/format.hpp>
#include <sstream>
#include <string>
2009-08-01 17:14:27 +02:00
using namespace lumiera;
using std::ostringstream;
using std::string;
2009-08-01 17:14:27 +02:00
using boost::format;
using boost::str;
using util::cStr;
2009-06-08 04:46:07 +02:00
2009-06-08 04:46:07 +02:00
namespace control {
2009-07-25 19:21:50 +02:00
LUMIERA_ERROR_DEFINE (INVALID_COMMAND, "Unknown or insufficiently defined command");
2009-08-01 17:14:27 +02:00
LUMIERA_ERROR_DEFINE (DUPLICATE_COMMAND, "Attempt to redefine an already existing command definition");
2009-07-25 19:21:50 +02:00
LUMIERA_ERROR_DEFINE (INVALID_ARGUMENTS, "Arguments provided for binding doesn't match stored command function parameters");
2009-07-12 18:55:33 +02:00
LUMIERA_ERROR_DEFINE (UNBOUND_ARGUMENTS, "Command mutation functor not yet usable, because arguments aren't bound");
2009-07-25 19:21:50 +02:00
LUMIERA_ERROR_DEFINE (MISSING_MEMENTO, "Undo functor not yet usable, because no undo state has been captured");
2009-06-08 04:46:07 +02:00
2009-07-29 16:55:15 +02:00
/** storage for the singleton factory used to access CommandRegistry */
lumiera::Singleton<CommandRegistry> CommandRegistry::instance;
2009-07-24 17:50:14 +02:00
Command::~Command() { }
2009-07-29 16:55:15 +02:00
2009-07-24 17:50:14 +02:00
/** */
Command
Command::get (Symbol cmdID)
{
Command cmd = CommandRegistry::instance().queryIndex (cmdID);
2009-08-01 17:14:27 +02:00
static format fmt("Command definition \"%s\" not found");
if (!cmd)
2009-08-01 17:14:27 +02:00
throw error::Invalid(str(fmt % cmdID), LUMIERA_ERROR_INVALID_COMMAND);
return cmd;
}
2009-07-24 17:50:14 +02:00
/** @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
2009-07-26 02:00:47 +02:00
Command::fetchDef (Symbol cmdID)
{
CommandRegistry& registry = CommandRegistry::instance();
Command cmd = registry.queryIndex (cmdID);
2009-07-29 16:55:15 +02:00
if (cmd)
return cmd;
2009-07-29 16:55:15 +02:00
2009-08-01 17:14:27 +02:00
// create an empty definition, later to be activated
Command newDefinition;
2009-07-29 16:55:15 +02:00
return registry.track (cmdID, newDefinition);
} // return new or currently registered cmd...
2009-07-26 02:00:47 +02:00
2009-08-01 17:14:27 +02:00
/** @internal to be invoked by CommandDef
* @throw std::bad_alloc, in which case
* CommandRegistry::killCommandImpl is invoked */
Command&
Command::activate (CommandImpl* implFrame)
2009-08-01 17:14:27 +02:00
{
static format fmt("Command \"%s\" already defined");
REQUIRE (implFrame);
2009-08-01 17:14:27 +02:00
if (this->isValid())
throw error::Logic (str (fmt % *this), LUMIERA_ERROR_DUPLICATE_COMMAND);
2009-08-01 17:14:27 +02:00
_Handle::activate (implFrame, CommandRegistry::killCommandImpl);
2009-08-01 17:14:27 +02:00
INFO (command, "Command \"%s\" defined OK", cStr(*this));
2009-08-01 17:14:27 +02:00
return *this;
}
/** @internal pass a new argument tuple to the
* CommandImpl without exposing implementation.
*/
void
Command::setArguments (Arguments& args)
{
_Handle::impl().setArguments(args);
}
Command
2009-07-26 02:00:47 +02:00
Command::storeDef (Symbol newCmdID)
{
CommandRegistry& registry = CommandRegistry::instance();
static format fmt("Unable to store %s as new command. ID \"%s\" is already in use");
if (registry.queryIndex (newCmdID))
throw error::Logic (str (fmt % *this % newCmdID), LUMIERA_ERROR_DUPLICATE_COMMAND);
Command cloneDefinition;
cloneDefinition.activate (registry.createCloneImpl(impl()));
return registry.track (newCmdID, cloneDefinition);
2009-07-26 02:00:47 +02:00
}
2009-07-24 17:50:14 +02:00
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 */
2009-07-24 17:50:14 +02:00
size_t
Command::definition_count()
{
return CommandRegistry::instance().index_size();
2009-07-24 17:50:14 +02:00
}
/** @return number distinguishable registered command \em instances */
2009-07-24 17:50:14 +02:00
size_t
Command::instance_count()
{
return CommandRegistry::instance().instance_count();
2009-07-24 17:50:14 +02:00
}
/** 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
*/
2009-07-24 17:50:14 +02:00
bool
CommandDef::isValid() const
{
if (prototype_)
{
Command cmd = CommandRegistry::instance().queryIndex (this->id_);
return cmd.isValid()
&& (prototype_ == cmd);
}
return false;
2009-07-24 17:50:14 +02:00
}
bool
Command::canExec() const
{
return isValid()
&& impl().canExec();
2009-07-24 17:50:14 +02:00
}
bool
Command::canUndo() const
{
return isValid()
&& impl().canUndo();
2009-07-24 17:50:14 +02:00
}
2009-08-01 17:14:27 +02:00
/** 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);
2009-08-01 17:14:27 +02:00
if (id)
repr << "(\""<<id<<"\") ";
else
repr << "(_xxx_) ";
if (!isValid())
repr << "NIL";
else
if (canUndo())
repr << "{undo}";
else
if (canExec())
repr << "{exec}";
else
repr << "{def}";
return repr.str();
}
2009-07-24 17:50:14 +02:00
ExecResult
2009-07-24 17:50:14 +02:00
Command::undo ()
{
HandlingPattern const& defaultPattern
= HandlingPattern::get (getDefaultHandlingPattern());
return exec (defaultPattern.howtoUNDO());
2009-07-24 17:50:14 +02:00
}
ExecResult
2009-07-24 17:50:14 +02:00
Command::exec (HandlingPattern const& execPattern)
{
return execPattern (*this);
}
ExecResult
Command::exec (HandlingPattern::ID pattID)
{
return HandlingPattern::get(pattID) (*this);
2009-07-24 17:50:14 +02:00
}
ExecResult
2009-07-26 02:00:47 +02:00
Command::execSync ()
{
return exec (HandlingPattern::get(HandlingPattern::SYNC_THROW));
2009-07-26 02:00:47 +02:00
}
HandlingPattern::ID
2009-07-24 17:50:14 +02:00
Command::getDefaultHandlingPattern() const
{
UNIMPLEMENTED ("manage the default command handling pattern");
}
2009-07-25 19:21:50 +02:00
HandlingPattern::ID
Command::setHandlingPattern (HandlingPattern::ID pattID)
{
UNIMPLEMENTED ("manage handling patterns in general");
}
2009-06-08 04:46:07 +02:00
} // namespace control