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