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