diff --git a/src/cardman.c b/src/cardman.c index 910a27c..6ac1099 100644 --- a/src/cardman.c +++ b/src/cardman.c @@ -1,1276 +1,1276 @@ /* cardman.c - The GNU Privacy Assistant: card manager. Copyright (C) 2008, 2009 g10 Code GmbH This file is part of GPA. GPA 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 3 of the License, or (at your option) any later version. GPA 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, see . */ /* The card manager window. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include "gpa.h" #include "gtktools.h" #include "gpawidgets.h" #include "siglist.h" #include "helpmenu.h" #include "icons.h" #include "cardman.h" #include "convert.h" #include "membuf.h" #include "gpagenkeycardop.h" #include "cm-object.h" #include "cm-openpgp.h" #include "cm-geldkarte.h" #include "cm-netkey.h" #include "cm-dinsig.h" #include "cm-unknown.h" /* Object's class definition. */ struct _GpaCardManagerClass { GtkWindowClass parent_class; }; /* Object definition. */ struct _GpaCardManager { GtkWindow parent_instance; GtkUIManager *ui_manager; GtkWidget *app_selector; /* Combo Box to select the application. */ GtkWidget *card_container; /* The container holding the card widget. */ GtkWidget *card_widget; /* The widget to display a card applciation. */ /* Labels in the status bar. */ GtkWidget *status_label; GtkWidget *status_text; const char *cardtypename; /* String with the card type's name. */ GType cardtype; /* Widget type of a supported card. */ gpa_filewatch_id_t watch; /* For watching the reader status file. */ int in_card_reload; /* Sentinel for card_reload. */ gpgme_ctx_t gpgagent; /* Gpgme context for the assuan connection with the gpg-agent. */ guint ticker_timeout_id; /* Source Id of the timeout ticker or 0. */ struct { int card_any; /* Any card event counter seen. */ unsigned int card; /* Last seen card event counter. */ } eventcounter; }; /* There is only one instance of the card manager class. Use a global variable to keep track of it. */ static GpaCardManager *this_instance; /* Local prototypes */ static void start_ticker (GpaCardManager *cardman); static void update_card_widget (GpaCardManager *cardman, const char *err_desc); static void gpa_card_manager_finalize (GObject *object); /************************************************************ ******************* Implementation ********************* ************************************************************/ /* Status bar handling. */ static GtkWidget * statusbar_new (GpaCardManager *cardman) { GtkWidget *hbox; GtkWidget *label; hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); label = gtk_label_new (NULL); cardman->status_label = label; gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); label = gtk_label_new (""); cardman->status_text = label; gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 5); gtk_widget_set_halign (GTK_WIDGET (hbox), 0); gtk_widget_set_valign (GTK_WIDGET (hbox), 1); return hbox; } static void statusbar_update (GpaCardManager *cardman, const char *text) { gtk_label_set_text (GTK_LABEL (cardman->status_text), text); } /* Signal handler for GpaCMObject's "update-status". */ static void statusbar_update_cb (GpaCardManager *cardman, gchar *status) { gtk_label_set_text (GTK_LABEL (cardman->status_text), status); } /* Signal handler for GpaCMObject's "alert-dialog". */ static void alert_dialog_cb (GpaCardManager *cardman, gchar *msg) { gpa_window_error (msg, GTK_WIDGET (cardman)); } static void update_title (GpaCardManager *cardman) { const char *title = _("Card Manager"); if (cardman->cardtype == G_TYPE_NONE) gpa_window_set_title (GTK_WINDOW (cardman), title); else { char *tmp; tmp = g_strdup_printf ("%s (%s)", title, cardman->cardtypename); gpa_window_set_title (GTK_WINDOW (cardman), tmp); xfree (tmp); } } static void update_info_visibility (GpaCardManager *cardman) { if (cardman->cardtype != G_TYPE_NONE) { char *tmp = g_strdup_printf (_("%s card detected."), cardman->cardtypename); statusbar_update (cardman, tmp); xfree (tmp); } else { statusbar_update (cardman, _("Checking for card...")); } } static gpg_error_t scd_data_cb (void *opaque, const void *data, size_t datalen) { /* g_debug ("DATA_CB: datalen=%d", (int)datalen); */ return 0; } static gpg_error_t scd_inq_cb (void *opaque, const char *name, const char *args, gpgme_data_t *r_data) { /* g_debug ("INQ_CB: name=`%s' args=`%s'", name, args); */ return 0; } static gpg_error_t scd_status_cb (void *opaque, const char *status, const char *args) { GpaCardManager *cardman = opaque; /* g_debug ("STATUS_CB: status=`%s' args=`%s'", status, args); */ if (!strcmp (status, "APPTYPE")) { cardman->cardtype = G_TYPE_NONE; if (!g_ascii_strcasecmp (args, "openpgp")) { cardman->cardtype = GPA_CM_OPENPGP_TYPE; cardman->cardtypename = "OpenPGP"; } else if (!g_ascii_strcasecmp (args, "nks")) { cardman->cardtype = GPA_CM_NETKEY_TYPE; cardman->cardtypename = "NetKey"; } else if (!g_ascii_strcasecmp (args, "dinsig")) { cardman->cardtype = GPA_CM_DINSIG_TYPE; cardman->cardtypename = "DINSIG"; } else if (!g_ascii_strcasecmp (args, "P15")) cardman->cardtypename = "PKCS#15"; else if (!g_ascii_strcasecmp (args, "geldkarte")) { cardman->cardtype = GPA_CM_GELDKARTE_TYPE; cardman->cardtypename = "Geldkarte"; } else if (!g_ascii_strcasecmp (args, "undefined")) { cardman->cardtype = GPA_CM_UNKNOWN_TYPE; cardman->cardtypename = "UNKNOWN"; } else cardman->cardtypename = "Unknown"; } else if ( !strcmp (status, "EVENTCOUNTER") ) { unsigned int count; if (sscanf (args, "%*u %*u %u ", &count) == 1) { cardman->eventcounter.card_any = 1; cardman->eventcounter.card = count; } } return 0; } /* Idle queue callback to mark a relaod operation finished. */ static gboolean card_reload_finish_idle_cb (void *user_data) { GpaCardManager *cardman = user_data; cardman->in_card_reload--; g_object_unref (cardman); return FALSE; /* Remove us from the idle queue. */ } /* This function is called to trigger a card-reload. */ static void card_reload (GpaCardManager *cardman) { gpg_error_t err, operr; const char *application; char *command_buf = NULL; const char *command; const char *err_desc = NULL; char *err_desc_buffer = NULL; int auto_app; if (!cardman->gpgagent) return; /* No support for GPGME_PROTOCOL_ASSUAN. */ /* Start the ticker if not yet done. */ start_ticker (cardman); if (!cardman->in_card_reload) { cardman->in_card_reload++; update_info_visibility (cardman); cardman->cardtype = G_TYPE_NONE; cardman->cardtypename = "Unknown"; /* The first thing we need to do is to issue the SERIALNO command; this makes sure that scdaemon initalizes the card if that has not yet been done. */ command = "SCD SERIALNO"; if (cardman->app_selector && (gtk_combo_box_get_active (GTK_COMBO_BOX (cardman->app_selector)) > 0) && (application = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (cardman->app_selector)))) { command_buf = g_strdup_printf ("%s %s", command, application); command = command_buf; auto_app = 0; } else auto_app = 1; err = gpgme_op_assuan_transact_ext (cardman->gpgagent, command, scd_data_cb, NULL, scd_inq_cb, NULL, scd_status_cb, cardman, &operr); if (!err) { err = operr; if (!auto_app && gpg_err_source (err) == GPG_ERR_SOURCE_SCD && gpg_err_code (err) == GPG_ERR_CONFLICT) { /* Not in auto select mode and the scdaemon told us about a conflicting use. We now do a restart and try again to display an application selection conflict error only if it is not due to our own connection to the scdaemon. */ if (!gpgme_op_assuan_transact_ext (cardman->gpgagent, "SCD RESTART", NULL, NULL, NULL, NULL, NULL, NULL, &operr) && !operr) { err = gpgme_op_assuan_transact_ext (cardman->gpgagent, command, scd_data_cb, NULL, scd_inq_cb, NULL, scd_status_cb, cardman, &operr); if (!err) err = operr; } } } if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT || gpg_err_code (err) == GPG_ERR_CARD_REMOVED) { err_desc = _("No card found."); } else if (gpg_err_source (err) == GPG_ERR_SOURCE_SCD && gpg_err_code (err) == GPG_ERR_CONFLICT) { err_desc = auto_app ? _("The selected card application is currently not available.") : _("Another process is using a different card application " "than the selected one.\n\n" "You may change the application selection mode to " "\"Auto\" to select the active application."); } else if (!auto_app && gpg_err_source (err) == GPG_ERR_SOURCE_SCD && gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED) { err_desc = _("The selected card application is not available."); } else if (err) { g_debug ("assuan command `%s' failed: %s <%s>\n", command, gpg_strerror (err), gpg_strsource (err)); if (!gpgme_op_assuan_transact_ext (cardman->gpgagent, "SCD SERIALNO undefined", NULL, NULL, NULL, NULL, NULL, NULL, &operr) && !operr) err = 0; else { err_desc = _("Error accessing the card."); statusbar_update (cardman, _("Error accessing card")); } } g_free (command_buf); if (!err) { /* Get the event counter to avoid a duplicate reload due to the ticker. */ gpgme_op_assuan_transact_ext (cardman->gpgagent, "GETEVENTCOUNTER", NULL, NULL, NULL, NULL, scd_status_cb, cardman, NULL); /* Now we need to get the APPTYPE of the card so that the correct GpaCM* object can can act on the data. */ command = "SCD GETATTR APPTYPE"; err = gpgme_op_assuan_transact_ext (cardman->gpgagent, command, scd_data_cb, NULL, scd_inq_cb, NULL, scd_status_cb, cardman, &operr); if (!err) err = operr; if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT || gpg_err_code (err) == GPG_ERR_CARD_REMOVED) statusbar_update (cardman, _("No card")); else if (err) { g_debug ("assuan command `%s' failed: %s <%s>\n", command, gpg_strerror (err), gpg_strsource (err)); statusbar_update (cardman, _("Error accessing card")); } } update_card_widget (cardman, err_desc); g_free (err_desc_buffer); err_desc_buffer = NULL; err_desc = NULL; update_title (cardman); update_info_visibility (cardman); /* We decrement our lock using a idle handler with lo priority. This gives us a better chance not to do a reload a second time on behalf of the file watcher or ticker. */ g_object_ref (cardman); g_idle_add_full (G_PRIORITY_LOW, card_reload_finish_idle_cb, cardman, NULL); } } /* This function is called when the user triggers a card-reload. */ static void card_reload_action (GSimpleAction *simple, GVariant *parameter, gpointer user_data) { // GpaCardManager *cardman = param; GpaCardManager *cardman = (GpaCardManager*)user_data; card_reload (cardman); } /* Idle queue callback to do a reload. */ static gboolean card_reload_idle_cb (void *user_data) { GpaCardManager *cardman = user_data; card_reload (cardman); g_object_unref (cardman); return FALSE; /* Remove us from the idle queue. */ } static gpg_error_t geteventcounter_status_cb (void *opaque, const char *status, const char *args) { GpaCardManager *cardman = opaque; if ( !strcmp (status, "EVENTCOUNTER") ) { unsigned int count; /* We don't check while we are already reloading a card. The last action of the card reload code will also update the event counter. */ if (!cardman->in_card_reload && sscanf (args, "%*u %*u %u ", &count) == 1) { if (cardman->eventcounter.card_any && cardman->eventcounter.card != count) { /* Actually we should not do a reload based only on the eventcounter but check the actual card status first. However simply triggering a reload is not different from the user hitting the reload button. */ g_object_ref (cardman); g_idle_add (card_reload_idle_cb, cardman); } cardman->eventcounter.card_any = 1; cardman->eventcounter.card = count; } } return 0; } /* This function is called by the timeout ticker started by start_ticker. It is used to poll scdaemon to detect a card status change. */ static gboolean ticker_cb (gpointer user_data) { GpaCardManager *cardman = user_data; if (!cardman || !cardman->ticker_timeout_id || !cardman->gpgagent || cardman->in_card_reload) return TRUE; /* Keep on ticking. */ /* Note that we are single threaded and thus there is no need to lock the assuan context. */ gpgme_op_assuan_transact_ext (cardman->gpgagent, "GETEVENTCOUNTER", NULL, NULL, NULL, NULL, geteventcounter_status_cb, cardman, NULL); return TRUE; /* Keep on ticking. */ } /* If no ticker is active start one. */ static void start_ticker (GpaCardManager *cardman) { if (disable_ticker) return; if (!cardman->ticker_timeout_id) { #if GTK_CHECK_VERSION (2, 14, 0) cardman->ticker_timeout_id = g_timeout_add_seconds (1, ticker_cb, cardman); #else cardman->ticker_timeout_id = g_timeout_add (1000, ticker_cb, cardman); #endif } } /* Signal handler for the completed signal of the key generation. */ static void card_genkey_completed (GpaCardManager *cardman, gpg_error_t err) { g_object_ref (cardman); g_idle_add (card_reload_idle_cb, cardman); } /* This function is called to triggers a key-generation. */ static void card_genkey (GpaCardManager *cardman) { gpg_error_t err, operr; GpaGenKeyCardOperation *op; char *keyattr; if (cardman->cardtype != GPA_CM_OPENPGP_TYPE) return; /* Not possible. */ if (!cardman->gpgagent) { g_debug ("Ooops: no assuan context"); return; } /* Note: This test works only with GnuPG > 2.0.10 but that version is anyway required for the card manager to work correctly. */ err = gpgme_op_assuan_transact_ext (cardman->gpgagent, "SCD GETINFO deny_admin", NULL, NULL, NULL, NULL, NULL, NULL, &operr); if (!err) err = operr; if (!err) { gpa_window_error ("Admin commands are disabled in scdamon.\n" "Key generation is not possible.", NULL); return; } keyattr = (cardman->card_widget ? gpa_cm_openpgp_get_key_attributes (cardman->card_widget) : NULL); op = gpa_gen_key_card_operation_new (GTK_WIDGET (cardman), keyattr); g_signal_connect_swapped (G_OBJECT (op), "completed", G_CALLBACK (card_genkey_completed), cardman); g_signal_connect (G_OBJECT (op), "completed", G_CALLBACK (g_object_unref), NULL); } /* This function is called when the user triggers a key-generation. */ static void card_genkey_action (GSimpleAction *simple, GVariant *parameter, gpointer user_data) { // GpaCardManager *cardman = param; GpaCardManager *cardman = this_instance; card_genkey (cardman); } static void watcher_cb (void *opaque, const char *filename, const char *reason) { GpaCardManager *cardman = opaque; if (cardman && strchr (reason, 'w') && !cardman->in_card_reload) { card_reload (cardman); } } /* Handle menu item "File/Close". */ static void file_close (GSimpleAction *simple, GVariant *parameter, gpointer param) { // GpaCardManager *cardman = this:ubsta; gtk_widget_destroy (GTK_WIDGET (this_instance)); } /* Handle menu item "File/Close". */ static void file_quit (GSimpleAction *simple, GVariant *parameter, gpointer param) { // GpaCardManager *cardman = this:ubsta; // gtk_widget_destroy (GTK_WIDGET (this_instance)); g_application_quit (G_APPLICATION (get_gpa_application ())); } /* Construct the card manager menu and toolbar widgets and return them. */ static void cardman_action_new (GpaCardManager *cardman, GtkWidget **menu_bar, GtkWidget **toolbar) { static const GActionEntry entries[] = { // Toplevel { "File", NULL }, { "Edit", NULL }, { "Card", NULL }, // File menu { "file_close", file_close }, { "file_quit", file_quit }, { "card_reload", card_reload_action }, { "card_genkey", card_genkey_action }, }; /* GtkAccelGroup *accel_group; GtkActionGroup *action_group; GtkAction *action; GError *error; action_group = gtk_action_group_new ("MenuActions"); gtk_action_group_set_translation_domain (action_group, PACKAGE); gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), cardman); gtk_action_group_add_actions (action_group, gpa_help_menu_action_entries, G_N_ELEMENTS (gpa_help_menu_action_entries), cardman); gtk_action_group_add_actions (action_group, gpa_windows_menu_action_entries, G_N_ELEMENTS (gpa_windows_menu_action_entries), cardman); gtk_action_group_add_actions (action_group, gpa_preferences_menu_action_entries, G_N_ELEMENTS (gpa_preferences_menu_action_entries), cardman); cardman->ui_manager = gtk_ui_manager_new (); gtk_ui_manager_insert_action_group (cardman->ui_manager, action_group, 0); accel_group = gtk_ui_manager_get_accel_group (cardman->ui_manager); gtk_window_add_accel_group (GTK_WINDOW (cardman), accel_group); if (! gtk_ui_manager_add_ui_from_string (cardman->ui_manager, ui_description, -1, &error)) { g_message ("building cardman menus failed: %s", error->message); g_error_free (error); exit (EXIT_FAILURE); } // Fixup the icon theme labels which are too long for the toolbar. action = gtk_action_group_get_action (action_group, "WindowsKeyringEditor"); g_object_set (action, "short_label", _("Keyring"), NULL); action = gtk_action_group_get_action (action_group, "WindowsFileManager"); g_object_set (action, "short_label", _("Files"), NULL); */ /* File Close Quit Edit Preferences Backend Preferences Card Refresh Generate Key Windows Keyring Manager File Manager Clipboard Card Manager Help About */ static const char *menu_string = "" "" "" "" "_File" "" "Close" "app.file_close" "" "" "Quit" "app.file_quit" "" "" "" "Edit" "" "Preferences" "app.edit_preferences" "" "" "Backend Preferences" "app.edit_backend_preferences" "" "" "" "Card" "" "Refresh" "app.card_reload" "" "" "Generate Key" "app.card_genkey" "" "" "" "Windows" "" "Keyring Manager" "app.windows_keyring_editor" "" "" "File Manager" "app.windows_file_manager" "" "" "Clipboard" "app.windows_clipboard" "" "" "Card Manager" "app.windows_card_manager" "" "" "" "Help" "" "About" "app.help_about" "" "" "" "" "True" "False" "False" "" "" "True" "False" "True" "view-refresh" "" "" "False" "True" "" "" "" "" "" "" "" "" "True" "False" "True" "preferences-desktop" "" "" "False" "True" "" "" "" "" "" "" "" "" "True" "False" "True" "keyring" "app.windows_keyring_editor" "" "" "False" "True" "" "" "" "" "True" "False" "True" "folder" "app.windows_file_manager" "" "" "False" "True" "" "" "" "" "True" "False" "True" "edit-paste" "app.windows_clipboard" "" "" "False" "True" "" "" "" ""; GtkBuilder *gtk_builder = gtk_builder_new_from_string (menu_string, -1); GMenuModel *menu_bar_model = G_MENU_MODEL (gtk_builder_get_object (GTK_BUILDER (gtk_builder), "menu")); *menu_bar = gtk_menu_bar_new_from_model (menu_bar_model); // GObject *grid = gtk_builder_get_object (GTK_BUILDER (gtk_builder), "toolbar"); *toolbar = gtk_builder_get_object (GTK_BUILDER (gtk_builder), "toolbar"); GtkApplication *gpa_app = get_gpa_application (); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), gpa_windows_menu_g_action_entries, G_N_ELEMENTS (gpa_windows_menu_g_action_entries), cardman); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), entries, G_N_ELEMENTS (entries), cardman); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), gpa_help_menu_g_action_entries, G_N_ELEMENTS (gpa_help_menu_g_action_entries), cardman); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), gpa_preferences_menu_g_action_entries, G_N_ELEMENTS (gpa_preferences_menu_g_action_entries), cardman); } /* Callback for the destroy signal. */ static void card_manager_closed (GtkWidget *widget, gpointer param) { this_instance = NULL; } static void update_card_widget (GpaCardManager *cardman, const char *error_description) { if (cardman->card_widget) { gtk_widget_destroy (cardman->card_widget); cardman->card_widget = NULL; } if (cardman->cardtype == GPA_CM_OPENPGP_TYPE) { cardman->card_widget = gpa_cm_openpgp_new (); } else if (cardman->cardtype == GPA_CM_GELDKARTE_TYPE) { cardman->card_widget = gpa_cm_geldkarte_new (); } else if (cardman->cardtype == GPA_CM_NETKEY_TYPE) { cardman->card_widget = gpa_cm_netkey_new (); } else if (cardman->cardtype == GPA_CM_DINSIG_TYPE) { cardman->card_widget = gpa_cm_dinsig_new (); } else if (cardman->cardtype == GPA_CM_UNKNOWN_TYPE) { cardman->card_widget = gpa_cm_unknown_new (); } else { if (!error_description) error_description = _("This card application is not yet supported."); cardman->card_widget = gtk_label_new (error_description); } gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (cardman->card_container), cardman->card_widget); gtk_widget_show_all (cardman->card_widget); /* We need to do the reload after a show_all so that a card application may hide parts of its window. */ if (GPA_IS_CM_OBJECT (cardman->card_widget)) { g_signal_connect_swapped (G_OBJECT (cardman->card_widget), "update-status", G_CALLBACK (statusbar_update_cb), cardman); g_signal_connect_swapped (G_OBJECT (cardman->card_widget), "alert-dialog", G_CALLBACK (alert_dialog_cb), cardman); /* Fixme: We should use a signal to reload the card widget instead of using a class test in each reload fucntion. */ gpa_cm_openpgp_reload (cardman->card_widget, cardman->gpgagent); gpa_cm_geldkarte_reload (cardman->card_widget, cardman->gpgagent); gpa_cm_netkey_reload (cardman->card_widget, cardman->gpgagent); gpa_cm_dinsig_reload (cardman->card_widget, cardman->gpgagent); gpa_cm_unknown_reload (cardman->card_widget, cardman->gpgagent); } } /* Handler for the "changed" signal of the application selector. */ static void app_selector_changed_cb (GtkComboBox *cbox, void *opaque) { GpaCardManager *cardman = opaque; g_object_ref (cardman); g_idle_add (card_reload_idle_cb, cardman); } /* Assuan data callback used by setup_app_selector. */ static gpg_error_t setup_app_selector_data_cb (void *opaque, const void *data, size_t datalen) { membuf_t *mb = opaque; put_membuf (mb, data, datalen); return 0; } /* Fill the app_selection box with the available applications. */ static void setup_app_selector (GpaCardManager *cardman) { gpg_error_t err, operr; membuf_t mb; char *string; char *p, *p0, *p1; if (!cardman->gpgagent || !cardman->app_selector) return; init_membuf (&mb, 256); err = gpgme_op_assuan_transact_ext (cardman->gpgagent, "SCD GETINFO app_list", setup_app_selector_data_cb, &mb, NULL, NULL, NULL, NULL, &operr); if (err || operr) { g_free (get_membuf (&mb, NULL)); return; } /* Make sure the data is a string and get it. */ put_membuf (&mb, "", 1); string = get_membuf (&mb, NULL); if (!string) return; /* Out of core. */ for (p=p0=string; *p; p++) { if (*p == '\n') { *p = 0; p1 = strchr (p0, ':'); if (p1) *p1 = 0; gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (cardman->app_selector), NULL, p0); if (p[1]) p0 = p+1; } } g_free (string); } static void construct_widgets (GpaCardManager *cardman) { GtkWidget *vbox; GtkWidget *hbox, *hbox1, *hbox2; GtkWidget *label; GtkWidget *icon; gchar *markup; GtkWidget *menubar; GtkWidget *toolbar; GtkWidget *statusbar; /* Set a default size for the main window. */ gtk_window_set_default_size (GTK_WINDOW (cardman), 680, 480); /* Realize the window so that we can create pixmaps without warnings. */ gtk_widget_realize (GTK_WIDGET (cardman)); /* Use a vbox to show the menu, toolbar and the file container. */ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); /* Get the menu and the toolbar. */ cardman_action_new (cardman, &menubar, &toolbar); gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, TRUE, 0); /* Add a fancy label that tells us: This is the card manager. */ hbox1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); GtkBuilder *gtk_builder = gtk_builder_new_from_string (icons_string, -1); icon = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (gtk_builder), "smartcard")); gtk_box_pack_start (GTK_BOX (hbox1), icon, FALSE, TRUE, 0); label = gtk_label_new (NULL); markup = g_strdup_printf ("%s", _("Card Manager")); gtk_label_set_markup (GTK_LABEL (label), markup); g_free (markup); gtk_box_pack_start (GTK_BOX (hbox1), label, TRUE, TRUE, 10); - gtk_widget_set_halign (GTK_WIDGET (label), 0); - gtk_widget_set_valign (GTK_WIDGET (label), 0.5); + gtk_widget_set_halign (GTK_WIDGET (label), GTK_ALIGN_START); + gtk_widget_set_valign (GTK_WIDGET (label), GTK_ALIGN_CENTER); /* Add a application selection box. */ hbox2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); label = gtk_label_new (_("Application selection:")); gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, TRUE, 5); cardman->app_selector = gtk_combo_box_text_new (); gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (cardman->app_selector), NULL, _("Auto")); gtk_combo_box_set_active (GTK_COMBO_BOX (cardman->app_selector), 0); gtk_box_pack_start (GTK_BOX (hbox2), cardman->app_selector, FALSE, TRUE, 0); /* Put Card Manager label and application selector into the same line. */ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_pack_start (GTK_BOX (hbox), hbox1, FALSE, FALSE, 0); gtk_box_pack_end (GTK_BOX (hbox), hbox2, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5); /* Create a container (a scolled window) which will take the actual card widget. This container is required so that we can easily change to a differet card widget. */ cardman->card_container = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (cardman->card_container), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start (GTK_BOX (vbox), cardman->card_container, TRUE, TRUE, 0); /* Update the container using the current card application. */ update_card_widget (cardman, NULL); statusbar = statusbar_new (cardman); gtk_box_pack_start (GTK_BOX (vbox), statusbar, FALSE, FALSE, 0); gtk_container_add (GTK_CONTAINER (cardman), vbox); } /************************************************************ ****************** Object Management ******************** ************************************************************/ static void gpa_card_manager_class_init (void *class_ptr, void *class_data) { GpaCardManagerClass *klass = class_ptr; G_OBJECT_CLASS (klass)->finalize = gpa_card_manager_finalize; } static void gpa_card_manager_init (GTypeInstance *instance, void *class_ptr) { GpaCardManager *cardman = GPA_CARD_MANAGER (instance); gpg_error_t err; char *fname; cardman->cardtype = G_TYPE_NONE; cardman->cardtypename = "Unknown"; update_title (cardman); construct_widgets (cardman); g_signal_connect (cardman, "destroy", G_CALLBACK (card_manager_closed), cardman); /* We use the file watcher to speed up card change detection. If it does not work (i.e. on non Linux based systems) the ticker takes care of it. */ fname = g_build_filename (gnupg_homedir, "reader_0.status", NULL); cardman->watch = gpa_add_filewatch (fname, "w", watcher_cb, cardman); xfree (fname); err = gpgme_new (&cardman->gpgagent); if (err) gpa_gpgme_error (err); err = gpgme_set_protocol (cardman->gpgagent, GPGME_PROTOCOL_ASSUAN); if (err) { if (gpg_err_code (err) == GPG_ERR_INV_VALUE) gpa_window_error (_("The GPGME library is too old to " "support smartcards."), NULL); else gpa_gpgme_warning (err); gpgme_release (cardman->gpgagent); cardman->gpgagent = NULL; } setup_app_selector (cardman); if (cardman->app_selector) g_signal_connect (cardman->app_selector, "changed", G_CALLBACK (app_selector_changed_cb), cardman); } static void gpa_card_manager_finalize (GObject *object) { GpaCardManager *cardman = GPA_CARD_MANAGER (object); if (cardman->gpgagent) { gpgme_release (cardman->gpgagent); cardman->gpgagent = NULL; } if (cardman->ticker_timeout_id) { g_source_remove (cardman->ticker_timeout_id); cardman->ticker_timeout_id = 0; } /* FIXME: Remove the watch object and all other resources. */ G_OBJECT_CLASS (g_type_class_peek_parent (GPA_CM_OPENPGP_GET_CLASS (cardman)))->finalize (object); } GType gpa_card_manager_get_type (void) { static GType this_type = 0; if (!this_type) { static const GTypeInfo this_info = { sizeof (GpaCardManagerClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, gpa_card_manager_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GpaCardManager), 0, /* n_preallocs */ gpa_card_manager_init, }; this_type = g_type_register_static (GTK_TYPE_WINDOW, "GpaCardManager", &this_info, 0); } return this_type; } /************************************************************ ********************** Public API ************************ ************************************************************/ GtkWidget * gpa_card_manager_get_instance (void) { if (!this_instance) { this_instance = g_object_new (GPA_CARD_MANAGER_TYPE, NULL); card_reload (this_instance); } return GTK_WIDGET (this_instance); } gboolean gpa_card_manager_is_open (void) { return !!this_instance; } diff --git a/src/clipboard.c b/src/clipboard.c index 1e39393..7cf0569 100644 --- a/src/clipboard.c +++ b/src/clipboard.c @@ -1,1539 +1,1539 @@ /* clipboard.c - The GNU Privacy Assistant Copyright (C) 2000, 2001 G-N-U GmbH. Copyright (C) 2007, 2008 g10 Code GmbH This file is part of GPA. GPA 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 3 of the License, or (at your option) any later version. GPA 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, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include "gpa.h" #include "gtktools.h" #include "gpgmetools.h" #include "gpawidgets.h" #include "siglist.h" #include "helpmenu.h" #include "icons.h" #include "clipboard.h" #include "gpafiledecryptop.h" #include "gpafileencryptop.h" #include "gpafilesignop.h" #include "gpafileverifyop.h" #include "icons.h" #include "helpmenu.h" /* Support for Gtk 2.8. */ #if ! GTK_CHECK_VERSION (2, 10, 0) #define gtk_text_buffer_get_has_selection(textbuf) \ gtk_text_buffer_get_selection_bounds (textbuf, NULL, NULL); #define gdk_atom_intern_static_string(str) gdk_atom_intern (str, FALSE) #define GTK_STOCK_SELECT_ALL "gtk-select-all" #define MY_GTK_TEXT_BUFFER_NO_HAS_SELECTION #endif /* FIXME: Move to a global file. */ #ifndef DIM #define DIM(array) (sizeof (array) / sizeof (*array)) #endif /* Object and class definition. */ struct _GpaClipboard { GtkWindow parent; GtkWidget *text_view; GtkTextBuffer *text_buffer; /* List of sensitive widgets. See below */ GList *selection_sensitive_actions; GList *paste_sensitive_actions; gboolean paste_p; }; struct _GpaClipboardClass { GtkWindowClass parent_class; }; /* There is only one instance of the clipboard class. Use a global variable to keep track of it. */ static GpaClipboard *instance; /* We also need to save the parent class. */ static GObjectClass *parent_class; /* Local prototypes */ static GObject *gpa_clipboard_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties); /* GtkWidget boilerplate. */ static void gpa_clipboard_finalize (GObject *object) { G_OBJECT_CLASS (parent_class)->finalize (object); } static void gpa_clipboard_init (GpaClipboard *clipboard) { clipboard->selection_sensitive_actions = NULL; clipboard->paste_sensitive_actions = NULL; } static void gpa_clipboard_class_init (GpaClipboardClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->constructor = gpa_clipboard_constructor; object_class->finalize = gpa_clipboard_finalize; } GType gpa_clipboard_get_type (void) { static GType clipboard_type = 0; if (!clipboard_type) { static const GTypeInfo clipboard_info = { sizeof (GpaClipboardClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) gpa_clipboard_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GpaClipboard), 0, /* n_preallocs */ (GInstanceInitFunc) gpa_clipboard_init, }; clipboard_type = g_type_register_static (GTK_TYPE_WINDOW, "GpaClipboard", &clipboard_info, 0); } return clipboard_type; } /* Definition of the sensitivity function type. */ typedef gboolean (*sensitivity_func_t)(gpointer); /* Add widget to the list of sensitive widgets of editor. */ static void add_selection_sensitive_action (GpaClipboard *clipboard, GSimpleAction *action) { clipboard->selection_sensitive_actions = g_list_append (clipboard->selection_sensitive_actions, action); } /* Return true if a selection is active. */ static gboolean has_selection (gpointer param) { GpaClipboard *clipboard = param; return gtk_text_buffer_get_has_selection (clipboard->text_buffer); } /* Update the sensitivity of the widget DATA and pass PARAM through to the sensitivity callback. Usable as an iterator function in g_list_foreach. */ static void update_selection_sensitive_action (gpointer data, gpointer param) { // gtk_action_set_sensitive (GTK_ACTION (data), has_selection (param)); g_simple_action_set_enabled (G_SIMPLE_ACTION (data), has_selection (param)); } /* Call update_selection_sensitive_widget for all widgets in the list of sensitive widgets and pass CLIPBOARD through as the user data parameter. */ static void update_selection_sensitive_actions (GpaClipboard *clipboard) { g_list_foreach (clipboard->selection_sensitive_actions, update_selection_sensitive_action, (gpointer) clipboard); } /* Add ACTION to the list of sensitive actions of CLIPBOARD. */ static void add_paste_sensitive_action (GpaClipboard *clipboard, GSimpleAction *action) { clipboard->paste_sensitive_actions = g_list_append (clipboard->paste_sensitive_actions, action); } static void update_paste_sensitive_action (gpointer data, gpointer param) { GpaClipboard *clipboard = param; // gtk_action_set_sensitive (GTK_ACTION (data), clipboard->paste_p); g_simple_action_set_enabled (G_SIMPLE_ACTION (data), clipboard->paste_p); } static void update_paste_sensitive_actions (GtkClipboard *clip, GtkSelectionData *selection_data, GpaClipboard *clipboard) { clipboard->paste_p = gtk_selection_data_targets_include_text (selection_data); g_list_foreach (clipboard->paste_sensitive_actions, update_paste_sensitive_action, (gpointer) clipboard); } static void set_paste_sensitivity (GpaClipboard *clipboard, GtkClipboard *clip) { GdkDisplay *display; display = gtk_clipboard_get_display (clip); if (gdk_display_supports_selection_notification (display)) gtk_clipboard_request_contents (clip, gdk_atom_intern_static_string ("TARGETS"), (GtkClipboardReceivedFunc) update_paste_sensitive_actions, clipboard); } static void clipboard_owner_change_cb (GtkClipboard *clip, GdkEventOwnerChange *event, GpaClipboard *clipboard) { set_paste_sensitivity (clipboard, clip); } /* Add a file created by an operation to the list */ static void file_created_cb (GpaFileOperation *op, gpa_file_item_t item, gpointer data) { GpaClipboard *clipboard = data; gboolean suc; const gchar *end; suc = g_utf8_validate (item->direct_out, item->direct_out_len, &end); if (! suc) { gchar *str; gsize len; str = g_strdup_printf ("No valid UTF-8 encoding at position %i.\n" "Assuming Latin-1 encoding instead.", ((int) (end - item->direct_out))); gpa_window_message (str, GTK_WIDGET (clipboard)); g_free (str); str = g_convert (item->direct_out, item->direct_out_len, "UTF-8", "ISO-8859-1", NULL, &len, NULL); if (str) { gtk_text_buffer_set_text (clipboard->text_buffer, str, len); g_free (str); return; } gpa_window_error ("Error converting Latin-1 to UTF-8", GTK_WIDGET (clipboard)); /* Enough warnings: Try to show even with invalid encoding. */ } gtk_text_buffer_set_text (clipboard->text_buffer, item->direct_out, item->direct_out_len); } /* Do whatever is required with a file operation, to ensure proper clean up */ static void register_operation (GpaClipboard *clipboard, GpaFileOperation *op) { g_signal_connect (G_OBJECT (op), "created_file", G_CALLBACK (file_created_cb), clipboard); g_signal_connect (G_OBJECT (op), "completed", G_CALLBACK (g_object_unref), NULL); } /* Actions as called by the menu items. */ /* Handle menu item "File/Clear". */ static void file_clear (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaClipboard *clipboard = param; gtk_text_buffer_set_text (clipboard->text_buffer, "", -1); } /* The directory last visited by load or save operations. */ static gchar *last_directory; static gchar * get_load_file_name (GtkWidget *parent, const gchar *title) { static GtkWidget *dialog; GtkResponseType response; gchar *filename = NULL; if (! dialog) { dialog = gtk_file_chooser_dialog_new (title, GTK_WINDOW (parent), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); } if (last_directory) gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), last_directory); gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (dialog)); /* Run the dialog until there is a valid response. */ response = gtk_dialog_run (GTK_DIALOG (dialog)); if (response == GTK_RESPONSE_OK) { filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); if (filename) filename = g_strdup (filename); } if (last_directory) g_free (last_directory); last_directory = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog)); if (last_directory) last_directory = g_strdup (last_directory); gtk_widget_hide (dialog); return filename; } /* Handle menu item "File/Open". */ static void file_open (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaClipboard *clipboard = param; gchar *filename; struct stat buf; int res; gboolean suc; gchar *contents; gsize length; GError *err = NULL; const gchar *end; filename = get_load_file_name (GTK_WIDGET (clipboard), _("Open File")); if (! filename) return; res = g_stat (filename, &buf); if (res < 0) { gchar *str; str = g_strdup_printf ("Error determining size of file %s:\n%s", filename, strerror (errno)); gpa_window_error (str, GTK_WIDGET (clipboard)); g_free (str); g_free (filename); return; } #define MAX_CLIPBOARD_SIZE (2*1024*1024) if (buf.st_size > MAX_CLIPBOARD_SIZE) { GtkWidget *window; GtkWidget *hbox; GtkWidget *labelMessage; GtkWidget *pixmap; gint result; gchar *str; window = gtk_dialog_new_with_buttons (_("GPA Message"), GTK_WINDOW (clipboard), GTK_DIALOG_MODAL, _("_Open"), GTK_RESPONSE_OK, _("_Cancel"), GTK_RESPONSE_CANCEL, NULL); gtk_container_set_border_width (GTK_CONTAINER (window), 5); gtk_dialog_set_default_response (GTK_DIALOG (window), GTK_RESPONSE_CANCEL); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); //gtk_box_pack_start_defaults (GTK_BOX (GTK_DIALOG (window)->vbox), hbox); GtkWidget *box = gtk_dialog_get_content_area (GTK_DIALOG (window)); gtk_box_pack_start( GTK_BOX (box), hbox, TRUE, TRUE, 0); pixmap = gtk_image_new_from_icon_name ("dialog-information", GTK_ICON_SIZE_DIALOG); gtk_box_pack_start (GTK_BOX (hbox), pixmap, TRUE, FALSE, 10); /* TRANSLATORS: The arguments are the filename, the integer size and the unit (such as KB or MB). */ str = g_strdup_printf (_("The file %s is %llu%s large. Do you really " " want to open it?"), filename, (unsigned long long)buf.st_size / 1024 / 1024, "MB"); labelMessage = gtk_label_new (str); g_free (str); gtk_label_set_line_wrap (GTK_LABEL (labelMessage), TRUE); gtk_box_pack_start (GTK_BOX (hbox), labelMessage, TRUE, FALSE, 10); gtk_widget_show_all (window); result = gtk_dialog_run (GTK_DIALOG (window)); gtk_widget_destroy (window); if (result != GTK_RESPONSE_OK) { g_free (filename); return; } } suc = g_file_get_contents (filename, &contents, &length, &err); if (! suc) { gchar *str; str = g_strdup_printf ("Error loading content of file %s:\n%s", filename, err->message); gpa_window_error (str, GTK_WIDGET (clipboard)); g_free (str); g_error_free (err); g_free (filename); return; } suc = g_utf8_validate (contents, length, &end); if (! suc) { gchar *str; str = g_strdup_printf ("Error opening file %s:\n" "No valid UTF-8 at position %i.", filename, ((int) (end - contents))); gpa_window_error (str, GTK_WIDGET (clipboard)); g_free (str); g_free (contents); g_free (filename); return; } gtk_text_buffer_set_text (clipboard->text_buffer, contents, length); g_free (contents); } /* Run the modal file selection dialog and return a new copy of the filename if the user pressed OK and NULL otherwise. */ static gchar * get_save_file_name (GtkWidget *parent, const gchar *title) { static GtkWidget *dialog; GtkResponseType response; gchar *filename = NULL; if (! dialog) { dialog = gtk_file_chooser_dialog_new (title, GTK_WINDOW (parent), GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE); } if (last_directory) gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), last_directory); gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (dialog)); /* Run the dialog until there is a valid response. */ response = gtk_dialog_run (GTK_DIALOG (dialog)); if (response == GTK_RESPONSE_OK) { filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); if (filename) filename = g_strdup (filename); } if (last_directory) g_free (last_directory); last_directory = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog)); if (last_directory) last_directory = g_strdup (last_directory); if (last_directory) g_free (last_directory); last_directory = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog)); if (last_directory) last_directory = g_strdup (last_directory); gtk_widget_hide (dialog); return filename; } /* Handle menu item "File/Save As...". */ static void file_save_as (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaClipboard *clipboard = param; gchar *filename; GError *err = NULL; gchar *contents; gssize length; gboolean suc; GtkTextIter begin; GtkTextIter end; filename = get_save_file_name (GTK_WIDGET (clipboard), _("Save As...")); if (! filename) return; gtk_text_buffer_get_bounds (clipboard->text_buffer, &begin, &end); contents = gtk_text_buffer_get_text (clipboard->text_buffer, &begin, &end, FALSE); length = strlen (contents); suc = g_file_set_contents (filename, contents, length, &err); g_free (contents); if (! suc) { gchar *str; str = g_strdup_printf ("Error saving content to file %s:\n%s", filename, err->message); gpa_window_error (str, GTK_WIDGET (clipboard)); g_free (str); g_error_free (err); g_free (filename); return; } g_free (filename); } /* Handle menu item "File/Verify". */ static void file_verify (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaClipboard *clipboard = (GpaClipboard *) param; GpaFileVerifyOperation *op; GList *files = NULL; gpa_file_item_t file_item; GtkTextIter begin; GtkTextIter end; gtk_text_buffer_get_bounds (clipboard->text_buffer, &begin, &end); file_item = g_malloc0 (sizeof (*file_item)); file_item->direct_name = g_strdup (_("Clipboard")); file_item->direct_in = gtk_text_buffer_get_slice (clipboard->text_buffer, &begin, &end, TRUE); /* FIXME: One would think there exists a function to get the number of bytes between two GtkTextIter, but no, that's too obvious. */ file_item->direct_in_len = strlen (file_item->direct_in); files = g_list_append (files, file_item); /* Start the operation. */ op = gpa_file_verify_operation_new (GTK_WIDGET (clipboard), files); register_operation (clipboard, GPA_FILE_OPERATION (op)); } /* Handle menu item "File/Sign". */ static void file_sign (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaClipboard *clipboard = (GpaClipboard *) param; GpaFileSignOperation *op; GList *files = NULL; gpa_file_item_t file_item; GtkTextIter begin; GtkTextIter end; gtk_text_buffer_get_bounds (clipboard->text_buffer, &begin, &end); file_item = g_malloc0 (sizeof (*file_item)); file_item->direct_name = g_strdup (_("Clipboard")); file_item->direct_in = gtk_text_buffer_get_text (clipboard->text_buffer, &begin, &end, FALSE); /* FIXME: One would think there exists a function to get the number of bytes between two GtkTextIter, but no, that's too obvious. */ file_item->direct_in_len = strlen (file_item->direct_in); files = g_list_append (files, file_item); /* Start the operation. */ op = gpa_file_sign_operation_new (GTK_WIDGET (clipboard), files, TRUE); register_operation (clipboard, GPA_FILE_OPERATION (op)); } /* Handle menu item "File/Encrypt". */ static void file_encrypt (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaClipboard *clipboard = (GpaClipboard *) param; GpaFileEncryptOperation *op; GList *files = NULL; gpa_file_item_t file_item; GtkTextIter begin; GtkTextIter end; gtk_text_buffer_get_bounds (clipboard->text_buffer, &begin, &end); file_item = g_malloc0 (sizeof (*file_item)); file_item->direct_name = g_strdup (_("Clipboard")); file_item->direct_in = gtk_text_buffer_get_text (clipboard->text_buffer, &begin, &end, FALSE); /* FIXME: One would think there exists a function to get the number of bytes between two GtkTextIter, but no, that's too obvious. */ file_item->direct_in_len = strlen (file_item->direct_in); files = g_list_append (files, file_item); /* Start the operation. */ op = gpa_file_encrypt_operation_new (GTK_WIDGET (clipboard), files, TRUE); register_operation (clipboard, GPA_FILE_OPERATION (op)); } /* Handle menu item "File/Decrypt". */ static void file_decrypt (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaClipboard *clipboard = (GpaClipboard *) param; GpaFileDecryptOperation *op; GList *files = NULL; gpa_file_item_t file_item; GtkTextIter begin; GtkTextIter end; gtk_text_buffer_get_bounds (clipboard->text_buffer, &begin, &end); file_item = g_malloc0 (sizeof (*file_item)); file_item->direct_name = g_strdup (_("Clipboard")); file_item->direct_in = gtk_text_buffer_get_text (clipboard->text_buffer, &begin, &end, FALSE); /* FIXME: One would think there exists a function to get the number of bytes between two GtkTextIter, but no, that's too obvious. */ file_item->direct_in_len = strlen (file_item->direct_in); files = g_list_append (files, file_item); /* Start the operation. */ op = gpa_file_decrypt_operation_new (GTK_WIDGET (clipboard), files); register_operation (clipboard, GPA_FILE_OPERATION (op)); } /* Handle menu item "File/Close". */ static void file_close (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaClipboard *clipboard = param; gtk_widget_destroy (GTK_WIDGET (clipboard)); } /* Handle menu item "File/Quit". */ static void file_quit (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaClipboard *clipboard = param; gtk_widget_destroy (GTK_WIDGET (clipboard)); } static void edit_cut (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaClipboard *clipboard = param; g_signal_emit_by_name (GTK_TEXT_VIEW (clipboard->text_view), "cut-clipboard"); } static void edit_copy (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaClipboard *clipboard = param; g_signal_emit_by_name (GTK_TEXT_VIEW (clipboard->text_view), "copy-clipboard"); } static void edit_paste (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaClipboard *clipboard = param; g_signal_emit_by_name (GTK_TEXT_VIEW (clipboard->text_view), "paste-clipboard"); } static void edit_delete (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaClipboard *clipboard = param; g_signal_emit_by_name (GTK_TEXT_VIEW (clipboard->text_view), "backspace"); } /* Handle menu item "Edit/Select All". */ static void edit_select_all (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaClipboard *clipboard = param; g_signal_emit_by_name (GTK_TEXT_VIEW (clipboard->text_view), "select-all"); } /* Construct the file manager menu window and return that object. */ static void clipboard_action_new (GpaClipboard *clipboard, GtkWidget **menu, GtkWidget **toolbar) { static const GActionEntry g_entries[] = { { "File", NULL }, { "Edit", NULL }, { "file_clear", file_clear }, { "file_open", file_open }, { "file_save_as", file_save_as }, { "file_sign", file_sign }, { "file_verify", file_verify }, { "file_encrypt", file_encrypt }, { "file_decrypt", file_decrypt }, { "file_close", file_close }, { "file_quit", file_quit }, #if g { "edit_undo", edit_undo }, { "edit_redo", edit_redo }, #endif { "edit_cut", edit_cut }, { "edit_copy", edit_copy }, { "edit_paste", edit_paste }, { "edit_delete", edit_delete }, { "edit_select_all", edit_select_all }, }; /* static const GtkActionEntry entries[] = { // Toplevel { "File", NULL, N_("_File"), NULL }, { "Edit", NULL, N_("_Edit"), NULL }, // File menu. { "FileClear", GTK_STOCK_CLEAR, NULL, NULL, N_("Clear buffer"), G_CALLBACK (file_clear) }, { "FileOpen", GTK_STOCK_OPEN, NULL, NULL, N_("Open a file"), G_CALLBACK (file_open) }, { "FileSaveAs", GTK_STOCK_SAVE_AS, NULL, NULL, N_("Save to a file"), G_CALLBACK (file_save_as) }, { "FileSign", GPA_STOCK_SIGN, NULL, NULL, N_("Sign buffer text"), G_CALLBACK (file_sign) }, { "FileVerify", GPA_STOCK_VERIFY, NULL, NULL, N_("Check signatures of buffer text"), G_CALLBACK (file_verify) }, { "FileEncrypt", GPA_STOCK_ENCRYPT, NULL, NULL, N_("Encrypt the buffer text"), G_CALLBACK (file_encrypt) }, { "FileDecrypt", GPA_STOCK_DECRYPT, NULL, NULL, N_("Decrypt the buffer text"), G_CALLBACK (file_decrypt) }, { "FileClose", GTK_STOCK_CLOSE, NULL, NULL, N_("Close the buffer"), G_CALLBACK (file_close) }, { "FileQuit", GTK_STOCK_QUIT, NULL, NULL, N_("Quit the program"), G_CALLBACK (g_application_quit) }, // Edit menu. #if 0 // FIXME: Not implemented yet. { "EditUndo", GTK_STOCK_UNDO, NULL, NULL, N_("Undo the last action"), G_CALLBACK (edit_undo) }, { "EditRedo", GTK_STOCK_REDO, NULL, NULL, N_("Redo the last undone action"), G_CALLBACK (edit_redo) }, #endif { "EditCut", GTK_STOCK_CUT, NULL, NULL, N_("Cut the selection"), G_CALLBACK (edit_cut) }, { "EditCopy", GTK_STOCK_COPY, NULL, NULL, N_("Copy the selection"), G_CALLBACK (edit_copy) }, { "EditPaste", GTK_STOCK_PASTE, NULL, NULL, N_("Paste the clipboard"), G_CALLBACK (edit_paste) }, { "EditDelete", GTK_STOCK_DELETE, NULL, NULL, N_("Delete the selected text"), G_CALLBACK (edit_delete) }, { "EditSelectAll", GTK_STOCK_SELECT_ALL, NULL, "A", N_("Select the entire document"), G_CALLBACK (edit_select_all) } }; */ static const char *ui_string = "" "" "" "_File" "" "Clear" "app.file_cleear" "" "" "Open" "app.file_open" "" "
" "" "Sign" "app.file_open" "" "" "Verify" "app.file_open" "" "" "Encrypt" "app.file_open" "" "" "Decrypt" "app.file_open" "" "
" "
" "" "Close" "app.file_open" "" "" "Quit" "app.file_open" "" "
" "
" "" "Edit" "" "Cut" "app.edit_cut" "" "" "Copy" "app.edit_copy" "" "" "Copy" "app.edit_paste" "" "" "Delete" "app.edit_delete" "" "
" "" "Select All" "app.edit_select_all" "" "
" "
" "" "Preferences" "app.edit_preferences" "" "" "Backend Preferences" "app.edit_backend_preferences" "" "
" "
" "" "Windows" "" "Keyring Manager" "app.windows_keyring_editor" "" "" "File Manager" "app.windows_file_manager" "" "" "Clipboard" "app.windows_clipboard" "" "" "Card Manager" "app.windows_card_manager" "" "" "" "Help" "" "About" "app.help_about" "" "" "
" "" "True" "False" "False" "" "" "True" "False" "True" "edit-clear" "" "" "False" "True" "" "" "" "" "" "" "" "" "True" "False" "True" "edit-cut" "" "" "False" "True" "" "" "" "" "True" "False" "True" "edit-copy" "" "" "False" "True" "" "" "" "" "True" "False" "True" "edit-paste" "" "" "False" "True" "" "" "" "" "" "" "" "" "True" "False" "True" "sign" "" "" "False" "True" "" "" "" "" "True" "False" "verify" "True" "app.windows_file_manager" "" "" "False" "True" "" "" "" "" "True" "False" "True" "encrypt" "app.windows_card_manager" "" "" "False" "True" "" "" "" "" "True" "False" "True" "decrypt" "app.windows_card_manager" "" "" "False" "True" "" "" "" "" "" "" "" "" "True" "False" "True" "preferences-desktop" "app.windows_card_manager" "" "" "False" "True" "" "" "" "" "" "" "" "" "True" "False" "True" "keyring" "app.windows_keyring_editor" "" "" "False" "True" "" "" "" "" "True" "False" "__glade_unnamed_10" "True" "folder" "app.windows_file_manager" "" "" "False" "True" "" "" #ifdef ENABLE_CARD_MANAGER "" "" "True" "False" "__glade_unnamed_10" "True" "smartcard" "app.windows_card_manager" "" "" "False" "True" "" "" #endif "" "
"; /* static const char *ui_description = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " #if 0 // Not implemented yet. " " " " " " #endif " " " " " " " " " " " " " " " " " " " " " " " " " " " " #ifdef ENABLE_CARD_MANAGER " " #endif " " " " #if 0 " " #endif " " " " " " " " " " #if 0 // Disabled because the toolbar arrow mode doesn't work, and the // toolbar takes up too much space otherwise. " " " " #endif " " " " " " " " " " " " " " " " " " " " " " " " " " " " #ifdef ENABLE_CARD_MANAGER " " #endif #if 0 " " #endif " " ""; */ #ifdef OLD_MENU GtkAccelGroup *accel_group; GtkActionGroup *action_group; GtkAction *action; GtkUIManager *ui_manager; GError *error; action_group = gtk_action_group_new ("MenuActions"); gtk_action_group_set_translation_domain (action_group, PACKAGE); gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), clipboard); gtk_action_group_add_actions (action_group, gpa_help_menu_action_entries, G_N_ELEMENTS (gpa_help_menu_action_entries), clipboard); gtk_action_group_add_actions (action_group, gpa_windows_menu_action_entries, G_N_ELEMENTS (gpa_windows_menu_action_entries), clipboard); gtk_action_group_add_actions (action_group, gpa_preferences_menu_action_entries, G_N_ELEMENTS (gpa_preferences_menu_action_entries), clipboard); ui_manager = gtk_ui_manager_new (); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); accel_group = gtk_ui_manager_get_accel_group (ui_manager); gtk_window_add_accel_group (GTK_WINDOW (clipboard), accel_group); if (! gtk_ui_manager_add_ui_from_string (ui_manager, ui_description, -1, &error)) { g_message ("building clipboard menus failed: %s", error->message); g_error_free (error); exit (EXIT_FAILURE); } /* Fixup the icon theme labels which are too long for the toolbar. */ action = gtk_action_group_get_action (action_group, "WindowsKeyringEditor"); g_object_set (action, "short_label", _("Keyring"), NULL); action = gtk_action_group_get_action (action_group, "WindowsFileManager"); g_object_set (action, "short_label", _("Files"), NULL); #ifdef ENABLE_CARD_MANAGER action = gtk_action_group_get_action (action_group, "WindowsCardManager"); g_object_set (action, "short_label", _("Card"), NULL); #endif /* Take care of sensitiveness of widgets. */ action = gtk_action_group_get_action (action_group, "EditCut"); add_selection_sensitive_action (clipboard, action); action = gtk_action_group_get_action (action_group, "EditCopy"); add_selection_sensitive_action (clipboard, action); action = gtk_action_group_get_action (action_group, "EditDelete"); add_selection_sensitive_action (clipboard, action); action = gtk_action_group_get_action (action_group, "EditPaste"); /* Initialized later. */ add_paste_sensitive_action (clipboard, action); *menu = gtk_ui_manager_get_widget (ui_manager, "/MainMenu"); *toolbar = gtk_ui_manager_get_widget (ui_manager, "/ToolBar"); gpa_toolbar_set_homogeneous (GTK_TOOLBAR (*toolbar), FALSE); #else GError **err; GtkBuilder *gtk_builder = gtk_builder_new_from_string (icons_string, -1); if (gtk_builder_add_from_string( gtk_builder, ui_string , -1, err) == 0) { printf("ERROR: %s \n", (*err)->message); } GMenuModel *menu_bar_model = G_MENU_MODEL (gtk_builder_get_object (GTK_BUILDER (gtk_builder), "menu")); *menu = gtk_menu_bar_new_from_model (menu_bar_model); GObject *grid = gtk_builder_get_object (GTK_BUILDER (gtk_builder), "toolbar"); GtkCssProvider *css_provider = gtk_css_provider_new(); GdkDisplay *display = gdk_display_get_default(); GdkScreen *screen = gdk_display_get_default_screen (display); gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER(css_provider), GTK_STYLE_PROVIDER_PRIORITY_USER); gtk_css_provider_load_from_data(css_provider, "#toolbar {\n" //" padding-left: 55px;\n" // " padding-right: 5px;\n" // " border-radius: 3px;\n" "}\n", -1, NULL); GtkStyleContext *style_context; style_context = gtk_widget_get_style_context (GTK_WIDGET (grid)); *toolbar = GTK_WIDGET (grid); // We must set the name to the toolbar for css to recognize it gtk_widget_set_name(*toolbar, "toolbar"); gtk_style_context_add_class (style_context, "toolbar"); GtkApplication *gpa_app = get_gpa_application (); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), gpa_windows_menu_g_action_entries, G_N_ELEMENTS (gpa_windows_menu_g_action_entries), clipboard); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), g_entries, G_N_ELEMENTS (g_entries), clipboard); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), gpa_help_menu_g_action_entries, G_N_ELEMENTS (gpa_help_menu_g_action_entries), clipboard); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), gpa_preferences_menu_g_action_entries, G_N_ELEMENTS (gpa_preferences_menu_g_action_entries), clipboard); GSimpleAction *action; action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "edit_cut"); add_selection_sensitive_action (clipboard, action); action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "edit_copy"); add_selection_sensitive_action (clipboard, action); action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "edit_delete"); add_selection_sensitive_action (clipboard, action); action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "edit_paste"); // Initialized later add_paste_sensitive_action (clipboard, action); #endif } /* Callback for the destroy signal. */ static void clipboard_closed (GtkWidget *widget, gpointer param) { instance = NULL; } /* Construct the clipboard text. */ static GtkWidget * clipboard_text_new (GpaClipboard *clipboard) { GtkWidget *scroller; clipboard->text_view = gtk_text_view_new (); gtk_widget_grab_focus (clipboard->text_view); gtk_text_view_set_left_margin (GTK_TEXT_VIEW (clipboard->text_view), 4); gtk_text_view_set_right_margin (GTK_TEXT_VIEW (clipboard->text_view), 4); clipboard->text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (clipboard->text_view)); #ifndef MY_GTK_TEXT_BUFFER_NO_HAS_SELECTION /* A change in selection status causes a property change, which we can listen in on. */ g_signal_connect_swapped (clipboard->text_buffer, "notify::has-selection", G_CALLBACK (update_selection_sensitive_actions), clipboard); #else /* Runs very often. The changed signal is necessary for backspace actions. */ g_signal_connect_swapped (clipboard->text_buffer, "mark-set", G_CALLBACK (update_selection_sensitive_actions), clipboard); g_signal_connect_after (clipboard->text_buffer, "backspace", G_CALLBACK (update_selection_sensitive_actions), clipboard); #endif scroller = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller), GTK_SHADOW_IN); gtk_container_add (GTK_CONTAINER (scroller), clipboard->text_view); return scroller; } /* Construct a new class object of GpaClipboard. */ static GObject * gpa_clipboard_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GObject *object; GpaClipboard *clipboard; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *icon; GtkWidget *label; gchar *markup; GtkWidget *menubar; GtkWidget *text_box; GtkWidget *text_frame; GtkWidget *toolbar; GtkWidget *align; guint pt, pb, pl, pr; /* Invoke parent's constructor. */ object = parent_class->constructor (type, n_construct_properties, construct_properties); clipboard = GPA_CLIPBOARD (object); /* Initialize. */ gpa_window_set_title (GTK_WINDOW (clipboard), _("Clipboard")); gtk_window_set_default_size (GTK_WINDOW (clipboard), 640, 480); /* Realize the window so that we can create pixmaps without warnings and also access the clipboard. */ gtk_widget_realize (GTK_WIDGET (clipboard)); /* Use a vbox to show the menu, toolbar and the text container. */ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); /* We need to create the text_buffer before we create the menus and the toolbar, because of widget sensitivity issues, which depend on the selection status of the text_buffer. */ text_frame = clipboard_text_new (clipboard); /* Get the menu and the toolbar. */ clipboard_action_new (clipboard, &menubar, &toolbar); gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, TRUE, 0); /* Add a fancy label that tells us: This is the clipboard. */ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5); /* FIXME: Need better icon. */ icon = gtk_image_new_from_icon_name ("edit-paste", GTK_ICON_SIZE_DND); gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, TRUE, 0); label = gtk_label_new (NULL); markup = g_strdup_printf ("%s", _("Clipboard")); gtk_label_set_markup (GTK_LABEL (label), markup); g_free (markup); gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 10); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); - + gtk_widget_set_halign (GTK_WIDGET (label), GTK_ALIGN_START); + gtk_widget_set_valign (GTK_WIDGET (label), GTK_ALIGN_CENTER); /* Third a text entry. */ text_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); align = gtk_alignment_new (0.5, 0.5, 1, 1); gtk_alignment_get_padding (GTK_ALIGNMENT (align), &pt, &pb, &pl, &pr); gtk_alignment_set_padding (GTK_ALIGNMENT (align), pt, pb + 5, pl + 5, pr + 5); gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (text_box), text_frame, TRUE, TRUE, 0); gtk_container_add (GTK_CONTAINER (align), text_box); gtk_container_add (GTK_CONTAINER (clipboard), vbox); g_signal_connect (object, "destroy", G_CALLBACK (clipboard_closed), object); /* Update the sensitivity of paste items. */ { GtkClipboard *clip; /* Do this once for all paste sensitive items. Note that the window is realized already. */ clip = gtk_widget_get_clipboard (GTK_WIDGET (clipboard), GDK_SELECTION_CLIPBOARD); g_signal_connect (clip, "owner_change", G_CALLBACK (clipboard_owner_change_cb), clipboard); set_paste_sensitivity (clipboard, clip); } /* Update the sensitivity of selection items. */ update_selection_sensitive_actions (clipboard); return object; } static GpaClipboard * gpa_clipboard_new () { GpaClipboard *clipboard; clipboard = g_object_new (GPA_CLIPBOARD_TYPE, NULL); return clipboard; } /* API */ GtkWidget * gpa_clipboard_get_instance (void) { if (! instance) instance = gpa_clipboard_new (); return GTK_WIDGET (instance); } gboolean gpa_clipboard_is_open (void) { return (instance != NULL); } diff --git a/src/fileman.c b/src/fileman.c index d561afb..42a9dc9 100644 --- a/src/fileman.c +++ b/src/fileman.c @@ -1,1279 +1,1281 @@ /* fileman.c - The GNU Privacy Assistant Copyright (C) 2000, 2001 G-N-U GmbH. Copyright (C) 2007, 2008 g10 Code GmbH This file is part of GPA. GPA 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 3 of the License, or (at your option) any later version. GPA 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, see . */ /* The file encryption/decryption/sign window. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include "gpa.h" #include "gtktools.h" #include "gpawidgets.h" #include "siglist.h" #include "helpmenu.h" #include "icons.h" #include "fileman.h" #include "gpafiledecryptop.h" #include "gpafileencryptop.h" #include "gpafilesignop.h" #include "gpafileverifyop.h" #if ! GTK_CHECK_VERSION (2, 10, 0) #define GTK_STOCK_SELECT_ALL "gtk-select-all" #endif /* Object and class definition. */ struct _GpaFileManager { GtkWindow parent; GtkWidget *window; GtkWidget *list_files; GList *selection_sensitive_actions; }; struct _GpaFileManagerClass { GtkWindowClass parent_class; }; /* There is only one instance of the file manage class. Use a global variable to keep track of it. */ static GpaFileManager *instance; /* We also need to save the parent class. */ static GObjectClass *parent_class; /* Definition of the sensitivity function type. */ typedef gboolean (*sensitivity_func_t)(gpointer); /* Constants to define the file list. */ enum { FILE_NAME_COLUMN, FILE_N_COLUMNS }; #define DND_TARGET_URI_LIST 1 /* Drag and drop target list. */ static GtkTargetEntry dnd_target_list[] = { { "text/uri-list", 0, DND_TARGET_URI_LIST } }; /* Local prototypes */ static GObject *gpa_file_manager_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties); /* * GtkWidget boilerplate. */ static void gpa_file_manager_finalize (GObject *object) { G_OBJECT_CLASS (parent_class)->finalize (object); } static void gpa_file_manager_init (GpaFileManager *fileman) { fileman->selection_sensitive_actions = NULL; } static void gpa_file_manager_class_init (GpaFileManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->constructor = gpa_file_manager_constructor; object_class->finalize = gpa_file_manager_finalize; } GType gpa_file_manager_get_type (void) { static GType fileman_type = 0; if (!fileman_type) { static const GTypeInfo fileman_info = { sizeof (GpaFileManagerClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) gpa_file_manager_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GpaFileManager), 0, /* n_preallocs */ (GInstanceInitFunc) gpa_file_manager_init, }; fileman_type = g_type_register_static (GTK_TYPE_WINDOW, "GpaFileManager", &fileman_info, 0); } return fileman_type; } /* * File manager methods */ /* Return the currently selected files as a new list of filenames * structs. The list and the texts must be freed by the caller. */ static GList * get_selected_files (GtkWidget *list) { GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (list)); GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list)); GList *selection = gtk_tree_selection_get_selected_rows (sel, &model); GList *files = NULL; while (selection) { gpa_file_item_t file_item; gchar *filename; GtkTreeIter iter; gtk_tree_model_get_iter (model, &iter, (GtkTreePath*) selection->data); gtk_tree_model_get (model, &iter, FILE_NAME_COLUMN, &filename, -1); file_item = g_malloc0 (sizeof (*file_item)); file_item->filename_in = filename; files = g_list_append (files, file_item); selection = g_list_next (selection); } /* Free the selection */ g_list_foreach (selection, (GFunc) gtk_tree_path_free, NULL); g_list_free (selection); return files; } /* Add file FILENAME to the file list of FILEMAN and select it */ static gboolean add_file (GpaFileManager *fileman, const gchar *filename) { GtkListStore *store; GtkTreeIter iter; GtkTreePath *path; GtkTreeSelection *sel; gchar *filename_utf8; /* The tree contains filenames in the UTF-8 encoding. */ filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL); /* Try to convert from the current locale as fallback. This is important for windows where g_filename_to_utf8 does not take locale into account because the filedialogs already convert to utf8. */ if (!filename_utf8) { filename_utf8 = g_locale_to_utf8 (filename, -1, NULL, NULL, NULL); } /* Last fallback is guranteed to never be NULL so in doubt we can still fail later showing a filename that can't be found to the user etc.*/ if (!filename_utf8) { filename_utf8 = g_filename_display_name (filename); } store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fileman->list_files))); /* Check for duplicates. */ path = gtk_tree_path_new_first (); if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path)) do { gchar *tmp; gboolean exists; gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, FILE_NAME_COLUMN, &tmp, -1); exists = g_str_equal (filename_utf8, tmp); g_free (tmp); if (exists) { g_free (filename_utf8); gtk_tree_path_free (path); return FALSE; /* This file is already in our list. */ } } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter)); gtk_tree_path_free (path); /* Append it to our list. */ gtk_list_store_append (store, &iter); /* FIXME: Add the file status when/if gpgme supports it */ gtk_list_store_set (store, &iter, FILE_NAME_COLUMN, filename_utf8, -1); /* Select the row */ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (fileman->list_files)); gtk_tree_selection_select_iter (sel, &iter); return TRUE; } /* Add a file created by an operation to the list */ static void file_created_cb (GpaFileOperation *op, gpa_file_item_t item, gpointer data) { GpaFileManager *fileman = data; add_file (fileman, item->filename_out); } /* Do whatever is required with a file operation, to ensure proper clean up */ static void register_operation (GpaFileManager *fileman, GpaFileOperation *op) { g_signal_connect (G_OBJECT (op), "created_file", G_CALLBACK (file_created_cb), fileman); g_signal_connect (G_OBJECT (op), "completed", G_CALLBACK (g_object_unref), NULL); } /* Management of the selection sensitive actions. */ /* Return true if a selection is active. */ static gboolean has_selection (gpointer param) { GpaFileManager *fileman = param; GtkTreeSelection *sel; sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (fileman->list_files)); return (gtk_tree_selection_count_selected_rows (sel) > 0); } /* Add WIDGET to the list of sensitive actions of FILEMAN. */ static void add_selection_sensitive_action (GpaFileManager *fileman, GSimpleAction *action, sensitivity_func_t callback) { g_object_set_data (G_OBJECT (action), "gpa_sensitivity", callback); fileman->selection_sensitive_actions = g_list_append (fileman->selection_sensitive_actions, action); } /* Update the sensitivity of the widget DATA and pass PARAM through to the sensitivity callback. Usable as an iterator function in g_list_foreach. */ static void update_selection_sensitive_action (gpointer data, gpointer param) { sensitivity_func_t func; func = g_object_get_data (G_OBJECT (data), "gpa_sensitivity"); g_simple_action_set_enabled (G_SIMPLE_ACTION (data), func (param)); } /* Call update_selection_sensitive_action for all widgets in the list of sensitive actions and pass FILEMAN through as the user data parameter. */ static void update_selection_sensitive_actions (GpaFileManager *fileman) { g_list_foreach (fileman->selection_sensitive_actions, update_selection_sensitive_action, (gpointer) fileman); } /* Actions as called by the menu items. */ static GSList * get_load_file_name (GtkWidget *parent, const gchar *title, const gchar *directory) { static GtkWidget *dialog; GtkResponseType response; GSList *filenames = NULL; if (! dialog) { dialog = gtk_file_chooser_dialog_new (title, GTK_WINDOW (parent), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE); } if (directory) gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), directory); gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (dialog)); /* Run the dialog until there is a valid response. */ response = gtk_dialog_run (GTK_DIALOG (dialog)); if (response == GTK_RESPONSE_OK) filenames = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (dialog)); gtk_widget_hide (dialog); return filenames; } /* Handle menu item "File/Open". */ void open_file_one (gpointer data, gpointer user_data) { GpaFileManager *fileman = user_data; gchar *filename = (gchar *) data; /* FIXME: We are ignoring errors here. */ add_file (fileman, filename); g_free (filename); } static void file_open (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaFileManager *fileman = param; GSList *filenames; filenames = get_load_file_name (GTK_WIDGET (fileman), _("Open File"), NULL); if (! filenames) return; g_slist_foreach (filenames, open_file_one, fileman); g_slist_free (filenames); } /* Handle menu item "File/Clear". */ static void file_clear (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaFileManager *fileman = param; GtkListStore *store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fileman->list_files))); gtk_list_store_clear (store); } /* Handle menu item "File/Verify". */ static void file_verify (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaFileManager *fileman = param; GList *files; GpaFileVerifyOperation *op; files = get_selected_files (fileman->list_files); if (!files) return; op = gpa_file_verify_operation_new (GTK_WIDGET (fileman), files); register_operation (fileman, GPA_FILE_OPERATION (op)); } /* Handle menu item "File/Sign". */ static void file_sign (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaFileManager *fileman = param; GList * files; GpaFileSignOperation *op; files = get_selected_files (fileman->list_files); if (!files) return; op = gpa_file_sign_operation_new (GTK_WIDGET (fileman), files, FALSE); register_operation (fileman, GPA_FILE_OPERATION (op)); } /* Handle menu item "File/Encrypt". */ static void file_encrypt (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaFileManager *fileman = param; GList *files; GpaFileEncryptOperation *op; files = get_selected_files (fileman->list_files); if (!files) return; op = gpa_file_encrypt_operation_new (GTK_WIDGET (fileman), files, FALSE); register_operation (fileman, GPA_FILE_OPERATION (op)); } /* Handle menu item "File/Decrypt". */ static void file_decrypt (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaFileManager *fileman = param; GList *files; GpaFileDecryptOperation *op; files = get_selected_files (fileman->list_files); if (!files) return; op = gpa_file_decrypt_verify_operation_new (GTK_WIDGET (fileman), files); register_operation (fileman, GPA_FILE_OPERATION (op)); } /* Handle menu item "File/Close". */ static void file_close (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaFileManager *fileman = param; gtk_widget_destroy (GTK_WIDGET (fileman)); } /* Handle menu item "Edit/Select All". */ static void edit_select_all (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaFileManager *fileman = param; gtk_tree_selection_select_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (fileman->list_files))); } /* Construct the file manager menu and toolbar widgets and return them. */ static void fileman_action_new (GpaFileManager *fileman, GtkWidget **menubar, GtkWidget **toolbar) { static const GActionEntry entries[] = { { "File", NULL }, { "Edit", NULL }, { "file_open", file_open }, { "file_clear", file_clear }, { "file_sign", file_sign }, { "file_verify", file_verify }, { "file_encrypt", file_encrypt }, { "file_decrypt", file_decrypt }, { "file_close", file_close }, { "file_quit", g_application_quit }, { "edit_select_all", edit_select_all }, }; /* static const GtkActionEntry old_entries[] = { // Toplevel. { "File", NULL, N_("_File"), NULL }, { "Edit", NULL, N_("_Edit"), NULL }, // File menu. { "FileOpen", GTK_STOCK_OPEN, NULL, NULL, N_("Open a file"), G_CALLBACK (file_open) }, { "FileClear", GTK_STOCK_CLEAR, NULL, NULL, N_("Close all files"), G_CALLBACK (file_clear) }, { "FileSign", GPA_STOCK_SIGN, NULL, NULL, N_("Sign the selected file"), G_CALLBACK (file_sign) }, { "FileVerify", GPA_STOCK_VERIFY, NULL, NULL, N_("Check signatures of selected file"), G_CALLBACK (file_verify) }, { "FileEncrypt", GPA_STOCK_ENCRYPT, NULL, NULL, N_("Encrypt the selected file"), G_CALLBACK (file_encrypt) }, { "FileDecrypt", GPA_STOCK_DECRYPT, NULL, NULL, N_("Decrypt the selected file"), G_CALLBACK (file_decrypt) }, { "FileClose", GTK_STOCK_CLOSE, NULL, NULL, N_("Close the window"), G_CALLBACK (file_close) }, { "FileQuit", GTK_STOCK_QUIT, NULL, NULL, N_("Quit the program"), G_CALLBACK (g_application_quit) }, // Edit menu. { "EditSelectAll", GTK_STOCK_SELECT_ALL, NULL, "A", N_("Select all files"), G_CALLBACK (edit_select_all) } }; */ /* static const char *ui_description = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " #ifdef ENABLE_CARD_MANAGER " " #endif " " " " #if 0 " " #endif " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " #ifdef ENABLE_CARD_MANAGER " " #endif #if 0 " " #endif " " ""; */ static const char *menu_string = { "" "" "" "_File" "
" "" "Open" "app.file_close" "" "" "Clear" "app.file_quit" "" "
" "
" "" "Sign" "app.file_close" "" "" "Verify" "app.file_quit" "" "" "Encrypt" "app.file_quit" "" "" "Decrypt" "app.file_quit" "" "
" "
" "" "Close" "app.file_close" "" "" "Quit" "app.file_quit" "" "
" "
" "" "Edit" "
" "" "Mark All" "app.file_close" "" "
" "
" "" "Backend Settings" "app.file_quit" "" "" "Settings" "app.file_quit" "" "
" "
" "" "Window" "" "Keyring Manager" "app.file_close" "" "" "File Manager" "app.file_quit" "" "" "Clipboard" "app.file_quit" "" "" "Card Manager" "app.file_quit" "" "" "" "Help" "" "About" "app.help_about" "" "" "
" "" "True" "False" "False" "" "" "True" "False" "True" "document-open" "" "" "False" "True" "" "" "" "" "True" "False" "True" "edit-clear" "" "" "False" "True" "" "" "" "" "" "" "" "" "True" "False" "True" "sign" "" "" "False" "True" "" "" "" "" "True" "False" "True" "verify" "" "" "False" "True" "" "" "" "" "True" "False" "True" "encrypt" "" "" "False" "True" "" "" "" "" "True" "False" "True" "decrypt" "" "" "False" "True" "" "" "" "" "" "" "" "" "True" "False" "True" "preferences-desktop" "" "" "False" "True" "" "" "" "" "" "" "" "" "True" "False" "True" "keyring" "app.windows_keyring_editor" "" "" "False" "True" "" "" "" "" "True" "False" "True" "edit-paste" "app.windows_clipboard" "" "" "False" "True" "" "" "" "" "True" "False" "True" "smartcard" "app.windows_card_manager" "" "" "False" "True" "" "" "" "
" }; /* GtkAccelGroup *accel_group; GtkActionGroup *action_group; GtkAction *action; GtkUIManager *ui_manager; GError *error; action_group = gtk_action_group_new ("MenuActions"); gtk_action_group_set_translation_domain (action_group, PACKAGE); gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), fileman); gtk_action_group_add_actions (action_group, gpa_help_menu_action_entries, G_N_ELEMENTS (gpa_help_menu_action_entries), fileman); gtk_action_group_add_actions (action_group, gpa_windows_menu_action_entries, G_N_ELEMENTS (gpa_windows_menu_action_entries), fileman); gtk_action_group_add_actions (action_group, gpa_preferences_menu_action_entries, G_N_ELEMENTS (gpa_preferences_menu_action_entries), fileman); ui_manager = gtk_ui_manager_new (); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); accel_group = gtk_ui_manager_get_accel_group (ui_manager); gtk_window_add_accel_group (GTK_WINDOW (fileman), accel_group); if (! gtk_ui_manager_add_ui_from_string (ui_manager, ui_description, -1, &error)) { g_message ("building fileman menus failed: %s", error->message); g_error_free (error); exit (EXIT_FAILURE); } // Fixup the icon theme labels which are too long for the toolbar. action = gtk_action_group_get_action (action_group, "WindowsKeyringEditor"); g_object_set (action, "short_label", _("Keyring"), NULL); #ifdef ENABLE_CARD_MANAGER action = gtk_action_group_get_action (action_group, "WindowsCardManager"); g_object_set (action, "short_label", _("Card"), NULL); #endif // Take care of sensitiveness of widgets. action = gtk_action_group_get_action (action_group, "FileSign"); add_selection_sensitive_action (fileman, action, has_selection); action = gtk_action_group_get_action (action_group, "FileVerify"); add_selection_sensitive_action (fileman, action, has_selection); action = gtk_action_group_get_action (action_group, "FileEncrypt"); add_selection_sensitive_action (fileman, action, has_selection); action = gtk_action_group_get_action (action_group, "FileDecrypt"); add_selection_sensitive_action (fileman, action, has_selection); *menubar = gtk_ui_manager_get_widget (ui_manager, "/MainMenu"); *toolbar = gtk_ui_manager_get_widget (ui_manager, "/ToolBar"); gpa_toolbar_set_homogeneous (GTK_TOOLBAR (*toolbar), FALSE); */ GError **err; GtkBuilder *gtk_builder = gtk_builder_new_from_string (menu_string, -1); if (gtk_builder_add_from_string( gtk_builder, menu_string , -1, err) == 0) { printf("ERROR: %s \n", (*err)->message); } GMenuModel *menu_bar_model = G_MENU_MODEL (gtk_builder_get_object (GTK_BUILDER (gtk_builder), "main_menu")); *menubar = gtk_menu_bar_new_from_model (menu_bar_model); /* GMenuModel *popup_menu_model = G_MENU_MODEL (gtk_builder_get_object (GTK_BUILDER (gtk_builder), "popupmenu")); *popup = gtk_menu_new_from_model (popup_menu_model); */ GObject *grid = gtk_builder_get_object (GTK_BUILDER (gtk_builder), "toolbar"); GtkCssProvider *css_provider = gtk_css_provider_new(); GdkDisplay *display = gdk_display_get_default(); GdkScreen *screen = gdk_display_get_default_screen (display); gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER(css_provider), GTK_STYLE_PROVIDER_PRIORITY_USER); gtk_css_provider_load_from_data(css_provider, "#toolbar {\n" //" padding-left: 55px;\n" // " padding-right: 5px;\n" // " border-radius: 3px;\n" "}\n", -1, NULL); GtkStyleContext *style_context; style_context = gtk_widget_get_style_context (GTK_WIDGET (grid)); //gtk_widget_add_css_class (grid, "toolbar"); *toolbar = grid; // We must set the name to the toolbar for css to recognize it gtk_widget_set_name(*toolbar, "toolbar"); gtk_style_context_add_class (style_context, "toolbar"); GtkApplication *gpa_app = get_gpa_application (); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), gpa_windows_menu_g_action_entries, G_N_ELEMENTS (gpa_windows_menu_g_action_entries), fileman); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), entries, G_N_ELEMENTS (entries), fileman); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), gpa_help_menu_g_action_entries, G_N_ELEMENTS (gpa_help_menu_g_action_entries), fileman); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), gpa_preferences_menu_g_action_entries, G_N_ELEMENTS (gpa_preferences_menu_g_action_entries), fileman); GSimpleAction *action; action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "file_sign"); add_selection_sensitive_action (fileman, action, has_selection); action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "file_verify"); add_selection_sensitive_action (fileman, action, has_selection); action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "file_encrypt"); add_selection_sensitive_action (fileman, action, has_selection); action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "file_decrypt"); add_selection_sensitive_action (fileman, action, has_selection); } /* Drag and Drop handler. */ /* Handler for "drag-drop". This signal is emitted when the user drops the selection. */ static gboolean dnd_drop_handler (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint tim, gpointer user_data) { GdkAtom target_type = gdk_atom_intern ("text/uri-list", FALSE); GList *targets = gdk_drag_context_list_targets(context); /* If the source offers a target we request the data from her. */ if (targets && g_list_find (targets, GDK_ATOM_TO_POINTER (target_type))) { gtk_drag_get_data (widget, context, target_type, tim); return TRUE; } return FALSE; } /* Handler for "drag-data-received". This signal is emitted when the data has been received from the source. */ static void dnd_data_received_handler (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection_data, guint target_type, guint tim, gpointer user_data) { GpaFileManager *fileman = user_data; gboolean dnd_success = FALSE; gboolean delete_selection_data = FALSE; // const guchar *our_selection_data = gtk_selection_data_get_data(selection_data); /* Is that usable by us? */ if (selection_data && gtk_selection_data_get_length(selection_data) >= 0 ) { GdkDragAction drag_action = gdk_drag_context_get_suggested_action(context); if (drag_action == GDK_ACTION_MOVE) delete_selection_data = TRUE; /* Check that we got a format we can use. */ if (target_type == DND_TARGET_URI_LIST) { //char *p = (char *) selection_data->data; char *p = (char *)gtk_selection_data_get_data(selection_data); char **list; int i; list = g_uri_list_extract_uris (p); for (i=0; list && list[i]; i++) { char *name = g_filename_from_uri (list[i], NULL, NULL); if (name) { /* Canonical line endings are required for an uri-list. */ if ((p = strchr (name, '\r'))) *p = 0; add_file (fileman, name); g_free (name); } } g_strfreev (list); dnd_success = TRUE; } } /* Finish the DnD processing. */ gtk_drag_finish (context, dnd_success, delete_selection_data, tim); } /* Construct the file list object. */ static GtkWidget * file_list_new (GpaFileManager * fileman) { GtkWidget *scrollerFile; GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkTreeSelection *sel; GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING); GtkWidget *list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("File"), renderer, "text", FILE_NAME_COLUMN, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list)); gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE); g_signal_connect_swapped (sel, "changed", G_CALLBACK (update_selection_sensitive_actions), fileman); gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (list), TRUE); scrollerFile = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollerFile), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrollerFile), GTK_SHADOW_IN); fileman->list_files = list; gtk_widget_grab_focus (list); gtk_container_add (GTK_CONTAINER (scrollerFile), list); return scrollerFile; } /* Callback for the destroy signal. */ static void file_manager_closed (GtkWidget *widget, gpointer param) { instance = NULL; } /* Construct a new class object of GpaFileManager. */ static GObject* gpa_file_manager_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GObject *object; GpaFileManager *fileman; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *label; GtkWidget *icon; gchar *markup; GtkWidget *menubar; GtkWidget *file_box; GtkWidget *file_frame; GtkWidget *toolbar; GtkWidget *align; guint pt, pb, pl, pr; /* Invoke parent's constructor. */ object = parent_class->constructor (type, n_construct_properties, construct_properties); fileman = GPA_FILE_MANAGER (object); /* Initialize. */ gpa_window_set_title (GTK_WINDOW (fileman), _("File Manager")); gtk_window_set_default_size (GTK_WINDOW (fileman), 640, 480); /* Realize the window so that we can create pixmaps without warnings. */ gtk_widget_realize (GTK_WIDGET (fileman)); /* Use a vbox to show the menu, toolbar and the file container. */ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); /* Get the menu and the toolbar. */ fileman_action_new (fileman, &menubar, &toolbar); gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, TRUE, 0); /* Add a fancy label that tells us: This is the file manager. */ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5); icon = gtk_image_new_from_icon_name ("folder", GTK_ICON_SIZE_DND); gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, TRUE, 0); label = gtk_label_new (NULL); markup = g_strdup_printf ("%s", _("File Manager")); gtk_label_set_markup (GTK_LABEL (label), markup); g_free (markup); gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 10); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_widget_set_halign (GTK_WIDGET (label), GTK_ALIGN_START); + gtk_widget_set_valign (GTK_WIDGET (label), GTK_ALIGN_CENTER); + /* Third a hbox with the file list. */ align = gtk_alignment_new (0.5, 0.5, 1, 1); gtk_alignment_get_padding (GTK_ALIGNMENT (align), &pt, &pb, &pl, &pr); gtk_alignment_set_padding (GTK_ALIGNMENT (align), pt, pb + 5, pl + 5, pr + 5); gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0); file_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); file_frame = file_list_new (fileman); gtk_box_pack_start (GTK_BOX (file_box), file_frame, TRUE, TRUE, 0); gtk_container_add (GTK_CONTAINER (align), file_box); gtk_container_add (GTK_CONTAINER (fileman), vbox); g_signal_connect (object, "destroy", G_CALLBACK (file_manager_closed), object); /* Make the file box a DnD destination. */ gtk_drag_dest_set (file_box, (GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT), dnd_target_list, DIM (dnd_target_list), GDK_ACTION_COPY); g_signal_connect (file_box, "drag-drop", G_CALLBACK (dnd_drop_handler), fileman); g_signal_connect (file_box, "drag-data-received", G_CALLBACK (dnd_data_received_handler), fileman); return object; } static GpaFileManager * gpa_fileman_new () { GpaFileManager *fileman; fileman = g_object_new (GPA_FILE_MANAGER_TYPE, NULL); update_selection_sensitive_actions (fileman); return fileman; } /* API */ GtkWidget * gpa_file_manager_get_instance (void) { if (!instance) { instance = gpa_fileman_new (); } return GTK_WIDGET (instance); } gboolean gpa_file_manager_is_open (void) { return (instance != NULL); } void gpa_file_manager_open_file (GpaFileManager *fileman, const gchar *filename) { if (!add_file (fileman, filename)) gpa_window_error (_("The file is already open."), GTK_WIDGET (fileman)); /* FIXME: Release filename? */ } diff --git a/src/keymanager.c b/src/keymanager.c index d09ed82..715414e 100644 --- a/src/keymanager.c +++ b/src/keymanager.c @@ -1,1950 +1,1950 @@ /* keymanager.c - The Key Manager. * Copyright (C) 2000, 2001 G-N-U GmbH. * Copyright (C) 2005, 2008, 2009 g10 Code GmbH. * * This file is part of GPA. * * GPA 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 3 of the License, or * (at your option) any later version. * * GPA 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, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include "gpa.h" #include "gtktools.h" #include "icons.h" #include "helpmenu.h" #include "gpawidgets.h" #include "ownertrustdlg.h" #include "keysigndlg.h" #include "keygendlg.h" #include "keygenwizard.h" #include "keyeditdlg.h" #include "keydeletedlg.h" #include "keylist.h" #include "siglist.h" #include "gpgmetools.h" #include "gpgmeedit.h" #include "keytable.h" #include "server-access.h" #include "options.h" #include "convert.h" #include "gpasubkeylist.h" #include "gpakeydeleteop.h" #include "gpakeysignop.h" #include "gpakeytrustop.h" #include "gpaexportfileop.h" #include "gpaexportclipop.h" #include "gpaexportserverop.h" #include "gpaimportfileop.h" #include "gpaimportclipop.h" #include "gpaimportserverop.h" #include "gpaimportbykeyidop.h" #include "gpabackupop.h" #include "gpagenkeyadvop.h" #include "gpagenkeysimpleop.h" #include "gpa-key-details.h" #include "keymanager.h" #if ! GTK_CHECK_VERSION (2, 10, 0) #define GTK_STOCK_SELECT_ALL "gtk-select-all" #endif /* Object's class definition. */ struct _GpaKeyManagerClass { GtkWindowClass parent_class; }; /* Object definition. */ struct _GpaKeyManager { GtkWindow parent_instance; /* The central list of keys. */ GpaKeyList *keylist; /* The "Show Ownertrust" toggle button. */ GtkWidget *toggle_show; /* The details widget. */ GtkWidget *details; /* Idle handler id for updates of the details widget. Will be nonzero whenever a handler is currently set and zero otherwise. */ guint details_idle_id; /* Labels in the status bar. */ GtkWidget *status_label; GtkWidget *status_key_user; GtkWidget *status_key_id; /* The popup menu. */ GtkWidget *popup_menu; /* List of sensitive widgets. See below. */ GList *selection_sensitive_actions; /* The currently selected key. */ gpgme_key_t current_key; /* Context used for retrieving the current key. */ GpaContext *ctx; /* Hack: warn the selection callback to ignore changes. Don't, ever, assign a value directly. Raise and lower it with increments. */ int freeze_selection; }; /* There is only one instance of the card manager class. Use a global variable to keep track of it. */ static GpaKeyManager *this_instance; /* Prototype of a sensitivity callback. Return TRUE if the widget should be sensitive, FALSE otherwise. The parameter is a pointer to the instance. */ typedef gboolean (*sensitivity_func_t) (gpointer); /* Local prototypes */ static int idle_update_details (gpointer param); static void keyring_update_details (GpaKeyManager *self); static void gpa_key_manager_finalize (GObject *object); /************************************************************ ******************* Implementation ********************* ************************************************************/ /* A simple sensitivity callback mechanism. The basic idea is that buttons (and other widgets like menu items as well) should know when they should be sensitive or not. The implementation here is very simple and quite specific for the key manager's needs. We maintain a list of sensitive widgets each of which has a sensitivity callback associated with them as the "gpa_sensitivity" data. The callback returns TRUE when the widget should be sensitive and FALSE otherwise. Whenever the selection in the key list widget changes we call update_selection_sensitive_actions which iterates through the widgets in the list, calls the sensitivity callback and changes the widget's sensitivity accordingly. */ /* Add widget to the list of sensitive widgets of editor. */ static void add_selection_sensitive_action (GpaKeyManager *self, GSimpleAction *action, sensitivity_func_t callback) { g_object_set_data (G_OBJECT (action), "gpa_sensitivity", callback); self->selection_sensitive_actions = g_list_append (self->selection_sensitive_actions, action); } /* Update the sensitivity of the widget data and pass param through to the sensitivity callback. Usable as iterator function in g_list_foreach. */ static void update_selection_sensitive_action (gpointer data, gpointer param) { sensitivity_func_t func; func = g_object_get_data (G_OBJECT (data), "gpa_sensitivity"); g_simple_action_set_enabled (G_SIMPLE_ACTION (data), func (param)); } /* Call update_selection_sensitive_widget for all widgets in the list of sensitive widgets and pass self through as the user data parameter. */ static void update_selection_sensitive_actions (GpaKeyManager *self) { g_list_foreach (self->selection_sensitive_actions, update_selection_sensitive_action, self); } /* Disable all the widgets in the list of sensitive widgets. To be used while the selection changes. */ static void disable_selection_sensitive_actions (GpaKeyManager *self) { GList *cur; cur = self->selection_sensitive_actions; while (cur) { g_simple_action_set_enabled (G_SIMPLE_ACTION (cur->data), FALSE); cur = g_list_next (cur); } } /* Helper functions to cope with selections. */ /* Return TRUE if the key list widget of the key manager has at least one selected item. Usable as a sensitivity callback. */ static gboolean key_manager_has_selection (gpointer param) { GpaKeyManager *self = param; return gpa_keylist_has_selection (self->keylist); } /* Return TRUE if the key list widget of the key manager has exactly one selected item. Usable as a sensitivity callback. */ static gboolean key_manager_has_single_selection (gpointer param) { GpaKeyManager *self = param; return gpa_keylist_has_single_selection (self->keylist); } /* Return TRUE if the key list widget of the key manager has exactly one selected OpenPGP item. Usable as a sensitivity callback. */ static gboolean key_manager_has_single_selection_OpenPGP (gpointer param) { GpaKeyManager *self = param; int result = 0; if (gpa_keylist_has_single_selection (self->keylist)) { gpgme_key_t key = gpa_keylist_get_selected_key (self->keylist); if (key && key->protocol == GPGME_PROTOCOL_OpenPGP) result = 1; gpgme_key_unref (key); } return result; } /* Return TRUE if the key list widget of the key manager has exactly one selected item and it is a private key. Usable as a sensitivity callback. */ static gboolean key_manager_has_private_selected (gpointer param) { GpaKeyManager *self = param; return gpa_keylist_has_single_secret_selection (GPA_KEYLIST(self->keylist)); } /* Return the the currently selected key. NULL if no key is selected. */ static gpgme_key_t key_manager_current_key (GpaKeyManager *self) { return self->current_key; } /* Action callbacks. */ static void gpa_key_manager_changed_wot_cb (gpointer data) { GpaKeyManager *self = data; gpa_keylist_start_reload (self->keylist); } static void gpa_key_manager_changed_wot_secret_cb (gpointer data) { GpaKeyManager *self = data; gpa_keylist_imported_secret_key (self->keylist); gpa_keylist_start_reload (self->keylist); } static void gpa_key_manager_key_modified (GpaKeyEditDialog *dialog, gpgme_key_t key, gpointer data) { GpaKeyManager *self = data; gpa_keylist_start_reload (self->keylist); } static void gpa_key_manager_new_key_cb (gpointer data, const gchar *fpr) { GpaKeyManager *self = data; gpa_keylist_new_key (GPA_KEYLIST (self->keylist), fpr); gpa_options_update_default_key (gpa_options_get_instance ()); } static void register_key_operation (GpaKeyManager *self, GpaKeyOperation *op) { g_signal_connect_swapped (G_OBJECT (op), "changed_wot", G_CALLBACK (gpa_key_manager_changed_wot_cb), self); g_signal_connect (G_OBJECT (op), "completed", G_CALLBACK (g_object_unref), self); } static void register_import_operation (GpaKeyManager *self, GpaImportOperation *op) { g_signal_connect_swapped (G_OBJECT (op), "imported_keys", G_CALLBACK (gpa_key_manager_changed_wot_cb), self); g_signal_connect_swapped (G_OBJECT (op), "imported_secret_keys", G_CALLBACK (gpa_key_manager_changed_wot_secret_cb), self); g_signal_connect (G_OBJECT (op), "completed", G_CALLBACK (g_object_unref), self); } static void register_generate_operation (GpaKeyManager *self, GpaGenKeyOperation *op) { g_signal_connect_swapped (G_OBJECT (op), "generated_key", G_CALLBACK (gpa_key_manager_new_key_cb), self); g_signal_connect (G_OBJECT (op), "completed", G_CALLBACK (g_object_unref), self); } static void register_operation (GpaKeyManager *self, GpaOperation *op) { g_signal_connect (G_OBJECT (op), "completed", G_CALLBACK (g_object_unref), self); } /* delete the selected keys */ static void key_manager_delete (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaKeyManager *self = param; GList *selection = gpa_keylist_get_selected_keys (self->keylist, GPGME_PROTOCOL_UNKNOWN); GpaKeyDeleteOperation *op = gpa_key_delete_operation_new (GTK_WIDGET (self), selection); register_key_operation (self, GPA_KEY_OPERATION (op)); } /* Return true if the public key key has been signed by the key with the id key_id, otherwise return FALSE. */ static gboolean key_has_been_signed (const gpgme_key_t key, const gpgme_key_t signer_key) { gboolean uid_signed, key_signed; const char *signer_id; gpgme_key_sig_t sig; gpgme_user_id_t uid; signer_id = signer_key->subkeys->keyid; /* We consider the key signed if all user IDs have been signed. */ key_signed = TRUE; for (uid = key->uids; key_signed && uid; uid = uid->next) { uid_signed = FALSE; for (sig = uid->signatures; !uid_signed && sig; sig = sig->next) if (g_str_equal (signer_id, sig->keyid)) uid_signed = TRUE; key_signed = key_signed && uid_signed; } return key_signed; } /* Return true if the key sign button should be sensitive, i.e. if there is at least one selected key and there is a default key. */ static gboolean key_manager_can_sign (gpointer param) { gboolean result = FALSE; gpgme_key_t default_key; default_key = gpa_options_get_default_key (gpa_options_get_instance ()); if (default_key && key_manager_has_single_selection (param)) { /* The most important requirements have been met, now check if the selected key was already signed with the default key. */ GpaKeyManager *self = param; gpgme_key_t key = key_manager_current_key (self); if (key->protocol == GPGME_PROTOCOL_OpenPGP) result = ! key_has_been_signed (key, default_key); } else if (default_key && key_manager_has_selection (param)) /* Always allow signing many keys at once. */ result = TRUE; return result; } /* sign the selected keys */ static void key_manager_sign (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaKeyManager *self = param; GList *selection; GpaKeySignOperation *op; if (! gpa_keylist_has_selection (self->keylist)) { /* This shouldn't happen because the button should be grayed out in this case. */ gpa_window_error (_("No keys selected for signing."), GTK_WIDGET (self)); return; } selection = gpa_keylist_get_selected_keys (self->keylist, GPGME_PROTOCOL_OpenPGP); if (selection) { op = gpa_key_sign_operation_new (GTK_WIDGET (self), selection); register_key_operation (self, GPA_KEY_OPERATION (op)); } } /* Invoke the "edit key" dialog. */ static void key_manager_edit (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaKeyManager *self = param; gpgme_key_t key; GtkWidget *dialog; if (! key_manager_has_private_selected (self)) return; key = key_manager_current_key (self); if (! key) return; dialog = gpa_key_edit_dialog_new (GTK_WIDGET (self), key); gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE); g_signal_connect (G_OBJECT (dialog), "key_modified", G_CALLBACK (gpa_key_manager_key_modified), self); gtk_widget_show_all (dialog); } static void key_manager_trust (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaKeyManager *self = param; GList *selection; GpaKeyTrustOperation *op; /* FIXME: Key trust operation currently does not support more than one key at a time. */ if (! key_manager_has_single_selection (self)) return; selection = gpa_keylist_get_selected_keys (self->keylist, GPGME_PROTOCOL_OpenPGP); if (selection) { op = gpa_key_trust_operation_new (GTK_WIDGET (self), selection); register_key_operation (self, GPA_KEY_OPERATION (op)); } } /* Import keys. */ static void key_manager_import (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaKeyManager *self = param; GpaImportFileOperation *op; op = gpa_import_file_operation_new (GTK_WIDGET (self)); register_import_operation (self, GPA_IMPORT_OPERATION (op)); } /* Export the selected keys to a file. */ static void key_manager_export (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaKeyManager *self = param; GList *selection; GpaExportFileOperation *op; selection = gpa_keylist_get_selected_keys (self->keylist, GPGME_PROTOCOL_UNKNOWN); if (! selection) return; op = gpa_export_file_operation_new (GTK_WIDGET (self), selection); register_operation (self, GPA_OPERATION (op)); } /* Import a key from the keyserver. */ #ifdef ENABLE_KEYSERVER_SUPPORT static void key_manager_retrieve (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaKeyManager *self = param; GpaImportServerOperation *op; op = gpa_import_server_operation_new (GTK_WIDGET (self)); register_import_operation (self, GPA_IMPORT_OPERATION (op)); } #endif /*ENABLE_KEYSERVER_SUPPORT*/ /* Refresh keys from the keyserver. */ #ifdef ENABLE_KEYSERVER_SUPPORT static void key_manager_refresh_keys (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaKeyManager *self = param; GpaImportByKeyidOperation *op; GList *selection; /* FIXME: The refresh-from-server operation currently only supports one key at a time. */ if (!key_manager_has_single_selection (self)) return; selection = gpa_keylist_get_selected_keys (self->keylist, GPGME_PROTOCOL_OPENPGP); if (selection) { op = gpa_import_bykeyid_operation_new (GTK_WIDGET (self), (gpgme_key_t) selection->data); register_import_operation (self, GPA_IMPORT_OPERATION (op)); } } #endif /*ENABLE_KEYSERVER_SUPPORT*/ /* Send a key to the keyserver. */ #ifdef ENABLE_KEYSERVER_SUPPORT static void key_manager_send (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaKeyManager *self = param; GList *selection; GpaExportServerOperation *op; /* FIXME: The export-to-server operation currently only supports exporting one key at a time. */ if (! key_manager_has_single_selection (self)) return; selection = gpa_keylist_get_selected_keys (self->keylist, GPGME_PROTOCOL_OPENPGP); if (selection) { op = gpa_export_server_operation_new (GTK_WIDGET (self), selection); register_operation (self, GPA_OPERATION (op)); } } #endif /*ENABLE_KEYSERVER_SUPPORT*/ /* Backup the default keys. */ static void key_manager_backup (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaKeyManager *self = param; gpgme_key_t key; GpaBackupOperation *op; if (! key_manager_has_private_selected (self)) return; key = key_manager_current_key (self); if (! key) return; op = gpa_backup_operation_new (GTK_WIDGET (self), key); register_operation (self, GPA_OPERATION (op)); } /* Run the advanced key generation dialog and if the user clicked OK, generate a new key pair and update the key list. */ static void key_manager_generate_key_advanced (gpointer param) { GpaKeyManager *self = param; GpaGenKeyAdvancedOperation *op; op = gpa_gen_key_advanced_operation_new (GTK_WIDGET (self)); register_generate_operation (self, GPA_GEN_KEY_OPERATION (op)); } /* Call the key generation wizard and update the key list if necessary */ static void key_manager_generate_key_simple (gpointer param) { GpaKeyManager *self = param; GpaGenKeySimpleOperation *op; op = gpa_gen_key_simple_operation_new (GTK_WIDGET (self)); register_generate_operation (self, GPA_GEN_KEY_OPERATION (op)); } /* Generate a key. */ static void key_manager_generate_key (GSimpleAction *simple, GVariant *parameter, gpointer param) { if (gpa_options_get_simplified_ui (gpa_options_get_instance ())) key_manager_generate_key_simple (param); else key_manager_generate_key_advanced (param); } /* Update everything that has to be updated when the selection in the key list changes. */ static void keyring_selection_update_actions (GpaKeyManager *self) { update_selection_sensitive_actions (self); keyring_update_details (self); } /* Callback for key listings invoked with the "next_key" signal. Used to receive and set the new current key. */ static void key_manager_key_listed (GpaContext *ctx, gpgme_key_t key, gpointer param) { GpaKeyManager *self = param; gpgme_key_unref (self->current_key); self->current_key = key; keyring_selection_update_actions (self); } /* FIXME: CHECK! Signal handler for selection changes. */ static void key_manager_selection_changed (GtkTreeSelection *treeselection, gpointer param) { GpaKeyManager *self = param; GList *selection; /* Some other piece of the keyring wants us to ignore this signal. */ if (self->freeze_selection) return; /* Update the current key. */ if (self->current_key) { /* Remove the previous one. */ gpgme_key_unref (self->current_key); self->current_key = NULL; } /* Abort retrieval of the current key. */ if (gpa_context_busy (self->ctx)) gpgme_op_keylist_end (self->ctx->ctx); /* Load the new one. */ if (gpa_keylist_has_single_selection (self->keylist) && (selection = gpa_keylist_get_selected_keys (self->keylist, GPGME_PROTOCOL_UNKNOWN))) { gpg_error_t err; gpgme_key_t key; int old_mode; key = (gpgme_key_t) selection->data; old_mode = gpgme_get_keylist_mode (self->ctx->ctx); /* With all the signatures and validating for the sake of X.509. Note that we should not save and restore the old protocol because the protocol should not be changed before the gpgme_op_keylist_end. Saving and restoring the keylist mode is okay. */ gpgme_set_keylist_mode (self->ctx->ctx, (old_mode #ifdef GPGME_KEYLIST_MODE_WITH_TOFU | GPGME_KEYLIST_MODE_WITH_TOFU #endif | GPGME_KEYLIST_MODE_SIGS | GPGME_KEYLIST_MODE_VALIDATE)); gpgme_set_protocol (self->ctx->ctx, key->protocol); err = gpgme_op_keylist_start (self->ctx->ctx, key->subkeys->fpr, FALSE); if (gpg_err_code (err) != GPG_ERR_NO_ERROR) gpa_gpgme_warning (err); gpgme_set_keylist_mode (self->ctx->ctx, old_mode); g_list_free (selection); /* Make sure the actions that depend on a current key are disabled. */ disable_selection_sensitive_actions (self); } else keyring_selection_update_actions (self); } /* FIXME: CHECK THIS! Signal handler for the map signal. If the simplified_ui flag is set and there's no private key in the key ring, ask the user whether he wants to generate a key. If so, call key_manager_generate_key which runs the appropriate dialog. Also, if the simplified_ui flag is set, remind the user if he has not yet created a backup copy of his private key. */ static void key_manager_mapped (gpointer param) { static gboolean asked_about_key_generation = FALSE; static gboolean asked_about_key_backup = FALSE; GpaKeyManager *self = param; if (gpa_options_get_simplified_ui (gpa_options_get_instance ())) { /* FIXME: We assume that the only reason a user might not have a default key is because he has no private keys. */ if (! asked_about_key_generation && ! gpa_options_have_default_key (gpa_options_get_instance())) { GtkWidget *dialog; GtkResponseType response; dialog = gtk_message_dialog_new (GTK_WINDOW (GTK_WIDGET (self)), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("You do not have a private key " "yet. Do you want to generate " "one now (recommended) or do it" " later?")); gtk_dialog_add_buttons (GTK_DIALOG (dialog), _("_Generate key now"), GTK_RESPONSE_OK, _("Do it _later"), GTK_RESPONSE_CANCEL, NULL); response = gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); if (response == GTK_RESPONSE_OK) key_manager_generate_key (NULL, NULL, param); asked_about_key_generation = TRUE; } else if (!asked_about_key_backup && !gpa_options_get_backup_generated (gpa_options_get_instance ()) && !gpa_options_have_default_key (gpa_options_get_instance())) { GtkWidget *dialog; GtkResponseType response; dialog = gtk_message_dialog_new (GTK_WINDOW (GTK_WIDGET (self)), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("You do not have a backup copy of" " your private key yet." " Do you want to backup your key " "now (recommended) or do it " "later?")); gtk_dialog_add_buttons (GTK_DIALOG (dialog), _("_Backup key now"), GTK_RESPONSE_OK, _("Do it _later"), GTK_RESPONSE_CANCEL, NULL); response = gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); if (response == GTK_RESPONSE_OK) { gpgme_key_t key; GpaBackupOperation *op; key = gpa_options_get_default_key (gpa_options_get_instance ()); if (key) { op = gpa_backup_operation_new (GTK_WIDGET (self), key); register_operation (self, GPA_OPERATION (op)); } } asked_about_key_backup = TRUE; } } } /* Close the key manager. */ static void key_manager_close (GSimpleAction *simple, GVariant *parameter, gpointer param) { gtk_widget_destroy (GTK_WIDGET (this_instance)); } /* Quit the program */ static void key_manager_quit (GSimpleAction *simple, GVariant *parameter, gpointer param) { gtk_widget_destroy (GTK_WIDGET (this_instance)); } /* select all keys in the keyring */ static void key_manager_select_all (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaKeyManager *self = param; GtkTreeSelection *selection; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->keylist)); gtk_tree_selection_select_all (selection); } /* Paste the clipboard into the keyring. */ static void key_manager_paste (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaKeyManager *self = param; GpaImportClipboardOperation *op; op = gpa_import_clipboard_operation_new (GTK_WIDGET (self)); register_import_operation (self, GPA_IMPORT_OPERATION (op)); } /* Copy the keys into the clipboard. */ static void key_manager_copy (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaKeyManager *self = param; GList *selection; GpaExportClipboardOperation *op; selection = gpa_keylist_get_selected_keys (self->keylist, GPGME_PROTOCOL_UNKNOWN); if (! selection) return; op = gpa_export_clipboard_operation_new (GTK_WIDGET (self), selection, 0); register_operation (self, GPA_OPERATION (op)); } /* Copy the secret keys into the clipboard. */ static void key_manager_copy_sec (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaKeyManager *self = param; GList *selection; GpaExportClipboardOperation *op; selection = gpa_keylist_get_selected_keys (self->keylist, GPGME_PROTOCOL_UNKNOWN); if (!selection || g_list_length (selection) != 1) return; op = gpa_export_clipboard_operation_new (GTK_WIDGET (self), selection, 1); register_operation (self, GPA_OPERATION (op)); } /* Copy the fingerprints of the keys into the clipboard. */ static void key_manager_copy_fpr (GSimpleAction *simple, GVariant *parameter, gpointer param) { GpaKeyManager *self = param; GList *selection, *item; gpgme_protocol_t prot = GPGME_PROTOCOL_UNKNOWN; gpgme_key_t key; char *buffer = NULL; char *p; selection = gpa_keylist_get_selected_keys (self->keylist, GPGME_PROTOCOL_UNKNOWN); if (!selection) return; for (item = selection; item; item = g_list_next (item)) { key = (gpgme_key_t)item->data; if (prot == GPGME_PROTOCOL_UNKNOWN) prot = key->protocol; else if (prot != key->protocol) { gpa_window_error (_("Only keys of the same procotol may be copied."), NULL); g_free (buffer); buffer = NULL; break; } if (prot == GPGME_PROTOCOL_UNKNOWN) ; else if (!buffer) buffer = g_strconcat (key->subkeys->fpr, "\n", NULL); else { p = g_strconcat (buffer, key->subkeys->fpr, "\n", NULL); g_free (buffer); buffer = p; } } if (buffer) { gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY), buffer, -1); gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), buffer, -1); } g_free (buffer); } /* Reload the key list. */ static void key_manager_refresh (GSimpleAction *simple, GVariant *parameter, gpointer user_data) { GpaKeyManager *self = user_data; /* Hack: To force reloading of secret keys we claim that a secret key has been imported. */ gpa_keylist_imported_secret_key (self->keylist); gpa_keylist_start_reload (self->keylist); } static void keyring_set_listing_cb (GSimpleAction *action, GtkRadioAction *current_action, gpointer param) { GpaKeyManager *self = param; gint detailed; detailed = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current_action)); if (detailed) { gpa_keylist_set_detailed (self->keylist); gpa_options_set_detailed_view (gpa_options_get_instance (), TRUE); } else { gpa_keylist_set_brief (self->keylist); gpa_options_set_detailed_view (gpa_options_get_instance (), FALSE); } } /* Create and return the menu bar for the key ring editor. */ static void key_manager_action_new (GpaKeyManager *self, GtkWidget **menu, GtkWidget **toolbar, GtkWidget **popup) { static const GActionEntry entries[] = { { "file", NULL }, { "edit", NULL }, { "keys", NULL }, { "file_close", key_manager_close }, { "file_quit", key_manager_quit }, { "edit_copy", key_manager_copy }, { "edit_copy_fingerprint", key_manager_copy_fpr }, { "edit_copy_sec", key_manager_copy_sec }, { "edit_paste", key_manager_paste }, { "edit_select_all", key_manager_select_all }, { "keys_refresh", key_manager_refresh }, { "keys_new", key_manager_generate_key }, { "keys_delete", key_manager_delete }, { "keys_sign", key_manager_sign }, { "keys_set_owner_trust", key_manager_trust }, { "keys_edit_private_key", key_manager_edit }, { "keys_import_keys", key_manager_import }, { "keys_export_keys", key_manager_export}, { "keys_backup_key", key_manager_backup}, #ifdef ENABLE_KEYSERVER_SUPPORT { "server_retrive", key_manager_retrieve }, { "server_refresh", key_manager_refresh_keys }, { "server_send", key_manager_send }, #endif }; static const GtkRadioActionEntry radio_entries[] = { { "DetailsBrief", "brief", NULL, NULL, N_("Show Brief Keylist"), 0 }, { "DetailsDetailed", GPA_STOCK_DETAILED, NULL, NULL, N_("Show Key Details"), 1 } }; static const char *menu_string = "" "" "" "_File" "" "Close" "app.file_close" "" "" "Quit" "app.file_quit" "" "" "" "Edit" "" "Copy" "app.edit_copy" "" "" "Paste" "app.edit_paste" "" "
" "" "Select All" "app.edit_select_all" "" "
" "
" "" "Preferences" "app.edit_preferences" "" "" "Backend Preferences" "app.edit_backend_preferences" "" "
" "
" "" "Keys" "" "Refresh" "app.keys_refresh" "" "
" "" "New Key" "app.keys_new" "" "" "Delete Keys" "app.keys_delete" "" "
" "
" "" "Sign Keys" "app.keys_sign" "" "" "Set Owner Trust" "app.keys_set_owner_trust" "" "" "Edit Private Key" "app.keys_edit_private_key" "" "
" "
" "" "Import Keys" "app.keys_import_keys" "" "" "Export Keys" "app.keys_export_keys" "" "" "Backup..." "app.keys_backup_key" "" "
" "
" "" "Windows" "" "Keyring Manager" "app.windows_keyring_editor" "" "" "File Manager" "app.windows_file_manager" "" "" "Clipboard" "app.windows_clipboard" "" "" "Card Manager" "app.windows_card_manager" "" "" #ifdef ENABLE_KEYSERVER_SUPPORT "" "Server" "" "Retieve Keys..." "app.server_retrive" "" "" "Send Keys..." "app.server_send" "" "" #endif // ENABLE_KEYSERVER_SUPPORT "" "Help" "" "About" "app.help_about" "" "" "
" "" "True" "False" "False" "" "" "True" "False" "True" "edit" "" "" "False" "True" "" "" "" "" "True" "False" "True" "delete" "" "" "False" "True" "" "" "" "" "True" "False" "True" "sign" "" "" "False" "True" "" "" "" "" "True" "False" "True" "import" "" "" "False" "True" "" "" "" "" "True" "False" "True" "export" "" "" "False" "True" "" "" "" "" "" "" "" "" "True" "False" "True" "app.windows_file_manager" "brief" "" "" "False" "True" "" "" "" "" "True" "False" "True" "detailed" "" "" "False" "True" "" "" "" "" "" "" "" "" "True" "False" "True" "preferences-desktop" "app.edit_preferences" "" "" "False" "True" "" "" "" "" "" "" "" "" "True" "False" "True" "view-refresh" "app.windows_card_manager" "" "" "False" "True" "" "" "" "" "" "" "" "" "True" "False" "True" "folder" "app.windows_file_manager" "" "" "False" "True" "" "" "" "" "True" "False" "True" "edit-paste" "app.windows_clipboard" "" "" "False" "True" "" "" "" "" "True" "False" "True" "smartcard" "app.windows_card_manager" "" "" "False" "True" "" "" "" "" "
" // "horizontal-buttons" "" "Copy Fingerprint" "app.edit_copy_fingerprint" "" "" "Copy" "app.edit_copy" "" "" "Paste" "app.edit_paste" "" "" "Delete Keys" "app.keys_delete" "" "
" "
" "" "Sign Keys" "app.keys_sign" "" "" "Set Ownertrust" "app.keys_set_owner_trust" "" "" "Edit Private Key" "app.keys_edit_private_key" "" "" "Copy Private Key" "app.key_manager_copy_fpr" "" "
" "
" "" "Export Keys" "app.key_manager_export" "" "" "Update Keys" "app.key_manager_refresh" "" "" "Send Keys" "app.server_send" "" "" "Backup" "app.keys_backup_key" "" "
" "
" "
"; GError **err; GtkBuilder *gtk_builder = gtk_builder_new (); if (gtk_builder_add_from_string( gtk_builder, icons_string , -1, err) == 0) { printf("ERROR icons: %s \n", (*err)->message); } if (gtk_builder_add_from_string( gtk_builder, menu_string , -1, err) == 0) { printf("ERROR menu: %s \n", (*err)->message); } GMenuModel *menu_bar_model = G_MENU_MODEL (gtk_builder_get_object (GTK_BUILDER (gtk_builder), "menu")); *menu = gtk_menu_bar_new_from_model (menu_bar_model); GMenuModel *popup_menu_model = G_MENU_MODEL (gtk_builder_get_object (GTK_BUILDER (gtk_builder), "popupmenu")); *popup = gtk_menu_new_from_model (popup_menu_model); GtkWidget *grid = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (gtk_builder), "toolbar")); GtkCssProvider *css_provider = gtk_css_provider_new(); GdkDisplay *display = gdk_display_get_default(); GdkScreen *screen = gdk_display_get_default_screen (display); gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER(css_provider), GTK_STYLE_PROVIDER_PRIORITY_USER); gtk_css_provider_load_from_data(css_provider, "#toolbar {\n" //" padding-left: 55px;\n" // " padding-right: 5px;\n" // " border-radius: 3px;\n" "}\n", -1, NULL); GtkStyleContext *style_context; style_context = gtk_widget_get_style_context (GTK_WIDGET (grid)); //gtk_widget_add_css_class (grid, "toolbar"); // We must set the name to the toolbar for css to recognize it gtk_widget_set_name(GTK_WIDGET(grid), "toolbar"); gtk_style_context_add_class (style_context, "toolbar"); GApplication *gpa_app = G_APPLICATION (get_gpa_application ()); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), gpa_windows_menu_g_action_entries, G_N_ELEMENTS (gpa_windows_menu_g_action_entries), self); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), entries, G_N_ELEMENTS (entries), self); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), gpa_help_menu_g_action_entries, G_N_ELEMENTS (gpa_help_menu_g_action_entries), self); g_action_map_add_action_entries (G_ACTION_MAP (gpa_app), gpa_preferences_menu_g_action_entries, G_N_ELEMENTS (gpa_preferences_menu_g_action_entries), self); GSimpleAction *action; action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "edit_copy"); add_selection_sensitive_action (self, action, key_manager_has_selection); action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "edit_copy_fingerprint"); add_selection_sensitive_action (self, action, key_manager_has_single_selection); action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "edit_copy_sec"); add_selection_sensitive_action (self, action, key_manager_has_single_selection); action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "keys_delete"); add_selection_sensitive_action (self, action, key_manager_has_selection); action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "keys_export_keys"); add_selection_sensitive_action (self, action, key_manager_has_selection); #ifdef ENABLE_KEYSERVER_SUPPORT action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "server_refresh"); add_selection_sensitive_action (self, action, key_manager_has_single_selection); action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "server_send"); add_selection_sensitive_action (self, action, key_manager_has_single_selection); #endif // ENABLE_KEYSERVER_SUPPORT action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "keys_set_owner_trust"); add_selection_sensitive_action (self, action, key_manager_has_single_selection_OpenPGP); action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "keys_sign"); add_selection_sensitive_action (self, action, key_manager_can_sign); action = (GSimpleAction*)g_action_map_lookup_action (G_ACTION_MAP (gpa_app), "keys_edit_private_key"); add_selection_sensitive_action (self, action, key_manager_has_private_selected); *toolbar = GTK_WIDGET (grid); } /* Update the details widget according to the current selection. This means that if there is exactly one key selected, display its properties in the pages, otherwise show the number of currently selected keys. */ static int idle_update_details (gpointer param) { GpaKeyManager *self = param; if (gpa_keylist_has_single_selection (self->keylist)) { gpgme_key_t key = key_manager_current_key (self); if (! key) { /* There is a single key selected, but the current key is NULL. This means the key has not been returned yet, so we exit the function asking GTK to run it again when there is time. */ return TRUE; } gpa_key_details_update (self->details, key, 1); } else { GList *selection = gpa_keylist_get_selected_keys (self->keylist, GPGME_PROTOCOL_UNKNOWN); if (selection) { gpa_key_details_update (self->details, NULL, g_list_length (selection)); g_list_free (selection); } } /* Set the idle id to NULL to indicate that the idle handler has been run. */ self->details_idle_id = 0; /* Return false to indicate that this function shouldn't be called again by GTK, only when we expicitly add it again. */ return FALSE; } /* Add an idle handler to update the details, but only when none has been set yet. */ static void keyring_update_details (GpaKeyManager *self) { if (! self->details_idle_id) self->details_idle_id = g_idle_add (idle_update_details, self); } /* Status bar handling. */ static GtkWidget * keyring_statusbar_new (GpaKeyManager *self) { GtkWidget *hbox; GtkWidget *label; hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); label = gtk_label_new (""); self->status_label = label; gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); label = gtk_label_new (""); self->status_key_id = label; gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 5); label = gtk_label_new (""); self->status_key_user = label; gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); return hbox; } /* Update the status bar. */ static void keyring_update_status_bar (GpaKeyManager *self) { gpgme_key_t key = gpa_options_get_default_key (gpa_options_get_instance ()); if (key) { gchar *string; gtk_label_set_text (GTK_LABEL (self->status_label), _("Selected default key:")); string = gpa_gpgme_key_get_userid (key->uids); gtk_label_set_text (GTK_LABEL (self->status_key_user), string); g_free (string); gtk_label_set_text (GTK_LABEL (self->status_key_id), gpa_gpgme_key_get_short_keyid (key)); } else { gtk_label_set_text (GTK_LABEL (self->status_label), _("No default key selected in the preferences.")); gtk_label_set_text (GTK_LABEL (self->status_key_user), ""); gtk_label_set_text (GTK_LABEL (self->status_key_id), ""); } } /* FIXME: Check. */ /* The context menu of the keyring list. This is the callback for the "button_press_event" signal. */ static gint display_popup_menu (GpaKeyManager *self, GdkEvent *event, GpaKeyList *list) { GtkMenu *menu; GdkEventButton *event_button; g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (event != NULL, FALSE); menu = GTK_MENU (self->popup_menu); if (event->type == GDK_BUTTON_PRESS) { event_button = (GdkEventButton *) event; if (event_button->button == 3) { GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list)); GtkTreePath *path; GtkTreeIter iter; /* Make sure the clicked key is selected. */ if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (list), event_button->x, event_button->y, &path, NULL, NULL, NULL)) { gtk_tree_model_get_iter (gtk_tree_view_get_model (GTK_TREE_VIEW(list)), &iter, path); if (! gtk_tree_selection_iter_is_selected (selection, &iter)) { /* Block selection updates. */ self->freeze_selection++; gtk_tree_selection_unselect_all (selection); self->freeze_selection--; gtk_tree_selection_select_path (selection, path); } gtk_menu_popup_at_pointer (menu, NULL); } return TRUE; } } return FALSE; } /* Signal handler for the "changed_default_key" signal. */ static void keyring_default_key_changed (GpaOptions *options, gpointer param) { GpaKeyManager *self = param; /* Update the status bar and the selection sensitive widgets because some depend on the default key. */ keyring_update_status_bar (self); update_selection_sensitive_actions (self); } /* Create all the widgets of this window. */ static void construct_widgets (GpaKeyManager *self) { GtkWidget *vbox; GtkWidget *label; GtkWidget *scrolled; GtkWidget *keylist; GtkWidget *menubar; GtkWidget *toolbar; GtkWidget *hbox; GtkWidget *icon; GtkWidget *paned; GtkWidget *statusbar; GtkWidget *main_box; GtkWidget *align; gchar *markup; guint pt, pb, pl, pr; gpa_window_set_title (GTK_WINDOW (self), _("Key Manager")); gtk_window_set_default_size (GTK_WINDOW (self), 680, 600); g_signal_connect_swapped (G_OBJECT (self), "map", G_CALLBACK (key_manager_mapped), self); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_add (GTK_CONTAINER (self), vbox); /* FIXME: Check next line. */ key_manager_action_new (self, &menubar, &toolbar, &self->popup_menu); gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, TRUE, 0); /* Add a fancy label that tells us: This is the key manager. */ /* FIXME: We should have a common function for this. */ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5); icon = gtk_image_new_from_resource ("/org/gnupg/gpa/keyringeditor.xpm"); gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, TRUE, 0); label = gtk_label_new (NULL); markup = g_strdup_printf ("%s", _("Key Manager")); gtk_label_set_markup (GTK_LABEL (label), markup); g_free (markup); gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 10); - gtk_widget_set_halign (GTK_WIDGET (label), 0); - gtk_widget_set_valign (GTK_WIDGET (label), 0.5); + gtk_widget_set_halign (GTK_WIDGET (label), GTK_ALIGN_START); + gtk_widget_set_valign (GTK_WIDGET (label), GTK_ALIGN_CENTER); paned = gtk_paned_new (GTK_ORIENTATION_VERTICAL); main_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); align = gtk_alignment_new (0.5, 0.5, 1, 1); gtk_alignment_get_padding (GTK_ALIGNMENT (align), &pt, &pb, &pl, &pr); gtk_alignment_set_padding (GTK_ALIGNMENT (align), pt, pb + 5, pl + 5, pr + 5); gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (main_box), paned, TRUE, TRUE, 0); gtk_container_add (GTK_CONTAINER (align), main_box); scrolled = gtk_scrolled_window_new (NULL, NULL); gtk_paned_pack1 (GTK_PANED (paned), scrolled, TRUE, TRUE); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); /* FIXME: Which shadow type - get it from a global resource? */ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN); keylist = gpa_keylist_new (GTK_WIDGET (self)); self->keylist = GPA_KEYLIST (keylist); if (gpa_options_get_detailed_view (gpa_options_get_instance())) gpa_keylist_set_detailed (self->keylist); else gpa_keylist_set_brief (self->keylist); gtk_container_add (GTK_CONTAINER (scrolled), keylist); gtk_menu_attach_to_widget (GTK_MENU (self->popup_menu), GTK_WIDGET (keylist), NULL); g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (keylist))), "changed", G_CALLBACK (key_manager_selection_changed), self); g_signal_connect_swapped (G_OBJECT (keylist), "button_press_event", G_CALLBACK (display_popup_menu), self); self->details = gpa_key_details_new (); gtk_paned_pack2 (GTK_PANED (paned), self->details, TRUE, TRUE); gtk_paned_set_position (GTK_PANED (paned), 250); statusbar = keyring_statusbar_new (self); gtk_box_pack_start (GTK_BOX (vbox), statusbar, FALSE, TRUE, 0); g_signal_connect (G_OBJECT (gpa_options_get_instance ()), "changed_default_key", G_CALLBACK (keyring_default_key_changed), self); keyring_update_status_bar (self); update_selection_sensitive_actions (self); keyring_update_details (self); self->current_key = NULL; self->ctx = gpa_context_new (); self->freeze_selection = 0; g_signal_connect (G_OBJECT (self->ctx), "next_key", G_CALLBACK (key_manager_key_listed), self); } /* Callback for the "destroy" signal. */ static void gpa_key_manager_closed (GtkWidget *widget, gpointer param) { this_instance = NULL; } /************************************************************ ****************** Object Management ******************** ************************************************************/ static void gpa_key_manager_class_init (void *class_ptr, void *class_data) { GpaKeyManagerClass *klass = class_ptr; G_OBJECT_CLASS (klass)->finalize = gpa_key_manager_finalize; } static void gpa_key_manager_init (GTypeInstance *instance, void *class_ptr) { GpaKeyManager *self = GPA_KEY_MANAGER (instance); construct_widgets (self); g_signal_connect (self, "destroy", G_CALLBACK (gpa_key_manager_closed), self); } static void gpa_key_manager_finalize (GObject *object) { GpaKeyManager *self = GPA_KEY_MANAGER (object); g_list_free (self->selection_sensitive_actions); self->selection_sensitive_actions = NULL; G_OBJECT_CLASS (g_type_class_peek_parent (GPA_KEY_MANAGER_GET_CLASS (self)))->finalize (object); } GType gpa_key_manager_get_type (void) { static GType this_type = 0; if (!this_type) { static const GTypeInfo this_info = { sizeof (GpaKeyManagerClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, gpa_key_manager_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GpaKeyManager), 0, /* n_preallocs */ gpa_key_manager_init, }; this_type = g_type_register_static (GTK_TYPE_WINDOW, "GpaKeyManager", &this_info, 0); } return this_type; } /************************************************************ ********************** Public API ************************ ************************************************************/ /* Return the instance of the the key manager. If a new instance has been created, TRUE is stored at R_CREATED, otherwise FALSE. */ GtkWidget * gpa_key_manager_get_instance (gboolean *r_created) { if (r_created) *r_created = !this_instance; if (!this_instance) { this_instance = g_object_new (GPA_KEY_MANAGER_TYPE, NULL); /*FIXME load_all_keys (this_instance);*/ } return GTK_WIDGET (this_instance); } gboolean gpa_key_manager_is_open (void) { return !!this_instance; } /* Return true if we should ask for a first time key generation. * * This function basically duplicates the conditions from * key_manager_mapped. However that function mus be used from a * key_manager context and can't easily be used from other GPA * components. */ gboolean key_manager_maybe_firsttime (void) { if (!gpa_options_get_simplified_ui (gpa_options_get_instance ())) return FALSE; if (!gpa_options_have_default_key (gpa_options_get_instance())) return TRUE; if (!gpa_options_get_backup_generated (gpa_options_get_instance ())) return TRUE; return FALSE; }