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 #include "action-holder.h"
24
25 #include "gtkapplication.h"
26 #include "gtkapplicationwindow.h"
27 #include "gtktreeview.h"
28 #include "gtkliststore.h"
29 #include "gtkwidgetprivate.h"
30 #include "gtkactionmuxerprivate.h"
31 #include "gtkpopover.h"
32 #include "gtklabel.h"
33 #include "gtkstack.h"
34 #include "gtklistbox.h"
35 #include "gtksizegroup.h"
36 #include "gtkboxlayout.h"
37
38
39 struct _GtkInspectorActions
40 {
41 GtkWidget parent;
42
43 GtkWidget *list;
44 GtkWidget *button;
45
46 GObject *object;
47
48 GListStore *actions;
49 GtkSortListModel *sorted;
50 GtkColumnViewColumn *name;
51 };
52
53 typedef struct _GtkInspectorActionsClass
54 {
55 GtkWidgetClass parent;
56 } GtkInspectorActionsClass;
57
58 enum {
59 PROP_0,
60 PROP_BUTTON
61 };
62
G_DEFINE_TYPE(GtkInspectorActions,gtk_inspector_actions,GTK_TYPE_WIDGET)63 G_DEFINE_TYPE (GtkInspectorActions, gtk_inspector_actions, GTK_TYPE_WIDGET)
64
65 static void
66 gtk_inspector_actions_init (GtkInspectorActions *sl)
67 {
68 GtkBoxLayout *layout;
69
70 gtk_widget_init_template (GTK_WIDGET (sl));
71
72 layout = GTK_BOX_LAYOUT (gtk_widget_get_layout_manager (GTK_WIDGET (sl)));
73 gtk_orientable_set_orientation (GTK_ORIENTABLE (layout), GTK_ORIENTATION_VERTICAL);
74 }
75
76 static void
action_added(GObject * owner,const char * action_name,GtkInspectorActions * sl)77 action_added (GObject *owner,
78 const char *action_name,
79 GtkInspectorActions *sl)
80 {
81 ActionHolder *holder = action_holder_new (owner, action_name);
82 g_list_store_append (sl->actions, holder);
83 g_object_unref (holder);
84 }
85
86 static void
setup_name_cb(GtkSignalListItemFactory * factory,GtkListItem * list_item)87 setup_name_cb (GtkSignalListItemFactory *factory,
88 GtkListItem *list_item)
89 {
90 GtkWidget *label;
91
92 label = gtk_label_new (NULL);
93 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
94 gtk_widget_add_css_class (label, "cell");
95 gtk_list_item_set_child (list_item, label);
96 }
97
98 static void
bind_name_cb(GtkSignalListItemFactory * factory,GtkListItem * list_item)99 bind_name_cb (GtkSignalListItemFactory *factory,
100 GtkListItem *list_item)
101 {
102 gpointer item;
103 GtkWidget *label;
104
105 item = gtk_list_item_get_item (list_item);
106 label = gtk_list_item_get_child (list_item);
107
108 gtk_label_set_label (GTK_LABEL (label), action_holder_get_name (ACTION_HOLDER (item)));
109 }
110
111 static void
setup_enabled_cb(GtkSignalListItemFactory * factory,GtkListItem * list_item)112 setup_enabled_cb (GtkSignalListItemFactory *factory,
113 GtkListItem *list_item)
114 {
115 GtkWidget *label;
116
117 label = gtk_label_new (NULL);
118 gtk_label_set_xalign (GTK_LABEL (label), 0.5);
119 gtk_widget_add_css_class (label, "cell");
120 gtk_list_item_set_child (list_item, label);
121 }
122
123 static void
bind_enabled_cb(GtkSignalListItemFactory * factory,GtkListItem * list_item)124 bind_enabled_cb (GtkSignalListItemFactory *factory,
125 GtkListItem *list_item)
126 {
127 gpointer item;
128 GtkWidget *label;
129 GObject *owner;
130 const char *name;
131 gboolean enabled = FALSE;
132
133 item = gtk_list_item_get_item (list_item);
134 label = gtk_list_item_get_child (list_item);
135
136 owner = action_holder_get_owner (ACTION_HOLDER (item));
137 name = action_holder_get_name (ACTION_HOLDER (item));
138 if (G_IS_ACTION_GROUP (owner))
139 enabled = g_action_group_get_action_enabled (G_ACTION_GROUP (owner), name);
140 else if (GTK_IS_ACTION_MUXER (owner))
141 gtk_action_muxer_query_action (GTK_ACTION_MUXER (owner), name,
142 &enabled, NULL, NULL, NULL, NULL);
143
144 gtk_label_set_label (GTK_LABEL (label), enabled ? "+" : "-");
145 }
146
147 static void
setup_parameter_cb(GtkSignalListItemFactory * factory,GtkListItem * list_item)148 setup_parameter_cb (GtkSignalListItemFactory *factory,
149 GtkListItem *list_item)
150 {
151 GtkWidget *label;
152
153 label = gtk_label_new (NULL);
154 gtk_label_set_xalign (GTK_LABEL (label), 0.5);
155 gtk_widget_add_css_class (label, "cell");
156 gtk_list_item_set_child (list_item, label);
157 }
158
159 static void
bind_parameter_cb(GtkSignalListItemFactory * factory,GtkListItem * list_item)160 bind_parameter_cb (GtkSignalListItemFactory *factory,
161 GtkListItem *list_item)
162 {
163 gpointer item;
164 GtkWidget *label;
165 GObject *owner;
166 const char *name;
167 const char *parameter;
168
169 item = gtk_list_item_get_item (list_item);
170 label = gtk_list_item_get_child (list_item);
171
172 owner = action_holder_get_owner (ACTION_HOLDER (item));
173 name = action_holder_get_name (ACTION_HOLDER (item));
174 if (G_IS_ACTION_GROUP (owner))
175 parameter = (const char *)g_action_group_get_action_parameter_type (G_ACTION_GROUP (owner), name);
176 else if (GTK_IS_ACTION_MUXER (owner))
177 gtk_action_muxer_query_action (GTK_ACTION_MUXER (owner), name,
178 NULL, (const GVariantType **)¶meter, NULL, NULL, NULL);
179 else
180 parameter = "(Unknown)";
181
182 gtk_label_set_label (GTK_LABEL (label), parameter);
183 }
184
185 static void
setup_state_cb(GtkSignalListItemFactory * factory,GtkListItem * list_item)186 setup_state_cb (GtkSignalListItemFactory *factory,
187 GtkListItem *list_item)
188 {
189 GtkWidget *label;
190
191 label = gtk_label_new (NULL);
192 gtk_widget_set_margin_start (label, 5);
193 gtk_widget_set_margin_end (label, 5);
194 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
195 gtk_widget_add_css_class (label, "cell");
196 gtk_list_item_set_child (list_item, label);
197 }
198
199 static void
bind_state_cb(GtkSignalListItemFactory * factory,GtkListItem * list_item)200 bind_state_cb (GtkSignalListItemFactory *factory,
201 GtkListItem *list_item)
202 {
203 gpointer item;
204 GtkWidget *label;
205 GObject *owner;
206 const char *name;
207 GVariant *state;
208
209 item = gtk_list_item_get_item (list_item);
210 label = gtk_list_item_get_child (list_item);
211
212 owner = action_holder_get_owner (ACTION_HOLDER (item));
213 name = action_holder_get_name (ACTION_HOLDER (item));
214 if (G_IS_ACTION_GROUP (owner))
215 state = g_action_group_get_action_state (G_ACTION_GROUP (owner), name);
216 else if (GTK_IS_ACTION_MUXER (owner))
217 gtk_action_muxer_query_action (GTK_ACTION_MUXER (owner), name,
218 NULL, NULL, NULL, NULL, &state);
219 else
220 state = NULL;
221
222 if (state)
223 {
224 char *state_string;
225
226 state_string = g_variant_print (state, FALSE);
227 gtk_label_set_label (GTK_LABEL (label), state_string);
228 g_free (state_string);
229 g_variant_unref (state);
230 }
231 else
232 gtk_label_set_label (GTK_LABEL (label), "");
233 }
234
235 static void
setup_changes_cb(GtkSignalListItemFactory * factory,GtkListItem * list_item)236 setup_changes_cb (GtkSignalListItemFactory *factory,
237 GtkListItem *list_item)
238 {
239 GtkWidget *editor;
240
241 editor = gtk_inspector_action_editor_new ();
242 gtk_widget_add_css_class (editor, "cell");
243 gtk_list_item_set_child (list_item, editor);
244 }
245
246 static void
bind_changes_cb(GtkSignalListItemFactory * factory,GtkListItem * list_item)247 bind_changes_cb (GtkSignalListItemFactory *factory,
248 GtkListItem *list_item)
249 {
250 gpointer item;
251 GObject *owner;
252 const char *name;
253 GtkWidget *editor;
254
255 item = gtk_list_item_get_item (list_item);
256 editor = gtk_list_item_get_child (list_item);
257
258 owner = action_holder_get_owner (ACTION_HOLDER (item));
259 name = action_holder_get_name (ACTION_HOLDER (item));
260
261 gtk_inspector_action_editor_set (GTK_INSPECTOR_ACTION_EDITOR (editor),
262 owner,
263 name);
264 }
265
266 static void
add_group(GtkInspectorActions * sl,GActionGroup * group)267 add_group (GtkInspectorActions *sl,
268 GActionGroup *group)
269 {
270 int i;
271 char **names;
272
273 names = g_action_group_list_actions (group);
274 for (i = 0; names[i]; i++)
275 action_added (G_OBJECT (group), names[i], sl);
276 g_strfreev (names);
277 }
278
279 static void
add_muxer(GtkInspectorActions * sl,GtkActionMuxer * muxer)280 add_muxer (GtkInspectorActions *sl,
281 GtkActionMuxer *muxer)
282 {
283 int i;
284 char **names;
285
286 names = gtk_action_muxer_list_actions (muxer, FALSE);
287 for (i = 0; names[i]; i++)
288 action_added (G_OBJECT (muxer), names[i], sl);
289 g_strfreev (names);
290 }
291
292 static gboolean
reload(GtkInspectorActions * sl)293 reload (GtkInspectorActions *sl)
294 {
295 gboolean loaded = FALSE;
296
297 g_object_unref (sl->actions);
298 sl->actions = g_list_store_new (ACTION_TYPE_HOLDER);
299
300 if (GTK_IS_APPLICATION (sl->object))
301 {
302 add_group (sl, G_ACTION_GROUP (sl->object));
303 loaded = TRUE;
304 }
305 else if (GTK_IS_WIDGET (sl->object))
306 {
307 GtkActionMuxer *muxer;
308
309 muxer = _gtk_widget_get_action_muxer (GTK_WIDGET (sl->object), FALSE);
310 if (muxer)
311 {
312 add_muxer (sl, muxer);
313 loaded = TRUE;
314 }
315 }
316
317 gtk_sort_list_model_set_model (sl->sorted, G_LIST_MODEL (sl->actions));
318
319 return loaded;
320 }
321
322 static void
refresh_all(GtkInspectorActions * sl)323 refresh_all (GtkInspectorActions *sl)
324 {
325 reload (sl);
326 }
327
328 void
gtk_inspector_actions_set_object(GtkInspectorActions * sl,GObject * object)329 gtk_inspector_actions_set_object (GtkInspectorActions *sl,
330 GObject *object)
331 {
332 GtkWidget *stack;
333 GtkStackPage *page;
334 gboolean loaded;
335
336 stack = gtk_widget_get_parent (GTK_WIDGET (sl));
337 page = gtk_stack_get_page (GTK_STACK (stack), GTK_WIDGET (sl));
338 gtk_stack_page_set_visible (page, FALSE);
339
340 g_set_object (&sl->object, object);
341
342 gtk_column_view_sort_by_column (GTK_COLUMN_VIEW (sl->list), sl->name, GTK_SORT_ASCENDING);
343 loaded = reload (sl);
344 gtk_stack_page_set_visible (page, loaded);
345 }
346
347 static void
get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)348 get_property (GObject *object,
349 guint param_id,
350 GValue *value,
351 GParamSpec *pspec)
352 {
353 GtkInspectorActions *sl = GTK_INSPECTOR_ACTIONS (object);
354
355 switch (param_id)
356 {
357 case PROP_BUTTON:
358 g_value_set_object (value, sl->button);
359 break;
360
361 default:
362 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
363 break;
364 }
365 }
366
367 static void
set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)368 set_property (GObject *object,
369 guint param_id,
370 const GValue *value,
371 GParamSpec *pspec)
372 {
373 GtkInspectorActions *sl = GTK_INSPECTOR_ACTIONS (object);
374
375 switch (param_id)
376 {
377 case PROP_BUTTON:
378 sl->button = g_value_get_object (value);
379 break;
380
381 default:
382 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
383 break;
384 }
385 }
386
387 static char *
holder_name(gpointer item)388 holder_name (gpointer item)
389 {
390 return g_strdup (action_holder_get_name (ACTION_HOLDER (item)));
391 }
392
393 static void
constructed(GObject * object)394 constructed (GObject *object)
395 {
396 GtkInspectorActions *sl = GTK_INSPECTOR_ACTIONS (object);
397 GtkSorter *sorter;
398 GListModel *model;
399
400 g_signal_connect_swapped (sl->button, "clicked",
401 G_CALLBACK (refresh_all), sl);
402
403 sorter = GTK_SORTER (gtk_string_sorter_new (gtk_cclosure_expression_new (G_TYPE_STRING,
404 NULL,
405 0, NULL,
406 (GCallback)holder_name,
407 NULL, NULL)));
408 gtk_column_view_column_set_sorter (sl->name, sorter);
409 g_object_unref (sorter);
410
411 sl->actions = g_list_store_new (ACTION_TYPE_HOLDER);
412 sl->sorted = gtk_sort_list_model_new (g_object_ref (G_LIST_MODEL (sl->actions)),
413 g_object_ref (gtk_column_view_get_sorter (GTK_COLUMN_VIEW (sl->list))));
414 model = G_LIST_MODEL (gtk_no_selection_new (g_object_ref (G_LIST_MODEL (sl->sorted))));
415 gtk_column_view_set_model (GTK_COLUMN_VIEW (sl->list), GTK_SELECTION_MODEL (model));
416 g_object_unref (model);
417 }
418
419 static void
dispose(GObject * object)420 dispose (GObject *object)
421 {
422 GtkInspectorActions *sl = GTK_INSPECTOR_ACTIONS (object);
423 GtkWidget *child;
424
425 g_clear_object (&sl->sorted);
426 g_clear_object (&sl->actions);
427 g_clear_object (&sl->object);
428
429 while ((child = gtk_widget_get_first_child (GTK_WIDGET (sl))))
430 gtk_widget_unparent (child);
431
432 G_OBJECT_CLASS (gtk_inspector_actions_parent_class)->dispose (object);
433 }
434
435 static void
gtk_inspector_actions_class_init(GtkInspectorActionsClass * klass)436 gtk_inspector_actions_class_init (GtkInspectorActionsClass *klass)
437 {
438 GObjectClass *object_class = G_OBJECT_CLASS (klass);
439 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
440
441 object_class->dispose = dispose;
442 object_class->get_property = get_property;
443 object_class->set_property = set_property;
444 object_class->constructed = constructed;
445
446 g_object_class_install_property (object_class, PROP_BUTTON,
447 g_param_spec_object ("button", NULL, NULL,
448 GTK_TYPE_WIDGET, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
449
450 gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/actions.ui");
451 gtk_widget_class_bind_template_child (widget_class, GtkInspectorActions, list);
452 gtk_widget_class_bind_template_child (widget_class, GtkInspectorActions, name);
453 gtk_widget_class_bind_template_callback (widget_class, setup_name_cb);
454 gtk_widget_class_bind_template_callback (widget_class, bind_name_cb);
455 gtk_widget_class_bind_template_callback (widget_class, setup_enabled_cb);
456 gtk_widget_class_bind_template_callback (widget_class, bind_enabled_cb);
457 gtk_widget_class_bind_template_callback (widget_class, setup_parameter_cb);
458 gtk_widget_class_bind_template_callback (widget_class, bind_parameter_cb);
459 gtk_widget_class_bind_template_callback (widget_class, setup_state_cb);
460 gtk_widget_class_bind_template_callback (widget_class, bind_state_cb);
461 gtk_widget_class_bind_template_callback (widget_class, setup_changes_cb);
462 gtk_widget_class_bind_template_callback (widget_class, bind_changes_cb);
463
464 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
465 }
466
467 // vim: set et sw=2 ts=2:
468