workaround changes in glibmm 2.49.x
[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 "pbd/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 struct ActionState {
57         GtkAction* action;
58         bool       sensitive;
59         ActionState (GtkAction* a, bool s) : action (a), sensitive (s) {}
60 };
61
62 typedef std::vector<ActionState> ActionStates;
63
64 static ActionStates action_states_to_restore;
65 static bool actions_disabled = false;
66
67 void
68 ActionManager::save_action_states ()
69 {
70         /* the C++ API for functions used here appears to be broken in
71            gtkmm2.6, so we fall back to the C level.
72         */
73         GList* list = gtk_ui_manager_get_action_groups (ActionManager::ui_manager->gobj());
74         GList* node;
75         GList* acts;
76
77         for (node = list; node; node = g_list_next (node)) {
78
79                 GtkActionGroup* group = (GtkActionGroup*) node->data;
80
81                 for (acts = gtk_action_group_list_actions (group); acts; acts = g_list_next (acts)) {
82                         GtkAction* action = (GtkAction*) acts->data;
83                         action_states_to_restore.push_back (ActionState (action, gtk_action_get_sensitive (action)));
84                 }
85         }
86 }
87
88 void
89 ActionManager::enable_active_actions ()
90 {
91         if (!actions_disabled) {
92                 return ;
93         }
94
95         for (ActionStates::iterator i = action_states_to_restore.begin(); i != action_states_to_restore.end(); ++i) {
96                 if ((*i).action && (*i).sensitive) {
97                         gtk_action_set_sensitive ((*i).action, true);
98                 }
99         }
100
101         action_states_to_restore.clear ();
102         actions_disabled = false;
103 }
104
105 void
106 ActionManager::disable_active_actions ()
107 {
108         if (actions_disabled == true ) {
109                 return ;
110         }
111         // save all action's states to action_states_to_restore
112         save_action_states ();
113
114         // set all action's states disabled
115         for (ActionStates::iterator i = action_states_to_restore.begin(); i != action_states_to_restore.end(); ++i) {
116                 if ((*i).sensitive) {
117                         gtk_action_set_sensitive ((*i).action, false);
118                 }
119         }
120         actions_disabled = true;
121 }
122
123 Widget*
124 ActionManager::get_widget (const char * name)
125 {
126         return ui_manager->get_widget (name);
127 }
128
129 RefPtr<Action>
130 ActionManager::get_action (const char* path)
131 {
132         if (!path) {
133                 return RefPtr<Action>();
134         }
135
136         /* Skip <Actions>/ in path */
137
138         int len = strlen (path);
139
140         if (len < 3) {
141                 /* shortest possible path: "a/b" */
142                 return RefPtr<Action>();
143         }
144
145         if (len > 10 && !strncmp (path, "<Actions>/", 10 )) {
146                 path = path+10;
147         } else if (path[0] == '/') {
148                 path++;
149         }
150
151         vector<char> copy(len+1);
152         strcpy (&copy[0], path);
153         char* slash = strchr (&copy[0], '/');
154         if (!slash) {
155                 return RefPtr<Action> ();
156         }
157         *slash = '\0';
158
159         return get_action (&copy[0], ++slash);
160
161 }
162
163 RefPtr<Action>
164 ActionManager::get_action (const char* group_name, const char* action_name)
165 {
166         /* the C++ API for functions used here appears to be broken in
167            gtkmm2.6, so we fall back to the C level.
168         */
169
170         if (! ui_manager) {
171                 return RefPtr<Action> ();
172         }
173
174         GList* list = gtk_ui_manager_get_action_groups (ui_manager->gobj());
175         GList* node;
176         RefPtr<Action> act;
177
178         for (node = list; node; node = g_list_next (node)) {
179
180                 GtkActionGroup* _ag = (GtkActionGroup*) node->data;
181
182                 if (strcmp (group_name,  gtk_action_group_get_name (_ag)) == 0) {
183
184                         GtkAction* _act;
185
186                         if ((_act = gtk_action_group_get_action (_ag, action_name)) != 0) {
187                                 act = Glib::wrap (_act, true);
188                                 break;
189                         }
190
191                         break;
192                 }
193         }
194
195         return act;
196 }
197
198 void
199 ActionManager::set_sensitive (vector<RefPtr<Action> >& actions, bool state)
200 {
201         // if actions weren't disabled
202         if (!actions_disabled) {
203                 for (vector<RefPtr<Action> >::iterator i = actions.begin(); i != actions.end(); ++i) {
204                         (*i)->set_sensitive (state);
205                 }
206         }
207         else {
208                 // actions were disabled
209                 // so we should just set necessary action's states in action_states_to_restore
210                 for (vector<RefPtr<Action> >::iterator i = actions.begin(); i != actions.end(); ++i) {
211                         // go through action_states_to_restore and set state of actions
212                         for (ActionStates::iterator j = action_states_to_restore.begin(); j != action_states_to_restore.end(); ++j) {
213                                 // all actions should have their individual name, so we can use it for comparison
214                                 if (gtk_action_get_name ((*j).action) == (*i)->get_name ()) {
215                                         (*j).sensitive = state;
216                                 }
217                         }
218                 }
219         }
220 }
221
222 void
223 ActionManager::check_toggleaction (string n)
224 {
225         set_toggleaction_state (n, true);
226 }
227
228 void
229 ActionManager::uncheck_toggleaction (string n)
230 {
231         set_toggleaction_state (n, false);
232 }
233
234 void
235 ActionManager::set_toggleaction_state (string n, bool s)
236 {
237         char const * name = n.c_str ();
238
239         const char *last_slash = strrchr (name, '/');
240
241         if (last_slash == 0) {
242                 fatal << string_compose ("programmer error: %1 %2", "illegal toggle action name", name) << endmsg;
243                 abort(); /*NOTREACHED*/
244                 return;
245         }
246
247         /* 10 = strlen ("<Actions>/") */
248         size_t len = last_slash - (name + 10);
249
250         char* group_name = new char[len+1];
251         memcpy (group_name, name + 10, len);
252         group_name[len] = '\0';
253
254         const char* action_name = last_slash + 1;
255
256         RefPtr<Action> act = get_action (group_name, action_name);
257         if (act) {
258                 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
259                 tact->set_active (s);
260         } else {
261                 error << string_compose (_("Unknown action name: %1"),  name) << endmsg;
262         }
263
264         delete [] group_name;
265 }
266
267 void
268 ActionManager::do_action (const char* group, const char*action)
269 {
270         Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (group, action);
271         if (act) {
272                 act->activate ();
273         }
274 }
275
276 void
277 ActionManager::set_toggle_action (const char* group, const char*action, bool yn)
278 {
279         Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (group, action);
280         if (act) {
281                 Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
282                 if (tact) {
283                         tact->set_active (yn);
284                 }
285         }
286 }