]> Pileus Git - ~andy/gtk/blob - gtk/gtklockbutton.c
Simplify GtkLockButton
[~andy/gtk] / gtk / gtklockbutton.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Red Hat, Inc.
3  * Author: Matthias Clasen
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22
23 #include "gtklockbutton.h"
24 #include "gtkbox.h"
25 #include "gtkimage.h"
26 #include "gtklabel.h"
27 #include "gtksizegroup.h"
28 #include "gtkintl.h"
29
30 /**
31  * SECTION:gtklockbutton
32  * @title: GtkLockButton
33  * @short_description: A widget to unlock or lock privileged operations
34  *
35  * GtkLockButton is a widget that can be used in control panels or
36  * preference dialogs to allow users to obtain and revoke authorizations
37  * needed to operate the controls. The required authorization is represented
38  * by a #GPermission object. Concrete implementations of #GPermission may use
39  * PolicyKit or some other authorization framework.
40  *
41  * If the user lacks the authorization but authorization can be obtained
42  * through authentication, the widget looks like this
43  * <informalexample><inlinegraphic fileref="lockbutton-locked.png"></inlinegraphic></informalexample>
44  * and the user can click the button to obtain the authorization. Depending
45  * on the platform, this may pop up an authentication dialog or ask the user
46  * to authenticate in some other way. Once authorization is obtained, the
47  * widget changes to this
48  * <informalexample><inlinegraphic fileref="lockbutton-unlocked.png"></inlinegraphic></informalexample>
49  * and the authorization can be dropped by clicking the button. If the user
50  * is not able to obtain authorization at all, the widget looks like this
51  * <informalexample><inlinegraphic fileref="lockbutton-sorry.png"></inlinegraphic></informalexample>
52  * If the user is authorized and cannot drop the authorization, the button
53  * is hidden.
54  *
55  * The text (and tooltips) that are shown in the various cases can be adjusted
56  * with the #GtkLockButton:text-lock, #GtkLockButton:text-unlock,
57  * #GtkLockButton:tooltip-lock, #GtkLockButton:tooltip-unlock and
58  * #GtkLockButton:tooltip-not-authorized properties.
59  */
60
61 struct _GtkLockButtonPrivate
62 {
63   GPermission *permission;
64   GCancellable *cancellable;
65
66   gchar *tooltip_lock;
67   gchar *tooltip_unlock;
68   gchar *tooltip_not_authorized;
69   GIcon *icon_lock;
70   GIcon *icon_unlock;
71
72   GtkWidget *box;
73   GtkWidget *image;
74   GtkWidget *label_lock;
75   GtkWidget *label_unlock;
76
77   GtkSizeGroup *label_group;
78 };
79
80 enum
81 {
82   PROP_0,
83   PROP_PERMISSION,
84   PROP_TEXT_LOCK,
85   PROP_TEXT_UNLOCK,
86   PROP_TOOLTIP_LOCK,
87   PROP_TOOLTIP_UNLOCK,
88   PROP_TOOLTIP_NOT_AUTHORIZED
89 };
90
91 static void update_state (GtkLockButton *button);
92 static void gtk_lock_button_clicked (GtkButton *button);
93
94 static void on_permission_changed (GPermission *permission,
95                                    GParamSpec  *pspec,
96                                    gpointer     user_data);
97
98 G_DEFINE_TYPE (GtkLockButton, gtk_lock_button, GTK_TYPE_BUTTON);
99
100 static void
101 gtk_lock_button_finalize (GObject *object)
102 {
103   GtkLockButton *button = GTK_LOCK_BUTTON (object);
104   GtkLockButtonPrivate *priv = button->priv;
105
106   g_free (priv->tooltip_lock);
107   g_free (priv->tooltip_unlock);
108   g_free (priv->tooltip_not_authorized);
109
110   g_object_unref (priv->icon_lock);
111   g_object_unref (priv->icon_unlock);
112   g_object_unref (priv->label_group);
113
114   if (priv->cancellable != NULL)
115     {
116       g_cancellable_cancel (priv->cancellable);
117       g_object_unref (priv->cancellable);
118     }
119
120   if (priv->permission)
121     {
122       g_signal_handlers_disconnect_by_func (priv->permission,
123                                             on_permission_changed,
124                                             button);
125       g_object_unref (priv->permission);
126     }
127
128   G_OBJECT_CLASS (gtk_lock_button_parent_class)->finalize (object);
129 }
130
131 static void
132 gtk_lock_button_get_property (GObject    *object,
133                               guint       property_id,
134                               GValue     *value,
135                               GParamSpec *pspec)
136 {
137   GtkLockButton *button = GTK_LOCK_BUTTON (object);
138   GtkLockButtonPrivate *priv = button->priv;
139
140   switch (property_id)
141     {
142     case PROP_PERMISSION:
143       g_value_set_object (value, priv->permission);
144       break;
145
146     case PROP_TEXT_LOCK:
147       g_value_set_string (value, gtk_label_get_text (GTK_LABEL (priv->label_lock)));
148       break;
149
150     case PROP_TEXT_UNLOCK:
151       g_value_set_string (value, gtk_label_get_text (GTK_LABEL (priv->label_unlock)));
152       break;
153
154     case PROP_TOOLTIP_LOCK:
155       g_value_set_string (value, priv->tooltip_lock);
156       break;
157
158     case PROP_TOOLTIP_UNLOCK:
159       g_value_set_string (value, priv->tooltip_unlock);
160       break;
161
162     case PROP_TOOLTIP_NOT_AUTHORIZED:
163       g_value_set_string (value, priv->tooltip_not_authorized);
164       break;
165
166     default:
167       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
168       break;
169     }
170 }
171
172 static void
173 gtk_lock_button_set_property (GObject      *object,
174                               guint         property_id,
175                               const GValue *value,
176                               GParamSpec   *pspec)
177 {
178   GtkLockButton *button = GTK_LOCK_BUTTON (object);
179   GtkLockButtonPrivate *priv = button->priv;
180
181   switch (property_id)
182     {
183     case PROP_PERMISSION:
184       gtk_lock_button_set_permission (button, g_value_get_object (value));
185       break;
186
187     case PROP_TEXT_LOCK:
188       gtk_label_set_text (GTK_LABEL (priv->label_lock), g_value_get_string (value));
189       break;
190
191     case PROP_TEXT_UNLOCK:
192       gtk_label_set_text (GTK_LABEL (priv->label_unlock), g_value_get_string (value));
193       break;
194
195     case PROP_TOOLTIP_LOCK:
196       g_free (priv->tooltip_lock);
197       priv->tooltip_lock = g_value_dup_string (value);
198       break;
199
200     case PROP_TOOLTIP_UNLOCK:
201       g_free (priv->tooltip_unlock);
202       priv->tooltip_unlock = g_value_dup_string (value);
203       break;
204
205     case PROP_TOOLTIP_NOT_AUTHORIZED:
206       g_free (priv->tooltip_not_authorized);
207       priv->tooltip_not_authorized = g_value_dup_string (value);
208       break;
209
210     default:
211       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
212       break;
213     }
214
215   update_state (button);
216 }
217
218 static void
219 gtk_lock_button_init (GtkLockButton *button)
220 {
221   GtkLockButtonPrivate *priv;
222   gchar *names[3];
223
224   button->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (button,
225                                                      GTK_TYPE_LOCK_BUTTON,
226                                                      GtkLockButtonPrivate);
227
228   priv->label_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
229   priv->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
230   gtk_widget_set_halign (priv->box, GTK_ALIGN_CENTER);
231   gtk_widget_set_valign (priv->box, GTK_ALIGN_CENTER);
232   gtk_widget_show (priv->box);
233   gtk_container_add (GTK_CONTAINER (button), priv->box);
234   priv->image = gtk_image_new ();
235   gtk_box_pack_start (GTK_BOX (priv->box), priv->image, FALSE, FALSE, 0);
236   gtk_widget_show (priv->image);
237   priv->label_lock = gtk_label_new ("");
238   gtk_misc_set_alignment (GTK_MISC (priv->label_lock), 0, 0.5);
239   gtk_widget_set_no_show_all (priv->label_lock, TRUE);
240   gtk_widget_show (priv->label_lock);
241   gtk_box_pack_start (GTK_BOX (priv->box), priv->label_lock, FALSE, FALSE, 0);
242   gtk_size_group_add_widget (priv->label_group, priv->label_lock);
243   priv->label_unlock = gtk_label_new ("");
244   gtk_misc_set_alignment (GTK_MISC (priv->label_unlock), 0, 0.5);
245   gtk_widget_set_no_show_all (priv->label_unlock, TRUE);
246   gtk_box_pack_start (GTK_BOX (priv->box), priv->label_unlock, FALSE, FALSE, 0);
247   gtk_size_group_add_widget (priv->label_group, priv->label_unlock);
248
249   names[0] = "changes-allow-symbolic";
250   names[1] = "changes-allow";
251   names[2] = NULL;
252   priv->icon_unlock = g_themed_icon_new_from_names (names, -1);
253
254   names[0] = "changes-prevent-symbolic";
255   names[1] = "changes-prevent";
256   names[2] = NULL;
257   priv->icon_lock = g_themed_icon_new_from_names (names, -1);
258
259   update_state (button);
260 }
261
262 static void
263 gtk_lock_button_class_init (GtkLockButtonClass *klass)
264 {
265   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
266   GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass);
267
268   gobject_class->finalize     = gtk_lock_button_finalize;
269   gobject_class->get_property = gtk_lock_button_get_property;
270   gobject_class->set_property = gtk_lock_button_set_property;
271
272   button_class->clicked = gtk_lock_button_clicked;
273
274   g_type_class_add_private (klass, sizeof (GtkLockButtonPrivate));
275
276   g_object_class_install_property (gobject_class, PROP_PERMISSION,
277     g_param_spec_object ("permission",
278                          P_("Permission"),
279                          P_("The GPermission object controlling this button"),
280                          G_TYPE_PERMISSION,
281                          G_PARAM_READWRITE |
282                          G_PARAM_STATIC_STRINGS));
283
284   g_object_class_install_property (gobject_class, PROP_TEXT_LOCK,
285     g_param_spec_string ("text-lock",
286                          P_("Lock Text"),
287                          P_("The text to display when prompting the user to lock"),
288                          _("Lock"),
289                          G_PARAM_READWRITE |
290                          G_PARAM_CONSTRUCT |
291                          G_PARAM_STATIC_STRINGS));
292
293   g_object_class_install_property (gobject_class, PROP_TEXT_UNLOCK,
294     g_param_spec_string ("text-unlock",
295                          P_("Unlock Text"),
296                          P_("The text to display when prompting the user to unlock"),
297                          _("Unlock"),
298                          G_PARAM_READWRITE |
299                          G_PARAM_CONSTRUCT |
300                          G_PARAM_STATIC_STRINGS));
301
302   g_object_class_install_property (gobject_class, PROP_TOOLTIP_LOCK,
303     g_param_spec_string ("tooltip-lock",
304                          P_("Lock Tooltip"),
305                          P_("The tooltip to display when prompting the user to lock"),
306                          _("Dialog is unlocked.\nClick to prevent further changes"),
307                          G_PARAM_READWRITE |
308                          G_PARAM_CONSTRUCT |
309                          G_PARAM_STATIC_STRINGS));
310
311   g_object_class_install_property (gobject_class, PROP_TOOLTIP_UNLOCK,
312     g_param_spec_string ("tooltip-unlock",
313                          P_("Unlock Tooltip"),
314                          P_("The tooltip to display when prompting the user to unlock"),
315                          _("Dialog is locked.\nClick to make changes"),
316                          G_PARAM_READWRITE |
317                          G_PARAM_CONSTRUCT |
318                          G_PARAM_STATIC_STRINGS));
319
320   g_object_class_install_property (gobject_class, PROP_TOOLTIP_NOT_AUTHORIZED,
321     g_param_spec_string ("tooltip-not-authorized",
322                          P_("Not Authorized Tooltip"),
323                          P_("The tooltip to display when prompting the user cannot obtain authorization"),
324                          _("System policy prevents changes.\nContact your system administrator"),
325                          G_PARAM_READWRITE |
326                          G_PARAM_CONSTRUCT |
327                          G_PARAM_STATIC_STRINGS));
328 }
329
330 static void
331 update_state (GtkLockButton *button)
332 {
333   GtkLockButtonPrivate *priv = button->priv;
334   gboolean allowed;
335   gboolean can_acquire;
336   gboolean can_release;
337   gboolean sensitive;
338   gboolean visible;
339   GIcon *icon;
340   const gchar *tooltip;
341
342   if (priv->permission)
343     {
344       allowed = g_permission_get_allowed (priv->permission);
345       can_acquire = g_permission_get_can_acquire (priv->permission);
346       can_release = g_permission_get_can_release (priv->permission);
347     }
348   else
349     {
350       allowed = TRUE;
351       can_acquire = FALSE;
352       can_release = FALSE;
353     }
354
355   if (allowed && can_release)
356     {
357       visible = TRUE;
358       sensitive = TRUE;
359       icon = priv->icon_lock;
360       tooltip = priv->tooltip_lock;
361     }
362   else if (allowed && !can_release)
363     {
364       visible = FALSE;
365       sensitive = TRUE;
366       icon = priv->icon_lock;
367       tooltip = priv->tooltip_lock;
368     }
369   else if (!allowed && can_acquire)
370     {
371       visible = TRUE;
372       sensitive = TRUE;
373       icon = priv->icon_unlock;
374       tooltip = priv->tooltip_unlock;
375     }
376   else if (!allowed && !can_acquire)
377     {
378       visible = TRUE;
379       sensitive = FALSE;
380       icon = priv->icon_unlock;
381       tooltip = priv->tooltip_not_authorized;
382     }
383   else
384     {
385       g_assert_not_reached ();
386     }
387
388   gtk_image_set_from_gicon (GTK_IMAGE (priv->image), icon, GTK_ICON_SIZE_BUTTON);
389   gtk_widget_set_visible (priv->label_lock, allowed);
390   gtk_widget_set_visible (priv->label_unlock, !allowed);
391   gtk_widget_set_tooltip_markup (GTK_WIDGET (button), tooltip);
392   gtk_widget_set_sensitive (GTK_WIDGET (button), sensitive);
393   gtk_widget_set_visible (GTK_WIDGET (button), visible);
394 }
395
396 static void
397 on_permission_changed (GPermission *permission,
398                        GParamSpec  *pspec,
399                        gpointer     user_data)
400 {
401   GtkLockButton *button = GTK_LOCK_BUTTON (user_data);
402
403   update_state (button);
404 }
405
406 static void
407 acquire_cb (GObject      *source,
408             GAsyncResult *result,
409             gpointer      user_data)
410 {
411   GtkLockButton *button = GTK_LOCK_BUTTON (user_data);
412   GtkLockButtonPrivate *priv = button->priv;
413   GError *error;
414
415   error = NULL;
416   if (!g_permission_acquire_finish (priv->permission, result, &error))
417     {
418       g_warning ("Error acquiring permission: %s", error->message);
419       g_error_free (error);
420     }
421
422   g_object_unref (priv->cancellable);
423   priv->cancellable = NULL;
424
425   update_state (button);
426 }
427
428 static void
429 release_cb (GObject      *source,
430             GAsyncResult *result,
431             gpointer      user_data)
432 {
433   GtkLockButton *button = GTK_LOCK_BUTTON (user_data);
434   GtkLockButtonPrivate *priv = button->priv;
435   GError *error;
436
437   error = NULL;
438   if (!g_permission_release_finish (priv->permission, result, &error))
439     {
440       g_warning ("Error releasing permission: %s", error->message);
441       g_error_free (error);
442     }
443
444   g_object_unref (priv->cancellable);
445   priv->cancellable = NULL;
446
447   update_state (button);
448 }
449
450 static void
451 gtk_lock_button_clicked (GtkButton *button)
452 {
453   GtkLockButtonPrivate *priv = GTK_LOCK_BUTTON (button)->priv;
454
455   /* if we already have a pending interactive check, then do nothing */
456   if (priv->cancellable != NULL)
457     return;
458
459   if (g_permission_get_allowed (priv->permission))
460     {
461       if (g_permission_get_can_release (priv->permission))
462         {
463           priv->cancellable = g_cancellable_new ();
464
465           g_permission_release_async (priv->permission,
466                                       priv->cancellable,
467                                       release_cb,
468                                       button);
469         }
470     }
471   else
472     {
473       if (g_permission_get_can_acquire (priv->permission))
474         {
475           priv->cancellable = g_cancellable_new ();
476
477           g_permission_acquire_async (priv->permission,
478                                       priv->cancellable,
479                                       acquire_cb,
480                                       button);
481         }
482     }
483 }
484
485 /**
486  * gtk_lock_button_new:
487  * @permission: (allow-none): a #GPermission
488  *
489  * Creates a new lock button which reflects the @permission.
490  *
491  * Returns: a new #GtkLockButton
492  *
493  * Since: 3.2
494  */
495 GtkWidget *
496 gtk_lock_button_new (GPermission *permission)
497 {
498   return GTK_WIDGET (g_object_new (GTK_TYPE_LOCK_BUTTON,
499                                    "permission", permission,
500                                    NULL));
501 }
502
503 /**
504  * gtk_lock_button_get_permission:
505  * @button: a #GtkLockButton
506  *
507  * Obtains the #GPermission object that controls @button.
508  *
509  * Returns: the #GPermission of @button
510  *
511  * Since: 3.2
512  */
513 GPermission *
514 gtk_lock_button_get_permission (GtkLockButton *button)
515 {
516   g_return_val_if_fail (GTK_IS_LOCK_BUTTON (button), NULL);
517
518   return button->priv->permission;
519 }
520
521 /**
522  * gtk_lock_button_set_permission:
523  * @button: a #GtkLockButton
524  * @permission: (allow-none): a #GPermission object, or %NULL
525  *
526  * Sets the #GPermission object that controls @button.
527  *
528  * Since: 3.2
529  */
530 void
531 gtk_lock_button_set_permission (GtkLockButton *button,
532                                 GPermission   *permission)
533 {
534   GtkLockButtonPrivate *priv;
535
536   g_return_if_fail (GTK_IS_LOCK_BUTTON (button));
537   g_return_if_fail (permission == NULL || G_IS_PERMISSION (permission));
538
539   priv = button->priv;
540
541   if (priv->permission != permission)
542     {
543       if (priv->permission)
544         {
545           g_signal_handlers_disconnect_by_func (priv->permission,
546                                                 on_permission_changed,
547                                                 button);
548           g_object_unref (priv->permission);
549         }
550
551       priv->permission = permission;
552
553       if (priv->permission)
554         {
555           g_object_ref (priv->permission);
556           g_signal_connect (priv->permission, "notify",
557                             G_CALLBACK (on_permission_changed), button);
558         }
559
560       update_state (button);
561
562       g_object_notify (G_OBJECT (button), "permission");
563     }
564 }