2 * Copyright (C) 2009-2019 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2010-2012 Carl Hetherington <carl@carlh.net>
4 * Copyright (C) 2014-2019 Robin Gareus <robin@gareus.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include <boost/shared_ptr.hpp>
30 #include <gtk/gtkaccelmap.h>
31 #include <gtk/gtkuimanager.h>
32 #include <gtk/gtkactiongroup.h>
35 #include <gtkmm/accelmap.h>
36 #include <gtkmm/uimanager.h>
38 #include <glibmm/miscutils.h>
40 #include "pbd/error.h"
41 #include "pbd/stacktrace.h"
43 #include "gtkmm2ext/actions.h"
44 #include "gtkmm2ext/utils.h"
53 using namespace Gtkmm2ext;
55 typedef std::map<std::string, Glib::RefPtr<Gtk::Action> > ActionMap;
56 static ActionMap actions;
57 typedef std::vector<Glib::RefPtr<Gtk::ActionGroup> > ActionGroups;
58 static ActionGroups groups;
60 RefPtr<UIManager> ActionManager::ui_manager;
61 string ActionManager::unbound_string = X_("--");
66 ActionState (GtkAction* a, bool s) : action (a), sensitive (s) {}
69 typedef std::vector<ActionState> ActionStates;
71 static ActionStates action_states_to_restore;
72 static bool actions_disabled = false;
75 ActionManager::MissingActionException::MissingActionException (std::string const & str)
76 : missing_action_name (str)
78 std::cerr << "MAE: " << str << std::endl;
82 ActionManager::MissingActionException::what () const throw ()
85 return strdup (string_compose ("missing action: %1", missing_action_name).c_str());
89 ActionManager::init ()
91 ui_manager = UIManager::create ();
95 ActionManager::save_action_states ()
97 for (ActionGroups::iterator g = groups.begin(); g != groups.end(); ++g) {
99 /* the C++ API for functions used here appears to be broken in
100 gtkmm2.6, so we fall back to the C level.
103 GtkActionGroup* group = (*g)->gobj();
105 for (GList* acts = gtk_action_group_list_actions (group); acts; acts = g_list_next (acts)) {
106 GtkAction* action = (GtkAction*) acts->data;
107 action_states_to_restore.push_back (ActionState (action, gtk_action_get_sensitive (action)));
113 ActionManager::set_sensitive (Glib::RefPtr<ActionGroup> group, bool yn)
115 /* the C++ API for functions used here appears to be broken in
116 gtkmm2.6, so we fall back to the C level.
119 GtkActionGroup* grp = group->gobj();
121 for (GList* acts = gtk_action_group_list_actions (grp); acts; acts = g_list_next (acts)) {
122 GtkAction* action = (GtkAction*) acts->data;
123 gtk_action_set_sensitive (action, yn);
128 ActionManager::enable_active_actions ()
130 if (!actions_disabled) {
134 for (ActionStates::iterator i = action_states_to_restore.begin(); i != action_states_to_restore.end(); ++i) {
135 if ((*i).action && (*i).sensitive) {
136 gtk_action_set_sensitive ((*i).action, true);
140 action_states_to_restore.clear ();
141 actions_disabled = false;
145 ActionManager::disable_active_actions ()
147 if (actions_disabled == true ) {
150 // save all action's states to action_states_to_restore
151 save_action_states ();
153 // set all action's states disabled
154 for (ActionStates::iterator i = action_states_to_restore.begin(); i != action_states_to_restore.end(); ++i) {
155 if ((*i).sensitive) {
156 gtk_action_set_sensitive ((*i).action, false);
159 actions_disabled = true;
163 ActionManager::get_widget (const char * name)
165 return ui_manager->get_widget (name);
169 ActionManager::set_sensitive (vector<RefPtr<Action> >& actions, bool state)
171 // if actions weren't disabled
172 if (!actions_disabled) {
173 for (vector<RefPtr<Action> >::iterator i = actions.begin(); i != actions.end(); ++i) {
174 (*i)->set_sensitive (state);
178 // actions were disabled
179 // so we should just set necessary action's states in action_states_to_restore
180 for (vector<RefPtr<Action> >::iterator i = actions.begin(); i != actions.end(); ++i) {
181 // go through action_states_to_restore and set state of actions
182 for (ActionStates::iterator j = action_states_to_restore.begin(); j != action_states_to_restore.end(); ++j) {
183 // all actions should have their individual name, so we can use it for comparison
184 if (gtk_action_get_name ((*j).action) == (*i)->get_name ()) {
185 (*j).sensitive = state;
193 ActionManager::check_toggleaction (const string& n)
195 set_toggleaction_state (n, true);
199 ActionManager::uncheck_toggleaction (const string& n)
201 set_toggleaction_state (n, false);
205 ActionManager::set_toggleaction_state (const string& n, bool s)
207 string::size_type pos = n.find ('/');
209 if (pos == string::npos || pos == n.size() - 1) {
210 error << string_compose ("illegal action name \"%1\" passed to ActionManager::set_toggleaction_state()", n) << endmsg;
214 if (!set_toggleaction_state (n.substr (0, pos).c_str(), n.substr (pos+1).c_str(), s)) {
215 error << string_compose (_("Unknown action name: %1/%2"), n.substr (0, pos), n.substr (pos+1)) << endmsg;
220 ActionManager::set_toggleaction_state (const char* group_name, const char* action_name, bool s)
222 RefPtr<Action> act = get_action (group_name, action_name);
224 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
226 tact->set_active (s);
234 ActionManager::do_action (const char* group, const char*action)
236 Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (group, action);
243 ActionManager::set_toggle_action (const char* group, const char*action, bool yn)
245 Glib::RefPtr<Gtk::ToggleAction> tact = ActionManager::get_toggle_action (group, action);
246 tact->set_active (yn);
250 ActionManager::get_action (const string& name, bool or_die)
252 ActionMap::const_iterator a = actions.find (name);
254 if (a != actions.end()) {
259 throw MissingActionException (name);
262 cerr << "Failed to find action: [" << name << ']' << endl;
263 return RefPtr<Action>();
267 ActionManager::get_toggle_action (const string& name, bool or_die)
269 RefPtr<Action> act = get_action (name, or_die);
272 return RefPtr<ToggleAction>();
275 return Glib::RefPtr<ToggleAction>::cast_dynamic (act);
279 ActionManager::get_radio_action (const string& name, bool or_die)
281 RefPtr<Action> act = get_action (name, or_die);
284 return RefPtr<RadioAction>();
287 return Glib::RefPtr<RadioAction>::cast_dynamic (act);
291 ActionManager::get_action (char const * group_name, char const * action_name, bool or_die)
293 string fullpath (group_name);
295 fullpath += action_name;
297 ActionMap::const_iterator a = actions.find (fullpath);
299 if (a != actions.end()) {
304 throw MissingActionException (string_compose ("%1/%2", group_name, action_name));
307 cerr << "Failed to find action (2): [" << fullpath << ']' << endl;
308 PBD::stacktrace (std::cerr, 20);
309 return RefPtr<Action>();
313 ActionManager::get_toggle_action (char const * group_name, char const * action_name, bool or_die)
315 RefPtr<ToggleAction> act = Glib::RefPtr<ToggleAction>::cast_dynamic (get_action (group_name, action_name, or_die));
322 throw MissingActionException (string_compose ("%1/%2", group_name, action_name));
325 return RefPtr<ToggleAction>();
329 ActionManager::get_radio_action (char const * group_name, char const * action_name, bool or_die)
331 RefPtr<RadioAction> act = Glib::RefPtr<RadioAction>::cast_dynamic (get_action (group_name, action_name, or_die));
338 throw MissingActionException (string_compose ("%1/%2", group_name, action_name));
341 return RefPtr<RadioAction>();
345 ActionManager::create_action_group (void * owner, string const & name)
347 for (ActionGroups::iterator g = groups.begin(); g != groups.end(); ++g) {
348 if ((*g)->get_name () == name) {
353 RefPtr<ActionGroup> g = ActionGroup::create (name);
355 g->set_data (X_("owner"), owner);
356 groups.push_back (g);
358 /* this is one of the places where our own Action management code
359 has to touch the GTK one, because we want the GtkUIManager to
360 be able to create widgets (particularly Menus) from our actions.
362 This is a a necessary step for that to happen.
366 ActionManager::ui_manager->insert_action_group (g);
373 ActionManager::get_action_group (string const & name)
375 for (ActionGroups::iterator g = groups.begin(); g != groups.end(); ++g) {
376 if ((*g)->get_name () == name) {
381 return RefPtr<ActionGroup> ();
385 ActionManager::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
389 RefPtr<Action> act = Action::create (name, label);
391 fullpath = group->get_name();
395 if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
400 /* already registered */
401 return RefPtr<Action> ();
405 ActionManager::register_action (RefPtr<ActionGroup> group,
406 const char* name, const char* label,
411 RefPtr<Action> act = Action::create (name, label);
413 fullpath = group->get_name();
417 if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
418 group->add (act, sl);
422 /* already registered */
423 return RefPtr<Action>();
427 ActionManager::register_radio_action (RefPtr<ActionGroup> group,
428 Gtk::RadioAction::Group& rgroup,
429 const char* name, const char* label,
434 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
435 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
437 fullpath = group->get_name();
441 if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
442 group->add (act, sl);
446 /* already registered */
447 return RefPtr<Action>();
451 ActionManager::register_radio_action (RefPtr<ActionGroup> group,
452 Gtk::RadioAction::Group& rgroup,
453 const char* name, const char* label,
454 sigc::slot<void,GtkAction*> sl,
459 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
460 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
461 ract->property_value() = value;
463 fullpath = group->get_name();
467 if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
468 group->add (act, sigc::bind (sl, act->gobj()));
472 /* already registered */
474 return RefPtr<Action>();
478 ActionManager::register_toggle_action (RefPtr<ActionGroup> group,
479 const char* name, const char* label, sigc::slot<void> sl)
483 fullpath = group->get_name();
487 RefPtr<Action> act = ToggleAction::create (name, label);
489 if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
490 group->add (act, sl);
494 /* already registered */
495 return RefPtr<Action>();
499 ActionManager::get_actions (void* owner, std::vector<Glib::RefPtr<Gtk::Action> >& acts)
501 for (ActionMap::const_iterator a = actions.begin(); a != actions.end(); ++a) {
503 Glib::RefPtr<Gtk::ActionGroup> group = a->second->property_action_group ();
504 if (group->get_data (X_("owner")) == owner) {
505 acts.push_back (a->second);
508 acts.push_back (a->second);
514 ActionManager::get_all_actions (std::vector<std::string>& paths,
515 std::vector<std::string>& labels,
516 std::vector<std::string>& tooltips,
517 std::vector<std::string>& keys,
518 std::vector<RefPtr<Action> >& acts)
520 for (ActionMap::const_iterator a = actions.begin(); a != actions.end(); ++a) {
522 Glib::RefPtr<Action> act = a->second;
524 /* strip the GTK-added <Actions>/ from the front */
525 paths.push_back (act->get_accel_path().substr (10));
526 labels.push_back (act->get_label());
527 tooltips.push_back (act->get_tooltip());
528 acts.push_back (act);
530 /* foreach binding */
533 Bindings* bindings = (*map)->bindings();
538 Bindings::Operation op;
540 key = bindings->get_binding_for_action (*act, op);
542 if (key == KeyboardKey::null_key()) {
543 keys.push_back (string());
545 keys.push_back (key.display_label());
548 keys.push_back (string());
551 keys.push_back (string());