Right now every dialog, extension, etc. has to have a header file - otherwise it's unusable. The underlying issue for dialogs is that there must be an action defined to open them. This means either including the dialog header in verbs.cpp, or wherever the dialog is used. I stumbled over OpenBabel sources some time ago and this gave me the following idea. We put this in some file, e.g. action-manager.h:
class ActionManager { ... typedef ActionVector std::vector<Glib::RefPtrGtk::Action >; class GetActionsMarshaller { ... bool marshal(InType x) { _actions.push_back(x); } OutType value() { return _actions; } ActionVector _actions; } ... static ActionManager *get() { static ActionManager *am = new ActionManager(); return am; } Glib::RefPtrGtk::ActionGroup get_document_action_group(Glib::ustring name) { Glib::RefPtrGtk::ActionGroup ag = Gtk::ActionGroup::create(name); ActionVector actions = signal_get_actions.emit(); for(ActionVector::iterator i = actions.begin(); i != actions.end(); ++i) { ag.add(*i); } return ag; } sigc::signal<Glib::RefPtrGtk::Action, GetActionsMarshaller> signal_get_actions; friend class GetActionsMarshaller; };
template<typename T> class ActionManagerEntry { ActionManagerEntry() { ActionManager::get()->signal_get_actions.bind(sigc::ptr_fun(T::create)); } };
Then in the dialog file we put this:
class ShowSomeDialogAction : public Gtk::Action { ... virtual void on_activate() { SomeDialog::show(); } static Glib::RefPtrGtk::Action create() { return Glib::RefPtrGtk::Action(new ShowSomeDialogAction); } } ActionManagerEntry<ShowSomeDialog> ame_show_some_dialog;
And to construct the action group we do this:
ActionManager *am = ActionManager::get(); Glib::RefPtrGtk::ActionGroup document_actions = am->get_document_action_group(document_name);
What happens here: Each ActionManagerEntry adds a callback to the ActionManager object. This way, the actions are not actually created until GTK is initialized and a document is opened. When we want to create an action group for a document, we send the get_actions signal. The GetActionsMarshaller class marshals the responses we get from the registered callbacks and creates a vector of actions, which are then added to an action group and returned. A similar approach can also be used to get rid of headers and centralized initialization for e.g. extensions
What do you think of this approach? On one hand, it scatters the action definitions across many files, but on the other it removes the need for centralized initialization and improves modularity (everything pertaining to a given dialog is in one file, including its actions).
Regards, Krzysztof Kosiński