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