]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesel.c
Include "." and ".." in the list of entries; they are needed for correct
[~andy/gtk] / gtk / gtkfilesel.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "config.h"
28
29 #include <stdio.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #ifdef HAVE_SYS_PARAM_H
33 #include <sys/param.h>
34 #endif
35 #include <stdlib.h>
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39 #include <string.h>
40 #include <errno.h>
41 #ifdef HAVE_PWD_H
42 #include <pwd.h>
43 #endif
44
45 #include <glib.h>               /* Include early to get G_OS_WIN32 and
46                                  * G_WITH_CYGWIN */
47
48 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
49 #include <ctype.h>
50 #define STRICT
51 #include <windows.h>
52 #undef STRICT
53 #endif /* G_OS_WIN32 || G_WITH_CYGWIN */
54 #ifdef G_OS_WIN32
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   gtk_label_set_mnemonic_widget (GTK_LABEL (filesel->selection_text),
804                                  filesel->selection_entry);
805
806   if (!cmpl_state_okay (filesel->cmpl_state))
807     {
808       gchar err_buf[256];
809
810       sprintf (err_buf, _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
811
812       gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
813     }
814   else
815     {
816       gtk_file_selection_populate (filesel, "", FALSE, TRUE);
817     }
818
819   gtk_widget_grab_focus (filesel->selection_entry);
820
821   gtk_widget_pop_composite_child ();
822 }
823
824 static gchar *
825 uri_list_extract_first_uri (const gchar* uri_list)
826 {
827   const gchar *p, *q;
828   
829   g_return_val_if_fail (uri_list != NULL, NULL);
830   
831   p = uri_list;
832   /* We don't actually try to validate the URI according to RFC
833    * 2396, or even check for allowed characters - we just ignore
834    * comments and trim whitespace off the ends.  We also
835    * allow LF delimination as well as the specified CRLF.
836    *
837    * We do allow comments like specified in RFC 2483.
838    */
839   while (p)
840     {
841       if (*p != '#')
842         {
843           while (g_ascii_isspace(*p))
844             p++;
845           
846           q = p;
847           while (*q && (*q != '\n') && (*q != '\r'))
848             q++;
849           
850           if (q > p)
851             {
852               q--;
853               while (q > p && g_ascii_isspace (*q))
854                 q--;
855
856               if (q > p)
857                 return g_strndup (p, q - p + 1);
858             }
859         }
860       p = strchr (p, '\n');
861       if (p)
862         p++;
863     }
864   return NULL;
865 }
866
867 static void
868 dnd_really_drop  (GtkWidget *dialog, gint response_id, GtkFileSelection *fs)
869 {
870   gchar *filename;
871   
872   if (response_id == GTK_RESPONSE_YES)
873     {
874       filename = g_object_get_data (G_OBJECT (dialog), "gtk-fs-dnd-filename");
875
876       gtk_file_selection_set_filename (fs, filename);
877     }
878   
879   gtk_widget_destroy (dialog);
880 }
881
882
883 static void
884 filenames_dropped (GtkWidget        *widget,
885                    GdkDragContext   *context,
886                    gint              x,
887                    gint              y,
888                    GtkSelectionData *selection_data,
889                    guint             info,
890                    guint             time)
891 {
892   char *uri = NULL;
893   char *filename = NULL;
894   char *hostname;
895   char this_hostname[257];
896   int res;
897   GError *error = NULL;
898         
899   if (!selection_data->data)
900     return;
901
902   uri = uri_list_extract_first_uri ((char *)selection_data->data);
903   
904   if (!uri)
905     return;
906
907   filename = g_filename_from_uri (uri, &hostname, &error);
908   g_free (uri);
909   
910   if (!filename)
911     {
912       g_warning ("Error getting dropped filename: %s\n",
913                  error->message);
914       g_error_free (error);
915       return;
916     }
917
918   res = gethostname (this_hostname, 256);
919   this_hostname[256] = 0;
920   
921   if ((hostname == NULL) ||
922       (res == 0 && strcmp (hostname, this_hostname) == 0) ||
923       (strcmp (hostname, "localhost") == 0))
924     gtk_file_selection_set_filename (GTK_FILE_SELECTION (widget),
925                                      filename);
926   else
927     {
928       GtkWidget *dialog;
929       gchar *filename_utf8;
930
931       /* Conversion back to UTF-8 should always succeed for the result
932        * of g_filename_from_uri()
933        */
934       filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
935       g_assert (filename_utf8);
936       
937       dialog = gtk_message_dialog_new (GTK_WINDOW (widget),
938                                        GTK_DIALOG_DESTROY_WITH_PARENT,
939                                        GTK_MESSAGE_QUESTION,
940                                        GTK_BUTTONS_YES_NO,
941                                        _("The file \"%s\" resides on another machine (called %s) and may not be available to this program.\n"
942                                          "Are you sure that you want to select it?"), filename_utf8, hostname);
943       g_free (filename_utf8);
944
945       g_object_set_data_full (G_OBJECT (dialog), "gtk-fs-dnd-filename", g_strdup (filename), g_free);
946       
947       g_signal_connect_data (dialog, "response",
948                              (GCallback) dnd_really_drop, 
949                              widget, NULL, 0);
950       
951       gtk_widget_show (dialog);
952     }
953
954   g_free (hostname);
955   g_free (filename);
956 }
957
958 enum
959 {
960   TARGET_URILIST,
961   TARGET_UTF8_STRING,
962   TARGET_STRING,
963   TARGET_TEXT,
964   TARGET_COMPOUND_TEXT
965 };
966
967
968 static void
969 filenames_drag_get (GtkWidget        *widget,
970                     GdkDragContext   *context,
971                     GtkSelectionData *selection_data,
972                     guint             info,
973                     guint             time,
974                     GtkFileSelection *filesel)
975 {
976   const gchar *file;
977   gchar *uri_list;
978   char hostname[256];
979   int res;
980   GError *error;
981
982   file = gtk_file_selection_get_filename (filesel);
983
984   if (file)
985     {
986       if (info == TARGET_URILIST)
987         {
988           res = gethostname (hostname, 256);
989           
990           error = NULL;
991           uri_list = g_filename_to_uri (file, (!res)?hostname:NULL, &error);
992           if (!uri_list)
993             {
994               g_warning ("Error getting filename: %s\n",
995                          error->message);
996               g_error_free (error);
997               return;
998             }
999           
1000           gtk_selection_data_set (selection_data,
1001                                   selection_data->target, 8,
1002                                   (void *)uri_list, strlen((char *)uri_list));
1003           g_free (uri_list);
1004         }
1005       else
1006         {
1007           gchar *filename_utf8 = g_filename_to_utf8 (file, -1, NULL, NULL, NULL);
1008           g_assert (filename_utf8);
1009           gtk_selection_data_set_text (selection_data, filename_utf8, -1);
1010           g_free (filename_utf8);
1011         }
1012     }
1013 }
1014
1015 static void
1016 file_selection_setup_dnd (GtkFileSelection *filesel)
1017 {
1018   GtkWidget *eventbox;
1019   static const GtkTargetEntry drop_types[] = {
1020     { "text/uri-list", 0, TARGET_URILIST}
1021   };
1022   static gint n_drop_types = sizeof(drop_types)/sizeof(drop_types[0]);
1023   static const GtkTargetEntry drag_types[] = {
1024     { "text/uri-list", 0, TARGET_URILIST},
1025     { "UTF8_STRING", 0, TARGET_UTF8_STRING },
1026     { "STRING", 0, 0 },
1027     { "TEXT",   0, 0 }, 
1028     { "COMPOUND_TEXT", 0, 0 }
1029   };
1030   static gint n_drag_types = sizeof(drag_types)/sizeof(drag_types[0]);
1031
1032   gtk_drag_dest_set (GTK_WIDGET (filesel),
1033                      GTK_DEST_DEFAULT_ALL,
1034                      drop_types, n_drop_types,
1035                      GDK_ACTION_COPY);
1036
1037   g_signal_connect (filesel, "drag_data_received",
1038                     G_CALLBACK (filenames_dropped), NULL);
1039
1040   eventbox = gtk_widget_get_parent (filesel->selection_text);
1041   gtk_drag_source_set (eventbox,
1042                        GDK_BUTTON1_MASK,
1043                        drag_types, n_drag_types,
1044                        GDK_ACTION_COPY);
1045
1046   g_signal_connect (eventbox, "drag_data_get",
1047                     G_CALLBACK (filenames_drag_get), filesel);
1048 }
1049
1050 GtkWidget*
1051 gtk_file_selection_new (const gchar *title)
1052 {
1053   GtkFileSelection *filesel;
1054
1055   filesel = g_object_new (GTK_TYPE_FILE_SELECTION, NULL);
1056   gtk_window_set_title (GTK_WINDOW (filesel), title);
1057   gtk_dialog_set_has_separator (GTK_DIALOG (filesel), FALSE);
1058
1059   file_selection_setup_dnd (filesel);
1060   
1061   return GTK_WIDGET (filesel);
1062 }
1063
1064 void
1065 gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
1066 {
1067   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1068     
1069   /* delete, create directory, and rename */
1070   if (!filesel->fileop_c_dir) 
1071     {
1072       filesel->fileop_c_dir = gtk_button_new_with_mnemonic (_("_New Folder"));
1073       g_signal_connect (filesel->fileop_c_dir, "clicked",
1074                         G_CALLBACK (gtk_file_selection_create_dir),
1075                         filesel);
1076       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
1077                           filesel->fileop_c_dir, TRUE, TRUE, 0);
1078       gtk_widget_show (filesel->fileop_c_dir);
1079     }
1080         
1081   if (!filesel->fileop_del_file) 
1082     {
1083       filesel->fileop_del_file = gtk_button_new_with_mnemonic (_("De_lete File"));
1084       g_signal_connect (filesel->fileop_del_file, "clicked",
1085                         G_CALLBACK (gtk_file_selection_delete_file),
1086                         filesel);
1087       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
1088                           filesel->fileop_del_file, TRUE, TRUE, 0);
1089       gtk_widget_show (filesel->fileop_del_file);
1090     }
1091
1092   if (!filesel->fileop_ren_file)
1093     {
1094       filesel->fileop_ren_file = gtk_button_new_with_mnemonic (_("_Rename File"));
1095       g_signal_connect (filesel->fileop_ren_file, "clicked",
1096                         G_CALLBACK (gtk_file_selection_rename_file),
1097                         filesel);
1098       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
1099                           filesel->fileop_ren_file, TRUE, TRUE, 0);
1100       gtk_widget_show (filesel->fileop_ren_file);
1101     }
1102   
1103   gtk_file_selection_update_fileops (filesel);
1104   
1105   g_object_notify (G_OBJECT (filesel), "show_fileops");
1106 }
1107
1108 void       
1109 gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
1110 {
1111   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1112     
1113   if (filesel->fileop_ren_file)
1114     {
1115       gtk_widget_destroy (filesel->fileop_ren_file);
1116       filesel->fileop_ren_file = NULL;
1117     }
1118
1119   if (filesel->fileop_del_file)
1120     {
1121       gtk_widget_destroy (filesel->fileop_del_file);
1122       filesel->fileop_del_file = NULL;
1123     }
1124
1125   if (filesel->fileop_c_dir)
1126     {
1127       gtk_widget_destroy (filesel->fileop_c_dir);
1128       filesel->fileop_c_dir = NULL;
1129     }
1130   g_object_notify (G_OBJECT (filesel), "show_fileops");
1131 }
1132
1133
1134
1135 /**
1136  * gtk_file_selection_set_filename:
1137  * @filesel: a #GtkFileSelection.
1138  * @filename:  a string to set as the default file name.
1139  * 
1140  * Sets a default path for the file requestor. If @filename includes a
1141  * directory path, then the requestor will open with that path as its
1142  * current working directory.
1143  *
1144  * The encoding of @filename is the on-disk encoding, which
1145  * may not be UTF-8. See g_filename_from_utf8().
1146  **/
1147 void
1148 gtk_file_selection_set_filename (GtkFileSelection *filesel,
1149                                  const gchar      *filename)
1150 {
1151   gchar *buf;
1152   const char *name, *last_slash;
1153   char *filename_utf8;
1154
1155   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1156   g_return_if_fail (filename != NULL);
1157
1158   filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
1159   g_return_if_fail (filename_utf8 != NULL);
1160
1161   last_slash = strrchr (filename_utf8, G_DIR_SEPARATOR);
1162
1163   if (!last_slash)
1164     {
1165       buf = g_strdup ("");
1166       name = filename_utf8;
1167     }
1168   else
1169     {
1170       buf = g_strdup (filename_utf8);
1171       buf[last_slash - filename_utf8 + 1] = 0;
1172       name = last_slash + 1;
1173     }
1174
1175   gtk_file_selection_populate (filesel, buf, FALSE, TRUE);
1176
1177   if (filesel->selection_entry)
1178     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
1179   g_free (buf);
1180   g_object_notify (G_OBJECT (filesel), "filename");
1181
1182   g_free (filename_utf8);
1183 }
1184
1185 /**
1186  * gtk_file_selection_get_filename:
1187  * @filesel: a #GtkFileSelection
1188  * 
1189  * This function returns the selected filename in the on-disk encoding
1190  * (see g_filename_from_utf8()), which may or may not be the same as that
1191  * used by GTK+ (UTF-8). To convert to UTF-8, call g_filename_to_utf8().
1192  * The returned string points to a statically allocated buffer and
1193  * should be copied if you plan to keep it around.
1194  *
1195  * If no file is selected then the selected directory path is returned.
1196  * 
1197  * Return value: currently-selected filename in the on-disk encoding.
1198  **/
1199 G_CONST_RETURN gchar*
1200 gtk_file_selection_get_filename (GtkFileSelection *filesel)
1201 {
1202   static const gchar nothing[2] = "";
1203   static gchar something[MAXPATHLEN*2];
1204   char *sys_filename;
1205   const char *text;
1206
1207   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
1208
1209 #ifdef G_WITH_CYGWIN
1210   translate_win32_path (filesel);
1211 #endif
1212   text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
1213   if (text)
1214     {
1215       sys_filename = g_filename_from_utf8 (cmpl_completion_fullname (text, filesel->cmpl_state), -1, NULL, NULL, NULL);
1216       if (!sys_filename)
1217         return nothing;
1218       strncpy (something, sys_filename, sizeof (something));
1219       g_free (sys_filename);
1220       return something;
1221     }
1222
1223   return nothing;
1224 }
1225
1226 void
1227 gtk_file_selection_complete (GtkFileSelection *filesel,
1228                              const gchar      *pattern)
1229 {
1230   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1231   g_return_if_fail (pattern != NULL);
1232
1233   if (filesel->selection_entry)
1234     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), pattern);
1235   gtk_file_selection_populate (filesel, (gchar*) pattern, TRUE, TRUE);
1236 }
1237
1238 static void
1239 gtk_file_selection_destroy (GtkObject *object)
1240 {
1241   GtkFileSelection *filesel;
1242   GList *list;
1243   HistoryCallbackArg *callback_arg;
1244   
1245   g_return_if_fail (GTK_IS_FILE_SELECTION (object));
1246   
1247   filesel = GTK_FILE_SELECTION (object);
1248   
1249   if (filesel->fileop_dialog)
1250     {
1251       gtk_widget_destroy (filesel->fileop_dialog);
1252       filesel->fileop_dialog = NULL;
1253     }
1254   
1255   if (filesel->history_list)
1256     {
1257       list = filesel->history_list;
1258       while (list)
1259         {
1260           callback_arg = list->data;
1261           g_free (callback_arg->directory);
1262           g_free (callback_arg);
1263           list = list->next;
1264         }
1265       g_list_free (filesel->history_list);
1266       filesel->history_list = NULL;
1267     }
1268
1269   if (filesel->cmpl_state)
1270     {
1271       cmpl_free_state (filesel->cmpl_state);
1272       filesel->cmpl_state = NULL;
1273     }
1274  
1275   if (filesel->selected_names)
1276     {
1277       free_selected_names (filesel->selected_names);
1278       filesel->selected_names = NULL;
1279     } 
1280
1281   if (filesel->last_selected)
1282     {
1283       g_free (filesel->last_selected);
1284       filesel->last_selected = NULL;
1285     }
1286
1287   GTK_OBJECT_CLASS (parent_class)->destroy (object);
1288 }
1289
1290 static void
1291 gtk_file_selection_map (GtkWidget *widget)
1292 {
1293   GtkFileSelection *filesel = GTK_FILE_SELECTION (widget);
1294
1295   /* Refresh the contents */
1296   gtk_file_selection_populate (filesel, "", FALSE, FALSE);
1297   
1298   GTK_WIDGET_CLASS (parent_class)->map (widget);
1299 }
1300
1301 static void
1302 gtk_file_selection_finalize (GObject *object)
1303 {
1304   GtkFileSelection *filesel = GTK_FILE_SELECTION (object);
1305
1306   g_free (filesel->fileop_file);
1307
1308   G_OBJECT_CLASS (parent_class)->finalize (object);
1309 }
1310
1311 /* Begin file operations callbacks */
1312
1313 static void
1314 gtk_file_selection_fileop_error (GtkFileSelection *fs,
1315                                  gchar            *error_message)
1316 {
1317   GtkWidget *dialog;
1318     
1319   g_return_if_fail (error_message != NULL);
1320
1321   /* main dialog */
1322   dialog = gtk_message_dialog_new (GTK_WINDOW (fs),
1323                                    GTK_DIALOG_DESTROY_WITH_PARENT,
1324                                    GTK_MESSAGE_ERROR,
1325                                    GTK_BUTTONS_CLOSE,
1326                                    "%s", error_message);
1327
1328   /* yes, we free it */
1329   g_free (error_message);
1330
1331   gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1332  
1333   g_signal_connect_swapped (dialog, "response",
1334                             G_CALLBACK (gtk_widget_destroy),
1335                             dialog);
1336
1337   gtk_widget_show (dialog);
1338 }
1339
1340 static void
1341 gtk_file_selection_fileop_destroy (GtkWidget *widget,
1342                                    gpointer   data)
1343 {
1344   GtkFileSelection *fs = data;
1345
1346   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1347   
1348   fs->fileop_dialog = NULL;
1349 }
1350
1351 static gboolean
1352 entry_is_empty (GtkEntry *entry)
1353 {
1354   const gchar *text = gtk_entry_get_text (entry);
1355   
1356   return *text == '\0';
1357 }
1358
1359 static void
1360 gtk_file_selection_fileop_entry_changed (GtkEntry   *entry,
1361                                          GtkWidget  *button)
1362 {
1363   gtk_widget_set_sensitive (button, !entry_is_empty (entry));
1364 }
1365
1366 static void
1367 gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
1368                                          gpointer   data)
1369 {
1370   GtkFileSelection *fs = data;
1371   const gchar *dirname;
1372   gchar *path;
1373   gchar *full_path;
1374   gchar *sys_full_path;
1375   gchar *buf;
1376   GError *error = NULL;
1377   CompletionState *cmpl_state;
1378   
1379   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1380
1381   dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1382   cmpl_state = (CompletionState*) fs->cmpl_state;
1383   path = cmpl_reference_position (cmpl_state);
1384   
1385   full_path = g_strconcat (path, G_DIR_SEPARATOR_S, dirname, NULL);
1386   sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1387   if (error)
1388     {
1389       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1390         buf = g_strdup_printf (_("The folder name \"%s\" contains symbols that are not allowed in filenames"), dirname);
1391       else
1392         buf = g_strdup_printf (_("Error creating folder \"%s\": %s\n%s"), dirname, error->message,
1393                                _("You probably used symbols not allowed in filenames."));
1394       gtk_file_selection_fileop_error (fs, buf);
1395       g_error_free (error);
1396       goto out;
1397     }
1398
1399   if (mkdir (sys_full_path, 0755) < 0) 
1400     {
1401       buf = g_strdup_printf (_("Error creating folder \"%s\": %s\n"), dirname,
1402                              g_strerror (errno));
1403       gtk_file_selection_fileop_error (fs, buf);
1404     }
1405
1406  out:
1407   g_free (full_path);
1408   g_free (sys_full_path);
1409   
1410   gtk_widget_destroy (fs->fileop_dialog);
1411   gtk_file_selection_populate (fs, "", FALSE, FALSE);
1412 }
1413   
1414 static void
1415 gtk_file_selection_create_dir (GtkWidget *widget,
1416                                gpointer   data)
1417 {
1418   GtkFileSelection *fs = data;
1419   GtkWidget *label;
1420   GtkWidget *dialog;
1421   GtkWidget *vbox;
1422   GtkWidget *button;
1423
1424   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1425
1426   if (fs->fileop_dialog)
1427     return;
1428   
1429   /* main dialog */
1430   dialog = gtk_dialog_new ();
1431   fs->fileop_dialog = dialog;
1432   g_signal_connect (dialog, "destroy",
1433                     G_CALLBACK (gtk_file_selection_fileop_destroy),
1434                     fs);
1435   gtk_window_set_title (GTK_WINDOW (dialog), _("New Folder"));
1436   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1437   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
1438
1439   /* If file dialog is grabbed, grab option dialog */
1440   /* When option dialog is closed, file dialog will be grabbed again */
1441   if (GTK_WINDOW (fs)->modal)
1442       gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1443
1444   vbox = gtk_vbox_new (FALSE, 0);
1445   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1446   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1447                      FALSE, FALSE, 0);
1448   gtk_widget_show( vbox);
1449   
1450   label = gtk_label_new_with_mnemonic (_("_Folder name:"));
1451   gtk_misc_set_alignment(GTK_MISC (label), 0.0, 0.0);
1452   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1453   gtk_widget_show (label);
1454
1455   /*  The directory entry widget  */
1456   fs->fileop_entry = gtk_entry_new ();
1457   gtk_label_set_mnemonic_widget (GTK_LABEL (label), fs->fileop_entry);
1458   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
1459                       TRUE, TRUE, 5);
1460   GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1461   gtk_widget_show (fs->fileop_entry);
1462   
1463   /* buttons */
1464   button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
1465   g_signal_connect_swapped (button, "clicked",
1466                             G_CALLBACK (gtk_widget_destroy),
1467                             dialog);
1468   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1469                      button, TRUE, TRUE, 0);
1470   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1471   gtk_widget_grab_default (button);
1472   gtk_widget_show (button);
1473
1474   gtk_widget_grab_focus (fs->fileop_entry);
1475
1476   button = gtk_button_new_with_mnemonic (_("C_reate"));
1477   gtk_widget_set_sensitive (button, FALSE);
1478   g_signal_connect (button, "clicked",
1479                     G_CALLBACK (gtk_file_selection_create_dir_confirmed),
1480                     fs);
1481   g_signal_connect (fs->fileop_entry, "changed",
1482                     G_CALLBACK (gtk_file_selection_fileop_entry_changed),
1483                     button);
1484
1485   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1486                      button, TRUE, TRUE, 0);
1487   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1488   gtk_widget_show (button);
1489   
1490   gtk_widget_show (dialog);
1491 }
1492
1493 static void
1494 gtk_file_selection_delete_file_response (GtkDialog *dialog, 
1495                                          gint       response_id,
1496                                          gpointer   data)
1497 {
1498   GtkFileSelection *fs = data;
1499   CompletionState *cmpl_state;
1500   gchar *path;
1501   gchar *full_path;
1502   gchar *sys_full_path;
1503   GError *error = NULL;
1504   gchar *buf;
1505   
1506   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1507
1508   if (response_id != GTK_RESPONSE_OK)
1509     {
1510       gtk_widget_destroy (GTK_WIDGET (dialog));
1511       return;
1512     }
1513
1514   cmpl_state = (CompletionState*) fs->cmpl_state;
1515   path = cmpl_reference_position (cmpl_state);
1516   
1517   full_path = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1518   sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1519   if (error)
1520     {
1521       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1522         buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"),
1523                                fs->fileop_file);
1524       else
1525         buf = g_strdup_printf (_("Error deleting file \"%s\": %s\n%s"),
1526                                fs->fileop_file, error->message,
1527                                _("It probably contains symbols not allowed in filenames."));
1528       
1529       gtk_file_selection_fileop_error (fs, buf);
1530       g_error_free (error);
1531       goto out;
1532     }
1533
1534   if (unlink (sys_full_path) < 0) 
1535     {
1536       buf = g_strdup_printf (_("Error deleting file \"%s\": %s"),
1537                              fs->fileop_file, g_strerror (errno));
1538       gtk_file_selection_fileop_error (fs, buf);
1539     }
1540   
1541  out:
1542   g_free (full_path);
1543   g_free (sys_full_path);
1544   
1545   gtk_widget_destroy (fs->fileop_dialog);
1546   gtk_file_selection_populate (fs, "", FALSE, TRUE);
1547 }
1548
1549 static void
1550 gtk_file_selection_delete_file (GtkWidget *widget,
1551                                 gpointer   data)
1552 {
1553   GtkFileSelection *fs = data;
1554   GtkWidget *dialog;
1555   const gchar *filename;
1556   
1557   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1558
1559   if (fs->fileop_dialog)
1560     return;
1561
1562 #ifdef G_WITH_CYGWIN
1563   translate_win32_path (fs);
1564 #endif
1565
1566   filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1567   if (strlen (filename) < 1)
1568     return;
1569
1570   g_free (fs->fileop_file);
1571   fs->fileop_file = g_strdup (filename);
1572   
1573   /* main dialog */
1574   fs->fileop_dialog = dialog = 
1575     gtk_message_dialog_new (GTK_WINDOW (fs),
1576                             GTK_WINDOW (fs)->modal ? GTK_DIALOG_MODAL : 0,
1577                             GTK_MESSAGE_QUESTION,
1578                             GTK_BUTTONS_NONE,
1579                             _("Really delete file \"%s\" ?"), filename);
1580
1581   g_signal_connect (dialog, "destroy",
1582                     G_CALLBACK (gtk_file_selection_fileop_destroy),
1583                     fs);
1584   gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
1585   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1586   
1587   /* buttons */
1588   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
1589                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1590                           GTK_STOCK_DELETE, GTK_RESPONSE_OK,
1591                           NULL);
1592
1593   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
1594
1595   g_signal_connect (dialog, "response",
1596                     G_CALLBACK (gtk_file_selection_delete_file_response),
1597                     fs);
1598   
1599   gtk_widget_show (dialog);
1600 }
1601
1602 static void
1603 gtk_file_selection_rename_file_confirmed (GtkWidget *widget,
1604                                           gpointer   data)
1605 {
1606   GtkFileSelection *fs = data;
1607   gchar *buf;
1608   const gchar *file;
1609   gchar *path;
1610   gchar *new_filename;
1611   gchar *old_filename;
1612   gchar *sys_new_filename;
1613   gchar *sys_old_filename;
1614   CompletionState *cmpl_state;
1615   GError *error = NULL;
1616   
1617   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1618
1619   file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1620   cmpl_state = (CompletionState*) fs->cmpl_state;
1621   path = cmpl_reference_position (cmpl_state);
1622   
1623   new_filename = g_strconcat (path, G_DIR_SEPARATOR_S, file, NULL);
1624   old_filename = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1625
1626   sys_new_filename = g_filename_from_utf8 (new_filename, -1, NULL, NULL, &error);
1627   if (error)
1628     {
1629       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1630         buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), new_filename);
1631       else
1632         buf = g_strdup_printf (_("Error renaming file to \"%s\": %s\n%s"),
1633                                new_filename, error->message,
1634                                _("You probably used symbols not allowed in filenames."));
1635       gtk_file_selection_fileop_error (fs, buf);
1636       g_error_free (error);
1637       goto out1;
1638     }
1639
1640   sys_old_filename = g_filename_from_utf8 (old_filename, -1, NULL, NULL, &error);
1641   if (error)
1642     {
1643       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1644         buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), old_filename);
1645       else
1646         buf = g_strdup_printf (_("Error renaming file \"%s\": %s\n%s"),
1647                                old_filename, error->message,
1648                                _("It probably contains symbols not allowed in filenames."));
1649       gtk_file_selection_fileop_error (fs, buf);
1650       g_error_free (error);
1651       goto out2;
1652     }
1653   
1654   if (rename (sys_old_filename, sys_new_filename) < 0) 
1655     {
1656       buf = g_strdup_printf (_("Error renaming file \"%s\" to \"%s\": %s"),
1657                              sys_old_filename, sys_new_filename,
1658                              g_strerror (errno));
1659       gtk_file_selection_fileop_error (fs, buf);
1660       goto out2;
1661     }
1662   
1663   gtk_file_selection_populate (fs, "", FALSE, FALSE);
1664   gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), file);
1665   
1666  out2:
1667   g_free (sys_old_filename);
1668
1669  out1:
1670   g_free (new_filename);
1671   g_free (old_filename);
1672   g_free (sys_new_filename);
1673   
1674   gtk_widget_destroy (fs->fileop_dialog);
1675 }
1676   
1677 static void
1678 gtk_file_selection_rename_file (GtkWidget *widget,
1679                                 gpointer   data)
1680 {
1681   GtkFileSelection *fs = data;
1682   GtkWidget *label;
1683   GtkWidget *dialog;
1684   GtkWidget *vbox;
1685   GtkWidget *button;
1686   gchar *buf;
1687   
1688   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1689
1690   if (fs->fileop_dialog)
1691           return;
1692
1693   g_free (fs->fileop_file);
1694   fs->fileop_file = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1695   if (strlen (fs->fileop_file) < 1)
1696     return;
1697   
1698   /* main dialog */
1699   fs->fileop_dialog = dialog = gtk_dialog_new ();
1700   g_signal_connect (dialog, "destroy",
1701                     G_CALLBACK (gtk_file_selection_fileop_destroy),
1702                     fs);
1703   gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File"));
1704   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1705   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
1706
1707   /* If file dialog is grabbed, grab option dialog */
1708   /* When option dialog  closed, file dialog will be grabbed again */
1709   if (GTK_WINDOW (fs)->modal)
1710     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1711   
1712   vbox = gtk_vbox_new (FALSE, 0);
1713   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1714   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1715                       FALSE, FALSE, 0);
1716   gtk_widget_show(vbox);
1717   
1718   buf = g_strdup_printf (_("Rename file \"%s\" to:"), fs->fileop_file);
1719   label = gtk_label_new (buf);
1720   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1721   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1722   gtk_widget_show (label);
1723   g_free (buf);
1724
1725   /* New filename entry */
1726   fs->fileop_entry = gtk_entry_new ();
1727   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
1728                       TRUE, TRUE, 5);
1729   GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1730   gtk_widget_show (fs->fileop_entry);
1731   
1732   gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
1733   gtk_editable_select_region (GTK_EDITABLE (fs->fileop_entry),
1734                               0, strlen (fs->fileop_file));
1735
1736   /* buttons */
1737   button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
1738   g_signal_connect_swapped (button, "clicked",
1739                             G_CALLBACK (gtk_widget_destroy),
1740                             dialog);
1741   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1742                       button, TRUE, TRUE, 0);
1743   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1744   gtk_widget_grab_default (button);
1745   gtk_widget_show (button);
1746
1747   gtk_widget_grab_focus (fs->fileop_entry);
1748
1749   button = gtk_button_new_with_mnemonic (_("_Rename"));
1750   g_signal_connect (button, "clicked",
1751                     G_CALLBACK (gtk_file_selection_rename_file_confirmed),
1752                     fs);
1753   g_signal_connect (fs->fileop_entry, "changed",
1754                     G_CALLBACK (gtk_file_selection_fileop_entry_changed),
1755                     button);
1756
1757   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1758                       button, TRUE, TRUE, 0);
1759   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1760   gtk_widget_show (button);
1761   
1762   gtk_widget_show (dialog);
1763 }
1764
1765 static gint
1766 gtk_file_selection_insert_text (GtkWidget   *widget,
1767                                 const gchar *new_text,
1768                                 gint         new_text_length,
1769                                 gint        *position,
1770                                 gpointer     user_data)
1771 {
1772   gchar *filename;
1773
1774   filename = g_filename_from_utf8 (new_text, new_text_length, NULL, NULL, NULL);
1775
1776   if (!filename)
1777     {
1778       gdk_display_beep (gtk_widget_get_display (widget));
1779       g_signal_stop_emission_by_name (widget, "insert_text");
1780       return FALSE;
1781     }
1782   
1783   g_free (filename);
1784   
1785   return TRUE;
1786 }
1787
1788 static void
1789 gtk_file_selection_update_fileops (GtkFileSelection *fs)
1790 {
1791   gboolean sensitive;
1792
1793   if (!fs->selection_entry)
1794     return;
1795
1796   sensitive = !entry_is_empty (GTK_ENTRY (fs->selection_entry));
1797
1798   if (fs->fileop_del_file)
1799     gtk_widget_set_sensitive (fs->fileop_del_file, sensitive);
1800   
1801   if (fs->fileop_ren_file)
1802     gtk_widget_set_sensitive (fs->fileop_ren_file, sensitive);
1803 }
1804
1805 static gint
1806 gtk_file_selection_key_press (GtkWidget   *widget,
1807                               GdkEventKey *event,
1808                               gpointer     user_data)
1809 {
1810   GtkFileSelection *fs;
1811   char *text;
1812
1813   g_return_val_if_fail (widget != NULL, FALSE);
1814   g_return_val_if_fail (event != NULL, FALSE);
1815
1816   if ((event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab) &&
1817       (event->state & gtk_accelerator_get_default_mod_mask ()) == 0)
1818     {
1819       fs = GTK_FILE_SELECTION (user_data);
1820 #ifdef G_WITH_CYGWIN
1821       translate_win32_path (fs);
1822 #endif
1823       text = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1824
1825       gtk_file_selection_populate (fs, text, TRUE, TRUE);
1826
1827       g_free (text);
1828
1829       return TRUE;
1830     }
1831
1832   return FALSE;
1833 }
1834
1835 static void
1836 gtk_file_selection_history_callback (GtkWidget *widget,
1837                                      gpointer   data)
1838 {
1839   GtkFileSelection *fs = data;
1840   HistoryCallbackArg *callback_arg;
1841   GList *list;
1842
1843   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1844
1845   list = fs->history_list;
1846   
1847   while (list) {
1848     callback_arg = list->data;
1849     
1850     if (callback_arg->menu_item == widget)
1851       {
1852         gtk_file_selection_populate (fs, callback_arg->directory, FALSE, FALSE);
1853         break;
1854       }
1855     
1856     list = list->next;
1857   }
1858 }
1859
1860 static void 
1861 gtk_file_selection_update_history_menu (GtkFileSelection *fs,
1862                                         gchar            *current_directory)
1863 {
1864   HistoryCallbackArg *callback_arg;
1865   GtkWidget *menu_item;
1866   GList *list;
1867   gchar *current_dir;
1868   gint dir_len;
1869   gint i;
1870   
1871   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1872   g_return_if_fail (current_directory != NULL);
1873   
1874   list = fs->history_list;
1875
1876   if (fs->history_menu) 
1877     {
1878       while (list) {
1879         callback_arg = list->data;
1880         g_free (callback_arg->directory);
1881         g_free (callback_arg);
1882         list = list->next;
1883       }
1884       g_list_free (fs->history_list);
1885       fs->history_list = NULL;
1886       
1887       gtk_widget_destroy (fs->history_menu);
1888     }
1889   
1890   fs->history_menu = gtk_menu_new ();
1891
1892   current_dir = g_strdup (current_directory);
1893
1894   dir_len = strlen (current_dir);
1895
1896   for (i = dir_len; i >= 0; i--)
1897     {
1898       /* the i == dir_len is to catch the full path for the first 
1899        * entry. */
1900       if ( (current_dir[i] == G_DIR_SEPARATOR) || (i == dir_len))
1901         {
1902           /* another small hack to catch the full path */
1903           if (i != dir_len) 
1904                   current_dir[i + 1] = '\0';
1905 #ifdef G_WITH_CYGWIN
1906           if (!strcmp (current_dir, "//"))
1907             continue;
1908 #endif
1909           menu_item = gtk_menu_item_new_with_label (current_dir);
1910           
1911           callback_arg = g_new (HistoryCallbackArg, 1);
1912           callback_arg->menu_item = menu_item;
1913           
1914           /* since the autocompletion gets confused if you don't 
1915            * supply a trailing '/' on a dir entry, set the full
1916            * (current) path to "" which just refreshes the filesel */
1917           if (dir_len == i)
1918             {
1919               callback_arg->directory = g_strdup ("");
1920             }
1921           else
1922             {
1923               callback_arg->directory = g_strdup (current_dir);
1924             }
1925           
1926           fs->history_list = g_list_append (fs->history_list, callback_arg);
1927           
1928           g_signal_connect (menu_item, "activate",
1929                             G_CALLBACK (gtk_file_selection_history_callback),
1930                             fs);
1931           gtk_menu_shell_append (GTK_MENU_SHELL (fs->history_menu), menu_item);
1932           gtk_widget_show (menu_item);
1933         }
1934     }
1935
1936   gtk_option_menu_set_menu (GTK_OPTION_MENU (fs->history_pulldown), 
1937                             fs->history_menu);
1938   g_free (current_dir);
1939 }
1940
1941 static gchar *
1942 get_real_filename (gchar    *filename,
1943                    gboolean  free_old)
1944 {
1945 #ifdef G_WITH_CYGWIN
1946   /* Check to see if the selection was a drive selector */
1947   if (isalpha (filename[0]) && (filename[1] == ':'))
1948     {
1949       gchar temp_filename[MAX_PATH];
1950       int len;
1951
1952       cygwin_conv_to_posix_path (filename, temp_filename);
1953
1954       /* we need trailing '/'. */
1955       len = strlen (temp_filename);
1956       if (len > 0 && temp_filename[len-1] != '/')
1957         {
1958           temp_filename[len]   = '/';
1959           temp_filename[len+1] = '\0';
1960         }
1961       
1962       if (free_old)
1963         g_free (filename);
1964
1965       return g_strdup (temp_filename);
1966     }
1967 #endif /* G_WITH_CYGWIN */
1968   return filename;
1969 }
1970
1971 static void
1972 gtk_file_selection_file_activate (GtkTreeView       *tree_view,
1973                                   GtkTreePath       *path,
1974                                   GtkTreeViewColumn *column,
1975                                   gpointer           user_data)
1976 {
1977   GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
1978   GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
1979   GtkTreeIter iter;  
1980   gchar *filename;
1981   
1982   gtk_tree_model_get_iter (model, &iter, path);
1983   gtk_tree_model_get (model, &iter, FILE_COLUMN, &filename, -1);
1984   filename = get_real_filename (filename, TRUE);
1985   gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1986   gtk_button_clicked (GTK_BUTTON (fs->ok_button));
1987
1988   g_free (filename);
1989 }
1990
1991 static void
1992 gtk_file_selection_dir_activate (GtkTreeView       *tree_view,
1993                                  GtkTreePath       *path,
1994                                  GtkTreeViewColumn *column,
1995                                  gpointer           user_data)
1996 {
1997   GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
1998   GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
1999   GtkTreeIter iter;
2000   gchar *filename;
2001
2002   gtk_tree_model_get_iter (model, &iter, path);
2003   gtk_tree_model_get (model, &iter, DIR_COLUMN, &filename, -1);
2004   filename = get_real_filename (filename, TRUE);
2005   gtk_file_selection_populate (fs, filename, FALSE, FALSE);
2006   g_free (filename);
2007 }
2008
2009 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
2010
2011 static void
2012 win32_gtk_add_drives_to_dir_list (GtkListStore *model)
2013 {
2014   gchar *textPtr;
2015   gchar buffer[128];
2016   char formatBuffer[128];
2017   GtkTreeIter iter;
2018
2019   /* Get the drives string */
2020   GetLogicalDriveStrings (sizeof (buffer), buffer);
2021
2022   /* Add the drives as necessary */
2023   textPtr = buffer;
2024   while (*textPtr != '\0')
2025     {
2026       /* Ignore floppies (?) */
2027       if ((tolower (textPtr[0]) != 'a') && (tolower (textPtr[0]) != 'b'))
2028         {
2029           /* Build the actual displayable string */
2030           sprintf (formatBuffer, "%c:\\", toupper (textPtr[0]));
2031
2032           /* Add to the list */
2033           gtk_list_store_append (model, &iter);
2034           gtk_list_store_set (model, &iter, DIR_COLUMN, formatBuffer, -1);
2035         }
2036       textPtr += (strlen (textPtr) + 1);
2037     }
2038 }
2039 #endif
2040
2041 static gchar *
2042 escape_underscores (const gchar *str)
2043 {
2044   GString *result = g_string_new (NULL);
2045   while (*str)
2046     {
2047       if (*str == '_')
2048         g_string_append_c (result, '_');
2049
2050       g_string_append_c (result, *str);
2051       str++;
2052     }
2053
2054   return g_string_free (result, FALSE);
2055 }
2056
2057 static void
2058 gtk_file_selection_populate (GtkFileSelection *fs,
2059                              gchar            *rel_path,
2060                              gboolean          try_complete,
2061                              gboolean          reset_entry)
2062 {
2063   CompletionState *cmpl_state;
2064   PossibleCompletion* poss;
2065   GtkTreeIter iter;
2066   GtkListStore *dir_model;
2067   GtkListStore *file_model;
2068   gchar* filename;
2069   gchar* rem_path = rel_path;
2070   gchar* sel_text;
2071   gint did_recurse = FALSE;
2072   gint possible_count = 0;
2073   gint selection_index = -1;
2074   
2075   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
2076
2077   cmpl_state = (CompletionState*) fs->cmpl_state;
2078   poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
2079
2080   if (!cmpl_state_okay (cmpl_state))
2081     {
2082       /* Something went wrong. */
2083       gtk_file_selection_abort (fs);
2084       return;
2085     }
2086
2087   g_assert (cmpl_state->reference_dir);
2088
2089   dir_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->dir_list)));
2090   file_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->file_list)));
2091
2092   gtk_list_store_clear (dir_model);
2093   gtk_list_store_clear (file_model);
2094
2095   /* Set the dir list to include ./ and ../ */
2096   gtk_list_store_append (dir_model, &iter);
2097   gtk_list_store_set (dir_model, &iter, DIR_COLUMN, "." G_DIR_SEPARATOR_S, -1);
2098   gtk_list_store_append (dir_model, &iter);
2099   gtk_list_store_set (dir_model, &iter, DIR_COLUMN, ".." G_DIR_SEPARATOR_S, -1);
2100
2101   while (poss)
2102     {
2103       if (cmpl_is_a_completion (poss))
2104         {
2105           possible_count += 1;
2106
2107           filename = cmpl_this_completion (poss);
2108
2109           if (cmpl_is_directory (poss))
2110             {
2111               if (strcmp (filename, "." G_DIR_SEPARATOR_S) != 0 &&
2112                   strcmp (filename, ".." G_DIR_SEPARATOR_S) != 0)
2113                 {
2114                   gtk_list_store_append (dir_model, &iter);
2115                   gtk_list_store_set (dir_model, &iter, DIR_COLUMN, filename, -1);
2116                 }
2117             }
2118           else
2119             {
2120               gtk_list_store_append (file_model, &iter);
2121               gtk_list_store_set (file_model, &iter, DIR_COLUMN, filename, -1);
2122             }
2123         }
2124
2125       poss = cmpl_next_completion (cmpl_state);
2126     }
2127
2128 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
2129   /* For Windows, add drives as potential selections */
2130   win32_gtk_add_drives_to_dir_list (dir_model);
2131 #endif
2132
2133   /* File lists are set. */
2134
2135   g_assert (cmpl_state->reference_dir);
2136
2137   if (try_complete)
2138     {
2139
2140       /* User is trying to complete filenames, so advance the user's input
2141        * string to the updated_text, which is the common leading substring
2142        * of all possible completions, and if its a directory attempt
2143        * attempt completions in it. */
2144
2145       if (cmpl_updated_text (cmpl_state)[0])
2146         {
2147
2148           if (cmpl_updated_dir (cmpl_state))
2149             {
2150               gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
2151
2152               did_recurse = TRUE;
2153
2154               gtk_file_selection_populate (fs, dir_name, TRUE, TRUE);
2155
2156               g_free (dir_name);
2157             }
2158           else
2159             {
2160               if (fs->selection_entry)
2161                       gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
2162                                           cmpl_updated_text (cmpl_state));
2163             }
2164         }
2165       else
2166         {
2167           selection_index = cmpl_last_valid_char (cmpl_state) -
2168                             (strlen (rel_path) - strlen (rem_path));
2169           if (fs->selection_entry)
2170             gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
2171         }
2172     }
2173   else if (reset_entry)
2174     {
2175       if (fs->selection_entry)
2176         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
2177     }
2178
2179   if (!did_recurse)
2180     {
2181       if (fs->selection_entry)
2182         gtk_editable_set_position (GTK_EDITABLE (fs->selection_entry),
2183                                    selection_index);
2184
2185       if (fs->selection_entry)
2186         {
2187           char *escaped = escape_underscores (cmpl_reference_position (cmpl_state));
2188           sel_text = g_strconcat (_("_Selection: "), escaped, NULL);
2189           g_free (escaped);
2190
2191           gtk_label_set_text_with_mnemonic (GTK_LABEL (fs->selection_text), sel_text);
2192           g_free (sel_text);
2193         }
2194
2195       if (fs->history_pulldown) 
2196         {
2197           gtk_file_selection_update_history_menu (fs, cmpl_reference_position (cmpl_state));
2198         }
2199       
2200     }
2201 }
2202
2203 static void
2204 gtk_file_selection_abort (GtkFileSelection *fs)
2205 {
2206   gchar err_buf[256];
2207
2208   sprintf (err_buf, _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
2209
2210   /*  BEEP gdk_beep();  */
2211
2212   if (fs->selection_entry)
2213     gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
2214 }
2215
2216 /**
2217  * gtk_file_selection_set_select_multiple:
2218  * @filesel: a #GtkFileSelection
2219  * @select_multiple: whether or not the user is allowed to select multiple
2220  * files in the file list.
2221  *
2222  * Sets whether the user is allowed to select multiple files in the file list.
2223  * Use gtk_file_selection_get_selections () to get the list of selected files.
2224  **/
2225 void
2226 gtk_file_selection_set_select_multiple (GtkFileSelection *filesel,
2227                                         gboolean          select_multiple)
2228 {
2229   GtkTreeSelection *sel;
2230   GtkSelectionMode mode;
2231
2232   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
2233
2234   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
2235
2236   mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE;
2237
2238   if (mode != gtk_tree_selection_get_mode (sel))
2239     {
2240       gtk_tree_selection_set_mode (sel, mode);
2241
2242       g_object_notify (G_OBJECT (filesel), "select-multiple");
2243     }
2244 }
2245
2246 /**
2247  * gtk_file_selection_get_select_multiple:
2248  * @filesel: a #GtkFileSelection
2249  *
2250  * Determines whether or not the user is allowed to select multiple files in
2251  * the file list. See gtk_file_selection_set_select_multiple().
2252  *
2253  * Return value: %TRUE if the user is allowed to select multiple files in the
2254  * file list
2255  **/
2256 gboolean
2257 gtk_file_selection_get_select_multiple (GtkFileSelection *filesel)
2258 {
2259   GtkTreeSelection *sel;
2260
2261   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), FALSE);
2262
2263   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
2264   return (gtk_tree_selection_get_mode (sel) == GTK_SELECTION_MULTIPLE);
2265 }
2266
2267 static void
2268 multiple_changed_foreach (GtkTreeModel *model,
2269                           GtkTreePath  *path,
2270                           GtkTreeIter  *iter,
2271                           gpointer      data)
2272 {
2273   GPtrArray *names = data;
2274   gchar *filename;
2275
2276   gtk_tree_model_get (model, iter, FILE_COLUMN, &filename, -1);
2277
2278   g_ptr_array_add (names, filename);
2279 }
2280
2281 static void
2282 free_selected_names (GPtrArray *names)
2283 {
2284   gint i;
2285
2286   for (i = 0; i < names->len; i++)
2287     g_free (g_ptr_array_index (names, i));
2288
2289   g_ptr_array_free (names, TRUE);
2290 }
2291
2292 static void
2293 gtk_file_selection_file_changed (GtkTreeSelection *selection,
2294                                  gpointer          user_data)
2295 {
2296   GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
2297   GPtrArray *new_names;
2298   gchar *filename;
2299   const gchar *entry;
2300   gint index = -1;
2301
2302   new_names = g_ptr_array_sized_new (8);
2303
2304   gtk_tree_selection_selected_foreach (selection,
2305                                        multiple_changed_foreach,
2306                                        new_names);
2307
2308   /* nothing selected */
2309   if (new_names->len == 0)
2310     {
2311       g_ptr_array_free (new_names, TRUE);
2312
2313       if (fs->selected_names != NULL)
2314         {
2315           free_selected_names (fs->selected_names);
2316           fs->selected_names = NULL;
2317         }
2318
2319       goto maybe_clear_entry;
2320     }
2321
2322   if (new_names->len != 1)
2323     {
2324       GPtrArray *old_names = fs->selected_names;
2325
2326       if (old_names != NULL)
2327         {
2328           /* A common case is selecting a range of files from top to bottom,
2329            * so quickly check for that to avoid looping over the entire list
2330            */
2331           if (compare_filenames (g_ptr_array_index (old_names, old_names->len - 1),
2332                                  g_ptr_array_index (new_names, new_names->len - 1)) != 0)
2333             index = new_names->len - 1;
2334           else
2335             {
2336               gint i = 0, j = 0, cmp;
2337
2338               /* do a quick diff, stopping at the first file not in the
2339                * old list
2340                */
2341               while (i < old_names->len && j < new_names->len)
2342                 {
2343                   cmp = compare_filenames (g_ptr_array_index (old_names, i),
2344                                            g_ptr_array_index (new_names, j));
2345                   if (cmp < 0)
2346                     {
2347                       i++;
2348                     }
2349                   else if (cmp == 0)
2350                     {
2351                       i++;
2352                       j++;
2353                     }
2354                   else if (cmp > 0)
2355                     {
2356                       index = j;
2357                       break;
2358                     }
2359                 }
2360
2361               /* we ran off the end of the old list */
2362               if (index == -1 && i < new_names->len)
2363                 index = j;
2364             }
2365         }
2366       else
2367         {
2368           /* A phantom anchor still exists at the point where the last item
2369            * was selected, which is used for subsequent range selections.
2370            * So search up from there.
2371            */
2372           if (fs->last_selected &&
2373               compare_filenames (fs->last_selected,
2374                                  g_ptr_array_index (new_names, 0)) == 0)
2375             index = new_names->len - 1;
2376           else
2377             index = 0;
2378         }
2379     }
2380   else
2381     index = 0;
2382
2383   if (fs->selected_names != NULL)
2384     free_selected_names (fs->selected_names);
2385
2386   fs->selected_names = new_names;
2387
2388   if (index != -1)
2389     {
2390       if (fs->last_selected != NULL)
2391         g_free (fs->last_selected);
2392
2393       fs->last_selected = g_strdup (g_ptr_array_index (new_names, index));
2394       filename = get_real_filename (fs->last_selected, FALSE);
2395
2396       gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
2397
2398       if (filename != fs->last_selected)
2399         g_free (filename);
2400       
2401       return;
2402     }
2403   
2404 maybe_clear_entry:
2405
2406   entry = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
2407   if ((entry != NULL) && (fs->last_selected != NULL) &&
2408       (compare_filenames (entry, fs->last_selected) == 0))
2409     gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
2410 }
2411
2412 /**
2413  * gtk_file_selection_get_selections:
2414  * @filesel: a #GtkFileSelection
2415  *
2416  * Retrieves the list of file selections the user has made in the dialog box.
2417  * This function is intended for use when the user can select multiple files
2418  * in the file list. The first file in the list is equivalent to what
2419  * gtk_file_selection_get_filename() would return.
2420  *
2421  * The filenames are in the encoding of g_filename_from_utf8(), which may or 
2422  * may not be the same as that used by GTK+ (UTF-8). To convert to UTF-8, call
2423  * g_filename_to_utf8() on each string.
2424  *
2425  * Return value: a newly-allocated %NULL-terminated array of strings. Use
2426  * g_strfreev() to free it.
2427  **/
2428 gchar **
2429 gtk_file_selection_get_selections (GtkFileSelection *filesel)
2430 {
2431   GPtrArray *names;
2432   gchar **selections;
2433   gchar *filename, *dirname;
2434   gchar *current, *buf;
2435   gint i, count;
2436   gboolean unselected_entry;
2437
2438   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), NULL);
2439
2440   filename = g_strdup (gtk_file_selection_get_filename (filesel));
2441
2442   if (strlen (filename) == 0)
2443     {
2444       g_free (filename);
2445       return NULL;
2446     }
2447
2448   names = filesel->selected_names;
2449
2450   if (names != NULL)
2451     selections = g_new (gchar *, names->len + 2);
2452   else
2453     selections = g_new (gchar *, 2);
2454
2455   count = 0;
2456   unselected_entry = TRUE;
2457
2458   if (names != NULL)
2459     {
2460       dirname = g_path_get_dirname (filename);
2461
2462       for (i = 0; i < names->len; i++)
2463         {
2464           buf = g_filename_from_utf8 (g_ptr_array_index (names, i), -1,
2465                                       NULL, NULL, NULL);
2466           current = g_build_filename (dirname, buf, NULL);
2467           g_free (buf);
2468
2469           selections[count++] = current;
2470
2471           if (unselected_entry && compare_filenames (current, filename) == 0)
2472             unselected_entry = FALSE;
2473         }
2474
2475       g_free (dirname);
2476     }
2477
2478   if (unselected_entry)
2479     selections[count++] = filename;
2480   else
2481     g_free (filename);
2482
2483   selections[count] = NULL;
2484
2485   return selections;
2486 }
2487
2488 /**********************************************************************/
2489 /*                        External Interface                          */
2490 /**********************************************************************/
2491
2492 /* The four completion state selectors
2493  */
2494 static gchar*
2495 cmpl_updated_text (CompletionState *cmpl_state)
2496 {
2497   return cmpl_state->updated_text;
2498 }
2499
2500 static gboolean
2501 cmpl_updated_dir (CompletionState *cmpl_state)
2502 {
2503   return cmpl_state->re_complete;
2504 }
2505
2506 static gchar*
2507 cmpl_reference_position (CompletionState *cmpl_state)
2508 {
2509   return cmpl_state->reference_dir->fullname;
2510 }
2511
2512 static gint
2513 cmpl_last_valid_char (CompletionState *cmpl_state)
2514 {
2515   return cmpl_state->last_valid_char;
2516 }
2517
2518 static gchar*
2519 cmpl_completion_fullname (const gchar     *text,
2520                           CompletionState *cmpl_state)
2521 {
2522   static const char nothing[2] = "";
2523
2524   if (!cmpl_state_okay (cmpl_state))
2525     {
2526       return nothing;
2527     }
2528   else if (g_path_is_absolute (text))
2529     {
2530       strcpy (cmpl_state->updated_text, text);
2531     }
2532 #ifdef HAVE_PWD_H
2533   else if (text[0] == '~')
2534     {
2535       CompletionDir* dir;
2536       char* slash;
2537
2538       dir = open_user_dir (text, cmpl_state);
2539
2540       if (!dir)
2541         {
2542           /* spencer says just return ~something, so
2543            * for now just do it. */
2544           strcpy (cmpl_state->updated_text, text);
2545         }
2546       else
2547         {
2548
2549           strcpy (cmpl_state->updated_text, dir->fullname);
2550
2551           slash = strchr (text, G_DIR_SEPARATOR);
2552
2553           if (slash)
2554             strcat (cmpl_state->updated_text, slash);
2555         }
2556     }
2557 #endif
2558   else
2559     {
2560       strcpy (cmpl_state->updated_text, cmpl_state->reference_dir->fullname);
2561       if (cmpl_state->updated_text[strlen (cmpl_state->updated_text) - 1] != G_DIR_SEPARATOR)
2562         strcat (cmpl_state->updated_text, G_DIR_SEPARATOR_S);
2563       strcat (cmpl_state->updated_text, text);
2564     }
2565
2566   return cmpl_state->updated_text;
2567 }
2568
2569 /* The three completion selectors
2570  */
2571 static gchar*
2572 cmpl_this_completion (PossibleCompletion* pc)
2573 {
2574   return pc->text;
2575 }
2576
2577 static gboolean
2578 cmpl_is_directory (PossibleCompletion* pc)
2579 {
2580   return pc->is_directory;
2581 }
2582
2583 static gint
2584 cmpl_is_a_completion (PossibleCompletion* pc)
2585 {
2586   return pc->is_a_completion;
2587 }
2588
2589 /**********************************************************************/
2590 /*                       Construction, deletion                       */
2591 /**********************************************************************/
2592
2593 static CompletionState*
2594 cmpl_init_state (void)
2595 {
2596   gchar *sys_getcwd_buf;
2597   gchar *utf8_cwd;
2598   CompletionState *new_state;
2599
2600   new_state = g_new (CompletionState, 1);
2601
2602   /* g_get_current_dir() returns a string in the "system" charset */
2603   sys_getcwd_buf = g_get_current_dir ();
2604   utf8_cwd = g_filename_to_utf8 (sys_getcwd_buf, -1, NULL, NULL, NULL);
2605   g_free (sys_getcwd_buf);
2606
2607 tryagain:
2608
2609   new_state->reference_dir = NULL;
2610   new_state->completion_dir = NULL;
2611   new_state->active_completion_dir = NULL;
2612   new_state->directory_storage = NULL;
2613   new_state->directory_sent_storage = NULL;
2614   new_state->last_valid_char = 0;
2615   new_state->updated_text = g_new (gchar, MAXPATHLEN);
2616   new_state->updated_text_alloc = MAXPATHLEN;
2617   new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
2618   new_state->the_completion.text_alloc = MAXPATHLEN;
2619   new_state->user_dir_name_buffer = NULL;
2620   new_state->user_directories = NULL;
2621
2622   new_state->reference_dir = open_dir (utf8_cwd, new_state);
2623
2624   if (!new_state->reference_dir)
2625     {
2626       /* Directories changing from underneath us, grumble */
2627       strcpy (utf8_cwd, G_DIR_SEPARATOR_S);
2628       goto tryagain;
2629     }
2630
2631   g_free (utf8_cwd);
2632   return new_state;
2633 }
2634
2635 static void
2636 cmpl_free_dir_list (GList* dp0)
2637 {
2638   GList *dp = dp0;
2639
2640   while (dp)
2641     {
2642       free_dir (dp->data);
2643       dp = dp->next;
2644     }
2645
2646   g_list_free (dp0);
2647 }
2648
2649 static void
2650 cmpl_free_dir_sent_list (GList* dp0)
2651 {
2652   GList *dp = dp0;
2653
2654   while (dp)
2655     {
2656       free_dir_sent (dp->data);
2657       dp = dp->next;
2658     }
2659
2660   g_list_free (dp0);
2661 }
2662
2663 static void
2664 cmpl_free_state (CompletionState* cmpl_state)
2665 {
2666   g_return_if_fail (cmpl_state != NULL);
2667
2668   cmpl_free_dir_list (cmpl_state->directory_storage);
2669   cmpl_free_dir_sent_list (cmpl_state->directory_sent_storage);
2670
2671   if (cmpl_state->user_dir_name_buffer)
2672     g_free (cmpl_state->user_dir_name_buffer);
2673   if (cmpl_state->user_directories)
2674     g_free (cmpl_state->user_directories);
2675   if (cmpl_state->the_completion.text)
2676     g_free (cmpl_state->the_completion.text);
2677   if (cmpl_state->updated_text)
2678     g_free (cmpl_state->updated_text);
2679
2680   g_free (cmpl_state);
2681 }
2682
2683 static void
2684 free_dir (CompletionDir* dir)
2685 {
2686   g_free (dir->cmpl_text);
2687   g_free (dir->fullname);
2688   g_free (dir);
2689 }
2690
2691 static void
2692 free_dir_sent (CompletionDirSent* sent)
2693 {
2694   gint i;
2695   for (i = 0; i < sent->entry_count; i++)
2696     g_free (sent->entries[i].entry_name);
2697   g_free (sent->entries);
2698   g_free (sent);
2699 }
2700
2701 static void
2702 prune_memory_usage (CompletionState *cmpl_state)
2703 {
2704   GList* cdsl = cmpl_state->directory_sent_storage;
2705   GList* cdl = cmpl_state->directory_storage;
2706   GList* cdl0 = cdl;
2707   gint len = 0;
2708
2709   for (; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
2710     cdsl = cdsl->next;
2711
2712   if (cdsl)
2713     {
2714       cmpl_free_dir_sent_list (cdsl->next);
2715       cdsl->next = NULL;
2716     }
2717
2718   cmpl_state->directory_storage = NULL;
2719   while (cdl)
2720     {
2721       if (cdl->data == cmpl_state->reference_dir)
2722         cmpl_state->directory_storage = g_list_prepend (NULL, cdl->data);
2723       else
2724         free_dir (cdl->data);
2725       cdl = cdl->next;
2726     }
2727
2728   g_list_free (cdl0);
2729 }
2730
2731 /**********************************************************************/
2732 /*                        The main entrances.                         */
2733 /**********************************************************************/
2734
2735 static PossibleCompletion*
2736 cmpl_completion_matches (gchar           *text_to_complete,
2737                          gchar          **remaining_text,
2738                          CompletionState *cmpl_state)
2739 {
2740   gchar* first_slash;
2741   PossibleCompletion *poss;
2742
2743   prune_memory_usage (cmpl_state);
2744
2745   g_assert (text_to_complete != NULL);
2746
2747   cmpl_state->user_completion_index = -1;
2748   cmpl_state->last_completion_text = text_to_complete;
2749   cmpl_state->the_completion.text[0] = 0;
2750   cmpl_state->last_valid_char = 0;
2751   cmpl_state->updated_text_len = -1;
2752   cmpl_state->updated_text[0] = 0;
2753   cmpl_state->re_complete = FALSE;
2754
2755 #ifdef HAVE_PWD_H
2756   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2757
2758   if (text_to_complete[0] == '~' && !first_slash)
2759     {
2760       /* Text starts with ~ and there is no slash, show all the
2761        * home directory completions.
2762        */
2763       poss = attempt_homedir_completion (text_to_complete, cmpl_state);
2764
2765       update_cmpl (poss, cmpl_state);
2766
2767       return poss;
2768     }
2769 #endif
2770   cmpl_state->reference_dir =
2771     open_ref_dir (text_to_complete, remaining_text, cmpl_state);
2772
2773   if (!cmpl_state->reference_dir)
2774     return NULL;
2775
2776   cmpl_state->completion_dir =
2777     find_completion_dir (*remaining_text, remaining_text, cmpl_state);
2778
2779   cmpl_state->last_valid_char = *remaining_text - text_to_complete;
2780
2781   if (!cmpl_state->completion_dir)
2782     return NULL;
2783
2784   cmpl_state->completion_dir->cmpl_index = -1;
2785   cmpl_state->completion_dir->cmpl_parent = NULL;
2786   cmpl_state->completion_dir->cmpl_text = g_strdup (*remaining_text);
2787
2788   cmpl_state->active_completion_dir = cmpl_state->completion_dir;
2789
2790   cmpl_state->reference_dir = cmpl_state->completion_dir;
2791
2792   poss = attempt_file_completion (cmpl_state);
2793
2794   update_cmpl (poss, cmpl_state);
2795
2796   return poss;
2797 }
2798
2799 static PossibleCompletion*
2800 cmpl_next_completion (CompletionState* cmpl_state)
2801 {
2802   PossibleCompletion* poss = NULL;
2803
2804   cmpl_state->the_completion.text[0] = 0;
2805
2806 #ifdef HAVE_PWD_H
2807   if (cmpl_state->user_completion_index >= 0)
2808     poss = attempt_homedir_completion (cmpl_state->last_completion_text, cmpl_state);
2809   else
2810     poss = attempt_file_completion (cmpl_state);
2811 #else
2812   poss = attempt_file_completion (cmpl_state);
2813 #endif
2814
2815   update_cmpl (poss, cmpl_state);
2816
2817   return poss;
2818 }
2819
2820 /**********************************************************************/
2821 /*                       Directory Operations                         */
2822 /**********************************************************************/
2823
2824 /* Open the directory where completion will begin from, if possible. */
2825 static CompletionDir*
2826 open_ref_dir (gchar           *text_to_complete,
2827               gchar          **remaining_text,
2828               CompletionState *cmpl_state)
2829 {
2830   gchar* first_slash;
2831   CompletionDir *new_dir;
2832
2833   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2834
2835 #ifdef G_WITH_CYGWIN
2836   if (text_to_complete[0] == '/' && text_to_complete[1] == '/')
2837     {
2838       char root_dir[5];
2839       sprintf (root_dir, "//%c", text_to_complete[2]);
2840
2841       new_dir = open_dir (root_dir, cmpl_state);
2842
2843       if (new_dir) {
2844         *remaining_text = text_to_complete + 4;
2845       }
2846     }
2847 #else
2848   if (FALSE)
2849     ;
2850 #endif
2851 #ifdef HAVE_PWD_H
2852   else if (text_to_complete[0] == '~')
2853     {
2854       new_dir = open_user_dir (text_to_complete, cmpl_state);
2855
2856       if (new_dir)
2857         {
2858           if (first_slash)
2859             *remaining_text = first_slash + 1;
2860           else
2861             *remaining_text = text_to_complete + strlen (text_to_complete);
2862         }
2863       else
2864         {
2865           return NULL;
2866         }
2867     }
2868 #endif
2869   else if (g_path_is_absolute (text_to_complete) || !cmpl_state->reference_dir)
2870     {
2871       gchar *tmp = g_strdup (text_to_complete);
2872       gchar *p;
2873
2874       p = tmp;
2875       while (*p && *p != '*' && *p != '?')
2876         p++;
2877
2878       *p = '\0';
2879       p = strrchr (tmp, G_DIR_SEPARATOR);
2880       if (p)
2881         {
2882           if (p == tmp)
2883             p++;
2884       
2885           *p = '\0';
2886
2887           new_dir = open_dir (tmp, cmpl_state);
2888
2889           if (new_dir)
2890             *remaining_text = text_to_complete + 
2891               ((p == tmp + 1) ? (p - tmp) : (p + 1 - tmp));
2892         }
2893       else
2894         {
2895           /* If no possible candidates, use the cwd */
2896           gchar *sys_curdir = g_get_current_dir ();
2897           gchar *utf8_curdir = g_filename_to_utf8 (sys_curdir, -1, NULL, NULL, NULL);
2898
2899           g_free (sys_curdir);
2900
2901           new_dir = open_dir (utf8_curdir, cmpl_state);
2902
2903           if (new_dir)
2904             *remaining_text = text_to_complete;
2905
2906           g_free (utf8_curdir);
2907         }
2908
2909       g_free (tmp);
2910     }
2911   else
2912     {
2913       *remaining_text = text_to_complete;
2914
2915       new_dir = open_dir (cmpl_state->reference_dir->fullname, cmpl_state);
2916     }
2917
2918   if (new_dir)
2919     {
2920       new_dir->cmpl_index = -1;
2921       new_dir->cmpl_parent = NULL;
2922     }
2923
2924   return new_dir;
2925 }
2926
2927 #ifdef HAVE_PWD_H
2928
2929 /* open a directory by user name */
2930 static CompletionDir*
2931 open_user_dir (const gchar     *text_to_complete,
2932                CompletionState *cmpl_state)
2933 {
2934   CompletionDir *result;
2935   gchar *first_slash;
2936   gint cmp_len;
2937
2938   g_assert (text_to_complete && text_to_complete[0] == '~');
2939
2940   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2941
2942   if (first_slash)
2943     cmp_len = first_slash - text_to_complete - 1;
2944   else
2945     cmp_len = strlen (text_to_complete + 1);
2946
2947   if (!cmp_len)
2948     {
2949       /* ~/ */
2950       const gchar *homedir = g_get_home_dir ();
2951       gchar *utf8_homedir = g_filename_to_utf8 (homedir, -1, NULL, NULL, NULL);
2952
2953       if (utf8_homedir)
2954         result = open_dir (utf8_homedir, cmpl_state);
2955       else
2956         result = NULL;
2957       
2958       g_free (utf8_homedir);
2959     }
2960   else
2961     {
2962       /* ~user/ */
2963       gchar* copy = g_new (char, cmp_len + 1);
2964       gchar *utf8_dir;
2965       struct passwd *pwd;
2966
2967       strncpy (copy, text_to_complete + 1, cmp_len);
2968       copy[cmp_len] = 0;
2969       pwd = getpwnam (copy);
2970       g_free (copy);
2971       if (!pwd)
2972         {
2973           cmpl_errno = errno;
2974           return NULL;
2975         }
2976       utf8_dir = g_filename_to_utf8 (pwd->pw_dir, -1, NULL, NULL, NULL);
2977       result = open_dir (utf8_dir, cmpl_state);
2978       g_free (utf8_dir);
2979     }
2980   return result;
2981 }
2982
2983 #endif
2984
2985 /* open a directory relative the the current relative directory */
2986 static CompletionDir*
2987 open_relative_dir (gchar           *dir_name,
2988                    CompletionDir   *dir,
2989                    CompletionState *cmpl_state)
2990 {
2991   CompletionDir *result;
2992   GString *path;
2993
2994   path = g_string_sized_new (dir->fullname_len + strlen (dir_name) + 10);
2995   g_string_assign (path, dir->fullname);
2996
2997   if (dir->fullname_len > 1
2998       && path->str[dir->fullname_len - 1] != G_DIR_SEPARATOR)
2999     g_string_append_c (path, G_DIR_SEPARATOR);
3000   g_string_append (path, dir_name);
3001
3002   result = open_dir (path->str, cmpl_state);
3003
3004   g_string_free (path, TRUE);
3005
3006   return result;
3007 }
3008
3009 /* after the cache lookup fails, really open a new directory */
3010 static CompletionDirSent*
3011 open_new_dir (gchar       *dir_name,
3012               struct stat *sbuf,
3013               gboolean     stat_subdirs)
3014 {
3015   CompletionDirSent *sent;
3016   GDir *directory;
3017   const char *dirent;
3018   GError *error = NULL;
3019   gint entry_count = 0;
3020   gint n_entries = 0;
3021   gint i;
3022   struct stat ent_sbuf;
3023   GString *path;
3024   gchar *sys_dir_name;
3025
3026   sent = g_new (CompletionDirSent, 1);
3027   sent->mtime = sbuf->st_mtime;
3028   sent->inode = sbuf->st_ino;
3029   sent->device = sbuf->st_dev;
3030
3031   path = g_string_sized_new (2*MAXPATHLEN + 10);
3032
3033   sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
3034   if (!sys_dir_name)
3035     {
3036       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3037       return NULL;
3038     }
3039   
3040   directory = g_dir_open (sys_dir_name, 0, &error);
3041   if (!directory)
3042     {
3043       cmpl_errno = error->code; /* ??? */
3044       g_free (sys_dir_name);
3045       return NULL;
3046     }
3047
3048   while ((dirent = g_dir_read_name (directory)) != NULL)
3049     entry_count++;
3050
3051   entry_count += 2;             /* For ".",".." */
3052
3053   sent->entries = g_new (CompletionDirEntry, entry_count);
3054   sent->entry_count = entry_count;
3055
3056   g_dir_rewind (directory);
3057
3058   for (i = 0; i < entry_count; i += 1)
3059     {
3060       GError *error = NULL;
3061
3062       if (i == 0)
3063         dirent = ".";
3064       else if (i == 1)
3065         dirent = "..";
3066       else
3067         {
3068           dirent = g_dir_read_name (directory);
3069           if (!dirent)          /* Directory changed */
3070             break;
3071         }
3072
3073       sent->entries[n_entries].entry_name = g_filename_to_utf8 (dirent, -1, NULL, NULL, &error);
3074       if (sent->entries[n_entries].entry_name == NULL
3075           || !g_utf8_validate (sent->entries[n_entries].entry_name, -1, NULL))
3076         {
3077           gchar *escaped_str = g_strescape (dirent, NULL);
3078           g_message (_("The filename \"%s\" couldn't be converted to UTF-8 "
3079                        "(try setting the environment variable G_BROKEN_FILENAMES): %s"),
3080                      escaped_str,
3081                      error->message ? error->message : _("Invalid Utf-8"));
3082           g_free (escaped_str);
3083           g_clear_error (&error);
3084           continue;
3085         }
3086       g_clear_error (&error);
3087       
3088       g_string_assign (path, sys_dir_name);
3089       if (path->str[path->len-1] != G_DIR_SEPARATOR)
3090         {
3091           g_string_append_c (path, G_DIR_SEPARATOR);
3092         }
3093       g_string_append (path, dirent);
3094
3095       if (stat_subdirs)
3096         {
3097           /* Here we know path->str is a "system charset" string */
3098           if (stat (path->str, &ent_sbuf) >= 0 && S_ISDIR (ent_sbuf.st_mode))
3099             sent->entries[n_entries].is_dir = TRUE;
3100           else
3101             /* stat may fail, and we don't mind, since it could be a
3102              * dangling symlink. */
3103             sent->entries[n_entries].is_dir = FALSE;
3104         }
3105       else
3106         sent->entries[n_entries].is_dir = 1;
3107
3108       n_entries++;
3109     }
3110   sent->entry_count = n_entries;
3111   
3112   g_free (sys_dir_name);
3113   g_string_free (path, TRUE);
3114   qsort (sent->entries, sent->entry_count, sizeof (CompletionDirEntry), compare_cmpl_dir);
3115
3116   g_dir_close (directory);
3117
3118   return sent;
3119 }
3120
3121 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
3122
3123 static gboolean
3124 check_dir (gchar       *dir_name,
3125            struct stat *result,
3126            gboolean    *stat_subdirs)
3127 {
3128   /* A list of directories that we know only contain other directories.
3129    * Trying to stat every file in these directories would be very
3130    * expensive.
3131    */
3132
3133   static struct {
3134     gchar *name;
3135     gboolean present;
3136     struct stat statbuf;
3137   } no_stat_dirs[] = {
3138     { "/afs", FALSE, { 0 } },
3139     { "/net", FALSE, { 0 } }
3140   };
3141
3142   static const gint n_no_stat_dirs = G_N_ELEMENTS (no_stat_dirs);
3143   static gboolean initialized = FALSE;
3144   gchar *sys_dir_name;
3145   gint i;
3146
3147   if (!initialized)
3148     {
3149       initialized = TRUE;
3150       for (i = 0; i < n_no_stat_dirs; i++)
3151         {
3152           if (stat (no_stat_dirs[i].name, &no_stat_dirs[i].statbuf) == 0)
3153             no_stat_dirs[i].present = TRUE;
3154         }
3155     }
3156
3157   sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
3158   if (!sys_dir_name)
3159     {
3160       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3161       return FALSE;
3162     }
3163   
3164   if (stat (sys_dir_name, result) < 0)
3165     {
3166       g_free (sys_dir_name);
3167       cmpl_errno = errno;
3168       return FALSE;
3169     }
3170   g_free (sys_dir_name);
3171
3172   *stat_subdirs = TRUE;
3173   for (i = 0; i < n_no_stat_dirs; i++)
3174     {
3175       if (no_stat_dirs[i].present &&
3176           (no_stat_dirs[i].statbuf.st_dev == result->st_dev) &&
3177           (no_stat_dirs[i].statbuf.st_ino == result->st_ino))
3178         {
3179           *stat_subdirs = FALSE;
3180           break;
3181         }
3182     }
3183
3184   return TRUE;
3185 }
3186
3187 #endif
3188
3189 /* open a directory by absolute pathname */
3190 static CompletionDir*
3191 open_dir (gchar           *dir_name,
3192           CompletionState *cmpl_state)
3193 {
3194   struct stat sbuf;
3195   gboolean stat_subdirs;
3196   CompletionDirSent *sent;
3197   GList* cdsl;
3198
3199 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
3200   if (!check_dir (dir_name, &sbuf, &stat_subdirs))
3201     return NULL;
3202
3203   cdsl = cmpl_state->directory_sent_storage;
3204
3205   while (cdsl)
3206     {
3207       sent = cdsl->data;
3208
3209       if (sent->inode == sbuf.st_ino &&
3210           sent->mtime == sbuf.st_mtime &&
3211           sent->device == sbuf.st_dev)
3212         return attach_dir (sent, dir_name, cmpl_state);
3213
3214       cdsl = cdsl->next;
3215     }
3216 #else
3217   stat_subdirs = TRUE;
3218 #endif
3219
3220   sent = open_new_dir (dir_name, &sbuf, stat_subdirs);
3221
3222   if (sent)
3223     {
3224       cmpl_state->directory_sent_storage =
3225         g_list_prepend (cmpl_state->directory_sent_storage, sent);
3226
3227       return attach_dir (sent, dir_name, cmpl_state);
3228     }
3229
3230   return NULL;
3231 }
3232
3233 static CompletionDir*
3234 attach_dir (CompletionDirSent *sent,
3235             gchar             *dir_name,
3236             CompletionState   *cmpl_state)
3237 {
3238   CompletionDir* new_dir;
3239
3240   new_dir = g_new (CompletionDir, 1);
3241
3242   cmpl_state->directory_storage =
3243     g_list_prepend (cmpl_state->directory_storage, new_dir);
3244
3245   new_dir->sent = sent;
3246   new_dir->fullname = g_strdup (dir_name);
3247   new_dir->fullname_len = strlen (dir_name);
3248   new_dir->cmpl_text = NULL;
3249
3250   return new_dir;
3251 }
3252
3253 static gint
3254 correct_dir_fullname (CompletionDir* cmpl_dir)
3255 {
3256   gint length = strlen (cmpl_dir->fullname);
3257   gchar *first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3258   gchar *sys_filename;
3259   struct stat sbuf;
3260
3261   /* Does it end with /. (\.) ? */
3262   if (length >= 2 &&
3263       strcmp (cmpl_dir->fullname + length - 2, G_DIR_SEPARATOR_S ".") == 0)
3264     {
3265       /* Is it just the root directory (on a drive) ? */
3266       if (cmpl_dir->fullname + length - 2 == first_slash)
3267         {
3268           cmpl_dir->fullname[length - 1] = 0;
3269           cmpl_dir->fullname_len = length - 1;
3270           return TRUE;
3271         }
3272       else
3273         {
3274           cmpl_dir->fullname[length - 2] = 0;
3275         }
3276     }
3277
3278   /* Ends with /./ (\.\)? */
3279   else if (length >= 3 &&
3280            strcmp (cmpl_dir->fullname + length - 3,
3281                    G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) == 0)
3282     cmpl_dir->fullname[length - 2] = 0;
3283
3284   /* Ends with /.. (\..) ? */
3285   else if (length >= 3 &&
3286            strcmp (cmpl_dir->fullname + length - 3,
3287                    G_DIR_SEPARATOR_S "..") == 0)
3288     {
3289       /* Is it just /.. (X:\..)? */
3290       if (cmpl_dir->fullname + length - 3 == first_slash)
3291         {
3292           cmpl_dir->fullname[length - 2] = 0;
3293           cmpl_dir->fullname_len = length - 2;
3294           return TRUE;
3295         }
3296
3297       sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3298       if (!sys_filename)
3299         {
3300           cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3301           return FALSE;
3302         }
3303       
3304       if (stat (sys_filename, &sbuf) < 0)
3305         {
3306           g_free (sys_filename);
3307           cmpl_errno = errno;
3308           return FALSE;
3309         }
3310       g_free (sys_filename);
3311
3312       cmpl_dir->fullname[length - 3] = 0;
3313
3314       if (!correct_parent (cmpl_dir, &sbuf))
3315         return FALSE;
3316     }
3317
3318   /* Ends with /../ (\..\)? */
3319   else if (length >= 4 &&
3320            strcmp (cmpl_dir->fullname + length - 4,
3321                    G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) == 0)
3322     {
3323       /* Is it just /../ (X:\..\)? */
3324       if (cmpl_dir->fullname + length - 4 == first_slash)
3325         {
3326           cmpl_dir->fullname[length - 3] = 0;
3327           cmpl_dir->fullname_len = length - 3;
3328           return TRUE;
3329         }
3330
3331       sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3332       if (!sys_filename)
3333         {
3334           cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3335           return FALSE;
3336         }
3337       
3338       if (stat (sys_filename, &sbuf) < 0)
3339         {
3340           g_free (sys_filename);
3341           cmpl_errno = errno;
3342           return FALSE;
3343         }
3344       g_free (sys_filename);
3345
3346       cmpl_dir->fullname[length - 4] = 0;
3347
3348       if (!correct_parent (cmpl_dir, &sbuf))
3349         return FALSE;
3350     }
3351
3352   cmpl_dir->fullname_len = strlen (cmpl_dir->fullname);
3353
3354   return TRUE;
3355 }
3356
3357 static gint
3358 correct_parent (CompletionDir *cmpl_dir,
3359                 struct stat   *sbuf)
3360 {
3361   struct stat parbuf;
3362   gchar *last_slash;
3363   gchar *first_slash;
3364   gchar *new_name;
3365   gchar *sys_filename;
3366   gchar c = 0;
3367
3368   last_slash = strrchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3369   g_assert (last_slash);
3370   first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3371
3372   /* Clever (?) way to check for top-level directory that works also on
3373    * Win32, where there is a drive letter and colon prefixed...
3374    */
3375   if (last_slash != first_slash)
3376     {
3377       last_slash[0] = 0;
3378     }
3379   else
3380     {
3381       c = last_slash[1];
3382       last_slash[1] = 0;
3383     }
3384
3385   sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3386   if (!sys_filename)
3387     {
3388       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3389       if (!c)
3390         last_slash[0] = G_DIR_SEPARATOR;
3391       return FALSE;
3392     }
3393   
3394   if (stat (sys_filename, &parbuf) < 0)
3395     {
3396       g_free (sys_filename);
3397       cmpl_errno = errno;
3398       if (!c)
3399         last_slash[0] = G_DIR_SEPARATOR;
3400       return FALSE;
3401     }
3402   g_free (sys_filename);
3403
3404 #ifndef G_OS_WIN32              /* No inode numbers on Win32 */
3405   if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
3406     /* it wasn't a link */
3407     return TRUE;
3408
3409   if (c)
3410     last_slash[1] = c;
3411   else
3412     last_slash[0] = G_DIR_SEPARATOR;
3413
3414   /* it was a link, have to figure it out the hard way */
3415
3416   new_name = find_parent_dir_fullname (cmpl_dir->fullname);
3417
3418   if (!new_name)
3419     return FALSE;
3420
3421   g_free (cmpl_dir->fullname);
3422
3423   cmpl_dir->fullname = new_name;
3424 #endif
3425
3426   return TRUE;
3427 }
3428
3429 #ifndef G_OS_WIN32
3430
3431 static gchar*
3432 find_parent_dir_fullname (gchar* dirname)
3433 {
3434   gchar *sys_orig_dir;
3435   gchar *result;
3436   gchar *sys_cwd;
3437   gchar *sys_dirname;
3438
3439   sys_orig_dir = g_get_current_dir ();
3440   sys_dirname = g_filename_from_utf8 (dirname, -1, NULL, NULL, NULL);
3441   if (!sys_dirname)
3442     {
3443       g_free (sys_orig_dir);
3444       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3445       return NULL;
3446     }
3447   
3448   if (chdir (sys_dirname) != 0 || chdir ("..") != 0)
3449     {
3450       g_free (sys_dirname);
3451       g_free (sys_orig_dir);
3452       cmpl_errno = errno;
3453       return NULL;
3454     }
3455   g_free (sys_dirname);
3456
3457   sys_cwd = g_get_current_dir ();
3458   result = g_filename_to_utf8 (sys_cwd, -1, NULL, NULL, NULL);
3459   g_free (sys_cwd);
3460
3461   if (chdir (sys_orig_dir) != 0)
3462     {
3463       cmpl_errno = errno;
3464       g_free (sys_orig_dir);
3465       return NULL;
3466     }
3467
3468   g_free (sys_orig_dir);
3469   return result;
3470 }
3471
3472 #endif
3473
3474 /**********************************************************************/
3475 /*                        Completion Operations                       */
3476 /**********************************************************************/
3477
3478 #ifdef HAVE_PWD_H
3479
3480 static PossibleCompletion*
3481 attempt_homedir_completion (gchar           *text_to_complete,
3482                             CompletionState *cmpl_state)
3483 {
3484   gint index, length;
3485
3486   if (!cmpl_state->user_dir_name_buffer &&
3487       !get_pwdb (cmpl_state))
3488     return NULL;
3489   length = strlen (text_to_complete) - 1;
3490
3491   cmpl_state->user_completion_index += 1;
3492
3493   while (cmpl_state->user_completion_index < cmpl_state->user_directories_len)
3494     {
3495       index = first_diff_index (text_to_complete + 1,
3496                                 cmpl_state->user_directories
3497                                 [cmpl_state->user_completion_index].login);
3498
3499       switch (index)
3500         {
3501         case PATTERN_MATCH:
3502           break;
3503         default:
3504           if (cmpl_state->last_valid_char < (index + 1))
3505             cmpl_state->last_valid_char = index + 1;
3506           cmpl_state->user_completion_index += 1;
3507           continue;
3508         }
3509
3510       cmpl_state->the_completion.is_a_completion = 1;
3511       cmpl_state->the_completion.is_directory = TRUE;
3512
3513       append_completion_text ("~", cmpl_state);
3514
3515       append_completion_text (cmpl_state->
3516                               user_directories[cmpl_state->user_completion_index].login,
3517                               cmpl_state);
3518
3519       return append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3520     }
3521
3522   if (text_to_complete[1]
3523       || cmpl_state->user_completion_index > cmpl_state->user_directories_len)
3524     {
3525       cmpl_state->user_completion_index = -1;
3526       return NULL;
3527     }
3528   else
3529     {
3530       cmpl_state->user_completion_index += 1;
3531       cmpl_state->the_completion.is_a_completion = 1;
3532       cmpl_state->the_completion.is_directory = TRUE;
3533
3534       return append_completion_text ("~" G_DIR_SEPARATOR_S, cmpl_state);
3535     }
3536 }
3537
3538 #endif
3539
3540 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
3541 #define FOLD(c) (tolower(c))
3542 #else
3543 #define FOLD(c) (c)
3544 #endif
3545
3546 /* returns the index (>= 0) of the first differing character,
3547  * PATTERN_MATCH if the completion matches */
3548 static gint
3549 first_diff_index (gchar *pat,
3550                   gchar *text)
3551 {
3552   gint diff = 0;
3553
3554   while (*pat && *text && FOLD (*text) == FOLD (*pat))
3555     {
3556       pat += 1;
3557       text += 1;
3558       diff += 1;
3559     }
3560
3561   if (*pat)
3562     return diff;
3563
3564   return PATTERN_MATCH;
3565 }
3566
3567 static PossibleCompletion*
3568 append_completion_text (gchar           *text,
3569                         CompletionState *cmpl_state)
3570 {
3571   gint len, i = 1;
3572
3573   if (!cmpl_state->the_completion.text)
3574     return NULL;
3575
3576   len = strlen (text) + strlen (cmpl_state->the_completion.text) + 1;
3577
3578   if (cmpl_state->the_completion.text_alloc > len)
3579     {
3580       strcat (cmpl_state->the_completion.text, text);
3581       return &cmpl_state->the_completion;
3582     }
3583
3584   while (i < len)
3585     i <<= 1;
3586
3587   cmpl_state->the_completion.text_alloc = i;
3588
3589   cmpl_state->the_completion.text = (gchar*) g_realloc (cmpl_state->the_completion.text, i);
3590
3591   if (!cmpl_state->the_completion.text)
3592     return NULL;
3593   else
3594     {
3595       strcat (cmpl_state->the_completion.text, text);
3596       return &cmpl_state->the_completion;
3597     }
3598 }
3599
3600 static CompletionDir*
3601 find_completion_dir (gchar          *text_to_complete,
3602                     gchar          **remaining_text,
3603                     CompletionState *cmpl_state)
3604 {
3605   gchar* first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
3606   CompletionDir* dir = cmpl_state->reference_dir;
3607   CompletionDir* next;
3608   *remaining_text = text_to_complete;
3609
3610   while (first_slash)
3611     {
3612       gint len = first_slash - *remaining_text;
3613       gint found = 0;
3614       gchar *found_name = NULL;         /* Quiet gcc */
3615       gint i;
3616       gchar* pat_buf = g_new (gchar, len + 1);
3617
3618       strncpy (pat_buf, *remaining_text, len);
3619       pat_buf[len] = 0;
3620
3621       for (i = 0; i < dir->sent->entry_count; i += 1)
3622         {
3623           if (dir->sent->entries[i].is_dir &&
3624              fnmatch (pat_buf, dir->sent->entries[i].entry_name,
3625                       FNMATCH_FLAGS)!= FNM_NOMATCH)
3626             {
3627               if (found)
3628                 {
3629                   g_free (pat_buf);
3630                   return dir;
3631                 }
3632               else
3633                 {
3634                   found = 1;
3635                   found_name = dir->sent->entries[i].entry_name;
3636                 }
3637             }
3638         }
3639
3640       if (!found)
3641         {
3642           /* Perhaps we are trying to open an automount directory */
3643           found_name = pat_buf;
3644         }
3645
3646       next = open_relative_dir (found_name, dir, cmpl_state);
3647       
3648       if (!next)
3649         {
3650           g_free (pat_buf);
3651           return NULL;
3652         }
3653       
3654       next->cmpl_parent = dir;
3655       
3656       dir = next;
3657       
3658       if (!correct_dir_fullname (dir))
3659         {
3660           g_free (pat_buf);
3661           return NULL;
3662         }
3663       
3664       *remaining_text = first_slash + 1;
3665       first_slash = strchr (*remaining_text, G_DIR_SEPARATOR);
3666
3667       g_free (pat_buf);
3668     }
3669
3670   return dir;
3671 }
3672
3673 static void
3674 update_cmpl (PossibleCompletion *poss,
3675              CompletionState    *cmpl_state)
3676 {
3677   gint cmpl_len;
3678
3679   if (!poss || !cmpl_is_a_completion (poss))
3680     return;
3681
3682   cmpl_len = strlen (cmpl_this_completion (poss));
3683
3684   if (cmpl_state->updated_text_alloc < cmpl_len + 1)
3685     {
3686       cmpl_state->updated_text =
3687         (gchar*)g_realloc (cmpl_state->updated_text,
3688                            cmpl_state->updated_text_alloc);
3689       cmpl_state->updated_text_alloc = 2*cmpl_len;
3690     }
3691
3692   if (cmpl_state->updated_text_len < 0)
3693     {
3694       strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3695       cmpl_state->updated_text_len = cmpl_len;
3696       cmpl_state->re_complete = cmpl_is_directory (poss);
3697     }
3698   else if (cmpl_state->updated_text_len == 0)
3699     {
3700       cmpl_state->re_complete = FALSE;
3701     }
3702   else
3703     {
3704       gint first_diff =
3705         first_diff_index (cmpl_state->updated_text,
3706                           cmpl_this_completion (poss));
3707
3708       cmpl_state->re_complete = FALSE;
3709
3710       if (first_diff == PATTERN_MATCH)
3711         return;
3712
3713       if (first_diff > cmpl_state->updated_text_len)
3714         strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3715
3716       cmpl_state->updated_text_len = first_diff;
3717       cmpl_state->updated_text[first_diff] = 0;
3718     }
3719 }
3720
3721 static PossibleCompletion*
3722 attempt_file_completion (CompletionState *cmpl_state)
3723 {
3724   gchar *pat_buf, *first_slash;
3725   CompletionDir *dir = cmpl_state->active_completion_dir;
3726
3727   dir->cmpl_index += 1;
3728
3729   if (dir->cmpl_index == dir->sent->entry_count)
3730     {
3731       if (dir->cmpl_parent == NULL)
3732         {
3733           cmpl_state->active_completion_dir = NULL;
3734
3735           return NULL;
3736         }
3737       else
3738         {
3739           cmpl_state->active_completion_dir = dir->cmpl_parent;
3740
3741           return attempt_file_completion (cmpl_state);
3742         }
3743     }
3744
3745   g_assert (dir->cmpl_text);
3746
3747   first_slash = strchr (dir->cmpl_text, G_DIR_SEPARATOR);
3748
3749   if (first_slash)
3750     {
3751       gint len = first_slash - dir->cmpl_text;
3752
3753       pat_buf = g_new (gchar, len + 1);
3754       strncpy (pat_buf, dir->cmpl_text, len);
3755       pat_buf[len] = 0;
3756     }
3757   else
3758     {
3759       gint len = strlen (dir->cmpl_text);
3760
3761       pat_buf = g_new (gchar, len + 2);
3762       strcpy (pat_buf, dir->cmpl_text);
3763       /* Don't append a * if the user entered one herself.
3764        * This way one can complete *.h and don't get matches
3765        * on any .help files, for instance.
3766        */
3767       if (strchr (pat_buf, '*') == NULL)
3768         strcpy (pat_buf + len, "*");
3769     }
3770
3771   if (first_slash)
3772     {
3773       if (dir->sent->entries[dir->cmpl_index].is_dir)
3774         {
3775           if (fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
3776                        FNMATCH_FLAGS) != FNM_NOMATCH)
3777             {
3778               CompletionDir* new_dir;
3779
3780               new_dir = open_relative_dir (dir->sent->entries[dir->cmpl_index].entry_name,
3781                                            dir, cmpl_state);
3782
3783               if (!new_dir)
3784                 {
3785                   g_free (pat_buf);
3786                   return NULL;
3787                 }
3788
3789               new_dir->cmpl_parent = dir;
3790
3791               new_dir->cmpl_index = -1;
3792               new_dir->cmpl_text = g_strdup (first_slash + 1);
3793
3794               cmpl_state->active_completion_dir = new_dir;
3795
3796               g_free (pat_buf);
3797               return attempt_file_completion (cmpl_state);
3798             }
3799           else
3800             {
3801               g_free (pat_buf);
3802               return attempt_file_completion (cmpl_state);
3803             }
3804         }
3805       else
3806         {
3807           g_free (pat_buf);
3808           return attempt_file_completion (cmpl_state);
3809         }
3810     }
3811   else
3812     {
3813       if (dir->cmpl_parent != NULL)
3814         {
3815           append_completion_text (dir->fullname +
3816                                   strlen (cmpl_state->completion_dir->fullname) + 1,
3817                                   cmpl_state);
3818           append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3819         }
3820
3821       append_completion_text (dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
3822
3823       cmpl_state->the_completion.is_a_completion =
3824         fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
3825                  FNMATCH_FLAGS) != FNM_NOMATCH;
3826
3827       cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
3828       if (dir->sent->entries[dir->cmpl_index].is_dir)
3829         append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3830
3831       g_free (pat_buf);
3832       return &cmpl_state->the_completion;
3833     }
3834 }
3835
3836 #ifdef HAVE_PWD_H
3837
3838 static gint
3839 get_pwdb (CompletionState* cmpl_state)
3840 {
3841   struct passwd *pwd_ptr;
3842   gchar* buf_ptr;
3843   gchar *utf8;
3844   gint len = 0, i, count = 0;
3845
3846   if (cmpl_state->user_dir_name_buffer)
3847     return TRUE;
3848   setpwent ();
3849
3850   while ((pwd_ptr = getpwent ()) != NULL)
3851     {
3852       utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3853       len += strlen (utf8);
3854       g_free (utf8);
3855       utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3856       len += strlen (utf8);
3857       g_free (utf8);
3858       len += 2;
3859       count += 1;
3860     }
3861
3862   setpwent ();
3863
3864   cmpl_state->user_dir_name_buffer = g_new (gchar, len);
3865   cmpl_state->user_directories = g_new (CompletionUserDir, count);
3866   cmpl_state->user_directories_len = count;
3867
3868   buf_ptr = cmpl_state->user_dir_name_buffer;
3869
3870   for (i = 0; i < count; i += 1)
3871     {
3872       pwd_ptr = getpwent ();
3873       if (!pwd_ptr)
3874         {
3875           cmpl_errno = errno;
3876           goto error;
3877         }
3878
3879       utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3880       strcpy (buf_ptr, utf8);
3881       g_free (utf8);
3882
3883       cmpl_state->user_directories[i].login = buf_ptr;
3884
3885       buf_ptr += strlen (buf_ptr);
3886       buf_ptr += 1;
3887
3888       utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3889       strcpy (buf_ptr, utf8);
3890       g_free (utf8);
3891
3892       cmpl_state->user_directories[i].homedir = buf_ptr;
3893
3894       buf_ptr += strlen (buf_ptr);
3895       buf_ptr += 1;
3896     }
3897
3898   qsort (cmpl_state->user_directories,
3899          cmpl_state->user_directories_len,
3900          sizeof (CompletionUserDir),
3901          compare_user_dir);
3902
3903   endpwent ();
3904
3905   return TRUE;
3906
3907 error:
3908
3909   if (cmpl_state->user_dir_name_buffer)
3910     g_free (cmpl_state->user_dir_name_buffer);
3911   if (cmpl_state->user_directories)
3912     g_free (cmpl_state->user_directories);
3913
3914   cmpl_state->user_dir_name_buffer = NULL;
3915   cmpl_state->user_directories = NULL;
3916
3917   return FALSE;
3918 }
3919
3920 static gint
3921 compare_user_dir (const void *a,
3922                   const void *b)
3923 {
3924   return strcmp ((((CompletionUserDir*)a))->login,
3925                  (((CompletionUserDir*)b))->login);
3926 }
3927
3928 #endif
3929
3930 static gint
3931 compare_cmpl_dir (const void *a,
3932                   const void *b)
3933 {
3934   return compare_filenames ((((CompletionDirEntry*)a))->entry_name,
3935                             (((CompletionDirEntry*)b))->entry_name);
3936 }
3937
3938 static gint
3939 cmpl_state_okay (CompletionState* cmpl_state)
3940 {
3941   return  cmpl_state && cmpl_state->reference_dir;
3942 }
3943
3944 static const gchar*
3945 cmpl_strerror (gint err)
3946 {
3947   if (err == CMPL_ERRNO_TOO_LONG)
3948     return _("Name too long");
3949   else if (err == CMPL_ERRNO_DID_NOT_CONVERT)
3950     return _("Couldn't convert filename");
3951   else
3952     return g_strerror (err);
3953 }