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