add ActionManager::set_sensitive() for Gtk::ActionGroup
[ardour.git] / libs / gtkmm2ext / actions.cc
1 /*
2     Copyright (C) 2005 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #include <cstring>
21 #include <vector>
22 #include <string>
23 #include <list>
24 #include <stack>
25 #include <stdint.h>
26
27 #include <boost/shared_ptr.hpp>
28
29 #include <gtk/gtkaccelmap.h>
30 #include <gtk/gtkuimanager.h>
31 #include <gtk/gtkactiongroup.h>
32
33 #include <gtkmm.h>
34 #include <gtkmm/accelmap.h>
35 #include <gtkmm/uimanager.h>
36
37 #include <glibmm/miscutils.h>
38
39 #include "pbd/error.h"
40
41 #include "gtkmm2ext/actions.h"
42 #include "gtkmm2ext/utils.h"
43
44 #include "pbd/i18n.h"
45
46 using namespace std;
47 using namespace Gtk;
48 using namespace Glib;
49 using namespace sigc;
50 using namespace PBD;
51 using namespace Gtkmm2ext;
52
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;
57
58 RefPtr<UIManager> ActionManager::ui_manager;
59 string ActionManager::unbound_string = X_("--");
60
61 struct ActionState {
62         GtkAction* action;
63         bool       sensitive;
64         ActionState (GtkAction* a, bool s) : action (a), sensitive (s) {}
65 };
66
67 typedef std::vector<ActionState> ActionStates;
68
69 static ActionStates action_states_to_restore;
70 static bool actions_disabled = false;
71
72 void
73 ActionManager::init ()
74 {
75         ui_manager = UIManager::create ();
76 }
77
78 void
79 ActionManager::save_action_states ()
80 {
81         for (ActionGroups::iterator g = groups.begin(); g != groups.end(); ++g) {
82
83                 /* the C++ API for functions used here appears to be broken in
84                    gtkmm2.6, so we fall back to the C level.
85                 */
86
87                 GtkActionGroup* group = (*g)->gobj();
88
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)));
92                 }
93         }
94 }
95
96 void
97 ActionManager::set_sensitive (Glib::RefPtr<ActionGroup> group, bool yn)
98 {
99         /* the C++ API for functions used here appears to be broken in
100            gtkmm2.6, so we fall back to the C level.
101         */
102
103         GtkActionGroup* grp = group->gobj();
104
105         for (GList* acts = gtk_action_group_list_actions (grp); acts; acts = g_list_next (acts)) {
106                 GtkAction* action = (GtkAction*) acts->data;
107                 gtk_action_set_sensitive (action, yn);
108         }
109 }
110
111 void
112 ActionManager::enable_active_actions ()
113 {
114         if (!actions_disabled) {
115                 return ;
116         }
117
118         for (ActionStates::iterator i = action_states_to_restore.begin(); i != action_states_to_restore.end(); ++i) {
119                 if ((*i).action && (*i).sensitive) {
120                         gtk_action_set_sensitive ((*i).action, true);
121                 }
122         }
123
124         action_states_to_restore.clear ();
125         actions_disabled = false;
126 }
127
128 void
129 ActionManager::disable_active_actions ()
130 {
131         if (actions_disabled == true ) {
132                 return ;
133         }
134         // save all action's states to action_states_to_restore
135         save_action_states ();
136
137         // set all action's states disabled
138         for (ActionStates::iterator i = action_states_to_restore.begin(); i != action_states_to_restore.end(); ++i) {
139                 if ((*i).sensitive) {
140                         gtk_action_set_sensitive ((*i).action, false);
141                 }
142         }
143         actions_disabled = true;
144 }
145
146 Widget*
147 ActionManager::get_widget (const char * name)
148 {
149         return ui_manager->get_widget (name);
150 }
151
152 void
153 ActionManager::set_sensitive (vector<RefPtr<Action> >& actions, bool state)
154 {
155         // if actions weren't disabled
156         if (!actions_disabled) {
157                 for (vector<RefPtr<Action> >::iterator i = actions.begin(); i != actions.end(); ++i) {
158                         (*i)->set_sensitive (state);
159                 }
160         }
161         else {
162                 // actions were disabled
163                 // so we should just set necessary action's states in action_states_to_restore
164                 for (vector<RefPtr<Action> >::iterator i = actions.begin(); i != actions.end(); ++i) {
165                         // go through action_states_to_restore and set state of actions
166                         for (ActionStates::iterator j = action_states_to_restore.begin(); j != action_states_to_restore.end(); ++j) {
167                                 // all actions should have their individual name, so we can use it for comparison
168                                 if (gtk_action_get_name ((*j).action) == (*i)->get_name ()) {
169                                         (*j).sensitive = state;
170                                 }
171                         }
172                 }
173         }
174 }
175
176 void
177 ActionManager::check_toggleaction (const string& n)
178 {
179         set_toggleaction_state (n, true);
180 }
181
182 void
183 ActionManager::uncheck_toggleaction (const string& n)
184 {
185         set_toggleaction_state (n, false);
186 }
187
188 void
189 ActionManager::set_toggleaction_state (const string& n, bool s)
190 {
191         char const * name = n.c_str ();
192
193         const char *last_slash = strrchr (name, '/');
194
195         if (last_slash == 0) {
196                 fatal << string_compose ("programmer error: %1 %2", "illegal toggle action name", name) << endmsg;
197                 abort(); /*NOTREACHED*/
198                 return;
199         }
200
201         /* 10 = strlen ("<Actions>/") */
202         size_t len = last_slash - (name + 10);
203
204         char* group_name = new char[len+1];
205         memcpy (group_name, name + 10, len);
206         group_name[len] = '\0';
207
208         const char* action_name = last_slash + 1;
209         if (!set_toggleaction_state (group_name, action_name, s)) {
210                 error << string_compose (_("Unknown action name: %1/%2"), group_name, action_name) << endmsg;
211         }
212
213         delete [] group_name;
214 }
215
216 bool
217 ActionManager::set_toggleaction_state (const char* group_name, const char* action_name, bool s)
218 {
219         RefPtr<Action> act = get_action (group_name, action_name);
220         if (act) {
221                 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
222                 if (tact) {
223                         tact->set_active (s);
224                         return true;
225                 }
226         }
227         return false;
228 }
229
230 void
231 ActionManager::do_action (const char* group, const char*action)
232 {
233         Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (group, action);
234         if (act) {
235                 act->activate ();
236         }
237 }
238
239 void
240 ActionManager::set_toggle_action (const char* group, const char*action, bool yn)
241 {
242         Glib::RefPtr<Gtk::ToggleAction> tact = ActionManager::get_toggle_action (group, action);
243         tact->set_active (yn);
244 }
245
246 RefPtr<Action>
247 ActionManager::get_action (const string& name, bool or_die)
248 {
249         ActionMap::const_iterator a = actions.find (name);
250
251         if (a != actions.end()) {
252                 return a->second;
253         }
254
255         if (or_die) {
256                 ::abort ();
257         }
258
259         cerr << "Failed to find action: [" << name << ']' << endl;
260         return RefPtr<Action>();
261 }
262
263 RefPtr<ToggleAction>
264 ActionManager::get_toggle_action (const string& name, bool or_die)
265 {
266         RefPtr<Action> act = get_action (name, or_die);
267
268         if (!act) {
269                 return RefPtr<ToggleAction>();
270         }
271
272         return Glib::RefPtr<ToggleAction>::cast_dynamic (act);
273 }
274
275 RefPtr<RadioAction>
276 ActionManager::get_radio_action (const string& name, bool or_die)
277 {
278         RefPtr<Action> act = get_action (name, or_die);
279
280         if (!act) {
281                 return RefPtr<RadioAction>();
282         }
283
284         return Glib::RefPtr<RadioAction>::cast_dynamic (act);
285 }
286
287 RefPtr<Action>
288 ActionManager::get_action (char const * group_name, char const * action_name, bool or_die)
289 {
290         string fullpath (group_name);
291         fullpath += '/';
292         fullpath += action_name;
293
294         ActionMap::const_iterator a = actions.find (fullpath);
295
296         if (a != actions.end()) {
297                 return a->second;
298         }
299
300         if (or_die) {
301                 ::abort ();
302         }
303
304         cerr << "Failed to find action (2): [" << fullpath << ']' << endl;
305         return RefPtr<Action>();
306 }
307
308 RefPtr<ToggleAction>
309 ActionManager::get_toggle_action (char const * group_name, char const * action_name, bool or_die)
310 {
311         RefPtr<Action> act = get_action (group_name, action_name, or_die);
312
313         if (!act) {
314                 return RefPtr<ToggleAction>();
315         }
316
317         return Glib::RefPtr<ToggleAction>::cast_dynamic (act);
318 }
319
320 RefPtr<RadioAction>
321 ActionManager::get_radio_action (char const * group_name, char const * action_name, bool or_die)
322 {
323         RefPtr<Action> act = get_action (group_name, action_name, or_die);
324
325         if (!act) {
326                 return RefPtr<RadioAction>();
327         }
328
329         return Glib::RefPtr<RadioAction>::cast_dynamic (act);
330 }
331
332
333 RefPtr<ActionGroup>
334 ActionManager::create_action_group (string const & name)
335 {
336         for (ActionGroups::iterator g = groups.begin(); g != groups.end(); ++g) {
337                 if ((*g)->get_name () == name) {
338                         return *g;
339                 }
340         }
341
342         RefPtr<ActionGroup> g = ActionGroup::create (name);
343
344         groups.push_back (g);
345
346         /* this is one of the places where our own Action management code
347            has to touch the GTK one, because we want the GtkUIManager to
348            be able to create widgets (particularly Menus) from our actions.
349
350            This is a a necessary step for that to happen.
351         */
352
353         if (g) {
354                 ActionManager::ui_manager->insert_action_group (g);
355         }
356
357         return g;
358 }
359
360 RefPtr<Action>
361 ActionManager::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
362 {
363         string fullpath;
364
365         RefPtr<Action> act = Action::create (name, label);
366
367         fullpath = group->get_name();
368         fullpath += '/';
369         fullpath += name;
370
371         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
372                 group->add (act);
373                 return act;
374         }
375
376         /* already registered */
377         return RefPtr<Action> ();
378 }
379
380 RefPtr<Action>
381 ActionManager::register_action (RefPtr<ActionGroup> group,
382                             const char* name, const char* label, sigc::slot<void> sl)
383 {
384         string fullpath;
385
386         RefPtr<Action> act = Action::create (name, label);
387
388         fullpath = group->get_name();
389         fullpath += '/';
390         fullpath += name;
391
392         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
393                 group->add (act, sl);
394                 return act;
395         }
396
397         /* already registered */
398         return RefPtr<Action>();
399 }
400
401 RefPtr<Action>
402 ActionManager::register_radio_action (RefPtr<ActionGroup> group,
403                                   Gtk::RadioAction::Group& rgroup,
404                                   const char* name, const char* label,
405                                   sigc::slot<void> sl)
406 {
407         string fullpath;
408
409         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
410         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
411
412         fullpath = group->get_name();
413         fullpath += '/';
414         fullpath += name;
415
416         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
417                 group->add (act, sl);
418                 return act;
419         }
420
421         /* already registered */
422         return RefPtr<Action>();
423 }
424
425 RefPtr<Action>
426 ActionManager::register_radio_action (RefPtr<ActionGroup> group,
427                                   Gtk::RadioAction::Group& rgroup,
428                                   const char* name, const char* label,
429                                   sigc::slot<void,GtkAction*> sl,
430                                   int value)
431 {
432         string fullpath;
433
434         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
435         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
436         ract->property_value() = value;
437
438         fullpath = group->get_name();
439         fullpath += '/';
440         fullpath += name;
441
442         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
443                 group->add (act, sigc::bind (sl, act->gobj()));
444                 return act;
445         }
446
447         /* already registered */
448
449         return RefPtr<Action>();
450 }
451
452 RefPtr<Action>
453 ActionManager::register_toggle_action (RefPtr<ActionGroup> group,
454                                    const char* name, const char* label, sigc::slot<void> sl)
455 {
456         string fullpath;
457
458         fullpath = group->get_name();
459         fullpath += '/';
460         fullpath += name;
461
462         RefPtr<Action> act = ToggleAction::create (name, label);
463
464         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
465                 group->add (act, sl);
466                 return act;
467         }
468
469         /* already registered */
470         return RefPtr<Action>();
471 }
472
473 void
474 ActionManager::get_all_actions (std::vector<std::string>& paths,
475                             std::vector<std::string>& labels,
476                             std::vector<std::string>& tooltips,
477                             std::vector<std::string>& keys,
478                             std::vector<RefPtr<Action> >& acts)
479 {
480         for (ActionMap::const_iterator a = actions.begin(); a != actions.end(); ++a) {
481
482                 Glib::RefPtr<Action> act = a->second;
483
484                         paths.push_back (act->get_accel_path());
485                         labels.push_back (act->get_label());
486                         tooltips.push_back (act->get_tooltip());
487                         acts.push_back (act);
488
489                         /* foreach binding */
490
491 #if 0
492                         Bindings* bindings = (*map)->bindings();
493
494                         if (bindings) {
495
496                                 KeyboardKey key;
497                                 Bindings::Operation op;
498
499                                 key = bindings->get_binding_for_action (*act, op);
500
501                                 if (key == KeyboardKey::null_key()) {
502                                         keys.push_back (string());
503                                 } else {
504                                         keys.push_back (key.display_label());
505                                 }
506                         } else {
507                                 keys.push_back (string());
508                         }
509                 }
510 #endif
511
512         }
513 }