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 "action-editor.h"
22
23 #include "gtksizegroup.h"
24 #include "gtktogglebutton.h"
25 #include "gtkentry.h"
26 #include "gtkbin.h"
27 #include "gtklabel.h"
28
29 struct _GtkInspectorActionEditorPrivate
30 {
31 GActionGroup *group;
32 gchar *prefix;
33 gchar *name;
34 gboolean enabled;
35 const GVariantType *parameter_type;
36 GVariantType *state_type;
37 GtkWidget *activate_button;
38 GtkWidget *parameter_entry;
39 GtkWidget *state_entry;
40 GtkSizeGroup *sg;
41 };
42
43 enum
44 {
45 PROP_0,
46 PROP_GROUP,
47 PROP_PREFIX,
48 PROP_NAME
49 };
50
G_DEFINE_TYPE_WITH_PRIVATE(GtkInspectorActionEditor,gtk_inspector_action_editor,GTK_TYPE_BOX)51 G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorActionEditor, gtk_inspector_action_editor, GTK_TYPE_BOX)
52
53 static void
54 gtk_inspector_action_editor_init (GtkInspectorActionEditor *editor)
55 {
56 editor->priv = gtk_inspector_action_editor_get_instance_private (editor);
57 g_object_set (editor,
58 "orientation", GTK_ORIENTATION_VERTICAL,
59 "spacing", 10,
60 "margin", 10,
61 NULL);
62 }
63
64 typedef void (*VariantEditorChanged) (GtkWidget *editor, gpointer data);
65
66 typedef struct {
67 GtkWidget *editor;
68 VariantEditorChanged callback;
69 gpointer data;
70 } VariantEditorData;
71
72 static void
variant_editor_changed_cb(GObject * obj,GParamSpec * pspec,VariantEditorData * data)73 variant_editor_changed_cb (GObject *obj,
74 GParamSpec *pspec,
75 VariantEditorData *data)
76 {
77 data->callback (data->editor, data->data);
78 }
79
80 static GtkWidget *
variant_editor_new(const GVariantType * type,VariantEditorChanged callback,gpointer data)81 variant_editor_new (const GVariantType *type,
82 VariantEditorChanged callback,
83 gpointer data)
84 {
85 GtkWidget *editor;
86 GtkWidget *label;
87 GtkWidget *entry;
88 VariantEditorData *d;
89
90 d = g_new (VariantEditorData, 1);
91 d->callback = callback;
92 d->data = data;
93
94 if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
95 {
96 editor = gtk_toggle_button_new_with_label ("FALSE");
97 g_signal_connect (editor, "notify::active", G_CALLBACK (variant_editor_changed_cb), d);
98 }
99 else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
100 {
101 editor = gtk_entry_new ();
102 g_signal_connect (editor, "notify::text", G_CALLBACK (variant_editor_changed_cb), d);
103 }
104 else
105 {
106 editor = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
107 entry = gtk_entry_new ();
108 gtk_container_add (GTK_CONTAINER (editor), entry);
109 label = gtk_label_new (g_variant_type_peek_string (type));
110 gtk_container_add (GTK_CONTAINER (editor), label);
111 g_signal_connect (entry, "notify::text", G_CALLBACK (variant_editor_changed_cb), d);
112 }
113
114 g_object_set_data (G_OBJECT (editor), "type", (gpointer)type);
115 d->editor = editor;
116 g_object_set_data_full (G_OBJECT (editor), "callback", d, g_free);
117
118 gtk_widget_show_all (editor);
119
120 return editor;
121 }
122
123 static void
variant_editor_set_value(GtkWidget * editor,GVariant * value)124 variant_editor_set_value (GtkWidget *editor,
125 GVariant *value)
126 {
127 const GVariantType *type;
128 gpointer data;
129
130 data = g_object_get_data (G_OBJECT (editor), "callback");
131 g_signal_handlers_block_by_func (editor, variant_editor_changed_cb, data);
132
133 type = g_variant_get_type (value);
134 if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
135 {
136 GtkToggleButton *tb = GTK_TOGGLE_BUTTON (editor);
137 GtkWidget *child;
138
139 gtk_toggle_button_set_active (tb, g_variant_get_boolean (value));
140 child = gtk_bin_get_child (GTK_BIN (tb));
141 gtk_label_set_text (GTK_LABEL (child),
142 g_variant_get_boolean (value) ? "TRUE" : "FALSE");
143 }
144 else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
145 {
146 GtkEntry *entry = GTK_ENTRY (editor);
147 gtk_entry_set_text (entry, g_variant_get_string (value, NULL));
148 }
149 else
150 {
151 GList *children;
152 GtkEntry *entry;
153 gchar *text;
154
155 children = gtk_container_get_children (GTK_CONTAINER (editor));
156 entry = children->data;
157 g_list_free (children);
158
159 text = g_variant_print (value, FALSE);
160 gtk_entry_set_text (entry, text);
161 g_free (text);
162 }
163
164 g_signal_handlers_unblock_by_func (editor, variant_editor_changed_cb, data);
165 }
166
167 static GVariant *
variant_editor_get_value(GtkWidget * editor)168 variant_editor_get_value (GtkWidget *editor)
169 {
170 const GVariantType *type;
171 GVariant *value;
172
173 type = (const GVariantType *) g_object_get_data (G_OBJECT (editor), "type");
174 if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
175 {
176 GtkToggleButton *tb = GTK_TOGGLE_BUTTON (editor);
177 value = g_variant_new_boolean (gtk_toggle_button_get_active (tb));
178 }
179 else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
180 {
181 GtkEntry *entry = GTK_ENTRY (editor);
182 value = g_variant_new_string (gtk_entry_get_text (entry));
183 }
184 else
185 {
186 GList *children;
187 GtkEntry *entry;
188 const gchar *text;
189
190 children = gtk_container_get_children (GTK_CONTAINER (editor));
191 entry = children->data;
192 text = gtk_entry_get_text (entry);
193 g_list_free (children);
194
195 value = g_variant_parse (type, text, NULL, NULL, NULL);
196 }
197
198 return value;
199 }
200
201 static void
activate_action(GtkWidget * button,GtkInspectorActionEditor * r)202 activate_action (GtkWidget *button,
203 GtkInspectorActionEditor *r)
204 {
205 GVariant *parameter = NULL;
206
207 if (r->priv->parameter_entry)
208 parameter = variant_editor_get_value (r->priv->parameter_entry);
209 g_action_group_activate_action (r->priv->group, r->priv->name, parameter);
210 }
211
212 static void
parameter_changed(GtkWidget * editor,gpointer data)213 parameter_changed (GtkWidget *editor,
214 gpointer data)
215 {
216 GtkInspectorActionEditor *r = data;
217 GVariant *value;
218
219 value = variant_editor_get_value (editor);
220 gtk_widget_set_sensitive (r->priv->activate_button, r->priv->enabled && value != NULL);
221 if (value)
222 g_variant_unref (value);
223 }
224
225 static void
state_changed(GtkWidget * editor,gpointer data)226 state_changed (GtkWidget *editor,
227 gpointer data)
228 {
229 GtkInspectorActionEditor *r = data;
230 GVariant *value;
231
232 value = variant_editor_get_value (editor);
233 if (value)
234 g_action_group_change_action_state (r->priv->group, r->priv->name, value);
235 }
236
237 static void
action_enabled_changed_cb(GActionGroup * group,const gchar * action_name,gboolean enabled,GtkInspectorActionEditor * r)238 action_enabled_changed_cb (GActionGroup *group,
239 const gchar *action_name,
240 gboolean enabled,
241 GtkInspectorActionEditor *r)
242 {
243 r->priv->enabled = enabled;
244 if (r->priv->parameter_entry)
245 {
246 gtk_widget_set_sensitive (r->priv->parameter_entry, enabled);
247 parameter_changed (r->priv->parameter_entry, r);
248 }
249 }
250
251 static void
action_state_changed_cb(GActionGroup * group,const gchar * action_name,GVariant * state,GtkInspectorActionEditor * r)252 action_state_changed_cb (GActionGroup *group,
253 const gchar *action_name,
254 GVariant *state,
255 GtkInspectorActionEditor *r)
256 {
257 if (r->priv->state_entry)
258 variant_editor_set_value (r->priv->state_entry, state);
259 }
260
261 static void
constructed(GObject * object)262 constructed (GObject *object)
263 {
264 GtkInspectorActionEditor *r = GTK_INSPECTOR_ACTION_EDITOR (object);
265 GVariant *state;
266 gchar *fullname;
267 GtkWidget *row;
268 GtkWidget *label;
269
270 r->priv->enabled = g_action_group_get_action_enabled (r->priv->group, r->priv->name);
271 state = g_action_group_get_action_state (r->priv->group, r->priv->name);
272
273 fullname = g_strdup_printf ("%s.%s", r->priv->prefix, r->priv->name);
274 gtk_container_add (GTK_CONTAINER (r), gtk_label_new (fullname));
275 g_free (fullname);
276
277 r->priv->sg = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
278
279 row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
280
281 r->priv->activate_button = gtk_button_new_with_label (_("Activate"));
282 g_signal_connect (r->priv->activate_button, "clicked", G_CALLBACK (activate_action), r);
283
284 gtk_size_group_add_widget (r->priv->sg, r->priv->activate_button);
285 gtk_widget_set_sensitive (r->priv->activate_button, r->priv->enabled);
286 gtk_container_add (GTK_CONTAINER (row), r->priv->activate_button);
287
288 r->priv->parameter_type = g_action_group_get_action_parameter_type (r->priv->group, r->priv->name);
289 if (r->priv->parameter_type)
290 {
291 r->priv->parameter_entry = variant_editor_new (r->priv->parameter_type, parameter_changed, r);
292 gtk_widget_set_sensitive (r->priv->parameter_entry, r->priv->enabled);
293 gtk_container_add (GTK_CONTAINER (row), r->priv->parameter_entry);
294 }
295
296 gtk_container_add (GTK_CONTAINER (r), row);
297
298 if (state)
299 {
300 r->priv->state_type = g_variant_type_copy (g_variant_get_type (state));
301 row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
302 label = gtk_label_new (_("State"));
303 gtk_size_group_add_widget (r->priv->sg, label);
304 gtk_container_add (GTK_CONTAINER (row), label);
305 r->priv->state_entry = variant_editor_new (r->priv->state_type, state_changed, r);
306 variant_editor_set_value (r->priv->state_entry, state);
307 gtk_container_add (GTK_CONTAINER (row), r->priv->state_entry);
308 gtk_container_add (GTK_CONTAINER (r), row);
309 }
310
311 g_signal_connect (r->priv->group, "action-enabled-changed",
312 G_CALLBACK (action_enabled_changed_cb), r);
313 g_signal_connect (r->priv->group, "action-state-changed",
314 G_CALLBACK (action_state_changed_cb), r);
315
316 gtk_widget_show_all (GTK_WIDGET (r));
317 }
318
319 static void
finalize(GObject * object)320 finalize (GObject *object)
321 {
322 GtkInspectorActionEditor *r = GTK_INSPECTOR_ACTION_EDITOR (object);
323
324 g_free (r->priv->prefix);
325 g_free (r->priv->name);
326 g_object_unref (r->priv->sg);
327 if (r->priv->state_type)
328 g_variant_type_free (r->priv->state_type);
329 g_signal_handlers_disconnect_by_func (r->priv->group, action_enabled_changed_cb, r);
330 g_signal_handlers_disconnect_by_func (r->priv->group, action_state_changed_cb, r);
331
332 G_OBJECT_CLASS (gtk_inspector_action_editor_parent_class)->finalize (object);
333 }
334
335 static void
get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)336 get_property (GObject *object,
337 guint param_id,
338 GValue *value,
339 GParamSpec *pspec)
340 {
341 GtkInspectorActionEditor *r = GTK_INSPECTOR_ACTION_EDITOR (object);
342
343 switch (param_id)
344 {
345 case PROP_GROUP:
346 g_value_set_object (value, r->priv->group);
347 break;
348
349 case PROP_PREFIX:
350 g_value_set_string (value, r->priv->prefix);
351 break;
352
353 case PROP_NAME:
354 g_value_set_string (value, r->priv->name);
355 break;
356
357 default:
358 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
359 break;
360 }
361 }
362
363 static void
set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)364 set_property (GObject *object,
365 guint param_id,
366 const GValue *value,
367 GParamSpec *pspec)
368 {
369 GtkInspectorActionEditor *r = GTK_INSPECTOR_ACTION_EDITOR (object);
370
371 switch (param_id)
372 {
373 case PROP_GROUP:
374 r->priv->group = g_value_get_object (value);
375 break;
376
377 case PROP_PREFIX:
378 g_free (r->priv->prefix);
379 r->priv->prefix = g_value_dup_string (value);
380 break;
381
382 case PROP_NAME:
383 g_free (r->priv->name);
384 r->priv->name = g_value_dup_string (value);
385 break;
386
387 default:
388 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
389 break;
390 }
391 }
392
393 static void
gtk_inspector_action_editor_class_init(GtkInspectorActionEditorClass * klass)394 gtk_inspector_action_editor_class_init (GtkInspectorActionEditorClass *klass)
395 {
396 GObjectClass *object_class = G_OBJECT_CLASS (klass);
397
398 object_class->constructed = constructed;
399 object_class->finalize = finalize;
400 object_class->get_property = get_property;
401 object_class->set_property = set_property;
402
403 g_object_class_install_property (object_class, PROP_GROUP,
404 g_param_spec_object ("group", "Action Group", "The Action Group containing the action",
405 G_TYPE_ACTION_GROUP, G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
406
407 g_object_class_install_property (object_class, PROP_PREFIX,
408 g_param_spec_string ("prefix", "Prefix", "The action name prefix",
409 NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
410
411 g_object_class_install_property (object_class, PROP_NAME,
412 g_param_spec_string ("name", "Name", "The action name",
413 NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
414 }
415
416 GtkWidget *
gtk_inspector_action_editor_new(GActionGroup * group,const gchar * prefix,const gchar * name)417 gtk_inspector_action_editor_new (GActionGroup *group,
418 const gchar *prefix,
419 const gchar *name)
420 {
421 return g_object_new (GTK_TYPE_INSPECTOR_ACTION_EDITOR,
422 "group", group,
423 "prefix", prefix,
424 "name", name,
425 NULL);
426 }
427