1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2010 Red Hat, Inc.
3 * Author: Matthias Clasen
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.
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.
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.
23 #include "gtklockbutton.h"
25 #include "gtkbutton.h"
27 #include "gtkeventbox.h"
29 #include "gtknotebook.h"
33 * SECTION:gtklockbutton
34 * @title: GtkLockButton
35 * @short_description: A widget to unlock or lock privileged operations
37 * GtkLockButton is a widget that can be used in control panels or
38 * preference dialogs to allow users to obtain and revoke authorizations
39 * needed to operate the controls. The required authorization is represented
40 * by a #GPermission object. Concrete implementations of #GPermission may use
41 * PolicyKit or some other authorization framework.
43 * If the user lacks the authorization but authorization can be obtained
44 * through authentication, the widget looks like this
45 * <informalexample><inlinegraphic fileref="lockbutton-locked.png"></inlinegraphic></informalexample>
46 * and the user can click the button to obtain the authorization. Depending
47 * on the platform, this may pop up an authentication dialog or ask the user
48 * to authenticate in some other way. Once authorization is obtained, the
49 * widget changes to this
50 * <informalexample><inlinegraphic fileref="lockbutton-unlocked.png"></inlinegraphic></informalexample>
51 * and the authorization can be dropped by clicking the button. If the user
52 * is not able to obtain authorization at all, the widget looks like this
53 * <informalexample><inlinegraphic fileref="lockbutton-sorry.png"></inlinegraphic></informalexample>
54 * If the user is authorized and cannot drop the authorization, the button
57 * The text (and tooltips) that are shown in the various cases can be adjusted
58 * with the #GtkLockButton:text-lock, #GtkLockButton:text-unlock,
59 * #GtkLockButton:text-not-authorized, #GtkLockButton:tooltip-lock,
60 * #GtkLockButton:tooltip-unlock and #GtkLockButton:tooltip-not-authorized
64 struct _GtkLockButtonPrivate
66 GPermission *permission;
69 gchar *tooltip_unlock;
70 gchar *tooltip_not_authorized;
78 GtkWidget *label_lock;
79 GtkWidget *label_unlock;
80 GtkWidget *label_not_authorized;
82 GCancellable *cancellable;
91 PROP_TEXT_NOT_AUTHORIZED,
94 PROP_TOOLTIP_NOT_AUTHORIZED
97 static void update_state (GtkLockButton *button);
98 static void update_tooltip (GtkLockButton *button);
100 static void on_permission_changed (GPermission *permission,
104 static void on_clicked (GtkButton *button,
107 static void on_button_press (GtkWidget *widget,
108 GdkEventButton *event,
111 G_DEFINE_TYPE (GtkLockButton, gtk_lock_button, GTK_TYPE_BIN);
114 gtk_lock_button_finalize (GObject *object)
116 GtkLockButton *button = GTK_LOCK_BUTTON (object);
117 GtkLockButtonPrivate *priv = button->priv;
119 g_free (priv->tooltip_lock);
120 g_free (priv->tooltip_unlock);
121 g_free (priv->tooltip_not_authorized);
123 if (priv->cancellable != NULL)
125 g_cancellable_cancel (priv->cancellable);
126 g_object_unref (priv->cancellable);
129 if (priv->permission)
131 g_signal_handlers_disconnect_by_func (priv->permission,
132 on_permission_changed,
134 g_object_unref (priv->permission);
137 G_OBJECT_CLASS (gtk_lock_button_parent_class)->finalize (object);
141 gtk_lock_button_get_property (GObject *object,
146 GtkLockButton *button = GTK_LOCK_BUTTON (object);
147 GtkLockButtonPrivate *priv = button->priv;
151 case PROP_PERMISSION:
152 g_value_set_object (value, priv->permission);
156 g_value_set_string (value,
157 gtk_label_get_text (GTK_LABEL (priv->label_lock)));
160 case PROP_TEXT_UNLOCK:
161 g_value_set_string (value,
162 gtk_label_get_text (GTK_LABEL (priv->label_unlock)));
165 case PROP_TEXT_NOT_AUTHORIZED:
166 g_value_set_string (value,
167 gtk_label_get_text (GTK_LABEL (priv->label_not_authorized)));
170 case PROP_TOOLTIP_LOCK:
171 g_value_set_string (value, priv->tooltip_lock);
174 case PROP_TOOLTIP_UNLOCK:
175 g_value_set_string (value, priv->tooltip_unlock);
178 case PROP_TOOLTIP_NOT_AUTHORIZED:
179 g_value_set_string (value, priv->tooltip_not_authorized);
183 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
189 gtk_lock_button_set_property (GObject *object,
194 GtkLockButton *button = GTK_LOCK_BUTTON (object);
195 GtkLockButtonPrivate *priv = button->priv;
199 case PROP_PERMISSION:
200 gtk_lock_button_set_permission (button, g_value_get_object (value));
204 gtk_label_set_text (GTK_LABEL (priv->label_lock), g_value_get_string (value));
207 case PROP_TEXT_UNLOCK:
208 gtk_label_set_text (GTK_LABEL (priv->label_unlock), g_value_get_string (value));
211 case PROP_TEXT_NOT_AUTHORIZED:
212 gtk_label_set_text (GTK_LABEL (priv->label_not_authorized), g_value_get_string (value));
215 case PROP_TOOLTIP_LOCK:
216 g_free (priv->tooltip_lock);
217 priv->tooltip_lock = g_value_dup_string (value);
218 update_tooltip (button);
221 case PROP_TOOLTIP_UNLOCK:
222 g_free (priv->tooltip_unlock);
223 priv->tooltip_unlock = g_value_dup_string (value);
224 update_tooltip (button);
227 case PROP_TOOLTIP_NOT_AUTHORIZED:
228 g_free (priv->tooltip_not_authorized);
229 priv->tooltip_not_authorized = g_value_dup_string (value);
230 update_tooltip (button);
234 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
240 gtk_lock_button_init (GtkLockButton *button)
242 GtkLockButtonPrivate *priv;
244 button->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (button,
245 GTK_TYPE_LOCK_BUTTON,
246 GtkLockButtonPrivate);
248 priv->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
249 gtk_container_add (GTK_CONTAINER (button), priv->box);
251 priv->eventbox = gtk_event_box_new ();
252 gtk_event_box_set_visible_window (GTK_EVENT_BOX (priv->eventbox), FALSE);
253 gtk_container_add (GTK_CONTAINER (priv->box), priv->eventbox);
254 gtk_widget_show (priv->eventbox);
255 priv->image = gtk_image_new ();
256 gtk_container_add (GTK_CONTAINER (priv->eventbox), priv->image);
257 gtk_widget_show (priv->image);
259 priv->notebook = gtk_notebook_new ();
260 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->notebook), FALSE);
261 gtk_notebook_set_show_border (GTK_NOTEBOOK (priv->notebook), FALSE);
262 gtk_widget_show (priv->notebook);
264 priv->button = gtk_button_new ();
265 gtk_container_add (GTK_CONTAINER (priv->button), priv->notebook);
266 gtk_widget_show (priv->button);
268 priv->label_lock = gtk_label_new ("");
269 gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook), priv->label_lock, NULL);
270 gtk_widget_show (priv->label_lock);
272 priv->label_unlock = gtk_label_new ("");
273 gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook), priv->label_unlock, NULL);
274 gtk_widget_show (priv->label_unlock);
276 priv->label_not_authorized = gtk_label_new ("");
277 gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook), priv->label_not_authorized, NULL);
278 gtk_widget_show (priv->label_not_authorized);
280 gtk_box_pack_start (GTK_BOX (priv->box), priv->button, FALSE, FALSE, 0);
281 gtk_widget_show (priv->button);
283 g_signal_connect (priv->eventbox, "button-press-event",
284 G_CALLBACK (on_button_press), button);
285 g_signal_connect (priv->button, "clicked",
286 G_CALLBACK (on_clicked), button);
288 gtk_widget_set_no_show_all (priv->box, TRUE);
290 update_state (button);
294 gtk_lock_button_get_preferred_width (GtkWidget *widget,
298 GtkLockButtonPrivate *priv = GTK_LOCK_BUTTON (widget)->priv;
300 gtk_widget_get_preferred_width (priv->box, minimum, natural);
304 gtk_lock_button_get_preferred_height (GtkWidget *widget,
308 GtkLockButtonPrivate *priv = GTK_LOCK_BUTTON (widget)->priv;
310 gtk_widget_get_preferred_height (priv->box, minimum, natural);
314 gtk_lock_button_size_allocate (GtkWidget *widget,
315 GtkAllocation *allocation)
317 GtkLockButtonPrivate *priv = GTK_LOCK_BUTTON (widget)->priv;
318 GtkRequisition requisition;
319 GtkAllocation child_allocation;
321 gtk_widget_set_allocation (widget, allocation);
322 gtk_widget_get_preferred_size (priv->box, &requisition, NULL);
323 child_allocation.x = allocation->x;
324 child_allocation.y = allocation->y;
325 child_allocation.width = requisition.width;
326 child_allocation.height = requisition.height;
327 gtk_widget_size_allocate (priv->box, &child_allocation);
331 gtk_lock_button_class_init (GtkLockButtonClass *klass)
333 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
334 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
336 gobject_class->finalize = gtk_lock_button_finalize;
337 gobject_class->get_property = gtk_lock_button_get_property;
338 gobject_class->set_property = gtk_lock_button_set_property;
340 widget_class->get_preferred_width = gtk_lock_button_get_preferred_width;
341 widget_class->get_preferred_height = gtk_lock_button_get_preferred_height;
342 widget_class->size_allocate = gtk_lock_button_size_allocate;
344 g_type_class_add_private (klass, sizeof (GtkLockButtonPrivate));
346 g_object_class_install_property (gobject_class, PROP_PERMISSION,
347 g_param_spec_object ("permission",
349 P_("The GPermission object controlling this button"),
352 G_PARAM_STATIC_STRINGS));
354 g_object_class_install_property (gobject_class, PROP_TEXT_LOCK,
355 g_param_spec_string ("text-lock",
357 P_("The text to display when prompting the user to lock"),
361 G_PARAM_STATIC_STRINGS));
363 g_object_class_install_property (gobject_class, PROP_TEXT_UNLOCK,
364 g_param_spec_string ("text-unlock",
366 P_("The text to display when prompting the user to unlock"),
370 G_PARAM_STATIC_STRINGS));
372 g_object_class_install_property (gobject_class, PROP_TEXT_NOT_AUTHORIZED,
373 g_param_spec_string ("text-not-authorized",
374 P_("Not Authorized Text"),
375 P_("The text to display when prompting the user cannot obtain authorization"),
379 G_PARAM_STATIC_STRINGS));
381 g_object_class_install_property (gobject_class, PROP_TOOLTIP_LOCK,
382 g_param_spec_string ("tooltip-lock",
384 P_("The tooltip to display when prompting the user to lock"),
385 _("Dialog is unlocked.\nClick to prevent further changes"),
388 G_PARAM_STATIC_STRINGS));
390 g_object_class_install_property (gobject_class, PROP_TOOLTIP_UNLOCK,
391 g_param_spec_string ("tooltip-unlock",
392 P_("Unlock Tooltip"),
393 P_("The tooltip to display when prompting the user to unlock"),
394 _("Dialog is locked.\nClick to make changes"),
397 G_PARAM_STATIC_STRINGS));
399 g_object_class_install_property (gobject_class, PROP_TOOLTIP_NOT_AUTHORIZED,
400 g_param_spec_string ("tooltip-not-authorized",
401 P_("Not Authorized Tooltip"),
402 P_("The tooltip to display when prompting the user cannot obtain authorization"),
403 _("System policy prevents changes.\nContact your system administrator"),
406 G_PARAM_STATIC_STRINGS));
410 update_tooltip (GtkLockButton *button)
412 GtkLockButtonPrivate *priv = button->priv;
413 const gchar *tooltip;
415 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook)))
418 tooltip = priv->tooltip_lock;
421 tooltip = priv->tooltip_unlock;
424 tooltip = priv->tooltip_not_authorized;
431 gtk_widget_set_tooltip_markup (priv->box, tooltip);
435 update_state (GtkLockButton *button)
437 GtkLockButtonPrivate *priv = button->priv;
439 gboolean can_acquire;
440 gboolean can_release;
446 if (priv->permission)
448 allowed = g_permission_get_allowed (priv->permission);
449 can_acquire = g_permission_get_can_acquire (priv->permission);
450 can_release = g_permission_get_can_release (priv->permission);
493 names[0] = "changes-allow-symbolic";
494 names[1] = "changes-allow";
496 icon = g_themed_icon_new_from_names (names, -1);
502 names[0] = "changes-prevent-symbolic";
503 names[1] = "changes-prevent";
505 icon = g_themed_icon_new_from_names (names, -1);
508 gtk_image_set_from_gicon (GTK_IMAGE (priv->image), icon, GTK_ICON_SIZE_BUTTON);
509 g_object_unref (icon);
511 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), page);
512 gtk_widget_set_sensitive (priv->box, sensitive);
513 gtk_widget_set_visible (priv->box, visible);
515 update_tooltip (button);
519 on_permission_changed (GPermission *permission,
523 GtkLockButton *button = GTK_LOCK_BUTTON (user_data);
525 update_state (button);
529 acquire_cb (GObject *source,
530 GAsyncResult *result,
533 GtkLockButton *button = GTK_LOCK_BUTTON (user_data);
534 GtkLockButtonPrivate *priv = button->priv;
538 if (!g_permission_acquire_finish (priv->permission, result, &error))
540 g_warning ("Error acquiring permission: %s", error->message);
541 g_error_free (error);
544 g_object_unref (priv->cancellable);
545 priv->cancellable = NULL;
547 update_state (button);
551 release_cb (GObject *source,
552 GAsyncResult *result,
555 GtkLockButton *button = GTK_LOCK_BUTTON (user_data);
556 GtkLockButtonPrivate *priv = button->priv;
560 if (!g_permission_release_finish (priv->permission, result, &error))
562 g_warning ("Error releasing permission: %s", error->message);
563 g_error_free (error);
566 g_object_unref (priv->cancellable);
567 priv->cancellable = NULL;
569 update_state (button);
573 handle_click (GtkLockButton *button)
575 GtkLockButtonPrivate *priv = button->priv;
577 /* if we already have a pending interactive check, then do nothing */
578 if (priv->cancellable != NULL)
581 if (g_permission_get_allowed (priv->permission))
583 if (g_permission_get_can_release (priv->permission))
585 priv->cancellable = g_cancellable_new ();
587 g_permission_release_async (priv->permission,
595 if (g_permission_get_can_acquire (priv->permission))
597 priv->cancellable = g_cancellable_new ();
599 g_permission_acquire_async (priv->permission,
608 on_clicked (GtkButton *button,
612 handle_click (GTK_LOCK_BUTTON (user_data));
616 on_button_press (GtkWidget *widget,
617 GdkEventButton *event,
620 handle_click (GTK_LOCK_BUTTON (user_data));
625 * gtk_lock_button_new:
626 * @permission: (allow-none): a #GPermission
628 * Creates a new lock button which reflects the @permission.
630 * Returns: a new #GtkLockButton
635 gtk_lock_button_new (GPermission *permission)
637 return GTK_WIDGET (g_object_new (GTK_TYPE_LOCK_BUTTON,
638 "permission", permission,
643 * gtk_lock_button_get_permission:
644 * @button: a #GtkLockButton
646 * Obtains the #GPermission object that controls @button.
648 * Returns: the #GPermission of @button
653 gtk_lock_button_get_permission (GtkLockButton *button)
655 g_return_val_if_fail (GTK_IS_LOCK_BUTTON (button), NULL);
657 return button->priv->permission;
661 * gtk_lock_button_set_permission:
662 * @button: a #GtkLockButton
663 * @permission: (allow-none): a #GPermission object, or %NULL
665 * Sets the #GPermission object that controls @button.
670 gtk_lock_button_set_permission (GtkLockButton *button,
671 GPermission *permission)
673 GtkLockButtonPrivate *priv;
675 g_return_if_fail (GTK_IS_LOCK_BUTTON (button));
676 g_return_if_fail (permission == NULL || G_IS_PERMISSION (permission));
680 if (priv->permission != permission)
682 if (priv->permission)
684 g_signal_handlers_disconnect_by_func (priv->permission,
685 on_permission_changed,
687 g_object_unref (priv->permission);
690 priv->permission = permission;
692 if (priv->permission)
694 g_object_ref (priv->permission);
695 g_signal_connect (priv->permission, "notify",
696 G_CALLBACK (on_permission_changed), button);
699 update_state (button);
701 g_object_notify (G_OBJECT (button), "permission");