Logo Search packages:      
Sourcecode: nautilus version File versions  Download package

nautilus-file-conflict-dialog.c

/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */

/* nautilus-file-conflict-dialog: dialog that handles file conflicts
   during transfer operations.

   Copyright (C) 2008-2010 Cosimo Cecchi

   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, write to the
   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
   
   Authors: Cosimo Cecchi <cosimoc@gnome.org>
*/

#include <config.h>
#include "nautilus-file-conflict-dialog.h"

#include <string.h>
#include <glib-object.h>
#include <gio/gio.h>
#include <glib/gi18n.h>
#include <pango/pango.h>
#include <eel/eel-vfs-extensions.h>

#include "nautilus-file.h"
#include "nautilus-icon-info.h"

00039 struct _NautilusFileConflictDialogDetails
{
      /* conflicting objects */
      NautilusFile *source;
      NautilusFile *destination;
      NautilusFile *dest_dir;

      gchar *conflict_name;
      NautilusFileListHandle *handle;
      gulong src_handler_id;
      gulong dest_handler_id;

      /* UI objects */
      GtkWidget *titles_vbox;
      GtkWidget *first_hbox;
      GtkWidget *second_hbox;
      GtkWidget *expander;
      GtkWidget *entry;
      GtkWidget *checkbox;
      GtkWidget *rename_button;
      GtkWidget *replace_button;
      GtkWidget *dest_image;
      GtkWidget *src_image;
};

G_DEFINE_TYPE (NautilusFileConflictDialog,
             nautilus_file_conflict_dialog,
             GTK_TYPE_DIALOG);

#define NAUTILUS_FILE_CONFLICT_DIALOG_GET_PRIVATE(object)         \
      (G_TYPE_INSTANCE_GET_PRIVATE ((object), NAUTILUS_TYPE_FILE_CONFLICT_DIALOG, \
                              NautilusFileConflictDialogDetails))

static void
file_icons_changed (NautilusFile *file,
                NautilusFileConflictDialog *fcd)
{
      GdkPixbuf *pixbuf;

      pixbuf = nautilus_file_get_icon_pixbuf (fcd->details->destination,
                                    NAUTILUS_ICON_SIZE_LARGE,
                                    TRUE,
                                    NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS);

      gtk_image_set_from_pixbuf (GTK_IMAGE (fcd->details->dest_image), pixbuf);
      g_object_unref (pixbuf);

      pixbuf = nautilus_file_get_icon_pixbuf (fcd->details->source,
                                    NAUTILUS_ICON_SIZE_LARGE,
                                    TRUE,
                                    NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS);

      gtk_image_set_from_pixbuf (GTK_IMAGE (fcd->details->src_image), pixbuf);
      g_object_unref (pixbuf);
}

static void
file_list_ready_cb (GList *files,
                gpointer user_data)
{
      NautilusFileConflictDialog *fcd = user_data;
      NautilusFile *src, *dest, *dest_dir;
      time_t src_mtime, dest_mtime;
      gboolean source_is_dir, dest_is_dir, should_show_type;
      NautilusFileConflictDialogDetails *details;
      char *primary_text, *message, *secondary_text;
      const gchar *message_extra;
      char *dest_name, *dest_dir_name, *edit_name;
      char *label_text;
      char *size, *date, *type = NULL;
      GdkPixbuf *pixbuf;
      GtkWidget *label;
      GString *str;
      PangoAttrList *attr_list;

      details = fcd->details;

      details->handle = NULL;

      dest_dir = g_list_nth_data (files, 0);
      dest = g_list_nth_data (files, 1);
      src = g_list_nth_data (files, 2);

      src_mtime = nautilus_file_get_mtime (src);
      dest_mtime = nautilus_file_get_mtime (dest);

      dest_name = nautilus_file_get_display_name (dest);
      dest_dir_name = nautilus_file_get_display_name (dest_dir);

      source_is_dir = nautilus_file_is_directory (src);
      dest_is_dir = nautilus_file_is_directory (dest);

      type = nautilus_file_get_mime_type (dest);
      should_show_type = !nautilus_file_is_mime_type (src, type);

      g_free (type);
      type = NULL;

      /* Set up the right labels */
      if (dest_is_dir) {
            if (source_is_dir) {
                  primary_text = g_strdup_printf
                        (_("Merge folder \"%s\"?"),
                         dest_name);

                  message_extra = 
                        _("Merging will ask for confirmation before replacing any files in "
                          "the folder that conflict with the files being copied.");

                  if (src_mtime > dest_mtime) {
                        message = g_strdup_printf (
                              _("An older folder with the same name already exists in \"%s\"."),
                              dest_dir_name);
                  } else if (src_mtime < dest_mtime) {
                        message = g_strdup_printf (
                              _("A newer folder with the same name already exists in \"%s\"."),
                              dest_dir_name);
                  } else {
                        message = g_strdup_printf (
                              _("Another folder with the same name already exists in \"%s\"."),
                              dest_dir_name);
                  }
            } else {
                  message_extra =
                        _("Replacing it will remove all files in the folder.");
                  primary_text = g_strdup_printf
                        (_("Replace folder \"%s\"?"), dest_name);
                  message = g_strdup_printf
                        (_("A folder with the same name already exists in \"%s\"."),
                         dest_dir_name);
            }
      } else {
            primary_text = g_strdup_printf
                  (_("Replace file \"%s\"?"), dest_name);

            message_extra = _("Replacing it will overwrite its content.");

            if (src_mtime > dest_mtime) {
                  message = g_strdup_printf (
                        _("An older file with the same name already exists in \"%s\"."),
                        dest_dir_name);
            } else if (src_mtime < dest_mtime) {
                  message = g_strdup_printf (
                        _("A newer file with the same name already exists in \"%s\"."),
                        dest_dir_name);
            } else {
                  message = g_strdup_printf (
                        _("Another file with the same name already exists in \"%s\"."),
                        dest_dir_name);
            }
      }

      secondary_text = g_strdup_printf ("%s\n%s", message, message_extra);
      g_free (message);

      label = gtk_label_new (primary_text);
      gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
      gtk_label_set_line_wrap_mode (GTK_LABEL (label), PANGO_WRAP_WORD_CHAR);
      gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
      gtk_box_pack_start (GTK_BOX (details->titles_vbox),
                      label, FALSE, FALSE, 0);
      gtk_widget_show (label);

      attr_list = pango_attr_list_new ();
      pango_attr_list_insert (attr_list, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
      pango_attr_list_insert (attr_list, pango_attr_scale_new (PANGO_SCALE_LARGE));
      g_object_set (label,
                  "attributes", attr_list,
                  NULL);

      pango_attr_list_unref (attr_list);

      label = gtk_label_new (secondary_text);
      gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
      gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
      gtk_box_pack_start (GTK_BOX (details->titles_vbox),
                      label, FALSE, FALSE, 0);
      gtk_widget_show (label);
      g_free (primary_text);
      g_free (secondary_text);

      /* Set up file icons */
      pixbuf = nautilus_file_get_icon_pixbuf (dest,
                                    NAUTILUS_ICON_SIZE_LARGE,
                                    TRUE,
                                    NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS);
      details->dest_image = gtk_image_new_from_pixbuf (pixbuf);
      gtk_box_pack_start (GTK_BOX (details->first_hbox),
                      details->dest_image, FALSE, FALSE, 0);
      gtk_widget_show (details->dest_image);
      g_object_unref (pixbuf);

      pixbuf = nautilus_file_get_icon_pixbuf (src,
                                    NAUTILUS_ICON_SIZE_LARGE,
                                    TRUE,
                                    NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS);
      details->src_image = gtk_image_new_from_pixbuf (pixbuf);
      gtk_box_pack_start (GTK_BOX (details->second_hbox),
                      details->src_image, FALSE, FALSE, 0);
      gtk_widget_show (details->src_image);
      g_object_unref (pixbuf);

      /* Set up labels */
      label = gtk_label_new (NULL);
      date = nautilus_file_get_string_attribute (dest,
                                       "date_modified");
      size = nautilus_file_get_string_attribute (dest, "size");

      if (should_show_type) {
            type = nautilus_file_get_string_attribute (dest, "type");
      }

      str = g_string_new (NULL);
      g_string_append_printf (str, "<b>%s</b>\n", _("Original file"));
      g_string_append_printf (str, "<i>%s</i> %s\n", _("Size:"), size);

      if (should_show_type) {
            g_string_append_printf (str, "<i>%s</i> %s\n", _("Type:"), type);
      }

      g_string_append_printf (str, "<i>%s</i> %s", _("Last modified:"), date);

      label_text = str->str;
      gtk_label_set_markup (GTK_LABEL (label),
                        label_text);
      gtk_box_pack_start (GTK_BOX (details->first_hbox),
                      label, FALSE, FALSE, 0);
      gtk_widget_show (label);

      g_free (size);
      g_free (type);
      g_free (date);
      g_string_erase (str, 0, -1);

      /* Second label */
      label = gtk_label_new (NULL);
      date = nautilus_file_get_string_attribute (src,
                                       "date_modified");
      size = nautilus_file_get_string_attribute (src, "size");

      if (should_show_type) {
            type = nautilus_file_get_string_attribute (src, "type");
      }

      g_string_append_printf (str, "<b>%s</b>\n", _("Replace with"));
      g_string_append_printf (str, "<i>%s</i> %s\n", _("Size:"), size);

      if (should_show_type) {
            g_string_append_printf (str, "<i>%s</i> %s\n", _("Type:"), type);
      }

      g_string_append_printf (str, "<i>%s</i> %s", _("Last modified:"), date);
      label_text = g_string_free (str, FALSE);

      gtk_label_set_markup (GTK_LABEL (label),
                        label_text);
      gtk_box_pack_start (GTK_BOX (details->second_hbox),
                      label, FALSE, FALSE, 0);
      gtk_widget_show (label);

      g_free (size);
      g_free (date);
      g_free (type);
      g_free (label_text);

      /* Populate the entry */
      edit_name = nautilus_file_get_edit_name (dest);
      details->conflict_name = edit_name;

      gtk_entry_set_text (GTK_ENTRY (details->entry), edit_name);

      if (source_is_dir && dest_is_dir) {
            gtk_button_set_label (GTK_BUTTON (details->replace_button),
                              _("Merge"));
      }

      nautilus_file_monitor_add (src, fcd, NAUTILUS_FILE_ATTRIBUTES_FOR_ICON);
      nautilus_file_monitor_add (dest, fcd, NAUTILUS_FILE_ATTRIBUTES_FOR_ICON);

      details->src_handler_id = g_signal_connect (src, "changed",
                    G_CALLBACK (file_icons_changed), fcd);
      details->dest_handler_id = g_signal_connect (dest, "changed",
                    G_CALLBACK (file_icons_changed), fcd);
}

static void
build_dialog_appearance (NautilusFileConflictDialog *fcd)
{
      GList *files = NULL;
      NautilusFileConflictDialogDetails *details = fcd->details;

      files = g_list_prepend (files, details->source);
      files = g_list_prepend (files, details->destination);
      files = g_list_prepend (files, details->dest_dir);

      nautilus_file_list_call_when_ready (files,
                                  NAUTILUS_FILE_ATTRIBUTES_FOR_ICON,
                                  &details->handle, file_list_ready_cb, fcd);
      g_list_free (files);
}

static void
set_source_and_destination (GtkWidget *w,
                      GFile *source,
                      GFile *destination,
                      GFile *dest_dir)
{
      NautilusFileConflictDialog *dialog;
      NautilusFileConflictDialogDetails *details;

      dialog = NAUTILUS_FILE_CONFLICT_DIALOG (w);
      details = dialog->details;

      details->source = nautilus_file_get (source);
      details->destination = nautilus_file_get (destination);
      details->dest_dir = nautilus_file_get (dest_dir);

      build_dialog_appearance (dialog);
}

static void
entry_text_changed_cb (GtkEditable *entry,
                   NautilusFileConflictDialog *dialog)
{
      NautilusFileConflictDialogDetails *details;

      details = dialog->details;

      /* The rename button is visible only if there's text
       * in the entry.
       */
      if  (g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (entry)), "") != 0 &&
           g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (entry)), details->conflict_name) != 0) {
            gtk_widget_hide (details->replace_button);
            gtk_widget_show (details->rename_button);

            gtk_widget_set_sensitive (details->checkbox, FALSE);

            gtk_dialog_set_default_response (GTK_DIALOG (dialog),
                                     CONFLICT_RESPONSE_RENAME);
      } else {
            gtk_widget_hide (details->rename_button);
            gtk_widget_show (details->replace_button);

            gtk_widget_set_sensitive (details->checkbox, TRUE);

            gtk_dialog_set_default_response (GTK_DIALOG (dialog),
                                     CONFLICT_RESPONSE_REPLACE);
      }
}

static void
expander_activated_cb (GtkExpander *w,
                   NautilusFileConflictDialog *dialog)
{
      NautilusFileConflictDialogDetails *details;
      int start_pos, end_pos;

      details = dialog->details;

      if (!gtk_expander_get_expanded (w)) {
            if (g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (details->entry)),
                         details->conflict_name) == 0) {
                  gtk_widget_grab_focus (details->entry);

                  eel_filename_get_rename_region (details->conflict_name,
                                          &start_pos, &end_pos);
                  gtk_editable_select_region (GTK_EDITABLE (details->entry),
                                        start_pos, end_pos);
            }
      }
}

static void
checkbox_toggled_cb (GtkToggleButton *t,
                 NautilusFileConflictDialog *dialog)
{
      NautilusFileConflictDialogDetails *details;

      details = dialog->details;

      gtk_widget_set_sensitive (details->expander,
                          !gtk_toggle_button_get_active (t));
      gtk_widget_set_sensitive (details->rename_button,
                          !gtk_toggle_button_get_active (t));

      if  (!gtk_toggle_button_get_active (t) &&
           g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (details->entry)),
                  "") != 0 &&
           g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (details->entry)),
                  details->conflict_name) != 0) {
            gtk_widget_hide (details->replace_button);
            gtk_widget_show (details->rename_button);
      } else {
            gtk_widget_hide (details->rename_button);
            gtk_widget_show (details->replace_button);
      }
}

static void
reset_button_clicked_cb (GtkButton *w,
                   NautilusFileConflictDialog *dialog)
{
      NautilusFileConflictDialogDetails *details;
      int start_pos, end_pos;

      details = dialog->details;

      gtk_entry_set_text (GTK_ENTRY (details->entry),
                      details->conflict_name);
      gtk_widget_grab_focus (details->entry);
      eel_filename_get_rename_region (details->conflict_name,
                              &start_pos, &end_pos);
      gtk_editable_select_region (GTK_EDITABLE (details->entry),
                            start_pos, end_pos);

}

static void
nautilus_file_conflict_dialog_init (NautilusFileConflictDialog *fcd)
{
      GtkWidget *hbox, *vbox, *vbox2, *alignment;
      GtkWidget *widget, *dialog_area;
      NautilusFileConflictDialogDetails *details;
      GtkDialog *dialog;

      details = fcd->details = NAUTILUS_FILE_CONFLICT_DIALOG_GET_PRIVATE (fcd);
      dialog = GTK_DIALOG (fcd);

      /* Setup the main hbox */
      hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
      dialog_area = gtk_dialog_get_content_area (dialog);
      gtk_box_pack_start (GTK_BOX (dialog_area), hbox, FALSE, FALSE, 0);
      gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);

      /* Setup the dialog image */
      widget = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING,
                                 GTK_ICON_SIZE_DIALOG);
      gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
      gtk_misc_set_alignment (GTK_MISC (widget), 0.5, 0.0);

      /* Setup the vbox containing the dialog body */
      vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
      gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);

      /* Setup the vbox for the dialog labels */
      widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
      gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
      details->titles_vbox = widget;

      /* Setup the hboxes to pack file infos into */
      alignment = gtk_alignment_new (0.0, 0.0, 0.0, 0.0);
      g_object_set (alignment, "left-padding", 12, NULL);
      vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
      gtk_container_add (GTK_CONTAINER (alignment), vbox2);
      gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);

      hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
      gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0);
      details->first_hbox = hbox;

      hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
      gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0);
      details->second_hbox = hbox;

      /* Setup the expander for the rename action */
      details->expander = gtk_expander_new_with_mnemonic (_("_Select a new name for the destination"));
      gtk_box_pack_start (GTK_BOX (vbox2), details->expander, FALSE, FALSE, 0);
      g_signal_connect (details->expander, "activate",
                    G_CALLBACK (expander_activated_cb), dialog);

      hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
      gtk_container_add (GTK_CONTAINER (details->expander), hbox);

      widget = gtk_entry_new ();
      gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 6);
      details->entry = widget;
      g_signal_connect (widget, "changed",
                    G_CALLBACK (entry_text_changed_cb), dialog);

      widget = gtk_button_new_with_label (_("Reset"));
      gtk_button_set_image (GTK_BUTTON (widget),
                        gtk_image_new_from_stock (GTK_STOCK_UNDO,
                                          GTK_ICON_SIZE_MENU));
      gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 6);
      g_signal_connect (widget, "clicked",
                    G_CALLBACK (reset_button_clicked_cb), dialog);

      gtk_widget_show_all (alignment);


      /* Setup the checkbox to apply the action to all files */
      widget = gtk_check_button_new_with_mnemonic (_("Apply this action to all files"));
      gtk_box_pack_start (GTK_BOX (vbox),
                      widget, FALSE, FALSE, 0);
      details->checkbox = widget;
      g_signal_connect (widget, "toggled",
                    G_CALLBACK (checkbox_toggled_cb), dialog);

      /* Add buttons */
      gtk_dialog_add_buttons (dialog,
                        GTK_STOCK_CANCEL,
                        GTK_RESPONSE_CANCEL,
                        _("_Skip"),
                        CONFLICT_RESPONSE_SKIP,
                        NULL);
      details->rename_button =
            gtk_dialog_add_button (dialog,
                               _("Re_name"),
                               CONFLICT_RESPONSE_RENAME);
      gtk_widget_hide (details->rename_button);

      details->replace_button =
            gtk_dialog_add_button (dialog,
                               _("Replace"),
                               CONFLICT_RESPONSE_REPLACE);
      gtk_widget_grab_focus (details->replace_button);

      /* Setup HIG properties */
      gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
      gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (dialog)), 14);
      gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);

      gtk_widget_show_all (dialog_area);
}

static void
do_finalize (GObject *self)
{
      NautilusFileConflictDialogDetails *details =
            NAUTILUS_FILE_CONFLICT_DIALOG (self)->details;

      g_free (details->conflict_name);

      if (details->handle != NULL) {
            nautilus_file_list_cancel_call_when_ready (details->handle);
      }

      if (details->src_handler_id) {
            g_signal_handler_disconnect (details->source, details->src_handler_id);
            nautilus_file_monitor_remove (details->source, self);
      }

      if (details->dest_handler_id) {
            g_signal_handler_disconnect (details->destination, details->dest_handler_id);
            nautilus_file_monitor_remove (details->destination, self);
      }

      nautilus_file_unref (details->source);
      nautilus_file_unref (details->destination);
      nautilus_file_unref (details->dest_dir);

      G_OBJECT_CLASS (nautilus_file_conflict_dialog_parent_class)->finalize (self);
}

static void
nautilus_file_conflict_dialog_class_init (NautilusFileConflictDialogClass *klass)
{
      G_OBJECT_CLASS (klass)->finalize = do_finalize;

      g_type_class_add_private (klass, sizeof (NautilusFileConflictDialogDetails));
}

char *
nautilus_file_conflict_dialog_get_new_name (NautilusFileConflictDialog *dialog)
{
      return g_strdup (gtk_entry_get_text
                   (GTK_ENTRY (dialog->details->entry)));
}

gboolean
nautilus_file_conflict_dialog_get_apply_to_all (NautilusFileConflictDialog *dialog)
{
      return gtk_toggle_button_get_active 
            (GTK_TOGGLE_BUTTON (dialog->details->checkbox));
}

GtkWidget *
nautilus_file_conflict_dialog_new (GtkWindow *parent,
                           GFile *source,
                           GFile *destination,
                           GFile *dest_dir)
{
      GtkWidget *dialog;
      
      dialog = GTK_WIDGET (g_object_new (NAUTILUS_TYPE_FILE_CONFLICT_DIALOG,
                                 "title", _("File conflict"),
                                 NULL));
      set_source_and_destination (dialog,
                            source,
                            destination,
                            dest_dir);
      gtk_window_set_transient_for (GTK_WINDOW (dialog),
                              parent);
      return dialog;
}

Generated by  Doxygen 1.6.0   Back to index