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 /* The Hurd doesn't define either PATH_MAX or MAXPATHLEN, so we put this
93 * in here, since the rest of the code in the file does require some
98 # define MAXPATHLEN PATH_MAX
100 # define MAXPATHLEN 2048
104 /* I've put this here so it doesn't get confused with the
105 * file completion interface */
106 typedef struct _HistoryCallbackArg HistoryCallbackArg;
108 struct _HistoryCallbackArg
111 GtkWidget *menu_item;
115 typedef struct _CompletionState CompletionState;
116 typedef struct _CompletionDir CompletionDir;
117 typedef struct _CompletionDirSent CompletionDirSent;
118 typedef struct _CompletionDirEntry CompletionDirEntry;
119 typedef struct _CompletionUserDir CompletionUserDir;
120 typedef struct _PossibleCompletion PossibleCompletion;
122 /* Non-external file completion decls and structures */
124 /* A contant telling PRCS how many directories to cache. Its actually
125 * kept in a list, so the geometry isn't important. */
126 #define CMPL_DIRECTORY_CACHE_SIZE 10
128 /* A constant used to determine whether a substring was an exact
129 * match by first_diff_index()
131 #define PATTERN_MATCH -1
132 /* The arguments used by all fnmatch() calls below
134 #define FNMATCH_FLAGS (FNM_PATHNAME | FNM_PERIOD)
136 #define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
138 /* This structure contains all the useful information about a directory
139 * for the purposes of filename completion. These structures are cached
140 * in the CompletionState struct. CompletionDir's are reference counted.
142 struct _CompletionDirSent
149 struct _CompletionDirEntry *entries;
152 struct _CompletionDir
154 CompletionDirSent *sent;
159 struct _CompletionDir *cmpl_parent;
164 /* This structure contains pairs of directory entry names with a flag saying
165 * whether or not they are a valid directory. NOTE: This information is used
166 * to provide the caller with information about whether to update its completions
167 * or try to open a file. Since directories are cached by the directory mtime,
168 * a symlink which points to an invalid file (which will not be a directory),
169 * will not be reevaluated if that file is created, unless the containing
170 * directory is touched. I consider this case to be worth ignoring (josh).
172 struct _CompletionDirEntry
178 struct _CompletionUserDir
184 struct _PossibleCompletion
186 /* accessible fields, all are accessed externally by functions
190 gint is_a_completion;
191 gboolean is_directory;
198 struct _CompletionState
200 gint last_valid_char;
202 gint updated_text_len;
203 gint updated_text_alloc;
204 gboolean re_complete;
206 gchar *user_dir_name_buffer;
207 gint user_directories_len;
209 gchar *last_completion_text;
211 gint user_completion_index; /* if >= 0, currently completing ~user */
213 struct _CompletionDir *completion_dir; /* directory completing from */
214 struct _CompletionDir *active_completion_dir;
216 struct _PossibleCompletion the_completion;
218 struct _CompletionDir *reference_dir; /* initial directory */
220 GList* directory_storage;
221 GList* directory_sent_storage;
223 struct _CompletionUserDir *user_directories;
227 /* File completion functions which would be external, were they used
228 * outside of this file.
231 static CompletionState* cmpl_init_state (void);
232 static void cmpl_free_state (CompletionState *cmpl_state);
233 static gint cmpl_state_okay (CompletionState* cmpl_state);
234 static gchar* cmpl_strerror (gint);
236 static PossibleCompletion* cmpl_completion_matches(gchar *text_to_complete,
237 gchar **remaining_text,
238 CompletionState *cmpl_state);
240 /* Returns a name for consideration, possibly a completion, this name
241 * will be invalid after the next call to cmpl_next_completion.
243 static char* cmpl_this_completion (PossibleCompletion*);
245 /* True if this completion matches the given text. Otherwise, this
246 * output can be used to have a list of non-completions.
248 static gint cmpl_is_a_completion (PossibleCompletion*);
250 /* True if the completion is a directory
252 static gboolean cmpl_is_directory (PossibleCompletion*);
254 /* Obtains the next completion, or NULL
256 static PossibleCompletion* cmpl_next_completion (CompletionState*);
258 /* Updating completions: the return value of cmpl_updated_text() will
259 * be text_to_complete completed as much as possible after the most
260 * recent call to cmpl_completion_matches. For the present
261 * application, this is the suggested replacement for the user's input
262 * string. You must CALL THIS AFTER ALL cmpl_text_completions have
265 static gchar* cmpl_updated_text (CompletionState* cmpl_state);
267 /* After updating, to see if the completion was a directory, call
268 * this. If it was, you should consider re-calling completion_matches.
270 static gboolean cmpl_updated_dir (CompletionState* cmpl_state);
272 /* Current location: if using file completion, return the current
273 * directory, from which file completion begins. More specifically,
274 * the cwd concatenated with all exact completions up to the last
275 * directory delimiter('/').
277 static gchar* cmpl_reference_position (CompletionState* cmpl_state);
279 /* backing up: if cmpl_completion_matches returns NULL, you may query
280 * the index of the last completable character into cmpl_updated_text.
282 static gint cmpl_last_valid_char (CompletionState* cmpl_state);
284 /* When the user selects a non-directory, call cmpl_completion_fullname
285 * to get the full name of the selected file.
287 static gchar* cmpl_completion_fullname (gchar*, CompletionState* cmpl_state);
290 /* Directory operations. */
291 static CompletionDir* open_ref_dir (gchar* text_to_complete,
292 gchar** remaining_text,
293 CompletionState* cmpl_state);
294 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
295 static gboolean check_dir (gchar *dir_name,
297 gboolean *stat_subdirs);
299 static CompletionDir* open_dir (gchar* dir_name,
300 CompletionState* cmpl_state);
302 static CompletionDir* open_user_dir (gchar* text_to_complete,
303 CompletionState *cmpl_state);
305 static CompletionDir* open_relative_dir (gchar* dir_name, CompletionDir* dir,
306 CompletionState *cmpl_state);
307 static CompletionDirSent* open_new_dir (gchar* dir_name,
309 gboolean stat_subdirs);
310 static gint correct_dir_fullname (CompletionDir* cmpl_dir);
311 static gint correct_parent (CompletionDir* cmpl_dir,
313 static gchar* find_parent_dir_fullname (gchar* dirname);
314 static CompletionDir* attach_dir (CompletionDirSent* sent,
316 CompletionState *cmpl_state);
317 static void free_dir_sent (CompletionDirSent* sent);
318 static void free_dir (CompletionDir *dir);
319 static void prune_memory_usage(CompletionState *cmpl_state);
321 /* Completion operations */
322 static PossibleCompletion* attempt_homedir_completion(gchar* text_to_complete,
323 CompletionState *cmpl_state);
324 static PossibleCompletion* attempt_file_completion(CompletionState *cmpl_state);
325 static CompletionDir* find_completion_dir(gchar* text_to_complete,
326 gchar** remaining_text,
327 CompletionState* cmpl_state);
328 static PossibleCompletion* append_completion_text(gchar* text,
329 CompletionState* cmpl_state);
331 static gint get_pwdb(CompletionState* cmpl_state);
332 static gint compare_user_dir(const void* a, const void* b);
334 static gint first_diff_index(gchar* pat, gchar* text);
335 static gint compare_cmpl_dir(const void* a, const void* b);
336 static void update_cmpl(PossibleCompletion* poss,
337 CompletionState* cmpl_state);
339 static void gtk_file_selection_class_init (GtkFileSelectionClass *klass);
340 static void gtk_file_selection_init (GtkFileSelection *filesel);
341 static void gtk_file_selection_destroy (GtkObject *object);
342 static gint gtk_file_selection_key_press (GtkWidget *widget,
346 static void gtk_file_selection_file_button (GtkWidget *widget,
349 GdkEventButton *bevent,
352 static void gtk_file_selection_dir_button (GtkWidget *widget,
355 GdkEventButton *bevent,
358 static void gtk_file_selection_populate (GtkFileSelection *fs,
361 static void gtk_file_selection_abort (GtkFileSelection *fs);
363 static void gtk_file_selection_update_history_menu (GtkFileSelection *fs,
366 static void gtk_file_selection_create_dir (GtkWidget *widget, gpointer data);
367 static void gtk_file_selection_delete_file (GtkWidget *widget, gpointer data);
368 static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data);
372 static GtkWindowClass *parent_class = NULL;
374 /* Saves errno when something cmpl does fails. */
375 static gint cmpl_errno;
379 * Take the path currently in the file selection
380 * entry field and translate as necessary from
381 * a WIN32 style to CYGWIN32 style path. For
382 * instance translate:
383 * x:\somepath\file.jpg
385 * //x/somepath/file.jpg
387 * Replace the path in the selection text field.
388 * Return a boolean value concerning whether a
389 * translation had to be made.
392 translate_win32_path (GtkFileSelection *filesel)
398 * Retrieve the current path
400 path = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
403 * Translate only if this looks like a DOS-ish
404 * path... First handle any drive letters.
406 if (isalpha (path[0]) && (path[1] == ':')) {
408 * This part kind of stinks... It isn't possible
409 * to know if there is enough space in the current
410 * string for the extra character required in this
411 * conversion. Assume that there isn't enough space
412 * and use the set function on the text field to
413 * set the newly created string.
415 gchar *newPath = g_strdup_printf ("//%c/%s", path[0], (path + 3));
416 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), newPath);
423 * Now, replace backslashes with forward slashes
426 if (strchr (path, '\\'))
429 for (index = 0; path[index] != '\0'; index++)
430 if (path[index] == '\\')
441 gtk_file_selection_get_type (void)
443 static GtkType file_selection_type = 0;
445 if (!file_selection_type)
447 static const GtkTypeInfo filesel_info =
450 sizeof (GtkFileSelection),
451 sizeof (GtkFileSelectionClass),
452 (GtkClassInitFunc) gtk_file_selection_class_init,
453 (GtkObjectInitFunc) gtk_file_selection_init,
454 /* reserved_1 */ NULL,
455 /* reserved_2 */ NULL,
456 (GtkClassInitFunc) NULL,
459 file_selection_type = gtk_type_unique (GTK_TYPE_DIALOG, &filesel_info);
462 return file_selection_type;
466 gtk_file_selection_class_init (GtkFileSelectionClass *class)
468 GtkObjectClass *object_class;
470 object_class = (GtkObjectClass*) class;
472 parent_class = gtk_type_class (GTK_TYPE_DIALOG);
474 object_class->destroy = gtk_file_selection_destroy;
478 gtk_file_selection_init (GtkFileSelection *filesel)
480 GtkWidget *entry_vbox;
482 GtkWidget *list_hbox;
483 GtkWidget *confirm_area;
484 GtkWidget *pulldown_hbox;
485 GtkWidget *scrolled_win;
489 char *file_title [2];
491 dialog = GTK_DIALOG (filesel);
493 filesel->cmpl_state = cmpl_init_state ();
495 /* The dialog-sized vertical box */
496 filesel->main_vbox = dialog->vbox;
497 gtk_container_set_border_width (GTK_CONTAINER (filesel), 10);
499 /* The horizontal box containing create, rename etc. buttons */
500 filesel->button_area = gtk_hbutton_box_new ();
501 gtk_button_box_set_layout (GTK_BUTTON_BOX (filesel->button_area), GTK_BUTTONBOX_START);
502 gtk_button_box_set_spacing (GTK_BUTTON_BOX (filesel->button_area), 0);
503 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->button_area,
505 gtk_widget_show (filesel->button_area);
507 gtk_file_selection_show_fileop_buttons (filesel);
509 /* hbox for pulldown menu */
510 pulldown_hbox = gtk_hbox_new (TRUE, 5);
511 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), pulldown_hbox, FALSE, FALSE, 0);
512 gtk_widget_show (pulldown_hbox);
515 filesel->history_pulldown = gtk_option_menu_new ();
516 gtk_widget_show (filesel->history_pulldown);
517 gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->history_pulldown,
520 /* The horizontal box containing the directory and file listboxes */
521 list_hbox = gtk_hbox_new (FALSE, 5);
522 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), list_hbox, TRUE, TRUE, 0);
523 gtk_widget_show (list_hbox);
525 /* The directories clist */
526 dir_title[0] = _("Directories");
528 filesel->dir_list = gtk_clist_new_with_titles (1, (gchar**) dir_title);
529 gtk_widget_set_usize (filesel->dir_list, DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
530 gtk_signal_connect (GTK_OBJECT (filesel->dir_list), "select_row",
531 (GtkSignalFunc) gtk_file_selection_dir_button,
533 gtk_clist_set_column_auto_resize (GTK_CLIST (filesel->dir_list), 0, TRUE);
534 gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list));
536 scrolled_win = gtk_scrolled_window_new (NULL, NULL);
537 gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->dir_list);
538 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
539 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
540 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
541 gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
542 gtk_widget_show (filesel->dir_list);
543 gtk_widget_show (scrolled_win);
545 /* The files clist */
546 file_title[0] = _("Files");
547 file_title[1] = NULL;
548 filesel->file_list = gtk_clist_new_with_titles (1, (gchar**) file_title);
549 gtk_widget_set_usize (filesel->file_list, FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
550 gtk_signal_connect (GTK_OBJECT (filesel->file_list), "select_row",
551 (GtkSignalFunc) gtk_file_selection_file_button,
553 gtk_clist_set_column_auto_resize (GTK_CLIST (filesel->file_list), 0, TRUE);
554 gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list));
556 scrolled_win = gtk_scrolled_window_new (NULL, NULL);
557 gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->file_list);
558 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
559 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
560 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
561 gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
562 gtk_widget_show (filesel->file_list);
563 gtk_widget_show (scrolled_win);
565 /* action area for packing buttons into. */
566 filesel->action_area = gtk_hbox_new (TRUE, 0);
567 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->action_area,
569 gtk_widget_show (filesel->action_area);
571 /* The OK/Cancel button area */
572 confirm_area = dialog->action_area;
575 filesel->ok_button = gtk_dialog_add_button (dialog,
579 gtk_widget_grab_default (filesel->ok_button);
581 /* The Cancel button */
582 filesel->cancel_button = gtk_dialog_add_button (dialog,
583 GTK_STOCK_BUTTON_CANCEL,
584 GTK_RESPONSE_CANCEL);
586 /* The selection entry widget */
587 entry_vbox = gtk_vbox_new (FALSE, 2);
588 gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 2);
589 gtk_widget_show (entry_vbox);
591 filesel->selection_text = label = gtk_label_new ("");
592 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
593 gtk_box_pack_start (GTK_BOX (entry_vbox), label, FALSE, FALSE, 0);
594 gtk_widget_show (label);
596 filesel->selection_entry = gtk_entry_new ();
597 gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "key_press_event",
598 (GtkSignalFunc) gtk_file_selection_key_press, filesel);
599 gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "focus_in_event",
600 (GtkSignalFunc) gtk_widget_grab_default,
601 GTK_OBJECT (filesel->ok_button));
602 gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "activate",
603 (GtkSignalFunc) gtk_button_clicked,
604 GTK_OBJECT (filesel->ok_button));
605 gtk_box_pack_start (GTK_BOX (entry_vbox), filesel->selection_entry, TRUE, TRUE, 0);
606 gtk_widget_show (filesel->selection_entry);
608 if (!cmpl_state_okay (filesel->cmpl_state))
612 sprintf (err_buf, _("Directory unreadable: %s"), cmpl_strerror (cmpl_errno));
614 gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
618 gtk_file_selection_populate (filesel, "", FALSE);
621 gtk_widget_grab_focus (filesel->selection_entry);
625 gtk_file_selection_new (const gchar *title)
627 GtkFileSelection *filesel;
629 filesel = gtk_type_new (GTK_TYPE_FILE_SELECTION);
630 gtk_window_set_title (GTK_WINDOW (filesel), title);
632 return GTK_WIDGET (filesel);
636 gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
638 g_return_if_fail (filesel != NULL);
639 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
641 /* delete, create directory, and rename */
642 if (!filesel->fileop_c_dir)
644 filesel->fileop_c_dir = gtk_button_new_with_label (_("Create Dir"));
645 gtk_signal_connect (GTK_OBJECT (filesel->fileop_c_dir), "clicked",
646 (GtkSignalFunc) gtk_file_selection_create_dir,
648 gtk_box_pack_start (GTK_BOX (filesel->button_area),
649 filesel->fileop_c_dir, TRUE, TRUE, 0);
650 gtk_widget_show (filesel->fileop_c_dir);
653 if (!filesel->fileop_del_file)
655 filesel->fileop_del_file = gtk_button_new_with_label (_("Delete File"));
656 gtk_signal_connect (GTK_OBJECT (filesel->fileop_del_file), "clicked",
657 (GtkSignalFunc) gtk_file_selection_delete_file,
659 gtk_box_pack_start (GTK_BOX (filesel->button_area),
660 filesel->fileop_del_file, TRUE, TRUE, 0);
661 gtk_widget_show (filesel->fileop_del_file);
664 if (!filesel->fileop_ren_file)
666 filesel->fileop_ren_file = gtk_button_new_with_label (_("Rename File"));
667 gtk_signal_connect (GTK_OBJECT (filesel->fileop_ren_file), "clicked",
668 (GtkSignalFunc) gtk_file_selection_rename_file,
670 gtk_box_pack_start (GTK_BOX (filesel->button_area),
671 filesel->fileop_ren_file, TRUE, TRUE, 0);
672 gtk_widget_show (filesel->fileop_ren_file);
675 gtk_widget_queue_resize (GTK_WIDGET (filesel));
679 gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
681 g_return_if_fail (filesel != NULL);
682 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
684 if (filesel->fileop_ren_file)
686 gtk_widget_destroy (filesel->fileop_ren_file);
687 filesel->fileop_ren_file = NULL;
690 if (filesel->fileop_del_file)
692 gtk_widget_destroy (filesel->fileop_del_file);
693 filesel->fileop_del_file = NULL;
696 if (filesel->fileop_c_dir)
698 gtk_widget_destroy (filesel->fileop_c_dir);
699 filesel->fileop_c_dir = NULL;
706 gtk_file_selection_set_filename (GtkFileSelection *filesel,
707 const gchar *filename)
710 const char *name, *last_slash;
712 g_return_if_fail (filesel != NULL);
713 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
714 g_return_if_fail (filename != NULL);
716 last_slash = strrchr (filename, G_DIR_SEPARATOR);
725 buf = g_strdup (filename);
726 buf[last_slash - filename + 1] = 0;
727 name = last_slash + 1;
730 gtk_file_selection_populate (filesel, buf, FALSE);
732 if (filesel->selection_entry)
733 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
738 gtk_file_selection_get_filename (GtkFileSelection *filesel)
740 static gchar nothing[2] = "";
741 static gchar something[MAXPATHLEN*2];
745 g_return_val_if_fail (filesel != NULL, nothing);
746 g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
749 translate_win32_path (filesel);
751 text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
754 sys_filename = g_filename_from_utf8 (cmpl_completion_fullname (text, filesel->cmpl_state), -1, NULL, NULL, NULL);
755 strncpy (something, sys_filename, sizeof (something));
756 g_free (sys_filename);
764 gtk_file_selection_complete (GtkFileSelection *filesel,
765 const gchar *pattern)
767 g_return_if_fail (filesel != NULL);
768 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
769 g_return_if_fail (pattern != NULL);
771 if (filesel->selection_entry)
772 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), pattern);
773 gtk_file_selection_populate (filesel, (gchar*) pattern, TRUE);
777 gtk_file_selection_destroy (GtkObject *object)
779 GtkFileSelection *filesel;
781 HistoryCallbackArg *callback_arg;
783 g_return_if_fail (GTK_IS_FILE_SELECTION (object));
785 filesel = GTK_FILE_SELECTION (object);
787 if (filesel->fileop_dialog)
789 gtk_widget_destroy (filesel->fileop_dialog);
790 filesel->fileop_dialog = NULL;
793 if (filesel->history_list)
795 list = filesel->history_list;
798 callback_arg = list->data;
799 g_free (callback_arg->directory);
800 g_free (callback_arg);
803 g_list_free (filesel->history_list);
804 filesel->history_list = NULL;
807 if (filesel->cmpl_state)
809 cmpl_free_state (filesel->cmpl_state);
810 filesel->cmpl_state = NULL;
813 GTK_OBJECT_CLASS (parent_class)->destroy (object);
816 /* Begin file operations callbacks */
819 gtk_file_selection_fileop_error (GtkFileSelection *fs,
820 gchar *error_message)
827 g_return_if_fail (error_message != NULL);
830 dialog = gtk_dialog_new ();
832 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
833 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
836 gtk_window_set_title (GTK_WINDOW (dialog), _("Error"));
837 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
839 /* If file dialog is grabbed, make this dialog modal too */
840 /* When error dialog is closed, file dialog will be grabbed again */
841 if (GTK_WINDOW (fs)->modal)
842 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
844 vbox = gtk_vbox_new (FALSE, 0);
845 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
846 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
848 gtk_widget_show (vbox);
850 label = gtk_label_new (error_message);
851 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
852 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
853 gtk_widget_show (label);
855 /* yes, we free it */
856 g_free (error_message);
859 button = gtk_button_new_with_label (_("Close"));
860 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
861 (GtkSignalFunc) gtk_widget_destroy,
863 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
864 button, TRUE, TRUE, 0);
865 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
866 gtk_widget_grab_default (button);
867 gtk_widget_show (button);
869 gtk_widget_show (dialog);
873 gtk_file_selection_fileop_destroy (GtkWidget *widget,
876 GtkFileSelection *fs = data;
878 g_return_if_fail (fs != NULL);
879 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
881 fs->fileop_dialog = NULL;
886 gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
889 GtkFileSelection *fs = data;
893 gchar *sys_full_path;
895 CompletionState *cmpl_state;
897 g_return_if_fail (fs != NULL);
898 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
900 dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
901 cmpl_state = (CompletionState*) fs->cmpl_state;
902 path = cmpl_reference_position (cmpl_state);
904 full_path = g_strconcat (path, G_DIR_SEPARATOR_S, dirname, NULL);
905 sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, NULL);
906 if (mkdir (sys_full_path, 0755) < 0)
908 buf = g_strconcat ("Error creating directory \"", dirname, "\": ",
909 g_strerror (errno), NULL);
910 gtk_file_selection_fileop_error (fs, buf);
913 g_free (sys_full_path);
915 gtk_widget_destroy (fs->fileop_dialog);
916 gtk_file_selection_populate (fs, "", FALSE);
920 gtk_file_selection_create_dir (GtkWidget *widget,
923 GtkFileSelection *fs = data;
929 g_return_if_fail (fs != NULL);
930 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
932 if (fs->fileop_dialog)
936 dialog = gtk_dialog_new ();
937 fs->fileop_dialog = dialog;
938 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
939 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
941 gtk_window_set_title (GTK_WINDOW (dialog), _("Create Directory"));
942 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
944 /* If file dialog is grabbed, grab option dialog */
945 /* When option dialog is closed, file dialog will be grabbed again */
946 if (GTK_WINDOW (fs)->modal)
947 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
949 vbox = gtk_vbox_new (FALSE, 0);
950 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
951 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
953 gtk_widget_show( vbox);
955 label = gtk_label_new (_("Directory name:"));
956 gtk_misc_set_alignment(GTK_MISC (label), 0.0, 0.0);
957 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
958 gtk_widget_show (label);
960 /* The directory entry widget */
961 fs->fileop_entry = gtk_entry_new ();
962 gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry,
964 GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
965 gtk_widget_show (fs->fileop_entry);
968 button = gtk_button_new_with_label (_("Create"));
969 gtk_signal_connect (GTK_OBJECT (button), "clicked",
970 (GtkSignalFunc) gtk_file_selection_create_dir_confirmed,
972 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
973 button, TRUE, TRUE, 0);
974 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
975 gtk_widget_show (button);
977 button = gtk_button_new_with_label (_("Cancel"));
978 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
979 (GtkSignalFunc) gtk_widget_destroy,
981 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
982 button, TRUE, TRUE, 0);
983 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
984 gtk_widget_grab_default (button);
985 gtk_widget_show (button);
987 gtk_widget_show (dialog);
991 gtk_file_selection_delete_file_confirmed (GtkWidget *widget,
994 GtkFileSelection *fs = data;
995 CompletionState *cmpl_state;
998 gchar *sys_full_path;
1001 g_return_if_fail (fs != NULL);
1002 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1004 cmpl_state = (CompletionState*) fs->cmpl_state;
1005 path = cmpl_reference_position (cmpl_state);
1007 full_path = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1008 sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, NULL);
1009 if (unlink (sys_full_path) < 0)
1011 buf = g_strconcat ("Error deleting file \"", fs->fileop_file, "\": ",
1012 g_strerror (errno), NULL);
1013 gtk_file_selection_fileop_error (fs, buf);
1016 g_free (sys_full_path);
1018 gtk_widget_destroy (fs->fileop_dialog);
1019 gtk_file_selection_populate (fs, "", FALSE);
1023 gtk_file_selection_delete_file (GtkWidget *widget,
1026 GtkFileSelection *fs = data;
1034 g_return_if_fail (fs != NULL);
1035 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1037 if (fs->fileop_dialog)
1040 #ifdef G_WITH_CYGWIN
1041 translate_win32_path (fs);
1044 filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1045 if (strlen (filename) < 1)
1048 fs->fileop_file = filename;
1051 fs->fileop_dialog = dialog = gtk_dialog_new ();
1052 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1053 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1055 gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
1056 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1058 /* If file dialog is grabbed, grab option dialog */
1059 /* When option dialog is closed, file dialog will be grabbed again */
1060 if (GTK_WINDOW (fs)->modal)
1061 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1063 vbox = gtk_vbox_new (FALSE, 0);
1064 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1065 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1067 gtk_widget_show (vbox);
1069 buf = g_strconcat ("Really delete file \"", filename, "\" ?", NULL);
1070 label = gtk_label_new (buf);
1071 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1072 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1073 gtk_widget_show (label);
1077 button = gtk_button_new_with_label (_("Delete"));
1078 gtk_signal_connect (GTK_OBJECT (button), "clicked",
1079 (GtkSignalFunc) gtk_file_selection_delete_file_confirmed,
1081 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1082 button, TRUE, TRUE, 0);
1083 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1084 gtk_widget_show (button);
1086 button = gtk_button_new_with_label (_("Cancel"));
1087 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1088 (GtkSignalFunc) gtk_widget_destroy,
1090 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1091 button, TRUE, TRUE, 0);
1092 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1093 gtk_widget_grab_default (button);
1094 gtk_widget_show (button);
1096 gtk_widget_show (dialog);
1101 gtk_file_selection_rename_file_confirmed (GtkWidget *widget,
1104 GtkFileSelection *fs = data;
1108 gchar *new_filename;
1109 gchar *old_filename;
1110 gchar *sys_new_filename;
1111 gchar *sys_old_filename;
1112 CompletionState *cmpl_state;
1114 g_return_if_fail (fs != NULL);
1115 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1117 file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1118 cmpl_state = (CompletionState*) fs->cmpl_state;
1119 path = cmpl_reference_position (cmpl_state);
1121 new_filename = g_strconcat (path, G_DIR_SEPARATOR_S, file, NULL);
1122 old_filename = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1124 sys_new_filename = g_filename_from_utf8 (new_filename, -1, NULL, NULL, NULL);
1125 sys_old_filename = g_filename_from_utf8 (old_filename, -1, NULL, NULL, NULL);
1127 if (rename (sys_old_filename, sys_new_filename) < 0)
1129 buf = g_strconcat ("Error renaming file \"", file, "\": ",
1130 g_strerror (errno), NULL);
1131 gtk_file_selection_fileop_error (fs, buf);
1133 g_free (new_filename);
1134 g_free (old_filename);
1135 g_free (sys_new_filename);
1136 g_free (sys_old_filename);
1138 gtk_widget_destroy (fs->fileop_dialog);
1139 gtk_file_selection_populate (fs, "", FALSE);
1143 gtk_file_selection_rename_file (GtkWidget *widget,
1146 GtkFileSelection *fs = data;
1153 g_return_if_fail (fs != NULL);
1154 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1156 if (fs->fileop_dialog)
1159 fs->fileop_file = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1160 if (strlen (fs->fileop_file) < 1)
1164 fs->fileop_dialog = dialog = gtk_dialog_new ();
1165 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1166 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1168 gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File"));
1169 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1171 /* If file dialog is grabbed, grab option dialog */
1172 /* When option dialog closed, file dialog will be grabbed again */
1173 if (GTK_WINDOW (fs)->modal)
1174 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1176 vbox = gtk_vbox_new (FALSE, 0);
1177 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1178 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1180 gtk_widget_show(vbox);
1182 buf = g_strconcat ("Rename file \"", fs->fileop_file, "\" to:", NULL);
1183 label = gtk_label_new(buf);
1184 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1185 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1186 gtk_widget_show (label);
1189 /* New filename entry */
1190 fs->fileop_entry = gtk_entry_new ();
1191 gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry,
1193 GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1194 gtk_widget_show (fs->fileop_entry);
1196 gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
1197 gtk_editable_select_region (GTK_EDITABLE (fs->fileop_entry),
1198 0, strlen (fs->fileop_file));
1201 button = gtk_button_new_with_label (_("Rename"));
1202 gtk_signal_connect (GTK_OBJECT (button), "clicked",
1203 (GtkSignalFunc) gtk_file_selection_rename_file_confirmed,
1205 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1206 button, TRUE, TRUE, 0);
1207 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1208 gtk_widget_show (button);
1210 button = gtk_button_new_with_label (_("Cancel"));
1211 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1212 (GtkSignalFunc) gtk_widget_destroy,
1214 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1215 button, TRUE, TRUE, 0);
1216 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1217 gtk_widget_grab_default (button);
1218 gtk_widget_show (button);
1220 gtk_widget_show (dialog);
1225 gtk_file_selection_key_press (GtkWidget *widget,
1229 GtkFileSelection *fs;
1232 g_return_val_if_fail (widget != NULL, FALSE);
1233 g_return_val_if_fail (event != NULL, FALSE);
1235 if (event->keyval == GDK_Tab)
1237 fs = GTK_FILE_SELECTION (user_data);
1238 #ifdef G_WITH_CYGWIN
1239 translate_win32_path (fs);
1241 text = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1243 text = g_strdup (text);
1245 gtk_file_selection_populate (fs, text, TRUE);
1249 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
1259 gtk_file_selection_history_callback (GtkWidget *widget,
1262 GtkFileSelection *fs = data;
1263 HistoryCallbackArg *callback_arg;
1266 g_return_if_fail (fs != NULL);
1267 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1269 list = fs->history_list;
1272 callback_arg = list->data;
1274 if (callback_arg->menu_item == widget)
1276 gtk_file_selection_populate (fs, callback_arg->directory, FALSE);
1285 gtk_file_selection_update_history_menu (GtkFileSelection *fs,
1286 gchar *current_directory)
1288 HistoryCallbackArg *callback_arg;
1289 GtkWidget *menu_item;
1295 g_return_if_fail (fs != NULL);
1296 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1297 g_return_if_fail (current_directory != NULL);
1299 list = fs->history_list;
1301 if (fs->history_menu)
1304 callback_arg = list->data;
1305 g_free (callback_arg->directory);
1306 g_free (callback_arg);
1309 g_list_free (fs->history_list);
1310 fs->history_list = NULL;
1312 gtk_widget_destroy (fs->history_menu);
1315 fs->history_menu = gtk_menu_new ();
1317 current_dir = g_strdup (current_directory);
1319 dir_len = strlen (current_dir);
1321 for (i = dir_len; i >= 0; i--)
1323 /* the i == dir_len is to catch the full path for the first
1325 if ( (current_dir[i] == G_DIR_SEPARATOR) || (i == dir_len))
1327 /* another small hack to catch the full path */
1329 current_dir[i + 1] = '\0';
1330 #ifdef G_WITH_CYGWIN
1331 if (!strcmp (current_dir, "//"))
1334 menu_item = gtk_menu_item_new_with_label (current_dir);
1336 callback_arg = g_new (HistoryCallbackArg, 1);
1337 callback_arg->menu_item = menu_item;
1339 /* since the autocompletion gets confused if you don't
1340 * supply a trailing '/' on a dir entry, set the full
1341 * (current) path to "" which just refreshes the filesel */
1344 callback_arg->directory = g_strdup ("");
1348 callback_arg->directory = g_strdup (current_dir);
1351 fs->history_list = g_list_append (fs->history_list, callback_arg);
1353 gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
1354 (GtkSignalFunc) gtk_file_selection_history_callback,
1356 gtk_menu_shell_append (GTK_MENU_SHELL (fs->history_menu), menu_item);
1357 gtk_widget_show (menu_item);
1361 gtk_option_menu_set_menu (GTK_OPTION_MENU (fs->history_pulldown),
1363 g_free (current_dir);
1367 gtk_file_selection_file_button (GtkWidget *widget,
1370 GdkEventButton *bevent,
1373 GtkFileSelection *fs = NULL;
1374 gchar *filename, *temp = NULL;
1376 g_return_if_fail (GTK_IS_CLIST (widget));
1379 g_return_if_fail (fs != NULL);
1380 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1382 gtk_clist_get_text (GTK_CLIST (fs->file_list), row, 0, &temp);
1383 filename = g_strdup (temp);
1385 #ifdef G_WITH_CYGWIN
1386 /* Check to see if the selection was a drive selector */
1387 if (isalpha (filename[0]) && (filename[1] == ':')) {
1388 /* It is... map it to a CYGWIN32 drive */
1389 gchar *temp_filename = g_strdup_printf ("//%c/", tolower (filename[0]));
1391 filename = temp_filename;
1393 #endif /* G_WITH_CYGWIN */
1398 switch (bevent->type)
1400 case GDK_2BUTTON_PRESS:
1401 gtk_button_clicked (GTK_BUTTON (fs->ok_button));
1405 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1409 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1416 gtk_file_selection_dir_button (GtkWidget *widget,
1419 GdkEventButton *bevent,
1422 GtkFileSelection *fs = NULL;
1423 gchar *filename, *temp = NULL;
1425 g_return_if_fail (GTK_IS_CLIST (widget));
1427 fs = GTK_FILE_SELECTION (user_data);
1428 g_return_if_fail (fs != NULL);
1429 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1431 gtk_clist_get_text (GTK_CLIST (fs->dir_list), row, 0, &temp);
1432 filename = g_strdup (temp);
1437 switch (bevent->type)
1439 case GDK_2BUTTON_PRESS:
1440 gtk_file_selection_populate (fs, filename, FALSE);
1444 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1448 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1454 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
1457 win32_gtk_add_drives_to_dir_list (GtkWidget *the_dir_list)
1459 gchar *text[2], *textPtr;
1461 char volumeNameBuf[128];
1462 char formatBuffer[128];
1466 /* Get the Drives string */
1467 GetLogicalDriveStrings (sizeof (buffer), buffer);
1469 /* Add the drives as necessary */
1471 while (*textPtr != '\0') {
1472 /* Get the volume information for this drive */
1473 if ((tolower (textPtr[0]) != 'a') && (tolower (textPtr[0]) != 'b'))
1475 /* Ignore floppies (?) */
1476 DWORD maxComponentLength, flags;
1478 GetVolumeInformation (textPtr,
1479 volumeNameBuf, sizeof(volumeNameBuf),
1480 NULL, &maxComponentLength,
1482 /* Build the actual displayable string */
1484 sprintf (formatBuffer, "%c:\\", toupper(textPtr[0]));
1485 #if 0 /* HB: removed to allow drive change AND directory update with one click */
1486 if (strlen (volumeNameBuf) > 0)
1487 sprintf (formatBuffer, "%s (%s)", formatBuffer, volumeNameBuf);
1489 /* Add to the list */
1490 text[0] = formatBuffer;
1491 gtk_clist_append (GTK_CLIST (the_dir_list), text);
1493 textPtr += (strlen (textPtr) + 1);
1499 gtk_file_selection_populate (GtkFileSelection *fs,
1503 CompletionState *cmpl_state;
1504 PossibleCompletion* poss;
1506 gchar* rem_path = rel_path;
1509 gint did_recurse = FALSE;
1510 gint possible_count = 0;
1511 gint selection_index = -1;
1513 g_return_if_fail (fs != NULL);
1514 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1516 cmpl_state = (CompletionState*) fs->cmpl_state;
1517 poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
1519 if (!cmpl_state_okay (cmpl_state))
1521 /* Something went wrong. */
1522 gtk_file_selection_abort (fs);
1526 g_assert (cmpl_state->reference_dir);
1528 gtk_clist_freeze (GTK_CLIST (fs->dir_list));
1529 gtk_clist_clear (GTK_CLIST (fs->dir_list));
1530 gtk_clist_freeze (GTK_CLIST (fs->file_list));
1531 gtk_clist_clear (GTK_CLIST (fs->file_list));
1533 /* Set the dir_list to include ./ and ../ */
1535 text[0] = "." G_DIR_SEPARATOR_S;
1536 gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1538 text[0] = ".." G_DIR_SEPARATOR_S;
1539 gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1543 if (cmpl_is_a_completion (poss))
1545 possible_count += 1;
1547 filename = cmpl_this_completion (poss);
1551 if (cmpl_is_directory (poss))
1553 if (strcmp (filename, "." G_DIR_SEPARATOR_S) != 0 &&
1554 strcmp (filename, ".." G_DIR_SEPARATOR_S) != 0)
1555 gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1559 gtk_clist_append (GTK_CLIST (fs->file_list), text);
1563 poss = cmpl_next_completion (cmpl_state);
1566 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
1567 /* For Windows, add drives as potential selections */
1568 win32_gtk_add_drives_to_dir_list (fs->dir_list);
1571 gtk_clist_thaw (GTK_CLIST (fs->dir_list));
1572 gtk_clist_thaw (GTK_CLIST (fs->file_list));
1574 /* File lists are set. */
1576 g_assert (cmpl_state->reference_dir);
1581 /* User is trying to complete filenames, so advance the user's input
1582 * string to the updated_text, which is the common leading substring
1583 * of all possible completions, and if its a directory attempt
1584 * attempt completions in it. */
1586 if (cmpl_updated_text (cmpl_state)[0])
1589 if (cmpl_updated_dir (cmpl_state))
1591 gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
1595 gtk_file_selection_populate (fs, dir_name, TRUE);
1601 if (fs->selection_entry)
1602 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
1603 cmpl_updated_text (cmpl_state));
1608 selection_index = cmpl_last_valid_char (cmpl_state) -
1609 (strlen (rel_path) - strlen (rem_path));
1610 if (fs->selection_entry)
1611 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
1616 if (fs->selection_entry)
1617 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
1622 if (fs->selection_entry)
1623 gtk_entry_set_position (GTK_ENTRY (fs->selection_entry), selection_index);
1625 if (fs->selection_entry)
1627 sel_text = g_strconcat (_("Selection: "),
1628 cmpl_reference_position (cmpl_state),
1631 gtk_label_set_text (GTK_LABEL (fs->selection_text), sel_text);
1635 if (fs->history_pulldown)
1637 gtk_file_selection_update_history_menu (fs, cmpl_reference_position (cmpl_state));
1644 gtk_file_selection_abort (GtkFileSelection *fs)
1648 sprintf (err_buf, _("Directory unreadable: %s"), cmpl_strerror (cmpl_errno));
1650 /* BEEP gdk_beep(); */
1652 if (fs->selection_entry)
1653 gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
1656 /**********************************************************************/
1657 /* External Interface */
1658 /**********************************************************************/
1660 /* The four completion state selectors
1663 cmpl_updated_text (CompletionState *cmpl_state)
1665 return cmpl_state->updated_text;
1669 cmpl_updated_dir (CompletionState *cmpl_state)
1671 return cmpl_state->re_complete;
1675 cmpl_reference_position (CompletionState *cmpl_state)
1677 return cmpl_state->reference_dir->fullname;
1681 cmpl_last_valid_char (CompletionState *cmpl_state)
1683 return cmpl_state->last_valid_char;
1687 cmpl_completion_fullname (gchar *text,
1688 CompletionState *cmpl_state)
1690 static char nothing[2] = "";
1692 if (!cmpl_state_okay (cmpl_state))
1696 else if (g_path_is_absolute (text))
1698 strcpy (cmpl_state->updated_text, text);
1701 else if (text[0] == '~')
1706 dir = open_user_dir (text, cmpl_state);
1710 /* spencer says just return ~something, so
1711 * for now just do it. */
1712 strcpy (cmpl_state->updated_text, text);
1717 strcpy (cmpl_state->updated_text, dir->fullname);
1719 slash = strchr (text, G_DIR_SEPARATOR);
1722 strcat (cmpl_state->updated_text, slash);
1728 strcpy (cmpl_state->updated_text, cmpl_state->reference_dir->fullname);
1729 if (cmpl_state->updated_text[strlen (cmpl_state->updated_text) - 1] != G_DIR_SEPARATOR)
1730 strcat (cmpl_state->updated_text, G_DIR_SEPARATOR_S);
1731 strcat (cmpl_state->updated_text, text);
1734 return cmpl_state->updated_text;
1737 /* The three completion selectors
1740 cmpl_this_completion (PossibleCompletion* pc)
1746 cmpl_is_directory (PossibleCompletion* pc)
1748 return pc->is_directory;
1752 cmpl_is_a_completion (PossibleCompletion* pc)
1754 return pc->is_a_completion;
1757 /**********************************************************************/
1758 /* Construction, deletion */
1759 /**********************************************************************/
1761 static CompletionState*
1762 cmpl_init_state (void)
1764 gchar *sys_getcwd_buf;
1766 CompletionState *new_state;
1768 new_state = g_new (CompletionState, 1);
1770 /* g_get_current_dir() returns a string in the "system" charset */
1771 sys_getcwd_buf = g_get_current_dir ();
1772 utf8_cwd = g_filename_to_utf8 (sys_getcwd_buf, -1, NULL, NULL, NULL);
1773 g_free (sys_getcwd_buf);
1777 new_state->reference_dir = NULL;
1778 new_state->completion_dir = NULL;
1779 new_state->active_completion_dir = NULL;
1780 new_state->directory_storage = NULL;
1781 new_state->directory_sent_storage = NULL;
1782 new_state->last_valid_char = 0;
1783 new_state->updated_text = g_new (gchar, MAXPATHLEN);
1784 new_state->updated_text_alloc = MAXPATHLEN;
1785 new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
1786 new_state->the_completion.text_alloc = MAXPATHLEN;
1787 new_state->user_dir_name_buffer = NULL;
1788 new_state->user_directories = NULL;
1790 new_state->reference_dir = open_dir (utf8_cwd, new_state);
1792 if (!new_state->reference_dir)
1794 /* Directories changing from underneath us, grumble */
1795 strcpy (utf8_cwd, G_DIR_SEPARATOR_S);
1804 cmpl_free_dir_list (GList* dp0)
1810 free_dir (dp->data);
1818 cmpl_free_dir_sent_list (GList* dp0)
1824 free_dir_sent (dp->data);
1832 cmpl_free_state (CompletionState* cmpl_state)
1834 g_return_if_fail (cmpl_state != NULL);
1836 cmpl_free_dir_list (cmpl_state->directory_storage);
1837 cmpl_free_dir_sent_list (cmpl_state->directory_sent_storage);
1839 if (cmpl_state->user_dir_name_buffer)
1840 g_free (cmpl_state->user_dir_name_buffer);
1841 if (cmpl_state->user_directories)
1842 g_free (cmpl_state->user_directories);
1843 if (cmpl_state->the_completion.text)
1844 g_free (cmpl_state->the_completion.text);
1845 if (cmpl_state->updated_text)
1846 g_free (cmpl_state->updated_text);
1848 g_free (cmpl_state);
1852 free_dir (CompletionDir* dir)
1854 g_free (dir->fullname);
1859 free_dir_sent (CompletionDirSent* sent)
1862 for (i = 0; i < sent->entry_count; i++)
1863 g_free (sent->entries[i].entry_name);
1864 g_free (sent->entries);
1869 prune_memory_usage (CompletionState *cmpl_state)
1871 GList* cdsl = cmpl_state->directory_sent_storage;
1872 GList* cdl = cmpl_state->directory_storage;
1876 for (; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
1881 cmpl_free_dir_sent_list (cdsl->next);
1885 cmpl_state->directory_storage = NULL;
1888 if (cdl->data == cmpl_state->reference_dir)
1889 cmpl_state->directory_storage = g_list_prepend (NULL, cdl->data);
1891 free_dir (cdl->data);
1898 /**********************************************************************/
1899 /* The main entrances. */
1900 /**********************************************************************/
1902 static PossibleCompletion*
1903 cmpl_completion_matches (gchar *text_to_complete,
1904 gchar **remaining_text,
1905 CompletionState *cmpl_state)
1908 PossibleCompletion *poss;
1910 prune_memory_usage (cmpl_state);
1912 g_assert (text_to_complete != NULL);
1914 cmpl_state->user_completion_index = -1;
1915 cmpl_state->last_completion_text = text_to_complete;
1916 cmpl_state->the_completion.text[0] = 0;
1917 cmpl_state->last_valid_char = 0;
1918 cmpl_state->updated_text_len = -1;
1919 cmpl_state->updated_text[0] = 0;
1920 cmpl_state->re_complete = FALSE;
1923 first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
1925 if (text_to_complete[0] == '~' && !first_slash)
1927 /* Text starts with ~ and there is no slash, show all the
1928 * home directory completions.
1930 poss = attempt_homedir_completion (text_to_complete, cmpl_state);
1932 update_cmpl (poss, cmpl_state);
1937 cmpl_state->reference_dir =
1938 open_ref_dir (text_to_complete, remaining_text, cmpl_state);
1940 if (!cmpl_state->reference_dir)
1943 cmpl_state->completion_dir =
1944 find_completion_dir (*remaining_text, remaining_text, cmpl_state);
1946 cmpl_state->last_valid_char = *remaining_text - text_to_complete;
1948 if (!cmpl_state->completion_dir)
1951 cmpl_state->completion_dir->cmpl_index = -1;
1952 cmpl_state->completion_dir->cmpl_parent = NULL;
1953 cmpl_state->completion_dir->cmpl_text = *remaining_text;
1955 cmpl_state->active_completion_dir = cmpl_state->completion_dir;
1957 cmpl_state->reference_dir = cmpl_state->completion_dir;
1959 poss = attempt_file_completion (cmpl_state);
1961 update_cmpl (poss, cmpl_state);
1966 static PossibleCompletion*
1967 cmpl_next_completion (CompletionState* cmpl_state)
1969 PossibleCompletion* poss = NULL;
1971 cmpl_state->the_completion.text[0] = 0;
1974 if (cmpl_state->user_completion_index >= 0)
1975 poss = attempt_homedir_completion (cmpl_state->last_completion_text, cmpl_state);
1977 poss = attempt_file_completion (cmpl_state);
1979 poss = attempt_file_completion (cmpl_state);
1982 update_cmpl (poss, cmpl_state);
1987 /**********************************************************************/
1988 /* Directory Operations */
1989 /**********************************************************************/
1991 /* Open the directory where completion will begin from, if possible. */
1992 static CompletionDir*
1993 open_ref_dir (gchar *text_to_complete,
1994 gchar **remaining_text,
1995 CompletionState *cmpl_state)
1998 CompletionDir *new_dir;
2000 first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2002 #ifdef G_WITH_CYGWIN
2003 if (text_to_complete[0] == '/' && text_to_complete[1] == '/')
2006 sprintf (root_dir, "//%c", text_to_complete[2]);
2008 new_dir = open_dir (root_dir, cmpl_state);
2011 *remaining_text = text_to_complete + 4;
2019 else if (text_to_complete[0] == '~')
2021 new_dir = open_user_dir (text_to_complete, cmpl_state);
2026 *remaining_text = first_slash + 1;
2028 *remaining_text = text_to_complete + strlen (text_to_complete);
2036 else if (g_path_is_absolute (text_to_complete) || !cmpl_state->reference_dir)
2038 gchar *tmp = g_strdup (text_to_complete);
2042 while (*p && *p != '*' && *p != '?')
2046 p = strrchr (tmp, G_DIR_SEPARATOR);
2054 new_dir = open_dir (tmp, cmpl_state);
2057 *remaining_text = text_to_complete +
2058 ((p == tmp + 1) ? (p - tmp) : (p + 1 - tmp));
2062 /* If no possible candidates, use the cwd */
2063 gchar *sys_curdir = g_get_current_dir ();
2064 gchar *utf8_curdir = g_filename_to_utf8 (sys_curdir, -1, NULL, NULL, NULL);
2066 g_free (sys_curdir);
2068 new_dir = open_dir (utf8_curdir, cmpl_state);
2071 *remaining_text = text_to_complete;
2073 g_free (utf8_curdir);
2080 *remaining_text = text_to_complete;
2082 new_dir = open_dir (cmpl_state->reference_dir->fullname, cmpl_state);
2087 new_dir->cmpl_index = -1;
2088 new_dir->cmpl_parent = NULL;
2096 /* open a directory by user name */
2097 static CompletionDir*
2098 open_user_dir (gchar *text_to_complete,
2099 CompletionState *cmpl_state)
2101 CompletionDir *result;
2105 g_assert (text_to_complete && text_to_complete[0] == '~');
2107 first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2110 cmp_len = first_slash - text_to_complete - 1;
2112 cmp_len = strlen (text_to_complete + 1);
2117 gchar *homedir = g_get_home_dir ();
2118 gchar *utf8_homedir = g_filename_to_utf8 (homedir, -1, NULL, NULL, NULL);
2123 result = open_dir (utf8_homedir, cmpl_state);
2127 g_free (utf8_homedir);
2132 gchar* copy = g_new (char, cmp_len + 1);
2136 strncpy (copy, text_to_complete + 1, cmp_len);
2138 pwd = getpwnam (copy);
2145 utf8_dir = g_filename_to_utf8 (pwd->pw_dir, -1, NULL, NULL, NULL);
2146 result = open_dir (utf8_dir, cmpl_state);
2154 /* open a directory relative the the current relative directory */
2155 static CompletionDir*
2156 open_relative_dir (gchar *dir_name,
2158 CompletionState *cmpl_state)
2160 CompletionDir *result;
2163 path = g_string_sized_new (dir->fullname_len + strlen (dir_name) + 10);
2164 g_string_assign (path, dir->fullname);
2166 if (dir->fullname_len > 1
2167 && path->str[dir->fullname_len - 1] != G_DIR_SEPARATOR)
2168 g_string_append_c (path, G_DIR_SEPARATOR);
2169 g_string_append (path, dir_name);
2171 result = open_dir (path->str, cmpl_state);
2173 g_string_free (path, TRUE);
2178 /* after the cache lookup fails, really open a new directory */
2179 static CompletionDirSent*
2180 open_new_dir (gchar *dir_name,
2182 gboolean stat_subdirs)
2184 CompletionDirSent *sent;
2186 struct dirent *dirent_ptr;
2187 gint entry_count = 0;
2189 struct stat ent_sbuf;
2191 gchar *sys_dir_name;
2193 sent = g_new (CompletionDirSent, 1);
2194 sent->mtime = sbuf->st_mtime;
2195 sent->inode = sbuf->st_ino;
2196 sent->device = sbuf->st_dev;
2198 path = g_string_sized_new (2*MAXPATHLEN + 10);
2200 sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
2201 directory = opendir (sys_dir_name);
2206 g_free (sys_dir_name);
2210 while ((dirent_ptr = readdir (directory)) != NULL)
2213 sent->entries = g_new (CompletionDirEntry, entry_count);
2214 sent->entry_count = entry_count;
2216 rewinddir (directory);
2218 for (i = 0; i < entry_count; i += 1)
2220 dirent_ptr = readdir (directory);
2225 closedir (directory);
2226 g_free (sys_dir_name);
2230 sent->entries[i].entry_name = g_filename_to_utf8 (dirent_ptr->d_name, -1, NULL, NULL, NULL);
2232 g_string_assign (path, sys_dir_name);
2233 if (path->str[path->len-1] != G_DIR_SEPARATOR)
2235 g_string_append_c (path, G_DIR_SEPARATOR);
2237 g_string_append (path, dirent_ptr->d_name);
2241 /* Here we know path->str is a "system charset" string */
2242 if (stat (path->str, &ent_sbuf) >= 0 && S_ISDIR (ent_sbuf.st_mode))
2243 sent->entries[i].is_dir = TRUE;
2245 /* stat may fail, and we don't mind, since it could be a
2246 * dangling symlink. */
2247 sent->entries[i].is_dir = FALSE;
2250 sent->entries[i].is_dir = 1;
2253 g_free (sys_dir_name);
2254 g_string_free (path, TRUE);
2255 qsort (sent->entries, sent->entry_count, sizeof (CompletionDirEntry), compare_cmpl_dir);
2257 closedir (directory);
2262 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
2265 check_dir (gchar *dir_name,
2266 struct stat *result,
2267 gboolean *stat_subdirs)
2269 /* A list of directories that we know only contain other directories.
2270 * Trying to stat every file in these directories would be very
2277 struct stat statbuf;
2278 } no_stat_dirs[] = {
2279 { "/afs", FALSE, { 0 } },
2280 { "/net", FALSE, { 0 } }
2283 static const gint n_no_stat_dirs = G_N_ELEMENTS (no_stat_dirs);
2284 static gboolean initialized = FALSE;
2285 gchar *sys_dir_name;
2291 for (i = 0; i < n_no_stat_dirs; i++)
2293 if (stat (no_stat_dirs[i].name, &no_stat_dirs[i].statbuf) == 0)
2294 no_stat_dirs[i].present = TRUE;
2298 sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
2299 if (stat (sys_dir_name, result) < 0)
2301 g_free (sys_dir_name);
2305 g_free (sys_dir_name);
2307 *stat_subdirs = TRUE;
2308 for (i = 0; i < n_no_stat_dirs; i++)
2310 if (no_stat_dirs[i].present &&
2311 (no_stat_dirs[i].statbuf.st_dev == result->st_dev) &&
2312 (no_stat_dirs[i].statbuf.st_ino == result->st_ino))
2314 *stat_subdirs = FALSE;
2324 /* open a directory by absolute pathname */
2325 static CompletionDir*
2326 open_dir (gchar *dir_name,
2327 CompletionState *cmpl_state)
2330 gboolean stat_subdirs;
2331 CompletionDirSent *sent;
2334 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
2335 if (!check_dir (dir_name, &sbuf, &stat_subdirs))
2338 cdsl = cmpl_state->directory_sent_storage;
2344 if (sent->inode == sbuf.st_ino &&
2345 sent->mtime == sbuf.st_mtime &&
2346 sent->device == sbuf.st_dev)
2347 return attach_dir (sent, dir_name, cmpl_state);
2352 stat_subdirs = TRUE;
2355 sent = open_new_dir (dir_name, &sbuf, stat_subdirs);
2359 cmpl_state->directory_sent_storage =
2360 g_list_prepend (cmpl_state->directory_sent_storage, sent);
2362 return attach_dir (sent, dir_name, cmpl_state);
2368 static CompletionDir*
2369 attach_dir (CompletionDirSent *sent,
2371 CompletionState *cmpl_state)
2373 CompletionDir* new_dir;
2375 new_dir = g_new (CompletionDir, 1);
2377 cmpl_state->directory_storage =
2378 g_list_prepend (cmpl_state->directory_storage, new_dir);
2380 new_dir->sent = sent;
2381 new_dir->fullname = g_strdup (dir_name);
2382 new_dir->fullname_len = strlen (dir_name);
2388 correct_dir_fullname (CompletionDir* cmpl_dir)
2390 gint length = strlen (cmpl_dir->fullname);
2391 gchar *first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
2392 gchar *sys_filename;
2395 /* Does it end with /. (\.) ? */
2397 strcmp (cmpl_dir->fullname + length - 2, G_DIR_SEPARATOR_S ".") == 0)
2399 /* Is it just the root directory (on a drive) ? */
2400 if (cmpl_dir->fullname + length - 2 == first_slash)
2402 cmpl_dir->fullname[length - 1] = 0;
2403 cmpl_dir->fullname_len = length - 1;
2408 cmpl_dir->fullname[length - 2] = 0;
2412 /* Ends with /./ (\.\)? */
2413 else if (length >= 3 &&
2414 strcmp (cmpl_dir->fullname + length - 3,
2415 G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) == 0)
2416 cmpl_dir->fullname[length - 2] = 0;
2418 /* Ends with /.. (\..) ? */
2419 else if (length >= 3 &&
2420 strcmp (cmpl_dir->fullname + length - 3,
2421 G_DIR_SEPARATOR_S "..") == 0)
2423 /* Is it just /.. (X:\..)? */
2424 if (cmpl_dir->fullname + length - 3 == first_slash)
2426 cmpl_dir->fullname[length - 2] = 0;
2427 cmpl_dir->fullname_len = length - 2;
2431 sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
2432 if (stat (sys_filename, &sbuf) < 0)
2434 g_free (sys_filename);
2438 g_free (sys_filename);
2440 cmpl_dir->fullname[length - 3] = 0;
2442 if (!correct_parent (cmpl_dir, &sbuf))
2446 /* Ends with /../ (\..\)? */
2447 else if (length >= 4 &&
2448 strcmp (cmpl_dir->fullname + length - 4,
2449 G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) == 0)
2451 /* Is it just /../ (X:\..\)? */
2452 if (cmpl_dir->fullname + length - 4 == first_slash)
2454 cmpl_dir->fullname[length - 3] = 0;
2455 cmpl_dir->fullname_len = length - 3;
2459 sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
2460 if (stat (sys_filename, &sbuf) < 0)
2462 g_free (sys_filename);
2466 g_free (sys_filename);
2468 cmpl_dir->fullname[length - 4] = 0;
2470 if (!correct_parent (cmpl_dir, &sbuf))
2474 cmpl_dir->fullname_len = strlen (cmpl_dir->fullname);
2480 correct_parent (CompletionDir *cmpl_dir,
2487 gchar *sys_filename;
2490 last_slash = strrchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
2491 g_assert (last_slash);
2492 first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
2494 /* Clever (?) way to check for top-level directory that works also on
2495 * Win32, where there is a drive letter and colon prefixed...
2497 if (last_slash != first_slash)
2507 sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
2508 if (stat (sys_filename, &parbuf) < 0)
2510 g_free (sys_filename);
2513 last_slash[0] = G_DIR_SEPARATOR;
2516 g_free (sys_filename);
2518 #ifndef G_OS_WIN32 /* No inode numbers on Win32 */
2519 if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
2520 /* it wasn't a link */
2526 last_slash[0] = G_DIR_SEPARATOR;
2528 /* it was a link, have to figure it out the hard way */
2530 new_name = find_parent_dir_fullname (cmpl_dir->fullname);
2535 g_free (cmpl_dir->fullname);
2537 cmpl_dir->fullname = new_name;
2546 find_parent_dir_fullname (gchar* dirname)
2548 gchar *sys_orig_dir;
2553 sys_orig_dir = g_get_current_dir ();
2554 sys_dirname = g_filename_from_utf8 (dirname, -1, NULL, NULL, NULL);
2555 if (chdir (sys_dirname) != 0 || chdir ("..") != 0)
2557 g_free (sys_dirname);
2558 g_free (sys_orig_dir);
2562 g_free (sys_dirname);
2564 sys_cwd = g_get_current_dir ();
2565 result = g_filename_to_utf8 (sys_cwd, -1, NULL, NULL, NULL);
2568 if (chdir (sys_orig_dir) != 0)
2571 g_free (sys_orig_dir);
2575 g_free (sys_orig_dir);
2581 /**********************************************************************/
2582 /* Completion Operations */
2583 /**********************************************************************/
2587 static PossibleCompletion*
2588 attempt_homedir_completion (gchar *text_to_complete,
2589 CompletionState *cmpl_state)
2593 if (!cmpl_state->user_dir_name_buffer &&
2594 !get_pwdb (cmpl_state))
2596 length = strlen (text_to_complete) - 1;
2598 cmpl_state->user_completion_index += 1;
2600 while (cmpl_state->user_completion_index < cmpl_state->user_directories_len)
2602 index = first_diff_index (text_to_complete + 1,
2603 cmpl_state->user_directories
2604 [cmpl_state->user_completion_index].login);
2611 if (cmpl_state->last_valid_char < (index + 1))
2612 cmpl_state->last_valid_char = index + 1;
2613 cmpl_state->user_completion_index += 1;
2617 cmpl_state->the_completion.is_a_completion = 1;
2618 cmpl_state->the_completion.is_directory = TRUE;
2620 append_completion_text ("~", cmpl_state);
2622 append_completion_text (cmpl_state->
2623 user_directories[cmpl_state->user_completion_index].login,
2626 return append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
2629 if (text_to_complete[1]
2630 || cmpl_state->user_completion_index > cmpl_state->user_directories_len)
2632 cmpl_state->user_completion_index = -1;
2637 cmpl_state->user_completion_index += 1;
2638 cmpl_state->the_completion.is_a_completion = 1;
2639 cmpl_state->the_completion.is_directory = TRUE;
2641 return append_completion_text ("~" G_DIR_SEPARATOR_S, cmpl_state);
2647 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
2648 #define FOLD(c) (tolower(c))
2653 /* returns the index (>= 0) of the first differing character,
2654 * PATTERN_MATCH if the completion matches */
2656 first_diff_index (gchar *pat,
2661 while (*pat && *text && FOLD (*text) == FOLD (*pat))
2671 return PATTERN_MATCH;
2674 static PossibleCompletion*
2675 append_completion_text (gchar *text,
2676 CompletionState *cmpl_state)
2680 if (!cmpl_state->the_completion.text)
2683 len = strlen (text) + strlen (cmpl_state->the_completion.text) + 1;
2685 if (cmpl_state->the_completion.text_alloc > len)
2687 strcat (cmpl_state->the_completion.text, text);
2688 return &cmpl_state->the_completion;
2694 cmpl_state->the_completion.text_alloc = i;
2696 cmpl_state->the_completion.text = (gchar*) g_realloc (cmpl_state->the_completion.text, i);
2698 if (!cmpl_state->the_completion.text)
2702 strcat (cmpl_state->the_completion.text, text);
2703 return &cmpl_state->the_completion;
2707 static CompletionDir*
2708 find_completion_dir (gchar *text_to_complete,
2709 gchar **remaining_text,
2710 CompletionState *cmpl_state)
2712 gchar* first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2713 CompletionDir* dir = cmpl_state->reference_dir;
2714 CompletionDir* next;
2715 *remaining_text = text_to_complete;
2719 gint len = first_slash - *remaining_text;
2721 gchar *found_name = NULL; /* Quiet gcc */
2723 gchar* pat_buf = g_new (gchar, len + 1);
2725 strncpy (pat_buf, *remaining_text, len);
2728 for (i = 0; i < dir->sent->entry_count; i += 1)
2730 if (dir->sent->entries[i].is_dir &&
2731 fnmatch (pat_buf, dir->sent->entries[i].entry_name,
2732 FNMATCH_FLAGS)!= FNM_NOMATCH)
2742 found_name = dir->sent->entries[i].entry_name;
2749 /* Perhaps we are trying to open an automount directory */
2750 found_name = pat_buf;
2753 next = open_relative_dir (found_name, dir, cmpl_state);
2761 next->cmpl_parent = dir;
2765 if (!correct_dir_fullname (dir))
2771 *remaining_text = first_slash + 1;
2772 first_slash = strchr (*remaining_text, G_DIR_SEPARATOR);
2781 update_cmpl (PossibleCompletion *poss,
2782 CompletionState *cmpl_state)
2786 if (!poss || !cmpl_is_a_completion (poss))
2789 cmpl_len = strlen (cmpl_this_completion (poss));
2791 if (cmpl_state->updated_text_alloc < cmpl_len + 1)
2793 cmpl_state->updated_text =
2794 (gchar*)g_realloc (cmpl_state->updated_text,
2795 cmpl_state->updated_text_alloc);
2796 cmpl_state->updated_text_alloc = 2*cmpl_len;
2799 if (cmpl_state->updated_text_len < 0)
2801 strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
2802 cmpl_state->updated_text_len = cmpl_len;
2803 cmpl_state->re_complete = cmpl_is_directory (poss);
2805 else if (cmpl_state->updated_text_len == 0)
2807 cmpl_state->re_complete = FALSE;
2812 first_diff_index (cmpl_state->updated_text,
2813 cmpl_this_completion (poss));
2815 cmpl_state->re_complete = FALSE;
2817 if (first_diff == PATTERN_MATCH)
2820 if (first_diff > cmpl_state->updated_text_len)
2821 strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
2823 cmpl_state->updated_text_len = first_diff;
2824 cmpl_state->updated_text[first_diff] = 0;
2828 static PossibleCompletion*
2829 attempt_file_completion (CompletionState *cmpl_state)
2831 gchar *pat_buf, *first_slash;
2832 CompletionDir *dir = cmpl_state->active_completion_dir;
2834 dir->cmpl_index += 1;
2836 if (dir->cmpl_index == dir->sent->entry_count)
2838 if (dir->cmpl_parent == NULL)
2840 cmpl_state->active_completion_dir = NULL;
2846 cmpl_state->active_completion_dir = dir->cmpl_parent;
2848 return attempt_file_completion (cmpl_state);
2852 g_assert (dir->cmpl_text);
2854 first_slash = strchr (dir->cmpl_text, G_DIR_SEPARATOR);
2858 gint len = first_slash - dir->cmpl_text;
2860 pat_buf = g_new (gchar, len + 1);
2861 strncpy (pat_buf, dir->cmpl_text, len);
2866 gint len = strlen (dir->cmpl_text);
2868 pat_buf = g_new (gchar, len + 2);
2869 strcpy (pat_buf, dir->cmpl_text);
2870 /* Don't append a * if the user entered one herself.
2871 * This way one can complete *.h and don't get matches
2872 * on any .help files, for instance.
2874 if (strchr (pat_buf, '*') == NULL)
2875 strcpy (pat_buf + len, "*");
2880 if (dir->sent->entries[dir->cmpl_index].is_dir)
2882 if (fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
2883 FNMATCH_FLAGS) != FNM_NOMATCH)
2885 CompletionDir* new_dir;
2887 new_dir = open_relative_dir (dir->sent->entries[dir->cmpl_index].entry_name,
2896 new_dir->cmpl_parent = dir;
2898 new_dir->cmpl_index = -1;
2899 new_dir->cmpl_text = first_slash + 1;
2901 cmpl_state->active_completion_dir = new_dir;
2904 return attempt_file_completion (cmpl_state);
2909 return attempt_file_completion (cmpl_state);
2915 return attempt_file_completion (cmpl_state);
2920 if (dir->cmpl_parent != NULL)
2922 append_completion_text (dir->fullname +
2923 strlen (cmpl_state->completion_dir->fullname) + 1,
2925 append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
2928 append_completion_text (dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
2930 cmpl_state->the_completion.is_a_completion =
2931 fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
2932 FNMATCH_FLAGS) != FNM_NOMATCH;
2934 cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
2935 if (dir->sent->entries[dir->cmpl_index].is_dir)
2936 append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
2939 return &cmpl_state->the_completion;
2946 get_pwdb (CompletionState* cmpl_state)
2948 struct passwd *pwd_ptr;
2951 gint len = 0, i, count = 0;
2953 if (cmpl_state->user_dir_name_buffer)
2957 while ((pwd_ptr = getpwent ()) != NULL)
2959 utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
2960 len += strlen (utf8);
2962 utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
2963 len += strlen (utf8);
2971 cmpl_state->user_dir_name_buffer = g_new (gchar, len);
2972 cmpl_state->user_directories = g_new (CompletionUserDir, count);
2973 cmpl_state->user_directories_len = count;
2975 buf_ptr = cmpl_state->user_dir_name_buffer;
2977 for (i = 0; i < count; i += 1)
2979 pwd_ptr = getpwent ();
2986 utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
2987 strcpy (buf_ptr, utf8);
2990 cmpl_state->user_directories[i].login = buf_ptr;
2992 buf_ptr += strlen (buf_ptr);
2995 utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
2996 strcpy (buf_ptr, utf8);
2999 cmpl_state->user_directories[i].homedir = buf_ptr;
3001 buf_ptr += strlen (buf_ptr);
3005 qsort (cmpl_state->user_directories,
3006 cmpl_state->user_directories_len,
3007 sizeof (CompletionUserDir),
3016 if (cmpl_state->user_dir_name_buffer)
3017 g_free (cmpl_state->user_dir_name_buffer);
3018 if (cmpl_state->user_directories)
3019 g_free (cmpl_state->user_directories);
3021 cmpl_state->user_dir_name_buffer = NULL;
3022 cmpl_state->user_directories = NULL;
3028 compare_user_dir (const void *a,
3031 return strcmp ((((CompletionUserDir*)a))->login,
3032 (((CompletionUserDir*)b))->login);
3038 compare_cmpl_dir (const void *a,
3041 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
3042 return strcmp ((((CompletionDirEntry*)a))->entry_name,
3043 (((CompletionDirEntry*)b))->entry_name);
3045 return g_strcasecmp ((((CompletionDirEntry*)a))->entry_name,
3046 (((CompletionDirEntry*)b))->entry_name);
3051 cmpl_state_okay (CompletionState* cmpl_state)
3053 return cmpl_state && cmpl_state->reference_dir;
3057 cmpl_strerror (gint err)
3059 if (err == CMPL_ERRNO_TOO_LONG)
3060 return "Name too long";
3062 return g_strerror (err);