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>
45 #include <glib.h> /* Include early to get G_OS_WIN32 and
48 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
53 #endif /* G_OS_WIN32 || G_WITH_CYGWIN */
55 #include <winsock.h> /* For gethostname */
60 #include "gdk/gdkkeysyms.h"
61 #include "gtkbutton.h"
62 #include "gtkcellrenderertext.h"
64 #include "gtkfilesel.h"
68 #include "gtkliststore.h"
70 #include "gtkscrolledwindow.h"
72 #include "gtksignal.h"
73 #include "gtktreeselection.h"
74 #include "gtktreeview.h"
77 #include "gtkmenuitem.h"
78 #include "gtkoptionmenu.h"
79 #include "gtkdialog.h"
80 #include "gtkmessagedialog.h"
83 #include "gtkeventbox.h"
86 #include "gtkhpaned.h"
91 #define mkdir(p,m) _mkdir(p)
93 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
95 #endif /* G_OS_WIN32 */
97 #define DIR_LIST_WIDTH 180
98 #define DIR_LIST_HEIGHT 180
99 #define FILE_LIST_WIDTH 180
100 #define FILE_LIST_HEIGHT 180
102 /* The Hurd doesn't define either PATH_MAX or MAXPATHLEN, so we put this
103 * in here, since the rest of the code in the file does require some
108 # define MAXPATHLEN PATH_MAX
110 # define MAXPATHLEN 2048
114 /* I've put this here so it doesn't get confused with the
115 * file completion interface */
116 typedef struct _HistoryCallbackArg HistoryCallbackArg;
118 struct _HistoryCallbackArg
121 GtkWidget *menu_item;
125 typedef struct _CompletionState CompletionState;
126 typedef struct _CompletionDir CompletionDir;
127 typedef struct _CompletionDirSent CompletionDirSent;
128 typedef struct _CompletionDirEntry CompletionDirEntry;
129 typedef struct _CompletionUserDir CompletionUserDir;
130 typedef struct _PossibleCompletion PossibleCompletion;
132 /* Non-external file completion decls and structures */
134 /* A contant telling PRCS how many directories to cache. Its actually
135 * kept in a list, so the geometry isn't important. */
136 #define CMPL_DIRECTORY_CACHE_SIZE 10
138 /* A constant used to determine whether a substring was an exact
139 * match by first_diff_index()
141 #define PATTERN_MATCH -1
142 /* The arguments used by all fnmatch() calls below
144 #define FNMATCH_FLAGS (FNM_PATHNAME | FNM_PERIOD)
146 #define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
147 #define CMPL_ERRNO_DID_NOT_CONVERT ((1<<16)-2)
149 /* This structure contains all the useful information about a directory
150 * for the purposes of filename completion. These structures are cached
151 * in the CompletionState struct. CompletionDir's are reference counted.
153 struct _CompletionDirSent
160 struct _CompletionDirEntry *entries;
163 struct _CompletionDir
165 CompletionDirSent *sent;
170 struct _CompletionDir *cmpl_parent;
175 /* This structure contains pairs of directory entry names with a flag saying
176 * whether or not they are a valid directory. NOTE: This information is used
177 * to provide the caller with information about whether to update its completions
178 * or try to open a file. Since directories are cached by the directory mtime,
179 * a symlink which points to an invalid file (which will not be a directory),
180 * will not be reevaluated if that file is created, unless the containing
181 * directory is touched. I consider this case to be worth ignoring (josh).
183 struct _CompletionDirEntry
189 struct _CompletionUserDir
195 struct _PossibleCompletion
197 /* accessible fields, all are accessed externally by functions
201 gint is_a_completion;
202 gboolean is_directory;
209 struct _CompletionState
211 gint last_valid_char;
213 gint updated_text_len;
214 gint updated_text_alloc;
215 gboolean re_complete;
217 gchar *user_dir_name_buffer;
218 gint user_directories_len;
220 gchar *last_completion_text;
222 gint user_completion_index; /* if >= 0, currently completing ~user */
224 struct _CompletionDir *completion_dir; /* directory completing from */
225 struct _CompletionDir *active_completion_dir;
227 struct _PossibleCompletion the_completion;
229 struct _CompletionDir *reference_dir; /* initial directory */
231 GList* directory_storage;
232 GList* directory_sent_storage;
234 struct _CompletionUserDir *user_directories;
252 /* File completion functions which would be external, were they used
253 * outside of this file.
256 static CompletionState* cmpl_init_state (void);
257 static void cmpl_free_state (CompletionState *cmpl_state);
258 static gint cmpl_state_okay (CompletionState* cmpl_state);
259 static const gchar* cmpl_strerror (gint);
261 static PossibleCompletion* cmpl_completion_matches(gchar *text_to_complete,
262 gchar **remaining_text,
263 CompletionState *cmpl_state);
265 /* Returns a name for consideration, possibly a completion, this name
266 * will be invalid after the next call to cmpl_next_completion.
268 static char* cmpl_this_completion (PossibleCompletion*);
270 /* True if this completion matches the given text. Otherwise, this
271 * output can be used to have a list of non-completions.
273 static gint cmpl_is_a_completion (PossibleCompletion*);
275 /* True if the completion is a directory
277 static gboolean cmpl_is_directory (PossibleCompletion*);
279 /* Obtains the next completion, or NULL
281 static PossibleCompletion* cmpl_next_completion (CompletionState*);
283 /* Updating completions: the return value of cmpl_updated_text() will
284 * be text_to_complete completed as much as possible after the most
285 * recent call to cmpl_completion_matches. For the present
286 * application, this is the suggested replacement for the user's input
287 * string. You must CALL THIS AFTER ALL cmpl_text_completions have
290 static gchar* cmpl_updated_text (CompletionState* cmpl_state);
292 /* After updating, to see if the completion was a directory, call
293 * this. If it was, you should consider re-calling completion_matches.
295 static gboolean cmpl_updated_dir (CompletionState* cmpl_state);
297 /* Current location: if using file completion, return the current
298 * directory, from which file completion begins. More specifically,
299 * the cwd concatenated with all exact completions up to the last
300 * directory delimiter('/').
302 static gchar* cmpl_reference_position (CompletionState* cmpl_state);
304 /* backing up: if cmpl_completion_matches returns NULL, you may query
305 * the index of the last completable character into cmpl_updated_text.
307 static gint cmpl_last_valid_char (CompletionState* cmpl_state);
309 /* When the user selects a non-directory, call cmpl_completion_fullname
310 * to get the full name of the selected file.
312 static gchar* cmpl_completion_fullname (const gchar*, CompletionState* cmpl_state);
315 /* Directory operations. */
316 static CompletionDir* open_ref_dir (gchar* text_to_complete,
317 gchar** remaining_text,
318 CompletionState* cmpl_state);
319 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
320 static gboolean check_dir (gchar *dir_name,
322 gboolean *stat_subdirs);
324 static CompletionDir* open_dir (gchar* dir_name,
325 CompletionState* cmpl_state);
327 static CompletionDir* open_user_dir (const gchar* text_to_complete,
328 CompletionState *cmpl_state);
330 static CompletionDir* open_relative_dir (gchar* dir_name, CompletionDir* dir,
331 CompletionState *cmpl_state);
332 static CompletionDirSent* open_new_dir (gchar* dir_name,
334 gboolean stat_subdirs);
335 static gint correct_dir_fullname (CompletionDir* cmpl_dir);
336 static gint correct_parent (CompletionDir* cmpl_dir,
339 static gchar* find_parent_dir_fullname (gchar* dirname);
341 static CompletionDir* attach_dir (CompletionDirSent* sent,
343 CompletionState *cmpl_state);
344 static void free_dir_sent (CompletionDirSent* sent);
345 static void free_dir (CompletionDir *dir);
346 static void prune_memory_usage(CompletionState *cmpl_state);
348 /* Completion operations */
350 static PossibleCompletion* attempt_homedir_completion(gchar* text_to_complete,
351 CompletionState *cmpl_state);
353 static PossibleCompletion* attempt_file_completion(CompletionState *cmpl_state);
354 static CompletionDir* find_completion_dir(gchar* text_to_complete,
355 gchar** remaining_text,
356 CompletionState* cmpl_state);
357 static PossibleCompletion* append_completion_text(gchar* text,
358 CompletionState* cmpl_state);
360 static gint get_pwdb(CompletionState* cmpl_state);
361 static gint compare_user_dir(const void* a, const void* b);
363 static gint first_diff_index(gchar* pat, gchar* text);
364 static gint compare_cmpl_dir(const void* a, const void* b);
365 static void update_cmpl(PossibleCompletion* poss,
366 CompletionState* cmpl_state);
368 static void gtk_file_selection_class_init (GtkFileSelectionClass *klass);
369 static void gtk_file_selection_set_property (GObject *object,
373 static void gtk_file_selection_get_property (GObject *object,
377 static void gtk_file_selection_init (GtkFileSelection *filesel);
378 static void gtk_file_selection_finalize (GObject *object);
379 static void gtk_file_selection_destroy (GtkObject *object);
380 static void gtk_file_selection_map (GtkWidget *widget);
381 static gint gtk_file_selection_key_press (GtkWidget *widget,
384 static gint gtk_file_selection_insert_text (GtkWidget *widget,
385 const gchar *new_text,
386 gint new_text_length,
390 static void gtk_file_selection_file_activate (GtkTreeView *tree_view,
392 GtkTreeViewColumn *column,
394 static void gtk_file_selection_file_changed (GtkTreeSelection *selection,
396 static void gtk_file_selection_dir_activate (GtkTreeView *tree_view,
398 GtkTreeViewColumn *column,
401 static void gtk_file_selection_populate (GtkFileSelection *fs,
403 gboolean try_complete,
404 gboolean reset_entry);
405 static void gtk_file_selection_abort (GtkFileSelection *fs);
407 static void gtk_file_selection_update_history_menu (GtkFileSelection *fs,
410 static void gtk_file_selection_create_dir (GtkWidget *widget, gpointer data);
411 static void gtk_file_selection_delete_file (GtkWidget *widget, gpointer data);
412 static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data);
414 static void free_selected_names (GPtrArray *names);
416 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
417 #define compare_filenames(a, b) strcmp(a, b)
419 #define compare_filenames(a, b) g_ascii_strcasecmp(a, b)
423 static GtkWindowClass *parent_class = NULL;
425 /* Saves errno when something cmpl does fails. */
426 static gint cmpl_errno;
430 * Take the path currently in the file selection
431 * entry field and translate as necessary from
432 * a WIN32 style to CYGWIN32 style path. For
433 * instance translate:
434 * x:\somepath\file.jpg
436 * //x/somepath/file.jpg
438 * Replace the path in the selection text field.
439 * Return a boolean value concerning whether a
440 * translation had to be made.
443 translate_win32_path (GtkFileSelection *filesel)
449 * Retrieve the current path
451 path = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
454 * Translate only if this looks like a DOS-ish
455 * path... First handle any drive letters.
457 if (isalpha (path[0]) && (path[1] == ':')) {
459 * This part kind of stinks... It isn't possible
460 * to know if there is enough space in the current
461 * string for the extra character required in this
462 * conversion. Assume that there isn't enough space
463 * and use the set function on the text field to
464 * set the newly created string.
466 gchar *newPath = g_strdup_printf ("//%c/%s", path[0], (path + 3));
467 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), newPath);
474 * Now, replace backslashes with forward slashes
477 if (strchr (path, '\\'))
480 for (index = 0; path[index] != '\0'; index++)
481 if (path[index] == '\\')
492 gtk_file_selection_get_type (void)
494 static GtkType file_selection_type = 0;
496 if (!file_selection_type)
498 static const GtkTypeInfo filesel_info =
501 sizeof (GtkFileSelection),
502 sizeof (GtkFileSelectionClass),
503 (GtkClassInitFunc) gtk_file_selection_class_init,
504 (GtkObjectInitFunc) gtk_file_selection_init,
505 /* reserved_1 */ NULL,
506 /* reserved_2 */ NULL,
507 (GtkClassInitFunc) NULL,
510 file_selection_type = gtk_type_unique (GTK_TYPE_DIALOG, &filesel_info);
513 return file_selection_type;
517 gtk_file_selection_class_init (GtkFileSelectionClass *class)
519 GObjectClass *gobject_class;
520 GtkObjectClass *object_class;
521 GtkWidgetClass *widget_class;
523 gobject_class = (GObjectClass*) class;
524 object_class = (GtkObjectClass*) class;
525 widget_class = (GtkWidgetClass*) class;
527 parent_class = gtk_type_class (GTK_TYPE_DIALOG);
529 gobject_class->finalize = gtk_file_selection_finalize;
530 gobject_class->set_property = gtk_file_selection_set_property;
531 gobject_class->get_property = gtk_file_selection_get_property;
533 g_object_class_install_property (gobject_class,
535 g_param_spec_string ("filename",
537 _("The currently selected filename."),
539 G_PARAM_READABLE | G_PARAM_WRITABLE));
540 g_object_class_install_property (gobject_class,
542 g_param_spec_boolean ("show_fileops",
543 _("Show file operations"),
544 _("Whether buttons for creating/manipulating files should be displayed."),
548 g_object_class_install_property (gobject_class,
549 PROP_SELECT_MULTIPLE,
550 g_param_spec_boolean ("select_multiple",
551 _("Select multiple"),
552 _("Whether to allow multiple files to be selected."),
556 object_class->destroy = gtk_file_selection_destroy;
557 widget_class->map = gtk_file_selection_map;
560 static void gtk_file_selection_set_property (GObject *object,
565 GtkFileSelection *filesel;
567 filesel = GTK_FILE_SELECTION (object);
572 gtk_file_selection_set_filename (filesel,
573 g_value_get_string (value));
575 case PROP_SHOW_FILEOPS:
576 if (g_value_get_boolean (value))
577 gtk_file_selection_show_fileop_buttons (filesel);
579 gtk_file_selection_hide_fileop_buttons (filesel);
581 case PROP_SELECT_MULTIPLE:
582 gtk_file_selection_set_select_multiple (filesel, g_value_get_boolean (value));
585 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
590 static void gtk_file_selection_get_property (GObject *object,
595 GtkFileSelection *filesel;
597 filesel = GTK_FILE_SELECTION (object);
602 g_value_set_string (value,
603 gtk_file_selection_get_filename(filesel));
606 case PROP_SHOW_FILEOPS:
607 /* This is a little bit hacky, but doing otherwise would require
608 * adding a field to the object.
610 g_value_set_boolean (value, (filesel->fileop_c_dir &&
611 filesel->fileop_del_file &&
612 filesel->fileop_ren_file));
614 case PROP_SELECT_MULTIPLE:
615 g_value_set_boolean (value, gtk_file_selection_get_select_multiple (filesel));
618 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
624 grab_default (GtkWidget *widget)
626 gtk_widget_grab_default (widget);
631 gtk_file_selection_init (GtkFileSelection *filesel)
633 GtkWidget *entry_vbox;
635 GtkWidget *list_hbox, *list_container;
636 GtkWidget *confirm_area;
637 GtkWidget *pulldown_hbox;
638 GtkWidget *scrolled_win;
644 GtkTreeViewColumn *column;
646 gtk_widget_push_composite_child ();
648 dialog = GTK_DIALOG (filesel);
650 filesel->cmpl_state = cmpl_init_state ();
652 /* The dialog-sized vertical box */
653 filesel->main_vbox = dialog->vbox;
654 gtk_container_set_border_width (GTK_CONTAINER (filesel), 10);
656 /* The horizontal box containing create, rename etc. buttons */
657 filesel->button_area = gtk_hbutton_box_new ();
658 gtk_button_box_set_layout (GTK_BUTTON_BOX (filesel->button_area), GTK_BUTTONBOX_START);
659 gtk_box_set_spacing (GTK_BOX (filesel->button_area), 0);
660 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->button_area,
662 gtk_widget_show (filesel->button_area);
664 gtk_file_selection_show_fileop_buttons (filesel);
666 /* hbox for pulldown menu */
667 pulldown_hbox = gtk_hbox_new (TRUE, 5);
668 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), pulldown_hbox, FALSE, FALSE, 0);
669 gtk_widget_show (pulldown_hbox);
672 filesel->history_pulldown = gtk_option_menu_new ();
673 gtk_widget_show (filesel->history_pulldown);
674 gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->history_pulldown,
677 /* The horizontal box containing the directory and file listboxes */
679 spacer = gtk_hbox_new (FALSE, 0);
680 gtk_widget_set_size_request (spacer, -1, 5);
681 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), spacer, FALSE, FALSE, 0);
682 gtk_widget_show (spacer);
684 list_hbox = gtk_hbox_new (FALSE, 5);
685 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), list_hbox, TRUE, TRUE, 0);
686 gtk_widget_show (list_hbox);
688 list_container = g_object_new (GTK_TYPE_HPANED,
694 list_container = list_hbox;
696 spacer = gtk_hbox_new (FALSE, 0);
697 gtk_widget_set_size_request (spacer, -1, 5);
698 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), spacer, FALSE, FALSE, 0);
699 gtk_widget_show (spacer);
701 /* The directories list */
703 model = gtk_list_store_new (1, G_TYPE_STRING);
704 filesel->dir_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
705 g_object_unref (model);
707 column = gtk_tree_view_column_new_with_attributes (_("Folders"),
708 gtk_cell_renderer_text_new (),
711 label = gtk_label_new_with_mnemonic (_("Fol_ders"));
712 gtk_label_set_mnemonic_widget (GTK_LABEL (label), filesel->dir_list);
713 gtk_widget_show (label);
714 gtk_tree_view_column_set_widget (column, label);
715 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
716 gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->dir_list), column);
718 gtk_widget_set_usize (filesel->dir_list, DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
719 g_signal_connect (filesel->dir_list, "row_activated",
720 G_CALLBACK (gtk_file_selection_dir_activate), filesel);
722 /* gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list)); */
724 scrolled_win = gtk_scrolled_window_new (NULL, NULL);
725 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN);
726 gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->dir_list);
727 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
728 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
729 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 0);
730 if (GTK_IS_PANED (list_container))
731 gtk_paned_pack1 (GTK_PANED (list_container), scrolled_win, TRUE, TRUE);
733 gtk_container_add (GTK_CONTAINER (list_container), scrolled_win);
734 gtk_widget_show (filesel->dir_list);
735 gtk_widget_show (scrolled_win);
738 model = gtk_list_store_new (1, G_TYPE_STRING);
739 filesel->file_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
740 g_object_unref (model);
742 column = gtk_tree_view_column_new_with_attributes (_("Files"),
743 gtk_cell_renderer_text_new (),
746 label = gtk_label_new_with_mnemonic (_("_Files"));
747 gtk_label_set_mnemonic_widget (GTK_LABEL (label), filesel->file_list);
748 gtk_widget_show (label);
749 gtk_tree_view_column_set_widget (column, label);
750 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
751 gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->file_list), column);
753 gtk_widget_set_usize (filesel->file_list, FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
754 g_signal_connect (filesel->file_list, "row_activated",
755 G_CALLBACK (gtk_file_selection_file_activate), filesel);
756 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list)), "changed",
757 G_CALLBACK (gtk_file_selection_file_changed), filesel);
759 /* gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list)); */
761 scrolled_win = gtk_scrolled_window_new (NULL, NULL);
762 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN);
763 gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->file_list);
764 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
765 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
766 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 0);
767 gtk_container_add (GTK_CONTAINER (list_container), scrolled_win);
768 gtk_widget_show (filesel->file_list);
769 gtk_widget_show (scrolled_win);
771 /* action area for packing buttons into. */
772 filesel->action_area = gtk_hbox_new (TRUE, 0);
773 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->action_area,
775 gtk_widget_show (filesel->action_area);
777 /* The OK/Cancel button area */
778 confirm_area = dialog->action_area;
780 /* The Cancel button */
781 filesel->cancel_button = gtk_dialog_add_button (dialog,
783 GTK_RESPONSE_CANCEL);
785 filesel->ok_button = gtk_dialog_add_button (dialog,
789 gtk_widget_grab_default (filesel->ok_button);
791 /* The selection entry widget */
792 entry_vbox = gtk_vbox_new (FALSE, 2);
793 gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 2);
794 gtk_widget_show (entry_vbox);
796 eventbox = gtk_event_box_new ();
797 filesel->selection_text = label = gtk_label_new ("");
798 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
799 gtk_container_add (GTK_CONTAINER (eventbox), label);
800 gtk_box_pack_start (GTK_BOX (entry_vbox), eventbox, FALSE, FALSE, 0);
801 gtk_widget_show (label);
802 gtk_widget_show (eventbox);
804 filesel->selection_entry = gtk_entry_new ();
805 gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "key_press_event",
806 (GtkSignalFunc) gtk_file_selection_key_press, filesel);
807 gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "insert_text",
808 (GtkSignalFunc) gtk_file_selection_insert_text, NULL);
809 gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "focus_in_event",
810 (GtkSignalFunc) grab_default,
811 GTK_OBJECT (filesel->ok_button));
812 gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "activate",
813 (GtkSignalFunc) gtk_button_clicked,
814 GTK_OBJECT (filesel->ok_button));
815 gtk_box_pack_start (GTK_BOX (entry_vbox), filesel->selection_entry, TRUE, TRUE, 0);
816 gtk_widget_show (filesel->selection_entry);
818 if (!cmpl_state_okay (filesel->cmpl_state))
822 sprintf (err_buf, _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
824 gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
828 gtk_file_selection_populate (filesel, "", FALSE, TRUE);
831 gtk_widget_grab_focus (filesel->selection_entry);
833 gtk_widget_pop_composite_child ();
837 uri_list_extract_first_uri (const gchar* uri_list)
841 g_return_val_if_fail (uri_list != NULL, NULL);
844 /* We don't actually try to validate the URI according to RFC
845 * 2396, or even check for allowed characters - we just ignore
846 * comments and trim whitespace off the ends. We also
847 * allow LF delimination as well as the specified CRLF.
849 * We do allow comments like specified in RFC 2483.
855 while (g_ascii_isspace(*p))
859 while (*q && (*q != '\n') && (*q != '\r'))
865 while (q > p && g_ascii_isspace (*q))
869 return g_strndup (p, q - p + 1);
872 p = strchr (p, '\n');
880 dnd_really_drop (GtkWidget *dialog, gint response_id, GtkFileSelection *fs)
884 if (response_id == GTK_RESPONSE_YES)
886 filename = g_object_get_data (G_OBJECT (dialog), "gtk-fs-dnd-filename");
888 gtk_file_selection_set_filename (fs, filename);
891 gtk_widget_destroy (dialog);
896 filenames_dropped (GtkWidget *widget,
897 GdkDragContext *context,
900 GtkSelectionData *selection_data,
905 char *filename = NULL;
907 char this_hostname[257];
909 GError *error = NULL;
911 if (!selection_data->data)
914 uri = uri_list_extract_first_uri ((char *)selection_data->data);
919 filename = g_filename_from_uri (uri, &hostname, &error);
924 g_warning ("Error getting dropped filename: %s\n",
926 g_error_free (error);
930 res = gethostname (this_hostname, 256);
931 this_hostname[256] = 0;
933 if ((hostname == NULL) ||
934 (res == 0 && strcmp (hostname, this_hostname) == 0) ||
935 (strcmp (hostname, "localhost") == 0))
936 gtk_file_selection_set_filename (GTK_FILE_SELECTION (widget),
942 dialog = gtk_message_dialog_new (GTK_WINDOW (widget),
943 GTK_DIALOG_DESTROY_WITH_PARENT,
944 GTK_MESSAGE_QUESTION,
946 _("The file \"%s\" resides on another machine (called %s) and may not be available to this program.\n"
947 "Are you sure that you want to select it?"), filename, hostname);
949 g_object_set_data_full (G_OBJECT (dialog), "gtk-fs-dnd-filename", g_strdup (filename), g_free);
951 g_signal_connect_data (dialog, "response",
952 (GCallback) dnd_really_drop,
955 gtk_widget_show (dialog);
973 filenames_drag_get (GtkWidget *widget,
974 GdkDragContext *context,
975 GtkSelectionData *selection_data,
978 GtkFileSelection *filesel)
986 file = gtk_file_selection_get_filename (filesel);
990 if (info == TARGET_URILIST)
992 res = gethostname (hostname, 256);
995 uri_list = g_filename_to_uri (file, (!res)?hostname:NULL, &error);
998 g_warning ("Error getting filename: %s\n",
1000 g_error_free (error);
1004 gtk_selection_data_set (selection_data,
1005 selection_data->target, 8,
1006 (void *)uri_list, strlen((char *)uri_list));
1011 g_print ("Setting text: '%s'\n", file);
1012 gtk_selection_data_set_text (selection_data, file, -1);
1018 file_selection_setup_dnd (GtkFileSelection *filesel)
1020 GtkWidget *eventbox;
1021 static GtkTargetEntry drop_types[] = {
1022 { "text/uri-list", 0, TARGET_URILIST}
1024 static gint n_drop_types = sizeof(drop_types)/sizeof(drop_types[0]);
1025 static GtkTargetEntry drag_types[] = {
1026 { "text/uri-list", 0, TARGET_URILIST},
1027 { "UTF8_STRING", 0, TARGET_UTF8_STRING },
1030 { "COMPOUND_TEXT", 0, 0 }
1032 static gint n_drag_types = sizeof(drag_types)/sizeof(drag_types[0]);
1034 gtk_drag_dest_set (GTK_WIDGET (filesel),
1035 GTK_DEST_DEFAULT_ALL,
1036 drop_types, n_drop_types,
1039 gtk_signal_connect (GTK_OBJECT(filesel), "drag_data_received",
1040 GTK_SIGNAL_FUNC(filenames_dropped), NULL);
1042 eventbox = gtk_widget_get_parent (filesel->selection_text);
1043 gtk_drag_source_set (eventbox,
1045 drag_types, n_drag_types,
1048 gtk_signal_connect (GTK_OBJECT (eventbox),
1050 GTK_SIGNAL_FUNC (filenames_drag_get),
1055 gtk_file_selection_new (const gchar *title)
1057 GtkFileSelection *filesel;
1059 filesel = gtk_type_new (GTK_TYPE_FILE_SELECTION);
1060 gtk_window_set_title (GTK_WINDOW (filesel), title);
1061 gtk_dialog_set_has_separator (GTK_DIALOG (filesel), FALSE);
1063 file_selection_setup_dnd (filesel);
1065 return GTK_WIDGET (filesel);
1069 gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
1071 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1073 /* delete, create directory, and rename */
1074 if (!filesel->fileop_c_dir)
1076 filesel->fileop_c_dir = gtk_button_new_with_mnemonic (_("_New Folder"));
1077 gtk_signal_connect (GTK_OBJECT (filesel->fileop_c_dir), "clicked",
1078 (GtkSignalFunc) gtk_file_selection_create_dir,
1079 (gpointer) filesel);
1080 gtk_box_pack_start (GTK_BOX (filesel->button_area),
1081 filesel->fileop_c_dir, TRUE, TRUE, 0);
1082 gtk_widget_show (filesel->fileop_c_dir);
1085 if (!filesel->fileop_del_file)
1087 filesel->fileop_del_file = gtk_button_new_with_mnemonic (_("De_lete File"));
1088 gtk_signal_connect (GTK_OBJECT (filesel->fileop_del_file), "clicked",
1089 (GtkSignalFunc) gtk_file_selection_delete_file,
1090 (gpointer) filesel);
1091 gtk_box_pack_start (GTK_BOX (filesel->button_area),
1092 filesel->fileop_del_file, TRUE, TRUE, 0);
1093 gtk_widget_show (filesel->fileop_del_file);
1096 if (!filesel->fileop_ren_file)
1098 filesel->fileop_ren_file = gtk_button_new_with_mnemonic (_("_Rename File"));
1099 gtk_signal_connect (GTK_OBJECT (filesel->fileop_ren_file), "clicked",
1100 (GtkSignalFunc) gtk_file_selection_rename_file,
1101 (gpointer) filesel);
1102 gtk_box_pack_start (GTK_BOX (filesel->button_area),
1103 filesel->fileop_ren_file, TRUE, TRUE, 0);
1104 gtk_widget_show (filesel->fileop_ren_file);
1106 g_object_notify (G_OBJECT (filesel), "show_fileops");
1107 gtk_widget_queue_resize (GTK_WIDGET (filesel));
1111 gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
1113 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1115 if (filesel->fileop_ren_file)
1117 gtk_widget_destroy (filesel->fileop_ren_file);
1118 filesel->fileop_ren_file = NULL;
1121 if (filesel->fileop_del_file)
1123 gtk_widget_destroy (filesel->fileop_del_file);
1124 filesel->fileop_del_file = NULL;
1127 if (filesel->fileop_c_dir)
1129 gtk_widget_destroy (filesel->fileop_c_dir);
1130 filesel->fileop_c_dir = NULL;
1132 g_object_notify (G_OBJECT (filesel), "show_fileops");
1138 gtk_file_selection_set_filename (GtkFileSelection *filesel,
1139 const gchar *filename)
1142 const char *name, *last_slash;
1144 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1145 g_return_if_fail (filename != NULL);
1147 last_slash = strrchr (filename, G_DIR_SEPARATOR);
1151 buf = g_strdup ("");
1156 buf = g_strdup (filename);
1157 buf[last_slash - filename + 1] = 0;
1158 name = last_slash + 1;
1161 gtk_file_selection_populate (filesel, buf, FALSE, TRUE);
1163 if (filesel->selection_entry)
1164 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
1166 g_object_notify (G_OBJECT (filesel), "filename");
1170 * gtk_file_selection_get_filename:
1171 * @filesel: a #GtkFileSelection
1173 * This function returns the selected filename in the on-disk encoding
1174 * (See g_filename_from_utf8()), which may or may not be the same as that
1175 * used by GTK+ (UTF-8). To convert to UTF-8, call g_filename_to_utf8().
1176 * The returned string points to a statically allocated buffer and
1177 * should be copied if you plan to keep it around.
1179 * If no file is selected then the selected directory path is returned.
1181 * Return value: currently-selected filename in the on-disk encoding.
1183 G_CONST_RETURN gchar*
1184 gtk_file_selection_get_filename (GtkFileSelection *filesel)
1186 static gchar nothing[2] = "";
1187 static gchar something[MAXPATHLEN*2];
1191 g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
1193 #ifdef G_WITH_CYGWIN
1194 translate_win32_path (filesel);
1196 text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
1199 sys_filename = g_filename_from_utf8 (cmpl_completion_fullname (text, filesel->cmpl_state), -1, NULL, NULL, NULL);
1202 strncpy (something, sys_filename, sizeof (something));
1203 g_free (sys_filename);
1211 gtk_file_selection_complete (GtkFileSelection *filesel,
1212 const gchar *pattern)
1214 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1215 g_return_if_fail (pattern != NULL);
1217 if (filesel->selection_entry)
1218 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), pattern);
1219 gtk_file_selection_populate (filesel, (gchar*) pattern, TRUE, TRUE);
1223 gtk_file_selection_destroy (GtkObject *object)
1225 GtkFileSelection *filesel;
1227 HistoryCallbackArg *callback_arg;
1229 g_return_if_fail (GTK_IS_FILE_SELECTION (object));
1231 filesel = GTK_FILE_SELECTION (object);
1233 if (filesel->fileop_dialog)
1235 gtk_widget_destroy (filesel->fileop_dialog);
1236 filesel->fileop_dialog = NULL;
1239 if (filesel->history_list)
1241 list = filesel->history_list;
1244 callback_arg = list->data;
1245 g_free (callback_arg->directory);
1246 g_free (callback_arg);
1249 g_list_free (filesel->history_list);
1250 filesel->history_list = NULL;
1253 if (filesel->cmpl_state)
1255 cmpl_free_state (filesel->cmpl_state);
1256 filesel->cmpl_state = NULL;
1259 if (filesel->selected_names)
1261 free_selected_names (filesel->selected_names);
1262 filesel->selected_names = NULL;
1265 if (filesel->last_selected)
1267 g_free (filesel->last_selected);
1268 filesel->last_selected = NULL;
1271 GTK_OBJECT_CLASS (parent_class)->destroy (object);
1275 gtk_file_selection_map (GtkWidget *widget)
1277 GtkFileSelection *filesel = GTK_FILE_SELECTION (widget);
1279 /* Refresh the contents */
1280 gtk_file_selection_populate (filesel, "", FALSE, FALSE);
1282 GTK_WIDGET_CLASS (parent_class)->map (widget);
1286 gtk_file_selection_finalize (GObject *object)
1288 GtkFileSelection *filesel = GTK_FILE_SELECTION (object);
1290 g_free (filesel->fileop_file);
1292 G_OBJECT_CLASS (parent_class)->finalize (object);
1295 /* Begin file operations callbacks */
1298 gtk_file_selection_fileop_error (GtkFileSelection *fs,
1299 gchar *error_message)
1303 g_return_if_fail (error_message != NULL);
1306 dialog = gtk_message_dialog_new (GTK_WINDOW (fs),
1307 GTK_DIALOG_DESTROY_WITH_PARENT,
1310 "%s", error_message);
1312 /* yes, we free it */
1313 g_free (error_message);
1315 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1317 gtk_signal_connect_object (GTK_OBJECT (dialog), "response",
1318 (GtkSignalFunc) gtk_widget_destroy,
1321 gtk_widget_show (dialog);
1325 gtk_file_selection_fileop_destroy (GtkWidget *widget,
1328 GtkFileSelection *fs = data;
1330 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1332 fs->fileop_dialog = NULL;
1337 gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
1340 GtkFileSelection *fs = data;
1341 const gchar *dirname;
1344 gchar *sys_full_path;
1346 GError *error = NULL;
1347 CompletionState *cmpl_state;
1349 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1351 dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1352 cmpl_state = (CompletionState*) fs->cmpl_state;
1353 path = cmpl_reference_position (cmpl_state);
1355 full_path = g_strconcat (path, G_DIR_SEPARATOR_S, dirname, NULL);
1356 sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1359 if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1360 buf = g_strdup_printf (_("The folder name \"%s\" contains symbols that are not allowed in filenames"), dirname);
1362 buf = g_strdup_printf (_("Error creating folder \"%s\": %s\n%s"), dirname, error->message,
1363 _("You probably used symbols not allowed in filenames."));
1364 gtk_file_selection_fileop_error (fs, buf);
1365 g_error_free (error);
1369 if (mkdir (sys_full_path, 0755) < 0)
1371 buf = g_strdup_printf (_("Error creating folder \"%s\": %s\n"), dirname,
1372 g_strerror (errno));
1373 gtk_file_selection_fileop_error (fs, buf);
1378 g_free (sys_full_path);
1380 gtk_widget_destroy (fs->fileop_dialog);
1381 gtk_file_selection_populate (fs, "", FALSE, FALSE);
1385 gtk_file_selection_create_dir (GtkWidget *widget,
1388 GtkFileSelection *fs = data;
1394 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1396 if (fs->fileop_dialog)
1400 dialog = gtk_dialog_new ();
1401 fs->fileop_dialog = dialog;
1402 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1403 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1405 gtk_window_set_title (GTK_WINDOW (dialog), _("New Folder"));
1406 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1407 gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
1409 /* If file dialog is grabbed, grab option dialog */
1410 /* When option dialog is closed, file dialog will be grabbed again */
1411 if (GTK_WINDOW (fs)->modal)
1412 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1414 vbox = gtk_vbox_new (FALSE, 0);
1415 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1416 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1418 gtk_widget_show( vbox);
1420 label = gtk_label_new_with_mnemonic (_("_Folder name:"));
1421 gtk_misc_set_alignment(GTK_MISC (label), 0.0, 0.0);
1422 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1423 gtk_widget_show (label);
1425 /* The directory entry widget */
1426 fs->fileop_entry = gtk_entry_new ();
1427 gtk_label_set_mnemonic_widget (GTK_LABEL (label), fs->fileop_entry);
1428 gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry,
1430 GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1431 gtk_widget_show (fs->fileop_entry);
1434 button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
1435 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1436 (GtkSignalFunc) gtk_widget_destroy,
1438 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1439 button, TRUE, TRUE, 0);
1440 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1441 gtk_widget_grab_default (button);
1442 gtk_widget_show (button);
1444 gtk_widget_grab_focus (fs->fileop_entry);
1446 button = gtk_button_new_with_label (_("Create"));
1447 gtk_signal_connect (GTK_OBJECT (button), "clicked",
1448 (GtkSignalFunc) gtk_file_selection_create_dir_confirmed,
1450 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1451 button, TRUE, TRUE, 0);
1452 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1453 gtk_widget_show (button);
1455 gtk_widget_show (dialog);
1459 gtk_file_selection_delete_file_response (GtkDialog *dialog,
1463 GtkFileSelection *fs = data;
1464 CompletionState *cmpl_state;
1467 gchar *sys_full_path;
1468 GError *error = NULL;
1471 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1473 if (response_id != GTK_RESPONSE_OK)
1475 gtk_widget_destroy (GTK_WIDGET (dialog));
1479 cmpl_state = (CompletionState*) fs->cmpl_state;
1480 path = cmpl_reference_position (cmpl_state);
1482 full_path = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1483 sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1486 if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1487 buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"),
1490 buf = g_strdup_printf (_("Error deleting file \"%s\": %s\n%s"),
1491 fs->fileop_file, error->message,
1492 _("It probably contains symbols not allowed in filenames."));
1494 gtk_file_selection_fileop_error (fs, buf);
1495 g_error_free (error);
1499 if (unlink (sys_full_path) < 0)
1501 buf = g_strdup_printf (_("Error deleting file \"%s\": %s"),
1502 fs->fileop_file, g_strerror (errno));
1503 gtk_file_selection_fileop_error (fs, buf);
1508 g_free (sys_full_path);
1510 gtk_widget_destroy (fs->fileop_dialog);
1511 gtk_file_selection_populate (fs, "", FALSE, TRUE);
1515 gtk_file_selection_delete_file (GtkWidget *widget,
1518 GtkFileSelection *fs = data;
1520 const gchar *filename;
1522 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1524 if (fs->fileop_dialog)
1527 #ifdef G_WITH_CYGWIN
1528 translate_win32_path (fs);
1531 filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1532 if (strlen (filename) < 1)
1535 g_free (fs->fileop_file);
1536 fs->fileop_file = g_strdup (filename);
1539 fs->fileop_dialog = dialog =
1540 gtk_message_dialog_new (GTK_WINDOW (fs),
1541 GTK_WINDOW (fs)->modal ? GTK_DIALOG_MODAL : 0,
1542 GTK_MESSAGE_QUESTION,
1544 _("Really delete file \"%s\" ?"), filename);
1546 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1547 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1549 gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
1550 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1553 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
1554 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1555 GTK_STOCK_DELETE, GTK_RESPONSE_OK,
1558 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
1560 g_signal_connect (G_OBJECT (dialog), "response",
1561 G_CALLBACK (gtk_file_selection_delete_file_response),
1564 gtk_widget_show (dialog);
1568 gtk_file_selection_rename_file_confirmed (GtkWidget *widget,
1571 GtkFileSelection *fs = data;
1575 gchar *new_filename;
1576 gchar *old_filename;
1577 gchar *sys_new_filename;
1578 gchar *sys_old_filename;
1579 CompletionState *cmpl_state;
1580 GError *error = NULL;
1582 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1584 file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1585 cmpl_state = (CompletionState*) fs->cmpl_state;
1586 path = cmpl_reference_position (cmpl_state);
1588 new_filename = g_strconcat (path, G_DIR_SEPARATOR_S, file, NULL);
1589 old_filename = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1591 sys_new_filename = g_filename_from_utf8 (new_filename, -1, NULL, NULL, &error);
1594 if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1595 buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), new_filename);
1597 buf = g_strdup_printf (_("Error renaming file to \"%s\": %s\n%s"),
1598 new_filename, error->message,
1599 _("You probably used symbols not allowed in filenames."));
1600 gtk_file_selection_fileop_error (fs, buf);
1601 g_error_free (error);
1605 sys_old_filename = g_filename_from_utf8 (old_filename, -1, NULL, NULL, &error);
1608 if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1609 buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), old_filename);
1611 buf = g_strdup_printf (_("Error renaming file \"%s\": %s\n%s"),
1612 old_filename, error->message,
1613 _("It probably contains symbols not allowed in filenames."));
1614 gtk_file_selection_fileop_error (fs, buf);
1615 g_error_free (error);
1619 if (rename (sys_old_filename, sys_new_filename) < 0)
1621 buf = g_strdup_printf (_("Error renaming file \"%s\" to \"%s\": %s"),
1622 sys_old_filename, sys_new_filename,
1623 g_strerror (errno));
1624 gtk_file_selection_fileop_error (fs, buf);
1628 gtk_file_selection_populate (fs, "", FALSE, FALSE);
1629 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), file);
1632 g_free (sys_old_filename);
1635 g_free (new_filename);
1636 g_free (old_filename);
1637 g_free (sys_new_filename);
1639 gtk_widget_destroy (fs->fileop_dialog);
1643 gtk_file_selection_rename_file (GtkWidget *widget,
1646 GtkFileSelection *fs = data;
1653 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1655 if (fs->fileop_dialog)
1658 g_free (fs->fileop_file);
1659 fs->fileop_file = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1660 if (strlen (fs->fileop_file) < 1)
1664 fs->fileop_dialog = dialog = gtk_dialog_new ();
1665 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1666 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1668 gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File"));
1669 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1670 gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
1672 /* If file dialog is grabbed, grab option dialog */
1673 /* When option dialog closed, file dialog will be grabbed again */
1674 if (GTK_WINDOW (fs)->modal)
1675 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1677 vbox = gtk_vbox_new (FALSE, 0);
1678 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1679 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1681 gtk_widget_show(vbox);
1683 buf = g_strdup_printf (_("Rename file \"%s\" to:"), fs->fileop_file);
1684 label = gtk_label_new (buf);
1685 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1686 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1687 gtk_widget_show (label);
1690 /* New filename entry */
1691 fs->fileop_entry = gtk_entry_new ();
1692 gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry,
1694 GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1695 gtk_widget_show (fs->fileop_entry);
1697 gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
1698 gtk_editable_select_region (GTK_EDITABLE (fs->fileop_entry),
1699 0, strlen (fs->fileop_file));
1702 button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
1703 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1704 (GtkSignalFunc) gtk_widget_destroy,
1706 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1707 button, TRUE, TRUE, 0);
1708 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1709 gtk_widget_grab_default (button);
1710 gtk_widget_show (button);
1712 gtk_widget_grab_focus (fs->fileop_entry);
1714 button = gtk_button_new_with_label (_("Rename"));
1715 gtk_signal_connect (GTK_OBJECT (button), "clicked",
1716 (GtkSignalFunc) gtk_file_selection_rename_file_confirmed,
1718 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1719 button, TRUE, TRUE, 0);
1720 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1721 gtk_widget_show (button);
1723 gtk_widget_show (dialog);
1727 gtk_file_selection_insert_text (GtkWidget *widget,
1728 const gchar *new_text,
1729 gint new_text_length,
1735 filename = g_filename_from_utf8 (new_text, new_text_length, NULL, NULL, NULL);
1739 gdk_display_beep (gtk_widget_get_display (widget));
1740 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "insert_text");
1750 gtk_file_selection_key_press (GtkWidget *widget,
1754 GtkFileSelection *fs;
1757 g_return_val_if_fail (widget != NULL, FALSE);
1758 g_return_val_if_fail (event != NULL, FALSE);
1760 if ((event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab) &&
1761 (event->state & gtk_accelerator_get_default_mod_mask ()) == 0)
1763 fs = GTK_FILE_SELECTION (user_data);
1764 #ifdef G_WITH_CYGWIN
1765 translate_win32_path (fs);
1767 text = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1769 gtk_file_selection_populate (fs, text, TRUE, TRUE);
1780 gtk_file_selection_history_callback (GtkWidget *widget,
1783 GtkFileSelection *fs = data;
1784 HistoryCallbackArg *callback_arg;
1787 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1789 list = fs->history_list;
1792 callback_arg = list->data;
1794 if (callback_arg->menu_item == widget)
1796 gtk_file_selection_populate (fs, callback_arg->directory, FALSE, FALSE);
1805 gtk_file_selection_update_history_menu (GtkFileSelection *fs,
1806 gchar *current_directory)
1808 HistoryCallbackArg *callback_arg;
1809 GtkWidget *menu_item;
1815 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1816 g_return_if_fail (current_directory != NULL);
1818 list = fs->history_list;
1820 if (fs->history_menu)
1823 callback_arg = list->data;
1824 g_free (callback_arg->directory);
1825 g_free (callback_arg);
1828 g_list_free (fs->history_list);
1829 fs->history_list = NULL;
1831 gtk_widget_destroy (fs->history_menu);
1834 fs->history_menu = gtk_menu_new ();
1836 current_dir = g_strdup (current_directory);
1838 dir_len = strlen (current_dir);
1840 for (i = dir_len; i >= 0; i--)
1842 /* the i == dir_len is to catch the full path for the first
1844 if ( (current_dir[i] == G_DIR_SEPARATOR) || (i == dir_len))
1846 /* another small hack to catch the full path */
1848 current_dir[i + 1] = '\0';
1849 #ifdef G_WITH_CYGWIN
1850 if (!strcmp (current_dir, "//"))
1853 menu_item = gtk_menu_item_new_with_label (current_dir);
1855 callback_arg = g_new (HistoryCallbackArg, 1);
1856 callback_arg->menu_item = menu_item;
1858 /* since the autocompletion gets confused if you don't
1859 * supply a trailing '/' on a dir entry, set the full
1860 * (current) path to "" which just refreshes the filesel */
1863 callback_arg->directory = g_strdup ("");
1867 callback_arg->directory = g_strdup (current_dir);
1870 fs->history_list = g_list_append (fs->history_list, callback_arg);
1872 gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
1873 (GtkSignalFunc) gtk_file_selection_history_callback,
1875 gtk_menu_shell_append (GTK_MENU_SHELL (fs->history_menu), menu_item);
1876 gtk_widget_show (menu_item);
1880 gtk_option_menu_set_menu (GTK_OPTION_MENU (fs->history_pulldown),
1882 g_free (current_dir);
1886 get_real_filename (gchar *filename,
1889 #ifdef G_WITH_CYGWIN
1890 /* Check to see if the selection was a drive selector */
1891 if (isalpha (filename[0]) && (filename[1] == ':'))
1893 /* It is... map it to a CYGWIN32 drive */
1894 gchar *temp_filename = g_strdup_printf ("//%c/", tolower (filename[0]));
1899 return temp_filename;
1903 #endif /* G_WITH_CYGWIN */
1907 gtk_file_selection_file_activate (GtkTreeView *tree_view,
1909 GtkTreeViewColumn *column,
1912 GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
1913 GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
1917 gtk_tree_model_get_iter (model, &iter, path);
1918 gtk_tree_model_get (model, &iter, FILE_COLUMN, &filename, -1);
1919 filename = get_real_filename (filename, TRUE);
1921 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1922 gtk_button_clicked (GTK_BUTTON (fs->ok_button));
1928 gtk_file_selection_dir_activate (GtkTreeView *tree_view,
1930 GtkTreeViewColumn *column,
1933 GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
1934 GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
1938 gtk_tree_model_get_iter (model, &iter, path);
1939 gtk_tree_model_get (model, &iter, DIR_COLUMN, &filename, -1);
1940 gtk_file_selection_populate (fs, filename, FALSE, FALSE);
1944 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
1947 win32_gtk_add_drives_to_dir_list (GtkListStore *model)
1951 char formatBuffer[128];
1954 /* Get the drives string */
1955 GetLogicalDriveStrings (sizeof (buffer), buffer);
1957 /* Add the drives as necessary */
1959 while (*textPtr != '\0')
1961 /* Ignore floppies (?) */
1962 if ((tolower (textPtr[0]) != 'a') && (tolower (textPtr[0]) != 'b'))
1964 /* Build the actual displayable string */
1965 sprintf (formatBuffer, "%c:\\", toupper (textPtr[0]));
1967 /* Add to the list */
1968 gtk_list_store_append (model, &iter);
1969 gtk_list_store_set (model, &iter, DIR_COLUMN, formatBuffer, -1);
1971 textPtr += (strlen (textPtr) + 1);
1977 gtk_file_selection_populate (GtkFileSelection *fs,
1979 gboolean try_complete,
1980 gboolean reset_entry)
1982 CompletionState *cmpl_state;
1983 PossibleCompletion* poss;
1985 GtkListStore *dir_model;
1986 GtkListStore *file_model;
1988 gchar* rem_path = rel_path;
1990 gint did_recurse = FALSE;
1991 gint possible_count = 0;
1992 gint selection_index = -1;
1994 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1996 cmpl_state = (CompletionState*) fs->cmpl_state;
1997 poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
1999 if (!cmpl_state_okay (cmpl_state))
2001 /* Something went wrong. */
2002 gtk_file_selection_abort (fs);
2006 g_assert (cmpl_state->reference_dir);
2008 dir_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->dir_list)));
2009 file_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->file_list)));
2011 gtk_list_store_clear (dir_model);
2012 gtk_list_store_clear (file_model);
2014 /* Set the dir list to include ./ and ../ */
2015 gtk_list_store_append (dir_model, &iter);
2016 gtk_list_store_set (dir_model, &iter, DIR_COLUMN, "." G_DIR_SEPARATOR_S, -1);
2017 gtk_list_store_append (dir_model, &iter);
2018 gtk_list_store_set (dir_model, &iter, DIR_COLUMN, ".." G_DIR_SEPARATOR_S, -1);
2022 if (cmpl_is_a_completion (poss))
2024 possible_count += 1;
2026 filename = cmpl_this_completion (poss);
2028 if (cmpl_is_directory (poss))
2030 if (strcmp (filename, "." G_DIR_SEPARATOR_S) != 0 &&
2031 strcmp (filename, ".." G_DIR_SEPARATOR_S) != 0)
2033 gtk_list_store_append (dir_model, &iter);
2034 gtk_list_store_set (dir_model, &iter, DIR_COLUMN, filename, -1);
2039 gtk_list_store_append (file_model, &iter);
2040 gtk_list_store_set (file_model, &iter, DIR_COLUMN, filename, -1);
2044 poss = cmpl_next_completion (cmpl_state);
2047 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
2048 /* For Windows, add drives as potential selections */
2049 win32_gtk_add_drives_to_dir_list (dir_model);
2052 /* File lists are set. */
2054 g_assert (cmpl_state->reference_dir);
2059 /* User is trying to complete filenames, so advance the user's input
2060 * string to the updated_text, which is the common leading substring
2061 * of all possible completions, and if its a directory attempt
2062 * attempt completions in it. */
2064 if (cmpl_updated_text (cmpl_state)[0])
2067 if (cmpl_updated_dir (cmpl_state))
2069 gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
2073 gtk_file_selection_populate (fs, dir_name, TRUE, TRUE);
2079 if (fs->selection_entry)
2080 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
2081 cmpl_updated_text (cmpl_state));
2086 selection_index = cmpl_last_valid_char (cmpl_state) -
2087 (strlen (rel_path) - strlen (rem_path));
2088 if (fs->selection_entry)
2089 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
2092 else if (reset_entry)
2094 if (fs->selection_entry)
2095 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
2100 if (fs->selection_entry)
2101 gtk_entry_set_position (GTK_ENTRY (fs->selection_entry), selection_index);
2103 if (fs->selection_entry)
2105 sel_text = g_strconcat (_("Selection: "),
2106 cmpl_reference_position (cmpl_state),
2109 gtk_label_set_text (GTK_LABEL (fs->selection_text), sel_text);
2113 if (fs->history_pulldown)
2115 gtk_file_selection_update_history_menu (fs, cmpl_reference_position (cmpl_state));
2122 gtk_file_selection_abort (GtkFileSelection *fs)
2126 sprintf (err_buf, _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
2128 /* BEEP gdk_beep(); */
2130 if (fs->selection_entry)
2131 gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
2135 * gtk_file_selection_set_select_multiple:
2136 * @filesel: a #GtkFileSelection
2137 * @select_multiple: whether or not the user is allowed to select multiple
2138 * files in the file list.
2140 * Sets whether the user is allowed to select multiple files in the file list.
2141 * Use gtk_file_selection_get_selections () to get the list of selected files.
2144 gtk_file_selection_set_select_multiple (GtkFileSelection *filesel,
2145 gboolean select_multiple)
2147 GtkTreeSelection *sel;
2148 GtkSelectionMode mode;
2150 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
2152 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
2154 mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE;
2156 if (mode != gtk_tree_selection_get_mode (sel))
2158 gtk_tree_selection_set_mode (sel, mode);
2160 g_object_notify (G_OBJECT (filesel), "select-multiple");
2165 * gtk_file_selection_get_select_multiple:
2166 * @filesel: a #GtkFileSelection
2168 * Determines whether or not the user is allowed to select multiple files in
2169 * the file list. See gtk_file_selection_set_select_multiple().
2171 * Return value: %TRUE if the user is allowed to select multiple files in the
2175 gtk_file_selection_get_select_multiple (GtkFileSelection *filesel)
2177 GtkTreeSelection *sel;
2179 g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), FALSE);
2181 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
2182 return (gtk_tree_selection_get_mode (sel) == GTK_SELECTION_MULTIPLE);
2186 multiple_changed_foreach (GtkTreeModel *model,
2191 GPtrArray *names = data;
2194 gtk_tree_model_get (model, iter, FILE_COLUMN, &filename, -1);
2196 g_ptr_array_add (names, filename);
2200 free_selected_names (GPtrArray *names)
2204 for (i = 0; i < names->len; i++)
2205 g_free (g_ptr_array_index (names, i));
2207 g_ptr_array_free (names, TRUE);
2211 gtk_file_selection_file_changed (GtkTreeSelection *selection,
2214 GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
2215 GPtrArray *new_names;
2220 new_names = g_ptr_array_sized_new (8);
2222 gtk_tree_selection_selected_foreach (selection,
2223 multiple_changed_foreach,
2226 /* nothing selected */
2227 if (new_names->len == 0)
2229 g_ptr_array_free (new_names, TRUE);
2231 if (fs->selected_names != NULL)
2233 free_selected_names (fs->selected_names);
2234 fs->selected_names = NULL;
2237 goto maybe_clear_entry;
2240 if (new_names->len != 1)
2242 GPtrArray *old_names = fs->selected_names;
2244 if (old_names != NULL)
2246 /* A common case is selecting a range of files from top to bottom,
2247 * so quickly check for that to avoid looping over the entire list
2249 if (compare_filenames (g_ptr_array_index (old_names, old_names->len - 1),
2250 g_ptr_array_index (new_names, new_names->len - 1)) != 0)
2251 index = new_names->len - 1;
2254 gint i = 0, j = 0, cmp;
2256 /* do a quick diff, stopping at the first file not in the
2259 while (i < old_names->len && j < new_names->len)
2261 cmp = compare_filenames (g_ptr_array_index (old_names, i),
2262 g_ptr_array_index (new_names, j));
2279 /* we ran off the end of the old list */
2280 if (index == -1 && i < new_names->len)
2286 /* A phantom anchor still exists at the point where the last item
2287 * was selected, which is used for subsequent range selections.
2288 * So search up from there.
2290 if (compare_filenames (fs->last_selected,
2291 g_ptr_array_index (new_names, 0)) == 0)
2292 index = new_names->len - 1;
2300 if (fs->selected_names != NULL)
2301 free_selected_names (fs->selected_names);
2303 fs->selected_names = new_names;
2307 if (fs->last_selected != NULL)
2308 g_free (fs->last_selected);
2310 fs->last_selected = g_strdup (g_ptr_array_index (new_names, index));
2311 filename = get_real_filename (fs->last_selected, FALSE);
2313 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
2315 if (filename != fs->last_selected)
2323 entry = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
2324 if ((entry != NULL) && (fs->last_selected != NULL) &&
2325 (compare_filenames (entry, fs->last_selected) == 0))
2326 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
2330 * gtk_file_selection_get_selections:
2331 * @filesel: a #GtkFileSelection
2333 * Retrieves the list of file selections the user has made in the dialog box.
2334 * This function is intended for use when the user can select multiple files
2335 * in the file list. The first file in the list is equivalent to what
2336 * gtk_file_selection_get_filename() would return.
2338 * The filenames are in the encoding of g_filename_from_utf8, which may or may
2339 * not be the same as that used by GTK+ (UTF-8). To convert to UTF-8, call
2340 * g_filename_to_utf8() on each string.
2342 * Return value: a newly-allocated %NULL-terminated array of strings. Use
2343 * g_strfreev() to free it.
2346 gtk_file_selection_get_selections (GtkFileSelection *filesel)
2350 gchar *filename, *dirname;
2351 gchar *current, *buf;
2354 g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), NULL);
2356 filename = g_strdup (gtk_file_selection_get_filename (filesel));
2358 if (strlen (filename) == 0)
2364 names = filesel->selected_names;
2367 selections = g_new (gchar *, names->len + 2);
2369 selections = g_new (gchar *, 2);
2372 selections[count++] = filename;
2376 dirname = g_path_get_dirname (filename);
2378 for (i = 0; i < names->len; i++)
2380 buf = g_filename_from_utf8 (g_ptr_array_index (names, i), -1,
2382 current = g_build_filename (dirname, buf, NULL);
2385 if (compare_filenames (current, filename) != 0)
2386 selections[count++] = current;
2394 selections[count] = NULL;
2399 /**********************************************************************/
2400 /* External Interface */
2401 /**********************************************************************/
2403 /* The four completion state selectors
2406 cmpl_updated_text (CompletionState *cmpl_state)
2408 return cmpl_state->updated_text;
2412 cmpl_updated_dir (CompletionState *cmpl_state)
2414 return cmpl_state->re_complete;
2418 cmpl_reference_position (CompletionState *cmpl_state)
2420 return cmpl_state->reference_dir->fullname;
2424 cmpl_last_valid_char (CompletionState *cmpl_state)
2426 return cmpl_state->last_valid_char;
2430 cmpl_completion_fullname (const gchar *text,
2431 CompletionState *cmpl_state)
2433 static char nothing[2] = "";
2435 if (!cmpl_state_okay (cmpl_state))
2439 else if (g_path_is_absolute (text))
2441 strcpy (cmpl_state->updated_text, text);
2444 else if (text[0] == '~')
2449 dir = open_user_dir (text, cmpl_state);
2453 /* spencer says just return ~something, so
2454 * for now just do it. */
2455 strcpy (cmpl_state->updated_text, text);
2460 strcpy (cmpl_state->updated_text, dir->fullname);
2462 slash = strchr (text, G_DIR_SEPARATOR);
2465 strcat (cmpl_state->updated_text, slash);
2471 strcpy (cmpl_state->updated_text, cmpl_state->reference_dir->fullname);
2472 if (cmpl_state->updated_text[strlen (cmpl_state->updated_text) - 1] != G_DIR_SEPARATOR)
2473 strcat (cmpl_state->updated_text, G_DIR_SEPARATOR_S);
2474 strcat (cmpl_state->updated_text, text);
2477 return cmpl_state->updated_text;
2480 /* The three completion selectors
2483 cmpl_this_completion (PossibleCompletion* pc)
2489 cmpl_is_directory (PossibleCompletion* pc)
2491 return pc->is_directory;
2495 cmpl_is_a_completion (PossibleCompletion* pc)
2497 return pc->is_a_completion;
2500 /**********************************************************************/
2501 /* Construction, deletion */
2502 /**********************************************************************/
2504 static CompletionState*
2505 cmpl_init_state (void)
2507 gchar *sys_getcwd_buf;
2509 CompletionState *new_state;
2511 new_state = g_new (CompletionState, 1);
2513 /* g_get_current_dir() returns a string in the "system" charset */
2514 sys_getcwd_buf = g_get_current_dir ();
2515 utf8_cwd = g_filename_to_utf8 (sys_getcwd_buf, -1, NULL, NULL, NULL);
2516 g_free (sys_getcwd_buf);
2520 new_state->reference_dir = NULL;
2521 new_state->completion_dir = NULL;
2522 new_state->active_completion_dir = NULL;
2523 new_state->directory_storage = NULL;
2524 new_state->directory_sent_storage = NULL;
2525 new_state->last_valid_char = 0;
2526 new_state->updated_text = g_new (gchar, MAXPATHLEN);
2527 new_state->updated_text_alloc = MAXPATHLEN;
2528 new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
2529 new_state->the_completion.text_alloc = MAXPATHLEN;
2530 new_state->user_dir_name_buffer = NULL;
2531 new_state->user_directories = NULL;
2533 new_state->reference_dir = open_dir (utf8_cwd, new_state);
2535 if (!new_state->reference_dir)
2537 /* Directories changing from underneath us, grumble */
2538 strcpy (utf8_cwd, G_DIR_SEPARATOR_S);
2547 cmpl_free_dir_list (GList* dp0)
2553 free_dir (dp->data);
2561 cmpl_free_dir_sent_list (GList* dp0)
2567 free_dir_sent (dp->data);
2575 cmpl_free_state (CompletionState* cmpl_state)
2577 g_return_if_fail (cmpl_state != NULL);
2579 cmpl_free_dir_list (cmpl_state->directory_storage);
2580 cmpl_free_dir_sent_list (cmpl_state->directory_sent_storage);
2582 if (cmpl_state->user_dir_name_buffer)
2583 g_free (cmpl_state->user_dir_name_buffer);
2584 if (cmpl_state->user_directories)
2585 g_free (cmpl_state->user_directories);
2586 if (cmpl_state->the_completion.text)
2587 g_free (cmpl_state->the_completion.text);
2588 if (cmpl_state->updated_text)
2589 g_free (cmpl_state->updated_text);
2591 g_free (cmpl_state);
2595 free_dir (CompletionDir* dir)
2597 g_free (dir->cmpl_text);
2598 g_free (dir->fullname);
2603 free_dir_sent (CompletionDirSent* sent)
2606 for (i = 0; i < sent->entry_count; i++)
2607 g_free (sent->entries[i].entry_name);
2608 g_free (sent->entries);
2613 prune_memory_usage (CompletionState *cmpl_state)
2615 GList* cdsl = cmpl_state->directory_sent_storage;
2616 GList* cdl = cmpl_state->directory_storage;
2620 for (; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
2625 cmpl_free_dir_sent_list (cdsl->next);
2629 cmpl_state->directory_storage = NULL;
2632 if (cdl->data == cmpl_state->reference_dir)
2633 cmpl_state->directory_storage = g_list_prepend (NULL, cdl->data);
2635 free_dir (cdl->data);
2642 /**********************************************************************/
2643 /* The main entrances. */
2644 /**********************************************************************/
2646 static PossibleCompletion*
2647 cmpl_completion_matches (gchar *text_to_complete,
2648 gchar **remaining_text,
2649 CompletionState *cmpl_state)
2652 PossibleCompletion *poss;
2654 prune_memory_usage (cmpl_state);
2656 g_assert (text_to_complete != NULL);
2658 cmpl_state->user_completion_index = -1;
2659 cmpl_state->last_completion_text = text_to_complete;
2660 cmpl_state->the_completion.text[0] = 0;
2661 cmpl_state->last_valid_char = 0;
2662 cmpl_state->updated_text_len = -1;
2663 cmpl_state->updated_text[0] = 0;
2664 cmpl_state->re_complete = FALSE;
2667 first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2669 if (text_to_complete[0] == '~' && !first_slash)
2671 /* Text starts with ~ and there is no slash, show all the
2672 * home directory completions.
2674 poss = attempt_homedir_completion (text_to_complete, cmpl_state);
2676 update_cmpl (poss, cmpl_state);
2681 cmpl_state->reference_dir =
2682 open_ref_dir (text_to_complete, remaining_text, cmpl_state);
2684 if (!cmpl_state->reference_dir)
2687 cmpl_state->completion_dir =
2688 find_completion_dir (*remaining_text, remaining_text, cmpl_state);
2690 cmpl_state->last_valid_char = *remaining_text - text_to_complete;
2692 if (!cmpl_state->completion_dir)
2695 cmpl_state->completion_dir->cmpl_index = -1;
2696 cmpl_state->completion_dir->cmpl_parent = NULL;
2697 cmpl_state->completion_dir->cmpl_text = g_strdup (*remaining_text);
2699 cmpl_state->active_completion_dir = cmpl_state->completion_dir;
2701 cmpl_state->reference_dir = cmpl_state->completion_dir;
2703 poss = attempt_file_completion (cmpl_state);
2705 update_cmpl (poss, cmpl_state);
2710 static PossibleCompletion*
2711 cmpl_next_completion (CompletionState* cmpl_state)
2713 PossibleCompletion* poss = NULL;
2715 cmpl_state->the_completion.text[0] = 0;
2718 if (cmpl_state->user_completion_index >= 0)
2719 poss = attempt_homedir_completion (cmpl_state->last_completion_text, cmpl_state);
2721 poss = attempt_file_completion (cmpl_state);
2723 poss = attempt_file_completion (cmpl_state);
2726 update_cmpl (poss, cmpl_state);
2731 /**********************************************************************/
2732 /* Directory Operations */
2733 /**********************************************************************/
2735 /* Open the directory where completion will begin from, if possible. */
2736 static CompletionDir*
2737 open_ref_dir (gchar *text_to_complete,
2738 gchar **remaining_text,
2739 CompletionState *cmpl_state)
2742 CompletionDir *new_dir;
2744 first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2746 #ifdef G_WITH_CYGWIN
2747 if (text_to_complete[0] == '/' && text_to_complete[1] == '/')
2750 sprintf (root_dir, "//%c", text_to_complete[2]);
2752 new_dir = open_dir (root_dir, cmpl_state);
2755 *remaining_text = text_to_complete + 4;
2763 else if (text_to_complete[0] == '~')
2765 new_dir = open_user_dir (text_to_complete, cmpl_state);
2770 *remaining_text = first_slash + 1;
2772 *remaining_text = text_to_complete + strlen (text_to_complete);
2780 else if (g_path_is_absolute (text_to_complete) || !cmpl_state->reference_dir)
2782 gchar *tmp = g_strdup (text_to_complete);
2786 while (*p && *p != '*' && *p != '?')
2790 p = strrchr (tmp, G_DIR_SEPARATOR);
2798 new_dir = open_dir (tmp, cmpl_state);
2801 *remaining_text = text_to_complete +
2802 ((p == tmp + 1) ? (p - tmp) : (p + 1 - tmp));
2806 /* If no possible candidates, use the cwd */
2807 gchar *sys_curdir = g_get_current_dir ();
2808 gchar *utf8_curdir = g_filename_to_utf8 (sys_curdir, -1, NULL, NULL, NULL);
2810 g_free (sys_curdir);
2812 new_dir = open_dir (utf8_curdir, cmpl_state);
2815 *remaining_text = text_to_complete;
2817 g_free (utf8_curdir);
2824 *remaining_text = text_to_complete;
2826 new_dir = open_dir (cmpl_state->reference_dir->fullname, cmpl_state);
2831 new_dir->cmpl_index = -1;
2832 new_dir->cmpl_parent = NULL;
2840 /* open a directory by user name */
2841 static CompletionDir*
2842 open_user_dir (const gchar *text_to_complete,
2843 CompletionState *cmpl_state)
2845 CompletionDir *result;
2849 g_assert (text_to_complete && text_to_complete[0] == '~');
2851 first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2854 cmp_len = first_slash - text_to_complete - 1;
2856 cmp_len = strlen (text_to_complete + 1);
2861 const gchar *homedir = g_get_home_dir ();
2862 gchar *utf8_homedir = g_filename_to_utf8 (homedir, -1, NULL, NULL, NULL);
2865 result = open_dir (utf8_homedir, cmpl_state);
2869 g_free (utf8_homedir);
2874 gchar* copy = g_new (char, cmp_len + 1);
2878 strncpy (copy, text_to_complete + 1, cmp_len);
2880 pwd = getpwnam (copy);
2887 utf8_dir = g_filename_to_utf8 (pwd->pw_dir, -1, NULL, NULL, NULL);
2888 result = open_dir (utf8_dir, cmpl_state);
2896 /* open a directory relative the the current relative directory */
2897 static CompletionDir*
2898 open_relative_dir (gchar *dir_name,
2900 CompletionState *cmpl_state)
2902 CompletionDir *result;
2905 path = g_string_sized_new (dir->fullname_len + strlen (dir_name) + 10);
2906 g_string_assign (path, dir->fullname);
2908 if (dir->fullname_len > 1
2909 && path->str[dir->fullname_len - 1] != G_DIR_SEPARATOR)
2910 g_string_append_c (path, G_DIR_SEPARATOR);
2911 g_string_append (path, dir_name);
2913 result = open_dir (path->str, cmpl_state);
2915 g_string_free (path, TRUE);
2920 /* after the cache lookup fails, really open a new directory */
2921 static CompletionDirSent*
2922 open_new_dir (gchar *dir_name,
2924 gboolean stat_subdirs)
2926 CompletionDirSent *sent;
2929 GError *error = NULL;
2930 gint entry_count = 0;
2933 struct stat ent_sbuf;
2935 gchar *sys_dir_name;
2937 sent = g_new (CompletionDirSent, 1);
2938 sent->mtime = sbuf->st_mtime;
2939 sent->inode = sbuf->st_ino;
2940 sent->device = sbuf->st_dev;
2942 path = g_string_sized_new (2*MAXPATHLEN + 10);
2944 sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
2947 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
2951 directory = g_dir_open (sys_dir_name, 0, &error);
2954 cmpl_errno = error->code; /* ??? */
2955 g_free (sys_dir_name);
2959 while ((dirent = g_dir_read_name (directory)) != NULL)
2962 sent->entries = g_new (CompletionDirEntry, entry_count);
2963 sent->entry_count = entry_count;
2965 g_dir_rewind (directory);
2967 for (i = 0; i < entry_count; i += 1)
2969 GError *error = NULL;
2971 dirent = g_dir_read_name (directory);
2975 g_warning ("Failure reading folder '%s'", sys_dir_name);
2976 g_dir_close (directory);
2977 g_free (sys_dir_name);
2981 sent->entries[n_entries].entry_name = g_filename_to_utf8 (dirent, -1, NULL, NULL, &error);
2982 if (sent->entries[n_entries].entry_name == NULL
2983 || !g_utf8_validate (sent->entries[n_entries].entry_name, -1, NULL))
2985 g_message (_("The filename \"%s\" couldn't be converted to UTF-8 "
2986 "(try setting the environment variable G_BROKEN_FILENAMES): %s"),
2988 error->message ? error->message : _("Invalid Utf-8"));
2989 g_clear_error (&error);
2992 g_clear_error (&error);
2994 g_string_assign (path, sys_dir_name);
2995 if (path->str[path->len-1] != G_DIR_SEPARATOR)
2997 g_string_append_c (path, G_DIR_SEPARATOR);
2999 g_string_append (path, dirent);
3003 /* Here we know path->str is a "system charset" string */
3004 if (stat (path->str, &ent_sbuf) >= 0 && S_ISDIR (ent_sbuf.st_mode))
3005 sent->entries[n_entries].is_dir = TRUE;
3007 /* stat may fail, and we don't mind, since it could be a
3008 * dangling symlink. */
3009 sent->entries[n_entries].is_dir = FALSE;
3012 sent->entries[n_entries].is_dir = 1;
3016 sent->entry_count = n_entries;
3018 g_free (sys_dir_name);
3019 g_string_free (path, TRUE);
3020 qsort (sent->entries, sent->entry_count, sizeof (CompletionDirEntry), compare_cmpl_dir);
3022 g_dir_close (directory);
3027 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
3030 check_dir (gchar *dir_name,
3031 struct stat *result,
3032 gboolean *stat_subdirs)
3034 /* A list of directories that we know only contain other directories.
3035 * Trying to stat every file in these directories would be very
3042 struct stat statbuf;
3043 } no_stat_dirs[] = {
3044 { "/afs", FALSE, { 0 } },
3045 { "/net", FALSE, { 0 } }
3048 static const gint n_no_stat_dirs = G_N_ELEMENTS (no_stat_dirs);
3049 static gboolean initialized = FALSE;
3050 gchar *sys_dir_name;
3056 for (i = 0; i < n_no_stat_dirs; i++)
3058 if (stat (no_stat_dirs[i].name, &no_stat_dirs[i].statbuf) == 0)
3059 no_stat_dirs[i].present = TRUE;
3063 sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
3066 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3070 if (stat (sys_dir_name, result) < 0)
3072 g_free (sys_dir_name);
3076 g_free (sys_dir_name);
3078 *stat_subdirs = TRUE;
3079 for (i = 0; i < n_no_stat_dirs; i++)
3081 if (no_stat_dirs[i].present &&
3082 (no_stat_dirs[i].statbuf.st_dev == result->st_dev) &&
3083 (no_stat_dirs[i].statbuf.st_ino == result->st_ino))
3085 *stat_subdirs = FALSE;
3095 /* open a directory by absolute pathname */
3096 static CompletionDir*
3097 open_dir (gchar *dir_name,
3098 CompletionState *cmpl_state)
3101 gboolean stat_subdirs;
3102 CompletionDirSent *sent;
3105 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
3106 if (!check_dir (dir_name, &sbuf, &stat_subdirs))
3109 cdsl = cmpl_state->directory_sent_storage;
3115 if (sent->inode == sbuf.st_ino &&
3116 sent->mtime == sbuf.st_mtime &&
3117 sent->device == sbuf.st_dev)
3118 return attach_dir (sent, dir_name, cmpl_state);
3123 stat_subdirs = TRUE;
3126 sent = open_new_dir (dir_name, &sbuf, stat_subdirs);
3130 cmpl_state->directory_sent_storage =
3131 g_list_prepend (cmpl_state->directory_sent_storage, sent);
3133 return attach_dir (sent, dir_name, cmpl_state);
3139 static CompletionDir*
3140 attach_dir (CompletionDirSent *sent,
3142 CompletionState *cmpl_state)
3144 CompletionDir* new_dir;
3146 new_dir = g_new (CompletionDir, 1);
3148 cmpl_state->directory_storage =
3149 g_list_prepend (cmpl_state->directory_storage, new_dir);
3151 new_dir->sent = sent;
3152 new_dir->fullname = g_strdup (dir_name);
3153 new_dir->fullname_len = strlen (dir_name);
3154 new_dir->cmpl_text = NULL;
3160 correct_dir_fullname (CompletionDir* cmpl_dir)
3162 gint length = strlen (cmpl_dir->fullname);
3163 gchar *first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3164 gchar *sys_filename;
3167 /* Does it end with /. (\.) ? */
3169 strcmp (cmpl_dir->fullname + length - 2, G_DIR_SEPARATOR_S ".") == 0)
3171 /* Is it just the root directory (on a drive) ? */
3172 if (cmpl_dir->fullname + length - 2 == first_slash)
3174 cmpl_dir->fullname[length - 1] = 0;
3175 cmpl_dir->fullname_len = length - 1;
3180 cmpl_dir->fullname[length - 2] = 0;
3184 /* Ends with /./ (\.\)? */
3185 else if (length >= 3 &&
3186 strcmp (cmpl_dir->fullname + length - 3,
3187 G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) == 0)
3188 cmpl_dir->fullname[length - 2] = 0;
3190 /* Ends with /.. (\..) ? */
3191 else if (length >= 3 &&
3192 strcmp (cmpl_dir->fullname + length - 3,
3193 G_DIR_SEPARATOR_S "..") == 0)
3195 /* Is it just /.. (X:\..)? */
3196 if (cmpl_dir->fullname + length - 3 == first_slash)
3198 cmpl_dir->fullname[length - 2] = 0;
3199 cmpl_dir->fullname_len = length - 2;
3203 sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3206 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3210 if (stat (sys_filename, &sbuf) < 0)
3212 g_free (sys_filename);
3216 g_free (sys_filename);
3218 cmpl_dir->fullname[length - 3] = 0;
3220 if (!correct_parent (cmpl_dir, &sbuf))
3224 /* Ends with /../ (\..\)? */
3225 else if (length >= 4 &&
3226 strcmp (cmpl_dir->fullname + length - 4,
3227 G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) == 0)
3229 /* Is it just /../ (X:\..\)? */
3230 if (cmpl_dir->fullname + length - 4 == first_slash)
3232 cmpl_dir->fullname[length - 3] = 0;
3233 cmpl_dir->fullname_len = length - 3;
3237 sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3240 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3244 if (stat (sys_filename, &sbuf) < 0)
3246 g_free (sys_filename);
3250 g_free (sys_filename);
3252 cmpl_dir->fullname[length - 4] = 0;
3254 if (!correct_parent (cmpl_dir, &sbuf))
3258 cmpl_dir->fullname_len = strlen (cmpl_dir->fullname);
3264 correct_parent (CompletionDir *cmpl_dir,
3271 gchar *sys_filename;
3274 last_slash = strrchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3275 g_assert (last_slash);
3276 first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3278 /* Clever (?) way to check for top-level directory that works also on
3279 * Win32, where there is a drive letter and colon prefixed...
3281 if (last_slash != first_slash)
3291 sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3294 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3296 last_slash[0] = G_DIR_SEPARATOR;
3300 if (stat (sys_filename, &parbuf) < 0)
3302 g_free (sys_filename);
3305 last_slash[0] = G_DIR_SEPARATOR;
3308 g_free (sys_filename);
3310 #ifndef G_OS_WIN32 /* No inode numbers on Win32 */
3311 if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
3312 /* it wasn't a link */
3318 last_slash[0] = G_DIR_SEPARATOR;
3320 /* it was a link, have to figure it out the hard way */
3322 new_name = find_parent_dir_fullname (cmpl_dir->fullname);
3327 g_free (cmpl_dir->fullname);
3329 cmpl_dir->fullname = new_name;
3338 find_parent_dir_fullname (gchar* dirname)
3340 gchar *sys_orig_dir;
3345 sys_orig_dir = g_get_current_dir ();
3346 sys_dirname = g_filename_from_utf8 (dirname, -1, NULL, NULL, NULL);
3349 g_free (sys_orig_dir);
3350 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3354 if (chdir (sys_dirname) != 0 || chdir ("..") != 0)
3356 g_free (sys_dirname);
3357 g_free (sys_orig_dir);
3361 g_free (sys_dirname);
3363 sys_cwd = g_get_current_dir ();
3364 result = g_filename_to_utf8 (sys_cwd, -1, NULL, NULL, NULL);
3367 if (chdir (sys_orig_dir) != 0)
3370 g_free (sys_orig_dir);
3374 g_free (sys_orig_dir);
3380 /**********************************************************************/
3381 /* Completion Operations */
3382 /**********************************************************************/
3386 static PossibleCompletion*
3387 attempt_homedir_completion (gchar *text_to_complete,
3388 CompletionState *cmpl_state)
3392 if (!cmpl_state->user_dir_name_buffer &&
3393 !get_pwdb (cmpl_state))
3395 length = strlen (text_to_complete) - 1;
3397 cmpl_state->user_completion_index += 1;
3399 while (cmpl_state->user_completion_index < cmpl_state->user_directories_len)
3401 index = first_diff_index (text_to_complete + 1,
3402 cmpl_state->user_directories
3403 [cmpl_state->user_completion_index].login);
3410 if (cmpl_state->last_valid_char < (index + 1))
3411 cmpl_state->last_valid_char = index + 1;
3412 cmpl_state->user_completion_index += 1;
3416 cmpl_state->the_completion.is_a_completion = 1;
3417 cmpl_state->the_completion.is_directory = TRUE;
3419 append_completion_text ("~", cmpl_state);
3421 append_completion_text (cmpl_state->
3422 user_directories[cmpl_state->user_completion_index].login,
3425 return append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3428 if (text_to_complete[1]
3429 || cmpl_state->user_completion_index > cmpl_state->user_directories_len)
3431 cmpl_state->user_completion_index = -1;
3436 cmpl_state->user_completion_index += 1;
3437 cmpl_state->the_completion.is_a_completion = 1;
3438 cmpl_state->the_completion.is_directory = TRUE;
3440 return append_completion_text ("~" G_DIR_SEPARATOR_S, cmpl_state);
3446 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
3447 #define FOLD(c) (tolower(c))
3452 /* returns the index (>= 0) of the first differing character,
3453 * PATTERN_MATCH if the completion matches */
3455 first_diff_index (gchar *pat,
3460 while (*pat && *text && FOLD (*text) == FOLD (*pat))
3470 return PATTERN_MATCH;
3473 static PossibleCompletion*
3474 append_completion_text (gchar *text,
3475 CompletionState *cmpl_state)
3479 if (!cmpl_state->the_completion.text)
3482 len = strlen (text) + strlen (cmpl_state->the_completion.text) + 1;
3484 if (cmpl_state->the_completion.text_alloc > len)
3486 strcat (cmpl_state->the_completion.text, text);
3487 return &cmpl_state->the_completion;
3493 cmpl_state->the_completion.text_alloc = i;
3495 cmpl_state->the_completion.text = (gchar*) g_realloc (cmpl_state->the_completion.text, i);
3497 if (!cmpl_state->the_completion.text)
3501 strcat (cmpl_state->the_completion.text, text);
3502 return &cmpl_state->the_completion;
3506 static CompletionDir*
3507 find_completion_dir (gchar *text_to_complete,
3508 gchar **remaining_text,
3509 CompletionState *cmpl_state)
3511 gchar* first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
3512 CompletionDir* dir = cmpl_state->reference_dir;
3513 CompletionDir* next;
3514 *remaining_text = text_to_complete;
3518 gint len = first_slash - *remaining_text;
3520 gchar *found_name = NULL; /* Quiet gcc */
3522 gchar* pat_buf = g_new (gchar, len + 1);
3524 strncpy (pat_buf, *remaining_text, len);
3527 for (i = 0; i < dir->sent->entry_count; i += 1)
3529 if (dir->sent->entries[i].is_dir &&
3530 fnmatch (pat_buf, dir->sent->entries[i].entry_name,
3531 FNMATCH_FLAGS)!= FNM_NOMATCH)
3541 found_name = dir->sent->entries[i].entry_name;
3548 /* Perhaps we are trying to open an automount directory */
3549 found_name = pat_buf;
3552 next = open_relative_dir (found_name, dir, cmpl_state);
3560 next->cmpl_parent = dir;
3564 if (!correct_dir_fullname (dir))
3570 *remaining_text = first_slash + 1;
3571 first_slash = strchr (*remaining_text, G_DIR_SEPARATOR);
3580 update_cmpl (PossibleCompletion *poss,
3581 CompletionState *cmpl_state)
3585 if (!poss || !cmpl_is_a_completion (poss))
3588 cmpl_len = strlen (cmpl_this_completion (poss));
3590 if (cmpl_state->updated_text_alloc < cmpl_len + 1)
3592 cmpl_state->updated_text =
3593 (gchar*)g_realloc (cmpl_state->updated_text,
3594 cmpl_state->updated_text_alloc);
3595 cmpl_state->updated_text_alloc = 2*cmpl_len;
3598 if (cmpl_state->updated_text_len < 0)
3600 strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3601 cmpl_state->updated_text_len = cmpl_len;
3602 cmpl_state->re_complete = cmpl_is_directory (poss);
3604 else if (cmpl_state->updated_text_len == 0)
3606 cmpl_state->re_complete = FALSE;
3611 first_diff_index (cmpl_state->updated_text,
3612 cmpl_this_completion (poss));
3614 cmpl_state->re_complete = FALSE;
3616 if (first_diff == PATTERN_MATCH)
3619 if (first_diff > cmpl_state->updated_text_len)
3620 strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3622 cmpl_state->updated_text_len = first_diff;
3623 cmpl_state->updated_text[first_diff] = 0;
3627 static PossibleCompletion*
3628 attempt_file_completion (CompletionState *cmpl_state)
3630 gchar *pat_buf, *first_slash;
3631 CompletionDir *dir = cmpl_state->active_completion_dir;
3633 dir->cmpl_index += 1;
3635 if (dir->cmpl_index == dir->sent->entry_count)
3637 if (dir->cmpl_parent == NULL)
3639 cmpl_state->active_completion_dir = NULL;
3645 cmpl_state->active_completion_dir = dir->cmpl_parent;
3647 return attempt_file_completion (cmpl_state);
3651 g_assert (dir->cmpl_text);
3653 first_slash = strchr (dir->cmpl_text, G_DIR_SEPARATOR);
3657 gint len = first_slash - dir->cmpl_text;
3659 pat_buf = g_new (gchar, len + 1);
3660 strncpy (pat_buf, dir->cmpl_text, len);
3665 gint len = strlen (dir->cmpl_text);
3667 pat_buf = g_new (gchar, len + 2);
3668 strcpy (pat_buf, dir->cmpl_text);
3669 /* Don't append a * if the user entered one herself.
3670 * This way one can complete *.h and don't get matches
3671 * on any .help files, for instance.
3673 if (strchr (pat_buf, '*') == NULL)
3674 strcpy (pat_buf + len, "*");
3679 if (dir->sent->entries[dir->cmpl_index].is_dir)
3681 if (fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
3682 FNMATCH_FLAGS) != FNM_NOMATCH)
3684 CompletionDir* new_dir;
3686 new_dir = open_relative_dir (dir->sent->entries[dir->cmpl_index].entry_name,
3695 new_dir->cmpl_parent = dir;
3697 new_dir->cmpl_index = -1;
3698 new_dir->cmpl_text = g_strdup (first_slash + 1);
3700 cmpl_state->active_completion_dir = new_dir;
3703 return attempt_file_completion (cmpl_state);
3708 return attempt_file_completion (cmpl_state);
3714 return attempt_file_completion (cmpl_state);
3719 if (dir->cmpl_parent != NULL)
3721 append_completion_text (dir->fullname +
3722 strlen (cmpl_state->completion_dir->fullname) + 1,
3724 append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3727 append_completion_text (dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
3729 cmpl_state->the_completion.is_a_completion =
3730 fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
3731 FNMATCH_FLAGS) != FNM_NOMATCH;
3733 cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
3734 if (dir->sent->entries[dir->cmpl_index].is_dir)
3735 append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3738 return &cmpl_state->the_completion;
3745 get_pwdb (CompletionState* cmpl_state)
3747 struct passwd *pwd_ptr;
3750 gint len = 0, i, count = 0;
3752 if (cmpl_state->user_dir_name_buffer)
3756 while ((pwd_ptr = getpwent ()) != NULL)
3758 utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3759 len += strlen (utf8);
3761 utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3762 len += strlen (utf8);
3770 cmpl_state->user_dir_name_buffer = g_new (gchar, len);
3771 cmpl_state->user_directories = g_new (CompletionUserDir, count);
3772 cmpl_state->user_directories_len = count;
3774 buf_ptr = cmpl_state->user_dir_name_buffer;
3776 for (i = 0; i < count; i += 1)
3778 pwd_ptr = getpwent ();
3785 utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3786 strcpy (buf_ptr, utf8);
3789 cmpl_state->user_directories[i].login = buf_ptr;
3791 buf_ptr += strlen (buf_ptr);
3794 utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3795 strcpy (buf_ptr, utf8);
3798 cmpl_state->user_directories[i].homedir = buf_ptr;
3800 buf_ptr += strlen (buf_ptr);
3804 qsort (cmpl_state->user_directories,
3805 cmpl_state->user_directories_len,
3806 sizeof (CompletionUserDir),
3815 if (cmpl_state->user_dir_name_buffer)
3816 g_free (cmpl_state->user_dir_name_buffer);
3817 if (cmpl_state->user_directories)
3818 g_free (cmpl_state->user_directories);
3820 cmpl_state->user_dir_name_buffer = NULL;
3821 cmpl_state->user_directories = NULL;
3827 compare_user_dir (const void *a,
3830 return strcmp ((((CompletionUserDir*)a))->login,
3831 (((CompletionUserDir*)b))->login);
3837 compare_cmpl_dir (const void *a,
3840 return compare_filenames ((((CompletionDirEntry*)a))->entry_name,
3841 (((CompletionDirEntry*)b))->entry_name);
3845 cmpl_state_okay (CompletionState* cmpl_state)
3847 return cmpl_state && cmpl_state->reference_dir;
3851 cmpl_strerror (gint err)
3853 if (err == CMPL_ERRNO_TOO_LONG)
3854 return _("Name too long");
3855 else if (err == CMPL_ERRNO_DID_NOT_CONVERT)
3856 return _("Couldn't convert filename");
3858 return g_strerror (err);