From 0b0e6bc987da4fd88a7854ebb12bde705e92c428 Mon Sep 17 00:00:00 2001 From: Perberos Date: Thu, 1 Dec 2011 21:51:44 -0300 Subject: moving from https://github.com/perberos/mate-desktop-environment --- capplets/about-me/AUTHORS | 2 + capplets/about-me/Makefile.am | 59 + capplets/about-me/e-image-chooser.c | 455 +++++++ capplets/about-me/e-image-chooser.h | 66 + capplets/about-me/eel-alert-dialog.c | 466 +++++++ capplets/about-me/eel-alert-dialog.h | 61 + capplets/about-me/eel-gtk-macros.h | 178 +++ capplets/about-me/fingerprint-strings.h | 111 ++ capplets/about-me/fprintd-marshal.list | 1 + capplets/about-me/icons/Makefile.am | 30 + capplets/about-me/icons/left-index-finger.png | Bin 0 -> 1515 bytes capplets/about-me/icons/left-index-finger.svg | 177 +++ capplets/about-me/icons/left-little-finger.png | Bin 0 -> 1500 bytes capplets/about-me/icons/left-little-finger.svg | 180 +++ capplets/about-me/icons/left-middle-finger.png | Bin 0 -> 1483 bytes capplets/about-me/icons/left-middle-finger.svg | 180 +++ capplets/about-me/icons/left-ring-finger.png | Bin 0 -> 1512 bytes capplets/about-me/icons/left-ring-finger.svg | 180 +++ capplets/about-me/icons/left-thumb.png | Bin 0 -> 1512 bytes capplets/about-me/icons/left-thumb.svg | 180 +++ capplets/about-me/icons/print_error.png | Bin 0 -> 4160 bytes capplets/about-me/icons/print_error.svg | 525 ++++++++ capplets/about-me/icons/print_ok.png | Bin 0 -> 3677 bytes capplets/about-me/icons/print_ok.svg | 310 +++++ capplets/about-me/icons/right-index-finger.png | Bin 0 -> 1506 bytes capplets/about-me/icons/right-index-finger.svg | 179 +++ capplets/about-me/icons/right-little-finger.png | Bin 0 -> 1479 bytes capplets/about-me/icons/right-little-finger.svg | 182 +++ capplets/about-me/icons/right-middle-finger.png | Bin 0 -> 1468 bytes capplets/about-me/icons/right-middle-finger.svg | 182 +++ capplets/about-me/icons/right-ring-finger.png | Bin 0 -> 1506 bytes capplets/about-me/icons/right-ring-finger.svg | 182 +++ capplets/about-me/icons/right-thumb.png | Bin 0 -> 1486 bytes capplets/about-me/icons/right-thumb.svg | 182 +++ capplets/about-me/mate-about-me-dialog.ui | 1622 +++++++++++++++++++++++ capplets/about-me/mate-about-me-fingerprint.c | 624 +++++++++ capplets/about-me/mate-about-me-fingerprint.h | 27 + capplets/about-me/mate-about-me-fingerprint.ui | 276 ++++ capplets/about-me/mate-about-me-password.c | 1136 ++++++++++++++++ capplets/about-me/mate-about-me-password.h | 9 + capplets/about-me/mate-about-me-password.ui | 319 +++++ capplets/about-me/mate-about-me.c | 1005 ++++++++++++++ capplets/about-me/mate-about-me.desktop.in.in | 14 + 43 files changed, 9100 insertions(+) create mode 100644 capplets/about-me/AUTHORS create mode 100644 capplets/about-me/Makefile.am create mode 100644 capplets/about-me/e-image-chooser.c create mode 100644 capplets/about-me/e-image-chooser.h create mode 100644 capplets/about-me/eel-alert-dialog.c create mode 100644 capplets/about-me/eel-alert-dialog.h create mode 100644 capplets/about-me/eel-gtk-macros.h create mode 100644 capplets/about-me/fingerprint-strings.h create mode 100644 capplets/about-me/fprintd-marshal.list create mode 100644 capplets/about-me/icons/Makefile.am create mode 100644 capplets/about-me/icons/left-index-finger.png create mode 100644 capplets/about-me/icons/left-index-finger.svg create mode 100644 capplets/about-me/icons/left-little-finger.png create mode 100644 capplets/about-me/icons/left-little-finger.svg create mode 100644 capplets/about-me/icons/left-middle-finger.png create mode 100644 capplets/about-me/icons/left-middle-finger.svg create mode 100644 capplets/about-me/icons/left-ring-finger.png create mode 100644 capplets/about-me/icons/left-ring-finger.svg create mode 100644 capplets/about-me/icons/left-thumb.png create mode 100644 capplets/about-me/icons/left-thumb.svg create mode 100644 capplets/about-me/icons/print_error.png create mode 100644 capplets/about-me/icons/print_error.svg create mode 100644 capplets/about-me/icons/print_ok.png create mode 100644 capplets/about-me/icons/print_ok.svg create mode 100644 capplets/about-me/icons/right-index-finger.png create mode 100644 capplets/about-me/icons/right-index-finger.svg create mode 100644 capplets/about-me/icons/right-little-finger.png create mode 100644 capplets/about-me/icons/right-little-finger.svg create mode 100644 capplets/about-me/icons/right-middle-finger.png create mode 100644 capplets/about-me/icons/right-middle-finger.svg create mode 100644 capplets/about-me/icons/right-ring-finger.png create mode 100644 capplets/about-me/icons/right-ring-finger.svg create mode 100644 capplets/about-me/icons/right-thumb.png create mode 100644 capplets/about-me/icons/right-thumb.svg create mode 100644 capplets/about-me/mate-about-me-dialog.ui create mode 100644 capplets/about-me/mate-about-me-fingerprint.c create mode 100644 capplets/about-me/mate-about-me-fingerprint.h create mode 100644 capplets/about-me/mate-about-me-fingerprint.ui create mode 100644 capplets/about-me/mate-about-me-password.c create mode 100644 capplets/about-me/mate-about-me-password.h create mode 100644 capplets/about-me/mate-about-me-password.ui create mode 100644 capplets/about-me/mate-about-me.c create mode 100644 capplets/about-me/mate-about-me.desktop.in.in (limited to 'capplets/about-me') diff --git a/capplets/about-me/AUTHORS b/capplets/about-me/AUTHORS new file mode 100644 index 00000000..c02ecb55 --- /dev/null +++ b/capplets/about-me/AUTHORS @@ -0,0 +1,2 @@ +Diego Gonzalez Gonzalez +Chris Toshok \ No newline at end of file diff --git a/capplets/about-me/Makefile.am b/capplets/about-me/Makefile.am new file mode 100644 index 00000000..6cd5e139 --- /dev/null +++ b/capplets/about-me/Makefile.am @@ -0,0 +1,59 @@ +SUBDIRS = icons + +# This is used in MATECC_CAPPLETS_CFLAGS +cappletname = about-me + +ui_files = mate-about-me-dialog.ui mate-about-me-password.ui mate-about-me-fingerprint.ui +Desktop_in_files = mate-about-me.desktop.in + +mate_about_me_SOURCES = \ + eel-alert-dialog.c \ + eel-alert-dialog.h \ + eel-gtk-macros.h \ + mate-about-me-password.c \ + mate-about-me-password.h \ + e-image-chooser.c \ + e-image-chooser.h \ + mate-about-me-fingerprint.c \ + mate-about-me-fingerprint.h \ + $(MARSHALFILES) \ + fingerprint-strings.h \ + mate-about-me.c + +MARSHALFILES = marshal.c marshal.h +BUILT_SOURCES = $(MARSHALFILES) + +marshal.h: fprintd-marshal.list + @GLIB_GENMARSHAL@ --prefix=fprintd_marshal $< --header > $@ +marshal.c: fprintd-marshal.list + @GLIB_GENMARSHAL@ --prefix=fprintd_marshal $< --body --header > $@ + +if BUILD_ABOUTME +bin_PROGRAMS = mate-about-me + +mate_about_me_LDADD = $(MATECC_CAPPLETS_LIBS) $(LIBEBOOK_LIBS) +mate_about_me_LDFLAGS = -export-dynamic + +@INTLTOOL_DESKTOP_RULE@ + +desktopdir = $(datadir)/applications +desktop_DATA = $(Desktop_in_files:.desktop.in=.desktop) + +uidir = $(pkgdatadir)/ui +ui_DATA = $(ui_files) + +INCLUDES = \ + $(MATECC_CAPPLETS_CFLAGS) \ + $(LIBEBOOK_CFLAGS) \ + -DDATADIR="\"$(datadir)\"" \ + -DMATECC_DATA_DIR="\"$(pkgdatadir)\"" \ + -DMATECC_UI_DIR="\"$(uidir)\"" \ + -DMATECC_PIXMAP_DIR="\"$(pkgdatadir)/pixmaps\"" \ + -DMATELOCALEDIR="\"$(datadir)/locale\"" + +endif # BUILD_ABOUTME + +CLEANFILES = $(MATECC_CAPPLETS_CLEANFILES) $(desktop_DATA) $(MARSHALFILES) +EXTRA_DIST = $(ui_files) fprintd-marshal.list + +-include $(top_srcdir)/git.mk diff --git a/capplets/about-me/e-image-chooser.c b/capplets/about-me/e-image-chooser.c new file mode 100644 index 00000000..31a189f1 --- /dev/null +++ b/capplets/about-me/e-image-chooser.c @@ -0,0 +1,455 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* e-image-chooser.c + * Copyright (C) 2004 Novell, Inc. + * Author: Chris Toshok + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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 library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include + +#include +#include +#include +#include + +#include "e-image-chooser.h" + +struct _EImageChooserPrivate { + + GtkWidget *image; + GtkWidget *browse_button; + + char *image_buf; + int image_buf_size; + int image_width; + int image_height; + + gboolean editable; +}; + +enum { + CHANGED, + LAST_SIGNAL +}; + + +static gint image_chooser_signals [LAST_SIGNAL] = { 0 }; + +static void e_image_chooser_init (EImageChooser *chooser); +static void e_image_chooser_class_init (EImageChooserClass *klass); +static void e_image_chooser_dispose (GObject *object); + +static gboolean image_drag_motion_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, guint time, EImageChooser *chooser); +static gboolean image_drag_drop_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, guint time, EImageChooser *chooser); +static void image_drag_data_received_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, + GtkSelectionData *selection_data, + guint info, guint time, EImageChooser *chooser); + +static GtkObjectClass *parent_class = NULL; +#define PARENT_TYPE GTK_TYPE_VBOX + +enum DndTargetType { + DND_TARGET_TYPE_URI_LIST +}; +#define URI_LIST_TYPE "text/uri-list" + +static GtkTargetEntry image_drag_types[] = { + { URI_LIST_TYPE, 0, DND_TARGET_TYPE_URI_LIST }, +}; +static const int num_image_drag_types = sizeof (image_drag_types) / sizeof (image_drag_types[0]); + +GtkWidget * +e_image_chooser_new (void) +{ + return g_object_new (E_TYPE_IMAGE_CHOOSER, NULL); +} + +GType +e_image_chooser_get_type (void) +{ + static GType eic_type = 0; + + if (!eic_type) { + static const GTypeInfo eic_info = { + sizeof (EImageChooserClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) e_image_chooser_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (EImageChooser), + 0, /* n_preallocs */ + (GInstanceInitFunc) e_image_chooser_init, + }; + + eic_type = g_type_register_static (PARENT_TYPE, "EImageChooser", &eic_info, 0); + } + + return eic_type; +} + + +static void +e_image_chooser_class_init (EImageChooserClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_ref (PARENT_TYPE); + + image_chooser_signals [CHANGED] = + g_signal_new ("changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EImageChooserClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + GTK_TYPE_NONE, 0); + + object_class->dispose = e_image_chooser_dispose; +} + +static void +e_image_chooser_init (EImageChooser *chooser) +{ + EImageChooserPrivate *priv; + + priv = chooser->priv = g_new0 (EImageChooserPrivate, 1); + + priv->image = gtk_image_new (); + + gtk_box_set_homogeneous (GTK_BOX (chooser), FALSE); + gtk_box_pack_start (GTK_BOX (chooser), priv->image, TRUE, TRUE, 0); + + gtk_drag_dest_set (priv->image, 0, image_drag_types, num_image_drag_types, GDK_ACTION_COPY); + g_signal_connect (priv->image, + "drag_motion", G_CALLBACK (image_drag_motion_cb), chooser); + g_signal_connect (priv->image, + "drag_drop", G_CALLBACK (image_drag_drop_cb), chooser); + g_signal_connect (priv->image, + "drag_data_received", G_CALLBACK (image_drag_data_received_cb), chooser); + + gtk_widget_show_all (priv->image); + + /* we default to being editable */ + priv->editable = TRUE; +} + +static void +e_image_chooser_dispose (GObject *object) +{ + EImageChooser *eic = E_IMAGE_CHOOSER (object); + + if (eic->priv) { + EImageChooserPrivate *priv = eic->priv; + + if (priv->image_buf) { + g_free (priv->image_buf); + priv->image_buf = NULL; + } + + g_free (eic->priv); + eic->priv = NULL; + } + + if (G_OBJECT_CLASS (parent_class)->dispose) + (* G_OBJECT_CLASS (parent_class)->dispose) (object); +} + + +static gboolean +set_image_from_data (EImageChooser *chooser, + char *data, int length) +{ + gboolean rv = FALSE; + GdkPixbufLoader *loader = gdk_pixbuf_loader_new (); + GdkPixbuf *pixbuf; + + gdk_pixbuf_loader_write (loader, data, length, NULL); + gdk_pixbuf_loader_close (loader, NULL); + + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + if (pixbuf) + g_object_ref (pixbuf); + g_object_unref (loader); + + if (pixbuf) { + GdkPixbuf *scaled; + GtkRequisition chooser_size; + + float scale; + int new_height, new_width; + + gtk_widget_size_request (gtk_widget_get_parent (GTK_WIDGET (chooser)), + &chooser_size); + chooser_size.width -= 5; + chooser_size.height -= 5; + + new_height = gdk_pixbuf_get_height (pixbuf); + new_width = gdk_pixbuf_get_width (pixbuf); + + if (chooser->priv->image_height == 0 + && chooser->priv->image_width == 0) { + scale = 1.0; + } + else if (chooser->priv->image_height < new_height + || chooser->priv->image_width < new_width) { + /* we need to scale down */ + if (new_height > new_width) + scale = (float)chooser_size.height / new_height; + else + scale = (float)chooser_size.width / new_width; + } + else { + /* we need to scale up */ + if (new_height > new_width) + scale = (float)new_height / chooser_size.height; + else + scale = (float)new_width / chooser_size.width; + } + + if (scale == 1.0) { + gtk_image_set_from_pixbuf (GTK_IMAGE (chooser->priv->image), pixbuf); + + chooser->priv->image_width = new_width; + chooser->priv->image_height = new_height; + } + else { + new_width *= scale; + new_height *= scale; + new_width = MIN (new_width, chooser_size.width); + new_height = MIN (new_height, chooser_size.height); + + scaled = gdk_pixbuf_scale_simple (pixbuf, + new_width, new_height, + GDK_INTERP_BILINEAR); + + gtk_image_set_from_pixbuf (GTK_IMAGE (chooser->priv->image), scaled); + g_object_unref (scaled); + } + + g_object_unref (pixbuf); + + g_free (chooser->priv->image_buf); + chooser->priv->image_buf = data; + chooser->priv->image_buf_size = length; + + g_signal_emit (chooser, + image_chooser_signals [CHANGED], 0); + + rv = TRUE; + } + + return rv; +} + +static gboolean +image_drag_motion_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, guint time, EImageChooser *chooser) +{ + GList *p; + + if (!chooser->priv->editable) + return FALSE; + + for (p = context->targets; p != NULL; p = p->next) { + char *possible_type; + + possible_type = gdk_atom_name (GDK_POINTER_TO_ATOM (p->data)); + if (!strcmp (possible_type, URI_LIST_TYPE)) { + g_free (possible_type); + gdk_drag_status (context, GDK_ACTION_COPY, time); + return TRUE; + } + + g_free (possible_type); + } + + return FALSE; +} + +static gboolean +image_drag_drop_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, guint time, EImageChooser *chooser) +{ + GList *p; + + if (!chooser->priv->editable) + return FALSE; + + if (context->targets == NULL) { + return FALSE; + } + + for (p = context->targets; p != NULL; p = p->next) { + char *possible_type; + + possible_type = gdk_atom_name (GDK_POINTER_TO_ATOM (p->data)); + if (!strcmp (possible_type, URI_LIST_TYPE)) { + g_free (possible_type); + gtk_drag_get_data (widget, context, + GDK_POINTER_TO_ATOM (p->data), + time); + return TRUE; + } + + g_free (possible_type); + } + + return FALSE; +} + +static void +image_drag_data_received_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, + GtkSelectionData *selection_data, + guint info, guint time, EImageChooser *chooser) +{ + char *target_type; + gboolean handled = FALSE; + + target_type = gdk_atom_name (gtk_selection_data_get_target (selection_data)); + + if (!strcmp (target_type, URI_LIST_TYPE)) { + const char *data = gtk_selection_data_get_data (selection_data); + char *uri; + GFile *file; + GInputStream *istream; + char *nl = strstr (data, "\r\n"); + + if (nl) + uri = g_strndup (data, nl - (char *) data); + else + uri = g_strdup (data); + + file = g_file_new_for_uri (uri); + istream = G_INPUT_STREAM (g_file_read (file, NULL, NULL)); + + if (istream != NULL) { + GFileInfo *info; + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_SIZE, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + + if (info != NULL) { + gsize size; + gboolean success; + gchar *buf; + + size = g_file_info_get_size (info); + g_object_unref (info); + + buf = g_malloc (size); + + success = g_input_stream_read_all (istream, + buf, + size, + &size, + NULL, + NULL); + g_input_stream_close (istream, NULL, NULL); + + if (success && + set_image_from_data (chooser, buf, size)) + handled = TRUE; + else + g_free (buf); + } + + g_object_unref (istream); + } + + g_object_unref (file); + g_free (uri); + } + + gtk_drag_finish (context, handled, FALSE, time); +} + +gboolean +e_image_chooser_set_from_file (EImageChooser *chooser, const char *filename) +{ + gchar *data; + gsize data_length; + + g_return_val_if_fail (E_IS_IMAGE_CHOOSER (chooser), FALSE); + g_return_val_if_fail (filename, FALSE); + + if (!g_file_get_contents (filename, &data, &data_length, NULL)) { + return FALSE; + } + + if (!set_image_from_data (chooser, data, data_length)) + g_free (data); + + return TRUE; +} + +void +e_image_chooser_set_editable (EImageChooser *chooser, gboolean editable) +{ + g_return_if_fail (E_IS_IMAGE_CHOOSER (chooser)); + + chooser->priv->editable = editable; + + gtk_widget_set_sensitive (chooser->priv->browse_button, editable); +} + +gboolean +e_image_chooser_get_image_data (EImageChooser *chooser, char **data, gsize *data_length) +{ + g_return_val_if_fail (E_IS_IMAGE_CHOOSER (chooser), FALSE); + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (data_length != NULL, FALSE); + + *data_length = chooser->priv->image_buf_size; + *data = g_malloc (*data_length); + memcpy (*data, chooser->priv->image_buf, *data_length); + + return TRUE; +} + +gboolean +e_image_chooser_set_image_data (EImageChooser *chooser, char *data, gsize data_length) +{ + char *buf; + + g_return_val_if_fail (E_IS_IMAGE_CHOOSER (chooser), FALSE); + g_return_val_if_fail (data != NULL, FALSE); + + /* yuck, a copy... */ + buf = g_malloc (data_length); + memcpy (buf, data, data_length); + + if (!set_image_from_data (chooser, buf, data_length)) { + g_free (buf); + return FALSE; + } + + return TRUE; +} diff --git a/capplets/about-me/e-image-chooser.h b/capplets/about-me/e-image-chooser.h new file mode 100644 index 00000000..b2b2fbf2 --- /dev/null +++ b/capplets/about-me/e-image-chooser.h @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* e-image-chooser.c + * Copyright (C) 2004 Novell, Inc. + * Author: Chris Toshok + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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 library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _E_IMAGE_CHOOSER_H_ +#define _E_IMAGE_CHOOSER_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define E_TYPE_IMAGE_CHOOSER (e_image_chooser_get_type ()) +#define E_IMAGE_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_IMAGE_CHOOSER, EImageChooser)) +#define E_IMAGE_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_IMAGE_CHOOSER, EImageChooserClass)) +#define E_IS_IMAGE_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_IMAGE_CHOOSER)) +#define E_IS_IMAGE_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), E_TYPE_IMAGE_CHOOSER)) + +typedef struct _EImageChooser EImageChooser; +typedef struct _EImageChooserClass EImageChooserClass; +typedef struct _EImageChooserPrivate EImageChooserPrivate; + +struct _EImageChooser +{ + GtkVBox parent; + + EImageChooserPrivate *priv; +}; + +struct _EImageChooserClass +{ + GtkVBoxClass parent_class; + + /* signals */ + void (*changed) (EImageChooser *chooser); + + +}; + +GtkWidget *e_image_chooser_new (void); +GType e_image_chooser_get_type (void); + +gboolean e_image_chooser_set_from_file (EImageChooser *chooser, const char *filename); +gboolean e_image_chooser_set_image_data (EImageChooser *chooser, char *data, gsize data_length); +void e_image_chooser_set_editable (EImageChooser *chooser, gboolean editable); + +gboolean e_image_chooser_get_image_data (EImageChooser *chooser, char **data, gsize *data_length); + +#endif /* _E_IMAGE_CHOOSER_H_ */ diff --git a/capplets/about-me/eel-alert-dialog.c b/capplets/about-me/eel-alert-dialog.c new file mode 100644 index 00000000..6370d6db --- /dev/null +++ b/capplets/about-me/eel-alert-dialog.c @@ -0,0 +1,466 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ + +/* eel-alert-dialog.c: An HIG compliant alert dialog. + + The Mate Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Mate Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Mate Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#include "eel-alert-dialog.h" +#include "eel-gtk-macros.h" + +#include +#include + +#include + +enum { + PROP_0, + PROP_ALERT_TYPE, + PROP_BUTTONS +}; + +struct _EelAlertDialogDetails { + GtkWidget *image; + GtkWidget *primary_label; + GtkWidget *secondary_label; + GtkWidget *details_expander; + GtkWidget *details_label; + GtkMessageType type; +}; + + +static gpointer parent_class; + +static void eel_alert_dialog_finalize (GObject *object); +static void eel_alert_dialog_class_init (EelAlertDialogClass *klass); +static void eel_alert_dialog_init (EelAlertDialog *dialog); +static void eel_alert_dialog_style_set (GtkWidget *widget, + GtkStyle *prev_style); +static void eel_alert_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void eel_alert_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void eel_alert_dialog_add_buttons (EelAlertDialog *alert_dialog, + GtkButtonsType buttons); + +GType +eel_alert_dialog_get_type (void) +{ + static GType dialog_type = 0; + + if (!dialog_type) { + + static const GTypeInfo dialog_info = + { + sizeof (EelAlertDialogClass), + NULL, + NULL, + (GClassInitFunc) eel_alert_dialog_class_init, + NULL, + NULL, + sizeof (EelAlertDialog), + 0, + (GInstanceInitFunc) eel_alert_dialog_init, + }; + + dialog_type = g_type_register_static (GTK_TYPE_DIALOG, "EelAlertDialog", + &dialog_info, 0); + } + return dialog_type; +} + +static void +eel_alert_dialog_class_init (EelAlertDialogClass *class) +{ + GtkWidgetClass *widget_class; + GObjectClass *gobject_class; + + widget_class = GTK_WIDGET_CLASS (class); + gobject_class = G_OBJECT_CLASS (class); + + parent_class = g_type_class_peek_parent (class); + + G_OBJECT_CLASS (class)->finalize = eel_alert_dialog_finalize; + + widget_class->style_set = eel_alert_dialog_style_set; + + gobject_class->set_property = eel_alert_dialog_set_property; + gobject_class->get_property = eel_alert_dialog_get_property; + + gtk_widget_class_install_style_property (widget_class, + g_param_spec_int ("alert_border", + _("Image/label border"), + _("Width of border around the label and image in the alert dialog"), + 0, + G_MAXINT, + 5, + G_PARAM_READABLE)); + + g_object_class_install_property (gobject_class, + PROP_ALERT_TYPE, + g_param_spec_enum ("alert_type", + _("Alert Type"), + _("The type of alert"), + GTK_TYPE_MESSAGE_TYPE, + GTK_MESSAGE_INFO, + G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)); + + g_object_class_install_property (gobject_class, + PROP_BUTTONS, + g_param_spec_enum ("buttons", + _("Alert Buttons"), + _("The buttons shown in the alert dialog"), + GTK_TYPE_BUTTONS_TYPE, + GTK_BUTTONS_NONE, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); +} + +static void +eel_alert_dialog_finalize (GObject *object) +{ + EelAlertDialog *dialog; + + dialog = EEL_ALERT_DIALOG (object); + + g_free (dialog->details); + + EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + + +static void +eel_alert_dialog_init (EelAlertDialog *dialog) +{ + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *expander; + + dialog->details = g_new0 (EelAlertDialogDetails, 1); + + dialog->details->primary_label = gtk_label_new (NULL); + dialog->details->secondary_label = gtk_label_new (NULL); + dialog->details->details_label = gtk_label_new (NULL); + dialog->details->image = gtk_image_new_from_stock (NULL, GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (dialog->details->image), 0.5, 0.0); + + gtk_label_set_line_wrap (GTK_LABEL (dialog->details->primary_label), TRUE); + gtk_label_set_selectable (GTK_LABEL (dialog->details->primary_label), TRUE); + gtk_label_set_use_markup (GTK_LABEL (dialog->details->primary_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (dialog->details->primary_label), 0.0, 0.5); + + gtk_label_set_line_wrap (GTK_LABEL (dialog->details->secondary_label), TRUE); + gtk_label_set_selectable (GTK_LABEL (dialog->details->secondary_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (dialog->details->secondary_label), 0.0, 0.5); + + gtk_label_set_line_wrap (GTK_LABEL (dialog->details->details_label), TRUE); + gtk_label_set_selectable (GTK_LABEL (dialog->details->details_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (dialog->details->details_label), 0.0, 0.5); + + hbox = gtk_hbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); + + gtk_box_pack_start (GTK_BOX (hbox), dialog->details->image, + FALSE, FALSE, 0); + + vbox = gtk_vbox_new (FALSE, 12); + + gtk_box_pack_start (GTK_BOX (hbox), vbox, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), dialog->details->primary_label, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), dialog->details->secondary_label, + FALSE, FALSE, 0); + + expander = gtk_expander_new_with_mnemonic (_("Show more _details")); + dialog->details->details_expander = expander; + gtk_expander_set_spacing (GTK_EXPANDER (expander), 6); + gtk_container_add (GTK_CONTAINER (expander), dialog->details->details_label); + + gtk_box_pack_start (GTK_BOX (vbox), expander, + FALSE, FALSE, 0); + + + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), hbox, + FALSE, FALSE, 0); + + gtk_widget_show_all (hbox); + gtk_widget_hide (expander); + +} + +static void +setup_type (EelAlertDialog *dialog, + GtkMessageType type) +{ + const gchar *stock_id = NULL; + GtkStockItem item; + + switch (type) { + case GTK_MESSAGE_INFO: + stock_id = GTK_STOCK_DIALOG_INFO; + break; + case GTK_MESSAGE_QUESTION: + stock_id = GTK_STOCK_DIALOG_QUESTION; + break; + case GTK_MESSAGE_WARNING: + stock_id = GTK_STOCK_DIALOG_WARNING; + break; + case GTK_MESSAGE_ERROR: + stock_id = GTK_STOCK_DIALOG_ERROR; + break; + default: + g_warning ("Unknown GtkMessageType %d", type); + break; + } + + if (stock_id == NULL) { + stock_id = GTK_STOCK_DIALOG_INFO; + } + + if (gtk_stock_lookup (stock_id, &item)) { + gtk_image_set_from_stock (GTK_IMAGE (dialog->details->image), stock_id, + GTK_ICON_SIZE_DIALOG); + } else { + g_warning ("Stock dialog ID doesn't exist?"); + } +} + +static void +eel_alert_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + EelAlertDialog *dialog; + + dialog = EEL_ALERT_DIALOG (object); + + switch (prop_id) { + case PROP_ALERT_TYPE: + dialog->details->type = g_value_get_enum (value); + setup_type (dialog, dialog->details->type); + break; + case PROP_BUTTONS: + eel_alert_dialog_add_buttons (dialog, g_value_get_enum (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +eel_alert_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EelAlertDialog *dialog; + + dialog = EEL_ALERT_DIALOG (object); + + switch (prop_id) { + case PROP_ALERT_TYPE: + g_value_set_enum (value, dialog->details->type); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +void +eel_alert_dialog_set_primary_label (EelAlertDialog *dialog, + const gchar *message) +{ + gchar *markup_str; + char *escaped_message; + + if (message != NULL) { + escaped_message = g_markup_escape_text (message, -1); + markup_str = g_strconcat ("", escaped_message, "", NULL); + gtk_label_set_markup (GTK_LABEL (EEL_ALERT_DIALOG (dialog)->details->primary_label), + markup_str); + g_free (markup_str); + g_free (escaped_message); + } +} + +void +eel_alert_dialog_set_secondary_label (EelAlertDialog *dialog, + const gchar *message) +{ + if (message != NULL) { + gtk_label_set_text (GTK_LABEL (EEL_ALERT_DIALOG (dialog)->details->secondary_label), + message); + } else { + gtk_widget_hide (EEL_ALERT_DIALOG (dialog)->details->secondary_label); + } +} + +void +eel_alert_dialog_set_details_label (EelAlertDialog *dialog, + const gchar *message) +{ + if (message != NULL) { + gtk_widget_show (dialog->details->details_expander); + gtk_label_set_text (GTK_LABEL (dialog->details->details_label), message); + } else { + gtk_widget_hide (dialog->details->details_expander); + } +} + + +GtkWidget* +eel_alert_dialog_new (GtkWindow *parent, + GtkDialogFlags flags, + GtkMessageType type, + GtkButtonsType buttons, + const gchar *primary_message, + const gchar *secondary_message, + const gchar *title) +{ + GtkWidget *widget; + GtkDialog *dialog; + + g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL); + + widget = g_object_new (EEL_TYPE_ALERT_DIALOG, + "alert_type", type, + "buttons", buttons, + NULL); + atk_object_set_role (gtk_widget_get_accessible (widget), ATK_ROLE_ALERT); + + dialog = GTK_DIALOG (widget); + + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14); + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + gtk_dialog_set_has_separator (dialog, FALSE); + + gtk_window_set_title (GTK_WINDOW (dialog), + (title != NULL) ? title : ""); + + eel_alert_dialog_set_primary_label (EEL_ALERT_DIALOG (dialog), + primary_message); + + eel_alert_dialog_set_secondary_label (EEL_ALERT_DIALOG (dialog), + secondary_message); + + if (parent != NULL) { + gtk_window_set_transient_for (GTK_WINDOW (widget), + GTK_WINDOW (parent)); + } + + if (flags & GTK_DIALOG_MODAL) { + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + } + + if (flags & GTK_DIALOG_DESTROY_WITH_PARENT) { + gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE); + } + return widget; +} + +static void +eel_alert_dialog_add_buttons (EelAlertDialog* alert_dialog, + GtkButtonsType buttons) +{ + GtkDialog* dialog; + + dialog = GTK_DIALOG (alert_dialog); + + switch (buttons) { + case GTK_BUTTONS_NONE: + break; + case GTK_BUTTONS_OK: + gtk_dialog_add_button (dialog, + GTK_STOCK_OK, + GTK_RESPONSE_OK); + gtk_dialog_set_default_response (dialog, + GTK_RESPONSE_OK); + break; + case GTK_BUTTONS_CLOSE: + gtk_dialog_add_button (dialog, + GTK_STOCK_CLOSE, + GTK_RESPONSE_CLOSE); + gtk_dialog_set_default_response (dialog, + GTK_RESPONSE_CLOSE); + break; + case GTK_BUTTONS_CANCEL: + gtk_dialog_add_button (dialog, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + gtk_dialog_set_default_response (dialog, + GTK_RESPONSE_CANCEL); + break; + case GTK_BUTTONS_YES_NO: + gtk_dialog_add_button (dialog, + GTK_STOCK_NO, + GTK_RESPONSE_NO); + gtk_dialog_add_button (dialog, + GTK_STOCK_YES, + GTK_RESPONSE_YES); + gtk_dialog_set_default_response (dialog, + GTK_RESPONSE_YES); + break; + case GTK_BUTTONS_OK_CANCEL: + gtk_dialog_add_button (dialog, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + gtk_dialog_add_button (dialog, + GTK_STOCK_OK, + GTK_RESPONSE_OK); + gtk_dialog_set_default_response (dialog, + GTK_RESPONSE_OK); + break; + default: + g_warning ("Unknown GtkButtonsType"); + break; + } + g_object_notify (G_OBJECT (alert_dialog), "buttons"); +} + +static void +eel_alert_dialog_style_set (GtkWidget *widget, + GtkStyle *prev_style) +{ + GtkWidget *parent; + gint border_width; + + border_width = 0; + + parent = gtk_widget_get_parent (EEL_ALERT_DIALOG (widget)->details->image); + + if (parent != NULL) { + gtk_widget_style_get (widget, "alert_border", + &border_width, NULL); + + gtk_container_set_border_width (GTK_CONTAINER (parent), + border_width); + } + + if (GTK_WIDGET_CLASS (parent_class)->style_set) { + (GTK_WIDGET_CLASS (parent_class)->style_set) (widget, prev_style); + } +} diff --git a/capplets/about-me/eel-alert-dialog.h b/capplets/about-me/eel-alert-dialog.h new file mode 100644 index 00000000..60c6b9c0 --- /dev/null +++ b/capplets/about-me/eel-alert-dialog.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ + +/* eel-alert-dialog.h: An HIG compliant alert dialog. + + The Mate Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Mate Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Mate Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#ifndef EEL_ALERT_DIALOG_H +#define EEL_ALERT_DIALOG_H + +#include + +#define EEL_TYPE_ALERT_DIALOG (eel_alert_dialog_get_type ()) +#define EEL_ALERT_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEL_TYPE_ALERT_DIALOG, EelAlertDialog)) + +typedef struct _EelAlertDialog EelAlertDialog; +typedef struct _EelAlertDialogClass EelAlertDialogClass; +typedef struct _EelAlertDialogDetails EelAlertDialogDetails; + +struct _EelAlertDialog +{ + GtkDialog parent_instance; + EelAlertDialogDetails *details; +}; + +struct _EelAlertDialogClass +{ + GtkDialogClass parent_class; +}; + +GType eel_alert_dialog_get_type (void); + +GtkWidget* eel_alert_dialog_new (GtkWindow *parent, + GtkDialogFlags flags, + GtkMessageType type, + GtkButtonsType buttons, + const gchar *primary_message, + const gchar *secondary_message, + const gchar *title); +void eel_alert_dialog_set_primary_label (EelAlertDialog *dialog, + const gchar *message); +void eel_alert_dialog_set_secondary_label (EelAlertDialog *dialog, + const gchar *message); +void eel_alert_dialog_set_details_label (EelAlertDialog *dialog, + const gchar *message); + +#endif /* EEL_ALERT_DIALOG_H */ diff --git a/capplets/about-me/eel-gtk-macros.h b/capplets/about-me/eel-gtk-macros.h new file mode 100644 index 00000000..b3a9d671 --- /dev/null +++ b/capplets/about-me/eel-gtk-macros.h @@ -0,0 +1,178 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + + eel-gtk-macros.h: Macros to reduce boilerplate when using GTK. + + Copyright (C) 1999, 2000, 2001 Eazel, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Authors: Darin Adler + Ramiro Estrugo +*/ + +#ifndef EEL_GTK_MACROS_H +#define EEL_GTK_MACROS_H + +#ifndef EEL_DISABLE_DEPRECATED + +/* Define a parent_class global and a get_type function for a GTK class. + Since this is boilerplate, it's better not to repeat it over and over again. + Called like this: + + EEL_CLASS_BOILERPLATE (EelBookmark, eel_bookmark, GTK_TYPE_OBJECT) + + The parent_class_type parameter is guaranteed to be evaluated only once + so it can be an expression, even an expression that contains a function call. +*/ + +#define EEL_CLASS_BOILERPLATE(class_name, prefix, parent_class_type) \ + EEL_BOILERPLATE (class_name, class_name, prefix, parent_class_type, \ + EEL_REGISTER_TYPE) +#define EEL_REGISTER_TYPE(class_name, corba_name) \ + g_type_register_static (parent_type, #class_name, &info, 0) + +#define EEL_BOILERPLATE(class_name, corba_name, prefix, parent_class_type, \ + register_type) \ + \ +static gpointer parent_class; \ + \ +GType \ +prefix##_get_type (void) \ +{ \ + GType parent_type; \ + static GType type; \ + \ + if (type == 0) { \ + static GTypeInfo info = { \ + sizeof (class_name##Class), \ + NULL, NULL, \ + (GClassInitFunc) prefix##_class_init, \ + NULL, NULL, \ + sizeof (class_name), 0, \ + (GInstanceInitFunc) prefix##_init, \ + NULL \ + }; \ + \ + parent_type = (parent_class_type); \ + type = register_type (class_name, corba_name); \ + parent_class = g_type_class_ref (parent_type); \ + } \ + \ + return type; \ +} + +/* Call a parent class version of a virtual function (or default + * signal handler since that's the same thing). Nice because it + * documents what it's doing and there is less chance for a + * typo. Depends on the parent class pointer having the conventional + * name "parent_class" as the boilerplate macro above does it. + */ +#define EEL_CALL_PARENT(parent_class_cast_macro, signal, parameters) \ + \ +G_STMT_START { \ + if (parent_class_cast_macro (parent_class)->signal != NULL) { \ + (* parent_class_cast_macro (parent_class)->signal) parameters;\ + } \ +} G_STMT_END + +/* Same thing, for functions with a return value. */ +#define EEL_CALL_PARENT_WITH_RETURN_VALUE(parent_class_cast_macro, signal, \ + parameters) \ + \ +(parent_class_cast_macro (parent_class)->signal == NULL) \ + ? 0 \ + : ((* parent_class_cast_macro (parent_class)->signal) parameters) + +#endif /* EEL_DISABLE_DEPRECATED */ + +/* Call a virtual function. Useful when the virtual function is not a + * signal, otherwise you want to gtk_signal emit. Nice because it + * documents what it's doing and there is less chance for a typo. + */ +#define EEL_CALL_METHOD(class_cast_macro, object, signal, parameters) \ + \ +G_STMT_START { \ + if (class_cast_macro (G_OBJECT_GET_CLASS (object))->signal != NULL) { \ + (* class_cast_macro (G_OBJECT_GET_CLASS (object))->signal) \ + parameters; \ + } \ +} G_STMT_END + +/* Same thing, for functions with a return value. */ +#define EEL_CALL_METHOD_WITH_RETURN_VALUE(class_cast_macro, object, signal, \ + parameters) \ + \ +(class_cast_macro (G_OBJECT_GET_CLASS (object))->signal == NULL) \ + ? 0 \ + : ((* class_cast_macro (G_OBJECT_GET_CLASS (object))->signal) \ + parameters) \ + +#ifndef G_DISABLE_ASSERT + +/* Define a signal that is not implemented by this class but must be + * implemented by subclasses. This macro should be used inside the + * class initialization function. The companion macro EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL + * must be used earlier in the file. Called like this: + * + * EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, + * fm_directory_view, + * clear); + */ +#define EEL_ASSIGN_MUST_OVERRIDE_SIGNAL(class_pointer, prefix, signal) \ + \ +* (void (**)(void)) & (class_pointer)->signal = prefix##_unimplemented_##signal + +/* Provide a debug-only implementation of a signal that must be implemented + * by subclasses. The debug-only implementation fires a warning if it is called. + * This macro should be placed as if it were a function, earlier in the file + * than the class initialization function. Called like this: + * + * EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, clear); + */ +#define EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL(prefix, signal) \ + \ +static void \ +prefix##_unimplemented_##signal (void) \ +{ \ + g_warning ("failed to override signal " #prefix "->" #signal); \ +} + +#else /* G_DISABLE_ASSERT */ + +#define EEL_DEFINE_MUST_OVERRIDE_SIGNAL(class_cast_macro, class_pointer, prefix, signal) +#define EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL(prefix, signal) +#define EEL_ASSIGN_MUST_OVERRIDE_SIGNAL(class_pointer, prefix, signal) + +#endif /* G_DISABLE_ASSERT */ + +/* Access a method. */ +#define EEL_ACCESS_METHOD(class_cast_macro, object, method) \ +(class_cast_macro (G_OBJECT_GET_CLASS (object))->method) + +/* Invoke a method for a given object. */ +#define EEL_INVOKE_METHOD(class_cast_macro, object, method, parameters) \ +((* EEL_ACCESS_METHOD (class_cast_macro, object, method)) parameters) + +/* Assert the non-nullness of a method for a given object. */ +#define EEL_ASSERT_METHOD(class_cast_macro, object, method) \ +g_assert (EEL_ACCESS_METHOD (class_cast_macro, object, method) != NULL) + +/* Invoke a method if it ain't null. */ +#define EEL_INVOKE_METHOD_IF(class_cast_macro, object, method, parameters) \ +(EEL_ACCESS_METHOD (class_cast_macro, object, method) ? 0 : \ + EEL_INVOKE_METHOD (class_cast_macro, object, method, parameters)) + +#endif /* EEL_GTK_MACROS_H */ diff --git a/capplets/about-me/fingerprint-strings.h b/capplets/about-me/fingerprint-strings.h new file mode 100644 index 00000000..d1b919e9 --- /dev/null +++ b/capplets/about-me/fingerprint-strings.h @@ -0,0 +1,111 @@ +/* + * Helper functions to translate statuses and actions to strings + * Copyright (C) 2008 Bastien Nocera + * + * Experimental code. This will be moved out of fprintd into it's own + * package once the system has matured. + * + * 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. + */ + +struct { + const char *dbus_name; + const char *place_str; + const char *swipe_str; +} fingers[11] = { + { "left-thumb", N_("Place your left thumb on %s"), N_("Swipe your left thumb on %s") }, + { "left-index-finger", N_("Place your left index finger on %s"), N_("Swipe your left index finger on %s") }, + { "left-middle-finger", N_("Place your left middle finger on %s"), N_("Swipe your left middle finger on %s") }, + { "left-ring-finger", N_("Place your left ring finger on %s"), N_("Swipe your left ring finger on %s") }, + { "left-little-finger", N_("Place your left little finger on %s"), N_("Swipe your left little finger on %s") }, + { "right-thumb", N_("Place your right thumb on %s"), N_("Swipe your right thumb on %s") }, + { "right-index-finger", N_("Place your right index finger on %s"), N_("Swipe your right index finger on %s") }, + { "right-middle-finger", N_("Place your right middle finger on %s"), N_("Swipe your right middle finger on %s") }, + { "right-ring-finger", N_("Place your right ring finger on %s"), N_("Swipe your right ring finger on %s") }, + { "right-little-finger", N_("Place your right little finger on %s"), N_("Swipe your right little finger on %s") }, + { NULL, NULL, NULL } +}; + +static const char *finger_str_to_msg(const char *finger_name, gboolean is_swipe) +{ + int i; + + if (finger_name == NULL) + return NULL; + + for (i = 0; fingers[i].dbus_name != NULL; i++) { + if (g_str_equal (fingers[i].dbus_name, finger_name)) { + if (is_swipe == FALSE) + return fingers[i].place_str; + else + return fingers[i].swipe_str; + } + } + + return NULL; +} + +/* Cases not handled: + * verify-no-match + * verify-match + * verify-unknown-error + */ +static const char *verify_result_str_to_msg(const char *result, gboolean is_swipe) +{ + if (result == NULL) + return NULL; + + if (strcmp (result, "verify-retry-scan") == 0) { + if (is_swipe == FALSE) + return N_("Place your finger on the reader again"); + else + return N_("Swipe your finger again"); + } + if (strcmp (result, "verify-swipe-too-short") == 0) + return N_("Swipe was too short, try again"); + if (strcmp (result, "verify-finger-not-centered") == 0) + return N_("Your finger was not centered, try swiping your finger again"); + if (strcmp (result, "verify-remove-and-retry") == 0) + return N_("Remove your finger, and try swiping your finger again"); + + return NULL; +} + +/* Cases not handled: + * enroll-completed + * enroll-failed + * enroll-unknown-error + */ +static const char *enroll_result_str_to_msg(const char *result, gboolean is_swipe) +{ + if (result == NULL) + return NULL; + + if (strcmp (result, "enroll-retry-scan") == 0 || strcmp (result, "enroll-stage-passed") == 0) { + if (is_swipe == FALSE) + return N_("Place your finger on the reader again"); + else + return N_("Swipe your finger again"); + } + if (strcmp (result, "enroll-swipe-too-short") == 0) + return N_("Swipe was too short, try again"); + if (strcmp (result, "enroll-finger-not-centered") == 0) + return N_("Your finger was not centered, try swiping your finger again"); + if (strcmp (result, "enroll-remove-and-retry") == 0) + return N_("Remove your finger, and try swiping your finger again"); + + return NULL; +} + diff --git a/capplets/about-me/fprintd-marshal.list b/capplets/about-me/fprintd-marshal.list new file mode 100644 index 00000000..c4effb63 --- /dev/null +++ b/capplets/about-me/fprintd-marshal.list @@ -0,0 +1 @@ +VOID:STRING,BOOLEAN diff --git a/capplets/about-me/icons/Makefile.am b/capplets/about-me/icons/Makefile.am new file mode 100644 index 00000000..a35a9e64 --- /dev/null +++ b/capplets/about-me/icons/Makefile.am @@ -0,0 +1,30 @@ +pixmapsdir = $(pkgdatadir)/pixmaps +pixmaps_DATA = \ + left-index-finger.svg \ + left-little-finger.svg \ + left-middle-finger.svg \ + left-ring-finger.svg \ + left-thumb.svg \ + print_error.svg \ + print_ok.svg \ + right-index-finger.svg \ + right-little-finger.svg \ + right-middle-finger.svg \ + right-ring-finger.svg \ + right-thumb.svg \ + left-index-finger.png \ + left-middle-finger.png \ + left-little-finger.png \ + left-ring-finger.png \ + left-thumb.png \ + print_error.png \ + print_ok.png \ + right-index-finger.png \ + right-middle-finger.png \ + right-little-finger.png \ + right-ring-finger.png \ + right-thumb.png + +EXTRA_DIST = $(pixmaps_DATA) + +-include $(top_srcdir)/git.mk diff --git a/capplets/about-me/icons/left-index-finger.png b/capplets/about-me/icons/left-index-finger.png new file mode 100644 index 00000000..1a9cb2c7 Binary files /dev/null and b/capplets/about-me/icons/left-index-finger.png differ diff --git a/capplets/about-me/icons/left-index-finger.svg b/capplets/about-me/icons/left-index-finger.svg new file mode 100644 index 00000000..3c36aeaa --- /dev/null +++ b/capplets/about-me/icons/left-index-finger.svg @@ -0,0 +1,177 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/left-little-finger.png b/capplets/about-me/icons/left-little-finger.png new file mode 100644 index 00000000..978942ee Binary files /dev/null and b/capplets/about-me/icons/left-little-finger.png differ diff --git a/capplets/about-me/icons/left-little-finger.svg b/capplets/about-me/icons/left-little-finger.svg new file mode 100644 index 00000000..0835854f --- /dev/null +++ b/capplets/about-me/icons/left-little-finger.svg @@ -0,0 +1,180 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/left-middle-finger.png b/capplets/about-me/icons/left-middle-finger.png new file mode 100644 index 00000000..406925e9 Binary files /dev/null and b/capplets/about-me/icons/left-middle-finger.png differ diff --git a/capplets/about-me/icons/left-middle-finger.svg b/capplets/about-me/icons/left-middle-finger.svg new file mode 100644 index 00000000..1082da2e --- /dev/null +++ b/capplets/about-me/icons/left-middle-finger.svg @@ -0,0 +1,180 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/left-ring-finger.png b/capplets/about-me/icons/left-ring-finger.png new file mode 100644 index 00000000..169ff687 Binary files /dev/null and b/capplets/about-me/icons/left-ring-finger.png differ diff --git a/capplets/about-me/icons/left-ring-finger.svg b/capplets/about-me/icons/left-ring-finger.svg new file mode 100644 index 00000000..50ace807 --- /dev/null +++ b/capplets/about-me/icons/left-ring-finger.svg @@ -0,0 +1,180 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/left-thumb.png b/capplets/about-me/icons/left-thumb.png new file mode 100644 index 00000000..eaf875d0 Binary files /dev/null and b/capplets/about-me/icons/left-thumb.png differ diff --git a/capplets/about-me/icons/left-thumb.svg b/capplets/about-me/icons/left-thumb.svg new file mode 100644 index 00000000..fd0f5827 --- /dev/null +++ b/capplets/about-me/icons/left-thumb.svg @@ -0,0 +1,180 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/print_error.png b/capplets/about-me/icons/print_error.png new file mode 100644 index 00000000..d67150b1 Binary files /dev/null and b/capplets/about-me/icons/print_error.png differ diff --git a/capplets/about-me/icons/print_error.svg b/capplets/about-me/icons/print_error.svg new file mode 100644 index 00000000..4ad6beed --- /dev/null +++ b/capplets/about-me/icons/print_error.svg @@ -0,0 +1,525 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/print_ok.png b/capplets/about-me/icons/print_ok.png new file mode 100644 index 00000000..4dd615e7 Binary files /dev/null and b/capplets/about-me/icons/print_ok.png differ diff --git a/capplets/about-me/icons/print_ok.svg b/capplets/about-me/icons/print_ok.svg new file mode 100644 index 00000000..ba821ef7 --- /dev/null +++ b/capplets/about-me/icons/print_ok.svg @@ -0,0 +1,310 @@ + + +image/svg+xml + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/right-index-finger.png b/capplets/about-me/icons/right-index-finger.png new file mode 100644 index 00000000..4aaeaac4 Binary files /dev/null and b/capplets/about-me/icons/right-index-finger.png differ diff --git a/capplets/about-me/icons/right-index-finger.svg b/capplets/about-me/icons/right-index-finger.svg new file mode 100644 index 00000000..5a621a2e --- /dev/null +++ b/capplets/about-me/icons/right-index-finger.svg @@ -0,0 +1,179 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/right-little-finger.png b/capplets/about-me/icons/right-little-finger.png new file mode 100644 index 00000000..17946afc Binary files /dev/null and b/capplets/about-me/icons/right-little-finger.png differ diff --git a/capplets/about-me/icons/right-little-finger.svg b/capplets/about-me/icons/right-little-finger.svg new file mode 100644 index 00000000..9fcec2af --- /dev/null +++ b/capplets/about-me/icons/right-little-finger.svg @@ -0,0 +1,182 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/right-middle-finger.png b/capplets/about-me/icons/right-middle-finger.png new file mode 100644 index 00000000..71bd41ba Binary files /dev/null and b/capplets/about-me/icons/right-middle-finger.png differ diff --git a/capplets/about-me/icons/right-middle-finger.svg b/capplets/about-me/icons/right-middle-finger.svg new file mode 100644 index 00000000..b33a654a --- /dev/null +++ b/capplets/about-me/icons/right-middle-finger.svg @@ -0,0 +1,182 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/right-ring-finger.png b/capplets/about-me/icons/right-ring-finger.png new file mode 100644 index 00000000..aa73ae6e Binary files /dev/null and b/capplets/about-me/icons/right-ring-finger.png differ diff --git a/capplets/about-me/icons/right-ring-finger.svg b/capplets/about-me/icons/right-ring-finger.svg new file mode 100644 index 00000000..9e264fe6 --- /dev/null +++ b/capplets/about-me/icons/right-ring-finger.svg @@ -0,0 +1,182 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/right-thumb.png b/capplets/about-me/icons/right-thumb.png new file mode 100644 index 00000000..1c967d61 Binary files /dev/null and b/capplets/about-me/icons/right-thumb.png differ diff --git a/capplets/about-me/icons/right-thumb.svg b/capplets/about-me/icons/right-thumb.svg new file mode 100644 index 00000000..0aa0f2e4 --- /dev/null +++ b/capplets/about-me/icons/right-thumb.svg @@ -0,0 +1,182 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/mate-about-me-dialog.ui b/capplets/about-me/mate-about-me-dialog.ui new file mode 100644 index 00000000..fdf79b6e --- /dev/null +++ b/capplets/about-me/mate-about-me-dialog.ui @@ -0,0 +1,1622 @@ + + + + + + 5 + About Me + dialog + False + + + True + vertical + 2 + + + True + 5 + vertical + 12 + + + True + + + True + 12 + + + 80 + 80 + True + True + False + Select your photo + + + + + + False + False + 0 + + + + + True + 0 + Full Name + + + False + False + 1 + + + + + 0 + + + + + True + vertical + 6 + + + True + 12 + + + True + 0 + User name: + + + False + False + 0 + + + + + True + 0 + + + 1 + + + + + False + False + 0 + + + + + Change Passwo_rd... + True + True + False + True + + + False + False + 1 + + + + + Enable _Fingerprint Login... + True + False + True + + + False + False + 2 + + + + + Disable _Fingerprint Login... + True + False + True + + + False + False + 3 + + + + + False + 1 + + + + + False + False + 0 + + + + + True + + + True + True + + + True + 12 + vertical + 18 + + + True + vertical + 6 + + + True + 0 + Email + + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 2 + 2 + 12 + 6 + + + True + 0 + _Work: + True + email-work-e + + + GTK_FILL + + + + + + True + 0 + _Home: + True + email-home-e + + + 1 + 2 + GTK_FILL + + + + + + True + True + + + 1 + 2 + + + + + + True + True + + + 1 + 2 + 1 + 2 + + + + + + 1 + + + + + 1 + + + + + False + False + 0 + + + + + True + vertical + 6 + + + True + 0 + Telephone + + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 2 + 4 + 12 + 6 + + + True + 0 + Wor_k: + True + phone-work-e + + + GTK_FILL + + + + + + True + 0 + Hom_e: + True + phone-home-e + + + 1 + 2 + GTK_FILL + + + + + + True + True + + + 1 + 2 + + + + + + True + True + + + 1 + 2 + 1 + 2 + + + + + + True + True + + + 3 + 4 + 1 + 2 + + + + + + True + True + + + 3 + 4 + + + + + + True + 0 + _Mobile: + True + phone-mobile-e + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + Work _fax: + True + phone-work-fax-e + + + 2 + 3 + GTK_FILL + + + + + + 1 + + + + + 1 + + + + + False + False + 1 + + + + + True + vertical + 6 + + + True + 0 + Instant Messaging + + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 3 + 4 + 12 + 6 + + + True + 0 + _XMPP: + True + im-jabber-e + + + GTK_FILL + + + + + + True + True + + + 1 + 2 + + + + + + True + True + + + 1 + 2 + 1 + 2 + + + + + + True + 0 + _Yahoo: + True + im-yahoo-e + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + M_SN: + True + im-msn-e + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + 0 + IC_Q: + True + im-icq-e + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + A_IM/iChat: + True + im-aim-e + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + _GroupWise: + True + im-groupwise-e + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + + True + True + + + 1 + 2 + 2 + 3 + + + + + + True + True + + + 3 + 4 + + + + + + True + True + + + 3 + 4 + 1 + 2 + + + + + + True + True + + + 3 + 4 + 2 + 3 + + + + + + 1 + + + + + 1 + + + + + False + False + 2 + + + + + + + True + Contact + + + False + + + + + True + 12 + vertical + 18 + + + True + vertical + + + True + vertical + + + True + 0 + Home + + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 4 + 4 + 12 + 6 + + + True + 0 + _Address: + True + addr-street-1 + + + GTK_FILL + + + + + + True + True + automatic + never + in + + + True + True + False + + + + + 1 + 2 + 3 + GTK_FILL + + + + + True + 0 + C_ity: + True + addr-locality-1 + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + _ZIP/Postal code: + True + addr-code-1 + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + _State/Province: + True + addr-region-1 + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + + True + 0 + Co_untry: + True + addr-country-1 + + + 2 + 3 + 3 + 4 + GTK_FILL + + + + + + True + 0 + P._O. box: + True + addr-po-1 + + + 3 + 4 + GTK_FILL + + + + + + True + True + + + 1 + 2 + 3 + 4 + + + + + + True + True + + + 3 + 4 + + + + + + True + True + + + 3 + 4 + 1 + 2 + + + + + + True + True + + + 3 + 4 + 3 + 4 + + + + + + True + True + + + 3 + 4 + 2 + 3 + + + + + + + + + + + + 1 + + + + + 1 + + + + + False + False + 0 + + + + + False + False + 0 + + + + + True + vertical + 6 + + + True + 0 + Work + + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 4 + 4 + 12 + 6 + + + True + 0 + A_ddress: + True + addr-street-2 + + + GTK_FILL + + + + + + True + True + automatic + never + in + + + True + True + False + + + + + 1 + 2 + 3 + GTK_FILL + + + + + True + 0 + ZIP/_Postal code: + True + addr-code-2 + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + State/Pro_vince: + True + addr-region-2 + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + + True + 0 + Cou_ntry: + True + addr-country-2 + + + 2 + 3 + 3 + 4 + GTK_FILL + + + + + + True + 0 + P.O. _box: + True + addr-po-2 + + + 3 + 4 + GTK_FILL + + + + + + True + 0 + Ci_ty: + True + addr-locality-2 + + + 2 + 3 + GTK_FILL + + + + + + True + True + + + 1 + 2 + 3 + 4 + + + + + + True + True + + + 3 + 4 + + + + + + True + True + + + 3 + 4 + 1 + 2 + + + + + + True + True + + + 3 + 4 + 2 + 3 + + + + + + True + True + + + 3 + 4 + 3 + 4 + + + + + + + + + + + + 1 + + + + + 1 + + + + + False + False + 1 + + + + + 1 + + + + + True + Address + + + 1 + False + + + + + True + 12 + vertical + 18 + + + True + vertical + 6 + + + True + 0 + Web + + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 3 + 2 + 12 + 6 + + + True + 0 + _Home page: + True + web-homepage-e + + + GTK_FILL + + + + + + True + 0 + Web _log: + True + web-weblog-e + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + Cale_ndar: + True + web-calendar-e + + + 2 + 3 + GTK_FILL + + + + + + True + True + + + 1 + 2 + + + + + + True + True + + + 1 + 2 + 1 + 2 + + + + + + True + True + + + 1 + 2 + 2 + 3 + + + + + + 1 + + + + + 1 + + + + + False + False + 0 + + + + + True + vertical + 6 + + + True + 0 + Job + + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 6 + 2 + 12 + 6 + + + True + 0 + _Profession: + True + job-profession-e + + + GTK_FILL + + + + + + True + 0 + C_ompany: + True + job-company-e + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + _Manager: + True + job-manager-e + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + _Title: + True + job-title-e + + + 3 + 4 + GTK_FILL + + + + + + True + 0 + _Department: + True + job-dept-e + + + 4 + 5 + GTK_FILL + + + + + + True + 0 + A_ssistant: + True + job-assistant-e + + + 5 + 6 + GTK_FILL + + + + + + True + True + + + 1 + 2 + + + + + + True + True + + + 1 + 2 + 1 + 2 + + + + + + True + True + + + 1 + 2 + 2 + 3 + + + + + + True + True + + + 1 + 2 + 3 + 4 + + + + + + True + True + + + 1 + 2 + 4 + 5 + + + + + + True + True + + + 1 + 2 + 5 + 6 + + + + + + 1 + + + + + 1 + + + + + False + False + 1 + + + + + 2 + + + + + True + Personal Info + + + 2 + False + + + + + 0 + + + + + 1 + + + + + 1 + + + + + True + end + + + gtk-close + True + True + True + False + True + + + False + False + 0 + + + + + False + end + 0 + + + + + + closebutton1 + + + diff --git a/capplets/about-me/mate-about-me-fingerprint.c b/capplets/about-me/mate-about-me-fingerprint.c new file mode 100644 index 00000000..0d6e40ae --- /dev/null +++ b/capplets/about-me/mate-about-me-fingerprint.c @@ -0,0 +1,624 @@ +/* mate-about-me-fingerprint.h + * Copyright (C) 2008 Bastien Nocera + * + * 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, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include +#include + +#include "fingerprint-strings.h" +#include "capplet-util.h" + +/* This must match the number of images on the 2nd page in the UI file */ +#define MAX_ENROLL_STAGES 5 + +/* Translate fprintd strings */ +#define TR(s) dgettext("fprintd", s) + +static DBusGProxy *manager = NULL; +static DBusGConnection *connection = NULL; +static gboolean is_disable = FALSE; + +enum { + STATE_NONE, + STATE_CLAIMED, + STATE_ENROLLING +}; + +typedef struct { + GtkWidget *enable; + GtkWidget *disable; + + GtkWidget *ass; + GtkBuilder *dialog; + + DBusGProxy *device; + gboolean is_swipe; + int num_enroll_stages; + int num_stages_done; + char *name; + const char *finger; + gint state; +} EnrollData; + +static void create_manager (void) +{ + GError *error = NULL; + + connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (connection == NULL) { + g_warning ("Failed to connect to session bus: %s", error->message); + return; + } + + manager = dbus_g_proxy_new_for_name (connection, + "net.reactivated.Fprint", + "/net/reactivated/Fprint/Manager", + "net.reactivated.Fprint.Manager"); +} + +static DBusGProxy * +get_first_device (void) +{ + DBusGProxy *device; + char *device_str; + + if (!dbus_g_proxy_call (manager, "GetDefaultDevice", NULL, G_TYPE_INVALID, + DBUS_TYPE_G_OBJECT_PATH, &device_str, G_TYPE_INVALID)) { + return NULL; + } + + device = dbus_g_proxy_new_for_name(connection, + "net.reactivated.Fprint", + device_str, + "net.reactivated.Fprint.Device"); + + g_free (device_str); + + return device; +} + +static const char * +get_reason_for_error (const char *dbus_error) +{ + if (g_str_equal (dbus_error, "net.reactivated.Fprint.Error.PermissionDenied")) + return N_("You are not allowed to access the device. Contact your system administrator."); + if (g_str_equal (dbus_error, "net.reactivated.Fprint.Error.AlreadyInUse")) + return N_("The device is already in use."); + if (g_str_equal (dbus_error, "net.reactivated.Fprint.Error.Internal")) + return N_("An internal error occured"); + + return NULL; +} + +static GtkWidget * +get_error_dialog (const char *title, + const char *dbus_error, + GtkWindow *parent) +{ + GtkWidget *error_dialog; + const char *reason; + + if (dbus_error == NULL) + g_warning ("get_error_dialog called with reason == NULL"); + + error_dialog = + gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", title); + reason = get_reason_for_error (dbus_error); + gtk_message_dialog_format_secondary_text + (GTK_MESSAGE_DIALOG (error_dialog), "%s", reason ? _(reason) : _(dbus_error)); + + gtk_window_set_title (GTK_WINDOW (error_dialog), ""); /* as per HIG */ + gtk_container_set_border_width (GTK_CONTAINER (error_dialog), 5); + gtk_dialog_set_default_response (GTK_DIALOG (error_dialog), + GTK_RESPONSE_OK); + gtk_window_set_modal (GTK_WINDOW (error_dialog), TRUE); + gtk_window_set_position (GTK_WINDOW (error_dialog), GTK_WIN_POS_CENTER_ON_PARENT); + + return error_dialog; +} + +void +set_fingerprint_label (GtkWidget *enable, GtkWidget *disable) +{ + char **fingers; + DBusGProxy *device; + GError *error = NULL; + + gtk_widget_set_no_show_all (enable, TRUE); + gtk_widget_set_no_show_all (disable, TRUE); + + if (manager == NULL) { + create_manager (); + if (manager == NULL) { + gtk_widget_hide (enable); + gtk_widget_hide (disable); + return; + } + } + + device = get_first_device (); + if (device == NULL) { + gtk_widget_hide (enable); + gtk_widget_hide (disable); + return; + } + + if (!dbus_g_proxy_call (device, "ListEnrolledFingers", &error, G_TYPE_STRING, "", G_TYPE_INVALID, + G_TYPE_STRV, &fingers, G_TYPE_INVALID)) { + if (dbus_g_error_has_name (error, "net.reactivated.Fprint.Error.NoEnrolledPrints") == FALSE) { + gtk_widget_hide (enable); + gtk_widget_hide (disable); + g_object_unref (device); + return; + } + fingers = NULL; + } + + if (fingers == NULL || g_strv_length (fingers) == 0) { + gtk_widget_hide (disable); + gtk_widget_show (enable); + is_disable = FALSE; + } else { + gtk_widget_hide (enable); + gtk_widget_show (disable); + is_disable = TRUE; + } + + g_strfreev (fingers); + g_object_unref (device); +} + +static void +delete_fingerprints (void) +{ + DBusGProxy *device; + + if (manager == NULL) { + create_manager (); + if (manager == NULL) + return; + } + + device = get_first_device (); + if (device == NULL) + return; + + dbus_g_proxy_call (device, "DeleteEnrolledFingers", NULL, G_TYPE_STRING, "", G_TYPE_INVALID, G_TYPE_INVALID); + + g_object_unref (device); +} + +static void +delete_fingerprints_question (GtkBuilder *dialog, GtkWidget *enable, GtkWidget *disable) +{ + GtkWidget *question; + GtkWidget *button; + + question = gtk_message_dialog_new (GTK_WINDOW (WID ("about-me-dialog")), + GTK_DIALOG_MODAL, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + _("Delete registered fingerprints?")); + gtk_dialog_add_button (GTK_DIALOG (question), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + + button = gtk_button_new_with_mnemonic (_("_Delete Fingerprints")); + gtk_button_set_image (GTK_BUTTON (button), gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_BUTTON)); + gtk_widget_set_can_default (button, TRUE); + gtk_widget_show (button); + gtk_dialog_add_action_widget (GTK_DIALOG (question), button, GTK_RESPONSE_OK); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (question), + _("Do you want to delete your registered fingerprints so fingerprint login is disabled?")); + gtk_container_set_border_width (GTK_CONTAINER (question), 5); + gtk_dialog_set_default_response (GTK_DIALOG (question), GTK_RESPONSE_OK); + gtk_window_set_position (GTK_WINDOW (question), GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_modal (GTK_WINDOW (question), TRUE); + + if (gtk_dialog_run (GTK_DIALOG (question)) == GTK_RESPONSE_OK) { + delete_fingerprints (); + set_fingerprint_label (enable, disable); + } + + gtk_widget_destroy (question); +} + +static void +enroll_data_destroy (EnrollData *data) +{ + switch (data->state) { + case STATE_ENROLLING: + dbus_g_proxy_call(data->device, "EnrollStop", NULL, G_TYPE_INVALID, G_TYPE_INVALID); + /* fall-through */ + case STATE_CLAIMED: + dbus_g_proxy_call(data->device, "Release", NULL, G_TYPE_INVALID, G_TYPE_INVALID); + /* fall-through */ + case STATE_NONE: + g_free (data->name); + g_object_unref (data->device); + g_object_unref (data->dialog); + gtk_widget_destroy (data->ass); + + g_free (data); + } +} + +static const char * +selected_finger (GtkBuilder *dialog) +{ + int index; + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (WID ("radiobutton1")))) { + gtk_widget_set_sensitive (WID ("finger_combobox"), FALSE); + return "right-index-finger"; + } + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (WID ("radiobutton2")))) { + gtk_widget_set_sensitive (WID ("finger_combobox"), FALSE); + return "left-index-finger"; + } + gtk_widget_set_sensitive (WID ("finger_combobox"), TRUE); + index = gtk_combo_box_get_active (GTK_COMBO_BOX (WID ("finger_combobox"))); + switch (index) { + case 0: + return "left-thumb"; + case 1: + return "left-middle-finger"; + case 2: + return "left-ring-finger"; + case 3: + return "left-little-finger"; + case 4: + return "right-thumb"; + case 5: + return "right-middle-finger"; + case 6: + return "right-ring-finger"; + case 7: + return "right-little-finger"; + default: + g_assert_not_reached (); + } + + return NULL; +} + +static void +finger_radio_button_toggled (GtkToggleButton *button, EnrollData *data) +{ + GtkBuilder *dialog = data->dialog; + char *msg; + + data->finger = selected_finger (data->dialog); + + msg = g_strdup_printf (TR(finger_str_to_msg (data->finger, data->is_swipe)), data->name); + gtk_label_set_text (GTK_LABEL (WID("enroll-label")), msg); + g_free (msg); +} + +static void +finger_combobox_changed (GtkComboBox *combobox, EnrollData *data) +{ + GtkBuilder *dialog = data->dialog; + char *msg; + + data->finger = selected_finger (data->dialog); + + msg = g_strdup_printf (TR(finger_str_to_msg (data->finger, data->is_swipe)), data->name); + gtk_label_set_text (GTK_LABEL (WID("enroll-label")), msg); + g_free (msg); +} + +static void +assistant_cancelled (GtkAssistant *ass, EnrollData *data) +{ + GtkWidget *enable, *disable; + + enable = data->enable; + disable = data->disable; + + enroll_data_destroy (data); + set_fingerprint_label (enable, disable); +} + +static void +enroll_result (GObject *object, const char *result, gboolean done, EnrollData *data) +{ + GtkBuilder *dialog = data->dialog; + char *msg; + + if (g_str_equal (result, "enroll-completed") || g_str_equal (result, "enroll-stage-passed")) { + char *name, *path; + + data->num_stages_done++; + name = g_strdup_printf ("image%d", data->num_stages_done); + path = g_build_filename (MATECC_PIXMAP_DIR, "print_ok.png", NULL); + gtk_image_set_from_file (GTK_IMAGE (WID (name)), path); + g_free (name); + g_free (path); + } + if (g_str_equal (result, "enroll-completed")) { + gtk_label_set_text (GTK_LABEL (WID ("status-label")), _("Done!")); + gtk_assistant_set_page_complete (GTK_ASSISTANT (data->ass), WID ("page2"), TRUE); + } + + if (done != FALSE) { + dbus_g_proxy_call(data->device, "EnrollStop", NULL, G_TYPE_INVALID, G_TYPE_INVALID); + data->state = STATE_CLAIMED; + if (g_str_equal (result, "enroll-completed") == FALSE) { + /* The enrollment failed, restart it */ + dbus_g_proxy_call(data->device, "EnrollStart", NULL, G_TYPE_STRING, data->finger, G_TYPE_INVALID, G_TYPE_INVALID); + data->state = STATE_ENROLLING; + result = "enroll-retry-scan"; + } else { + return; + } + } + + msg = g_strdup_printf (TR(enroll_result_str_to_msg (result, data->is_swipe)), data->name); + gtk_label_set_text (GTK_LABEL (WID ("status-label")), msg); + g_free (msg); +} + +static void +assistant_prepare (GtkAssistant *ass, GtkWidget *page, EnrollData *data) +{ + const char *name; + + name = g_object_get_data (G_OBJECT (page), "name"); + if (name == NULL) + return; + + if (g_str_equal (name, "enroll")) { + DBusGProxy *p; + GError *error = NULL; + GtkBuilder *dialog = data->dialog; + char *path; + guint i; + GValue value = { 0, }; + + if (!dbus_g_proxy_call (data->device, "Claim", &error, G_TYPE_STRING, "", G_TYPE_INVALID, G_TYPE_INVALID)) { + GtkWidget *d; + char *msg; + + /* translators: + * The variable is the name of the device, for example: + * "Could you not access "Digital Persona U.are.U 4000/4000B" device */ + msg = g_strdup_printf (_("Could not access '%s' device"), data->name); + d = get_error_dialog (msg, dbus_g_error_get_name (error), GTK_WINDOW (data->ass)); + g_error_free (error); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (d); + g_free (msg); + + enroll_data_destroy (data); + + return; + } + data->state = STATE_CLAIMED; + + p = dbus_g_proxy_new_from_proxy (data->device, "org.freedesktop.DBus.Properties", NULL); + if (!dbus_g_proxy_call (p, "Get", NULL, G_TYPE_STRING, "net.reactivated.Fprint.Device", G_TYPE_STRING, "num-enroll-stages", G_TYPE_INVALID, + G_TYPE_VALUE, &value, G_TYPE_INVALID) || g_value_get_int (&value) < 1) { + GtkWidget *d; + char *msg; + + /* translators: + * The variable is the name of the device, for example: + * "Could you not access "Digital Persona U.are.U 4000/4000B" device */ + msg = g_strdup_printf (_("Could not access '%s' device"), data->name); + d = get_error_dialog (msg, "net.reactivated.Fprint.Error.Internal", GTK_WINDOW (data->ass)); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (d); + g_free (msg); + + enroll_data_destroy (data); + + g_object_unref (p); + return; + } + g_object_unref (p); + + data->num_enroll_stages = g_value_get_int (&value); + + /* Hide the extra "bulbs" if not needed */ + for (i = MAX_ENROLL_STAGES; i > data->num_enroll_stages; i--) { + char *name; + + name = g_strdup_printf ("image%d", i); + gtk_widget_hide (WID (name)); + g_free (name); + } + /* And set the right image */ + { + char *filename; + + filename = g_strdup_printf ("%s.png", data->finger); + path = g_build_filename (MATECC_PIXMAP_DIR, filename, NULL); + g_free (filename); + } + for (i = 1; i <= data->num_enroll_stages; i++) { + char *name; + name = g_strdup_printf ("image%d", i); + gtk_image_set_from_file (GTK_IMAGE (WID (name)), path); + g_free (name); + } + g_free (path); + + dbus_g_proxy_add_signal(data->device, "EnrollStatus", G_TYPE_STRING, G_TYPE_BOOLEAN, NULL); + dbus_g_proxy_connect_signal(data->device, "EnrollStatus", G_CALLBACK(enroll_result), data, NULL); + + if (!dbus_g_proxy_call(data->device, "EnrollStart", &error, G_TYPE_STRING, data->finger, G_TYPE_INVALID, G_TYPE_INVALID)) { + GtkWidget *d; + char *msg; + + /* translators: + * The variable is the name of the device, for example: + * "Could you not access "Digital Persona U.are.U 4000/4000B" device */ + msg = g_strdup_printf (_("Could not start finger capture on '%s' device"), data->name); + d = get_error_dialog (msg, dbus_g_error_get_name (error), GTK_WINDOW (data->ass)); + g_error_free (error); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (d); + g_free (msg); + + enroll_data_destroy (data); + + return; + } + data->state = STATE_ENROLLING;; + } else { + if (data->state == STATE_ENROLLING) { + dbus_g_proxy_call(data->device, "EnrollStop", NULL, G_TYPE_INVALID, G_TYPE_INVALID); + data->state = STATE_CLAIMED; + } + if (data->state == STATE_CLAIMED) { + dbus_g_proxy_call(data->device, "Release", NULL, G_TYPE_INVALID, G_TYPE_INVALID); + data->state = STATE_NONE; + } + } +} + +static void +enroll_fingerprints (GtkWindow *parent, GtkWidget *enable, GtkWidget *disable) +{ + DBusGProxy *device, *p; + GHashTable *props; + GtkBuilder *dialog; + EnrollData *data; + GtkWidget *ass; + char *msg; + + device = NULL; + + if (manager == NULL) { + create_manager (); + if (manager != NULL) + device = get_first_device (); + } else { + device = get_first_device (); + } + + if (manager == NULL || device == NULL) { + GtkWidget *d; + + d = get_error_dialog (_("Could not access any fingerprint readers"), + _("Please contact your system administrator for help."), + parent); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (d); + return; + } + + data = g_new0 (EnrollData, 1); + data->device = device; + data->enable = enable; + data->disable = disable; + + /* Get some details about the device */ + p = dbus_g_proxy_new_from_proxy (device, "org.freedesktop.DBus.Properties", NULL); + if (dbus_g_proxy_call (p, "GetAll", NULL, G_TYPE_STRING, "net.reactivated.Fprint.Device", G_TYPE_INVALID, + dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE), &props, G_TYPE_INVALID)) { + const char *scan_type; + data->name = g_value_dup_string (g_hash_table_lookup (props, "name")); + scan_type = g_value_dup_string (g_hash_table_lookup (props, "scan-type")); + if (g_str_equal (scan_type, "swipe")) + data->is_swipe = TRUE; + g_hash_table_destroy (props); + } + g_object_unref (p); + + dialog = gtk_builder_new (); + gtk_builder_add_from_file (dialog, MATECC_UI_DIR "/mate-about-me-fingerprint.ui", NULL); + data->dialog = dialog; + + ass = WID ("assistant"); + gtk_window_set_title (GTK_WINDOW (ass), _("Enable Fingerprint Login")); + gtk_window_set_transient_for (GTK_WINDOW (ass), parent); + gtk_window_set_position (GTK_WINDOW (ass), GTK_WIN_POS_CENTER_ON_PARENT); + g_signal_connect (G_OBJECT (ass), "cancel", + G_CALLBACK (assistant_cancelled), data); + g_signal_connect (G_OBJECT (ass), "close", + G_CALLBACK (assistant_cancelled), data); + g_signal_connect (G_OBJECT (ass), "prepare", + G_CALLBACK (assistant_prepare), data); + + /* Page 1 */ + gtk_combo_box_set_active (GTK_COMBO_BOX (WID ("finger_combobox")), 0); + + g_signal_connect (G_OBJECT (WID ("radiobutton1")), "toggled", + G_CALLBACK (finger_radio_button_toggled), data); + g_signal_connect (G_OBJECT (WID ("radiobutton2")), "toggled", + G_CALLBACK (finger_radio_button_toggled), data); + g_signal_connect (G_OBJECT (WID ("radiobutton3")), "toggled", + G_CALLBACK (finger_radio_button_toggled), data); + g_signal_connect (G_OBJECT (WID ("finger_combobox")), "changed", + G_CALLBACK (finger_combobox_changed), data); + + data->finger = selected_finger (dialog); + + g_object_set_data (G_OBJECT (WID("page1")), "name", "intro"); + + /* translators: + * The variable is the name of the device, for example: + * "To enable fingerprint login, you need to save one of your fingerprints, using the + * 'Digital Persona U.are.U 4000/4000B' device." */ + msg = g_strdup_printf (_("To enable fingerprint login, you need to save one of your fingerprints, using the '%s' device."), + data->name); + gtk_label_set_text (GTK_LABEL (WID("intro-label")), msg); + g_free (msg); + + gtk_assistant_set_page_complete (GTK_ASSISTANT (ass), WID("page1"), TRUE); + + /* Page 2 */ + if (data->is_swipe != FALSE) + gtk_assistant_set_page_title (GTK_ASSISTANT (ass), WID("page2"), _("Swipe finger on reader")); + else + gtk_assistant_set_page_title (GTK_ASSISTANT (ass), WID("page2"), _("Place finger on reader")); + + g_object_set_data (G_OBJECT (WID("page2")), "name", "enroll"); + + msg = g_strdup_printf (TR(finger_str_to_msg (data->finger, data->is_swipe)), data->name); + gtk_label_set_text (GTK_LABEL (WID("enroll-label")), msg); + g_free (msg); + + /* Page 3 */ + g_object_set_data (G_OBJECT (WID("page3")), "name", "summary"); + + data->ass = ass; + gtk_widget_show_all (ass); +} + +void +fingerprint_button_clicked (GtkBuilder *dialog, + GtkWidget *enable, + GtkWidget *disable) +{ + bindtextdomain ("fprintd", MATELOCALEDIR); + bind_textdomain_codeset ("fprintd", "UTF-8"); + + if (is_disable != FALSE) { + delete_fingerprints_question (dialog, enable, disable); + } else { + enroll_fingerprints (GTK_WINDOW (WID ("about-me-dialog")), enable, disable); + } +} + diff --git a/capplets/about-me/mate-about-me-fingerprint.h b/capplets/about-me/mate-about-me-fingerprint.h new file mode 100644 index 00000000..c60ca358 --- /dev/null +++ b/capplets/about-me/mate-about-me-fingerprint.h @@ -0,0 +1,27 @@ +/* mate-about-me-fingerprint.h + * Copyright (C) 2008 Bastien Nocera + * + * 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, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +void set_fingerprint_label (GtkWidget *enable, + GtkWidget *disable); +void fingerprint_button_clicked (GtkBuilder *dialog, + GtkWidget *enable, + GtkWidget *disable); + diff --git a/capplets/about-me/mate-about-me-fingerprint.ui b/capplets/about-me/mate-about-me-fingerprint.ui new file mode 100644 index 00000000..7f154336 --- /dev/null +++ b/capplets/about-me/mate-about-me-fingerprint.ui @@ -0,0 +1,276 @@ + + + + + + + + + + + + Left thumb + + + Left middle finger + + + Left ring finger + + + Left little finger + + + Right thumb + + + Right middle finger + + + Right ring finger + + + Right little finger + + + + + 12 + Enable Fingerprint Login + + + + + + True + 12 + vertical + 12 + + + True + 6 + + + True + gtk-dialog-info + 6 + + + False + 0 + + + + + True + To enable fingerprint login, you need to save one of your fingerprints, using the Acme Foobar 5000. + True + + + False + False + 1 + + + + + 0 + + + + + True + vertical + + + Right index finger + True + True + False + True + True + True + + + False + False + 0 + + + + + Left index finger + True + True + False + True + True + radiobutton1 + + + False + False + 1 + + + + + True + + + Other finger: + True + True + False + True + True + radiobutton1 + + + False + False + 0 + + + + + True + False + model1 + + + + 0 + + + + + False + 1 + + + + + False + 2 + + + + + 1 + + + + + Select finger + + + + + True + vertical + + + True + In order to save your fingerprints, you need to swipe your thumb on the "Acme foobar" device. + True + + + False + False + 0 + + + + + True + + + + + + True + gtk-no + 6 + + + 1 + + + + + True + gtk-no + 6 + + + 2 + + + + + True + gtk-no + 6 + + + 3 + + + + + True + gtk-no + 6 + + + 4 + + + + + True + gtk-no + 6 + + + 5 + + + + + + + + 1 + + + + + True + + + False + False + 2 + + + + + Swipe finger on reader + + + + + True + Your fingerprint was successfully saved. You should now be able to log in using your fingerprint reader. + True + + + summary + Done! + + + + diff --git a/capplets/about-me/mate-about-me-password.c b/capplets/about-me/mate-about-me-password.c new file mode 100644 index 00000000..0699c28d --- /dev/null +++ b/capplets/about-me/mate-about-me-password.c @@ -0,0 +1,1136 @@ +/* mate-about-me.c + * Copyright (C) 2002 Diego Gonzalez + * Copyright (C) 2006 Johannes H. Jensen + * + * Written by: Diego Gonzalez + * Modified by: Johannes H. Jensen + * + * 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, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Parts of this code come from Mate-System-Tools. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* Are all of these needed? */ +#include +#include +#include +#include +#include +#include +#include + +#if __sun +#include +#include +#endif + +#include "capplet-util.h" +#include "eel-alert-dialog.h" + +/* Passwd states */ +typedef enum { + PASSWD_STATE_NONE, /* Passwd is not asking for anything */ + PASSWD_STATE_AUTH, /* Passwd is asking for our current password */ + PASSWD_STATE_NEW, /* Passwd is asking for our new password */ + PASSWD_STATE_RETYPE, /* Passwd is asking for our retyped new password */ + PASSWD_STATE_ERR /* Passwd reported an error but has not yet exited */ +} PasswdState; + +typedef struct { + GtkBuilder *ui; + + /* Commonly used widgets */ + GtkEntry *current_password; + GtkEntry *new_password; + GtkEntry *retyped_password; + GtkImage *dialog_image; + GtkLabel *status_label; + + /* Whether we have authenticated */ + gboolean authenticated; + + /* Communication with the passwd program */ + GPid backend_pid; + + GIOChannel *backend_stdin; + GIOChannel *backend_stdout; + + GQueue *backend_stdin_queue; /* Write queue to backend_stdin */ + + /* GMainLoop IDs */ + guint backend_child_watch_id; /* g_child_watch_add (PID) */ + guint backend_stdout_watch_id; /* g_io_add_watch (stdout) */ + + /* State of the passwd program */ + PasswdState backend_state; + +} PasswordDialog; + +/* Buffer size for backend output */ +#define BUFSIZE 64 + +/* + * Error handling {{ + */ +#define PASSDLG_ERROR (mate_about_me_password_error_quark()) + +GQuark mate_about_me_password_error_quark(void) +{ + static GQuark q = 0; + + if (q == 0) { + q = g_quark_from_static_string("mate_about_me_password_error"); + } + + return q; +} + +/* error codes */ +enum { + PASSDLG_ERROR_NONE, + PASSDLG_ERROR_NEW_PASSWORD_EMPTY, + PASSDLG_ERROR_RETYPED_PASSWORD_EMPTY, + PASSDLG_ERROR_PASSWORDS_NOT_EQUAL, + PASSDLG_ERROR_BACKEND, /* Backend error */ + PASSDLG_ERROR_USER, /* Generic user error */ + PASSDLG_ERROR_FAILED /* Fatal failure, error->message should explain */ +}; + +/* + * }} Error handling + */ + +/* + * Prototypes {{ + */ +static void +stop_passwd (PasswordDialog *pdialog); + +static void +free_passwd_resources (PasswordDialog *pdialog); + +static gboolean +io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswordDialog *pdialog); + +static void +passdlg_set_auth_state (PasswordDialog *pdialog, gboolean state); + +static void +passdlg_set_status (PasswordDialog *pdialog, gchar *msg); + +static void +passdlg_set_busy (PasswordDialog *pdialog, gboolean busy); + +static void +passdlg_clear (PasswordDialog *pdialog); + +static guint +passdlg_refresh_password_state (PasswordDialog *pdialog); + +/* + * }} Prototypes + */ + +/* + * Spawning and closing of backend {{ + */ + +/* Child watcher */ +static void +child_watch_cb (GPid pid, gint status, PasswordDialog *pdialog) +{ + if (WIFEXITED (status)) { + if (WEXITSTATUS (status) >= 255) { + g_warning (_("Child exited unexpectedly")); + } + } + + free_passwd_resources (pdialog); +} + +/* Spawn passwd backend + * Returns: TRUE on success, FALSE otherwise and sets error appropriately */ +static gboolean +spawn_passwd (PasswordDialog *pdialog, GError **error) +{ + gchar *argv[2]; + gchar *envp[1]; + gint my_stdin, my_stdout, my_stderr; + + argv[0] = "/usr/bin/passwd"; /* Is it safe to rely on a hard-coded path? */ + argv[1] = NULL; + + envp[0] = NULL; /* If we pass an empty array as the environment, + * will the childs environment be empty, and the + * locales set to the C default? From the manual: + * "If envp is NULL, the child inherits its + * parent'senvironment." + * If I'm wrong here, we somehow have to set + * the locales here. + */ + + if (!g_spawn_async_with_pipes (NULL, /* Working directory */ + argv, /* Argument vector */ + envp, /* Environment */ + G_SPAWN_DO_NOT_REAP_CHILD, /* Flags */ + NULL, /* Child setup */ + NULL, /* Data to child setup */ + &pdialog->backend_pid, /* PID */ + &my_stdin, /* Stdin */ + &my_stdout, /* Stdout */ + &my_stderr, /* Stderr */ + error)) { /* GError */ + + /* An error occured */ + free_passwd_resources (pdialog); + + return FALSE; + } + + /* 2>&1 */ + if (dup2 (my_stderr, my_stdout) == -1) { + /* Failed! */ + g_set_error (error, + PASSDLG_ERROR, + PASSDLG_ERROR_BACKEND, + strerror (errno)); + + /* Clean up */ + stop_passwd (pdialog); + + return FALSE; + } + + /* Open IO Channels */ + pdialog->backend_stdin = g_io_channel_unix_new (my_stdin); + pdialog->backend_stdout = g_io_channel_unix_new (my_stdout); + + /* Set raw encoding */ + /* Set nonblocking mode */ + if (g_io_channel_set_encoding (pdialog->backend_stdin, NULL, error) != G_IO_STATUS_NORMAL || + g_io_channel_set_encoding (pdialog->backend_stdout, NULL, error) != G_IO_STATUS_NORMAL || + g_io_channel_set_flags (pdialog->backend_stdin, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL || + g_io_channel_set_flags (pdialog->backend_stdout, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ) { + + /* Clean up */ + stop_passwd (pdialog); + return FALSE; + } + + /* Turn off buffering */ + g_io_channel_set_buffered (pdialog->backend_stdin, FALSE); + g_io_channel_set_buffered (pdialog->backend_stdout, FALSE); + + /* Add IO Channel watcher */ + pdialog->backend_stdout_watch_id = g_io_add_watch (pdialog->backend_stdout, + G_IO_IN | G_IO_PRI, + (GIOFunc) io_watch_stdout, pdialog); + + /* Add child watcher */ + pdialog->backend_child_watch_id = g_child_watch_add (pdialog->backend_pid, (GChildWatchFunc) child_watch_cb, pdialog); + + /* Success! */ + + return TRUE; +} + +/* Stop passwd backend */ +static void +stop_passwd (PasswordDialog *pdialog) +{ + /* This is the standard way of returning from the dialog with passwd. + * If we return this way we can safely kill passwd as it has completed + * its task. + */ + + if (pdialog->backend_pid != -1) { + kill (pdialog->backend_pid, 9); + } + + /* We must run free_passwd_resources here and not let our child + * watcher do it, since it will access invalid memory after the + * dialog has been closed and cleaned up. + * + * If we had more than a single thread we'd need to remove + * the child watch before trying to kill the child. + */ + free_passwd_resources (pdialog); +} + +/* Clean up passwd resources */ +static void +free_passwd_resources (PasswordDialog *pdialog) +{ + GError *error = NULL; + + /* Remove the child watcher */ + if (pdialog->backend_child_watch_id != 0) { + + g_source_remove (pdialog->backend_child_watch_id); + + pdialog->backend_child_watch_id = 0; + } + + + /* Close IO channels (internal file descriptors are automatically closed) */ + if (pdialog->backend_stdin != NULL) { + + if (g_io_channel_shutdown (pdialog->backend_stdin, TRUE, &error) != G_IO_STATUS_NORMAL) { + g_warning (_("Could not shutdown backend_stdin IO channel: %s"), error->message); + g_error_free (error); + error = NULL; + } + + g_io_channel_unref (pdialog->backend_stdin); + + pdialog->backend_stdin = NULL; + } + + if (pdialog->backend_stdout != NULL) { + + if (g_io_channel_shutdown (pdialog->backend_stdout, TRUE, &error) != G_IO_STATUS_NORMAL) { + g_warning (_("Could not shutdown backend_stdout IO channel: %s"), error->message); + g_error_free (error); + error = NULL; + } + + g_io_channel_unref (pdialog->backend_stdout); + + pdialog->backend_stdout = NULL; + } + + /* Remove IO watcher */ + if (pdialog->backend_stdout_watch_id != 0) { + + g_source_remove (pdialog->backend_stdout_watch_id); + + pdialog->backend_stdout_watch_id = 0; + } + + /* Close PID */ + if (pdialog->backend_pid != -1) { + + g_spawn_close_pid (pdialog->backend_pid); + + pdialog->backend_pid = -1; + } + + /* Clear backend state */ + pdialog->backend_state = PASSWD_STATE_NONE; +} + +/* + * }} Spawning and closing of backend + */ + +/* + * Backend communication code {{ + */ + +/* Write the first element of queue through channel */ +static void +io_queue_pop (GQueue *queue, GIOChannel *channel) +{ + gchar *buf; + gsize bytes_written; + GError *error = NULL; + + buf = g_queue_pop_head (queue); + + if (buf != NULL) { + + if (g_io_channel_write_chars (channel, buf, -1, &bytes_written, &error) != G_IO_STATUS_NORMAL) { + g_warning ("Could not write queue element \"%s\" to channel: %s", buf, error->message); + g_error_free (error); + } + + g_free (buf); + } +} + +/* Goes through the argument list, checking if one of them occurs in str + * Returns: TRUE as soon as an element is found to match, FALSE otherwise */ +static gboolean +is_string_complete (gchar *str, ...) +{ + va_list ap; + gchar *arg; + + if (strlen (str) == 0) { + return FALSE; + } + + va_start (ap, str); + + while ((arg = va_arg (ap, char *)) != NULL) { + if (g_strrstr (str, arg) != NULL) { + va_end (ap); + return TRUE; + } + } + + va_end (ap); + + return FALSE; +} + +/* Authentication attempt succeeded. Update the GUI accordingly. */ +static void +authenticated_user (PasswordDialog *pdialog) +{ + pdialog->backend_state = PASSWD_STATE_NEW; + + if (pdialog->authenticated) { + /* This is a re-authentication + * It succeeded, so pop our new password from the queue */ + io_queue_pop (pdialog->backend_stdin_queue, pdialog->backend_stdin); + } + + /* Update UI state */ + passdlg_set_auth_state (pdialog, TRUE); + passdlg_set_status (pdialog, _("Authenticated!")); + + /* Check to see if the passwords are valid + * (They might be non-empty if the user had to re-authenticate, + * and thus we need to enable the change-password-button) */ + passdlg_refresh_password_state (pdialog); +} + +/* + * IO watcher for stdout, called whenever there is data to read from the backend. + * This is where most of the actual IO handling happens. + */ +static gboolean +io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswordDialog *pdialog) +{ + static GString *str = NULL; /* Persistent buffer */ + + gchar buf[BUFSIZE]; /* Temporary buffer */ + gsize bytes_read; + GError *error = NULL; + + gchar *msg = NULL; /* Status error message */ + GtkBuilder *dialog; + + gboolean reinit = FALSE; + + /* Initialize buffer */ + if (str == NULL) { + str = g_string_new (""); + } + + dialog = pdialog->ui; + + if (g_io_channel_read_chars (source, buf, BUFSIZE, &bytes_read, &error) != G_IO_STATUS_NORMAL) { + g_warning ("IO Channel read error: %s", error->message); + g_error_free (error); + + return TRUE; + } + + str = g_string_append_len (str, buf, bytes_read); + + /* In which state is the backend? */ + switch (pdialog->backend_state) { + case PASSWD_STATE_AUTH: + /* Passwd is asking for our current password */ + + if (is_string_complete (str->str, "assword: ", "failure", "wrong", "error", NULL)) { + /* Which response did we get? */ + passdlg_set_busy (pdialog, FALSE); + + if (g_strrstr (str->str, "assword: ") != NULL) { + /* Authentication successful */ + + authenticated_user (pdialog); + + } else { + /* Authentication failed */ + + if (pdialog->authenticated) { + /* This is a re-auth, and it failed. + * The password must have been changed in the meantime! + * Ask the user to re-authenticate + */ + + /* Update status message and auth state */ + passdlg_set_status (pdialog, _("Your password has been changed since you initially authenticated! Please re-authenticate.")); + } else { + passdlg_set_status (pdialog, _("That password was incorrect.")); + } + + /* Focus current password */ + gtk_widget_grab_focus (GTK_WIDGET (pdialog->current_password)); + } + + reinit = TRUE; + } + break; + case PASSWD_STATE_NEW: + /* Passwd is asking for our new password */ + + if (is_string_complete (str->str, "assword: ", NULL)) { + /* Advance to next state */ + pdialog->backend_state = PASSWD_STATE_RETYPE; + + /* Pop retyped password from queue and into IO channel */ + io_queue_pop (pdialog->backend_stdin_queue, pdialog->backend_stdin); + + reinit = TRUE; + } + break; + case PASSWD_STATE_RETYPE: + /* Passwd is asking for our retyped new password */ + + if (is_string_complete (str->str, "successfully", + "short", + "longer", + "palindrome", + "dictionary", + "simpl", /* catches both simple and simplistic */ + "similar", + "different", + "case", + "wrapped", + "recovered", + "recent" + "unchanged", + "match", + "1 numeric or special", + "failure", + NULL)) { + + /* What response did we get? */ + passdlg_set_busy (pdialog, FALSE); + + if (g_strrstr (str->str, "successfully") != NULL) { + /* Hooray! */ + + passdlg_clear (pdialog); + passdlg_set_status (pdialog, _("Your password has been changed.")); + } else { + /* Ohnoes! */ + + /* Focus new password */ + gtk_widget_grab_focus (GTK_WIDGET (pdialog->new_password)); + + if (g_strrstr (str->str, "recovered") != NULL) { + /* What does this indicate? + * "Authentication information cannot be recovered?" from libpam? */ + msg = g_strdup_printf (_("System error: %s."), str->str); + } else if (g_strrstr (str->str, "short") != NULL || + g_strrstr (str->str, "longer") != NULL) { + msg = g_strdup (_("The password is too short.")); + } else if (g_strrstr (str->str, "palindrome") != NULL || + g_strrstr (str->str, "simpl") != NULL || + g_strrstr (str->str, "dictionary") != NULL) { + msg = g_strdup (_("The password is too simple.")); + } else if (g_strrstr (str->str, "similar") != NULL || + g_strrstr (str->str, "different") != NULL || + g_strrstr (str->str, "case") != NULL || + g_strrstr (str->str, "wrapped") != NULL) { + msg = g_strdup (_("The old and new passwords are too similar.")); + } else if (g_strrstr (str->str, "1 numeric or special") != NULL) { + msg = g_strdup (_("The new password must contain numeric or special character(s).")); + } else if (g_strrstr (str->str, "unchanged") != NULL || + g_strrstr (str->str, "match") != NULL) { + msg = g_strdup (_("The old and new passwords are the same.")); + } else if (g_strrstr (str->str, "recent") != NULL) { + msg = g_strdup (_("The new password has already been used recently.")); + } else if (g_strrstr (str->str, "failure") != NULL) { + /* Authentication failure */ + msg = g_strdup (_("Your password has been changed since you initially authenticated! Please re-authenticate.")); + + passdlg_set_auth_state (pdialog, FALSE); + } + } + + reinit = TRUE; + + if (msg != NULL) { + /* An error occured! */ + passdlg_set_status (pdialog, msg); + g_free (msg); + + /* At this point, passwd might have exited, in which case + * child_watch_cb should clean up for us and remove this watcher. + * On some error conditions though, passwd just re-prompts us + * for our new password. */ + + pdialog->backend_state = PASSWD_STATE_ERR; + } + + /* child_watch_cb should clean up for us now */ + } + break; + case PASSWD_STATE_NONE: + /* Passwd is not asking for anything yet */ + if (is_string_complete (str->str, "assword: ", NULL)) { + + /* If the user does not have a password set, + * passwd will immediately ask for the new password, + * so skip the AUTH phase */ + if (is_string_complete (str->str, "new", "New", NULL)) { + gchar *pw; + + pdialog->backend_state = PASSWD_STATE_NEW; + + passdlg_set_busy (pdialog, FALSE); + authenticated_user (pdialog); + + /* since passwd didn't ask for our old password + * in this case, simply remove it from the queue */ + pw = g_queue_pop_head (pdialog->backend_stdin_queue); + g_free (pw); + } else { + + pdialog->backend_state = PASSWD_STATE_AUTH; + + /* Pop the IO queue, i.e. send current password */ + io_queue_pop (pdialog->backend_stdin_queue, pdialog->backend_stdin); + } + + reinit = TRUE; + } + break; + default: + /* Passwd has returned an error */ + reinit = TRUE; + break; + } + + if (reinit) { + g_string_free (str, TRUE); + str = NULL; + } + + /* Continue calling us */ + return TRUE; +} + +/* + * }} Backend communication code + */ + +/* Adds the current password to the IO queue */ +static void +authenticate (PasswordDialog *pdialog) +{ + gchar *s; + + s = g_strdup_printf ("%s\n", gtk_entry_get_text (pdialog->current_password)); + + g_queue_push_tail (pdialog->backend_stdin_queue, s); +} + +/* Adds the new password twice to the IO queue */ +static void +update_password (PasswordDialog *pdialog) +{ + gchar *s; + + s = g_strdup_printf ("%s\n", gtk_entry_get_text (pdialog->new_password)); + + g_queue_push_tail (pdialog->backend_stdin_queue, s); + /* We need to allocate new space because io_queue_pop() g_free()s + * every element of the queue after it's done */ + g_queue_push_tail (pdialog->backend_stdin_queue, g_strdup (s)); +} + +/* Sets dialog busy state according to busy + * + * When busy: + * Sets the cursor to busy + * Disables the interface to prevent that the user interferes + * Reverts all this when non-busy + * + * Note that this function takes into account the + * authentication state of the dialog. So setting the + * dialog to busy and then back to normal should leave + * the dialog unchanged. + */ +static void +passdlg_set_busy (PasswordDialog *pdialog, gboolean busy) +{ + GtkBuilder *dialog; + GtkWidget *toplevel; + GdkCursor *cursor = NULL; + GdkDisplay *display; + + dialog = pdialog->ui; + + /* Set cursor */ + toplevel = WID ("change-password"); + display = gtk_widget_get_display (toplevel); + if (busy) { + cursor = gdk_cursor_new_for_display (display, GDK_WATCH); + } + + gdk_window_set_cursor (gtk_widget_get_window (toplevel), cursor); + gdk_display_flush (display); + + if (busy) { + gdk_cursor_unref (cursor); + } + + /* Disable/Enable UI */ + if (pdialog->authenticated) { + /* Authenticated state */ + + /* Enable/disable new password section */ + g_object_set (pdialog->new_password, "sensitive", !busy, NULL); + g_object_set (pdialog->retyped_password, "sensitive", !busy, NULL); + g_object_set (WID ("new-password-label"), "sensitive", !busy, NULL); + g_object_set (WID ("retyped-password-label"), "sensitive", !busy, NULL); + + /* Enable/disable change password button */ + g_object_set (WID ("change-password-button"), "sensitive", !busy, NULL); + + } else { + /* Not-authenticated state */ + + /* Enable/disable auth section state */ + g_object_set (pdialog->current_password, "sensitive", !busy, NULL); + g_object_set (WID ("authenticate-button"), "sensitive", !busy, NULL); + g_object_set (WID ("current-password-label"), "sensitive", !busy, NULL); + } +} + +/* Launch an error dialog */ +static void +passdlg_error_dialog (GtkWindow *parent, const gchar *title, + const gchar *msg, const gchar *details) +{ + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, + msg); + if (title) + gtk_window_set_title (GTK_WINDOW (dialog), title); + + if (details) + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + details); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +/* Set authenticated state of dialog according to state + * + * When in authenticated state: + * Disables authentication-part of interface + * Enables new-password-part of interface + * When in not-authenticated state: + * Enables authentication-part of interface + * Disables new-password-part of interface + * Disables the change-password-button + */ +static void +passdlg_set_auth_state (PasswordDialog *pdialog, gboolean state) +{ + GtkBuilder *dialog; + + dialog = pdialog->ui; + + /* Widgets which require a not-authenticated state to be accessible */ + g_object_set (pdialog->current_password, "sensitive", !state, NULL); + g_object_set (WID ("current-password-label"), "sensitive", !state, NULL); + g_object_set (WID ("authenticate-button"), "sensitive", !state, NULL); + + /* Widgets which require authentication to be accessible */ + g_object_set (pdialog->new_password, "sensitive", state, NULL); + g_object_set (pdialog->retyped_password, "sensitive", state, NULL); + g_object_set (WID ("new-password-label"), "sensitive", state, NULL); + g_object_set (WID ("retyped-password-label"), "sensitive", state, NULL); + + if (!state) { + /* Disable change-password-button when in not-authenticated state */ + g_object_set (WID ("change-password-button"), "sensitive", FALSE, NULL); + } + + pdialog->authenticated = state; + + if (state) { + /* Authenticated state */ + + /* Focus new password */ + gtk_widget_grab_focus (GTK_WIDGET (pdialog->new_password)); + + /* Set open lock image */ + gtk_image_set_from_icon_name (GTK_IMAGE (pdialog->dialog_image), "changes-allow", GTK_ICON_SIZE_DIALOG); + } else { + /* Not authenticated state */ + + /* Focus current password */ + gtk_widget_grab_focus (GTK_WIDGET (pdialog->current_password)); + + /* Set closed lock image */ + gtk_image_set_from_icon_name (GTK_IMAGE (pdialog->dialog_image), "changes-prevent", GTK_ICON_SIZE_DIALOG); + } +} + +/* Set status field message */ +static void +passdlg_set_status (PasswordDialog *pdialog, gchar *msg) +{ + g_object_set (pdialog->status_label, "label", msg, NULL); +} + +/* Clear dialog (except the status message) */ +static void +passdlg_clear (PasswordDialog *pdialog) +{ + /* Set non-authenticated state */ + passdlg_set_auth_state (pdialog, FALSE); + + /* Clear password entries */ + gtk_entry_set_text (pdialog->current_password, ""); + gtk_entry_set_text (pdialog->new_password, ""); + gtk_entry_set_text (pdialog->retyped_password, ""); +} + +/* Start backend and handle errors + * If backend is already running, stop it + * If an error occurs, show error dialog */ +static gboolean +passdlg_spawn_passwd (PasswordDialog *pdialog) +{ + GError *error = NULL; + gchar *details; + + /* If backend is running, stop it */ + stop_passwd (pdialog); + + /* Spawn backend */ + if (!spawn_passwd (pdialog, &error)) { + GtkWidget *parent = GTK_WIDGET (gtk_builder_get_object (pdialog->ui, "change-password")); + + /* translators: Unable to launch : */ + details = g_strdup_printf (_("Unable to launch %s: %s"), + "/usr/bin/passwd", error->message); + + passdlg_error_dialog (GTK_WINDOW (parent), + _("Unable to launch backend"), + _("A system error has occurred"), + details); + + g_free (details); + g_error_free (error); + + return FALSE; + } + + return TRUE; +} + +/* Called when the "Authenticate" button is clicked */ +static void +passdlg_authenticate (GtkButton *button, PasswordDialog *pdialog) +{ + /* Set busy as this can be a long process */ + passdlg_set_busy (pdialog, TRUE); + + /* Update status message */ + passdlg_set_status (pdialog, _("Checking password...")); + + /* Spawn backend */ + if (!passdlg_spawn_passwd (pdialog)) { + passdlg_set_busy (pdialog, FALSE); + return; + } + + authenticate (pdialog); + + /* Our IO watcher should now handle the rest */ +} + +/* Validate passwords + * Returns: + * PASSDLG_ERROR_NONE (0) if passwords are valid + * PASSDLG_ERROR_NEW_PASSWORD_EMPTY + * PASSDLG_ERROR_RETYPED_PASSWORD_EMPTY + * PASSDLG_ERROR_PASSWORDS_NOT_EQUAL + */ +static guint +passdlg_validate_passwords (PasswordDialog *pdialog) +{ + GtkBuilder *dialog; + const gchar *new_password, *retyped_password; + glong nlen, rlen; + + dialog = pdialog->ui; + + new_password = gtk_entry_get_text (pdialog->new_password); + retyped_password = gtk_entry_get_text (pdialog->retyped_password); + + nlen = g_utf8_strlen (new_password, -1); + rlen = g_utf8_strlen (retyped_password, -1); + + if (nlen == 0) { + /* New password empty */ + return PASSDLG_ERROR_NEW_PASSWORD_EMPTY; + } + + if (rlen == 0) { + /* Retyped password empty */ + return PASSDLG_ERROR_RETYPED_PASSWORD_EMPTY; + } + + if (nlen != rlen || strncmp (new_password, retyped_password, nlen) != 0) { + /* Passwords not equal */ + return PASSDLG_ERROR_PASSWORDS_NOT_EQUAL; + } + + /* Success */ + return PASSDLG_ERROR_NONE; +} + +/* Refresh the valid password UI state, i.e. re-validate + * and enable/disable the Change Password button. + * Returns: Return value of passdlg_validate_passwords */ +static guint +passdlg_refresh_password_state (PasswordDialog *pdialog) +{ + GtkBuilder *dialog; + guint ret; + gboolean valid = FALSE; + + dialog = pdialog->ui; + + ret = passdlg_validate_passwords (pdialog); + + if (ret == PASSDLG_ERROR_NONE) { + valid = TRUE; + } + + g_object_set (WID ("change-password-button"), "sensitive", valid, NULL); + + return ret; +} + +/* Called whenever any of the new password fields have changed */ +static void +passdlg_check_password (GtkEntry *entry, PasswordDialog *pdialog) +{ + guint ret; + + ret = passdlg_refresh_password_state (pdialog); + + switch (ret) { + case PASSDLG_ERROR_NONE: + passdlg_set_status (pdialog, _("Click Change password to change your password.")); + break; + case PASSDLG_ERROR_NEW_PASSWORD_EMPTY: + passdlg_set_status (pdialog, _("Please type your password in the New password field.")); + break; + case PASSDLG_ERROR_RETYPED_PASSWORD_EMPTY: + passdlg_set_status (pdialog, _("Please type your password again in the Retype new password field.")); + break; + case PASSDLG_ERROR_PASSWORDS_NOT_EQUAL: + passdlg_set_status (pdialog, _("The two passwords are not equal.")); + break; + default: + g_warning ("Unknown passdlg_check_password error: %d", ret); + break; + } +} + +/* Called when the "Change password" dialog-button is clicked + * Returns: TRUE if we want to keep the dialog running, FALSE otherwise */ +static gboolean +passdlg_process_response (PasswordDialog *pdialog, gint response_id) +{ + + if (response_id == GTK_RESPONSE_OK) { + /* Set busy as this can be a long process */ + passdlg_set_busy (pdialog, TRUE); + + /* Stop passwd if an error occured and it is still running */ + if (pdialog->backend_state == PASSWD_STATE_ERR) { + + /* Stop passwd, free resources */ + stop_passwd (pdialog); + } + + /* Check that the backend is still running, or that an error + * hass occured but it has not yet exited */ + if (pdialog->backend_pid == -1) { + /* If it is not, re-run authentication */ + + /* Spawn backend */ + if (!passdlg_spawn_passwd (pdialog)) { + return TRUE; + } + + /* Add current and new passwords to queue */ + authenticate (pdialog); + update_password (pdialog); + } else { + /* Only add new passwords to queue */ + update_password (pdialog); + + /* Pop new password through the backend */ + io_queue_pop (pdialog->backend_stdin_queue, pdialog->backend_stdin); + } + + /* Our IO watcher should now handle the rest */ + + /* Keep the dialog running */ + return TRUE; + } + + return FALSE; +} + +/* Activates (moves focus or activates) widget w */ +static void +passdlg_activate (GtkEntry *entry, GtkWidget *w) +{ + if (GTK_IS_BUTTON (w)) { + gtk_widget_activate (w); + } else { + gtk_widget_grab_focus (w); + } +} + +/* Initialize password dialog */ +static void +passdlg_init (PasswordDialog *pdialog, GtkWindow *parent) +{ + GtkBuilder *dialog; + GtkWidget *wpassdlg; + GtkAccelGroup *group; + + /* Initialize dialog */ + dialog = gtk_builder_new (); + gtk_builder_add_from_file (dialog, MATECC_UI_DIR "/mate-about-me-password.ui", NULL); + pdialog->ui = dialog; + + wpassdlg = WID ("change-password"); + capplet_set_icon (wpassdlg, "user-info"); + + group = gtk_accel_group_new (); + + /* + * Initialize backend + */ + + /* Initialize backend_pid. -1 means the backend is not running */ + pdialog->backend_pid = -1; + + /* Initialize IO Channels */ + pdialog->backend_stdin = NULL; + pdialog->backend_stdout = NULL; + + /* Initialize write queue */ + pdialog->backend_stdin_queue = g_queue_new (); + + /* Initialize watchers */ + pdialog->backend_child_watch_id = 0; + pdialog->backend_stdout_watch_id = 0; + + /* Initialize backend state */ + pdialog->backend_state = PASSWD_STATE_NONE; + + /* + * Initialize UI + */ + + /* Initialize pdialog widgets */ + pdialog->current_password = GTK_ENTRY (WID ("current-password")); + pdialog->new_password = GTK_ENTRY (WID ("new-password")); + pdialog->retyped_password = GTK_ENTRY (WID ("retyped-password")); + pdialog->dialog_image = GTK_IMAGE (WID ("dialog-image")); + pdialog->status_label = GTK_LABEL (WID ("status-label")); + + /* Initialize accelerators */ + gtk_widget_add_accelerator (GTK_WIDGET (pdialog->current_password), + "activate", group, + GDK_Return, 0, + 0); + + gtk_widget_add_accelerator (GTK_WIDGET (pdialog->new_password), + "activate", group, + GDK_Return, 0, + 0); + + /* Activate authenticate-button when enter is pressed in current-password */ + g_signal_connect (G_OBJECT (pdialog->current_password), "activate", + G_CALLBACK (passdlg_activate), WID ("authenticate-button")); + + /* Activate retyped-password when enter is pressed in new-password */ + g_signal_connect (G_OBJECT (pdialog->new_password), "activate", + G_CALLBACK (passdlg_activate), pdialog->retyped_password); + + /* Clear status message */ + passdlg_set_status (pdialog, ""); + + /* Set non-authenticated state */ + passdlg_set_auth_state (pdialog, FALSE); + + /* Connect signal handlers */ + g_signal_connect (G_OBJECT (WID ("authenticate-button")), "clicked", + G_CALLBACK (passdlg_authenticate), pdialog); + + /* Verify new passwords on-the-fly */ + g_signal_connect (G_OBJECT (WID ("new-password")), "changed", + G_CALLBACK (passdlg_check_password), pdialog); + g_signal_connect (G_OBJECT (WID ("retyped-password")), "changed", + G_CALLBACK (passdlg_check_password), pdialog); + + /* Set misc dialog properties */ + gtk_window_set_resizable (GTK_WINDOW (wpassdlg), FALSE); + gtk_window_set_transient_for (GTK_WINDOW (wpassdlg), GTK_WINDOW (parent)); +} + +/* Main */ +void +mate_about_me_password (GtkWindow *parent) +{ + PasswordDialog *pdialog; + GtkBuilder *dialog; + GtkWidget *wpassdlg; + + gint result; + gboolean response; + + /* Initialize dialog */ + pdialog = g_new0 (PasswordDialog, 1); + passdlg_init (pdialog, parent); + + dialog = pdialog->ui; + wpassdlg = WID ("change-password"); + + /* Go! */ + gtk_widget_show_all (wpassdlg); + + do { + result = gtk_dialog_run (GTK_DIALOG (wpassdlg)); + response = passdlg_process_response (pdialog, result); + } while (response); + + /* Clean up */ + stop_passwd (pdialog); + gtk_widget_destroy (wpassdlg); + g_queue_free (pdialog->backend_stdin_queue); + g_object_unref (dialog); + g_free (pdialog); +} diff --git a/capplets/about-me/mate-about-me-password.h b/capplets/about-me/mate-about-me-password.h new file mode 100644 index 00000000..39f67cd8 --- /dev/null +++ b/capplets/about-me/mate-about-me-password.h @@ -0,0 +1,9 @@ +#ifndef __MATE_ABOUT_ME_PASSWORD_H__ +#define __MATE_ABOUT_ME_PASSWORD_H__ + +#include + +void +mate_about_me_password (GtkWindow *parent); + +#endif /* __MATE_ABOUT_ME_PASSWORD_H__ */ diff --git a/capplets/about-me/mate-about-me-password.ui b/capplets/about-me/mate-about-me-password.ui new file mode 100644 index 00000000..b9c433a5 --- /dev/null +++ b/capplets/about-me/mate-about-me-password.ui @@ -0,0 +1,319 @@ + + + + + + 6 + Change password + False + dialog + False + + + True + vertical + 6 + + + True + 6 + 12 + + + True + 0 + gtk-dialog-authentication + 6 + + + False + False + 0 + + + + + True + vertical + 6 + + + True + 0 + Change your password + + + + + + + False + False + 0 + + + + + True + 0 + 0 + To change your password, enter your current password in the field below and click <b>Authenticate</b>. +After you have authenticated, enter your new password, retype it for verification and click <b>Change password</b>. + True + True + + + False + False + 1 + + + + + True + 6 + + + True + vertical + 6 + True + + + True + 0 + Current _password: + True + current-password + + + False + False + 0 + + + + + True + False + 0 + _New password: + True + new-password + + + False + False + 1 + + + + + True + False + 0 + _Retype new password: + True + retyped-password + + + False + False + 2 + + + + + False + False + 0 + + + + + True + vertical + 6 + True + + + True + 6 + + + True + True + False + + + 0 + + + + + True + True + False + + + True + 0 + 0 + + + True + 2 + + + True + gtk-apply + + + False + False + 0 + + + + + True + _Authenticate + True + + + False + False + 1 + + + + + + + + + False + False + 1 + + + + + 0 + + + + + True + False + True + False + + + False + False + 1 + + + + + True + False + True + False + True + + + False + False + 2 + + + + + 1 + + + + + False + False + 2 + + + + + True + Please type your password again in the <b>Retype new password</b> field. + True + center + True + + + 3 + + + + + 1 + + + + + False + False + 1 + + + + + True + end + + + gtk-close + True + True + True + False + True + + + False + False + 0 + + + + + Change pa_ssword + True + False + True + True + True + False + True + + + False + False + 1 + + + + + False + False + end + 0 + + + + + + cancel-button + change-password-button + + + diff --git a/capplets/about-me/mate-about-me.c b/capplets/about-me/mate-about-me.c new file mode 100644 index 00000000..0935ab51 --- /dev/null +++ b/capplets/about-me/mate-about-me.c @@ -0,0 +1,1005 @@ +/* mate-about-me.c + * Copyright (C) 2002 Diego Gonzalez + * + * Written by: Diego Gonzalez + * + * 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, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +#define MATE_DESKTOP_USE_UNSTABLE_API +#include + +#include "e-image-chooser.h" +#include "mate-about-me-password.h" +#include "mate-about-me-fingerprint.h" +#include "marshal.h" + +#include "capplet-util.h" + +#define MAX_HEIGHT 150 +#define MAX_WIDTH 150 + +#define EMAIL_SLOTS 4 + +typedef struct { + EContact *contact; + EBook *book; + + GtkBuilder *dialog; + GtkWidget *enable_fingerprint_button; + GtkWidget *disable_fingerprint_button; + GtkWidget *image_chooser; + + GdkScreen *screen; + GtkIconTheme *theme; + MateDesktopThumbnailFactory *thumbs; + + EContactAddress *addr1; + EContactAddress *addr2; + gchar *email[EMAIL_SLOTS]; + const gchar *email_types[EMAIL_SLOTS]; + + gboolean have_image; + gboolean image_changed; + gboolean create_self; + + gchar *person; + gchar *login; + gchar *username; + + guint commit_timeout_id; +} MateAboutMe; + +static MateAboutMe *me = NULL; + +struct WidToCid { + gchar *wid; + guint cid; +}; + +enum { + ADDRESS_STREET = 1, + ADDRESS_POBOX, + ADDRESS_LOCALITY, + ADDRESS_CODE, + ADDRESS_REGION, + ADDRESS_COUNTRY +}; + +#define EMAIL_WORK 0 +#define EMAIL_HOME 1 +#define ADDRESS_HOME 21 +#define ADDRESS_WORK 27 + +struct WidToCid ids[] = { + + { "email-work-e", 0 }, /* 00 */ + { "email-home-e", 1 }, /* 01 */ + + { "phone-home-e", E_CONTACT_PHONE_HOME }, /* 02 */ + { "phone-mobile-e", E_CONTACT_PHONE_MOBILE }, /* 03 */ + { "phone-work-e", E_CONTACT_PHONE_BUSINESS }, /* 04 */ + { "phone-work-fax-e", E_CONTACT_PHONE_BUSINESS_FAX }, /* 05 */ + + { "im-jabber-e", E_CONTACT_IM_JABBER_HOME_1 }, /* 06 */ + { "im-msn-e", E_CONTACT_IM_MSN_HOME_1 }, /* 07 */ + { "im-icq-e", E_CONTACT_IM_ICQ_HOME_1 }, /* 08 */ + { "im-yahoo-e", E_CONTACT_IM_YAHOO_HOME_1 }, /* 09 */ + { "im-aim-e", E_CONTACT_IM_AIM_HOME_1 }, /* 10 */ + { "im-groupwise-e", E_CONTACT_IM_GROUPWISE_HOME_1 }, /* 11 */ + + { "web-homepage-e", E_CONTACT_HOMEPAGE_URL }, /* 12 */ + { "web-calendar-e", E_CONTACT_CALENDAR_URI }, /* 13 */ + { "web-weblog-e", E_CONTACT_BLOG_URL }, /* 14 */ + + { "job-profession-e", E_CONTACT_ROLE }, /* 15 */ + { "job-title-e", E_CONTACT_TITLE }, /* 16 */ + { "job-dept-e", E_CONTACT_ORG_UNIT }, /* 17 */ + { "job-assistant-e", E_CONTACT_ASSISTANT }, /* 18 */ + { "job-company-e", E_CONTACT_ORG }, /* 19 */ + { "job-manager-e", E_CONTACT_MANAGER }, /* 20 */ + + { "addr-street-1", ADDRESS_STREET }, /* 21 */ + { "addr-po-1", ADDRESS_POBOX }, /* 22 */ + { "addr-locality-1", ADDRESS_LOCALITY }, /* 23 */ + { "addr-code-1", ADDRESS_CODE }, /* 24 */ + { "addr-region-1", ADDRESS_REGION }, /* 25 */ + { "addr-country-1", ADDRESS_COUNTRY }, /* 26 */ + + { "addr-street-2", ADDRESS_STREET }, /* 27 */ + { "addr-po-2", ADDRESS_POBOX }, /* 28 */ + { "addr-locality-2", ADDRESS_LOCALITY }, /* 29 */ + { "addr-code-2", ADDRESS_CODE }, /* 30 */ + { "addr-region-2", ADDRESS_REGION }, /* 31 */ + { "addr-country-2", ADDRESS_COUNTRY }, /* 32 */ + + { NULL, 0 } +}; + +#define ATTRIBUTE_HOME "HOME" +#define ATTRIBUTE_WORK "WORK" +#define ATTRIBUTE_OTHER "OTHER" + +static void about_me_set_address_field (EContactAddress *, guint, gchar *); + + +/*** Utility functions ***/ +static void +about_me_error (GtkWindow *parent, gchar *str) +{ + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, str); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +/********************/ +static void +about_me_destroy (void) +{ + e_contact_address_free (me->addr1); + e_contact_address_free (me->addr2); + + if (me->contact) + g_object_unref (me->contact); + if (me->book) + g_object_unref (me->book); + if (me->dialog) + g_object_unref (me->dialog); + + g_free (me->email[0]); + g_free (me->email[1]); + g_free (me->email[2]); + g_free (me->email[3]); + + g_free (me->person); + g_free (me->login); + g_free (me->username); + g_free (me); + me = NULL; +} + +static void +about_me_update_email (MateAboutMe *me) +{ + GList *attrs = NULL; + gint i; + + for (i = 0; i < EMAIL_SLOTS; ++i) { + if (me->email[i] != NULL) { + EVCardAttribute *attr; + const gchar *type = me->email_types[i]; + + attr = e_vcard_attribute_new (NULL, EVC_EMAIL); + + e_vcard_attribute_add_param_with_value (attr, + e_vcard_attribute_param_new (EVC_TYPE), + type ? type : ATTRIBUTE_OTHER); + + e_vcard_attribute_add_value (attr, me->email[i]); + attrs = g_list_append (attrs, attr); + } + } + + e_contact_set_attributes (me->contact, E_CONTACT_EMAIL, attrs); + + g_list_foreach (attrs, (GFunc) e_vcard_attribute_free, NULL); + g_list_free (attrs); +} + +static void +about_me_commit (MateAboutMe *me) +{ + EContactName *name; + + char *strings[4], **stringptr; + char *fileas; + + name = NULL; + + if (me->create_self) { + if (me->username == NULL) + fileas = g_strdup ("Myself"); + else { + name = e_contact_name_from_string (me->username); + + stringptr = strings; + if (name->family && *name->family) + *(stringptr++) = name->family; + if (name->given && *name->given) + *(stringptr++) = name->given; + *stringptr = NULL; + fileas = g_strjoinv (", ", strings); + } + + e_contact_set (me->contact, E_CONTACT_FILE_AS, fileas); + e_contact_set (me->contact, E_CONTACT_NICKNAME, me->login); + e_contact_set (me->contact, E_CONTACT_FULL_NAME, me->username); + + e_contact_name_free (name); + g_free (fileas); + } + + about_me_update_email (me); + + if (me->create_self) { + e_book_add_contact (me->book, me->contact, NULL); + e_book_set_self (me->book, me->contact, NULL); + } else { + if (!e_book_commit_contact (me->book, me->contact, NULL)) + g_warning ("Could not save contact information"); + } + + me->create_self = FALSE; +} + +static gboolean +about_me_commit_from_timeout (MateAboutMe *me) +{ + about_me_commit (me); + + return FALSE; +} + +static gboolean +about_me_focus_out (GtkWidget *widget, GdkEventFocus *event, MateAboutMe *unused) +{ + gchar *str; + const gchar *wid; + gint i; + + if (me == NULL) + return FALSE; + + wid = gtk_buildable_get_name (GTK_BUILDABLE (widget)); + + if (wid == NULL) + return FALSE; + + for (i = 0; ids[i].wid != NULL; i++) + if (strcmp (ids[i].wid, wid) == 0) + break; + + if (GTK_IS_ENTRY (widget)) { + str = gtk_editable_get_chars (GTK_EDITABLE (widget), 0, -1); + } else if (GTK_IS_TEXT_VIEW (widget)) { + GtkTextBuffer *buffer; + GtkTextIter iter_start; + GtkTextIter iter_end; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget)); + gtk_text_buffer_get_start_iter (buffer, &iter_start); + iter_end = iter_start; + gtk_text_iter_forward_to_end (&iter_end); + str = gtk_text_iter_get_text (&iter_start, &iter_end); + } else { + return FALSE; + } + + if (i == EMAIL_HOME || i == EMAIL_WORK) { + + g_free (me->email[ids[i].cid]); + if (str[0] == '\0') + me->email[ids[i].cid] = NULL; + else + me->email[ids[i].cid] = g_strdup (str); + me->email_types[ids[i].cid] = (i == EMAIL_HOME) ? ATTRIBUTE_HOME : ATTRIBUTE_WORK; + /* FIXME: i'm getting an empty address field in evolution */ + } else if (i >= ADDRESS_HOME && i < ADDRESS_WORK) { + about_me_set_address_field (me->addr1, ids[i].cid, str); + e_contact_set (me->contact, E_CONTACT_ADDRESS_HOME, me->addr1); + } else if (i >= ADDRESS_WORK) { + about_me_set_address_field (me->addr2, ids[i].cid, str); + e_contact_set (me->contact, E_CONTACT_ADDRESS_WORK, me->addr2); + } else { + e_contact_set (me->contact, ids[i].cid, str); + } + + g_free (str); + + if (me->commit_timeout_id) { + g_source_remove (me->commit_timeout_id); + } + + me->commit_timeout_id = g_timeout_add (600, (GSourceFunc) about_me_commit_from_timeout, me); + + return FALSE; +} + +/* + * Helpers + */ + +static gchar * +about_me_get_address_field (EContactAddress *addr, guint cid) +{ + gchar *str; + + if (addr == NULL) { + return NULL; + } + + switch (cid) { + case ADDRESS_STREET: + str = addr->street; + break; + case ADDRESS_POBOX: + str = addr->po; + break; + case ADDRESS_LOCALITY: + str = addr->locality; + break; + case ADDRESS_CODE: + str = addr->code; + break; + case ADDRESS_REGION: + str = addr->region; + break; + case ADDRESS_COUNTRY: + str = addr->country; + break; + default: + str = NULL; + break; + } + + return str; +} + +static void +about_me_set_address_field (EContactAddress *addr, guint cid, gchar *str) +{ + switch (cid) { + case ADDRESS_STREET: + g_free (addr->street); + addr->street = g_strdup (str); + break; + case ADDRESS_POBOX: + g_free (addr->po); + addr->po = g_strdup (str); + break; + case ADDRESS_LOCALITY: + g_free (addr->locality); + addr->locality = g_strdup (str); + break; + case ADDRESS_CODE: + g_free (addr->code); + addr->code = g_strdup (str); + break; + case ADDRESS_REGION: + g_free (addr->region); + addr->region = g_strdup (str); + break; + case ADDRESS_COUNTRY: + g_free (addr->country); + addr->country = g_strdup (str); + break; + } +} + +static void +about_me_setup_email (MateAboutMe *me) +{ + GList *attrs, *la; + gboolean has_home = FALSE, has_work = FALSE; + guint i; + + attrs = e_contact_get_attributes (me->contact, E_CONTACT_EMAIL); + + for (la = attrs, i = 0; la; la = la->next, ++i) { + EVCardAttribute *a = la->data; + + me->email[i] = e_vcard_attribute_get_value (a); + if (e_vcard_attribute_has_type (a, ATTRIBUTE_HOME)) { + me->email_types[i] = ATTRIBUTE_HOME; + if (!has_home) { + ids[EMAIL_HOME].cid = i; + has_home = TRUE; + } + } else if (e_vcard_attribute_has_type (a, ATTRIBUTE_WORK)) { + me->email_types[i] = ATTRIBUTE_WORK; + if (!has_work) { + ids[EMAIL_WORK].cid = i; + has_work = TRUE; + } + } else { + me->email_types[i] = ATTRIBUTE_OTHER; + } + + e_vcard_attribute_free (a); + } + + g_list_free (attrs); + + if (ids[EMAIL_HOME].cid == ids[EMAIL_WORK].cid) { + if (has_home) + ids[EMAIL_WORK].cid = 1; + else + ids[EMAIL_HOME].cid = 0; + } + + me->email_types[ids[EMAIL_WORK].cid] = ATTRIBUTE_WORK; + me->email_types[ids[EMAIL_HOME].cid] = ATTRIBUTE_HOME; +} + +/** + * about_me_load_string_field: + * + * wid: UI widget name + * cid: id of the field (EDS id) + * aid: position in the array WidToCid + **/ + +static void +about_me_load_string_field (MateAboutMe *me, const gchar *wid, guint cid, guint aid) +{ + GtkWidget *widget; + GtkBuilder *dialog; + const gchar *str; + + dialog = me->dialog; + + widget = WID (wid); + + if (me->create_self == TRUE) { + g_signal_connect (widget, "focus-out-event", G_CALLBACK (about_me_focus_out), me); + return; + } + + if (aid == EMAIL_HOME || aid == EMAIL_WORK) { + str = e_contact_get_const (me->contact, E_CONTACT_EMAIL_1 + cid); + } else if (aid >= ADDRESS_HOME && aid < ADDRESS_WORK) { + str = about_me_get_address_field (me->addr1, cid); + } else if (aid >= ADDRESS_WORK) { + str = about_me_get_address_field (me->addr2, cid); + } else { + str = e_contact_get_const (me->contact, cid); + } + + str = str ? str : ""; + + if (GTK_IS_ENTRY (widget)) { + gtk_entry_set_text (GTK_ENTRY (widget), str); + } else if (GTK_IS_TEXT_VIEW (widget)) { + GtkTextBuffer *buffer; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget)); + gtk_text_buffer_set_text (buffer, str, -1); + } + + g_signal_connect (widget, "focus-out-event", G_CALLBACK (about_me_focus_out), me); +} + +static void +about_me_load_photo (MateAboutMe *me, EContact *contact) +{ + GtkBuilder *dialog; + EContactPhoto *photo; + + dialog = me->dialog; + + if (me->person) + e_image_chooser_set_from_file (E_IMAGE_CHOOSER (me->image_chooser), me->person); + + photo = e_contact_get (contact, E_CONTACT_PHOTO); + + if (photo && photo->type == E_CONTACT_PHOTO_TYPE_INLINED) { + me->have_image = TRUE; + e_image_chooser_set_image_data (E_IMAGE_CHOOSER (me->image_chooser), + (char *) photo->data.inlined.data, photo->data.inlined.length); + e_contact_photo_free (photo); + } else { + me->have_image = FALSE; + } +} + +static void +about_me_update_photo (MateAboutMe *me) +{ + GtkBuilder *dialog; + EContactPhoto *photo; + gchar *file; + GError *error; + + guchar *data; + gsize length; + + dialog = me->dialog; + + + if (me->image_changed && me->have_image) { + GdkPixbufLoader *loader = gdk_pixbuf_loader_new (); + GdkPixbuf *pixbuf, *scaled; + int height, width; + gboolean do_scale = FALSE; + float scale; + + e_image_chooser_get_image_data (E_IMAGE_CHOOSER (me->image_chooser), (char **) &data, &length); + + /* Before updating the image in EDS scale it to a reasonable size + so that the user doesn't get an application that does not respond + or that takes 100% CPU */ + gdk_pixbuf_loader_write (loader, data, length, NULL); + gdk_pixbuf_loader_close (loader, NULL); + + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + + if (pixbuf) + g_object_ref (pixbuf); + + g_object_unref (loader); + + height = gdk_pixbuf_get_height (pixbuf); + width = gdk_pixbuf_get_width (pixbuf); + + if (height >= width && height > MAX_HEIGHT) { + scale = (float)MAX_HEIGHT/height; + do_scale = TRUE; + } else if (width > height && width > MAX_WIDTH) { + scale = (float)MAX_WIDTH/width; + do_scale = TRUE; + } + + if (do_scale) { + char *scaled_data = NULL; + gsize scaled_length; + + scaled = gdk_pixbuf_scale_simple (pixbuf, width*scale, height*scale, GDK_INTERP_BILINEAR); + gdk_pixbuf_save_to_buffer (scaled, &scaled_data, &scaled_length, "png", NULL, + "compression", "9", NULL); + + g_free (data); + data = (guchar *) scaled_data; + length = scaled_length; + } + + photo = g_new0 (EContactPhoto, 1); + photo->type = E_CONTACT_PHOTO_TYPE_INLINED; + photo->data.inlined.data = data; + photo->data.inlined.length = length; + e_contact_set (me->contact, E_CONTACT_PHOTO, photo); + + /* Save the image for MDM */ + /* FIXME: I would have to read the default used by the mdmgreeter program */ + error = NULL; + file = g_build_filename (g_get_home_dir (), ".face", NULL); + if (g_file_set_contents (file, + (gchar *) photo->data.inlined.data, + photo->data.inlined.length, + &error) != FALSE) { + g_chmod (file, 0644); + } else { + g_warning ("Could not create %s: %s", file, error->message); + g_error_free (error); + } + + g_free (file); + + e_contact_photo_free (photo); + + } else if (me->image_changed && !me->have_image) { + /* Update the image in the card */ + e_contact_set (me->contact, E_CONTACT_PHOTO, NULL); + + file = g_build_filename (g_get_home_dir (), ".face", NULL); + + g_unlink (file); + + g_free (file); + } + + about_me_commit (me); +} + +static void +about_me_load_info (MateAboutMe *me) +{ + gint i; + + if (me->create_self == FALSE) { + me->addr1 = e_contact_get (me->contact, E_CONTACT_ADDRESS_HOME); + if (me->addr1 == NULL) + me->addr1 = g_new0 (EContactAddress, 1); + me->addr2 = e_contact_get (me->contact, E_CONTACT_ADDRESS_WORK); + if (me->addr2 == NULL) + me->addr2 = g_new0 (EContactAddress, 1); + } else { + me->addr1 = g_new0 (EContactAddress, 1); + me->addr2 = g_new0 (EContactAddress, 1); + } + + for (i = 0; ids[i].wid != NULL; i++) { + about_me_load_string_field (me, ids[i].wid, ids[i].cid, i); + } + + set_fingerprint_label (me->enable_fingerprint_button, + me->disable_fingerprint_button); +} + +static void +about_me_update_preview (GtkFileChooser *chooser, + MateAboutMe *me) +{ + gchar *uri; + + uri = gtk_file_chooser_get_preview_uri (chooser); + + if (uri) { + GtkWidget *image; + GdkPixbuf *pixbuf = NULL; + GFile *file; + GFileInfo *file_info; + + if (!me->thumbs) + me->thumbs = mate_desktop_thumbnail_factory_new (MATE_DESKTOP_THUMBNAIL_SIZE_NORMAL); + + file = g_file_new_for_uri (uri); + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + g_object_unref (file); + + if (file_info != NULL) { + const gchar *content_type; + + content_type = g_file_info_get_content_type (file_info); + if (content_type) { + gchar *mime_type; + + mime_type = g_content_type_get_mime_type (content_type); + + pixbuf = mate_desktop_thumbnail_factory_generate_thumbnail (me->thumbs, + uri, + mime_type); + g_free (mime_type); + } + g_object_unref (file_info); + } + + image = gtk_file_chooser_get_preview_widget (chooser); + + if (pixbuf != NULL) { + gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf); + g_object_unref (pixbuf); + } else { + gtk_image_set_from_stock (GTK_IMAGE (image), + "gtk-dialog-question", + GTK_ICON_SIZE_DIALOG); + } + } + gtk_file_chooser_set_preview_widget_active (chooser, TRUE); +} + +static void +about_me_image_clicked_cb (GtkWidget *button, MateAboutMe *me) +{ + GtkFileChooser *chooser_dialog; + gint response; + GtkBuilder *dialog; + GtkWidget *image; + const gchar *chooser_dir = DATADIR"/pixmaps/faces"; + const gchar *pics_dir; + GtkFileFilter *filter; + + dialog = me->dialog; + + chooser_dialog = GTK_FILE_CHOOSER ( + gtk_file_chooser_dialog_new (_("Select Image"), GTK_WINDOW (WID ("about-me-dialog")), + GTK_FILE_CHOOSER_ACTION_OPEN, + _("No Image"), GTK_RESPONSE_NO, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL)); + gtk_window_set_modal (GTK_WINDOW (chooser_dialog), TRUE); + gtk_dialog_set_default_response (GTK_DIALOG (chooser_dialog), GTK_RESPONSE_ACCEPT); + + gtk_file_chooser_add_shortcut_folder (chooser_dialog, chooser_dir, NULL); + pics_dir = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES); + if (pics_dir != NULL) + gtk_file_chooser_add_shortcut_folder (chooser_dialog, pics_dir, NULL); + + if (!g_file_test (chooser_dir, G_FILE_TEST_IS_DIR)) + chooser_dir = g_get_home_dir (); + + gtk_file_chooser_set_current_folder (chooser_dialog, chooser_dir); + gtk_file_chooser_set_use_preview_label (chooser_dialog, FALSE); + + image = gtk_image_new (); + gtk_file_chooser_set_preview_widget (chooser_dialog, image); + gtk_widget_set_size_request (image, 128, -1); + + gtk_widget_show (image); + + g_signal_connect (chooser_dialog, "update-preview", + G_CALLBACK (about_me_update_preview), me); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("Images")); + gtk_file_filter_add_pixbuf_formats (filter); + gtk_file_chooser_add_filter (chooser_dialog, filter); + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("All Files")); + gtk_file_filter_add_pattern(filter, "*"); + gtk_file_chooser_add_filter (chooser_dialog, filter); + + response = gtk_dialog_run (GTK_DIALOG (chooser_dialog)); + + if (response == GTK_RESPONSE_ACCEPT) { + gchar* filename; + + filename = gtk_file_chooser_get_filename (chooser_dialog); + me->have_image = TRUE; + me->image_changed = TRUE; + + e_image_chooser_set_from_file (E_IMAGE_CHOOSER (me->image_chooser), filename); + g_free (filename); + about_me_update_photo (me); + } else if (response == GTK_RESPONSE_NO) { + me->have_image = FALSE; + me->image_changed = TRUE; + e_image_chooser_set_from_file (E_IMAGE_CHOOSER (me->image_chooser), me->person); + about_me_update_photo (me); + } + + gtk_widget_destroy (GTK_WIDGET (chooser_dialog)); +} + +static void +about_me_image_changed_cb (GtkWidget *widget, MateAboutMe *me) +{ + me->have_image = TRUE; + me->image_changed = TRUE; + about_me_update_photo (me); +} + +/* About Me Dialog Callbacks */ + +static void +about_me_icon_theme_changed (GtkWindow *window, + GtkIconTheme *theme) +{ + GtkIconInfo *icon; + + icon = gtk_icon_theme_lookup_icon (me->theme, "stock_person", 80, 0); + if (icon == NULL) { + g_debug ("Icon not found"); + } + g_free (me->person); + me->person = g_strdup (gtk_icon_info_get_filename (icon)); + + gtk_icon_info_free (icon); + + if (me->have_image) + e_image_chooser_set_from_file (E_IMAGE_CHOOSER (me->image_chooser), me->person); +} + +static void +about_me_button_clicked_cb (GtkDialog *dialog, gint response_id, MateAboutMe *me) +{ + if (response_id == GTK_RESPONSE_HELP) + g_print ("Help goes here"); + else { + if (me->commit_timeout_id) { + g_source_remove (me->commit_timeout_id); + about_me_commit (me); + } + + about_me_destroy (); + gtk_main_quit (); + } +} + +static void +about_me_passwd_clicked_cb (GtkWidget *button, MateAboutMe *me) +{ + GtkBuilder *dialog; + + dialog = me->dialog; + mate_about_me_password (GTK_WINDOW (WID ("about-me-dialog"))); +} + +static void +about_me_fingerprint_button_clicked_cb (GtkWidget *button, MateAboutMe *me) +{ + fingerprint_button_clicked (me->dialog, + me->enable_fingerprint_button, + me->disable_fingerprint_button); +} + +static gint +about_me_setup_dialog (void) +{ + GtkWidget *widget; + GtkWidget *main_dialog; + GtkIconInfo *icon; + GtkBuilder *dialog; + GError *error = NULL; + GList *chain; + gchar *str; + + me = g_new0 (MateAboutMe, 1); + + dialog = gtk_builder_new (); + gtk_builder_add_from_file (dialog, MATECC_UI_DIR "/mate-about-me-dialog.ui", NULL); + + me->image_chooser = e_image_chooser_new (); + gtk_container_add (GTK_CONTAINER (WID ("button-image")), me->image_chooser); + + if (dialog == NULL) { + about_me_destroy (); + return -1; + } + + me->dialog = dialog; + + /* Connect the close button signal */ + main_dialog = WID ("about-me-dialog"); + g_signal_connect (main_dialog, "response", + G_CALLBACK (about_me_button_clicked_cb), me); + + gtk_window_set_resizable (GTK_WINDOW (main_dialog), FALSE); + capplet_set_icon (main_dialog, "user-info"); + + /* Setup theme details */ + me->screen = gtk_window_get_screen (GTK_WINDOW (main_dialog)); + me->theme = gtk_icon_theme_get_for_screen (me->screen); + + icon = gtk_icon_theme_lookup_icon (me->theme, "stock_person", 80, 0); + if (icon != NULL) { + me->person = g_strdup (gtk_icon_info_get_filename (icon)); + gtk_icon_info_free (icon); + } + + g_signal_connect_object (me->theme, "changed", + G_CALLBACK (about_me_icon_theme_changed), + main_dialog, + G_CONNECT_SWAPPED); + + /* Get the self contact */ + if (!e_book_get_self (&me->contact, &me->book, &error)) { + if (error->code == E_BOOK_ERROR_PROTOCOL_NOT_SUPPORTED) { + about_me_error (NULL, _("There was an error while trying to get the addressbook information\n" \ + "Evolution Data Server can't handle the protocol")); + g_clear_error (&error); + about_me_destroy (); + return -1; + } + + g_clear_error (&error); + + me->create_self = TRUE; + me->contact = e_contact_new (); + + if (me->book == NULL) { + me->book = e_book_new_system_addressbook (&error); + if (me->book == NULL || error != NULL) { + g_error ("%s", error->message); + g_clear_error (&error); + } + + if (e_book_open (me->book, FALSE, NULL) == FALSE) { + about_me_error (GTK_WINDOW (main_dialog), + _("Unable to open address book")); + g_clear_error (&error); + } + } + } else { + about_me_setup_email (me); + } + + me->login = g_strdup (g_get_user_name ()); + me->username = g_strdup (g_get_real_name ()); + + /* Contact Tab */ + about_me_load_photo (me, me->contact); + + widget = WID ("fullname"); + str = g_strdup_printf ("%s", me->username); + + gtk_label_set_markup (GTK_LABEL (widget), str); + g_free (str); + + widget = WID ("login"); + gtk_label_set_text (GTK_LABEL (widget), me->login); + + str = g_strdup_printf (_("About %s"), me->username); + gtk_window_set_title (GTK_WINDOW (main_dialog), str); + g_free (str); + + widget = WID ("password"); + g_signal_connect (widget, "clicked", + G_CALLBACK (about_me_passwd_clicked_cb), me); + + widget = WID ("button-image"); + g_signal_connect (widget, "clicked", + G_CALLBACK (about_me_image_clicked_cb), me); + + me->enable_fingerprint_button = WID ("enable_fingerprint_button"); + me->disable_fingerprint_button = WID ("disable_fingerprint_button"); + + g_signal_connect (me->enable_fingerprint_button, "clicked", + G_CALLBACK (about_me_fingerprint_button_clicked_cb), me); + g_signal_connect (me->disable_fingerprint_button, "clicked", + G_CALLBACK (about_me_fingerprint_button_clicked_cb), me); + + g_signal_connect (me->image_chooser, "changed", + G_CALLBACK (about_me_image_changed_cb), me); + + /* Address tab: set up the focus chains */ + chain = g_list_prepend (NULL, WID ("addr-country-1")); + chain = g_list_prepend (chain, WID ("addr-po-1")); + chain = g_list_prepend (chain, WID ("addr-region-1")); + chain = g_list_prepend (chain, WID ("addr-code-1")); + chain = g_list_prepend (chain, WID ("addr-locality-1")); + chain = g_list_prepend (chain, WID ("addr-scrolledwindow-1")); + widget = WID ("addr-table-1"); + gtk_container_set_focus_chain (GTK_CONTAINER (widget), chain); + g_list_free (chain); + + chain = g_list_prepend (NULL, WID ("addr-country-2")); + chain = g_list_prepend (chain, WID ("addr-po-2")); + chain = g_list_prepend (chain, WID ("addr-region-2")); + chain = g_list_prepend (chain, WID ("addr-code-2")); + chain = g_list_prepend (chain, WID ("addr-locality-2")); + chain = g_list_prepend (chain, WID ("addr-scrolledwindow-2")); + widget = WID ("addr-table-2"); + gtk_container_set_focus_chain (GTK_CONTAINER (widget), chain); + g_list_free (chain); + + about_me_load_info (me); + + gtk_widget_show_all (main_dialog); + + return 0; +} + +int +main (int argc, char **argv) +{ + int rc = 0; + + capplet_init (NULL, &argc, &argv); + + if (!g_thread_supported ()) + g_thread_init (NULL); + + dbus_g_object_register_marshaller (fprintd_marshal_VOID__STRING_BOOLEAN, + G_TYPE_NONE, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_INVALID); + + rc = about_me_setup_dialog (); + + if (rc != -1) { + gtk_main (); + } + + return rc; +} diff --git a/capplets/about-me/mate-about-me.desktop.in.in b/capplets/about-me/mate-about-me.desktop.in.in new file mode 100644 index 00000000..e8393304 --- /dev/null +++ b/capplets/about-me/mate-about-me.desktop.in.in @@ -0,0 +1,14 @@ +[Desktop Entry] +_Name=About Me +_Comment=Set your personal information +Exec=mate-about-me +Icon=user-info +Terminal=false +Type=Application +StartupNotify=true +Categories=MATE;GTK;Settings;X-MATE-PersonalSettings; +OnlyShowIn=MATE; +X-MATE-Bugzilla-Bugzilla=MATE +X-MATE-Bugzilla-Product=mate-control-center +X-MATE-Bugzilla-Component=about-me +X-MATE-Bugzilla-Version=@VERSION@ -- cgit v1.2.1