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