[network-manager-applet] gsm: implement initial unlock dialog for PIN & PUK



commit c6ae5b3c0501deadf89ca78fda578b387d8f73c8
Author: Dan Williams <dcbw redhat com>
Date:   Thu Mar 4 00:34:50 2010 -0800

    gsm: implement initial unlock dialog for PIN & PUK
    
    Needs some PolicyKit love but that will come later.

 src/applet-device-gsm.c |  288 ++++++++++++++++++++++++++++++------
 src/applet-dialogs.c    |  373 +++++++++++++++++++++++++++++++++++++++++++++++
 src/applet-dialogs.h    |   33 ++++
 src/applet.glade        |  261 +++++++++++++++++++++++++++++++++
 4 files changed, 907 insertions(+), 48 deletions(-)
---
diff --git a/src/applet-device-gsm.c b/src/applet-device-gsm.c
index fe4ff9a..f1a6a22 100644
--- a/src/applet-device-gsm.c
+++ b/src/applet-device-gsm.c
@@ -44,6 +44,36 @@
 #include "mb-menu-item.h"
 #include "nma-marshal.h"
 
+
+typedef struct {
+	NMDevice *device;
+
+	DBusGProxy *props_proxy;
+	DBusGProxy *card_proxy;
+	DBusGProxy *net_proxy;
+
+	gboolean quality_valid;
+	guint32 quality;
+
+	char *unlock_required;
+
+	/* reg_state is (1 + MM reg state) so that 0 means we haven't gotten a
+	 * value from MM yet.  0 is a valid MM GSM reg state.
+	 */
+	guint reg_state;
+	char *op_code;
+	char *op_name;
+
+	gboolean nopoll;
+	guint32 poll_id;
+
+	/* Unlock dialog stuff */
+	GtkWidget *dialog;
+} GsmDeviceInfo;
+
+static void unlock_dialog_destroy (GsmDeviceInfo *info);
+
+
 typedef struct {
 	NMApplet *applet;
 	NMDevice *device;
@@ -199,27 +229,6 @@ add_connection_item (NMDevice *device,
 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 }
 
-typedef struct {
-	DBusGProxy *props_proxy;
-	DBusGProxy *card_proxy;
-	DBusGProxy *net_proxy;
-
-	gboolean quality_valid;
-	guint32 quality;
-
-	char *unlock_required;
-
-	/* reg_state is (1 + MM reg state) so that 0 means we haven't gotten a
-	 * value from MM yet.  0 is a valid MM GSM reg state.
-	 */
-	guint reg_state;
-	char *op_code;
-	char *op_name;
-
-	gboolean nopoll;
-	guint32 poll_id;
-} GsmDeviceInfo;
-
 static guint32
 state_for_info (GsmDeviceInfo *info)
 {
@@ -467,7 +476,7 @@ pin_entry_changed (GtkEditable *editable, gpointer user_data)
 }
 
 static void
-destroy_gsm_dialog (gpointer user_data, GObject *finalized)
+secrets_dialog_destroy (gpointer user_data, GObject *finalized)
 {
 	NMGsmSecretsInfo *info = user_data;
 
@@ -581,7 +590,7 @@ get_existing_secrets_cb (NMSettingsConnectionInterface *connection,
 	}
 
 	nm_connection_clear_secrets (NM_CONNECTION (info->connection));
-	destroy_gsm_dialog (info, NULL);
+	secrets_dialog_destroy (info, NULL);
 }
 
 static void
@@ -595,7 +604,7 @@ get_gsm_secrets_cb (GtkDialog *dialog,
 	/* Got a user response, clear the NMActiveConnection destroy handler for
 	 * this dialog since this function will now take over dialog destruction.
 	 */
-	g_object_weak_unref (G_OBJECT (info->active_connection), destroy_gsm_dialog, info);
+	g_object_weak_unref (G_OBJECT (info->active_connection), secrets_dialog_destroy, info);
 
 	if (response == GTK_RESPONSE_OK) {
 		const char *hints[2] = { info->secret_name, NULL };
@@ -627,13 +636,12 @@ get_gsm_secrets_cb (GtkDialog *dialog,
 		g_error_free (error);
 
 		nm_connection_clear_secrets (NM_CONNECTION (info->connection));
-		destroy_gsm_dialog (info, NULL);
+		secrets_dialog_destroy (info, NULL);
 	}
 }
 
 static GtkWidget *
 ask_for_pin_puk (NMDevice *device,
-                 NMConnection *connection,
                  const char *secret_name,
                  GtkEntry **out_secret_entry)
 {
@@ -702,7 +710,7 @@ gsm_get_secrets (NMDevice *device,
                  NMApplet *applet,
                  GError **error)
 {
-	NMGsmSecretsInfo *info;
+	NMGsmSecretsInfo *secrets_info;
 	GtkWidget *widget;
 	GtkEntry *secret_entry = NULL;
 
@@ -716,9 +724,16 @@ gsm_get_secrets (NMDevice *device,
 	}
 
 	if (   !strcmp (hints[0], NM_SETTING_GSM_PIN)
-	    || !strcmp (hints[0], NM_SETTING_GSM_PUK))
-		widget = ask_for_pin_puk (device, NM_CONNECTION (connection), hints[0], &secret_entry);
-	else if (!strcmp (hints[0], NM_SETTING_GSM_PASSWORD))
+	    || !strcmp (hints[0], NM_SETTING_GSM_PUK)) {
+		GsmDeviceInfo *info = g_object_get_data (G_OBJECT (device), "devinfo");
+
+		g_assert (info);
+		/* A GetSecrets PIN dialog overrides the initial unlock dialog */
+		if (info->dialog)
+			unlock_dialog_destroy (info);
+
+		widget = ask_for_pin_puk (device, hints[0], &secret_entry);
+	} else if (!strcmp (hints[0], NM_SETTING_GSM_PASSWORD))
 		widget = applet_mobile_password_dialog_new (device, NM_CONNECTION (connection), &secret_entry);
 	else {
 		g_set_error (error,
@@ -738,22 +753,22 @@ gsm_get_secrets (NMDevice *device,
 		return FALSE;
 	}
 
-	info = g_malloc0 (sizeof (NMGsmSecretsInfo));
-	info->callback = callback;
-	info->callback_data = callback_data;
-	info->applet = applet;
-	info->active_connection = active_connection;
-	info->connection = g_object_ref (connection);
-	info->secret_name = g_strdup (hints[0]);
-	info->dialog = widget;
-	info->secret_entry = secret_entry;
+	secrets_info = g_malloc0 (sizeof (NMGsmSecretsInfo));
+	secrets_info->callback = callback;
+	secrets_info->callback_data = callback_data;
+	secrets_info->applet = applet;
+	secrets_info->active_connection = active_connection;
+	secrets_info->connection = g_object_ref (connection);
+	secrets_info->secret_name = g_strdup (hints[0]);
+	secrets_info->dialog = widget;
+	secrets_info->secret_entry = secret_entry;
 
-	g_signal_connect (widget, "response", G_CALLBACK (get_gsm_secrets_cb), info);
+	g_signal_connect (widget, "response", G_CALLBACK (get_gsm_secrets_cb), secrets_info);
 
 	/* Attach a destroy notifier to the NMActiveConnection so we can destroy
 	 * the dialog when the active connection goes away.
 	 */
-	g_object_weak_ref (G_OBJECT (active_connection), destroy_gsm_dialog, info);
+	g_object_weak_ref (G_OBJECT (active_connection), secrets_dialog_destroy, secrets_info);
 
 	gtk_window_set_position (GTK_WINDOW (widget), GTK_WIN_POS_CENTER_ALWAYS);
 	gtk_widget_realize (GTK_WIDGET (widget));
@@ -762,6 +777,174 @@ gsm_get_secrets (NMDevice *device,
 	return TRUE;
 }
 
+/********************************************************************/
+
+static void
+unlock_dialog_destroy (GsmDeviceInfo *info)
+{
+	applet_mobile_pin_dialog_destroy (info->dialog);
+	info->dialog = NULL;
+}
+
+static void
+unlock_pin_reply (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+	GsmDeviceInfo *info = user_data;
+	GError *error = NULL;
+
+	if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) {
+		unlock_dialog_destroy (info);
+		return;
+	}
+
+	/* FIXME: show the error in the dialog or something */
+
+	applet_mobile_pin_dialog_stop_spinner (info->dialog);
+	g_warning ("%s: error unlocking with PIN: %s", __func__, error->message);
+	g_clear_error (&error);
+}
+
+static void
+unlock_puk_reply (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+	GsmDeviceInfo *info = user_data;
+	GError *error = NULL;
+
+	if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) {
+		unlock_dialog_destroy (info);
+		return;
+	}
+
+	/* FIXME: show the error in the dialog or something */
+
+	applet_mobile_pin_dialog_stop_spinner (info->dialog);
+	g_warning ("%s: error unlocking with PIN: %s", __func__, error->message);
+	g_clear_error (&error);
+}
+
+#define UNLOCK_CODE_PIN 1
+#define UNLOCK_CODE_PUK 2
+
+static void
+unlock_dialog_response (GtkDialog *dialog,
+                        gint response,
+                        gpointer user_data)
+{
+	GsmDeviceInfo *info = user_data;
+	const char *code1, *code2;
+	guint32 unlock_code;
+
+	if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT) {
+		unlock_dialog_destroy (info);
+		return;
+	}
+
+	/* Start the spinner to show the progress of the unlock */
+	applet_mobile_pin_dialog_start_spinner (info->dialog, _("Sending unlock code..."));
+
+	unlock_code = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (info->dialog), "unlock-code"));
+	if (!unlock_code) {
+		g_warn_if_fail (unlock_code != 0);
+		unlock_dialog_destroy (info);
+		return;
+	}
+
+	code1 = applet_mobile_pin_dialog_get_entry1 (info->dialog);
+	if (!code1 || !strlen (code1)) {
+		g_warn_if_fail (code1 != NULL);
+		g_warn_if_fail (strlen (code1));
+		unlock_dialog_destroy (info);
+		return;
+	}
+
+	/* Send the code to ModemManager */
+	if (unlock_code == UNLOCK_CODE_PIN) {
+		dbus_g_proxy_begin_call (info->card_proxy, "SendPin",
+		                         unlock_pin_reply, info, NULL,
+		                         G_TYPE_STRING, code1, G_TYPE_INVALID);
+	} else if (unlock_code == UNLOCK_CODE_PUK) {
+		code2 = applet_mobile_pin_dialog_get_entry2 (info->dialog);
+		if (!code2) {
+			g_warn_if_fail (code2 != NULL);
+			unlock_dialog_destroy (info);
+			return;
+		}
+
+		dbus_g_proxy_begin_call (info->card_proxy, "SendPuk",
+		                         unlock_puk_reply, info, NULL,
+		                         G_TYPE_STRING, code1,
+		                         G_TYPE_STRING, code2,
+		                         G_TYPE_INVALID);
+	}
+}
+
+static void
+unlock_dialog_new (NMDevice *device, GsmDeviceInfo *info)
+{
+	const char *header = NULL;
+	const char *title = NULL;
+	char *desc = NULL;
+	const char *label1 = NULL, *label2 = NULL, *label3 = NULL;
+	const char *device_desc;
+	gboolean match23 = FALSE;
+	guint32 label1_min = 0, label2_min = 0, label3_min = 0;
+	guint32 label1_max = 0, label2_max = 0, label3_max = 0;
+	guint32 unlock_code = 0;
+
+	g_return_if_fail (info->unlock_required != NULL);
+
+	if (info->dialog)
+		return;
+
+	/* Figure out the dialog text based on the required unlock code */
+	device_desc = utils_get_device_description (device);
+	if (!strcmp (info->unlock_required, "sim-pin")) {
+		title = _("SIM PIN unlock required");
+		header = _("SIM PIN Unlock Required");
+		/* FIXME: some warning about # of times you can enter incorrect PIN */
+		desc = g_strdup_printf (_("The mobile broadband device '%s' requires a SIM PIN code before it can be used."), device_desc);
+		label1 = _("PIN code:");
+		label1_min = 4;
+		label1_max = 8;
+		unlock_code = UNLOCK_CODE_PIN;
+	} else if (!strcmp (info->unlock_required, "sim-puk")) {
+		title = _("SIM PUK unlock required");
+		header = _("SIM PUK Unlock Required");
+		/* FIXME: some warning about # of times you can enter incorrect PUK */
+		desc = g_strdup_printf (_("The mobile broadband device '%s' requires a SIM PUK code before it can be used."), device_desc);
+		label1 = _("PUK code:");
+		label1_min = label1_max = 8;
+		label2 = _("New PIN code:");
+		label3 = _("Re-enter new PIN code:");
+		label2_min = label3_min = 4;
+		label2_max = label3_max = 8;
+		match23 = TRUE;
+		unlock_code = UNLOCK_CODE_PUK;
+	} else {
+		g_warning ("Unhandled unlock request for '%s'", info->unlock_required);
+		return;
+	}
+
+	/* Construct and run the dialog */
+	info->dialog = applet_mobile_pin_dialog_new (title, header, desc);
+	g_free (desc);
+	g_return_if_fail (info->dialog != NULL);
+
+	g_object_set_data (G_OBJECT (info->dialog), "unlock-code", GUINT_TO_POINTER (unlock_code));
+	applet_mobile_pin_dialog_match_23 (info->dialog, match23);
+
+	applet_mobile_pin_dialog_set_entry1 (info->dialog, label1, label1_min, label1_max);
+	if (label2)
+		applet_mobile_pin_dialog_set_entry2 (info->dialog, label2, label2_min, label2_max);
+	if (label3)
+		applet_mobile_pin_dialog_set_entry3 (info->dialog, label3, label3_min, label3_max);
+
+	g_signal_connect (info->dialog, "response", G_CALLBACK (unlock_dialog_response), info);
+	applet_mobile_pin_dialog_present (info->dialog, FALSE);
+}
+
+/********************************************************************/
+
 static void
 gsm_device_info_free (gpointer data)
 {
@@ -777,6 +960,9 @@ gsm_device_info_free (gpointer data)
 	if (info->poll_id)
 		g_source_remove (info->poll_id);
 
+	if (info->dialog)
+		unlock_dialog_destroy (info);
+
 	g_free (info->op_code);
 	g_free (info->op_name);
 	memset (info, 0, sizeof (GsmDeviceInfo));
@@ -844,17 +1030,19 @@ unlock_reply (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
 {
 	GsmDeviceInfo *info = user_data;
 	GError *error = NULL;
-	char *unlock = NULL;
+	GValue value = { 0 };
 
 	if (dbus_g_proxy_end_call (proxy, call, &error,
-	                           G_TYPE_STRING, &unlock,
+	                           G_TYPE_VALUE, &value,
 	                           G_TYPE_INVALID)) {
-		g_free (info->unlock_required);
-		info->unlock_required = unlock;
+		if (G_VALUE_HOLDS_STRING (&value)) {
+			g_free (info->unlock_required);
+			info->unlock_required = g_value_dup_string (&value);
 
-		if (info->unlock_required) {
-			/* Handle unlock */
+			if (info->unlock_required)
+				unlock_dialog_new (info->device, info);
 		}
+		g_value_unset (&value);
 	}
 
 	g_clear_error (&error);
@@ -924,7 +1112,10 @@ modem_properties_changed (DBusGProxy *proxy,
 		info->unlock_required = g_value_dup_string (value);
 
 		if (info->unlock_required) {
-			/* Handle unlock */
+			/* FIXME: handle unlock changes; like if the user enters the wrong
+			 * pin too many times we want to show the PUK dialog instead of the
+			 * pin dialog.
+			 */
 		}
 	}
 }
@@ -944,6 +1135,7 @@ gsm_device_added (NMDevice *device, NMApplet *applet)
 		return;
 
 	info = g_malloc0 (sizeof (GsmDeviceInfo));
+	info->device = device;
 
 	/* Don't bother polling if the device isn't usable */
 	state = nm_device_get_state (device);
diff --git a/src/applet-dialogs.c b/src/applet-dialogs.c
index 54251ae..149187f 100644
--- a/src/applet-dialogs.c
+++ b/src/applet-dialogs.c
@@ -23,6 +23,7 @@
 #include <netinet/in.h>
 #include <sys/socket.h>
 #include <arpa/inet.h>
+#include <ctype.h>
 
 #include <nm-device-ethernet.h>
 #include <nm-device-wifi.h>
@@ -43,6 +44,7 @@
 
 #include "applet-dialogs.h"
 #include "utils.h"
+#include "nma-bling-spinner.h"
 
 
 static void
@@ -756,3 +758,374 @@ applet_mobile_password_dialog_new (NMDevice *device,
 	return GTK_WIDGET (dialog);
 }
 
+/**********************************************************************/
+
+static void
+mpd_entry_changed (GtkWidget *widget, gpointer user_data)
+{
+	GtkWidget *dialog = GTK_WIDGET (user_data);
+	GladeXML *xml = g_object_get_data (G_OBJECT (dialog), "xml");
+	GtkWidget *entry;
+	guint32 minlen;
+	gboolean valid = FALSE;
+	const char *text, *text2 = NULL, *text3 = NULL;
+	gboolean match23;
+
+	g_return_if_fail (xml != NULL);
+
+	entry = glade_xml_get_widget (xml, "code1_entry");
+	if (g_object_get_data (G_OBJECT (entry), "active")) {
+		minlen = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (entry), "minlen"));
+		text = gtk_entry_get_text (GTK_ENTRY (entry));
+		if (text && (strlen (text) < minlen))
+			goto done;
+	}
+
+	entry = glade_xml_get_widget (xml, "code2_entry");
+	if (g_object_get_data (G_OBJECT (entry), "active")) {
+		minlen = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (entry), "minlen"));
+		text2 = gtk_entry_get_text (GTK_ENTRY (entry));
+		if (text2 && (strlen (text2) < minlen))
+			goto done;
+	}
+
+	entry = glade_xml_get_widget (xml, "code3_entry");
+	if (g_object_get_data (G_OBJECT (entry), "active")) {
+		minlen = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (entry), "minlen"));
+		text3 = gtk_entry_get_text (GTK_ENTRY (entry));
+		if (text3 && (strlen (text3) < minlen))
+			goto done;
+	}
+
+	/* Validate 2 & 3 if they are supposed to be the same */
+	match23 = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (dialog), "match23"));
+	if (match23) {
+		if (!text2 || !text3 || strcmp (text2, text3))
+			goto done;
+	}
+
+	valid = TRUE;
+
+done:
+	widget = glade_xml_get_widget (xml, "unlock_button");
+	g_warn_if_fail (widget != NULL);
+	gtk_widget_set_sensitive (widget, valid);
+	if (valid)
+		gtk_widget_grab_default (widget);
+}
+
+void
+applet_mobile_pin_dialog_destroy (GtkWidget *widget)
+{
+	gtk_widget_hide (widget);
+	gtk_widget_destroy (widget);
+}
+
+static void
+mpd_cancel_dialog (GtkDialog *dialog)
+{
+	gtk_dialog_response (dialog, GTK_RESPONSE_CANCEL);
+}
+
+GtkWidget *
+applet_mobile_pin_dialog_new (const char *title,
+                              const char *header,
+                              const char *desc)
+{
+	char *glade_file, *str;
+	GladeXML *xml;
+	GtkWidget *dialog;
+	GtkWidget *widget;
+
+	g_return_val_if_fail (title != NULL, NULL);
+	g_return_val_if_fail (header != NULL, NULL);
+	g_return_val_if_fail (desc != NULL, NULL);
+
+	glade_file = g_build_filename (GLADEDIR, "applet.glade", NULL);
+	g_return_val_if_fail (glade_file != NULL, NULL);
+	xml = glade_xml_new (glade_file, "unlock_dialog", NULL);
+	g_free (glade_file);
+	g_return_val_if_fail (xml != NULL, NULL);
+
+	dialog = glade_xml_get_widget (xml, "unlock_dialog");
+	if (!dialog) {
+		g_object_unref (xml);
+		g_return_val_if_fail (dialog != NULL, NULL);
+	}
+
+	g_object_set_data_full (G_OBJECT (dialog), "xml", xml, (GDestroyNotify) g_object_unref);
+
+	gtk_window_set_title (GTK_WINDOW (dialog), title);
+
+	widget = glade_xml_get_widget (xml, "header_label");
+	str = g_strdup_printf ("<span size=\"larger\" weight=\"bold\">%s</span>", header);
+	gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
+	gtk_label_set_markup (GTK_LABEL (widget), str);
+	g_free (str);
+
+	widget = glade_xml_get_widget (xml, "desc_label");
+	gtk_label_set_text (GTK_LABEL (widget), desc);
+
+	g_signal_connect (dialog, "delete-event", G_CALLBACK (mpd_cancel_dialog), NULL);
+
+	mpd_entry_changed (NULL, dialog);
+
+	return dialog;
+}
+
+void
+applet_mobile_pin_dialog_present (GtkWidget *dialog, gboolean now)
+{
+	GladeXML *xml;
+	GtkWidget *widget;
+
+	g_return_if_fail (dialog != NULL);
+	xml = g_object_get_data (G_OBJECT (dialog), "xml");
+	g_return_if_fail (xml != NULL);
+
+	gtk_widget_show_all (dialog);
+
+	widget = glade_xml_get_widget (xml, "progress_hbox");
+	gtk_widget_hide (widget);
+
+	/* Hide inactive entries */
+
+	widget = glade_xml_get_widget (xml, "code2_entry");
+	if (!g_object_get_data (G_OBJECT (widget), "active")) {
+		gtk_widget_hide (widget);
+		widget = glade_xml_get_widget (xml, "code2_label");
+		gtk_widget_hide (widget);
+	}
+
+	widget = glade_xml_get_widget (xml, "code3_entry");
+	if (!g_object_get_data (G_OBJECT (widget), "active")) {
+		gtk_widget_hide (widget);
+		widget = glade_xml_get_widget (xml, "code3_label");
+		gtk_widget_hide (widget);
+	}
+
+	/* Need to resize the dialog after hiding widgets */
+	gtk_window_resize (GTK_WINDOW (dialog), 400, 100);
+
+	/* Show the dialog */
+	gtk_widget_realize (dialog);
+	if (now)
+		gtk_window_present_with_time (GTK_WINDOW (dialog), gdk_x11_get_server_time (dialog->window));
+	else
+		gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+mpd_entry_filter (GtkEntry *entry,
+                  const char *text,
+                  gint length,
+                  gint *position,
+                  gpointer user_data)
+{
+	GtkEditable *editable = GTK_EDITABLE (entry);
+	int i, count = 0;
+	gchar *result = g_malloc0 (length);
+
+	/* Digits only */
+	for (i = 0; i < length; i++) {
+		if (isdigit (text[i]))
+			result[count++] = text[i];
+	}
+
+	if (count > 0) {
+		g_signal_handlers_block_by_func (G_OBJECT (editable),
+		                                 G_CALLBACK (mpd_entry_filter),
+		                                 user_data);
+		gtk_editable_insert_text (editable, result, count, position);
+		g_signal_handlers_unblock_by_func (G_OBJECT (editable),
+		                                   G_CALLBACK (mpd_entry_filter),
+		                                   user_data);
+	}
+	g_signal_stop_emission_by_name (G_OBJECT (editable), "insert-text");
+	g_free (result);
+}
+
+static void
+mpd_set_entry (GtkWidget *dialog,
+               const char *entry_name,
+               const char *label_name,
+               const char *label,
+               guint32 minlen,
+               guint32 maxlen)
+{
+	GladeXML *xml;
+	GtkWidget *widget;
+	gboolean entry2_active = FALSE;
+	gboolean entry3_active = FALSE;
+
+	g_return_if_fail (dialog != NULL);
+	xml = g_object_get_data (G_OBJECT (dialog), "xml");
+	g_return_if_fail (xml != NULL);
+
+	widget = glade_xml_get_widget (xml, label_name);
+	gtk_label_set_text (GTK_LABEL (widget), label);
+
+	widget = glade_xml_get_widget (xml, entry_name);
+	g_signal_connect (widget, "changed", G_CALLBACK (mpd_entry_changed), dialog);
+	g_signal_connect (widget, "insert-text", G_CALLBACK (mpd_entry_filter), NULL);
+
+	if (maxlen)
+		gtk_entry_set_max_length (GTK_ENTRY (widget), maxlen);
+	g_object_set_data (G_OBJECT (widget), "minlen", GUINT_TO_POINTER (minlen));
+
+	/* Tag it so we know it's active */
+	g_object_set_data (G_OBJECT (widget), "active", GUINT_TO_POINTER (1));
+
+	/* Make a single-entry dialog look better */
+	widget = glade_xml_get_widget (xml, "code2_entry");
+	entry2_active = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), "active"));
+	widget = glade_xml_get_widget (xml, "code3_entry");
+	entry3_active = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), "active"));
+
+	widget = glade_xml_get_widget (xml, "table1");
+	if (entry2_active || entry3_active)
+		gtk_table_set_row_spacings (GTK_TABLE (widget), 6);
+	else
+		gtk_table_set_row_spacings (GTK_TABLE (widget), 0);
+
+	mpd_entry_changed (NULL, dialog);
+}
+
+void
+applet_mobile_pin_dialog_set_entry1 (GtkWidget *dialog,
+                                     const char *label,
+                                     guint32 minlen,
+                                     guint32 maxlen)
+{
+	mpd_set_entry (dialog, "code1_entry", "code1_label", label, minlen, maxlen);
+}
+
+void
+applet_mobile_pin_dialog_set_entry2 (GtkWidget *dialog,
+                                     const char *label,
+                                     guint32 minlen,
+                                     guint32 maxlen)
+{
+	mpd_set_entry (dialog, "code2_entry", "code2_label", label, minlen, maxlen);
+}
+
+void
+applet_mobile_pin_dialog_set_entry3 (GtkWidget *dialog,
+                                     const char *label,
+                                     guint32 minlen,
+                                     guint32 maxlen)
+{
+	mpd_set_entry (dialog, "code3_entry", "code3_label", label, minlen, maxlen);
+}
+
+void applet_mobile_pin_dialog_match_23 (GtkWidget *dialog, gboolean match)
+{
+	g_return_if_fail (dialog != NULL);
+
+	g_object_set_data (G_OBJECT (dialog), "match23", GUINT_TO_POINTER (match));
+}
+
+static const char *
+mpd_get_entry (GtkWidget *dialog, const char *entry_name)
+{
+	GladeXML *xml;
+	GtkWidget *widget;
+
+	g_return_val_if_fail (dialog != NULL, NULL);
+	xml = g_object_get_data (G_OBJECT (dialog), "xml");
+	g_return_val_if_fail (xml != NULL, NULL);
+
+	widget = glade_xml_get_widget (xml, entry_name);
+	return gtk_entry_get_text (GTK_ENTRY (widget));
+}
+
+const char *
+applet_mobile_pin_dialog_get_entry1 (GtkWidget *dialog)
+{
+	return mpd_get_entry (dialog, "code1_entry");
+}
+
+const char *
+applet_mobile_pin_dialog_get_entry2 (GtkWidget *dialog)
+{
+	return mpd_get_entry (dialog, "code2_entry");
+}
+
+const char *
+applet_mobile_pin_dialog_get_entry3 (GtkWidget *dialog)
+{
+	return mpd_get_entry (dialog, "code3_entry");
+}
+
+void
+applet_mobile_pin_dialog_start_spinner (GtkWidget *dialog, const char *text)
+{
+	GladeXML *xml;
+	GtkWidget *spinner, *widget, *hbox, *align;
+
+	g_return_if_fail (dialog != NULL);
+	g_return_if_fail (text != NULL);
+
+	xml = g_object_get_data (G_OBJECT (dialog), "xml");
+	g_return_if_fail (xml != NULL);
+
+	spinner = nma_bling_spinner_new ();
+	g_return_if_fail (spinner != NULL);
+	g_object_set_data (G_OBJECT (dialog), "spinner", spinner);
+
+	align = glade_xml_get_widget (xml, "spinner_alignment");
+	gtk_container_add (GTK_CONTAINER (align), spinner);
+	nma_bling_spinner_start (NMA_BLING_SPINNER (spinner));
+
+	widget = glade_xml_get_widget (xml, "progress_label");
+	gtk_label_set_text (GTK_LABEL (widget), text);
+	gtk_widget_show (widget);
+
+	hbox = glade_xml_get_widget (xml, "progress_hbox");
+	gtk_widget_show_all (hbox);
+
+	/* Desensitize everything while spinning */
+	widget = glade_xml_get_widget (xml, "code1_entry");
+	gtk_widget_set_sensitive (widget, FALSE);
+	widget = glade_xml_get_widget (xml, "code2_entry");
+	gtk_widget_set_sensitive (widget, FALSE);
+	widget = glade_xml_get_widget (xml, "code3_entry");
+	gtk_widget_set_sensitive (widget, FALSE);
+	widget = glade_xml_get_widget (xml, "unlock_button");
+	gtk_widget_set_sensitive (widget, FALSE);
+	widget = glade_xml_get_widget (xml, "cancel_button");
+	gtk_widget_set_sensitive (widget, FALSE);
+}
+
+void
+applet_mobile_pin_dialog_stop_spinner (GtkWidget *dialog)
+{
+	GladeXML *xml;
+	GtkWidget *spinner, *widget;
+
+	g_return_if_fail (dialog != NULL);
+
+	xml = g_object_get_data (G_OBJECT (dialog), "xml");
+	g_return_if_fail (xml != NULL);
+
+	spinner = g_object_get_data (G_OBJECT (dialog), "spinner");
+	g_return_if_fail (spinner != NULL);
+	nma_bling_spinner_stop (NMA_BLING_SPINNER (spinner));
+	gtk_widget_hide (spinner);
+
+	widget = glade_xml_get_widget (xml, "progress_label");
+	gtk_widget_hide (widget);
+
+	/* Resensitize stuff */
+	widget = glade_xml_get_widget (xml, "code1_entry");
+	gtk_widget_set_sensitive (widget, TRUE);
+	widget = glade_xml_get_widget (xml, "code2_entry");
+	gtk_widget_set_sensitive (widget, TRUE);
+	widget = glade_xml_get_widget (xml, "code3_entry");
+	gtk_widget_set_sensitive (widget, TRUE);
+	widget = glade_xml_get_widget (xml, "unlock_button");
+	gtk_widget_set_sensitive (widget, TRUE);
+	widget = glade_xml_get_widget (xml, "cancel_button");
+	gtk_widget_set_sensitive (widget, TRUE);
+}
+
diff --git a/src/applet-dialogs.h b/src/applet-dialogs.h
index fcf54eb..1e7546e 100644
--- a/src/applet-dialogs.h
+++ b/src/applet-dialogs.h
@@ -37,4 +37,37 @@ GtkWidget *applet_mobile_password_dialog_new (NMDevice *device,
                                               NMConnection *connection,
                                               GtkEntry **out_secret_entry);
 
+/******** Mobile PIN dialog ********/
+
+GtkWidget *applet_mobile_pin_dialog_new (const char *title,
+                                         const char *header,
+                                         const char *desc);
+
+void applet_mobile_pin_dialog_present (GtkWidget *dialog, gboolean now);
+
+void applet_mobile_pin_dialog_destroy (GtkWidget *dialog);
+
+void applet_mobile_pin_dialog_set_entry1 (GtkWidget *dialog,
+                                          const char *label,
+                                          guint32 minlen,
+                                          guint32 maxlen);
+const char *applet_mobile_pin_dialog_get_entry1 (GtkWidget *dialog);
+
+void applet_mobile_pin_dialog_set_entry2 (GtkWidget *dialog,
+                                          const char *label,
+                                          guint32 minlen,
+                                          guint32 maxlen);
+const char *applet_mobile_pin_dialog_get_entry2 (GtkWidget *dialog);
+
+void applet_mobile_pin_dialog_set_entry3 (GtkWidget *dialog,
+                                          const char *label,
+                                          guint32 minlen,
+                                          guint32 maxlen);
+const char *applet_mobile_pin_dialog_get_entry3 (GtkWidget *dialog);
+
+void applet_mobile_pin_dialog_match_23 (GtkWidget *dialog, gboolean match);
+
+void applet_mobile_pin_dialog_start_spinner (GtkWidget *dialog, const char *text);
+void applet_mobile_pin_dialog_stop_spinner (GtkWidget *dialog);
+
 #endif /* __APPLET_DIALOGS_H__ */
diff --git a/src/applet.glade b/src/applet.glade
index ac8bbde..b506dc5 100644
--- a/src/applet.glade
+++ b/src/applet.glade
@@ -1797,4 +1797,265 @@ Version 1</property>
       </widget>
     </child>
   </widget>
+  <widget class="GtkDialog" id="unlock_dialog">
+    <property name="border_width">5</property>
+    <property name="window_position">center</property>
+    <property name="default_width">400</property>
+    <property name="icon_name">dialog-password</property>
+    <property name="type_hint">dialog</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox4">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child>
+          <widget class="GtkHBox" id="hbox1">
+            <property name="visible">True</property>
+            <property name="spacing">6</property>
+            <child>
+              <widget class="GtkAlignment" id="alignment1">
+                <property name="visible">True</property>
+                <property name="yalign">0</property>
+                <child>
+                  <widget class="GtkImage" id="image1">
+                    <property name="visible">True</property>
+                    <property name="yalign">0</property>
+                    <property name="stock">gtk-dialog-authentication</property>
+                    <property name="icon-size">6</property>
+                  </widget>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="padding">6</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkVBox" id="vbox1">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">12</property>
+                <child>
+                  <widget class="GtkLabel" id="header_label">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">label</property>
+                    <property name="use_markup">True</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="padding">12</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="desc_label">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">label</property>
+                    <property name="wrap">True</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkAlignment" id="alignment2">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="yalign">0</property>
+                    <property name="top_padding">12</property>
+                    <property name="bottom_padding">12</property>
+                    <property name="left_padding">12</property>
+                    <property name="right_padding">12</property>
+                    <child>
+                      <widget class="GtkTable" id="table1">
+                        <property name="visible">True</property>
+                        <property name="n_rows">4</property>
+                        <property name="n_columns">2</property>
+                        <property name="column_spacing">6</property>
+                        <property name="row_spacing">6</property>
+                        <child>
+                          <widget class="GtkLabel" id="code1_label">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">label</property>
+                          </widget>
+                          <packing>
+                            <property name="x_options">GTK_FILL</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkEntry" id="code1_entry">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="has_focus">True</property>
+                            <property name="invisible_char">&#x25CF;</property>
+                          </widget>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkLabel" id="code2_label">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">label</property>
+                          </widget>
+                          <packing>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                            <property name="x_options">GTK_FILL</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkEntry" id="code2_entry">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="invisible_char">&#x25CF;</property>
+                          </widget>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkLabel" id="code3_label">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">label</property>
+                          </widget>
+                          <packing>
+                            <property name="top_attach">2</property>
+                            <property name="bottom_attach">3</property>
+                            <property name="x_options">GTK_FILL</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkEntry" id="code3_entry">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="invisible_char">&#x25CF;</property>
+                          </widget>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="top_attach">2</property>
+                            <property name="bottom_attach">3</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                        <child>
+                          <widget class="GtkHBox" id="progress_hbox">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <widget class="GtkAlignment" id="spinner_alignment">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <child>
+                                  <placeholder/>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="progress_label">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                              </widget>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </widget>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="top_attach">3</property>
+                            <property name="bottom_attach">4</property>
+                            <property name="x_options">GTK_FILL</property>
+                            <property name="y_options">GTK_FILL</property>
+                            <property name="y_padding">6</property>
+                          </packing>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area4">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <widget class="GtkButton" id="cancel_button">
+                <property name="label">gtk-cancel</property>
+                <property name="response_id">-6</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkButton" id="unlock_button">
+                <property name="label" translatable="yes">_Unlock</property>
+                <property name="response_id">-5</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="has_default">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_underline">True</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
 </glade-interface>



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