]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkfilesel.c
Updates.
[~andy/gtk] / gtk / gtkfilesel.c
index 23a43a2b8571f0ecc027d652bf14701b3697c52a..902272b14e4e426e708009acf89cd4b78230acb0 100644 (file)
@@ -44,6 +44,9 @@
 #ifdef HAVE_PWD_H
 #include <pwd.h>
 #endif
+#ifdef HAVE_WINSOCK_H
+#include <winsock.h>           /* For gethostname */
+#endif
 
 #include "fnmatch.h"
 
@@ -68,6 +71,8 @@
 #include "gtkdialog.h"
 #include "gtkmessagedialog.h"
 #include "gtkintl.h"
+#include "gtkdnd.h"
+#include "gtkeventbox.h"
 
 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
 #define STRICT
@@ -238,7 +243,7 @@ enum {
 static CompletionState*    cmpl_init_state        (void);
 static void                cmpl_free_state        (CompletionState *cmpl_state);
 static gint                cmpl_state_okay        (CompletionState* cmpl_state);
-static gchar*              cmpl_strerror          (gint);
+static const gchar*        cmpl_strerror          (gint);
 
 static PossibleCompletion* cmpl_completion_matches(gchar           *text_to_complete,
                                                   gchar          **remaining_text,
@@ -291,7 +296,7 @@ static gint                cmpl_last_valid_char    (CompletionState* cmpl_state)
 /* When the user selects a non-directory, call cmpl_completion_fullname
  * to get the full name of the selected file.
  */
-static gchar*              cmpl_completion_fullname (gchar*, CompletionState* cmpl_state);
+static gchar*              cmpl_completion_fullname (const gchar*, CompletionState* cmpl_state);
 
 
 /* Directory operations. */
@@ -306,7 +311,7 @@ static gboolean       check_dir            (gchar *dir_name,
 static CompletionDir* open_dir             (gchar* dir_name,
                                            CompletionState* cmpl_state);
 #ifdef HAVE_PWD_H
-static CompletionDir* open_user_dir        (gchar* text_to_complete,
+static CompletionDir* open_user_dir        (const gchar* text_to_complete,
                                            CompletionState *cmpl_state);
 #endif
 static CompletionDir* open_relative_dir    (gchar* dir_name, CompletionDir* dir,
@@ -353,6 +358,7 @@ static void gtk_file_selection_get_property  (GObject         *object,
                                              GValue          *value,
                                              GParamSpec      *pspec);
 static void gtk_file_selection_init          (GtkFileSelection      *filesel);
+static void gtk_file_selection_finalize      (GObject               *object);
 static void gtk_file_selection_destroy       (GtkObject             *object);
 static gint gtk_file_selection_key_press     (GtkWidget             *widget,
                                              GdkEventKey           *event,
@@ -493,6 +499,7 @@ gtk_file_selection_class_init (GtkFileSelectionClass *class)
 
   parent_class = gtk_type_class (GTK_TYPE_DIALOG);
 
+  gobject_class->finalize = gtk_file_selection_finalize;
   gobject_class->set_property = gtk_file_selection_set_property;
   gobject_class->get_property = gtk_file_selection_get_property;
    
@@ -572,7 +579,13 @@ static void gtk_file_selection_get_property (GObject         *object,
     }
 }
 
-
+static gboolean
+grab_default (GtkWidget *widget)
+{
+  gtk_widget_grab_default (widget);
+  return FALSE;
+}
+     
 static void
 gtk_file_selection_init (GtkFileSelection *filesel)
 {
@@ -582,6 +595,7 @@ gtk_file_selection_init (GtkFileSelection *filesel)
   GtkWidget *confirm_area;
   GtkWidget *pulldown_hbox;
   GtkWidget *scrolled_win;
+  GtkWidget *eventbox;
   GtkDialog *dialog;
   
   char *dir_title [2];
@@ -672,25 +686,28 @@ gtk_file_selection_init (GtkFileSelection *filesel)
 
   /*  The OK button  */
   filesel->ok_button = gtk_dialog_add_button (dialog,
-                                              GTK_STOCK_BUTTON_OK,
+                                              GTK_STOCK_OK,
                                               GTK_RESPONSE_OK);
   
   gtk_widget_grab_default (filesel->ok_button);
 
   /*  The Cancel button  */
   filesel->cancel_button = gtk_dialog_add_button (dialog,
-                                                  GTK_STOCK_BUTTON_CANCEL,
+                                                  GTK_STOCK_CANCEL,
                                                   GTK_RESPONSE_CANCEL);
 
   /*  The selection entry widget  */
   entry_vbox = gtk_vbox_new (FALSE, 2);
   gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 2);
   gtk_widget_show (entry_vbox);
-
+  
+  eventbox = gtk_event_box_new ();
   filesel->selection_text = label = gtk_label_new ("");
   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-  gtk_box_pack_start (GTK_BOX (entry_vbox), label, FALSE, FALSE, 0);
+  gtk_container_add (GTK_CONTAINER (eventbox), label);
+  gtk_box_pack_start (GTK_BOX (entry_vbox), eventbox, FALSE, FALSE, 0);
   gtk_widget_show (label);
+  gtk_widget_show (eventbox);
 
   filesel->selection_entry = gtk_entry_new ();
   gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "key_press_event",
@@ -698,7 +715,7 @@ gtk_file_selection_init (GtkFileSelection *filesel)
   gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "insert_text",
                      (GtkSignalFunc) gtk_file_selection_insert_text, NULL);
   gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "focus_in_event",
-                            (GtkSignalFunc) gtk_widget_grab_default,
+                            (GtkSignalFunc) grab_default,
                             GTK_OBJECT (filesel->ok_button));
   gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "activate",
                              (GtkSignalFunc) gtk_button_clicked,
@@ -722,6 +739,224 @@ gtk_file_selection_init (GtkFileSelection *filesel)
   gtk_widget_grab_focus (filesel->selection_entry);
 }
 
+static gchar *
+uri_list_extract_first_uri (const gchar* uri_list)
+{
+  const gchar *p, *q;
+  
+  g_return_val_if_fail (uri_list != NULL, NULL);
+  
+  p = uri_list;
+  /* We don't actually try to validate the URI according to RFC
+   * 2396, or even check for allowed characters - we just ignore
+   * comments and trim whitespace off the ends.  We also
+   * allow LF delimination as well as the specified CRLF.
+   *
+   * We do allow comments like specified in RFC 2483.
+   */
+  while (p)
+    {
+      if (*p != '#')
+       {
+         while (g_ascii_isspace(*p))
+           p++;
+         
+         q = p;
+         while (*q && (*q != '\n') && (*q != '\r'))
+           q++;
+         
+         if (q > p)
+           {
+             q--;
+             while (q > p && g_ascii_isspace (*q))
+               q--;
+
+             if (q > p)
+               return g_strndup (p, q - p + 1);
+           }
+       }
+      p = strchr (p, '\n');
+      if (p)
+       p++;
+    }
+  return NULL;
+}
+
+static void
+dnd_really_drop  (GtkWidget *dialog, gint response_id, GtkFileSelection *fs)
+{
+  gchar *filename;
+  
+  if (response_id == GTK_RESPONSE_YES)
+    {
+      filename = g_object_get_data (G_OBJECT (dialog), "gtk-fs-dnd-filename");
+
+      gtk_file_selection_set_filename (fs, filename);
+    }
+  
+  gtk_widget_destroy (dialog);
+}
+
+
+static void
+filenames_dropped (GtkWidget        *widget,
+                  GdkDragContext   *context,
+                  gint              x,
+                  gint              y,
+                  GtkSelectionData *selection_data,
+                  guint             info,
+                  guint             time)
+{
+  char *uri = NULL;
+  char *filename = NULL;
+  char *hostname;
+  char this_hostname[257];
+  int res;
+  GError *error = NULL;
+       
+  if (!selection_data->data)
+    return;
+
+  uri = uri_list_extract_first_uri ((char *)selection_data->data);
+  
+  if (!uri)
+    return;
+
+  filename = g_filename_from_uri (uri, &hostname, &error);
+  g_free (uri);
+  
+  if (!filename)
+    {
+      g_warning ("Error getting dropped filename: %s\n",
+                error->message);
+      g_error_free (error);
+      return;
+    }
+
+  res = gethostname (this_hostname, 256);
+  this_hostname[256] = 0;
+  
+  if ((hostname == NULL) ||
+      (res == 0 && strcmp (hostname, this_hostname) == 0) ||
+      (strcmp (hostname, "localhost") == 0))
+    gtk_file_selection_set_filename (GTK_FILE_SELECTION (widget),
+                                    filename);
+  else
+    {
+      GtkWidget *dialog;
+      
+      dialog = gtk_message_dialog_new (GTK_WINDOW (widget),
+                                      GTK_DIALOG_DESTROY_WITH_PARENT,
+                                      GTK_MESSAGE_QUESTION,
+                                      GTK_BUTTONS_YES_NO,
+                                      _("The file \"%s\" resides on another machine (called %s) and may not be availible to this program.\n"
+                                        "Are you sure that you want to select it?"), filename, hostname);
+
+      g_object_set_data_full (G_OBJECT (dialog), "gtk-fs-dnd-filename", g_strdup (filename), g_free);
+      
+      g_signal_connect_data (dialog, "response",
+                            (GCallback) dnd_really_drop, 
+                            widget, NULL, 0);
+      
+      gtk_widget_show (dialog);
+    }
+
+  g_free (hostname);
+  g_free (filename);
+}
+
+enum
+{
+  TARGET_URILIST,
+  TARGET_UTF8_STRING,
+  TARGET_STRING,
+  TARGET_TEXT,
+  TARGET_COMPOUND_TEXT
+};
+
+
+static void
+filenames_drag_get (GtkWidget        *widget,
+                   GdkDragContext   *context,
+                   GtkSelectionData *selection_data,
+                   guint             info,
+                   guint             time,
+                   GtkFileSelection *filesel)
+{
+  const gchar *file;
+  gchar *uri_list;
+  char hostname[256];
+  int res;
+  GError *error;
+
+  file = gtk_file_selection_get_filename (filesel);
+
+  if (file)
+    {
+      if (info == TARGET_URILIST)
+       {
+         res = gethostname (hostname, 256);
+         
+         error = NULL;
+         uri_list = g_filename_to_uri (file, (!res)?hostname:NULL, &error);
+         if (!uri_list)
+           {
+             g_warning ("Error getting filename: %s\n",
+                        error->message);
+             g_error_free (error);
+             return;
+           }
+         
+         gtk_selection_data_set (selection_data,
+                                 selection_data->target, 8,
+                                 (void *)uri_list, strlen((char *)uri_list));
+         g_free (uri_list);
+       }
+      else
+       {
+         g_print ("Setting text: '%s'\n", file);
+         gtk_selection_data_set_text (selection_data, file);
+       }
+    }
+}
+
+static void
+file_selection_setup_dnd (GtkFileSelection *filesel)
+{
+  GtkWidget *eventbox;
+  static GtkTargetEntry drop_types[] = {
+    { "text/uri-list", 0, TARGET_URILIST}
+  };
+  static gint n_drop_types = sizeof(drop_types)/sizeof(drop_types[0]);
+  static GtkTargetEntry drag_types[] = {
+    { "text/uri-list", 0, TARGET_URILIST},
+    { "UTF8_STRING", 0, TARGET_UTF8_STRING },
+    { "STRING", 0, 0 },
+    { "TEXT",   0, 0 }, 
+    { "COMPOUND_TEXT", 0, 0 }
+  };
+  static gint n_drag_types = sizeof(drag_types)/sizeof(drag_types[0]);
+
+  gtk_drag_dest_set (GTK_WIDGET (filesel),
+                    GTK_DEST_DEFAULT_ALL,
+                    drop_types, n_drop_types,
+                    GDK_ACTION_COPY);
+
+  gtk_signal_connect (GTK_OBJECT(filesel), "drag_data_received",
+                     GTK_SIGNAL_FUNC(filenames_dropped), NULL);
+
+  eventbox = gtk_widget_get_parent (filesel->selection_text);
+  gtk_drag_source_set (eventbox,
+                      GDK_BUTTON1_MASK,
+                      drag_types, n_drag_types,
+                      GDK_ACTION_COPY);
+
+  gtk_signal_connect (GTK_OBJECT (eventbox),
+                     "drag_data_get",
+                     GTK_SIGNAL_FUNC (filenames_drag_get),
+                     filesel);
+}
+
 GtkWidget*
 gtk_file_selection_new (const gchar *title)
 {
@@ -731,13 +966,14 @@ gtk_file_selection_new (const gchar *title)
   gtk_window_set_title (GTK_WINDOW (filesel), title);
   gtk_dialog_set_has_separator (GTK_DIALOG (filesel), FALSE);
 
+  file_selection_setup_dnd (filesel);
+  
   return GTK_WIDGET (filesel);
 }
 
 void
 gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
 {
-  g_return_if_fail (filesel != NULL);
   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
     
   /* delete, create directory, and rename */
@@ -780,7 +1016,6 @@ gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
 void       
 gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
 {
-  g_return_if_fail (filesel != NULL);
   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
     
   if (filesel->fileop_ren_file)
@@ -812,7 +1047,6 @@ gtk_file_selection_set_filename (GtkFileSelection *filesel,
   gchar *buf;
   const char *name, *last_slash;
 
-  g_return_if_fail (filesel != NULL);
   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
   g_return_if_fail (filename != NULL);
 
@@ -856,9 +1090,8 @@ gtk_file_selection_get_filename (GtkFileSelection *filesel)
   static gchar nothing[2] = "";
   static gchar something[MAXPATHLEN*2];
   char *sys_filename;
-  char *text;
+  const char *text;
 
-  g_return_val_if_fail (filesel != NULL, nothing);
   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
 
 #ifdef G_WITH_CYGWIN
@@ -882,7 +1115,6 @@ void
 gtk_file_selection_complete (GtkFileSelection *filesel,
                             const gchar      *pattern)
 {
-  g_return_if_fail (filesel != NULL);
   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
   g_return_if_fail (pattern != NULL);
 
@@ -931,6 +1163,14 @@ gtk_file_selection_destroy (GtkObject *object)
   GTK_OBJECT_CLASS (parent_class)->destroy (object);
 }
 
+static void
+gtk_file_selection_finalize (GObject *object)
+{
+  GtkFileSelection *filesel = GTK_FILE_SELECTION (object);
+
+  g_free (filesel->fileop_file);
+}
+
 /* Begin file operations callbacks */
 
 static void
@@ -966,7 +1206,6 @@ gtk_file_selection_fileop_destroy (GtkWidget *widget,
 {
   GtkFileSelection *fs = data;
 
-  g_return_if_fail (fs != NULL);
   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
   
   fs->fileop_dialog = NULL;
@@ -978,7 +1217,7 @@ gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
                                         gpointer   data)
 {
   GtkFileSelection *fs = data;
-  gchar *dirname;
+  const gchar *dirname;
   gchar *path;
   gchar *full_path;
   gchar *sys_full_path;
@@ -986,7 +1225,6 @@ gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
   GError *error = NULL;
   CompletionState *cmpl_state;
   
-  g_return_if_fail (fs != NULL);
   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
   dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
@@ -1032,7 +1270,6 @@ gtk_file_selection_create_dir (GtkWidget *widget,
   GtkWidget *vbox;
   GtkWidget *button;
 
-  g_return_if_fail (fs != NULL);
   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
   if (fs->fileop_dialog)
@@ -1106,7 +1343,6 @@ gtk_file_selection_delete_file_confirmed (GtkWidget *widget,
   GError *error = NULL;
   gchar *buf;
   
-  g_return_if_fail (fs != NULL);
   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
   cmpl_state = (CompletionState*) fs->cmpl_state;
@@ -1153,10 +1389,9 @@ gtk_file_selection_delete_file (GtkWidget *widget,
   GtkWidget *vbox;
   GtkWidget *button;
   GtkWidget *dialog;
-  gchar *filename;
+  const gchar *filename;
   gchar *buf;
   
-  g_return_if_fail (fs != NULL);
   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
   if (fs->fileop_dialog)
@@ -1170,7 +1405,8 @@ gtk_file_selection_delete_file (GtkWidget *widget,
   if (strlen (filename) < 1)
     return;
 
-  fs->fileop_file = filename;
+  g_free (fs->fileop_file);
+  fs->fileop_file = g_strdup (filename);
   
   /* main dialog */
   fs->fileop_dialog = dialog = gtk_dialog_new ();
@@ -1228,7 +1464,7 @@ gtk_file_selection_rename_file_confirmed (GtkWidget *widget,
 {
   GtkFileSelection *fs = data;
   gchar *buf;
-  gchar *file;
+  const gchar *file;
   gchar *path;
   gchar *new_filename;
   gchar *old_filename;
@@ -1237,7 +1473,6 @@ gtk_file_selection_rename_file_confirmed (GtkWidget *widget,
   CompletionState *cmpl_state;
   GError *error = NULL;
   
-  g_return_if_fail (fs != NULL);
   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
   file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
@@ -1306,13 +1541,13 @@ gtk_file_selection_rename_file (GtkWidget *widget,
   GtkWidget *button;
   gchar *buf;
   
-  g_return_if_fail (fs != NULL);
   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
   if (fs->fileop_dialog)
          return;
 
-  fs->fileop_file = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
+  g_free (fs->fileop_file);
+  fs->fileop_file = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
   if (strlen (fs->fileop_file) < 1)
     return;
   
@@ -1410,22 +1645,20 @@ gtk_file_selection_key_press (GtkWidget   *widget,
   g_return_val_if_fail (widget != NULL, FALSE);
   g_return_val_if_fail (event != NULL, FALSE);
 
-  if (event->keyval == GDK_Tab)
+  if (event->keyval == GDK_Tab ||
+      event->keyval == GDK_ISO_Left_Tab ||
+      event->keyval == GDK_KP_Tab)
     {
       fs = GTK_FILE_SELECTION (user_data);
 #ifdef G_WITH_CYGWIN
       translate_win32_path (fs);
 #endif
-      text = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
-
-      text = g_strdup (text);
+      text = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
 
       gtk_file_selection_populate (fs, text, TRUE);
 
       g_free (text);
 
-      gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
-
       return TRUE;
     }
 
@@ -1441,7 +1674,6 @@ gtk_file_selection_history_callback (GtkWidget *widget,
   HistoryCallbackArg *callback_arg;
   GList *list;
 
-  g_return_if_fail (fs != NULL);
   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
   list = fs->history_list;
@@ -1470,7 +1702,6 @@ gtk_file_selection_update_history_menu (GtkFileSelection *fs,
   gint dir_len;
   gint i;
   
-  g_return_if_fail (fs != NULL);
   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
   g_return_if_fail (current_directory != NULL);
   
@@ -1554,7 +1785,6 @@ gtk_file_selection_file_button (GtkWidget      *widget,
   g_return_if_fail (GTK_IS_CLIST (widget));
 
   fs = user_data;
-  g_return_if_fail (fs != NULL);
   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
   
   gtk_clist_get_text (GTK_CLIST (fs->file_list), row, 0, &temp);
@@ -1562,12 +1792,13 @@ gtk_file_selection_file_button (GtkWidget      *widget,
 
 #ifdef G_WITH_CYGWIN
   /* Check to see if the selection was a drive selector */
-  if (isalpha (filename[0]) && (filename[1] == ':')) {
-    /* It is... map it to a CYGWIN32 drive */
-    gchar *temp_filename = g_strdup_printf ("//%c/", tolower (filename[0]));
-    g_free(filename);
-    filename = temp_filename;
-  }
+  if (isalpha (filename[0]) && (filename[1] == ':'))
+    {
+      /* It is... map it to a CYGWIN32 drive */
+      gchar *temp_filename = g_strdup_printf ("//%c/", tolower (filename[0]));
+      g_free(filename);
+      filename = temp_filename;
+    }
 #endif /* G_WITH_CYGWIN */
 
   if (filename)
@@ -1603,7 +1834,6 @@ gtk_file_selection_dir_button (GtkWidget      *widget,
   g_return_if_fail (GTK_IS_CLIST (widget));
 
   fs = GTK_FILE_SELECTION (user_data);
-  g_return_if_fail (fs != NULL);
   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
 
   gtk_clist_get_text (GTK_CLIST (fs->dir_list), row, 0, &temp);
@@ -1688,7 +1918,6 @@ gtk_file_selection_populate (GtkFileSelection *fs,
   gint possible_count = 0;
   gint selection_index = -1;
   
-  g_return_if_fail (fs != NULL);
   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
   
   cmpl_state = (CompletionState*) fs->cmpl_state;
@@ -1862,7 +2091,7 @@ cmpl_last_valid_char (CompletionState *cmpl_state)
 }
 
 static gchar*
-cmpl_completion_fullname (gchar           *text,
+cmpl_completion_fullname (const gchar     *text,
                          CompletionState *cmpl_state)
 {
   static char nothing[2] = "";
@@ -2273,7 +2502,7 @@ open_ref_dir (gchar           *text_to_complete,
 
 /* open a directory by user name */
 static CompletionDir*
-open_user_dir (gchar           *text_to_complete,
+open_user_dir (const gchar     *text_to_complete,
               CompletionState *cmpl_state)
 {
   CompletionDir *result;
@@ -2292,11 +2521,9 @@ open_user_dir (gchar           *text_to_complete,
   if (!cmp_len)
     {
       /* ~/ */
-      gchar *homedir = g_get_home_dir ();
+      const gchar *homedir = g_get_home_dir ();
       gchar *utf8_homedir = g_filename_to_utf8 (homedir, -1, NULL, NULL, NULL);
 
-      g_free (homedir);
-
       if (utf8_homedir)
        result = open_dir (utf8_homedir, cmpl_state);
       else
@@ -3278,7 +3505,7 @@ cmpl_state_okay (CompletionState* cmpl_state)
   return  cmpl_state && cmpl_state->reference_dir;
 }
 
-static gchar*
+static const gchar*
 cmpl_strerror (gint err)
 {
   if (err == CMPL_ERRNO_TOO_LONG)