1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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.
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.
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.
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/.
30 #include <sys/types.h>
32 #ifdef HAVE_SYS_PARAM_H
33 #include <sys/param.h>
50 #include "gdk/gdkkeysyms.h"
51 #include "gtkbutton.h"
53 #include "gtkfilesel.h"
58 #include "gtklistitem.h"
60 #include "gtkscrolledwindow.h"
62 #include "gtksignal.h"
65 #include "gtkmenuitem.h"
66 #include "gtkoptionmenu.h"
68 #include "gtkdialog.h"
71 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
78 #define mkdir(p,m) _mkdir(p)
80 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
83 #endif /* G_OS_WIN32 */
85 #endif /* G_OS_WIN32 || G_WITH_CYGWIN */
87 #define DIR_LIST_WIDTH 180
88 #define DIR_LIST_HEIGHT 180
89 #define FILE_LIST_WIDTH 180
90 #define FILE_LIST_HEIGHT 180
92 /* I've put this here so it doesn't get confused with the
93 * file completion interface */
94 typedef struct _HistoryCallbackArg HistoryCallbackArg;
96 struct _HistoryCallbackArg
103 typedef struct _CompletionState CompletionState;
104 typedef struct _CompletionDir CompletionDir;
105 typedef struct _CompletionDirSent CompletionDirSent;
106 typedef struct _CompletionDirEntry CompletionDirEntry;
107 typedef struct _CompletionUserDir CompletionUserDir;
108 typedef struct _PossibleCompletion PossibleCompletion;
110 /* Non-external file completion decls and structures */
112 /* A contant telling PRCS how many directories to cache. Its actually
113 * kept in a list, so the geometry isn't important. */
114 #define CMPL_DIRECTORY_CACHE_SIZE 10
116 /* A constant used to determine whether a substring was an exact
117 * match by first_diff_index()
119 #define PATTERN_MATCH -1
120 /* The arguments used by all fnmatch() calls below
122 #define FNMATCH_FLAGS (FNM_PATHNAME | FNM_PERIOD)
124 #define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
126 /* This structure contains all the useful information about a directory
127 * for the purposes of filename completion. These structures are cached
128 * in the CompletionState struct. CompletionDir's are reference counted.
130 struct _CompletionDirSent
137 struct _CompletionDirEntry *entries;
140 struct _CompletionDir
142 CompletionDirSent *sent;
147 struct _CompletionDir *cmpl_parent;
152 /* This structure contains pairs of directory entry names with a flag saying
153 * whether or not they are a valid directory. NOTE: This information is used
154 * to provide the caller with information about whether to update its completions
155 * or try to open a file. Since directories are cached by the directory mtime,
156 * a symlink which points to an invalid file (which will not be a directory),
157 * will not be reevaluated if that file is created, unless the containing
158 * directory is touched. I consider this case to be worth ignoring (josh).
160 struct _CompletionDirEntry
166 struct _CompletionUserDir
172 struct _PossibleCompletion
174 /* accessible fields, all are accessed externally by functions
178 gint is_a_completion;
179 gboolean is_directory;
186 struct _CompletionState
188 gint last_valid_char;
190 gint updated_text_len;
191 gint updated_text_alloc;
192 gboolean re_complete;
194 gchar *user_dir_name_buffer;
195 gint user_directories_len;
197 gchar *last_completion_text;
199 gint user_completion_index; /* if >= 0, currently completing ~user */
201 struct _CompletionDir *completion_dir; /* directory completing from */
202 struct _CompletionDir *active_completion_dir;
204 struct _PossibleCompletion the_completion;
206 struct _CompletionDir *reference_dir; /* initial directory */
208 GList* directory_storage;
209 GList* directory_sent_storage;
211 struct _CompletionUserDir *user_directories;
215 /* File completion functions which would be external, were they used
216 * outside of this file.
219 static CompletionState* cmpl_init_state (void);
220 static void cmpl_free_state (CompletionState *cmpl_state);
221 static gint cmpl_state_okay (CompletionState* cmpl_state);
222 static gchar* cmpl_strerror (gint);
224 static PossibleCompletion* cmpl_completion_matches(gchar *text_to_complete,
225 gchar **remaining_text,
226 CompletionState *cmpl_state);
228 /* Returns a name for consideration, possibly a completion, this name
229 * will be invalid after the next call to cmpl_next_completion.
231 static char* cmpl_this_completion (PossibleCompletion*);
233 /* True if this completion matches the given text. Otherwise, this
234 * output can be used to have a list of non-completions.
236 static gint cmpl_is_a_completion (PossibleCompletion*);
238 /* True if the completion is a directory
240 static gboolean cmpl_is_directory (PossibleCompletion*);
242 /* Obtains the next completion, or NULL
244 static PossibleCompletion* cmpl_next_completion (CompletionState*);
246 /* Updating completions: the return value of cmpl_updated_text() will
247 * be text_to_complete completed as much as possible after the most
248 * recent call to cmpl_completion_matches. For the present
249 * application, this is the suggested replacement for the user's input
250 * string. You must CALL THIS AFTER ALL cmpl_text_completions have
253 static gchar* cmpl_updated_text (CompletionState* cmpl_state);
255 /* After updating, to see if the completion was a directory, call
256 * this. If it was, you should consider re-calling completion_matches.
258 static gboolean cmpl_updated_dir (CompletionState* cmpl_state);
260 /* Current location: if using file completion, return the current
261 * directory, from which file completion begins. More specifically,
262 * the cwd concatenated with all exact completions up to the last
263 * directory delimiter('/').
265 static gchar* cmpl_reference_position (CompletionState* cmpl_state);
267 /* backing up: if cmpl_completion_matches returns NULL, you may query
268 * the index of the last completable character into cmpl_updated_text.
270 static gint cmpl_last_valid_char (CompletionState* cmpl_state);
272 /* When the user selects a non-directory, call cmpl_completion_fullname
273 * to get the full name of the selected file.
275 static gchar* cmpl_completion_fullname (gchar*, CompletionState* cmpl_state);
278 /* Directory operations. */
279 static CompletionDir* open_ref_dir (gchar* text_to_complete,
280 gchar** remaining_text,
281 CompletionState* cmpl_state);
282 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
283 static gboolean check_dir (gchar *dir_name,
285 gboolean *stat_subdirs);
287 static CompletionDir* open_dir (gchar* dir_name,
288 CompletionState* cmpl_state);
290 static CompletionDir* open_user_dir (gchar* text_to_complete,
291 CompletionState *cmpl_state);
293 static CompletionDir* open_relative_dir (gchar* dir_name, CompletionDir* dir,
294 CompletionState *cmpl_state);
295 static CompletionDirSent* open_new_dir (gchar* dir_name,
297 gboolean stat_subdirs);
298 static gint correct_dir_fullname (CompletionDir* cmpl_dir);
299 static gint correct_parent (CompletionDir* cmpl_dir,
301 static gchar* find_parent_dir_fullname (gchar* dirname);
302 static CompletionDir* attach_dir (CompletionDirSent* sent,
304 CompletionState *cmpl_state);
305 static void free_dir_sent (CompletionDirSent* sent);
306 static void free_dir (CompletionDir *dir);
307 static void prune_memory_usage(CompletionState *cmpl_state);
309 /* Completion operations */
310 static PossibleCompletion* attempt_homedir_completion(gchar* text_to_complete,
311 CompletionState *cmpl_state);
312 static PossibleCompletion* attempt_file_completion(CompletionState *cmpl_state);
313 static CompletionDir* find_completion_dir(gchar* text_to_complete,
314 gchar** remaining_text,
315 CompletionState* cmpl_state);
316 static PossibleCompletion* append_completion_text(gchar* text,
317 CompletionState* cmpl_state);
319 static gint get_pwdb(CompletionState* cmpl_state);
320 static gint compare_user_dir(const void* a, const void* b);
322 static gint first_diff_index(gchar* pat, gchar* text);
323 static gint compare_cmpl_dir(const void* a, const void* b);
324 static void update_cmpl(PossibleCompletion* poss,
325 CompletionState* cmpl_state);
327 static void gtk_file_selection_class_init (GtkFileSelectionClass *klass);
328 static void gtk_file_selection_init (GtkFileSelection *filesel);
329 static void gtk_file_selection_destroy (GtkObject *object);
330 static gint gtk_file_selection_key_press (GtkWidget *widget,
334 static void gtk_file_selection_file_button (GtkWidget *widget,
337 GdkEventButton *bevent,
340 static void gtk_file_selection_dir_button (GtkWidget *widget,
343 GdkEventButton *bevent,
346 static void gtk_file_selection_populate (GtkFileSelection *fs,
349 static void gtk_file_selection_abort (GtkFileSelection *fs);
351 static void gtk_file_selection_update_history_menu (GtkFileSelection *fs,
354 static void gtk_file_selection_create_dir (GtkWidget *widget, gpointer data);
355 static void gtk_file_selection_delete_file (GtkWidget *widget, gpointer data);
356 static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data);
360 static GtkWindowClass *parent_class = NULL;
362 /* Saves errno when something cmpl does fails. */
363 static gint cmpl_errno;
367 * Take the path currently in the file selection
368 * entry field and translate as necessary from
369 * a WIN32 style to CYGWIN32 style path. For
370 * instance translate:
371 * x:\somepath\file.jpg
373 * //x/somepath/file.jpg
375 * Replace the path in the selection text field.
376 * Return a boolean value concerning whether a
377 * translation had to be made.
380 translate_win32_path (GtkFileSelection *filesel)
386 * Retrieve the current path
388 path = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
391 * Translate only if this looks like a DOS-ish
392 * path... First handle any drive letters.
394 if (isalpha (path[0]) && (path[1] == ':')) {
396 * This part kind of stinks... It isn't possible
397 * to know if there is enough space in the current
398 * string for the extra character required in this
399 * conversion. Assume that there isn't enough space
400 * and use the set function on the text field to
401 * set the newly created string.
403 gchar *newPath = g_strdup_printf ("//%c/%s", path[0], (path + 3));
404 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), newPath);
411 * Now, replace backslashes with forward slashes
414 if (strchr (path, '\\'))
417 for (index = 0; path[index] != '\0'; index++)
418 if (path[index] == '\\')
429 gtk_file_selection_get_type (void)
431 static GtkType file_selection_type = 0;
433 if (!file_selection_type)
435 static const GtkTypeInfo filesel_info =
438 sizeof (GtkFileSelection),
439 sizeof (GtkFileSelectionClass),
440 (GtkClassInitFunc) gtk_file_selection_class_init,
441 (GtkObjectInitFunc) gtk_file_selection_init,
442 /* reserved_1 */ NULL,
443 /* reserved_2 */ NULL,
444 (GtkClassInitFunc) NULL,
447 file_selection_type = gtk_type_unique (GTK_TYPE_DIALOG, &filesel_info);
450 return file_selection_type;
454 gtk_file_selection_class_init (GtkFileSelectionClass *class)
456 GtkObjectClass *object_class;
458 object_class = (GtkObjectClass*) class;
460 parent_class = gtk_type_class (GTK_TYPE_DIALOG);
462 object_class->destroy = gtk_file_selection_destroy;
466 gtk_file_selection_init (GtkFileSelection *filesel)
468 GtkWidget *entry_vbox;
470 GtkWidget *list_hbox;
471 GtkWidget *confirm_area;
472 GtkWidget *pulldown_hbox;
473 GtkWidget *scrolled_win;
477 char *file_title [2];
479 dialog = GTK_DIALOG (filesel);
481 filesel->cmpl_state = cmpl_init_state ();
483 /* The dialog-sized vertical box */
484 filesel->main_vbox = dialog->vbox;
485 gtk_container_set_border_width (GTK_CONTAINER (filesel), 10);
487 /* The horizontal box containing create, rename etc. buttons */
488 filesel->button_area = gtk_hbutton_box_new ();
489 gtk_button_box_set_layout (GTK_BUTTON_BOX (filesel->button_area), GTK_BUTTONBOX_START);
490 gtk_button_box_set_spacing (GTK_BUTTON_BOX (filesel->button_area), 0);
491 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->button_area,
493 gtk_widget_show (filesel->button_area);
495 gtk_file_selection_show_fileop_buttons (filesel);
497 /* hbox for pulldown menu */
498 pulldown_hbox = gtk_hbox_new (TRUE, 5);
499 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), pulldown_hbox, FALSE, FALSE, 0);
500 gtk_widget_show (pulldown_hbox);
503 filesel->history_pulldown = gtk_option_menu_new ();
504 gtk_widget_show (filesel->history_pulldown);
505 gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->history_pulldown,
508 /* The horizontal box containing the directory and file listboxes */
509 list_hbox = gtk_hbox_new (FALSE, 5);
510 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), list_hbox, TRUE, TRUE, 0);
511 gtk_widget_show (list_hbox);
513 /* The directories clist */
514 dir_title[0] = _("Directories");
516 filesel->dir_list = gtk_clist_new_with_titles (1, (gchar**) dir_title);
517 gtk_widget_set_usize (filesel->dir_list, DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
518 gtk_signal_connect (GTK_OBJECT (filesel->dir_list), "select_row",
519 (GtkSignalFunc) gtk_file_selection_dir_button,
521 gtk_clist_set_column_auto_resize (GTK_CLIST (filesel->dir_list), 0, TRUE);
522 gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list));
524 scrolled_win = gtk_scrolled_window_new (NULL, NULL);
525 gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->dir_list);
526 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
527 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
528 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
529 gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
530 gtk_widget_show (filesel->dir_list);
531 gtk_widget_show (scrolled_win);
533 /* The files clist */
534 file_title[0] = _("Files");
535 file_title[1] = NULL;
536 filesel->file_list = gtk_clist_new_with_titles (1, (gchar**) file_title);
537 gtk_widget_set_usize (filesel->file_list, FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
538 gtk_signal_connect (GTK_OBJECT (filesel->file_list), "select_row",
539 (GtkSignalFunc) gtk_file_selection_file_button,
541 gtk_clist_set_column_auto_resize (GTK_CLIST (filesel->file_list), 0, TRUE);
542 gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list));
544 scrolled_win = gtk_scrolled_window_new (NULL, NULL);
545 gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->file_list);
546 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
547 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
548 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
549 gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
550 gtk_widget_show (filesel->file_list);
551 gtk_widget_show (scrolled_win);
553 /* action area for packing buttons into. */
554 filesel->action_area = gtk_hbox_new (TRUE, 0);
555 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->action_area,
557 gtk_widget_show (filesel->action_area);
559 /* The OK/Cancel button area */
560 confirm_area = dialog->action_area;
563 filesel->ok_button = gtk_dialog_add_button (dialog,
567 gtk_widget_grab_default (filesel->ok_button);
569 /* The Cancel button */
570 filesel->cancel_button = gtk_dialog_add_button (dialog,
571 GTK_STOCK_BUTTON_CANCEL,
572 GTK_RESPONSE_CANCEL);
574 /* The selection entry widget */
575 entry_vbox = gtk_vbox_new (FALSE, 2);
576 gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 2);
577 gtk_widget_show (entry_vbox);
579 filesel->selection_text = label = gtk_label_new ("");
580 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
581 gtk_box_pack_start (GTK_BOX (entry_vbox), label, FALSE, FALSE, 0);
582 gtk_widget_show (label);
584 filesel->selection_entry = gtk_entry_new ();
585 gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "key_press_event",
586 (GtkSignalFunc) gtk_file_selection_key_press, filesel);
587 gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "focus_in_event",
588 (GtkSignalFunc) gtk_widget_grab_default,
589 GTK_OBJECT (filesel->ok_button));
590 gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "activate",
591 (GtkSignalFunc) gtk_button_clicked,
592 GTK_OBJECT (filesel->ok_button));
593 gtk_box_pack_start (GTK_BOX (entry_vbox), filesel->selection_entry, TRUE, TRUE, 0);
594 gtk_widget_show (filesel->selection_entry);
596 if (!cmpl_state_okay (filesel->cmpl_state))
600 sprintf (err_buf, _("Directory unreadable: %s"), cmpl_strerror (cmpl_errno));
602 gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
606 gtk_file_selection_populate (filesel, "", FALSE);
609 gtk_widget_grab_focus (filesel->selection_entry);
613 gtk_file_selection_new (const gchar *title)
615 GtkFileSelection *filesel;
617 filesel = gtk_type_new (GTK_TYPE_FILE_SELECTION);
618 gtk_window_set_title (GTK_WINDOW (filesel), title);
620 return GTK_WIDGET (filesel);
624 gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
626 g_return_if_fail (filesel != NULL);
627 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
629 /* delete, create directory, and rename */
630 if (!filesel->fileop_c_dir)
632 filesel->fileop_c_dir = gtk_button_new_with_label (_("Create Dir"));
633 gtk_signal_connect (GTK_OBJECT (filesel->fileop_c_dir), "clicked",
634 (GtkSignalFunc) gtk_file_selection_create_dir,
636 gtk_box_pack_start (GTK_BOX (filesel->button_area),
637 filesel->fileop_c_dir, TRUE, TRUE, 0);
638 gtk_widget_show (filesel->fileop_c_dir);
641 if (!filesel->fileop_del_file)
643 filesel->fileop_del_file = gtk_button_new_with_label (_("Delete File"));
644 gtk_signal_connect (GTK_OBJECT (filesel->fileop_del_file), "clicked",
645 (GtkSignalFunc) gtk_file_selection_delete_file,
647 gtk_box_pack_start (GTK_BOX (filesel->button_area),
648 filesel->fileop_del_file, TRUE, TRUE, 0);
649 gtk_widget_show (filesel->fileop_del_file);
652 if (!filesel->fileop_ren_file)
654 filesel->fileop_ren_file = gtk_button_new_with_label (_("Rename File"));
655 gtk_signal_connect (GTK_OBJECT (filesel->fileop_ren_file), "clicked",
656 (GtkSignalFunc) gtk_file_selection_rename_file,
658 gtk_box_pack_start (GTK_BOX (filesel->button_area),
659 filesel->fileop_ren_file, TRUE, TRUE, 0);
660 gtk_widget_show (filesel->fileop_ren_file);
663 gtk_widget_queue_resize (GTK_WIDGET (filesel));
667 gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
669 g_return_if_fail (filesel != NULL);
670 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
672 if (filesel->fileop_ren_file)
674 gtk_widget_destroy (filesel->fileop_ren_file);
675 filesel->fileop_ren_file = NULL;
678 if (filesel->fileop_del_file)
680 gtk_widget_destroy (filesel->fileop_del_file);
681 filesel->fileop_del_file = NULL;
684 if (filesel->fileop_c_dir)
686 gtk_widget_destroy (filesel->fileop_c_dir);
687 filesel->fileop_c_dir = NULL;
694 gtk_file_selection_set_filename (GtkFileSelection *filesel,
695 const gchar *filename)
698 const char *name, *last_slash;
700 g_return_if_fail (filesel != NULL);
701 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
702 g_return_if_fail (filename != NULL);
704 last_slash = strrchr (filename, G_DIR_SEPARATOR);
713 buf = g_strdup (filename);
714 buf[last_slash - filename + 1] = 0;
715 name = last_slash + 1;
718 gtk_file_selection_populate (filesel, buf, FALSE);
720 if (filesel->selection_entry)
721 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
726 gtk_file_selection_get_filename (GtkFileSelection *filesel)
728 static gchar nothing[2] = "";
729 static gchar something[MAXPATHLEN*2];
733 g_return_val_if_fail (filesel != NULL, nothing);
734 g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
737 translate_win32_path (filesel);
739 text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
742 sys_filename = g_filename_from_utf8 (cmpl_completion_fullname (text, filesel->cmpl_state), -1, NULL, NULL, NULL);
743 strncpy (something, sys_filename, sizeof (something));
744 g_free (sys_filename);
752 gtk_file_selection_complete (GtkFileSelection *filesel,
753 const gchar *pattern)
755 g_return_if_fail (filesel != NULL);
756 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
757 g_return_if_fail (pattern != NULL);
759 if (filesel->selection_entry)
760 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), pattern);
761 gtk_file_selection_populate (filesel, (gchar*) pattern, TRUE);
765 gtk_file_selection_destroy (GtkObject *object)
767 GtkFileSelection *filesel;
769 HistoryCallbackArg *callback_arg;
771 g_return_if_fail (GTK_IS_FILE_SELECTION (object));
773 filesel = GTK_FILE_SELECTION (object);
775 if (filesel->fileop_dialog)
777 gtk_widget_destroy (filesel->fileop_dialog);
778 filesel->fileop_dialog = NULL;
781 if (filesel->history_list)
783 list = filesel->history_list;
786 callback_arg = list->data;
787 g_free (callback_arg->directory);
788 g_free (callback_arg);
791 g_list_free (filesel->history_list);
792 filesel->history_list = NULL;
795 if (filesel->cmpl_state)
797 cmpl_free_state (filesel->cmpl_state);
798 filesel->cmpl_state = NULL;
801 GTK_OBJECT_CLASS (parent_class)->destroy (object);
804 /* Begin file operations callbacks */
807 gtk_file_selection_fileop_error (GtkFileSelection *fs,
808 gchar *error_message)
815 g_return_if_fail (error_message != NULL);
818 dialog = gtk_dialog_new ();
820 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
821 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
824 gtk_window_set_title (GTK_WINDOW (dialog), _("Error"));
825 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
827 /* If file dialog is grabbed, make this dialog modal too */
828 /* When error dialog is closed, file dialog will be grabbed again */
829 if (GTK_WINDOW (fs)->modal)
830 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
832 vbox = gtk_vbox_new (FALSE, 0);
833 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
834 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
836 gtk_widget_show (vbox);
838 label = gtk_label_new (error_message);
839 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
840 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
841 gtk_widget_show (label);
843 /* yes, we free it */
844 g_free (error_message);
847 button = gtk_button_new_with_label (_("Close"));
848 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
849 (GtkSignalFunc) gtk_widget_destroy,
851 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
852 button, TRUE, TRUE, 0);
853 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
854 gtk_widget_grab_default (button);
855 gtk_widget_show (button);
857 gtk_widget_show (dialog);
861 gtk_file_selection_fileop_destroy (GtkWidget *widget,
864 GtkFileSelection *fs = data;
866 g_return_if_fail (fs != NULL);
867 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
869 fs->fileop_dialog = NULL;
874 gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
877 GtkFileSelection *fs = data;
881 gchar *sys_full_path;
883 CompletionState *cmpl_state;
885 g_return_if_fail (fs != NULL);
886 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
888 dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
889 cmpl_state = (CompletionState*) fs->cmpl_state;
890 path = cmpl_reference_position (cmpl_state);
892 full_path = g_strconcat (path, G_DIR_SEPARATOR_S, dirname, NULL);
893 sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, NULL);
894 if (mkdir (sys_full_path, 0755) < 0)
896 buf = g_strconcat ("Error creating directory \"", dirname, "\": ",
897 g_strerror (errno), NULL);
898 gtk_file_selection_fileop_error (fs, buf);
901 g_free (sys_full_path);
903 gtk_widget_destroy (fs->fileop_dialog);
904 gtk_file_selection_populate (fs, "", FALSE);
908 gtk_file_selection_create_dir (GtkWidget *widget,
911 GtkFileSelection *fs = data;
917 g_return_if_fail (fs != NULL);
918 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
920 if (fs->fileop_dialog)
924 dialog = gtk_dialog_new ();
925 fs->fileop_dialog = dialog;
926 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
927 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
929 gtk_window_set_title (GTK_WINDOW (dialog), _("Create Directory"));
930 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
932 /* If file dialog is grabbed, grab option dialog */
933 /* When option dialog is closed, file dialog will be grabbed again */
934 if (GTK_WINDOW (fs)->modal)
935 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
937 vbox = gtk_vbox_new (FALSE, 0);
938 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
939 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
941 gtk_widget_show( vbox);
943 label = gtk_label_new (_("Directory name:"));
944 gtk_misc_set_alignment(GTK_MISC (label), 0.0, 0.0);
945 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
946 gtk_widget_show (label);
948 /* The directory entry widget */
949 fs->fileop_entry = gtk_entry_new ();
950 gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry,
952 GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
953 gtk_widget_show (fs->fileop_entry);
956 button = gtk_button_new_with_label (_("Create"));
957 gtk_signal_connect (GTK_OBJECT (button), "clicked",
958 (GtkSignalFunc) gtk_file_selection_create_dir_confirmed,
960 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
961 button, TRUE, TRUE, 0);
962 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
963 gtk_widget_show (button);
965 button = gtk_button_new_with_label (_("Cancel"));
966 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
967 (GtkSignalFunc) gtk_widget_destroy,
969 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
970 button, TRUE, TRUE, 0);
971 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
972 gtk_widget_grab_default (button);
973 gtk_widget_show (button);
975 gtk_widget_show (dialog);
979 gtk_file_selection_delete_file_confirmed (GtkWidget *widget,
982 GtkFileSelection *fs = data;
983 CompletionState *cmpl_state;
986 gchar *sys_full_path;
989 g_return_if_fail (fs != NULL);
990 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
992 cmpl_state = (CompletionState*) fs->cmpl_state;
993 path = cmpl_reference_position (cmpl_state);
995 full_path = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
996 sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, NULL);
997 if (unlink (sys_full_path) < 0)
999 buf = g_strconcat ("Error deleting file \"", fs->fileop_file, "\": ",
1000 g_strerror (errno), NULL);
1001 gtk_file_selection_fileop_error (fs, buf);
1004 g_free (sys_full_path);
1006 gtk_widget_destroy (fs->fileop_dialog);
1007 gtk_file_selection_populate (fs, "", FALSE);
1011 gtk_file_selection_delete_file (GtkWidget *widget,
1014 GtkFileSelection *fs = data;
1022 g_return_if_fail (fs != NULL);
1023 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1025 if (fs->fileop_dialog)
1028 #ifdef G_WITH_CYGWIN
1029 translate_win32_path (fs);
1032 filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1033 if (strlen (filename) < 1)
1036 fs->fileop_file = filename;
1039 fs->fileop_dialog = dialog = gtk_dialog_new ();
1040 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1041 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1043 gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
1044 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1046 /* If file dialog is grabbed, grab option dialog */
1047 /* When option dialog is closed, file dialog will be grabbed again */
1048 if (GTK_WINDOW (fs)->modal)
1049 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1051 vbox = gtk_vbox_new (FALSE, 0);
1052 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1053 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1055 gtk_widget_show (vbox);
1057 buf = g_strconcat ("Really delete file \"", filename, "\" ?", NULL);
1058 label = gtk_label_new (buf);
1059 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1060 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1061 gtk_widget_show (label);
1065 button = gtk_button_new_with_label (_("Delete"));
1066 gtk_signal_connect (GTK_OBJECT (button), "clicked",
1067 (GtkSignalFunc) gtk_file_selection_delete_file_confirmed,
1069 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1070 button, TRUE, TRUE, 0);
1071 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1072 gtk_widget_show (button);
1074 button = gtk_button_new_with_label (_("Cancel"));
1075 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1076 (GtkSignalFunc) gtk_widget_destroy,
1078 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1079 button, TRUE, TRUE, 0);
1080 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1081 gtk_widget_grab_default (button);
1082 gtk_widget_show (button);
1084 gtk_widget_show (dialog);
1089 gtk_file_selection_rename_file_confirmed (GtkWidget *widget,
1092 GtkFileSelection *fs = data;
1096 gchar *new_filename;
1097 gchar *old_filename;
1098 gchar *sys_new_filename;
1099 gchar *sys_old_filename;
1100 CompletionState *cmpl_state;
1102 g_return_if_fail (fs != NULL);
1103 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1105 file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1106 cmpl_state = (CompletionState*) fs->cmpl_state;
1107 path = cmpl_reference_position (cmpl_state);
1109 new_filename = g_strconcat (path, G_DIR_SEPARATOR_S, file, NULL);
1110 old_filename = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1112 sys_new_filename = g_filename_from_utf8 (new_filename, -1, NULL, NULL, NULL);
1113 sys_old_filename = g_filename_from_utf8 (old_filename, -1, NULL, NULL, NULL);
1115 if (rename (sys_old_filename, sys_new_filename) < 0)
1117 buf = g_strconcat ("Error renaming file \"", file, "\": ",
1118 g_strerror (errno), NULL);
1119 gtk_file_selection_fileop_error (fs, buf);
1121 g_free (new_filename);
1122 g_free (old_filename);
1123 g_free (sys_new_filename);
1124 g_free (sys_old_filename);
1126 gtk_widget_destroy (fs->fileop_dialog);
1127 gtk_file_selection_populate (fs, "", FALSE);
1131 gtk_file_selection_rename_file (GtkWidget *widget,
1134 GtkFileSelection *fs = data;
1141 g_return_if_fail (fs != NULL);
1142 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1144 if (fs->fileop_dialog)
1147 fs->fileop_file = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1148 if (strlen (fs->fileop_file) < 1)
1152 fs->fileop_dialog = dialog = gtk_dialog_new ();
1153 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1154 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1156 gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File"));
1157 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1159 /* If file dialog is grabbed, grab option dialog */
1160 /* When option dialog closed, file dialog will be grabbed again */
1161 if (GTK_WINDOW (fs)->modal)
1162 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1164 vbox = gtk_vbox_new (FALSE, 0);
1165 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1166 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1168 gtk_widget_show(vbox);
1170 buf = g_strconcat ("Rename file \"", fs->fileop_file, "\" to:", NULL);
1171 label = gtk_label_new(buf);
1172 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1173 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1174 gtk_widget_show (label);
1177 /* New filename entry */
1178 fs->fileop_entry = gtk_entry_new ();
1179 gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry,
1181 GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1182 gtk_widget_show (fs->fileop_entry);
1184 gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
1185 gtk_editable_select_region (GTK_EDITABLE (fs->fileop_entry),
1186 0, strlen (fs->fileop_file));
1189 button = gtk_button_new_with_label (_("Rename"));
1190 gtk_signal_connect (GTK_OBJECT (button), "clicked",
1191 (GtkSignalFunc) gtk_file_selection_rename_file_confirmed,
1193 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1194 button, TRUE, TRUE, 0);
1195 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1196 gtk_widget_show (button);
1198 button = gtk_button_new_with_label (_("Cancel"));
1199 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1200 (GtkSignalFunc) gtk_widget_destroy,
1202 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1203 button, TRUE, TRUE, 0);
1204 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1205 gtk_widget_grab_default (button);
1206 gtk_widget_show (button);
1208 gtk_widget_show (dialog);
1213 gtk_file_selection_key_press (GtkWidget *widget,
1217 GtkFileSelection *fs;
1220 g_return_val_if_fail (widget != NULL, FALSE);
1221 g_return_val_if_fail (event != NULL, FALSE);
1223 if (event->keyval == GDK_Tab)
1225 fs = GTK_FILE_SELECTION (user_data);
1226 #ifdef G_WITH_CYGWIN
1227 translate_win32_path (fs);
1229 text = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1231 text = g_strdup (text);
1233 gtk_file_selection_populate (fs, text, TRUE);
1237 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
1247 gtk_file_selection_history_callback (GtkWidget *widget,
1250 GtkFileSelection *fs = data;
1251 HistoryCallbackArg *callback_arg;
1254 g_return_if_fail (fs != NULL);
1255 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1257 list = fs->history_list;
1260 callback_arg = list->data;
1262 if (callback_arg->menu_item == widget)
1264 gtk_file_selection_populate (fs, callback_arg->directory, FALSE);
1273 gtk_file_selection_update_history_menu (GtkFileSelection *fs,
1274 gchar *current_directory)
1276 HistoryCallbackArg *callback_arg;
1277 GtkWidget *menu_item;
1283 g_return_if_fail (fs != NULL);
1284 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1285 g_return_if_fail (current_directory != NULL);
1287 list = fs->history_list;
1289 if (fs->history_menu)
1292 callback_arg = list->data;
1293 g_free (callback_arg->directory);
1294 g_free (callback_arg);
1297 g_list_free (fs->history_list);
1298 fs->history_list = NULL;
1300 gtk_widget_destroy (fs->history_menu);
1303 fs->history_menu = gtk_menu_new ();
1305 current_dir = g_strdup (current_directory);
1307 dir_len = strlen (current_dir);
1309 for (i = dir_len; i >= 0; i--)
1311 /* the i == dir_len is to catch the full path for the first
1313 if ( (current_dir[i] == G_DIR_SEPARATOR) || (i == dir_len))
1315 /* another small hack to catch the full path */
1317 current_dir[i + 1] = '\0';
1318 #ifdef G_WITH_CYGWIN
1319 if (!strcmp (current_dir, "//"))
1322 menu_item = gtk_menu_item_new_with_label (current_dir);
1324 callback_arg = g_new (HistoryCallbackArg, 1);
1325 callback_arg->menu_item = menu_item;
1327 /* since the autocompletion gets confused if you don't
1328 * supply a trailing '/' on a dir entry, set the full
1329 * (current) path to "" which just refreshes the filesel */
1332 callback_arg->directory = g_strdup ("");
1336 callback_arg->directory = g_strdup (current_dir);
1339 fs->history_list = g_list_append (fs->history_list, callback_arg);
1341 gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
1342 (GtkSignalFunc) gtk_file_selection_history_callback,
1344 gtk_menu_shell_append (GTK_MENU_SHELL (fs->history_menu), menu_item);
1345 gtk_widget_show (menu_item);
1349 gtk_option_menu_set_menu (GTK_OPTION_MENU (fs->history_pulldown),
1351 g_free (current_dir);
1355 gtk_file_selection_file_button (GtkWidget *widget,
1358 GdkEventButton *bevent,
1361 GtkFileSelection *fs = NULL;
1362 gchar *filename, *temp = NULL;
1364 g_return_if_fail (GTK_IS_CLIST (widget));
1367 g_return_if_fail (fs != NULL);
1368 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1370 gtk_clist_get_text (GTK_CLIST (fs->file_list), row, 0, &temp);
1371 filename = g_strdup (temp);
1373 #ifdef G_WITH_CYGWIN
1374 /* Check to see if the selection was a drive selector */
1375 if (isalpha (filename[0]) && (filename[1] == ':')) {
1376 /* It is... map it to a CYGWIN32 drive */
1377 gchar *temp_filename = g_strdup_printf ("//%c/", tolower (filename[0]));
1379 filename = temp_filename;
1381 #endif /* G_WITH_CYGWIN */
1386 switch (bevent->type)
1388 case GDK_2BUTTON_PRESS:
1389 gtk_button_clicked (GTK_BUTTON (fs->ok_button));
1393 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1397 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1404 gtk_file_selection_dir_button (GtkWidget *widget,
1407 GdkEventButton *bevent,
1410 GtkFileSelection *fs = NULL;
1411 gchar *filename, *temp = NULL;
1413 g_return_if_fail (GTK_IS_CLIST (widget));
1415 fs = GTK_FILE_SELECTION (user_data);
1416 g_return_if_fail (fs != NULL);
1417 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1419 gtk_clist_get_text (GTK_CLIST (fs->dir_list), row, 0, &temp);
1420 filename = g_strdup (temp);
1425 switch (bevent->type)
1427 case GDK_2BUTTON_PRESS:
1428 gtk_file_selection_populate (fs, filename, FALSE);
1432 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1436 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1442 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
1445 win32_gtk_add_drives_to_dir_list (GtkWidget *the_dir_list)
1447 gchar *text[2], *textPtr;
1449 char volumeNameBuf[128];
1450 char formatBuffer[128];
1454 /* Get the Drives string */
1455 GetLogicalDriveStrings (sizeof (buffer), buffer);
1457 /* Add the drives as necessary */
1459 while (*textPtr != '\0') {
1460 /* Get the volume information for this drive */
1461 if ((tolower (textPtr[0]) != 'a') && (tolower (textPtr[0]) != 'b'))
1463 /* Ignore floppies (?) */
1464 DWORD maxComponentLength, flags;
1466 GetVolumeInformation (textPtr,
1467 volumeNameBuf, sizeof(volumeNameBuf),
1468 NULL, &maxComponentLength,
1470 /* Build the actual displayable string */
1472 sprintf (formatBuffer, "%c:\\", toupper(textPtr[0]));
1473 #if 0 /* HB: removed to allow drive change AND directory update with one click */
1474 if (strlen (volumeNameBuf) > 0)
1475 sprintf (formatBuffer, "%s (%s)", formatBuffer, volumeNameBuf);
1477 /* Add to the list */
1478 text[0] = formatBuffer;
1479 gtk_clist_append (GTK_CLIST (the_dir_list), text);
1481 textPtr += (strlen (textPtr) + 1);
1487 gtk_file_selection_populate (GtkFileSelection *fs,
1491 CompletionState *cmpl_state;
1492 PossibleCompletion* poss;
1494 gchar* rem_path = rel_path;
1497 gint did_recurse = FALSE;
1498 gint possible_count = 0;
1499 gint selection_index = -1;
1501 g_return_if_fail (fs != NULL);
1502 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1504 cmpl_state = (CompletionState*) fs->cmpl_state;
1505 poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
1507 if (!cmpl_state_okay (cmpl_state))
1509 /* Something went wrong. */
1510 gtk_file_selection_abort (fs);
1514 g_assert (cmpl_state->reference_dir);
1516 gtk_clist_freeze (GTK_CLIST (fs->dir_list));
1517 gtk_clist_clear (GTK_CLIST (fs->dir_list));
1518 gtk_clist_freeze (GTK_CLIST (fs->file_list));
1519 gtk_clist_clear (GTK_CLIST (fs->file_list));
1521 /* Set the dir_list to include ./ and ../ */
1523 text[0] = "." G_DIR_SEPARATOR_S;
1524 gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1526 text[0] = ".." G_DIR_SEPARATOR_S;
1527 gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1531 if (cmpl_is_a_completion (poss))
1533 possible_count += 1;
1535 filename = cmpl_this_completion (poss);
1539 if (cmpl_is_directory (poss))
1541 if (strcmp (filename, "." G_DIR_SEPARATOR_S) != 0 &&
1542 strcmp (filename, ".." G_DIR_SEPARATOR_S) != 0)
1543 gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1547 gtk_clist_append (GTK_CLIST (fs->file_list), text);
1551 poss = cmpl_next_completion (cmpl_state);
1554 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
1555 /* For Windows, add drives as potential selections */
1556 win32_gtk_add_drives_to_dir_list (fs->dir_list);
1559 gtk_clist_thaw (GTK_CLIST (fs->dir_list));
1560 gtk_clist_thaw (GTK_CLIST (fs->file_list));
1562 /* File lists are set. */
1564 g_assert (cmpl_state->reference_dir);
1569 /* User is trying to complete filenames, so advance the user's input
1570 * string to the updated_text, which is the common leading substring
1571 * of all possible completions, and if its a directory attempt
1572 * attempt completions in it. */
1574 if (cmpl_updated_text (cmpl_state)[0])
1577 if (cmpl_updated_dir (cmpl_state))
1579 gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
1583 gtk_file_selection_populate (fs, dir_name, TRUE);
1589 if (fs->selection_entry)
1590 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
1591 cmpl_updated_text (cmpl_state));
1596 selection_index = cmpl_last_valid_char (cmpl_state) -
1597 (strlen (rel_path) - strlen (rem_path));
1598 if (fs->selection_entry)
1599 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
1604 if (fs->selection_entry)
1605 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
1610 if (fs->selection_entry)
1611 gtk_entry_set_position (GTK_ENTRY (fs->selection_entry), selection_index);
1613 if (fs->selection_entry)
1615 sel_text = g_strconcat (_("Selection: "),
1616 cmpl_reference_position (cmpl_state),
1619 gtk_label_set_text (GTK_LABEL (fs->selection_text), sel_text);
1623 if (fs->history_pulldown)
1625 gtk_file_selection_update_history_menu (fs, cmpl_reference_position (cmpl_state));
1632 gtk_file_selection_abort (GtkFileSelection *fs)
1636 sprintf (err_buf, _("Directory unreadable: %s"), cmpl_strerror (cmpl_errno));
1638 /* BEEP gdk_beep(); */
1640 if (fs->selection_entry)
1641 gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
1644 /**********************************************************************/
1645 /* External Interface */
1646 /**********************************************************************/
1648 /* The four completion state selectors
1651 cmpl_updated_text (CompletionState *cmpl_state)
1653 return cmpl_state->updated_text;
1657 cmpl_updated_dir (CompletionState *cmpl_state)
1659 return cmpl_state->re_complete;
1663 cmpl_reference_position (CompletionState *cmpl_state)
1665 return cmpl_state->reference_dir->fullname;
1669 cmpl_last_valid_char (CompletionState *cmpl_state)
1671 return cmpl_state->last_valid_char;
1675 cmpl_completion_fullname (gchar *text,
1676 CompletionState *cmpl_state)
1678 static char nothing[2] = "";
1680 if (!cmpl_state_okay (cmpl_state))
1684 else if (g_path_is_absolute (text))
1686 strcpy (cmpl_state->updated_text, text);
1689 else if (text[0] == '~')
1694 dir = open_user_dir (text, cmpl_state);
1698 /* spencer says just return ~something, so
1699 * for now just do it. */
1700 strcpy (cmpl_state->updated_text, text);
1705 strcpy (cmpl_state->updated_text, dir->fullname);
1707 slash = strchr (text, G_DIR_SEPARATOR);
1710 strcat (cmpl_state->updated_text, slash);
1716 strcpy (cmpl_state->updated_text, cmpl_state->reference_dir->fullname);
1717 if (cmpl_state->updated_text[strlen (cmpl_state->updated_text) - 1] != G_DIR_SEPARATOR)
1718 strcat (cmpl_state->updated_text, G_DIR_SEPARATOR_S);
1719 strcat (cmpl_state->updated_text, text);
1722 return cmpl_state->updated_text;
1725 /* The three completion selectors
1728 cmpl_this_completion (PossibleCompletion* pc)
1734 cmpl_is_directory (PossibleCompletion* pc)
1736 return pc->is_directory;
1740 cmpl_is_a_completion (PossibleCompletion* pc)
1742 return pc->is_a_completion;
1745 /**********************************************************************/
1746 /* Construction, deletion */
1747 /**********************************************************************/
1749 static CompletionState*
1750 cmpl_init_state (void)
1752 gchar *sys_getcwd_buf;
1754 CompletionState *new_state;
1756 new_state = g_new (CompletionState, 1);
1758 /* g_get_current_dir() returns a string in the "system" charset */
1759 sys_getcwd_buf = g_get_current_dir ();
1760 utf8_cwd = g_filename_to_utf8 (sys_getcwd_buf, -1, NULL, NULL, NULL);
1761 g_free (sys_getcwd_buf);
1765 new_state->reference_dir = NULL;
1766 new_state->completion_dir = NULL;
1767 new_state->active_completion_dir = NULL;
1768 new_state->directory_storage = NULL;
1769 new_state->directory_sent_storage = NULL;
1770 new_state->last_valid_char = 0;
1771 new_state->updated_text = g_new (gchar, MAXPATHLEN);
1772 new_state->updated_text_alloc = MAXPATHLEN;
1773 new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
1774 new_state->the_completion.text_alloc = MAXPATHLEN;
1775 new_state->user_dir_name_buffer = NULL;
1776 new_state->user_directories = NULL;
1778 new_state->reference_dir = open_dir (utf8_cwd, new_state);
1780 if (!new_state->reference_dir)
1782 /* Directories changing from underneath us, grumble */
1783 strcpy (utf8_cwd, G_DIR_SEPARATOR_S);
1792 cmpl_free_dir_list (GList* dp0)
1798 free_dir (dp->data);
1806 cmpl_free_dir_sent_list (GList* dp0)
1812 free_dir_sent (dp->data);
1820 cmpl_free_state (CompletionState* cmpl_state)
1822 g_return_if_fail (cmpl_state != NULL);
1824 cmpl_free_dir_list (cmpl_state->directory_storage);
1825 cmpl_free_dir_sent_list (cmpl_state->directory_sent_storage);
1827 if (cmpl_state->user_dir_name_buffer)
1828 g_free (cmpl_state->user_dir_name_buffer);
1829 if (cmpl_state->user_directories)
1830 g_free (cmpl_state->user_directories);
1831 if (cmpl_state->the_completion.text)
1832 g_free (cmpl_state->the_completion.text);
1833 if (cmpl_state->updated_text)
1834 g_free (cmpl_state->updated_text);
1836 g_free (cmpl_state);
1840 free_dir (CompletionDir* dir)
1842 g_free (dir->fullname);
1847 free_dir_sent (CompletionDirSent* sent)
1850 for (i = 0; i < sent->entry_count; i++)
1851 g_free (sent->entries[i].entry_name);
1852 g_free (sent->entries);
1857 prune_memory_usage (CompletionState *cmpl_state)
1859 GList* cdsl = cmpl_state->directory_sent_storage;
1860 GList* cdl = cmpl_state->directory_storage;
1864 for (; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
1869 cmpl_free_dir_sent_list (cdsl->next);
1873 cmpl_state->directory_storage = NULL;
1876 if (cdl->data == cmpl_state->reference_dir)
1877 cmpl_state->directory_storage = g_list_prepend (NULL, cdl->data);
1879 free_dir (cdl->data);
1886 /**********************************************************************/
1887 /* The main entrances. */
1888 /**********************************************************************/
1890 static PossibleCompletion*
1891 cmpl_completion_matches (gchar *text_to_complete,
1892 gchar **remaining_text,
1893 CompletionState *cmpl_state)
1896 PossibleCompletion *poss;
1898 prune_memory_usage (cmpl_state);
1900 g_assert (text_to_complete != NULL);
1902 cmpl_state->user_completion_index = -1;
1903 cmpl_state->last_completion_text = text_to_complete;
1904 cmpl_state->the_completion.text[0] = 0;
1905 cmpl_state->last_valid_char = 0;
1906 cmpl_state->updated_text_len = -1;
1907 cmpl_state->updated_text[0] = 0;
1908 cmpl_state->re_complete = FALSE;
1911 first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
1913 if (text_to_complete[0] == '~' && !first_slash)
1915 /* Text starts with ~ and there is no slash, show all the
1916 * home directory completions.
1918 poss = attempt_homedir_completion (text_to_complete, cmpl_state);
1920 update_cmpl (poss, cmpl_state);
1925 cmpl_state->reference_dir =
1926 open_ref_dir (text_to_complete, remaining_text, cmpl_state);
1928 if (!cmpl_state->reference_dir)
1931 cmpl_state->completion_dir =
1932 find_completion_dir (*remaining_text, remaining_text, cmpl_state);
1934 cmpl_state->last_valid_char = *remaining_text - text_to_complete;
1936 if (!cmpl_state->completion_dir)
1939 cmpl_state->completion_dir->cmpl_index = -1;
1940 cmpl_state->completion_dir->cmpl_parent = NULL;
1941 cmpl_state->completion_dir->cmpl_text = *remaining_text;
1943 cmpl_state->active_completion_dir = cmpl_state->completion_dir;
1945 cmpl_state->reference_dir = cmpl_state->completion_dir;
1947 poss = attempt_file_completion (cmpl_state);
1949 update_cmpl (poss, cmpl_state);
1954 static PossibleCompletion*
1955 cmpl_next_completion (CompletionState* cmpl_state)
1957 PossibleCompletion* poss = NULL;
1959 cmpl_state->the_completion.text[0] = 0;
1962 if (cmpl_state->user_completion_index >= 0)
1963 poss = attempt_homedir_completion (cmpl_state->last_completion_text, cmpl_state);
1965 poss = attempt_file_completion (cmpl_state);
1967 poss = attempt_file_completion (cmpl_state);
1970 update_cmpl (poss, cmpl_state);
1975 /**********************************************************************/
1976 /* Directory Operations */
1977 /**********************************************************************/
1979 /* Open the directory where completion will begin from, if possible. */
1980 static CompletionDir*
1981 open_ref_dir (gchar *text_to_complete,
1982 gchar **remaining_text,
1983 CompletionState *cmpl_state)
1986 CompletionDir *new_dir;
1988 first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
1990 #ifdef G_WITH_CYGWIN
1991 if (text_to_complete[0] == '/' && text_to_complete[1] == '/')
1994 sprintf (root_dir, "//%c", text_to_complete[2]);
1996 new_dir = open_dir (root_dir, cmpl_state);
1999 *remaining_text = text_to_complete + 4;
2007 else if (text_to_complete[0] == '~')
2009 new_dir = open_user_dir (text_to_complete, cmpl_state);
2014 *remaining_text = first_slash + 1;
2016 *remaining_text = text_to_complete + strlen (text_to_complete);
2024 else if (g_path_is_absolute (text_to_complete) || !cmpl_state->reference_dir)
2026 gchar *tmp = g_strdup (text_to_complete);
2030 while (*p && *p != '*' && *p != '?')
2034 p = strrchr (tmp, G_DIR_SEPARATOR);
2042 new_dir = open_dir (tmp, cmpl_state);
2045 *remaining_text = text_to_complete +
2046 ((p == tmp + 1) ? (p - tmp) : (p + 1 - tmp));
2050 /* If no possible candidates, use the cwd */
2051 gchar *sys_curdir = g_get_current_dir ();
2052 gchar *utf8_curdir = g_filename_to_utf8 (sys_curdir, -1, NULL, NULL, NULL);
2054 g_free (sys_curdir);
2056 new_dir = open_dir (utf8_curdir, cmpl_state);
2059 *remaining_text = text_to_complete;
2061 g_free (utf8_curdir);
2068 *remaining_text = text_to_complete;
2070 new_dir = open_dir (cmpl_state->reference_dir->fullname, cmpl_state);
2075 new_dir->cmpl_index = -1;
2076 new_dir->cmpl_parent = NULL;
2084 /* open a directory by user name */
2085 static CompletionDir*
2086 open_user_dir (gchar *text_to_complete,
2087 CompletionState *cmpl_state)
2089 CompletionDir *result;
2093 g_assert (text_to_complete && text_to_complete[0] == '~');
2095 first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2098 cmp_len = first_slash - text_to_complete - 1;
2100 cmp_len = strlen (text_to_complete + 1);
2105 gchar *homedir = g_get_home_dir ();
2106 gchar *utf8_homedir = g_filename_to_utf8 (homedir, -1, NULL, NULL, NULL);
2111 result = open_dir (utf8_homedir, cmpl_state);
2115 g_free (utf8_homedir);
2120 gchar* copy = g_new (char, cmp_len + 1);
2124 strncpy (copy, text_to_complete + 1, cmp_len);
2126 pwd = getpwnam (copy);
2133 utf8_dir = g_filename_to_utf8 (pwd->pw_dir, -1, NULL, NULL, NULL);
2134 result = open_dir (utf8_dir, cmpl_state);
2142 /* open a directory relative the the current relative directory */
2143 static CompletionDir*
2144 open_relative_dir (gchar *dir_name,
2146 CompletionState *cmpl_state)
2148 CompletionDir *result;
2151 path = g_string_sized_new (dir->fullname_len + strlen (dir_name) + 10);
2152 g_string_assign (path, dir->fullname);
2154 if (dir->fullname_len > 1
2155 && path->str[dir->fullname_len - 1] != G_DIR_SEPARATOR)
2156 g_string_append_c (path, G_DIR_SEPARATOR);
2157 g_string_append (path, dir_name);
2159 result = open_dir (path->str, cmpl_state);
2161 g_string_free (path, TRUE);
2166 /* after the cache lookup fails, really open a new directory */
2167 static CompletionDirSent*
2168 open_new_dir (gchar *dir_name,
2170 gboolean stat_subdirs)
2172 CompletionDirSent *sent;
2174 struct dirent *dirent_ptr;
2175 gint entry_count = 0;
2177 struct stat ent_sbuf;
2179 gchar *sys_dir_name;
2181 sent = g_new (CompletionDirSent, 1);
2182 sent->mtime = sbuf->st_mtime;
2183 sent->inode = sbuf->st_ino;
2184 sent->device = sbuf->st_dev;
2186 path = g_string_sized_new (2*MAXPATHLEN + 10);
2188 sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
2189 directory = opendir (sys_dir_name);
2194 g_free (sys_dir_name);
2198 while ((dirent_ptr = readdir (directory)) != NULL)
2201 sent->entries = g_new (CompletionDirEntry, entry_count);
2202 sent->entry_count = entry_count;
2204 rewinddir (directory);
2206 for (i = 0; i < entry_count; i += 1)
2208 dirent_ptr = readdir (directory);
2213 closedir (directory);
2214 g_free (sys_dir_name);
2218 sent->entries[i].entry_name = g_filename_to_utf8 (dirent_ptr->d_name, -1, NULL, NULL, NULL);
2220 g_string_assign (path, sys_dir_name);
2221 if (path->str[path->len-1] != G_DIR_SEPARATOR)
2223 g_string_append_c (path, G_DIR_SEPARATOR);
2225 g_string_append (path, dirent_ptr->d_name);
2229 /* Here we know path->str is a "system charset" string */
2230 if (stat (path->str, &ent_sbuf) >= 0 && S_ISDIR (ent_sbuf.st_mode))
2231 sent->entries[i].is_dir = TRUE;
2233 /* stat may fail, and we don't mind, since it could be a
2234 * dangling symlink. */
2235 sent->entries[i].is_dir = FALSE;
2238 sent->entries[i].is_dir = 1;
2241 g_free (sys_dir_name);
2242 g_string_free (path, TRUE);
2243 qsort (sent->entries, sent->entry_count, sizeof (CompletionDirEntry), compare_cmpl_dir);
2245 closedir (directory);
2250 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
2253 check_dir (gchar *dir_name,
2254 struct stat *result,
2255 gboolean *stat_subdirs)
2257 /* A list of directories that we know only contain other directories.
2258 * Trying to stat every file in these directories would be very
2265 struct stat statbuf;
2266 } no_stat_dirs[] = {
2267 { "/afs", FALSE, { 0 } },
2268 { "/net", FALSE, { 0 } }
2271 static const gint n_no_stat_dirs = G_N_ELEMENTS (no_stat_dirs);
2272 static gboolean initialized = FALSE;
2273 gchar *sys_dir_name;
2279 for (i = 0; i < n_no_stat_dirs; i++)
2281 if (stat (no_stat_dirs[i].name, &no_stat_dirs[i].statbuf) == 0)
2282 no_stat_dirs[i].present = TRUE;
2286 sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
2287 if (stat (sys_dir_name, result) < 0)
2289 g_free (sys_dir_name);
2293 g_free (sys_dir_name);
2295 *stat_subdirs = TRUE;
2296 for (i = 0; i < n_no_stat_dirs; i++)
2298 if (no_stat_dirs[i].present &&
2299 (no_stat_dirs[i].statbuf.st_dev == result->st_dev) &&
2300 (no_stat_dirs[i].statbuf.st_ino == result->st_ino))
2302 *stat_subdirs = FALSE;
2312 /* open a directory by absolute pathname */
2313 static CompletionDir*
2314 open_dir (gchar *dir_name,
2315 CompletionState *cmpl_state)
2318 gboolean stat_subdirs;
2319 CompletionDirSent *sent;
2322 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
2323 if (!check_dir (dir_name, &sbuf, &stat_subdirs))
2326 cdsl = cmpl_state->directory_sent_storage;
2332 if (sent->inode == sbuf.st_ino &&
2333 sent->mtime == sbuf.st_mtime &&
2334 sent->device == sbuf.st_dev)
2335 return attach_dir (sent, dir_name, cmpl_state);
2340 stat_subdirs = TRUE;
2343 sent = open_new_dir (dir_name, &sbuf, stat_subdirs);
2347 cmpl_state->directory_sent_storage =
2348 g_list_prepend (cmpl_state->directory_sent_storage, sent);
2350 return attach_dir (sent, dir_name, cmpl_state);
2356 static CompletionDir*
2357 attach_dir (CompletionDirSent *sent,
2359 CompletionState *cmpl_state)
2361 CompletionDir* new_dir;
2363 new_dir = g_new (CompletionDir, 1);
2365 cmpl_state->directory_storage =
2366 g_list_prepend (cmpl_state->directory_storage, new_dir);
2368 new_dir->sent = sent;
2369 new_dir->fullname = g_strdup (dir_name);
2370 new_dir->fullname_len = strlen (dir_name);
2376 correct_dir_fullname (CompletionDir* cmpl_dir)
2378 gint length = strlen (cmpl_dir->fullname);
2379 gchar *first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
2380 gchar *sys_filename;
2383 /* Does it end with /. (\.) ? */
2385 strcmp (cmpl_dir->fullname + length - 2, G_DIR_SEPARATOR_S ".") == 0)
2387 /* Is it just the root directory (on a drive) ? */
2388 if (cmpl_dir->fullname + length - 2 == first_slash)
2390 cmpl_dir->fullname[length - 1] = 0;
2391 cmpl_dir->fullname_len = length - 1;
2396 cmpl_dir->fullname[length - 2] = 0;
2400 /* Ends with /./ (\.\)? */
2401 else if (length >= 3 &&
2402 strcmp (cmpl_dir->fullname + length - 3,
2403 G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) == 0)
2404 cmpl_dir->fullname[length - 2] = 0;
2406 /* Ends with /.. (\..) ? */
2407 else if (length >= 3 &&
2408 strcmp (cmpl_dir->fullname + length - 3,
2409 G_DIR_SEPARATOR_S "..") == 0)
2411 /* Is it just /.. (X:\..)? */
2412 if (cmpl_dir->fullname + length - 3 == first_slash)
2414 cmpl_dir->fullname[length - 2] = 0;
2415 cmpl_dir->fullname_len = length - 2;
2419 sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
2420 if (stat (sys_filename, &sbuf) < 0)
2422 g_free (sys_filename);
2426 g_free (sys_filename);
2428 cmpl_dir->fullname[length - 3] = 0;
2430 if (!correct_parent (cmpl_dir, &sbuf))
2434 /* Ends with /../ (\..\)? */
2435 else if (length >= 4 &&
2436 strcmp (cmpl_dir->fullname + length - 4,
2437 G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) == 0)
2439 /* Is it just /../ (X:\..\)? */
2440 if (cmpl_dir->fullname + length - 4 == first_slash)
2442 cmpl_dir->fullname[length - 3] = 0;
2443 cmpl_dir->fullname_len = length - 3;
2447 sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
2448 if (stat (sys_filename, &sbuf) < 0)
2450 g_free (sys_filename);
2454 g_free (sys_filename);
2456 cmpl_dir->fullname[length - 4] = 0;
2458 if (!correct_parent (cmpl_dir, &sbuf))
2462 cmpl_dir->fullname_len = strlen (cmpl_dir->fullname);
2468 correct_parent (CompletionDir *cmpl_dir,
2475 gchar *sys_filename;
2478 last_slash = strrchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
2479 g_assert (last_slash);
2480 first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
2482 /* Clever (?) way to check for top-level directory that works also on
2483 * Win32, where there is a drive letter and colon prefixed...
2485 if (last_slash != first_slash)
2495 sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
2496 if (stat (sys_filename, &parbuf) < 0)
2498 g_free (sys_filename);
2501 last_slash[0] = G_DIR_SEPARATOR;
2504 g_free (sys_filename);
2506 #ifndef G_OS_WIN32 /* No inode numbers on Win32 */
2507 if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
2508 /* it wasn't a link */
2514 last_slash[0] = G_DIR_SEPARATOR;
2516 /* it was a link, have to figure it out the hard way */
2518 new_name = find_parent_dir_fullname (cmpl_dir->fullname);
2523 g_free (cmpl_dir->fullname);
2525 cmpl_dir->fullname = new_name;
2534 find_parent_dir_fullname (gchar* dirname)
2536 gchar *sys_orig_dir;
2541 sys_orig_dir = g_get_current_dir ();
2542 sys_dirname = g_filename_from_utf8 (dirname, -1, NULL, NULL, NULL);
2543 if (chdir (sys_dirname) != 0 || chdir ("..") != 0)
2545 g_free (sys_dirname);
2546 g_free (sys_orig_dir);
2550 g_free (sys_dirname);
2552 sys_cwd = g_get_current_dir ();
2553 result = g_filename_to_utf8 (sys_cwd, -1, NULL, NULL, NULL);
2556 if (chdir (sys_orig_dir) != 0)
2559 g_free (sys_orig_dir);
2563 g_free (sys_orig_dir);
2569 /**********************************************************************/
2570 /* Completion Operations */
2571 /**********************************************************************/
2575 static PossibleCompletion*
2576 attempt_homedir_completion (gchar *text_to_complete,
2577 CompletionState *cmpl_state)
2581 if (!cmpl_state->user_dir_name_buffer &&
2582 !get_pwdb (cmpl_state))
2584 length = strlen (text_to_complete) - 1;
2586 cmpl_state->user_completion_index += 1;
2588 while (cmpl_state->user_completion_index < cmpl_state->user_directories_len)
2590 index = first_diff_index (text_to_complete + 1,
2591 cmpl_state->user_directories
2592 [cmpl_state->user_completion_index].login);
2599 if (cmpl_state->last_valid_char < (index + 1))
2600 cmpl_state->last_valid_char = index + 1;
2601 cmpl_state->user_completion_index += 1;
2605 cmpl_state->the_completion.is_a_completion = 1;
2606 cmpl_state->the_completion.is_directory = TRUE;
2608 append_completion_text ("~", cmpl_state);
2610 append_completion_text (cmpl_state->
2611 user_directories[cmpl_state->user_completion_index].login,
2614 return append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
2617 if (text_to_complete[1]
2618 || cmpl_state->user_completion_index > cmpl_state->user_directories_len)
2620 cmpl_state->user_completion_index = -1;
2625 cmpl_state->user_completion_index += 1;
2626 cmpl_state->the_completion.is_a_completion = 1;
2627 cmpl_state->the_completion.is_directory = TRUE;
2629 return append_completion_text ("~" G_DIR_SEPARATOR_S, cmpl_state);
2635 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
2636 #define FOLD(c) (tolower(c))
2641 /* returns the index (>= 0) of the first differing character,
2642 * PATTERN_MATCH if the completion matches */
2644 first_diff_index (gchar *pat,
2649 while (*pat && *text && FOLD (*text) == FOLD (*pat))
2659 return PATTERN_MATCH;
2662 static PossibleCompletion*
2663 append_completion_text (gchar *text,
2664 CompletionState *cmpl_state)
2668 if (!cmpl_state->the_completion.text)
2671 len = strlen (text) + strlen (cmpl_state->the_completion.text) + 1;
2673 if (cmpl_state->the_completion.text_alloc > len)
2675 strcat (cmpl_state->the_completion.text, text);
2676 return &cmpl_state->the_completion;
2682 cmpl_state->the_completion.text_alloc = i;
2684 cmpl_state->the_completion.text = (gchar*) g_realloc (cmpl_state->the_completion.text, i);
2686 if (!cmpl_state->the_completion.text)
2690 strcat (cmpl_state->the_completion.text, text);
2691 return &cmpl_state->the_completion;
2695 static CompletionDir*
2696 find_completion_dir (gchar *text_to_complete,
2697 gchar **remaining_text,
2698 CompletionState *cmpl_state)
2700 gchar* first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2701 CompletionDir* dir = cmpl_state->reference_dir;
2702 CompletionDir* next;
2703 *remaining_text = text_to_complete;
2707 gint len = first_slash - *remaining_text;
2709 gchar *found_name = NULL; /* Quiet gcc */
2711 gchar* pat_buf = g_new (gchar, len + 1);
2713 strncpy (pat_buf, *remaining_text, len);
2716 for (i = 0; i < dir->sent->entry_count; i += 1)
2718 if (dir->sent->entries[i].is_dir &&
2719 fnmatch (pat_buf, dir->sent->entries[i].entry_name,
2720 FNMATCH_FLAGS)!= FNM_NOMATCH)
2730 found_name = dir->sent->entries[i].entry_name;
2737 /* Perhaps we are trying to open an automount directory */
2738 found_name = pat_buf;
2741 next = open_relative_dir (found_name, dir, cmpl_state);
2749 next->cmpl_parent = dir;
2753 if (!correct_dir_fullname (dir))
2759 *remaining_text = first_slash + 1;
2760 first_slash = strchr (*remaining_text, G_DIR_SEPARATOR);
2769 update_cmpl (PossibleCompletion *poss,
2770 CompletionState *cmpl_state)
2774 if (!poss || !cmpl_is_a_completion (poss))
2777 cmpl_len = strlen (cmpl_this_completion (poss));
2779 if (cmpl_state->updated_text_alloc < cmpl_len + 1)
2781 cmpl_state->updated_text =
2782 (gchar*)g_realloc (cmpl_state->updated_text,
2783 cmpl_state->updated_text_alloc);
2784 cmpl_state->updated_text_alloc = 2*cmpl_len;
2787 if (cmpl_state->updated_text_len < 0)
2789 strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
2790 cmpl_state->updated_text_len = cmpl_len;
2791 cmpl_state->re_complete = cmpl_is_directory (poss);
2793 else if (cmpl_state->updated_text_len == 0)
2795 cmpl_state->re_complete = FALSE;
2800 first_diff_index (cmpl_state->updated_text,
2801 cmpl_this_completion (poss));
2803 cmpl_state->re_complete = FALSE;
2805 if (first_diff == PATTERN_MATCH)
2808 if (first_diff > cmpl_state->updated_text_len)
2809 strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
2811 cmpl_state->updated_text_len = first_diff;
2812 cmpl_state->updated_text[first_diff] = 0;
2816 static PossibleCompletion*
2817 attempt_file_completion (CompletionState *cmpl_state)
2819 gchar *pat_buf, *first_slash;
2820 CompletionDir *dir = cmpl_state->active_completion_dir;
2822 dir->cmpl_index += 1;
2824 if (dir->cmpl_index == dir->sent->entry_count)
2826 if (dir->cmpl_parent == NULL)
2828 cmpl_state->active_completion_dir = NULL;
2834 cmpl_state->active_completion_dir = dir->cmpl_parent;
2836 return attempt_file_completion (cmpl_state);
2840 g_assert (dir->cmpl_text);
2842 first_slash = strchr (dir->cmpl_text, G_DIR_SEPARATOR);
2846 gint len = first_slash - dir->cmpl_text;
2848 pat_buf = g_new (gchar, len + 1);
2849 strncpy (pat_buf, dir->cmpl_text, len);
2854 gint len = strlen (dir->cmpl_text);
2856 pat_buf = g_new (gchar, len + 2);
2857 strcpy (pat_buf, dir->cmpl_text);
2858 /* Don't append a * if the user entered one herself.
2859 * This way one can complete *.h and don't get matches
2860 * on any .help files, for instance.
2862 if (strchr (pat_buf, '*') == NULL)
2863 strcpy (pat_buf + len, "*");
2868 if (dir->sent->entries[dir->cmpl_index].is_dir)
2870 if (fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
2871 FNMATCH_FLAGS) != FNM_NOMATCH)
2873 CompletionDir* new_dir;
2875 new_dir = open_relative_dir (dir->sent->entries[dir->cmpl_index].entry_name,
2884 new_dir->cmpl_parent = dir;
2886 new_dir->cmpl_index = -1;
2887 new_dir->cmpl_text = first_slash + 1;
2889 cmpl_state->active_completion_dir = new_dir;
2892 return attempt_file_completion (cmpl_state);
2897 return attempt_file_completion (cmpl_state);
2903 return attempt_file_completion (cmpl_state);
2908 if (dir->cmpl_parent != NULL)
2910 append_completion_text (dir->fullname +
2911 strlen (cmpl_state->completion_dir->fullname) + 1,
2913 append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
2916 append_completion_text (dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
2918 cmpl_state->the_completion.is_a_completion =
2919 fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
2920 FNMATCH_FLAGS) != FNM_NOMATCH;
2922 cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
2923 if (dir->sent->entries[dir->cmpl_index].is_dir)
2924 append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
2927 return &cmpl_state->the_completion;
2934 get_pwdb (CompletionState* cmpl_state)
2936 struct passwd *pwd_ptr;
2939 gint len = 0, i, count = 0;
2941 if (cmpl_state->user_dir_name_buffer)
2945 while ((pwd_ptr = getpwent ()) != NULL)
2947 utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
2948 len += strlen (utf8);
2950 utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
2951 len += strlen (utf8);
2959 cmpl_state->user_dir_name_buffer = g_new (gchar, len);
2960 cmpl_state->user_directories = g_new (CompletionUserDir, count);
2961 cmpl_state->user_directories_len = count;
2963 buf_ptr = cmpl_state->user_dir_name_buffer;
2965 for (i = 0; i < count; i += 1)
2967 pwd_ptr = getpwent ();
2974 utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
2975 strcpy (buf_ptr, utf8);
2978 cmpl_state->user_directories[i].login = buf_ptr;
2980 buf_ptr += strlen (buf_ptr);
2983 utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
2984 strcpy (buf_ptr, utf8);
2987 cmpl_state->user_directories[i].homedir = buf_ptr;
2989 buf_ptr += strlen (buf_ptr);
2993 qsort (cmpl_state->user_directories,
2994 cmpl_state->user_directories_len,
2995 sizeof (CompletionUserDir),
3004 if (cmpl_state->user_dir_name_buffer)
3005 g_free (cmpl_state->user_dir_name_buffer);
3006 if (cmpl_state->user_directories)
3007 g_free (cmpl_state->user_directories);
3009 cmpl_state->user_dir_name_buffer = NULL;
3010 cmpl_state->user_directories = NULL;
3016 compare_user_dir (const void *a,
3019 return strcmp ((((CompletionUserDir*)a))->login,
3020 (((CompletionUserDir*)b))->login);
3026 compare_cmpl_dir (const void *a,
3029 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
3030 return strcmp ((((CompletionDirEntry*)a))->entry_name,
3031 (((CompletionDirEntry*)b))->entry_name);
3033 return g_strcasecmp ((((CompletionDirEntry*)a))->entry_name,
3034 (((CompletionDirEntry*)b))->entry_name);
3039 cmpl_state_okay (CompletionState* cmpl_state)
3041 return cmpl_state && cmpl_state->reference_dir;
3045 cmpl_strerror (gint err)
3047 if (err == CMPL_ERRNO_TOO_LONG)
3048 return "Name too long";
3050 return g_strerror (err);