Merge branch 'cairocanvas'
[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 "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 RefPtr<UIManager> ActionManager::ui_manager;
54 string ActionManager::unbound_string = "--";
55
56
57 RefPtr<Action>
58 ActionManager::register_action (RefPtr<ActionGroup> group, const char * name, const char * label, slot<void> sl)
59 {
60         RefPtr<Action> act;
61
62         act = Action::create (name, label);
63         group->add (act, sl);
64
65         return act;
66 }
67
68 RefPtr<Action>
69 ActionManager::register_action (RefPtr<ActionGroup> group, const char * name, const char * label)
70 {
71         RefPtr<Action> act;
72
73         act = Action::create (name, label);
74         group->add (act);
75
76         return act;
77 }
78
79
80 RefPtr<Action>
81 ActionManager::register_radio_action (RefPtr<ActionGroup> group, RadioAction::Group& rgroup, const char * name, const char * label, slot<void> sl)
82 {
83         RefPtr<Action> act;
84
85         act = RadioAction::create (rgroup, name, label);
86         group->add (act, sl);
87
88         return act;
89 }
90
91 RefPtr<Action>
92 ActionManager::register_radio_action (
93         RefPtr<ActionGroup> group, RadioAction::Group& rgroup, string const & name, string const & label, string const & tooltip, slot<void> sl
94         )
95 {
96         RefPtr<Action> act;
97
98         act = RadioAction::create (rgroup, name, label, tooltip);
99         group->add (act, sl);
100
101         return act;
102 }
103
104 RefPtr<Action>
105 ActionManager::register_toggle_action (RefPtr<ActionGroup> group, const char * name, const char * label, slot<void> sl)
106 {
107         RefPtr<Action> act;
108
109         act = ToggleAction::create (name, label);
110         group->add (act, sl);
111
112         return act;
113 }
114
115 RefPtr<Action>
116 ActionManager::register_toggle_action (RefPtr<ActionGroup> group, string const & name, string const & label, string const & tooltip, slot<void> sl)
117 {
118         RefPtr<Action> act;
119
120         act = ToggleAction::create (name, label, tooltip);
121         group->add (act, sl);
122
123         return act;
124 }
125
126 bool
127 ActionManager::lookup_entry (const ustring accel_path, Gtk::AccelKey& key)
128 {
129         GtkAccelKey gkey;
130         bool known = gtk_accel_map_lookup_entry (accel_path.c_str(), &gkey);
131
132         if (known) {
133                 key = AccelKey (gkey.accel_key, Gdk::ModifierType (gkey.accel_mods));
134         } else {
135                 key = AccelKey (GDK_VoidSymbol, Gdk::ModifierType (0));
136         }
137
138         return known;
139 }
140
141 struct SortActionsByLabel {
142     bool operator() (Glib::RefPtr<Gtk::Action> a, Glib::RefPtr<Gtk::Action> b) {
143             ustring astr = a->get_accel_path();
144             ustring bstr = b->get_accel_path();
145             return astr < bstr;
146     }
147 };
148
149 void
150 ActionManager::get_all_actions (vector<string>& groups, vector<string>& names, vector<string>& tooltips, vector<AccelKey>& bindings)
151 {
152         /* the C++ API for functions used here appears to be broken in
153            gtkmm2.6, so we fall back to the C level.
154         */
155
156         GList* list = gtk_ui_manager_get_action_groups (ui_manager->gobj());
157         GList* node;
158         GList* acts;
159
160         for (node = list; node; node = g_list_next (node)) {
161
162                 GtkActionGroup* group = (GtkActionGroup*) node->data;
163
164                 /* first pass: collect them all */
165
166                 typedef std::list<Glib::RefPtr<Gtk::Action> > action_list;
167                 action_list the_acts;
168
169                 for (acts = gtk_action_group_list_actions (group); acts; acts = g_list_next (acts)) {
170                         GtkAction* action = (GtkAction*) acts->data;
171                         the_acts.push_back (Glib::wrap (action, true));
172                 }
173
174                 /* now sort by label */
175
176                 SortActionsByLabel cmp;
177                 the_acts.sort (cmp);
178
179                 for (action_list::iterator a = the_acts.begin(); a != the_acts.end(); ++a) {
180
181                         string accel_path = (*a)->get_accel_path ();
182
183                         groups.push_back (gtk_action_group_get_name(group));
184                         names.push_back (accel_path.substr (accel_path.find_last_of ('/') + 1));
185                         tooltips.push_back ((*a)->get_tooltip ());
186
187                         AccelKey key;
188                         lookup_entry (accel_path, key);
189                         bindings.push_back (AccelKey (key.get_key(), Gdk::ModifierType (key.get_mod())));
190                 }
191         }
192 }
193
194 void
195 ActionManager::get_all_actions (vector<string>& names, vector<string>& paths, vector<string>& tooltips, vector<string>& keys, vector<AccelKey>& bindings)
196 {
197         /* the C++ API for functions used here appears to be broken in
198            gtkmm2.6, so we fall back to the C level.
199         */
200
201         GList* list = gtk_ui_manager_get_action_groups (ui_manager->gobj());
202         GList* node;
203         GList* acts;
204
205         for (node = list; node; node = g_list_next (node)) {
206
207                 GtkActionGroup* group = (GtkActionGroup*) node->data;
208
209                 /* first pass: collect them all */
210
211                 typedef std::list<Glib::RefPtr<Gtk::Action> > action_list;
212                 action_list the_acts;
213
214                 for (acts = gtk_action_group_list_actions (group); acts; acts = g_list_next (acts)) {
215                         GtkAction* action = (GtkAction*) acts->data;
216                         the_acts.push_back (Glib::wrap (action, true));
217                 }
218
219                 /* now sort by label */
220
221                 SortActionsByLabel cmp;
222                 the_acts.sort (cmp);
223
224                 for (action_list::iterator a = the_acts.begin(); a != the_acts.end(); ++a) {
225
226                         ustring const label = (*a)->property_label ();
227                         string const accel_path = (*a)->get_accel_path ();
228
229                         names.push_back (label);
230                         paths.push_back (accel_path);
231                         tooltips.push_back ((*a)->get_tooltip ());
232
233                         AccelKey key;
234                         keys.push_back (get_key_representation (accel_path, key));
235                         bindings.push_back (AccelKey (key.get_key(), Gdk::ModifierType (key.get_mod())));
236                 }
237         }
238 }
239
240 void
241 ActionManager::enable_accelerators ()
242 {
243         /* the C++ API for functions used here appears to be broken in
244            gtkmm2.6, so we fall back to the C level.
245         */
246
247         GList* list = gtk_ui_manager_get_action_groups (ui_manager->gobj());
248         GList* node;
249         GList* acts;
250         string ui_string = "<ui>";
251
252         /* get all actions, build a string describing them all as <accelerator
253          * action="name"/>
254          */
255
256         for (node = list; node; node = g_list_next (node)) {
257
258                 GtkActionGroup* group = (GtkActionGroup*) node->data;
259
260                 for (acts = gtk_action_group_list_actions (group); acts; acts = g_list_next (acts)) {
261                         ui_string += "<accelerator action=\"";
262                         
263                         /* OK, this is pretty stupid ... there is the full
264                          * accel path returned by gtk_action_get_accel_path ()
265                          * but of course the UIManager doesn't use that, but
266                          * just a name, which is the last component of the
267                          * path. What a totally ridiculous design.
268                          */
269
270                         string fullpath = gtk_action_get_accel_path ((GtkAction*) acts->data);
271
272                         ui_string += Glib::path_get_basename (fullpath);
273                         ui_string += "\"/>";
274                 }
275         }
276
277         ui_string += "</ui>";
278
279         /* and load it */
280
281         ui_manager->add_ui_from_string (ui_string);
282 }
283
284 struct ActionState {
285         GtkAction* action;
286         bool       sensitive;
287         ActionState (GtkAction* a, bool s) : action (a), sensitive (s) {}
288 };
289
290 typedef std::vector<ActionState> ActionStates;
291
292 static std::stack<boost::shared_ptr<ActionStates> > state_stack;
293
294 static boost::shared_ptr<ActionStates>
295 get_action_state ()
296 {
297         boost::shared_ptr<ActionStates> state = boost::shared_ptr<ActionStates>(new ActionStates);
298         
299         /* the C++ API for functions used here appears to be broken in
300            gtkmm2.6, so we fall back to the C level.
301         */
302
303         GList* list = gtk_ui_manager_get_action_groups (ActionManager::ui_manager->gobj());
304         GList* node;
305         GList* acts;
306
307         for (node = list; node; node = g_list_next (node)) {
308
309                 GtkActionGroup* group = (GtkActionGroup*) node->data;
310
311                 /* first pass: collect them all */
312
313                 typedef std::list<Glib::RefPtr<Gtk::Action> > action_list;
314                 action_list the_acts;
315
316                 for (acts = gtk_action_group_list_actions (group); acts; acts = g_list_next (acts)) {
317                         GtkAction* action = (GtkAction*) acts->data;
318
319                         state->push_back (ActionState (action, gtk_action_get_sensitive (action)));
320                 }
321         }
322         
323         return state;
324 }
325
326 void
327 ActionManager::push_action_state ()
328 {
329         state_stack.push (get_action_state());
330 }
331
332 void
333 ActionManager::pop_action_state ()
334 {
335         if (state_stack.empty()) {
336                 warning << string_compose (_("programming error: %1"), X_("ActionManager::pop_action_state called with empty stack")) << endmsg;
337                 return;
338         }
339
340         boost::shared_ptr<ActionStates> as = state_stack.top ();
341         state_stack.pop ();
342         
343         for (ActionStates::iterator i = as->begin(); i != as->end(); ++i) {
344                 gtk_action_set_sensitive ((*i).action, (*i).sensitive);
345         }
346 }
347
348 void
349 ActionManager::disable_all_actions ()
350 {
351         push_action_state ();
352         boost::shared_ptr<ActionStates> as = state_stack.top ();
353         
354         for (ActionStates::iterator i = as->begin(); i != as->end(); ++i) {
355                 gtk_action_set_sensitive ((*i).action, false);
356         }
357 }
358
359 void
360 ActionManager::add_action_group (RefPtr<ActionGroup> grp)
361 {
362         ui_manager->insert_action_group (grp);
363 }
364
365 Widget*
366 ActionManager::get_widget (const char * name)
367 {
368         return ui_manager->get_widget (name);
369 }
370
371 RefPtr<Action>
372 ActionManager::get_action (const char* path)
373 {
374         if (!path) {
375                 return RefPtr<Action>();
376         }
377
378         /* Skip <Actions>/ in path */
379
380         int len = strlen (path);
381
382         if (len < 3) {
383                 /* shortest possible path: "a/b" */
384                 return RefPtr<Action>();
385         }
386
387         if (len > 10 && !strncmp (path, "<Actions>/", 10 )) {
388                 path = path+10;
389         } else if (path[0] == '/') {
390                 path++;
391         }
392
393         vector<char> copy(len+1);
394         strcpy (&copy[0], path);
395         char* slash = strchr (&copy[0], '/');
396         if (!slash) {
397                 return RefPtr<Action> ();
398         }
399         *slash = '\0';
400
401         return get_action (&copy[0], ++slash);
402         
403 }
404
405 RefPtr<Action>
406 ActionManager::get_action (const char* group_name, const char* action_name)
407 {
408         /* the C++ API for functions used here appears to be broken in
409            gtkmm2.6, so we fall back to the C level.
410         */
411
412         if (ui_manager == 0) {
413                 return RefPtr<Action> ();
414         }
415
416         GList* list = gtk_ui_manager_get_action_groups (ui_manager->gobj());
417         GList* node;
418         RefPtr<Action> act;
419
420         for (node = list; node; node = g_list_next (node)) {
421
422                 GtkActionGroup* _ag = (GtkActionGroup*) node->data;
423
424                 if (strcmp (group_name,  gtk_action_group_get_name (_ag)) == 0) {
425
426                         GtkAction* _act;
427
428                         if ((_act = gtk_action_group_get_action (_ag, action_name)) != 0) {
429                                 act = Glib::wrap (_act, true);
430                                 break;
431                         }
432                 }
433         }
434
435         return act;
436 }
437
438 RefPtr<Action>
439 ActionManager::get_action_from_name (const char* name)
440 {
441         /* the C++ API for functions used here appears to be broken in
442            gtkmm2.6, so we fall back to the C level.
443         */
444
445         GList* list = gtk_ui_manager_get_action_groups (ui_manager->gobj());
446         GList* node;
447         GList* acts;
448
449         for (node = list; node; node = g_list_next (node)) {
450
451                 GtkActionGroup* group = (GtkActionGroup*) node->data;
452
453                 for (acts = gtk_action_group_list_actions (group); acts; acts = g_list_next (acts)) {
454                         GtkAction* action = (GtkAction*) acts->data;
455                         if (!strcmp (gtk_action_get_name (action), name)) {
456                                 return Glib::wrap (action, true);
457                         }
458                 }
459         }
460
461         return RefPtr<Action>();
462 }
463
464 void
465 ActionManager::set_sensitive (vector<RefPtr<Action> >& actions, bool state)
466 {
467         for (vector<RefPtr<Action> >::iterator i = actions.begin(); i != actions.end(); ++i) {
468                 (*i)->set_sensitive (state);
469         }
470 }
471
472 void
473 ActionManager::check_toggleaction (string n)
474 {
475         set_toggleaction_state (n, true);
476 }
477
478 void
479 ActionManager::uncheck_toggleaction (string n)
480 {
481         set_toggleaction_state (n, false);
482 }
483
484 void
485 ActionManager::set_toggleaction_state (string n, bool s)
486 {
487         char const * name = n.c_str ();
488         
489         const char *last_slash = strrchr (name, '/');
490
491         if (last_slash == 0) {
492                 fatal << string_compose ("programmer error: %1 %2", "illegal toggle action name", name) << endmsg;
493                 /*NOTREACHED*/
494                 return;
495         }
496
497         /* 10 = strlen ("<Actions>/") */
498         size_t len = last_slash - (name + 10);
499
500         char* group_name = new char[len+1];
501         memcpy (group_name, name + 10, len);
502         group_name[len] = '\0';
503
504         const char* action_name = last_slash + 1;
505
506         RefPtr<Action> act = get_action (group_name, action_name);
507         if (act) {
508                 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
509                 tact->set_active (s);
510         } else {
511                 error << string_compose (_("Unknown action name: %1"),  name) << endmsg;
512         }
513
514         delete [] group_name;
515 }
516
517 string
518 ActionManager::get_key_representation (const string& accel_path, AccelKey& key)
519 {
520         bool known = lookup_entry (accel_path, key);
521         
522         if (known) {
523                 uint32_t k = possibly_translate_legal_accelerator_to_real_key (key.get_key());
524                 key = AccelKey (k, Gdk::ModifierType (key.get_mod()));
525                 return ui_manager->get_accel_group()->get_label (key.get_key(), Gdk::ModifierType (key.get_mod()));
526         } 
527         
528         return unbound_string;
529 }
530
531 void
532 ActionManager::do_action (const char* group, const char*action)
533 {
534         Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (group, action);
535         if (act) {
536                 act->activate ();
537         }
538 }
539
540 void
541 ActionManager::set_toggle_action (const char* group, const char*action, bool yn)
542 {
543         Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (group, action);
544         if (act) {
545                 Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
546                 if (tact) {
547                         tact->set_active (yn);
548                 }
549         }
550 }
551