add DEBUG::Destruction output for Port
[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<ActionGroup>
364 ActionManager::get_action_group (string const & name)
365 {
366         for (ActionGroups::iterator g = groups.begin(); g != groups.end(); ++g) {
367                 if ((*g)->get_name () == name) {
368                         return *g;
369                 }
370         }
371
372         return RefPtr<ActionGroup> ();
373 }
374
375 RefPtr<Action>
376 ActionManager::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
377 {
378         string fullpath;
379
380         RefPtr<Action> act = Action::create (name, label);
381
382         fullpath = group->get_name();
383         fullpath += '/';
384         fullpath += name;
385
386         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
387                 group->add (act);
388                 return act;
389         }
390
391         /* already registered */
392         return RefPtr<Action> ();
393 }
394
395 RefPtr<Action>
396 ActionManager::register_action (RefPtr<ActionGroup> group,
397                             const char* name, const char* label, sigc::slot<void> sl)
398 {
399         string fullpath;
400
401         RefPtr<Action> act = Action::create (name, label);
402
403         fullpath = group->get_name();
404         fullpath += '/';
405         fullpath += name;
406
407         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
408                 group->add (act, sl);
409                 return act;
410         }
411
412         /* already registered */
413         return RefPtr<Action>();
414 }
415
416 RefPtr<Action>
417 ActionManager::register_radio_action (RefPtr<ActionGroup> group,
418                                   Gtk::RadioAction::Group& rgroup,
419                                   const char* name, const char* label,
420                                   sigc::slot<void> sl)
421 {
422         string fullpath;
423
424         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
425         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
426
427         fullpath = group->get_name();
428         fullpath += '/';
429         fullpath += name;
430
431         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
432                 group->add (act, sl);
433                 return act;
434         }
435
436         /* already registered */
437         return RefPtr<Action>();
438 }
439
440 RefPtr<Action>
441 ActionManager::register_radio_action (RefPtr<ActionGroup> group,
442                                   Gtk::RadioAction::Group& rgroup,
443                                   const char* name, const char* label,
444                                   sigc::slot<void,GtkAction*> sl,
445                                   int value)
446 {
447         string fullpath;
448
449         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
450         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
451         ract->property_value() = value;
452
453         fullpath = group->get_name();
454         fullpath += '/';
455         fullpath += name;
456
457         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
458                 group->add (act, sigc::bind (sl, act->gobj()));
459                 return act;
460         }
461
462         /* already registered */
463
464         return RefPtr<Action>();
465 }
466
467 RefPtr<Action>
468 ActionManager::register_toggle_action (RefPtr<ActionGroup> group,
469                                    const char* name, const char* label, sigc::slot<void> sl)
470 {
471         string fullpath;
472
473         fullpath = group->get_name();
474         fullpath += '/';
475         fullpath += name;
476
477         RefPtr<Action> act = ToggleAction::create (name, label);
478
479         if (actions.insert (ActionMap::value_type (fullpath, act)).second) {
480                 group->add (act, sl);
481                 return act;
482         }
483
484         /* already registered */
485         return RefPtr<Action>();
486 }
487
488 void
489 ActionManager::get_actions (void* owner, std::vector<Glib::RefPtr<Gtk::Action> >& acts)
490 {
491         for (ActionMap::const_iterator a = actions.begin(); a != actions.end(); ++a) {
492                 if (owner) {
493                         Glib::RefPtr<Gtk::ActionGroup> group = a->second->property_action_group ();
494                         if (group->get_data (X_("owner")) == owner) {
495                                 acts.push_back (a->second);
496                         }
497                 } else {
498                         acts.push_back (a->second);
499                 }
500         }
501 }
502
503 void
504 ActionManager::get_all_actions (std::vector<std::string>& paths,
505                             std::vector<std::string>& labels,
506                             std::vector<std::string>& tooltips,
507                             std::vector<std::string>& keys,
508                             std::vector<RefPtr<Action> >& acts)
509 {
510         for (ActionMap::const_iterator a = actions.begin(); a != actions.end(); ++a) {
511
512                 Glib::RefPtr<Action> act = a->second;
513
514                         paths.push_back (act->get_accel_path());
515                         labels.push_back (act->get_label());
516                         tooltips.push_back (act->get_tooltip());
517                         acts.push_back (act);
518
519                         /* foreach binding */
520
521 #if 0
522                         Bindings* bindings = (*map)->bindings();
523
524                         if (bindings) {
525
526                                 KeyboardKey key;
527                                 Bindings::Operation op;
528
529                                 key = bindings->get_binding_for_action (*act, op);
530
531                                 if (key == KeyboardKey::null_key()) {
532                                         keys.push_back (string());
533                                 } else {
534                                         keys.push_back (key.display_label());
535                                 }
536                         } else {
537                                 keys.push_back (string());
538                         }
539                 }
540 #endif
541
542         }
543 }