]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesel.c
99534e19a47402cc22d536e418d506ed6730ff51
[~andy/gtk] / gtk / gtkfilesel.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "config.h"
28
29 #include <stdio.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #ifdef HAVE_SYS_PARAM_H
33 #include <sys/param.h>
34 #endif
35 #include <stdlib.h>
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39 #include <string.h>
40 #include <errno.h>
41 #ifdef HAVE_PWD_H
42 #include <pwd.h>
43 #endif
44
45 #include <glib.h>               /* Include early to get G_OS_WIN32 and
46                                  * G_WITH_CYGWIN */
47
48 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
49 #include <ctype.h>
50 #define STRICT
51 #include <windows.h>
52 #undef STRICT
53 #endif /* G_OS_WIN32 || G_WITH_CYGWIN */
54 #ifdef HAVE_WINSOCK_H
55 #include <winsock.h>            /* For gethostname */
56 #endif
57
58 #include "fnmatch.h"
59
60 #include "gdk/gdkkeysyms.h"
61 #include "gtkbutton.h"
62 #include "gtkcellrenderertext.h"
63 #include "gtkentry.h"
64 #include "gtkfilesel.h"
65 #include "gtkhbox.h"
66 #include "gtkhbbox.h"
67 #include "gtklabel.h"
68 #include "gtkliststore.h"
69 #include "gtkmain.h"
70 #include "gtkscrolledwindow.h"
71 #include "gtkstock.h"
72 #include "gtksignal.h"
73 #include "gtktreeselection.h"
74 #include "gtktreeview.h"
75 #include "gtkvbox.h"
76 #include "gtkmenu.h"
77 #include "gtkmenuitem.h"
78 #include "gtkoptionmenu.h"
79 #include "gtkdialog.h"
80 #include "gtkmessagedialog.h"
81 #include "gtkintl.h"
82 #include "gtkdnd.h"
83 #include "gtkeventbox.h"
84
85
86 #ifdef G_OS_WIN32
87 #include <direct.h>
88 #include <io.h>
89 #define mkdir(p,m) _mkdir(p)
90 #ifndef S_ISDIR
91 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
92 #endif
93 #endif /* G_OS_WIN32 */
94
95 #define DIR_LIST_WIDTH   180
96 #define DIR_LIST_HEIGHT  180
97 #define FILE_LIST_WIDTH  180
98 #define FILE_LIST_HEIGHT 180
99
100 /* The Hurd doesn't define either PATH_MAX or MAXPATHLEN, so we put this
101  * in here, since the rest of the code in the file does require some
102  * fixed maximum.
103  */
104 #ifndef MAXPATHLEN
105 #  ifdef PATH_MAX
106 #    define MAXPATHLEN PATH_MAX
107 #  else
108 #    define MAXPATHLEN 2048
109 #  endif
110 #endif
111
112 /* I've put this here so it doesn't get confused with the 
113  * file completion interface */
114 typedef struct _HistoryCallbackArg HistoryCallbackArg;
115
116 struct _HistoryCallbackArg
117 {
118   gchar *directory;
119   GtkWidget *menu_item;
120 };
121
122
123 typedef struct _CompletionState    CompletionState;
124 typedef struct _CompletionDir      CompletionDir;
125 typedef struct _CompletionDirSent  CompletionDirSent;
126 typedef struct _CompletionDirEntry CompletionDirEntry;
127 typedef struct _CompletionUserDir  CompletionUserDir;
128 typedef struct _PossibleCompletion PossibleCompletion;
129
130 /* Non-external file completion decls and structures */
131
132 /* A contant telling PRCS how many directories to cache.  Its actually
133  * kept in a list, so the geometry isn't important. */
134 #define CMPL_DIRECTORY_CACHE_SIZE 10
135
136 /* A constant used to determine whether a substring was an exact
137  * match by first_diff_index()
138  */
139 #define PATTERN_MATCH -1
140 /* The arguments used by all fnmatch() calls below
141  */
142 #define FNMATCH_FLAGS (FNM_PATHNAME | FNM_PERIOD)
143
144 #define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
145 #define CMPL_ERRNO_DID_NOT_CONVERT ((1<<16)-2)
146
147 /* This structure contains all the useful information about a directory
148  * for the purposes of filename completion.  These structures are cached
149  * in the CompletionState struct.  CompletionDir's are reference counted.
150  */
151 struct _CompletionDirSent
152 {
153   ino_t inode;
154   time_t mtime;
155   dev_t device;
156
157   gint entry_count;
158   struct _CompletionDirEntry *entries;
159 };
160
161 struct _CompletionDir
162 {
163   CompletionDirSent *sent;
164
165   gchar *fullname;
166   gint fullname_len;
167
168   struct _CompletionDir *cmpl_parent;
169   gint cmpl_index;
170   gchar *cmpl_text;
171 };
172
173 /* This structure contains pairs of directory entry names with a flag saying
174  * whether or not they are a valid directory.  NOTE: This information is used
175  * to provide the caller with information about whether to update its completions
176  * or try to open a file.  Since directories are cached by the directory mtime,
177  * a symlink which points to an invalid file (which will not be a directory),
178  * will not be reevaluated if that file is created, unless the containing
179  * directory is touched.  I consider this case to be worth ignoring (josh).
180  */
181 struct _CompletionDirEntry
182 {
183   gboolean is_dir;
184   gchar *entry_name;
185 };
186
187 struct _CompletionUserDir
188 {
189   gchar *login;
190   gchar *homedir;
191 };
192
193 struct _PossibleCompletion
194 {
195   /* accessible fields, all are accessed externally by functions
196    * declared above
197    */
198   gchar *text;
199   gint is_a_completion;
200   gboolean is_directory;
201
202   /* Private fields
203    */
204   gint text_alloc;
205 };
206
207 struct _CompletionState
208 {
209   gint last_valid_char;
210   gchar *updated_text;
211   gint updated_text_len;
212   gint updated_text_alloc;
213   gboolean re_complete;
214
215   gchar *user_dir_name_buffer;
216   gint user_directories_len;
217
218   gchar *last_completion_text;
219
220   gint user_completion_index; /* if >= 0, currently completing ~user */
221
222   struct _CompletionDir *completion_dir; /* directory completing from */
223   struct _CompletionDir *active_completion_dir;
224
225   struct _PossibleCompletion the_completion;
226
227   struct _CompletionDir *reference_dir; /* initial directory */
228
229   GList* directory_storage;
230   GList* directory_sent_storage;
231
232   struct _CompletionUserDir *user_directories;
233 };
234
235 enum {
236   PROP_0,
237   PROP_SHOW_FILEOPS,
238   PROP_FILENAME,
239   PROP_SELECT_MULTIPLE
240 };
241
242 enum {
243   DIR_COLUMN
244 };
245
246 enum {
247   FILE_COLUMN
248 };
249
250 /* File completion functions which would be external, were they used
251  * outside of this file.
252  */
253
254 static CompletionState*    cmpl_init_state        (void);
255 static void                cmpl_free_state        (CompletionState *cmpl_state);
256 static gint                cmpl_state_okay        (CompletionState* cmpl_state);
257 static const gchar*        cmpl_strerror          (gint);
258
259 static PossibleCompletion* cmpl_completion_matches(gchar           *text_to_complete,
260                                                    gchar          **remaining_text,
261                                                    CompletionState *cmpl_state);
262
263 /* Returns a name for consideration, possibly a completion, this name
264  * will be invalid after the next call to cmpl_next_completion.
265  */
266 static char*               cmpl_this_completion   (PossibleCompletion*);
267
268 /* True if this completion matches the given text.  Otherwise, this
269  * output can be used to have a list of non-completions.
270  */
271 static gint                cmpl_is_a_completion   (PossibleCompletion*);
272
273 /* True if the completion is a directory
274  */
275 static gboolean            cmpl_is_directory      (PossibleCompletion*);
276
277 /* Obtains the next completion, or NULL
278  */
279 static PossibleCompletion* cmpl_next_completion   (CompletionState*);
280
281 /* Updating completions: the return value of cmpl_updated_text() will
282  * be text_to_complete completed as much as possible after the most
283  * recent call to cmpl_completion_matches.  For the present
284  * application, this is the suggested replacement for the user's input
285  * string.  You must CALL THIS AFTER ALL cmpl_text_completions have
286  * been received.
287  */
288 static gchar*              cmpl_updated_text       (CompletionState* cmpl_state);
289
290 /* After updating, to see if the completion was a directory, call
291  * this.  If it was, you should consider re-calling completion_matches.
292  */
293 static gboolean            cmpl_updated_dir        (CompletionState* cmpl_state);
294
295 /* Current location: if using file completion, return the current
296  * directory, from which file completion begins.  More specifically,
297  * the cwd concatenated with all exact completions up to the last
298  * directory delimiter('/').
299  */
300 static gchar*              cmpl_reference_position (CompletionState* cmpl_state);
301
302 /* backing up: if cmpl_completion_matches returns NULL, you may query
303  * the index of the last completable character into cmpl_updated_text.
304  */
305 static gint                cmpl_last_valid_char    (CompletionState* cmpl_state);
306
307 /* When the user selects a non-directory, call cmpl_completion_fullname
308  * to get the full name of the selected file.
309  */
310 static gchar*              cmpl_completion_fullname (const gchar*, CompletionState* cmpl_state);
311
312
313 /* Directory operations. */
314 static CompletionDir* open_ref_dir         (gchar* text_to_complete,
315                                             gchar** remaining_text,
316                                             CompletionState* cmpl_state);
317 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
318 static gboolean       check_dir            (gchar *dir_name, 
319                                             struct stat *result, 
320                                             gboolean *stat_subdirs);
321 #endif
322 static CompletionDir* open_dir             (gchar* dir_name,
323                                             CompletionState* cmpl_state);
324 #ifdef HAVE_PWD_H
325 static CompletionDir* open_user_dir        (const gchar* text_to_complete,
326                                             CompletionState *cmpl_state);
327 #endif
328 static CompletionDir* open_relative_dir    (gchar* dir_name, CompletionDir* dir,
329                                             CompletionState *cmpl_state);
330 static CompletionDirSent* open_new_dir     (gchar* dir_name, 
331                                             struct stat* sbuf,
332                                             gboolean stat_subdirs);
333 static gint           correct_dir_fullname (CompletionDir* cmpl_dir);
334 static gint           correct_parent       (CompletionDir* cmpl_dir,
335                                             struct stat *sbuf);
336 #ifndef G_OS_WIN32
337 static gchar*         find_parent_dir_fullname    (gchar* dirname);
338 #endif
339 static CompletionDir* attach_dir           (CompletionDirSent* sent,
340                                             gchar* dir_name,
341                                             CompletionState *cmpl_state);
342 static void           free_dir_sent (CompletionDirSent* sent);
343 static void           free_dir      (CompletionDir  *dir);
344 static void           prune_memory_usage(CompletionState *cmpl_state);
345
346 /* Completion operations */
347 #ifdef HAVE_PWD_H
348 static PossibleCompletion* attempt_homedir_completion(gchar* text_to_complete,
349                                                       CompletionState *cmpl_state);
350 #endif
351 static PossibleCompletion* attempt_file_completion(CompletionState *cmpl_state);
352 static CompletionDir* find_completion_dir(gchar* text_to_complete,
353                                           gchar** remaining_text,
354                                           CompletionState* cmpl_state);
355 static PossibleCompletion* append_completion_text(gchar* text,
356                                                   CompletionState* cmpl_state);
357 #ifdef HAVE_PWD_H
358 static gint get_pwdb(CompletionState* cmpl_state);
359 static gint compare_user_dir(const void* a, const void* b);
360 #endif
361 static gint first_diff_index(gchar* pat, gchar* text);
362 static gint compare_cmpl_dir(const void* a, const void* b);
363 static void update_cmpl(PossibleCompletion* poss,
364                         CompletionState* cmpl_state);
365
366 static void gtk_file_selection_class_init    (GtkFileSelectionClass *klass);
367 static void gtk_file_selection_set_property  (GObject         *object,
368                                               guint            prop_id,
369                                               const GValue    *value,
370                                               GParamSpec      *pspec);
371 static void gtk_file_selection_get_property  (GObject         *object,
372                                               guint            prop_id,
373                                               GValue          *value,
374                                               GParamSpec      *pspec);
375 static void gtk_file_selection_init          (GtkFileSelection      *filesel);
376 static void gtk_file_selection_finalize      (GObject               *object);
377 static void gtk_file_selection_destroy       (GtkObject             *object);
378 static void gtk_file_selection_map           (GtkWidget             *widget);
379 static gint gtk_file_selection_key_press     (GtkWidget             *widget,
380                                               GdkEventKey           *event,
381                                               gpointer               user_data);
382 static gint gtk_file_selection_insert_text   (GtkWidget             *widget,
383                                               const gchar           *new_text,
384                                               gint                   new_text_length,
385                                               gint                  *position,
386                                               gpointer               user_data);
387
388 static void gtk_file_selection_file_activate (GtkTreeView       *tree_view,
389                                               GtkTreePath       *path,
390                                               GtkTreeViewColumn *column,
391                                               gpointer           user_data);
392 static void gtk_file_selection_file_changed  (GtkTreeSelection  *selection,
393                                               gpointer           user_data);
394 static void gtk_file_selection_dir_activate  (GtkTreeView       *tree_view,
395                                               GtkTreePath       *path,
396                                               GtkTreeViewColumn *column,
397                                               gpointer           user_data);
398
399 static void gtk_file_selection_populate      (GtkFileSelection      *fs,
400                                               gchar                 *rel_path,
401                                               gboolean               try_complete,
402                                               gboolean               reset_entry);
403 static void gtk_file_selection_abort         (GtkFileSelection      *fs);
404
405 static void gtk_file_selection_update_history_menu (GtkFileSelection       *fs,
406                                                     gchar                  *current_dir);
407
408 static void gtk_file_selection_create_dir  (GtkWidget *widget, gpointer data);
409 static void gtk_file_selection_delete_file (GtkWidget *widget, gpointer data);
410 static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data);
411
412 static void free_selected_names (GPtrArray *names);
413
414 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
415 #define compare_filenames(a, b) strcmp(a, b)
416 #else
417 #define compare_filenames(a, b) g_ascii_strcasecmp(a, b)
418 #endif
419
420
421 static GtkWindowClass *parent_class = NULL;
422
423 /* Saves errno when something cmpl does fails. */
424 static gint cmpl_errno;
425
426 #ifdef G_WITH_CYGWIN
427 /*
428  * Take the path currently in the file selection
429  * entry field and translate as necessary from
430  * a WIN32 style to CYGWIN32 style path.  For
431  * instance translate:
432  * x:\somepath\file.jpg
433  * to:
434  * //x/somepath/file.jpg
435  *
436  * Replace the path in the selection text field.
437  * Return a boolean value concerning whether a
438  * translation had to be made.
439  */
440 static int
441 translate_win32_path (GtkFileSelection *filesel)
442 {
443   int updated = 0;
444   gchar *path;
445
446   /*
447    * Retrieve the current path
448    */
449   path = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
450
451   /*
452    * Translate only if this looks like a DOS-ish
453    * path... First handle any drive letters.
454    */
455   if (isalpha (path[0]) && (path[1] == ':')) {
456     /*
457      * This part kind of stinks... It isn't possible
458      * to know if there is enough space in the current
459      * string for the extra character required in this
460      * conversion.  Assume that there isn't enough space
461      * and use the set function on the text field to
462      * set the newly created string.
463      */
464     gchar *newPath = g_strdup_printf ("//%c/%s", path[0], (path + 3));
465     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), newPath);
466
467     path = newPath;
468     updated = 1;
469   }
470
471   /*
472    * Now, replace backslashes with forward slashes 
473    * if necessary.
474    */
475   if (strchr (path, '\\'))
476     {
477       int index;
478       for (index = 0; path[index] != '\0'; index++)
479         if (path[index] == '\\')
480           path[index] = '/';
481       
482       updated = 1;
483     }
484     
485   return updated;
486 }
487 #endif
488
489 GtkType
490 gtk_file_selection_get_type (void)
491 {
492   static GtkType file_selection_type = 0;
493
494   if (!file_selection_type)
495     {
496       static const GtkTypeInfo filesel_info =
497       {
498         "GtkFileSelection",
499         sizeof (GtkFileSelection),
500         sizeof (GtkFileSelectionClass),
501         (GtkClassInitFunc) gtk_file_selection_class_init,
502         (GtkObjectInitFunc) gtk_file_selection_init,
503         /* reserved_1 */ NULL,
504         /* reserved_2 */ NULL,
505         (GtkClassInitFunc) NULL,
506       };
507
508       file_selection_type = gtk_type_unique (GTK_TYPE_DIALOG, &filesel_info);
509     }
510
511   return file_selection_type;
512 }
513
514 static void
515 gtk_file_selection_class_init (GtkFileSelectionClass *class)
516 {
517   GObjectClass *gobject_class;
518   GtkObjectClass *object_class;
519   GtkWidgetClass *widget_class;
520
521   gobject_class = (GObjectClass*) class;
522   object_class = (GtkObjectClass*) class;
523   widget_class = (GtkWidgetClass*) class;
524
525   parent_class = gtk_type_class (GTK_TYPE_DIALOG);
526
527   gobject_class->finalize = gtk_file_selection_finalize;
528   gobject_class->set_property = gtk_file_selection_set_property;
529   gobject_class->get_property = gtk_file_selection_get_property;
530    
531   g_object_class_install_property (gobject_class,
532                                    PROP_FILENAME,
533                                    g_param_spec_string ("filename",
534                                                         _("Filename"),
535                                                         _("The currently selected filename."),
536                                                         NULL,
537                                                         G_PARAM_READABLE | G_PARAM_WRITABLE));
538   g_object_class_install_property (gobject_class,
539                                    PROP_SHOW_FILEOPS,
540                                    g_param_spec_boolean ("show_fileops",
541                                                          _("Show file operations"),
542                                                          _("Whether buttons for creating/manipulating files should be displayed."),
543                                                          FALSE,
544                                                          G_PARAM_READABLE |
545                                                          G_PARAM_WRITABLE));
546   g_object_class_install_property (gobject_class,
547                                    PROP_SELECT_MULTIPLE,
548                                    g_param_spec_boolean ("select_multiple",
549                                                          _("Select multiple"),
550                                                          _("Whether to allow multiple files to be selected."),
551                                                          FALSE,
552                                                          G_PARAM_READABLE |
553                                                          G_PARAM_WRITABLE));
554   object_class->destroy = gtk_file_selection_destroy;
555   widget_class->map = gtk_file_selection_map;
556 }
557
558 static void gtk_file_selection_set_property (GObject         *object,
559                                              guint            prop_id,
560                                              const GValue    *value,
561                                              GParamSpec      *pspec)
562 {
563   GtkFileSelection *filesel;
564
565   filesel = GTK_FILE_SELECTION (object);
566
567   switch (prop_id)
568     {
569     case PROP_FILENAME:
570       gtk_file_selection_set_filename (filesel,
571                                        g_value_get_string (value));
572       break;
573     case PROP_SHOW_FILEOPS:
574       if (g_value_get_boolean (value))
575          gtk_file_selection_show_fileop_buttons (filesel);
576       else
577          gtk_file_selection_hide_fileop_buttons (filesel);
578       break;
579     case PROP_SELECT_MULTIPLE:
580       gtk_file_selection_set_select_multiple (filesel, g_value_get_boolean (value));
581       break;
582     default:
583       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
584       break;
585     }
586 }
587
588 static void gtk_file_selection_get_property (GObject         *object,
589                                              guint            prop_id,
590                                              GValue          *value,
591                                              GParamSpec      *pspec)
592 {
593   GtkFileSelection *filesel;
594
595   filesel = GTK_FILE_SELECTION (object);
596
597   switch (prop_id)
598     {
599     case PROP_FILENAME:
600       g_value_set_string (value,
601                           gtk_file_selection_get_filename(filesel));
602       break;
603
604     case PROP_SHOW_FILEOPS:
605       /* This is a little bit hacky, but doing otherwise would require
606        * adding a field to the object.
607        */
608       g_value_set_boolean (value, (filesel->fileop_c_dir && 
609                                    filesel->fileop_del_file &&
610                                    filesel->fileop_ren_file));
611       break;
612     case PROP_SELECT_MULTIPLE:
613       g_value_set_boolean (value, gtk_file_selection_get_select_multiple (filesel));
614       break;
615     default:
616       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
617       break;
618     }
619 }
620
621 static gboolean
622 grab_default (GtkWidget *widget)
623 {
624   gtk_widget_grab_default (widget);
625   return FALSE;
626 }
627      
628 static void
629 gtk_file_selection_init (GtkFileSelection *filesel)
630 {
631   GtkWidget *entry_vbox;
632   GtkWidget *label;
633   GtkWidget *list_hbox;
634   GtkWidget *confirm_area;
635   GtkWidget *pulldown_hbox;
636   GtkWidget *scrolled_win;
637   GtkWidget *eventbox;
638   GtkDialog *dialog;
639
640   GtkListStore *model;
641   GtkTreeViewColumn *column;
642   
643   gtk_widget_push_composite_child ();
644
645   dialog = GTK_DIALOG (filesel);
646
647   filesel->cmpl_state = cmpl_init_state ();
648
649   /* The dialog-sized vertical box  */
650   filesel->main_vbox = dialog->vbox;
651   gtk_container_set_border_width (GTK_CONTAINER (filesel), 10);
652
653   /* The horizontal box containing create, rename etc. buttons */
654   filesel->button_area = gtk_hbutton_box_new ();
655   gtk_button_box_set_layout (GTK_BUTTON_BOX (filesel->button_area), GTK_BUTTONBOX_START);
656   gtk_box_set_spacing (GTK_BOX (filesel->button_area), 0);
657   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->button_area, 
658                       FALSE, FALSE, 0);
659   gtk_widget_show (filesel->button_area);
660   
661   gtk_file_selection_show_fileop_buttons (filesel);
662
663   /* hbox for pulldown menu */
664   pulldown_hbox = gtk_hbox_new (TRUE, 5);
665   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), pulldown_hbox, FALSE, FALSE, 0);
666   gtk_widget_show (pulldown_hbox);
667   
668   /* Pulldown menu */
669   filesel->history_pulldown = gtk_option_menu_new ();
670   gtk_widget_show (filesel->history_pulldown);
671   gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->history_pulldown, 
672                       FALSE, FALSE, 0);
673     
674   /*  The horizontal box containing the directory and file listboxes  */
675   list_hbox = gtk_hbox_new (FALSE, 5);
676   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), list_hbox, TRUE, TRUE, 0);
677   gtk_widget_show (list_hbox);
678
679   /* The directories list */
680
681   model = gtk_list_store_new (1, G_TYPE_STRING);
682   filesel->dir_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
683   g_object_unref (model);
684
685   column = gtk_tree_view_column_new_with_attributes (_("Folders"),
686                                                      gtk_cell_renderer_text_new (),
687                                                      "text", DIR_COLUMN,
688                                                      NULL);
689   label = gtk_label_new_with_mnemonic (_("Fol_ders"));
690   gtk_label_set_mnemonic_widget (GTK_LABEL (label), filesel->dir_list);
691   gtk_widget_show (label);
692   gtk_tree_view_column_set_widget (column, label);
693   gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
694   gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->dir_list), column);
695
696   gtk_widget_set_usize (filesel->dir_list, DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
697   g_signal_connect (filesel->dir_list, "row_activated",
698                     G_CALLBACK (gtk_file_selection_dir_activate), filesel);
699
700   /*  gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list)); */
701
702   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
703   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN);  
704   gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->dir_list);
705   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
706                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
707   gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
708   gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
709   gtk_widget_show (filesel->dir_list);
710   gtk_widget_show (scrolled_win);
711
712   /* The files list */
713   model = gtk_list_store_new (1, G_TYPE_STRING);
714   filesel->file_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
715   g_object_unref (model);
716
717   column = gtk_tree_view_column_new_with_attributes (_("Files"),
718                                                      gtk_cell_renderer_text_new (),
719                                                      "text", FILE_COLUMN,
720                                                      NULL);
721   label = gtk_label_new_with_mnemonic (_("_Files"));
722   gtk_label_set_mnemonic_widget (GTK_LABEL (label), filesel->file_list);
723   gtk_widget_show (label);
724   gtk_tree_view_column_set_widget (column, label);
725   gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
726   gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->file_list), column);
727
728   gtk_widget_set_usize (filesel->file_list, FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
729   g_signal_connect (filesel->file_list, "row_activated",
730                     G_CALLBACK (gtk_file_selection_file_activate), filesel);
731   g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list)), "changed",
732                     G_CALLBACK (gtk_file_selection_file_changed), filesel);
733
734   /* gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list)); */
735
736   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
737   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN);
738   gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->file_list);
739   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
740                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
741   gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
742   gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
743   gtk_widget_show (filesel->file_list);
744   gtk_widget_show (scrolled_win);
745
746   /* action area for packing buttons into. */
747   filesel->action_area = gtk_hbox_new (TRUE, 0);
748   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->action_area, 
749                       FALSE, FALSE, 0);
750   gtk_widget_show (filesel->action_area);
751   
752   /*  The OK/Cancel button area */
753   confirm_area = dialog->action_area;
754
755   /*  The Cancel button  */
756   filesel->cancel_button = gtk_dialog_add_button (dialog,
757                                                   GTK_STOCK_CANCEL,
758                                                   GTK_RESPONSE_CANCEL);
759   /*  The OK button  */
760   filesel->ok_button = gtk_dialog_add_button (dialog,
761                                               GTK_STOCK_OK,
762                                               GTK_RESPONSE_OK);
763   
764   gtk_widget_grab_default (filesel->ok_button);
765
766   /*  The selection entry widget  */
767   entry_vbox = gtk_vbox_new (FALSE, 2);
768   gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 2);
769   gtk_widget_show (entry_vbox);
770   
771   eventbox = gtk_event_box_new ();
772   filesel->selection_text = label = gtk_label_new ("");
773   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
774   gtk_container_add (GTK_CONTAINER (eventbox), label);
775   gtk_box_pack_start (GTK_BOX (entry_vbox), eventbox, FALSE, FALSE, 0);
776   gtk_widget_show (label);
777   gtk_widget_show (eventbox);
778
779   filesel->selection_entry = gtk_entry_new ();
780   gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "key_press_event",
781                       (GtkSignalFunc) gtk_file_selection_key_press, filesel);
782   gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "insert_text",
783                       (GtkSignalFunc) gtk_file_selection_insert_text, NULL);
784   gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "focus_in_event",
785                              (GtkSignalFunc) grab_default,
786                              GTK_OBJECT (filesel->ok_button));
787   gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "activate",
788                              (GtkSignalFunc) gtk_button_clicked,
789                              GTK_OBJECT (filesel->ok_button));
790   gtk_box_pack_start (GTK_BOX (entry_vbox), filesel->selection_entry, TRUE, TRUE, 0);
791   gtk_widget_show (filesel->selection_entry);
792
793   if (!cmpl_state_okay (filesel->cmpl_state))
794     {
795       gchar err_buf[256];
796
797       sprintf (err_buf, _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
798
799       gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
800     }
801   else
802     {
803       gtk_file_selection_populate (filesel, "", FALSE, TRUE);
804     }
805
806   gtk_widget_grab_focus (filesel->selection_entry);
807
808   gtk_widget_pop_composite_child ();
809 }
810
811 static gchar *
812 uri_list_extract_first_uri (const gchar* uri_list)
813 {
814   const gchar *p, *q;
815   
816   g_return_val_if_fail (uri_list != NULL, NULL);
817   
818   p = uri_list;
819   /* We don't actually try to validate the URI according to RFC
820    * 2396, or even check for allowed characters - we just ignore
821    * comments and trim whitespace off the ends.  We also
822    * allow LF delimination as well as the specified CRLF.
823    *
824    * We do allow comments like specified in RFC 2483.
825    */
826   while (p)
827     {
828       if (*p != '#')
829         {
830           while (g_ascii_isspace(*p))
831             p++;
832           
833           q = p;
834           while (*q && (*q != '\n') && (*q != '\r'))
835             q++;
836           
837           if (q > p)
838             {
839               q--;
840               while (q > p && g_ascii_isspace (*q))
841                 q--;
842
843               if (q > p)
844                 return g_strndup (p, q - p + 1);
845             }
846         }
847       p = strchr (p, '\n');
848       if (p)
849         p++;
850     }
851   return NULL;
852 }
853
854 static void
855 dnd_really_drop  (GtkWidget *dialog, gint response_id, GtkFileSelection *fs)
856 {
857   gchar *filename;
858   
859   if (response_id == GTK_RESPONSE_YES)
860     {
861       filename = g_object_get_data (G_OBJECT (dialog), "gtk-fs-dnd-filename");
862
863       gtk_file_selection_set_filename (fs, filename);
864     }
865   
866   gtk_widget_destroy (dialog);
867 }
868
869
870 static void
871 filenames_dropped (GtkWidget        *widget,
872                    GdkDragContext   *context,
873                    gint              x,
874                    gint              y,
875                    GtkSelectionData *selection_data,
876                    guint             info,
877                    guint             time)
878 {
879   char *uri = NULL;
880   char *filename = NULL;
881   char *hostname;
882   char this_hostname[257];
883   int res;
884   GError *error = NULL;
885         
886   if (!selection_data->data)
887     return;
888
889   uri = uri_list_extract_first_uri ((char *)selection_data->data);
890   
891   if (!uri)
892     return;
893
894   filename = g_filename_from_uri (uri, &hostname, &error);
895   g_free (uri);
896   
897   if (!filename)
898     {
899       g_warning ("Error getting dropped filename: %s\n",
900                  error->message);
901       g_error_free (error);
902       return;
903     }
904
905   res = gethostname (this_hostname, 256);
906   this_hostname[256] = 0;
907   
908   if ((hostname == NULL) ||
909       (res == 0 && strcmp (hostname, this_hostname) == 0) ||
910       (strcmp (hostname, "localhost") == 0))
911     gtk_file_selection_set_filename (GTK_FILE_SELECTION (widget),
912                                      filename);
913   else
914     {
915       GtkWidget *dialog;
916       
917       dialog = gtk_message_dialog_new (GTK_WINDOW (widget),
918                                        GTK_DIALOG_DESTROY_WITH_PARENT,
919                                        GTK_MESSAGE_QUESTION,
920                                        GTK_BUTTONS_YES_NO,
921                                        _("The file \"%s\" resides on another machine (called %s) and may not be availible to this program.\n"
922                                          "Are you sure that you want to select it?"), filename, hostname);
923
924       g_object_set_data_full (G_OBJECT (dialog), "gtk-fs-dnd-filename", g_strdup (filename), g_free);
925       
926       g_signal_connect_data (dialog, "response",
927                              (GCallback) dnd_really_drop, 
928                              widget, NULL, 0);
929       
930       gtk_widget_show (dialog);
931     }
932
933   g_free (hostname);
934   g_free (filename);
935 }
936
937 enum
938 {
939   TARGET_URILIST,
940   TARGET_UTF8_STRING,
941   TARGET_STRING,
942   TARGET_TEXT,
943   TARGET_COMPOUND_TEXT
944 };
945
946
947 static void
948 filenames_drag_get (GtkWidget        *widget,
949                     GdkDragContext   *context,
950                     GtkSelectionData *selection_data,
951                     guint             info,
952                     guint             time,
953                     GtkFileSelection *filesel)
954 {
955   const gchar *file;
956   gchar *uri_list;
957   char hostname[256];
958   int res;
959   GError *error;
960
961   file = gtk_file_selection_get_filename (filesel);
962
963   if (file)
964     {
965       if (info == TARGET_URILIST)
966         {
967           res = gethostname (hostname, 256);
968           
969           error = NULL;
970           uri_list = g_filename_to_uri (file, (!res)?hostname:NULL, &error);
971           if (!uri_list)
972             {
973               g_warning ("Error getting filename: %s\n",
974                          error->message);
975               g_error_free (error);
976               return;
977             }
978           
979           gtk_selection_data_set (selection_data,
980                                   selection_data->target, 8,
981                                   (void *)uri_list, strlen((char *)uri_list));
982           g_free (uri_list);
983         }
984       else
985         {
986           g_print ("Setting text: '%s'\n", file);
987           gtk_selection_data_set_text (selection_data, file, -1);
988         }
989     }
990 }
991
992 static void
993 file_selection_setup_dnd (GtkFileSelection *filesel)
994 {
995   GtkWidget *eventbox;
996   static GtkTargetEntry drop_types[] = {
997     { "text/uri-list", 0, TARGET_URILIST}
998   };
999   static gint n_drop_types = sizeof(drop_types)/sizeof(drop_types[0]);
1000   static GtkTargetEntry drag_types[] = {
1001     { "text/uri-list", 0, TARGET_URILIST},
1002     { "UTF8_STRING", 0, TARGET_UTF8_STRING },
1003     { "STRING", 0, 0 },
1004     { "TEXT",   0, 0 }, 
1005     { "COMPOUND_TEXT", 0, 0 }
1006   };
1007   static gint n_drag_types = sizeof(drag_types)/sizeof(drag_types[0]);
1008
1009   gtk_drag_dest_set (GTK_WIDGET (filesel),
1010                      GTK_DEST_DEFAULT_ALL,
1011                      drop_types, n_drop_types,
1012                      GDK_ACTION_COPY);
1013
1014   gtk_signal_connect (GTK_OBJECT(filesel), "drag_data_received",
1015                       GTK_SIGNAL_FUNC(filenames_dropped), NULL);
1016
1017   eventbox = gtk_widget_get_parent (filesel->selection_text);
1018   gtk_drag_source_set (eventbox,
1019                        GDK_BUTTON1_MASK,
1020                        drag_types, n_drag_types,
1021                        GDK_ACTION_COPY);
1022
1023   gtk_signal_connect (GTK_OBJECT (eventbox),
1024                       "drag_data_get",
1025                       GTK_SIGNAL_FUNC (filenames_drag_get),
1026                       filesel);
1027 }
1028
1029 GtkWidget*
1030 gtk_file_selection_new (const gchar *title)
1031 {
1032   GtkFileSelection *filesel;
1033
1034   filesel = gtk_type_new (GTK_TYPE_FILE_SELECTION);
1035   gtk_window_set_title (GTK_WINDOW (filesel), title);
1036   gtk_dialog_set_has_separator (GTK_DIALOG (filesel), FALSE);
1037
1038   file_selection_setup_dnd (filesel);
1039   
1040   return GTK_WIDGET (filesel);
1041 }
1042
1043 void
1044 gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
1045 {
1046   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1047     
1048   /* delete, create directory, and rename */
1049   if (!filesel->fileop_c_dir) 
1050     {
1051       filesel->fileop_c_dir = gtk_button_new_with_mnemonic (_("_New Folder"));
1052       gtk_signal_connect (GTK_OBJECT (filesel->fileop_c_dir), "clicked",
1053                           (GtkSignalFunc) gtk_file_selection_create_dir, 
1054                           (gpointer) filesel);
1055       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
1056                           filesel->fileop_c_dir, TRUE, TRUE, 0);
1057       gtk_widget_show (filesel->fileop_c_dir);
1058     }
1059         
1060   if (!filesel->fileop_del_file) 
1061     {
1062       filesel->fileop_del_file = gtk_button_new_with_mnemonic (_("De_lete File"));
1063       gtk_signal_connect (GTK_OBJECT (filesel->fileop_del_file), "clicked",
1064                           (GtkSignalFunc) gtk_file_selection_delete_file, 
1065                           (gpointer) filesel);
1066       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
1067                           filesel->fileop_del_file, TRUE, TRUE, 0);
1068       gtk_widget_show (filesel->fileop_del_file);
1069     }
1070
1071   if (!filesel->fileop_ren_file)
1072     {
1073       filesel->fileop_ren_file = gtk_button_new_with_mnemonic (_("_Rename File"));
1074       gtk_signal_connect (GTK_OBJECT (filesel->fileop_ren_file), "clicked",
1075                           (GtkSignalFunc) gtk_file_selection_rename_file, 
1076                           (gpointer) filesel);
1077       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
1078                           filesel->fileop_ren_file, TRUE, TRUE, 0);
1079       gtk_widget_show (filesel->fileop_ren_file);
1080     }
1081   g_object_notify (G_OBJECT (filesel), "show_fileops");
1082   gtk_widget_queue_resize (GTK_WIDGET (filesel));
1083 }
1084
1085 void       
1086 gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
1087 {
1088   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1089     
1090   if (filesel->fileop_ren_file)
1091     {
1092       gtk_widget_destroy (filesel->fileop_ren_file);
1093       filesel->fileop_ren_file = NULL;
1094     }
1095
1096   if (filesel->fileop_del_file)
1097     {
1098       gtk_widget_destroy (filesel->fileop_del_file);
1099       filesel->fileop_del_file = NULL;
1100     }
1101
1102   if (filesel->fileop_c_dir)
1103     {
1104       gtk_widget_destroy (filesel->fileop_c_dir);
1105       filesel->fileop_c_dir = NULL;
1106     }
1107   g_object_notify (G_OBJECT (filesel), "show_fileops");
1108 }
1109
1110
1111
1112 void
1113 gtk_file_selection_set_filename (GtkFileSelection *filesel,
1114                                  const gchar      *filename)
1115 {
1116   gchar *buf;
1117   const char *name, *last_slash;
1118
1119   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1120   g_return_if_fail (filename != NULL);
1121
1122   last_slash = strrchr (filename, G_DIR_SEPARATOR);
1123
1124   if (!last_slash)
1125     {
1126       buf = g_strdup ("");
1127       name = filename;
1128     }
1129   else
1130     {
1131       buf = g_strdup (filename);
1132       buf[last_slash - filename + 1] = 0;
1133       name = last_slash + 1;
1134     }
1135
1136   gtk_file_selection_populate (filesel, buf, FALSE, TRUE);
1137
1138   if (filesel->selection_entry)
1139     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
1140   g_free (buf);
1141   g_object_notify (G_OBJECT (filesel), "filename");
1142 }
1143
1144 /**
1145  * gtk_file_selection_get_filename:
1146  * @filesel: a #GtkFileSelection
1147  * 
1148  * This function returns the selected filename in encoding of
1149  * g_filename_from_utf8(), which may or may not be the same as that
1150  * used by GTK+ (UTF-8). To convert to UTF-8, call g_filename_to_utf8().
1151  * The returned string points to a statically allocated buffer and
1152  * should be copied if you plan to keep it around.
1153  * 
1154  * Return value: currently-selected filename in locale's encoding
1155  **/
1156 G_CONST_RETURN gchar*
1157 gtk_file_selection_get_filename (GtkFileSelection *filesel)
1158 {
1159   static gchar nothing[2] = "";
1160   static gchar something[MAXPATHLEN*2];
1161   char *sys_filename;
1162   const char *text;
1163
1164   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
1165
1166 #ifdef G_WITH_CYGWIN
1167   translate_win32_path (filesel);
1168 #endif
1169   text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
1170   if (text)
1171     {
1172       sys_filename = g_filename_from_utf8 (cmpl_completion_fullname (text, filesel->cmpl_state), -1, NULL, NULL, NULL);
1173       if (!sys_filename)
1174         return nothing;
1175       strncpy (something, sys_filename, sizeof (something));
1176       g_free (sys_filename);
1177       return something;
1178     }
1179
1180   return nothing;
1181 }
1182
1183 void
1184 gtk_file_selection_complete (GtkFileSelection *filesel,
1185                              const gchar      *pattern)
1186 {
1187   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1188   g_return_if_fail (pattern != NULL);
1189
1190   if (filesel->selection_entry)
1191     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), pattern);
1192   gtk_file_selection_populate (filesel, (gchar*) pattern, TRUE, TRUE);
1193 }
1194
1195 static void
1196 gtk_file_selection_destroy (GtkObject *object)
1197 {
1198   GtkFileSelection *filesel;
1199   GList *list;
1200   HistoryCallbackArg *callback_arg;
1201   
1202   g_return_if_fail (GTK_IS_FILE_SELECTION (object));
1203   
1204   filesel = GTK_FILE_SELECTION (object);
1205   
1206   if (filesel->fileop_dialog)
1207     {
1208       gtk_widget_destroy (filesel->fileop_dialog);
1209       filesel->fileop_dialog = NULL;
1210     }
1211   
1212   if (filesel->history_list)
1213     {
1214       list = filesel->history_list;
1215       while (list)
1216         {
1217           callback_arg = list->data;
1218           g_free (callback_arg->directory);
1219           g_free (callback_arg);
1220           list = list->next;
1221         }
1222       g_list_free (filesel->history_list);
1223       filesel->history_list = NULL;
1224     }
1225
1226   if (filesel->cmpl_state)
1227     {
1228       cmpl_free_state (filesel->cmpl_state);
1229       filesel->cmpl_state = NULL;
1230     }
1231  
1232   if (filesel->selected_names)
1233     {
1234       free_selected_names (filesel->selected_names);
1235       filesel->selected_names = NULL;
1236     } 
1237
1238   if (filesel->last_selected)
1239     {
1240       g_free (filesel->last_selected);
1241       filesel->last_selected = NULL;
1242     }
1243
1244   GTK_OBJECT_CLASS (parent_class)->destroy (object);
1245 }
1246
1247 static void
1248 gtk_file_selection_map (GtkWidget *widget)
1249 {
1250   GtkFileSelection *filesel = GTK_FILE_SELECTION (widget);
1251
1252   /* Refresh the contents */
1253   gtk_file_selection_populate (filesel, "", FALSE, FALSE);
1254   
1255   GTK_WIDGET_CLASS (parent_class)->map (widget);
1256 }
1257
1258 static void
1259 gtk_file_selection_finalize (GObject *object)
1260 {
1261   GtkFileSelection *filesel = GTK_FILE_SELECTION (object);
1262
1263   g_free (filesel->fileop_file);
1264
1265   G_OBJECT_CLASS (parent_class)->finalize (object);
1266 }
1267
1268 /* Begin file operations callbacks */
1269
1270 static void
1271 gtk_file_selection_fileop_error (GtkFileSelection *fs,
1272                                  gchar            *error_message)
1273 {
1274   GtkWidget *dialog;
1275     
1276   g_return_if_fail (error_message != NULL);
1277
1278   /* main dialog */
1279   dialog = gtk_message_dialog_new (GTK_WINDOW (fs),
1280                                    GTK_DIALOG_DESTROY_WITH_PARENT,
1281                                    GTK_MESSAGE_ERROR,
1282                                    GTK_BUTTONS_CLOSE,
1283                                    "%s", error_message);
1284
1285   /* yes, we free it */
1286   g_free (error_message);
1287
1288   gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1289  
1290   gtk_signal_connect_object (GTK_OBJECT (dialog), "response",
1291                              (GtkSignalFunc) gtk_widget_destroy, 
1292                              (gpointer) dialog);
1293
1294   gtk_widget_show (dialog);
1295 }
1296
1297 static void
1298 gtk_file_selection_fileop_destroy (GtkWidget *widget,
1299                                    gpointer   data)
1300 {
1301   GtkFileSelection *fs = data;
1302
1303   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1304   
1305   fs->fileop_dialog = NULL;
1306 }
1307
1308
1309 static void
1310 gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
1311                                          gpointer   data)
1312 {
1313   GtkFileSelection *fs = data;
1314   const gchar *dirname;
1315   gchar *path;
1316   gchar *full_path;
1317   gchar *sys_full_path;
1318   gchar *buf;
1319   GError *error = NULL;
1320   CompletionState *cmpl_state;
1321   
1322   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1323
1324   dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1325   cmpl_state = (CompletionState*) fs->cmpl_state;
1326   path = cmpl_reference_position (cmpl_state);
1327   
1328   full_path = g_strconcat (path, G_DIR_SEPARATOR_S, dirname, NULL);
1329   sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1330   if (error)
1331     {
1332       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1333         buf = g_strdup_printf (_("The folder name \"%s\" contains symbols that are not allowed in filenames"), dirname);
1334       else
1335         buf = g_strdup_printf (_("Error creating folder \"%s\": %s\n%s"), dirname, error->message,
1336                                _("You probably used symbols not allowed in filenames."));
1337       gtk_file_selection_fileop_error (fs, buf);
1338       g_error_free (error);
1339       goto out;
1340     }
1341
1342   if (mkdir (sys_full_path, 0755) < 0) 
1343     {
1344       buf = g_strdup_printf (_("Error creating folder \"%s\": %s\n"), dirname,
1345                              g_strerror (errno));
1346       gtk_file_selection_fileop_error (fs, buf);
1347     }
1348
1349  out:
1350   g_free (full_path);
1351   g_free (sys_full_path);
1352   
1353   gtk_widget_destroy (fs->fileop_dialog);
1354   gtk_file_selection_populate (fs, "", FALSE, FALSE);
1355 }
1356   
1357 static void
1358 gtk_file_selection_create_dir (GtkWidget *widget,
1359                                gpointer   data)
1360 {
1361   GtkFileSelection *fs = data;
1362   GtkWidget *label;
1363   GtkWidget *dialog;
1364   GtkWidget *vbox;
1365   GtkWidget *button;
1366
1367   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1368
1369   if (fs->fileop_dialog)
1370     return;
1371   
1372   /* main dialog */
1373   dialog = gtk_dialog_new ();
1374   fs->fileop_dialog = dialog;
1375   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1376                       (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
1377                       (gpointer) fs);
1378   gtk_window_set_title (GTK_WINDOW (dialog), _("New Folder"));
1379   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1380   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
1381
1382   /* If file dialog is grabbed, grab option dialog */
1383   /* When option dialog is closed, file dialog will be grabbed again */
1384   if (GTK_WINDOW (fs)->modal)
1385       gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1386
1387   vbox = gtk_vbox_new (FALSE, 0);
1388   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1389   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1390                      FALSE, FALSE, 0);
1391   gtk_widget_show( vbox);
1392   
1393   label = gtk_label_new_with_mnemonic (_("_Folder name:"));
1394   gtk_misc_set_alignment(GTK_MISC (label), 0.0, 0.0);
1395   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1396   gtk_widget_show (label);
1397
1398   /*  The directory entry widget  */
1399   fs->fileop_entry = gtk_entry_new ();
1400   gtk_label_set_mnemonic_widget (GTK_LABEL (label), fs->fileop_entry);
1401   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
1402                       TRUE, TRUE, 5);
1403   GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1404   gtk_widget_show (fs->fileop_entry);
1405   
1406   /* buttons */
1407   button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
1408   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1409                              (GtkSignalFunc) gtk_widget_destroy, 
1410                              (gpointer) dialog);
1411   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1412                      button, TRUE, TRUE, 0);
1413   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1414   gtk_widget_grab_default (button);
1415   gtk_widget_show (button);
1416
1417   gtk_widget_grab_focus (fs->fileop_entry);
1418
1419   button = gtk_button_new_with_label (_("Create"));
1420   gtk_signal_connect (GTK_OBJECT (button), "clicked",
1421                       (GtkSignalFunc) gtk_file_selection_create_dir_confirmed, 
1422                       (gpointer) fs);
1423   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1424                      button, TRUE, TRUE, 0);
1425   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1426   gtk_widget_show (button);
1427   
1428   gtk_widget_show (dialog);
1429 }
1430
1431 static void
1432 gtk_file_selection_delete_file_response (GtkDialog *dialog, 
1433                                          gint       response_id,
1434                                          gpointer   data)
1435 {
1436   GtkFileSelection *fs = data;
1437   CompletionState *cmpl_state;
1438   gchar *path;
1439   gchar *full_path;
1440   gchar *sys_full_path;
1441   GError *error = NULL;
1442   gchar *buf;
1443   
1444   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1445
1446   if (response_id != GTK_RESPONSE_OK)
1447     {
1448       gtk_widget_destroy (GTK_WIDGET (dialog));
1449       return;
1450     }
1451
1452   cmpl_state = (CompletionState*) fs->cmpl_state;
1453   path = cmpl_reference_position (cmpl_state);
1454   
1455   full_path = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1456   sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1457   if (error)
1458     {
1459       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1460         buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"),
1461                                fs->fileop_file);
1462       else
1463         buf = g_strdup_printf (_("Error deleting file \"%s\": %s\n%s"),
1464                                fs->fileop_file, error->message,
1465                                _("It probably contains symbols not allowed in filenames."));
1466       
1467       gtk_file_selection_fileop_error (fs, buf);
1468       g_error_free (error);
1469       goto out;
1470     }
1471
1472   if (unlink (sys_full_path) < 0) 
1473     {
1474       buf = g_strdup_printf (_("Error deleting file \"%s\": %s"),
1475                              fs->fileop_file, g_strerror (errno));
1476       gtk_file_selection_fileop_error (fs, buf);
1477     }
1478   
1479  out:
1480   g_free (full_path);
1481   g_free (sys_full_path);
1482   
1483   gtk_widget_destroy (fs->fileop_dialog);
1484   gtk_file_selection_populate (fs, "", FALSE, TRUE);
1485 }
1486
1487 static void
1488 gtk_file_selection_delete_file (GtkWidget *widget,
1489                                 gpointer   data)
1490 {
1491   GtkFileSelection *fs = data;
1492   GtkWidget *dialog;
1493   const gchar *filename;
1494   
1495   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1496
1497   if (fs->fileop_dialog)
1498     return;
1499
1500 #ifdef G_WITH_CYGWIN
1501   translate_win32_path (fs);
1502 #endif
1503
1504   filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1505   if (strlen (filename) < 1)
1506     return;
1507
1508   g_free (fs->fileop_file);
1509   fs->fileop_file = g_strdup (filename);
1510   
1511   /* main dialog */
1512   fs->fileop_dialog = dialog = 
1513     gtk_message_dialog_new (GTK_WINDOW (fs),
1514                             GTK_WINDOW (fs)->modal ? GTK_DIALOG_MODAL : 0,
1515                             GTK_MESSAGE_QUESTION,
1516                             GTK_BUTTONS_NONE,
1517                             _("Really delete file \"%s\" ?"), filename);
1518
1519   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1520                       (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
1521                       (gpointer) fs);
1522   gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
1523   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1524   
1525   /* buttons */
1526   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
1527                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1528                           GTK_STOCK_DELETE, GTK_RESPONSE_OK,
1529                           NULL);
1530
1531   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
1532
1533   g_signal_connect (G_OBJECT (dialog), "response",
1534                     G_CALLBACK (gtk_file_selection_delete_file_response),
1535                     fs);
1536   
1537   gtk_widget_show (dialog);
1538 }
1539
1540 static void
1541 gtk_file_selection_rename_file_confirmed (GtkWidget *widget,
1542                                           gpointer   data)
1543 {
1544   GtkFileSelection *fs = data;
1545   gchar *buf;
1546   const gchar *file;
1547   gchar *path;
1548   gchar *new_filename;
1549   gchar *old_filename;
1550   gchar *sys_new_filename;
1551   gchar *sys_old_filename;
1552   CompletionState *cmpl_state;
1553   GError *error = NULL;
1554   
1555   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1556
1557   file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1558   cmpl_state = (CompletionState*) fs->cmpl_state;
1559   path = cmpl_reference_position (cmpl_state);
1560   
1561   new_filename = g_strconcat (path, G_DIR_SEPARATOR_S, file, NULL);
1562   old_filename = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1563
1564   sys_new_filename = g_filename_from_utf8 (new_filename, -1, NULL, NULL, &error);
1565   if (error)
1566     {
1567       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1568         buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), new_filename);
1569       else
1570         buf = g_strdup_printf (_("Error renaming file to \"%s\": %s\n%s"),
1571                                new_filename, error->message,
1572                                _("You probably used symbols not allowed in filenames."));
1573       gtk_file_selection_fileop_error (fs, buf);
1574       g_error_free (error);
1575       goto out1;
1576     }
1577
1578   sys_old_filename = g_filename_from_utf8 (old_filename, -1, NULL, NULL, &error);
1579   if (error)
1580     {
1581       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1582         buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), old_filename);
1583       else
1584         buf = g_strdup_printf (_("Error renaming file \"%s\": %s\n%s"),
1585                                old_filename, error->message,
1586                                _("It probably contains symbols not allowed in filenames."));
1587       gtk_file_selection_fileop_error (fs, buf);
1588       g_error_free (error);
1589       goto out2;
1590     }
1591   
1592   if (rename (sys_old_filename, sys_new_filename) < 0) 
1593     {
1594       buf = g_strdup_printf (_("Error renaming file \"%s\" to \"%s\": %s"),
1595                              sys_old_filename, sys_new_filename,
1596                              g_strerror (errno));
1597       gtk_file_selection_fileop_error (fs, buf);
1598       goto out2;
1599     }
1600   
1601   gtk_file_selection_populate (fs, "", FALSE, FALSE);
1602   gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), file);
1603   
1604  out2:
1605   g_free (sys_old_filename);
1606
1607  out1:
1608   g_free (new_filename);
1609   g_free (old_filename);
1610   g_free (sys_new_filename);
1611   
1612   gtk_widget_destroy (fs->fileop_dialog);
1613 }
1614   
1615 static void
1616 gtk_file_selection_rename_file (GtkWidget *widget,
1617                                 gpointer   data)
1618 {
1619   GtkFileSelection *fs = data;
1620   GtkWidget *label;
1621   GtkWidget *dialog;
1622   GtkWidget *vbox;
1623   GtkWidget *button;
1624   gchar *buf;
1625   
1626   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1627
1628   if (fs->fileop_dialog)
1629           return;
1630
1631   g_free (fs->fileop_file);
1632   fs->fileop_file = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1633   if (strlen (fs->fileop_file) < 1)
1634     return;
1635   
1636   /* main dialog */
1637   fs->fileop_dialog = dialog = gtk_dialog_new ();
1638   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1639                       (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
1640                       (gpointer) fs);
1641   gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File"));
1642   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1643   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
1644
1645   /* If file dialog is grabbed, grab option dialog */
1646   /* When option dialog  closed, file dialog will be grabbed again */
1647   if (GTK_WINDOW (fs)->modal)
1648     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1649   
1650   vbox = gtk_vbox_new (FALSE, 0);
1651   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1652   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1653                       FALSE, FALSE, 0);
1654   gtk_widget_show(vbox);
1655   
1656   buf = g_strdup_printf (_("Rename file \"%s\" to:"), fs->fileop_file);
1657   label = gtk_label_new (buf);
1658   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1659   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1660   gtk_widget_show (label);
1661   g_free (buf);
1662
1663   /* New filename entry */
1664   fs->fileop_entry = gtk_entry_new ();
1665   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
1666                       TRUE, TRUE, 5);
1667   GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1668   gtk_widget_show (fs->fileop_entry);
1669   
1670   gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
1671   gtk_editable_select_region (GTK_EDITABLE (fs->fileop_entry),
1672                               0, strlen (fs->fileop_file));
1673
1674   /* buttons */
1675   button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
1676   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1677                              (GtkSignalFunc) gtk_widget_destroy, 
1678                              (gpointer) dialog);
1679   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1680                       button, TRUE, TRUE, 0);
1681   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1682   gtk_widget_grab_default (button);
1683   gtk_widget_show (button);
1684
1685   gtk_widget_grab_focus (fs->fileop_entry);
1686
1687   button = gtk_button_new_with_label (_("Rename"));
1688   gtk_signal_connect (GTK_OBJECT (button), "clicked",
1689                       (GtkSignalFunc) gtk_file_selection_rename_file_confirmed, 
1690                       (gpointer) fs);
1691   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1692                       button, TRUE, TRUE, 0);
1693   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1694   gtk_widget_show (button);
1695   
1696   gtk_widget_show (dialog);
1697 }
1698
1699 static gint
1700 gtk_file_selection_insert_text (GtkWidget   *widget,
1701                                 const gchar *new_text,
1702                                 gint         new_text_length,
1703                                 gint        *position,
1704                                 gpointer     user_data)
1705 {
1706   gchar *filename;
1707
1708   filename = g_filename_from_utf8 (new_text, new_text_length, NULL, NULL, NULL);
1709
1710   if (!filename)
1711     {
1712       gdk_beep ();
1713       gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "insert_text");
1714       return FALSE;
1715     }
1716   
1717   g_free (filename);
1718   
1719   return TRUE;
1720 }
1721
1722 static gint
1723 gtk_file_selection_key_press (GtkWidget   *widget,
1724                               GdkEventKey *event,
1725                               gpointer     user_data)
1726 {
1727   GtkFileSelection *fs;
1728   char *text;
1729
1730   g_return_val_if_fail (widget != NULL, FALSE);
1731   g_return_val_if_fail (event != NULL, FALSE);
1732
1733   if ((event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab) &&
1734       (event->state & gtk_accelerator_get_default_mod_mask ()) == 0)
1735     {
1736       fs = GTK_FILE_SELECTION (user_data);
1737 #ifdef G_WITH_CYGWIN
1738       translate_win32_path (fs);
1739 #endif
1740       text = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1741
1742       gtk_file_selection_populate (fs, text, TRUE, TRUE);
1743
1744       g_free (text);
1745
1746       return TRUE;
1747     }
1748
1749   return FALSE;
1750 }
1751
1752 static void
1753 gtk_file_selection_history_callback (GtkWidget *widget,
1754                                      gpointer   data)
1755 {
1756   GtkFileSelection *fs = data;
1757   HistoryCallbackArg *callback_arg;
1758   GList *list;
1759
1760   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1761
1762   list = fs->history_list;
1763   
1764   while (list) {
1765     callback_arg = list->data;
1766     
1767     if (callback_arg->menu_item == widget)
1768       {
1769         gtk_file_selection_populate (fs, callback_arg->directory, FALSE, FALSE);
1770         break;
1771       }
1772     
1773     list = list->next;
1774   }
1775 }
1776
1777 static void 
1778 gtk_file_selection_update_history_menu (GtkFileSelection *fs,
1779                                         gchar            *current_directory)
1780 {
1781   HistoryCallbackArg *callback_arg;
1782   GtkWidget *menu_item;
1783   GList *list;
1784   gchar *current_dir;
1785   gint dir_len;
1786   gint i;
1787   
1788   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1789   g_return_if_fail (current_directory != NULL);
1790   
1791   list = fs->history_list;
1792
1793   if (fs->history_menu) 
1794     {
1795       while (list) {
1796         callback_arg = list->data;
1797         g_free (callback_arg->directory);
1798         g_free (callback_arg);
1799         list = list->next;
1800       }
1801       g_list_free (fs->history_list);
1802       fs->history_list = NULL;
1803       
1804       gtk_widget_destroy (fs->history_menu);
1805     }
1806   
1807   fs->history_menu = gtk_menu_new ();
1808
1809   current_dir = g_strdup (current_directory);
1810
1811   dir_len = strlen (current_dir);
1812
1813   for (i = dir_len; i >= 0; i--)
1814     {
1815       /* the i == dir_len is to catch the full path for the first 
1816        * entry. */
1817       if ( (current_dir[i] == G_DIR_SEPARATOR) || (i == dir_len))
1818         {
1819           /* another small hack to catch the full path */
1820           if (i != dir_len) 
1821                   current_dir[i + 1] = '\0';
1822 #ifdef G_WITH_CYGWIN
1823           if (!strcmp (current_dir, "//"))
1824             continue;
1825 #endif
1826           menu_item = gtk_menu_item_new_with_label (current_dir);
1827           
1828           callback_arg = g_new (HistoryCallbackArg, 1);
1829           callback_arg->menu_item = menu_item;
1830           
1831           /* since the autocompletion gets confused if you don't 
1832            * supply a trailing '/' on a dir entry, set the full
1833            * (current) path to "" which just refreshes the filesel */
1834           if (dir_len == i)
1835             {
1836               callback_arg->directory = g_strdup ("");
1837             }
1838           else
1839             {
1840               callback_arg->directory = g_strdup (current_dir);
1841             }
1842           
1843           fs->history_list = g_list_append (fs->history_list, callback_arg);
1844           
1845           gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
1846                               (GtkSignalFunc) gtk_file_selection_history_callback,
1847                               (gpointer) fs);
1848           gtk_menu_shell_append (GTK_MENU_SHELL (fs->history_menu), menu_item);
1849           gtk_widget_show (menu_item);
1850         }
1851     }
1852
1853   gtk_option_menu_set_menu (GTK_OPTION_MENU (fs->history_pulldown), 
1854                             fs->history_menu);
1855   g_free (current_dir);
1856 }
1857
1858 static gchar *
1859 get_real_filename (gchar    *filename,
1860                    gboolean  free_old)
1861 {
1862 #ifdef G_WITH_CYGWIN
1863   /* Check to see if the selection was a drive selector */
1864   if (isalpha (filename[0]) && (filename[1] == ':'))
1865     {
1866       /* It is... map it to a CYGWIN32 drive */
1867       gchar *temp_filename = g_strdup_printf ("//%c/", tolower (filename[0]));
1868
1869       if (free_old)
1870         g_free (filename);
1871
1872       return temp_filename;
1873     }
1874 #else
1875   return filename;
1876 #endif /* G_WITH_CYGWIN */
1877 }
1878
1879 static void
1880 gtk_file_selection_file_activate (GtkTreeView       *tree_view,
1881                                   GtkTreePath       *path,
1882                                   GtkTreeViewColumn *column,
1883                                   gpointer           user_data)
1884 {
1885   GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
1886   GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
1887   GtkTreeIter iter;  
1888   gchar *filename;
1889   
1890   gtk_tree_model_get_iter (model, &iter, path);
1891   gtk_tree_model_get (model, &iter, FILE_COLUMN, &filename, -1);
1892   filename = get_real_filename (filename, TRUE);
1893
1894   gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1895   gtk_button_clicked (GTK_BUTTON (fs->ok_button));
1896
1897   g_free (filename);
1898 }
1899
1900 static void
1901 gtk_file_selection_dir_activate (GtkTreeView       *tree_view,
1902                                  GtkTreePath       *path,
1903                                  GtkTreeViewColumn *column,
1904                                  gpointer           user_data)
1905 {
1906   GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
1907   GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
1908   GtkTreeIter iter;
1909   gchar *filename;
1910
1911   gtk_tree_model_get_iter (model, &iter, path);
1912   gtk_tree_model_get (model, &iter, DIR_COLUMN, &filename, -1);
1913   gtk_file_selection_populate (fs, filename, FALSE, FALSE);
1914   g_free (filename);
1915 }
1916
1917 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
1918
1919 static void
1920 win32_gtk_add_drives_to_dir_list (GtkListStore *model)
1921 {
1922   gchar *textPtr;
1923   gchar buffer[128];
1924   char formatBuffer[128];
1925   GtkTreeIter iter;
1926
1927   /* Get the drives string */
1928   GetLogicalDriveStrings (sizeof (buffer), buffer);
1929
1930   /* Add the drives as necessary */
1931   textPtr = buffer;
1932   while (*textPtr != '\0')
1933     {
1934       /* Ignore floppies (?) */
1935       if ((tolower (textPtr[0]) != 'a') && (tolower (textPtr[0]) != 'b'))
1936         {
1937           /* Build the actual displayable string */
1938           sprintf (formatBuffer, "%c:\\", toupper (textPtr[0]));
1939
1940           /* Add to the list */
1941           gtk_list_store_append (model, &iter);
1942           gtk_list_store_set (model, &iter, DIR_COLUMN, formatBuffer, -1);
1943         }
1944       textPtr += (strlen (textPtr) + 1);
1945     }
1946 }
1947 #endif
1948
1949 static void
1950 gtk_file_selection_populate (GtkFileSelection *fs,
1951                              gchar            *rel_path,
1952                              gboolean          try_complete,
1953                              gboolean          reset_entry)
1954 {
1955   CompletionState *cmpl_state;
1956   PossibleCompletion* poss;
1957   GtkTreeIter iter;
1958   GtkListStore *dir_model;
1959   GtkListStore *file_model;
1960   gchar* filename;
1961   gchar* rem_path = rel_path;
1962   gchar* sel_text;
1963   gint did_recurse = FALSE;
1964   gint possible_count = 0;
1965   gint selection_index = -1;
1966   
1967   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1968
1969   cmpl_state = (CompletionState*) fs->cmpl_state;
1970   poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
1971
1972   if (!cmpl_state_okay (cmpl_state))
1973     {
1974       /* Something went wrong. */
1975       gtk_file_selection_abort (fs);
1976       return;
1977     }
1978
1979   g_assert (cmpl_state->reference_dir);
1980
1981   dir_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->dir_list)));
1982   file_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->file_list)));
1983
1984   gtk_list_store_clear (dir_model);
1985   gtk_list_store_clear (file_model);
1986
1987   /* Set the dir list to include ./ and ../ */
1988   gtk_list_store_append (dir_model, &iter);
1989   gtk_list_store_set (dir_model, &iter, DIR_COLUMN, "." G_DIR_SEPARATOR_S, -1);
1990   gtk_list_store_append (dir_model, &iter);
1991   gtk_list_store_set (dir_model, &iter, DIR_COLUMN, ".." G_DIR_SEPARATOR_S, -1);
1992
1993   while (poss)
1994     {
1995       if (cmpl_is_a_completion (poss))
1996         {
1997           possible_count += 1;
1998
1999           filename = cmpl_this_completion (poss);
2000
2001           if (cmpl_is_directory (poss))
2002             {
2003               if (strcmp (filename, "." G_DIR_SEPARATOR_S) != 0 &&
2004                   strcmp (filename, ".." G_DIR_SEPARATOR_S) != 0)
2005                 {
2006                   gtk_list_store_append (dir_model, &iter);
2007                   gtk_list_store_set (dir_model, &iter, DIR_COLUMN, filename, -1);
2008                 }
2009             }
2010           else
2011             {
2012               gtk_list_store_append (file_model, &iter);
2013               gtk_list_store_set (file_model, &iter, DIR_COLUMN, filename, -1);
2014             }
2015         }
2016
2017       poss = cmpl_next_completion (cmpl_state);
2018     }
2019
2020 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
2021   /* For Windows, add drives as potential selections */
2022   win32_gtk_add_drives_to_dir_list (dir_model);
2023 #endif
2024
2025   /* File lists are set. */
2026
2027   g_assert (cmpl_state->reference_dir);
2028
2029   if (try_complete)
2030     {
2031
2032       /* User is trying to complete filenames, so advance the user's input
2033        * string to the updated_text, which is the common leading substring
2034        * of all possible completions, and if its a directory attempt
2035        * attempt completions in it. */
2036
2037       if (cmpl_updated_text (cmpl_state)[0])
2038         {
2039
2040           if (cmpl_updated_dir (cmpl_state))
2041             {
2042               gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
2043
2044               did_recurse = TRUE;
2045
2046               gtk_file_selection_populate (fs, dir_name, TRUE, TRUE);
2047
2048               g_free (dir_name);
2049             }
2050           else
2051             {
2052               if (fs->selection_entry)
2053                       gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
2054                                           cmpl_updated_text (cmpl_state));
2055             }
2056         }
2057       else
2058         {
2059           selection_index = cmpl_last_valid_char (cmpl_state) -
2060                             (strlen (rel_path) - strlen (rem_path));
2061           if (fs->selection_entry)
2062             gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
2063         }
2064     }
2065   else if (reset_entry)
2066     {
2067       if (fs->selection_entry)
2068         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
2069     }
2070
2071   if (!did_recurse)
2072     {
2073       if (fs->selection_entry)
2074         gtk_entry_set_position (GTK_ENTRY (fs->selection_entry), selection_index);
2075
2076       if (fs->selection_entry)
2077         {
2078           sel_text = g_strconcat (_("Selection: "),
2079                                   cmpl_reference_position (cmpl_state),
2080                                   NULL);
2081
2082           gtk_label_set_text (GTK_LABEL (fs->selection_text), sel_text);
2083           g_free (sel_text);
2084         }
2085
2086       if (fs->history_pulldown) 
2087         {
2088           gtk_file_selection_update_history_menu (fs, cmpl_reference_position (cmpl_state));
2089         }
2090       
2091     }
2092 }
2093
2094 static void
2095 gtk_file_selection_abort (GtkFileSelection *fs)
2096 {
2097   gchar err_buf[256];
2098
2099   sprintf (err_buf, _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
2100
2101   /*  BEEP gdk_beep();  */
2102
2103   if (fs->selection_entry)
2104     gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
2105 }
2106
2107 /**
2108  * gtk_file_selection_set_select_multiple:
2109  * @filesel: a #GtkFileSelection
2110  * @select_multiple: whether or not the user is allowed to select multiple
2111  * files in the file list.
2112  *
2113  * Sets whether the user is allowed to select multiple files in the file list.
2114  * Use gtk_file_selection_get_selections () to get the list of selected files.
2115  **/
2116 void
2117 gtk_file_selection_set_select_multiple (GtkFileSelection *filesel,
2118                                         gboolean          select_multiple)
2119 {
2120   GtkTreeSelection *sel;
2121   GtkSelectionMode mode;
2122
2123   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
2124
2125   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
2126
2127   mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE;
2128
2129   if (mode != gtk_tree_selection_get_mode (sel))
2130     {
2131       gtk_tree_selection_set_mode (sel, mode);
2132
2133       g_object_notify (G_OBJECT (filesel), "select-multiple");
2134     }
2135 }
2136
2137 /**
2138  * gtk_file_selection_get_select_multiple:
2139  * @filesel: a #GtkFileSelection
2140  *
2141  * Determines whether or not the user is allowed to select multiple files in
2142  * the file list. See gtk_file_selection_set_select_multiple().
2143  *
2144  * Return value: %TRUE if the user is allowed to select multiple files in the
2145  * file list
2146  **/
2147 gboolean
2148 gtk_file_selection_get_select_multiple (GtkFileSelection *filesel)
2149 {
2150   GtkTreeSelection *sel;
2151
2152   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), FALSE);
2153
2154   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
2155   return (gtk_tree_selection_get_mode (sel) == GTK_SELECTION_MULTIPLE);
2156 }
2157
2158 static void
2159 multiple_changed_foreach (GtkTreeModel *model,
2160                           GtkTreePath  *path,
2161                           GtkTreeIter  *iter,
2162                           gpointer      data)
2163 {
2164   GPtrArray *names = data;
2165   gchar *filename;
2166
2167   gtk_tree_model_get (model, iter, FILE_COLUMN, &filename, -1);
2168
2169   g_ptr_array_add (names, filename);
2170 }
2171
2172 static void
2173 free_selected_names (GPtrArray *names)
2174 {
2175   gint i;
2176
2177   for (i = 0; i < names->len; i++)
2178     g_free (g_ptr_array_index (names, i));
2179
2180   g_ptr_array_free (names, TRUE);
2181 }
2182
2183 static void
2184 gtk_file_selection_file_changed (GtkTreeSelection *selection,
2185                                  gpointer          user_data)
2186 {
2187   GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
2188   GPtrArray *new_names;
2189   gchar *filename;
2190   const gchar *entry;
2191   gint index = -1;
2192
2193   new_names = g_ptr_array_sized_new (8);
2194
2195   gtk_tree_selection_selected_foreach (selection,
2196                                        multiple_changed_foreach,
2197                                        new_names);
2198
2199   /* nothing selected */
2200   if (new_names->len == 0)
2201     {
2202       g_ptr_array_free (new_names, TRUE);
2203
2204       if (fs->selected_names != NULL)
2205         {
2206           free_selected_names (fs->selected_names);
2207           fs->selected_names = NULL;
2208         }
2209
2210       goto maybe_clear_entry;
2211     }
2212
2213   if (new_names->len != 1)
2214     {
2215       GPtrArray *old_names = fs->selected_names;
2216
2217       if (old_names != NULL)
2218         {
2219           /* A common case is selecting a range of files from top to bottom,
2220            * so quickly check for that to avoid looping over the entire list
2221            */
2222           if (compare_filenames (g_ptr_array_index (old_names, old_names->len - 1),
2223                                  g_ptr_array_index (new_names, new_names->len - 1)) != 0)
2224             index = new_names->len - 1;
2225           else
2226             {
2227               gint i = 0, j = 0, cmp;
2228
2229               /* do a quick diff, stopping at the first file not in the
2230                * old list
2231                */
2232               while (i < old_names->len && j < new_names->len)
2233                 {
2234                   cmp = compare_filenames (g_ptr_array_index (old_names, i),
2235                                            g_ptr_array_index (new_names, j));
2236                   if (cmp < 0)
2237                     {
2238                       i++;
2239                     }
2240                   else if (cmp == 0)
2241                     {
2242                       i++;
2243                       j++;
2244                     }
2245                   else if (cmp > 0)
2246                     {
2247                       index = j;
2248                       break;
2249                     }
2250                 }
2251
2252               /* we ran off the end of the old list */
2253               if (index == -1 && i < new_names->len)
2254                 index = j;
2255             }
2256         }
2257       else
2258         {
2259           /* A phantom anchor still exists at the point where the last item
2260            * was selected, which is used for subsequent range selections.
2261            * So search up from there.
2262            */
2263           if (compare_filenames (fs->last_selected,
2264                                  g_ptr_array_index (new_names, 0)) == 0)
2265             index = new_names->len - 1;
2266           else
2267             index = 0;
2268         }
2269     }
2270   else
2271     index = 0;
2272
2273   if (fs->selected_names != NULL)
2274     free_selected_names (fs->selected_names);
2275
2276   fs->selected_names = new_names;
2277
2278   if (index != -1)
2279     {
2280       if (fs->last_selected != NULL)
2281         g_free (fs->last_selected);
2282
2283       fs->last_selected = g_strdup (g_ptr_array_index (new_names, index));
2284       filename = get_real_filename (fs->last_selected, FALSE);
2285
2286       gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
2287
2288       if (filename != fs->last_selected)
2289         g_free (filename);
2290       
2291       return;
2292     }
2293   
2294 maybe_clear_entry:
2295
2296   entry = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
2297   if ((entry != NULL) && (fs->last_selected != NULL) &&
2298       (compare_filenames (entry, fs->last_selected) == 0))
2299     gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
2300 }
2301
2302 /**
2303  * gtk_file_selection_get_selections:
2304  * @filesel: a #GtkFileSelection
2305  *
2306  * Retrieves the list of file selections the user has made in the dialog box.
2307  * This function is intended for use when the user can select multiple files
2308  * in the file list. The first file in the list is equivalent to what
2309  * gtk_file_selection_get_filename() would return.
2310  *
2311  * The filenames are in the encoding of g_filename_from_utf8, which may or may
2312  * not be the same as that used by GTK+ (UTF-8). To convert to UTF-8, call
2313  * g_filename_to_utf8() on each string.
2314  *
2315  * Return value: a newly-allocated %NULL-terminated array of strings. Use
2316  * g_strfreev() to free it.
2317  **/
2318 gchar **
2319 gtk_file_selection_get_selections (GtkFileSelection *filesel)
2320 {
2321   GPtrArray *names;
2322   gchar **selections;
2323   gchar *filename, *dirname;
2324   gchar *current, *buf;
2325   gint i, count;
2326
2327   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), NULL);
2328
2329   filename = g_strdup (gtk_file_selection_get_filename (filesel));
2330
2331   if (strlen (filename) == 0)
2332     {
2333       g_free (filename);
2334       return NULL;
2335     }
2336
2337   names = filesel->selected_names;
2338
2339   if (names != NULL)
2340     selections = g_new (gchar *, names->len + 2);
2341   else
2342     selections = g_new (gchar *, 2);
2343
2344   count = 0;
2345   selections[count++] = filename;
2346
2347   if (names != NULL)
2348     {
2349       dirname = g_path_get_dirname (filename);
2350
2351       for (i = 0; i < names->len; i++)
2352         {
2353           buf = g_filename_from_utf8 (g_ptr_array_index (names, i), -1,
2354                                       NULL, NULL, NULL);
2355           current = g_build_filename (dirname, buf, NULL);
2356           g_free (buf);
2357
2358           if (compare_filenames (current, filename) != 0)
2359             selections[count++] = current;
2360           else
2361             g_free (current);
2362         }
2363     }
2364
2365   selections[count] = NULL;
2366
2367   return selections;
2368 }
2369
2370 /**********************************************************************/
2371 /*                        External Interface                          */
2372 /**********************************************************************/
2373
2374 /* The four completion state selectors
2375  */
2376 static gchar*
2377 cmpl_updated_text (CompletionState *cmpl_state)
2378 {
2379   return cmpl_state->updated_text;
2380 }
2381
2382 static gboolean
2383 cmpl_updated_dir (CompletionState *cmpl_state)
2384 {
2385   return cmpl_state->re_complete;
2386 }
2387
2388 static gchar*
2389 cmpl_reference_position (CompletionState *cmpl_state)
2390 {
2391   return cmpl_state->reference_dir->fullname;
2392 }
2393
2394 static gint
2395 cmpl_last_valid_char (CompletionState *cmpl_state)
2396 {
2397   return cmpl_state->last_valid_char;
2398 }
2399
2400 static gchar*
2401 cmpl_completion_fullname (const gchar     *text,
2402                           CompletionState *cmpl_state)
2403 {
2404   static char nothing[2] = "";
2405
2406   if (!cmpl_state_okay (cmpl_state))
2407     {
2408       return nothing;
2409     }
2410   else if (g_path_is_absolute (text))
2411     {
2412       strcpy (cmpl_state->updated_text, text);
2413     }
2414 #ifdef HAVE_PWD_H
2415   else if (text[0] == '~')
2416     {
2417       CompletionDir* dir;
2418       char* slash;
2419
2420       dir = open_user_dir (text, cmpl_state);
2421
2422       if (!dir)
2423         {
2424           /* spencer says just return ~something, so
2425            * for now just do it. */
2426           strcpy (cmpl_state->updated_text, text);
2427         }
2428       else
2429         {
2430
2431           strcpy (cmpl_state->updated_text, dir->fullname);
2432
2433           slash = strchr (text, G_DIR_SEPARATOR);
2434
2435           if (slash)
2436             strcat (cmpl_state->updated_text, slash);
2437         }
2438     }
2439 #endif
2440   else
2441     {
2442       strcpy (cmpl_state->updated_text, cmpl_state->reference_dir->fullname);
2443       if (cmpl_state->updated_text[strlen (cmpl_state->updated_text) - 1] != G_DIR_SEPARATOR)
2444         strcat (cmpl_state->updated_text, G_DIR_SEPARATOR_S);
2445       strcat (cmpl_state->updated_text, text);
2446     }
2447
2448   return cmpl_state->updated_text;
2449 }
2450
2451 /* The three completion selectors
2452  */
2453 static gchar*
2454 cmpl_this_completion (PossibleCompletion* pc)
2455 {
2456   return pc->text;
2457 }
2458
2459 static gboolean
2460 cmpl_is_directory (PossibleCompletion* pc)
2461 {
2462   return pc->is_directory;
2463 }
2464
2465 static gint
2466 cmpl_is_a_completion (PossibleCompletion* pc)
2467 {
2468   return pc->is_a_completion;
2469 }
2470
2471 /**********************************************************************/
2472 /*                       Construction, deletion                       */
2473 /**********************************************************************/
2474
2475 static CompletionState*
2476 cmpl_init_state (void)
2477 {
2478   gchar *sys_getcwd_buf;
2479   gchar *utf8_cwd;
2480   CompletionState *new_state;
2481
2482   new_state = g_new (CompletionState, 1);
2483
2484   /* g_get_current_dir() returns a string in the "system" charset */
2485   sys_getcwd_buf = g_get_current_dir ();
2486   utf8_cwd = g_filename_to_utf8 (sys_getcwd_buf, -1, NULL, NULL, NULL);
2487   g_free (sys_getcwd_buf);
2488
2489 tryagain:
2490
2491   new_state->reference_dir = NULL;
2492   new_state->completion_dir = NULL;
2493   new_state->active_completion_dir = NULL;
2494   new_state->directory_storage = NULL;
2495   new_state->directory_sent_storage = NULL;
2496   new_state->last_valid_char = 0;
2497   new_state->updated_text = g_new (gchar, MAXPATHLEN);
2498   new_state->updated_text_alloc = MAXPATHLEN;
2499   new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
2500   new_state->the_completion.text_alloc = MAXPATHLEN;
2501   new_state->user_dir_name_buffer = NULL;
2502   new_state->user_directories = NULL;
2503
2504   new_state->reference_dir = open_dir (utf8_cwd, new_state);
2505
2506   if (!new_state->reference_dir)
2507     {
2508       /* Directories changing from underneath us, grumble */
2509       strcpy (utf8_cwd, G_DIR_SEPARATOR_S);
2510       goto tryagain;
2511     }
2512
2513   g_free (utf8_cwd);
2514   return new_state;
2515 }
2516
2517 static void
2518 cmpl_free_dir_list (GList* dp0)
2519 {
2520   GList *dp = dp0;
2521
2522   while (dp)
2523     {
2524       free_dir (dp->data);
2525       dp = dp->next;
2526     }
2527
2528   g_list_free (dp0);
2529 }
2530
2531 static void
2532 cmpl_free_dir_sent_list (GList* dp0)
2533 {
2534   GList *dp = dp0;
2535
2536   while (dp)
2537     {
2538       free_dir_sent (dp->data);
2539       dp = dp->next;
2540     }
2541
2542   g_list_free (dp0);
2543 }
2544
2545 static void
2546 cmpl_free_state (CompletionState* cmpl_state)
2547 {
2548   g_return_if_fail (cmpl_state != NULL);
2549
2550   cmpl_free_dir_list (cmpl_state->directory_storage);
2551   cmpl_free_dir_sent_list (cmpl_state->directory_sent_storage);
2552
2553   if (cmpl_state->user_dir_name_buffer)
2554     g_free (cmpl_state->user_dir_name_buffer);
2555   if (cmpl_state->user_directories)
2556     g_free (cmpl_state->user_directories);
2557   if (cmpl_state->the_completion.text)
2558     g_free (cmpl_state->the_completion.text);
2559   if (cmpl_state->updated_text)
2560     g_free (cmpl_state->updated_text);
2561
2562   g_free (cmpl_state);
2563 }
2564
2565 static void
2566 free_dir (CompletionDir* dir)
2567 {
2568   g_free (dir->cmpl_text);
2569   g_free (dir->fullname);
2570   g_free (dir);
2571 }
2572
2573 static void
2574 free_dir_sent (CompletionDirSent* sent)
2575 {
2576   gint i;
2577   for (i = 0; i < sent->entry_count; i++)
2578     g_free (sent->entries[i].entry_name);
2579   g_free (sent->entries);
2580   g_free (sent);
2581 }
2582
2583 static void
2584 prune_memory_usage (CompletionState *cmpl_state)
2585 {
2586   GList* cdsl = cmpl_state->directory_sent_storage;
2587   GList* cdl = cmpl_state->directory_storage;
2588   GList* cdl0 = cdl;
2589   gint len = 0;
2590
2591   for (; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
2592     cdsl = cdsl->next;
2593
2594   if (cdsl)
2595     {
2596       cmpl_free_dir_sent_list (cdsl->next);
2597       cdsl->next = NULL;
2598     }
2599
2600   cmpl_state->directory_storage = NULL;
2601   while (cdl)
2602     {
2603       if (cdl->data == cmpl_state->reference_dir)
2604         cmpl_state->directory_storage = g_list_prepend (NULL, cdl->data);
2605       else
2606         free_dir (cdl->data);
2607       cdl = cdl->next;
2608     }
2609
2610   g_list_free (cdl0);
2611 }
2612
2613 /**********************************************************************/
2614 /*                        The main entrances.                         */
2615 /**********************************************************************/
2616
2617 static PossibleCompletion*
2618 cmpl_completion_matches (gchar           *text_to_complete,
2619                          gchar          **remaining_text,
2620                          CompletionState *cmpl_state)
2621 {
2622   gchar* first_slash;
2623   PossibleCompletion *poss;
2624
2625   prune_memory_usage (cmpl_state);
2626
2627   g_assert (text_to_complete != NULL);
2628
2629   cmpl_state->user_completion_index = -1;
2630   cmpl_state->last_completion_text = text_to_complete;
2631   cmpl_state->the_completion.text[0] = 0;
2632   cmpl_state->last_valid_char = 0;
2633   cmpl_state->updated_text_len = -1;
2634   cmpl_state->updated_text[0] = 0;
2635   cmpl_state->re_complete = FALSE;
2636
2637 #ifdef HAVE_PWD_H
2638   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2639
2640   if (text_to_complete[0] == '~' && !first_slash)
2641     {
2642       /* Text starts with ~ and there is no slash, show all the
2643        * home directory completions.
2644        */
2645       poss = attempt_homedir_completion (text_to_complete, cmpl_state);
2646
2647       update_cmpl (poss, cmpl_state);
2648
2649       return poss;
2650     }
2651 #endif
2652   cmpl_state->reference_dir =
2653     open_ref_dir (text_to_complete, remaining_text, cmpl_state);
2654
2655   if (!cmpl_state->reference_dir)
2656     return NULL;
2657
2658   cmpl_state->completion_dir =
2659     find_completion_dir (*remaining_text, remaining_text, cmpl_state);
2660
2661   cmpl_state->last_valid_char = *remaining_text - text_to_complete;
2662
2663   if (!cmpl_state->completion_dir)
2664     return NULL;
2665
2666   cmpl_state->completion_dir->cmpl_index = -1;
2667   cmpl_state->completion_dir->cmpl_parent = NULL;
2668   cmpl_state->completion_dir->cmpl_text = g_strdup (*remaining_text);
2669
2670   cmpl_state->active_completion_dir = cmpl_state->completion_dir;
2671
2672   cmpl_state->reference_dir = cmpl_state->completion_dir;
2673
2674   poss = attempt_file_completion (cmpl_state);
2675
2676   update_cmpl (poss, cmpl_state);
2677
2678   return poss;
2679 }
2680
2681 static PossibleCompletion*
2682 cmpl_next_completion (CompletionState* cmpl_state)
2683 {
2684   PossibleCompletion* poss = NULL;
2685
2686   cmpl_state->the_completion.text[0] = 0;
2687
2688 #ifdef HAVE_PWD_H
2689   if (cmpl_state->user_completion_index >= 0)
2690     poss = attempt_homedir_completion (cmpl_state->last_completion_text, cmpl_state);
2691   else
2692     poss = attempt_file_completion (cmpl_state);
2693 #else
2694   poss = attempt_file_completion (cmpl_state);
2695 #endif
2696
2697   update_cmpl (poss, cmpl_state);
2698
2699   return poss;
2700 }
2701
2702 /**********************************************************************/
2703 /*                       Directory Operations                         */
2704 /**********************************************************************/
2705
2706 /* Open the directory where completion will begin from, if possible. */
2707 static CompletionDir*
2708 open_ref_dir (gchar           *text_to_complete,
2709               gchar          **remaining_text,
2710               CompletionState *cmpl_state)
2711 {
2712   gchar* first_slash;
2713   CompletionDir *new_dir;
2714
2715   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2716
2717 #ifdef G_WITH_CYGWIN
2718   if (text_to_complete[0] == '/' && text_to_complete[1] == '/')
2719     {
2720       char root_dir[5];
2721       sprintf (root_dir, "//%c", text_to_complete[2]);
2722
2723       new_dir = open_dir (root_dir, cmpl_state);
2724
2725       if (new_dir) {
2726         *remaining_text = text_to_complete + 4;
2727       }
2728     }
2729 #else
2730   if (FALSE)
2731     ;
2732 #endif
2733 #ifdef HAVE_PWD_H
2734   else if (text_to_complete[0] == '~')
2735     {
2736       new_dir = open_user_dir (text_to_complete, cmpl_state);
2737
2738       if (new_dir)
2739         {
2740           if (first_slash)
2741             *remaining_text = first_slash + 1;
2742           else
2743             *remaining_text = text_to_complete + strlen (text_to_complete);
2744         }
2745       else
2746         {
2747           return NULL;
2748         }
2749     }
2750 #endif
2751   else if (g_path_is_absolute (text_to_complete) || !cmpl_state->reference_dir)
2752     {
2753       gchar *tmp = g_strdup (text_to_complete);
2754       gchar *p;
2755
2756       p = tmp;
2757       while (*p && *p != '*' && *p != '?')
2758         p++;
2759
2760       *p = '\0';
2761       p = strrchr (tmp, G_DIR_SEPARATOR);
2762       if (p)
2763         {
2764           if (p == tmp)
2765             p++;
2766       
2767           *p = '\0';
2768
2769           new_dir = open_dir (tmp, cmpl_state);
2770
2771           if (new_dir)
2772             *remaining_text = text_to_complete + 
2773               ((p == tmp + 1) ? (p - tmp) : (p + 1 - tmp));
2774         }
2775       else
2776         {
2777           /* If no possible candidates, use the cwd */
2778           gchar *sys_curdir = g_get_current_dir ();
2779           gchar *utf8_curdir = g_filename_to_utf8 (sys_curdir, -1, NULL, NULL, NULL);
2780
2781           g_free (sys_curdir);
2782
2783           new_dir = open_dir (utf8_curdir, cmpl_state);
2784
2785           if (new_dir)
2786             *remaining_text = text_to_complete;
2787
2788           g_free (utf8_curdir);
2789         }
2790
2791       g_free (tmp);
2792     }
2793   else
2794     {
2795       *remaining_text = text_to_complete;
2796
2797       new_dir = open_dir (cmpl_state->reference_dir->fullname, cmpl_state);
2798     }
2799
2800   if (new_dir)
2801     {
2802       new_dir->cmpl_index = -1;
2803       new_dir->cmpl_parent = NULL;
2804     }
2805
2806   return new_dir;
2807 }
2808
2809 #ifdef HAVE_PWD_H
2810
2811 /* open a directory by user name */
2812 static CompletionDir*
2813 open_user_dir (const gchar     *text_to_complete,
2814                CompletionState *cmpl_state)
2815 {
2816   CompletionDir *result;
2817   gchar *first_slash;
2818   gint cmp_len;
2819
2820   g_assert (text_to_complete && text_to_complete[0] == '~');
2821
2822   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2823
2824   if (first_slash)
2825     cmp_len = first_slash - text_to_complete - 1;
2826   else
2827     cmp_len = strlen (text_to_complete + 1);
2828
2829   if (!cmp_len)
2830     {
2831       /* ~/ */
2832       const gchar *homedir = g_get_home_dir ();
2833       gchar *utf8_homedir = g_filename_to_utf8 (homedir, -1, NULL, NULL, NULL);
2834
2835       if (utf8_homedir)
2836         result = open_dir (utf8_homedir, cmpl_state);
2837       else
2838         result = NULL;
2839       
2840       g_free (utf8_homedir);
2841     }
2842   else
2843     {
2844       /* ~user/ */
2845       gchar* copy = g_new (char, cmp_len + 1);
2846       gchar *utf8_dir;
2847       struct passwd *pwd;
2848
2849       strncpy (copy, text_to_complete + 1, cmp_len);
2850       copy[cmp_len] = 0;
2851       pwd = getpwnam (copy);
2852       g_free (copy);
2853       if (!pwd)
2854         {
2855           cmpl_errno = errno;
2856           return NULL;
2857         }
2858       utf8_dir = g_filename_to_utf8 (pwd->pw_dir, -1, NULL, NULL, NULL);
2859       result = open_dir (utf8_dir, cmpl_state);
2860       g_free (utf8_dir);
2861     }
2862   return result;
2863 }
2864
2865 #endif
2866
2867 /* open a directory relative the the current relative directory */
2868 static CompletionDir*
2869 open_relative_dir (gchar           *dir_name,
2870                    CompletionDir   *dir,
2871                    CompletionState *cmpl_state)
2872 {
2873   CompletionDir *result;
2874   GString *path;
2875
2876   path = g_string_sized_new (dir->fullname_len + strlen (dir_name) + 10);
2877   g_string_assign (path, dir->fullname);
2878
2879   if (dir->fullname_len > 1
2880       && path->str[dir->fullname_len - 1] != G_DIR_SEPARATOR)
2881     g_string_append_c (path, G_DIR_SEPARATOR);
2882   g_string_append (path, dir_name);
2883
2884   result = open_dir (path->str, cmpl_state);
2885
2886   g_string_free (path, TRUE);
2887
2888   return result;
2889 }
2890
2891 /* after the cache lookup fails, really open a new directory */
2892 static CompletionDirSent*
2893 open_new_dir (gchar       *dir_name,
2894               struct stat *sbuf,
2895               gboolean     stat_subdirs)
2896 {
2897   CompletionDirSent *sent;
2898   GDir *directory;
2899   const char *dirent;
2900   GError *error;
2901   gint entry_count = 0;
2902   gint n_entries = 0;
2903   gint i;
2904   struct stat ent_sbuf;
2905   GString *path;
2906   gchar *sys_dir_name;
2907
2908   sent = g_new (CompletionDirSent, 1);
2909   sent->mtime = sbuf->st_mtime;
2910   sent->inode = sbuf->st_ino;
2911   sent->device = sbuf->st_dev;
2912
2913   path = g_string_sized_new (2*MAXPATHLEN + 10);
2914
2915   sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
2916   if (!sys_dir_name)
2917     {
2918       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
2919       return NULL;
2920     }
2921   
2922   directory = g_dir_open (sys_dir_name, 0, &error);
2923   if (!directory)
2924     {
2925       cmpl_errno = error->code; /* ??? */
2926       g_free (sys_dir_name);
2927       return NULL;
2928     }
2929
2930   while ((dirent = g_dir_read_name (directory)) != NULL)
2931     entry_count++;
2932
2933   sent->entries = g_new (CompletionDirEntry, entry_count);
2934   sent->entry_count = entry_count;
2935
2936   g_dir_rewind (directory);
2937
2938   for (i = 0; i < entry_count; i += 1)
2939     {
2940       dirent = g_dir_read_name (directory);
2941
2942       if (!dirent)
2943         {
2944           g_warning ("Failure reading folder '%s'", sys_dir_name);
2945           g_dir_close (directory);
2946           g_free (sys_dir_name);
2947           return NULL;
2948         }
2949
2950       sent->entries[n_entries].entry_name = g_filename_to_utf8 (dirent, -1, NULL, NULL, NULL);
2951       if (!g_utf8_validate (sent->entries[n_entries].entry_name, -1, NULL))
2952         {
2953           g_warning (_("The filename %s couldn't be converted to UTF-8. Try setting the environment variable G_BROKEN_FILENAMES."), dirent);
2954           continue;
2955         }
2956
2957       g_string_assign (path, sys_dir_name);
2958       if (path->str[path->len-1] != G_DIR_SEPARATOR)
2959         {
2960           g_string_append_c (path, G_DIR_SEPARATOR);
2961         }
2962       g_string_append (path, dirent);
2963
2964       if (stat_subdirs)
2965         {
2966           /* Here we know path->str is a "system charset" string */
2967           if (stat (path->str, &ent_sbuf) >= 0 && S_ISDIR (ent_sbuf.st_mode))
2968             sent->entries[n_entries].is_dir = TRUE;
2969           else
2970             /* stat may fail, and we don't mind, since it could be a
2971              * dangling symlink. */
2972             sent->entries[n_entries].is_dir = FALSE;
2973         }
2974       else
2975         sent->entries[n_entries].is_dir = 1;
2976
2977       n_entries++;
2978     }
2979   sent->entry_count = n_entries;
2980   
2981   g_free (sys_dir_name);
2982   g_string_free (path, TRUE);
2983   qsort (sent->entries, sent->entry_count, sizeof (CompletionDirEntry), compare_cmpl_dir);
2984
2985   g_dir_close (directory);
2986
2987   return sent;
2988 }
2989
2990 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
2991
2992 static gboolean
2993 check_dir (gchar       *dir_name,
2994            struct stat *result,
2995            gboolean    *stat_subdirs)
2996 {
2997   /* A list of directories that we know only contain other directories.
2998    * Trying to stat every file in these directories would be very
2999    * expensive.
3000    */
3001
3002   static struct {
3003     gchar *name;
3004     gboolean present;
3005     struct stat statbuf;
3006   } no_stat_dirs[] = {
3007     { "/afs", FALSE, { 0 } },
3008     { "/net", FALSE, { 0 } }
3009   };
3010
3011   static const gint n_no_stat_dirs = G_N_ELEMENTS (no_stat_dirs);
3012   static gboolean initialized = FALSE;
3013   gchar *sys_dir_name;
3014   gint i;
3015
3016   if (!initialized)
3017     {
3018       initialized = TRUE;
3019       for (i = 0; i < n_no_stat_dirs; i++)
3020         {
3021           if (stat (no_stat_dirs[i].name, &no_stat_dirs[i].statbuf) == 0)
3022             no_stat_dirs[i].present = TRUE;
3023         }
3024     }
3025
3026   sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
3027   if (!sys_dir_name)
3028     {
3029       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3030       return FALSE;
3031     }
3032   
3033   if (stat (sys_dir_name, result) < 0)
3034     {
3035       g_free (sys_dir_name);
3036       cmpl_errno = errno;
3037       return FALSE;
3038     }
3039   g_free (sys_dir_name);
3040
3041   *stat_subdirs = TRUE;
3042   for (i = 0; i < n_no_stat_dirs; i++)
3043     {
3044       if (no_stat_dirs[i].present &&
3045           (no_stat_dirs[i].statbuf.st_dev == result->st_dev) &&
3046           (no_stat_dirs[i].statbuf.st_ino == result->st_ino))
3047         {
3048           *stat_subdirs = FALSE;
3049           break;
3050         }
3051     }
3052
3053   return TRUE;
3054 }
3055
3056 #endif
3057
3058 /* open a directory by absolute pathname */
3059 static CompletionDir*
3060 open_dir (gchar           *dir_name,
3061           CompletionState *cmpl_state)
3062 {
3063   struct stat sbuf;
3064   gboolean stat_subdirs;
3065   CompletionDirSent *sent;
3066   GList* cdsl;
3067
3068 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
3069   if (!check_dir (dir_name, &sbuf, &stat_subdirs))
3070     return NULL;
3071
3072   cdsl = cmpl_state->directory_sent_storage;
3073
3074   while (cdsl)
3075     {
3076       sent = cdsl->data;
3077
3078       if (sent->inode == sbuf.st_ino &&
3079           sent->mtime == sbuf.st_mtime &&
3080           sent->device == sbuf.st_dev)
3081         return attach_dir (sent, dir_name, cmpl_state);
3082
3083       cdsl = cdsl->next;
3084     }
3085 #else
3086   stat_subdirs = TRUE;
3087 #endif
3088
3089   sent = open_new_dir (dir_name, &sbuf, stat_subdirs);
3090
3091   if (sent)
3092     {
3093       cmpl_state->directory_sent_storage =
3094         g_list_prepend (cmpl_state->directory_sent_storage, sent);
3095
3096       return attach_dir (sent, dir_name, cmpl_state);
3097     }
3098
3099   return NULL;
3100 }
3101
3102 static CompletionDir*
3103 attach_dir (CompletionDirSent *sent,
3104             gchar             *dir_name,
3105             CompletionState   *cmpl_state)
3106 {
3107   CompletionDir* new_dir;
3108
3109   new_dir = g_new (CompletionDir, 1);
3110
3111   cmpl_state->directory_storage =
3112     g_list_prepend (cmpl_state->directory_storage, new_dir);
3113
3114   new_dir->sent = sent;
3115   new_dir->fullname = g_strdup (dir_name);
3116   new_dir->fullname_len = strlen (dir_name);
3117   new_dir->cmpl_text = NULL;
3118
3119   return new_dir;
3120 }
3121
3122 static gint
3123 correct_dir_fullname (CompletionDir* cmpl_dir)
3124 {
3125   gint length = strlen (cmpl_dir->fullname);
3126   gchar *first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3127   gchar *sys_filename;
3128   struct stat sbuf;
3129
3130   /* Does it end with /. (\.) ? */
3131   if (length >= 2 &&
3132       strcmp (cmpl_dir->fullname + length - 2, G_DIR_SEPARATOR_S ".") == 0)
3133     {
3134       /* Is it just the root directory (on a drive) ? */
3135       if (cmpl_dir->fullname + length - 2 == first_slash)
3136         {
3137           cmpl_dir->fullname[length - 1] = 0;
3138           cmpl_dir->fullname_len = length - 1;
3139           return TRUE;
3140         }
3141       else
3142         {
3143           cmpl_dir->fullname[length - 2] = 0;
3144         }
3145     }
3146
3147   /* Ends with /./ (\.\)? */
3148   else if (length >= 3 &&
3149            strcmp (cmpl_dir->fullname + length - 3,
3150                    G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) == 0)
3151     cmpl_dir->fullname[length - 2] = 0;
3152
3153   /* Ends with /.. (\..) ? */
3154   else if (length >= 3 &&
3155            strcmp (cmpl_dir->fullname + length - 3,
3156                    G_DIR_SEPARATOR_S "..") == 0)
3157     {
3158       /* Is it just /.. (X:\..)? */
3159       if (cmpl_dir->fullname + length - 3 == first_slash)
3160         {
3161           cmpl_dir->fullname[length - 2] = 0;
3162           cmpl_dir->fullname_len = length - 2;
3163           return TRUE;
3164         }
3165
3166       sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3167       if (!sys_filename)
3168         {
3169           cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3170           return FALSE;
3171         }
3172       
3173       if (stat (sys_filename, &sbuf) < 0)
3174         {
3175           g_free (sys_filename);
3176           cmpl_errno = errno;
3177           return FALSE;
3178         }
3179       g_free (sys_filename);
3180
3181       cmpl_dir->fullname[length - 3] = 0;
3182
3183       if (!correct_parent (cmpl_dir, &sbuf))
3184         return FALSE;
3185     }
3186
3187   /* Ends with /../ (\..\)? */
3188   else if (length >= 4 &&
3189            strcmp (cmpl_dir->fullname + length - 4,
3190                    G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) == 0)
3191     {
3192       /* Is it just /../ (X:\..\)? */
3193       if (cmpl_dir->fullname + length - 4 == first_slash)
3194         {
3195           cmpl_dir->fullname[length - 3] = 0;
3196           cmpl_dir->fullname_len = length - 3;
3197           return TRUE;
3198         }
3199
3200       sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3201       if (!sys_filename)
3202         {
3203           cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3204           return FALSE;
3205         }
3206       
3207       if (stat (sys_filename, &sbuf) < 0)
3208         {
3209           g_free (sys_filename);
3210           cmpl_errno = errno;
3211           return FALSE;
3212         }
3213       g_free (sys_filename);
3214
3215       cmpl_dir->fullname[length - 4] = 0;
3216
3217       if (!correct_parent (cmpl_dir, &sbuf))
3218         return FALSE;
3219     }
3220
3221   cmpl_dir->fullname_len = strlen (cmpl_dir->fullname);
3222
3223   return TRUE;
3224 }
3225
3226 static gint
3227 correct_parent (CompletionDir *cmpl_dir,
3228                 struct stat   *sbuf)
3229 {
3230   struct stat parbuf;
3231   gchar *last_slash;
3232   gchar *first_slash;
3233   gchar *new_name;
3234   gchar *sys_filename;
3235   gchar c = 0;
3236
3237   last_slash = strrchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3238   g_assert (last_slash);
3239   first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3240
3241   /* Clever (?) way to check for top-level directory that works also on
3242    * Win32, where there is a drive letter and colon prefixed...
3243    */
3244   if (last_slash != first_slash)
3245     {
3246       last_slash[0] = 0;
3247     }
3248   else
3249     {
3250       c = last_slash[1];
3251       last_slash[1] = 0;
3252     }
3253
3254   sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3255   if (!sys_filename)
3256     {
3257       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3258       if (!c)
3259         last_slash[0] = G_DIR_SEPARATOR;
3260       return FALSE;
3261     }
3262   
3263   if (stat (sys_filename, &parbuf) < 0)
3264     {
3265       g_free (sys_filename);
3266       cmpl_errno = errno;
3267       if (!c)
3268         last_slash[0] = G_DIR_SEPARATOR;
3269       return FALSE;
3270     }
3271   g_free (sys_filename);
3272
3273 #ifndef G_OS_WIN32              /* No inode numbers on Win32 */
3274   if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
3275     /* it wasn't a link */
3276     return TRUE;
3277
3278   if (c)
3279     last_slash[1] = c;
3280   else
3281     last_slash[0] = G_DIR_SEPARATOR;
3282
3283   /* it was a link, have to figure it out the hard way */
3284
3285   new_name = find_parent_dir_fullname (cmpl_dir->fullname);
3286
3287   if (!new_name)
3288     return FALSE;
3289
3290   g_free (cmpl_dir->fullname);
3291
3292   cmpl_dir->fullname = new_name;
3293 #endif
3294
3295   return TRUE;
3296 }
3297
3298 #ifndef G_OS_WIN32
3299
3300 static gchar*
3301 find_parent_dir_fullname (gchar* dirname)
3302 {
3303   gchar *sys_orig_dir;
3304   gchar *result;
3305   gchar *sys_cwd;
3306   gchar *sys_dirname;
3307
3308   sys_orig_dir = g_get_current_dir ();
3309   sys_dirname = g_filename_from_utf8 (dirname, -1, NULL, NULL, NULL);
3310   if (!sys_dirname)
3311     {
3312       g_free (sys_orig_dir);
3313       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3314       return NULL;
3315     }
3316   
3317   if (chdir (sys_dirname) != 0 || chdir ("..") != 0)
3318     {
3319       g_free (sys_dirname);
3320       g_free (sys_orig_dir);
3321       cmpl_errno = errno;
3322       return NULL;
3323     }
3324   g_free (sys_dirname);
3325
3326   sys_cwd = g_get_current_dir ();
3327   result = g_filename_to_utf8 (sys_cwd, -1, NULL, NULL, NULL);
3328   g_free (sys_cwd);
3329
3330   if (chdir (sys_orig_dir) != 0)
3331     {
3332       cmpl_errno = errno;
3333       g_free (sys_orig_dir);
3334       return NULL;
3335     }
3336
3337   g_free (sys_orig_dir);
3338   return result;
3339 }
3340
3341 #endif
3342
3343 /**********************************************************************/
3344 /*                        Completion Operations                       */
3345 /**********************************************************************/
3346
3347 #ifdef HAVE_PWD_H
3348
3349 static PossibleCompletion*
3350 attempt_homedir_completion (gchar           *text_to_complete,
3351                             CompletionState *cmpl_state)
3352 {
3353   gint index, length;
3354
3355   if (!cmpl_state->user_dir_name_buffer &&
3356       !get_pwdb (cmpl_state))
3357     return NULL;
3358   length = strlen (text_to_complete) - 1;
3359
3360   cmpl_state->user_completion_index += 1;
3361
3362   while (cmpl_state->user_completion_index < cmpl_state->user_directories_len)
3363     {
3364       index = first_diff_index (text_to_complete + 1,
3365                                 cmpl_state->user_directories
3366                                 [cmpl_state->user_completion_index].login);
3367
3368       switch (index)
3369         {
3370         case PATTERN_MATCH:
3371           break;
3372         default:
3373           if (cmpl_state->last_valid_char < (index + 1))
3374             cmpl_state->last_valid_char = index + 1;
3375           cmpl_state->user_completion_index += 1;
3376           continue;
3377         }
3378
3379       cmpl_state->the_completion.is_a_completion = 1;
3380       cmpl_state->the_completion.is_directory = TRUE;
3381
3382       append_completion_text ("~", cmpl_state);
3383
3384       append_completion_text (cmpl_state->
3385                               user_directories[cmpl_state->user_completion_index].login,
3386                               cmpl_state);
3387
3388       return append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3389     }
3390
3391   if (text_to_complete[1]
3392       || cmpl_state->user_completion_index > cmpl_state->user_directories_len)
3393     {
3394       cmpl_state->user_completion_index = -1;
3395       return NULL;
3396     }
3397   else
3398     {
3399       cmpl_state->user_completion_index += 1;
3400       cmpl_state->the_completion.is_a_completion = 1;
3401       cmpl_state->the_completion.is_directory = TRUE;
3402
3403       return append_completion_text ("~" G_DIR_SEPARATOR_S, cmpl_state);
3404     }
3405 }
3406
3407 #endif
3408
3409 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
3410 #define FOLD(c) (tolower(c))
3411 #else
3412 #define FOLD(c) (c)
3413 #endif
3414
3415 /* returns the index (>= 0) of the first differing character,
3416  * PATTERN_MATCH if the completion matches */
3417 static gint
3418 first_diff_index (gchar *pat,
3419                   gchar *text)
3420 {
3421   gint diff = 0;
3422
3423   while (*pat && *text && FOLD (*text) == FOLD (*pat))
3424     {
3425       pat += 1;
3426       text += 1;
3427       diff += 1;
3428     }
3429
3430   if (*pat)
3431     return diff;
3432
3433   return PATTERN_MATCH;
3434 }
3435
3436 static PossibleCompletion*
3437 append_completion_text (gchar           *text,
3438                         CompletionState *cmpl_state)
3439 {
3440   gint len, i = 1;
3441
3442   if (!cmpl_state->the_completion.text)
3443     return NULL;
3444
3445   len = strlen (text) + strlen (cmpl_state->the_completion.text) + 1;
3446
3447   if (cmpl_state->the_completion.text_alloc > len)
3448     {
3449       strcat (cmpl_state->the_completion.text, text);
3450       return &cmpl_state->the_completion;
3451     }
3452
3453   while (i < len)
3454     i <<= 1;
3455
3456   cmpl_state->the_completion.text_alloc = i;
3457
3458   cmpl_state->the_completion.text = (gchar*) g_realloc (cmpl_state->the_completion.text, i);
3459
3460   if (!cmpl_state->the_completion.text)
3461     return NULL;
3462   else
3463     {
3464       strcat (cmpl_state->the_completion.text, text);
3465       return &cmpl_state->the_completion;
3466     }
3467 }
3468
3469 static CompletionDir*
3470 find_completion_dir (gchar          *text_to_complete,
3471                     gchar          **remaining_text,
3472                     CompletionState *cmpl_state)
3473 {
3474   gchar* first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
3475   CompletionDir* dir = cmpl_state->reference_dir;
3476   CompletionDir* next;
3477   *remaining_text = text_to_complete;
3478
3479   while (first_slash)
3480     {
3481       gint len = first_slash - *remaining_text;
3482       gint found = 0;
3483       gchar *found_name = NULL;         /* Quiet gcc */
3484       gint i;
3485       gchar* pat_buf = g_new (gchar, len + 1);
3486
3487       strncpy (pat_buf, *remaining_text, len);
3488       pat_buf[len] = 0;
3489
3490       for (i = 0; i < dir->sent->entry_count; i += 1)
3491         {
3492           if (dir->sent->entries[i].is_dir &&
3493              fnmatch (pat_buf, dir->sent->entries[i].entry_name,
3494                       FNMATCH_FLAGS)!= FNM_NOMATCH)
3495             {
3496               if (found)
3497                 {
3498                   g_free (pat_buf);
3499                   return dir;
3500                 }
3501               else
3502                 {
3503                   found = 1;
3504                   found_name = dir->sent->entries[i].entry_name;
3505                 }
3506             }
3507         }
3508
3509       if (!found)
3510         {
3511           /* Perhaps we are trying to open an automount directory */
3512           found_name = pat_buf;
3513         }
3514
3515       next = open_relative_dir (found_name, dir, cmpl_state);
3516       
3517       if (!next)
3518         {
3519           g_free (pat_buf);
3520           return NULL;
3521         }
3522       
3523       next->cmpl_parent = dir;
3524       
3525       dir = next;
3526       
3527       if (!correct_dir_fullname (dir))
3528         {
3529           g_free (pat_buf);
3530           return NULL;
3531         }
3532       
3533       *remaining_text = first_slash + 1;
3534       first_slash = strchr (*remaining_text, G_DIR_SEPARATOR);
3535
3536       g_free (pat_buf);
3537     }
3538
3539   return dir;
3540 }
3541
3542 static void
3543 update_cmpl (PossibleCompletion *poss,
3544              CompletionState    *cmpl_state)
3545 {
3546   gint cmpl_len;
3547
3548   if (!poss || !cmpl_is_a_completion (poss))
3549     return;
3550
3551   cmpl_len = strlen (cmpl_this_completion (poss));
3552
3553   if (cmpl_state->updated_text_alloc < cmpl_len + 1)
3554     {
3555       cmpl_state->updated_text =
3556         (gchar*)g_realloc (cmpl_state->updated_text,
3557                            cmpl_state->updated_text_alloc);
3558       cmpl_state->updated_text_alloc = 2*cmpl_len;
3559     }
3560
3561   if (cmpl_state->updated_text_len < 0)
3562     {
3563       strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3564       cmpl_state->updated_text_len = cmpl_len;
3565       cmpl_state->re_complete = cmpl_is_directory (poss);
3566     }
3567   else if (cmpl_state->updated_text_len == 0)
3568     {
3569       cmpl_state->re_complete = FALSE;
3570     }
3571   else
3572     {
3573       gint first_diff =
3574         first_diff_index (cmpl_state->updated_text,
3575                           cmpl_this_completion (poss));
3576
3577       cmpl_state->re_complete = FALSE;
3578
3579       if (first_diff == PATTERN_MATCH)
3580         return;
3581
3582       if (first_diff > cmpl_state->updated_text_len)
3583         strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3584
3585       cmpl_state->updated_text_len = first_diff;
3586       cmpl_state->updated_text[first_diff] = 0;
3587     }
3588 }
3589
3590 static PossibleCompletion*
3591 attempt_file_completion (CompletionState *cmpl_state)
3592 {
3593   gchar *pat_buf, *first_slash;
3594   CompletionDir *dir = cmpl_state->active_completion_dir;
3595
3596   dir->cmpl_index += 1;
3597
3598   if (dir->cmpl_index == dir->sent->entry_count)
3599     {
3600       if (dir->cmpl_parent == NULL)
3601         {
3602           cmpl_state->active_completion_dir = NULL;
3603
3604           return NULL;
3605         }
3606       else
3607         {
3608           cmpl_state->active_completion_dir = dir->cmpl_parent;
3609
3610           return attempt_file_completion (cmpl_state);
3611         }
3612     }
3613
3614   g_assert (dir->cmpl_text);
3615
3616   first_slash = strchr (dir->cmpl_text, G_DIR_SEPARATOR);
3617
3618   if (first_slash)
3619     {
3620       gint len = first_slash - dir->cmpl_text;
3621
3622       pat_buf = g_new (gchar, len + 1);
3623       strncpy (pat_buf, dir->cmpl_text, len);
3624       pat_buf[len] = 0;
3625     }
3626   else
3627     {
3628       gint len = strlen (dir->cmpl_text);
3629
3630       pat_buf = g_new (gchar, len + 2);
3631       strcpy (pat_buf, dir->cmpl_text);
3632       /* Don't append a * if the user entered one herself.
3633        * This way one can complete *.h and don't get matches
3634        * on any .help files, for instance.
3635        */
3636       if (strchr (pat_buf, '*') == NULL)
3637         strcpy (pat_buf + len, "*");
3638     }
3639
3640   if (first_slash)
3641     {
3642       if (dir->sent->entries[dir->cmpl_index].is_dir)
3643         {
3644           if (fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
3645                        FNMATCH_FLAGS) != FNM_NOMATCH)
3646             {
3647               CompletionDir* new_dir;
3648
3649               new_dir = open_relative_dir (dir->sent->entries[dir->cmpl_index].entry_name,
3650                                            dir, cmpl_state);
3651
3652               if (!new_dir)
3653                 {
3654                   g_free (pat_buf);
3655                   return NULL;
3656                 }
3657
3658               new_dir->cmpl_parent = dir;
3659
3660               new_dir->cmpl_index = -1;
3661               new_dir->cmpl_text = g_strdup (first_slash + 1);
3662
3663               cmpl_state->active_completion_dir = new_dir;
3664
3665               g_free (pat_buf);
3666               return attempt_file_completion (cmpl_state);
3667             }
3668           else
3669             {
3670               g_free (pat_buf);
3671               return attempt_file_completion (cmpl_state);
3672             }
3673         }
3674       else
3675         {
3676           g_free (pat_buf);
3677           return attempt_file_completion (cmpl_state);
3678         }
3679     }
3680   else
3681     {
3682       if (dir->cmpl_parent != NULL)
3683         {
3684           append_completion_text (dir->fullname +
3685                                   strlen (cmpl_state->completion_dir->fullname) + 1,
3686                                   cmpl_state);
3687           append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3688         }
3689
3690       append_completion_text (dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
3691
3692       cmpl_state->the_completion.is_a_completion =
3693         fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
3694                  FNMATCH_FLAGS) != FNM_NOMATCH;
3695
3696       cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
3697       if (dir->sent->entries[dir->cmpl_index].is_dir)
3698         append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3699
3700       g_free (pat_buf);
3701       return &cmpl_state->the_completion;
3702     }
3703 }
3704
3705 #ifdef HAVE_PWD_H
3706
3707 static gint
3708 get_pwdb (CompletionState* cmpl_state)
3709 {
3710   struct passwd *pwd_ptr;
3711   gchar* buf_ptr;
3712   gchar *utf8;
3713   gint len = 0, i, count = 0;
3714
3715   if (cmpl_state->user_dir_name_buffer)
3716     return TRUE;
3717   setpwent ();
3718
3719   while ((pwd_ptr = getpwent ()) != NULL)
3720     {
3721       utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3722       len += strlen (utf8);
3723       g_free (utf8);
3724       utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3725       len += strlen (utf8);
3726       g_free (utf8);
3727       len += 2;
3728       count += 1;
3729     }
3730
3731   setpwent ();
3732
3733   cmpl_state->user_dir_name_buffer = g_new (gchar, len);
3734   cmpl_state->user_directories = g_new (CompletionUserDir, count);
3735   cmpl_state->user_directories_len = count;
3736
3737   buf_ptr = cmpl_state->user_dir_name_buffer;
3738
3739   for (i = 0; i < count; i += 1)
3740     {
3741       pwd_ptr = getpwent ();
3742       if (!pwd_ptr)
3743         {
3744           cmpl_errno = errno;
3745           goto error;
3746         }
3747
3748       utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3749       strcpy (buf_ptr, utf8);
3750       g_free (utf8);
3751
3752       cmpl_state->user_directories[i].login = buf_ptr;
3753
3754       buf_ptr += strlen (buf_ptr);
3755       buf_ptr += 1;
3756
3757       utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3758       strcpy (buf_ptr, utf8);
3759       g_free (utf8);
3760
3761       cmpl_state->user_directories[i].homedir = buf_ptr;
3762
3763       buf_ptr += strlen (buf_ptr);
3764       buf_ptr += 1;
3765     }
3766
3767   qsort (cmpl_state->user_directories,
3768          cmpl_state->user_directories_len,
3769          sizeof (CompletionUserDir),
3770          compare_user_dir);
3771
3772   endpwent ();
3773
3774   return TRUE;
3775
3776 error:
3777
3778   if (cmpl_state->user_dir_name_buffer)
3779     g_free (cmpl_state->user_dir_name_buffer);
3780   if (cmpl_state->user_directories)
3781     g_free (cmpl_state->user_directories);
3782
3783   cmpl_state->user_dir_name_buffer = NULL;
3784   cmpl_state->user_directories = NULL;
3785
3786   return FALSE;
3787 }
3788
3789 static gint
3790 compare_user_dir (const void *a,
3791                   const void *b)
3792 {
3793   return strcmp ((((CompletionUserDir*)a))->login,
3794                  (((CompletionUserDir*)b))->login);
3795 }
3796
3797 #endif
3798
3799 static gint
3800 compare_cmpl_dir (const void *a,
3801                   const void *b)
3802 {
3803   return compare_filenames ((((CompletionDirEntry*)a))->entry_name,
3804                             (((CompletionDirEntry*)b))->entry_name);
3805 }
3806
3807 static gint
3808 cmpl_state_okay (CompletionState* cmpl_state)
3809 {
3810   return  cmpl_state && cmpl_state->reference_dir;
3811 }
3812
3813 static const gchar*
3814 cmpl_strerror (gint err)
3815 {
3816   if (err == CMPL_ERRNO_TOO_LONG)
3817     return _("Name too long");
3818   else if (err == CMPL_ERRNO_DID_NOT_CONVERT)
3819     return _("Couldn't convert filename");
3820   else
3821     return g_strerror (err);
3822 }