NO-OP: whitespace
[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<ToggleAction> act = Glib::RefPtr<ToggleAction>::cast_dynamic (get_action (group_name, action_name, or_die));
314
315         if (act) {
316                 return act;
317         }
318
319         if (or_die) {
320                 ::abort ();
321         }
322
323         return RefPtr<ToggleAction>();
324 }
325
326 RefPtr<RadioAction>
327 ActionManager::get_radio_action (char const * group_name, char const * action_name, bool or_die)
328 {
329         RefPtr<RadioAction> act = Glib::RefPtr<RadioAction>::cast_dynamic (get_action (group_name, action_name, or_die));
330
331         if (act) {
332                 return act;
333         }
334
335         if (or_die) {
336                 ::abort ();
337         }
338
339         return RefPtr<RadioAction>();
340 }
341
342 RefPtr<ActionGroup>
343 ActionManager::create_action_group (void * owner, string const & name)
344 {
345         for (ActionGroups::iterator g = groups.begin(); g != groups.end(); ++g) {
346                 if ((*g)->get_name () == name) {
347                         return *g;
348                 }
349         }
350
351         RefPtr<ActionGroup> g = ActionGroup::create (name);
352
353         g->set_data (X_("owner"), owner);
354         groups.push_back (g);
355
356         /* this is one of the places where our own Action management code
357            has to touch the GTK one, because we want the GtkUIManager to
358            be able to create widgets (particularly Menus) from our actions.
359
360            This is a a necessary step for that to happen.
361         */
362
363         if (g) {
364                 ActionManager::ui_manager->insert_action_group (g);
365         }
366
367         return g;
368 }
369
370 RefPtr<ActionGroup>
371 ActionManager::get_action_group (string const & name)
372 {
373         for (ActionGroups::iterator g = groups.begin(); g != groups.end(); ++g) {
374                 if ((*g)->get_name () == name) {
375                         return *g;
376                 }
377         }
378
379         return RefPtr<ActionGroup> ();
380 }
381
382 RefPtr<Action>
383 ActionManager::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
384 {
385         string fullpath;
386
387         RefPtr<Action> act = Action::create (name, label);
388
389         fullpath = group->get_name();
390         fullpath += '/';
391         fullpath += name;
392
393         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
394                 group->add (act);
395                 return act;
396         }
397
398         /* already registered */
399         return RefPtr<Action> ();
400 }
401
402 RefPtr<Action>
403 ActionManager::register_action (RefPtr<ActionGroup> group,
404                                 const char* name, const char* label,
405                                 sigc::slot<void> sl)
406 {
407         string fullpath;
408
409         RefPtr<Action> act = Action::create (name, label);
410
411         fullpath = group->get_name();
412         fullpath += '/';
413         fullpath += name;
414
415         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
416                 group->add (act, sl);
417                 return act;
418         }
419
420         /* already registered */
421         return RefPtr<Action>();
422 }
423
424 RefPtr<Action>
425 ActionManager::register_radio_action (RefPtr<ActionGroup> group,
426                                       Gtk::RadioAction::Group& rgroup,
427                                       const char* name, const char* label,
428                                       sigc::slot<void> sl)
429 {
430         string fullpath;
431
432         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
433         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
434
435         fullpath = group->get_name();
436         fullpath += '/';
437         fullpath += name;
438
439         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
440                 group->add (act, sl);
441                 return act;
442         }
443
444         /* already registered */
445         return RefPtr<Action>();
446 }
447
448 RefPtr<Action>
449 ActionManager::register_radio_action (RefPtr<ActionGroup> group,
450                                       Gtk::RadioAction::Group& rgroup,
451                                       const char* name, const char* label,
452                                       sigc::slot<void,GtkAction*> sl,
453                                       int value)
454 {
455         string fullpath;
456
457         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
458         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
459         ract->property_value() = value;
460
461         fullpath = group->get_name();
462         fullpath += '/';
463         fullpath += name;
464
465         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
466                 group->add (act, sigc::bind (sl, act->gobj()));
467                 return act;
468         }
469
470         /* already registered */
471
472         return RefPtr<Action>();
473 }
474
475 RefPtr<Action>
476 ActionManager::register_toggle_action (RefPtr<ActionGroup> group,
477                                    const char* name, const char* label, sigc::slot<void> sl)
478 {
479         string fullpath;
480
481         fullpath = group->get_name();
482         fullpath += '/';
483         fullpath += name;
484
485         RefPtr<Action> act = ToggleAction::create (name, label);
486
487         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
488                 group->add (act, sl);
489                 return act;
490         }
491
492         /* already registered */
493         return RefPtr<Action>();
494 }
495
496 void
497 ActionManager::get_actions (void* owner, std::vector<Glib::RefPtr<Gtk::Action> >& acts)
498 {
499         for (ActionMap::const_iterator a = actions.begin(); a != actions.end(); ++a) {
500                 if (owner) {
501                         Glib::RefPtr<Gtk::ActionGroup> group = a->second->property_action_group ();
502                         if (group->get_data (X_("owner")) == owner) {
503                                 acts.push_back (a->second);
504                         }
505                 } else {
506                         acts.push_back (a->second);
507                 }
508         }
509 }
510
511 void
512 ActionManager::get_all_actions (std::vector<std::string>& paths,
513                             std::vector<std::string>& labels,
514                             std::vector<std::string>& tooltips,
515                             std::vector<std::string>& keys,
516                             std::vector<RefPtr<Action> >& acts)
517 {
518         for (ActionMap::const_iterator a = actions.begin(); a != actions.end(); ++a) {
519
520                 Glib::RefPtr<Action> act = a->second;
521
522                         paths.push_back (act->get_accel_path());
523                         labels.push_back (act->get_label());
524                         tooltips.push_back (act->get_tooltip());
525                         acts.push_back (act);
526
527                         /* foreach binding */
528
529 #if 0
530                         Bindings* bindings = (*map)->bindings();
531
532                         if (bindings) {
533
534                                 KeyboardKey key;
535                                 Bindings::Operation op;
536
537                                 key = bindings->get_binding_for_action (*act, op);
538
539                                 if (key == KeyboardKey::null_key()) {
540                                         keys.push_back (string());
541                                 } else {
542                                         keys.push_back (key.display_label());
543                                 }
544                         } else {
545                                 keys.push_back (string());
546                         }
547                 }
548 #endif
549
550         }
551 }