diff options
Diffstat (limited to 'src/nautilus-tags-dialog.c')
-rw-r--r-- | src/nautilus-tags-dialog.c | 620 |
1 files changed, 620 insertions, 0 deletions
diff --git a/src/nautilus-tags-dialog.c b/src/nautilus-tags-dialog.c new file mode 100644 index 000000000..db194aa71 --- /dev/null +++ b/src/nautilus-tags-dialog.c @@ -0,0 +1,620 @@ +/* nautilus-tags-dialog.c + * + * Copyright (C) 2017 Alexandru Pandelea <alexandru.pandelea@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "nautilus-tags-dialog.h" +#include "nautilus-tag-manager.h" +#include "nautilus-tag-widget.h" + +typedef enum +{ + NO_ACTION, + UPDATE_TAGS +} ChosenAction; + +struct _NautilusTagsDialog +{ + GtkDialog parent; + + NautilusWindow *window; + GList *selection; + + GtkWidget *cancel_button; + GtkWidget *update_tags_button; + GtkWidget *tags_entry; + GtkWidget *color_button; + GtkWidget *tags_listbox; + GtkWidget *selection_tags_box; + GtkWidget *add_tag_button; + + NautilusTagManager *tag_manager; + GQueue *all_tags; + GQueue *selection_tags; + GQueue *new_selection_tags; + + /* queues that represent all the tags */ + GQueue *list_box_rows; + GQueue *all_tags_widgets; + /* initilay it has tags widgets that the selection + * has, but updates when a tag is removed/added */ + GQueue *selection_tags_widgets; + + ChosenAction action; + + GCancellable *cancellable_selection; + GCancellable *cancellable_update; + GCancellable *cancellable_get_files; +}; + +G_DEFINE_TYPE (NautilusTagsDialog, nautilus_tags_dialog, GTK_TYPE_DIALOG); + +static void on_selection_tags_obtained (GObject *object, + GAsyncResult *res, + gpointer user_data); + +static void on_tags_updated (GObject *object, + GAsyncResult *res, + gpointer user_data); + +static void +update_tags (NautilusTagsDialog *dialog) +{ + GQueue *all_tags_check; + GList *l; + GList *l_check; + TagData *tag_data; + TagData *tag_data_check; + + all_tags_check = nautilus_tag_manager_get_all_tags (dialog->tag_manager); + + for (l = g_queue_peek_head_link (dialog->all_tags), l_check = g_queue_peek_head_link (all_tags_check); + l != NULL && l_check != NULL; l = l->next, l_check = l_check->next) + { + tag_data = l->data; + tag_data_check = l_check->data; + + if (g_strcmp0 (tag_data->id, tag_data_check->id) != 0 || + g_strcmp0 (tag_data->name, tag_data_check->name) != 0) + { + return; + } + } + + nautilus_tag_manager_get_selection_tags (G_OBJECT (dialog), + dialog->selection, + on_selection_tags_obtained, + dialog->cancellable_selection); +} + +static gint +get_tag_position_in_listbox (NautilusTagsDialog *dialog, + const gchar *tag_name) +{ + GList *l; + gint index = 0; + g_autofree gchar *casefold_tag_name = NULL; + gchar *casefold_widget_tag_name; + + casefold_tag_name = g_utf8_casefold (tag_name, -1); + + for (l = g_queue_peek_head_link (dialog->all_tags_widgets), index = 0; + l != NULL; + l = l->next, index++) + { + casefold_widget_tag_name = g_utf8_casefold (nautilus_tag_widget_get_tag_name (NAUTILUS_TAG_WIDGET (l->data)), -1); + + if (g_strcmp0 (casefold_tag_name, casefold_widget_tag_name) < 0) + { + g_free (casefold_widget_tag_name); + + return index; + } + + g_free (casefold_widget_tag_name); + } + + return index; +} + +static gboolean +on_tag_widget_button_press_event (GtkWidget *old_tag_widget, + GdkEvent *event, + gpointer user_data) +{ + NautilusTagsDialog *dialog; + gboolean got_button; + guint button; + GtkWidget *row; + GtkWidget *tag_widget; + gint index; + + dialog = NAUTILUS_TAGS_DIALOG (user_data); + + got_button = gdk_event_get_button (event, &button); + + if (got_button && button == GDK_BUTTON_PRIMARY) + { + if (nautilus_tag_queue_has_tag (dialog->all_tags, + nautilus_tag_widget_get_tag_name (NAUTILUS_TAG_WIDGET (old_tag_widget)))) + { + row = gtk_list_box_row_new (); + + tag_widget = nautilus_tag_widget_new (nautilus_tag_widget_get_tag_name (NAUTILUS_TAG_WIDGET (old_tag_widget)), + nautilus_tag_widget_get_tag_id (NAUTILUS_TAG_WIDGET (old_tag_widget)), + FALSE); + + g_queue_push_tail (dialog->list_box_rows, row); + + gtk_container_add (GTK_CONTAINER (row), tag_widget); + gtk_widget_show_all (row); + + index = get_tag_position_in_listbox (dialog, + nautilus_tag_widget_get_tag_name (NAUTILUS_TAG_WIDGET (old_tag_widget))); + + gtk_list_box_insert (GTK_LIST_BOX (dialog->tags_listbox), + row, + index); + + g_queue_push_nth (dialog->all_tags_widgets, tag_widget, index); + } + + g_queue_remove (dialog->selection_tags_widgets, old_tag_widget); + + gtk_widget_destroy (old_tag_widget); + } + + return FALSE; +} + +static void +on_row_activated (GtkListBox *box, + GtkListBoxRow *row, + gpointer user_data) +{ + NautilusTagsDialog *dialog; + + dialog = NAUTILUS_TAGS_DIALOG (user_data); + + GtkWidget *old_tag_widget; + GtkWidget *tag_widget; + + old_tag_widget = gtk_bin_get_child (GTK_BIN (row)); + + tag_widget = nautilus_tag_widget_new (nautilus_tag_widget_get_tag_name (NAUTILUS_TAG_WIDGET (old_tag_widget)), + nautilus_tag_widget_get_tag_id (NAUTILUS_TAG_WIDGET (old_tag_widget)), + TRUE); + + g_signal_connect (tag_widget, + "button-press-event", + G_CALLBACK (on_tag_widget_button_press_event), + dialog); + + gtk_container_add (GTK_CONTAINER (dialog->selection_tags_box), tag_widget); + gtk_widget_show_all (dialog->selection_tags_box); + + g_queue_push_tail (dialog->selection_tags_widgets, tag_widget); + + g_queue_remove (dialog->all_tags_widgets, old_tag_widget); + g_queue_remove (dialog->list_box_rows, row); + gtk_widget_destroy (GTK_WIDGET (row)); +} + +static void +fill_selection_box (NautilusTagsDialog *dialog) +{ + GList *l; + GtkWidget *tag_widget; + TagData *tag_data; + gint index; + GtkListBoxRow *row; + + dialog->selection_tags_widgets = g_queue_new (); + + for (l = g_queue_peek_head_link (dialog->selection_tags); l != NULL; l = l->next) + { + tag_data = l->data; + + if (nautilus_tag_widget_queue_contains_tag (dialog->selection_tags_widgets, + tag_data->name) || tag_data->name == NULL) + { + continue; + } + + if (nautilus_tag_widget_queue_contains_tag (dialog->all_tags_widgets, + tag_data->name)) + { + tag_widget = nautilus_tag_widget_queue_get_tag_with_name (dialog->all_tags_widgets, + tag_data->name); + + index = g_queue_index (dialog->all_tags_widgets, tag_widget); + g_queue_remove (dialog->all_tags_widgets, tag_widget); + + row = gtk_list_box_get_row_at_index (GTK_LIST_BOX(dialog->tags_listbox), index); + g_queue_remove (dialog->list_box_rows, row); + gtk_widget_destroy (GTK_WIDGET (row)); + } + + tag_widget = nautilus_tag_widget_new (tag_data->name, tag_data->id, TRUE); + + g_signal_connect (tag_widget, + "button-press-event", + G_CALLBACK (on_tag_widget_button_press_event), + dialog); + + gtk_container_add (GTK_CONTAINER (dialog->selection_tags_box), tag_widget); + + g_queue_push_tail (dialog->selection_tags_widgets, tag_widget); + } + + gtk_widget_show_all (dialog->selection_tags_box); + + + if (!nautilus_tag_widget_queue_contains_tag (dialog->selection_tags_widgets, + gtk_entry_get_text (GTK_ENTRY (dialog->tags_entry))) && + g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (dialog->tags_entry)), "") != 0) + { + gtk_widget_set_sensitive (dialog->add_tag_button, TRUE); + } +} + +static void +fill_list_box (NautilusTagsDialog *dialog) +{ + GList *l; + TagData *tag_data; + GtkWidget *row; + GtkWidget *tag_widget; + + dialog->all_tags_widgets = g_queue_new (); + + for (l = g_queue_peek_head_link (dialog->all_tags); l != NULL; l = l->next) + { + tag_data = l->data; + + row = gtk_list_box_row_new (); + + tag_widget = nautilus_tag_widget_new (tag_data->name, tag_data->id, FALSE); + + g_queue_push_tail (dialog->list_box_rows, row); + + gtk_container_add (GTK_CONTAINER (row), tag_widget); + gtk_widget_show_all (row); + + gtk_container_add (GTK_CONTAINER (dialog->tags_listbox), row); + + g_queue_push_tail (dialog->all_tags_widgets, tag_widget); + } +} + +static void +nautilus_tags_dialog_on_response (NautilusTagsDialog *dialog, + gint response_id, + gpointer user_data) +{ + if (response_id == GTK_RESPONSE_OK) + { + dialog->action = UPDATE_TAGS; + + update_tags (dialog); + } + else + { + gtk_widget_destroy (GTK_WIDGET (dialog)); + } +} + +static void +on_selection_tags_obtained (GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + NautilusTagsDialog *dialog; + g_autoptr (GError) error = NULL; + TagData *tag_data; + GQueue *selection_tags_check; + GList *l_check; + GList *l; + TagData *tag_data_check; + NautilusTagWidget *tag_widget; + GTask *task; + + dialog = NAUTILUS_TAGS_DIALOG (object); + + if (dialog->action == NO_ACTION) + { + dialog->selection_tags = nautilus_tag_manager_gpointer_task_finish (object, res, &error); + + fill_selection_box (dialog); + } + else + { + selection_tags_check = nautilus_tag_manager_gpointer_task_finish (object, res, &error); + + for (l = g_queue_peek_head_link (dialog->selection_tags), l_check = g_queue_peek_head_link (selection_tags_check); + l != NULL && l_check != NULL; l = l->next, l_check = l_check->next) + { + tag_data = l->data; + tag_data_check = l_check->data; + + if (g_strcmp0 (tag_data->id, tag_data_check->id) != 0 || + g_strcmp0 (tag_data->name, tag_data_check->name) != 0) + { + return; + } + } + + dialog->new_selection_tags = g_queue_new (); + + for (l = g_queue_peek_head_link (dialog->selection_tags_widgets); l != NULL; l = l->next) + { + tag_widget = NAUTILUS_TAG_WIDGET (l->data); + + tag_data = nautilus_tag_data_new (nautilus_tag_widget_get_tag_id (tag_widget), + nautilus_tag_widget_get_tag_name (tag_widget), + NULL); + + g_queue_push_tail (dialog->new_selection_tags, tag_data); + } + + nautilus_tag_manager_update_tags (dialog->tag_manager, + G_OBJECT (dialog), + dialog->selection, + dialog->selection_tags, + dialog->new_selection_tags, + on_tags_updated, + dialog->cancellable_update); + } + + task = user_data; + g_clear_object (&task); +} + +static void +on_tags_updated (GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + NautilusTagsDialog *dialog; + g_autoptr (GError) error = NULL; + gboolean result; + GTask *task; + + dialog = NAUTILUS_TAGS_DIALOG (object); + + result = nautilus_tag_manager_update_tags_finish (object, res, &error); + + task = user_data; + g_clear_object (&task); + + if (!result) + { + g_warning ("something went wrong while updating tags"); + } + + gtk_widget_destroy (GTK_WIDGET (dialog)); + +} + +static void +on_tags_entry_activate (NautilusTagsDialog *dialog) +{ + if (gtk_widget_get_sensitive (dialog->add_tag_button)) + { + g_signal_emit_by_name (dialog->add_tag_button, "clicked", dialog); + } +} + +static void +on_tags_entry_changed (NautilusTagsDialog *dialog) +{ + /* check if selection has this tag and update add button acordingly */ + + if (nautilus_tag_widget_queue_contains_tag (dialog->selection_tags_widgets, + gtk_entry_get_text (GTK_ENTRY (dialog->tags_entry))) || + g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (dialog->tags_entry)), "") == 0 || + dialog->selection_tags_widgets == NULL) + { + gtk_widget_set_sensitive (dialog->add_tag_button, FALSE); + } + else + { + gtk_widget_set_sensitive (dialog->add_tag_button, TRUE); + } +} + +static void +on_add_button_clicked (GtkButton *button, + gpointer user_data) +{ + NautilusTagsDialog *dialog; + g_autofree gchar *tag_id = NULL; + g_autofree gchar *color_rgb = NULL; + const gchar *const_tag_id; + GdkRGBA color; + GtkWidget *tag_widget; + GtkWidget *old_tag_widget; + GtkListBoxRow *row; + gint index; + + dialog = NAUTILUS_TAGS_DIALOG (user_data); + + /* if tag exists but it's not in selection: move from listbox to box */ + if (nautilus_tag_queue_has_tag (dialog->all_tags, + gtk_entry_get_text (GTK_ENTRY (dialog->tags_entry)))) + { + old_tag_widget = nautilus_tag_widget_queue_get_tag_with_name (dialog->all_tags_widgets, + gtk_entry_get_text (GTK_ENTRY (dialog->tags_entry))); + + const_tag_id = nautilus_tag_widget_get_tag_id (NAUTILUS_TAG_WIDGET (old_tag_widget)); + + tag_widget = nautilus_tag_widget_new (gtk_entry_get_text (GTK_ENTRY (dialog->tags_entry)), + const_tag_id, + TRUE); + + index = g_queue_index (dialog->all_tags_widgets, old_tag_widget); + g_queue_remove (dialog->all_tags_widgets, old_tag_widget); + + row = gtk_list_box_get_row_at_index (GTK_LIST_BOX(dialog->tags_listbox), index); + g_queue_remove (dialog->list_box_rows, row); + gtk_widget_destroy (GTK_WIDGET (row)); + } + else + { + /* if tag doesn't exist and it's not in selection: create new widget for box */ + gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (dialog->color_button), &color); + color_rgb = gdk_rgba_to_string (&color); + tag_id = g_strdup_printf ("org:gnome:nautilus:tag:%s:%s", + gtk_entry_get_text (GTK_ENTRY (dialog->tags_entry)), + color_rgb); + + tag_widget = nautilus_tag_widget_new (gtk_entry_get_text (GTK_ENTRY (dialog->tags_entry)), tag_id, TRUE); + } + + g_signal_connect (tag_widget, + "button-press-event", + G_CALLBACK (on_tag_widget_button_press_event), + dialog); + + gtk_container_add (GTK_CONTAINER (dialog->selection_tags_box), tag_widget); + + g_queue_push_tail (dialog->selection_tags_widgets, tag_widget); + + gtk_widget_show_all (dialog->selection_tags_box); + + gtk_entry_set_text (GTK_ENTRY (dialog->tags_entry), ""); +} + +static void +nautilus_tags_dialog_finalize (GObject *object) +{ + NautilusTagsDialog *dialog; + + dialog = NAUTILUS_TAGS_DIALOG (object); + + g_cancellable_cancel (dialog->cancellable_selection); + g_clear_object (&dialog->cancellable_selection); + + g_cancellable_cancel (dialog->cancellable_update); + g_clear_object (&dialog->cancellable_update); + + g_cancellable_cancel (dialog->cancellable_get_files); + g_clear_object (&dialog->cancellable_get_files); + + if (dialog->all_tags) + { + g_queue_free_full (dialog->all_tags, nautilus_tag_data_free); + } + if (dialog->selection_tags) + { + g_queue_free_full (dialog->selection_tags, nautilus_tag_data_free); + } + if (dialog->new_selection_tags) + { + g_queue_free_full (dialog->new_selection_tags, nautilus_tag_data_free); + } + if (dialog->selection_tags_widgets) + { + g_queue_free (dialog->selection_tags_widgets); + } + if (dialog->all_tags_widgets) + { + g_queue_free (dialog->all_tags_widgets); + } + if (dialog->list_box_rows) + { + g_queue_free (dialog->list_box_rows); + } + + g_clear_object (&dialog->tag_manager); + + G_OBJECT_CLASS (nautilus_tags_dialog_parent_class)->finalize (object); +} + +static void +nautilus_tags_dialog_class_init (NautilusTagsDialogClass *klass) +{ + GtkWidgetClass *widget_class; + GObjectClass *oclass; + + widget_class = GTK_WIDGET_CLASS (klass); + oclass = G_OBJECT_CLASS (klass); + + oclass->finalize = nautilus_tags_dialog_finalize; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/nautilus/ui/nautilus-tags-dialog.ui"); + + gtk_widget_class_bind_template_child (widget_class, NautilusTagsDialog, cancel_button); + gtk_widget_class_bind_template_child (widget_class, NautilusTagsDialog, update_tags_button); + gtk_widget_class_bind_template_child (widget_class, NautilusTagsDialog, tags_entry); + gtk_widget_class_bind_template_child (widget_class, NautilusTagsDialog, color_button); + gtk_widget_class_bind_template_child (widget_class, NautilusTagsDialog, tags_listbox); + gtk_widget_class_bind_template_child (widget_class, NautilusTagsDialog, selection_tags_box); + gtk_widget_class_bind_template_child (widget_class, NautilusTagsDialog, add_tag_button); + + gtk_widget_class_bind_template_callback (widget_class, nautilus_tags_dialog_on_response); + gtk_widget_class_bind_template_callback (widget_class, on_tags_entry_activate); + gtk_widget_class_bind_template_callback (widget_class, on_tags_entry_changed); + gtk_widget_class_bind_template_callback (widget_class, on_add_button_clicked); +} + +GtkWidget* nautilus_tags_dialog_new (GList *selection, + NautilusWindow *window) +{ + NautilusTagsDialog *dialog; + + dialog = g_object_new (NAUTILUS_TYPE_TAGS_DIALOG, "use-header-bar", TRUE, NULL); + + dialog->window = window; + dialog->selection = selection; + + gtk_window_set_transient_for (GTK_WINDOW (dialog), + GTK_WINDOW (window)); + + g_signal_connect (dialog->tags_listbox, "row-activated", G_CALLBACK (on_row_activated), dialog); + + dialog->cancellable_selection = g_cancellable_new (); + dialog->cancellable_update = g_cancellable_new (); + + dialog->tag_manager = nautilus_tag_manager_new (NULL, NULL, NULL); + + dialog->all_tags = nautilus_tag_manager_get_all_tags (dialog->tag_manager); + + fill_list_box (dialog); + + dialog->action = NO_ACTION; + + nautilus_tag_manager_get_selection_tags (G_OBJECT (dialog), + selection, + on_selection_tags_obtained, + dialog->cancellable_selection); + + return GTK_WIDGET (dialog); +} + +static void +nautilus_tags_dialog_init (NautilusTagsDialog *self) +{ + GdkRGBA color; + + gtk_widget_init_template (GTK_WIDGET (self)); + + self->list_box_rows = g_queue_new (); + + gdk_rgba_parse (&color, "rgb(220,220,220)"); + gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (self->color_button), + &color); +}
\ No newline at end of file |