]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserentry.c
Don't handle errors when committing the completion
[~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   RefreshStatus status;
1115   gboolean is_error;
1116   char *feedback_msg;
1117
1118   status = refresh_current_folder_and_file_part (chooser_entry, REFRESH_UP_TO_CURSOR_POSITION);
1119
1120   is_error = FALSE;
1121
1122   switch (status)
1123     {
1124     case REFRESH_OK:
1125       g_assert (chooser_entry->current_folder_file != NULL);
1126
1127       if (chooser_entry->current_folder
1128           && _gtk_folder_is_finished_loading (chooser_entry->current_folder))
1129         {
1130           explicitly_complete (chooser_entry);
1131         }
1132       else
1133         {
1134           chooser_entry->load_complete_action = LOAD_COMPLETE_EXPLICIT_COMPLETION;
1135
1136           /* translators: this text is shown while the system is searching
1137            * for possible completions for text in a file chooser entry 
1138            */
1139           pop_up_completion_feedback (chooser_entry, _("Completing..."));
1140         }
1141
1142       break;
1143
1144     case REFRESH_INVALID_INPUT:
1145       is_error = TRUE;
1146       feedback_msg = _("Invalid path");
1147       break;
1148
1149     case REFRESH_INCOMPLETE_HOSTNAME:
1150       is_error = TRUE;
1151
1152       if (chooser_entry->local_only)
1153         feedback_msg = _("Only local files may be selected"); /* hostnames in a local_only file chooser?  user error */
1154       else
1155         {
1156           /* Another option is to complete the hostname based on the remote volumes that are mounted */
1157           feedback_msg = _("Incomplete hostname; end it with '/'");
1158         }
1159
1160       break;
1161
1162     case REFRESH_NONEXISTENT:
1163       is_error = TRUE;
1164       feedback_msg = _("Path does not exist");
1165       break;
1166
1167     case REFRESH_NOT_LOCAL:
1168       is_error = TRUE;
1169       feedback_msg = _("Only local files may be selected");
1170       break;
1171
1172     default:
1173       g_assert_not_reached ();
1174       return;
1175     }
1176
1177   if (is_error)
1178     {
1179       g_assert (chooser_entry->current_folder_file == NULL);
1180
1181       beep (chooser_entry);
1182       pop_up_completion_feedback (chooser_entry, feedback_msg);
1183       chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING;
1184     }
1185 }
1186
1187 static gboolean
1188 gtk_file_chooser_entry_focus (GtkWidget        *widget,
1189                               GtkDirectionType  direction)
1190 {
1191   GtkFileChooserEntry *chooser_entry;
1192   GtkEditable *editable;
1193   GtkEntry *entry;
1194   GdkModifierType state;
1195   gboolean control_pressed;
1196
1197   chooser_entry = GTK_FILE_CHOOSER_ENTRY (widget);
1198   editable = GTK_EDITABLE (widget);
1199   entry = GTK_ENTRY (widget);
1200
1201   if (!chooser_entry->eat_tabs)
1202     return GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->focus (widget, direction);
1203
1204   control_pressed = FALSE;
1205
1206   if (gtk_get_current_event_state (&state))
1207     {
1208       if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
1209         control_pressed = TRUE;
1210     }
1211
1212   /* This is a bit evil -- it makes Tab never leave the entry. It basically
1213    * makes it 'safe' for people to hit. */
1214   if ((direction == GTK_DIR_TAB_FORWARD) &&
1215       (GTK_WIDGET_HAS_FOCUS (widget)) &&
1216       (! control_pressed))
1217     {
1218       if (chooser_entry->has_completion)
1219         gtk_editable_set_position (editable, entry->text_length);
1220       else
1221         start_explicit_completion (chooser_entry);
1222
1223       return TRUE;
1224     }
1225   else
1226     return GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->focus (widget, direction);
1227 }
1228
1229 static gboolean
1230 gtk_file_chooser_entry_focus_out_event (GtkWidget     *widget,
1231                                         GdkEventFocus *event)
1232 {
1233   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (widget);
1234
1235   chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING;
1236  
1237   return GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->focus_out_event (widget, event);
1238 }
1239
1240 static void
1241 commit_completion_and_refresh (GtkFileChooserEntry *chooser_entry)
1242 {
1243   if (chooser_entry->has_completion)
1244     {
1245       gtk_editable_set_position (GTK_EDITABLE (chooser_entry),
1246                                  GTK_ENTRY (chooser_entry)->text_length);
1247     }
1248
1249   /* Here we ignore the result of refresh_current_folder_and_file_part(); there is nothing we can do with it */
1250   refresh_current_folder_and_file_part (chooser_entry, REFRESH_WHOLE_TEXT);
1251 }
1252
1253 static void
1254 gtk_file_chooser_entry_activate (GtkEntry *entry)
1255 {
1256   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (entry);
1257
1258   commit_completion_and_refresh (chooser_entry);
1259   GTK_ENTRY_CLASS (_gtk_file_chooser_entry_parent_class)->activate (entry);
1260 }
1261
1262 static void
1263 discard_completion_store (GtkFileChooserEntry *chooser_entry)
1264 {
1265   if (!chooser_entry->completion_store)
1266     return;
1267
1268   gtk_entry_completion_set_model (gtk_entry_get_completion (GTK_ENTRY (chooser_entry)), NULL);
1269   g_object_unref (chooser_entry->completion_store);
1270   chooser_entry->completion_store = NULL;
1271 }
1272
1273 /* Fills the completion store from the contents of the current folder */
1274 static void
1275 populate_completion_store (GtkFileChooserEntry *chooser_entry)
1276 {
1277   GSList *files;
1278   GSList *tmp_list;
1279
1280   discard_completion_store (chooser_entry);
1281
1282   files = _gtk_folder_list_children (chooser_entry->current_folder);
1283
1284   chooser_entry->completion_store = gtk_list_store_new (N_COLUMNS,
1285                                                         G_TYPE_STRING,
1286                                                         G_TYPE_FILE);
1287
1288   for (tmp_list = files; tmp_list; tmp_list = tmp_list->next)
1289     {
1290       GFileInfo *info;
1291       GFile *file;
1292
1293       file = tmp_list->data;
1294
1295       info = _gtk_folder_get_info (chooser_entry->current_folder, file);
1296
1297       if (info)
1298         {
1299           gchar *display_name = g_strdup (g_file_info_get_display_name (info));
1300           GtkTreeIter iter;
1301
1302           display_name = maybe_append_separator_to_file (chooser_entry, file, display_name);
1303
1304           gtk_list_store_append (chooser_entry->completion_store, &iter);
1305           gtk_list_store_set (chooser_entry->completion_store, &iter,
1306                               DISPLAY_NAME_COLUMN, display_name,
1307                               FILE_COLUMN, file,
1308                               -1);
1309
1310           g_object_unref (info);
1311           g_free (display_name);
1312         }
1313     }
1314
1315   g_slist_foreach (files, (GFunc) g_object_unref, NULL);
1316   g_slist_free (files);
1317
1318   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (chooser_entry->completion_store),
1319                                         DISPLAY_NAME_COLUMN, GTK_SORT_ASCENDING);
1320
1321   gtk_entry_completion_set_model (gtk_entry_get_completion (GTK_ENTRY (chooser_entry)),
1322                                   GTK_TREE_MODEL (chooser_entry->completion_store));
1323 }
1324
1325 /* When we finish loading the current folder, this function should get called to
1326  * perform the deferred autocompletion or explicit completion.
1327  */
1328 static void
1329 perform_load_complete_action (GtkFileChooserEntry *chooser_entry)
1330 {
1331   switch (chooser_entry->load_complete_action)
1332     {
1333     case LOAD_COMPLETE_NOTHING:
1334       break;
1335
1336     case LOAD_COMPLETE_AUTOCOMPLETE:
1337       autocomplete (chooser_entry);
1338       break;
1339
1340     case LOAD_COMPLETE_EXPLICIT_COMPLETION:
1341       explicitly_complete (chooser_entry);
1342       break;
1343
1344     default:
1345       g_assert_not_reached ();
1346     }
1347
1348   chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING;
1349 }
1350
1351 static void
1352 finish_folder_load (GtkFileChooserEntry *chooser_entry)
1353 {
1354   populate_completion_store (chooser_entry);
1355   perform_load_complete_action (chooser_entry);
1356
1357   gtk_widget_set_tooltip_text (GTK_WIDGET (chooser_entry), NULL);
1358 }
1359
1360 /* Callback when the current folder finishes loading */
1361 static void
1362 finished_loading_cb (GtkFolder *folder,
1363                      gpointer   data)
1364 {
1365   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (data);
1366
1367   finish_folder_load (chooser_entry);
1368 }
1369
1370 /* Callback when the current folder's handle gets obtained (not necessarily loaded completely) */
1371 static void
1372 load_directory_get_folder_callback (GCancellable  *cancellable,
1373                                     GtkFolder     *folder,
1374                                     const GError  *error,
1375                                     gpointer       data)
1376 {
1377   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1378   GtkFileChooserEntry *chooser_entry = data;
1379
1380   if (cancellable != chooser_entry->load_folder_cancellable)
1381     goto out;
1382
1383   chooser_entry->load_folder_cancellable = NULL;
1384
1385   if (error)
1386     {
1387       LoadCompleteAction old_load_complete_action;
1388
1389       old_load_complete_action = chooser_entry->load_complete_action;
1390
1391       discard_completion_store (chooser_entry);
1392       clear_completions (chooser_entry);
1393
1394       if (old_load_complete_action == LOAD_COMPLETE_EXPLICIT_COMPLETION)
1395         {
1396           /* Since this came from explicit user action (Tab completion), we'll present errors visually */
1397
1398           beep (chooser_entry);
1399           pop_up_completion_feedback (chooser_entry, error->message);
1400         }
1401
1402       discard_current_folder (chooser_entry);
1403     }
1404
1405   if (cancelled || error)
1406     goto out;
1407
1408   g_assert (folder != NULL);
1409   chooser_entry->current_folder = g_object_ref (folder);
1410
1411   discard_completion_store (chooser_entry);
1412
1413   if (_gtk_folder_is_finished_loading (chooser_entry->current_folder))
1414     finish_folder_load (chooser_entry);
1415   else
1416     g_signal_connect (chooser_entry->current_folder, "finished-loading",
1417                       G_CALLBACK (finished_loading_cb), chooser_entry);
1418
1419 out:
1420   g_object_unref (chooser_entry);
1421   g_object_unref (cancellable);
1422 }
1423
1424 static void
1425 start_loading_current_folder (GtkFileChooserEntry *chooser_entry)
1426 {
1427   if (chooser_entry->current_folder_file == NULL ||
1428       chooser_entry->file_system == NULL)
1429     return;
1430
1431   if (chooser_entry->local_only
1432       && !g_file_is_native (chooser_entry->current_folder_file))
1433     return;
1434
1435   g_assert (chooser_entry->current_folder == NULL);
1436   g_assert (chooser_entry->load_folder_cancellable == NULL);
1437
1438   chooser_entry->load_folder_cancellable =
1439     _gtk_file_system_get_folder (chooser_entry->file_system,
1440                                  chooser_entry->current_folder_file,
1441                                 "standard::name,standard::display-name,standard::type",
1442                                  load_directory_get_folder_callback,
1443                                  g_object_ref (chooser_entry));
1444 }
1445
1446 static void
1447 reload_current_folder (GtkFileChooserEntry *chooser_entry,
1448                        GFile               *folder_file,
1449                        gboolean             force_reload)
1450 {
1451   gboolean reload = FALSE;
1452
1453   if (chooser_entry->current_folder_file)
1454     {
1455       if ((folder_file && !(g_file_equal (folder_file, chooser_entry->current_folder_file)
1456                             && chooser_entry->load_folder_cancellable))
1457           || force_reload)
1458         {
1459           reload = TRUE;
1460
1461           /* We changed our current directory.  We need to clear out the old
1462            * directory information.
1463            */
1464           if (chooser_entry->load_folder_cancellable)
1465             {
1466               g_cancellable_cancel (chooser_entry->load_folder_cancellable);
1467               chooser_entry->load_folder_cancellable = NULL;
1468             }
1469
1470           discard_current_folder (chooser_entry);
1471           g_object_unref (chooser_entry->current_folder_file);
1472           chooser_entry->current_folder_file = (folder_file) ? g_object_ref (folder_file) : NULL;
1473         }
1474     }
1475   else
1476     {
1477       chooser_entry->current_folder_file = (folder_file) ? g_object_ref (folder_file) : NULL;
1478       reload = TRUE;
1479     }
1480
1481   if (reload)
1482     start_loading_current_folder (chooser_entry);
1483 }
1484
1485 static RefreshStatus
1486 refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry,
1487                                       RefreshMode          refresh_mode)
1488 {
1489   GtkEditable *editable;
1490   gint end_pos;
1491   gchar *text;
1492   GFile *folder_file;
1493   gchar *file_part;
1494   gsize total_len, file_part_len;
1495   gint file_part_pos;
1496   GError *error;
1497   RefreshStatus result;
1498
1499   editable = GTK_EDITABLE (chooser_entry);
1500
1501   switch (refresh_mode)
1502     {
1503     case REFRESH_UP_TO_CURSOR_POSITION:
1504       end_pos = gtk_editable_get_position (editable);
1505       break;
1506
1507     case REFRESH_WHOLE_TEXT:
1508       end_pos = GTK_ENTRY (chooser_entry)->text_length;
1509       break;
1510
1511     default:
1512       g_assert_not_reached ();
1513       return REFRESH_INVALID_INPUT;
1514     }
1515
1516   text = gtk_editable_get_chars (editable, 0, end_pos);
1517
1518   error = NULL;
1519   if (!chooser_entry->file_system ||
1520       !chooser_entry->base_folder ||
1521       !_gtk_file_system_parse (chooser_entry->file_system,
1522                                chooser_entry->base_folder, text,
1523                                &folder_file, &file_part, &error))
1524     {
1525       if (g_error_matches (error, GTK_FILE_CHOOSER_ERROR, GTK_FILE_CHOOSER_ERROR_INCOMPLETE_HOSTNAME))
1526         {
1527           folder_file = NULL;
1528           result = REFRESH_INCOMPLETE_HOSTNAME;
1529         }
1530       else
1531         {
1532           folder_file = (chooser_entry->base_folder) ? g_object_ref (chooser_entry->base_folder) : NULL;
1533
1534           if (g_error_matches (error, GTK_FILE_CHOOSER_ERROR, GTK_FILE_CHOOSER_ERROR_NONEXISTENT))
1535             result = REFRESH_NONEXISTENT;
1536           else
1537             result = REFRESH_INVALID_INPUT;
1538         }
1539
1540       if (error)
1541         g_error_free (error);
1542
1543       file_part = g_strdup ("");
1544       file_part_pos = -1;
1545     }
1546   else
1547     {
1548       file_part_len = strlen (file_part);
1549       total_len = strlen (text);
1550       if (total_len > file_part_len)
1551         file_part_pos = g_utf8_strlen (text, total_len - file_part_len);
1552       else
1553         file_part_pos = 0;
1554
1555       result = REFRESH_OK;
1556     }
1557
1558   g_free (text);
1559
1560   g_free (chooser_entry->file_part);
1561
1562   chooser_entry->file_part = file_part;
1563   chooser_entry->file_part_pos = file_part_pos;
1564
1565   /* FMQ: this needs to return an error if the folder is not local */
1566   reload_current_folder (chooser_entry, folder_file, file_part_pos == -1);
1567
1568   if (folder_file)
1569     g_object_unref (folder_file);
1570
1571   return result;
1572 }
1573
1574 static void
1575 autocomplete (GtkFileChooserEntry *chooser_entry)
1576 {
1577   g_assert (chooser_entry->current_folder != NULL);
1578   g_assert (_gtk_folder_is_finished_loading (chooser_entry->current_folder));
1579   g_assert (gtk_editable_get_position (GTK_EDITABLE (chooser_entry)) == GTK_ENTRY (chooser_entry)->text_length);
1580
1581   append_common_prefix (chooser_entry, TRUE, FALSE);
1582 }
1583
1584 static void
1585 start_autocompletion (GtkFileChooserEntry *chooser_entry)
1586 {
1587   /* FMQ: get result from the function below */
1588   refresh_current_folder_and_file_part (chooser_entry, REFRESH_UP_TO_CURSOR_POSITION);
1589
1590   if (!chooser_entry->current_folder)
1591     {
1592       /* We don't beep or anything, since this is autocompletion - the user
1593        * didn't request any action explicitly.
1594        */
1595       return;
1596     }
1597
1598   if (_gtk_folder_is_finished_loading (chooser_entry->current_folder))
1599     autocomplete (chooser_entry);
1600   else
1601     chooser_entry->load_complete_action = LOAD_COMPLETE_AUTOCOMPLETE;
1602 }
1603
1604 static gboolean
1605 start_autocompletion_idle_handler (gpointer data)
1606 {
1607   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (data);
1608
1609   start_autocompletion (chooser_entry);
1610
1611   chooser_entry->start_autocompletion_idle_id = 0;
1612
1613   return FALSE;
1614 }
1615
1616 static void
1617 install_start_autocompletion_idle (GtkFileChooserEntry *chooser_entry)
1618 {
1619   if (chooser_entry->start_autocompletion_idle_id != 0)
1620     return;
1621
1622   chooser_entry->start_autocompletion_idle_id = gdk_threads_add_idle (start_autocompletion_idle_handler, chooser_entry);
1623 }
1624
1625 #ifdef G_OS_WIN32
1626 static gint
1627 insert_text_callback (GtkFileChooserEntry *chooser_entry,
1628                       const gchar         *new_text,
1629                       gint                 new_text_length,
1630                       gint                *position,
1631                       gpointer             user_data)
1632 {
1633   const gchar *colon = memchr (new_text, ':', new_text_length);
1634   gint i;
1635
1636   /* Disallow these characters altogether */
1637   for (i = 0; i < new_text_length; i++)
1638     {
1639       if (new_text[i] == '<' ||
1640           new_text[i] == '>' ||
1641           new_text[i] == '"' ||
1642           new_text[i] == '|' ||
1643           new_text[i] == '*' ||
1644           new_text[i] == '?')
1645         break;
1646     }
1647
1648   if (i < new_text_length ||
1649       /* Disallow entering text that would cause a colon to be anywhere except
1650        * after a drive letter.
1651        */
1652       (colon != NULL &&
1653        *position + (colon - new_text) != 1) ||
1654       (new_text_length > 0 &&
1655        *position <= 1 &&
1656        GTK_ENTRY (chooser_entry)->text_length >= 2 &&
1657        gtk_entry_get_text (GTK_ENTRY (chooser_entry))[1] == ':'))
1658     {
1659       gtk_widget_error_bell (GTK_WIDGET (chooser_entry));
1660       g_signal_stop_emission_by_name (chooser_entry, "insert_text");
1661       return FALSE;
1662     }
1663
1664   return TRUE;
1665 }
1666
1667 static void
1668 delete_text_callback (GtkFileChooserEntry *chooser_entry,
1669                       gint                 start_pos,
1670                       gint                 end_pos,
1671                       gpointer             user_data)
1672 {
1673   /* If deleting a drive letter, delete the colon, too */
1674   if (start_pos == 0 && end_pos == 1 &&
1675       GTK_ENTRY (chooser_entry)->text_length >= 2 &&
1676       gtk_entry_get_text (GTK_ENTRY (chooser_entry))[1] == ':')
1677     {
1678       g_signal_handlers_block_by_func (chooser_entry,
1679                                        G_CALLBACK (delete_text_callback),
1680                                        user_data);
1681       gtk_editable_delete_text (GTK_EDITABLE (chooser_entry), 0, 1);
1682       g_signal_handlers_unblock_by_func (chooser_entry,
1683                                          G_CALLBACK (delete_text_callback),
1684                                          user_data);
1685     }
1686 }
1687 #endif
1688
1689 /**
1690  * _gtk_file_chooser_entry_new:
1691  * @eat_tabs: If %FALSE, allow focus navigation with the tab key.
1692  *
1693  * Creates a new #GtkFileChooserEntry object. #GtkFileChooserEntry
1694  * is an internal implementation widget for the GTK+ file chooser
1695  * which is an entry with completion with respect to a
1696  * #GtkFileSystem object.
1697  *
1698  * Return value: the newly created #GtkFileChooserEntry
1699  **/
1700 GtkWidget *
1701 _gtk_file_chooser_entry_new (gboolean eat_tabs)
1702 {
1703   GtkFileChooserEntry *chooser_entry;
1704
1705   chooser_entry = g_object_new (GTK_TYPE_FILE_CHOOSER_ENTRY, NULL);
1706   chooser_entry->eat_tabs = (eat_tabs != FALSE);
1707
1708   return GTK_WIDGET (chooser_entry);
1709 }
1710
1711 /**
1712  * _gtk_file_chooser_entry_set_file_system:
1713  * @chooser_entry: a #GtkFileChooser
1714  * @file_system: an object implementing #GtkFileSystem
1715  *
1716  * Sets the file system for @chooser_entry.
1717  **/
1718 void
1719 _gtk_file_chooser_entry_set_file_system (GtkFileChooserEntry *chooser_entry,
1720                                          GtkFileSystem       *file_system)
1721 {
1722   g_return_if_fail (GTK_IS_FILE_CHOOSER_ENTRY (chooser_entry));
1723   g_return_if_fail (GTK_IS_FILE_SYSTEM (file_system));
1724
1725   if (file_system != chooser_entry->file_system)
1726     {
1727       if (chooser_entry->file_system)
1728         g_object_unref (chooser_entry->file_system);
1729
1730       chooser_entry->file_system = g_object_ref (file_system);
1731     }
1732 }
1733
1734 /**
1735  * _gtk_file_chooser_entry_set_base_folder:
1736  * @chooser_entry: a #GtkFileChooserEntry
1737  * @file: file for a folder in the chooser entries current file system.
1738  *
1739  * Sets the folder with respect to which completions occur.
1740  **/
1741 void
1742 _gtk_file_chooser_entry_set_base_folder (GtkFileChooserEntry *chooser_entry,
1743                                          GFile               *file)
1744 {
1745   if (chooser_entry->base_folder)
1746     g_object_unref (chooser_entry->base_folder);
1747
1748   chooser_entry->base_folder = file;
1749
1750   if (chooser_entry->base_folder)
1751     g_object_ref (chooser_entry->base_folder);
1752
1753   clear_completions (chooser_entry);
1754   _gtk_file_chooser_entry_select_filename (chooser_entry);
1755 }
1756
1757 /**
1758  * _gtk_file_chooser_entry_get_current_folder:
1759  * @chooser_entry: a #GtkFileChooserEntry
1760  *
1761  * Gets the current folder for the #GtkFileChooserEntry. If the
1762  * user has only entered a filename, this will be in the base folder
1763  * (see _gtk_file_chooser_entry_set_base_folder()), but if the
1764  * user has entered a relative or absolute path, then it will
1765  * be different.  If the user has entered unparsable text, or text which
1766  * the entry cannot handle, this will return %NULL.
1767  *
1768  * Return value: the file for the current folder - this value is owned by the
1769  *  chooser entry and must not be modified or freed.
1770  **/
1771 GFile *
1772 _gtk_file_chooser_entry_get_current_folder (GtkFileChooserEntry *chooser_entry)
1773 {
1774   commit_completion_and_refresh (chooser_entry);
1775   return chooser_entry->current_folder_file;
1776 }
1777
1778 /**
1779  * _gtk_file_chooser_entry_get_file_part:
1780  * @chooser_entry: a #GtkFileChooserEntry
1781  *
1782  * Gets the non-folder portion of whatever the user has entered
1783  * into the file selector. What is returned is a UTF-8 string,
1784  * and if a filename path is needed, g_file_get_child_for_display_name()
1785  * must be used
1786   *
1787  * Return value: the entered filename - this value is owned by the
1788  *  chooser entry and must not be modified or freed.
1789  **/
1790 const gchar *
1791 _gtk_file_chooser_entry_get_file_part (GtkFileChooserEntry *chooser_entry)
1792 {
1793   commit_completion_and_refresh (chooser_entry);
1794   return chooser_entry->file_part;
1795 }
1796
1797 /**
1798  * _gtk_file_chooser_entry_set_file_part:
1799  * @chooser_entry: a #GtkFileChooserEntry
1800  * @file_part: text to display in the entry, in UTF-8
1801  *
1802  * Sets the current text shown in the file chooser entry.
1803  **/
1804 void
1805 _gtk_file_chooser_entry_set_file_part (GtkFileChooserEntry *chooser_entry,
1806                                        const gchar         *file_part)
1807 {
1808   g_return_if_fail (GTK_IS_FILE_CHOOSER_ENTRY (chooser_entry));
1809
1810   chooser_entry->in_change = TRUE;
1811   clear_completions (chooser_entry);
1812   gtk_entry_set_text (GTK_ENTRY (chooser_entry), file_part);
1813   chooser_entry->in_change = FALSE;
1814 }
1815
1816
1817 /**
1818  * _gtk_file_chooser_entry_set_action:
1819  * @chooser_entry: a #GtkFileChooserEntry
1820  * @action: the action which is performed by the file selector using this entry
1821  *
1822  * Sets action which is performed by the file selector using this entry. 
1823  * The #GtkFileChooserEntry will use different completion strategies for 
1824  * different actions.
1825  **/
1826 void
1827 _gtk_file_chooser_entry_set_action (GtkFileChooserEntry *chooser_entry,
1828                                     GtkFileChooserAction action)
1829 {
1830   g_return_if_fail (GTK_IS_FILE_CHOOSER_ENTRY (chooser_entry));
1831   
1832   if (chooser_entry->action != action)
1833     {
1834       GtkEntryCompletion *comp;
1835
1836       chooser_entry->action = action;
1837
1838       comp = gtk_entry_get_completion (GTK_ENTRY (chooser_entry));
1839
1840       /* FIXME: do we need to actually set the following? */
1841
1842       switch (action)
1843         {
1844         case GTK_FILE_CHOOSER_ACTION_OPEN:
1845         case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1846           gtk_entry_completion_set_popup_single_match (comp, FALSE);
1847           break;
1848         case GTK_FILE_CHOOSER_ACTION_SAVE:
1849         case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
1850           gtk_entry_completion_set_popup_single_match (comp, TRUE);
1851           break;
1852         }
1853     }
1854 }
1855
1856
1857 /**
1858  * _gtk_file_chooser_entry_get_action:
1859  * @chooser_entry: a #GtkFileChooserEntry
1860  *
1861  * Gets the action for this entry. 
1862  *
1863  * Returns: the action
1864  **/
1865 GtkFileChooserAction
1866 _gtk_file_chooser_entry_get_action (GtkFileChooserEntry *chooser_entry)
1867 {
1868   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_ENTRY (chooser_entry),
1869                         GTK_FILE_CHOOSER_ACTION_OPEN);
1870   
1871   return chooser_entry->action;
1872 }
1873
1874 gboolean
1875 _gtk_file_chooser_entry_get_is_folder (GtkFileChooserEntry *chooser_entry,
1876                                        GFile               *file)
1877 {
1878   gboolean retval = FALSE;
1879
1880   if (chooser_entry->current_folder)
1881     {
1882       GFileInfo *file_info;
1883
1884       file_info = _gtk_folder_get_info (chooser_entry->current_folder, file);
1885
1886       if (file_info)
1887         {
1888           retval = _gtk_file_info_consider_as_directory (file_info);
1889           g_object_unref (file_info);
1890         }
1891     }
1892
1893   return retval;
1894 }
1895
1896
1897 /*
1898  * _gtk_file_chooser_entry_select_filename:
1899  * @chooser_entry: a #GtkFileChooserEntry
1900  *
1901  * Selects the filename (without the extension) for user edition.
1902  */
1903 void
1904 _gtk_file_chooser_entry_select_filename (GtkFileChooserEntry *chooser_entry)
1905 {
1906   const gchar *str, *ext;
1907   glong len = -1;
1908
1909   if (chooser_entry->action == GTK_FILE_CHOOSER_ACTION_SAVE)
1910     {
1911       str = gtk_entry_get_text (GTK_ENTRY (chooser_entry));
1912       ext = g_strrstr (str, ".");
1913
1914       if (ext)
1915        len = g_utf8_pointer_to_offset (str, ext);
1916     }
1917
1918   gtk_editable_select_region (GTK_EDITABLE (chooser_entry), 0, (gint) len);
1919 }
1920
1921 void
1922 _gtk_file_chooser_entry_set_local_only (GtkFileChooserEntry *chooser_entry,
1923                                         gboolean             local_only)
1924 {
1925   chooser_entry->local_only = local_only;
1926   clear_completions (chooser_entry);
1927 }
1928
1929 gboolean
1930 _gtk_file_chooser_entry_get_local_only (GtkFileChooserEntry *chooser_entry)
1931 {
1932   return chooser_entry->local_only;
1933 }