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