ardour
actions.cc
Go to the documentation of this file.
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;
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>
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 
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
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 
296 {
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
328 {
329  state_stack.push (get_action_state());
330 }
331 
332 void
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
350 {
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>
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
474 {
475  set_toggleaction_state (n, true);
476 }
477 
478 void
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  abort(); /*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 
ActionState(GtkAction *a, bool s)
Definition: actions.cc:287
LIBPBD_API Transmitter fatal
LIBGTKMM2EXT_API Gtk::Widget * get_widget(const char *name)
Definition: actions.cc:366
LIBGTKMM2EXT_API void enable_accelerators()
Definition: actions.cc:241
bool lookup_entry(const string accel_path, Gtk::AccelKey &key)
Definition: mtest.cc:67
Definition: ardour_ui.h:130
LIBGTKMM2EXT_API Glib::RefPtr< Gtk::Action > get_action(const char *group, const char *name)
Definition: actions.cc:406
LIBGTKMM2EXT_API Glib::RefPtr< Gtk::Action > get_action_from_name(const char *name)
Definition: actions.cc:439
LIBGTKMM2EXT_API void set_toggle_action(const char *group, const char *name, bool)
Definition: actions.cc:541
LIBGTKMM2EXT_API void do_action(const char *group, const char *name)
Definition: actions.cc:532
Definition: Beats.hpp:239
LIBPBD_API Transmitter error
LIBPBD_API Transmitter warning
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
static Glib::RefPtr< UIManager > ui_manager
Definition: actions.cc:66
LIBGTKMM2EXT_API uint32_t possibly_translate_legal_accelerator_to_real_key(uint32_t keyval)
Definition: utils.cc:446
static std::stack< boost::shared_ptr< ActionStates > > state_stack
Definition: actions.cc:292
LIBGTKMM2EXT_API void disable_all_actions()
Definition: actions.cc:349
#define _(Text)
Definition: i18n.h:11
LIBGTKMM2EXT_API void set_sensitive(std::vector< Glib::RefPtr< Gtk::Action > > &actions, bool)
#define X_(Text)
Definition: i18n.h:13
bool sensitive
Definition: actions.cc:286
LIBGTKMM2EXT_API Glib::RefPtr< Gtk::Action > register_radio_action(Glib::RefPtr< Gtk::ActionGroup > group, Gtk::RadioAction::Group &, const char *name, const char *label, sigc::slot< void > sl, guint key, Gdk::ModifierType mods)
LIBGTKMM2EXT_API void uncheck_toggleaction(std::string)
GtkAction * action
Definition: actions.cc:285
LIBGTKMM2EXT_API void set_toggleaction_state(std::string, bool)
LIBGTKMM2EXT_API std::string unbound_string
Definition: actions.cc:54
LIBGTKMM2EXT_API void check_toggleaction(std::string)
LIBGTKMM2EXT_API Glib::RefPtr< Gtk::UIManager > ui_manager
Definition: actions.cc:53
const char * name
static boost::shared_ptr< ActionStates > get_action_state()
Definition: actions.cc:295
LIBGTKMM2EXT_API Glib::RefPtr< Gtk::Action > register_toggle_action(Glib::RefPtr< Gtk::ActionGroup > group, const char *name, const char *label, sigc::slot< void > sl, guint key, Gdk::ModifierType mods)
LIBGTKMM2EXT_API std::string get_key_representation(const std::string &accel_path, Gtk::AccelKey &key)
LIBGTKMM2EXT_API bool lookup_entry(const Glib::ustring accel_path, Gtk::AccelKey &key)
Definition: debug.h:30
LIBGTKMM2EXT_API void pop_action_state()
Definition: actions.cc:333
LIBGTKMM2EXT_API void get_all_actions(std::vector< std::string > &names, std::vector< std::string > &paths, std::vector< std::string > &tooltips, std::vector< std::string > &keys, std::vector< Gtk::AccelKey > &bindings)
LIBGTKMM2EXT_API void push_action_state()
Definition: actions.cc:327
LIBGTKMM2EXT_API void add_action_group(Glib::RefPtr< Gtk::ActionGroup >)
std::vector< ActionState > ActionStates
Definition: actions.cc:290
LIBGTKMM2EXT_API Glib::RefPtr< Gtk::Action > register_action(Glib::RefPtr< Gtk::ActionGroup > group, const char *name, const char *label)
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208