/* * Dialog.hpp * * Created on: Jan 5, 2010 * Author: crueger */ #ifndef DIALOG_HPP_ #define DIALOG_HPP_ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "LinearAlgebra/RealSpaceMatrix.hpp" #include "LinearAlgebra/Vector.hpp" #include "Parameters/Parameter.hpp" #include "Parameters/Specifics/KeyValuePair.hpp" class atom; class RealSpaceMatrix; class element; class molecule; /** Dialog is one of the two main classes of the UIFactory base class. * * The Dialog is meant for asking the user for information needed to perform * actions he desires, such as asking for a position in space or a length. * * For this purpose there is the base class Query and numerous specializations * for each of the types to be asked. There are primitives integer, doubles and * string, but also advanced types such as element, molecule or Vector. There * is also an empty query for displaying text. * * Note that the templatization of Dialog::query() allows for easy implementation * of new types that correspond to/are derived from old ones. * *

How do Dialogs operate?

* * Dialogs are initiated by Action::FillDialog() function calls. Within Action::Call() * a dialog is created and passed on to FillDialog(), which is specialized in each * specific Action to ask for the specific parameter it needs. * * Dialogs are derived for each of the UI types * -# CommandLineDialog * -# QtDialog * -# TextDialog * * This "asking" for parameters is done via the Query class. There are four types * of Query types: * -# Query, members in class Dialog * -# CommandLineQuery, members in class CommandLineDialog * -# QtQuery, members in class QtDialog * -# TextQuery, members in class TextDialog * Each embodies a certain way of asking the user for the specific type of parameter * needed, e.g. a file via the TextMenu interface would be done in member functions of * TextDialog::FileTextQuery. * * * Generally, the sequence of events is as follows: * -# Action::fillDialog() calls upon Dialog::query for some type T, let's say T * stands for double * -# Dialog::query call a function queryDouble() * -# depending on the currently used UIFactory, the Dialog above is actually either * of the three specialized types, let's say CommandLine. And we see that within * CommandLineDialog we have the respective method ueryDouble() that registers * a new instance of the class CommandLineDialog::DoubleCommandLineQuery. * -# The Query's are first registered, as multiple parameters may be needed for * a single Action and we don't want the popping up of a dialog window for each * alone. Rather, we want to merge the Query's into a single Dialog. Therefore, * they are first registered and then executed in sequence. This is done in * Dialog::display(), i.e. when the dialog is finally shown to the user. * -# Then each of the registered Query's, here our CommandLineDialog:: * DoubleCommandLineQuery, constructor is called. * -# This will call the constructor of its base class, where the actual value * is stored and later stored into the ValueStorage class by * Dialog::Query::setResult(). * -# Also, e.g. for the Qt interface, the buttons, labels and so forth are * created and added to the dialog. * -# Now the users enters information for each UI something different happens: * -# CommandLine: The actual value retrieval is done by the CommandLineParser and * the boost::program_options library, the value is stored therein for the moment. * (see here: http://www.boost.org/doc/libs/1_44_0/doc/html/program_options/) * -# TextMenu: The value is requested via std::cin from the user. * -# QtMenu: The users enters the value in a Dialog. Due to Qt necessities a * Pipe class obtains the value from the Dialog with Qt's signal/slot mechanism * and put it into Dialog::...Query value. * -# in our case DoubleCommandLineQuery::handle() will be called. The value is * retrieved and put into Dialog::DoubleQuery::tmp * -# Finally, for each Query, also Dialog::DoubleQuery, setResult() is called which * puts the value as a string into the ValueStorage under a given token that is * associated with a type (and thereby we assure type-safety). * *

Regarding derived types of types with already present queries

* * Example: We have got a derived Vector class, called BoxVector, that is by any means * a Vector but with one difference: it is always bound to lie within the current domain. * With regards to type-casting it to and from a string however, nothing is different * between Vector and BoxVector. * * So, do we need a new Query for this? * No. * * We just have to do this: * -# add a specialization of Dialog::query where queryVector()is used. * @code * template <> void Dialog::query(const std::string &title, const std::string &description) { * queryVector(title, description); * } * @endcode * -# make sure that * -# ValueStorage::setCurrentValue() the specialization for class Vector has an * if case also for BoxVector and does something appropriate. * -# ValueStorage::queryCurrentValue() has another specialization doing the same * as for Vector but for BoxVector in its signature. * * And that's all. * * *

Adding new queries

* * Check first whether you really need a new query or whether we can derive it and re-use * one of the present ones. * * Due to the three present UI interfaces we have to add a specific Query for each, here * is a list: * -# add a token (i.e. a unique name for the query parameter) and its type to the * global list in \ref GlobalListOfParameterQueries.hpp. This will via boost * preprocessor magic add definitions and some intermediatr template specializations. * -# add a specialization for each of the UI interfaces of Dialog::...Query class. * -# add class declaration of QtDialog::...Query in \ref QtQuery.hpp * -# add CommandLineDialog::...Query, TextDialog::...Query, QtDialog::...Query * -# TypeEnumContainer add new type to query. Make the Type name match with the token * -# CommandLineParser::AddOptionToParser() add new type to query * -# CommandLineParser_valdiates.[ch]pp: If given above as a new type * program_options::value, define and implement a validate() function here. * * Note that the Empty..Query is always specificly present as it has not type and thus * does not fit into the preprocessor magic scheme (on the plus side it also serves * as a kind of visualization of what the preprocessor actually does). * */ class Dialog { public: Dialog(const std::string &_title); virtual ~Dialog(); template void query(Parameter &, const std::string ="", const std::string = ""); virtual void queryEmpty(const std::string ="", const std::string = "")=0; virtual void queryVector(Parameter &, const std::string ="", const std::string = "")=0; virtual void queryVectors(Parameter< std::vector > &, const std::string ="", const std::string = "")=0; /** With the following boost::preprocessor code we generate virtual function * definitions for all desired query types in the abstract class Dialog. */ #include "UIElements/GlobalListOfParameterQueries.hpp" #include "UIElements/Dialog_impl_pre.hpp" // iterate over all parameter query types #if defined GLOBALLISTOFPARAMETERQUERIES_Token && defined GLOBALLISTOFPARAMETERQUERIES_Type #define SUFFIX =0 #define BOOST_PP_LOCAL_MACRO(n) dialog_declaration(~, n, GLOBALLISTOFPARAMETERQUERIES_Token, GLOBALLISTOFPARAMETERQUERIES_Type) #define BOOST_PP_LOCAL_LIMITS (0, MAXPARAMETERTOKENS-1) #include BOOST_PP_LOCAL_ITERATE() #undef dialog_declaration #undef SUFFIX #endif #include "Dialog_impl_undef.hpp" /* End of preprocessor code piece */ virtual bool display(); virtual void handleAll(); virtual bool checkAll(); virtual void setAll(); virtual bool hasQueries(); virtual void update(){} public: // methodology for handling queries // all queries are stored and then performed at appropriate times //these queries can be handled by this dialog /** Base class for all queries. * * A query is request to the user for a value of a specific type. * E.g. All \ref Action's need parameters to perform a specific function. * These are obtained from the user at run-time via a Query regardless * of the interface that the user is using. * * A Query just has title and description and serves as the general interface * to queries. TQuery is a templatization of the Query containing a protected * member variable of the specific type and also a Parameter<> reference of * the type that actually belongs to the Action that triggered/created the * Query. handle() is UI-specific and sets the (temporary) member variable. * However, isValid() in TQuery checks via the Parameter<> reference whether * the variable is valid with the given Validators and setResult() finally * set the Parameter with the temporary variable. * * For each type there is a derived class per \b UI, e.g. for the * \link userinterfaces-textmenu textmenu \endlink we have * \ref BooleanTextQuery that derives from \ref TQuery. This derived * class has to implement the Query::handle() function that actually sets * the protected member variable to something the user has entered. * * Thanks to the templated TQuery this is a as simple as it can get. The * handle() has to be UI-specific, hence has to be implemented once for * every present UI. But all other code can be used for either of them. * * \section query-howto How to add another query? * * Let's say we want to query for a type called \a Value. * * Then, we do the following: * -# add a virtual function queryValue inside class Dialog. * -# now, for each of the GUIs we basically have to add a sub-class for the * respective Query inside the derived Dialog that implements handle(). * -# QT: typically we use a onUpdate() function here attached the Qt * signals and handle then just does nothing. * -# CommandLine: nothing special, handle() imports value from \a * CommandLineParser and sets the tmp variable. * -# Text: nothing special, handle() queries the user and sets the tmp * variable */ class Query { friend class Dialog; public: Query(const std::string _title, const std::string _description = ""); virtual ~Query(); virtual void handle()=0; virtual bool isValid()=0; virtual void setResult()=0; protected: const std::string getTitle() const; const std::string getDescription() const; private: const std::string title; //!< short title of the query const std::string description; //!< longer description for tooltips or for help }; template class TQuery : public Query { public: TQuery(Parameter &_param, const std::string title, const std::string _description = "") : Query(title, _description), handleSuccess(false), param(_param) {} virtual ~TQuery(){} virtual void handle()=0; virtual bool isValid(){ return param.isValid(temp); } virtual void setResult(){ if (handleSuccess) param.set(temp); } protected: T temp; bool handleSuccess; Parameter ¶m; }; // Empty Query is just meant for showing text, such as version, help, initial message or alike class EmptyQuery : public Query { public: EmptyQuery(const std::string title, const std::string _description = ""); virtual ~EmptyQuery(); virtual void handle()=0; virtual bool isValid(){ return true; } virtual void setResult(); }; void registerQuery(Query* query); private: std::list queries; }; // we have specialization of Vector to allow internal storing as string template <> void Dialog::query(Parameter &, const std::string, const std::string); template <> void Dialog::query< std::vector >(Parameter< std::vector > &, const std::string, const std::string); /** Template specialization for Query to allow internal storing of a * string instead of a Vector. * * Because we need to evaluate the string as a possible GeometryRegistry key * and we may do this only when the Action (whose options we are querying) * is executed, not before. */ template <> class Dialog::TQuery : public Query { public: TQuery(Parameter &_param, const std::string title, const std::string _description = "") : Query(title, _description), handleSuccess(false), param(_param) {} virtual ~TQuery(){} virtual void handle()=0; virtual bool isValid(){ return param.isValidAsString(temp); } virtual void setResult(){ if (handleSuccess) param.setAsString(temp); } protected: std::string temp; bool handleSuccess; Parameter ¶m; }; template <> class Dialog::TQuery< std::vector > : public Query { public: TQuery(Parameter< std::vector > &_param, const std::string title, const std::string _description = "") : Query(title, _description), handleSuccess(false), param(_param) {} virtual ~TQuery(){} virtual void handle()=0; virtual bool isValid(); virtual void setResult(); protected: std::vector temp; bool handleSuccess; Parameter< std::vector > ¶m; }; #endif /* DIALOG_HPP_ */