consolidate ActionMap and ActionManager APIs into a single namespace
[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::enable_active_actions ()
98 {
99         if (!actions_disabled) {
100                 return ;
101         }
102
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);
106                 }
107         }
108
109         action_states_to_restore.clear ();
110         actions_disabled = false;
111 }
112
113 void
114 ActionManager::disable_active_actions ()
115 {
116         if (actions_disabled == true ) {
117                 return ;
118         }
119         // save all action's states to action_states_to_restore
120         save_action_states ();
121
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);
126                 }
127         }
128         actions_disabled = true;
129 }
130
131 Widget*
132 ActionManager::get_widget (const char * name)
133 {
134         return ui_manager->get_widget (name);
135 }
136
137 void
138 ActionManager::set_sensitive (vector<RefPtr<Action> >& actions, bool state)
139 {
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);
144                 }
145         }
146         else {
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;
155                                 }
156                         }
157                 }
158         }
159 }
160
161 void
162 ActionManager::check_toggleaction (const string& n)
163 {
164         set_toggleaction_state (n, true);
165 }
166
167 void
168 ActionManager::uncheck_toggleaction (const string& n)
169 {
170         set_toggleaction_state (n, false);
171 }
172
173 void
174 ActionManager::set_toggleaction_state (const string& n, bool s)
175 {
176         char const * name = n.c_str ();
177
178         const char *last_slash = strrchr (name, '/');
179
180         if (last_slash == 0) {
181                 fatal << string_compose ("programmer error: %1 %2", "illegal toggle action name", name) << endmsg;
182                 abort(); /*NOTREACHED*/
183                 return;
184         }
185
186         /* 10 = strlen ("<Actions>/") */
187         size_t len = last_slash - (name + 10);
188
189         char* group_name = new char[len+1];
190         memcpy (group_name, name + 10, len);
191         group_name[len] = '\0';
192
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;
196         }
197
198         delete [] group_name;
199 }
200
201 bool
202 ActionManager::set_toggleaction_state (const char* group_name, const char* action_name, bool s)
203 {
204         RefPtr<Action> act = find_action (group_name, action_name);
205         if (act) {
206                 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
207                 if (tact) {
208                         tact->set_active (s);
209                         return true;
210                 }
211         }
212         return false;
213 }
214
215 void
216 ActionManager::do_action (const char* group, const char*action)
217 {
218         Glib::RefPtr<Gtk::Action> act = ActionManager::find_action (group, action);
219         if (act) {
220                 act->activate ();
221         }
222 }
223
224 void
225 ActionManager::set_toggle_action (const char* group, const char*action, bool yn)
226 {
227         Glib::RefPtr<Gtk::ToggleAction> tact = ActionManager::find_toggle_action (group, action);
228         tact->set_active (yn);
229 }
230
231 RefPtr<Action>
232 ActionManager::find_action (const string& name, bool or_die)
233 {
234         ActionMap::const_iterator a = actions.find (name);
235
236         if (a != actions.end()) {
237                 return a->second;
238         }
239
240         if (or_die) {
241                 ::abort ();
242         }
243
244         cerr << "Failed to find action: [" << name << ']' << endl;
245         return RefPtr<Action>();
246 }
247
248 RefPtr<ToggleAction>
249 ActionManager::find_toggle_action (const string& name, bool or_die)
250 {
251         RefPtr<Action> act = find_action (name, or_die);
252
253         if (!act) {
254                 return RefPtr<ToggleAction>();
255         }
256
257         return Glib::RefPtr<ToggleAction>::cast_dynamic (act);
258 }
259
260 RefPtr<RadioAction>
261 ActionManager::find_radio_action (const string& name, bool or_die)
262 {
263         RefPtr<Action> act = find_action (name, or_die);
264
265         if (!act) {
266                 return RefPtr<RadioAction>();
267         }
268
269         return Glib::RefPtr<RadioAction>::cast_dynamic (act);
270 }
271
272 RefPtr<Action>
273 ActionManager::find_action (char const * group_name, char const * action_name, bool or_die)
274 {
275         string fullpath (group_name);
276         fullpath += '/';
277         fullpath += action_name;
278
279         ActionMap::const_iterator a = actions.find (fullpath);
280
281         if (a != actions.end()) {
282                 return a->second;
283         }
284
285         if (or_die) {
286                 ::abort ();
287         }
288
289         cerr << "Failed to find action (2): [" << fullpath << ']' << endl;
290         return RefPtr<Action>();
291 }
292
293 RefPtr<ToggleAction>
294 ActionManager::find_toggle_action (char const * group_name, char const * action_name, bool or_die)
295 {
296         RefPtr<Action> act = find_action (group_name, action_name, or_die);
297
298         if (!act) {
299                 return RefPtr<ToggleAction>();
300         }
301
302         return Glib::RefPtr<ToggleAction>::cast_dynamic (act);
303 }
304
305 RefPtr<RadioAction>
306 ActionManager::find_radio_action (char const * group_name, char const * action_name, bool or_die)
307 {
308         RefPtr<Action> act = find_action (group_name, action_name, or_die);
309
310         if (!act) {
311                 return RefPtr<RadioAction>();
312         }
313
314         return Glib::RefPtr<RadioAction>::cast_dynamic (act);
315 }
316
317
318 RefPtr<ActionGroup>
319 ActionManager::create_action_group (string const & name)
320 {
321         for (ActionGroups::iterator g = groups.begin(); g != groups.end(); ++g) {
322                 if ((*g)->get_name () == name) {
323                         return *g;
324                 }
325         }
326
327         RefPtr<ActionGroup> g = ActionGroup::create (name);
328
329         groups.push_back (g);
330
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.
334
335            This is a a necessary step for that to happen.
336         */
337
338         if (g) {
339                 ActionManager::ui_manager->insert_action_group (g);
340         }
341
342         return g;
343 }
344
345 RefPtr<Action>
346 ActionManager::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
347 {
348         string fullpath;
349
350         RefPtr<Action> act = Action::create (name, label);
351
352         fullpath = group->get_name();
353         fullpath += '/';
354         fullpath += name;
355
356         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
357                 group->add (act);
358                 return act;
359         }
360
361         /* already registered */
362         return RefPtr<Action> ();
363 }
364
365 RefPtr<Action>
366 ActionManager::register_action (RefPtr<ActionGroup> group,
367                             const char* name, const char* label, sigc::slot<void> sl)
368 {
369         string fullpath;
370
371         RefPtr<Action> act = Action::create (name, label);
372
373         fullpath = group->get_name();
374         fullpath += '/';
375         fullpath += name;
376
377         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
378                 group->add (act, sl);
379                 return act;
380         }
381
382         /* already registered */
383         return RefPtr<Action>();
384 }
385
386 RefPtr<Action>
387 ActionManager::register_radio_action (RefPtr<ActionGroup> group,
388                                   Gtk::RadioAction::Group& rgroup,
389                                   const char* name, const char* label,
390                                   sigc::slot<void> sl)
391 {
392         string fullpath;
393
394         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
395         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
396
397         fullpath = group->get_name();
398         fullpath += '/';
399         fullpath += name;
400
401         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
402                 group->add (act, sl);
403                 return act;
404         }
405
406         /* already registered */
407         return RefPtr<Action>();
408 }
409
410 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,
415                                   int value)
416 {
417         string fullpath;
418
419         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
420         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
421         ract->property_value() = value;
422
423         fullpath = group->get_name();
424         fullpath += '/';
425         fullpath += name;
426
427         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
428                 group->add (act, sigc::bind (sl, act->gobj()));
429                 return act;
430         }
431
432         /* already registered */
433
434         return RefPtr<Action>();
435 }
436
437 RefPtr<Action>
438 ActionManager::register_toggle_action (RefPtr<ActionGroup> group,
439                                    const char* name, const char* label, sigc::slot<void> sl)
440 {
441         string fullpath;
442
443         fullpath = group->get_name();
444         fullpath += '/';
445         fullpath += name;
446
447         RefPtr<Action> act = ToggleAction::create (name, label);
448
449         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
450                 group->add (act, sl);
451                 return act;
452         }
453
454         /* already registered */
455         return RefPtr<Action>();
456 }
457
458 void
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)
464 {
465         for (ActionMap::const_iterator a = actions.begin(); a != actions.end(); ++a) {
466
467                 Glib::RefPtr<Action> act = a->second;
468
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);
473
474                         /* foreach binding */
475
476 #if 0
477                         Bindings* bindings = (*map)->bindings();
478
479                         if (bindings) {
480
481                                 KeyboardKey key;
482                                 Bindings::Operation op;
483
484                                 key = bindings->get_binding_for_action (*act, op);
485
486                                 if (key == KeyboardKey::null_key()) {
487                                         keys.push_back (string());
488                                 } else {
489                                         keys.push_back (key.display_label());
490                                 }
491                         } else {
492                                 keys.push_back (string());
493                         }
494                 }
495 #endif
496
497         }
498 }