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
39struct _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
53typedef struct _GtkInspectorActionsClass
54{
55 GtkWidgetClass parent;
56} GtkInspectorActionsClass;
57
58enum {
59 PROP_0,
60 PROP_BUTTON
61};
62
63G_DEFINE_TYPE (GtkInspectorActions, gtk_inspector_actions, GTK_TYPE_WIDGET)
64
65static void
66gtk_inspector_actions_init (GtkInspectorActions *sl)
67{
68 GtkBoxLayout *layout;
69
70 gtk_widget_init_template (GTK_WIDGET (sl));
71
72 layout = GTK_BOX_LAYOUT (ptr: gtk_widget_get_layout_manager (GTK_WIDGET (sl)));
73 gtk_orientable_set_orientation (GTK_ORIENTABLE (layout), orientation: GTK_ORIENTATION_VERTICAL);
74}
75
76static void
77action_added (GObject *owner,
78 const char *action_name,
79 GtkInspectorActions *sl)
80{
81 ActionHolder *holder = action_holder_new (owner, name: action_name);
82 g_list_store_append (store: sl->actions, item: holder);
83 g_object_unref (object: holder);
84}
85
86static void
87setup_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), xalign: 0.0);
94 gtk_widget_add_css_class (widget: label, css_class: "cell");
95 gtk_list_item_set_child (self: list_item, child: label);
96}
97
98static void
99bind_name_cb (GtkSignalListItemFactory *factory,
100 GtkListItem *list_item)
101{
102 gpointer item;
103 GtkWidget *label;
104
105 item = gtk_list_item_get_item (self: list_item);
106 label = gtk_list_item_get_child (self: list_item);
107
108 gtk_label_set_label (GTK_LABEL (label), str: action_holder_get_name (holder: ACTION_HOLDER (ptr: item)));
109}
110
111static void
112setup_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), xalign: 0.5);
119 gtk_widget_add_css_class (widget: label, css_class: "cell");
120 gtk_list_item_set_child (self: list_item, child: label);
121}
122
123static void
124bind_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 (self: list_item);
134 label = gtk_list_item_get_child (self: list_item);
135
136 owner = action_holder_get_owner (holder: ACTION_HOLDER (ptr: item));
137 name = action_holder_get_name (holder: ACTION_HOLDER (ptr: item));
138 if (G_IS_ACTION_GROUP (owner))
139 enabled = g_action_group_get_action_enabled (G_ACTION_GROUP (owner), action_name: name);
140 else if (GTK_IS_ACTION_MUXER (owner))
141 gtk_action_muxer_query_action (GTK_ACTION_MUXER (owner), action_name: name,
142 enabled: &enabled, NULL, NULL, NULL, NULL);
143
144 gtk_label_set_label (GTK_LABEL (label), str: enabled ? "+" : "-");
145}
146
147static void
148setup_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), xalign: 0.5);
155 gtk_widget_add_css_class (widget: label, css_class: "cell");
156 gtk_list_item_set_child (self: list_item, child: label);
157}
158
159static void
160bind_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 (self: list_item);
170 label = gtk_list_item_get_child (self: list_item);
171
172 owner = action_holder_get_owner (holder: ACTION_HOLDER (ptr: item));
173 name = action_holder_get_name (holder: ACTION_HOLDER (ptr: item));
174 if (G_IS_ACTION_GROUP (owner))
175 parameter = (const char *)g_action_group_get_action_parameter_type (G_ACTION_GROUP (owner), action_name: name);
176 else if (GTK_IS_ACTION_MUXER (owner))
177 gtk_action_muxer_query_action (GTK_ACTION_MUXER (owner), action_name: name,
178 NULL, parameter_type: (const GVariantType **)&parameter, NULL, NULL, NULL);
179 else
180 parameter = "(Unknown)";
181
182 gtk_label_set_label (GTK_LABEL (label), str: parameter);
183}
184
185static void
186setup_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 (widget: label, margin: 5);
193 gtk_widget_set_margin_end (widget: label, margin: 5);
194 gtk_label_set_xalign (GTK_LABEL (label), xalign: 0.0);
195 gtk_widget_add_css_class (widget: label, css_class: "cell");
196 gtk_list_item_set_child (self: list_item, child: label);
197}
198
199static void
200bind_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 (self: list_item);
210 label = gtk_list_item_get_child (self: list_item);
211
212 owner = action_holder_get_owner (holder: ACTION_HOLDER (ptr: item));
213 name = action_holder_get_name (holder: ACTION_HOLDER (ptr: item));
214 if (G_IS_ACTION_GROUP (owner))
215 state = g_action_group_get_action_state (G_ACTION_GROUP (owner), action_name: name);
216 else if (GTK_IS_ACTION_MUXER (owner))
217 gtk_action_muxer_query_action (GTK_ACTION_MUXER (owner), action_name: name,
218 NULL, NULL, NULL, NULL, state: &state);
219 else
220 state = NULL;
221
222 if (state)
223 {
224 char *state_string;
225
226 state_string = g_variant_print (value: state, FALSE);
227 gtk_label_set_label (GTK_LABEL (label), str: state_string);
228 g_free (mem: state_string);
229 g_variant_unref (value: state);
230 }
231 else
232 gtk_label_set_label (GTK_LABEL (label), str: "");
233}
234
235static void
236setup_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 (widget: editor, css_class: "cell");
243 gtk_list_item_set_child (self: list_item, child: editor);
244}
245
246static void
247bind_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 (self: list_item);
256 editor = gtk_list_item_get_child (self: list_item);
257
258 owner = action_holder_get_owner (holder: ACTION_HOLDER (ptr: item));
259 name = action_holder_get_name (holder: ACTION_HOLDER (ptr: item));
260
261 gtk_inspector_action_editor_set (GTK_INSPECTOR_ACTION_EDITOR (editor),
262 owner,
263 name);
264}
265
266static void
267add_group (GtkInspectorActions *sl,
268 GActionGroup *group)
269{
270 int i;
271 char **names;
272
273 names = g_action_group_list_actions (action_group: group);
274 for (i = 0; names[i]; i++)
275 action_added (G_OBJECT (group), action_name: names[i], sl);
276 g_strfreev (str_array: names);
277}
278
279static void
280add_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), action_name: names[i], sl);
289 g_strfreev (str_array: names);
290}
291
292static gboolean
293reload (GtkInspectorActions *sl)
294{
295 gboolean loaded = FALSE;
296
297 g_object_unref (object: 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 (self: sl->sorted, model: G_LIST_MODEL (ptr: sl->actions));
318
319 return loaded;
320}
321
322static void
323refresh_all (GtkInspectorActions *sl)
324{
325 reload (sl);
326}
327
328void
329gtk_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 (self: page, FALSE);
339
340 g_set_object (&sl->object, object);
341
342 gtk_column_view_sort_by_column (GTK_COLUMN_VIEW (sl->list), column: sl->name, direction: GTK_SORT_ASCENDING);
343 loaded = reload (sl);
344 gtk_stack_page_set_visible (self: page, visible: loaded);
345}
346
347static void
348get_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, v_object: sl->button);
359 break;
360
361 default:
362 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
363 break;
364 }
365}
366
367static void
368set_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
387static char *
388holder_name (gpointer item)
389{
390 return g_strdup (str: action_holder_get_name (holder: ACTION_HOLDER (ptr: item)));
391}
392
393static void
394constructed (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 (ptr: gtk_string_sorter_new (expression: gtk_cclosure_expression_new (G_TYPE_STRING,
404 NULL,
405 n_params: 0, NULL,
406 callback_func: (GCallback)holder_name,
407 NULL, NULL)));
408 gtk_column_view_column_set_sorter (self: sl->name, sorter);
409 g_object_unref (object: 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 (ptr: gtk_no_selection_new (g_object_ref (G_LIST_MODEL (sl->sorted))));
415 gtk_column_view_set_model (GTK_COLUMN_VIEW (sl->list), model: GTK_SELECTION_MODEL (ptr: model));
416 g_object_unref (object: model);
417}
418
419static void
420dispose (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 (widget: child);
431
432 G_OBJECT_CLASS (gtk_inspector_actions_parent_class)->dispose (object);
433}
434
435static void
436gtk_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 (oclass: object_class, property_id: PROP_BUTTON,
447 pspec: g_param_spec_object (name: "button", NULL, NULL,
448 GTK_TYPE_WIDGET, flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
449
450 gtk_widget_class_set_template_from_resource (widget_class, resource_name: "/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

source code of gtk/gtk/inspector/actions.c