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