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