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 Library 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 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library 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-1999. 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"
61 #include "gtksignal.h"
64 #include "gtkmenuitem.h"
65 #include "gtkoptionmenu.h"
67 #include "gtkdialog.h"
77 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
79 #define mkdir(path,mode) mkdir(path)
83 #define DIR_LIST_WIDTH 180
84 #define DIR_LIST_HEIGHT 180
85 #define FILE_LIST_WIDTH 180
86 #define FILE_LIST_HEIGHT 180
88 /* I've put this here so it doesn't get confused with the
89 * file completion interface */
90 typedef struct _HistoryCallbackArg HistoryCallbackArg;
92 struct _HistoryCallbackArg
99 typedef struct _CompletionState CompletionState;
100 typedef struct _CompletionDir CompletionDir;
101 typedef struct _CompletionDirSent CompletionDirSent;
102 typedef struct _CompletionDirEntry CompletionDirEntry;
103 typedef struct _CompletionUserDir CompletionUserDir;
104 typedef struct _PossibleCompletion PossibleCompletion;
106 /* Non-external file completion decls and structures */
108 /* A contant telling PRCS how many directories to cache. Its actually
109 * kept in a list, so the geometry isn't important. */
110 #define CMPL_DIRECTORY_CACHE_SIZE 10
112 /* A constant used to determine whether a substring was an exact
113 * match by first_diff_index()
115 #define PATTERN_MATCH -1
116 /* The arguments used by all fnmatch() calls below
118 #define FNMATCH_FLAGS (FNM_PATHNAME | FNM_PERIOD)
120 #define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
122 /* This structure contains all the useful information about a directory
123 * for the purposes of filename completion. These structures are cached
124 * in the CompletionState struct. CompletionDir's are reference counted.
126 struct _CompletionDirSent
133 gchar *name_buffer; /* memory segment containing names of all entries */
135 struct _CompletionDirEntry *entries;
138 struct _CompletionDir
140 CompletionDirSent *sent;
145 struct _CompletionDir *cmpl_parent;
150 /* This structure contains pairs of directory entry names with a flag saying
151 * whether or not they are a valid directory. NOTE: This information is used
152 * to provide the caller with information about whether to update its completions
153 * or try to open a file. Since directories are cached by the directory mtime,
154 * a symlink which points to an invalid file (which will not be a directory),
155 * will not be reevaluated if that file is created, unless the containing
156 * directory is touched. I consider this case to be worth ignoring (josh).
158 struct _CompletionDirEntry
164 struct _CompletionUserDir
170 struct _PossibleCompletion
172 /* accessible fields, all are accessed externally by functions
176 gint is_a_completion;
184 struct _CompletionState
186 gint last_valid_char;
188 gint updated_text_len;
189 gint updated_text_alloc;
192 gchar *user_dir_name_buffer;
193 gint user_directories_len;
195 gchar *last_completion_text;
197 gint user_completion_index; /* if >= 0, currently completing ~user */
199 struct _CompletionDir *completion_dir; /* directory completing from */
200 struct _CompletionDir *active_completion_dir;
202 struct _PossibleCompletion the_completion;
204 struct _CompletionDir *reference_dir; /* initial directory */
206 GList* directory_storage;
207 GList* directory_sent_storage;
209 struct _CompletionUserDir *user_directories;
213 /* File completion functions which would be external, were they used
214 * outside of this file.
217 static CompletionState* cmpl_init_state (void);
218 static void cmpl_free_state (CompletionState *cmpl_state);
219 static gint cmpl_state_okay (CompletionState* cmpl_state);
220 static gchar* cmpl_strerror (gint);
222 static PossibleCompletion* cmpl_completion_matches(gchar *text_to_complete,
223 gchar **remaining_text,
224 CompletionState *cmpl_state);
226 /* Returns a name for consideration, possibly a completion, this name
227 * will be invalid after the next call to cmpl_next_completion.
229 static char* cmpl_this_completion (PossibleCompletion*);
231 /* True if this completion matches the given text. Otherwise, this
232 * output can be used to have a list of non-completions.
234 static gint cmpl_is_a_completion (PossibleCompletion*);
236 /* True if the completion is a directory
238 static gint cmpl_is_directory (PossibleCompletion*);
240 /* Obtains the next completion, or NULL
242 static PossibleCompletion* cmpl_next_completion (CompletionState*);
244 /* Updating completions: the return value of cmpl_updated_text() will
245 * be text_to_complete completed as much as possible after the most
246 * recent call to cmpl_completion_matches. For the present
247 * application, this is the suggested replacement for the user's input
248 * string. You must CALL THIS AFTER ALL cmpl_text_completions have
251 static gchar* cmpl_updated_text (CompletionState* cmpl_state);
253 /* After updating, to see if the completion was a directory, call
254 * this. If it was, you should consider re-calling completion_matches.
256 static gint cmpl_updated_dir (CompletionState* cmpl_state);
258 /* Current location: if using file completion, return the current
259 * directory, from which file completion begins. More specifically,
260 * the cwd concatenated with all exact completions up to the last
261 * directory delimiter('/').
263 static gchar* cmpl_reference_position (CompletionState* cmpl_state);
265 /* backing up: if cmpl_completion_matches returns NULL, you may query
266 * the index of the last completable character into cmpl_updated_text.
268 static gint cmpl_last_valid_char (CompletionState* cmpl_state);
270 /* When the user selects a non-directory, call cmpl_completion_fullname
271 * to get the full name of the selected file.
273 static gchar* cmpl_completion_fullname (gchar*, CompletionState* cmpl_state);
276 /* Directory operations. */
277 static CompletionDir* open_ref_dir (gchar* text_to_complete,
278 gchar** remaining_text,
279 CompletionState* cmpl_state);
281 static gboolean check_dir (gchar *dir_name,
283 gboolean *stat_subdirs);
285 static CompletionDir* open_dir (gchar* dir_name,
286 CompletionState* cmpl_state);
288 static CompletionDir* open_user_dir (gchar* text_to_complete,
289 CompletionState *cmpl_state);
291 static CompletionDir* open_relative_dir (gchar* dir_name, CompletionDir* dir,
292 CompletionState *cmpl_state);
293 static CompletionDirSent* open_new_dir (gchar* dir_name,
295 gboolean stat_subdirs);
296 static gint correct_dir_fullname (CompletionDir* cmpl_dir);
297 static gint correct_parent (CompletionDir* cmpl_dir,
299 static gchar* find_parent_dir_fullname (gchar* dirname);
300 static CompletionDir* attach_dir (CompletionDirSent* sent,
302 CompletionState *cmpl_state);
303 static void free_dir_sent (CompletionDirSent* sent);
304 static void free_dir (CompletionDir *dir);
305 static void prune_memory_usage(CompletionState *cmpl_state);
307 /* Completion operations */
308 static PossibleCompletion* attempt_homedir_completion(gchar* text_to_complete,
309 CompletionState *cmpl_state);
310 static PossibleCompletion* attempt_file_completion(CompletionState *cmpl_state);
311 static CompletionDir* find_completion_dir(gchar* text_to_complete,
312 gchar** remaining_text,
313 CompletionState* cmpl_state);
314 static PossibleCompletion* append_completion_text(gchar* text,
315 CompletionState* cmpl_state);
317 static gint get_pwdb(CompletionState* cmpl_state);
318 static gint compare_user_dir(const void* a, const void* b);
320 static gint first_diff_index(gchar* pat, gchar* text);
321 static gint compare_cmpl_dir(const void* a, const void* b);
322 static void update_cmpl(PossibleCompletion* poss,
323 CompletionState* cmpl_state);
325 static void gtk_file_selection_class_init (GtkFileSelectionClass *klass);
326 static void gtk_file_selection_init (GtkFileSelection *filesel);
327 static void gtk_file_selection_destroy (GtkObject *object);
328 static gint gtk_file_selection_key_press (GtkWidget *widget,
332 static void gtk_file_selection_file_button (GtkWidget *widget,
335 GdkEventButton *bevent,
338 static void gtk_file_selection_dir_button (GtkWidget *widget,
341 GdkEventButton *bevent,
344 static void gtk_file_selection_populate (GtkFileSelection *fs,
347 static void gtk_file_selection_abort (GtkFileSelection *fs);
349 static void gtk_file_selection_update_history_menu (GtkFileSelection *fs,
352 static void gtk_file_selection_create_dir (GtkWidget *widget, gpointer data);
353 static void gtk_file_selection_delete_file (GtkWidget *widget, gpointer data);
354 static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data);
358 static GtkWindowClass *parent_class = NULL;
360 /* Saves errno when something cmpl does fails. */
361 static gint cmpl_errno;
365 * Take the path currently in the file selection
366 * entry field and translate as necessary from
367 * a WIN32 style to CYGWIN32 style path. For
368 * instance translate:
369 * x:\somepath\file.jpg
371 * //x/somepath/file.jpg
373 * Replace the path in the selection text field.
374 * Return a boolean value concerning whether a
375 * translation had to be made.
378 translate_win32_path(GtkFileSelection *filesel)
384 * Retrieve the current path
386 path = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
389 * Translate only if this looks like a DOS-ish
390 * path... First handle any drive letters.
392 if (isalpha(path[0]) && (path[1] == ':')) {
394 * This part kind of stinks... It isn't possible
395 * to know if there is enough space in the current
396 * string for the extra character required in this
397 * conversion. Assume that there isn't enough space
398 * and use the set function on the text field to
399 * set the newly created string.
403 newPath = g_malloc(strlen(path) + 2);
404 sprintf(newPath, "//%c/%s", path[0], (path + 3));
405 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), newPath);
412 * Now, replace backslashes with forward slashes
415 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_WINDOW, &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_WINDOW);
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;
476 char *file_title [2];
478 filesel->cmpl_state = cmpl_init_state ();
480 /* The dialog-sized vertical box */
481 filesel->main_vbox = gtk_vbox_new (FALSE, 10);
482 gtk_container_set_border_width (GTK_CONTAINER (filesel), 10);
483 gtk_container_add (GTK_CONTAINER (filesel), filesel->main_vbox);
484 gtk_widget_show (filesel->main_vbox);
486 /* The horizontal box containing create, rename etc. buttons */
487 filesel->button_area = gtk_hbutton_box_new ();
488 gtk_button_box_set_layout(GTK_BUTTON_BOX(filesel->button_area), GTK_BUTTONBOX_START);
489 gtk_button_box_set_spacing(GTK_BUTTON_BOX(filesel->button_area), 0);
490 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->button_area,
492 gtk_widget_show (filesel->button_area);
494 gtk_file_selection_show_fileop_buttons(filesel);
496 /* hbox for pulldown menu */
497 pulldown_hbox = gtk_hbox_new (TRUE, 5);
498 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), pulldown_hbox, FALSE, FALSE, 0);
499 gtk_widget_show (pulldown_hbox);
502 filesel->history_pulldown = gtk_option_menu_new ();
503 gtk_widget_show (filesel->history_pulldown);
504 gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->history_pulldown,
507 /* The horizontal box containing the directory and file listboxes */
508 list_hbox = gtk_hbox_new (FALSE, 5);
509 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), list_hbox, TRUE, TRUE, 0);
510 gtk_widget_show (list_hbox);
512 /* The directories clist */
513 dir_title[0] = _("Directories");
515 filesel->dir_list = gtk_clist_new_with_titles (1, (gchar**) dir_title);
516 gtk_widget_set_usize (filesel->dir_list, DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
517 gtk_signal_connect (GTK_OBJECT (filesel->dir_list), "select_row",
518 (GtkSignalFunc) gtk_file_selection_dir_button,
520 gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list));
522 scrolled_win = gtk_scrolled_window_new (NULL, NULL);
523 gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->dir_list);
524 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
525 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
526 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
527 gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
528 gtk_widget_show (filesel->dir_list);
529 gtk_widget_show (scrolled_win);
531 /* The files clist */
532 file_title[0] = _("Files");
533 file_title[1] = NULL;
534 filesel->file_list = gtk_clist_new_with_titles (1, (gchar**) file_title);
535 gtk_widget_set_usize (filesel->file_list, FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
536 gtk_signal_connect (GTK_OBJECT (filesel->file_list), "select_row",
537 (GtkSignalFunc) gtk_file_selection_file_button,
539 gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list));
541 scrolled_win = gtk_scrolled_window_new (NULL, NULL);
542 gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->file_list);
543 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
544 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
545 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
546 gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
547 gtk_widget_show (filesel->file_list);
548 gtk_widget_show (scrolled_win);
550 /* action area for packing buttons into. */
551 filesel->action_area = gtk_hbox_new (TRUE, 0);
552 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->action_area,
554 gtk_widget_show (filesel->action_area);
556 /* The OK/Cancel button area */
557 confirm_area = gtk_hbutton_box_new ();
558 gtk_button_box_set_layout(GTK_BUTTON_BOX(confirm_area), GTK_BUTTONBOX_END);
559 gtk_button_box_set_spacing(GTK_BUTTON_BOX(confirm_area), 5);
560 gtk_box_pack_end (GTK_BOX (filesel->main_vbox), confirm_area, FALSE, FALSE, 0);
561 gtk_widget_show (confirm_area);
564 filesel->ok_button = gtk_button_new_with_label (_("OK"));
565 GTK_WIDGET_SET_FLAGS (filesel->ok_button, GTK_CAN_DEFAULT);
566 gtk_box_pack_start (GTK_BOX (confirm_area), filesel->ok_button, TRUE, TRUE, 0);
567 gtk_widget_grab_default (filesel->ok_button);
568 gtk_widget_show (filesel->ok_button);
570 /* The Cancel button */
571 filesel->cancel_button = gtk_button_new_with_label (_("Cancel"));
572 GTK_WIDGET_SET_FLAGS (filesel->cancel_button, GTK_CAN_DEFAULT);
573 gtk_box_pack_start (GTK_BOX (confirm_area), filesel->cancel_button, TRUE, TRUE, 0);
574 gtk_widget_show (filesel->cancel_button);
576 /* The selection entry widget */
577 entry_vbox = gtk_vbox_new (FALSE, 2);
578 gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 0);
579 gtk_widget_show (entry_vbox);
581 filesel->selection_text = label = gtk_label_new ("");
582 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
583 gtk_box_pack_start (GTK_BOX (entry_vbox), label, FALSE, FALSE, 0);
584 gtk_widget_show (label);
586 filesel->selection_entry = gtk_entry_new ();
587 gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "key_press_event",
588 (GtkSignalFunc) gtk_file_selection_key_press, filesel);
589 gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "focus_in_event",
590 (GtkSignalFunc) gtk_widget_grab_default,
591 GTK_OBJECT (filesel->ok_button));
592 gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "activate",
593 (GtkSignalFunc) gtk_button_clicked,
594 GTK_OBJECT (filesel->ok_button));
595 gtk_box_pack_start (GTK_BOX (entry_vbox), filesel->selection_entry, TRUE, TRUE, 0);
596 gtk_widget_show (filesel->selection_entry);
598 if (!cmpl_state_okay (filesel->cmpl_state))
602 sprintf (err_buf, _("Directory unreadable: %s"), cmpl_strerror (cmpl_errno));
604 gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
608 gtk_file_selection_populate (filesel, "", FALSE);
611 gtk_widget_grab_focus (filesel->selection_entry);
615 gtk_file_selection_new (const gchar *title)
617 GtkFileSelection *filesel;
619 filesel = gtk_type_new (GTK_TYPE_FILE_SELECTION);
620 gtk_window_set_title (GTK_WINDOW (filesel), title);
622 return GTK_WIDGET (filesel);
626 gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
628 g_return_if_fail (filesel != NULL);
629 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
631 /* delete, create directory, and rename */
632 if (!filesel->fileop_c_dir)
634 filesel->fileop_c_dir = gtk_button_new_with_label (_("Create Dir"));
635 gtk_signal_connect (GTK_OBJECT (filesel->fileop_c_dir), "clicked",
636 (GtkSignalFunc) gtk_file_selection_create_dir,
638 gtk_box_pack_start (GTK_BOX (filesel->button_area),
639 filesel->fileop_c_dir, TRUE, TRUE, 0);
640 gtk_widget_show (filesel->fileop_c_dir);
643 if (!filesel->fileop_del_file)
645 filesel->fileop_del_file = gtk_button_new_with_label (_("Delete File"));
646 gtk_signal_connect (GTK_OBJECT (filesel->fileop_del_file), "clicked",
647 (GtkSignalFunc) gtk_file_selection_delete_file,
649 gtk_box_pack_start (GTK_BOX (filesel->button_area),
650 filesel->fileop_del_file, TRUE, TRUE, 0);
651 gtk_widget_show (filesel->fileop_del_file);
654 if (!filesel->fileop_ren_file)
656 filesel->fileop_ren_file = gtk_button_new_with_label (_("Rename File"));
657 gtk_signal_connect (GTK_OBJECT (filesel->fileop_ren_file), "clicked",
658 (GtkSignalFunc) gtk_file_selection_rename_file,
660 gtk_box_pack_start (GTK_BOX (filesel->button_area),
661 filesel->fileop_ren_file, TRUE, TRUE, 0);
662 gtk_widget_show (filesel->fileop_ren_file);
665 gtk_widget_queue_resize(GTK_WIDGET(filesel));
669 gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
671 g_return_if_fail (filesel != NULL);
672 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
674 if (filesel->fileop_ren_file)
676 gtk_widget_destroy (filesel->fileop_ren_file);
677 filesel->fileop_ren_file = NULL;
680 if (filesel->fileop_del_file)
682 gtk_widget_destroy (filesel->fileop_del_file);
683 filesel->fileop_del_file = NULL;
686 if (filesel->fileop_c_dir)
688 gtk_widget_destroy (filesel->fileop_c_dir);
689 filesel->fileop_c_dir = NULL;
696 gtk_file_selection_set_filename (GtkFileSelection *filesel,
697 const gchar *filename)
699 char buf[MAXPATHLEN];
700 const char *name, *last_slash;
702 g_return_if_fail (filesel != NULL);
703 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
704 g_return_if_fail (filename != NULL);
706 last_slash = strrchr (filename, G_DIR_SEPARATOR);
715 gint len = MIN (MAXPATHLEN - 1, last_slash - filename + 1);
717 strncpy (buf, filename, len);
720 name = last_slash + 1;
723 gtk_file_selection_populate (filesel, buf, FALSE);
725 if (filesel->selection_entry)
726 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
730 gtk_file_selection_get_filename (GtkFileSelection *filesel)
732 static char nothing[2] = "";
736 g_return_val_if_fail (filesel != NULL, nothing);
737 g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
740 translate_win32_path(filesel);
742 text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
745 filename = cmpl_completion_fullname (text, filesel->cmpl_state);
753 gtk_file_selection_complete (GtkFileSelection *filesel,
754 const gchar *pattern)
756 g_return_if_fail (filesel != NULL);
757 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
758 g_return_if_fail (pattern != NULL);
760 if (filesel->selection_entry)
761 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), pattern);
762 gtk_file_selection_populate (filesel, (gchar*) pattern, TRUE);
766 gtk_file_selection_destroy (GtkObject *object)
768 GtkFileSelection *filesel;
770 HistoryCallbackArg *callback_arg;
772 g_return_if_fail (object != NULL);
773 g_return_if_fail (GTK_IS_FILE_SELECTION (object));
775 filesel = GTK_FILE_SELECTION (object);
777 if (filesel->fileop_dialog)
778 gtk_widget_destroy (filesel->fileop_dialog);
780 if (filesel->history_list)
782 list = filesel->history_list;
785 callback_arg = list->data;
786 g_free (callback_arg->directory);
787 g_free (callback_arg);
790 g_list_free (filesel->history_list);
791 filesel->history_list = NULL;
794 cmpl_free_state (filesel->cmpl_state);
795 filesel->cmpl_state = NULL;
797 if (GTK_OBJECT_CLASS (parent_class)->destroy)
798 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
801 /* Begin file operations callbacks */
804 gtk_file_selection_fileop_error (gchar *error_message)
811 g_return_if_fail (error_message != NULL);
814 dialog = gtk_dialog_new ();
816 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
817 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
820 gtk_window_set_title (GTK_WINDOW (dialog), _("Error"));
821 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
823 vbox = gtk_vbox_new(FALSE, 0);
824 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
825 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
827 gtk_widget_show(vbox);
829 label = gtk_label_new(error_message);
830 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
831 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
832 gtk_widget_show(label);
834 /* yes, we free it */
835 g_free (error_message);
838 button = gtk_button_new_with_label (_("Close"));
839 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
840 (GtkSignalFunc) gtk_widget_destroy,
842 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
843 button, TRUE, TRUE, 0);
844 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
845 gtk_widget_grab_default(button);
846 gtk_widget_show (button);
848 gtk_widget_show (dialog);
852 gtk_file_selection_fileop_destroy (GtkWidget *widget, gpointer data)
854 GtkFileSelection *fs = data;
856 g_return_if_fail (fs != NULL);
857 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
859 fs->fileop_dialog = NULL;
864 gtk_file_selection_create_dir_confirmed (GtkWidget *widget, gpointer data)
866 GtkFileSelection *fs = data;
871 CompletionState *cmpl_state;
873 g_return_if_fail (fs != NULL);
874 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
876 dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
877 cmpl_state = (CompletionState*) fs->cmpl_state;
878 path = cmpl_reference_position (cmpl_state);
880 full_path = g_strconcat (path, G_DIR_SEPARATOR_S, dirname, NULL);
881 if ( (mkdir (full_path, 0755) < 0) )
883 buf = g_strconcat ("Error creating directory \"", dirname, "\": ",
884 g_strerror(errno), NULL);
885 gtk_file_selection_fileop_error (buf);
889 gtk_widget_destroy (fs->fileop_dialog);
890 gtk_file_selection_populate (fs, "", FALSE);
894 gtk_file_selection_create_dir (GtkWidget *widget, gpointer data)
896 GtkFileSelection *fs = data;
902 g_return_if_fail (fs != NULL);
903 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
905 if (fs->fileop_dialog)
909 fs->fileop_dialog = dialog = gtk_dialog_new ();
910 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
911 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
913 gtk_window_set_title (GTK_WINDOW (dialog), _("Create Directory"));
914 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
916 /* If file dialog is grabbed, grab option dialog */
917 /* When option dialog is closed, file dialog will be grabbed again */
918 if (GTK_WINDOW(fs)->modal)
919 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
921 vbox = gtk_vbox_new(FALSE, 0);
922 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
923 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
925 gtk_widget_show(vbox);
927 label = gtk_label_new(_("Directory name:"));
928 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
929 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
930 gtk_widget_show(label);
932 /* The directory entry widget */
933 fs->fileop_entry = gtk_entry_new ();
934 gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry,
936 GTK_WIDGET_SET_FLAGS(fs->fileop_entry, GTK_CAN_DEFAULT);
937 gtk_widget_show (fs->fileop_entry);
940 button = gtk_button_new_with_label (_("Create"));
941 gtk_signal_connect (GTK_OBJECT (button), "clicked",
942 (GtkSignalFunc) gtk_file_selection_create_dir_confirmed,
944 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
945 button, TRUE, TRUE, 0);
946 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
947 gtk_widget_show(button);
949 button = gtk_button_new_with_label (_("Cancel"));
950 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
951 (GtkSignalFunc) gtk_widget_destroy,
953 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
954 button, TRUE, TRUE, 0);
955 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
956 gtk_widget_grab_default(button);
957 gtk_widget_show (button);
959 gtk_widget_show (dialog);
963 gtk_file_selection_delete_file_confirmed (GtkWidget *widget, gpointer data)
965 GtkFileSelection *fs = data;
966 CompletionState *cmpl_state;
971 g_return_if_fail (fs != NULL);
972 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
974 cmpl_state = (CompletionState*) fs->cmpl_state;
975 path = cmpl_reference_position (cmpl_state);
977 full_path = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
978 if ( (unlink (full_path) < 0) )
980 buf = g_strconcat ("Error deleting file \"", fs->fileop_file, "\": ",
981 g_strerror(errno), NULL);
982 gtk_file_selection_fileop_error (buf);
986 gtk_widget_destroy (fs->fileop_dialog);
987 gtk_file_selection_populate (fs, "", FALSE);
991 gtk_file_selection_delete_file (GtkWidget *widget, gpointer data)
993 GtkFileSelection *fs = data;
1001 g_return_if_fail (fs != NULL);
1002 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1004 if (fs->fileop_dialog)
1008 translate_win32_path(fs);
1011 filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1012 if (strlen(filename) < 1)
1015 fs->fileop_file = filename;
1018 fs->fileop_dialog = dialog = gtk_dialog_new ();
1019 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1020 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1022 gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
1023 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1025 /* If file dialog is grabbed, grab option dialog */
1026 /* When option dialog is closed, file dialog will be grabbed again */
1027 if (GTK_WINDOW(fs)->modal)
1028 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
1030 vbox = gtk_vbox_new(FALSE, 0);
1031 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
1032 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
1034 gtk_widget_show(vbox);
1036 buf = g_strconcat ("Really delete file \"", filename, "\" ?", NULL);
1037 label = gtk_label_new(buf);
1038 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
1039 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
1040 gtk_widget_show(label);
1044 button = gtk_button_new_with_label (_("Delete"));
1045 gtk_signal_connect (GTK_OBJECT (button), "clicked",
1046 (GtkSignalFunc) gtk_file_selection_delete_file_confirmed,
1048 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1049 button, TRUE, TRUE, 0);
1050 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1051 gtk_widget_show(button);
1053 button = gtk_button_new_with_label (_("Cancel"));
1054 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1055 (GtkSignalFunc) gtk_widget_destroy,
1057 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1058 button, TRUE, TRUE, 0);
1059 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1060 gtk_widget_grab_default(button);
1061 gtk_widget_show (button);
1063 gtk_widget_show (dialog);
1068 gtk_file_selection_rename_file_confirmed (GtkWidget *widget, gpointer data)
1070 GtkFileSelection *fs = data;
1074 gchar *new_filename;
1075 gchar *old_filename;
1076 CompletionState *cmpl_state;
1078 g_return_if_fail (fs != NULL);
1079 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1081 file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1082 cmpl_state = (CompletionState*) fs->cmpl_state;
1083 path = cmpl_reference_position (cmpl_state);
1085 new_filename = g_strconcat (path, G_DIR_SEPARATOR_S, file, NULL);
1086 old_filename = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1088 if ( (rename (old_filename, new_filename)) < 0)
1090 buf = g_strconcat ("Error renaming file \"", file, "\": ",
1091 g_strerror(errno), NULL);
1092 gtk_file_selection_fileop_error (buf);
1094 g_free (new_filename);
1095 g_free (old_filename);
1097 gtk_widget_destroy (fs->fileop_dialog);
1098 gtk_file_selection_populate (fs, "", FALSE);
1102 gtk_file_selection_rename_file (GtkWidget *widget, gpointer data)
1104 GtkFileSelection *fs = data;
1111 g_return_if_fail (fs != NULL);
1112 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1114 if (fs->fileop_dialog)
1117 fs->fileop_file = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1118 if (strlen(fs->fileop_file) < 1)
1122 fs->fileop_dialog = dialog = gtk_dialog_new ();
1123 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1124 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1126 gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File"));
1127 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1129 /* If file dialog is grabbed, grab option dialog */
1130 /* When option dialog closed, file dialog will be grabbed again */
1131 if (GTK_WINDOW(fs)->modal)
1132 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
1134 vbox = gtk_vbox_new(FALSE, 0);
1135 gtk_container_set_border_width (GTK_CONTAINER(vbox), 8);
1136 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
1138 gtk_widget_show(vbox);
1140 buf = g_strconcat ("Rename file \"", fs->fileop_file, "\" to:", NULL);
1141 label = gtk_label_new(buf);
1142 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
1143 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
1144 gtk_widget_show(label);
1147 /* New filename entry */
1148 fs->fileop_entry = gtk_entry_new ();
1149 gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry,
1151 GTK_WIDGET_SET_FLAGS(fs->fileop_entry, GTK_CAN_DEFAULT);
1152 gtk_widget_show (fs->fileop_entry);
1154 gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
1155 gtk_editable_select_region (GTK_EDITABLE (fs->fileop_entry),
1156 0, strlen (fs->fileop_file));
1159 button = gtk_button_new_with_label (_("Rename"));
1160 gtk_signal_connect (GTK_OBJECT (button), "clicked",
1161 (GtkSignalFunc) gtk_file_selection_rename_file_confirmed,
1163 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1164 button, TRUE, TRUE, 0);
1165 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1166 gtk_widget_show(button);
1168 button = gtk_button_new_with_label (_("Cancel"));
1169 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1170 (GtkSignalFunc) gtk_widget_destroy,
1172 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1173 button, TRUE, TRUE, 0);
1174 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1175 gtk_widget_grab_default(button);
1176 gtk_widget_show (button);
1178 gtk_widget_show (dialog);
1183 gtk_file_selection_key_press (GtkWidget *widget,
1187 GtkFileSelection *fs;
1190 g_return_val_if_fail (widget != NULL, FALSE);
1191 g_return_val_if_fail (event != NULL, FALSE);
1193 if (event->keyval == GDK_Tab)
1195 fs = GTK_FILE_SELECTION (user_data);
1197 translate_win32_path(fs);
1199 text = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1201 text = g_strdup (text);
1203 gtk_file_selection_populate (fs, text, TRUE);
1207 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
1217 gtk_file_selection_history_callback (GtkWidget *widget, gpointer data)
1219 GtkFileSelection *fs = data;
1220 HistoryCallbackArg *callback_arg;
1223 g_return_if_fail (fs != NULL);
1224 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1226 list = fs->history_list;
1229 callback_arg = list->data;
1231 if (callback_arg->menu_item == widget)
1233 gtk_file_selection_populate (fs, callback_arg->directory, FALSE);
1242 gtk_file_selection_update_history_menu (GtkFileSelection *fs,
1243 gchar *current_directory)
1245 HistoryCallbackArg *callback_arg;
1246 GtkWidget *menu_item;
1252 g_return_if_fail (fs != NULL);
1253 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1254 g_return_if_fail (current_directory != NULL);
1256 list = fs->history_list;
1258 if (fs->history_menu)
1261 callback_arg = list->data;
1262 g_free (callback_arg->directory);
1263 g_free (callback_arg);
1266 g_list_free (fs->history_list);
1267 fs->history_list = NULL;
1269 gtk_widget_destroy (fs->history_menu);
1272 fs->history_menu = gtk_menu_new();
1274 current_dir = g_strdup (current_directory);
1276 dir_len = strlen (current_dir);
1278 for (i = dir_len; i >= 0; i--)
1280 /* the i == dir_len is to catch the full path for the first
1282 if ( (current_dir[i] == G_DIR_SEPARATOR) || (i == dir_len))
1284 /* another small hack to catch the full path */
1286 current_dir[i + 1] = '\0';
1288 if (!strcmp(current_dir, "//"))
1291 menu_item = gtk_menu_item_new_with_label (current_dir);
1293 callback_arg = g_new (HistoryCallbackArg, 1);
1294 callback_arg->menu_item = menu_item;
1296 /* since the autocompletion gets confused if you don't
1297 * supply a trailing '/' on a dir entry, set the full
1298 * (current) path to "" which just refreshes the filesel */
1300 callback_arg->directory = g_strdup ("");
1302 callback_arg->directory = g_strdup (current_dir);
1305 fs->history_list = g_list_append (fs->history_list, callback_arg);
1307 gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
1308 (GtkSignalFunc) gtk_file_selection_history_callback,
1310 gtk_menu_append (GTK_MENU (fs->history_menu), menu_item);
1311 gtk_widget_show (menu_item);
1315 gtk_option_menu_set_menu (GTK_OPTION_MENU (fs->history_pulldown),
1317 g_free (current_dir);
1321 gtk_file_selection_file_button (GtkWidget *widget,
1324 GdkEventButton *bevent,
1327 GtkFileSelection *fs = NULL;
1328 gchar *filename, *temp = NULL;
1330 g_return_if_fail (GTK_IS_CLIST (widget));
1333 g_return_if_fail (fs != NULL);
1334 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1336 gtk_clist_get_text (GTK_CLIST (fs->file_list), row, 0, &temp);
1337 filename = g_strdup (temp);
1340 /* Check to see if the selection was a drive selector */
1341 if (isalpha(filename[0]) && (filename[1] == ':')) {
1342 /* It is... map it to a CYGWIN32 drive */
1343 char temp_filename[10];
1345 sprintf(temp_filename, "//%c/", tolower(filename[0]));
1347 filename = g_strdup(temp_filename);
1349 #endif /* CYGWIN32 */
1354 switch (bevent->type)
1356 case GDK_2BUTTON_PRESS:
1357 gtk_button_clicked (GTK_BUTTON (fs->ok_button));
1361 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1365 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1372 gtk_file_selection_dir_button (GtkWidget *widget,
1375 GdkEventButton *bevent,
1378 GtkFileSelection *fs = NULL;
1379 gchar *filename, *temp = NULL;
1381 g_return_if_fail (GTK_IS_CLIST (widget));
1383 fs = GTK_FILE_SELECTION (user_data);
1384 g_return_if_fail (fs != NULL);
1385 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1387 gtk_clist_get_text (GTK_CLIST (fs->dir_list), row, 0, &temp);
1388 filename = g_strdup (temp);
1393 switch (bevent->type)
1395 case GDK_2BUTTON_PRESS:
1396 gtk_file_selection_populate (fs, filename, FALSE);
1400 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1404 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1413 win32_gtk_add_drives_to_dir_list(GtkWidget *the_dir_list)
1415 gchar *text[2], *textPtr;
1417 char volumeNameBuf[128];
1418 char formatBuffer[128];
1423 /* Get the Drives string */
1424 GetLogicalDriveStrings(sizeof(buffer), buffer);
1426 /* Add the drives as necessary */
1428 while (*textPtr != '\0') {
1429 /* Get the volume information for this drive */
1430 if ((tolower(textPtr[0]) != 'a') && (tolower(textPtr[0]) != 'b'))
1432 /* Ignore floppies (?) */
1433 DWORD maxComponentLength, flags;
1435 GetVolumeInformation(textPtr,
1436 volumeNameBuf, sizeof(volumeNameBuf),
1437 NULL, &maxComponentLength,
1439 /* Build the actual displayable string */
1441 sprintf(formatBuffer, "%c:\\", toupper(textPtr[0]));
1442 #if 0 /* HB: removed to allow drive change AND directory update with one click */
1443 if (strlen(volumeNameBuf) > 0)
1444 sprintf(formatBuffer, "%s (%s)", formatBuffer, volumeNameBuf);
1446 /* Add to the list */
1447 text[0] = formatBuffer;
1448 row = gtk_clist_append (GTK_CLIST (the_dir_list), text);
1450 textPtr += (strlen(textPtr) + 1);
1456 gtk_file_selection_populate (GtkFileSelection *fs,
1460 CompletionState *cmpl_state;
1461 PossibleCompletion* poss;
1464 gchar* rem_path = rel_path;
1467 gint did_recurse = FALSE;
1468 gint possible_count = 0;
1469 gint selection_index = -1;
1470 gint file_list_width;
1471 gint dir_list_width;
1473 g_return_if_fail (fs != NULL);
1474 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1476 cmpl_state = (CompletionState*) fs->cmpl_state;
1477 poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
1479 if (!cmpl_state_okay (cmpl_state))
1481 /* Something went wrong. */
1482 gtk_file_selection_abort (fs);
1486 g_assert (cmpl_state->reference_dir);
1488 gtk_clist_freeze (GTK_CLIST (fs->dir_list));
1489 gtk_clist_clear (GTK_CLIST (fs->dir_list));
1490 gtk_clist_freeze (GTK_CLIST (fs->file_list));
1491 gtk_clist_clear (GTK_CLIST (fs->file_list));
1493 /* Set the dir_list to include ./ and ../ */
1495 text[0] = "." G_DIR_SEPARATOR_S;
1496 row = gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1498 text[0] = ".." G_DIR_SEPARATOR_S;
1499 row = gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1501 /*reset the max widths of the lists*/
1502 dir_list_width = gdk_string_width(fs->dir_list->style->font,".." G_DIR_SEPARATOR_S);
1503 gtk_clist_set_column_width(GTK_CLIST(fs->dir_list),0,dir_list_width);
1504 file_list_width = 1;
1505 gtk_clist_set_column_width(GTK_CLIST(fs->file_list),0,file_list_width);
1509 if (cmpl_is_a_completion (poss))
1511 possible_count += 1;
1513 filename = cmpl_this_completion (poss);
1517 if (cmpl_is_directory (poss))
1519 if (strcmp (filename, "." G_DIR_SEPARATOR_S) != 0 &&
1520 strcmp (filename, ".." G_DIR_SEPARATOR_S) != 0)
1522 int width = gdk_string_width(fs->dir_list->style->font,
1524 row = gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1525 if(width > dir_list_width)
1527 dir_list_width = width;
1528 gtk_clist_set_column_width(GTK_CLIST(fs->dir_list),0,
1535 int width = gdk_string_width(fs->file_list->style->font,
1537 row = gtk_clist_append (GTK_CLIST (fs->file_list), text);
1538 if(width > file_list_width)
1540 file_list_width = width;
1541 gtk_clist_set_column_width(GTK_CLIST(fs->file_list),0,
1547 poss = cmpl_next_completion (cmpl_state);
1551 /* For Windows, add drives as potential selections */
1552 win32_gtk_add_drives_to_dir_list (fs->dir_list);
1555 gtk_clist_thaw (GTK_CLIST (fs->dir_list));
1556 gtk_clist_thaw (GTK_CLIST (fs->file_list));
1558 /* File lists are set. */
1560 g_assert (cmpl_state->reference_dir);
1565 /* User is trying to complete filenames, so advance the user's input
1566 * string to the updated_text, which is the common leading substring
1567 * of all possible completions, and if its a directory attempt
1568 * attempt completions in it. */
1570 if (cmpl_updated_text (cmpl_state)[0])
1573 if (cmpl_updated_dir (cmpl_state))
1575 gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
1579 gtk_file_selection_populate (fs, dir_name, TRUE);
1585 if (fs->selection_entry)
1586 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
1587 cmpl_updated_text (cmpl_state));
1592 selection_index = cmpl_last_valid_char (cmpl_state) -
1593 (strlen (rel_path) - strlen (rem_path));
1594 if (fs->selection_entry)
1595 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
1600 if (fs->selection_entry)
1601 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
1606 if (fs->selection_entry)
1607 gtk_entry_set_position (GTK_ENTRY (fs->selection_entry), selection_index);
1609 if (fs->selection_entry)
1611 sel_text = g_strconcat (_("Selection: "),
1612 cmpl_reference_position (cmpl_state),
1615 gtk_label_set_text (GTK_LABEL (fs->selection_text), sel_text);
1619 if (fs->history_pulldown)
1621 gtk_file_selection_update_history_menu (fs, cmpl_reference_position (cmpl_state));
1628 gtk_file_selection_abort (GtkFileSelection *fs)
1632 sprintf (err_buf, _("Directory unreadable: %s"), cmpl_strerror (cmpl_errno));
1634 /* BEEP gdk_beep(); */
1636 if (fs->selection_entry)
1637 gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
1640 /**********************************************************************/
1641 /* External Interface */
1642 /**********************************************************************/
1644 /* The four completion state selectors
1647 cmpl_updated_text (CompletionState* cmpl_state)
1649 return cmpl_state->updated_text;
1653 cmpl_updated_dir (CompletionState* cmpl_state)
1655 return cmpl_state->re_complete;
1659 cmpl_reference_position (CompletionState* cmpl_state)
1661 return cmpl_state->reference_dir->fullname;
1665 cmpl_last_valid_char (CompletionState* cmpl_state)
1667 return cmpl_state->last_valid_char;
1671 cmpl_completion_fullname (gchar* text, CompletionState* cmpl_state)
1673 static char nothing[2] = "";
1675 if (!cmpl_state_okay (cmpl_state))
1679 else if (g_path_is_absolute (text))
1681 strcpy (cmpl_state->updated_text, text);
1684 else if (text[0] == '~')
1689 dir = open_user_dir (text, cmpl_state);
1693 /* spencer says just return ~something, so
1694 * for now just do it. */
1695 strcpy (cmpl_state->updated_text, text);
1700 strcpy (cmpl_state->updated_text, dir->fullname);
1702 slash = strchr (text, G_DIR_SEPARATOR);
1705 strcat (cmpl_state->updated_text, slash);
1711 strcpy (cmpl_state->updated_text, cmpl_state->reference_dir->fullname);
1712 if (cmpl_state->updated_text[strlen (cmpl_state->updated_text) - 1] != G_DIR_SEPARATOR)
1713 strcat (cmpl_state->updated_text, G_DIR_SEPARATOR_S);
1714 strcat (cmpl_state->updated_text, text);
1717 return cmpl_state->updated_text;
1720 /* The three completion selectors
1723 cmpl_this_completion (PossibleCompletion* pc)
1729 cmpl_is_directory (PossibleCompletion* pc)
1731 return pc->is_directory;
1735 cmpl_is_a_completion (PossibleCompletion* pc)
1737 return pc->is_a_completion;
1740 /**********************************************************************/
1741 /* Construction, deletion */
1742 /**********************************************************************/
1744 static CompletionState*
1745 cmpl_init_state (void)
1748 CompletionState *new_state;
1750 new_state = g_new (CompletionState, 1);
1752 getcwd_buf = g_get_current_dir ();
1756 new_state->reference_dir = NULL;
1757 new_state->completion_dir = NULL;
1758 new_state->active_completion_dir = NULL;
1759 new_state->directory_storage = NULL;
1760 new_state->directory_sent_storage = NULL;
1761 new_state->last_valid_char = 0;
1762 new_state->updated_text = g_new (gchar, MAXPATHLEN);
1763 new_state->updated_text_alloc = MAXPATHLEN;
1764 new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
1765 new_state->the_completion.text_alloc = MAXPATHLEN;
1766 new_state->user_dir_name_buffer = NULL;
1767 new_state->user_directories = NULL;
1769 new_state->reference_dir = open_dir (getcwd_buf, new_state);
1771 if (!new_state->reference_dir)
1773 /* Directories changing from underneath us, grumble */
1774 strcpy (getcwd_buf, G_DIR_SEPARATOR_S);
1778 g_free (getcwd_buf);
1783 cmpl_free_dir_list(GList* dp0)
1788 free_dir (dp->data);
1796 cmpl_free_dir_sent_list(GList* dp0)
1801 free_dir_sent (dp->data);
1809 cmpl_free_state (CompletionState* cmpl_state)
1811 cmpl_free_dir_list (cmpl_state->directory_storage);
1812 cmpl_free_dir_sent_list (cmpl_state->directory_sent_storage);
1814 if (cmpl_state->user_dir_name_buffer)
1815 g_free (cmpl_state->user_dir_name_buffer);
1816 if (cmpl_state->user_directories)
1817 g_free (cmpl_state->user_directories);
1818 if (cmpl_state->the_completion.text)
1819 g_free (cmpl_state->the_completion.text);
1820 if (cmpl_state->updated_text)
1821 g_free (cmpl_state->updated_text);
1823 g_free (cmpl_state);
1827 free_dir(CompletionDir* dir)
1829 g_free(dir->fullname);
1834 free_dir_sent(CompletionDirSent* sent)
1836 g_free(sent->name_buffer);
1837 g_free(sent->entries);
1842 prune_memory_usage(CompletionState *cmpl_state)
1844 GList* cdsl = cmpl_state->directory_sent_storage;
1845 GList* cdl = cmpl_state->directory_storage;
1849 for(; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
1853 cmpl_free_dir_sent_list(cdsl->next);
1857 cmpl_state->directory_storage = NULL;
1859 if (cdl->data == cmpl_state->reference_dir)
1860 cmpl_state->directory_storage = g_list_prepend(NULL, cdl->data);
1862 free_dir (cdl->data);
1869 /**********************************************************************/
1870 /* The main entrances. */
1871 /**********************************************************************/
1873 static PossibleCompletion*
1874 cmpl_completion_matches (gchar* text_to_complete,
1875 gchar** remaining_text,
1876 CompletionState* cmpl_state)
1879 PossibleCompletion *poss;
1881 prune_memory_usage(cmpl_state);
1883 g_assert (text_to_complete != NULL);
1885 cmpl_state->user_completion_index = -1;
1886 cmpl_state->last_completion_text = text_to_complete;
1887 cmpl_state->the_completion.text[0] = 0;
1888 cmpl_state->last_valid_char = 0;
1889 cmpl_state->updated_text_len = -1;
1890 cmpl_state->updated_text[0] = 0;
1891 cmpl_state->re_complete = FALSE;
1894 first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
1896 if (text_to_complete[0] == '~' && !first_slash)
1898 /* Text starts with ~ and there is no slash, show all the
1899 * home directory completions.
1901 poss = attempt_homedir_completion (text_to_complete, cmpl_state);
1903 update_cmpl(poss, cmpl_state);
1908 cmpl_state->reference_dir =
1909 open_ref_dir (text_to_complete, remaining_text, cmpl_state);
1911 if(!cmpl_state->reference_dir)
1914 cmpl_state->completion_dir =
1915 find_completion_dir (*remaining_text, remaining_text, cmpl_state);
1917 cmpl_state->last_valid_char = *remaining_text - text_to_complete;
1919 if(!cmpl_state->completion_dir)
1922 cmpl_state->completion_dir->cmpl_index = -1;
1923 cmpl_state->completion_dir->cmpl_parent = NULL;
1924 cmpl_state->completion_dir->cmpl_text = *remaining_text;
1926 cmpl_state->active_completion_dir = cmpl_state->completion_dir;
1928 cmpl_state->reference_dir = cmpl_state->completion_dir;
1930 poss = attempt_file_completion(cmpl_state);
1932 update_cmpl(poss, cmpl_state);
1937 static PossibleCompletion*
1938 cmpl_next_completion (CompletionState* cmpl_state)
1940 PossibleCompletion* poss = NULL;
1942 cmpl_state->the_completion.text[0] = 0;
1945 if(cmpl_state->user_completion_index >= 0)
1946 poss = attempt_homedir_completion(cmpl_state->last_completion_text, cmpl_state);
1948 poss = attempt_file_completion(cmpl_state);
1950 poss = attempt_file_completion(cmpl_state);
1953 update_cmpl(poss, cmpl_state);
1958 /**********************************************************************/
1959 /* Directory Operations */
1960 /**********************************************************************/
1962 /* Open the directory where completion will begin from, if possible. */
1963 static CompletionDir*
1964 open_ref_dir(gchar* text_to_complete,
1965 gchar** remaining_text,
1966 CompletionState* cmpl_state)
1969 CompletionDir *new_dir;
1971 first_slash = strchr(text_to_complete, G_DIR_SEPARATOR);
1974 if (text_to_complete[0] == '/' && text_to_complete[1] == '/')
1977 sprintf(root_dir, "//%c", text_to_complete[2]);
1979 new_dir = open_dir(root_dir, cmpl_state);
1982 *remaining_text = text_to_complete + 4;
1989 else if (g_path_is_absolute (text_to_complete) || !cmpl_state->reference_dir)
1994 rootlen = g_path_skip_root (text_to_complete) - text_to_complete;
1995 root = g_malloc (rootlen + 1);
1996 memcpy (root, text_to_complete, rootlen);
1997 root[rootlen] = '\0';
1998 new_dir = open_dir (root, cmpl_state);
2000 *remaining_text = g_path_skip_root (text_to_complete);
2004 else if (text_to_complete[0] == '~')
2006 new_dir = open_user_dir(text_to_complete, cmpl_state);
2011 *remaining_text = first_slash + 1;
2013 *remaining_text = text_to_complete + strlen(text_to_complete);
2023 *remaining_text = text_to_complete;
2025 new_dir = open_dir(cmpl_state->reference_dir->fullname, cmpl_state);
2030 new_dir->cmpl_index = -1;
2031 new_dir->cmpl_parent = NULL;
2039 /* open a directory by user name */
2040 static CompletionDir*
2041 open_user_dir(gchar* text_to_complete,
2042 CompletionState *cmpl_state)
2047 g_assert(text_to_complete && text_to_complete[0] == '~');
2049 first_slash = strchr(text_to_complete, G_DIR_SEPARATOR);
2052 cmp_len = first_slash - text_to_complete - 1;
2054 cmp_len = strlen(text_to_complete + 1);
2059 gchar *homedir = g_get_home_dir ();
2062 return open_dir(homedir, cmpl_state);
2069 char* copy = g_new(char, cmp_len + 1);
2071 strncpy(copy, text_to_complete + 1, cmp_len);
2073 pwd = getpwnam(copy);
2081 return open_dir(pwd->pw_dir, cmpl_state);
2087 /* open a directory relative the the current relative directory */
2088 static CompletionDir*
2089 open_relative_dir(gchar* dir_name,
2091 CompletionState *cmpl_state)
2093 gchar path_buf[2*MAXPATHLEN];
2095 if(dir->fullname_len + strlen(dir_name) + 2 >= MAXPATHLEN)
2097 cmpl_errno = CMPL_ERRNO_TOO_LONG;
2101 strcpy(path_buf, dir->fullname);
2103 if(dir->fullname_len > 1)
2105 if (path_buf[dir->fullname_len - 1] != G_DIR_SEPARATOR)
2107 path_buf[dir->fullname_len] = G_DIR_SEPARATOR;
2108 strcpy (path_buf + dir->fullname_len + 1, dir_name);
2111 strcpy (path_buf + dir->fullname_len, dir_name);
2115 strcpy(path_buf + dir->fullname_len, dir_name);
2118 return open_dir(path_buf, cmpl_state);
2121 /* after the cache lookup fails, really open a new directory */
2122 static CompletionDirSent*
2123 open_new_dir(gchar* dir_name, struct stat* sbuf, gboolean stat_subdirs)
2125 CompletionDirSent* sent;
2128 struct dirent *dirent_ptr;
2129 gint buffer_size = 0;
2130 gint entry_count = 0;
2132 struct stat ent_sbuf;
2133 char path_buf[MAXPATHLEN*2];
2136 sent = g_new(CompletionDirSent, 1);
2137 sent->mtime = sbuf->st_mtime;
2138 sent->inode = sbuf->st_ino;
2139 sent->device = sbuf->st_dev;
2141 path_buf_len = strlen(dir_name);
2143 if (path_buf_len > MAXPATHLEN)
2145 cmpl_errno = CMPL_ERRNO_TOO_LONG;
2149 strcpy(path_buf, dir_name);
2151 directory = opendir(dir_name);
2159 while((dirent_ptr = readdir(directory)) != NULL)
2161 int entry_len = strlen(dirent_ptr->d_name);
2162 buffer_size += entry_len + 1;
2165 if(path_buf_len + entry_len + 2 >= MAXPATHLEN)
2167 cmpl_errno = CMPL_ERRNO_TOO_LONG;
2168 closedir(directory);
2173 sent->name_buffer = g_new(gchar, buffer_size);
2174 sent->entries = g_new(CompletionDirEntry, entry_count);
2175 sent->entry_count = entry_count;
2177 buffer_ptr = sent->name_buffer;
2179 rewinddir(directory);
2181 for(i = 0; i < entry_count; i += 1)
2183 dirent_ptr = readdir(directory);
2188 closedir(directory);
2192 strcpy(buffer_ptr, dirent_ptr->d_name);
2193 sent->entries[i].entry_name = buffer_ptr;
2194 buffer_ptr += strlen(dirent_ptr->d_name);
2198 if (path_buf[path_buf_len-1] != G_DIR_SEPARATOR)
2200 path_buf[path_buf_len] = G_DIR_SEPARATOR;
2201 strcpy(path_buf + path_buf_len + 1, dirent_ptr->d_name);
2204 strcpy(path_buf + path_buf_len, dirent_ptr->d_name);
2208 if(stat(path_buf, &ent_sbuf) >= 0 && S_ISDIR(ent_sbuf.st_mode))
2209 sent->entries[i].is_dir = 1;
2211 /* stat may fail, and we don't mind, since it could be a
2212 * dangling symlink. */
2213 sent->entries[i].is_dir = 0;
2216 sent->entries[i].is_dir = 1;
2219 qsort(sent->entries, sent->entry_count, sizeof(CompletionDirEntry), compare_cmpl_dir);
2221 closedir(directory);
2229 check_dir(gchar *dir_name, struct stat *result, gboolean *stat_subdirs)
2231 /* A list of directories that we know only contain other directories.
2232 * Trying to stat every file in these directories would be very
2239 struct stat statbuf;
2240 } no_stat_dirs[] = {
2241 { "/afs", FALSE, { 0 } },
2242 { "/net", FALSE, { 0 } }
2245 static const gint n_no_stat_dirs = sizeof(no_stat_dirs) / sizeof(no_stat_dirs[0]);
2246 static gboolean initialized = FALSE;
2253 for (i = 0; i < n_no_stat_dirs; i++)
2255 if (stat (no_stat_dirs[i].name, &no_stat_dirs[i].statbuf) == 0)
2256 no_stat_dirs[i].present = TRUE;
2260 if(stat(dir_name, result) < 0)
2266 *stat_subdirs = TRUE;
2267 for (i=0; i<n_no_stat_dirs; i++)
2269 if (no_stat_dirs[i].present &&
2270 (no_stat_dirs[i].statbuf.st_dev == result->st_dev) &&
2271 (no_stat_dirs[i].statbuf.st_ino == result->st_ino))
2273 *stat_subdirs = FALSE;
2283 /* open a directory by absolute pathname */
2284 static CompletionDir*
2285 open_dir(gchar* dir_name, CompletionState* cmpl_state)
2288 gboolean stat_subdirs;
2289 CompletionDirSent *sent;
2293 if (!check_dir (dir_name, &sbuf, &stat_subdirs))
2296 cdsl = cmpl_state->directory_sent_storage;
2302 if(sent->inode == sbuf.st_ino &&
2303 sent->mtime == sbuf.st_mtime &&
2304 sent->device == sbuf.st_dev)
2305 return attach_dir(sent, dir_name, cmpl_state);
2310 stat_subdirs = TRUE;
2313 sent = open_new_dir(dir_name, &sbuf, stat_subdirs);
2316 cmpl_state->directory_sent_storage =
2317 g_list_prepend(cmpl_state->directory_sent_storage, sent);
2319 return attach_dir(sent, dir_name, cmpl_state);
2325 static CompletionDir*
2326 attach_dir(CompletionDirSent* sent, gchar* dir_name, CompletionState *cmpl_state)
2328 CompletionDir* new_dir;
2330 new_dir = g_new(CompletionDir, 1);
2332 cmpl_state->directory_storage =
2333 g_list_prepend(cmpl_state->directory_storage, new_dir);
2335 new_dir->sent = sent;
2336 new_dir->fullname = g_strdup(dir_name);
2337 new_dir->fullname_len = strlen(dir_name);
2343 correct_dir_fullname(CompletionDir* cmpl_dir)
2345 gint length = strlen(cmpl_dir->fullname);
2346 gchar *first_slash = strchr(cmpl_dir->fullname, G_DIR_SEPARATOR);
2349 /* Does it end with /. (\.) ? */
2351 strcmp(cmpl_dir->fullname + length - 2, G_DIR_SEPARATOR_S ".") == 0)
2353 /* Is it just the root directory (on a drive) ? */
2354 if (cmpl_dir->fullname + length - 2 == first_slash)
2356 cmpl_dir->fullname[length - 1] = 0;
2357 cmpl_dir->fullname_len = length - 1;
2362 cmpl_dir->fullname[length - 2] = 0;
2366 /* Ends with /./ (\.\)? */
2367 else if (length >= 3 &&
2368 strcmp(cmpl_dir->fullname + length - 3,
2369 G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) == 0)
2370 cmpl_dir->fullname[length - 2] = 0;
2372 /* Ends with /.. (\..) ? */
2373 else if (length >= 3 &&
2374 strcmp(cmpl_dir->fullname + length - 3,
2375 G_DIR_SEPARATOR_S "..") == 0)
2377 /* Is it just /.. (X:\..)? */
2378 if(cmpl_dir->fullname + length - 3 == first_slash)
2380 cmpl_dir->fullname[length - 2] = 0;
2381 cmpl_dir->fullname_len = length - 2;
2385 if(stat(cmpl_dir->fullname, &sbuf) < 0)
2391 cmpl_dir->fullname[length - 3] = 0;
2393 if(!correct_parent(cmpl_dir, &sbuf))
2397 /* Ends with /../ (\..\)? */
2398 else if (length >= 4 &&
2399 strcmp(cmpl_dir->fullname + length - 4,
2400 G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) == 0)
2402 /* Is it just /../ (X:\..\)? */
2403 if(cmpl_dir->fullname + length - 4 == first_slash)
2405 cmpl_dir->fullname[length - 3] = 0;
2406 cmpl_dir->fullname_len = length - 3;
2410 if(stat(cmpl_dir->fullname, &sbuf) < 0)
2416 cmpl_dir->fullname[length - 4] = 0;
2418 if(!correct_parent(cmpl_dir, &sbuf))
2422 cmpl_dir->fullname_len = strlen(cmpl_dir->fullname);
2428 correct_parent(CompletionDir* cmpl_dir, struct stat *sbuf)
2436 last_slash = strrchr(cmpl_dir->fullname, G_DIR_SEPARATOR);
2437 g_assert(last_slash);
2438 first_slash = strchr(cmpl_dir->fullname, G_DIR_SEPARATOR);
2440 /* Clever (?) way to check for top-level directory that works also on
2441 * Win32, where there is a drive letter and colon prefixed...
2443 if (last_slash != first_slash)
2453 if (stat(cmpl_dir->fullname, &parbuf) < 0)
2457 last_slash[0] = G_DIR_SEPARATOR;
2461 #ifndef NATIVE_WIN32 /* No inode numbers on Win32 */
2462 if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
2463 /* it wasn't a link */
2469 last_slash[0] = G_DIR_SEPARATOR;
2471 /* it was a link, have to figure it out the hard way */
2473 new_name = find_parent_dir_fullname(cmpl_dir->fullname);
2478 g_free(cmpl_dir->fullname);
2480 cmpl_dir->fullname = new_name;
2486 #ifndef NATIVE_WIN32
2489 find_parent_dir_fullname(gchar* dirname)
2494 orig_dir = g_get_current_dir ();
2496 if(chdir(dirname) != 0 || chdir("..") != 0)
2502 result = g_get_current_dir ();
2504 if(chdir(orig_dir) != 0)
2516 /**********************************************************************/
2517 /* Completion Operations */
2518 /**********************************************************************/
2522 static PossibleCompletion*
2523 attempt_homedir_completion(gchar* text_to_complete,
2524 CompletionState *cmpl_state)
2528 if (!cmpl_state->user_dir_name_buffer &&
2529 !get_pwdb(cmpl_state))
2531 length = strlen(text_to_complete) - 1;
2533 cmpl_state->user_completion_index += 1;
2535 while(cmpl_state->user_completion_index < cmpl_state->user_directories_len)
2537 index = first_diff_index(text_to_complete + 1,
2538 cmpl_state->user_directories
2539 [cmpl_state->user_completion_index].login);
2546 if(cmpl_state->last_valid_char < (index + 1))
2547 cmpl_state->last_valid_char = index + 1;
2548 cmpl_state->user_completion_index += 1;
2552 cmpl_state->the_completion.is_a_completion = 1;
2553 cmpl_state->the_completion.is_directory = 1;
2555 append_completion_text("~", cmpl_state);
2557 append_completion_text(cmpl_state->
2558 user_directories[cmpl_state->user_completion_index].login,
2561 return append_completion_text(G_DIR_SEPARATOR_S, cmpl_state);
2564 if(text_to_complete[1] ||
2565 cmpl_state->user_completion_index > cmpl_state->user_directories_len)
2567 cmpl_state->user_completion_index = -1;
2572 cmpl_state->user_completion_index += 1;
2573 cmpl_state->the_completion.is_a_completion = 1;
2574 cmpl_state->the_completion.is_directory = 1;
2576 return append_completion_text("~" G_DIR_SEPARATOR_S, cmpl_state);
2583 #define FOLD(c) (tolower(c))
2588 /* returns the index (>= 0) of the first differing character,
2589 * PATTERN_MATCH if the completion matches */
2591 first_diff_index(gchar* pat, gchar* text)
2595 while(*pat && *text && FOLD(*text) == FOLD(*pat))
2605 return PATTERN_MATCH;
2608 static PossibleCompletion*
2609 append_completion_text(gchar* text, CompletionState* cmpl_state)
2613 if(!cmpl_state->the_completion.text)
2616 len = strlen(text) + strlen(cmpl_state->the_completion.text) + 1;
2618 if(cmpl_state->the_completion.text_alloc > len)
2620 strcat(cmpl_state->the_completion.text, text);
2621 return &cmpl_state->the_completion;
2624 while(i < len) { i <<= 1; }
2626 cmpl_state->the_completion.text_alloc = i;
2628 cmpl_state->the_completion.text = (gchar*)g_realloc(cmpl_state->the_completion.text, i);
2630 if(!cmpl_state->the_completion.text)
2634 strcat(cmpl_state->the_completion.text, text);
2635 return &cmpl_state->the_completion;
2639 static CompletionDir*
2640 find_completion_dir(gchar* text_to_complete,
2641 gchar** remaining_text,
2642 CompletionState* cmpl_state)
2644 gchar* first_slash = strchr(text_to_complete, G_DIR_SEPARATOR);
2645 CompletionDir* dir = cmpl_state->reference_dir;
2646 CompletionDir* next;
2647 *remaining_text = text_to_complete;
2651 gint len = first_slash - *remaining_text;
2653 gchar *found_name = NULL; /* Quiet gcc */
2655 gchar* pat_buf = g_new (gchar, len + 1);
2657 strncpy(pat_buf, *remaining_text, len);
2660 for(i = 0; i < dir->sent->entry_count; i += 1)
2662 if(dir->sent->entries[i].is_dir &&
2663 fnmatch(pat_buf, dir->sent->entries[i].entry_name,
2664 FNMATCH_FLAGS)!= FNM_NOMATCH)
2674 found_name = dir->sent->entries[i].entry_name;
2681 /* Perhaps we are trying to open an automount directory */
2682 found_name = pat_buf;
2685 next = open_relative_dir(found_name, dir, cmpl_state);
2693 next->cmpl_parent = dir;
2697 if(!correct_dir_fullname(dir))
2703 *remaining_text = first_slash + 1;
2704 first_slash = strchr(*remaining_text, G_DIR_SEPARATOR);
2713 update_cmpl(PossibleCompletion* poss, CompletionState* cmpl_state)
2717 if(!poss || !cmpl_is_a_completion(poss))
2720 cmpl_len = strlen(cmpl_this_completion(poss));
2722 if(cmpl_state->updated_text_alloc < cmpl_len + 1)
2724 cmpl_state->updated_text =
2725 (gchar*)g_realloc(cmpl_state->updated_text,
2726 cmpl_state->updated_text_alloc);
2727 cmpl_state->updated_text_alloc = 2*cmpl_len;
2730 if(cmpl_state->updated_text_len < 0)
2732 strcpy(cmpl_state->updated_text, cmpl_this_completion(poss));
2733 cmpl_state->updated_text_len = cmpl_len;
2734 cmpl_state->re_complete = cmpl_is_directory(poss);
2736 else if(cmpl_state->updated_text_len == 0)
2738 cmpl_state->re_complete = FALSE;
2743 first_diff_index(cmpl_state->updated_text,
2744 cmpl_this_completion(poss));
2746 cmpl_state->re_complete = FALSE;
2748 if(first_diff == PATTERN_MATCH)
2751 if(first_diff > cmpl_state->updated_text_len)
2752 strcpy(cmpl_state->updated_text, cmpl_this_completion(poss));
2754 cmpl_state->updated_text_len = first_diff;
2755 cmpl_state->updated_text[first_diff] = 0;
2759 static PossibleCompletion*
2760 attempt_file_completion(CompletionState *cmpl_state)
2762 gchar *pat_buf, *first_slash;
2763 CompletionDir *dir = cmpl_state->active_completion_dir;
2765 dir->cmpl_index += 1;
2767 if(dir->cmpl_index == dir->sent->entry_count)
2769 if(dir->cmpl_parent == NULL)
2771 cmpl_state->active_completion_dir = NULL;
2777 cmpl_state->active_completion_dir = dir->cmpl_parent;
2779 return attempt_file_completion(cmpl_state);
2783 g_assert(dir->cmpl_text);
2785 first_slash = strchr(dir->cmpl_text, G_DIR_SEPARATOR);
2789 gint len = first_slash - dir->cmpl_text;
2791 pat_buf = g_new (gchar, len + 1);
2792 strncpy(pat_buf, dir->cmpl_text, len);
2797 gint len = strlen(dir->cmpl_text);
2799 pat_buf = g_new (gchar, len + 2);
2800 strcpy(pat_buf, dir->cmpl_text);
2801 /* Don't append a * if the user entered one herself.
2802 * This way one can complete *.h and don't get matches
2803 * on any .help files, for instance.
2805 if (strchr(pat_buf, '*') == NULL)
2806 strcpy(pat_buf + len, "*");
2811 if(dir->sent->entries[dir->cmpl_index].is_dir)
2813 if(fnmatch(pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
2814 FNMATCH_FLAGS) != FNM_NOMATCH)
2816 CompletionDir* new_dir;
2818 new_dir = open_relative_dir(dir->sent->entries[dir->cmpl_index].entry_name,
2827 new_dir->cmpl_parent = dir;
2829 new_dir->cmpl_index = -1;
2830 new_dir->cmpl_text = first_slash + 1;
2832 cmpl_state->active_completion_dir = new_dir;
2835 return attempt_file_completion(cmpl_state);
2840 return attempt_file_completion(cmpl_state);
2846 return attempt_file_completion(cmpl_state);
2851 if(dir->cmpl_parent != NULL)
2853 append_completion_text(dir->fullname +
2854 strlen(cmpl_state->completion_dir->fullname) + 1,
2856 append_completion_text(G_DIR_SEPARATOR_S, cmpl_state);
2859 append_completion_text(dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
2861 cmpl_state->the_completion.is_a_completion =
2862 (fnmatch(pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
2863 FNMATCH_FLAGS) != FNM_NOMATCH);
2865 cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
2866 if(dir->sent->entries[dir->cmpl_index].is_dir)
2867 append_completion_text(G_DIR_SEPARATOR_S, cmpl_state);
2870 return &cmpl_state->the_completion;
2877 get_pwdb(CompletionState* cmpl_state)
2879 struct passwd *pwd_ptr;
2881 gint len = 0, i, count = 0;
2883 if(cmpl_state->user_dir_name_buffer)
2887 while ((pwd_ptr = getpwent()) != NULL)
2889 len += strlen(pwd_ptr->pw_name);
2890 len += strlen(pwd_ptr->pw_dir);
2897 cmpl_state->user_dir_name_buffer = g_new(gchar, len);
2898 cmpl_state->user_directories = g_new(CompletionUserDir, count);
2899 cmpl_state->user_directories_len = count;
2901 buf_ptr = cmpl_state->user_dir_name_buffer;
2903 for(i = 0; i < count; i += 1)
2905 pwd_ptr = getpwent();
2912 strcpy(buf_ptr, pwd_ptr->pw_name);
2913 cmpl_state->user_directories[i].login = buf_ptr;
2914 buf_ptr += strlen(buf_ptr);
2916 strcpy(buf_ptr, pwd_ptr->pw_dir);
2917 cmpl_state->user_directories[i].homedir = buf_ptr;
2918 buf_ptr += strlen(buf_ptr);
2922 qsort(cmpl_state->user_directories,
2923 cmpl_state->user_directories_len,
2924 sizeof(CompletionUserDir),
2933 if(cmpl_state->user_dir_name_buffer)
2934 g_free(cmpl_state->user_dir_name_buffer);
2935 if(cmpl_state->user_directories)
2936 g_free(cmpl_state->user_directories);
2938 cmpl_state->user_dir_name_buffer = NULL;
2939 cmpl_state->user_directories = NULL;
2945 compare_user_dir(const void* a, const void* b)
2947 return strcmp((((CompletionUserDir*)a))->login,
2948 (((CompletionUserDir*)b))->login);
2954 compare_cmpl_dir(const void* a, const void* b)
2957 return strcmp((((CompletionDirEntry*)a))->entry_name,
2958 (((CompletionDirEntry*)b))->entry_name);
2960 return g_strcasecmp((((CompletionDirEntry*)a))->entry_name,
2961 (((CompletionDirEntry*)b))->entry_name);
2966 cmpl_state_okay(CompletionState* cmpl_state)
2968 return cmpl_state && cmpl_state->reference_dir;
2972 cmpl_strerror(gint err)
2974 if(err == CMPL_ERRNO_TOO_LONG)
2975 return "Name too long";
2977 return g_strerror (err);