1 /* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2003-2004 XangXiu Yang
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License Version 2 as
6 * published by the Free Software Foundation;
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
22 #include <sys/types.h>
24 #ifdef HAVE_SYS_PARAM_H
25 #include <sys/param.h>
37 #include <glib.h> /* Include early to get G_OS_WIN32 and
40 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
45 #endif /* G_OS_WIN32 || G_WITH_CYGWIN */
47 #include <winsock.h> /* For gethostname */
50 #include "gdk/gdkkeysyms.h"
52 #include <lttvwindow/gtkdirsel.h>
60 #define mkdir(p,m) _mkdir(p)
62 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
64 #endif /* G_OS_WIN32 */
67 #include <sys/cygwin.h> /* For cygwin_conv_to_posix_path */
70 #define DIR_LIST_WIDTH 180
71 #define DIR_LIST_HEIGHT 180
72 #define FILE_LIST_WIDTH 180
73 #define FILE_LIST_HEIGHT 180
75 /* The Hurd doesn't define either PATH_MAX or MAXPATHLEN, so we put this
76 * in here, since the rest of the code in the file does require some
81 # define MAXPATHLEN PATH_MAX
83 # define MAXPATHLEN 2048
87 /* I've put this here so it doesn't get confused with the
88 * file completion interface */
89 typedef struct _HistoryCallbackArg HistoryCallbackArg
;
91 struct _HistoryCallbackArg
98 typedef struct _CompletionState CompletionState
;
99 typedef struct _CompletionDir CompletionDir
;
100 typedef struct _CompletionDirSent CompletionDirSent
;
101 typedef struct _CompletionDirEntry CompletionDirEntry
;
102 typedef struct _CompletionUserDir CompletionUserDir
;
103 typedef struct _PossibleCompletion PossibleCompletion
;
105 /* Non-external file completion decls and structures */
107 /* A contant telling PRCS how many directories to cache. Its actually
108 * kept in a list, so the geometry isn't important. */
109 #define CMPL_DIRECTORY_CACHE_SIZE 10
111 /* A constant used to determine whether a substring was an exact
112 * match by first_diff_index()
114 #define PATTERN_MATCH -1
115 #define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
116 #define CMPL_ERRNO_DID_NOT_CONVERT ((1<<16)-2)
118 /* This structure contains all the useful information about a directory
119 * for the purposes of filename completion. These structures are cached
120 * in the CompletionState struct. CompletionDir's are reference counted.
122 struct _CompletionDirSent
129 struct _CompletionDirEntry
*entries
;
132 struct _CompletionDir
134 CompletionDirSent
*sent
;
139 struct _CompletionDir
*cmpl_parent
;
144 /* This structure contains pairs of directory entry names with a flag saying
145 * whether or not they are a valid directory. NOTE: This information is used
146 * to provide the caller with information about whether to update its completions
147 * or try to open a file. Since directories are cached by the directory mtime,
148 * a symlink which points to an invalid file (which will not be a directory),
149 * will not be reevaluated if that file is created, unless the containing
150 * directory is touched. I consider this case to be worth ignoring (josh).
152 struct _CompletionDirEntry
159 struct _CompletionUserDir
165 struct _PossibleCompletion
167 /* accessible fields, all are accessed externally by functions
171 gint is_a_completion
;
172 gboolean is_directory
;
179 struct _CompletionState
181 gint last_valid_char
;
183 gint updated_text_len
;
184 gint updated_text_alloc
;
185 gboolean re_complete
;
187 gchar
*user_dir_name_buffer
;
188 gint user_directories_len
;
190 gchar
*last_completion_text
;
192 gint user_completion_index
; /* if >= 0, currently completing ~user */
194 struct _CompletionDir
*completion_dir
; /* directory completing from */
195 struct _CompletionDir
*active_completion_dir
;
197 struct _PossibleCompletion the_completion
;
199 struct _CompletionDir
*reference_dir
; /* initial directory */
201 GList
* directory_storage
;
202 GList
* directory_sent_storage
;
204 struct _CompletionUserDir
*user_directories
;
222 /* File completion functions which would be external, were they used
223 * outside of this file.
226 static CompletionState
* cmpl_init_state (void);
227 static void cmpl_free_state (CompletionState
*cmpl_state
);
228 static gint
cmpl_state_okay (CompletionState
* cmpl_state
);
229 static const gchar
* cmpl_strerror (gint
);
231 static PossibleCompletion
* cmpl_completion_matches(gchar
*text_to_complete
,
232 gchar
**remaining_text
,
233 CompletionState
*cmpl_state
);
235 /* Returns a name for consideration, possibly a completion, this name
236 * will be invalid after the next call to cmpl_next_completion.
238 static char* cmpl_this_completion (PossibleCompletion
*);
240 /* True if this completion matches the given text. Otherwise, this
241 * output can be used to have a list of non-completions.
243 static gint
cmpl_is_a_completion (PossibleCompletion
*);
245 /* True if the completion is a directory
247 static gboolean
cmpl_is_directory (PossibleCompletion
*);
249 /* Obtains the next completion, or NULL
251 static PossibleCompletion
* cmpl_next_completion (CompletionState
*);
253 /* Updating completions: the return value of cmpl_updated_text() will
254 * be text_to_complete completed as much as possible after the most
255 * recent call to cmpl_completion_matches. For the present
256 * application, this is the suggested replacement for the user's input
257 * string. You must CALL THIS AFTER ALL cmpl_text_completions have
260 static gchar
* cmpl_updated_text (CompletionState
* cmpl_state
);
262 /* After updating, to see if the completion was a directory, call
263 * this. If it was, you should consider re-calling completion_matches.
265 static gboolean
cmpl_updated_dir (CompletionState
* cmpl_state
);
267 /* Current location: if using file completion, return the current
268 * directory, from which file completion begins. More specifically,
269 * the cwd concatenated with all exact completions up to the last
270 * directory delimiter('/').
272 static gchar
* cmpl_reference_position (CompletionState
* cmpl_state
);
274 /* backing up: if cmpl_completion_matches returns NULL, you may query
275 * the index of the last completable character into cmpl_updated_text.
277 static gint
cmpl_last_valid_char (CompletionState
* cmpl_state
);
279 /* When the user selects a non-directory, call cmpl_completion_fullname
280 * to get the full name of the selected file.
282 static const gchar
* cmpl_completion_fullname (const gchar
*, CompletionState
* cmpl_state
);
285 /* Directory operations. */
286 static CompletionDir
* open_ref_dir (gchar
* text_to_complete
,
287 gchar
** remaining_text
,
288 CompletionState
* cmpl_state
);
289 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
290 static gboolean
check_dir (gchar
*dir_name
,
292 gboolean
*stat_subdirs
);
294 static CompletionDir
* open_dir (gchar
* dir_name
,
295 CompletionState
* cmpl_state
);
297 static CompletionDir
* open_user_dir (const gchar
* text_to_complete
,
298 CompletionState
*cmpl_state
);
300 static CompletionDir
* open_relative_dir (gchar
* dir_name
, CompletionDir
* dir
,
301 CompletionState
*cmpl_state
);
302 static CompletionDirSent
* open_new_dir (gchar
* dir_name
,
304 gboolean stat_subdirs
);
305 static gint
correct_dir_fullname (CompletionDir
* cmpl_dir
);
306 static gint
correct_parent (CompletionDir
* cmpl_dir
,
309 static gchar
* find_parent_dir_fullname (gchar
* dirname
);
311 static CompletionDir
* attach_dir (CompletionDirSent
* sent
,
313 CompletionState
*cmpl_state
);
314 static void free_dir_sent (CompletionDirSent
* sent
);
315 static void free_dir (CompletionDir
*dir
);
316 static void prune_memory_usage(CompletionState
*cmpl_state
);
318 /* Completion operations */
320 static PossibleCompletion
* attempt_homedir_completion(gchar
* text_to_complete
,
321 CompletionState
*cmpl_state
);
323 static PossibleCompletion
* attempt_dir_completion(CompletionState
*cmpl_state
);
324 static CompletionDir
* find_completion_dir(gchar
* text_to_complete
,
325 gchar
** remaining_text
,
326 CompletionState
* cmpl_state
);
327 static PossibleCompletion
* append_completion_text(gchar
* text
,
328 CompletionState
* cmpl_state
);
330 static gint
get_pwdb(CompletionState
* cmpl_state
);
331 static gint
compare_user_dir(const void* a
, const void* b
);
333 static gint
first_diff_index(gchar
* pat
, gchar
* text
);
334 static gint
compare_cmpl_dir(const void* a
, const void* b
);
335 static void update_cmpl(PossibleCompletion
* poss
,
336 CompletionState
* cmpl_state
);
338 static void gtk_dir_selection_class_init (GtkDirSelectionClass
*klass
);
339 static void gtk_dir_selection_set_property (GObject
*object
,
343 static void gtk_dir_selection_get_property (GObject
*object
,
347 static void gtk_dir_selection_init (GtkDirSelection
*filesel
);
348 static void gtk_dir_selection_finalize (GObject
*object
);
349 static void gtk_dir_selection_destroy (GtkObject
*object
);
350 static void gtk_dir_selection_map (GtkWidget
*widget
);
351 static gint
gtk_dir_selection_key_press (GtkWidget
*widget
,
354 static gint
gtk_dir_selection_insert_text (GtkWidget
*widget
,
355 const gchar
*new_text
,
356 gint new_text_length
,
359 static void gtk_dir_selection_update_fileops (GtkDirSelection
*filesel
);
361 static void gtk_dir_selection_file_activate (GtkTreeView
*tree_view
,
363 GtkTreeViewColumn
*column
,
365 static void gtk_dir_selection_file_changed (GtkTreeSelection
*selection
,
367 static void gtk_dir_selection_dir_activate (GtkTreeView
*tree_view
,
369 GtkTreeViewColumn
*column
,
371 static void gtk_dir_selection_dir_changed (GtkTreeSelection
*selection
,
373 static void gtk_dir_selection_populate (GtkDirSelection
*fs
,
375 gboolean try_complete
,
376 gboolean reset_entry
);
377 static void gtk_dir_selection_abort (GtkDirSelection
*fs
);
379 static void gtk_dir_selection_update_history_menu (GtkDirSelection
*fs
,
382 static void gtk_dir_selection_create_dir (GtkWidget
*widget
, gpointer data
);
383 static void gtk_dir_selection_delete_file (GtkWidget
*widget
, gpointer data
);
384 static void gtk_dir_selection_rename_file (GtkWidget
*widget
, gpointer data
);
386 static void free_selected_names (GPtrArray
*names
);
388 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
389 #define compare_filenames(a, b) strcmp(a, b)
391 #define compare_filenames(a, b) g_ascii_strcasecmp(a, b)
395 static GtkWindowClass
*parent_class
= NULL
;
397 /* Saves errno when something cmpl does fails. */
398 static gint cmpl_errno
;
402 * Take the path currently in the file selection
403 * entry field and translate as necessary from
404 * a WIN32 style to CYGWIN32 style path. For
405 * instance translate:
406 * x:\somepath\file.jpg
408 * /cygdrive/x/somepath/file.jpg
410 * Replace the path in the selection text field.
411 * Return a boolean value concerning whether a
412 * translation had to be made.
415 translate_win32_path (GtkDirSelection
*filesel
)
419 gchar newPath
[MAX_PATH
];
422 * Retrieve the current path
424 path
= gtk_entry_get_text (GTK_ENTRY (filesel
->selection_entry
));
426 cygwin_conv_to_posix_path (path
, newPath
);
427 updated
= (strcmp (path
, newPath
) != 0);
430 gtk_entry_set_text (GTK_ENTRY (filesel
->selection_entry
), newPath
);
437 gtk_dir_selection_get_type (void)
439 static GType file_selection_type
= 0;
441 if (!file_selection_type
)
443 static const GTypeInfo filesel_info
=
445 sizeof (GtkDirSelectionClass
),
446 NULL
, /* base_init */
447 NULL
, /* base_finalize */
448 (GClassInitFunc
) gtk_dir_selection_class_init
,
449 NULL
, /* class_finalize */
450 NULL
, /* class_data */
451 sizeof (GtkDirSelection
),
453 (GInstanceInitFunc
) gtk_dir_selection_init
,
456 file_selection_type
=
457 g_type_register_static (GTK_TYPE_DIALOG
, "GtkDirSelection",
461 return file_selection_type
;
465 gtk_dir_selection_class_init (GtkDirSelectionClass
*class)
467 GObjectClass
*gobject_class
;
468 GtkObjectClass
*object_class
;
469 GtkWidgetClass
*widget_class
;
471 gobject_class
= (GObjectClass
*) class;
472 object_class
= (GtkObjectClass
*) class;
473 widget_class
= (GtkWidgetClass
*) class;
475 parent_class
= g_type_class_peek_parent (class);
477 gobject_class
->finalize
= gtk_dir_selection_finalize
;
478 gobject_class
->set_property
= gtk_dir_selection_set_property
;
479 gobject_class
->get_property
= gtk_dir_selection_get_property
;
481 g_object_class_install_property (gobject_class
,
483 g_param_spec_string ("filename",
485 _("The currently selected filename"),
487 G_PARAM_READABLE
| G_PARAM_WRITABLE
));
488 g_object_class_install_property (gobject_class
,
490 g_param_spec_boolean ("show_fileops",
491 _("Show file operations"),
492 _("Whether buttons for creating/manipulating files should be displayed"),
496 g_object_class_install_property (gobject_class
,
497 PROP_SELECT_MULTIPLE
,
498 g_param_spec_boolean ("select_multiple",
499 _("Select multiple"),
500 _("Whether to allow multiple files to be selected"),
504 object_class
->destroy
= gtk_dir_selection_destroy
;
505 widget_class
->map
= gtk_dir_selection_map
;
508 static void gtk_dir_selection_set_property (GObject
*object
,
513 GtkDirSelection
*filesel
;
515 filesel
= GTK_DIR_SELECTION (object
);
520 gtk_dir_selection_set_filename (filesel
,
521 g_value_get_string (value
));
523 case PROP_SHOW_FILEOPS
:
524 if (g_value_get_boolean (value
))
525 gtk_dir_selection_show_fileop_buttons (filesel
);
527 gtk_dir_selection_hide_fileop_buttons (filesel
);
529 case PROP_SELECT_MULTIPLE
:
530 gtk_dir_selection_set_select_multiple (filesel
, g_value_get_boolean (value
));
533 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
538 static void gtk_dir_selection_get_property (GObject
*object
,
543 GtkDirSelection
*filesel
;
545 filesel
= GTK_DIR_SELECTION (object
);
550 g_value_set_string (value
,
551 gtk_dir_selection_get_filename(filesel
));
554 case PROP_SHOW_FILEOPS
:
555 /* This is a little bit hacky, but doing otherwise would require
556 * adding a field to the object.
558 g_value_set_boolean (value
, (filesel
->fileop_c_dir
&&
559 filesel
->fileop_del_file
&&
560 filesel
->fileop_ren_file
));
562 case PROP_SELECT_MULTIPLE
:
563 g_value_set_boolean (value
, gtk_dir_selection_get_select_multiple (filesel
));
566 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
572 grab_default (GtkWidget
*widget
)
574 gtk_widget_grab_default (widget
);
579 gtk_dir_selection_init (GtkDirSelection
*filesel
)
581 GtkWidget
*entry_vbox
;
583 GtkWidget
*list_hbox
, *list_container
;
584 GtkWidget
*confirm_area
;
585 GtkWidget
*pulldown_hbox
;
586 GtkWidget
*scrolled_win
;
592 GtkTreeViewColumn
*column
;
594 gtk_widget_push_composite_child ();
596 dialog
= GTK_DIALOG (filesel
);
598 filesel
->cmpl_state
= cmpl_init_state ();
600 /* The dialog-sized vertical box */
601 filesel
->main_vbox
= dialog
->vbox
;
602 gtk_container_set_border_width (GTK_CONTAINER (filesel
), 10);
604 /* The horizontal box containing create, rename etc. buttons */
605 filesel
->button_area
= gtk_hbutton_box_new ();
606 gtk_button_box_set_layout (GTK_BUTTON_BOX (filesel
->button_area
), GTK_BUTTONBOX_START
);
607 gtk_box_set_spacing (GTK_BOX (filesel
->button_area
), 0);
608 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), filesel
->button_area
,
610 gtk_widget_show (filesel
->button_area
);
612 gtk_dir_selection_show_fileop_buttons (filesel
);
614 /* hbox for pulldown menu */
615 pulldown_hbox
= gtk_hbox_new (TRUE
, 5);
616 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), pulldown_hbox
, FALSE
, FALSE
, 0);
617 gtk_widget_show (pulldown_hbox
);
620 filesel
->history_pulldown
= gtk_option_menu_new ();
621 // gtk_widget_show (filesel->history_pulldown);
622 // gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->history_pulldown,
625 /* The horizontal box containing the directory and file listboxes */
627 spacer
= gtk_hbox_new (FALSE
, 0);
628 gtk_widget_set_size_request (spacer
, -1, 5);
629 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), spacer
, FALSE
, FALSE
, 0);
630 gtk_widget_show (spacer
);
632 list_hbox
= gtk_hbox_new (FALSE
, 5);
633 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), list_hbox
, TRUE
, TRUE
, 0);
634 gtk_widget_show (list_hbox
);
636 list_container
= g_object_new (GTK_TYPE_HPANED
,
642 list_container
= list_hbox
;
644 spacer
= gtk_hbox_new (FALSE
, 0);
645 gtk_widget_set_size_request (spacer
, -1, 5);
646 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), spacer
, FALSE
, FALSE
, 0);
647 gtk_widget_show (spacer
);
649 /* The directories list */
651 model
= gtk_list_store_new (1, G_TYPE_STRING
);
652 filesel
->dir_list
= gtk_tree_view_new_with_model (GTK_TREE_MODEL (model
));
653 g_object_unref (model
);
655 column
= gtk_tree_view_column_new_with_attributes (_("Folders"),
656 gtk_cell_renderer_text_new (),
659 label
= gtk_label_new_with_mnemonic (_("Fol_ders"));
660 gtk_label_set_mnemonic_widget (GTK_LABEL (label
), filesel
->dir_list
);
661 gtk_widget_show (label
);
662 gtk_tree_view_column_set_widget (column
, label
);
663 gtk_tree_view_column_set_sizing (column
, GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
664 gtk_tree_view_append_column (GTK_TREE_VIEW (filesel
->dir_list
), column
);
666 gtk_widget_set_size_request (filesel
->dir_list
,
667 DIR_LIST_WIDTH
, DIR_LIST_HEIGHT
);
668 g_signal_connect (filesel
->dir_list
, "row_activated",
669 G_CALLBACK (gtk_dir_selection_dir_activate
), filesel
);
670 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel
->dir_list
)), "changed",
671 G_CALLBACK (gtk_dir_selection_dir_changed
), filesel
);
673 /* gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list)); */
675 scrolled_win
= gtk_scrolled_window_new (NULL
, NULL
);
676 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win
), GTK_SHADOW_IN
);
677 gtk_container_add (GTK_CONTAINER (scrolled_win
), filesel
->dir_list
);
678 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win
),
679 GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
);
680 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win
), 0);
681 if (GTK_IS_PANED (list_container
))
682 gtk_paned_pack1 (GTK_PANED (list_container
), scrolled_win
, TRUE
, TRUE
);
684 gtk_container_add (GTK_CONTAINER (list_container
), scrolled_win
);
685 gtk_widget_show (filesel
->dir_list
);
686 gtk_widget_show (scrolled_win
);
689 model
= gtk_list_store_new (1, G_TYPE_STRING
);
690 filesel
->file_list
= gtk_tree_view_new_with_model (GTK_TREE_MODEL (model
));
691 g_object_unref (model
);
693 column
= gtk_tree_view_column_new_with_attributes (_("Files"),
694 gtk_cell_renderer_text_new (),
697 label
= gtk_label_new_with_mnemonic (_("_Files"));
698 gtk_label_set_mnemonic_widget (GTK_LABEL (label
), filesel
->file_list
);
699 gtk_widget_show (label
);
700 gtk_tree_view_column_set_widget (column
, label
);
701 gtk_tree_view_column_set_sizing (column
, GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
702 gtk_tree_view_append_column (GTK_TREE_VIEW (filesel
->file_list
), column
);
704 gtk_widget_set_size_request (filesel
->file_list
,
705 FILE_LIST_WIDTH
, FILE_LIST_HEIGHT
);
706 g_signal_connect (filesel
->file_list
, "row_activated",
707 G_CALLBACK (gtk_dir_selection_file_activate
), filesel
);
708 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel
->file_list
)), "changed",
709 G_CALLBACK (gtk_dir_selection_file_changed
), filesel
);
711 /* gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list)); */
713 scrolled_win
= gtk_scrolled_window_new (NULL
, NULL
);
714 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win
), GTK_SHADOW_IN
);
715 gtk_container_add (GTK_CONTAINER (scrolled_win
), filesel
->file_list
);
716 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win
),
717 GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
);
718 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win
), 0);
719 // gtk_container_add (GTK_CONTAINER (list_container), scrolled_win);
720 // gtk_widget_show (filesel->file_list);
721 // gtk_widget_show (scrolled_win);
723 /* action area for packing buttons into. */
724 filesel
->action_area
= gtk_hbox_new (TRUE
, 0);
725 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), filesel
->action_area
,
727 gtk_widget_show (filesel
->action_area
);
729 /* The OK/Cancel button area */
730 confirm_area
= dialog
->action_area
;
732 /* The Cancel button */
733 filesel
->cancel_button
= gtk_dialog_add_button (dialog
,
735 GTK_RESPONSE_CANCEL
);
737 filesel
->ok_button
= gtk_dialog_add_button (dialog
,
741 gtk_widget_grab_default (filesel
->ok_button
);
743 /* The selection entry widget */
744 entry_vbox
= gtk_vbox_new (FALSE
, 2);
745 gtk_box_pack_end (GTK_BOX (filesel
->main_vbox
), entry_vbox
, FALSE
, FALSE
, 2);
746 gtk_widget_show (entry_vbox
);
748 eventbox
= gtk_event_box_new ();
749 filesel
->selection_text
= label
= gtk_label_new ("");
750 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
751 gtk_container_add (GTK_CONTAINER (eventbox
), label
);
752 gtk_box_pack_start (GTK_BOX (entry_vbox
), eventbox
, FALSE
, FALSE
, 0);
753 gtk_widget_show (label
);
754 gtk_widget_show (eventbox
);
756 filesel
->selection_entry
= gtk_entry_new ();
757 g_signal_connect (filesel
->selection_entry
, "key_press_event",
758 G_CALLBACK (gtk_dir_selection_key_press
), filesel
);
759 g_signal_connect (filesel
->selection_entry
, "insert_text",
760 G_CALLBACK (gtk_dir_selection_insert_text
), NULL
);
761 g_signal_connect_swapped (filesel
->selection_entry
, "changed",
762 G_CALLBACK (gtk_dir_selection_update_fileops
), filesel
);
763 g_signal_connect_swapped (filesel
->selection_entry
, "focus_in_event",
764 G_CALLBACK (grab_default
),
766 g_signal_connect_swapped (filesel
->selection_entry
, "activate",
767 G_CALLBACK (gtk_button_clicked
),
770 gtk_box_pack_start (GTK_BOX (entry_vbox
), filesel
->selection_entry
, TRUE
, TRUE
, 0);
771 gtk_widget_show (filesel
->selection_entry
);
773 gtk_label_set_mnemonic_widget (GTK_LABEL (filesel
->selection_text
),
774 filesel
->selection_entry
);
776 if (!cmpl_state_okay (filesel
->cmpl_state
))
780 g_snprintf (err_buf
, sizeof (err_buf
), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno
));
782 gtk_label_set_text (GTK_LABEL (filesel
->selection_text
), err_buf
);
786 gtk_dir_selection_populate (filesel
, "", FALSE
, TRUE
);
789 gtk_widget_grab_focus (filesel
->selection_entry
);
791 gtk_widget_pop_composite_child ();
795 uri_list_extract_first_uri (const gchar
* uri_list
)
799 g_return_val_if_fail (uri_list
!= NULL
, NULL
);
802 /* We don't actually try to validate the URI according to RFC
803 * 2396, or even check for allowed characters - we just ignore
804 * comments and trim whitespace off the ends. We also
805 * allow LF delimination as well as the specified CRLF.
807 * We do allow comments like specified in RFC 2483.
813 while (g_ascii_isspace(*p
))
817 while (*q
&& (*q
!= '\n') && (*q
!= '\r'))
823 while (q
> p
&& g_ascii_isspace (*q
))
827 return g_strndup (p
, q
- p
+ 1);
830 p
= strchr (p
, '\n');
838 dnd_really_drop (GtkWidget
*dialog
, gint response_id
, GtkDirSelection
*fs
)
842 if (response_id
== GTK_RESPONSE_YES
)
844 filename
= g_object_get_data (G_OBJECT (dialog
), "gtk-fs-dnd-filename");
846 gtk_dir_selection_set_filename (fs
, filename
);
849 gtk_widget_destroy (dialog
);
854 filenames_dropped (GtkWidget
*widget
,
855 GdkDragContext
*context
,
858 GtkSelectionData
*selection_data
,
863 char *filename
= NULL
;
865 char this_hostname
[257];
867 GError
*error
= NULL
;
869 if (!selection_data
->data
)
872 uri
= uri_list_extract_first_uri ((char *)selection_data
->data
);
877 filename
= g_filename_from_uri (uri
, &hostname
, &error
);
882 g_warning ("Error getting dropped filename: %s\n",
884 g_error_free (error
);
888 res
= gethostname (this_hostname
, 256);
889 this_hostname
[256] = 0;
891 if ((hostname
== NULL
) ||
892 (res
== 0 && strcmp (hostname
, this_hostname
) == 0) ||
893 (strcmp (hostname
, "localhost") == 0))
894 gtk_dir_selection_set_filename (GTK_DIR_SELECTION (widget
),
899 gchar
*filename_utf8
;
901 /* Conversion back to UTF-8 should always succeed for the result
902 * of g_filename_from_uri()
904 filename_utf8
= g_filename_to_utf8 (filename
, -1, NULL
, NULL
, NULL
);
905 g_assert (filename_utf8
);
907 dialog
= gtk_message_dialog_new (GTK_WINDOW (widget
),
908 GTK_DIALOG_DESTROY_WITH_PARENT
,
909 GTK_MESSAGE_QUESTION
,
911 _("The file \"%s\" resides on another machine (called %s) and may not be available to this program.\n"
912 "Are you sure that you want to select it?"), filename_utf8
, hostname
);
913 g_free (filename_utf8
);
915 g_object_set_data_full (G_OBJECT (dialog
), "gtk-fs-dnd-filename", g_strdup (filename
), g_free
);
917 g_signal_connect_data (dialog
, "response",
918 (GCallback
) dnd_really_drop
,
921 gtk_widget_show (dialog
);
939 filenames_drag_get (GtkWidget
*widget
,
940 GdkDragContext
*context
,
941 GtkSelectionData
*selection_data
,
944 GtkDirSelection
*filesel
)
952 file
= gtk_dir_selection_get_filename (filesel
);
956 if (info
== TARGET_URILIST
)
958 res
= gethostname (hostname
, 256);
961 uri_list
= g_filename_to_uri (file
, (!res
)?hostname
:NULL
, &error
);
964 g_warning ("Error getting filename: %s\n",
966 g_error_free (error
);
970 gtk_selection_data_set (selection_data
,
971 selection_data
->target
, 8,
972 (void *)uri_list
, strlen((char *)uri_list
));
977 gchar
*filename_utf8
= g_filename_to_utf8 (file
, -1, NULL
, NULL
, NULL
);
978 g_assert (filename_utf8
);
979 gtk_selection_data_set_text (selection_data
, filename_utf8
, -1);
980 g_free (filename_utf8
);
986 file_selection_setup_dnd (GtkDirSelection
*filesel
)
989 static const GtkTargetEntry drop_types
[] = {
990 { "text/uri-list", 0, TARGET_URILIST
}
992 static gint n_drop_types
= sizeof(drop_types
)/sizeof(drop_types
[0]);
993 static const GtkTargetEntry drag_types
[] = {
994 { "text/uri-list", 0, TARGET_URILIST
},
995 { "UTF8_STRING", 0, TARGET_UTF8_STRING
},
998 { "COMPOUND_TEXT", 0, 0 }
1000 static gint n_drag_types
= sizeof(drag_types
)/sizeof(drag_types
[0]);
1002 gtk_drag_dest_set (GTK_WIDGET (filesel
),
1003 GTK_DEST_DEFAULT_ALL
,
1004 drop_types
, n_drop_types
,
1007 g_signal_connect (filesel
, "drag_data_received",
1008 G_CALLBACK (filenames_dropped
), NULL
);
1010 eventbox
= gtk_widget_get_parent (filesel
->selection_text
);
1011 gtk_drag_source_set (eventbox
,
1013 drag_types
, n_drag_types
,
1016 g_signal_connect (eventbox
, "drag_data_get",
1017 G_CALLBACK (filenames_drag_get
), filesel
);
1021 gtk_dir_selection_new (const gchar
*title
)
1023 GtkDirSelection
*filesel
;
1025 filesel
= g_object_new (GTK_TYPE_DIR_SELECTION
, NULL
);
1026 gtk_window_set_title (GTK_WINDOW (filesel
), title
);
1027 gtk_dialog_set_has_separator (GTK_DIALOG (filesel
), FALSE
);
1029 file_selection_setup_dnd (filesel
);
1031 return GTK_WIDGET (filesel
);
1035 gtk_dir_selection_show_fileop_buttons (GtkDirSelection
*filesel
)
1037 g_return_if_fail (GTK_IS_DIR_SELECTION (filesel
));
1039 /* delete, create directory, and rename */
1040 if (!filesel
->fileop_c_dir
)
1042 filesel
->fileop_c_dir
= gtk_button_new_with_mnemonic (_("_New Folder"));
1043 g_signal_connect (filesel
->fileop_c_dir
, "clicked",
1044 G_CALLBACK (gtk_dir_selection_create_dir
),
1046 // gtk_box_pack_start (GTK_BOX (filesel->button_area),
1047 // filesel->fileop_c_dir, TRUE, TRUE, 0);
1048 // gtk_widget_show (filesel->fileop_c_dir);
1051 if (!filesel
->fileop_del_file
)
1053 filesel
->fileop_del_file
= gtk_button_new_with_mnemonic (_("De_lete File"));
1054 g_signal_connect (filesel
->fileop_del_file
, "clicked",
1055 G_CALLBACK (gtk_dir_selection_delete_file
),
1057 // gtk_box_pack_start (GTK_BOX (filesel->button_area),
1058 // filesel->fileop_del_file, TRUE, TRUE, 0);
1059 // gtk_widget_show (filesel->fileop_del_file);
1062 if (!filesel
->fileop_ren_file
)
1064 filesel
->fileop_ren_file
= gtk_button_new_with_mnemonic (_("_Rename File"));
1065 g_signal_connect (filesel
->fileop_ren_file
, "clicked",
1066 G_CALLBACK (gtk_dir_selection_rename_file
),
1068 // gtk_box_pack_start (GTK_BOX (filesel->button_area),
1069 // filesel->fileop_ren_file, TRUE, TRUE, 0);
1070 // gtk_widget_show (filesel->fileop_ren_file);
1073 gtk_dir_selection_update_fileops (filesel
);
1075 g_object_notify (G_OBJECT (filesel
), "show_fileops");
1079 gtk_dir_selection_hide_fileop_buttons (GtkDirSelection
*filesel
)
1081 g_return_if_fail (GTK_IS_DIR_SELECTION (filesel
));
1083 if (filesel
->fileop_ren_file
)
1085 gtk_widget_destroy (filesel
->fileop_ren_file
);
1086 filesel
->fileop_ren_file
= NULL
;
1089 if (filesel
->fileop_del_file
)
1091 gtk_widget_destroy (filesel
->fileop_del_file
);
1092 filesel
->fileop_del_file
= NULL
;
1095 if (filesel
->fileop_c_dir
)
1097 gtk_widget_destroy (filesel
->fileop_c_dir
);
1098 filesel
->fileop_c_dir
= NULL
;
1100 g_object_notify (G_OBJECT (filesel
), "show_fileops");
1106 * gtk_dir_selection_set_filename:
1107 * @filesel: a #GtkDirSelection.
1108 * @filename: a string to set as the default file name.
1110 * Sets a default path for the file requestor. If @filename includes a
1111 * directory path, then the requestor will open with that path as its
1112 * current working directory.
1114 * The encoding of @filename is the on-disk encoding, which
1115 * may not be UTF-8. See g_filename_from_utf8().
1118 gtk_dir_selection_set_filename (GtkDirSelection
*filesel
,
1119 const gchar
*filename
)
1122 const char *name
, *last_slash
;
1123 char *filename_utf8
;
1125 g_return_if_fail (GTK_IS_DIR_SELECTION (filesel
));
1126 g_return_if_fail (filename
!= NULL
);
1128 filename_utf8
= g_filename_to_utf8 (filename
, -1, NULL
, NULL
, NULL
);
1129 g_return_if_fail (filename_utf8
!= NULL
);
1131 last_slash
= strrchr (filename_utf8
, G_DIR_SEPARATOR
);
1135 buf
= g_strdup ("");
1136 name
= filename_utf8
;
1140 buf
= g_strdup (filename_utf8
);
1141 buf
[last_slash
- filename_utf8
+ 1] = 0;
1142 name
= last_slash
+ 1;
1145 gtk_dir_selection_populate (filesel
, buf
, FALSE
, TRUE
);
1147 if (filesel
->selection_entry
)
1148 gtk_entry_set_text (GTK_ENTRY (filesel
->selection_entry
), name
);
1150 g_object_notify (G_OBJECT (filesel
), "filename");
1152 g_free (filename_utf8
);
1156 * gtk_dir_selection_get_filename:
1157 * @filesel: a #GtkDirSelection
1159 * This function returns the selected filename in the on-disk encoding
1160 * (see g_filename_from_utf8()), which may or may not be the same as that
1161 * used by GTK+ (UTF-8). To convert to UTF-8, call g_filename_to_utf8().
1162 * The returned string points to a statically allocated buffer and
1163 * should be copied if you plan to keep it around.
1165 * If no file is selected then the selected directory path is returned.
1167 * Return value: currently-selected filename in the on-disk encoding.
1169 G_CONST_RETURN gchar
*
1170 gtk_dir_selection_get_filename (GtkDirSelection
*filesel
)
1172 static const gchar nothing
[2] = "";
1173 static gchar something
[MAXPATHLEN
*2];
1177 g_return_val_if_fail (GTK_IS_DIR_SELECTION (filesel
), nothing
);
1179 #ifdef G_WITH_CYGWIN
1180 translate_win32_path (filesel
);
1182 text
= gtk_entry_get_text (GTK_ENTRY (filesel
->selection_entry
));
1185 sys_filename
= g_filename_from_utf8 (cmpl_completion_fullname (text
, filesel
->cmpl_state
), -1, NULL
, NULL
, NULL
);
1188 strncpy (something
, sys_filename
, sizeof (something
));
1189 g_free (sys_filename
);
1197 gtk_dir_selection_complete (GtkDirSelection
*filesel
,
1198 const gchar
*pattern
)
1200 g_return_if_fail (GTK_IS_DIR_SELECTION (filesel
));
1201 g_return_if_fail (pattern
!= NULL
);
1203 if (filesel
->selection_entry
)
1204 gtk_entry_set_text (GTK_ENTRY (filesel
->selection_entry
), pattern
);
1205 gtk_dir_selection_populate (filesel
, (gchar
*) pattern
, TRUE
, TRUE
);
1209 gtk_dir_selection_destroy (GtkObject
*object
)
1211 GtkDirSelection
*filesel
;
1213 HistoryCallbackArg
*callback_arg
;
1215 g_return_if_fail (GTK_IS_DIR_SELECTION (object
));
1217 filesel
= GTK_DIR_SELECTION (object
);
1219 if (filesel
->fileop_dialog
)
1221 gtk_widget_destroy (filesel
->fileop_dialog
);
1222 filesel
->fileop_dialog
= NULL
;
1225 if (filesel
->history_list
)
1227 list
= filesel
->history_list
;
1230 callback_arg
= list
->data
;
1231 g_free (callback_arg
->directory
);
1232 g_free (callback_arg
);
1235 g_list_free (filesel
->history_list
);
1236 filesel
->history_list
= NULL
;
1239 if (filesel
->cmpl_state
)
1241 cmpl_free_state (filesel
->cmpl_state
);
1242 filesel
->cmpl_state
= NULL
;
1245 if (filesel
->selected_names
)
1247 free_selected_names (filesel
->selected_names
);
1248 filesel
->selected_names
= NULL
;
1251 if (filesel
->last_selected
)
1253 g_free (filesel
->last_selected
);
1254 filesel
->last_selected
= NULL
;
1257 GTK_OBJECT_CLASS (parent_class
)->destroy (object
);
1261 gtk_dir_selection_map (GtkWidget
*widget
)
1263 GtkDirSelection
*filesel
= GTK_DIR_SELECTION (widget
);
1265 /* Refresh the contents */
1266 gtk_dir_selection_populate (filesel
, "", FALSE
, FALSE
);
1268 GTK_WIDGET_CLASS (parent_class
)->map (widget
);
1272 gtk_dir_selection_finalize (GObject
*object
)
1274 GtkDirSelection
*filesel
= GTK_DIR_SELECTION (object
);
1276 g_free (filesel
->fileop_file
);
1278 G_OBJECT_CLASS (parent_class
)->finalize (object
);
1281 /* Begin file operations callbacks */
1284 gtk_dir_selection_fileop_error (GtkDirSelection
*fs
,
1285 gchar
*error_message
)
1289 g_return_if_fail (error_message
!= NULL
);
1292 dialog
= gtk_message_dialog_new (GTK_WINDOW (fs
),
1293 GTK_DIALOG_DESTROY_WITH_PARENT
,
1296 "%s", error_message
);
1298 /* yes, we free it */
1299 g_free (error_message
);
1301 gtk_window_set_modal (GTK_WINDOW (dialog
), TRUE
);
1303 g_signal_connect_swapped (dialog
, "response",
1304 G_CALLBACK (gtk_widget_destroy
),
1307 gtk_widget_show (dialog
);
1311 gtk_dir_selection_fileop_destroy (GtkWidget
*widget
,
1314 GtkDirSelection
*fs
= data
;
1316 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1318 fs
->fileop_dialog
= NULL
;
1322 entry_is_empty (GtkEntry
*entry
)
1324 const gchar
*text
= gtk_entry_get_text (entry
);
1326 return *text
== '\0';
1330 gtk_dir_selection_fileop_entry_changed (GtkEntry
*entry
,
1333 gtk_widget_set_sensitive (button
, !entry_is_empty (entry
));
1337 gtk_dir_selection_create_dir_confirmed (GtkWidget
*widget
,
1340 GtkDirSelection
*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_DIR_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_dir_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_dir_selection_fileop_error (fs
, buf
);
1378 g_free (sys_full_path
);
1380 gtk_widget_destroy (fs
->fileop_dialog
);
1381 gtk_dir_selection_populate (fs
, "", FALSE
, FALSE
);
1385 gtk_dir_selection_create_dir (GtkWidget
*widget
,
1388 GtkDirSelection
*fs
= data
;
1394 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1396 if (fs
->fileop_dialog
)
1400 dialog
= gtk_dialog_new ();
1401 fs
->fileop_dialog
= dialog
;
1402 g_signal_connect (dialog
, "destroy",
1403 G_CALLBACK (gtk_dir_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 g_signal_connect_swapped (button
, "clicked",
1436 G_CALLBACK (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_mnemonic (_("C_reate"));
1447 gtk_widget_set_sensitive (button
, FALSE
);
1448 g_signal_connect (button
, "clicked",
1449 G_CALLBACK (gtk_dir_selection_create_dir_confirmed
),
1451 g_signal_connect (fs
->fileop_entry
, "changed",
1452 G_CALLBACK (gtk_dir_selection_fileop_entry_changed
),
1455 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->action_area
),
1456 button
, TRUE
, TRUE
, 0);
1457 GTK_WIDGET_SET_FLAGS (button
, GTK_CAN_DEFAULT
);
1458 gtk_widget_show (button
);
1460 gtk_widget_show (dialog
);
1464 gtk_dir_selection_delete_dir_response (GtkDialog
*dialog
,
1468 GtkDirSelection
*fs
= data
;
1469 CompletionState
*cmpl_state
;
1472 gchar
*sys_full_path
;
1473 GError
*error
= NULL
;
1476 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1478 if (response_id
!= GTK_RESPONSE_OK
)
1480 gtk_widget_destroy (GTK_WIDGET (dialog
));
1484 cmpl_state
= (CompletionState
*) fs
->cmpl_state
;
1485 path
= cmpl_reference_position (cmpl_state
);
1487 full_path
= g_strconcat (path
, G_DIR_SEPARATOR_S
, fs
->fileop_file
, NULL
);
1488 sys_full_path
= g_filename_from_utf8 (full_path
, -1, NULL
, NULL
, &error
);
1491 if (g_error_matches (error
, G_CONVERT_ERROR
, G_CONVERT_ERROR_ILLEGAL_SEQUENCE
))
1492 buf
= g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"),
1495 buf
= g_strdup_printf (_("Error deleting file \"%s\": %s\n%s"),
1496 fs
->fileop_file
, error
->message
,
1497 _("It probably contains symbols not allowed in filenames."));
1499 gtk_dir_selection_fileop_error (fs
, buf
);
1500 g_error_free (error
);
1504 if (unlink (sys_full_path
) < 0)
1506 buf
= g_strdup_printf (_("Error deleting file \"%s\": %s"),
1507 fs
->fileop_file
, g_strerror (errno
));
1508 gtk_dir_selection_fileop_error (fs
, buf
);
1513 g_free (sys_full_path
);
1515 gtk_widget_destroy (fs
->fileop_dialog
);
1516 gtk_dir_selection_populate (fs
, "", FALSE
, TRUE
);
1520 gtk_dir_selection_delete_file (GtkWidget
*widget
,
1523 GtkDirSelection
*fs
= data
;
1525 const gchar
*filename
;
1527 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1529 if (fs
->fileop_dialog
)
1532 #ifdef G_WITH_CYGWIN
1533 translate_win32_path (fs
);
1536 filename
= gtk_entry_get_text (GTK_ENTRY (fs
->selection_entry
));
1537 if (strlen (filename
) < 1)
1540 g_free (fs
->fileop_file
);
1541 fs
->fileop_file
= g_strdup (filename
);
1544 fs
->fileop_dialog
= dialog
=
1545 gtk_message_dialog_new (GTK_WINDOW (fs
),
1546 GTK_WINDOW (fs
)->modal
? GTK_DIALOG_MODAL
: 0,
1547 GTK_MESSAGE_QUESTION
,
1549 _("Really delete file \"%s\" ?"), filename
);
1551 g_signal_connect (dialog
, "destroy",
1552 G_CALLBACK (gtk_dir_selection_fileop_destroy
),
1554 gtk_window_set_title (GTK_WINDOW (dialog
), _("Delete File"));
1555 gtk_window_set_position (GTK_WINDOW (dialog
), GTK_WIN_POS_MOUSE
);
1558 gtk_dialog_add_buttons (GTK_DIALOG (dialog
),
1559 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1560 GTK_STOCK_DELETE
, GTK_RESPONSE_OK
,
1563 gtk_dialog_set_default_response (GTK_DIALOG (dialog
), GTK_RESPONSE_CANCEL
);
1565 g_signal_connect (dialog
, "response",
1566 G_CALLBACK (gtk_dir_selection_delete_dir_response
),
1569 gtk_widget_show (dialog
);
1573 gtk_dir_selection_rename_dir_confirmed (GtkWidget
*widget
,
1576 GtkDirSelection
*fs
= data
;
1580 gchar
*new_filename
;
1581 gchar
*old_filename
;
1582 gchar
*sys_new_filename
;
1583 gchar
*sys_old_filename
;
1584 CompletionState
*cmpl_state
;
1585 GError
*error
= NULL
;
1587 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1589 file
= gtk_entry_get_text (GTK_ENTRY (fs
->fileop_entry
));
1590 cmpl_state
= (CompletionState
*) fs
->cmpl_state
;
1591 path
= cmpl_reference_position (cmpl_state
);
1593 new_filename
= g_strconcat (path
, G_DIR_SEPARATOR_S
, file
, NULL
);
1594 old_filename
= g_strconcat (path
, G_DIR_SEPARATOR_S
, fs
->fileop_file
, NULL
);
1596 sys_new_filename
= g_filename_from_utf8 (new_filename
, -1, NULL
, NULL
, &error
);
1599 if (g_error_matches (error
, G_CONVERT_ERROR
, G_CONVERT_ERROR_ILLEGAL_SEQUENCE
))
1600 buf
= g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), new_filename
);
1602 buf
= g_strdup_printf (_("Error renaming file to \"%s\": %s\n%s"),
1603 new_filename
, error
->message
,
1604 _("You probably used symbols not allowed in filenames."));
1605 gtk_dir_selection_fileop_error (fs
, buf
);
1606 g_error_free (error
);
1610 sys_old_filename
= g_filename_from_utf8 (old_filename
, -1, NULL
, NULL
, &error
);
1613 if (g_error_matches (error
, G_CONVERT_ERROR
, G_CONVERT_ERROR_ILLEGAL_SEQUENCE
))
1614 buf
= g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), old_filename
);
1616 buf
= g_strdup_printf (_("Error renaming file \"%s\": %s\n%s"),
1617 old_filename
, error
->message
,
1618 _("It probably contains symbols not allowed in filenames."));
1619 gtk_dir_selection_fileop_error (fs
, buf
);
1620 g_error_free (error
);
1624 if (rename (sys_old_filename
, sys_new_filename
) < 0)
1626 buf
= g_strdup_printf (_("Error renaming file \"%s\" to \"%s\": %s"),
1627 sys_old_filename
, sys_new_filename
,
1628 g_strerror (errno
));
1629 gtk_dir_selection_fileop_error (fs
, buf
);
1633 gtk_dir_selection_populate (fs
, "", FALSE
, FALSE
);
1634 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), file
);
1637 g_free (sys_old_filename
);
1640 g_free (new_filename
);
1641 g_free (old_filename
);
1642 g_free (sys_new_filename
);
1644 gtk_widget_destroy (fs
->fileop_dialog
);
1648 gtk_dir_selection_rename_file (GtkWidget
*widget
,
1651 GtkDirSelection
*fs
= data
;
1658 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1660 if (fs
->fileop_dialog
)
1663 g_free (fs
->fileop_file
);
1664 fs
->fileop_file
= g_strdup (gtk_entry_get_text (GTK_ENTRY (fs
->selection_entry
)));
1665 if (strlen (fs
->fileop_file
) < 1)
1669 fs
->fileop_dialog
= dialog
= gtk_dialog_new ();
1670 g_signal_connect (dialog
, "destroy",
1671 G_CALLBACK (gtk_dir_selection_fileop_destroy
),
1673 gtk_window_set_title (GTK_WINDOW (dialog
), _("Rename File"));
1674 gtk_window_set_position (GTK_WINDOW (dialog
), GTK_WIN_POS_MOUSE
);
1675 gtk_window_set_transient_for (GTK_WINDOW (dialog
), GTK_WINDOW (fs
));
1677 /* If file dialog is grabbed, grab option dialog */
1678 /* When option dialog closed, file dialog will be grabbed again */
1679 if (GTK_WINDOW (fs
)->modal
)
1680 gtk_window_set_modal (GTK_WINDOW (dialog
), TRUE
);
1682 vbox
= gtk_vbox_new (FALSE
, 0);
1683 gtk_container_set_border_width (GTK_CONTAINER (vbox
), 8);
1684 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), vbox
,
1686 gtk_widget_show(vbox
);
1688 buf
= g_strdup_printf (_("Rename file \"%s\" to:"), fs
->fileop_file
);
1689 label
= gtk_label_new (buf
);
1690 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.0);
1691 gtk_box_pack_start (GTK_BOX (vbox
), label
, FALSE
, FALSE
, 5);
1692 gtk_widget_show (label
);
1695 /* New filename entry */
1696 fs
->fileop_entry
= gtk_entry_new ();
1697 gtk_box_pack_start (GTK_BOX (vbox
), fs
->fileop_entry
,
1699 GTK_WIDGET_SET_FLAGS (fs
->fileop_entry
, GTK_CAN_DEFAULT
);
1700 gtk_widget_show (fs
->fileop_entry
);
1702 gtk_entry_set_text (GTK_ENTRY (fs
->fileop_entry
), fs
->fileop_file
);
1703 gtk_editable_select_region (GTK_EDITABLE (fs
->fileop_entry
),
1704 0, strlen (fs
->fileop_file
));
1707 button
= gtk_button_new_from_stock (GTK_STOCK_CANCEL
);
1708 g_signal_connect_swapped (button
, "clicked",
1709 G_CALLBACK (gtk_widget_destroy
),
1711 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->action_area
),
1712 button
, TRUE
, TRUE
, 0);
1713 GTK_WIDGET_SET_FLAGS (button
, GTK_CAN_DEFAULT
);
1714 gtk_widget_grab_default (button
);
1715 gtk_widget_show (button
);
1717 gtk_widget_grab_focus (fs
->fileop_entry
);
1719 button
= gtk_button_new_with_mnemonic (_("_Rename"));
1720 g_signal_connect (button
, "clicked",
1721 G_CALLBACK (gtk_dir_selection_rename_dir_confirmed
),
1723 g_signal_connect (fs
->fileop_entry
, "changed",
1724 G_CALLBACK (gtk_dir_selection_fileop_entry_changed
),
1727 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->action_area
),
1728 button
, TRUE
, TRUE
, 0);
1729 GTK_WIDGET_SET_FLAGS (button
, GTK_CAN_DEFAULT
);
1730 gtk_widget_show (button
);
1732 gtk_widget_show (dialog
);
1736 gtk_dir_selection_insert_text (GtkWidget
*widget
,
1737 const gchar
*new_text
,
1738 gint new_text_length
,
1744 filename
= g_filename_from_utf8 (new_text
, new_text_length
, NULL
, NULL
, NULL
);
1748 gdk_display_beep (gtk_widget_get_display (widget
));
1749 g_signal_stop_emission_by_name (widget
, "insert_text");
1759 gtk_dir_selection_update_fileops (GtkDirSelection
*fs
)
1763 if (!fs
->selection_entry
)
1766 sensitive
= !entry_is_empty (GTK_ENTRY (fs
->selection_entry
));
1768 if (fs
->fileop_del_file
)
1769 gtk_widget_set_sensitive (fs
->fileop_del_file
, sensitive
);
1771 if (fs
->fileop_ren_file
)
1772 gtk_widget_set_sensitive (fs
->fileop_ren_file
, sensitive
);
1776 gtk_dir_selection_key_press (GtkWidget
*widget
,
1780 GtkDirSelection
*fs
;
1783 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1784 g_return_val_if_fail (event
!= NULL
, FALSE
);
1786 if ((event
->keyval
== GDK_Tab
|| event
->keyval
== GDK_KP_Tab
) &&
1787 (event
->state
& gtk_accelerator_get_default_mod_mask ()) == 0)
1789 fs
= GTK_DIR_SELECTION (user_data
);
1790 #ifdef G_WITH_CYGWIN
1791 translate_win32_path (fs
);
1793 text
= g_strdup (gtk_entry_get_text (GTK_ENTRY (fs
->selection_entry
)));
1795 gtk_dir_selection_populate (fs
, text
, TRUE
, TRUE
);
1806 gtk_dir_selection_history_callback (GtkWidget
*widget
,
1809 GtkDirSelection
*fs
= data
;
1810 HistoryCallbackArg
*callback_arg
;
1813 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1815 list
= fs
->history_list
;
1818 callback_arg
= list
->data
;
1820 if (callback_arg
->menu_item
== widget
)
1822 gtk_dir_selection_populate (fs
, callback_arg
->directory
, FALSE
, FALSE
);
1831 gtk_dir_selection_update_history_menu (GtkDirSelection
*fs
,
1832 gchar
*current_directory
)
1834 HistoryCallbackArg
*callback_arg
;
1835 GtkWidget
*menu_item
;
1841 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1842 g_return_if_fail (current_directory
!= NULL
);
1844 list
= fs
->history_list
;
1846 if (fs
->history_menu
)
1849 callback_arg
= list
->data
;
1850 g_free (callback_arg
->directory
);
1851 g_free (callback_arg
);
1854 g_list_free (fs
->history_list
);
1855 fs
->history_list
= NULL
;
1857 gtk_widget_destroy (fs
->history_menu
);
1860 fs
->history_menu
= gtk_menu_new ();
1862 current_dir
= g_strdup (current_directory
);
1864 dir_len
= strlen (current_dir
);
1866 for (i
= dir_len
; i
>= 0; i
--)
1868 /* the i == dir_len is to catch the full path for the first
1870 if ( (current_dir
[i
] == G_DIR_SEPARATOR
) || (i
== dir_len
))
1872 /* another small hack to catch the full path */
1874 current_dir
[i
+ 1] = '\0';
1875 #ifdef G_WITH_CYGWIN
1876 if (!strcmp (current_dir
, "//"))
1879 menu_item
= gtk_menu_item_new_with_label (current_dir
);
1881 callback_arg
= g_new (HistoryCallbackArg
, 1);
1882 callback_arg
->menu_item
= menu_item
;
1884 /* since the autocompletion gets confused if you don't
1885 * supply a trailing '/' on a dir entry, set the full
1886 * (current) path to "" which just refreshes the filesel */
1889 callback_arg
->directory
= g_strdup ("");
1893 callback_arg
->directory
= g_strdup (current_dir
);
1896 fs
->history_list
= g_list_append (fs
->history_list
, callback_arg
);
1898 g_signal_connect (menu_item
, "activate",
1899 G_CALLBACK (gtk_dir_selection_history_callback
),
1901 gtk_menu_shell_append (GTK_MENU_SHELL (fs
->history_menu
), menu_item
);
1902 gtk_widget_show (menu_item
);
1906 gtk_option_menu_set_menu (GTK_OPTION_MENU (fs
->history_pulldown
),
1908 g_free (current_dir
);
1912 get_real_filename (gchar
*filename
,
1915 #ifdef G_WITH_CYGWIN
1916 /* Check to see if the selection was a drive selector */
1917 if (isalpha (filename
[0]) && (filename
[1] == ':'))
1919 gchar temp_filename
[MAX_PATH
];
1922 cygwin_conv_to_posix_path (filename
, temp_filename
);
1924 /* we need trailing '/'. */
1925 len
= strlen (temp_filename
);
1926 if (len
> 0 && temp_filename
[len
-1] != '/')
1928 temp_filename
[len
] = '/';
1929 temp_filename
[len
+1] = '\0';
1935 return g_strdup (temp_filename
);
1937 #endif /* G_WITH_CYGWIN */
1942 gtk_dir_selection_file_activate (GtkTreeView
*tree_view
,
1944 GtkTreeViewColumn
*column
,
1947 GtkDirSelection
*fs
= GTK_DIR_SELECTION (user_data
);
1948 GtkTreeModel
*model
= gtk_tree_view_get_model (tree_view
);
1952 gtk_tree_model_get_iter (model
, &iter
, path
);
1953 gtk_tree_model_get (model
, &iter
, FILE_COLUMN
, &filename
, -1);
1954 filename
= get_real_filename (filename
, TRUE
);
1955 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), filename
);
1956 gtk_button_clicked (GTK_BUTTON (fs
->ok_button
));
1962 gtk_dir_selection_dir_activate (GtkTreeView
*tree_view
,
1964 GtkTreeViewColumn
*column
,
1967 GtkDirSelection
*fs
= GTK_DIR_SELECTION (user_data
);
1968 GtkTreeModel
*model
= gtk_tree_view_get_model (tree_view
);
1972 gtk_tree_model_get_iter (model
, &iter
, path
);
1973 gtk_tree_model_get (model
, &iter
, DIR_COLUMN
, &filename
, -1);
1974 filename
= get_real_filename (filename
, TRUE
);
1975 gtk_dir_selection_populate (fs
, filename
, FALSE
, FALSE
);
1979 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
1982 win32_gtk_add_drives_to_dir_list (GtkListStore
*model
)
1986 char formatBuffer
[128];
1989 /* Get the drives string */
1990 GetLogicalDriveStrings (sizeof (buffer
), buffer
);
1992 /* Add the drives as necessary */
1994 while (*textPtr
!= '\0')
1996 /* Ignore floppies (?) */
1997 if ((tolower (textPtr
[0]) != 'a') && (tolower (textPtr
[0]) != 'b'))
1999 /* Build the actual displayable string */
2000 g_snprintf (formatBuffer
, sizeof (formatBuffer
), "%c:\\", toupper (textPtr
[0]));
2002 /* Add to the list */
2003 gtk_list_store_append (model
, &iter
);
2004 gtk_list_store_set (model
, &iter
, DIR_COLUMN
, formatBuffer
, -1);
2006 textPtr
+= (strlen (textPtr
) + 1);
2012 escape_underscores (const gchar
*str
)
2014 GString
*result
= g_string_new (NULL
);
2018 g_string_append_c (result
, '_');
2020 g_string_append_c (result
, *str
);
2024 return g_string_free (result
, FALSE
);
2028 gtk_dir_selection_populate (GtkDirSelection
*fs
,
2030 gboolean try_complete
,
2031 gboolean reset_entry
)
2033 CompletionState
*cmpl_state
;
2034 PossibleCompletion
* poss
;
2036 GtkListStore
*dir_model
;
2037 GtkListStore
*file_model
;
2039 gchar
* rem_path
= rel_path
;
2041 gint did_recurse
= FALSE
;
2042 gint possible_count
= 0;
2043 gint selection_index
= -1;
2045 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
2047 cmpl_state
= (CompletionState
*) fs
->cmpl_state
;
2048 poss
= cmpl_completion_matches (rel_path
, &rem_path
, cmpl_state
);
2050 if (!cmpl_state_okay (cmpl_state
))
2052 /* Something went wrong. */
2053 gtk_dir_selection_abort (fs
);
2057 g_assert (cmpl_state
->reference_dir
);
2059 dir_model
= GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs
->dir_list
)));
2060 file_model
= GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs
->file_list
)));
2062 gtk_list_store_clear (dir_model
);
2063 gtk_list_store_clear (file_model
);
2065 /* Set the dir list to include ./ and ../ */
2066 gtk_list_store_append (dir_model
, &iter
);
2067 gtk_list_store_set (dir_model
, &iter
, DIR_COLUMN
, "." G_DIR_SEPARATOR_S
, -1);
2068 gtk_list_store_append (dir_model
, &iter
);
2069 gtk_list_store_set (dir_model
, &iter
, DIR_COLUMN
, ".." G_DIR_SEPARATOR_S
, -1);
2073 if (cmpl_is_a_completion (poss
))
2075 possible_count
+= 1;
2077 filename
= cmpl_this_completion (poss
);
2079 if (cmpl_is_directory (poss
))
2081 if (strcmp (filename
, "." G_DIR_SEPARATOR_S
) != 0 &&
2082 strcmp (filename
, ".." G_DIR_SEPARATOR_S
) != 0)
2084 gtk_list_store_append (dir_model
, &iter
);
2085 gtk_list_store_set (dir_model
, &iter
, DIR_COLUMN
, filename
, -1);
2090 gtk_list_store_append (file_model
, &iter
);
2091 gtk_list_store_set (file_model
, &iter
, DIR_COLUMN
, filename
, -1);
2095 poss
= cmpl_next_completion (cmpl_state
);
2098 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
2099 /* For Windows, add drives as potential selections */
2100 win32_gtk_add_drives_to_dir_list (dir_model
);
2103 /* File lists are set. */
2105 g_assert (cmpl_state
->reference_dir
);
2110 /* User is trying to complete filenames, so advance the user's input
2111 * string to the updated_text, which is the common leading substring
2112 * of all possible completions, and if its a directory attempt
2113 * attempt completions in it. */
2115 if (cmpl_updated_text (cmpl_state
)[0])
2118 if (cmpl_updated_dir (cmpl_state
))
2120 gchar
* dir_name
= g_strdup (cmpl_updated_text (cmpl_state
));
2124 gtk_dir_selection_populate (fs
, dir_name
, TRUE
, TRUE
);
2130 if (fs
->selection_entry
)
2131 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
),
2132 cmpl_updated_text (cmpl_state
));
2137 selection_index
= cmpl_last_valid_char (cmpl_state
) -
2138 (strlen (rel_path
) - strlen (rem_path
));
2139 if (fs
->selection_entry
)
2140 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), rem_path
);
2143 else if (reset_entry
)
2145 if (fs
->selection_entry
)
2146 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), "");
2151 if (fs
->selection_entry
)
2152 gtk_editable_set_position (GTK_EDITABLE (fs
->selection_entry
),
2155 if (fs
->selection_entry
)
2157 char *escaped
= escape_underscores (cmpl_reference_position (cmpl_state
));
2158 sel_text
= g_strconcat (_("_Selection: "), escaped
, NULL
);
2161 gtk_label_set_text_with_mnemonic (GTK_LABEL (fs
->selection_text
), sel_text
);
2162 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), cmpl_reference_position (cmpl_state
));
2166 if (fs
->history_pulldown
)
2168 gtk_dir_selection_update_history_menu (fs
, cmpl_reference_position (cmpl_state
));
2175 gtk_dir_selection_abort (GtkDirSelection
*fs
)
2179 g_snprintf (err_buf
, sizeof (err_buf
), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno
));
2181 /* BEEP gdk_beep(); */
2183 if (fs
->selection_entry
)
2184 gtk_label_set_text (GTK_LABEL (fs
->selection_text
), err_buf
);
2188 * gtk_dir_selection_set_select_multiple:
2189 * @filesel: a #GtkDirSelection
2190 * @select_multiple: whether or not the user is allowed to select multiple
2191 * files in the file list.
2193 * Sets whether the user is allowed to select multiple files in the file list.
2194 * Use gtk_dir_selection_get_selections () to get the list of selected files.
2197 gtk_dir_selection_set_select_multiple (GtkDirSelection
*filesel
,
2198 gboolean select_multiple
)
2200 GtkTreeSelection
*sel
;
2201 GtkSelectionMode mode
;
2203 g_return_if_fail (GTK_IS_DIR_SELECTION (filesel
));
2205 sel
= gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel
->file_list
));
2207 mode
= select_multiple
? GTK_SELECTION_MULTIPLE
: GTK_SELECTION_SINGLE
;
2209 if (mode
!= gtk_tree_selection_get_mode (sel
))
2211 gtk_tree_selection_set_mode (sel
, mode
);
2213 g_object_notify (G_OBJECT (filesel
), "select-multiple");
2218 * gtk_dir_selection_get_select_multiple:
2219 * @filesel: a #GtkDirSelection
2221 * Determines whether or not the user is allowed to select multiple files in
2222 * the file list. See gtk_dir_selection_set_select_multiple().
2224 * Return value: %TRUE if the user is allowed to select multiple files in the
2228 gtk_dir_selection_get_select_multiple (GtkDirSelection
*filesel
)
2230 GtkTreeSelection
*sel
;
2232 g_return_val_if_fail (GTK_IS_DIR_SELECTION (filesel
), FALSE
);
2234 sel
= gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel
->file_list
));
2235 return (gtk_tree_selection_get_mode (sel
) == GTK_SELECTION_MULTIPLE
);
2239 multiple_changed_foreach (GtkTreeModel
*model
,
2244 GPtrArray
*names
= data
;
2247 gtk_tree_model_get (model
, iter
, FILE_COLUMN
, &filename
, -1);
2249 g_ptr_array_add (names
, filename
);
2253 free_selected_names (GPtrArray
*names
)
2257 for (i
= 0; i
< names
->len
; i
++)
2258 g_free (g_ptr_array_index (names
, i
));
2260 g_ptr_array_free (names
, TRUE
);
2264 gtk_dir_selection_file_changed (GtkTreeSelection
*selection
,
2267 GtkDirSelection
*fs
= GTK_DIR_SELECTION (user_data
);
2268 GPtrArray
*new_names
;
2273 new_names
= g_ptr_array_sized_new (8);
2275 gtk_tree_selection_selected_foreach (selection
,
2276 multiple_changed_foreach
,
2279 /* nothing selected */
2280 if (new_names
->len
== 0)
2282 g_ptr_array_free (new_names
, TRUE
);
2284 if (fs
->selected_names
!= NULL
)
2286 free_selected_names (fs
->selected_names
);
2287 fs
->selected_names
= NULL
;
2290 goto maybe_clear_entry
;
2293 if (new_names
->len
!= 1)
2295 GPtrArray
*old_names
= fs
->selected_names
;
2297 if (old_names
!= NULL
)
2299 /* A common case is selecting a range of files from top to bottom,
2300 * so quickly check for that to avoid looping over the entire list
2302 if (compare_filenames (g_ptr_array_index (old_names
, old_names
->len
- 1),
2303 g_ptr_array_index (new_names
, new_names
->len
- 1)) != 0)
2304 index
= new_names
->len
- 1;
2307 gint i
= 0, j
= 0, cmp
;
2309 /* do a quick diff, stopping at the first file not in the
2312 while (i
< old_names
->len
&& j
< new_names
->len
)
2314 cmp
= compare_filenames (g_ptr_array_index (old_names
, i
),
2315 g_ptr_array_index (new_names
, j
));
2332 /* we ran off the end of the old list */
2333 if (index
== -1 && i
< new_names
->len
)
2339 /* A phantom anchor still exists at the point where the last item
2340 * was selected, which is used for subsequent range selections.
2341 * So search up from there.
2343 if (fs
->last_selected
&&
2344 compare_filenames (fs
->last_selected
,
2345 g_ptr_array_index (new_names
, 0)) == 0)
2346 index
= new_names
->len
- 1;
2354 if (fs
->selected_names
!= NULL
)
2355 free_selected_names (fs
->selected_names
);
2357 fs
->selected_names
= new_names
;
2361 if (fs
->last_selected
!= NULL
)
2362 g_free (fs
->last_selected
);
2364 fs
->last_selected
= g_strdup (g_ptr_array_index (new_names
, index
));
2365 filename
= get_real_filename (fs
->last_selected
, FALSE
);
2367 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), filename
);
2369 if (filename
!= fs
->last_selected
)
2377 entry
= gtk_entry_get_text (GTK_ENTRY (fs
->selection_entry
));
2378 if ((entry
!= NULL
) && (fs
->last_selected
!= NULL
) &&
2379 (compare_filenames (entry
, fs
->last_selected
) == 0))
2380 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), "");
2384 gtk_dir_selection_dir_changed (GtkTreeSelection
*selection
,
2387 GtkDirSelection
*fs
= GTK_DIR_SELECTION (user_data
);
2388 GPtrArray
*new_names
;
2393 new_names
= g_ptr_array_sized_new (8);
2395 gtk_tree_selection_selected_foreach (selection
,
2396 multiple_changed_foreach
,
2399 /* nothing selected */
2400 if (new_names
->len
== 0)
2402 g_ptr_array_free (new_names
, TRUE
);
2404 if (fs
->selected_names
!= NULL
)
2406 free_selected_names (fs
->selected_names
);
2407 fs
->selected_names
= NULL
;
2410 goto maybe_clear_entry
;
2413 if (new_names
->len
!= 1)
2415 GPtrArray
*old_names
= fs
->selected_names
;
2417 if (old_names
!= NULL
)
2419 /* A common case is selecting a range of files from top to bottom,
2420 * so quickly check for that to avoid looping over the entire list
2422 if (compare_filenames (g_ptr_array_index (old_names
, old_names
->len
- 1),
2423 g_ptr_array_index (new_names
, new_names
->len
- 1)) != 0)
2424 index
= new_names
->len
- 1;
2427 gint i
= 0, j
= 0, cmp
;
2429 /* do a quick diff, stopping at the first file not in the
2432 while (i
< old_names
->len
&& j
< new_names
->len
)
2434 cmp
= compare_filenames (g_ptr_array_index (old_names
, i
),
2435 g_ptr_array_index (new_names
, j
));
2452 /* we ran off the end of the old list */
2453 if (index
== -1 && i
< new_names
->len
)
2459 /* A phantom anchor still exists at the point where the last item
2460 * was selected, which is used for subsequent range selections.
2461 * So search up from there.
2463 if (fs
->last_selected
&&
2464 compare_filenames (fs
->last_selected
,
2465 g_ptr_array_index (new_names
, 0)) == 0)
2466 index
= new_names
->len
- 1;
2474 if (fs
->selected_names
!= NULL
)
2475 free_selected_names (fs
->selected_names
);
2477 fs
->selected_names
= new_names
;
2483 err
= gtk_label_get_text (GTK_LABEL (fs
->selection_text
));
2484 err
+= 11; //pass over "Selection: "
2485 sprintf(str
,"%s\0",err
);
2488 if (fs
->last_selected
!= NULL
)
2489 g_free (fs
->last_selected
);
2491 fs
->last_selected
= g_strdup (g_ptr_array_index (new_names
, index
));
2492 filename
= get_real_filename (fs
->last_selected
, FALSE
);
2495 strcat(str
,filename
);
2496 str
[strlen(str
)-1] = '\0';
2498 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), str
);
2500 if (filename
!= fs
->last_selected
)
2508 entry
= gtk_entry_get_text (GTK_ENTRY (fs
->selection_entry
));
2509 if ((entry
!= NULL
) && (fs
->last_selected
!= NULL
) &&
2510 (compare_filenames (entry
, fs
->last_selected
) == 0))
2511 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), "");
2515 * gtk_dir_selection_get_selections:
2516 * @filesel: a #GtkDirSelection
2518 * Retrieves the list of file selections the user has made in the dialog box.
2519 * This function is intended for use when the user can select multiple files
2520 * in the file list. The first file in the list is equivalent to what
2521 * gtk_dir_selection_get_filename() would return.
2523 * The filenames are in the encoding of g_filename_from_utf8(), which may or
2524 * may not be the same as that used by GTK+ (UTF-8). To convert to UTF-8, call
2525 * g_filename_to_utf8() on each string.
2527 * Return value: a newly-allocated %NULL-terminated array of strings. Use
2528 * g_strfreev() to free it.
2531 gtk_dir_selection_get_selections (GtkDirSelection
*filesel
)
2535 gchar
*filename
, *dirname
;
2536 gchar
*current
, *buf
;
2538 gboolean unselected_entry
;
2540 g_return_val_if_fail (GTK_IS_DIR_SELECTION (filesel
), NULL
);
2542 filename
= g_strdup (gtk_dir_selection_get_filename (filesel
));
2544 if (strlen (filename
) == 0)
2550 names
= filesel
->selected_names
;
2553 selections
= g_new (gchar
*, names
->len
+ 2);
2555 selections
= g_new (gchar
*, 2);
2558 unselected_entry
= TRUE
;
2562 dirname
= g_path_get_dirname (filename
);
2564 for (i
= 0; i
< names
->len
; i
++)
2566 buf
= g_filename_from_utf8 (g_ptr_array_index (names
, i
), -1,
2568 current
= g_build_filename (dirname
, buf
, NULL
);
2571 selections
[count
++] = current
;
2573 if (unselected_entry
&& compare_filenames (current
, filename
) == 0)
2574 unselected_entry
= FALSE
;
2580 if (unselected_entry
)
2581 selections
[count
++] = filename
;
2585 selections
[count
] = NULL
;
2590 /**********************************************************************/
2591 /* External Interface */
2592 /**********************************************************************/
2594 /* The four completion state selectors
2597 cmpl_updated_text (CompletionState
*cmpl_state
)
2599 return cmpl_state
->updated_text
;
2603 cmpl_updated_dir (CompletionState
*cmpl_state
)
2605 return cmpl_state
->re_complete
;
2609 cmpl_reference_position (CompletionState
*cmpl_state
)
2611 return cmpl_state
->reference_dir
->fullname
;
2615 cmpl_last_valid_char (CompletionState
*cmpl_state
)
2617 return cmpl_state
->last_valid_char
;
2621 cmpl_completion_fullname (const gchar
*text
,
2622 CompletionState
*cmpl_state
)
2624 static const char nothing
[2] = "";
2626 if (!cmpl_state_okay (cmpl_state
))
2630 else if (g_path_is_absolute (text
))
2632 strcpy (cmpl_state
->updated_text
, text
);
2635 else if (text
[0] == '~')
2640 dir
= open_user_dir (text
, cmpl_state
);
2644 /* spencer says just return ~something, so
2645 * for now just do it. */
2646 strcpy (cmpl_state
->updated_text
, text
);
2651 strcpy (cmpl_state
->updated_text
, dir
->fullname
);
2653 slash
= strchr (text
, G_DIR_SEPARATOR
);
2656 strcat (cmpl_state
->updated_text
, slash
);
2662 strcpy (cmpl_state
->updated_text
, cmpl_state
->reference_dir
->fullname
);
2663 if (cmpl_state
->updated_text
[strlen (cmpl_state
->updated_text
) - 1] != G_DIR_SEPARATOR
)
2664 strcat (cmpl_state
->updated_text
, G_DIR_SEPARATOR_S
);
2665 strcat (cmpl_state
->updated_text
, text
);
2668 return cmpl_state
->updated_text
;
2671 /* The three completion selectors
2674 cmpl_this_completion (PossibleCompletion
* pc
)
2680 cmpl_is_directory (PossibleCompletion
* pc
)
2682 return pc
->is_directory
;
2686 cmpl_is_a_completion (PossibleCompletion
* pc
)
2688 return pc
->is_a_completion
;
2691 /**********************************************************************/
2692 /* Construction, deletion */
2693 /**********************************************************************/
2695 static CompletionState
*
2696 cmpl_init_state (void)
2698 gchar
*sys_getcwd_buf
;
2700 CompletionState
*new_state
;
2702 new_state
= g_new (CompletionState
, 1);
2704 /* g_get_current_dir() returns a string in the "system" charset */
2705 sys_getcwd_buf
= g_get_current_dir ();
2706 utf8_cwd
= g_filename_to_utf8 (sys_getcwd_buf
, -1, NULL
, NULL
, NULL
);
2707 g_free (sys_getcwd_buf
);
2711 new_state
->reference_dir
= NULL
;
2712 new_state
->completion_dir
= NULL
;
2713 new_state
->active_completion_dir
= NULL
;
2714 new_state
->directory_storage
= NULL
;
2715 new_state
->directory_sent_storage
= NULL
;
2716 new_state
->last_valid_char
= 0;
2717 new_state
->updated_text
= g_new (gchar
, MAXPATHLEN
);
2718 new_state
->updated_text_alloc
= MAXPATHLEN
;
2719 new_state
->the_completion
.text
= g_new (gchar
, MAXPATHLEN
);
2720 new_state
->the_completion
.text_alloc
= MAXPATHLEN
;
2721 new_state
->user_dir_name_buffer
= NULL
;
2722 new_state
->user_directories
= NULL
;
2724 new_state
->reference_dir
= open_dir (utf8_cwd
, new_state
);
2726 if (!new_state
->reference_dir
)
2728 /* Directories changing from underneath us, grumble */
2729 strcpy (utf8_cwd
, G_DIR_SEPARATOR_S
);
2738 cmpl_free_dir_list (GList
* dp0
)
2744 free_dir (dp
->data
);
2752 cmpl_free_dir_sent_list (GList
* dp0
)
2758 free_dir_sent (dp
->data
);
2766 cmpl_free_state (CompletionState
* cmpl_state
)
2768 g_return_if_fail (cmpl_state
!= NULL
);
2770 cmpl_free_dir_list (cmpl_state
->directory_storage
);
2771 cmpl_free_dir_sent_list (cmpl_state
->directory_sent_storage
);
2773 if (cmpl_state
->user_dir_name_buffer
)
2774 g_free (cmpl_state
->user_dir_name_buffer
);
2775 if (cmpl_state
->user_directories
)
2776 g_free (cmpl_state
->user_directories
);
2777 if (cmpl_state
->the_completion
.text
)
2778 g_free (cmpl_state
->the_completion
.text
);
2779 if (cmpl_state
->updated_text
)
2780 g_free (cmpl_state
->updated_text
);
2782 g_free (cmpl_state
);
2786 free_dir (CompletionDir
* dir
)
2788 g_free (dir
->cmpl_text
);
2789 g_free (dir
->fullname
);
2794 free_dir_sent (CompletionDirSent
* sent
)
2797 for (i
= 0; i
< sent
->entry_count
; i
++)
2799 g_free (sent
->entries
[i
].entry_name
);
2800 g_free (sent
->entries
[i
].sort_key
);
2802 g_free (sent
->entries
);
2807 prune_memory_usage (CompletionState
*cmpl_state
)
2809 GList
* cdsl
= cmpl_state
->directory_sent_storage
;
2810 GList
* cdl
= cmpl_state
->directory_storage
;
2814 for (; cdsl
&& len
< CMPL_DIRECTORY_CACHE_SIZE
; len
+= 1)
2819 cmpl_free_dir_sent_list (cdsl
->next
);
2823 cmpl_state
->directory_storage
= NULL
;
2826 if (cdl
->data
== cmpl_state
->reference_dir
)
2827 cmpl_state
->directory_storage
= g_list_prepend (NULL
, cdl
->data
);
2829 free_dir (cdl
->data
);
2836 /**********************************************************************/
2837 /* The main entrances. */
2838 /**********************************************************************/
2840 static PossibleCompletion
*
2841 cmpl_completion_matches (gchar
*text_to_complete
,
2842 gchar
**remaining_text
,
2843 CompletionState
*cmpl_state
)
2846 PossibleCompletion
*poss
;
2848 prune_memory_usage (cmpl_state
);
2850 g_assert (text_to_complete
!= NULL
);
2852 cmpl_state
->user_completion_index
= -1;
2853 cmpl_state
->last_completion_text
= text_to_complete
;
2854 cmpl_state
->the_completion
.text
[0] = 0;
2855 cmpl_state
->last_valid_char
= 0;
2856 cmpl_state
->updated_text_len
= -1;
2857 cmpl_state
->updated_text
[0] = 0;
2858 cmpl_state
->re_complete
= FALSE
;
2861 first_slash
= strchr (text_to_complete
, G_DIR_SEPARATOR
);
2863 if (text_to_complete
[0] == '~' && !first_slash
)
2865 /* Text starts with ~ and there is no slash, show all the
2866 * home directory completions.
2868 poss
= attempt_homedir_completion (text_to_complete
, cmpl_state
);
2870 update_cmpl (poss
, cmpl_state
);
2875 cmpl_state
->reference_dir
=
2876 open_ref_dir (text_to_complete
, remaining_text
, cmpl_state
);
2878 if (!cmpl_state
->reference_dir
)
2881 cmpl_state
->completion_dir
=
2882 find_completion_dir (*remaining_text
, remaining_text
, cmpl_state
);
2884 cmpl_state
->last_valid_char
= *remaining_text
- text_to_complete
;
2886 if (!cmpl_state
->completion_dir
)
2889 cmpl_state
->completion_dir
->cmpl_index
= -1;
2890 cmpl_state
->completion_dir
->cmpl_parent
= NULL
;
2891 cmpl_state
->completion_dir
->cmpl_text
= g_strdup (*remaining_text
);
2893 cmpl_state
->active_completion_dir
= cmpl_state
->completion_dir
;
2895 cmpl_state
->reference_dir
= cmpl_state
->completion_dir
;
2897 poss
= attempt_dir_completion (cmpl_state
);
2899 update_cmpl (poss
, cmpl_state
);
2904 static PossibleCompletion
*
2905 cmpl_next_completion (CompletionState
* cmpl_state
)
2907 PossibleCompletion
* poss
= NULL
;
2909 cmpl_state
->the_completion
.text
[0] = 0;
2912 if (cmpl_state
->user_completion_index
>= 0)
2913 poss
= attempt_homedir_completion (cmpl_state
->last_completion_text
, cmpl_state
);
2915 poss
= attempt_dir_completion (cmpl_state
);
2917 poss
= attempt_dir_completion (cmpl_state
);
2920 update_cmpl (poss
, cmpl_state
);
2925 /**********************************************************************/
2926 /* Directory Operations */
2927 /**********************************************************************/
2929 /* Open the directory where completion will begin from, if possible. */
2930 static CompletionDir
*
2931 open_ref_dir (gchar
*text_to_complete
,
2932 gchar
**remaining_text
,
2933 CompletionState
*cmpl_state
)
2936 CompletionDir
*new_dir
;
2938 first_slash
= strchr (text_to_complete
, G_DIR_SEPARATOR
);
2940 #ifdef G_WITH_CYGWIN
2941 if (text_to_complete
[0] == '/' && text_to_complete
[1] == '/')
2944 g_snprintf (root_dir
, sizeof (root_dir
), "//%c", text_to_complete
[2]);
2946 new_dir
= open_dir (root_dir
, cmpl_state
);
2949 *remaining_text
= text_to_complete
+ 4;
2957 else if (text_to_complete
[0] == '~')
2959 new_dir
= open_user_dir (text_to_complete
, cmpl_state
);
2964 *remaining_text
= first_slash
+ 1;
2966 *remaining_text
= text_to_complete
+ strlen (text_to_complete
);
2974 else if (g_path_is_absolute (text_to_complete
) || !cmpl_state
->reference_dir
)
2976 gchar
*tmp
= g_strdup (text_to_complete
);
2980 while (*p
&& *p
!= '*' && *p
!= '?')
2984 p
= strrchr (tmp
, G_DIR_SEPARATOR
);
2992 new_dir
= open_dir (tmp
, cmpl_state
);
2995 *remaining_text
= text_to_complete
+
2996 ((p
== tmp
+ 1) ? (p
- tmp
) : (p
+ 1 - tmp
));
3000 /* If no possible candidates, use the cwd */
3001 gchar
*sys_curdir
= g_get_current_dir ();
3002 gchar
*utf8_curdir
= g_filename_to_utf8 (sys_curdir
, -1, NULL
, NULL
, NULL
);
3004 g_free (sys_curdir
);
3006 new_dir
= open_dir (utf8_curdir
, cmpl_state
);
3009 *remaining_text
= text_to_complete
;
3011 g_free (utf8_curdir
);
3018 *remaining_text
= text_to_complete
;
3020 new_dir
= open_dir (cmpl_state
->reference_dir
->fullname
, cmpl_state
);
3025 new_dir
->cmpl_index
= -1;
3026 new_dir
->cmpl_parent
= NULL
;
3034 /* open a directory by user name */
3035 static CompletionDir
*
3036 open_user_dir (const gchar
*text_to_complete
,
3037 CompletionState
*cmpl_state
)
3039 CompletionDir
*result
;
3043 g_assert (text_to_complete
&& text_to_complete
[0] == '~');
3045 first_slash
= strchr (text_to_complete
, G_DIR_SEPARATOR
);
3048 cmp_len
= first_slash
- text_to_complete
- 1;
3050 cmp_len
= strlen (text_to_complete
+ 1);
3055 const gchar
*homedir
= g_get_home_dir ();
3056 gchar
*utf8_homedir
= g_filename_to_utf8 (homedir
, -1, NULL
, NULL
, NULL
);
3059 result
= open_dir (utf8_homedir
, cmpl_state
);
3063 g_free (utf8_homedir
);
3068 gchar
* copy
= g_new (char, cmp_len
+ 1);
3072 strncpy (copy
, text_to_complete
+ 1, cmp_len
);
3074 pwd
= getpwnam (copy
);
3081 utf8_dir
= g_filename_to_utf8 (pwd
->pw_dir
, -1, NULL
, NULL
, NULL
);
3082 result
= open_dir (utf8_dir
, cmpl_state
);
3090 /* open a directory relative the the current relative directory */
3091 static CompletionDir
*
3092 open_relative_dir (gchar
*dir_name
,
3094 CompletionState
*cmpl_state
)
3096 CompletionDir
*result
;
3099 path
= g_string_sized_new (dir
->fullname_len
+ strlen (dir_name
) + 10);
3100 g_string_assign (path
, dir
->fullname
);
3102 if (dir
->fullname_len
> 1
3103 && path
->str
[dir
->fullname_len
- 1] != G_DIR_SEPARATOR
)
3104 g_string_append_c (path
, G_DIR_SEPARATOR
);
3105 g_string_append (path
, dir_name
);
3107 result
= open_dir (path
->str
, cmpl_state
);
3109 g_string_free (path
, TRUE
);
3114 /* after the cache lookup fails, really open a new directory */
3115 static CompletionDirSent
*
3116 open_new_dir (gchar
*dir_name
,
3118 gboolean stat_subdirs
)
3120 CompletionDirSent
*sent
;
3123 GError
*error
= NULL
;
3124 gint entry_count
= 0;
3127 struct stat ent_sbuf
;
3129 gchar
*sys_dir_name
;
3131 sent
= g_new (CompletionDirSent
, 1);
3132 sent
->mtime
= sbuf
->st_mtime
;
3133 sent
->inode
= sbuf
->st_ino
;
3134 sent
->device
= sbuf
->st_dev
;
3136 path
= g_string_sized_new (2*MAXPATHLEN
+ 10);
3138 sys_dir_name
= g_filename_from_utf8 (dir_name
, -1, NULL
, NULL
, NULL
);
3141 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3145 directory
= g_dir_open (sys_dir_name
, 0, &error
);
3148 cmpl_errno
= error
->code
; /* ??? */
3149 g_free (sys_dir_name
);
3153 while ((dirent
= g_dir_read_name (directory
)) != NULL
)
3156 entry_count
+= 2; /* For ".",".." */
3158 sent
->entries
= g_new (CompletionDirEntry
, entry_count
);
3159 sent
->entry_count
= entry_count
;
3161 g_dir_rewind (directory
);
3163 for (i
= 0; i
< entry_count
; i
+= 1)
3165 GError
*error
= NULL
;
3173 dirent
= g_dir_read_name (directory
);
3174 if (!dirent
) /* Directory changed */
3178 sent
->entries
[n_entries
].entry_name
= g_filename_to_utf8 (dirent
, -1, NULL
, NULL
, &error
);
3179 if (sent
->entries
[n_entries
].entry_name
== NULL
3180 || !g_utf8_validate (sent
->entries
[n_entries
].entry_name
, -1, NULL
))
3182 gchar
*escaped_str
= g_strescape (dirent
, NULL
);
3183 g_message (_("The filename \"%s\" couldn't be converted to UTF-8 "
3184 "(try setting the environment variable G_BROKEN_FILENAMES): %s"),
3186 error
->message
? error
->message
: _("Invalid Utf-8"));
3187 g_free (escaped_str
);
3188 g_clear_error (&error
);
3191 g_clear_error (&error
);
3193 sent
->entries
[n_entries
].sort_key
= g_utf8_collate_key (sent
->entries
[n_entries
].entry_name
, -1);
3195 g_string_assign (path
, sys_dir_name
);
3196 if (path
->str
[path
->len
-1] != G_DIR_SEPARATOR
)
3198 g_string_append_c (path
, G_DIR_SEPARATOR
);
3200 g_string_append (path
, dirent
);
3204 /* Here we know path->str is a "system charset" string */
3205 if (stat (path
->str
, &ent_sbuf
) >= 0 && S_ISDIR (ent_sbuf
.st_mode
))
3206 sent
->entries
[n_entries
].is_dir
= TRUE
;
3208 /* stat may fail, and we don't mind, since it could be a
3209 * dangling symlink. */
3210 sent
->entries
[n_entries
].is_dir
= FALSE
;
3213 sent
->entries
[n_entries
].is_dir
= 1;
3217 sent
->entry_count
= n_entries
;
3219 g_free (sys_dir_name
);
3220 g_string_free (path
, TRUE
);
3221 qsort (sent
->entries
, sent
->entry_count
, sizeof (CompletionDirEntry
), compare_cmpl_dir
);
3223 g_dir_close (directory
);
3228 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
3231 check_dir (gchar
*dir_name
,
3232 struct stat
*result
,
3233 gboolean
*stat_subdirs
)
3235 /* A list of directories that we know only contain other directories.
3236 * Trying to stat every file in these directories would be very
3243 struct stat statbuf
;
3244 } no_stat_dirs
[] = {
3245 { "/afs", FALSE
, { 0 } },
3246 { "/net", FALSE
, { 0 } }
3249 static const gint n_no_stat_dirs
= G_N_ELEMENTS (no_stat_dirs
);
3250 static gboolean initialized
= FALSE
;
3251 gchar
*sys_dir_name
;
3257 for (i
= 0; i
< n_no_stat_dirs
; i
++)
3259 if (stat (no_stat_dirs
[i
].name
, &no_stat_dirs
[i
].statbuf
) == 0)
3260 no_stat_dirs
[i
].present
= TRUE
;
3264 sys_dir_name
= g_filename_from_utf8 (dir_name
, -1, NULL
, NULL
, NULL
);
3267 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3271 if (stat (sys_dir_name
, result
) < 0)
3273 g_free (sys_dir_name
);
3277 g_free (sys_dir_name
);
3279 *stat_subdirs
= TRUE
;
3280 for (i
= 0; i
< n_no_stat_dirs
; i
++)
3282 if (no_stat_dirs
[i
].present
&&
3283 (no_stat_dirs
[i
].statbuf
.st_dev
== result
->st_dev
) &&
3284 (no_stat_dirs
[i
].statbuf
.st_ino
== result
->st_ino
))
3286 *stat_subdirs
= FALSE
;
3296 /* open a directory by absolute pathname */
3297 static CompletionDir
*
3298 open_dir (gchar
*dir_name
,
3299 CompletionState
*cmpl_state
)
3302 gboolean stat_subdirs
;
3303 CompletionDirSent
*sent
;
3306 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
3307 if (!check_dir (dir_name
, &sbuf
, &stat_subdirs
))
3310 cdsl
= cmpl_state
->directory_sent_storage
;
3316 if (sent
->inode
== sbuf
.st_ino
&&
3317 sent
->mtime
== sbuf
.st_mtime
&&
3318 sent
->device
== sbuf
.st_dev
)
3319 return attach_dir (sent
, dir_name
, cmpl_state
);
3324 stat_subdirs
= TRUE
;
3327 sent
= open_new_dir (dir_name
, &sbuf
, stat_subdirs
);
3331 cmpl_state
->directory_sent_storage
=
3332 g_list_prepend (cmpl_state
->directory_sent_storage
, sent
);
3334 return attach_dir (sent
, dir_name
, cmpl_state
);
3340 static CompletionDir
*
3341 attach_dir (CompletionDirSent
*sent
,
3343 CompletionState
*cmpl_state
)
3345 CompletionDir
* new_dir
;
3347 new_dir
= g_new (CompletionDir
, 1);
3349 cmpl_state
->directory_storage
=
3350 g_list_prepend (cmpl_state
->directory_storage
, new_dir
);
3352 new_dir
->sent
= sent
;
3353 new_dir
->fullname
= g_strdup (dir_name
);
3354 new_dir
->fullname_len
= strlen (dir_name
);
3355 new_dir
->cmpl_text
= NULL
;
3361 correct_dir_fullname (CompletionDir
* cmpl_dir
)
3363 gint length
= strlen (cmpl_dir
->fullname
);
3364 gchar
*first_slash
= strchr (cmpl_dir
->fullname
, G_DIR_SEPARATOR
);
3365 gchar
*sys_filename
;
3368 /* Does it end with /. (\.) ? */
3370 strcmp (cmpl_dir
->fullname
+ length
- 2, G_DIR_SEPARATOR_S
".") == 0)
3372 /* Is it just the root directory (on a drive) ? */
3373 if (cmpl_dir
->fullname
+ length
- 2 == first_slash
)
3375 cmpl_dir
->fullname
[length
- 1] = 0;
3376 cmpl_dir
->fullname_len
= length
- 1;
3381 cmpl_dir
->fullname
[length
- 2] = 0;
3385 /* Ends with /./ (\.\)? */
3386 else if (length
>= 3 &&
3387 strcmp (cmpl_dir
->fullname
+ length
- 3,
3388 G_DIR_SEPARATOR_S
"." G_DIR_SEPARATOR_S
) == 0)
3389 cmpl_dir
->fullname
[length
- 2] = 0;
3391 /* Ends with /.. (\..) ? */
3392 else if (length
>= 3 &&
3393 strcmp (cmpl_dir
->fullname
+ length
- 3,
3394 G_DIR_SEPARATOR_S
"..") == 0)
3396 /* Is it just /.. (X:\..)? */
3397 if (cmpl_dir
->fullname
+ length
- 3 == first_slash
)
3399 cmpl_dir
->fullname
[length
- 2] = 0;
3400 cmpl_dir
->fullname_len
= length
- 2;
3404 sys_filename
= g_filename_from_utf8 (cmpl_dir
->fullname
, -1, NULL
, NULL
, NULL
);
3407 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3411 if (stat (sys_filename
, &sbuf
) < 0)
3413 g_free (sys_filename
);
3417 g_free (sys_filename
);
3419 cmpl_dir
->fullname
[length
- 3] = 0;
3421 if (!correct_parent (cmpl_dir
, &sbuf
))
3425 /* Ends with /../ (\..\)? */
3426 else if (length
>= 4 &&
3427 strcmp (cmpl_dir
->fullname
+ length
- 4,
3428 G_DIR_SEPARATOR_S
".." G_DIR_SEPARATOR_S
) == 0)
3430 /* Is it just /../ (X:\..\)? */
3431 if (cmpl_dir
->fullname
+ length
- 4 == first_slash
)
3433 cmpl_dir
->fullname
[length
- 3] = 0;
3434 cmpl_dir
->fullname_len
= length
- 3;
3438 sys_filename
= g_filename_from_utf8 (cmpl_dir
->fullname
, -1, NULL
, NULL
, NULL
);
3441 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3445 if (stat (sys_filename
, &sbuf
) < 0)
3447 g_free (sys_filename
);
3451 g_free (sys_filename
);
3453 cmpl_dir
->fullname
[length
- 4] = 0;
3455 if (!correct_parent (cmpl_dir
, &sbuf
))
3459 cmpl_dir
->fullname_len
= strlen (cmpl_dir
->fullname
);
3465 correct_parent (CompletionDir
*cmpl_dir
,
3472 gchar
*sys_filename
;
3475 last_slash
= strrchr (cmpl_dir
->fullname
, G_DIR_SEPARATOR
);
3476 g_assert (last_slash
);
3477 first_slash
= strchr (cmpl_dir
->fullname
, G_DIR_SEPARATOR
);
3479 /* Clever (?) way to check for top-level directory that works also on
3480 * Win32, where there is a drive letter and colon prefixed...
3482 if (last_slash
!= first_slash
)
3492 sys_filename
= g_filename_from_utf8 (cmpl_dir
->fullname
, -1, NULL
, NULL
, NULL
);
3495 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3497 last_slash
[0] = G_DIR_SEPARATOR
;
3501 if (stat (sys_filename
, &parbuf
) < 0)
3503 g_free (sys_filename
);
3506 last_slash
[0] = G_DIR_SEPARATOR
;
3509 g_free (sys_filename
);
3511 #ifndef G_OS_WIN32 /* No inode numbers on Win32 */
3512 if (parbuf
.st_ino
== sbuf
->st_ino
&& parbuf
.st_dev
== sbuf
->st_dev
)
3513 /* it wasn't a link */
3519 last_slash
[0] = G_DIR_SEPARATOR
;
3521 /* it was a link, have to figure it out the hard way */
3523 new_name
= find_parent_dir_fullname (cmpl_dir
->fullname
);
3528 g_free (cmpl_dir
->fullname
);
3530 cmpl_dir
->fullname
= new_name
;
3539 find_parent_dir_fullname (gchar
* dirname
)
3541 gchar
*sys_orig_dir
;
3546 sys_orig_dir
= g_get_current_dir ();
3547 sys_dirname
= g_filename_from_utf8 (dirname
, -1, NULL
, NULL
, NULL
);
3550 g_free (sys_orig_dir
);
3551 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3555 if (chdir (sys_dirname
) != 0 || chdir ("..") != 0)
3558 chdir (sys_orig_dir
);
3559 g_free (sys_dirname
);
3560 g_free (sys_orig_dir
);
3563 g_free (sys_dirname
);
3565 sys_cwd
= g_get_current_dir ();
3566 result
= g_filename_to_utf8 (sys_cwd
, -1, NULL
, NULL
, NULL
);
3569 if (chdir (sys_orig_dir
) != 0)
3572 g_free (sys_orig_dir
);
3576 g_free (sys_orig_dir
);
3582 /**********************************************************************/
3583 /* Completion Operations */
3584 /**********************************************************************/
3588 static PossibleCompletion
*
3589 attempt_homedir_completion (gchar
*text_to_complete
,
3590 CompletionState
*cmpl_state
)
3594 if (!cmpl_state
->user_dir_name_buffer
&&
3595 !get_pwdb (cmpl_state
))
3597 length
= strlen (text_to_complete
) - 1;
3599 cmpl_state
->user_completion_index
+= 1;
3601 while (cmpl_state
->user_completion_index
< cmpl_state
->user_directories_len
)
3603 index
= first_diff_index (text_to_complete
+ 1,
3604 cmpl_state
->user_directories
3605 [cmpl_state
->user_completion_index
].login
);
3612 if (cmpl_state
->last_valid_char
< (index
+ 1))
3613 cmpl_state
->last_valid_char
= index
+ 1;
3614 cmpl_state
->user_completion_index
+= 1;
3618 cmpl_state
->the_completion
.is_a_completion
= 1;
3619 cmpl_state
->the_completion
.is_directory
= TRUE
;
3621 append_completion_text ("~", cmpl_state
);
3623 append_completion_text (cmpl_state
->
3624 user_directories
[cmpl_state
->user_completion_index
].login
,
3627 return append_completion_text (G_DIR_SEPARATOR_S
, cmpl_state
);
3630 if (text_to_complete
[1]
3631 || cmpl_state
->user_completion_index
> cmpl_state
->user_directories_len
)
3633 cmpl_state
->user_completion_index
= -1;
3638 cmpl_state
->user_completion_index
+= 1;
3639 cmpl_state
->the_completion
.is_a_completion
= 1;
3640 cmpl_state
->the_completion
.is_directory
= TRUE
;
3642 return append_completion_text ("~" G_DIR_SEPARATOR_S
, cmpl_state
);
3648 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
3649 #define FOLD(c) (tolower(c))
3654 /* returns the index (>= 0) of the first differing character,
3655 * PATTERN_MATCH if the completion matches */
3657 first_diff_index (gchar
*pat
,
3662 while (*pat
&& *text
&& FOLD (*text
) == FOLD (*pat
))
3672 return PATTERN_MATCH
;
3675 static PossibleCompletion
*
3676 append_completion_text (gchar
*text
,
3677 CompletionState
*cmpl_state
)
3681 if (!cmpl_state
->the_completion
.text
)
3684 len
= strlen (text
) + strlen (cmpl_state
->the_completion
.text
) + 1;
3686 if (cmpl_state
->the_completion
.text_alloc
> len
)
3688 strcat (cmpl_state
->the_completion
.text
, text
);
3689 return &cmpl_state
->the_completion
;
3695 cmpl_state
->the_completion
.text_alloc
= i
;
3697 cmpl_state
->the_completion
.text
= (gchar
*) g_realloc (cmpl_state
->the_completion
.text
, i
);
3699 if (!cmpl_state
->the_completion
.text
)
3703 strcat (cmpl_state
->the_completion
.text
, text
);
3704 return &cmpl_state
->the_completion
;
3708 static CompletionDir
*
3709 find_completion_dir (gchar
*text_to_complete
,
3710 gchar
**remaining_text
,
3711 CompletionState
*cmpl_state
)
3713 gchar
* first_slash
= strchr (text_to_complete
, G_DIR_SEPARATOR
);
3714 CompletionDir
* dir
= cmpl_state
->reference_dir
;
3715 CompletionDir
* next
;
3716 *remaining_text
= text_to_complete
;
3720 gint len
= first_slash
- *remaining_text
;
3722 gchar
*found_name
= NULL
; /* Quiet gcc */
3724 gchar
* pat_buf
= g_new (gchar
, len
+ 1);
3726 strncpy (pat_buf
, *remaining_text
, len
);
3729 for (i
= 0; i
< dir
->sent
->entry_count
; i
+= 1)
3731 if (dir
->sent
->entries
[i
].is_dir
&&
3732 _gtk_fnmatch (pat_buf
, dir
->sent
->entries
[i
].entry_name
))
3742 found_name
= dir
->sent
->entries
[i
].entry_name
;
3749 /* Perhaps we are trying to open an automount directory */
3750 found_name
= pat_buf
;
3753 next
= open_relative_dir (found_name
, dir
, cmpl_state
);
3761 next
->cmpl_parent
= dir
;
3765 if (!correct_dir_fullname (dir
))
3771 *remaining_text
= first_slash
+ 1;
3772 first_slash
= strchr (*remaining_text
, G_DIR_SEPARATOR
);
3781 update_cmpl (PossibleCompletion
*poss
,
3782 CompletionState
*cmpl_state
)
3786 if (!poss
|| !cmpl_is_a_completion (poss
))
3789 cmpl_len
= strlen (cmpl_this_completion (poss
));
3791 if (cmpl_state
->updated_text_alloc
< cmpl_len
+ 1)
3793 cmpl_state
->updated_text
=
3794 (gchar
*)g_realloc (cmpl_state
->updated_text
,
3795 cmpl_state
->updated_text_alloc
);
3796 cmpl_state
->updated_text_alloc
= 2*cmpl_len
;
3799 if (cmpl_state
->updated_text_len
< 0)
3801 strcpy (cmpl_state
->updated_text
, cmpl_this_completion (poss
));
3802 cmpl_state
->updated_text_len
= cmpl_len
;
3803 cmpl_state
->re_complete
= cmpl_is_directory (poss
);
3805 else if (cmpl_state
->updated_text_len
== 0)
3807 cmpl_state
->re_complete
= FALSE
;
3812 first_diff_index (cmpl_state
->updated_text
,
3813 cmpl_this_completion (poss
));
3815 cmpl_state
->re_complete
= FALSE
;
3817 if (first_diff
== PATTERN_MATCH
)
3820 if (first_diff
> cmpl_state
->updated_text_len
)
3821 strcpy (cmpl_state
->updated_text
, cmpl_this_completion (poss
));
3823 cmpl_state
->updated_text_len
= first_diff
;
3824 cmpl_state
->updated_text
[first_diff
] = 0;
3828 static PossibleCompletion
*
3829 attempt_dir_completion (CompletionState
*cmpl_state
)
3831 gchar
*pat_buf
, *first_slash
;
3832 CompletionDir
*dir
= cmpl_state
->active_completion_dir
;
3834 dir
->cmpl_index
+= 1;
3836 if (dir
->cmpl_index
== dir
->sent
->entry_count
)
3838 if (dir
->cmpl_parent
== NULL
)
3840 cmpl_state
->active_completion_dir
= NULL
;
3846 cmpl_state
->active_completion_dir
= dir
->cmpl_parent
;
3848 return attempt_dir_completion (cmpl_state
);
3852 g_assert (dir
->cmpl_text
);
3854 first_slash
= strchr (dir
->cmpl_text
, G_DIR_SEPARATOR
);
3858 gint len
= first_slash
- dir
->cmpl_text
;
3860 pat_buf
= g_new (gchar
, len
+ 1);
3861 strncpy (pat_buf
, dir
->cmpl_text
, len
);
3866 gint len
= strlen (dir
->cmpl_text
);
3868 pat_buf
= g_new (gchar
, len
+ 2);
3869 strcpy (pat_buf
, dir
->cmpl_text
);
3870 /* Don't append a * if the user entered one herself.
3871 * This way one can complete *.h and don't get matches
3872 * on any .help files, for instance.
3874 if (strchr (pat_buf
, '*') == NULL
)
3875 strcpy (pat_buf
+ len
, "*");
3880 if (dir
->sent
->entries
[dir
->cmpl_index
].is_dir
)
3882 if (_gtk_fnmatch (pat_buf
, dir
->sent
->entries
[dir
->cmpl_index
].entry_name
))
3884 CompletionDir
* new_dir
;
3886 new_dir
= open_relative_dir (dir
->sent
->entries
[dir
->cmpl_index
].entry_name
,
3895 new_dir
->cmpl_parent
= dir
;
3897 new_dir
->cmpl_index
= -1;
3898 new_dir
->cmpl_text
= g_strdup (first_slash
+ 1);
3900 cmpl_state
->active_completion_dir
= new_dir
;
3903 return attempt_dir_completion (cmpl_state
);
3908 return attempt_dir_completion (cmpl_state
);
3914 return attempt_dir_completion (cmpl_state
);
3919 if (dir
->cmpl_parent
!= NULL
)
3921 append_completion_text (dir
->fullname
+
3922 strlen (cmpl_state
->completion_dir
->fullname
) + 1,
3924 append_completion_text (G_DIR_SEPARATOR_S
, cmpl_state
);
3927 append_completion_text (dir
->sent
->entries
[dir
->cmpl_index
].entry_name
, cmpl_state
);
3929 cmpl_state
->the_completion
.is_a_completion
=
3930 _gtk_fnmatch (pat_buf
, dir
->sent
->entries
[dir
->cmpl_index
].entry_name
);
3932 cmpl_state
->the_completion
.is_directory
= dir
->sent
->entries
[dir
->cmpl_index
].is_dir
;
3933 if (dir
->sent
->entries
[dir
->cmpl_index
].is_dir
)
3934 append_completion_text (G_DIR_SEPARATOR_S
, cmpl_state
);
3937 return &cmpl_state
->the_completion
;
3944 get_pwdb (CompletionState
* cmpl_state
)
3946 struct passwd
*pwd_ptr
;
3949 gint len
= 0, i
, count
= 0;
3951 if (cmpl_state
->user_dir_name_buffer
)
3955 while ((pwd_ptr
= getpwent ()) != NULL
)
3957 utf8
= g_filename_to_utf8 (pwd_ptr
->pw_name
, -1, NULL
, NULL
, NULL
);
3958 len
+= strlen (utf8
);
3960 utf8
= g_filename_to_utf8 (pwd_ptr
->pw_dir
, -1, NULL
, NULL
, NULL
);
3961 len
+= strlen (utf8
);
3969 cmpl_state
->user_dir_name_buffer
= g_new (gchar
, len
);
3970 cmpl_state
->user_directories
= g_new (CompletionUserDir
, count
);
3971 cmpl_state
->user_directories_len
= count
;
3973 buf_ptr
= cmpl_state
->user_dir_name_buffer
;
3975 for (i
= 0; i
< count
; i
+= 1)
3977 pwd_ptr
= getpwent ();
3984 utf8
= g_filename_to_utf8 (pwd_ptr
->pw_name
, -1, NULL
, NULL
, NULL
);
3985 strcpy (buf_ptr
, utf8
);
3988 cmpl_state
->user_directories
[i
].login
= buf_ptr
;
3990 buf_ptr
+= strlen (buf_ptr
);
3993 utf8
= g_filename_to_utf8 (pwd_ptr
->pw_dir
, -1, NULL
, NULL
, NULL
);
3994 strcpy (buf_ptr
, utf8
);
3997 cmpl_state
->user_directories
[i
].homedir
= buf_ptr
;
3999 buf_ptr
+= strlen (buf_ptr
);
4003 qsort (cmpl_state
->user_directories
,
4004 cmpl_state
->user_directories_len
,
4005 sizeof (CompletionUserDir
),
4014 if (cmpl_state
->user_dir_name_buffer
)
4015 g_free (cmpl_state
->user_dir_name_buffer
);
4016 if (cmpl_state
->user_directories
)
4017 g_free (cmpl_state
->user_directories
);
4019 cmpl_state
->user_dir_name_buffer
= NULL
;
4020 cmpl_state
->user_directories
= NULL
;
4026 compare_user_dir (const void *a
,
4029 return strcmp ((((CompletionUserDir
*)a
))->login
,
4030 (((CompletionUserDir
*)b
))->login
);
4036 compare_cmpl_dir (const void *a
,
4040 return strcmp (((CompletionDirEntry
*)a
)->sort_key
,
4041 (((CompletionDirEntry
*)b
))->sort_key
);
4045 cmpl_state_okay (CompletionState
* cmpl_state
)
4047 return cmpl_state
&& cmpl_state
->reference_dir
;
4051 cmpl_strerror (gint err
)
4053 if (err
== CMPL_ERRNO_TOO_LONG
)
4054 return _("Name too long");
4055 else if (err
== CMPL_ERRNO_DID_NOT_CONVERT
)
4056 return _("Couldn't convert filename");
4058 return g_strerror (err
);
4061 const gchar
* gtk_dir_selection_get_dir (GtkDirSelection
*filesel
)
4063 return gtk_entry_get_text (GTK_ENTRY (filesel
->selection_entry
));