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 #include "variant-editor.h"
23 
24 #include "gtktogglebutton.h"
25 #include "gtkentry.h"
26 #include "gtklabel.h"
27 #include "gtkbox.h"
28 #include "gtkboxlayout.h"
29 #include "gtkorientable.h"
30 #include "gtkactionmuxerprivate.h"
31 
32 struct _GtkInspectorActionEditor
33 {
34   GtkWidget parent;
35 
36   GObject *owner;
37   char *name;
38   gboolean enabled;
39   const GVariantType *parameter_type;
40   GVariantType *state_type;
41   GtkWidget *activate_button;
42   GtkWidget *parameter_entry;
43   GtkWidget *state_entry;
44   GtkWidget *state_editor;
45 };
46 
47 typedef struct
48 {
49   GtkWidgetClass parent;
50 } GtkInspectorActionEditorClass;
51 
52 enum
53 {
54   PROP_0,
55   PROP_OWNER,
56   PROP_NAME,
57   PROP_SIZEGROUP
58 };
59 
60 G_DEFINE_TYPE (GtkInspectorActionEditor, gtk_inspector_action_editor, GTK_TYPE_WIDGET)
61 
62 static void update_widgets (GtkInspectorActionEditor *r);
63 
64 static void
activate_action(GtkWidget * button,GtkInspectorActionEditor * r)65 activate_action (GtkWidget                *button,
66                  GtkInspectorActionEditor *r)
67 {
68   GVariant *parameter = NULL;
69 
70   if (r->parameter_entry)
71     parameter = gtk_inspector_variant_editor_get_value (r->parameter_entry);
72   if (G_IS_ACTION_GROUP (r->owner))
73     g_action_group_activate_action (G_ACTION_GROUP (r->owner), r->name, parameter);
74   else if (GTK_IS_ACTION_MUXER (r->owner))
75     gtk_action_muxer_activate_action (GTK_ACTION_MUXER (r->owner), r->name, parameter);
76   update_widgets (r);
77 }
78 
79 static void
parameter_changed(GtkWidget * editor,gpointer data)80 parameter_changed (GtkWidget *editor,
81                    gpointer   data)
82 {
83   GtkInspectorActionEditor *r = data;
84   GVariant *value;
85 
86   value = gtk_inspector_variant_editor_get_value (editor);
87   gtk_widget_set_sensitive (r->activate_button, r->enabled && value != NULL);
88   if (value)
89     g_variant_unref (value);
90 }
91 
92 static void
state_changed(GtkWidget * editor,gpointer data)93 state_changed (GtkWidget *editor,
94                gpointer   data)
95 {
96   GtkInspectorActionEditor *r = data;
97   GVariant *value;
98 
99   value = gtk_inspector_variant_editor_get_value (editor);
100   if (value)
101     {
102       if (G_IS_ACTION_GROUP (r->owner))
103         g_action_group_change_action_state (G_ACTION_GROUP (r->owner), r->name, value);
104       else if (GTK_IS_ACTION_MUXER (r->owner))
105         gtk_action_muxer_change_action_state (GTK_ACTION_MUXER (r->owner), r->name, value);
106     }
107 }
108 
109 static void
gtk_inspector_action_editor_init(GtkInspectorActionEditor * r)110 gtk_inspector_action_editor_init (GtkInspectorActionEditor *r)
111 {
112   GtkBoxLayout *layout;
113   GtkWidget *row, *activate, *label;
114 
115   layout = GTK_BOX_LAYOUT (gtk_widget_get_layout_manager (GTK_WIDGET (r)));
116   gtk_orientable_set_orientation (GTK_ORIENTABLE (layout), GTK_ORIENTATION_HORIZONTAL);
117   gtk_box_layout_set_spacing (layout, 10);
118 
119   row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
120   activate = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
121   gtk_box_append (GTK_BOX (row), activate);
122 
123   r->activate_button = gtk_button_new_with_label (_("Activate"));
124   g_signal_connect (r->activate_button, "clicked", G_CALLBACK (activate_action), r);
125 
126   gtk_box_append (GTK_BOX (activate), r->activate_button);
127 
128   r->parameter_entry = gtk_inspector_variant_editor_new (NULL, parameter_changed, r);
129   gtk_widget_hide (r->parameter_entry);
130   gtk_box_append (GTK_BOX (activate), r->parameter_entry);
131 
132   gtk_widget_set_parent (row, GTK_WIDGET (r));
133 
134   r->state_editor = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
135   label = gtk_label_new (_("Set State"));
136   gtk_box_append (GTK_BOX (r->state_editor), label);
137   r->state_entry = gtk_inspector_variant_editor_new (NULL, state_changed, r);
138   gtk_box_append (GTK_BOX (r->state_editor), r->state_entry);
139   gtk_widget_set_parent (r->state_editor, GTK_WIDGET (r));
140   gtk_widget_hide (r->state_editor);
141 }
142 
143 static void
update_enabled(GtkInspectorActionEditor * r,gboolean enabled)144 update_enabled (GtkInspectorActionEditor *r,
145                 gboolean                  enabled)
146 {
147   r->enabled = enabled;
148   if (r->parameter_entry)
149     {
150       gtk_widget_set_sensitive (r->parameter_entry, enabled);
151       parameter_changed (r->parameter_entry, r);
152     }
153   if (r->activate_button)
154     gtk_widget_set_sensitive (r->activate_button, enabled);
155 }
156 
157 static void
action_enabled_changed_cb(GActionGroup * group,const char * action_name,gboolean enabled,GtkInspectorActionEditor * r)158 action_enabled_changed_cb (GActionGroup             *group,
159                            const char               *action_name,
160                            gboolean                  enabled,
161                            GtkInspectorActionEditor *r)
162 {
163   if (g_str_equal (action_name, r->name))
164     update_enabled (r, enabled);
165 }
166 
167 static void
update_state(GtkInspectorActionEditor * r,GVariant * state)168 update_state (GtkInspectorActionEditor *r,
169               GVariant                 *state)
170 {
171   if (r->state_entry)
172     gtk_inspector_variant_editor_set_value (r->state_entry, state);
173 }
174 
175 static void
action_state_changed_cb(GActionGroup * group,const char * action_name,GVariant * state,GtkInspectorActionEditor * r)176 action_state_changed_cb (GActionGroup             *group,
177                          const char               *action_name,
178                          GVariant                 *state,
179                          GtkInspectorActionEditor *r)
180 {
181   if (g_str_equal (action_name, r->name))
182     update_state (r, state);
183 }
184 
185 static void
update_widgets(GtkInspectorActionEditor * r)186 update_widgets (GtkInspectorActionEditor *r)
187 {
188   GVariant *state;
189 
190   if (G_IS_ACTION_GROUP (r->owner))
191     g_action_group_query_action (G_ACTION_GROUP (r->owner), r->name,
192                                  &r->enabled, &r->parameter_type, NULL, NULL,
193                                  &state);
194   else if (GTK_IS_ACTION_MUXER (r->owner))
195     gtk_action_muxer_query_action (GTK_ACTION_MUXER (r->owner), r->name,
196                                    &r->enabled, &r->parameter_type, NULL, NULL,
197                                    &state);
198   else
199     state = NULL;
200 
201   gtk_widget_set_sensitive (r->activate_button, r->enabled);
202 
203   if (r->parameter_type)
204     {
205       gtk_inspector_variant_editor_set_type (r->parameter_entry, r->parameter_type);
206       gtk_widget_show (r->parameter_entry);
207       gtk_widget_set_sensitive (r->parameter_entry, r->enabled);
208     }
209   else
210     gtk_widget_hide (r->parameter_entry);
211 
212   if (state)
213     {
214       if (r->state_type)
215         g_variant_type_free (r->state_type);
216       r->state_type = g_variant_type_copy (g_variant_get_type (state));
217       gtk_inspector_variant_editor_set_value (r->state_entry, state);
218       gtk_widget_show (r->state_editor);
219     }
220   else
221     gtk_widget_hide (r->state_editor);
222 
223   if (G_IS_ACTION_GROUP (r->owner))
224     {
225       g_signal_connect (r->owner, "action-enabled-changed",
226                         G_CALLBACK (action_enabled_changed_cb), r);
227       g_signal_connect (r->owner, "action-state-changed",
228                         G_CALLBACK (action_state_changed_cb), r);
229     }
230 }
231 
232 static void
dispose(GObject * object)233 dispose (GObject *object)
234 {
235   GtkInspectorActionEditor *r = GTK_INSPECTOR_ACTION_EDITOR (object);
236   GtkWidget *child;
237 
238   g_free (r->name);
239   if (r->state_type)
240     g_variant_type_free (r->state_type);
241   if (r->owner)
242     {
243       g_signal_handlers_disconnect_by_func (r->owner, action_enabled_changed_cb, r);
244       g_signal_handlers_disconnect_by_func (r->owner, action_state_changed_cb, r);
245     }
246 
247   while ((child = gtk_widget_get_first_child (GTK_WIDGET (r))))
248     gtk_widget_unparent (child);
249 
250   G_OBJECT_CLASS (gtk_inspector_action_editor_parent_class)->dispose (object);
251 }
252 
253 static void
get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)254 get_property (GObject    *object,
255               guint       param_id,
256               GValue     *value,
257               GParamSpec *pspec)
258 {
259   GtkInspectorActionEditor *r = GTK_INSPECTOR_ACTION_EDITOR (object);
260 
261   switch (param_id)
262     {
263     case PROP_OWNER:
264       g_value_set_object (value, r->owner);
265       break;
266 
267     case PROP_NAME:
268       g_value_set_string (value, r->name);
269       break;
270 
271     default:
272       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
273       break;
274     }
275 }
276 
277 static void
set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)278 set_property (GObject      *object,
279               guint         param_id,
280               const GValue *value,
281               GParamSpec   *pspec)
282 {
283   GtkInspectorActionEditor *r = GTK_INSPECTOR_ACTION_EDITOR (object);
284 
285   switch (param_id)
286     {
287     case PROP_OWNER:
288       if (r->owner)
289         {
290           g_signal_handlers_disconnect_by_func (r->owner, action_enabled_changed_cb, r);
291           g_signal_handlers_disconnect_by_func (r->owner, action_state_changed_cb, r);
292         }
293       r->owner = g_value_get_object (value);
294       break;
295 
296     case PROP_NAME:
297       g_free (r->name);
298       r->name = g_value_dup_string (value);
299       break;
300 
301     default:
302       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
303       break;
304     }
305 }
306 
307 static void
gtk_inspector_action_editor_class_init(GtkInspectorActionEditorClass * klass)308 gtk_inspector_action_editor_class_init (GtkInspectorActionEditorClass *klass)
309 {
310   GObjectClass *object_class = G_OBJECT_CLASS (klass);
311   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
312 
313   object_class->dispose = dispose;
314   object_class->get_property = get_property;
315   object_class->set_property = set_property;
316 
317   g_object_class_install_property (object_class, PROP_OWNER,
318       g_param_spec_object ("owner", "Owner", "The owner of the action",
319                            G_TYPE_OBJECT, G_PARAM_READWRITE));
320 
321   g_object_class_install_property (object_class, PROP_NAME,
322       g_param_spec_string ("name", "Name", "The action name",
323                            NULL, G_PARAM_READWRITE));
324 
325   gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
326 }
327 
328 GtkWidget *
gtk_inspector_action_editor_new(void)329 gtk_inspector_action_editor_new (void)
330 {
331   return g_object_new (GTK_TYPE_INSPECTOR_ACTION_EDITOR, NULL);
332 }
333 
334 void
gtk_inspector_action_editor_set(GtkInspectorActionEditor * self,GObject * owner,const char * name)335 gtk_inspector_action_editor_set (GtkInspectorActionEditor *self,
336                                  GObject                  *owner,
337                                  const char               *name)
338 {
339   g_object_set (self, "owner", owner, "name", name, NULL);
340   update_widgets (self);
341 }
342 
343 void
gtk_inspector_action_editor_update(GtkInspectorActionEditor * r,gboolean enabled,GVariant * state)344 gtk_inspector_action_editor_update (GtkInspectorActionEditor *r,
345                                     gboolean                  enabled,
346                                     GVariant                 *state)
347 {
348   update_enabled (r, enabled);
349   update_state (r, state);
350 }
351