[gnome-initial-setup] accounts: Bring back the avatar chooser



commit 06366dad1b639f2a58097bf0c0859730aa3ccb53
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Feb 25 01:30:55 2014 -0500

    accounts: Bring back the avatar chooser
    
    Since the design has an avatar chooser again now,
    bring it back.

 configure.ac                                       |   11 +
 gnome-initial-setup/pages/account/Makefile.am      |    1 +
 .../pages/account/gis-account-page-local.c         |   86 +++++
 .../pages/account/gis-account-page-local.ui        |    2 +-
 .../pages/account/um-photo-dialog.c                |  391 ++++++++++++++++++++
 .../pages/account/um-photo-dialog.h                |   42 ++
 gnome-initial-setup/pages/account/um-utils.c       |   88 +++++
 gnome-initial-setup/pages/account/um-utils.h       |   13 +
 8 files changed, 633 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 593f2c1..38050a5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -44,6 +44,17 @@ PKG_CHECK_MODULES(INITIAL_SETUP,
                   json-glib-1.0
                   pwquality)
 
+PKG_CHECK_MODULES(CHEESE,
+                  cheese
+                  cheese-gtk >= 3.3.5,
+                  have_cheese=yes, have_cheese=no)
+AM_CONDITIONAL(HAVE_CHEESE, [test x$have_cheese = xyes])
+if test x$have_cheese = xyes; then
+  INITIAL_SETUP_CFLAGS="$INITIAL_SETUP_CFLAGS $CHEESE_CFLAGS"
+  INITIAL_SETUP_LIBS="$INITIAL_SETUP_LIBS $CHEESE_LIBS"
+  AC_DEFINE(HAVE_CHEESE, 1, [Build with Cheese support?])
+fi
+
 PKG_CHECK_MODULES(COPY_WORKER, gio-2.0)
 
 AC_ARG_ENABLE(ibus,
diff --git a/gnome-initial-setup/pages/account/Makefile.am b/gnome-initial-setup/pages/account/Makefile.am
index 051d7ba..5250214 100644
--- a/gnome-initial-setup/pages/account/Makefile.am
+++ b/gnome-initial-setup/pages/account/Makefile.am
@@ -30,6 +30,7 @@ libgisaccount_la_SOURCES =                                            \
        gis-account-page-enterprise.c gis-account-page-enterprise.h     \
        um-realm-manager.c um-realm-manager.h                           \
        um-utils.c um-utils.h                                           \
+       um-photo-dialog.c um-photo-dialog.h                             \
        $(NULL)
 
 libgisaccount_la_CFLAGS = $(INITIAL_SETUP_CFLAGS) -I "$(srcdir)/../.."
diff --git a/gnome-initial-setup/pages/account/gis-account-page-local.c 
b/gnome-initial-setup/pages/account/gis-account-page-local.c
index 60c07d9..17a0a53 100644
--- a/gnome-initial-setup/pages/account/gis-account-page-local.c
+++ b/gnome-initial-setup/pages/account/gis-account-page-local.c
@@ -31,6 +31,7 @@
 #include <string.h>
 #include <act/act-user-manager.h>
 #include "um-utils.h"
+#include "um-photo-dialog.h"
 
 #define GOA_API_IS_SUBJECT_TO_CHANGE
 #include <goa/goa.h>
@@ -41,10 +42,15 @@
 
 struct _GisAccountPageLocalPrivate
 {
+  GtkWidget *avatar_button;
   GtkWidget *avatar_image;
   GtkWidget *subtitle;
   GtkWidget *fullname_entry;
   GtkWidget *username_combo;
+  UmPhotoDialog *photo_dialog;
+
+  GdkPixbuf *avatar_pixbuf;
+  gchar *avatar_filename;
 
   ActUser *act_user;
   ActUserManager *act_client;
@@ -190,6 +196,7 @@ prepopulate_account_page (GisAccountPageLocal *page)
 
   if (pixbuf) {
     gtk_image_set_from_pixbuf (GTK_IMAGE (priv->avatar_image), pixbuf);
+    priv->avatar_pixbuf = pixbuf;
   }
 
   g_free (name);
@@ -262,6 +269,37 @@ username_changed (GtkComboBoxText     *combo,
 }
 
 static void
+avatar_callback (GdkPixbuf   *pixbuf,
+                 const gchar *filename,
+                 gpointer     user_data)
+{
+  GisAccountPageLocal *page = user_data;
+  GisAccountPageLocalPrivate *priv = gis_account_page_local_get_instance_private (page);
+  GdkPixbuf *tmp;
+
+  g_clear_object (&priv->avatar_pixbuf);
+  g_free (priv->avatar_filename);
+  priv->avatar_filename = NULL;
+
+  if (pixbuf) {
+    priv->avatar_pixbuf = g_object_ref (pixbuf);
+    tmp = gdk_pixbuf_scale_simple (pixbuf, 96, 96, GDK_INTERP_BILINEAR);
+    gtk_image_set_from_pixbuf (GTK_IMAGE (priv->avatar_image), tmp);
+    g_object_unref (tmp);
+  }
+  else if (filename) {
+    priv->avatar_filename = g_strdup (filename);
+    tmp = gdk_pixbuf_new_from_file_at_size (filename, 96, 96, NULL);
+    gtk_image_set_from_pixbuf (GTK_IMAGE (priv->avatar_image), tmp);
+    g_object_unref (tmp);
+  }
+  else {
+    gtk_image_set_pixel_size (GTK_IMAGE (priv->avatar_image), 96);
+    gtk_image_set_from_icon_name (GTK_IMAGE (priv->avatar_image), "avatar-default-symbolic", 1);
+  }
+}
+
+static void
 gis_account_page_local_constructed (GObject *object)
 {
   GisAccountPageLocal *page = GIS_ACCOUNT_PAGE_LOCAL (object);
@@ -297,6 +335,10 @@ gis_account_page_local_constructed (GObject *object)
                       G_CALLBACK (accounts_changed), page);
     prepopulate_account_page (page);
   }
+
+  priv->photo_dialog = um_photo_dialog_new (priv->avatar_button,
+                                            avatar_callback,
+                                            page);
 }
 
 static void
@@ -306,11 +348,52 @@ gis_account_page_local_dispose (GObject *object)
   GisAccountPageLocalPrivate *priv = gis_account_page_local_get_instance_private (page);
 
   g_clear_object (&priv->goa_client);
+  g_clear_object (&priv->avatar_pixbuf);
+  g_clear_pointer (&priv->avatar_filename, g_free);
+  g_clear_pointer (&priv->photo_dialog, um_photo_dialog_free);
 
   G_OBJECT_CLASS (gis_account_page_local_parent_class)->dispose (object);
 }
 
 static void
+set_user_avatar (GisAccountPageLocal *page)
+{
+  GisAccountPageLocalPrivate *priv = gis_account_page_local_get_instance_private (page);
+  GFile *file = NULL;
+  GFileIOStream *io_stream = NULL;
+  GOutputStream *stream = NULL;
+  GError *error = NULL;
+
+  if (priv->avatar_filename != NULL) {
+    act_user_set_icon_file (priv->act_user, priv->avatar_filename);
+    return;
+  }
+
+  if (priv->avatar_pixbuf == NULL) {
+    return;
+  }
+
+  file = g_file_new_tmp ("usericonXXXXXX", &io_stream, &error);
+  if (error != NULL)
+    goto out;
+
+  stream = g_io_stream_get_output_stream (G_IO_STREAM (io_stream));
+  if (!gdk_pixbuf_save_to_stream (priv->avatar_pixbuf, stream, "png", NULL, &error, NULL))
+    goto out;
+
+  act_user_set_icon_file (priv->act_user, g_file_get_path (file));
+
+ out:
+  if (error != NULL) {
+    g_warning ("failed to save image: %s", error->message);
+    g_error_free (error);
+  }
+  g_clear_object (&stream);
+  g_clear_object (&io_stream);
+  g_clear_object (&file);
+}
+
+static void
 local_create_user (GisAccountPageLocal *page)
 {
   GisAccountPageLocalPrivate *priv = gis_account_page_local_get_instance_private (page);
@@ -331,6 +414,8 @@ local_create_user (GisAccountPageLocal *page)
   act_user_set_user_name (priv->act_user, username);
   act_user_set_account_type (priv->act_user, priv->account_type);
 
+  set_user_avatar (page);
+
   g_signal_emit (page, signals[USER_CREATED], 0, priv->act_user, "");
 }
 
@@ -341,6 +426,7 @@ gis_account_page_local_class_init (GisAccountPageLocalClass *klass)
 
   gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), 
"/org/gnome/initial-setup/gis-account-page-local.ui");
 
+  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAccountPageLocal, 
avatar_button);
   gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAccountPageLocal, avatar_image);
   gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAccountPageLocal, subtitle);
   gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAccountPageLocal, 
fullname_entry);
diff --git a/gnome-initial-setup/pages/account/gis-account-page-local.ui 
b/gnome-initial-setup/pages/account/gis-account-page-local.ui
index 289a5fe..865fa07 100644
--- a/gnome-initial-setup/pages/account/gis-account-page-local.ui
+++ b/gnome-initial-setup/pages/account/gis-account-page-local.ui
@@ -9,7 +9,7 @@
         <property name="valign">fill</property>
         <property name="orientation">vertical</property>
         <child>
-          <object class="GtkButton" id="avatar_button">
+          <object class="GtkToggleButton" id="avatar_button">
             <property name="visible">True</property>
             <property name="margin_top">54</property>
             <property name="halign">center</property>
diff --git a/gnome-initial-setup/pages/account/um-photo-dialog.c 
b/gnome-initial-setup/pages/account/um-photo-dialog.c
new file mode 100644
index 0000000..79ecc74
--- /dev/null
+++ b/gnome-initial-setup/pages/account/um-photo-dialog.c
@@ -0,0 +1,391 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2009-2010  Red Hat, Inc,
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ *
+ * Written by: Matthias Clasen <mclasen redhat com>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#ifdef HAVE_CHEESE
+#include <cheese-avatar-chooser.h>
+#include <cheese-camera-device.h>
+#include <cheese-camera-device-monitor.h>
+#endif /* HAVE_CHEESE */
+
+#include "um-photo-dialog.h"
+#include "um-utils.h"
+
+#define ROW_SPAN 6
+
+struct _UmPhotoDialog {
+        GtkWidget *photo_popup;
+        GtkWidget *popup_button;
+
+#ifdef HAVE_CHEESE
+        CheeseCameraDeviceMonitor *monitor;
+        GtkWidget *take_photo_menuitem;
+        guint num_cameras;
+#endif /* HAVE_CHEESE */
+
+        SelectAvatarCallback *callback;
+        gpointer              data;
+};
+
+static void
+none_icon_selected (GtkMenuItem   *menuitem,
+                    UmPhotoDialog *um)
+{
+        um->callback (NULL, NULL, um->data);
+}
+
+#ifdef HAVE_CHEESE
+static gboolean
+destroy_chooser (GtkWidget *chooser)
+{
+        gtk_widget_destroy (chooser);
+        return FALSE;
+}
+
+static void
+webcam_response_cb (GtkDialog     *dialog,
+                    int            response,
+                    UmPhotoDialog  *um)
+{
+        if (response == GTK_RESPONSE_ACCEPT) {
+                GdkPixbuf *pb, *pb2;
+
+                g_object_get (G_OBJECT (dialog), "pixbuf", &pb, NULL);
+                pb2 = gdk_pixbuf_scale_simple (pb, 96, 96, GDK_INTERP_BILINEAR);
+
+                um->callback (pb2, NULL, um->data);
+
+                g_object_unref (pb2);
+                g_object_unref (pb);
+        }
+        if (response != GTK_RESPONSE_DELETE_EVENT &&
+            response != GTK_RESPONSE_NONE)
+                g_idle_add ((GSourceFunc) destroy_chooser, dialog);
+}
+
+static void
+webcam_icon_selected (GtkMenuItem   *menuitem,
+                      UmPhotoDialog *um)
+{
+        GtkWidget *window;
+
+        window = cheese_avatar_chooser_new ();
+        gtk_window_set_transient_for (GTK_WINDOW (window),
+                                      GTK_WINDOW (gtk_widget_get_toplevel (um->popup_button)));
+        gtk_window_set_modal (GTK_WINDOW (window), TRUE);
+        g_signal_connect (G_OBJECT (window), "response",
+                          G_CALLBACK (webcam_response_cb), um);
+        gtk_widget_show (window);
+}
+
+static void
+update_photo_menu_status (UmPhotoDialog *um)
+{
+        if (um->num_cameras == 0)
+                gtk_widget_set_sensitive (um->take_photo_menuitem, FALSE);
+        else
+                gtk_widget_set_sensitive (um->take_photo_menuitem, TRUE);
+}
+
+static void
+device_added (CheeseCameraDeviceMonitor *monitor,
+              CheeseCameraDevice        *device,
+              UmPhotoDialog             *um)
+{
+        um->num_cameras++;
+        update_photo_menu_status (um);
+}
+
+static void
+device_removed (CheeseCameraDeviceMonitor *monitor,
+                const char                *id,
+                UmPhotoDialog             *um)
+{
+        um->num_cameras--;
+        update_photo_menu_status (um);
+}
+
+#endif /* HAVE_CHEESE */
+
+static void
+stock_icon_selected (GtkMenuItem   *menuitem,
+                     UmPhotoDialog *um)
+{
+        const char *filename;
+
+        filename = g_object_get_data (G_OBJECT (menuitem), "filename");
+        um->callback (NULL, filename, um->data);
+}
+
+static GtkWidget *
+menu_item_for_filename (UmPhotoDialog *um,
+                        const char    *filename)
+{
+        GtkWidget *image, *menuitem;
+        GFile *file;
+        GIcon *icon;
+
+        file = g_file_new_for_path (filename);
+        icon = g_file_icon_new (file);
+        g_object_unref (file);
+        image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_DIALOG);
+        g_object_unref (icon);
+
+        menuitem = gtk_menu_item_new ();
+        gtk_container_add (GTK_CONTAINER (menuitem), image);
+        gtk_widget_show_all (menuitem);
+
+        g_object_set_data_full (G_OBJECT (menuitem), "filename",
+                                g_strdup (filename), (GDestroyNotify) g_free);
+        g_signal_connect (G_OBJECT (menuitem), "activate",
+                          G_CALLBACK (stock_icon_selected), um);
+
+        return menuitem;
+}
+
+static void
+setup_photo_popup (UmPhotoDialog *um)
+{
+        GtkWidget *menu, *menuitem, *image;
+        guint x, y;
+        const gchar * const * dirs;
+        guint i;
+        GDir *dir;
+        const char *face;
+        gboolean none_item_shown;
+        gboolean added_faces;
+
+        menu = gtk_menu_new ();
+
+        x = 0;
+        y = 0;
+        none_item_shown = added_faces = FALSE;
+
+        dirs = g_get_system_data_dirs ();
+        for (i = 0; dirs[i] != NULL; i++) {
+                char *path;
+
+                path = g_build_filename (dirs[i], "pixmaps", "faces", NULL);
+                dir = g_dir_open (path, 0, NULL);
+                if (dir == NULL) {
+                        g_free (path);
+                        continue;
+                }
+
+                while ((face = g_dir_read_name (dir)) != NULL) {
+                        char *filename;
+
+                        added_faces = TRUE;
+
+                        filename = g_build_filename (path, face, NULL);
+                        menuitem = menu_item_for_filename (um, filename);
+                        g_free (filename);
+                        if (menuitem == NULL)
+                                continue;
+
+                        gtk_menu_attach (GTK_MENU (menu), GTK_WIDGET (menuitem),
+                                         x, x + 1, y, y + 1);
+                        gtk_widget_show (menuitem);
+
+                        x++;
+                        if (x >= ROW_SPAN - 1) {
+                                y++;
+                                x = 0;
+                        }
+                }
+                g_dir_close (dir);
+                g_free (path);
+
+                if (added_faces)
+                        break;
+        }
+
+        if (!added_faces)
+                goto skip_faces;
+
+        image = gtk_image_new_from_icon_name ("avatar-default", GTK_ICON_SIZE_DIALOG);
+        menuitem = gtk_menu_item_new ();
+        gtk_container_add (GTK_CONTAINER (menuitem), image);
+        gtk_widget_show_all (menuitem);
+        gtk_menu_attach (GTK_MENU (menu), GTK_WIDGET (menuitem),
+                         x, x + 1, y, y + 1);
+        g_signal_connect (G_OBJECT (menuitem), "activate",
+                          G_CALLBACK (none_icon_selected), um);
+        gtk_widget_show (menuitem);
+        none_item_shown = TRUE;
+        y++;
+
+skip_faces:
+        if (!none_item_shown) {
+                menuitem = gtk_menu_item_new_with_label (_("Disable image"));
+                gtk_menu_attach (GTK_MENU (menu), GTK_WIDGET (menuitem),
+                                 0, ROW_SPAN - 1, y, y + 1);
+                g_signal_connect (G_OBJECT (menuitem), "activate",
+                                  G_CALLBACK (none_icon_selected), um);
+                gtk_widget_show (menuitem);
+                y++;
+        }
+
+        /* Separator */
+        menuitem = gtk_separator_menu_item_new ();
+        gtk_menu_attach (GTK_MENU (menu), GTK_WIDGET (menuitem),
+                         0, ROW_SPAN - 1, y, y + 1);
+        gtk_widget_show (menuitem);
+
+        y++;
+
+#ifdef HAVE_CHEESE
+        um->take_photo_menuitem = gtk_menu_item_new_with_label (_("Take a photo..."));
+        gtk_menu_attach (GTK_MENU (menu), GTK_WIDGET (um->take_photo_menuitem),
+                         0, ROW_SPAN - 1, y, y + 1);
+        g_signal_connect (G_OBJECT (um->take_photo_menuitem), "activate",
+                          G_CALLBACK (webcam_icon_selected), um);
+        gtk_widget_set_sensitive (um->take_photo_menuitem, FALSE);
+        gtk_widget_show (um->take_photo_menuitem);
+
+        um->monitor = cheese_camera_device_monitor_new ();
+        g_signal_connect (G_OBJECT (um->monitor), "added",
+                          G_CALLBACK (device_added), um);
+        g_signal_connect (G_OBJECT (um->monitor), "removed",
+                          G_CALLBACK (device_removed), um);
+        cheese_camera_device_monitor_coldplug (um->monitor);
+
+        y++;
+#endif /* HAVE_CHEESE */
+
+        um->photo_popup = menu;
+}
+
+static void
+popup_icon_menu (GtkToggleButton *button, UmPhotoDialog *um)
+{
+        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) && !gtk_widget_get_visible 
(um->photo_popup)) {
+                gtk_menu_popup (GTK_MENU (um->photo_popup),
+                                NULL, NULL,
+                                (GtkMenuPositionFunc) popup_menu_below_button, um->popup_button,
+                                0, gtk_get_current_event_time ());
+        } else {
+                gtk_menu_popdown (GTK_MENU (um->photo_popup));
+        }
+}
+
+static gboolean
+on_popup_button_button_pressed (GtkToggleButton *button,
+                                GdkEventButton *event,
+                                UmPhotoDialog  *um)
+{
+        if (event->button == 1) {
+                if (!gtk_widget_get_visible (um->photo_popup)) {
+                        popup_icon_menu (button, um);
+                        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+                } else {
+                        gtk_menu_popdown (GTK_MENU (um->photo_popup));
+                        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), FALSE);
+                }
+
+                return TRUE;
+        }
+
+        return FALSE;
+}
+
+static void
+on_photo_popup_unmap (GtkWidget     *popup_menu,
+                      UmPhotoDialog *um)
+{
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (um->popup_button), FALSE);
+}
+
+static void
+popup_button_draw (GtkWidget      *widget,
+                   cairo_t        *cr,
+                   UmPhotoDialog  *um)
+{
+        if (gtk_widget_get_state (gtk_bin_get_child (GTK_BIN (widget))) != GTK_STATE_PRELIGHT &&
+            !gtk_widget_is_focus (widget)) {
+                return;
+        }
+
+        down_arrow (gtk_widget_get_style_context (widget),
+                    cr,
+                    gtk_widget_get_allocated_width (widget) - 12,
+                    gtk_widget_get_allocated_height (widget) - 12,
+                    12, 12);
+}
+
+static void
+popup_button_focus_changed (GObject       *button,
+                            GParamSpec    *pspec,
+                            UmPhotoDialog *um)
+{
+        gtk_widget_queue_draw (gtk_bin_get_child (GTK_BIN (button)));
+}
+
+UmPhotoDialog *
+um_photo_dialog_new (GtkWidget            *button,
+                     SelectAvatarCallback  callback,
+                     gpointer              data)
+{
+        UmPhotoDialog *um;
+
+        um = g_new0 (UmPhotoDialog, 1);
+
+        /* Set up the popup */
+        um->popup_button = button;
+        setup_photo_popup (um);
+        g_signal_connect (button, "toggled",
+                          G_CALLBACK (popup_icon_menu), um);
+        g_signal_connect (button, "button-press-event",
+                          G_CALLBACK (on_popup_button_button_pressed), um);
+        g_signal_connect (button, "notify::is-focus",
+                          G_CALLBACK (popup_button_focus_changed), um);
+        g_signal_connect_after (button, "draw",
+                                G_CALLBACK (popup_button_draw), um);
+
+        g_signal_connect (um->photo_popup, "unmap",
+                          G_CALLBACK (on_photo_popup_unmap), um);
+
+        um->callback = callback;
+        um->data = data;
+
+        return um;
+}
+
+void
+um_photo_dialog_free (UmPhotoDialog *um)
+{
+        gtk_widget_destroy (um->photo_popup);
+
+#ifdef HAVE_CHEESE
+        if (um->monitor)
+                g_object_unref (um->monitor);
+#endif
+
+        g_free (um);
+}
diff --git a/gnome-initial-setup/pages/account/um-photo-dialog.h 
b/gnome-initial-setup/pages/account/um-photo-dialog.h
new file mode 100644
index 0000000..1819c97
--- /dev/null
+++ b/gnome-initial-setup/pages/account/um-photo-dialog.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2009-2010  Red Hat, Inc,
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ *
+ * Written by: Matthias Clasen <mclasen redhat com>
+ */
+
+#ifndef __UM_PHOTO_DIALOG_H__
+#define __UM_PHOTO_DIALOG_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _UmPhotoDialog UmPhotoDialog;
+typedef void (SelectAvatarCallback) (GdkPixbuf   *pixbuf,
+                                     const gchar *filename,
+                                     gpointer     data);
+
+UmPhotoDialog *um_photo_dialog_new      (GtkWidget            *button,
+                                         SelectAvatarCallback  callback,
+                                         gpointer              data);
+void           um_photo_dialog_free     (UmPhotoDialog *dialog);
+
+G_END_DECLS
+
+#endif
diff --git a/gnome-initial-setup/pages/account/um-utils.c b/gnome-initial-setup/pages/account/um-utils.c
index 0ee5920..5d58cbb 100644
--- a/gnome-initial-setup/pages/account/um-utils.c
+++ b/gnome-initial-setup/pages/account/um-utils.c
@@ -62,6 +62,94 @@ clear_entry_validation_error (GtkEntry *entry)
         g_object_set (entry, "caps-lock-warning", TRUE, NULL);
 }
 
+void
+popup_menu_below_button (GtkMenu   *menu,
+                         gint      *x,
+                         gint      *y,
+                         gboolean  *push_in,
+                         GtkWidget *button)
+{
+        GtkRequisition menu_req;
+        GtkTextDirection direction;
+        GtkAllocation allocation;
+
+        gtk_widget_get_preferred_size (GTK_WIDGET (menu), NULL, &menu_req);
+
+        direction = gtk_widget_get_direction (button);
+
+        gdk_window_get_origin (gtk_widget_get_window (button), x, y);
+        gtk_widget_get_allocation (button, &allocation);
+        *x += allocation.x;
+        *y += allocation.y + allocation.height;
+
+        if (direction == GTK_TEXT_DIR_LTR)
+                *x += MAX (allocation.width - menu_req.width, 0);
+        else if (menu_req.width > allocation.width)
+                *x -= menu_req.width - allocation.width;
+
+        *push_in = TRUE;
+}
+
+void
+down_arrow (GtkStyleContext *context,
+            cairo_t         *cr,
+            gint             x,
+            gint             y,
+            gint             width,
+            gint             height)
+{
+        GtkStateFlags flags;
+        GdkRGBA fg_color;
+        GdkRGBA outline_color;
+        gdouble vertical_overshoot;
+        gint diameter;
+        gdouble radius;
+        gdouble x_double, y_double;
+        gdouble angle;
+        gint line_width;
+
+        flags = gtk_style_context_get_state (context);
+
+        gtk_style_context_get_color (context, flags, &fg_color);
+        gtk_style_context_get_border_color (context, flags, &outline_color);
+
+        line_width = 1;
+        angle = G_PI / 2;
+        vertical_overshoot = line_width / 2.0 * (1. / tan (G_PI / 8));
+        if (line_width % 2 == 1)
+                vertical_overshoot = ceil (0.5 + vertical_overshoot) - 0.5;
+        else
+                vertical_overshoot = ceil (vertical_overshoot);
+        diameter = (gint) MAX (3, width - 2 * vertical_overshoot);
+        diameter -= (1 - (diameter + line_width) % 2);
+        radius = diameter / 2.;
+        x_double = floor ((x + width / 2) - (radius + line_width) / 2.) + (radius + line_width) / 2.;
+
+        y_double = (y + height / 2) - 0.5;
+
+        cairo_save (cr);
+
+        cairo_translate (cr, x_double, y_double);
+        cairo_rotate (cr, angle);
+
+        cairo_move_to (cr, - radius / 2., - radius);
+        cairo_line_to (cr,   radius / 2.,   0);
+        cairo_line_to (cr, - radius / 2.,   radius);
+
+        cairo_close_path (cr);
+
+        cairo_set_line_width (cr, line_width);
+
+        gdk_cairo_set_source_rgba (cr, &fg_color);
+
+        cairo_fill_preserve (cr);
+
+        gdk_cairo_set_source_rgba (cr, &outline_color);
+        cairo_stroke (cr);
+
+        cairo_restore (cr);
+}
+
 #define MAXNAMELEN  (UT_NAMESIZE - 1)
 
 static gboolean
diff --git a/gnome-initial-setup/pages/account/um-utils.h b/gnome-initial-setup/pages/account/um-utils.h
index 5befff5..ec534dc 100644
--- a/gnome-initial-setup/pages/account/um-utils.h
+++ b/gnome-initial-setup/pages/account/um-utils.h
@@ -31,6 +31,19 @@ void     set_entry_validation_error       (GtkEntry    *entry,
                                            const gchar *text);
 void     clear_entry_validation_error     (GtkEntry    *entry);
 
+void     popup_menu_below_button          (GtkMenu     *menu,
+                                           gint        *x,
+                                           gint        *y,
+                                           gboolean    *push_in,
+                                           GtkWidget   *button);
+
+void     down_arrow                       (GtkStyleContext *context,
+                                           cairo_t         *cr,
+                                           gint             x,
+                                           gint             y,
+                                           gint             width,
+                                           gint             height);
+
 gboolean is_valid_name                    (const gchar     *name);
 gboolean is_valid_username                (const gchar     *name,
                                            gchar          **tip);


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