1 /*
2 * Copyright (c) 2014 Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library 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 GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19 #include <glib/gi18n-lib.h>
20
21 #include "actions.h"
22 #include "action-editor.h"
23
24 #include "gtkapplication.h"
25 #include "gtkapplicationwindow.h"
26 #include "gtktreeview.h"
27 #include "gtkliststore.h"
28 #include "gtkwidgetprivate.h"
29 #include "gtkpopover.h"
30 #include "gtklabel.h"
31
32 enum
33 {
34 COLUMN_PREFIX,
35 COLUMN_NAME,
36 COLUMN_ENABLED,
37 COLUMN_PARAMETER,
38 COLUMN_STATE,
39 COLUMN_GROUP
40 };
41
42 struct _GtkInspectorActionsPrivate
43 {
44 GtkListStore *model;
45 GHashTable *groups;
46 GHashTable *iters;
47 };
48
G_DEFINE_TYPE_WITH_PRIVATE(GtkInspectorActions,gtk_inspector_actions,GTK_TYPE_BOX)49 G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorActions, gtk_inspector_actions, GTK_TYPE_BOX)
50
51 static void
52 gtk_inspector_actions_init (GtkInspectorActions *sl)
53 {
54 sl->priv = gtk_inspector_actions_get_instance_private (sl);
55 sl->priv->iters = g_hash_table_new_full (g_str_hash,
56 g_str_equal,
57 g_free,
58 (GDestroyNotify) gtk_tree_iter_free);
59 sl->priv->groups = g_hash_table_new_full (g_direct_hash,
60 g_direct_equal,
61 NULL,
62 g_free);
63 gtk_widget_init_template (GTK_WIDGET (sl));
64 }
65
66 static void
add_action(GtkInspectorActions * sl,GActionGroup * group,const gchar * prefix,const gchar * name)67 add_action (GtkInspectorActions *sl,
68 GActionGroup *group,
69 const gchar *prefix,
70 const gchar *name)
71 {
72 GtkTreeIter iter;
73 gboolean enabled;
74 const gchar *parameter;
75 GVariant *state;
76 gchar *state_string;
77
78 enabled = g_action_group_get_action_enabled (group, name);
79 parameter = (const gchar *)g_action_group_get_action_parameter_type (group, name);
80 state = g_action_group_get_action_state (group, name);
81 if (state)
82 state_string = g_variant_print (state, FALSE);
83 else
84 state_string = g_strdup ("");
85 gtk_list_store_append (sl->priv->model, &iter);
86 gtk_list_store_set (sl->priv->model, &iter,
87 COLUMN_PREFIX, prefix,
88 COLUMN_NAME, name,
89 COLUMN_ENABLED, enabled,
90 COLUMN_PARAMETER, parameter,
91 COLUMN_STATE, state_string,
92 COLUMN_GROUP, group,
93 -1);
94 g_hash_table_insert (sl->priv->iters,
95 g_strconcat (prefix, ".", name, NULL),
96 gtk_tree_iter_copy (&iter));
97 g_free (state_string);
98 }
99
100 static void
action_added_cb(GActionGroup * group,const gchar * action_name,GtkInspectorActions * sl)101 action_added_cb (GActionGroup *group,
102 const gchar *action_name,
103 GtkInspectorActions *sl)
104 {
105 const gchar *prefix;
106 prefix = g_hash_table_lookup (sl->priv->groups, group);
107 add_action (sl, group, prefix, action_name);
108 }
109
110 static void
action_removed_cb(GActionGroup * group,const gchar * action_name,GtkInspectorActions * sl)111 action_removed_cb (GActionGroup *group,
112 const gchar *action_name,
113 GtkInspectorActions *sl)
114 {
115 const gchar *prefix;
116 gchar *key;
117 GtkTreeIter *iter;
118 prefix = g_hash_table_lookup (sl->priv->groups, group);
119 key = g_strconcat (prefix, ".", action_name, NULL);
120 iter = g_hash_table_lookup (sl->priv->iters, key);
121 gtk_list_store_remove (sl->priv->model, iter);
122 g_hash_table_remove (sl->priv->iters, key);
123 g_free (key);
124 }
125
126 static void
action_enabled_changed_cb(GActionGroup * group,const gchar * action_name,gboolean enabled,GtkInspectorActions * sl)127 action_enabled_changed_cb (GActionGroup *group,
128 const gchar *action_name,
129 gboolean enabled,
130 GtkInspectorActions *sl)
131 {
132 const gchar *prefix;
133 gchar *key;
134 GtkTreeIter *iter;
135 prefix = g_hash_table_lookup (sl->priv->groups, group);
136 key = g_strconcat (prefix, ".", action_name, NULL);
137 iter = g_hash_table_lookup (sl->priv->iters, key);
138 gtk_list_store_set (sl->priv->model, iter,
139 COLUMN_ENABLED, enabled,
140 -1);
141 g_free (key);
142 }
143
144 static void
action_state_changed_cb(GActionGroup * group,const gchar * action_name,GVariant * state,GtkInspectorActions * sl)145 action_state_changed_cb (GActionGroup *group,
146 const gchar *action_name,
147 GVariant *state,
148 GtkInspectorActions *sl)
149 {
150 const gchar *prefix;
151 gchar *key;
152 GtkTreeIter *iter;
153 gchar *state_string;
154 prefix = g_hash_table_lookup (sl->priv->groups, group);
155 key = g_strconcat (prefix, ".", action_name, NULL);
156 iter = g_hash_table_lookup (sl->priv->iters, key);
157 if (state)
158 state_string = g_variant_print (state, FALSE);
159 else
160 state_string = g_strdup ("");
161 gtk_list_store_set (sl->priv->model, iter,
162 COLUMN_STATE, state_string,
163 -1);
164 g_free (state_string);
165 g_free (key);
166 }
167
168 static void
add_group(GtkInspectorActions * sl,GActionGroup * group,const gchar * prefix)169 add_group (GtkInspectorActions *sl,
170 GActionGroup *group,
171 const gchar *prefix)
172 {
173 gint i;
174 gchar **names;
175
176 gtk_widget_show (GTK_WIDGET (sl));
177
178 g_signal_connect (group, "action-added", G_CALLBACK (action_added_cb), sl);
179 g_signal_connect (group, "action-removed", G_CALLBACK (action_removed_cb), sl);
180 g_signal_connect (group, "action-enabled-changed", G_CALLBACK (action_enabled_changed_cb), sl);
181 g_signal_connect (group, "action-state-changed", G_CALLBACK (action_state_changed_cb), sl);
182 g_hash_table_insert (sl->priv->groups, group, g_strdup (prefix));
183
184 names = g_action_group_list_actions (group);
185 for (i = 0; names[i]; i++)
186 add_action (sl, group, prefix, names[i]);
187 g_strfreev (names);
188 }
189
190 static void
disconnect_group(gpointer key,gpointer value,gpointer data)191 disconnect_group (gpointer key, gpointer value, gpointer data)
192 {
193 GActionGroup *group = key;
194 GtkInspectorActions *sl = data;
195
196 g_signal_handlers_disconnect_by_func (group, action_added_cb, sl);
197 g_signal_handlers_disconnect_by_func (group, action_removed_cb, sl);
198 g_signal_handlers_disconnect_by_func (group, action_enabled_changed_cb, sl);
199 g_signal_handlers_disconnect_by_func (group, action_state_changed_cb, sl);
200 }
201
202 void
gtk_inspector_actions_set_object(GtkInspectorActions * sl,GObject * object)203 gtk_inspector_actions_set_object (GtkInspectorActions *sl,
204 GObject *object)
205 {
206 gtk_widget_hide (GTK_WIDGET (sl));
207 g_hash_table_foreach (sl->priv->groups, disconnect_group, sl);
208 g_hash_table_remove_all (sl->priv->groups);
209 g_hash_table_remove_all (sl->priv->iters);
210 gtk_list_store_clear (sl->priv->model);
211
212 if (GTK_IS_APPLICATION (object))
213 add_group (sl, G_ACTION_GROUP (object), "app");
214 else if (GTK_IS_APPLICATION_WINDOW (object))
215 add_group (sl, G_ACTION_GROUP (object), "win");
216 else if (GTK_IS_WIDGET (object))
217 {
218 const gchar **prefixes;
219 GActionGroup *group;
220 gint i;
221
222 prefixes = gtk_widget_list_action_prefixes (GTK_WIDGET (object));
223 if (prefixes)
224 {
225 for (i = 0; prefixes[i]; i++)
226 {
227 group = gtk_widget_get_action_group (GTK_WIDGET (object), prefixes[i]);
228 add_group (sl, group, prefixes[i]);
229 }
230 g_free (prefixes);
231 }
232 }
233 }
234
235 static void
row_activated(GtkTreeView * tv,GtkTreePath * path,GtkTreeViewColumn * col,GtkInspectorActions * sl)236 row_activated (GtkTreeView *tv,
237 GtkTreePath *path,
238 GtkTreeViewColumn *col,
239 GtkInspectorActions *sl)
240 {
241 GtkTreeIter iter;
242 GdkRectangle rect;
243 GtkWidget *popover;
244 gchar *prefix;
245 gchar *name;
246 GActionGroup *group;
247 GtkWidget *editor;
248
249 gtk_tree_model_get_iter (GTK_TREE_MODEL (sl->priv->model), &iter, path);
250 gtk_tree_model_get (GTK_TREE_MODEL (sl->priv->model),
251 &iter,
252 COLUMN_PREFIX, &prefix,
253 COLUMN_NAME, &name,
254 COLUMN_GROUP, &group,
255 -1);
256
257 gtk_tree_model_get_iter (GTK_TREE_MODEL (sl->priv->model), &iter, path);
258 gtk_tree_view_get_cell_area (tv, path, col, &rect);
259 gtk_tree_view_convert_bin_window_to_widget_coords (tv, rect.x, rect.y, &rect.x, &rect.y);
260
261 popover = gtk_popover_new (GTK_WIDGET (tv));
262 gtk_popover_set_pointing_to (GTK_POPOVER (popover), &rect);
263
264 editor = gtk_inspector_action_editor_new (group, prefix, name);
265 gtk_container_add (GTK_CONTAINER (popover), editor);
266 gtk_popover_popup (GTK_POPOVER (popover));
267
268 g_signal_connect (popover, "hide", G_CALLBACK (gtk_widget_destroy), NULL);
269
270 g_free (name);
271 g_free (prefix);
272 }
273
274 static void
gtk_inspector_actions_class_init(GtkInspectorActionsClass * klass)275 gtk_inspector_actions_class_init (GtkInspectorActionsClass *klass)
276 {
277 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
278
279 gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/actions.ui");
280 gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorActions, model);
281 gtk_widget_class_bind_template_callback (widget_class, row_activated);
282 }
283
284 // vim: set et sw=2 ts=2:
285