2 Copyright (C) 2005 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <boost/shared_ptr.hpp>
29 #include <gtk/gtkaccelmap.h>
30 #include <gtk/gtkuimanager.h>
31 #include <gtk/gtkactiongroup.h>
34 #include <gtkmm/accelmap.h>
35 #include <gtkmm/uimanager.h>
37 #include <glibmm/miscutils.h>
39 #include "pbd/error.h"
40 #include "pbd/stacktrace.h"
42 #include "gtkmm2ext/actions.h"
43 #include "gtkmm2ext/utils.h"
52 using namespace Gtkmm2ext;
54 typedef std::map<std::string, Glib::RefPtr<Gtk::Action> > ActionMap;
55 static ActionMap actions;
56 typedef std::vector<Glib::RefPtr<Gtk::ActionGroup> > ActionGroups;
57 static ActionGroups groups;
59 RefPtr<UIManager> ActionManager::ui_manager;
60 string ActionManager::unbound_string = X_("--");
65 ActionState (GtkAction* a, bool s) : action (a), sensitive (s) {}
68 typedef std::vector<ActionState> ActionStates;
70 static ActionStates action_states_to_restore;
71 static bool actions_disabled = false;
74 ActionManager::MissingActionException::MissingActionException (std::string const & str)
75 : missing_action_name (str)
77 std::cerr << "MAE: " << str << std::endl;
81 ActionManager::MissingActionException::what () const throw ()
84 return strdup (string_compose ("missing action: %1", missing_action_name).c_str());
88 ActionManager::init ()
90 ui_manager = UIManager::create ();
94 ActionManager::save_action_states ()
96 for (ActionGroups::iterator g = groups.begin(); g != groups.end(); ++g) {
98 /* the C++ API for functions used here appears to be broken in
99 gtkmm2.6, so we fall back to the C level.
102 GtkActionGroup* group = (*g)->gobj();
104 for (GList* acts = gtk_action_group_list_actions (group); acts; acts = g_list_next (acts)) {
105 GtkAction* action = (GtkAction*) acts->data;
106 action_states_to_restore.push_back (ActionState (action, gtk_action_get_sensitive (action)));
112 ActionManager::set_sensitive (Glib::RefPtr<ActionGroup> group, bool yn)
114 /* the C++ API for functions used here appears to be broken in
115 gtkmm2.6, so we fall back to the C level.
118 GtkActionGroup* grp = group->gobj();
120 for (GList* acts = gtk_action_group_list_actions (grp); acts; acts = g_list_next (acts)) {
121 GtkAction* action = (GtkAction*) acts->data;
122 gtk_action_set_sensitive (action, yn);
127 ActionManager::enable_active_actions ()
129 if (!actions_disabled) {
133 for (ActionStates::iterator i = action_states_to_restore.begin(); i != action_states_to_restore.end(); ++i) {
134 if ((*i).action && (*i).sensitive) {
135 gtk_action_set_sensitive ((*i).action, true);
139 action_states_to_restore.clear ();
140 actions_disabled = false;
144 ActionManager::disable_active_actions ()
146 if (actions_disabled == true ) {
149 // save all action's states to action_states_to_restore
150 save_action_states ();
152 // set all action's states disabled
153 for (ActionStates::iterator i = action_states_to_restore.begin(); i != action_states_to_restore.end(); ++i) {
154 if ((*i).sensitive) {
155 gtk_action_set_sensitive ((*i).action, false);
158 actions_disabled = true;
162 ActionManager::get_widget (const char * name)
164 return ui_manager->get_widget (name);
168 ActionManager::set_sensitive (vector<RefPtr<Action> >& actions, bool state)
170 // if actions weren't disabled
171 if (!actions_disabled) {
172 for (vector<RefPtr<Action> >::iterator i = actions.begin(); i != actions.end(); ++i) {
173 (*i)->set_sensitive (state);
177 // actions were disabled
178 // so we should just set necessary action's states in action_states_to_restore
179 for (vector<RefPtr<Action> >::iterator i = actions.begin(); i != actions.end(); ++i) {
180 // go through action_states_to_restore and set state of actions
181 for (ActionStates::iterator j = action_states_to_restore.begin(); j != action_states_to_restore.end(); ++j) {
182 // all actions should have their individual name, so we can use it for comparison
183 if (gtk_action_get_name ((*j).action) == (*i)->get_name ()) {
184 (*j).sensitive = state;
192 ActionManager::check_toggleaction (const string& n)
194 set_toggleaction_state (n, true);
198 ActionManager::uncheck_toggleaction (const string& n)
200 set_toggleaction_state (n, false);
204 ActionManager::set_toggleaction_state (const string& n, bool s)
206 string::size_type pos = n.find ('/');
208 if (pos == string::npos || pos == n.size() - 1) {
209 error << string_compose ("illegal action name \"%1\" passed to ActionManager::set_toggleaction_state()", n) << endmsg;
213 if (!set_toggleaction_state (n.substr (0, pos).c_str(), n.substr (pos+1).c_str(), s)) {
214 error << string_compose (_("Unknown action name: %1/%2"), n.substr (0, pos), n.substr (pos+1)) << endmsg;
219 ActionManager::set_toggleaction_state (const char* group_name, const char* action_name, bool s)
221 RefPtr<Action> act = get_action (group_name, action_name);
223 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
225 tact->set_active (s);
233 ActionManager::do_action (const char* group, const char*action)
235 Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (group, action);
242 ActionManager::set_toggle_action (const char* group, const char*action, bool yn)
244 Glib::RefPtr<Gtk::ToggleAction> tact = ActionManager::get_toggle_action (group, action);
245 tact->set_active (yn);
249 ActionManager::get_action (const string& name, bool or_die)
251 ActionMap::const_iterator a = actions.find (name);
253 if (a != actions.end()) {
258 throw MissingActionException (name);
261 cerr << "Failed to find action: [" << name << ']' << endl;
262 return RefPtr<Action>();
266 ActionManager::get_toggle_action (const string& name, bool or_die)
268 RefPtr<Action> act = get_action (name, or_die);
271 return RefPtr<ToggleAction>();
274 return Glib::RefPtr<ToggleAction>::cast_dynamic (act);
278 ActionManager::get_radio_action (const string& name, bool or_die)
280 RefPtr<Action> act = get_action (name, or_die);
283 return RefPtr<RadioAction>();
286 return Glib::RefPtr<RadioAction>::cast_dynamic (act);
290 ActionManager::get_action (char const * group_name, char const * action_name, bool or_die)
292 string fullpath (group_name);
294 fullpath += action_name;
296 ActionMap::const_iterator a = actions.find (fullpath);
298 if (a != actions.end()) {
303 throw MissingActionException (string_compose ("%1/%2", group_name, action_name));
306 cerr << "Failed to find action (2): [" << fullpath << ']' << endl;
307 PBD::stacktrace (std::cerr, 20);
308 return RefPtr<Action>();
312 ActionManager::get_toggle_action (char const * group_name, char const * action_name, bool or_die)
314 RefPtr<ToggleAction> act = Glib::RefPtr<ToggleAction>::cast_dynamic (get_action (group_name, action_name, or_die));
321 throw MissingActionException (string_compose ("%1/%2", group_name, action_name));
324 return RefPtr<ToggleAction>();
328 ActionManager::get_radio_action (char const * group_name, char const * action_name, bool or_die)
330 RefPtr<RadioAction> act = Glib::RefPtr<RadioAction>::cast_dynamic (get_action (group_name, action_name, or_die));
337 throw MissingActionException (string_compose ("%1/%2", group_name, action_name));
340 return RefPtr<RadioAction>();
344 ActionManager::create_action_group (void * owner, string const & name)
346 for (ActionGroups::iterator g = groups.begin(); g != groups.end(); ++g) {
347 if ((*g)->get_name () == name) {
352 RefPtr<ActionGroup> g = ActionGroup::create (name);
354 g->set_data (X_("owner"), owner);
355 groups.push_back (g);
357 /* this is one of the places where our own Action management code
358 has to touch the GTK one, because we want the GtkUIManager to
359 be able to create widgets (particularly Menus) from our actions.
361 This is a a necessary step for that to happen.
365 ActionManager::ui_manager->insert_action_group (g);
372 ActionManager::get_action_group (string const & name)
374 for (ActionGroups::iterator g = groups.begin(); g != groups.end(); ++g) {
375 if ((*g)->get_name () == name) {
380 return RefPtr<ActionGroup> ();
384 ActionManager::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
388 RefPtr<Action> act = Action::create (name, label);
390 fullpath = group->get_name();
394 if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
399 /* already registered */
400 return RefPtr<Action> ();
404 ActionManager::register_action (RefPtr<ActionGroup> group,
405 const char* name, const char* label,
410 RefPtr<Action> act = Action::create (name, label);
412 fullpath = group->get_name();
416 if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
417 group->add (act, sl);
421 /* already registered */
422 return RefPtr<Action>();
426 ActionManager::register_radio_action (RefPtr<ActionGroup> group,
427 Gtk::RadioAction::Group& rgroup,
428 const char* name, const char* label,
433 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
434 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
436 fullpath = group->get_name();
440 if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
441 group->add (act, sl);
445 /* already registered */
446 return RefPtr<Action>();
450 ActionManager::register_radio_action (RefPtr<ActionGroup> group,
451 Gtk::RadioAction::Group& rgroup,
452 const char* name, const char* label,
453 sigc::slot<void,GtkAction*> sl,
458 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
459 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
460 ract->property_value() = value;
462 fullpath = group->get_name();
466 if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
467 group->add (act, sigc::bind (sl, act->gobj()));
471 /* already registered */
473 return RefPtr<Action>();
477 ActionManager::register_toggle_action (RefPtr<ActionGroup> group,
478 const char* name, const char* label, sigc::slot<void> sl)
482 fullpath = group->get_name();
486 RefPtr<Action> act = ToggleAction::create (name, label);
488 if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
489 group->add (act, sl);
493 /* already registered */
494 return RefPtr<Action>();
498 ActionManager::get_actions (void* owner, std::vector<Glib::RefPtr<Gtk::Action> >& acts)
500 for (ActionMap::const_iterator a = actions.begin(); a != actions.end(); ++a) {
502 Glib::RefPtr<Gtk::ActionGroup> group = a->second->property_action_group ();
503 if (group->get_data (X_("owner")) == owner) {
504 acts.push_back (a->second);
507 acts.push_back (a->second);
513 ActionManager::get_all_actions (std::vector<std::string>& paths,
514 std::vector<std::string>& labels,
515 std::vector<std::string>& tooltips,
516 std::vector<std::string>& keys,
517 std::vector<RefPtr<Action> >& acts)
519 for (ActionMap::const_iterator a = actions.begin(); a != actions.end(); ++a) {
521 Glib::RefPtr<Action> act = a->second;
523 /* strip the GTK-added <Actions>/ from the front */
524 paths.push_back (act->get_accel_path().substr (10));
525 labels.push_back (act->get_label());
526 tooltips.push_back (act->get_tooltip());
527 acts.push_back (act);
529 /* foreach binding */
532 Bindings* bindings = (*map)->bindings();
537 Bindings::Operation op;
539 key = bindings->get_binding_for_action (*act, op);
541 if (key == KeyboardKey::null_key()) {
542 keys.push_back (string());
544 keys.push_back (key.display_label());
547 keys.push_back (string());
550 keys.push_back (string());