]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkfilechooserentry.c
Return an error code when refreshing the entry from the user's input
[~andy/gtk] / gtk / gtkfilechooserentry.c
index 29149344c398f62e9fef28841090d964e15f0236..66f0f24346fb9ecfba826524d0ec09ff039620d8 100644 (file)
@@ -50,6 +50,15 @@ typedef enum {
   LOAD_COMPLETE_EXPLICIT_COMPLETION
 } LoadCompleteAction;
 
+typedef enum
+{
+  REFRESH_OK,
+  REFRESH_INVALID_INPUT,
+  REFRESH_INCOMPLETE_HOSTNAME,
+  REFRESH_NONEXISTENT,
+  REFRESH_NOT_LOCAL
+} RefreshStatus;
+
 struct _GtkFileChooserEntry
 {
   GtkEntry parent_instance;
@@ -79,6 +88,7 @@ struct _GtkFileChooserEntry
   guint has_completion : 1;
   guint in_change      : 1;
   guint eat_tabs       : 1;
+  guint local_only     : 1;
 };
 
 enum
@@ -143,10 +153,10 @@ typedef enum {
   REFRESH_WHOLE_TEXT
 } RefreshMode;
 
-static void refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry,
+static RefreshStatus refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry,
                                                  RefreshMode refresh_mode);
-static void finished_loading_cb (GFile         *file,
-                                gpointer       data);
+static void finished_loading_cb (GtkFolder *folder,
+                                gpointer   data);
 static void autocomplete (GtkFileChooserEntry *chooser_entry);
 static void install_start_autocompletion_idle (GtkFileChooserEntry *chooser_entry);
 static void remove_completion_feedback (GtkFileChooserEntry *chooser_entry);
@@ -194,6 +204,8 @@ _gtk_file_chooser_entry_init (GtkFileChooserEntry *chooser_entry)
   GtkEntryCompletion *comp;
   GtkCellRenderer *cell;
 
+  chooser_entry->local_only = TRUE;
+
   g_object_set (chooser_entry, "truncate-multiline", TRUE, NULL);
 
   comp = gtk_entry_completion_new ();
@@ -211,16 +223,16 @@ _gtk_file_chooser_entry_init (GtkFileChooserEntry *chooser_entry)
                                  cell,
                                  "text", 0);
 
-  g_signal_connect (comp, "match_selected",
+  g_signal_connect (comp, "match-selected",
                    G_CALLBACK (match_selected_callback), chooser_entry);
 
   gtk_entry_set_completion (GTK_ENTRY (chooser_entry), comp);
   g_object_unref (comp);
 
 #ifdef G_OS_WIN32
-  g_signal_connect (chooser_entry, "insert_text",
+  g_signal_connect (chooser_entry, "insert-text",
                    G_CALLBACK (insert_text_callback), NULL);
-  g_signal_connect (chooser_entry, "delete_text",
+  g_signal_connect (chooser_entry, "delete-text",
                    G_CALLBACK (delete_text_callback), NULL);
 #endif
 }
@@ -244,12 +256,25 @@ gtk_file_chooser_entry_finalize (GObject *object)
   G_OBJECT_CLASS (_gtk_file_chooser_entry_parent_class)->finalize (object);
 }
 
+static void
+discard_current_folder (GtkFileChooserEntry *chooser_entry)
+{
+  if (chooser_entry->current_folder)
+    {
+      g_signal_handlers_disconnect_by_func (chooser_entry->current_folder,
+                                           G_CALLBACK (finished_loading_cb), chooser_entry);
+      g_object_unref (chooser_entry->current_folder);
+      chooser_entry->current_folder = NULL;
+    }
+}
+
 static void
 gtk_file_chooser_entry_dispose (GObject *object)
 {
   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (object);
 
   remove_completion_feedback (chooser_entry);
+  discard_current_folder (chooser_entry);
 
   if (chooser_entry->start_autocompletion_idle_id != 0)
     {
@@ -269,14 +294,6 @@ gtk_file_chooser_entry_dispose (GObject *object)
       chooser_entry->load_folder_cancellable = NULL;
     }
 
-  if (chooser_entry->current_folder)
-    {
-      g_signal_handlers_disconnect_by_func (chooser_entry->current_folder,
-                                           G_CALLBACK (finished_loading_cb), chooser_entry);
-      g_object_unref (chooser_entry->current_folder);
-      chooser_entry->current_folder = NULL;
-    }
-
   if (chooser_entry->file_system)
     {
       g_object_unref (chooser_entry->file_system);
@@ -439,7 +456,7 @@ maybe_append_separator_to_file (GtkFileChooserEntry *chooser_entry,
 
       if (info)
        {
-         if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
+         if (_gtk_file_info_consider_as_directory (info))
            {
              gchar *tmp = display_name;
              display_name = g_strconcat (tmp, G_DIR_SEPARATOR_S, NULL);
@@ -653,7 +670,12 @@ append_common_prefix (GtkFileChooserEntry *chooser_entry,
   error = NULL;
   if (!find_common_prefix (chooser_entry, &common_prefix, &unique_file, &is_complete_not_unique, &prefix_expands_the_file_part, &error))
     {
-      if (show_errors)
+      /* If the user types an incomplete hostname ("http://foo" without a slash
+       * after that), it's not an error.  We just don't want to pop up a
+       * meaningless completion window in that state.
+       */
+      if (!g_error_matches (error, GTK_FILE_CHOOSER_ERROR, GTK_FILE_CHOOSER_ERROR_INCOMPLETE_HOSTNAME)
+         && show_errors)
        {
          beep (chooser_entry);
          pop_up_completion_feedback (chooser_entry, _("Invalid path"));
@@ -862,31 +884,15 @@ completion_feedback_window_expose_event_cb (GtkWidget      *widget,
 static void
 set_invisible_mouse_cursor (GdkWindow *window)
 {
-  /* Stolen from gtkentry.c:set_invisible_cursor() */
-  /* FIXME: implement a stupid public gdk_window_set_invisible_mouse_cursor() */
-
-  GdkBitmap *empty_bitmap;
+  GdkDisplay *display;
   GdkCursor *cursor;
-  GdkColor useless;
-  char invisible_cursor_bits[] = { 0x0 };
-
-  useless.red = useless.green = useless.blue = 0;
-  useless.pixel = 0;
 
-  empty_bitmap = gdk_bitmap_create_from_data (window,
-                                             invisible_cursor_bits,
-                                             1, 1);
-
-  cursor = gdk_cursor_new_from_pixmap (empty_bitmap,
-                                      empty_bitmap,
-                                      &useless,
-                                      &useless, 0, 0);
+  display = gdk_drawable_get_display (window);
+  cursor = gdk_cursor_new_for_display (display, GDK_BLANK_CURSOR);
 
   gdk_window_set_cursor (window, cursor);
 
   gdk_cursor_unref (cursor);
-
-  g_object_unref (empty_bitmap);
 }
 
 static void
@@ -923,7 +929,7 @@ create_completion_feedback_window (GtkFileChooserEntry *chooser_entry)
   gtk_container_add (GTK_CONTAINER (chooser_entry->completion_feedback_window), alignment);
   gtk_widget_show (alignment);
 
-  g_signal_connect (chooser_entry->completion_feedback_window, "expose_event",
+  g_signal_connect (chooser_entry->completion_feedback_window, "expose-event",
                    G_CALLBACK (completion_feedback_window_expose_event_cb), chooser_entry);
   g_signal_connect (chooser_entry->completion_feedback_window, "realize",
                    G_CALLBACK (completion_feedback_window_realize_cb), chooser_entry);
@@ -1105,6 +1111,7 @@ explicitly_complete (GtkFileChooserEntry *chooser_entry)
 static void
 start_explicit_completion (GtkFileChooserEntry *chooser_entry)
 {
+  /* FMQ: get result from the function below */
   refresh_current_folder_and_file_part (chooser_entry, REFRESH_UP_TO_CURSOR_POSITION);
 
   if (!chooser_entry->current_folder_file)
@@ -1118,6 +1125,16 @@ start_explicit_completion (GtkFileChooserEntry *chooser_entry)
       return;
     }
 
+  if (chooser_entry->local_only
+      && !g_file_is_native (chooser_entry->current_folder_file))
+    {
+      beep (chooser_entry);
+      pop_up_completion_feedback (chooser_entry, _("Only local files can be selected"));
+
+      chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING;
+      return;
+    }
+
   if (chooser_entry->current_folder
       && _gtk_folder_is_finished_loading (chooser_entry->current_folder))
     {
@@ -1196,6 +1213,7 @@ commit_completion_and_refresh (GtkFileChooserEntry *chooser_entry)
                                 GTK_ENTRY (chooser_entry)->text_length);
     }
 
+  /* FMQ: get result from the function below */
   refresh_current_folder_and_file_part (chooser_entry, REFRESH_WHOLE_TEXT);
 }
 
@@ -1308,8 +1326,8 @@ finish_folder_load (GtkFileChooserEntry *chooser_entry)
 
 /* Callback when the current folder finishes loading */
 static void
-finished_loading_cb (GFile    *file,
-                    gpointer  data)
+finished_loading_cb (GtkFolder *folder,
+                    gpointer   data)
 {
   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (data);
 
@@ -1337,6 +1355,7 @@ load_directory_get_folder_callback (GCancellable  *cancellable,
 
       old_load_complete_action = chooser_entry->load_complete_action;
 
+      discard_completion_store (chooser_entry);
       clear_completions (chooser_entry);
 
       if (old_load_complete_action == LOAD_COMPLETE_EXPLICIT_COMPLETION)
@@ -1347,11 +1366,7 @@ load_directory_get_folder_callback (GCancellable  *cancellable,
          pop_up_completion_feedback (chooser_entry, error->message);
        }
 
-      if (chooser_entry->current_folder)
-       {
-         g_object_unref (chooser_entry->current_folder);
-         chooser_entry->current_folder = NULL;
-       }
+      discard_current_folder (chooser_entry);
     }
 
   if (cancelled || error)
@@ -1380,6 +1395,10 @@ start_loading_current_folder (GtkFileChooserEntry *chooser_entry)
       chooser_entry->file_system == NULL)
     return;
 
+  if (chooser_entry->local_only
+      && !g_file_is_native (chooser_entry->current_folder_file))
+    return;
+
   g_assert (chooser_entry->current_folder == NULL);
   g_assert (chooser_entry->load_folder_cancellable == NULL);
 
@@ -1400,7 +1419,8 @@ reload_current_folder (GtkFileChooserEntry *chooser_entry,
 
   if (chooser_entry->current_folder_file)
     {
-      if ((folder_file && !g_file_equal (folder_file, chooser_entry->current_folder_file))
+      if ((folder_file && !(g_file_equal (folder_file, chooser_entry->current_folder_file)
+                           && chooser_entry->load_folder_cancellable))
          || force_reload)
        {
          reload = TRUE;
@@ -1408,20 +1428,15 @@ reload_current_folder (GtkFileChooserEntry *chooser_entry,
          /* We changed our current directory.  We need to clear out the old
           * directory information.
           */
-         if (chooser_entry->current_folder)
-           {
-             if (chooser_entry->load_folder_cancellable)
-               {
-                 g_cancellable_cancel (chooser_entry->load_folder_cancellable);
-                 chooser_entry->load_folder_cancellable = NULL;
-               }
-
-             g_object_unref (chooser_entry->current_folder);
-             chooser_entry->current_folder = NULL;
-           }
+          if (chooser_entry->load_folder_cancellable)
+            {
+              g_cancellable_cancel (chooser_entry->load_folder_cancellable);
+              chooser_entry->load_folder_cancellable = NULL;
+            }
 
+          discard_current_folder (chooser_entry);
          g_object_unref (chooser_entry->current_folder_file);
-         chooser_entry->current_folder_file = g_object_ref (folder_file);
+         chooser_entry->current_folder_file = (folder_file) ? g_object_ref (folder_file) : NULL;
        }
     }
   else
@@ -1434,7 +1449,7 @@ reload_current_folder (GtkFileChooserEntry *chooser_entry,
     start_loading_current_folder (chooser_entry);
 }
 
-static void
+static RefreshStatus
 refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry,
                                      RefreshMode          refresh_mode)
 {
@@ -1445,6 +1460,8 @@ refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry,
   gchar *file_part;
   gsize total_len, file_part_len;
   gint file_part_pos;
+  GError *error;
+  RefreshStatus result;
 
   editable = GTK_EDITABLE (chooser_entry);
 
@@ -1460,18 +1477,36 @@ refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry,
 
     default:
       g_assert_not_reached ();
-      return;
+      return REFRESH_INVALID_INPUT;
     }
 
   text = gtk_editable_get_chars (editable, 0, end_pos);
-  
+
+  error = NULL;
   if (!chooser_entry->file_system ||
       !chooser_entry->base_folder ||
       !_gtk_file_system_parse (chooser_entry->file_system,
                               chooser_entry->base_folder, text,
-                              &folder_file, &file_part, NULL)) /* NULL-GError */
+                              &folder_file, &file_part, &error))
     {
-      folder_file = (chooser_entry->base_folder) ? g_object_ref (chooser_entry->base_folder) : NULL;
+      if (g_error_matches (error, GTK_FILE_CHOOSER_ERROR, GTK_FILE_CHOOSER_ERROR_INCOMPLETE_HOSTNAME))
+       {
+         folder_file = NULL;
+         result = REFRESH_INCOMPLETE_HOSTNAME;
+       }
+      else
+       {
+         folder_file = (chooser_entry->base_folder) ? g_object_ref (chooser_entry->base_folder) : NULL;
+
+         if (g_error_matches (error, GTK_FILE_CHOOSER_ERROR, GTK_FILE_CHOOSER_ERROR_NONEXISTENT))
+           result = REFRESH_NONEXISTENT;
+         else
+           result = REFRESH_INVALID_INPUT;
+       }
+
+      if (error)
+       g_error_free (error);
+
       file_part = g_strdup ("");
       file_part_pos = -1;
     }
@@ -1483,6 +1518,8 @@ refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry,
        file_part_pos = g_utf8_strlen (text, total_len - file_part_len);
       else
        file_part_pos = 0;
+
+      result = REFRESH_OK;
     }
 
   g_free (text);
@@ -1492,10 +1529,13 @@ refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry,
   chooser_entry->file_part = file_part;
   chooser_entry->file_part_pos = file_part_pos;
 
+  /* FMQ: this needs to return an error if the folder is not local */
   reload_current_folder (chooser_entry, folder_file, file_part_pos == -1);
 
   if (folder_file)
     g_object_unref (folder_file);
+
+  return result;
 }
 
 static void
@@ -1511,6 +1551,7 @@ autocomplete (GtkFileChooserEntry *chooser_entry)
 static void
 start_autocompletion (GtkFileChooserEntry *chooser_entry)
 {
+  /* FMQ: get result from the function below */
   refresh_current_folder_and_file_part (chooser_entry, REFRESH_UP_TO_CURSOR_POSITION);
 
   if (!chooser_entry->current_folder)
@@ -1545,7 +1586,7 @@ install_start_autocompletion_idle (GtkFileChooserEntry *chooser_entry)
   if (chooser_entry->start_autocompletion_idle_id != 0)
     return;
 
-  chooser_entry->start_autocompletion_idle_id = g_idle_add (start_autocompletion_idle_handler, chooser_entry);
+  chooser_entry->start_autocompletion_idle_id = gdk_threads_add_idle (start_autocompletion_idle_handler, chooser_entry);
 }
 
 #ifdef G_OS_WIN32
@@ -1812,7 +1853,7 @@ _gtk_file_chooser_entry_get_is_folder (GtkFileChooserEntry *chooser_entry,
 
       if (file_info)
         {
-         retval = (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY);
+         retval = _gtk_file_info_consider_as_directory (file_info);
          g_object_unref (file_info);
        }
     }
@@ -1845,3 +1886,16 @@ _gtk_file_chooser_entry_select_filename (GtkFileChooserEntry *chooser_entry)
   gtk_editable_select_region (GTK_EDITABLE (chooser_entry), 0, (gint) len);
 }
 
+void
+_gtk_file_chooser_entry_set_local_only (GtkFileChooserEntry *chooser_entry,
+                                        gboolean             local_only)
+{
+  chooser_entry->local_only = local_only;
+  clear_completions (chooser_entry);
+}
+
+gboolean
+_gtk_file_chooser_entry_get_local_only (GtkFileChooserEntry *chooser_entry)
+{
+  return chooser_entry->local_only;
+}