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