/* nautilus-compress-dialog-controller.h * * Copyright (C) 2016 the Nautilus developers * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, see . * */ #include #include #include #include #include "nautilus-compress-dialog-controller.h" #include "nautilus-global-preferences.h" struct _NautilusCompressDialogController { NautilusFileNameWidgetController parent_instance; GtkWidget *compress_dialog; GtkWidget *activate_button; GtkWidget *error_label; GtkWidget *name_entry; GtkWidget *extension_dropdown; GtkSizeGroup *extension_sizegroup; GtkWidget *passphrase_label; GtkWidget *passphrase_entry; const char *extension; gchar *passphrase; gulong response_handler_id; }; G_DEFINE_TYPE (NautilusCompressDialogController, nautilus_compress_dialog_controller, NAUTILUS_TYPE_FILE_NAME_WIDGET_CONTROLLER); #define NAUTILUS_TYPE_COMPRESS_ITEM (nautilus_compress_item_get_type ()) G_DECLARE_FINAL_TYPE (NautilusCompressItem, nautilus_compress_item, NAUTILUS, COMPRESS_ITEM, GObject) struct _NautilusCompressItem { GObject parent_instance; NautilusCompressionFormat format; char *extension; char *description; }; G_DEFINE_TYPE (NautilusCompressItem, nautilus_compress_item, G_TYPE_OBJECT); static void nautilus_compress_item_init (NautilusCompressItem *item) { } static void nautilus_compress_item_finalize (GObject *object) { NautilusCompressItem *item = NAUTILUS_COMPRESS_ITEM (object); g_free (item->extension); g_free (item->description); G_OBJECT_CLASS (nautilus_compress_item_parent_class)->finalize (object); } static void nautilus_compress_item_class_init (NautilusCompressItemClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); object_class->finalize = nautilus_compress_item_finalize; } static NautilusCompressItem * nautilus_compress_item_new (NautilusCompressionFormat format, const char *extension, const char *description) { NautilusCompressItem *item = g_object_new (NAUTILUS_TYPE_COMPRESS_ITEM, NULL); item->format = format; item->extension = g_strdup (extension); item->description = g_strdup (description); return item; } static gboolean nautilus_compress_dialog_controller_name_is_valid (NautilusFileNameWidgetController *self, gchar *name, gchar **error_message) { gboolean is_valid; is_valid = TRUE; if (strlen (name) == 0) { is_valid = FALSE; } else if (strstr (name, "/") != NULL) { is_valid = FALSE; *error_message = _("Archive names cannot contain “/”."); } else if (strcmp (name, ".") == 0) { is_valid = FALSE; *error_message = _("An archive cannot be called “.”."); } else if (strcmp (name, "..") == 0) { is_valid = FALSE; *error_message = _("An archive cannot be called “..”."); } else if (nautilus_file_name_widget_controller_is_name_too_long (self, name)) { is_valid = FALSE; *error_message = _("Archive name is too long."); } if (is_valid && g_str_has_prefix (name, ".")) { /* We must warn about the side effect */ *error_message = _("Archives with “.” at the beginning of their name are hidden."); } return is_valid; } static gchar * nautilus_compress_dialog_controller_get_new_name (NautilusFileNameWidgetController *controller) { NautilusCompressDialogController *self; g_autofree gchar *basename = NULL; gchar *error_message = NULL; gboolean valid_name; self = NAUTILUS_COMPRESS_DIALOG_CONTROLLER (controller); basename = NAUTILUS_FILE_NAME_WIDGET_CONTROLLER_CLASS (nautilus_compress_dialog_controller_parent_class)->get_new_name (controller); /* Do not check or add the extension if the name is invalid */ valid_name = nautilus_compress_dialog_controller_name_is_valid (controller, basename, &error_message); if (!valid_name) { return g_strdup (basename); } if (g_str_has_suffix (basename, self->extension)) { return g_strdup (basename); } return g_strconcat (basename, self->extension, NULL); } static void compress_dialog_controller_on_response (GtkDialog *dialog, gint response_id, gpointer user_data) { NautilusCompressDialogController *controller; controller = NAUTILUS_COMPRESS_DIALOG_CONTROLLER (user_data); if (response_id != GTK_RESPONSE_OK) { g_signal_emit_by_name (controller, "cancelled"); } } static void update_selected_format (NautilusCompressDialogController *self) { gboolean show_passphrase = FALSE; guint selected; GListModel *model; NautilusCompressItem *item; selected = gtk_drop_down_get_selected (GTK_DROP_DOWN (self->extension_dropdown)); if (selected == GTK_INVALID_LIST_POSITION) { return; } model = gtk_drop_down_get_model (GTK_DROP_DOWN (self->extension_dropdown)); item = g_list_model_get_item (model, selected); if (item == NULL) { return; } if (item->format == NAUTILUS_COMPRESSION_ENCRYPTED_ZIP) { show_passphrase = TRUE; } self->extension = item->extension; gtk_widget_set_visible (self->passphrase_label, show_passphrase); gtk_widget_set_visible (self->passphrase_entry, show_passphrase); if (!show_passphrase) { gtk_editable_set_text (GTK_EDITABLE (self->passphrase_entry), ""); gtk_entry_set_visibility (GTK_ENTRY (self->passphrase_entry), FALSE); gtk_entry_set_icon_from_icon_name (GTK_ENTRY (self->passphrase_entry), GTK_ENTRY_ICON_SECONDARY, "view-conceal"); } g_settings_set_enum (nautilus_compression_preferences, NAUTILUS_PREFERENCES_DEFAULT_COMPRESSION_FORMAT, item->format); /* Since the extension changes when the button is toggled, force a * verification of the new file name by simulating an entry change */ gtk_widget_set_sensitive (self->activate_button, FALSE); g_signal_emit_by_name (self->name_entry, "changed"); } static void extension_dropdown_setup_item (GtkSignalListItemFactory *factory, GtkListItem *item, gpointer user_data) { GtkWidget *title; title = gtk_label_new (""); gtk_label_set_xalign (GTK_LABEL (title), 0.0); g_object_set_data (G_OBJECT (item), "title", title); gtk_list_item_set_child (item, title); } static void extension_dropdown_setup_item_full (GtkSignalListItemFactory *factory, GtkListItem *item, gpointer user_data) { GtkWidget *hbox, *vbox, *title, *subtitle, *checkmark; title = gtk_label_new (""); gtk_label_set_xalign (GTK_LABEL (title), 0.0); gtk_widget_set_halign (title, GTK_ALIGN_START); subtitle = gtk_label_new (""); gtk_label_set_xalign (GTK_LABEL (subtitle), 0.0); gtk_widget_add_css_class (subtitle, "dim-label"); gtk_widget_add_css_class (subtitle, "caption"); checkmark = gtk_image_new_from_icon_name ("object-select-symbolic"); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3); gtk_widget_set_hexpand (vbox, TRUE); gtk_box_append (GTK_BOX (hbox), vbox); gtk_box_append (GTK_BOX (vbox), title); gtk_box_append (GTK_BOX (vbox), subtitle); gtk_box_append (GTK_BOX (hbox), checkmark); g_object_set_data (G_OBJECT (item), "title", title); g_object_set_data (G_OBJECT (item), "subtitle", subtitle); g_object_set_data (G_OBJECT (item), "checkmark", checkmark); gtk_list_item_set_child (item, hbox); } static void extension_dropdown_on_selected_item_notify (GtkDropDown *dropdown, GParamSpec *pspec, GtkListItem *item) { GtkWidget *checkmark; checkmark = g_object_get_data (G_OBJECT (item), "checkmark"); if (gtk_drop_down_get_selected_item (dropdown) == gtk_list_item_get_item (item)) { gtk_widget_set_opacity (checkmark, 1.0); } else { gtk_widget_set_opacity (checkmark, 0.0); } } static void extension_dropdown_bind (GtkSignalListItemFactory *factory, GtkListItem *list_item, gpointer user_data) { NautilusCompressDialogController *self; GtkWidget *title, *subtitle, *checkmark; NautilusCompressItem *item; self = NAUTILUS_COMPRESS_DIALOG_CONTROLLER (user_data); item = gtk_list_item_get_item (list_item); title = g_object_get_data (G_OBJECT (list_item), "title"); subtitle = g_object_get_data (G_OBJECT (list_item), "subtitle"); checkmark = g_object_get_data (G_OBJECT (list_item), "checkmark"); gtk_label_set_label (GTK_LABEL (title), item->extension); gtk_size_group_add_widget (self->extension_sizegroup, title); if (item->format == NAUTILUS_COMPRESSION_ENCRYPTED_ZIP) { gtk_widget_add_css_class (title, "encrypted_zip"); } if (subtitle) { gtk_label_set_label (GTK_LABEL (subtitle), item->description); } if (checkmark) { g_signal_connect (self->extension_dropdown, "notify::selected-item", G_CALLBACK (extension_dropdown_on_selected_item_notify), list_item); extension_dropdown_on_selected_item_notify (GTK_DROP_DOWN (self->extension_dropdown), NULL, list_item); } } static void extension_dropdown_unbind (GtkSignalListItemFactory *factory, GtkListItem *item, gpointer user_data) { NautilusCompressDialogController *self; GtkWidget *title; self = NAUTILUS_COMPRESS_DIALOG_CONTROLLER (user_data); g_signal_handlers_disconnect_by_func (self->extension_dropdown, extension_dropdown_on_selected_item_notify, item); title = g_object_get_data (G_OBJECT (item), "title"); if (title) { gtk_widget_remove_css_class (title, "encrypted_zip"); } } static void passphrase_entry_on_changed (GtkEditable *editable, gpointer user_data) { NautilusCompressDialogController *self; const gchar *error_message; self = NAUTILUS_COMPRESS_DIALOG_CONTROLLER (user_data); g_free (self->passphrase); self->passphrase = g_strdup (gtk_editable_get_text (GTK_EDITABLE (self->passphrase_entry))); /* Simulate a change of the name_entry to ensure the correct sensitivity of * the activate_button, but only if the name_entry is valid in order to * avoid changes of the error_revealer. */ error_message = gtk_label_get_text (GTK_LABEL (self->error_label)); if (error_message[0] == '\0') { gtk_widget_set_sensitive (self->activate_button, FALSE); g_signal_emit_by_name (self->name_entry, "changed"); } } static void passphrase_entry_on_icon_press (GtkEntry *entry, GtkEntryIconPosition icon_pos, gpointer user_data) { NautilusCompressDialogController *self; gboolean visibility; self = NAUTILUS_COMPRESS_DIALOG_CONTROLLER (user_data); visibility = gtk_entry_get_visibility (GTK_ENTRY (self->passphrase_entry)); gtk_entry_set_icon_from_icon_name (GTK_ENTRY (self->passphrase_entry), GTK_ENTRY_ICON_SECONDARY, visibility ? "view-conceal" : "view-reveal"); gtk_entry_set_visibility (GTK_ENTRY (self->passphrase_entry), !visibility); } static void activate_button_on_sensitive_notify (GObject *gobject, GParamSpec *pspec, gpointer user_data) { NautilusCompressDialogController *self; NautilusCompressionFormat format; self = NAUTILUS_COMPRESS_DIALOG_CONTROLLER (user_data); format = g_settings_get_enum (nautilus_compression_preferences, NAUTILUS_PREFERENCES_DEFAULT_COMPRESSION_FORMAT); if (format == NAUTILUS_COMPRESSION_ENCRYPTED_ZIP && (self->passphrase == NULL || self->passphrase[0] == '\0')) { /* Reset sensitivity of the activate_button if password is not set. */ gtk_widget_set_sensitive (self->activate_button, FALSE); } } static void extension_dropdown_setup (NautilusCompressDialogController *self) { GtkListItemFactory *factory, *list_factory; GListStore *store; NautilusCompressItem *item; NautilusCompressionFormat format; gint i; store = g_list_store_new (NAUTILUS_TYPE_COMPRESS_ITEM); item = nautilus_compress_item_new (NAUTILUS_COMPRESSION_ZIP, ".zip", _("Compatible with all operating systems.")); g_list_store_append (store, item); g_object_unref (item); item = nautilus_compress_item_new (NAUTILUS_COMPRESSION_ENCRYPTED_ZIP, ".zip", _("Password protected .zip, must be installed on Windows and Mac.")); g_list_store_append (store, item); g_object_unref (item); item = nautilus_compress_item_new (NAUTILUS_COMPRESSION_TAR_XZ, ".tar.xz", _("Smaller archives but Linux and Mac only.")); g_list_store_append (store, item); g_object_unref (item); item = nautilus_compress_item_new (NAUTILUS_COMPRESSION_7ZIP, ".7z", _("Smaller archives but must be installed on Windows and Mac.")); g_list_store_append (store, item); g_object_unref (item); factory = gtk_signal_list_item_factory_new (); g_signal_connect_object (factory, "setup", G_CALLBACK (extension_dropdown_setup_item), self, 0); g_signal_connect_object (factory, "bind", G_CALLBACK (extension_dropdown_bind), self, 0); g_signal_connect_object (factory, "unbind", G_CALLBACK (extension_dropdown_unbind), self, 0); list_factory = gtk_signal_list_item_factory_new (); g_signal_connect_object (list_factory, "setup", G_CALLBACK (extension_dropdown_setup_item_full), self, 0); g_signal_connect_object (list_factory, "bind", G_CALLBACK (extension_dropdown_bind), self, 0); g_signal_connect_object (list_factory, "unbind", G_CALLBACK (extension_dropdown_unbind), self, 0); gtk_drop_down_set_factory (GTK_DROP_DOWN (self->extension_dropdown), factory); gtk_drop_down_set_list_factory (GTK_DROP_DOWN (self->extension_dropdown), list_factory); gtk_drop_down_set_model (GTK_DROP_DOWN (self->extension_dropdown), G_LIST_MODEL (store)); format = g_settings_get_enum (nautilus_compression_preferences, NAUTILUS_PREFERENCES_DEFAULT_COMPRESSION_FORMAT); for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (store)); i++) { item = g_list_model_get_item (G_LIST_MODEL (store), i); if (item->format == format) { gtk_drop_down_set_selected (GTK_DROP_DOWN (self->extension_dropdown), i); update_selected_format (self); g_object_unref (item); break; } g_object_unref (item); } g_object_unref (store); g_object_unref (factory); g_object_unref (list_factory); } NautilusCompressDialogController * nautilus_compress_dialog_controller_new (GtkWindow *parent_window, NautilusDirectory *destination_directory, gchar *initial_name) { NautilusCompressDialogController *self; g_autoptr (GtkBuilder) builder = NULL; GtkWidget *compress_dialog; GtkWidget *error_revealer; GtkWidget *error_label; GtkWidget *name_entry; GtkWidget *activate_button; GtkWidget *extension_dropdown; GtkSizeGroup *extension_sizegroup; GtkWidget *passphrase_label; GtkWidget *passphrase_entry; builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/ui/nautilus-compress-dialog.ui"); compress_dialog = GTK_WIDGET (gtk_builder_get_object (builder, "compress_dialog")); error_revealer = GTK_WIDGET (gtk_builder_get_object (builder, "error_revealer")); error_label = GTK_WIDGET (gtk_builder_get_object (builder, "error_label")); name_entry = GTK_WIDGET (gtk_builder_get_object (builder, "name_entry")); activate_button = GTK_WIDGET (gtk_builder_get_object (builder, "activate_button")); extension_dropdown = GTK_WIDGET (gtk_builder_get_object (builder, "extension_dropdown")); extension_sizegroup = GTK_SIZE_GROUP (gtk_builder_get_object (builder, "extension_sizegroup")); passphrase_label = GTK_WIDGET (gtk_builder_get_object (builder, "passphrase_label")); passphrase_entry = GTK_WIDGET (gtk_builder_get_object (builder, "passphrase_entry")); gtk_window_set_transient_for (GTK_WINDOW (compress_dialog), parent_window); self = g_object_new (NAUTILUS_TYPE_COMPRESS_DIALOG_CONTROLLER, "error-revealer", error_revealer, "error-label", error_label, "name-entry", name_entry, "activate-button", activate_button, "containing-directory", destination_directory, NULL); self->compress_dialog = compress_dialog; self->activate_button = activate_button; self->error_label = error_label; self->name_entry = name_entry; self->extension_dropdown = extension_dropdown; self->extension_sizegroup = extension_sizegroup; self->passphrase_label = passphrase_label; self->passphrase_entry = passphrase_entry; extension_dropdown_setup (self); self->response_handler_id = g_signal_connect (compress_dialog, "response", (GCallback) compress_dialog_controller_on_response, self); g_signal_connect (self->passphrase_entry, "changed", G_CALLBACK (passphrase_entry_on_changed), self); g_signal_connect (self->passphrase_entry, "icon-press", G_CALLBACK (passphrase_entry_on_icon_press), self); g_signal_connect (self->activate_button, "notify::sensitive", G_CALLBACK (activate_button_on_sensitive_notify), self); g_signal_connect_swapped (self->extension_dropdown, "notify::selected-item", G_CALLBACK (update_selected_format), self); if (initial_name != NULL) { gtk_editable_set_text (GTK_EDITABLE (name_entry), initial_name); } gtk_widget_show (compress_dialog); return self; } static void nautilus_compress_dialog_controller_init (NautilusCompressDialogController *self) { } static void nautilus_compress_dialog_controller_finalize (GObject *object) { NautilusCompressDialogController *self; self = NAUTILUS_COMPRESS_DIALOG_CONTROLLER (object); if (self->compress_dialog != NULL) { g_clear_signal_handler (&self->response_handler_id, self->compress_dialog); gtk_window_destroy (GTK_WINDOW (self->compress_dialog)); self->compress_dialog = NULL; } g_free (self->passphrase); G_OBJECT_CLASS (nautilus_compress_dialog_controller_parent_class)->finalize (object); } static void nautilus_compress_dialog_controller_class_init (NautilusCompressDialogControllerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); NautilusFileNameWidgetControllerClass *parent_class = NAUTILUS_FILE_NAME_WIDGET_CONTROLLER_CLASS (klass); object_class->finalize = nautilus_compress_dialog_controller_finalize; parent_class->get_new_name = nautilus_compress_dialog_controller_get_new_name; parent_class->name_is_valid = nautilus_compress_dialog_controller_name_is_valid; } const gchar * nautilus_compress_dialog_controller_get_passphrase (NautilusCompressDialogController *self) { return self->passphrase; }