[accounts-dialog: 1/2] Some tooltip trickery



commit d6935060118cb232422aa0afe6f03902345a75b6
Author: Matthias Clasen <mclasen redhat com>
Date:   Mon Feb 1 22:18:25 2010 -0500

    Some tooltip trickery

 src/main.c     |   94 +++++++++++++++++++++++++-
 src/um-utils.c |  202 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/um-utils.h |   10 +++
 3 files changed, 304 insertions(+), 2 deletions(-)
---
diff --git a/src/main.c b/src/main.c
index 4440a82..aff24bd 100644
--- a/src/main.c
+++ b/src/main.c
@@ -53,6 +53,7 @@
 #include "um-password-dialog.h"
 #include "um-photo-dialog.h"
 #include "um-fingerprint-dialog.h"
+#include "um-utils.h"
 
 #include "gdm-languages.h"
 
@@ -1118,6 +1119,7 @@ lockbutton_changed (PolkitLockButton *button,
         gboolean is_authorized;
         gboolean self_selected;
         UmUser *user;
+        GtkWidget *widget;
 
         user = get_selected_user (d);
         if (!user) {
@@ -1127,8 +1129,29 @@ lockbutton_changed (PolkitLockButton *button,
         is_authorized = polkit_lock_button_get_is_authorized (button);
         self_selected = um_user_get_uid (user) == geteuid ();
 
-        gtk_widget_set_sensitive (get_widget (d, "add-user-button"), is_authorized);
-        gtk_widget_set_sensitive (get_widget (d, "delete-user-button"), is_authorized && !self_selected);
+        widget = get_widget (d, "add-user-button");
+        gtk_widget_set_sensitive (widget, is_authorized);
+        if (is_authorized) {
+                setup_tooltip_with_embedded_icon (widget, _("Create a user"), NULL, NULL);
+        }
+        else {
+                setup_tooltip_with_embedded_icon (widget,
+                                                  _("To create a user,\nclick the * icon first"),
+                                                  "*",
+                                                  "stock_lock");
+        }
+
+        widget = get_widget (d, "delete-user-button");
+        gtk_widget_set_sensitive (widget, is_authorized && !self_selected);
+        if (is_authorized) {
+                setup_tooltip_with_embedded_icon (widget, _("Delete the selected user"), NULL, NULL);
+        }
+        else {
+                setup_tooltip_with_embedded_icon (widget,
+                                                  _("To delete the selected user,\nclick the * icon first"),
+                                                  "*",
+                                                  "stock_lock");
+        }
 
         if (is_authorized) {
                 if (gtk_notebook_get_current_page (GTK_NOTEBOOK (get_widget (d, "account-type-notebook"))) == 0) {
@@ -1336,17 +1359,57 @@ setup_main_window (UserAccountDialog *d)
         button = get_widget (d, "user-list-button");
         g_signal_connect (button, "toggled", G_CALLBACK (toggle_user_list), d);
 
+        button = get_widget (d, "user-icon-nonbutton");
+        setup_tooltip_with_embedded_icon (button,
+                                          _("To make changes,\nclick the * icon first"),
+                                          "*",
+                                          "stock_lock");
+        g_signal_connect (button, "button-release-event",
+                           G_CALLBACK (show_tooltip_now), NULL);
+
+        button = get_widget (d, "full-name-value-label");
+        setup_tooltip_with_embedded_icon (button,
+                                          _("To make changes,\nclick the * icon first"),
+                                          "*",
+                                          "stock_lock");
+        g_signal_connect (button, "button-release-event",
+                           G_CALLBACK (show_tooltip_now), NULL);
+
         button = get_widget (d, "full-name-button");
         g_signal_connect (button, "clicked", G_CALLBACK (change_name), d);
 
+        button = get_widget (d, "account-type-value-label");
+        setup_tooltip_with_embedded_icon (button,
+                                          _("To change the account type,\nclick the * icon first"),
+                                          "*",
+                                          "stock_lock");
+        g_signal_connect (button, "button-release-event",
+                           G_CALLBACK (show_tooltip_now), NULL);
+
         button = get_widget (d, "account-type-button");
         g_signal_connect (button, "clicked", G_CALLBACK (change_account_type_start), d);
         button = get_widget (d, "account-type-combo");
         g_signal_connect (button, "changed", G_CALLBACK (account_type_changed), d);
 
+        button = get_widget (d, "account-password-value-label");
+        setup_tooltip_with_embedded_icon (button,
+                                          _("To make changes,\nclick the * icon first"),
+                                          "*",
+                                          "stock_lock");
+        g_signal_connect (button, "button-release-event",
+                           G_CALLBACK (show_tooltip_now), NULL);
+
         button = get_widget (d, "account-password-button");
         g_signal_connect (button, "clicked", G_CALLBACK (change_password), d);
 
+        button = get_widget (d, "account-email-value-label");
+        setup_tooltip_with_embedded_icon (button,
+                                          _("To make changes,\nclick the * icon first"),
+                                          "*",
+                                          "stock_lock");
+        g_signal_connect (button, "button-release-event",
+                           G_CALLBACK (show_tooltip_now), NULL);
+
         button = get_widget (d, "account-email-button");
         g_signal_connect (button, "clicked", G_CALLBACK (change_email_start), d);
         button = get_widget (d, "account-email-entry");
@@ -1354,6 +1417,14 @@ setup_main_window (UserAccountDialog *d)
         g_signal_connect (button, "focus-out-event", G_CALLBACK (change_email_focus_out), d);
         g_signal_connect (button, "key-press-event", G_CALLBACK (change_email_key_press), d);
 
+        button = get_widget (d, "account-language-value-label");
+        setup_tooltip_with_embedded_icon (button,
+                                          _("To make changes,\nclick the * icon first"),
+                                          "*",
+                                          "stock_lock");
+        g_signal_connect (button, "button-release-event",
+                           G_CALLBACK (show_tooltip_now), NULL);
+
         button = get_widget (d, "account-language-button");
         g_signal_connect (button, "clicked", G_CALLBACK (change_language_start), d);
         button = get_widget (d, "account-language-combo");
@@ -1363,6 +1434,14 @@ setup_main_window (UserAccountDialog *d)
         /* ugly hack to catch the combo boxes losing focus */
         g_signal_connect (window, "set-focus", G_CALLBACK (focus_moved), d);
 
+        button = get_widget (d, "account-location-value-label");
+        setup_tooltip_with_embedded_icon (button,
+                                          _("To make changes,\nclick the * icon first"),
+                                          "*",
+                                          "stock_lock");
+        g_signal_connect (button, "button-release-event",
+                           G_CALLBACK (show_tooltip_now), NULL);
+
         button = get_widget (d, "account-location-button");
         g_signal_connect (button, "clicked", G_CALLBACK (change_location_start), d);
         button = get_widget (d, "account-location-entry");
@@ -1382,6 +1461,17 @@ setup_main_window (UserAccountDialog *d)
                           G_CALLBACK (lockbutton_changed), d);
         lockbutton_changed (POLKIT_LOCK_BUTTON (button), d);
         d->lock_button = button;
+
+        button = get_widget (d, "add-user-button");
+        setup_tooltip_with_embedded_icon (button,
+                                          _("To create a user,\nclick the * icon first"),
+                                          "*",
+                                          "stock_lock");
+        button = get_widget (d, "delete-user-button");
+        setup_tooltip_with_embedded_icon (button,
+                                          _("To delete the selected user,\nclick the * icon first"),
+                                          "*",
+                                          "stock_lock");
 }
 
 static void
diff --git a/src/um-utils.c b/src/um-utils.c
index 26d7702..3c75cf5 100644
--- a/src/um-utils.c
+++ b/src/um-utils.c
@@ -82,3 +82,205 @@ um_compute_short_name (const gchar *name)
 
         return res;
 }
+
+typedef struct {
+        const gchar *text;
+        const gchar *placeholder_str;
+        const gchar *stock_id;
+        gunichar placeholder;
+        gulong query_id;
+} IconShapeData;
+
+static IconShapeData *
+icon_shape_data_new (const gchar *text,
+                     const gchar *placeholder,
+                     const gchar *stock_id)
+{
+        IconShapeData *data;
+
+        data = g_new0 (IconShapeData, 1);
+
+        data->text = g_strdup (text);
+        data->placeholder_str = g_strdup (placeholder);
+        data->placeholder = g_utf8_get_char_validated (placeholder, -1);
+        data->stock_id = g_strdup (stock_id);
+
+        return data;
+}
+
+static void
+icon_shape_data_free (IconShapeData *data)
+{
+        g_free (data->text);
+        g_free (data->placeholder_str);
+        g_free (data->stock_id);
+        g_free (data);
+}
+
+static void
+icon_shape_renderer (cairo_t        *cr,
+                     PangoAttrShape *attr,
+                     gboolean        do_path,
+                     gpointer        user_data)
+{
+        IconShapeData *data = user_data;
+        gdouble x, y;
+
+        cairo_get_current_point (cr, &x, &y);
+        if (GPOINTER_TO_UINT (attr->data) == data->placeholder) {
+                gdouble ascent;
+                gdouble height;
+                gdouble width;
+                GdkPixbuf *pixbuf;
+
+                ascent = pango_units_to_double (attr->ink_rect.y);
+                height = pango_units_to_double (attr->ink_rect.height);
+                width = pango_units_to_double (attr->ink_rect.width);
+                pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+                                                   data->stock_id,
+                                                   (gint)height,
+                                                   GTK_ICON_LOOKUP_FORCE_SIZE | GTK_ICON_LOOKUP_USE_BUILTIN,
+                                                   NULL);
+
+                cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+                cairo_reset_clip (cr);
+                gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y + ascent);
+                cairo_paint (cr);
+                g_object_unref (pixbuf);
+        }
+}
+
+PangoAttrList *
+create_shape_attr_list_for_layout (PangoLayout   *layout,
+                                   IconShapeData *data)
+{
+        PangoAttrList *attrs;
+        PangoFontMetrics *metrics;
+        gint ascent, descent;
+        PangoRectangle ink_rect, logical_rect;
+        const gchar *p;
+        const gchar *text;
+        gint placeholder_len;
+
+        /* Get font metrics and prepare fancy shape size */
+        metrics = pango_context_get_metrics (pango_layout_get_context (layout),
+                                             pango_layout_get_font_description (layout),
+                                             NULL);
+        ascent = pango_font_metrics_get_ascent (metrics);
+        descent = pango_font_metrics_get_descent (metrics);
+        pango_font_metrics_unref (metrics);
+
+        logical_rect.x = 0;
+        logical_rect.y = - ascent;
+        logical_rect.width = ascent + descent;
+        logical_rect.height = ascent + descent;
+
+        ink_rect = logical_rect;
+
+        attrs = pango_attr_list_new ();
+        text = pango_layout_get_text (layout);
+        placeholder_len = strlen (data->placeholder_str);
+        for (p = text; (p = strstr (p, data->placeholder_str)); p += placeholder_len) {
+                PangoAttribute *attr;
+
+                attr = pango_attr_shape_new_with_data (&ink_rect,
+                                                       &logical_rect,
+                                                       GUINT_TO_POINTER (g_utf8_get_char (p)),
+                                                       NULL, NULL);
+
+                attr->start_index = p - text;
+                attr->end_index = attr->start_index + placeholder_len;
+
+                pango_attr_list_insert (attrs, attr);
+        }
+
+        return attrs;
+}
+
+static gboolean
+query_unlock_tooltip (GtkWidget  *widget,
+                      gint        x,
+                      gint        y,
+                      gboolean    keyboard_tooltip,
+                      GtkTooltip *tooltip,
+                      gpointer    user_data)
+{
+        GtkWidget *label;
+        PangoLayout *layout;
+        PangoAttrList *attrs;
+        IconShapeData *data;
+
+        data = g_object_get_data (G_OBJECT (widget), "icon-shape-data");
+        label = g_object_get_data (G_OBJECT (widget), "tooltip-label");
+        if (label == NULL) {
+                label = gtk_label_new (data->text);
+                g_object_ref_sink (label);
+                g_object_set_data_full (G_OBJECT (widget),
+                                        "tooltip-label", label, g_object_unref);
+        }
+
+        layout = gtk_label_get_layout (GTK_LABEL (label));
+        pango_cairo_context_set_shape_renderer (pango_layout_get_context (layout),
+                                                icon_shape_renderer,
+                                                data, NULL);
+
+        attrs = create_shape_attr_list_for_layout (layout, data);
+        gtk_label_set_attributes (GTK_LABEL (label), attrs);
+        pango_attr_list_unref (attrs);
+
+        gtk_tooltip_set_custom (tooltip, label);
+
+        return TRUE;
+}
+
+void
+setup_tooltip_with_embedded_icon (GtkWidget   *widget,
+                                  const gchar *text,
+                                  const gchar *placeholder,
+                                  const gchar *stock_id)
+{
+        GtkWidget *label;
+        IconShapeData *data;
+
+        data = g_object_get_data (G_OBJECT (widget), "icon-shape-data");
+        if (data) {
+                gtk_widget_set_has_tooltip (widget, FALSE);
+                g_signal_handler_disconnect (widget, data->query_id);
+                g_object_set_data (G_OBJECT (widget), "icon-shape-data", NULL);
+                g_object_set_data (G_OBJECT (widget), "tooltip-label", NULL);
+        }
+
+        if (!placeholder) {
+                gtk_widget_set_tooltip_text (widget, text);
+                return;
+        }
+
+        data = icon_shape_data_new (text, placeholder, stock_id);
+        g_object_set_data_full (G_OBJECT (widget),
+                                "icon-shape-data",
+                                data,
+                                icon_shape_data_free);
+
+        gtk_widget_set_has_tooltip (widget, TRUE);
+        data->query_id = g_signal_connect (widget, "query-tooltip",
+                                           G_CALLBACK (query_unlock_tooltip), NULL);
+
+}
+
+gboolean
+show_tooltip_now (GtkWidget *widget,
+                  GdkEvent  *event)
+{
+        GtkSettings *settings;
+        gint timeout;
+
+        settings = gtk_widget_get_settings (widget);
+
+        g_object_get (settings, "gtk-tooltip-timeout", &timeout, NULL);
+        g_object_set (settings, "gtk-tooltip-timeout", 1, NULL);
+        gtk_tooltip_trigger_tooltip_query (gtk_widget_get_display (widget));
+        g_object_set (settings, "gtk-tooltip-timeout", timeout, NULL);
+
+        return FALSE;
+}
+
diff --git a/src/um-utils.h b/src/um-utils.h
index 6788107..5d49bf6 100644
--- a/src/um-utils.h
+++ b/src/um-utils.h
@@ -22,10 +22,20 @@
 #ifndef __UM_UTILS_H__
 #define __UM_UTILS_H__
 
+#include <gtk/gtk.h>
+
 G_BEGIN_DECLS
 
 gchar *um_compute_short_name (const gchar *name);
 
+void     setup_tooltip_with_embedded_icon (GtkWidget   *widget,
+                                           const gchar *text,
+                                           const gchar *placeholder,
+                                           const gchar *stock_id);
+gboolean show_tooltip_now                 (GtkWidget   *widget,
+                                           GdkEvent    *event);
+
+
 G_END_DECLS
 
 #endif



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]