X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkfilesel.c;h=ad0516c1096b709e3fbc4254e918b0dae0e76aca;hb=fa966c6aa755e27c6d470402bf06e86cc5d2fdc8;hp=99534e19a47402cc22d536e418d506ed6730ff51;hpb=bc94033dd3c2c5826557593d6d066f4d82da3eae;p=~andy%2Fgtk diff --git a/gtk/gtkfilesel.c b/gtk/gtkfilesel.c index 99534e19a..ad0516c10 100644 --- a/gtk/gtkfilesel.c +++ b/gtk/gtkfilesel.c @@ -24,7 +24,7 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ -#include "config.h" +#include #include #include @@ -42,46 +42,49 @@ #include #endif -#include /* Include early to get G_OS_WIN32 and - * G_WITH_CYGWIN */ +#include /* Include early to get G_OS_WIN32 etc */ -#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN) +#if defined(G_PLATFORM_WIN32) #include #define STRICT #include #undef STRICT -#endif /* G_OS_WIN32 || G_WITH_CYGWIN */ -#ifdef HAVE_WINSOCK_H +#endif /* G_PLATFORM_WIN32 */ +#ifdef G_OS_WIN32 #include /* For gethostname */ #endif -#include "fnmatch.h" - #include "gdk/gdkkeysyms.h" +#include "gtkalias.h" #include "gtkbutton.h" #include "gtkcellrenderertext.h" #include "gtkentry.h" #include "gtkfilesel.h" #include "gtkhbox.h" #include "gtkhbbox.h" +#include "gtkintl.h" #include "gtklabel.h" #include "gtkliststore.h" #include "gtkmain.h" +#include "gtkprivate.h" #include "gtkscrolledwindow.h" #include "gtkstock.h" -#include "gtksignal.h" #include "gtktreeselection.h" #include "gtktreeview.h" #include "gtkvbox.h" #include "gtkmenu.h" #include "gtkmenuitem.h" -#include "gtkoptionmenu.h" #include "gtkdialog.h" #include "gtkmessagedialog.h" -#include "gtkintl.h" #include "gtkdnd.h" #include "gtkeventbox.h" +#undef GTK_DISABLE_DEPRECATED +#include "gtkoptionmenu.h" +#define GTK_DISABLE_DEPRECATED + +#define WANT_HPANED 1 +#include "gtkhpaned.h" #ifdef G_OS_WIN32 #include @@ -92,6 +95,10 @@ #endif #endif /* G_OS_WIN32 */ +#ifdef G_WITH_CYGWIN +#include /* For cygwin_conv_to_posix_path */ +#endif + #define DIR_LIST_WIDTH 180 #define DIR_LIST_HEIGHT 180 #define FILE_LIST_WIDTH 180 @@ -137,10 +144,6 @@ typedef struct _PossibleCompletion PossibleCompletion; * match by first_diff_index() */ #define PATTERN_MATCH -1 -/* The arguments used by all fnmatch() calls below - */ -#define FNMATCH_FLAGS (FNM_PATHNAME | FNM_PERIOD) - #define CMPL_ERRNO_TOO_LONG ((1<<16)-1) #define CMPL_ERRNO_DID_NOT_CONVERT ((1<<16)-2) @@ -150,9 +153,11 @@ typedef struct _PossibleCompletion PossibleCompletion; */ struct _CompletionDirSent { +#ifndef G_PLATFORM_WIN32 ino_t inode; time_t mtime; dev_t device; +#endif gint entry_count; struct _CompletionDirEntry *entries; @@ -182,6 +187,7 @@ struct _CompletionDirEntry { gboolean is_dir; gchar *entry_name; + gchar *sort_key; }; struct _CompletionUserDir @@ -299,10 +305,15 @@ static gboolean cmpl_updated_dir (CompletionState* cmpl_state) */ static gchar* cmpl_reference_position (CompletionState* cmpl_state); +#if 0 +/* This doesn't work currently and would require changes + * to fnmatch.c to get working. + */ /* backing up: if cmpl_completion_matches returns NULL, you may query * the index of the last completable character into cmpl_updated_text. */ static gint cmpl_last_valid_char (CompletionState* cmpl_state); +#endif /* When the user selects a non-directory, call cmpl_completion_fullname * to get the full name of the selected file. @@ -314,7 +325,7 @@ static gchar* cmpl_completion_fullname (const gchar*, CompletionSta static CompletionDir* open_ref_dir (gchar* text_to_complete, gchar** remaining_text, CompletionState* cmpl_state); -#if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN) +#ifndef G_PLATFORM_WIN32 static gboolean check_dir (gchar *dir_name, struct stat *result, gboolean *stat_subdirs); @@ -333,7 +344,7 @@ static CompletionDirSent* open_new_dir (gchar* dir_name, static gint correct_dir_fullname (CompletionDir* cmpl_dir); static gint correct_parent (CompletionDir* cmpl_dir, struct stat *sbuf); -#ifndef G_OS_WIN32 +#ifndef G_PLATFORM_WIN32 static gchar* find_parent_dir_fullname (gchar* dirname); #endif static CompletionDir* attach_dir (CompletionDirSent* sent, @@ -384,6 +395,7 @@ static gint gtk_file_selection_insert_text (GtkWidget *widget, gint new_text_length, gint *position, gpointer user_data); +static void gtk_file_selection_update_fileops (GtkFileSelection *filesel); static void gtk_file_selection_file_activate (GtkTreeView *tree_view, GtkTreePath *path, @@ -411,12 +423,50 @@ static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data); static void free_selected_names (GPtrArray *names); -#if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN) -#define compare_filenames(a, b) strcmp(a, b) +#ifndef G_PLATFORM_WIN32 + +#define compare_utf8_filenames(a, b) strcmp(a, b) +#define compare_sys_filenames(a, b) strcmp(a, b) + #else -#define compare_filenames(a, b) g_ascii_strcasecmp(a, b) -#endif +static gint +compare_utf8_filenames (const gchar *a, + const gchar *b) +{ + gchar *a_folded, *b_folded; + gint retval; + + a_folded = g_utf8_strdown (a, -1); + b_folded = g_utf8_strdown (b, -1); + + retval = strcmp (a_folded, b_folded); + + g_free (a_folded); + g_free (b_folded); + + return retval; +} + +static gint +compare_sys_filenames (const gchar *a, + const gchar *b) +{ + gchar *a_utf8, *b_utf8; + gint retval; + + a_utf8 = g_filename_to_utf8 (a, -1, NULL, NULL, NULL); + b_utf8 = g_filename_to_utf8 (b, -1, NULL, NULL, NULL); + + retval = compare_utf8_filenames (a_utf8, b_utf8); + + g_free (a_utf8); + g_free (b_utf8); + + return retval; +} + +#endif static GtkWindowClass *parent_class = NULL; @@ -427,11 +477,11 @@ static gint cmpl_errno; /* * Take the path currently in the file selection * entry field and translate as necessary from - * a WIN32 style to CYGWIN32 style path. For + * a Win32 style to Cygwin style path. For * instance translate: * x:\somepath\file.jpg * to: - * //x/somepath/file.jpg + * /cygdrive/x/somepath/file.jpg * * Replace the path in the selection text field. * Return a boolean value concerning whether a @@ -441,71 +491,47 @@ static int translate_win32_path (GtkFileSelection *filesel) { int updated = 0; - gchar *path; + const gchar *path; + gchar newPath[MAX_PATH]; /* * Retrieve the current path */ path = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry)); - /* - * Translate only if this looks like a DOS-ish - * path... First handle any drive letters. - */ - if (isalpha (path[0]) && (path[1] == ':')) { - /* - * This part kind of stinks... It isn't possible - * to know if there is enough space in the current - * string for the extra character required in this - * conversion. Assume that there isn't enough space - * and use the set function on the text field to - * set the newly created string. - */ - gchar *newPath = g_strdup_printf ("//%c/%s", path[0], (path + 3)); - gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), newPath); - - path = newPath; - updated = 1; - } + cygwin_conv_to_posix_path (path, newPath); + updated = (strcmp (path, newPath) != 0); - /* - * Now, replace backslashes with forward slashes - * if necessary. - */ - if (strchr (path, '\\')) - { - int index; - for (index = 0; path[index] != '\0'; index++) - if (path[index] == '\\') - path[index] = '/'; - - updated = 1; - } + if (updated) + gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), newPath); return updated; } #endif -GtkType +GType gtk_file_selection_get_type (void) { - static GtkType file_selection_type = 0; + static GType file_selection_type = 0; if (!file_selection_type) { - static const GtkTypeInfo filesel_info = + static const GTypeInfo filesel_info = { - "GtkFileSelection", - sizeof (GtkFileSelection), sizeof (GtkFileSelectionClass), - (GtkClassInitFunc) gtk_file_selection_class_init, - (GtkObjectInitFunc) gtk_file_selection_init, - /* reserved_1 */ NULL, - /* reserved_2 */ NULL, - (GtkClassInitFunc) NULL, + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gtk_file_selection_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkFileSelection), + 0, /* n_preallocs */ + (GInstanceInitFunc) gtk_file_selection_init, }; - file_selection_type = gtk_type_unique (GTK_TYPE_DIALOG, &filesel_info); + file_selection_type = + g_type_register_static (GTK_TYPE_DIALOG, "GtkFileSelection", + &filesel_info, 0); } return file_selection_type; @@ -522,7 +548,7 @@ gtk_file_selection_class_init (GtkFileSelectionClass *class) object_class = (GtkObjectClass*) class; widget_class = (GtkWidgetClass*) class; - parent_class = gtk_type_class (GTK_TYPE_DIALOG); + parent_class = g_type_class_peek_parent (class); gobject_class->finalize = gtk_file_selection_finalize; gobject_class->set_property = gtk_file_selection_set_property; @@ -531,23 +557,23 @@ gtk_file_selection_class_init (GtkFileSelectionClass *class) g_object_class_install_property (gobject_class, PROP_FILENAME, g_param_spec_string ("filename", - _("Filename"), - _("The currently selected filename."), + P_("Filename"), + P_("The currently selected filename"), NULL, G_PARAM_READABLE | G_PARAM_WRITABLE)); g_object_class_install_property (gobject_class, PROP_SHOW_FILEOPS, g_param_spec_boolean ("show_fileops", - _("Show file operations"), - _("Whether buttons for creating/manipulating files should be displayed."), + P_("Show file operations"), + P_("Whether buttons for creating/manipulating files should be displayed"), FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE)); g_object_class_install_property (gobject_class, PROP_SELECT_MULTIPLE, g_param_spec_boolean ("select_multiple", - _("Select multiple"), - _("Whether to allow multiple files to be selected."), + P_("Select multiple"), + P_("Whether to allow multiple files to be selected"), FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE)); @@ -630,11 +656,12 @@ gtk_file_selection_init (GtkFileSelection *filesel) { GtkWidget *entry_vbox; GtkWidget *label; - GtkWidget *list_hbox; + GtkWidget *list_hbox, *list_container; GtkWidget *confirm_area; GtkWidget *pulldown_hbox; GtkWidget *scrolled_win; GtkWidget *eventbox; + GtkWidget *spacer; GtkDialog *dialog; GtkListStore *model; @@ -672,10 +699,29 @@ gtk_file_selection_init (GtkFileSelection *filesel) FALSE, FALSE, 0); /* The horizontal box containing the directory and file listboxes */ + + spacer = gtk_hbox_new (FALSE, 0); + gtk_widget_set_size_request (spacer, -1, 5); + gtk_box_pack_start (GTK_BOX (filesel->main_vbox), spacer, FALSE, FALSE, 0); + gtk_widget_show (spacer); + list_hbox = gtk_hbox_new (FALSE, 5); gtk_box_pack_start (GTK_BOX (filesel->main_vbox), list_hbox, TRUE, TRUE, 0); gtk_widget_show (list_hbox); + if (WANT_HPANED) + list_container = g_object_new (GTK_TYPE_HPANED, + "visible", TRUE, + "parent", list_hbox, + "border_width", 0, + NULL); + else + list_container = list_hbox; + spacer = gtk_hbox_new (FALSE, 0); + gtk_widget_set_size_request (spacer, -1, 5); + gtk_box_pack_start (GTK_BOX (filesel->main_vbox), spacer, FALSE, FALSE, 0); + gtk_widget_show (spacer); + /* The directories list */ model = gtk_list_store_new (1, G_TYPE_STRING); @@ -693,7 +739,8 @@ gtk_file_selection_init (GtkFileSelection *filesel) gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->dir_list), column); - gtk_widget_set_usize (filesel->dir_list, DIR_LIST_WIDTH, DIR_LIST_HEIGHT); + gtk_widget_set_size_request (filesel->dir_list, + DIR_LIST_WIDTH, DIR_LIST_HEIGHT); g_signal_connect (filesel->dir_list, "row_activated", G_CALLBACK (gtk_file_selection_dir_activate), filesel); @@ -704,8 +751,11 @@ gtk_file_selection_init (GtkFileSelection *filesel) gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->dir_list); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); - gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5); - gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 0); + if (GTK_IS_PANED (list_container)) + gtk_paned_pack1 (GTK_PANED (list_container), scrolled_win, TRUE, TRUE); + else + gtk_container_add (GTK_CONTAINER (list_container), scrolled_win); gtk_widget_show (filesel->dir_list); gtk_widget_show (scrolled_win); @@ -725,7 +775,8 @@ gtk_file_selection_init (GtkFileSelection *filesel) gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->file_list), column); - gtk_widget_set_usize (filesel->file_list, FILE_LIST_WIDTH, FILE_LIST_HEIGHT); + gtk_widget_set_size_request (filesel->file_list, + FILE_LIST_WIDTH, FILE_LIST_HEIGHT); g_signal_connect (filesel->file_list, "row_activated", G_CALLBACK (gtk_file_selection_file_activate), filesel); g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list)), "changed", @@ -738,8 +789,8 @@ gtk_file_selection_init (GtkFileSelection *filesel) gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->file_list); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); - gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5); - gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 0); + gtk_container_add (GTK_CONTAINER (list_container), scrolled_win); gtk_widget_show (filesel->file_list); gtk_widget_show (scrolled_win); @@ -777,24 +828,30 @@ gtk_file_selection_init (GtkFileSelection *filesel) gtk_widget_show (eventbox); filesel->selection_entry = gtk_entry_new (); - gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "key_press_event", - (GtkSignalFunc) gtk_file_selection_key_press, filesel); - gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "insert_text", - (GtkSignalFunc) gtk_file_selection_insert_text, NULL); - gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "focus_in_event", - (GtkSignalFunc) grab_default, - GTK_OBJECT (filesel->ok_button)); - gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "activate", - (GtkSignalFunc) gtk_button_clicked, - GTK_OBJECT (filesel->ok_button)); + g_signal_connect (filesel->selection_entry, "key_press_event", + G_CALLBACK (gtk_file_selection_key_press), filesel); + g_signal_connect (filesel->selection_entry, "insert_text", + G_CALLBACK (gtk_file_selection_insert_text), NULL); + g_signal_connect_swapped (filesel->selection_entry, "changed", + G_CALLBACK (gtk_file_selection_update_fileops), filesel); + g_signal_connect_swapped (filesel->selection_entry, "focus_in_event", + G_CALLBACK (grab_default), + filesel->ok_button); + g_signal_connect_swapped (filesel->selection_entry, "activate", + G_CALLBACK (gtk_button_clicked), + filesel->ok_button); + gtk_box_pack_start (GTK_BOX (entry_vbox), filesel->selection_entry, TRUE, TRUE, 0); gtk_widget_show (filesel->selection_entry); + gtk_label_set_mnemonic_widget (GTK_LABEL (filesel->selection_text), + filesel->selection_entry); + if (!cmpl_state_okay (filesel->cmpl_state)) { gchar err_buf[256]; - sprintf (err_buf, _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno)); + g_snprintf (err_buf, sizeof (err_buf), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno)); gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf); } @@ -913,13 +970,21 @@ filenames_dropped (GtkWidget *widget, else { GtkWidget *dialog; + gchar *filename_utf8; + + /* Conversion back to UTF-8 should always succeed for the result + * of g_filename_from_uri() + */ + filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL); + g_assert (filename_utf8); dialog = gtk_message_dialog_new (GTK_WINDOW (widget), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, - _("The file \"%s\" resides on another machine (called %s) and may not be availible to this program.\n" - "Are you sure that you want to select it?"), filename, hostname); + _("The file \"%s\" resides on another machine (called %s) and may not be available to this program.\n" + "Are you sure that you want to select it?"), filename_utf8, hostname); + g_free (filename_utf8); g_object_set_data_full (G_OBJECT (dialog), "gtk-fs-dnd-filename", g_strdup (filename), g_free); @@ -983,8 +1048,10 @@ filenames_drag_get (GtkWidget *widget, } else { - g_print ("Setting text: '%s'\n", file); - gtk_selection_data_set_text (selection_data, file, -1); + gchar *filename_utf8 = g_filename_to_utf8 (file, -1, NULL, NULL, NULL); + g_assert (filename_utf8); + gtk_selection_data_set_text (selection_data, filename_utf8, -1); + g_free (filename_utf8); } } } @@ -993,11 +1060,11 @@ static void file_selection_setup_dnd (GtkFileSelection *filesel) { GtkWidget *eventbox; - static GtkTargetEntry drop_types[] = { + static const GtkTargetEntry drop_types[] = { { "text/uri-list", 0, TARGET_URILIST} }; static gint n_drop_types = sizeof(drop_types)/sizeof(drop_types[0]); - static GtkTargetEntry drag_types[] = { + static const GtkTargetEntry drag_types[] = { { "text/uri-list", 0, TARGET_URILIST}, { "UTF8_STRING", 0, TARGET_UTF8_STRING }, { "STRING", 0, 0 }, @@ -1011,8 +1078,8 @@ file_selection_setup_dnd (GtkFileSelection *filesel) drop_types, n_drop_types, GDK_ACTION_COPY); - gtk_signal_connect (GTK_OBJECT(filesel), "drag_data_received", - GTK_SIGNAL_FUNC(filenames_dropped), NULL); + g_signal_connect (filesel, "drag_data_received", + G_CALLBACK (filenames_dropped), NULL); eventbox = gtk_widget_get_parent (filesel->selection_text); gtk_drag_source_set (eventbox, @@ -1020,10 +1087,8 @@ file_selection_setup_dnd (GtkFileSelection *filesel) drag_types, n_drag_types, GDK_ACTION_COPY); - gtk_signal_connect (GTK_OBJECT (eventbox), - "drag_data_get", - GTK_SIGNAL_FUNC (filenames_drag_get), - filesel); + g_signal_connect (eventbox, "drag_data_get", + G_CALLBACK (filenames_drag_get), filesel); } GtkWidget* @@ -1031,7 +1096,7 @@ gtk_file_selection_new (const gchar *title) { GtkFileSelection *filesel; - filesel = gtk_type_new (GTK_TYPE_FILE_SELECTION); + filesel = g_object_new (GTK_TYPE_FILE_SELECTION, NULL); gtk_window_set_title (GTK_WINDOW (filesel), title); gtk_dialog_set_has_separator (GTK_DIALOG (filesel), FALSE); @@ -1049,9 +1114,9 @@ gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel) if (!filesel->fileop_c_dir) { filesel->fileop_c_dir = gtk_button_new_with_mnemonic (_("_New Folder")); - gtk_signal_connect (GTK_OBJECT (filesel->fileop_c_dir), "clicked", - (GtkSignalFunc) gtk_file_selection_create_dir, - (gpointer) filesel); + g_signal_connect (filesel->fileop_c_dir, "clicked", + G_CALLBACK (gtk_file_selection_create_dir), + filesel); gtk_box_pack_start (GTK_BOX (filesel->button_area), filesel->fileop_c_dir, TRUE, TRUE, 0); gtk_widget_show (filesel->fileop_c_dir); @@ -1060,9 +1125,9 @@ gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel) if (!filesel->fileop_del_file) { filesel->fileop_del_file = gtk_button_new_with_mnemonic (_("De_lete File")); - gtk_signal_connect (GTK_OBJECT (filesel->fileop_del_file), "clicked", - (GtkSignalFunc) gtk_file_selection_delete_file, - (gpointer) filesel); + g_signal_connect (filesel->fileop_del_file, "clicked", + G_CALLBACK (gtk_file_selection_delete_file), + filesel); gtk_box_pack_start (GTK_BOX (filesel->button_area), filesel->fileop_del_file, TRUE, TRUE, 0); gtk_widget_show (filesel->fileop_del_file); @@ -1071,15 +1136,17 @@ gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel) if (!filesel->fileop_ren_file) { filesel->fileop_ren_file = gtk_button_new_with_mnemonic (_("_Rename File")); - gtk_signal_connect (GTK_OBJECT (filesel->fileop_ren_file), "clicked", - (GtkSignalFunc) gtk_file_selection_rename_file, - (gpointer) filesel); + g_signal_connect (filesel->fileop_ren_file, "clicked", + G_CALLBACK (gtk_file_selection_rename_file), + filesel); gtk_box_pack_start (GTK_BOX (filesel->button_area), filesel->fileop_ren_file, TRUE, TRUE, 0); gtk_widget_show (filesel->fileop_ren_file); } + + gtk_file_selection_update_fileops (filesel); + g_object_notify (G_OBJECT (filesel), "show_fileops"); - gtk_widget_queue_resize (GTK_WIDGET (filesel)); } void @@ -1109,27 +1176,47 @@ gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel) +/** + * gtk_file_selection_set_filename: + * @filesel: a #GtkFileSelection. + * @filename: a string to set as the default file name. + * + * Sets a default path for the file requestor. If @filename includes a + * directory path, then the requestor will open with that path as its + * current working directory. + * + * This has the consequence that in order to open the requestor with a + * working directory and an empty filename, @filename must have a trailing + * directory separator. + * + * The encoding of @filename is the on-disk encoding, which + * may not be UTF-8. See g_filename_from_utf8(). + **/ void gtk_file_selection_set_filename (GtkFileSelection *filesel, const gchar *filename) { gchar *buf; const char *name, *last_slash; + char *filename_utf8; g_return_if_fail (GTK_IS_FILE_SELECTION (filesel)); g_return_if_fail (filename != NULL); - last_slash = strrchr (filename, G_DIR_SEPARATOR); + filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL); + g_return_if_fail (filename_utf8 != NULL); + + last_slash = strrchr (filename_utf8, G_DIR_SEPARATOR); if (!last_slash) { buf = g_strdup (""); - name = filename; + name = filename_utf8; } else { - buf = g_strdup (filename); - buf[last_slash - filename + 1] = 0; + buf = g_strdup (filename_utf8); + buf[last_slash - filename_utf8 + 1] = 0; name = last_slash + 1; } @@ -1139,24 +1226,28 @@ gtk_file_selection_set_filename (GtkFileSelection *filesel, gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name); g_free (buf); g_object_notify (G_OBJECT (filesel), "filename"); + + g_free (filename_utf8); } /** * gtk_file_selection_get_filename: * @filesel: a #GtkFileSelection * - * This function returns the selected filename in encoding of - * g_filename_from_utf8(), which may or may not be the same as that + * This function returns the selected filename in the on-disk encoding + * (see g_filename_from_utf8()), which may or may not be the same as that * used by GTK+ (UTF-8). To convert to UTF-8, call g_filename_to_utf8(). * The returned string points to a statically allocated buffer and * should be copied if you plan to keep it around. + * + * If no file is selected then the selected directory path is returned. * - * Return value: currently-selected filename in locale's encoding + * Return value: currently-selected filename in the on-disk encoding. **/ G_CONST_RETURN gchar* gtk_file_selection_get_filename (GtkFileSelection *filesel) { - static gchar nothing[2] = ""; + static const gchar nothing[2] = ""; static gchar something[MAXPATHLEN*2]; char *sys_filename; const char *text; @@ -1169,7 +1260,9 @@ gtk_file_selection_get_filename (GtkFileSelection *filesel) text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry)); if (text) { - sys_filename = g_filename_from_utf8 (cmpl_completion_fullname (text, filesel->cmpl_state), -1, NULL, NULL, NULL); + gchar *fullname = cmpl_completion_fullname (text, filesel->cmpl_state); + sys_filename = g_filename_from_utf8 (fullname, -1, NULL, NULL, NULL); + g_free (fullname); if (!sys_filename) return nothing; strncpy (something, sys_filename, sizeof (something)); @@ -1279,7 +1372,7 @@ gtk_file_selection_fileop_error (GtkFileSelection *fs, dialog = gtk_message_dialog_new (GTK_WINDOW (fs), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, + GTK_BUTTONS_OK, "%s", error_message); /* yes, we free it */ @@ -1287,9 +1380,9 @@ gtk_file_selection_fileop_error (GtkFileSelection *fs, gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); - gtk_signal_connect_object (GTK_OBJECT (dialog), "response", - (GtkSignalFunc) gtk_widget_destroy, - (gpointer) dialog); + g_signal_connect_swapped (dialog, "response", + G_CALLBACK (gtk_widget_destroy), + dialog); gtk_widget_show (dialog); } @@ -1305,6 +1398,20 @@ gtk_file_selection_fileop_destroy (GtkWidget *widget, fs->fileop_dialog = NULL; } +static gboolean +entry_is_empty (GtkEntry *entry) +{ + const gchar *text = gtk_entry_get_text (entry); + + return *text == '\0'; +} + +static void +gtk_file_selection_fileop_entry_changed (GtkEntry *entry, + GtkWidget *button) +{ + gtk_widget_set_sensitive (button, !entry_is_empty (entry)); +} static void gtk_file_selection_create_dir_confirmed (GtkWidget *widget, @@ -1339,7 +1446,7 @@ gtk_file_selection_create_dir_confirmed (GtkWidget *widget, goto out; } - if (mkdir (sys_full_path, 0755) < 0) + if (mkdir (sys_full_path, 0777) < 0) { buf = g_strdup_printf (_("Error creating folder \"%s\": %s\n"), dirname, g_strerror (errno)); @@ -1372,9 +1479,9 @@ gtk_file_selection_create_dir (GtkWidget *widget, /* main dialog */ dialog = gtk_dialog_new (); fs->fileop_dialog = dialog; - gtk_signal_connect (GTK_OBJECT (dialog), "destroy", - (GtkSignalFunc) gtk_file_selection_fileop_destroy, - (gpointer) fs); + g_signal_connect (dialog, "destroy", + G_CALLBACK (gtk_file_selection_fileop_destroy), + fs); gtk_window_set_title (GTK_WINDOW (dialog), _("New Folder")); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs)); @@ -1401,29 +1508,29 @@ gtk_file_selection_create_dir (GtkWidget *widget, gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, TRUE, TRUE, 5); GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT); + gtk_entry_set_activates_default (GTK_ENTRY (fs->fileop_entry), TRUE); gtk_widget_show (fs->fileop_entry); /* buttons */ - button = gtk_button_new_from_stock (GTK_STOCK_CANCEL); - gtk_signal_connect_object (GTK_OBJECT (button), "clicked", - (GtkSignalFunc) gtk_widget_destroy, - (gpointer) dialog); - gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area), - button, TRUE, TRUE, 0); - GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); - gtk_widget_grab_default (button); - gtk_widget_show (button); + button = gtk_dialog_add_button (GTK_DIALOG (dialog), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + g_signal_connect_swapped (button, "clicked", + G_CALLBACK (gtk_widget_destroy), + dialog); gtk_widget_grab_focus (fs->fileop_entry); - button = gtk_button_new_with_label (_("Create")); - gtk_signal_connect (GTK_OBJECT (button), "clicked", - (GtkSignalFunc) gtk_file_selection_create_dir_confirmed, - (gpointer) fs); - gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area), - button, TRUE, TRUE, 0); - GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); - gtk_widget_show (button); + button = gtk_dialog_add_button (GTK_DIALOG (dialog), + _("C_reate"), GTK_RESPONSE_OK); + gtk_widget_set_sensitive (button, FALSE); + g_signal_connect (button, "clicked", + G_CALLBACK (gtk_file_selection_create_dir_confirmed), + fs); + g_signal_connect (fs->fileop_entry, "changed", + G_CALLBACK (gtk_file_selection_fileop_entry_changed), + button); + + gtk_widget_grab_default (button); gtk_widget_show (dialog); } @@ -1514,11 +1621,11 @@ gtk_file_selection_delete_file (GtkWidget *widget, GTK_WINDOW (fs)->modal ? GTK_DIALOG_MODAL : 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, - _("Really delete file \"%s\" ?"), filename); + _("Really delete file \"%s\"?"), filename); - gtk_signal_connect (GTK_OBJECT (dialog), "destroy", - (GtkSignalFunc) gtk_file_selection_fileop_destroy, - (gpointer) fs); + g_signal_connect (dialog, "destroy", + G_CALLBACK (gtk_file_selection_fileop_destroy), + fs); gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File")); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); @@ -1530,7 +1637,7 @@ gtk_file_selection_delete_file (GtkWidget *widget, gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL); - g_signal_connect (G_OBJECT (dialog), "response", + g_signal_connect (dialog, "response", G_CALLBACK (gtk_file_selection_delete_file_response), fs); @@ -1635,9 +1742,9 @@ gtk_file_selection_rename_file (GtkWidget *widget, /* main dialog */ fs->fileop_dialog = dialog = gtk_dialog_new (); - gtk_signal_connect (GTK_OBJECT (dialog), "destroy", - (GtkSignalFunc) gtk_file_selection_fileop_destroy, - (gpointer) fs); + g_signal_connect (dialog, "destroy", + G_CALLBACK (gtk_file_selection_fileop_destroy), + fs); gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File")); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs)); @@ -1665,6 +1772,7 @@ gtk_file_selection_rename_file (GtkWidget *widget, gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, TRUE, TRUE, 5); GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT); + gtk_entry_set_activates_default (GTK_ENTRY (fs->fileop_entry), TRUE); gtk_widget_show (fs->fileop_entry); gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file); @@ -1672,26 +1780,24 @@ gtk_file_selection_rename_file (GtkWidget *widget, 0, strlen (fs->fileop_file)); /* buttons */ - button = gtk_button_new_from_stock (GTK_STOCK_CANCEL); - gtk_signal_connect_object (GTK_OBJECT (button), "clicked", - (GtkSignalFunc) gtk_widget_destroy, - (gpointer) dialog); - gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area), - button, TRUE, TRUE, 0); - GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); - gtk_widget_grab_default (button); - gtk_widget_show (button); + button = gtk_dialog_add_button (GTK_DIALOG (dialog), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + g_signal_connect_swapped (button, "clicked", + G_CALLBACK (gtk_widget_destroy), + dialog); gtk_widget_grab_focus (fs->fileop_entry); - button = gtk_button_new_with_label (_("Rename")); - gtk_signal_connect (GTK_OBJECT (button), "clicked", - (GtkSignalFunc) gtk_file_selection_rename_file_confirmed, - (gpointer) fs); - gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area), - button, TRUE, TRUE, 0); - GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); - gtk_widget_show (button); + button = gtk_dialog_add_button (GTK_DIALOG (dialog), + _("_Rename"), GTK_RESPONSE_OK); + g_signal_connect (button, "clicked", + G_CALLBACK (gtk_file_selection_rename_file_confirmed), + fs); + g_signal_connect (fs->fileop_entry, "changed", + G_CALLBACK (gtk_file_selection_fileop_entry_changed), + button); + + gtk_widget_grab_default (button); gtk_widget_show (dialog); } @@ -1709,8 +1815,8 @@ gtk_file_selection_insert_text (GtkWidget *widget, if (!filename) { - gdk_beep (); - gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "insert_text"); + gdk_display_beep (gtk_widget_get_display (widget)); + g_signal_stop_emission_by_name (widget, "insert_text"); return FALSE; } @@ -1719,6 +1825,23 @@ gtk_file_selection_insert_text (GtkWidget *widget, return TRUE; } +static void +gtk_file_selection_update_fileops (GtkFileSelection *fs) +{ + gboolean sensitive; + + if (!fs->selection_entry) + return; + + sensitive = !entry_is_empty (GTK_ENTRY (fs->selection_entry)); + + if (fs->fileop_del_file) + gtk_widget_set_sensitive (fs->fileop_del_file, sensitive); + + if (fs->fileop_ren_file) + gtk_widget_set_sensitive (fs->fileop_ren_file, sensitive); +} + static gint gtk_file_selection_key_press (GtkWidget *widget, GdkEventKey *event, @@ -1842,9 +1965,9 @@ gtk_file_selection_update_history_menu (GtkFileSelection *fs, fs->history_list = g_list_append (fs->history_list, callback_arg); - gtk_signal_connect (GTK_OBJECT (menu_item), "activate", - (GtkSignalFunc) gtk_file_selection_history_callback, - (gpointer) fs); + g_signal_connect (menu_item, "activate", + G_CALLBACK (gtk_file_selection_history_callback), + fs); gtk_menu_shell_append (GTK_MENU_SHELL (fs->history_menu), menu_item); gtk_widget_show (menu_item); } @@ -1863,17 +1986,26 @@ get_real_filename (gchar *filename, /* Check to see if the selection was a drive selector */ if (isalpha (filename[0]) && (filename[1] == ':')) { - /* It is... map it to a CYGWIN32 drive */ - gchar *temp_filename = g_strdup_printf ("//%c/", tolower (filename[0])); + gchar temp_filename[MAX_PATH]; + int len; + cygwin_conv_to_posix_path (filename, temp_filename); + + /* we need trailing '/'. */ + len = strlen (temp_filename); + if (len > 0 && temp_filename[len-1] != '/') + { + temp_filename[len] = '/'; + temp_filename[len+1] = '\0'; + } + if (free_old) g_free (filename); - return temp_filename; + return g_strdup (temp_filename); } -#else - return filename; #endif /* G_WITH_CYGWIN */ + return filename; } static void @@ -1890,7 +2022,6 @@ gtk_file_selection_file_activate (GtkTreeView *tree_view, gtk_tree_model_get_iter (model, &iter, path); gtk_tree_model_get (model, &iter, FILE_COLUMN, &filename, -1); filename = get_real_filename (filename, TRUE); - gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename); gtk_button_clicked (GTK_BUTTON (fs->ok_button)); @@ -1910,11 +2041,12 @@ gtk_file_selection_dir_activate (GtkTreeView *tree_view, gtk_tree_model_get_iter (model, &iter, path); gtk_tree_model_get (model, &iter, DIR_COLUMN, &filename, -1); + filename = get_real_filename (filename, TRUE); gtk_file_selection_populate (fs, filename, FALSE, FALSE); g_free (filename); } -#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN) +#ifdef G_PLATFORM_WIN32 static void win32_gtk_add_drives_to_dir_list (GtkListStore *model) @@ -1932,10 +2064,10 @@ win32_gtk_add_drives_to_dir_list (GtkListStore *model) while (*textPtr != '\0') { /* Ignore floppies (?) */ - if ((tolower (textPtr[0]) != 'a') && (tolower (textPtr[0]) != 'b')) + if (GetDriveType (textPtr) != DRIVE_REMOVABLE) { /* Build the actual displayable string */ - sprintf (formatBuffer, "%c:\\", toupper (textPtr[0])); + g_snprintf (formatBuffer, sizeof (formatBuffer), "%c:\\", toupper (textPtr[0])); /* Add to the list */ gtk_list_store_append (model, &iter); @@ -1946,6 +2078,22 @@ win32_gtk_add_drives_to_dir_list (GtkListStore *model) } #endif +static gchar * +escape_underscores (const gchar *str) +{ + GString *result = g_string_new (NULL); + while (*str) + { + if (*str == '_') + g_string_append_c (result, '_'); + + g_string_append_c (result, *str); + str++; + } + + return g_string_free (result, FALSE); +} + static void gtk_file_selection_populate (GtkFileSelection *fs, gchar *rel_path, @@ -1962,7 +2110,6 @@ gtk_file_selection_populate (GtkFileSelection *fs, gchar* sel_text; gint did_recurse = FALSE; gint possible_count = 0; - gint selection_index = -1; g_return_if_fail (GTK_IS_FILE_SELECTION (fs)); @@ -2017,7 +2164,7 @@ gtk_file_selection_populate (GtkFileSelection *fs, poss = cmpl_next_completion (cmpl_state); } -#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN) +#ifdef G_PLATFORM_WIN32 /* For Windows, add drives as potential selections */ win32_gtk_add_drives_to_dir_list (dir_model); #endif @@ -2056,8 +2203,6 @@ gtk_file_selection_populate (GtkFileSelection *fs, } else { - selection_index = cmpl_last_valid_char (cmpl_state) - - (strlen (rel_path) - strlen (rem_path)); if (fs->selection_entry) gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path); } @@ -2070,16 +2215,16 @@ gtk_file_selection_populate (GtkFileSelection *fs, if (!did_recurse) { - if (fs->selection_entry) - gtk_entry_set_position (GTK_ENTRY (fs->selection_entry), selection_index); + if (fs->selection_entry && try_complete) + gtk_editable_set_position (GTK_EDITABLE (fs->selection_entry), -1); if (fs->selection_entry) { - sel_text = g_strconcat (_("Selection: "), - cmpl_reference_position (cmpl_state), - NULL); + char *escaped = escape_underscores (cmpl_reference_position (cmpl_state)); + sel_text = g_strconcat (_("_Selection: "), escaped, NULL); + g_free (escaped); - gtk_label_set_text (GTK_LABEL (fs->selection_text), sel_text); + gtk_label_set_text_with_mnemonic (GTK_LABEL (fs->selection_text), sel_text); g_free (sel_text); } @@ -2096,7 +2241,7 @@ gtk_file_selection_abort (GtkFileSelection *fs) { gchar err_buf[256]; - sprintf (err_buf, _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno)); + g_snprintf (err_buf, sizeof (err_buf), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno)); /* BEEP gdk_beep(); */ @@ -2219,8 +2364,8 @@ gtk_file_selection_file_changed (GtkTreeSelection *selection, /* A common case is selecting a range of files from top to bottom, * so quickly check for that to avoid looping over the entire list */ - if (compare_filenames (g_ptr_array_index (old_names, old_names->len - 1), - g_ptr_array_index (new_names, new_names->len - 1)) != 0) + if (compare_utf8_filenames (g_ptr_array_index (old_names, old_names->len - 1), + g_ptr_array_index (new_names, new_names->len - 1)) != 0) index = new_names->len - 1; else { @@ -2231,8 +2376,8 @@ gtk_file_selection_file_changed (GtkTreeSelection *selection, */ while (i < old_names->len && j < new_names->len) { - cmp = compare_filenames (g_ptr_array_index (old_names, i), - g_ptr_array_index (new_names, j)); + cmp = compare_utf8_filenames (g_ptr_array_index (old_names, i), + g_ptr_array_index (new_names, j)); if (cmp < 0) { i++; @@ -2260,8 +2405,9 @@ gtk_file_selection_file_changed (GtkTreeSelection *selection, * was selected, which is used for subsequent range selections. * So search up from there. */ - if (compare_filenames (fs->last_selected, - g_ptr_array_index (new_names, 0)) == 0) + if (fs->last_selected && + compare_utf8_filenames (fs->last_selected, + g_ptr_array_index (new_names, 0)) == 0) index = new_names->len - 1; else index = 0; @@ -2295,7 +2441,7 @@ maybe_clear_entry: entry = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)); if ((entry != NULL) && (fs->last_selected != NULL) && - (compare_filenames (entry, fs->last_selected) == 0)) + (compare_utf8_filenames (entry, fs->last_selected) == 0)) gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), ""); } @@ -2308,8 +2454,8 @@ maybe_clear_entry: * in the file list. The first file in the list is equivalent to what * gtk_file_selection_get_filename() would return. * - * The filenames are in the encoding of g_filename_from_utf8, which may or may - * not be the same as that used by GTK+ (UTF-8). To convert to UTF-8, call + * The filenames are in the encoding of g_filename_from_utf8(), which may or + * may not be the same as that used by GTK+ (UTF-8). To convert to UTF-8, call * g_filename_to_utf8() on each string. * * Return value: a newly-allocated %NULL-terminated array of strings. Use @@ -2323,6 +2469,7 @@ gtk_file_selection_get_selections (GtkFileSelection *filesel) gchar *filename, *dirname; gchar *current, *buf; gint i, count; + gboolean unselected_entry; g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), NULL); @@ -2342,7 +2489,7 @@ gtk_file_selection_get_selections (GtkFileSelection *filesel) selections = g_new (gchar *, 2); count = 0; - selections[count++] = filename; + unselected_entry = TRUE; if (names != NULL) { @@ -2355,13 +2502,20 @@ gtk_file_selection_get_selections (GtkFileSelection *filesel) current = g_build_filename (dirname, buf, NULL); g_free (buf); - if (compare_filenames (current, filename) != 0) - selections[count++] = current; - else - g_free (current); + selections[count++] = current; + + if (unselected_entry && compare_sys_filenames (current, filename) == 0) + unselected_entry = FALSE; } + + g_free (dirname); } + if (unselected_entry) + selections[count++] = filename; + else + g_free (filename); + selections[count] = NULL; return selections; @@ -2391,25 +2545,28 @@ cmpl_reference_position (CompletionState *cmpl_state) return cmpl_state->reference_dir->fullname; } +#if 0 +/* This doesn't work currently and would require changes + * to fnmatch.c to get working. + */ static gint cmpl_last_valid_char (CompletionState *cmpl_state) { return cmpl_state->last_valid_char; } +#endif static gchar* cmpl_completion_fullname (const gchar *text, CompletionState *cmpl_state) { - static char nothing[2] = ""; - if (!cmpl_state_okay (cmpl_state)) { - return nothing; + return g_strdup (""); } else if (g_path_is_absolute (text)) { - strcpy (cmpl_state->updated_text, text); + return g_strdup (text); } #ifdef HAVE_PWD_H else if (text[0] == '~') @@ -2419,33 +2576,19 @@ cmpl_completion_fullname (const gchar *text, dir = open_user_dir (text, cmpl_state); - if (!dir) - { - /* spencer says just return ~something, so - * for now just do it. */ - strcpy (cmpl_state->updated_text, text); - } - else + if (dir) { - - strcpy (cmpl_state->updated_text, dir->fullname); - slash = strchr (text, G_DIR_SEPARATOR); - - if (slash) - strcat (cmpl_state->updated_text, slash); + + /* slash may be NULL, that works too */ + return g_strconcat (dir->fullname, slash, NULL); } } #endif - else - { - strcpy (cmpl_state->updated_text, cmpl_state->reference_dir->fullname); - if (cmpl_state->updated_text[strlen (cmpl_state->updated_text) - 1] != G_DIR_SEPARATOR) - strcat (cmpl_state->updated_text, G_DIR_SEPARATOR_S); - strcat (cmpl_state->updated_text, text); - } - - return cmpl_state->updated_text; + + return g_build_filename (cmpl_state->reference_dir->fullname, + text, + NULL); } /* The three completion selectors @@ -2472,19 +2615,55 @@ cmpl_is_a_completion (PossibleCompletion* pc) /* Construction, deletion */ /**********************************************************************/ +/* Get the nearest parent of the current directory for which + * we can convert the filename into UTF-8. With paranoia. + * Returns "." when all goes wrong. + */ +static gchar * +get_current_dir_utf8 (void) +{ + gchar *dir = g_get_current_dir (); + gchar *dir_utf8 = NULL; + + while (TRUE) + { + gchar *last_slash; + + dir_utf8 = g_filename_to_utf8 (dir, -1, NULL, NULL, NULL); + if (dir_utf8) + break; + + last_slash = strrchr (dir, G_DIR_SEPARATOR); + if (!last_slash) /* g_get_current_dir() wasn't absolute! */ + break; + + if (last_slash + 1 == g_path_skip_root (dir)) /* Parent directory is a root directory */ + { + if (last_slash[1] == '\0') /* Root misencoded! */ + break; + else + last_slash[1] = '\0'; + } + else + last_slash[0] = '\0'; + + g_assert (last_slash); + } + + g_free (dir); + + return dir_utf8 ? dir_utf8 : g_strdup ("."); +} + static CompletionState* cmpl_init_state (void) { - gchar *sys_getcwd_buf; gchar *utf8_cwd; CompletionState *new_state; new_state = g_new (CompletionState, 1); - /* g_get_current_dir() returns a string in the "system" charset */ - sys_getcwd_buf = g_get_current_dir (); - utf8_cwd = g_filename_to_utf8 (sys_getcwd_buf, -1, NULL, NULL, NULL); - g_free (sys_getcwd_buf); + utf8_cwd = get_current_dir_utf8 (); tryagain: @@ -2575,7 +2754,10 @@ free_dir_sent (CompletionDirSent* sent) { gint i; for (i = 0; i < sent->entry_count; i++) - g_free (sent->entries[i].entry_name); + { + g_free (sent->entries[i].entry_name); + g_free (sent->entries[i].sort_key); + } g_free (sent->entries); g_free (sent); } @@ -2619,7 +2801,9 @@ cmpl_completion_matches (gchar *text_to_complete, gchar **remaining_text, CompletionState *cmpl_state) { +#ifdef HAVE_PWD_H gchar* first_slash; +#endif PossibleCompletion *poss; prune_memory_usage (cmpl_state); @@ -2718,7 +2902,7 @@ open_ref_dir (gchar *text_to_complete, if (text_to_complete[0] == '/' && text_to_complete[1] == '/') { char root_dir[5]; - sprintf (root_dir, "//%c", text_to_complete[2]); + g_snprintf (root_dir, sizeof (root_dir), "//%c", text_to_complete[2]); new_dir = open_dir (root_dir, cmpl_state); @@ -2761,24 +2945,20 @@ open_ref_dir (gchar *text_to_complete, p = strrchr (tmp, G_DIR_SEPARATOR); if (p) { - if (p == tmp) + if (p + 1 == g_path_skip_root (tmp)) p++; *p = '\0'; - new_dir = open_dir (tmp, cmpl_state); if (new_dir) *remaining_text = text_to_complete + - ((p == tmp + 1) ? (p - tmp) : (p + 1 - tmp)); + ((p == g_path_skip_root (tmp)) ? (p - tmp) : (p + 1 - tmp)); } else { /* If no possible candidates, use the cwd */ - gchar *sys_curdir = g_get_current_dir (); - gchar *utf8_curdir = g_filename_to_utf8 (sys_curdir, -1, NULL, NULL, NULL); - - g_free (sys_curdir); + gchar *utf8_curdir = get_current_dir_utf8 (); new_dir = open_dir (utf8_curdir, cmpl_state); @@ -2897,7 +3077,7 @@ open_new_dir (gchar *dir_name, CompletionDirSent *sent; GDir *directory; const char *dirent; - GError *error; + GError *error = NULL; gint entry_count = 0; gint n_entries = 0; gint i; @@ -2906,10 +3086,11 @@ open_new_dir (gchar *dir_name, gchar *sys_dir_name; sent = g_new (CompletionDirSent, 1); +#ifndef G_PLATFORM_WIN32 sent->mtime = sbuf->st_mtime; sent->inode = sbuf->st_ino; sent->device = sbuf->st_dev; - +#endif path = g_string_sized_new (2*MAXPATHLEN + 10); sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL); @@ -2929,6 +3110,7 @@ open_new_dir (gchar *dir_name, while ((dirent = g_dir_read_name (directory)) != NULL) entry_count++; + entry_count += 2; /* For ".",".." */ sent->entries = g_new (CompletionDirEntry, entry_count); sent->entry_count = entry_count; @@ -2937,23 +3119,36 @@ open_new_dir (gchar *dir_name, for (i = 0; i < entry_count; i += 1) { - dirent = g_dir_read_name (directory); + GError *error = NULL; - if (!dirent) + if (i == 0) + dirent = "."; + else if (i == 1) + dirent = ".."; + else { - g_warning ("Failure reading folder '%s'", sys_dir_name); - g_dir_close (directory); - g_free (sys_dir_name); - return NULL; + dirent = g_dir_read_name (directory); + if (!dirent) /* Directory changed */ + break; } - sent->entries[n_entries].entry_name = g_filename_to_utf8 (dirent, -1, NULL, NULL, NULL); - if (!g_utf8_validate (sent->entries[n_entries].entry_name, -1, NULL)) + sent->entries[n_entries].entry_name = g_filename_to_utf8 (dirent, -1, NULL, NULL, &error); + if (sent->entries[n_entries].entry_name == NULL + || !g_utf8_validate (sent->entries[n_entries].entry_name, -1, NULL)) { - g_warning (_("The filename %s couldn't be converted to UTF-8. Try setting the environment variable G_BROKEN_FILENAMES."), dirent); + gchar *escaped_str = g_strescape (dirent, NULL); + g_message (_("The filename \"%s\" couldn't be converted to UTF-8. " + "(try setting the environment variable G_FILENAME_ENCODING): %s"), + escaped_str, + error->message ? error->message : _("Invalid UTF-8")); + g_free (escaped_str); + g_clear_error (&error); continue; } - + g_clear_error (&error); + + sent->entries[n_entries].sort_key = g_utf8_collate_key (sent->entries[n_entries].entry_name, -1); + g_string_assign (path, sys_dir_name); if (path->str[path->len-1] != G_DIR_SEPARATOR) { @@ -2987,7 +3182,7 @@ open_new_dir (gchar *dir_name, return sent; } -#if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN) +#ifndef G_PLATFORM_WIN32 static gboolean check_dir (gchar *dir_name, @@ -3060,12 +3255,14 @@ static CompletionDir* open_dir (gchar *dir_name, CompletionState *cmpl_state) { +#ifndef G_PLATFORM_WIN32 struct stat sbuf; gboolean stat_subdirs; - CompletionDirSent *sent; GList* cdsl; +#endif + CompletionDirSent *sent; -#if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN) +#ifndef G_PLATFORM_WIN32 if (!check_dir (dir_name, &sbuf, &stat_subdirs)) return NULL; @@ -3082,11 +3279,11 @@ open_dir (gchar *dir_name, cdsl = cdsl->next; } -#else - stat_subdirs = TRUE; -#endif sent = open_new_dir (dir_name, &sbuf, stat_subdirs); +#else + sent = open_new_dir (dir_name, NULL, TRUE); +#endif if (sent) { @@ -3230,7 +3427,9 @@ correct_parent (CompletionDir *cmpl_dir, struct stat parbuf; gchar *last_slash; gchar *first_slash; +#ifndef G_PLATFORM_WIN32 gchar *new_name; +#endif gchar *sys_filename; gchar c = 0; @@ -3270,7 +3469,7 @@ correct_parent (CompletionDir *cmpl_dir, } g_free (sys_filename); -#ifndef G_OS_WIN32 /* No inode numbers on Win32 */ +#ifndef G_PLATFORM_WIN32 /* No inode numbers on Win32 */ if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev) /* it wasn't a link */ return TRUE; @@ -3295,7 +3494,7 @@ correct_parent (CompletionDir *cmpl_dir, return TRUE; } -#ifndef G_OS_WIN32 +#ifndef G_PLATFORM_WIN32 static gchar* find_parent_dir_fullname (gchar* dirname) @@ -3316,9 +3515,10 @@ find_parent_dir_fullname (gchar* dirname) if (chdir (sys_dirname) != 0 || chdir ("..") != 0) { + cmpl_errno = errno; + chdir (sys_orig_dir); g_free (sys_dirname); g_free (sys_orig_dir); - cmpl_errno = errno; return NULL; } g_free (sys_dirname); @@ -3406,7 +3606,12 @@ attempt_homedir_completion (gchar *text_to_complete, #endif -#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN) +#ifdef G_PLATFORM_WIN32 +/* FIXME: determine whether we should casefold all Unicode letters + * here, too (and in in first_diff_index() walk through the strings with + * g_utf8_next_char()), or if this folding isn't actually needed at + * all. + */ #define FOLD(c) (tolower(c)) #else #define FOLD(c) (c) @@ -3490,8 +3695,7 @@ find_completion_dir (gchar *text_to_complete, for (i = 0; i < dir->sent->entry_count; i += 1) { if (dir->sent->entries[i].is_dir && - fnmatch (pat_buf, dir->sent->entries[i].entry_name, - FNMATCH_FLAGS)!= FNM_NOMATCH) + _gtk_fnmatch (pat_buf, dir->sent->entries[i].entry_name, TRUE)) { if (found) { @@ -3518,7 +3722,7 @@ find_completion_dir (gchar *text_to_complete, { g_free (pat_buf); return NULL; - } +} next->cmpl_parent = dir; @@ -3552,10 +3756,10 @@ update_cmpl (PossibleCompletion *poss, if (cmpl_state->updated_text_alloc < cmpl_len + 1) { + cmpl_state->updated_text_alloc = 2*cmpl_len; cmpl_state->updated_text = (gchar*)g_realloc (cmpl_state->updated_text, cmpl_state->updated_text_alloc); - cmpl_state->updated_text_alloc = 2*cmpl_len; } if (cmpl_state->updated_text_len < 0) @@ -3641,8 +3845,7 @@ attempt_file_completion (CompletionState *cmpl_state) { if (dir->sent->entries[dir->cmpl_index].is_dir) { - if (fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name, - FNMATCH_FLAGS) != FNM_NOMATCH) + if (_gtk_fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name, TRUE)) { CompletionDir* new_dir; @@ -3690,8 +3893,7 @@ attempt_file_completion (CompletionState *cmpl_state) append_completion_text (dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state); cmpl_state->the_completion.is_a_completion = - fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name, - FNMATCH_FLAGS) != FNM_NOMATCH; + _gtk_fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name, TRUE); cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir; if (dir->sent->entries[dir->cmpl_index].is_dir) @@ -3800,8 +4002,9 @@ static gint compare_cmpl_dir (const void *a, const void *b) { - return compare_filenames ((((CompletionDirEntry*)a))->entry_name, - (((CompletionDirEntry*)b))->entry_name); + + return strcmp (((CompletionDirEntry*)a)->sort_key, + (((CompletionDirEntry*)b))->sort_key); } static gint