[gnome-initial-setup] accounts: Bring back the avatar chooser
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-initial-setup] accounts: Bring back the avatar chooser
- Date: Tue, 25 Feb 2014 06:31:45 +0000 (UTC)
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]