/* * * Copyright (C) 2013 Bastien Nocera * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "bluetooth-pairing-dialog.h" #include "bluetooth-enums.h" #include "gnome-bluetooth-enum-types.h" #include "bluetooth-settings-resources.h" #define BLUETOOTH_PAIRING_DIALOG_GET_PRIVATE(obj) \ (bluetooth_pairing_dialog_get_instance_private (obj)) typedef struct _BluetoothPairingDialogPrivate BluetoothPairingDialogPrivate; struct _BluetoothPairingDialogPrivate { GtkWidget *title; GtkWidget *help_label; GtkWidget *label_pin; GtkWidget *entry_pin; GtkWidget *pin_notebook; GtkWidget *done; GtkWidget *spinner; GtkWidget *cancel; BluetoothPairingMode mode; char *pin; }; G_DEFINE_TYPE_WITH_PRIVATE(BluetoothPairingDialog, bluetooth_pairing_dialog, GTK_TYPE_DIALOG) enum { CONFIRMATION_PAGE, DISPLAY_PAGE, MESSAGE_PAGE }; void bluetooth_pairing_dialog_set_mode (BluetoothPairingDialog *self, BluetoothPairingMode mode, const char *pin, const char *device_name) { BluetoothPairingDialogPrivate *priv = BLUETOOTH_PAIRING_DIALOG_GET_PRIVATE (self); char *title; char *help; GtkStyleContext *context; priv->mode = mode; g_clear_pointer (&priv->pin, g_free); priv->pin = g_strdup (pin); gtk_entry_set_text (GTK_ENTRY (priv->entry_pin), pin ? pin : ""); gtk_label_set_text (GTK_LABEL (priv->label_pin), pin); switch (mode) { case BLUETOOTH_PAIRING_MODE_PIN_QUERY: gtk_widget_show (priv->done); gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->pin_notebook), CONFIRMATION_PAGE); title = g_strdup(_("Confirm Bluetooth PIN")); help = g_strdup_printf (_("Please confirm the PIN that was entered on “%s”."), device_name); break; case BLUETOOTH_PAIRING_MODE_PIN_CONFIRMATION: gtk_widget_show (priv->done); gtk_button_set_label (GTK_BUTTON (priv->done), _("Confirm")); gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->pin_notebook), CONFIRMATION_PAGE); title = g_strdup(_("Confirm Bluetooth PIN")); help = g_strdup_printf (_("Confirm the Bluetooth PIN for “%s”. This can usually be found in the device’s manual."), device_name); break; case BLUETOOTH_PAIRING_MODE_PIN_DISPLAY_NORMAL: case BLUETOOTH_PAIRING_MODE_PIN_DISPLAY_KEYBOARD: case BLUETOOTH_PAIRING_MODE_PIN_DISPLAY_ICADE: gtk_widget_hide (priv->done); title = g_strdup_printf (_("Pairing “%s”"), device_name); gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->pin_notebook), DISPLAY_PAGE); break; case BLUETOOTH_PAIRING_MODE_PIN_MATCH: gtk_button_set_label (GTK_BUTTON (priv->done), _("Confirm")); gtk_widget_show (priv->done); gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->pin_notebook), DISPLAY_PAGE); title = g_strdup(_("Confirm Bluetooth PIN")); help = g_strdup_printf (_("Please confirm that the following PIN matches the one displayed on “%s”."), device_name); break; case BLUETOOTH_PAIRING_MODE_YES_NO: gtk_widget_show (priv->done); gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->pin_notebook), MESSAGE_PAGE); title = g_strdup (_("Bluetooth Pairing Request")); help = g_strdup_printf (_("“%s” wants to pair with this device. Do you want to allow pairing?"), device_name); break; case BLUETOOTH_PAIRING_MODE_CONFIRM_AUTH: gtk_widget_show (priv->done); gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->pin_notebook), MESSAGE_PAGE); title = g_strdup (_("Confirm Bluetooth Connection")); help = g_strdup_printf (_("“%s” wants to connect with this device. Do you want to allow it?"), device_name); break; default: g_assert_not_reached (); } switch (mode) { case BLUETOOTH_PAIRING_MODE_PIN_DISPLAY_NORMAL: help = g_strdup_printf (_("Please enter the following PIN on “%s”."), device_name); break; case BLUETOOTH_PAIRING_MODE_PIN_DISPLAY_KEYBOARD: help = g_strdup_printf (_("Please enter the following PIN on “%s”. Then press “Return” on the keyboard."), device_name); break; case BLUETOOTH_PAIRING_MODE_PIN_DISPLAY_ICADE: help = g_strdup (_("Please move the joystick of your iCade in the following directions. Then press any of the white buttons.")); break; default: g_assert (help); } if (mode == BLUETOOTH_PAIRING_MODE_YES_NO || mode == BLUETOOTH_PAIRING_MODE_CONFIRM_AUTH) { gtk_button_set_label (GTK_BUTTON (priv->done), _("Allow")); context = gtk_widget_get_style_context (priv->done); gtk_style_context_remove_class (context, "suggested-action"); gtk_button_set_label (GTK_BUTTON (priv->cancel), _("Dismiss")); context = gtk_widget_get_style_context (priv->cancel); gtk_style_context_add_class (context, "destructive-action"); gtk_widget_hide (priv->pin_notebook); } else { gtk_button_set_label (GTK_BUTTON (priv->done), _("Confirm")); context = gtk_widget_get_style_context (priv->done); gtk_style_context_add_class (context, "suggested-action"); gtk_button_set_label (GTK_BUTTON (priv->cancel), _("Cancel")); context = gtk_widget_get_style_context (priv->cancel); gtk_style_context_remove_class (context, "destructive-action"); gtk_widget_show (priv->pin_notebook); } gtk_label_set_text (GTK_LABEL (priv->title), title); gtk_label_set_text (GTK_LABEL (priv->help_label), help); g_free (title); g_free (help); } BluetoothPairingMode bluetooth_pairing_dialog_get_mode (BluetoothPairingDialog *self) { BluetoothPairingDialogPrivate *priv = BLUETOOTH_PAIRING_DIALOG_GET_PRIVATE (self); return priv->mode; } char * bluetooth_pairing_dialog_get_pin (BluetoothPairingDialog *self) { BluetoothPairingDialogPrivate *priv = BLUETOOTH_PAIRING_DIALOG_GET_PRIVATE (self); g_assert (priv->mode == BLUETOOTH_PAIRING_MODE_PIN_CONFIRMATION || priv->mode == BLUETOOTH_PAIRING_MODE_PIN_QUERY); g_assert (gtk_widget_is_sensitive (GTK_WIDGET (priv->done))); return g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->entry_pin))); } void bluetooth_pairing_dialog_set_pin_entered (BluetoothPairingDialog *self, guint entered) { BluetoothPairingDialogPrivate *priv = BLUETOOTH_PAIRING_DIALOG_GET_PRIVATE (self); char *done; g_assert (priv->mode == BLUETOOTH_PAIRING_MODE_PIN_DISPLAY_KEYBOARD); g_assert (priv->pin); if (entered > 0) { gunichar invisible; GString *str; guint i; invisible = gtk_entry_get_invisible_char (GTK_ENTRY (priv->entry_pin)); str = g_string_new (NULL); for (i = 0; i < entered; i++) g_string_append_unichar (str, invisible); if (entered < strlen (priv->pin)) g_string_append (str, priv->pin + entered); done = g_string_free (str, FALSE); } else { done = g_strdup (priv->pin); } gtk_label_set_text (GTK_LABEL (priv->label_pin), done); g_free (done); } static void response_cb (GtkWidget *button, gpointer user_data) { BluetoothPairingDialogPrivate *priv = BLUETOOTH_PAIRING_DIALOG_GET_PRIVATE (user_data); int response; if (button == priv->done) response = GTK_RESPONSE_ACCEPT; else if (button == priv->cancel) response = GTK_RESPONSE_CANCEL; else g_assert_not_reached (); gtk_dialog_response (GTK_DIALOG (user_data), response); } static void text_changed_cb (GObject *gobject, GParamSpec *pspec, gpointer user_data) { BluetoothPairingDialogPrivate *priv = BLUETOOTH_PAIRING_DIALOG_GET_PRIVATE (user_data); const char *text; if (priv->mode != BLUETOOTH_PAIRING_MODE_PIN_CONFIRMATION && priv->mode != BLUETOOTH_PAIRING_MODE_PIN_QUERY) return; text = gtk_entry_get_text (GTK_ENTRY (priv->entry_pin)); if (!text || strlen (text) < 4) gtk_widget_set_sensitive (GTK_WIDGET (priv->done), FALSE); else gtk_widget_set_sensitive (GTK_WIDGET (priv->done), TRUE); } static void bluetooth_pairing_dialog_init (BluetoothPairingDialog *self) { GtkCssProvider *provider; gtk_widget_init_template (GTK_WIDGET (self)); gtk_widget_set_size_request (GTK_WIDGET (self), 380, -1); gtk_window_set_resizable (GTK_WINDOW (self), FALSE); provider = gtk_css_provider_new (); gtk_css_provider_load_from_resource (provider, "/org/gnome/bluetooth/bluetooth-settings.css"); gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER); g_object_unref (provider); } static void bluetooth_pairing_dialog_constructed (GObject *object) { BluetoothPairingDialog *self = BLUETOOTH_PAIRING_DIALOG (object); BluetoothPairingDialogPrivate *priv = BLUETOOTH_PAIRING_DIALOG_GET_PRIVATE (self); GtkWidget *header; GtkStyleContext *context; /* Header */ header = gtk_dialog_get_header_bar (GTK_DIALOG (self)); priv->title = gtk_label_new (""); gtk_header_bar_set_custom_title (GTK_HEADER_BAR (header), priv->title); gtk_widget_show (priv->title); gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (header), FALSE); /* OK button */ priv->done = gtk_button_new_with_label (_("Accept")); gtk_widget_set_no_show_all (priv->done, TRUE); gtk_widget_set_can_default (GTK_WIDGET (priv->done), TRUE); g_signal_connect (G_OBJECT (priv->done), "clicked", G_CALLBACK (response_cb), self); gtk_header_bar_pack_end (GTK_HEADER_BAR (header), priv->done); /* Spinner */ priv->spinner = gtk_spinner_new (); gtk_widget_set_margin_end (priv->spinner, 12); gtk_widget_set_no_show_all (priv->spinner, TRUE); gtk_header_bar_pack_end (GTK_HEADER_BAR (header), priv->spinner); g_object_bind_property (priv->spinner, "visible", priv->spinner, "active", 0); g_object_bind_property (priv->spinner, "visible", priv->done, "visible", G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN | G_BINDING_BIDIRECTIONAL); /* Cancel button */ priv->cancel = gtk_button_new_with_label (_("Cancel")); g_signal_connect (G_OBJECT (priv->cancel), "clicked", G_CALLBACK (response_cb), self); gtk_header_bar_pack_start (GTK_HEADER_BAR (header), priv->cancel); gtk_widget_show (priv->cancel); gtk_widget_grab_default (GTK_WIDGET (priv->done)); g_signal_connect (G_OBJECT (priv->entry_pin), "notify::text", G_CALLBACK (text_changed_cb), self); context = gtk_widget_get_style_context (priv->done); gtk_style_context_add_class (context, "suggested-action"); context = gtk_widget_get_style_context (priv->title); gtk_style_context_add_class (context, "title"); } static void bluetooth_pairing_dialog_finalize (GObject *object) { BluetoothPairingDialogPrivate *priv = BLUETOOTH_PAIRING_DIALOG_GET_PRIVATE (BLUETOOTH_PAIRING_DIALOG (object)); g_free (priv->pin); G_OBJECT_CLASS(bluetooth_pairing_dialog_parent_class)->finalize(object); } static void bluetooth_pairing_dialog_class_init (BluetoothPairingDialogClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); object_class->constructed = bluetooth_pairing_dialog_constructed; object_class->finalize = bluetooth_pairing_dialog_finalize; /* Bind class to template */ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/bluetooth/bluetooth-pairing-dialog.ui"); gtk_widget_class_bind_template_child_private (widget_class, BluetoothPairingDialog, help_label); gtk_widget_class_bind_template_child_private (widget_class, BluetoothPairingDialog, pin_notebook); gtk_widget_class_bind_template_child_private (widget_class, BluetoothPairingDialog, entry_pin); gtk_widget_class_bind_template_child_private (widget_class, BluetoothPairingDialog, label_pin); } /** * bluetooth_pairing_dialog_new: * * Returns a new #BluetoothPairingDialog widget. * * Return value: A #BluetoothPairingDialog widget **/ GtkWidget * bluetooth_pairing_dialog_new (void) { return g_object_new (BLUETOOTH_TYPE_PAIRING_DIALOG, "use-header-bar", 1, NULL); }