]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserentry.c
Return an error code when refreshing the entry from the user's input
[~andy/gtk] / gtk / gtkfilechooserentry.c
1 /* GTK - The GIMP Toolkit
2  * gtkfilechooserentry.c: Entry with filename completion
3  * Copyright (C) 2003, Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22 #include <string.h>
23
24 #include "gtkalignment.h"
25 #include "gtkcelllayout.h"
26 #include "gtkcellrenderertext.h"
27 #include "gtkentry.h"
28 #include "gtkfilechooserentry.h"
29 #include "gtklabel.h"
30 #include "gtkmain.h"
31 #include "gtkwindow.h"
32 #include "gtkintl.h"
33 #include "gtkalias.h"
34
35 typedef struct _GtkFileChooserEntryClass GtkFileChooserEntryClass;
36
37 #define GTK_FILE_CHOOSER_ENTRY_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_ENTRY, GtkFileChooserEntryClass))
38 #define GTK_IS_FILE_CHOOSER_ENTRY_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_ENTRY))
39 #define GTK_FILE_CHOOSER_ENTRY_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_ENTRY, GtkFileChooserEntryClass))
40
41 struct _GtkFileChooserEntryClass
42 {
43   GtkEntryClass parent_class;
44 };
45
46 /* Action to take when the current folder finishes loading (for explicit or automatic completion) */
47 typedef enum {
48   LOAD_COMPLETE_NOTHING,
49   LOAD_COMPLETE_AUTOCOMPLETE,
50   LOAD_COMPLETE_EXPLICIT_COMPLETION
51 } LoadCompleteAction;
52
53 typedef enum
54 {
55   REFRESH_OK,
56   REFRESH_INVALID_INPUT,
57   REFRESH_INCOMPLETE_HOSTNAME,
58   REFRESH_NONEXISTENT,
59   REFRESH_NOT_LOCAL
60 } RefreshStatus;
61
62 struct _GtkFileChooserEntry
63 {
64   GtkEntry parent_instance;
65
66   GtkFileChooserAction action;
67
68   GtkFileSystem *file_system;
69   GFile *base_folder;
70   GFile *current_folder_file;
71   gchar *file_part;
72   gint file_part_pos;
73
74   /* Folder being loaded or already loaded */
75   GtkFolder *current_folder;
76   GCancellable *load_folder_cancellable;
77
78   LoadCompleteAction load_complete_action;
79
80   GtkListStore *completion_store;
81
82   guint start_autocompletion_idle_id;
83
84   GtkWidget *completion_feedback_window;
85   GtkWidget *completion_feedback_label;
86   guint completion_feedback_timeout_id;
87
88   guint has_completion : 1;
89   guint in_change      : 1;
90   guint eat_tabs       : 1;
91   guint local_only     : 1;
92 };
93
94 enum
95 {
96   DISPLAY_NAME_COLUMN,
97   FILE_COLUMN,
98   N_COLUMNS
99 };
100
101 #define COMPLETION_FEEDBACK_TIMEOUT_MS 2000
102
103 static void     gtk_file_chooser_entry_iface_init     (GtkEditableClass *iface);
104
105 static void     gtk_file_chooser_entry_finalize       (GObject          *object);
106 static void     gtk_file_chooser_entry_dispose        (GObject          *object);
107 static void     gtk_file_chooser_entry_grab_focus     (GtkWidget        *widget);
108 static void     gtk_file_chooser_entry_unmap          (GtkWidget        *widget);
109 static gboolean gtk_file_chooser_entry_focus          (GtkWidget        *widget,
110                                                        GtkDirectionType  direction);
111 static gboolean gtk_file_chooser_entry_focus_out_event (GtkWidget       *widget,
112                                                         GdkEventFocus   *event);
113 static void     gtk_file_chooser_entry_activate       (GtkEntry         *entry);
114 static void     gtk_file_chooser_entry_do_insert_text (GtkEditable *editable,
115                                                        const gchar *new_text,
116                                                        gint         new_text_length,
117                                                        gint        *position);
118 static void     gtk_file_chooser_entry_do_delete_text (GtkEditable *editable,
119                                                        gint         start_pos,
120                                                        gint         end_pos);
121 static void     gtk_file_chooser_entry_set_position (GtkEditable *editable,
122                                                      gint         position);
123 static void     gtk_file_chooser_entry_set_selection_bounds (GtkEditable *editable,
124                                                              gint         start_pos,
125                                                              gint         end_pos);
126
127 #ifdef G_OS_WIN32
128 static gint     insert_text_callback      (GtkFileChooserEntry *widget,
129                                            const gchar         *new_text,
130                                            gint                 new_text_length,
131                                            gint                *position,
132                                            gpointer             user_data);
133 static void     delete_text_callback      (GtkFileChooserEntry *widget,
134                                            gint                 start_pos,
135                                            gint                 end_pos,
136                                            gpointer             user_data);
137 #endif
138
139 static gboolean match_selected_callback   (GtkEntryCompletion  *completion,
140                                            GtkTreeModel        *model,
141                                            GtkTreeIter         *iter,
142                                            GtkFileChooserEntry *chooser_entry);
143 static gboolean completion_match_func     (GtkEntryCompletion  *comp,
144                                            const char          *key,
145                                            GtkTreeIter         *iter,
146                                            gpointer             data);
147 static char    *maybe_append_separator_to_file (GtkFileChooserEntry *chooser_entry,
148                                                 GFile               *file,
149                                                 gchar               *display_name);
150
151 typedef enum {
152   REFRESH_UP_TO_CURSOR_POSITION,
153   REFRESH_WHOLE_TEXT
154 } RefreshMode;
155
156 static RefreshStatus refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry,
157                                                   RefreshMode refresh_mode);
158 static void finished_loading_cb (GtkFolder *folder,
159                                  gpointer   data);
160 static void autocomplete (GtkFileChooserEntry *chooser_entry);
161 static void install_start_autocompletion_idle (GtkFileChooserEntry *chooser_entry);
162 static void remove_completion_feedback (GtkFileChooserEntry *chooser_entry);
163 static void pop_up_completion_feedback (GtkFileChooserEntry *chooser_entry,
164                                         const gchar         *feedback);
165
166 static GtkEditableClass *parent_editable_iface;
167
168 G_DEFINE_TYPE_WITH_CODE (GtkFileChooserEntry, _gtk_file_chooser_entry, GTK_TYPE_ENTRY,
169                          G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,
170                                                 gtk_file_chooser_entry_iface_init))
171
172 static void
173 _gtk_file_chooser_entry_class_init (GtkFileChooserEntryClass *class)
174 {
175   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
176   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
177   GtkEntryClass *entry_class = GTK_ENTRY_CLASS (class);
178
179   gobject_class->finalize = gtk_file_chooser_entry_finalize;
180   gobject_class->dispose = gtk_file_chooser_entry_dispose;
181
182   widget_class->grab_focus = gtk_file_chooser_entry_grab_focus;
183   widget_class->unmap = gtk_file_chooser_entry_unmap;
184   widget_class->focus = gtk_file_chooser_entry_focus;
185   widget_class->focus_out_event = gtk_file_chooser_entry_focus_out_event;
186
187   entry_class->activate = gtk_file_chooser_entry_activate;
188 }
189
190 static void
191 gtk_file_chooser_entry_iface_init (GtkEditableClass *iface)
192 {
193   parent_editable_iface = g_type_interface_peek_parent (iface);
194
195   iface->do_insert_text = gtk_file_chooser_entry_do_insert_text;
196   iface->do_delete_text = gtk_file_chooser_entry_do_delete_text;
197   iface->set_position = gtk_file_chooser_entry_set_position;
198   iface->set_selection_bounds = gtk_file_chooser_entry_set_selection_bounds;
199 }
200
201 static void
202 _gtk_file_chooser_entry_init (GtkFileChooserEntry *chooser_entry)
203 {
204   GtkEntryCompletion *comp;
205   GtkCellRenderer *cell;
206
207   chooser_entry->local_only = TRUE;
208
209   g_object_set (chooser_entry, "truncate-multiline", TRUE, NULL);
210
211   comp = gtk_entry_completion_new ();
212   gtk_entry_completion_set_popup_single_match (comp, FALSE);
213
214   gtk_entry_completion_set_match_func (comp,
215                                        completion_match_func,
216                                        chooser_entry,
217                                        NULL);
218
219   cell = gtk_cell_renderer_text_new ();
220   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (comp),
221                               cell, TRUE);
222   gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (comp),
223                                  cell,
224                                  "text", 0);
225
226   g_signal_connect (comp, "match-selected",
227                     G_CALLBACK (match_selected_callback), chooser_entry);
228
229   gtk_entry_set_completion (GTK_ENTRY (chooser_entry), comp);
230   g_object_unref (comp);
231
232 #ifdef G_OS_WIN32
233   g_signal_connect (chooser_entry, "insert-text",
234                     G_CALLBACK (insert_text_callback), NULL);
235   g_signal_connect (chooser_entry, "delete-text",
236                     G_CALLBACK (delete_text_callback), NULL);
237 #endif
238 }
239
240 static void
241 gtk_file_chooser_entry_finalize (GObject *object)
242 {
243   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (object);
244
245   if (chooser_entry->base_folder)
246     g_object_unref (chooser_entry->base_folder);
247
248   if (chooser_entry->current_folder)
249     g_object_unref (chooser_entry->current_folder);
250
251   if (chooser_entry->current_folder_file)
252     g_object_unref (chooser_entry->current_folder_file);
253
254   g_free (chooser_entry->file_part);
255
256   G_OBJECT_CLASS (_gtk_file_chooser_entry_parent_class)->finalize (object);
257 }
258
259 static void
260 discard_current_folder (GtkFileChooserEntry *chooser_entry)
261 {
262   if (chooser_entry->current_folder)
263     {
264       g_signal_handlers_disconnect_by_func (chooser_entry->current_folder,
265                                             G_CALLBACK (finished_loading_cb), chooser_entry);
266       g_object_unref (chooser_entry->current_folder);
267       chooser_entry->current_folder = NULL;
268     }
269 }
270
271 static void
272 gtk_file_chooser_entry_dispose (GObject *object)
273 {
274   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (object);
275
276   remove_completion_feedback (chooser_entry);
277   discard_current_folder (chooser_entry);
278
279   if (chooser_entry->start_autocompletion_idle_id != 0)
280     {
281       g_source_remove (chooser_entry->start_autocompletion_idle_id);
282       chooser_entry->start_autocompletion_idle_id = 0;
283     }
284
285   if (chooser_entry->completion_store)
286     {
287       g_object_unref (chooser_entry->completion_store);
288       chooser_entry->completion_store = NULL;
289     }
290
291   if (chooser_entry->load_folder_cancellable)
292     {
293       g_cancellable_cancel (chooser_entry->load_folder_cancellable);
294       chooser_entry->load_folder_cancellable = NULL;
295     }
296
297   if (chooser_entry->file_system)
298     {
299       g_object_unref (chooser_entry->file_system);
300       chooser_entry->file_system = NULL;
301     }
302
303   G_OBJECT_CLASS (_gtk_file_chooser_entry_parent_class)->dispose (object);
304 }
305
306 /* Match functions for the GtkEntryCompletion */
307 static gboolean
308 match_selected_callback (GtkEntryCompletion  *completion,
309                          GtkTreeModel        *model,
310                          GtkTreeIter         *iter,
311                          GtkFileChooserEntry *chooser_entry)
312 {
313   char *display_name;
314   GFile *file;
315   gint pos;
316   
317   gtk_tree_model_get (model, iter,
318                       DISPLAY_NAME_COLUMN, &display_name,
319                       FILE_COLUMN, &file,
320                       -1);
321
322   if (!display_name || !file)
323     {
324       /* these shouldn't complain if passed NULL */
325       g_object_unref (file);
326       g_free (display_name);
327       return FALSE;
328     }
329
330   display_name = maybe_append_separator_to_file (chooser_entry, file, display_name);
331
332   pos = chooser_entry->file_part_pos;
333
334   /* We don't set in_change here as we want to update the current_folder
335    * variable */
336   gtk_editable_delete_text (GTK_EDITABLE (chooser_entry),
337                             pos, -1);
338   gtk_editable_insert_text (GTK_EDITABLE (chooser_entry),
339                             display_name, -1, 
340                             &pos);
341   gtk_editable_set_position (GTK_EDITABLE (chooser_entry), -1);
342
343   g_object_unref (file);
344   g_free (display_name);
345
346   return TRUE;
347 }
348
349 /* Match function for the GtkEntryCompletion */
350 static gboolean
351 completion_match_func (GtkEntryCompletion *comp,
352                        const char         *key_unused,
353                        GtkTreeIter        *iter,
354                        gpointer            data)
355 {
356   GtkFileChooserEntry *chooser_entry;
357   char *name;
358   gboolean result;
359   char *norm_file_part;
360   char *norm_name;
361
362   chooser_entry = GTK_FILE_CHOOSER_ENTRY (data);
363
364   /* We ignore the key because it is the contents of the entry.  Instead, we
365    * just use our precomputed file_part.
366    */
367   if (!chooser_entry->file_part)
368     {
369       return FALSE;
370     }
371
372   gtk_tree_model_get (GTK_TREE_MODEL (chooser_entry->completion_store), iter, DISPLAY_NAME_COLUMN, &name, -1);
373   if (!name)
374     {
375       return FALSE; /* Uninitialized row, ugh */
376     }
377
378   /* If we have an empty file_part, then we're at the root of a directory.  In
379    * that case, we want to match all non-dot files.  We might want to match
380    * dot_files too if show_hidden is TRUE on the fileselector in the future.
381    */
382   /* Additionally, support for gnome .hidden files would be sweet, too */
383   if (chooser_entry->file_part[0] == '\000')
384     {
385       if (name[0] == '.')
386         result = FALSE;
387       else
388         result = TRUE;
389       g_free (name);
390
391       return result;
392     }
393
394
395   norm_file_part = g_utf8_normalize (chooser_entry->file_part, -1, G_NORMALIZE_ALL);
396   norm_name = g_utf8_normalize (name, -1, G_NORMALIZE_ALL);
397
398 #ifdef G_PLATFORM_WIN32
399   {
400     gchar *temp;
401
402     temp = norm_file_part;
403     norm_file_part = g_utf8_casefold (norm_file_part, -1);
404     g_free (temp);
405
406     temp = norm_name;
407     norm_name = g_utf8_casefold (norm_name, -1);
408     g_free (temp);
409   }
410 #endif
411
412   result = (strncmp (norm_file_part, norm_name, strlen (norm_file_part)) == 0);
413
414   g_free (norm_file_part);
415   g_free (norm_name);
416   g_free (name);
417   
418   return result;
419 }
420
421 static void
422 clear_completions (GtkFileChooserEntry *chooser_entry)
423 {
424   chooser_entry->has_completion = FALSE;
425   chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING;
426
427   remove_completion_feedback (chooser_entry);
428 }
429
430 static void
431 beep (GtkFileChooserEntry *chooser_entry)
432 {
433   gtk_widget_error_bell (GTK_WIDGET (chooser_entry));
434 }
435
436 /* This function will append a directory separator to paths to
437  * display_name iff the path associated with it is a directory.
438  * maybe_append_separator_to_file will g_free the display_name and
439  * return a new one if needed.  Otherwise, it will return the old one.
440  * You should be safe calling
441  *
442  * display_name = maybe_append_separator_to_file (entry, file, display_name);
443  * ...
444  * g_free (display_name);
445  */
446 static char *
447 maybe_append_separator_to_file (GtkFileChooserEntry *chooser_entry,
448                                 GFile               *file,
449                                 gchar               *display_name)
450 {
451   if (!g_str_has_suffix (display_name, G_DIR_SEPARATOR_S) && file)
452     {
453       GFileInfo *info;
454
455       info = _gtk_folder_get_info (chooser_entry->current_folder, file);
456
457       if (info)
458         {
459           if (_gtk_file_info_consider_as_directory (info))
460             {
461               gchar *tmp = display_name;
462               display_name = g_strconcat (tmp, G_DIR_SEPARATOR_S, NULL);
463               g_free (tmp);
464             }
465
466           g_object_unref (info);
467         }
468     }
469
470   return display_name;
471 }
472
473 static char *
474 trim_dir_separator_suffix (const char *str)
475 {
476   int len;
477
478   len = strlen (str);
479   if (len > 0 && G_IS_DIR_SEPARATOR (str[len - 1]))
480     return g_strndup (str, len - 1);
481   else
482     return g_strdup (str);
483 }
484
485 /* Determines if the completion model has entries with a common prefix relative
486  * to the current contents of the entry.  Also, if there's one and only one such
487  * path, stores it in unique_path_ret.
488  */
489 static gboolean
490 find_common_prefix (GtkFileChooserEntry *chooser_entry,
491                     gchar               **common_prefix_ret,
492                     GFile               **unique_file_ret,
493                     gboolean             *is_complete_not_unique_ret,
494                     gboolean             *prefix_expands_the_file_part_ret,
495                     GError              **error)
496 {
497   GtkEditable *editable;
498   GtkTreeIter iter;
499   gboolean parsed;
500   gboolean valid;
501   char *text_up_to_cursor;
502   GFile *parsed_folder_file;
503   char *parsed_file_part;
504
505   *common_prefix_ret = NULL;
506   *unique_file_ret = NULL;
507   *is_complete_not_unique_ret = FALSE;
508   *prefix_expands_the_file_part_ret = FALSE;
509
510   editable = GTK_EDITABLE (chooser_entry);
511
512   text_up_to_cursor = gtk_editable_get_chars (editable, 0, gtk_editable_get_position (editable));
513
514   parsed = _gtk_file_system_parse (chooser_entry->file_system,
515                                    chooser_entry->base_folder,
516                                    text_up_to_cursor,
517                                    &parsed_folder_file,
518                                    &parsed_file_part,
519                                    error);
520
521   g_free (text_up_to_cursor);
522
523   if (!parsed)
524     return FALSE;
525
526   g_assert (parsed_folder_file != NULL
527             && chooser_entry->current_folder != NULL
528             && g_file_equal (parsed_folder_file, chooser_entry->current_folder_file));
529
530   g_object_unref (parsed_folder_file);
531
532   /* First pass: find the common prefix */
533
534   valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (chooser_entry->completion_store), &iter);
535
536   while (valid)
537     {
538       gchar *display_name;
539       GFile *file;
540
541       gtk_tree_model_get (GTK_TREE_MODEL (chooser_entry->completion_store),
542                           &iter,
543                           DISPLAY_NAME_COLUMN, &display_name,
544                           FILE_COLUMN, &file,
545                           -1);
546
547       if (g_str_has_prefix (display_name, parsed_file_part))
548         {
549           if (!*common_prefix_ret)
550             {
551               *common_prefix_ret = trim_dir_separator_suffix (display_name);
552               *unique_file_ret = g_object_ref (file);
553             }
554           else
555             {
556               gchar *p = *common_prefix_ret;
557               const gchar *q = display_name;
558
559               while (*p && *p == *q)
560                 {
561                   p++;
562                   q++;
563                 }
564
565               *p = '\0';
566
567               if (*unique_file_ret)
568                 {
569                   g_object_unref (*unique_file_ret);
570                   *unique_file_ret = NULL;
571                 }
572             }
573         }
574
575       g_free (display_name);
576       g_object_unref (file);
577       valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (chooser_entry->completion_store), &iter);
578     }
579
580   /* Second pass: see if the prefix we found is a complete match */
581
582   if (*common_prefix_ret != NULL)
583     {
584       valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (chooser_entry->completion_store), &iter);
585
586       while (valid)
587         {
588           gchar *display_name;
589           int len;
590
591           gtk_tree_model_get (GTK_TREE_MODEL (chooser_entry->completion_store),
592                               &iter,
593                               DISPLAY_NAME_COLUMN, &display_name,
594                               -1);
595           len = strlen (display_name);
596           g_assert (len > 0);
597
598           if (G_IS_DIR_SEPARATOR (display_name[len - 1]))
599             len--;
600
601           if (strncmp (*common_prefix_ret, display_name, len) == 0)
602             *is_complete_not_unique_ret = TRUE;
603
604           g_free (display_name);
605           valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (chooser_entry->completion_store), &iter);
606         }
607
608       /* Finally:  Did we generate a new completion, or was the user's input already completed as far as it could go? */
609
610       *prefix_expands_the_file_part_ret = g_utf8_strlen (*common_prefix_ret, -1) > g_utf8_strlen (parsed_file_part, -1);
611     }
612
613   g_free (parsed_file_part);
614
615   return TRUE;
616 }
617
618 static gboolean
619 char_after_cursor_is_directory_separator (GtkFileChooserEntry *chooser_entry)
620 {
621   int cursor_pos;
622   gboolean result;
623
624   result = FALSE;
625
626   cursor_pos = gtk_editable_get_position (GTK_EDITABLE (chooser_entry));
627   if (cursor_pos < GTK_ENTRY (chooser_entry)->text_length)
628     {
629       char *next_char_str;
630
631       next_char_str = gtk_editable_get_chars (GTK_EDITABLE (chooser_entry), cursor_pos, cursor_pos + 1);
632       if (G_IS_DIR_SEPARATOR (*next_char_str))
633         result = TRUE;
634
635       g_free (next_char_str);
636     }
637
638   return result;
639 }
640
641 typedef enum {
642   INVALID_INPUT,                /* what the user typed is bogus */
643   NO_MATCH,                     /* no matches based on what the user typed */
644   NOTHING_INSERTED_COMPLETE,    /* what the user typed is already completed as far as it will go */
645   NOTHING_INSERTED_UNIQUE,      /* what the user typed is already completed, and is a unique match */
646   COMPLETED,                    /* completion inserted (ambiguous suffix) */
647   COMPLETED_UNIQUE,             /* completion inserted, and it is a complete name and a unique match */
648   COMPLETE_BUT_NOT_UNIQUE       /* completion inserted, it is a complete name but not unique */
649 } CommonPrefixResult;
650
651 /* Finds a common prefix based on the contents of the entry and mandatorily appends it */
652 static CommonPrefixResult
653 append_common_prefix (GtkFileChooserEntry *chooser_entry,
654                       gboolean             highlight,
655                       gboolean             show_errors)
656 {
657   gchar *common_prefix;
658   GFile *unique_file;
659   gboolean is_complete_not_unique;
660   gboolean prefix_expands_the_file_part;
661   GError *error;
662   CommonPrefixResult result;
663   gboolean have_result;
664
665   clear_completions (chooser_entry);
666
667   if (chooser_entry->completion_store == NULL)
668     return NO_MATCH;
669
670   error = NULL;
671   if (!find_common_prefix (chooser_entry, &common_prefix, &unique_file, &is_complete_not_unique, &prefix_expands_the_file_part, &error))
672     {
673       /* If the user types an incomplete hostname ("http://foo" without a slash
674        * after that), it's not an error.  We just don't want to pop up a
675        * meaningless completion window in that state.
676        */
677       if (!g_error_matches (error, GTK_FILE_CHOOSER_ERROR, GTK_FILE_CHOOSER_ERROR_INCOMPLETE_HOSTNAME)
678           && show_errors)
679         {
680           beep (chooser_entry);
681           pop_up_completion_feedback (chooser_entry, _("Invalid path"));
682         }
683
684       g_error_free (error);
685
686       return INVALID_INPUT;
687     }
688
689   have_result = FALSE;
690
691   if (unique_file)
692     {
693       if (!char_after_cursor_is_directory_separator (chooser_entry))
694         common_prefix = maybe_append_separator_to_file (chooser_entry,
695                                                         unique_file,
696                                                         common_prefix);
697
698       g_object_unref (unique_file);
699
700       if (common_prefix)
701         {
702           if (prefix_expands_the_file_part)
703             result = COMPLETED_UNIQUE;
704           else
705             result = NOTHING_INSERTED_UNIQUE;
706         }
707       else
708         result = INVALID_INPUT;
709
710       have_result = TRUE;
711     }
712   else
713     {
714       if (is_complete_not_unique)
715         {
716           result = COMPLETE_BUT_NOT_UNIQUE;
717           have_result = TRUE;
718         }
719     }
720
721   if (common_prefix)
722     {
723       gint cursor_pos;
724       gint common_prefix_len;
725       gint pos;
726
727       cursor_pos = gtk_editable_get_position (GTK_EDITABLE (chooser_entry));
728       common_prefix_len = g_utf8_strlen (common_prefix, -1);
729
730       pos = chooser_entry->file_part_pos;
731
732       if (prefix_expands_the_file_part)
733         {
734           chooser_entry->in_change = TRUE;
735           gtk_editable_delete_text (GTK_EDITABLE (chooser_entry),
736                                     pos, cursor_pos);
737           gtk_editable_insert_text (GTK_EDITABLE (chooser_entry),
738                                     common_prefix, -1, 
739                                     &pos);
740           chooser_entry->in_change = FALSE;
741
742           if (highlight)
743             {
744               gtk_editable_select_region (GTK_EDITABLE (chooser_entry),
745                                           cursor_pos,
746                                           pos); /* equivalent to cursor_pos + common_prefix_len); */
747               chooser_entry->has_completion = TRUE;
748             }
749           else
750             gtk_editable_set_position (GTK_EDITABLE (chooser_entry), pos);
751         }
752       else if (!have_result)
753         {
754           result = NOTHING_INSERTED_COMPLETE;
755           have_result = TRUE;
756         }
757
758       g_free (common_prefix);
759
760       if (have_result)
761         return result;
762       else
763         return COMPLETED;
764     }
765   else
766     {
767       if (have_result)
768         return result;
769       else
770         return NO_MATCH;
771     }
772 }
773
774 static void
775 gtk_file_chooser_entry_do_insert_text (GtkEditable *editable,
776                                        const gchar *new_text,
777                                        gint         new_text_length,
778                                        gint        *position)
779 {
780   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (editable);
781   gint old_text_len;
782   gint insert_pos;
783
784   old_text_len = GTK_ENTRY (chooser_entry)->text_length;
785   insert_pos = *position;
786
787   parent_editable_iface->do_insert_text (editable, new_text, new_text_length, position);
788
789   if (chooser_entry->in_change)
790     return;
791
792   remove_completion_feedback (chooser_entry);
793
794   if ((chooser_entry->action == GTK_FILE_CHOOSER_ACTION_OPEN
795        || chooser_entry->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
796       && insert_pos == old_text_len)
797     install_start_autocompletion_idle (chooser_entry);
798 }
799
800 static void
801 clear_completions_if_not_in_change (GtkFileChooserEntry *chooser_entry)
802 {
803   if (chooser_entry->in_change)
804     return;
805
806   clear_completions (chooser_entry);
807 }
808
809 static void
810 gtk_file_chooser_entry_do_delete_text (GtkEditable *editable,
811                                        gint         start_pos,
812                                        gint         end_pos)
813 {
814   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (editable);
815
816   parent_editable_iface->do_delete_text (editable, start_pos, end_pos);
817
818   clear_completions_if_not_in_change (chooser_entry);
819 }
820
821 static void
822 gtk_file_chooser_entry_set_position (GtkEditable *editable,
823                                      gint         position)
824 {
825   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (editable);
826
827   parent_editable_iface->set_position (editable, position);
828
829   clear_completions_if_not_in_change (chooser_entry);
830 }
831
832 static void
833 gtk_file_chooser_entry_set_selection_bounds (GtkEditable *editable,
834                                              gint         start_pos,
835                                              gint         end_pos)
836 {
837   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (editable);
838
839   parent_editable_iface->set_selection_bounds (editable, start_pos, end_pos);
840
841   clear_completions_if_not_in_change (chooser_entry);
842 }
843
844 static void
845 gtk_file_chooser_entry_grab_focus (GtkWidget *widget)
846 {
847   GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->grab_focus (widget);
848   _gtk_file_chooser_entry_select_filename (GTK_FILE_CHOOSER_ENTRY (widget));
849 }
850
851 static void
852 gtk_file_chooser_entry_unmap (GtkWidget *widget)
853 {
854   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (widget);
855
856   remove_completion_feedback (chooser_entry);
857
858   GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->unmap (widget);
859 }
860
861 static gboolean
862 completion_feedback_window_expose_event_cb (GtkWidget      *widget,
863                                             GdkEventExpose *event,
864                                             gpointer        data)
865 {
866   /* Stolen from gtk_tooltip_paint_window() */
867
868   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (data);
869
870   gtk_paint_flat_box (chooser_entry->completion_feedback_window->style,
871                       chooser_entry->completion_feedback_window->window,
872                       GTK_STATE_NORMAL,
873                       GTK_SHADOW_OUT,
874                       NULL,
875                       chooser_entry->completion_feedback_window,
876                       "tooltip",
877                       0, 0,
878                       chooser_entry->completion_feedback_window->allocation.width,
879                       chooser_entry->completion_feedback_window->allocation.height);
880
881   return FALSE;
882 }
883
884 static void
885 set_invisible_mouse_cursor (GdkWindow *window)
886 {
887   GdkDisplay *display;
888   GdkCursor *cursor;
889
890   display = gdk_drawable_get_display (window);
891   cursor = gdk_cursor_new_for_display (display, GDK_BLANK_CURSOR);
892
893   gdk_window_set_cursor (window, cursor);
894
895   gdk_cursor_unref (cursor);
896 }
897
898 static void
899 completion_feedback_window_realize_cb (GtkWidget *widget,
900                                        gpointer data)
901 {
902   /* We hide the mouse cursor inside the completion feedback window, since
903    * GtkEntry hides the cursor when the user types.  We don't want the cursor to
904    * come back if the completion feedback ends up where the mouse is.
905    */
906   set_invisible_mouse_cursor (widget->window);
907 }
908
909 static void
910 create_completion_feedback_window (GtkFileChooserEntry *chooser_entry)
911 {
912   /* Stolen from gtk_tooltip_init() */
913
914   GtkWidget *alignment;
915
916   chooser_entry->completion_feedback_window = gtk_window_new (GTK_WINDOW_POPUP);
917   gtk_window_set_type_hint (GTK_WINDOW (chooser_entry->completion_feedback_window),
918                             GDK_WINDOW_TYPE_HINT_TOOLTIP);
919   gtk_widget_set_app_paintable (chooser_entry->completion_feedback_window, TRUE);
920   gtk_window_set_resizable (GTK_WINDOW (chooser_entry->completion_feedback_window), FALSE);
921   gtk_widget_set_name (chooser_entry->completion_feedback_window, "gtk-tooltip");
922
923   alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
924   gtk_alignment_set_padding (GTK_ALIGNMENT (alignment),
925                              chooser_entry->completion_feedback_window->style->ythickness,
926                              chooser_entry->completion_feedback_window->style->ythickness,
927                              chooser_entry->completion_feedback_window->style->xthickness,
928                              chooser_entry->completion_feedback_window->style->xthickness);
929   gtk_container_add (GTK_CONTAINER (chooser_entry->completion_feedback_window), alignment);
930   gtk_widget_show (alignment);
931
932   g_signal_connect (chooser_entry->completion_feedback_window, "expose-event",
933                     G_CALLBACK (completion_feedback_window_expose_event_cb), chooser_entry);
934   g_signal_connect (chooser_entry->completion_feedback_window, "realize",
935                     G_CALLBACK (completion_feedback_window_realize_cb), chooser_entry);
936   /* FIXME: connect to motion-notify-event, and *show* the cursor when the mouse moves */
937
938   chooser_entry->completion_feedback_label = gtk_label_new (NULL);
939   gtk_container_add (GTK_CONTAINER (alignment), chooser_entry->completion_feedback_label);
940   gtk_widget_show (chooser_entry->completion_feedback_label);
941 }
942
943 static gboolean
944 completion_feedback_timeout_cb (gpointer data)
945 {
946   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (data);
947
948   chooser_entry->completion_feedback_timeout_id = 0;
949
950   remove_completion_feedback (chooser_entry);
951   return FALSE;
952 }
953
954 static void
955 install_completion_feedback_timer (GtkFileChooserEntry *chooser_entry)
956 {
957   if (chooser_entry->completion_feedback_timeout_id != 0)
958     g_source_remove (chooser_entry->completion_feedback_timeout_id);
959
960   chooser_entry->completion_feedback_timeout_id = gdk_threads_add_timeout (COMPLETION_FEEDBACK_TIMEOUT_MS,
961                                                                            completion_feedback_timeout_cb,
962                                                                            chooser_entry);
963 }
964
965 /* Gets the x position of the text cursor in the entry, in widget coordinates */
966 static void
967 get_entry_cursor_x (GtkFileChooserEntry *chooser_entry,
968                     gint                *x_ret)
969 {
970   /* FIXME: see the docs for gtk_entry_get_layout_offsets().  As an exercise for
971    * the reader, you have to implement support for the entry's scroll offset and
972    * RTL layouts and all the fancy Pango stuff.
973    */
974
975   PangoLayout *layout;
976   gint layout_x, layout_y;
977   gint layout_index;
978   PangoRectangle strong_pos;
979
980   layout = gtk_entry_get_layout (GTK_ENTRY (chooser_entry));
981
982   gtk_entry_get_layout_offsets (GTK_ENTRY (chooser_entry), &layout_x, &layout_y);
983
984   layout_index = gtk_entry_text_index_to_layout_index (GTK_ENTRY (chooser_entry),
985                                                        GTK_ENTRY (chooser_entry)->current_pos);
986
987   pango_layout_get_cursor_pos (layout, layout_index, &strong_pos, NULL);
988
989   *x_ret = layout_x + strong_pos.x / PANGO_SCALE;
990 }
991
992 static void
993 show_completion_feedback_window (GtkFileChooserEntry *chooser_entry)
994 {
995   /* More or less stolen from gtk_tooltip_position() */
996
997   GtkRequisition feedback_req;
998   gint entry_x, entry_y;
999   gint cursor_x;
1000   GtkAllocation *entry_allocation;
1001   int feedback_x, feedback_y;
1002
1003   gtk_widget_size_request (chooser_entry->completion_feedback_window, &feedback_req);
1004
1005   gdk_window_get_origin (GTK_WIDGET (chooser_entry)->window, &entry_x, &entry_y);
1006   entry_allocation = &(GTK_WIDGET (chooser_entry)->allocation);
1007
1008   get_entry_cursor_x (chooser_entry, &cursor_x);
1009
1010   /* FIXME: fit to the screen if we bump on the screen's edge */
1011   feedback_x = entry_x + cursor_x + entry_allocation->height / 2; /* cheap "half M-width" */
1012   feedback_y = entry_y + (entry_allocation->height - feedback_req.height) / 2;
1013
1014   gtk_window_move (GTK_WINDOW (chooser_entry->completion_feedback_window), feedback_x, feedback_y);
1015   gtk_widget_show (chooser_entry->completion_feedback_window);
1016
1017   install_completion_feedback_timer (chooser_entry);
1018 }
1019
1020 static void
1021 pop_up_completion_feedback (GtkFileChooserEntry *chooser_entry,
1022                             const gchar         *feedback)
1023 {
1024   if (chooser_entry->completion_feedback_window == NULL)
1025     create_completion_feedback_window (chooser_entry);
1026
1027   gtk_label_set_text (GTK_LABEL (chooser_entry->completion_feedback_label), feedback);
1028
1029   show_completion_feedback_window (chooser_entry);
1030 }
1031
1032 static void
1033 remove_completion_feedback (GtkFileChooserEntry *chooser_entry)
1034 {
1035   if (chooser_entry->completion_feedback_window)
1036     gtk_widget_destroy (chooser_entry->completion_feedback_window);
1037
1038   chooser_entry->completion_feedback_window = NULL;
1039   chooser_entry->completion_feedback_label = NULL;
1040
1041   if (chooser_entry->completion_feedback_timeout_id != 0)
1042     {
1043       g_source_remove (chooser_entry->completion_feedback_timeout_id);
1044       chooser_entry->completion_feedback_timeout_id = 0;
1045     }
1046 }
1047
1048 static void
1049 explicitly_complete (GtkFileChooserEntry *chooser_entry)
1050 {
1051   CommonPrefixResult result;
1052
1053   g_assert (chooser_entry->current_folder != NULL);
1054   g_assert (_gtk_folder_is_finished_loading (chooser_entry->current_folder));
1055
1056   /* FIXME: see what Emacs does in case there is no common prefix, or there is more than one match:
1057    *
1058    * - If there is a common prefix, insert it (done)
1059    * - If there is no common prefix, pop up the suggestion window
1060    * - If there are no matches at all, beep and bring up a tooltip (done)
1061    * - If the suggestion window is already up, scroll it
1062    */
1063   result = append_common_prefix (chooser_entry, FALSE, TRUE);
1064
1065   switch (result)
1066     {
1067     case INVALID_INPUT:
1068       /* We already beeped in append_common_prefix(); do nothing here */
1069       break;
1070
1071     case NO_MATCH:
1072       beep (chooser_entry);
1073       /* translators: this text is shown when there are no completions 
1074        * for something the user typed in a file chooser entry
1075        */
1076       pop_up_completion_feedback (chooser_entry, _("No match"));
1077       break;
1078
1079     case NOTHING_INSERTED_COMPLETE:
1080       /* FIXME: pop up the suggestion window or scroll it */
1081       break;
1082
1083     case NOTHING_INSERTED_UNIQUE:
1084       /* translators: this text is shown when there is exactly one completion 
1085        * for something the user typed in a file chooser entry
1086        */
1087       pop_up_completion_feedback (chooser_entry, _("Sole completion"));
1088       break;
1089
1090     case COMPLETED:
1091       /* Nothing to do */
1092       break;
1093
1094     case COMPLETED_UNIQUE:
1095       /* Nothing to do */
1096       break;
1097
1098     case COMPLETE_BUT_NOT_UNIQUE:
1099       /* translators: this text is shown when the text in a file chooser
1100        * entry is a complete filename, but could be continued to find
1101        * a longer match
1102        */
1103       pop_up_completion_feedback (chooser_entry, _("Complete, but not unique"));
1104       break;
1105
1106     default:
1107       g_assert_not_reached ();
1108     }
1109 }
1110
1111 static void
1112 start_explicit_completion (GtkFileChooserEntry *chooser_entry)
1113 {
1114   /* FMQ: get result from the function below */
1115   refresh_current_folder_and_file_part (chooser_entry, REFRESH_UP_TO_CURSOR_POSITION);
1116
1117   if (!chooser_entry->current_folder_file)
1118     {
1119       /* Here, no folder path means we couldn't parse what the user typed. */
1120
1121       beep (chooser_entry);
1122       pop_up_completion_feedback (chooser_entry, _("Invalid path"));
1123
1124       chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING;
1125       return;
1126     }
1127
1128   if (chooser_entry->local_only
1129       && !g_file_is_native (chooser_entry->current_folder_file))
1130     {
1131       beep (chooser_entry);
1132       pop_up_completion_feedback (chooser_entry, _("Only local files can be selected"));
1133
1134       chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING;
1135       return;
1136     }
1137
1138   if (chooser_entry->current_folder
1139       && _gtk_folder_is_finished_loading (chooser_entry->current_folder))
1140     {
1141       explicitly_complete (chooser_entry);
1142     }
1143   else
1144     {
1145       chooser_entry->load_complete_action = LOAD_COMPLETE_EXPLICIT_COMPLETION;
1146
1147       /* translators: this text is shown while the system is searching
1148        * for possible completions for text in a file chooser entry 
1149        */
1150       pop_up_completion_feedback (chooser_entry, _("Completing..."));
1151     }
1152 }
1153
1154 static gboolean
1155 gtk_file_chooser_entry_focus (GtkWidget        *widget,
1156                               GtkDirectionType  direction)
1157 {
1158   GtkFileChooserEntry *chooser_entry;
1159   GtkEditable *editable;
1160   GtkEntry *entry;
1161   GdkModifierType state;
1162   gboolean control_pressed;
1163
1164   chooser_entry = GTK_FILE_CHOOSER_ENTRY (widget);
1165   editable = GTK_EDITABLE (widget);
1166   entry = GTK_ENTRY (widget);
1167
1168   if (!chooser_entry->eat_tabs)
1169     return GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->focus (widget, direction);
1170
1171   control_pressed = FALSE;
1172
1173   if (gtk_get_current_event_state (&state))
1174     {
1175       if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
1176         control_pressed = TRUE;
1177     }
1178
1179   /* This is a bit evil -- it makes Tab never leave the entry. It basically
1180    * makes it 'safe' for people to hit. */
1181   if ((direction == GTK_DIR_TAB_FORWARD) &&
1182       (GTK_WIDGET_HAS_FOCUS (widget)) &&
1183       (! control_pressed))
1184     {
1185       if (chooser_entry->has_completion)
1186         gtk_editable_set_position (editable, entry->text_length);
1187       else
1188         start_explicit_completion (chooser_entry);
1189
1190       return TRUE;
1191     }
1192   else
1193     return GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->focus (widget, direction);
1194 }
1195
1196 static gboolean
1197 gtk_file_chooser_entry_focus_out_event (GtkWidget     *widget,
1198                                         GdkEventFocus *event)
1199 {
1200   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (widget);
1201
1202   chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING;
1203  
1204   return GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->focus_out_event (widget, event);
1205 }
1206
1207 static void
1208 commit_completion_and_refresh (GtkFileChooserEntry *chooser_entry)
1209 {
1210   if (chooser_entry->has_completion)
1211     {
1212       gtk_editable_set_position (GTK_EDITABLE (chooser_entry),
1213                                  GTK_ENTRY (chooser_entry)->text_length);
1214     }
1215
1216   /* FMQ: get result from the function below */
1217   refresh_current_folder_and_file_part (chooser_entry, REFRESH_WHOLE_TEXT);
1218 }
1219
1220 static void
1221 gtk_file_chooser_entry_activate (GtkEntry *entry)
1222 {
1223   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (entry);
1224
1225   commit_completion_and_refresh (chooser_entry);
1226   GTK_ENTRY_CLASS (_gtk_file_chooser_entry_parent_class)->activate (entry);
1227 }
1228
1229 static void
1230 discard_completion_store (GtkFileChooserEntry *chooser_entry)
1231 {
1232   if (!chooser_entry->completion_store)
1233     return;
1234
1235   gtk_entry_completion_set_model (gtk_entry_get_completion (GTK_ENTRY (chooser_entry)), NULL);
1236   g_object_unref (chooser_entry->completion_store);
1237   chooser_entry->completion_store = NULL;
1238 }
1239
1240 /* Fills the completion store from the contents of the current folder */
1241 static void
1242 populate_completion_store (GtkFileChooserEntry *chooser_entry)
1243 {
1244   GSList *files;
1245   GSList *tmp_list;
1246
1247   discard_completion_store (chooser_entry);
1248
1249   files = _gtk_folder_list_children (chooser_entry->current_folder);
1250
1251   chooser_entry->completion_store = gtk_list_store_new (N_COLUMNS,
1252                                                         G_TYPE_STRING,
1253                                                         G_TYPE_FILE);
1254
1255   for (tmp_list = files; tmp_list; tmp_list = tmp_list->next)
1256     {
1257       GFileInfo *info;
1258       GFile *file;
1259
1260       file = tmp_list->data;
1261
1262       info = _gtk_folder_get_info (chooser_entry->current_folder, file);
1263
1264       if (info)
1265         {
1266           gchar *display_name = g_strdup (g_file_info_get_display_name (info));
1267           GtkTreeIter iter;
1268
1269           display_name = maybe_append_separator_to_file (chooser_entry, file, display_name);
1270
1271           gtk_list_store_append (chooser_entry->completion_store, &iter);
1272           gtk_list_store_set (chooser_entry->completion_store, &iter,
1273                               DISPLAY_NAME_COLUMN, display_name,
1274                               FILE_COLUMN, file,
1275                               -1);
1276
1277           g_object_unref (info);
1278           g_free (display_name);
1279         }
1280     }
1281
1282   g_slist_foreach (files, (GFunc) g_object_unref, NULL);
1283   g_slist_free (files);
1284
1285   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (chooser_entry->completion_store),
1286                                         DISPLAY_NAME_COLUMN, GTK_SORT_ASCENDING);
1287
1288   gtk_entry_completion_set_model (gtk_entry_get_completion (GTK_ENTRY (chooser_entry)),
1289                                   GTK_TREE_MODEL (chooser_entry->completion_store));
1290 }
1291
1292 /* When we finish loading the current folder, this function should get called to
1293  * perform the deferred autocompletion or explicit completion.
1294  */
1295 static void
1296 perform_load_complete_action (GtkFileChooserEntry *chooser_entry)
1297 {
1298   switch (chooser_entry->load_complete_action)
1299     {
1300     case LOAD_COMPLETE_NOTHING:
1301       break;
1302
1303     case LOAD_COMPLETE_AUTOCOMPLETE:
1304       autocomplete (chooser_entry);
1305       break;
1306
1307     case LOAD_COMPLETE_EXPLICIT_COMPLETION:
1308       explicitly_complete (chooser_entry);
1309       break;
1310
1311     default:
1312       g_assert_not_reached ();
1313     }
1314
1315   chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING;
1316 }
1317
1318 static void
1319 finish_folder_load (GtkFileChooserEntry *chooser_entry)
1320 {
1321   populate_completion_store (chooser_entry);
1322   perform_load_complete_action (chooser_entry);
1323
1324   gtk_widget_set_tooltip_text (GTK_WIDGET (chooser_entry), NULL);
1325 }
1326
1327 /* Callback when the current folder finishes loading */
1328 static void
1329 finished_loading_cb (GtkFolder *folder,
1330                      gpointer   data)
1331 {
1332   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (data);
1333
1334   finish_folder_load (chooser_entry);
1335 }
1336
1337 /* Callback when the current folder's handle gets obtained (not necessarily loaded completely) */
1338 static void
1339 load_directory_get_folder_callback (GCancellable  *cancellable,
1340                                     GtkFolder     *folder,
1341                                     const GError  *error,
1342                                     gpointer       data)
1343 {
1344   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1345   GtkFileChooserEntry *chooser_entry = data;
1346
1347   if (cancellable != chooser_entry->load_folder_cancellable)
1348     goto out;
1349
1350   chooser_entry->load_folder_cancellable = NULL;
1351
1352   if (error)
1353     {
1354       LoadCompleteAction old_load_complete_action;
1355
1356       old_load_complete_action = chooser_entry->load_complete_action;
1357
1358       discard_completion_store (chooser_entry);
1359       clear_completions (chooser_entry);
1360
1361       if (old_load_complete_action == LOAD_COMPLETE_EXPLICIT_COMPLETION)
1362         {
1363           /* Since this came from explicit user action (Tab completion), we'll present errors visually */
1364
1365           beep (chooser_entry);
1366           pop_up_completion_feedback (chooser_entry, error->message);
1367         }
1368
1369       discard_current_folder (chooser_entry);
1370     }
1371
1372   if (cancelled || error)
1373     goto out;
1374
1375   g_assert (folder != NULL);
1376   chooser_entry->current_folder = g_object_ref (folder);
1377
1378   discard_completion_store (chooser_entry);
1379
1380   if (_gtk_folder_is_finished_loading (chooser_entry->current_folder))
1381     finish_folder_load (chooser_entry);
1382   else
1383     g_signal_connect (chooser_entry->current_folder, "finished-loading",
1384                       G_CALLBACK (finished_loading_cb), chooser_entry);
1385
1386 out:
1387   g_object_unref (chooser_entry);
1388   g_object_unref (cancellable);
1389 }
1390
1391 static void
1392 start_loading_current_folder (GtkFileChooserEntry *chooser_entry)
1393 {
1394   if (chooser_entry->current_folder_file == NULL ||
1395       chooser_entry->file_system == NULL)
1396     return;
1397
1398   if (chooser_entry->local_only
1399       && !g_file_is_native (chooser_entry->current_folder_file))
1400     return;
1401
1402   g_assert (chooser_entry->current_folder == NULL);
1403   g_assert (chooser_entry->load_folder_cancellable == NULL);
1404
1405   chooser_entry->load_folder_cancellable =
1406     _gtk_file_system_get_folder (chooser_entry->file_system,
1407                                  chooser_entry->current_folder_file,
1408                                 "standard::name,standard::display-name,standard::type",
1409                                  load_directory_get_folder_callback,
1410                                  g_object_ref (chooser_entry));
1411 }
1412
1413 static void
1414 reload_current_folder (GtkFileChooserEntry *chooser_entry,
1415                        GFile               *folder_file,
1416                        gboolean             force_reload)
1417 {
1418   gboolean reload = FALSE;
1419
1420   if (chooser_entry->current_folder_file)
1421     {
1422       if ((folder_file && !(g_file_equal (folder_file, chooser_entry->current_folder_file)
1423                             && chooser_entry->load_folder_cancellable))
1424           || force_reload)
1425         {
1426           reload = TRUE;
1427
1428           /* We changed our current directory.  We need to clear out the old
1429            * directory information.
1430            */
1431           if (chooser_entry->load_folder_cancellable)
1432             {
1433               g_cancellable_cancel (chooser_entry->load_folder_cancellable);
1434               chooser_entry->load_folder_cancellable = NULL;
1435             }
1436
1437           discard_current_folder (chooser_entry);
1438           g_object_unref (chooser_entry->current_folder_file);
1439           chooser_entry->current_folder_file = (folder_file) ? g_object_ref (folder_file) : NULL;
1440         }
1441     }
1442   else
1443     {
1444       chooser_entry->current_folder_file = (folder_file) ? g_object_ref (folder_file) : NULL;
1445       reload = TRUE;
1446     }
1447
1448   if (reload)
1449     start_loading_current_folder (chooser_entry);
1450 }
1451
1452 static RefreshStatus
1453 refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry,
1454                                       RefreshMode          refresh_mode)
1455 {
1456   GtkEditable *editable;
1457   gint end_pos;
1458   gchar *text;
1459   GFile *folder_file;
1460   gchar *file_part;
1461   gsize total_len, file_part_len;
1462   gint file_part_pos;
1463   GError *error;
1464   RefreshStatus result;
1465
1466   editable = GTK_EDITABLE (chooser_entry);
1467
1468   switch (refresh_mode)
1469     {
1470     case REFRESH_UP_TO_CURSOR_POSITION:
1471       end_pos = gtk_editable_get_position (editable);
1472       break;
1473
1474     case REFRESH_WHOLE_TEXT:
1475       end_pos = GTK_ENTRY (chooser_entry)->text_length;
1476       break;
1477
1478     default:
1479       g_assert_not_reached ();
1480       return REFRESH_INVALID_INPUT;
1481     }
1482
1483   text = gtk_editable_get_chars (editable, 0, end_pos);
1484
1485   error = NULL;
1486   if (!chooser_entry->file_system ||
1487       !chooser_entry->base_folder ||
1488       !_gtk_file_system_parse (chooser_entry->file_system,
1489                                chooser_entry->base_folder, text,
1490                                &folder_file, &file_part, &error))
1491     {
1492       if (g_error_matches (error, GTK_FILE_CHOOSER_ERROR, GTK_FILE_CHOOSER_ERROR_INCOMPLETE_HOSTNAME))
1493         {
1494           folder_file = NULL;
1495           result = REFRESH_INCOMPLETE_HOSTNAME;
1496         }
1497       else
1498         {
1499           folder_file = (chooser_entry->base_folder) ? g_object_ref (chooser_entry->base_folder) : NULL;
1500
1501           if (g_error_matches (error, GTK_FILE_CHOOSER_ERROR, GTK_FILE_CHOOSER_ERROR_NONEXISTENT))
1502             result = REFRESH_NONEXISTENT;
1503           else
1504             result = REFRESH_INVALID_INPUT;
1505         }
1506
1507       if (error)
1508         g_error_free (error);
1509
1510       file_part = g_strdup ("");
1511       file_part_pos = -1;
1512     }
1513   else
1514     {
1515       file_part_len = strlen (file_part);
1516       total_len = strlen (text);
1517       if (total_len > file_part_len)
1518         file_part_pos = g_utf8_strlen (text, total_len - file_part_len);
1519       else
1520         file_part_pos = 0;
1521
1522       result = REFRESH_OK;
1523     }
1524
1525   g_free (text);
1526
1527   g_free (chooser_entry->file_part);
1528
1529   chooser_entry->file_part = file_part;
1530   chooser_entry->file_part_pos = file_part_pos;
1531
1532   /* FMQ: this needs to return an error if the folder is not local */
1533   reload_current_folder (chooser_entry, folder_file, file_part_pos == -1);
1534
1535   if (folder_file)
1536     g_object_unref (folder_file);
1537
1538   return result;
1539 }
1540
1541 static void
1542 autocomplete (GtkFileChooserEntry *chooser_entry)
1543 {
1544   g_assert (chooser_entry->current_folder != NULL);
1545   g_assert (_gtk_folder_is_finished_loading (chooser_entry->current_folder));
1546   g_assert (gtk_editable_get_position (GTK_EDITABLE (chooser_entry)) == GTK_ENTRY (chooser_entry)->text_length);
1547
1548   append_common_prefix (chooser_entry, TRUE, FALSE);
1549 }
1550
1551 static void
1552 start_autocompletion (GtkFileChooserEntry *chooser_entry)
1553 {
1554   /* FMQ: get result from the function below */
1555   refresh_current_folder_and_file_part (chooser_entry, REFRESH_UP_TO_CURSOR_POSITION);
1556
1557   if (!chooser_entry->current_folder)
1558     {
1559       /* We don't beep or anything, since this is autocompletion - the user
1560        * didn't request any action explicitly.
1561        */
1562       return;
1563     }
1564
1565   if (_gtk_folder_is_finished_loading (chooser_entry->current_folder))
1566     autocomplete (chooser_entry);
1567   else
1568     chooser_entry->load_complete_action = LOAD_COMPLETE_AUTOCOMPLETE;
1569 }
1570
1571 static gboolean
1572 start_autocompletion_idle_handler (gpointer data)
1573 {
1574   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (data);
1575
1576   start_autocompletion (chooser_entry);
1577
1578   chooser_entry->start_autocompletion_idle_id = 0;
1579
1580   return FALSE;
1581 }
1582
1583 static void
1584 install_start_autocompletion_idle (GtkFileChooserEntry *chooser_entry)
1585 {
1586   if (chooser_entry->start_autocompletion_idle_id != 0)
1587     return;
1588
1589   chooser_entry->start_autocompletion_idle_id = gdk_threads_add_idle (start_autocompletion_idle_handler, chooser_entry);
1590 }
1591
1592 #ifdef G_OS_WIN32
1593 static gint
1594 insert_text_callback (GtkFileChooserEntry *chooser_entry,
1595                       const gchar         *new_text,
1596                       gint                 new_text_length,
1597                       gint                *position,
1598                       gpointer             user_data)
1599 {
1600   const gchar *colon = memchr (new_text, ':', new_text_length);
1601   gint i;
1602
1603   /* Disallow these characters altogether */
1604   for (i = 0; i < new_text_length; i++)
1605     {
1606       if (new_text[i] == '<' ||
1607           new_text[i] == '>' ||
1608           new_text[i] == '"' ||
1609           new_text[i] == '|' ||
1610           new_text[i] == '*' ||
1611           new_text[i] == '?')
1612         break;
1613     }
1614
1615   if (i < new_text_length ||
1616       /* Disallow entering text that would cause a colon to be anywhere except
1617        * after a drive letter.
1618        */
1619       (colon != NULL &&
1620        *position + (colon - new_text) != 1) ||
1621       (new_text_length > 0 &&
1622        *position <= 1 &&
1623        GTK_ENTRY (chooser_entry)->text_length >= 2 &&
1624        gtk_entry_get_text (GTK_ENTRY (chooser_entry))[1] == ':'))
1625     {
1626       gtk_widget_error_bell (GTK_WIDGET (chooser_entry));
1627       g_signal_stop_emission_by_name (chooser_entry, "insert_text");
1628       return FALSE;
1629     }
1630
1631   return TRUE;
1632 }
1633
1634 static void
1635 delete_text_callback (GtkFileChooserEntry *chooser_entry,
1636                       gint                 start_pos,
1637                       gint                 end_pos,
1638                       gpointer             user_data)
1639 {
1640   /* If deleting a drive letter, delete the colon, too */
1641   if (start_pos == 0 && end_pos == 1 &&
1642       GTK_ENTRY (chooser_entry)->text_length >= 2 &&
1643       gtk_entry_get_text (GTK_ENTRY (chooser_entry))[1] == ':')
1644     {
1645       g_signal_handlers_block_by_func (chooser_entry,
1646                                        G_CALLBACK (delete_text_callback),
1647                                        user_data);
1648       gtk_editable_delete_text (GTK_EDITABLE (chooser_entry), 0, 1);
1649       g_signal_handlers_unblock_by_func (chooser_entry,
1650                                          G_CALLBACK (delete_text_callback),
1651                                          user_data);
1652     }
1653 }
1654 #endif
1655
1656 /**
1657  * _gtk_file_chooser_entry_new:
1658  * @eat_tabs: If %FALSE, allow focus navigation with the tab key.
1659  *
1660  * Creates a new #GtkFileChooserEntry object. #GtkFileChooserEntry
1661  * is an internal implementation widget for the GTK+ file chooser
1662  * which is an entry with completion with respect to a
1663  * #GtkFileSystem object.
1664  *
1665  * Return value: the newly created #GtkFileChooserEntry
1666  **/
1667 GtkWidget *
1668 _gtk_file_chooser_entry_new (gboolean eat_tabs)
1669 {
1670   GtkFileChooserEntry *chooser_entry;
1671
1672   chooser_entry = g_object_new (GTK_TYPE_FILE_CHOOSER_ENTRY, NULL);
1673   chooser_entry->eat_tabs = (eat_tabs != FALSE);
1674
1675   return GTK_WIDGET (chooser_entry);
1676 }
1677
1678 /**
1679  * _gtk_file_chooser_entry_set_file_system:
1680  * @chooser_entry: a #GtkFileChooser
1681  * @file_system: an object implementing #GtkFileSystem
1682  *
1683  * Sets the file system for @chooser_entry.
1684  **/
1685 void
1686 _gtk_file_chooser_entry_set_file_system (GtkFileChooserEntry *chooser_entry,
1687                                          GtkFileSystem       *file_system)
1688 {
1689   g_return_if_fail (GTK_IS_FILE_CHOOSER_ENTRY (chooser_entry));
1690   g_return_if_fail (GTK_IS_FILE_SYSTEM (file_system));
1691
1692   if (file_system != chooser_entry->file_system)
1693     {
1694       if (chooser_entry->file_system)
1695         g_object_unref (chooser_entry->file_system);
1696
1697       chooser_entry->file_system = g_object_ref (file_system);
1698     }
1699 }
1700
1701 /**
1702  * _gtk_file_chooser_entry_set_base_folder:
1703  * @chooser_entry: a #GtkFileChooserEntry
1704  * @file: file for a folder in the chooser entries current file system.
1705  *
1706  * Sets the folder with respect to which completions occur.
1707  **/
1708 void
1709 _gtk_file_chooser_entry_set_base_folder (GtkFileChooserEntry *chooser_entry,
1710                                          GFile               *file)
1711 {
1712   if (chooser_entry->base_folder)
1713     g_object_unref (chooser_entry->base_folder);
1714
1715   chooser_entry->base_folder = file;
1716
1717   if (chooser_entry->base_folder)
1718     g_object_ref (chooser_entry->base_folder);
1719
1720   clear_completions (chooser_entry);
1721   _gtk_file_chooser_entry_select_filename (chooser_entry);
1722 }
1723
1724 /**
1725  * _gtk_file_chooser_entry_get_current_folder:
1726  * @chooser_entry: a #GtkFileChooserEntry
1727  *
1728  * Gets the current folder for the #GtkFileChooserEntry. If the
1729  * user has only entered a filename, this will be the base folder
1730  * (see _gtk_file_chooser_entry_set_base_folder()), but if the
1731  * user has entered a relative or absolute path, then it will
1732  * be different. If the user has entered a relative or absolute
1733  * path that doesn't point to a folder in the file system, it will
1734  * be %NULL.
1735  *
1736  * Return value: the file for the current folder - this value is owned by the
1737  *  chooser entry and must not be modified or freed.
1738  **/
1739 GFile *
1740 _gtk_file_chooser_entry_get_current_folder (GtkFileChooserEntry *chooser_entry)
1741 {
1742   commit_completion_and_refresh (chooser_entry);
1743   return chooser_entry->current_folder_file;
1744 }
1745
1746 /**
1747  * _gtk_file_chooser_entry_get_file_part:
1748  * @chooser_entry: a #GtkFileChooserEntry
1749  *
1750  * Gets the non-folder portion of whatever the user has entered
1751  * into the file selector. What is returned is a UTF-8 string,
1752  * and if a filename path is needed, g_file_get_child_for_display_name()
1753  * must be used
1754   *
1755  * Return value: the entered filename - this value is owned by the
1756  *  chooser entry and must not be modified or freed.
1757  **/
1758 const gchar *
1759 _gtk_file_chooser_entry_get_file_part (GtkFileChooserEntry *chooser_entry)
1760 {
1761   commit_completion_and_refresh (chooser_entry);
1762   return chooser_entry->file_part;
1763 }
1764
1765 /**
1766  * _gtk_file_chooser_entry_set_file_part:
1767  * @chooser_entry: a #GtkFileChooserEntry
1768  * @file_part: text to display in the entry, in UTF-8
1769  *
1770  * Sets the current text shown in the file chooser entry.
1771  **/
1772 void
1773 _gtk_file_chooser_entry_set_file_part (GtkFileChooserEntry *chooser_entry,
1774                                        const gchar         *file_part)
1775 {
1776   g_return_if_fail (GTK_IS_FILE_CHOOSER_ENTRY (chooser_entry));
1777
1778   chooser_entry->in_change = TRUE;
1779   clear_completions (chooser_entry);
1780   gtk_entry_set_text (GTK_ENTRY (chooser_entry), file_part);
1781   chooser_entry->in_change = FALSE;
1782 }
1783
1784
1785 /**
1786  * _gtk_file_chooser_entry_set_action:
1787  * @chooser_entry: a #GtkFileChooserEntry
1788  * @action: the action which is performed by the file selector using this entry
1789  *
1790  * Sets action which is performed by the file selector using this entry. 
1791  * The #GtkFileChooserEntry will use different completion strategies for 
1792  * different actions.
1793  **/
1794 void
1795 _gtk_file_chooser_entry_set_action (GtkFileChooserEntry *chooser_entry,
1796                                     GtkFileChooserAction action)
1797 {
1798   g_return_if_fail (GTK_IS_FILE_CHOOSER_ENTRY (chooser_entry));
1799   
1800   if (chooser_entry->action != action)
1801     {
1802       GtkEntryCompletion *comp;
1803
1804       chooser_entry->action = action;
1805
1806       comp = gtk_entry_get_completion (GTK_ENTRY (chooser_entry));
1807
1808       /* FIXME: do we need to actually set the following? */
1809
1810       switch (action)
1811         {
1812         case GTK_FILE_CHOOSER_ACTION_OPEN:
1813         case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1814           gtk_entry_completion_set_popup_single_match (comp, FALSE);
1815           break;
1816         case GTK_FILE_CHOOSER_ACTION_SAVE:
1817         case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
1818           gtk_entry_completion_set_popup_single_match (comp, TRUE);
1819           break;
1820         }
1821     }
1822 }
1823
1824
1825 /**
1826  * _gtk_file_chooser_entry_get_action:
1827  * @chooser_entry: a #GtkFileChooserEntry
1828  *
1829  * Gets the action for this entry. 
1830  *
1831  * Returns: the action
1832  **/
1833 GtkFileChooserAction
1834 _gtk_file_chooser_entry_get_action (GtkFileChooserEntry *chooser_entry)
1835 {
1836   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_ENTRY (chooser_entry),
1837                         GTK_FILE_CHOOSER_ACTION_OPEN);
1838   
1839   return chooser_entry->action;
1840 }
1841
1842 gboolean
1843 _gtk_file_chooser_entry_get_is_folder (GtkFileChooserEntry *chooser_entry,
1844                                        GFile               *file)
1845 {
1846   gboolean retval = FALSE;
1847
1848   if (chooser_entry->current_folder)
1849     {
1850       GFileInfo *file_info;
1851
1852       file_info = _gtk_folder_get_info (chooser_entry->current_folder, file);
1853
1854       if (file_info)
1855         {
1856           retval = _gtk_file_info_consider_as_directory (file_info);
1857           g_object_unref (file_info);
1858         }
1859     }
1860
1861   return retval;
1862 }
1863
1864
1865 /*
1866  * _gtk_file_chooser_entry_select_filename:
1867  * @chooser_entry: a #GtkFileChooserEntry
1868  *
1869  * Selects the filename (without the extension) for user edition.
1870  */
1871 void
1872 _gtk_file_chooser_entry_select_filename (GtkFileChooserEntry *chooser_entry)
1873 {
1874   const gchar *str, *ext;
1875   glong len = -1;
1876
1877   if (chooser_entry->action == GTK_FILE_CHOOSER_ACTION_SAVE)
1878     {
1879       str = gtk_entry_get_text (GTK_ENTRY (chooser_entry));
1880       ext = g_strrstr (str, ".");
1881
1882       if (ext)
1883        len = g_utf8_pointer_to_offset (str, ext);
1884     }
1885
1886   gtk_editable_select_region (GTK_EDITABLE (chooser_entry), 0, (gint) len);
1887 }
1888
1889 void
1890 _gtk_file_chooser_entry_set_local_only (GtkFileChooserEntry *chooser_entry,
1891                                         gboolean             local_only)
1892 {
1893   chooser_entry->local_only = local_only;
1894   clear_completions (chooser_entry);
1895 }
1896
1897 gboolean
1898 _gtk_file_chooser_entry_get_local_only (GtkFileChooserEntry *chooser_entry)
1899 {
1900   return chooser_entry->local_only;
1901 }