]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserentry.c
Update comments on explicit completion
[~andy/gtk] / gtk / gtkfilechooserentry.c
1 /* GTK - The GIMP Toolkit
2  * gtkfilechooserentry.c: Entry with filename completion
3  * Copyright (C) 2003, Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include <config.h>
22 #include <string.h>
23
24 #include "gtkcelllayout.h"
25 #include "gtkcellrenderertext.h"
26 #include "gtkentry.h"
27 #include "gtkfilechooserentry.h"
28 #include "gtkmain.h"
29 #include "gtkintl.h"
30 #include "gtkalias.h"
31
32 typedef struct _GtkFileChooserEntryClass GtkFileChooserEntryClass;
33
34 #define GTK_FILE_CHOOSER_ENTRY_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_ENTRY, GtkFileChooserEntryClass))
35 #define GTK_IS_FILE_CHOOSER_ENTRY_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_ENTRY))
36 #define GTK_FILE_CHOOSER_ENTRY_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_ENTRY, GtkFileChooserEntryClass))
37
38 struct _GtkFileChooserEntryClass
39 {
40   GtkEntryClass parent_class;
41 };
42
43 /* Action to take when the current folder finishes loading (for explicit or automatic completion) */
44 typedef enum {
45   LOAD_COMPLETE_NOTHING,
46   LOAD_COMPLETE_AUTOCOMPLETE,
47   LOAD_COMPLETE_EXPLICIT_COMPLETION
48 } LoadCompleteAction;
49
50 struct _GtkFileChooserEntry
51 {
52   GtkEntry parent_instance;
53
54   GtkFileChooserAction action;
55
56   GtkFileSystem *file_system;
57   GtkFilePath *base_folder;
58   gchar *file_part;
59   gint file_part_pos;
60
61   /* Folder being loaded or already loaded */
62   GtkFilePath *current_folder_path;
63   GtkFileFolder *current_folder;
64   GtkFileSystemHandle *load_folder_handle;
65
66   LoadCompleteAction load_complete_action;
67
68   GtkListStore *completion_store;
69
70   guint start_autocompletion_idle_id;
71
72   guint has_completion : 1;
73   guint in_change      : 1;
74   guint eat_tabs       : 1;
75 };
76
77 enum
78 {
79   DISPLAY_NAME_COLUMN,
80   PATH_COLUMN,
81   N_COLUMNS
82 };
83
84 static void     gtk_file_chooser_entry_iface_init     (GtkEditableClass *iface);
85
86 static void     gtk_file_chooser_entry_finalize       (GObject          *object);
87 static void     gtk_file_chooser_entry_dispose        (GObject          *object);
88 static void     gtk_file_chooser_entry_grab_focus     (GtkWidget        *widget);
89 static gboolean gtk_file_chooser_entry_focus          (GtkWidget        *widget,
90                                                        GtkDirectionType  direction);
91 static gboolean gtk_file_chooser_entry_focus_out_event (GtkWidget       *widget,
92                                                         GdkEventFocus   *event);
93 static void     gtk_file_chooser_entry_activate       (GtkEntry         *entry);
94 static void     gtk_file_chooser_entry_do_insert_text (GtkEditable *editable,
95                                                        const gchar *new_text,
96                                                        gint         new_text_length,
97                                                        gint        *position);
98 static void     gtk_file_chooser_entry_do_delete_text (GtkEditable *editable,
99                                                        gint         start_pos,
100                                                        gint         end_pos);
101 static void     gtk_file_chooser_entry_set_position (GtkEditable *editable,
102                                                      gint         position);
103 static void     gtk_file_chooser_entry_set_selection_bounds (GtkEditable *editable,
104                                                              gint         start_pos,
105                                                              gint         end_pos);
106
107 #ifdef G_OS_WIN32
108 static gint     insert_text_callback      (GtkFileChooserEntry *widget,
109                                            const gchar         *new_text,
110                                            gint                 new_text_length,
111                                            gint                *position,
112                                            gpointer             user_data);
113 static void     delete_text_callback      (GtkFileChooserEntry *widget,
114                                            gint                 start_pos,
115                                            gint                 end_pos,
116                                            gpointer             user_data);
117 #endif
118
119 static gboolean match_selected_callback   (GtkEntryCompletion  *completion,
120                                            GtkTreeModel        *model,
121                                            GtkTreeIter         *iter,
122                                            GtkFileChooserEntry *chooser_entry);
123 static gboolean completion_match_func     (GtkEntryCompletion  *comp,
124                                            const char          *key,
125                                            GtkTreeIter         *iter,
126                                            gpointer             data);
127 static char    *maybe_append_separator_to_path (GtkFileChooserEntry *chooser_entry,
128                                                 GtkFilePath         *path,
129                                                 gchar               *display_name);
130
131 typedef enum {
132   REFRESH_UP_TO_CURSOR_POSITION,
133   REFRESH_WHOLE_TEXT
134 } RefreshMode;
135
136 static void refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry,
137                                                   RefreshMode refresh_mode);
138 static void finished_loading_cb (GtkFileFolder *folder,
139                                  gpointer       data);
140 static void autocomplete (GtkFileChooserEntry *chooser_entry);
141 static void install_start_autocompletion_idle (GtkFileChooserEntry *chooser_entry);
142
143 static GtkEditableClass *parent_editable_iface;
144
145 G_DEFINE_TYPE_WITH_CODE (GtkFileChooserEntry, _gtk_file_chooser_entry, GTK_TYPE_ENTRY,
146                          G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,
147                                                 gtk_file_chooser_entry_iface_init))
148
149 static void
150 _gtk_file_chooser_entry_class_init (GtkFileChooserEntryClass *class)
151 {
152   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
153   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
154   GtkEntryClass *entry_class = GTK_ENTRY_CLASS (class);
155
156   gobject_class->finalize = gtk_file_chooser_entry_finalize;
157   gobject_class->dispose = gtk_file_chooser_entry_dispose;
158
159   widget_class->grab_focus = gtk_file_chooser_entry_grab_focus;
160   widget_class->focus = gtk_file_chooser_entry_focus;
161   widget_class->focus_out_event = gtk_file_chooser_entry_focus_out_event;
162
163   entry_class->activate = gtk_file_chooser_entry_activate;
164 }
165
166 static void
167 gtk_file_chooser_entry_iface_init (GtkEditableClass *iface)
168 {
169   parent_editable_iface = g_type_interface_peek_parent (iface);
170
171   iface->do_insert_text = gtk_file_chooser_entry_do_insert_text;
172   iface->do_delete_text = gtk_file_chooser_entry_do_delete_text;
173   iface->set_position = gtk_file_chooser_entry_set_position;
174   iface->set_selection_bounds = gtk_file_chooser_entry_set_selection_bounds;
175 }
176
177 static void
178 _gtk_file_chooser_entry_init (GtkFileChooserEntry *chooser_entry)
179 {
180   GtkEntryCompletion *comp;
181   GtkCellRenderer *cell;
182
183   g_object_set (chooser_entry, "truncate-multiline", TRUE, NULL);
184
185   comp = gtk_entry_completion_new ();
186   gtk_entry_completion_set_popup_single_match (comp, FALSE);
187
188   gtk_entry_completion_set_match_func (comp,
189                                        completion_match_func,
190                                        chooser_entry,
191                                        NULL);
192
193   cell = gtk_cell_renderer_text_new ();
194   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (comp),
195                               cell, TRUE);
196   gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (comp),
197                                  cell,
198                                  "text", 0);
199
200   g_signal_connect (comp, "match_selected",
201                     G_CALLBACK (match_selected_callback), chooser_entry);
202
203   gtk_entry_set_completion (GTK_ENTRY (chooser_entry), comp);
204   g_object_unref (comp);
205
206 #ifdef G_OS_WIN32
207   g_signal_connect (chooser_entry, "insert_text",
208                     G_CALLBACK (insert_text_callback), NULL);
209   g_signal_connect (chooser_entry, "delete_text",
210                     G_CALLBACK (delete_text_callback), NULL);
211 #endif
212 }
213
214 static void
215 gtk_file_chooser_entry_finalize (GObject *object)
216 {
217   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (object);
218
219   gtk_file_path_free (chooser_entry->base_folder);
220   gtk_file_path_free (chooser_entry->current_folder_path);
221   g_free (chooser_entry->file_part);
222
223   G_OBJECT_CLASS (_gtk_file_chooser_entry_parent_class)->finalize (object);
224 }
225
226 static void
227 gtk_file_chooser_entry_dispose (GObject *object)
228 {
229   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (object);
230
231   if (chooser_entry->start_autocompletion_idle_id != 0)
232     {
233       g_source_remove (chooser_entry->start_autocompletion_idle_id);
234       chooser_entry->start_autocompletion_idle_id = 0;
235     }
236
237   if (chooser_entry->completion_store)
238     {
239       g_object_unref (chooser_entry->completion_store);
240       chooser_entry->completion_store = NULL;
241     }
242
243   if (chooser_entry->load_folder_handle)
244     {
245       gtk_file_system_cancel_operation (chooser_entry->load_folder_handle);
246       chooser_entry->load_folder_handle = NULL;
247     }
248
249   if (chooser_entry->current_folder)
250     {
251       g_signal_handlers_disconnect_by_func (chooser_entry->current_folder,
252                                             G_CALLBACK (finished_loading_cb), chooser_entry);
253       g_object_unref (chooser_entry->current_folder);
254       chooser_entry->current_folder = NULL;
255     }
256
257   if (chooser_entry->file_system)
258     {
259       g_object_unref (chooser_entry->file_system);
260       chooser_entry->file_system = NULL;
261     }
262
263   G_OBJECT_CLASS (_gtk_file_chooser_entry_parent_class)->dispose (object);
264 }
265
266 /* Match functions for the GtkEntryCompletion */
267 static gboolean
268 match_selected_callback (GtkEntryCompletion  *completion,
269                          GtkTreeModel        *model,
270                          GtkTreeIter         *iter,
271                          GtkFileChooserEntry *chooser_entry)
272 {
273   char *display_name;
274   GtkFilePath *path;
275   gint pos;
276   
277   gtk_tree_model_get (model, iter,
278                       DISPLAY_NAME_COLUMN, &display_name,
279                       PATH_COLUMN, &path,
280                       -1);
281
282   if (!display_name || !path)
283     {
284       /* these shouldn't complain if passed NULL */
285       gtk_file_path_free (path);
286       g_free (display_name);
287       return FALSE;
288     }
289
290   display_name = maybe_append_separator_to_path (chooser_entry, path, display_name);
291
292   pos = chooser_entry->file_part_pos;
293
294   /* We don't set in_change here as we want to update the current_folder
295    * variable */
296   gtk_editable_delete_text (GTK_EDITABLE (chooser_entry),
297                             pos, -1);
298   gtk_editable_insert_text (GTK_EDITABLE (chooser_entry),
299                             display_name, -1, 
300                             &pos);
301   gtk_editable_set_position (GTK_EDITABLE (chooser_entry), -1);
302
303   gtk_file_path_free (path);
304   g_free (display_name);
305
306   return TRUE;
307 }
308
309 /* Match function for the GtkEntryCompletion */
310 static gboolean
311 completion_match_func (GtkEntryCompletion *comp,
312                        const char         *key_unused,
313                        GtkTreeIter        *iter,
314                        gpointer            data)
315 {
316   GtkFileChooserEntry *chooser_entry;
317   char *name;
318   gboolean result;
319   char *norm_file_part;
320   char *norm_name;
321
322   chooser_entry = GTK_FILE_CHOOSER_ENTRY (data);
323
324   /* We ignore the key because it is the contents of the entry.  Instead, we
325    * just use our precomputed file_part.
326    */
327   if (!chooser_entry->file_part)
328     {
329       return FALSE;
330     }
331
332   gtk_tree_model_get (GTK_TREE_MODEL (chooser_entry->completion_store), iter, DISPLAY_NAME_COLUMN, &name, -1);
333   if (!name)
334     {
335       return FALSE; /* Uninitialized row, ugh */
336     }
337
338   /* If we have an empty file_part, then we're at the root of a directory.  In
339    * that case, we want to match all non-dot files.  We might want to match
340    * dot_files too if show_hidden is TRUE on the fileselector in the future.
341    */
342   /* Additionally, support for gnome .hidden files would be sweet, too */
343   if (chooser_entry->file_part[0] == '\000')
344     {
345       if (name[0] == '.')
346         result = FALSE;
347       else
348         result = TRUE;
349       g_free (name);
350
351       return result;
352     }
353
354
355   norm_file_part = g_utf8_normalize (chooser_entry->file_part, -1, G_NORMALIZE_ALL);
356   norm_name = g_utf8_normalize (name, -1, G_NORMALIZE_ALL);
357
358 #ifdef G_PLATFORM_WIN32
359   {
360     gchar *temp;
361
362     temp = norm_file_part;
363     norm_file_part = g_utf8_casefold (norm_file_part, -1);
364     g_free (temp);
365
366     temp = norm_name;
367     norm_name = g_utf8_casefold (norm_name, -1);
368     g_free (temp);
369   }
370 #endif
371
372   result = (strncmp (norm_file_part, norm_name, strlen (norm_file_part)) == 0);
373
374   g_free (norm_file_part);
375   g_free (norm_name);
376   g_free (name);
377   
378   return result;
379 }
380
381 static void
382 clear_completions (GtkFileChooserEntry *chooser_entry)
383 {
384   chooser_entry->has_completion = FALSE;
385   chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING;
386 }
387
388 static void
389 beep (GtkFileChooserEntry *chooser_entry)
390 {
391   gdk_display_beep (gtk_widget_get_display (GTK_WIDGET (chooser_entry)));
392 }
393
394 /* This function will append a directory separator to paths to
395  * display_name iff the path associated with it is a directory.
396  * maybe_append_separator_to_path will g_free the display_name and
397  * return a new one if needed.  Otherwise, it will return the old one.
398  * You should be safe calling
399  *
400  * display_name = maybe_append_separator_to_path (entry, path, display_name);
401  * ...
402  * g_free (display_name);
403  */
404 static char *
405 maybe_append_separator_to_path (GtkFileChooserEntry *chooser_entry,
406                                 GtkFilePath         *path,
407                                 gchar               *display_name)
408 {
409   if (!g_str_has_suffix (display_name, G_DIR_SEPARATOR_S) && path)
410     {
411       GtkFileInfo *info;
412             
413       info = gtk_file_folder_get_info (chooser_entry->current_folder,
414                                        path, NULL); /* NULL-GError */
415
416       if (info)
417         {
418           if (gtk_file_info_get_is_folder (info))
419             {
420               gchar *tmp = display_name;
421               display_name = g_strconcat (tmp, G_DIR_SEPARATOR_S, NULL);
422               g_free (tmp);
423             }
424           
425           gtk_file_info_free (info);
426         }
427     }
428
429   return display_name;
430 }
431
432 /* Determines if the completion model has entries with a common prefix relative
433  * to the current contents of the entry.  Also, if there's one and only one such
434  * path, stores it in unique_path_ret.
435  */
436 static gboolean
437 find_common_prefix (GtkFileChooserEntry *chooser_entry,
438                     gchar               **common_prefix_ret,
439                     GtkFilePath         **unique_path_ret,
440                     GError              **error)
441 {
442   GtkEditable *editable;
443   GtkTreeIter iter;
444   gboolean parsed;
445   gboolean valid;
446   char *text_up_to_cursor;
447   GtkFilePath *parsed_folder_path;
448   char *parsed_file_part;
449
450   *common_prefix_ret = NULL;
451   *unique_path_ret = NULL;
452
453   if (chooser_entry->completion_store == NULL)
454     return;
455
456   editable = GTK_EDITABLE (chooser_entry);
457
458   text_up_to_cursor = gtk_editable_get_chars (editable, 0, gtk_editable_get_position (editable));
459
460   parsed = gtk_file_system_parse (chooser_entry->file_system,
461                                   chooser_entry->base_folder,
462                                   text_up_to_cursor,
463                                   &parsed_folder_path,
464                                   &parsed_file_part,
465                                   error);
466
467   printf ("Text up to cursor: \"%s\"\n", text_up_to_cursor);
468   printf ("parsed_folder_path: \"%s\"\nparsed_file_part: \"%s\"\n",
469           parsed_folder_path ? (char *) parsed_folder_path : "<NONE>",
470           parsed_file_part ? parsed_file_part : "<NONE>");
471
472   g_free (text_up_to_cursor);
473
474   if (!parsed)
475     return FALSE;
476
477   g_assert (parsed_folder_path != NULL
478             && chooser_entry->current_folder_path != NULL
479             && gtk_file_path_compare (parsed_folder_path, chooser_entry->current_folder_path) == 0);
480
481   valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (chooser_entry->completion_store), &iter);
482
483   while (valid)
484     {
485       gchar *display_name;
486       GtkFilePath *path;
487
488       gtk_tree_model_get (GTK_TREE_MODEL (chooser_entry->completion_store),
489                           &iter,
490                           DISPLAY_NAME_COLUMN, &display_name,
491                           PATH_COLUMN, &path,
492                           -1);
493
494       if (g_str_has_prefix (display_name, parsed_file_part))
495         {
496           if (!*common_prefix_ret)
497             {
498               *common_prefix_ret = g_strdup (display_name);
499               *unique_path_ret = gtk_file_path_copy (path);
500             }
501           else
502             {
503               gchar *p = *common_prefix_ret;
504               const gchar *q = display_name;
505                   
506               while (*p && *p == *q)
507                 {
508                   p++;
509                   q++;
510                 }
511                   
512               *p = '\0';
513
514               gtk_file_path_free (*unique_path_ret);
515               *unique_path_ret = NULL;
516             }
517         }
518
519       g_free (display_name);
520       gtk_file_path_free (path);
521       valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (chooser_entry->completion_store), &iter);
522     }
523
524   gtk_file_path_free (parsed_folder_path);
525   g_free (parsed_file_part);
526
527   return TRUE;
528 }
529
530 typedef enum {
531   INVALID_INPUT,
532   NO_MATCH,
533   COMPLETED,
534   COMPLETED_UNIQUE,
535   COMPLETE_BUT_NOT_UNIQUE
536 } CommonPrefixResult;
537
538 /* Finds a common prefix based on the contents of the entry and mandatorily appends it */
539 static CommonPrefixResult
540 append_common_prefix (GtkFileChooserEntry *chooser_entry,
541                       gboolean             highlight,
542                       gboolean             show_errors)
543 {
544   gchar *common_prefix;
545   GtkFilePath *unique_path;
546   GError *error;
547   CommonPrefixResult result;
548   gboolean have_result;
549
550   clear_completions (chooser_entry);
551
552   error = NULL;
553   if (!find_common_prefix (chooser_entry, &common_prefix, &unique_path, &error))
554     {
555       if (show_errors)
556         {
557           beep (chooser_entry);
558           /* FIXME: display the error somehow */
559         }
560
561       g_error_free (error);
562
563       return INVALID_INPUT;
564     }
565
566   have_result = FALSE;
567
568   if (unique_path)
569     {
570       common_prefix = maybe_append_separator_to_path (chooser_entry,
571                                                       unique_path,
572                                                       common_prefix);
573       gtk_file_path_free (unique_path);
574
575       if (common_prefix)
576         result = UNIQUE_PREFIX_APPENDED;
577       else
578         result = INVALID_INPUT;
579
580       have_result = TRUE;
581     }
582   else
583     {
584       /* FIXME:  if there was no unique_path, but there was a common_prefix,
585        * then we *may* have "complete but not unique".  find_common_prefix()
586        * needs to say if the match was a complete entry, or a partial one.
587        */
588     }
589
590   printf ("common prefix: \"%s\"\n",
591           common_prefix ? common_prefix : "<NONE>");
592
593   if (common_prefix)
594     {
595       gint cursor_pos;
596       gint common_prefix_len;
597       gint pos;
598
599       cursor_pos = gtk_editable_get_position (GTK_EDITABLE (chooser_entry));
600       common_prefix_len = g_utf8_strlen (common_prefix, -1);
601
602       pos = chooser_entry->file_part_pos;
603
604       chooser_entry->in_change = TRUE;
605       printf ("Deleting range (%d, %d)\n", pos, cursor_pos);
606       gtk_editable_delete_text (GTK_EDITABLE (chooser_entry),
607                                 pos, cursor_pos);
608       printf ("Inserting common prefix at %d\n", pos);
609       gtk_editable_insert_text (GTK_EDITABLE (chooser_entry),
610                                 common_prefix, -1, 
611                                 &pos);
612       chooser_entry->in_change = FALSE;
613
614       if (highlight)
615         {
616           printf ("Selecting range (%d, %d)\n", cursor_pos, pos);
617           gtk_editable_select_region (GTK_EDITABLE (chooser_entry),
618                                       cursor_pos,
619                                       pos); /* cursor_pos + common_prefix_len); */
620           chooser_entry->has_completion = TRUE;
621         }
622       else
623         gtk_editable_set_position (GTK_EDITABLE (chooser_entry), pos);
624
625       g_free (common_prefix);
626
627       if (have_result)
628         return result;
629       else
630         return PREFIX_APPENDED;
631     }
632   else
633     {
634       if (have_result)
635         return result;
636       else
637         return NO_COMMON_PREFIX;
638     }
639 }
640
641 static void
642 gtk_file_chooser_entry_do_insert_text (GtkEditable *editable,
643                                        const gchar *new_text,
644                                        gint         new_text_length,
645                                        gint        *position)
646 {
647   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (editable);
648   gint old_text_len;
649   gint insert_pos;
650
651   old_text_len = GTK_ENTRY (chooser_entry)->text_length;
652   insert_pos = *position;
653
654   parent_editable_iface->do_insert_text (editable, new_text, new_text_length, position);
655
656   if (chooser_entry->in_change)
657     return;
658
659   if ((chooser_entry->action == GTK_FILE_CHOOSER_ACTION_OPEN
660        || chooser_entry->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
661       && insert_pos == old_text_len)
662     install_start_autocompletion_idle (chooser_entry);
663 }
664
665 static void
666 clear_completions_if_not_in_change (GtkFileChooserEntry *chooser_entry)
667 {
668   if (chooser_entry->in_change)
669     return;
670
671   clear_completions (chooser_entry);
672 }
673
674 static void
675 gtk_file_chooser_entry_do_delete_text (GtkEditable *editable,
676                                        gint         start_pos,
677                                        gint         end_pos)
678 {
679   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (editable);
680
681   parent_editable_iface->do_delete_text (editable, start_pos, end_pos);
682
683   clear_completions_if_not_in_change (chooser_entry);
684 }
685
686 static void
687 gtk_file_chooser_entry_set_position (GtkEditable *editable,
688                                      gint         position)
689 {
690   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (editable);
691
692   parent_editable_iface->set_position (editable, position);
693
694   clear_completions_if_not_in_change (chooser_entry);
695 }
696
697 static void
698 gtk_file_chooser_entry_set_selection_bounds (GtkEditable *editable,
699                                              gint         start_pos,
700                                              gint         end_pos)
701 {
702   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (editable);
703
704   parent_editable_iface->set_selection_bounds (editable, start_pos, end_pos);
705
706   clear_completions_if_not_in_change (chooser_entry);
707 }
708
709 static void
710 gtk_file_chooser_entry_grab_focus (GtkWidget *widget)
711 {
712   GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->grab_focus (widget);
713   _gtk_file_chooser_entry_select_filename (GTK_FILE_CHOOSER_ENTRY (widget));
714 }
715
716 static void
717 explicitly_complete (GtkFileChooserEntry *chooser_entry)
718 {
719   CommonPrefixResult result;
720
721   g_assert (chooser_entry->current_folder != NULL);
722   g_assert (gtk_file_folder_is_finished_loading (chooser_entry->current_folder));
723
724   /* FIXME: see what Emacs does in case there is no common prefix, or there is more than one match:
725    *
726    * - If there is a common prefix, insert it
727    * - If there is no common prefix, pop up the suggestion window
728    * - If there are no matches at all, beep and bring up a tooltip
729    * - If the suggestion window is already up, scroll it
730    */
731   result = append_common_prefix (chooser_entry, FALSE, TRUE);
732
733   switch (result)
734     {
735     case PREFIX_APPENDED:
736       break;
737
738     case UNIQUE_PREFIX_APPENDED:
739       break;
740
741     case INVALID_INPUT:
742       break;
743
744     case NO_COMMON_PREFIX:
745       /* FIXME: pop up the suggestion window */
746       /* FIXME: we need to distinguish between "no match" and "many matches" */
747       break;
748
749     default:
750       g_assert_not_reached ();
751     }
752
753   /* FIXME: this bit of code is commented out for reference; this is how we used to force the suggestion window to pop up
754    *
755    * Trigger the completion window to pop up again by a 
756    * zero-length insertion, a bit of a hack.
757    *
758    * gtk_editable_insert_text (editable, "", -1, &pos);
759    */
760 }
761
762 static void
763 start_explicit_completion (GtkFileChooserEntry *chooser_entry)
764 {
765   printf ("Starting explicit completion - refreshing current folder\n");
766
767   refresh_current_folder_and_file_part (chooser_entry, REFRESH_UP_TO_CURSOR_POSITION);
768
769   if (!chooser_entry->current_folder_path)
770     {
771       /* Here, no folder path means we couldn't parse what the user typed. */
772
773       printf ("We don't have a current_folder_path - means the user typed something bogus\n");
774
775       beep (chooser_entry);
776       /* FIXME: present a tooltip to tell the user that his folder is invalid */
777
778       chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING;
779       return;
780     }
781
782   if (chooser_entry->current_folder
783       && gtk_file_folder_is_finished_loading (chooser_entry->current_folder))
784     {
785       printf ("File folder is finished loading, doing explicit completion immediately\n");
786       explicitly_complete (chooser_entry);
787     }
788   else
789     {
790       printf ("File folder is not yet loaded; will do explicit completion later\n");
791       chooser_entry->load_complete_action = LOAD_COMPLETE_EXPLICIT_COMPLETION;
792
793       /* FIXME: here, Emacs would say, "Making completion list..." */
794     }
795 }
796
797 static gboolean
798 gtk_file_chooser_entry_focus (GtkWidget        *widget,
799                               GtkDirectionType  direction)
800 {
801   GtkFileChooserEntry *chooser_entry;
802   GtkEditable *editable;
803   GtkEntry *entry;
804   GdkModifierType state;
805   gboolean control_pressed;
806
807   chooser_entry = GTK_FILE_CHOOSER_ENTRY (widget);
808   editable = GTK_EDITABLE (widget);
809   entry = GTK_ENTRY (widget);
810
811   if (!chooser_entry->eat_tabs)
812     return GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->focus (widget, direction);
813
814   control_pressed = FALSE;
815
816   if (gtk_get_current_event_state (&state))
817     {
818       if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
819         control_pressed = TRUE;
820     }
821
822   /* This is a bit evil -- it makes Tab never leave the entry. It basically
823    * makes it 'safe' for people to hit. */
824   if ((direction == GTK_DIR_TAB_FORWARD) &&
825       (GTK_WIDGET_HAS_FOCUS (widget)) &&
826       (! control_pressed))
827     {
828       if (chooser_entry->has_completion)
829         {
830           gboolean has_selection;
831           gint sel_end;
832
833           printf ("Hit Tab, and we have a completion!  Will unselect it\n");
834
835           has_selection = gtk_editable_get_selection_bounds (editable, NULL, &sel_end);
836           g_assert (has_selection && sel_end == GTK_ENTRY (entry)->text_length);
837
838           gtk_editable_set_position (editable, sel_end);
839         }
840       else
841         {
842           printf ("Hit Tab, we don't have a selected completion.  Will start explicit completion\n");
843
844           start_explicit_completion (chooser_entry);
845         }
846
847       return TRUE;
848     }
849   else
850     return GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->focus (widget, direction);
851 }
852
853 static gboolean
854 gtk_file_chooser_entry_focus_out_event (GtkWidget     *widget,
855                                         GdkEventFocus *event)
856 {
857   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (widget);
858
859   chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING;
860  
861   return GTK_WIDGET_CLASS (_gtk_file_chooser_entry_parent_class)->focus_out_event (widget, event);
862 }
863
864 static void
865 gtk_file_chooser_entry_activate (GtkEntry *entry)
866 {
867   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (entry);
868
869   if (chooser_entry->has_completion)
870     {
871       gtk_editable_set_position (GTK_EDITABLE (entry),
872                                  entry->text_length);
873     }
874
875   refresh_current_folder_and_file_part (chooser_entry, REFRESH_WHOLE_TEXT);
876
877   GTK_ENTRY_CLASS (_gtk_file_chooser_entry_parent_class)->activate (entry);
878 }
879
880 static void
881 discard_completion_store (GtkFileChooserEntry *chooser_entry)
882 {
883   if (!chooser_entry->completion_store)
884     return;
885
886   gtk_entry_completion_set_model (gtk_entry_get_completion (GTK_ENTRY (chooser_entry)), NULL);
887   g_object_unref (chooser_entry->completion_store);
888   chooser_entry->completion_store = NULL;
889 }
890
891 /* Fills the completion store from the contents of the current folder */
892 static void
893 populate_completion_store (GtkFileChooserEntry *chooser_entry)
894 {
895   GSList *paths;
896   GSList *tmp_list;
897
898   printf ("Populating completion store\n");
899
900   if (!gtk_file_folder_list_children (chooser_entry->current_folder, &paths, NULL)) /* NULL-GError */
901     return;
902
903   discard_completion_store (chooser_entry);
904
905   chooser_entry->completion_store = gtk_list_store_new (N_COLUMNS,
906                                                         G_TYPE_STRING,
907                                                         GTK_TYPE_FILE_PATH);
908
909   for (tmp_list = paths; tmp_list; tmp_list = tmp_list->next)
910     {
911       GtkFileInfo *info;
912       GtkFilePath *path;
913
914       path = tmp_list->data;
915
916       info = gtk_file_folder_get_info (chooser_entry->current_folder,
917                                        path,
918                                        NULL); /* NULL-GError */
919       if (info)
920         {
921           gchar *display_name = g_strdup (gtk_file_info_get_display_name (info));
922           GtkTreeIter iter;
923
924           display_name = maybe_append_separator_to_path (chooser_entry, path, display_name);
925
926           gtk_list_store_append (chooser_entry->completion_store, &iter);
927           gtk_list_store_set (chooser_entry->completion_store, &iter,
928                               DISPLAY_NAME_COLUMN, display_name,
929                               PATH_COLUMN, path,
930                               -1);
931
932           gtk_file_info_free (info);
933           g_free (display_name);
934         }
935     }
936
937   gtk_file_paths_free (paths);
938
939   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (chooser_entry->completion_store),
940                                         DISPLAY_NAME_COLUMN, GTK_SORT_ASCENDING);
941
942   gtk_entry_completion_set_model (gtk_entry_get_completion (GTK_ENTRY (chooser_entry)),
943                                   GTK_TREE_MODEL (chooser_entry->completion_store));
944 }
945
946 /* When we finish loading the current folder, this function should get called to
947  * perform the deferred autocompletion or explicit completion.
948  */
949 static void
950 perform_load_complete_action (GtkFileChooserEntry *chooser_entry)
951 {
952   switch (chooser_entry->load_complete_action)
953     {
954     case LOAD_COMPLETE_NOTHING:
955       break;
956
957     case LOAD_COMPLETE_AUTOCOMPLETE:
958       printf ("Load is complete; will autocomplete immediately\n");
959       autocomplete (chooser_entry);
960       break;
961
962     case LOAD_COMPLETE_EXPLICIT_COMPLETION:
963       printf ("Load is  complete; will do explicit completion\n");
964       explicitly_complete (chooser_entry);
965       break;
966
967     default:
968       g_assert_not_reached ();
969     }
970
971   chooser_entry->load_complete_action = LOAD_COMPLETE_NOTHING;
972 }
973
974 static void
975 finish_folder_load (GtkFileChooserEntry *chooser_entry)
976 {
977   populate_completion_store (chooser_entry);
978   perform_load_complete_action (chooser_entry);
979
980   gtk_widget_set_tooltip_text (GTK_WIDGET (chooser_entry), NULL);
981 }
982
983 /* Callback when the current folder finishes loading */
984 static void
985 finished_loading_cb (GtkFileFolder *folder,
986                      gpointer       data)
987 {
988   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (data);
989
990   printf ("Folder finished loading asynchronously!  Will populate the completion store\n");
991
992   finish_folder_load (chooser_entry);
993 }
994
995 /* Callback when the current folder's handle gets obtained (not necessarily loaded completely) */
996 static void
997 load_directory_get_folder_callback (GtkFileSystemHandle *handle,
998                                     GtkFileFolder       *folder,
999                                     const GError        *error,
1000                                     gpointer             data)
1001 {
1002   gboolean cancelled = handle->cancelled;
1003   GtkFileChooserEntry *chooser_entry = data;
1004
1005   if (handle != chooser_entry->load_folder_handle)
1006     goto out;
1007
1008   chooser_entry->load_folder_handle = NULL;
1009
1010   /* FIXME: if there was an error *AND* we had a pending explicit completion, beep and pop up a
1011    * tooltip to say that the folder could not be loaded.
1012    */
1013
1014   /* FIXME: if error, remove the current tooltip ("making completion list") */
1015
1016   if (cancelled || error)
1017     goto out;
1018
1019   g_assert (folder != NULL);
1020   chooser_entry->current_folder = folder;
1021
1022   printf ("Got folder asynchronously!\n");
1023
1024   discard_completion_store (chooser_entry);
1025
1026   if (gtk_file_folder_is_finished_loading (chooser_entry->current_folder))
1027     {
1028       printf ("And the folder is already finished loading.  Will populate the completion store.\n");
1029       finish_folder_load (chooser_entry);
1030     }
1031   else
1032     {
1033       printf ("Folder is not yet completely loaded.  Will load it asynchronously...\n");
1034       g_signal_connect (chooser_entry->current_folder, "finished-loading",
1035                         G_CALLBACK (finished_loading_cb), chooser_entry);
1036     }
1037
1038 out:
1039   g_object_unref (chooser_entry);
1040   g_object_unref (handle);
1041 }
1042
1043 static void
1044 start_loading_current_folder (GtkFileChooserEntry *chooser_entry)
1045 {
1046   if (chooser_entry->current_folder_path == NULL ||
1047       chooser_entry->file_system == NULL)
1048     return;
1049
1050   g_assert (chooser_entry->current_folder == NULL);
1051   g_assert (chooser_entry->load_folder_handle == NULL);
1052
1053   printf ("Starting async load of folder %s\n", (char *) chooser_entry->current_folder_path);
1054
1055   chooser_entry->load_folder_handle =
1056     gtk_file_system_get_folder (chooser_entry->file_system,
1057                                 chooser_entry->current_folder_path,
1058                                 GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_IS_FOLDER,
1059                                 load_directory_get_folder_callback,
1060                                 g_object_ref (chooser_entry));
1061 }
1062
1063 static void
1064 reload_current_folder (GtkFileChooserEntry *chooser_entry,
1065                        GtkFilePath         *folder_path,
1066                        gboolean             force_reload)
1067 {
1068   gboolean reload = FALSE;
1069
1070   if (chooser_entry->current_folder_path)
1071     {
1072       if ((folder_path && gtk_file_path_compare (folder_path, chooser_entry->current_folder_path) != 0)
1073           || force_reload)
1074         {
1075           reload = TRUE;
1076
1077           /* We changed our current directory.  We need to clear out the old
1078            * directory information.
1079            */
1080           if (chooser_entry->current_folder)
1081             {
1082               if (chooser_entry->load_folder_handle)
1083                 {
1084                   printf ("Cancelling folder load\n");
1085                   gtk_file_system_cancel_operation (chooser_entry->load_folder_handle);
1086                   chooser_entry->load_folder_handle = NULL;
1087                 }
1088
1089               g_object_unref (chooser_entry->current_folder);
1090               chooser_entry->current_folder = NULL;
1091             }
1092
1093           gtk_file_path_free (chooser_entry->current_folder_path);
1094           chooser_entry->current_folder_path = gtk_file_path_copy (folder_path);
1095         }
1096     }
1097   else
1098     {
1099       chooser_entry->current_folder_path = gtk_file_path_copy (folder_path);
1100       reload = TRUE;
1101     }
1102
1103   if (reload)
1104     start_loading_current_folder (chooser_entry);
1105 }
1106
1107 static void
1108 refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry,
1109                                       RefreshMode          refresh_mode)
1110 {
1111   GtkEditable *editable;
1112   gint end_pos;
1113   gchar *text;
1114   GtkFilePath *folder_path;
1115   gchar *file_part;
1116   gsize total_len, file_part_len;
1117   gint file_part_pos;
1118
1119   editable = GTK_EDITABLE (chooser_entry);
1120
1121   switch (refresh_mode)
1122     {
1123     case REFRESH_UP_TO_CURSOR_POSITION:
1124       end_pos = gtk_editable_get_position (editable);
1125       break;
1126
1127     case REFRESH_WHOLE_TEXT:
1128       end_pos = GTK_ENTRY (chooser_entry)->text_length;
1129       break;
1130
1131     default:
1132       g_assert_not_reached ();
1133       return;
1134     }
1135
1136   text = gtk_editable_get_chars (editable, 0, end_pos);
1137   
1138   if (!chooser_entry->file_system ||
1139       !chooser_entry->base_folder ||
1140       !gtk_file_system_parse (chooser_entry->file_system,
1141                               chooser_entry->base_folder, text,
1142                               &folder_path, &file_part, NULL)) /* NULL-GError */
1143     {
1144       folder_path = gtk_file_path_copy (chooser_entry->base_folder);
1145       file_part = g_strdup ("");
1146       file_part_pos = -1;
1147     }
1148   else
1149     {
1150       file_part_len = strlen (file_part);
1151       total_len = strlen (text);
1152       if (total_len > file_part_len)
1153         file_part_pos = g_utf8_strlen (text, total_len - file_part_len);
1154       else
1155         file_part_pos = 0;
1156     }
1157
1158   printf ("Parsed text \"%s\", file_part=\"%s\", file_part_pos=%d, folder_path=\"%s\"\n",
1159           text,
1160           file_part,
1161           file_part_pos,
1162           folder_path ? (char *) folder_path : "(NULL)");
1163
1164   g_free (text);
1165
1166   g_free (chooser_entry->file_part);
1167
1168   chooser_entry->file_part = file_part;
1169   chooser_entry->file_part_pos = file_part_pos;
1170
1171   reload_current_folder (chooser_entry, folder_path, file_part_pos == -1);
1172   gtk_file_path_free (folder_path);
1173 }
1174
1175 static void
1176 autocomplete (GtkFileChooserEntry *chooser_entry)
1177 {
1178   g_assert (chooser_entry->current_folder != NULL);
1179   g_assert (gtk_file_folder_is_finished_loading (chooser_entry->current_folder));
1180   g_assert (gtk_editable_get_position (GTK_EDITABLE (chooser_entry)) == GTK_ENTRY (chooser_entry)->text_length);
1181
1182   printf ("Doing autocompletion since our folder is finished loading\n");
1183
1184   append_common_prefix (chooser_entry, TRUE, FALSE);
1185 }
1186
1187 static void
1188 start_autocompletion (GtkFileChooserEntry *chooser_entry)
1189 {
1190   printf ("Starting autocompletion\n");
1191
1192   refresh_current_folder_and_file_part (chooser_entry, REFRESH_UP_TO_CURSOR_POSITION);
1193
1194   if (!chooser_entry->current_folder)
1195     {
1196       /* We don't beep or anything, since this is autocompletion - the user
1197        * didn't request any action explicitly.
1198        */
1199       printf ("No current_folder; not doing autocompletion after all\n");
1200       return;
1201     }
1202
1203   if (gtk_file_folder_is_finished_loading (chooser_entry->current_folder))
1204     {
1205       printf ("File folder is finished loading; doing autocompletion immediately\n");
1206       autocomplete (chooser_entry);
1207     }
1208   else
1209     {
1210       printf ("File folder is not yet loaded; will do autocompletion later\n");
1211       chooser_entry->load_complete_action = LOAD_COMPLETE_AUTOCOMPLETE;
1212     }
1213 }
1214
1215 static gboolean
1216 start_autocompletion_idle_handler (gpointer data)
1217 {
1218   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (data);
1219
1220   start_autocompletion (chooser_entry);
1221
1222   chooser_entry->start_autocompletion_idle_id = 0;
1223
1224   return FALSE;
1225 }
1226
1227 static void
1228 install_start_autocompletion_idle (GtkFileChooserEntry *chooser_entry)
1229 {
1230   if (chooser_entry->start_autocompletion_idle_id != 0)
1231     return;
1232
1233   chooser_entry->start_autocompletion_idle_id = g_idle_add (start_autocompletion_idle_handler, chooser_entry);
1234 }
1235
1236 #ifdef G_OS_WIN32
1237 static gint
1238 insert_text_callback (GtkFileChooserEntry *chooser_entry,
1239                       const gchar         *new_text,
1240                       gint                 new_text_length,
1241                       gint                *position,
1242                       gpointer             user_data)
1243 {
1244   const gchar *colon = memchr (new_text, ':', new_text_length);
1245   gint i;
1246
1247   /* Disallow these characters altogether */
1248   for (i = 0; i < new_text_length; i++)
1249     {
1250       if (new_text[i] == '<' ||
1251           new_text[i] == '>' ||
1252           new_text[i] == '"' ||
1253           new_text[i] == '|' ||
1254           new_text[i] == '*' ||
1255           new_text[i] == '?')
1256         break;
1257     }
1258
1259   if (i < new_text_length ||
1260       /* Disallow entering text that would cause a colon to be anywhere except
1261        * after a drive letter.
1262        */
1263       (colon != NULL &&
1264        *position + (colon - new_text) != 1) ||
1265       (new_text_length > 0 &&
1266        *position <= 1 &&
1267        GTK_ENTRY (chooser_entry)->text_length >= 2 &&
1268        gtk_entry_get_text (GTK_ENTRY (chooser_entry))[1] == ':'))
1269     {
1270       gtk_widget_error_bell (GTK_WIDGET (chooser_entry));
1271       g_signal_stop_emission_by_name (chooser_entry, "insert_text");
1272       return FALSE;
1273     }
1274
1275   return TRUE;
1276 }
1277
1278 static void
1279 delete_text_callback (GtkFileChooserEntry *chooser_entry,
1280                       gint                 start_pos,
1281                       gint                 end_pos,
1282                       gpointer             user_data)
1283 {
1284   /* If deleting a drive letter, delete the colon, too */
1285   if (start_pos == 0 && end_pos == 1 &&
1286       GTK_ENTRY (chooser_entry)->text_length >= 2 &&
1287       gtk_entry_get_text (GTK_ENTRY (chooser_entry))[1] == ':')
1288     {
1289       g_signal_handlers_block_by_func (chooser_entry,
1290                                        G_CALLBACK (delete_text_callback),
1291                                        user_data);
1292       gtk_editable_delete_text (GTK_EDITABLE (chooser_entry), 0, 1);
1293       g_signal_handlers_unblock_by_func (chooser_entry,
1294                                          G_CALLBACK (delete_text_callback),
1295                                          user_data);
1296     }
1297 }
1298 #endif
1299
1300 /**
1301  * _gtk_file_chooser_entry_new:
1302  * @eat_tabs: If %FALSE, allow focus navigation with the tab key.
1303  *
1304  * Creates a new #GtkFileChooserEntry object. #GtkFileChooserEntry
1305  * is an internal implementation widget for the GTK+ file chooser
1306  * which is an entry with completion with respect to a
1307  * #GtkFileSystem object.
1308  *
1309  * Return value: the newly created #GtkFileChooserEntry
1310  **/
1311 GtkWidget *
1312 _gtk_file_chooser_entry_new (gboolean eat_tabs)
1313 {
1314   GtkFileChooserEntry *chooser_entry;
1315
1316   chooser_entry = g_object_new (GTK_TYPE_FILE_CHOOSER_ENTRY, NULL);
1317   chooser_entry->eat_tabs = (eat_tabs != FALSE);
1318
1319   return GTK_WIDGET (chooser_entry);
1320 }
1321
1322 /**
1323  * _gtk_file_chooser_entry_set_file_system:
1324  * @chooser_entry: a #GtkFileChooser
1325  * @file_system: an object implementing #GtkFileSystem
1326  *
1327  * Sets the file system for @chooser_entry.
1328  **/
1329 void
1330 _gtk_file_chooser_entry_set_file_system (GtkFileChooserEntry *chooser_entry,
1331                                          GtkFileSystem       *file_system)
1332 {
1333   g_return_if_fail (GTK_IS_FILE_CHOOSER_ENTRY (chooser_entry));
1334   g_return_if_fail (GTK_IS_FILE_SYSTEM (file_system));
1335
1336   if (file_system != chooser_entry->file_system)
1337     {
1338       if (chooser_entry->file_system)
1339         g_object_unref (chooser_entry->file_system);
1340
1341       chooser_entry->file_system = g_object_ref (file_system);
1342     }
1343 }
1344
1345 /**
1346  * _gtk_file_chooser_entry_set_base_folder:
1347  * @chooser_entry: a #GtkFileChooserEntry
1348  * @path: path of a folder in the chooser entries current file system.
1349  *
1350  * Sets the folder with respect to which completions occur.
1351  **/
1352 void
1353 _gtk_file_chooser_entry_set_base_folder (GtkFileChooserEntry *chooser_entry,
1354                                          const GtkFilePath   *path)
1355 {
1356   if (chooser_entry->base_folder)
1357     gtk_file_path_free (chooser_entry->base_folder);
1358
1359   chooser_entry->base_folder = gtk_file_path_copy (path);
1360
1361   clear_completions (chooser_entry);
1362   _gtk_file_chooser_entry_select_filename (chooser_entry);
1363 }
1364
1365 /**
1366  * _gtk_file_chooser_entry_get_current_folder:
1367  * @chooser_entry: a #GtkFileChooserEntry
1368  *
1369  * Gets the current folder for the #GtkFileChooserEntry. If the
1370  * user has only entered a filename, this will be the base folder
1371  * (see _gtk_file_chooser_entry_set_base_folder()), but if the
1372  * user has entered a relative or absolute path, then it will
1373  * be different. If the user has entered a relative or absolute
1374  * path that doesn't point to a folder in the file system, it will
1375  * be %NULL.
1376  *
1377  * Return value: the path of current folder - this value is owned by the
1378  *  chooser entry and must not be modified or freed.
1379  **/
1380 const GtkFilePath *
1381 _gtk_file_chooser_entry_get_current_folder (GtkFileChooserEntry *chooser_entry)
1382 {
1383   if (chooser_entry->has_completion)
1384     {
1385       gtk_editable_set_position (GTK_EDITABLE (chooser_entry),
1386                                  GTK_ENTRY (chooser_entry)->text_length);
1387     }
1388   return chooser_entry->current_folder_path;
1389 }
1390
1391 /**
1392  * _gtk_file_chooser_entry_get_file_part:
1393  * @chooser_entry: a #GtkFileChooserEntry
1394  *
1395  * Gets the non-folder portion of whatever the user has entered
1396  * into the file selector. What is returned is a UTF-8 string,
1397  * and if a filename path is needed, gtk_file_system_make_path()
1398  * must be used
1399   *
1400  * Return value: the entered filename - this value is owned by the
1401  *  chooser entry and must not be modified or freed.
1402  **/
1403 const gchar *
1404 _gtk_file_chooser_entry_get_file_part (GtkFileChooserEntry *chooser_entry)
1405 {
1406   if (chooser_entry->has_completion)
1407     {
1408       gtk_editable_set_position (GTK_EDITABLE (chooser_entry),
1409                                  GTK_ENTRY (chooser_entry)->text_length);
1410     }
1411
1412   refresh_current_folder_and_file_part (chooser_entry, REFRESH_WHOLE_TEXT);
1413
1414   return chooser_entry->file_part;
1415 }
1416
1417 /**
1418  * _gtk_file_chooser_entry_set_file_part:
1419  * @chooser_entry: a #GtkFileChooserEntry
1420  * @file_part: text to display in the entry, in UTF-8
1421  *
1422  * Sets the current text shown in the file chooser entry.
1423  **/
1424 void
1425 _gtk_file_chooser_entry_set_file_part (GtkFileChooserEntry *chooser_entry,
1426                                        const gchar         *file_part)
1427 {
1428   g_return_if_fail (GTK_IS_FILE_CHOOSER_ENTRY (chooser_entry));
1429
1430   chooser_entry->in_change = TRUE;
1431   clear_completions (chooser_entry);
1432   gtk_entry_set_text (GTK_ENTRY (chooser_entry), file_part);
1433   chooser_entry->in_change = FALSE;
1434 }
1435
1436
1437 /**
1438  * _gtk_file_chooser_entry_set_action:
1439  * @chooser_entry: a #GtkFileChooserEntry
1440  * @action: the action which is performed by the file selector using this entry
1441  *
1442  * Sets action which is performed by the file selector using this entry. 
1443  * The #GtkFileChooserEntry will use different completion strategies for 
1444  * different actions.
1445  **/
1446 void
1447 _gtk_file_chooser_entry_set_action (GtkFileChooserEntry *chooser_entry,
1448                                     GtkFileChooserAction action)
1449 {
1450   g_return_if_fail (GTK_IS_FILE_CHOOSER_ENTRY (chooser_entry));
1451   
1452   if (chooser_entry->action != action)
1453     {
1454       GtkEntryCompletion *comp;
1455
1456       chooser_entry->action = action;
1457
1458       comp = gtk_entry_get_completion (GTK_ENTRY (chooser_entry));
1459
1460       switch (action)
1461         {
1462         case GTK_FILE_CHOOSER_ACTION_OPEN:
1463         case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1464           gtk_entry_completion_set_popup_single_match (comp, FALSE);
1465           break;
1466         case GTK_FILE_CHOOSER_ACTION_SAVE:
1467         case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
1468           gtk_entry_completion_set_popup_single_match (comp, TRUE);
1469           break;
1470         }
1471     }
1472 }
1473
1474
1475 /**
1476  * _gtk_file_chooser_entry_get_action:
1477  * @chooser_entry: a #GtkFileChooserEntry
1478  *
1479  * Gets the action for this entry. 
1480  *
1481  * Returns: the action
1482  **/
1483 GtkFileChooserAction
1484 _gtk_file_chooser_entry_get_action (GtkFileChooserEntry *chooser_entry)
1485 {
1486   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_ENTRY (chooser_entry),
1487                         GTK_FILE_CHOOSER_ACTION_OPEN);
1488   
1489   return chooser_entry->action;
1490 }
1491
1492 gboolean
1493 _gtk_file_chooser_entry_get_is_folder (GtkFileChooserEntry *chooser_entry,
1494                                        const GtkFilePath   *path)
1495 {
1496   gboolean retval = FALSE;
1497
1498   if (chooser_entry->current_folder)
1499     {
1500       GtkFileInfo *file_info;
1501
1502       file_info = gtk_file_folder_get_info (chooser_entry->current_folder,
1503                                             path, NULL);
1504       if (file_info)
1505         {
1506           retval = gtk_file_info_get_is_folder (file_info);
1507           gtk_file_info_free (file_info);
1508         }
1509     }
1510
1511   return retval;
1512 }
1513
1514
1515 /*
1516  * _gtk_file_chooser_entry_select_filename:
1517  * @chooser_entry: a #GtkFileChooserEntry
1518  *
1519  * Selects the filename (without the extension) for user edition.
1520  */
1521 void
1522 _gtk_file_chooser_entry_select_filename (GtkFileChooserEntry *chooser_entry)
1523 {
1524   const gchar *str, *ext;
1525   glong len = -1;
1526
1527   if (chooser_entry->action == GTK_FILE_CHOOSER_ACTION_SAVE)
1528     {
1529       str = gtk_entry_get_text (GTK_ENTRY (chooser_entry));
1530       ext = g_strrstr (str, ".");
1531
1532       if (ext)
1533        len = g_utf8_pointer_to_offset (str, ext);
1534     }
1535
1536   gtk_editable_select_region (GTK_EDITABLE (chooser_entry), 0, (gint) len);
1537 }
1538