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"
41 #include "gtkmm2ext/actions.h"
42 #include "gtkmm2ext/utils.h"
51 using namespace Gtkmm2ext;
53 typedef std::map<std::string, Glib::RefPtr<Gtk::Action> > ActionMap;
54 static ActionMap actions;
55 typedef std::vector<Glib::RefPtr<Gtk::ActionGroup> > ActionGroups;
56 static ActionGroups groups;
58 RefPtr<UIManager> ActionManager::ui_manager;
59 string ActionManager::unbound_string = X_("--");
64 ActionState (GtkAction* a, bool s) : action (a), sensitive (s) {}
67 typedef std::vector<ActionState> ActionStates;
69 static ActionStates action_states_to_restore;
70 static bool actions_disabled = false;
73 ActionManager::init ()
75 ui_manager = UIManager::create ();
79 ActionManager::save_action_states ()
81 for (ActionGroups::iterator g = groups.begin(); g != groups.end(); ++g) {
83 /* the C++ API for functions used here appears to be broken in
84 gtkmm2.6, so we fall back to the C level.
87 GtkActionGroup* group = (*g)->gobj();
89 for (GList* acts = gtk_action_group_list_actions (group); acts; acts = g_list_next (acts)) {
90 GtkAction* action = (GtkAction*) acts->data;
91 action_states_to_restore.push_back (ActionState (action, gtk_action_get_sensitive (action)));
97 ActionManager::enable_active_actions ()
99 if (!actions_disabled) {
103 for (ActionStates::iterator i = action_states_to_restore.begin(); i != action_states_to_restore.end(); ++i) {
104 if ((*i).action && (*i).sensitive) {
105 gtk_action_set_sensitive ((*i).action, true);
109 action_states_to_restore.clear ();
110 actions_disabled = false;
114 ActionManager::disable_active_actions ()
116 if (actions_disabled == true ) {
119 // save all action's states to action_states_to_restore
120 save_action_states ();
122 // set all action's states disabled
123 for (ActionStates::iterator i = action_states_to_restore.begin(); i != action_states_to_restore.end(); ++i) {
124 if ((*i).sensitive) {
125 gtk_action_set_sensitive ((*i).action, false);
128 actions_disabled = true;
132 ActionManager::get_widget (const char * name)
134 return ui_manager->get_widget (name);
138 ActionManager::set_sensitive (vector<RefPtr<Action> >& actions, bool state)
140 // if actions weren't disabled
141 if (!actions_disabled) {
142 for (vector<RefPtr<Action> >::iterator i = actions.begin(); i != actions.end(); ++i) {
143 (*i)->set_sensitive (state);
147 // actions were disabled
148 // so we should just set necessary action's states in action_states_to_restore
149 for (vector<RefPtr<Action> >::iterator i = actions.begin(); i != actions.end(); ++i) {
150 // go through action_states_to_restore and set state of actions
151 for (ActionStates::iterator j = action_states_to_restore.begin(); j != action_states_to_restore.end(); ++j) {
152 // all actions should have their individual name, so we can use it for comparison
153 if (gtk_action_get_name ((*j).action) == (*i)->get_name ()) {
154 (*j).sensitive = state;
162 ActionManager::check_toggleaction (const string& n)
164 set_toggleaction_state (n, true);
168 ActionManager::uncheck_toggleaction (const string& n)
170 set_toggleaction_state (n, false);
174 ActionManager::set_toggleaction_state (const string& n, bool s)
176 char const * name = n.c_str ();
178 const char *last_slash = strrchr (name, '/');
180 if (last_slash == 0) {
181 fatal << string_compose ("programmer error: %1 %2", "illegal toggle action name", name) << endmsg;
182 abort(); /*NOTREACHED*/
186 /* 10 = strlen ("<Actions>/") */
187 size_t len = last_slash - (name + 10);
189 char* group_name = new char[len+1];
190 memcpy (group_name, name + 10, len);
191 group_name[len] = '\0';
193 const char* action_name = last_slash + 1;
194 if (!set_toggleaction_state (group_name, action_name, s)) {
195 error << string_compose (_("Unknown action name: %1/%2"), group_name, action_name) << endmsg;
198 delete [] group_name;
202 ActionManager::set_toggleaction_state (const char* group_name, const char* action_name, bool s)
204 RefPtr<Action> act = find_action (group_name, action_name);
206 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
208 tact->set_active (s);
216 ActionManager::do_action (const char* group, const char*action)
218 Glib::RefPtr<Gtk::Action> act = ActionManager::find_action (group, action);
225 ActionManager::set_toggle_action (const char* group, const char*action, bool yn)
227 Glib::RefPtr<Gtk::ToggleAction> tact = ActionManager::find_toggle_action (group, action);
228 tact->set_active (yn);
232 ActionManager::find_action (const string& name, bool or_die)
234 ActionMap::const_iterator a = actions.find (name);
236 if (a != actions.end()) {
244 cerr << "Failed to find action: [" << name << ']' << endl;
245 return RefPtr<Action>();
249 ActionManager::find_toggle_action (const string& name, bool or_die)
251 RefPtr<Action> act = find_action (name, or_die);
254 return RefPtr<ToggleAction>();
257 return Glib::RefPtr<ToggleAction>::cast_dynamic (act);
261 ActionManager::find_radio_action (const string& name, bool or_die)
263 RefPtr<Action> act = find_action (name, or_die);
266 return RefPtr<RadioAction>();
269 return Glib::RefPtr<RadioAction>::cast_dynamic (act);
273 ActionManager::find_action (char const * group_name, char const * action_name, bool or_die)
275 string fullpath (group_name);
277 fullpath += action_name;
279 ActionMap::const_iterator a = actions.find (fullpath);
281 if (a != actions.end()) {
289 cerr << "Failed to find action (2): [" << fullpath << ']' << endl;
290 return RefPtr<Action>();
294 ActionManager::find_toggle_action (char const * group_name, char const * action_name, bool or_die)
296 RefPtr<Action> act = find_action (group_name, action_name, or_die);
299 return RefPtr<ToggleAction>();
302 return Glib::RefPtr<ToggleAction>::cast_dynamic (act);
306 ActionManager::find_radio_action (char const * group_name, char const * action_name, bool or_die)
308 RefPtr<Action> act = find_action (group_name, action_name, or_die);
311 return RefPtr<RadioAction>();
314 return Glib::RefPtr<RadioAction>::cast_dynamic (act);
319 ActionManager::create_action_group (string const & name)
321 for (ActionGroups::iterator g = groups.begin(); g != groups.end(); ++g) {
322 if ((*g)->get_name () == name) {
327 RefPtr<ActionGroup> g = ActionGroup::create (name);
329 groups.push_back (g);
331 /* this is one of the places where our own Action management code
332 has to touch the GTK one, because we want the GtkUIManager to
333 be able to create widgets (particularly Menus) from our actions.
335 This is a a necessary step for that to happen.
339 ActionManager::ui_manager->insert_action_group (g);
346 ActionManager::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
350 RefPtr<Action> act = Action::create (name, label);
352 fullpath = group->get_name();
356 if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
361 /* already registered */
362 return RefPtr<Action> ();
366 ActionManager::register_action (RefPtr<ActionGroup> group,
367 const char* name, const char* label, sigc::slot<void> sl)
371 RefPtr<Action> act = Action::create (name, label);
373 fullpath = group->get_name();
377 if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
378 group->add (act, sl);
382 /* already registered */
383 return RefPtr<Action>();
387 ActionManager::register_radio_action (RefPtr<ActionGroup> group,
388 Gtk::RadioAction::Group& rgroup,
389 const char* name, const char* label,
394 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
395 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
397 fullpath = group->get_name();
401 if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
402 group->add (act, sl);
406 /* already registered */
407 return RefPtr<Action>();
411 ActionManager::register_radio_action (RefPtr<ActionGroup> group,
412 Gtk::RadioAction::Group& rgroup,
413 const char* name, const char* label,
414 sigc::slot<void,GtkAction*> sl,
419 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
420 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
421 ract->property_value() = value;
423 fullpath = group->get_name();
427 if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
428 group->add (act, sigc::bind (sl, act->gobj()));
432 /* already registered */
434 return RefPtr<Action>();
438 ActionManager::register_toggle_action (RefPtr<ActionGroup> group,
439 const char* name, const char* label, sigc::slot<void> sl)
443 fullpath = group->get_name();
447 RefPtr<Action> act = ToggleAction::create (name, label);
449 if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
450 group->add (act, sl);
454 /* already registered */
455 return RefPtr<Action>();
459 ActionManager::get_all_actions (std::vector<std::string>& paths,
460 std::vector<std::string>& labels,
461 std::vector<std::string>& tooltips,
462 std::vector<std::string>& keys,
463 std::vector<RefPtr<Action> >& acts)
465 for (ActionMap::const_iterator a = actions.begin(); a != actions.end(); ++a) {
467 Glib::RefPtr<Action> act = a->second;
469 paths.push_back (act->get_accel_path());
470 labels.push_back (act->get_label());
471 tooltips.push_back (act->get_tooltip());
472 acts.push_back (act);
474 /* foreach binding */
477 Bindings* bindings = (*map)->bindings();
482 Bindings::Operation op;
484 key = bindings->get_binding_for_action (*act, op);
486 if (key == KeyboardKey::null_key()) {
487 keys.push_back (string());
489 keys.push_back (key.display_label());
492 keys.push_back (string());