1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
31 static char * back_xpm[] = {
82 static char * up_xpm[] = {
136 static char * forward_xpm[] = {
190 static char * refresh_xpm[] = {
229 #include <sys/types.h>
230 #include <sys/stat.h>
231 #include <sys/param.h>
243 #if ( defined TORRIE_DEBUG || defined LEO )
244 #include <gdk/gdkkeysyms.h>
245 #include <gtk/gtkbutton.h>
246 #include <gtk/gtkentry.h>
247 #include "gtkfilesel-linux.h"
248 #include <gtk/gtkhbox.h>
249 #include <gtk/gtkhbbox.h>
250 #include <gtk/gtklabel.h>
251 #include <gtk/gtklist.h>
252 #include <gtk/gtklistitem.h>
253 #include <gtk/gtkmain.h>
254 #include <gtk/gtkscrolledwindow.h>
255 #include <gtk/gtksignal.h>
256 #include <gtk/gtkvbox.h>
257 #include <gtk/gtkmenu.h>
258 #include <gtk/gtkmenuitem.h>
259 #include <gtk/gtkoptionmenu.h>
260 #include <gtk/gtkclist.h>
261 #include <gtk/gtkdialog.h>
262 #include <gtk/gtkcombo.h>
263 #include <gtk/gtkframe.h>
264 #include <gtk/gtkhpaned.h>
265 #include <gtk/gtktable.h>
266 #include <gtk/gtkpixmap.h>
267 #include <gtk/gtknotebook.h>
268 #include <gtk/gtkhseparator.h>
269 #include <gtk/gtktogglebutton.h>
271 #include "gdk/gdkkeysyms.h"
272 #include "gtkbutton.h"
273 #include "gtkentry.h"
274 #include "gtkfilesel.h"
276 #include "gtkhbbox.h"
277 #include "gtklabel.h"
279 #include "gtklistitem.h"
281 #include "gtkscrolledwindow.h"
282 #include "gtksignal.h"
285 #include "gtkmenuitem.h"
286 #include "gtkoptionmenu.h"
287 #include "gtkclist.h"
288 #include "gtkdialog.h"
289 #include "gtkcombo.h"
290 #include "gtkframe.h"
291 #include "gtkhpaned.h"
292 #include "gtktable.h"
293 #include "gtkpixmap.h"
294 #include "gtknotebook.h"
295 #include "gtkhseparator.h"
296 #include "gtktogglebutton.h"
304 #include "forward.xpm"
305 #include "refresh.xpm"
308 #define DIR_LIST_WIDTH 180
309 #define DIR_LIST_HEIGHT 180
310 #define FILE_LIST_WIDTH 180
311 #define FILE_LIST_HEIGHT 180
312 #define BOOKMARK_FILE "/.gtkfilesel_bookmarks"
313 #define MASK_FILE "/.gtkfilesel_masks"
314 #define TIME_STRING_BUF 50
316 /* I've put this here so it doesn't get confused with the
317 * file completion interface */
318 typedef struct _HistoryCallbackArg HistoryCallbackArg;
320 struct _HistoryCallbackArg
323 GtkWidget *menu_item;
327 typedef struct _BookmarkMenuStruct BookmarkMenuStruct;
328 struct _BookmarkMenuStruct {
329 GtkWidget *menu_item;
334 typedef struct _CompletionState CompletionState;
335 typedef struct _CompletionDir CompletionDir;
336 typedef struct _CompletionDirSent CompletionDirSent;
337 typedef struct _CompletionDirEntry CompletionDirEntry;
338 typedef struct _CompletionUserDir CompletionUserDir;
339 typedef struct _PossibleCompletion PossibleCompletion;
341 /* Non-external file completion decls and structures */
343 /* A contant telling PRCS how many directories to cache. Its actually
344 * kept in a list, so the geometry isn't important. */
345 #define CMPL_DIRECTORY_CACHE_SIZE 10
347 /* A constant used to determine whether a substring was an exact
348 * match by first_diff_index()
350 #define PATTERN_MATCH -1
351 /* The arguments used by all fnmatch() calls below
353 #define FNMATCH_FLAGS ( FNM_PATHNAME | FNM_PERIOD )
355 #define CMPL_ERRNO_TOO_LONG ( ( 1 << 16 ) - 1 )
357 /* This structure contains all the useful information about a directory
358 * for the purposes of filename completion. These structures are cached
359 * in the CompletionState struct. CompletionDir's are reference counted.
361 struct _CompletionDirSent
368 gchar *name_buffer; /* memory segment containing names of all entries */
370 struct _CompletionDirEntry *entries;
373 struct _CompletionDir
375 CompletionDirSent *sent;
380 struct _CompletionDir *cmpl_parent;
385 /* This structure contains pairs of directory entry names with a flag saying
386 * whether or not they are a valid directory. NOTE: This information is used
387 * to provide the caller with information about whether to update its completions
388 * or try to open a file. Since directories are cached by the directory mtime,
389 * a symlink which points to an invalid file (which will not be a directory),
390 * will not be reevaluated if that file is created, unless the containing
391 * directory is touched. I consider this case to be worth ignoring (josh).
393 struct _CompletionDirEntry
399 struct _CompletionUserDir
405 struct _PossibleCompletion
407 /* accessible fields, all are accessed externally by functions
411 gint is_a_completion;
423 struct _CompletionState
425 gint last_valid_char;
427 gint updated_text_len;
428 gint updated_text_alloc;
431 gchar *user_dir_name_buffer;
432 gint user_directories_len;
434 gchar *last_completion_text;
436 gint user_completion_index; /* if >= 0, currently completing ~user */
438 struct _CompletionDir *completion_dir; /* directory completing from */
439 struct _CompletionDir *active_completion_dir;
441 struct _PossibleCompletion the_completion;
443 struct _CompletionDir *reference_dir; /* initial directory */
445 GList* directory_storage;
446 GList* directory_sent_storage;
448 struct _CompletionUserDir *user_directories;
451 /* Widgets from the Properties Dialog */
452 typedef struct _PropertiesPrivate PropertiesPrivate;
454 struct _PropertiesPrivate
456 GtkWidget *mode_label;
457 GtkWidget *mode_buttons[12];
460 /* pixmap creation function */
461 GtkWidget* create_pixmap( GtkWidget *widget,
462 const gchar *pixmap_char );
464 /* File completion functions which would be external, were they used
465 * outside of this file.
468 static CompletionState* cmpl_init_state( void );
469 static void cmpl_free_state( CompletionState *cmpl_state );
470 static gint cmpl_state_okay( CompletionState* cmpl_state );
471 static gchar* cmpl_strerror( gint );
473 static PossibleCompletion* cmpl_completion_matches( gchar *text_to_complete,
474 gchar **remaining_text,
475 CompletionState *cmpl_state );
477 /* Returns a name for consideration, possibly a completion, this name
478 * will be invalid after the next call to cmpl_next_completion.
480 static char* cmpl_this_completion( PossibleCompletion* );
482 /* True if this completion matches the given text. Otherwise, this
483 * output can be used to have a list of non-completions.
485 static gint cmpl_is_a_completion( PossibleCompletion* );
487 /* True if the completion is a directory
489 static gint cmpl_is_directory( PossibleCompletion* );
491 /* Obtains the next completion, or NULL
493 static PossibleCompletion* cmpl_next_completion( CompletionState* );
495 /* Updating completions: the return value of cmpl_updated_text() will
496 * be text_to_complete completed as much as possible after the most
497 * recent call to cmpl_completion_matches. For the present
498 * application, this is the suggested replacement for the user's input
499 * string. You must CALL THIS AFTER ALL cmpl_text_completions have
502 static gchar* cmpl_updated_text( CompletionState* cmpl_state );
504 /* After updating, to see if the completion was a directory, call
505 * this. If it was, you should consider re-calling completion_matches.
507 static gint cmpl_updated_dir( CompletionState* cmpl_state );
509 /* Current location: if using file completion, return the current
510 * directory, from which file completion begins. More specifically,
511 * the cwd concatenated with all exact completions up to the last
512 * directory delimiter('/').
514 static gchar* cmpl_reference_position( CompletionState* cmpl_state );
516 /* backing up: if cmpl_completion_matches returns NULL, you may query
517 * the index of the last completable character into cmpl_updated_text.
519 static gint cmpl_last_valid_char( CompletionState* cmpl_state );
521 /* When the user selects a non-directory, call cmpl_completion_fullname
522 * to get the full name of the selected file.
524 static gchar* cmpl_completion_fullname( gchar*, CompletionState* cmpl_state );
527 /* Directory operations. */
528 static CompletionDir* open_ref_dir( gchar* text_to_complete,
529 gchar** remaining_text,
530 CompletionState* cmpl_state );
531 static gboolean check_dir( gchar *dir_name,
533 gboolean *stat_subdirs );
534 static CompletionDir* open_dir( gchar* dir_name,
535 CompletionState* cmpl_state );
536 static CompletionDir* open_user_dir( gchar* text_to_complete,
537 CompletionState *cmpl_state );
538 static CompletionDir* open_relative_dir( gchar* dir_name, CompletionDir* dir,
539 CompletionState *cmpl_state );
540 static CompletionDirSent* open_new_dir( gchar* dir_name,
542 gboolean stat_subdirs );
543 static gint correct_dir_fullname( CompletionDir* cmpl_dir );
544 static gint correct_parent( CompletionDir* cmpl_dir,
546 static gchar* find_parent_dir_fullname( gchar* dirname );
547 static CompletionDir* attach_dir( CompletionDirSent* sent,
549 CompletionState *cmpl_state );
550 static void free_dir_sent( CompletionDirSent* sent );
551 static void free_dir( CompletionDir *dir );
552 static void prune_memory_usage( CompletionState *cmpl_state );
554 /* Completion operations */
555 static PossibleCompletion* attempt_homedir_completion( gchar* text_to_complete,
556 CompletionState *cmpl_state );
557 static PossibleCompletion* attempt_file_completion( CompletionState *cmpl_state );
558 static CompletionDir* find_completion_dir( gchar* text_to_complete,
559 gchar** remaining_text,
560 CompletionState* cmpl_state );
561 static PossibleCompletion* append_completion_text( gchar* text,
562 CompletionState* cmpl_state );
563 static gint get_pwdb( CompletionState* cmpl_state );
564 static gint first_diff_index( gchar* pat, gchar* text );
565 static gint compare_user_dir( const void* a, const void* b );
566 static gint compare_cmpl_dir( const void* a, const void* b );
567 static void update_cmpl( PossibleCompletion* poss,
568 CompletionState* cmpl_state );
570 static void gtk_file_selection_class_init( GtkFileSelectionClass *klass );
571 static void gtk_file_selection_init( GtkFileSelection *filesel );
572 static void gtk_file_selection_realize( GtkWidget *widget );
573 static void gtk_file_selection_destroy( GtkObject *object );
574 static gint gtk_file_selection_key_press( GtkWidget *widget,
576 gpointer user_data );
578 static void gtk_file_selection_file_button( GtkWidget *widget,
581 GdkEventButton *bevent,
582 gpointer user_data );
584 static void gtk_file_selection_dir_button( GtkWidget *widget,
587 GdkEventButton *bevent,
590 static void gtk_file_selection_undir_button( GtkWidget *widget,
593 GdkEventButton *bevent,
596 static void gtk_file_selection_populate( GtkFileSelection *fs,
599 static void gtk_file_selection_abort( GtkFileSelection *fs );
601 static void gtk_file_selection_update_history_menu( GtkFileSelection *fs,
602 gchar *current_dir );
604 static void gtk_file_selection_create_dir( gpointer data );
605 static void gtk_file_selection_delete_file( gpointer data );
606 static void gtk_file_selection_rename_file( gpointer data );
607 static void gtk_file_selection_properties( gpointer data );
608 static void gtk_file_selection_properties_update_mode( GtkWidget *widget, gpointer data );
609 static mode_t gtk_file_selection_properties_get_mode( PropertiesPrivate* private );
611 static gboolean gtk_file_selection_history_combo_callback( GtkWidget *widget, GdkEventKey *event, gpointer data );
612 static gboolean gtk_file_selection_history_combo_list_key_handler( GtkWidget *widget,
614 gpointer user_data );
615 static gboolean gtk_file_selection_history_combo_list_callback( GtkWidget *thelist,
616 GdkEventButton *event,
618 gpointer user_data );
619 static void gtk_file_selection_bookmark_callback( GtkWidget *widget, gpointer data );
620 static void gtk_file_selection_mask_entry_callback( GtkWidget *widget, gpointer data );
621 static gint gtk_file_selection_mask_entry_key_callback( GtkWidget *widget, GdkEventKey *event, gpointer data );
622 static gint gtk_file_selection_mask_entry_button_callback( GtkWidget *widget, GdkEventButton *event, gpointer data );
624 //static void gtk_file_selection_home_button (GtkWidget *widget, gpointer data);
625 static void gtk_file_selection_bookmark_button( GtkWidget *widget,
626 GtkFileSelection *fs );
628 static void gtk_file_selection_up_button( GtkWidget *widget, gpointer data );
629 static void gtk_file_selection_prev_button( GtkWidget *widget, gpointer data );
630 static void gtk_file_selection_next_button( GtkWidget *widget, gpointer data );
631 static void gtk_file_selection_refresh_button( GtkWidget *widget, gpointer data );
633 static gint gtk_file_selection_files_list_key_callback( GtkWidget *widget, GdkEventKey *event, gpointer data );
636 static gint gtk_file_selection_match_char( gchar, gchar *mask );
637 static gint gtk_file_selection_match_mask( gchar *,gchar * );
639 static void gtk_file_selection_load_bookmarks( GtkFileSelection *fs );
640 static void gtk_file_selection_add_bookmark( GtkFileSelection *fs, gchar *desc, gchar *path );
641 gint gtk_file_selection_save_bookmarks( GtkFileSelection *fs );
643 static void gtk_file_selection_load_masks( GtkFileSelection *fs );
645 static gint gtk_file_selection_show_fileop_menu( GtkCList *clist,
647 GtkFileSelection *fs );
650 static GtkWindowClass *parent_class = NULL;
652 /* Saves errno when something cmpl does fails. */
653 static gint cmpl_errno;
657 * Take the path currently in the file selection
658 * entry field and translate as necessary from
659 * a WIN32 style to CYGWIN32 style path. For
660 * instance translate:
661 * x:\somepath\file.jpg
663 * //x/somepath/file.jpg
665 * Replace the path in the selection text field.
666 * Return a boolean value concerning whether a
667 * translation had to be made.
670 translate_win32_path( GtkFileSelection *filesel ){
675 * Retrieve the current path
677 path = gtk_entry_get_text( GTK_ENTRY( filesel->selection_entry ) );
680 * Translate only if this looks like a DOS-ish
681 * path... First handle any drive letters.
683 if ( isalpha( path[0] ) && ( path[1] == ':' ) ) {
685 * This part kind of stinks... It isn't possible
686 * to know if there is enough space in the current
687 * string for the extra character required in this
688 * conversion. Assume that there isn't enough space
689 * and use the set function on the text field to
690 * set the newly created string.
692 gchar *newPath = g_strdup_printf( "//%c/%s", path[0], ( path + 3 ) );
693 gtk_entry_set_text( GTK_ENTRY( filesel->selection_entry ), newPath );
700 * Now, replace backslashes with forward slashes
703 if ( strchr( path, '\\' ) ) {
705 for ( index = 0; path[index] != '\0'; index++ )
706 if ( path[index] == '\\' ) {
718 * Make prev and next inactive if their respective *
719 * histories are empty.
720 * Add facilities for handling hidden files and *
722 * Add an api to access the mask, and hidden files *
723 * check box? (prob not in 1.2.x series) *
726 /* Routine for applying mask to filenames *
727 * Need to be optimized to minimize recursion *
728 * help the for loop by looking for the next *
729 * instance of the mask character following *
730 * the '*'. ei *.c -- look for '.' *
731 * Also, swap all *? pairs (-> ?*), as that *
732 * will make it possible to look ahead (? *
733 * makes it very nondeterministic as in *?.c *
734 * which really is ?*.c *
737 static gint gtk_file_selection_match_char( gchar text, gchar *mask ){
744 if ( mask[0] == '[' ) {
745 if ( !strchr( mask,']' ) ) {
750 maskc = g_strdup( mask + 1 ); /* get the portion of mask inside []*/
751 ( *( strchr( maskc + 1,']' ) ) ) = 0;
752 s = strlen( (char *)maskc );
754 for ( x = 0 ; x < s ; x++ ) {
755 if ( maskc[x] == '-' ) {
759 nextc = maskc[x + 1];
761 if ( nextc > lastc ) {
762 if ( ( lastc <= text ) && ( nextc >= text ) ) {
767 else if ( ( lastc >= text ) && ( nextc <= text ) ) {
772 else if ( text == maskc[x] ) {
783 if ( mask[0] == '?' ) {
786 if ( mask[0] == text ) {
794 static gint gtk_file_selection_match_mask1( gchar *text, gchar *mask ){
801 if ( mask[0] == 0 && text[0] == 0 ) {
805 if ( mask[0] == '*' ) {
806 for ( tc = 0; tc <= strlen( text ); tc++ )
808 if ( gtk_file_selection_match_mask1( text + tc, mask + 1 ) ) {
814 mc = gtk_file_selection_match_char( text[0], mask );
817 return gtk_file_selection_match_mask1( text + 1, mask + mc );
824 static gint gtk_file_selection_match_mask( gchar *text, gchar *mask ){
829 masks = g_strdup( mask );
831 emask = strchr( masks,'<' );
834 emask = strchr( bmask,'>' );
844 if ( ( emask = strchr( bmask,',' ) ) || ( emask = strchr( bmask,';' ) ) ) {
846 if ( gtk_file_selection_match_mask1( text, bmask ) ) {
855 if ( gtk_file_selection_match_mask1( text, bmask ) ) {
864 gtk_file_selection_load_bookmarks( GtkFileSelection *fs ){
866 gchar *bookmark_file;
867 gchar *bookmark_data;
868 struct stat file_info;
872 BookmarkMenuStruct *item;
875 if ( fs->bookmark_list ) { //erase
876 list = fs->bookmark_list;
879 g_free( item->desc );
880 g_free( item->path );
884 g_list_free( fs->bookmark_list );
885 fs->bookmark_list = NULL;
886 gtk_widget_destroy( fs->bookmark_menu );
889 fs->bookmark_menu = gtk_menu_new();
892 item = g_malloc( sizeof( item ) );
893 item->menu_item = gtk_menu_item_new();
894 gtk_widget_show( item->menu_item );
895 gtk_menu_append( GTK_MENU( fs->bookmark_menu ), item->menu_item );
897 item = g_malloc( sizeof( item ) );
898 item->desc = g_strdup( "Add bookmark" );
899 item->path = g_strdup( "." );
900 item->menu_item = gtk_menu_item_new_with_label( item->desc );
901 gtk_widget_show( item->menu_item );
902 //fs->bookmark_list=g_list_append(fs->bookmark_list,item);
904 gtk_menu_append( GTK_MENU( fs->bookmark_menu ), item->menu_item );
906 item = g_malloc( sizeof( item ) );
907 item->desc = g_strdup( "Edit bookmark" );
908 item->path = g_strdup( "." );
909 item->menu_item = gtk_menu_item_new_with_label( item->desc );
910 gtk_widget_show( item->menu_item );
911 //fs->bookmark_list=g_list_append(fs->bookmark_list,item);
913 gtk_menu_append( GTK_MENU( fs->bookmark_menu ), item->menu_item );
915 bookmark_file = g_strconcat( g_get_home_dir(), BOOKMARK_FILE,NULL );
916 if ( !stat( bookmark_file,&file_info ) && ( file = open( bookmark_file, O_RDONLY ) ) > 0 ) {
917 if ( file_info.st_size < 65536 ) {
918 bookmark_data = g_malloc( file_info.st_size );
920 if ( file && read( file, bookmark_data, file_info.st_size ) ) {
923 while ( cp < file_info.st_size )
925 while ( cp < file_info.st_size && bookmark_data[cp] != '<' )
927 bookmark_data[cp] = 0;
928 item = g_malloc( sizeof( BookmarkMenuStruct ) );
929 item->desc = g_strdup( bookmark_data + lp );
932 while ( cp < file_info.st_size && bookmark_data[cp] != '>' )
935 bookmark_data[cp] = 0;
937 item->path = g_strdup( bookmark_data + lp );
938 gtk_file_selection_add_bookmark( (gpointer) fs, (gpointer) item->desc, (gpointer) item->path );
942 while ( cp < file_info.st_size && bookmark_data[cp] < 33 )
953 /* Add some default items, then save off to bookmarks file */
955 gtk_file_selection_add_bookmark( (gpointer) fs, "Home", "~/" );
956 gtk_file_selection_add_bookmark( (gpointer) fs, "Root", "/" );
958 gtk_file_selection_save_bookmarks( (gpointer) fs );
963 gtk_file_selection_add_bookmark( GtkFileSelection *fs, gchar *desc, gchar *path ){
964 /* Add item to menu */
965 BookmarkMenuStruct *item;
966 item = g_malloc( sizeof( item ) );
967 item->desc = (gpointer) desc;
968 item->path = (gpointer) path;
969 item->menu_item = gtk_menu_item_new_with_label( item->desc );
970 gtk_widget_show( item->menu_item );
971 fs->bookmark_list = g_list_append( fs->bookmark_list,item );
972 gtk_signal_connect( GTK_OBJECT( item->menu_item ), "activate",
973 (GtkSignalFunc) gtk_file_selection_bookmark_callback,
975 gtk_menu_insert( GTK_MENU( fs->bookmark_menu ), item->menu_item, g_list_length( fs->bookmark_list ) - 1 );
979 gtk_file_selection_save_bookmarks( GtkFileSelection *fs ){
980 BookmarkMenuStruct *item;
981 gchar *bookmark_file;
986 bookmark_file = g_strconcat( g_get_home_dir(), BOOKMARK_FILE,NULL );
988 if ( ( file = open( bookmark_file, O_CREAT | O_WRONLY | O_TRUNC, 0600 ) ) > 0 ) {
989 for ( list = g_list_first( fs->bookmark_list ); list != NULL; list = g_list_next( list ) ) {
991 item_data = g_strconcat( item->desc, " <", item->path, ">\n", NULL );
992 if ( write( file, item_data, strlen( item_data ) ) != strlen( item_data ) ) {
1008 gtk_file_selection_load_masks( GtkFileSelection *fs ){
1013 struct stat file_info;
1027 masks_file=g_strconcat(g_get_home_dir(), MASK_FILE,NULL); //put in #define
1028 if(!stat(masks_file,&file_info))
1030 if(file_info.st_size <65536 )
1032 masks_data=g_malloc(file_info.st_size);
1034 file = open(masks_file, O_RDONLY );
1036 if(file && read(file, masks_data, file_info.st_size))
1040 while (cp < file_info.st_size)
1042 while (cp < file_info.st_size && masks_data[cp] != '>' )
1046 if (masks_data[lp]=='<') { //if there was no description, strip off brackets
1050 // g_print("%s\n",masks_data+lp);
1051 fs->masks = g_list_append(fs->masks, g_strdup(masks_data+lp));
1053 while(cp < file_info.st_size && masks_data[cp] < 33 )
1064 /* masks is still null, fill it with default data... */
1066 fs->masks = g_list_append(fs->masks, "all files <*>");
1067 fs->masks = g_list_append(fs->masks, "mp3s/playlists <*.mp3,*.m3u>");
1068 fs->masks = g_list_append(fs->masks, "src/hdr <*.[CcHh],*.[Cc][Cc],*.[Hh][Hh],*.cpp>");
1069 fs->masks = g_list_append(fs->masks, "html docs <*.html,*.htm,*.HTM,*.php*,*.inc>");
1070 fs->masks = g_list_append(fs->masks, "images <*.png,*.jpg,*.jpeg,*.gif,*.xpm,*.tiff>");
1071 fs->masks = g_list_append(fs->masks, "package <*.rpm,*.deb>");
1072 fs->masks = g_list_append(fs->masks, "archive <*.tgz,*.tb2,*.tar*,*.zip,*.rar>");
1073 fs->masks = g_list_append(fs->masks, "compressed <*.Z,*.gz,*.bz2>");
1078 void gtk_file_selection_clear_masks( GtkFileSelection *filesel ){
1081 g_return_if_fail( filesel != NULL );
1082 g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) );
1084 list = filesel->masks;
1087 g_free( list->data );
1090 filesel->masks = NULL;
1092 gtk_list_clear_items( GTK_LIST( GTK_COMBO( filesel->mask_entry )->list ), 0, -1 );
1095 void gtk_file_selection_set_masks( GtkFileSelection *filesel, const gchar **masks ){
1096 g_return_if_fail( filesel != NULL );
1097 g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) );
1101 filesel->masks = g_list_append( filesel->masks, ( gpointer ) * masks );
1105 if ( filesel->masks ) {
1106 gtk_combo_set_popdown_strings( GTK_COMBO( filesel->mask_entry ), filesel->masks );
1111 gtk_file_selection_get_type( void ){
1112 static GtkType file_selection_type = 0;
1114 if ( !file_selection_type ) {
1115 static const GtkTypeInfo filesel_info =
1118 sizeof( GtkFileSelection ),
1119 sizeof( GtkFileSelectionClass ),
1120 (GtkClassInitFunc) gtk_file_selection_class_init,
1121 (GtkObjectInitFunc) gtk_file_selection_init,
1122 /* reserved_1 */ NULL,
1123 /* reserved_2 */ NULL,
1124 (GtkClassInitFunc) NULL,
1127 file_selection_type = gtk_type_unique( GTK_TYPE_WINDOW, &filesel_info );
1130 return file_selection_type;
1134 gtk_file_selection_class_init( GtkFileSelectionClass *class ){
1135 GtkObjectClass *object_class;
1136 GtkWidgetClass *widget_class;
1138 object_class = (GtkObjectClass*) class;
1140 parent_class = gtk_type_class( GTK_TYPE_WINDOW );
1142 widget_class = GTK_WIDGET_CLASS( class );
1144 widget_class->realize = gtk_file_selection_realize;
1145 object_class->destroy = gtk_file_selection_destroy;
1149 gtk_file_selection_init( GtkFileSelection *filesel ){
1150 GtkWidget *entry_vbox;
1152 GtkWidget *list_vbox;
1153 GtkWidget *confirm_area;
1158 GtkWidget *pulldown_hbox;
1159 GtkWidget *scrolled_win;
1160 GtkWidget *mask_label;
1161 GtkWidget *bigframe;
1164 GtkWidget *menu_item;
1167 char *dir_title [2];
1168 char *file_title [2];
1170 filesel->cmpl_state = cmpl_init_state();
1172 filesel->mask = NULL;
1173 filesel->prev_history = NULL;
1174 filesel->next_history = NULL;
1175 filesel->saved_entry = NULL;
1176 filesel->bookmark_list = NULL;
1177 filesel->masks = NULL;
1178 filesel->selection_text = NULL;
1179 filesel->fileop_data = NULL;
1181 gtk_file_selection_load_masks( filesel );
1182 gtk_file_selection_load_bookmarks( filesel );
1184 /* The dialog-sized vertical box */
1185 filesel->main_vbox = gtk_vbox_new( FALSE, 0 );
1186 gtk_container_set_border_width( GTK_CONTAINER( filesel ), 0 );
1187 gtk_container_add( GTK_CONTAINER( filesel ), filesel->main_vbox );
1188 gtk_widget_show( filesel->main_vbox );
1190 /* hbox for pulldown menu */
1191 pulldown_hbox = gtk_hbox_new( FALSE, 0 );
1192 gtk_box_pack_start( GTK_BOX( filesel->main_vbox ), pulldown_hbox, FALSE, FALSE, 0 );
1193 gtk_widget_show( pulldown_hbox );
1195 /* The horizontal box containing create, rename etc. buttons */
1198 filesel->button_area = gtk_hbutton_box_new ();
1199 gtk_button_box_set_layout(GTK_BUTTON_BOX(filesel->button_area), GTK_BUTTONBOX_START);
1200 gtk_button_box_set_spacing(GTK_BUTTON_BOX(filesel->button_area), 0);
1201 gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->button_area,
1203 gtk_button_box_set_child_size(GTK_BUTTON_BOX(filesel->button_area),0,0);
1204 gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX(filesel->button_area),0,0);
1207 filesel->button_area = gtk_hbox_new( TRUE,0 );
1208 //gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->button_area,
1209 // FALSE, FALSE, 0);
1211 //gtk_widget_show (filesel->button_area);
1213 gtk_file_selection_show_fileop_buttons( filesel );
1214 /* frame to put the following hbox in */
1215 bigframe = gtk_frame_new( NULL );
1216 gtk_box_pack_start( GTK_BOX( filesel->main_vbox ), bigframe, TRUE, TRUE, 0 );
1217 gtk_frame_set_shadow_type( GTK_FRAME( bigframe ), GTK_SHADOW_OUT );
1218 gtk_widget_show( bigframe );
1221 list_vbox = gtk_vbox_new( FALSE,3 );
1222 gtk_widget_show( list_vbox );
1223 gtk_container_add( GTK_CONTAINER( bigframe ), list_vbox );
1224 gtk_container_set_border_width( GTK_CONTAINER( list_vbox ),2 );
1225 gtk_widget_show( list_vbox );
1227 /* The horizontal box containing the directory and file listboxes */
1228 // list_hbox = gtk_hbox_new (FALSE, 3);
1229 //gtk_container_add (GTK_CONTAINER(bigframe), list_hbox);
1230 //gtk_container_set_border_width (GTK_CONTAINER (list_hbox), 3);
1231 // gtk_box_pack_start(GTK_BOX(list_vbox), list_hbox, FALSE,FALSE,0);
1232 // gtk_widget_show (list_hbox);
1234 hpaned = gtk_hpaned_new();
1235 gtk_widget_show( hpaned );
1236 gtk_container_set_border_width( GTK_CONTAINER( hpaned ), 1 );
1237 gtk_paned_set_gutter_size( GTK_PANED( hpaned ), 10 );
1238 gtk_box_pack_start( GTK_BOX( list_vbox ), hpaned,TRUE,TRUE,0 );
1240 /* vbox to put the buttons and directory listing in */
1241 vbox = gtk_vbox_new( FALSE, 3 );
1242 gtk_widget_show( vbox );
1243 gtk_container_add( GTK_CONTAINER( hpaned ),vbox );
1244 //gtk_box_pack_start (GTK_BOX (hpaned), vbox, FALSE, FALSE, 0);
1246 hbox = gtk_hbox_new( FALSE, 4 );
1247 gtk_widget_show( hbox );
1248 gtk_box_pack_start( GTK_BOX( vbox ), hbox, FALSE, FALSE, 0 );
1250 // home_button = gtk_button_new_with_label (_("Home"));
1251 // gtk_widget_show (home_button);
1252 // gtk_signal_connect (GTK_OBJECT (home_button), "clicked",
1253 // (GtkSignalFunc) gtk_file_selection_home_button,
1254 // (gpointer) filesel);
1255 // gtk_box_pack_start (GTK_BOX (hbox), home_button, TRUE,TRUE, 0);
1257 /* Here we add the bookmark menu button */
1258 #define If we 're going to make bookmark a menu, we don't need
1259 #define to keep it in the filesel structure
1260 button = gtk_button_new_with_label( _( "Bookmarks" ) );
1261 gtk_widget_show( button );
1262 gtk_box_pack_start( GTK_BOX( hbox ), button, FALSE,FALSE,0 );
1263 gtk_signal_connect( GTK_OBJECT( button ), "clicked",
1264 (GtkSignalFunc) gtk_file_selection_bookmark_button,
1265 (gpointer) filesel );
1267 hbox2 = gtk_hbox_new( FALSE, 2 );
1268 gtk_box_pack_start( GTK_BOX( hbox ), hbox2, FALSE, FALSE, 0 );
1269 gtk_widget_show( hbox2 );
1272 button = gtk_button_new();
1273 gtk_signal_connect( GTK_OBJECT( button ), "clicked",
1274 (GtkSignalFunc) gtk_file_selection_prev_button,
1275 (gpointer) filesel );
1276 gtk_widget_show( button );
1277 gtk_box_pack_start( GTK_BOX( hbox2 ), button, FALSE,FALSE, 0 );
1278 pixmap = create_pixmap( filesel->main_vbox, (gpointer) back_xpm );
1279 gtk_widget_show( pixmap );
1280 gtk_container_add( GTK_CONTAINER( button ), pixmap );
1283 button = gtk_button_new();
1284 gtk_signal_connect( GTK_OBJECT( button ), "clicked",
1285 (GtkSignalFunc) gtk_file_selection_up_button,
1286 (gpointer) filesel );
1287 gtk_widget_show( button );
1288 gtk_box_pack_start( GTK_BOX( hbox2 ), button, FALSE,FALSE, 0 );
1289 pixmap = create_pixmap( filesel->main_vbox, (gpointer) up_xpm );
1290 gtk_widget_show( pixmap );
1291 gtk_container_add( GTK_CONTAINER( button ), pixmap );
1294 button = gtk_button_new();
1295 gtk_widget_show( button );
1296 gtk_signal_connect( GTK_OBJECT( button ), "clicked",
1297 (GtkSignalFunc) gtk_file_selection_next_button,
1298 (gpointer) filesel );
1299 gtk_box_pack_start( GTK_BOX( hbox2 ), button, FALSE,FALSE, 0 );
1300 pixmap = create_pixmap( filesel->main_vbox, (gpointer) forward_xpm );
1301 gtk_widget_show( pixmap );
1302 gtk_container_add( GTK_CONTAINER( button ), pixmap );
1304 /* refresh button */
1305 button = gtk_button_new();
1306 gtk_widget_show( button );
1307 gtk_signal_connect( GTK_OBJECT( button ), "clicked",
1308 (GtkSignalFunc) gtk_file_selection_refresh_button,
1309 (gpointer) filesel );
1310 gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE,FALSE, 0 );
1311 pixmap = create_pixmap( filesel->main_vbox, (gpointer) refresh_xpm );
1312 gtk_widget_show( pixmap );
1313 gtk_container_add( GTK_CONTAINER( button ), pixmap );
1315 /* menu for right click file operations */
1316 filesel->fileop_menu = gtk_menu_new();
1318 menu_item = gtk_menu_item_new_with_label( _( "Rename..." ) );
1319 gtk_widget_show( menu_item );
1320 gtk_signal_connect_object( GTK_OBJECT( menu_item ), "activate",
1321 (GtkSignalFunc) gtk_file_selection_rename_file,
1322 (gpointer) filesel );
1323 gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item );
1325 menu_item = gtk_menu_item_new_with_label( _( "Delete" ) );
1326 gtk_widget_show( menu_item );
1327 gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item );
1328 gtk_signal_connect_object( GTK_OBJECT( menu_item ), "activate",
1329 (GtkSignalFunc) gtk_file_selection_delete_file,
1330 (gpointer) filesel );
1332 menu_item = gtk_menu_item_new();
1333 gtk_widget_show( menu_item );
1334 gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item );
1336 menu_item = gtk_menu_item_new_with_label( _( "Create Directory..." ) );
1337 gtk_signal_connect_object( GTK_OBJECT( menu_item ), "activate",
1338 (GtkSignalFunc) gtk_file_selection_create_dir,
1339 (gpointer) filesel );
1340 gtk_widget_show( menu_item );
1341 gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item );
1343 menu_item = gtk_menu_item_new();
1344 gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item );
1345 gtk_widget_show( menu_item );
1347 menu_item = gtk_menu_item_new_with_label( _( "Properties..." ) );
1348 gtk_signal_connect_object( GTK_OBJECT( menu_item ), "activate",
1349 (GtkSignalFunc) gtk_file_selection_properties,
1350 (gpointer) filesel );
1351 gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item );
1352 gtk_widget_show( menu_item );
1354 /* The directories clist */
1355 dir_title[0] = _( "Directories" );
1356 dir_title[1] = NULL;
1357 filesel->dir_list = gtk_clist_new_with_titles( 1, (gchar**) dir_title );
1358 gtk_widget_set_usize( filesel->dir_list, DIR_LIST_WIDTH, DIR_LIST_HEIGHT );
1359 gtk_signal_connect( GTK_OBJECT( filesel->dir_list ), "select_row",
1360 (GtkSignalFunc) gtk_file_selection_dir_button,
1361 (gpointer) filesel );
1362 gtk_signal_connect( GTK_OBJECT( filesel->dir_list ), "unselect_row",
1363 (GtkSignalFunc) gtk_file_selection_undir_button,
1364 (gpointer) filesel );
1365 gtk_signal_connect( GTK_OBJECT( filesel->dir_list ), "button_press_event",
1366 GTK_SIGNAL_FUNC( gtk_file_selection_show_fileop_menu ),
1367 (gpointer) filesel );
1369 gtk_clist_column_titles_passive( GTK_CLIST( filesel->dir_list ) );
1371 scrolled_win = gtk_scrolled_window_new( NULL, NULL );
1372 gtk_container_add( GTK_CONTAINER( scrolled_win ), filesel->dir_list );
1373 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolled_win ),
1374 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1375 gtk_box_pack_start( GTK_BOX( vbox ), scrolled_win, TRUE,TRUE, 0 );
1376 //gtk_container_add(GTK_CONTAINER(hpaned), scrolled_win);
1378 gtk_widget_show( filesel->dir_list );
1379 gtk_widget_show( scrolled_win );
1381 vbox = gtk_vbox_new( FALSE, 3 );
1382 gtk_widget_show( vbox );
1383 gtk_container_add( GTK_CONTAINER( hpaned ),vbox );
1384 /* vbox area for mask entry and files clist */
1386 hbox = gtk_hbox_new( FALSE, 2 );
1387 gtk_widget_show( hbox );
1388 gtk_box_pack_start( GTK_BOX( vbox ), hbox, FALSE, FALSE, 0 );
1390 mask_label = gtk_label_new( _( "Mask:" ) );
1391 gtk_widget_show( mask_label );
1392 gtk_box_pack_start( GTK_BOX( hbox ), mask_label, FALSE, FALSE, 2 );
1395 filesel->mask_entry = gtk_entry_new ();
1396 gtk_widget_show (filesel->mask_entry);
1397 gtk_signal_connect(GTK_OBJECT(filesel->mask_entry),"activate",
1398 (GtkSignalFunc) gtk_file_4_mask_entry_callback,
1399 (gpointer) filesel);
1400 gtk_box_pack_start (GTK_BOX (hbox),filesel->mask_entry, TRUE, TRUE, 0);
1403 filesel->mask_entry = gtk_combo_new();
1404 gtk_widget_show( filesel->mask_entry );
1405 gtk_combo_set_value_in_list( GTK_COMBO( filesel->mask_entry ),FALSE,FALSE );
1406 gtk_signal_connect( GTK_OBJECT( GTK_COMBO( filesel->mask_entry )->entry ),"activate",
1407 (GtkSignalFunc) gtk_file_selection_mask_entry_callback,
1408 (gpointer) filesel );
1409 gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->mask_entry )->entry ),"key-press-event",
1410 (GtkSignalFunc) gtk_file_selection_mask_entry_key_callback,
1411 (gpointer) filesel );
1413 gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->mask_entry )->list ),"button-release-event",
1414 (GtkSignalFunc) gtk_file_selection_mask_entry_button_callback,
1415 (gpointer) filesel );
1416 gtk_box_pack_start( GTK_BOX( hbox ),filesel->mask_entry, TRUE, TRUE, 0 );
1418 if ( filesel->masks ) {
1419 gtk_combo_set_popdown_strings( GTK_COMBO( filesel->mask_entry ), filesel->masks );
1423 /* The files clist */
1424 file_title[0] = _( "Files" );
1425 file_title[1] = NULL;
1426 filesel->file_list = gtk_clist_new_with_titles( 1, (gchar**) file_title );
1427 gtk_widget_set_usize( filesel->file_list, FILE_LIST_WIDTH, FILE_LIST_HEIGHT );
1428 gtk_signal_connect( GTK_OBJECT( filesel->file_list ), "select_row",
1429 (GtkSignalFunc) gtk_file_selection_file_button,
1430 (gpointer) filesel );
1431 gtk_signal_connect( GTK_OBJECT( filesel->file_list ), "key-press-event",
1432 (GtkSignalFunc) gtk_file_selection_files_list_key_callback,
1433 (gpointer) filesel );
1435 gtk_signal_connect( GTK_OBJECT( filesel->file_list ), "button_press_event",
1436 GTK_SIGNAL_FUNC( gtk_file_selection_show_fileop_menu ),
1437 (gpointer) filesel );
1439 gtk_clist_column_titles_passive( GTK_CLIST( filesel->file_list ) );
1441 scrolled_win = gtk_scrolled_window_new( NULL, NULL );
1442 gtk_container_add( GTK_CONTAINER( scrolled_win ), filesel->file_list );
1443 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolled_win ),
1444 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1445 gtk_box_pack_start( GTK_BOX( vbox ), scrolled_win, TRUE, TRUE, 0 );
1446 gtk_widget_show( filesel->file_list );
1447 gtk_widget_show( scrolled_win );
1449 /* action area for packing buttons into. */
1450 filesel->action_area = gtk_hbox_new( TRUE, 0 );
1451 gtk_box_pack_start( GTK_BOX( filesel->main_vbox ), filesel->action_area,
1453 gtk_widget_show( filesel->action_area );
1456 hbox=gtk_hbox_new(FALSE,0);
1457 gtk_box_pack_end (GTK_BOX (filesel->main_vbox), hbox, FALSE,FALSE, 0);
1458 gtk_widget_show (hbox);
1461 /* The selection entry widget */
1463 entry_vbox = gtk_vbox_new( FALSE, 0 );
1464 gtk_box_pack_end( GTK_BOX( filesel->main_vbox ), entry_vbox, FALSE, FALSE, 0 );
1465 gtk_widget_show( entry_vbox );
1467 table = gtk_table_new( 2, 2, FALSE );
1468 gtk_box_pack_start( GTK_BOX( entry_vbox ), table, TRUE, TRUE, 0 );
1469 gtk_container_set_border_width( GTK_CONTAINER( table ), 4 );
1470 gtk_table_set_row_spacings( GTK_TABLE( table ), 2 );
1471 gtk_table_set_col_spacings( GTK_TABLE( table ), 4 );
1474 label = gtk_label_new( _( "Selection:" ) );
1475 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
1476 (GtkAttachOptions) ( 0 ),
1477 (GtkAttachOptions) ( 0 ), 0, 0 );
1478 gtk_widget_show( label );
1481 filesel->selection_entry = gtk_entry_new();
1482 gtk_signal_connect( GTK_OBJECT( filesel->selection_entry ), "key_press_event",
1483 (GtkSignalFunc) gtk_file_selection_key_press, filesel );
1484 gtk_table_attach( GTK_TABLE( table ), filesel->selection_entry, 1, 2, 0, 1,
1485 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
1486 (GtkAttachOptions) ( 0 ), 0, 0 );
1487 gtk_widget_show( filesel->selection_entry );
1490 label = gtk_label_new( _( "Directory:" ) );
1491 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
1492 (GtkAttachOptions) ( 0 ),
1493 (GtkAttachOptions) ( 0 ), 0, 0 );
1494 gtk_widget_show( label );
1497 filesel->history_combo = gtk_combo_new();
1498 gtk_combo_set_value_in_list( GTK_COMBO( filesel->history_combo ),FALSE,FALSE );
1499 gtk_table_attach( GTK_TABLE( table ), filesel->history_combo, 1, 2, 1, 2,
1500 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
1501 (GtkAttachOptions) ( 0 ), 0, 0 );
1502 gtk_widget_show( filesel->history_combo );
1504 gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->history_combo )->entry ),"key-press-event",
1505 (GtkSignalFunc) gtk_file_selection_history_combo_callback,
1506 (gpointer) filesel );
1508 gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->history_combo )->list ),"button-press-event",
1509 (GtkSignalFunc) gtk_file_selection_history_combo_list_callback,
1510 (gpointer) filesel );
1512 gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->history_combo )->list ),"key-press-event",
1513 (GtkSignalFunc) gtk_file_selection_history_combo_list_key_handler,
1514 (gpointer) filesel );
1516 filesel->selection_text = NULL;
1519 /* The OK/Cancel button area */
1520 confirm_area = gtk_hbutton_box_new();
1521 gtk_button_box_set_layout( GTK_BUTTON_BOX( confirm_area ), GTK_BUTTONBOX_END );
1522 gtk_button_box_set_spacing( GTK_BUTTON_BOX( confirm_area ), 5 );
1523 gtk_box_pack_end( GTK_BOX( entry_vbox ), confirm_area, FALSE, FALSE, 0 );
1524 gtk_widget_show( confirm_area );
1527 filesel->ok_button = gtk_button_new_with_label( _( "OK" ) );
1528 GTK_WIDGET_SET_FLAGS( filesel->ok_button, GTK_CAN_DEFAULT );
1529 gtk_box_pack_start( GTK_BOX( confirm_area ), filesel->ok_button, TRUE, TRUE, 0 );
1530 gtk_signal_connect_object( GTK_OBJECT( filesel->selection_entry ), "focus_in_event",
1531 (GtkSignalFunc) gtk_widget_grab_default,
1532 GTK_OBJECT( filesel->ok_button ) );
1533 gtk_signal_connect_object( GTK_OBJECT( filesel->selection_entry ), "activate",
1534 (GtkSignalFunc) gtk_button_clicked,
1535 GTK_OBJECT( filesel->ok_button ) );
1536 gtk_widget_grab_default( filesel->ok_button );
1537 gtk_widget_show( filesel->ok_button );
1539 /* The Cancel button */
1540 filesel->cancel_button = gtk_button_new_with_label( _( "Cancel" ) );
1541 GTK_WIDGET_SET_FLAGS( filesel->cancel_button, GTK_CAN_DEFAULT );
1542 gtk_box_pack_start( GTK_BOX( confirm_area ), filesel->cancel_button, TRUE, TRUE, 0 );
1543 gtk_widget_show( filesel->cancel_button );
1545 gtk_widget_show( table );
1549 filesel->selection_text = label = gtk_label_new ("");
1550 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1551 gtk_box_pack_start (GTK_BOX (entry_vbox), label, FALSE, FALSE, 0);
1552 gtk_widget_show (label);
1556 if ( !cmpl_state_okay( filesel->cmpl_state ) ) {
1559 sprintf( err_buf, _( "Directory unreadable: %s" ), cmpl_strerror( cmpl_errno ) );
1562 gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
1567 gtk_file_selection_populate( filesel, "", FALSE );
1570 gtk_widget_grab_focus( filesel->selection_entry );
1574 gtk_file_selection_new( const gchar *title ){
1575 GtkFileSelection *filesel;
1577 filesel = gtk_type_new( GTK_TYPE_FILE_SELECTION );
1578 gtk_window_set_title( GTK_WINDOW( filesel ), title );
1579 /* !!! put check here to figure out if screen > 640x480, if true
1580 We need to make the file selection dialog bigger. much bigger..
1581 or maybe we should keep it at a certan percentage of the screen
1584 gtk_window_set_default_size( GTK_WINDOW( filesel ), 520, 420 );
1585 return GTK_WIDGET( filesel );
1589 gtk_file_selection_show_fileop_buttons( GtkFileSelection *filesel ){
1590 g_return_if_fail( filesel != NULL );
1591 g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) );
1595 /* delete, create directory, and rename */
1597 if (!filesel->fileop_c_dir)
1599 filesel->fileop_c_dir = gtk_button_new_with_label (_("MkDir"));
1600 gtk_signal_connect (GTK_OBJECT (filesel->fileop_c_dir), "clicked",
1601 (GtkSignalFunc) gtk_file_selection_create_dir,
1602 (gpointer) filesel);
1603 gtk_box_pack_start (GTK_BOX (filesel->button_area),
1604 filesel->fileop_c_dir, TRUE,TRUE, 0);
1605 gtk_widget_show (filesel->fileop_c_dir);
1608 if (!filesel->fileop_del_file)
1610 filesel->fileop_del_file = gtk_button_new_with_label (_("Delete"));
1611 gtk_signal_connect (GTK_OBJECT (filesel->fileop_del_file), "clicked",
1612 (GtkSignalFunc) gtk_file_selection_delete_file,
1613 (gpointer) filesel);
1614 gtk_box_pack_start (GTK_BOX (filesel->button_area),
1615 filesel->fileop_del_file, TRUE,TRUE, 0);
1616 gtk_widget_show (filesel->fileop_del_file);
1619 if (!filesel->fileop_ren_file)
1621 filesel->fileop_ren_file = gtk_button_new_with_label (_("Rename"));
1622 gtk_signal_connect (GTK_OBJECT (filesel->fileop_ren_file), "clicked",
1623 (GtkSignalFunc) gtk_file_selection_rename_file,
1624 (gpointer) filesel);
1625 gtk_box_pack_start (GTK_BOX (filesel->button_area),
1626 filesel->fileop_ren_file, TRUE,TRUE, 0);
1627 gtk_widget_show (filesel->fileop_ren_file);
1630 gtk_widget_queue_resize(GTK_WIDGET(filesel));
1635 gtk_file_selection_hide_fileop_buttons( GtkFileSelection *filesel ){
1636 g_return_if_fail( filesel != NULL );
1637 g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) );
1641 if (filesel->fileop_ren_file)
1643 gtk_widget_destroy (filesel->fileop_ren_file);
1644 filesel->fileop_ren_file = NULL;
1647 if (filesel->fileop_del_file)
1649 gtk_widget_destroy (filesel->fileop_del_file);
1650 filesel->fileop_del_file = NULL;
1653 if (filesel->fileop_c_dir)
1655 gtk_widget_destroy (filesel->fileop_c_dir);
1656 filesel->fileop_c_dir = NULL;
1664 gtk_file_selection_set_filename( GtkFileSelection *filesel,
1665 const gchar *filename ){
1666 char buf[MAXPATHLEN];
1667 const char *name, *last_slash;
1669 g_return_if_fail( filesel != NULL );
1670 g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) );
1671 g_return_if_fail( filename != NULL );
1673 last_slash = strrchr( filename, '/' );
1675 if ( !last_slash ) {
1681 gint len = MIN( MAXPATHLEN - 1, last_slash - filename + 1 );
1683 strncpy( buf, filename, len );
1686 name = last_slash + 1;
1689 gtk_file_selection_populate( filesel, buf, FALSE );
1691 if ( filesel->selection_entry ) {
1692 gtk_entry_set_text( GTK_ENTRY( filesel->selection_entry ), name );
1697 gtk_file_selection_get_filename( GtkFileSelection *filesel ){
1698 static char nothing[2] = "";
1702 g_return_val_if_fail( filesel != NULL, nothing );
1703 g_return_val_if_fail( GTK_IS_FILE_SELECTION( filesel ), nothing );
1705 text = gtk_entry_get_text( GTK_ENTRY( filesel->selection_entry ) );
1707 filename = cmpl_completion_fullname( text, filesel->cmpl_state );
1715 gtk_file_selection_complete( GtkFileSelection *filesel,
1716 const gchar *pattern ){
1720 g_return_if_fail( filesel != NULL );
1721 g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) );
1722 g_return_if_fail( pattern != NULL );
1724 if ( filesel->selection_entry ) {
1725 gtk_entry_set_text( GTK_ENTRY( filesel->selection_entry ), pattern );
1728 if ( strchr( pattern,'*' ) || strchr( pattern,'?' ) ) {
1729 for ( x = strlen( pattern ); x >= 0; x-- )
1731 if ( pattern[x] == '/' ) {
1735 gtk_entry_set_text( GTK_ENTRY( filesel->mask_entry ),g_strdup( pattern + x + 1 ) );
1737 if ( filesel->mask ) {
1738 g_free( filesel->mask );
1741 filesel->mask = g_strdup( pattern + x + 1 );
1742 new_pattern = g_strdup( pattern );
1743 new_pattern[x + 1] = 0;
1744 gtk_file_selection_populate( filesel, (gchar*) new_pattern, TRUE );
1745 g_free( new_pattern );
1749 gtk_file_selection_populate( filesel, (gchar*) pattern, TRUE );
1754 gtk_file_selection_realize( GtkWidget *widget ){
1755 GtkFileSelection *filesel;
1756 const gchar *masks[] = { "All Files <*>", NULL };
1758 g_return_if_fail( widget != NULL );
1759 g_return_if_fail( GTK_IS_FILE_SELECTION( widget ) );
1761 filesel = GTK_FILE_SELECTION( widget );
1763 /* make sure that we have at least one mask */
1764 if ( !filesel->masks ) {
1765 gtk_file_selection_set_masks( filesel, masks );
1768 filesel->mask = g_strdup( (gchar*) filesel->masks->data );
1769 gtk_file_selection_populate( filesel, "", FALSE );
1772 if ( GTK_WIDGET_CLASS( parent_class )->realize ) {
1773 ( *GTK_WIDGET_CLASS( parent_class )->realize )( widget );
1778 gtk_file_selection_destroy( GtkObject *object ){
1779 GtkFileSelection *filesel;
1782 g_return_if_fail( object != NULL );
1783 g_return_if_fail( GTK_IS_FILE_SELECTION( object ) );
1785 filesel = GTK_FILE_SELECTION( object );
1787 if ( filesel->fileop_dialog ) {
1788 gtk_widget_destroy( filesel->fileop_dialog );
1791 if ( filesel->next_history ) {
1792 list = filesel->next_history;
1795 g_free( list->data );
1799 g_list_free( filesel->next_history );
1800 filesel->next_history = NULL;
1802 if ( filesel->prev_history ) {
1803 list = filesel->prev_history;
1806 g_free( list->data );
1810 g_list_free( filesel->prev_history );
1811 filesel->prev_history = NULL;
1813 if ( filesel->mask ) {
1814 g_free( filesel->mask );
1815 filesel->mask = NULL;
1818 cmpl_free_state( filesel->cmpl_state );
1819 filesel->cmpl_state = NULL;
1821 if ( GTK_OBJECT_CLASS( parent_class )->destroy ) {
1822 ( *GTK_OBJECT_CLASS( parent_class )->destroy )( object );
1826 /* Begin file operations callbacks */
1829 gtk_file_selection_show_fileop_menu( GtkCList *clist, GdkEvent *event, GtkFileSelection *fs ){
1830 GdkEventButton *event_button;
1832 g_return_val_if_fail( clist != NULL, FALSE );
1833 g_return_val_if_fail( GTK_IS_CLIST( clist ), FALSE );
1834 g_return_val_if_fail( event != NULL, FALSE );
1835 g_return_val_if_fail( fs != NULL, FALSE );
1836 g_return_val_if_fail( GTK_FILE_SELECTION( fs ), FALSE );
1838 if ( event->type == GDK_BUTTON_PRESS ) {
1839 event_button = (GdkEventButton *) event;
1840 if ( event_button->button == 3 ) {
1842 gtk_menu_popup( GTK_MENU( fs->fileop_menu ), NULL, NULL, NULL, NULL,
1843 event_button->button, event_button->time );
1852 gtk_file_selection_fileop_error( GtkFileSelection *fs, gchar *error_message ){
1858 g_return_if_fail( error_message != NULL );
1861 dialog = gtk_dialog_new();
1863 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1864 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1867 gtk_window_set_title( GTK_WINDOW( dialog ), _( "Error" ) );
1868 gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE );
1870 /* If file dialog is grabbed, make this dialog modal too */
1871 /* When error dialog is closed, file dialog will be grabbed again */
1872 if ( GTK_WINDOW( fs )->modal ) {
1873 gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
1876 vbox = gtk_vbox_new( FALSE, 0 );
1877 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 8 );
1878 gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), vbox,
1880 gtk_widget_show( vbox );
1882 label = gtk_label_new( error_message );
1883 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.0 );
1884 gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 5 );
1885 gtk_widget_show( label );
1887 /* yes, we free it */
1888 g_free( error_message );
1891 button = gtk_button_new_with_label( _( "Close" ) );
1892 gtk_signal_connect_object( GTK_OBJECT( button ), "clicked",
1893 (GtkSignalFunc) gtk_widget_destroy,
1894 (gpointer) dialog );
1895 gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
1896 button, TRUE, TRUE, 0 );
1897 GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
1898 gtk_widget_grab_default( button );
1899 gtk_widget_show( button );
1901 gtk_widget_show( dialog );
1905 gtk_file_selection_fileop_destroy( GtkWidget *widget, gpointer data ){
1906 GtkFileSelection *fs = data;
1908 g_return_if_fail( fs != NULL );
1909 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1911 fs->fileop_dialog = NULL;
1912 g_free( fs->fileop_data );
1913 fs->fileop_data = NULL;
1918 gtk_file_selection_create_dir_confirmed( GtkWidget *widget, gpointer data ){
1919 GtkFileSelection *fs = data;
1924 CompletionState *cmpl_state;
1926 g_return_if_fail( fs != NULL );
1927 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1929 dirname = gtk_entry_get_text( GTK_ENTRY( fs->fileop_entry ) );
1930 cmpl_state = (CompletionState*) fs->cmpl_state;
1931 path = cmpl_reference_position( cmpl_state );
1933 full_path = g_strconcat( path, "/", dirname, NULL );
1934 if ( ( mkdir( full_path, 0755 ) < 0 ) ) {
1935 buf = g_strconcat( "Error creating directory \"", dirname, "\": ",
1936 g_strerror( errno ), NULL );
1937 gtk_file_selection_fileop_error( fs, buf );
1939 g_free( full_path );
1941 gtk_widget_destroy( fs->fileop_dialog );
1942 gtk_file_selection_populate( fs, "", FALSE );
1946 gtk_file_selection_create_dir( gpointer data ){
1947 GtkFileSelection *fs = data;
1953 g_return_if_fail( fs != NULL );
1954 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1956 if ( fs->fileop_dialog ) {
1961 fs->fileop_dialog = dialog = gtk_dialog_new();
1962 gtk_signal_connect( GTK_OBJECT( dialog ), "destroy",
1963 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1965 gtk_window_set_title( GTK_WINDOW( dialog ), _( "Create Directory" ) );
1966 gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE );
1968 /* If file dialog is grabbed, grab option dialog */
1969 /* When option dialog is closed, file dialog will be grabbed again */
1970 if ( GTK_WINDOW( fs )->modal ) {
1971 gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
1974 vbox = gtk_vbox_new( FALSE, 0 );
1975 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 8 );
1976 gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), vbox,
1978 gtk_widget_show( vbox );
1980 label = gtk_label_new( _( "Directory name:" ) );
1981 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.0 );
1982 gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 5 );
1983 gtk_widget_show( label );
1985 /* The directory entry widget */
1986 fs->fileop_entry = gtk_entry_new();
1987 gtk_box_pack_start( GTK_BOX( vbox ), fs->fileop_entry,
1989 GTK_WIDGET_SET_FLAGS( fs->fileop_entry, GTK_CAN_DEFAULT );
1990 gtk_widget_show( fs->fileop_entry );
1993 button = gtk_button_new_with_label( _( "Create" ) );
1994 gtk_signal_connect( GTK_OBJECT( button ), "clicked",
1995 (GtkSignalFunc) gtk_file_selection_create_dir_confirmed,
1997 gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
1998 button, TRUE, TRUE, 0 );
1999 GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
2000 gtk_widget_show( button );
2002 button = gtk_button_new_with_label( _( "Cancel" ) );
2003 gtk_signal_connect_object( GTK_OBJECT( button ), "clicked",
2004 (GtkSignalFunc) gtk_widget_destroy,
2005 (gpointer) dialog );
2006 gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
2007 button, TRUE, TRUE, 0 );
2008 GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
2009 gtk_widget_grab_default( button );
2010 gtk_widget_show( button );
2012 gtk_widget_show( dialog );
2016 gtk_file_selection_delete_file_confirmed( GtkWidget *widget, gpointer data ){
2017 GtkFileSelection *fs = data;
2018 CompletionState *cmpl_state;
2023 g_return_if_fail( fs != NULL );
2024 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2026 cmpl_state = (CompletionState*) fs->cmpl_state;
2027 path = cmpl_reference_position( cmpl_state );
2029 full_path = g_strconcat( path, "/", fs->fileop_file, NULL );
2030 if ( ( unlink( full_path ) < 0 ) ) {
2031 buf = g_strconcat( "Error deleting file \"", fs->fileop_file, "\": ",
2032 g_strerror( errno ), NULL );
2033 gtk_file_selection_fileop_error( fs, buf );
2035 g_free( full_path );
2037 gtk_widget_destroy( fs->fileop_dialog );
2038 gtk_file_selection_populate( fs, "", FALSE );
2042 gtk_file_selection_delete_file( gpointer data ){
2043 GtkFileSelection *fs = data;
2051 g_return_if_fail( fs != NULL );
2052 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2054 if ( fs->fileop_dialog ) {
2058 filename = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) );
2059 if ( strlen( filename ) < 1 ) {
2063 fs->fileop_file = filename;
2066 fs->fileop_dialog = dialog = gtk_dialog_new();
2067 gtk_signal_connect( GTK_OBJECT( dialog ), "destroy",
2068 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
2070 gtk_window_set_title( GTK_WINDOW( dialog ), _( "Delete File" ) );
2071 gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE );
2073 /* If file dialog is grabbed, grab option dialog */
2074 /* When option dialog is closed, file dialog will be grabbed again */
2075 if ( GTK_WINDOW( fs )->modal ) {
2076 gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
2079 vbox = gtk_vbox_new( FALSE, 0 );
2080 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 8 );
2081 gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), vbox,
2083 gtk_widget_show( vbox );
2085 buf = g_strconcat( "Really delete file \"", filename, "\" ?", NULL );
2086 label = gtk_label_new( buf );
2087 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.0 );
2088 gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 5 );
2089 gtk_widget_show( label );
2093 button = gtk_button_new_with_label( _( "Delete" ) );
2094 gtk_signal_connect( GTK_OBJECT( button ), "clicked",
2095 (GtkSignalFunc) gtk_file_selection_delete_file_confirmed,
2097 gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
2098 button, TRUE, TRUE, 0 );
2099 GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
2100 gtk_widget_show( button );
2102 button = gtk_button_new_with_label( _( "Cancel" ) );
2103 gtk_signal_connect_object( GTK_OBJECT( button ), "clicked",
2104 (GtkSignalFunc) gtk_widget_destroy,
2105 (gpointer) dialog );
2106 gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
2107 button, TRUE, TRUE, 0 );
2108 GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
2109 gtk_widget_grab_default( button );
2110 gtk_widget_show( button );
2112 gtk_widget_show( dialog );
2117 gtk_file_selection_rename_file_confirmed( GtkWidget *widget, gpointer data ){
2118 GtkFileSelection *fs = data;
2122 gchar *new_filename;
2123 gchar *old_filename;
2124 CompletionState *cmpl_state;
2126 g_return_if_fail( fs != NULL );
2127 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2129 file = gtk_entry_get_text( GTK_ENTRY( fs->fileop_entry ) );
2130 cmpl_state = (CompletionState*) fs->cmpl_state;
2131 path = cmpl_reference_position( cmpl_state );
2133 new_filename = g_strconcat( path, "/", file, NULL );
2134 old_filename = g_strconcat( path, "/", fs->fileop_file, NULL );
2136 if ( strcmp( new_filename, old_filename ) ) {
2137 if ( ( rename( old_filename, new_filename ) ) < 0 ) {
2138 buf = g_strconcat( "Error renaming file \"", file, "\": ",
2139 g_strerror( errno ), NULL );
2140 gtk_file_selection_fileop_error( fs, buf );
2143 g_free( new_filename );
2144 g_free( old_filename );
2146 gtk_widget_destroy( fs->fileop_dialog );
2147 gtk_file_selection_populate( fs, "", FALSE );
2151 gtk_file_selection_file_mode_confirmed( GtkWidget *widget, gpointer data ){
2152 GtkFileSelection *fs = data;
2153 PropertiesPrivate *priv = fs->fileop_data;
2154 CompletionState *cmpl_state;
2155 gchar *filename, *file, *path;
2158 mode = gtk_file_selection_properties_get_mode( priv );
2160 file = gtk_entry_get_text( GTK_ENTRY( fs->fileop_entry ) );
2161 cmpl_state = (CompletionState*) fs->cmpl_state;
2162 path = cmpl_reference_position( cmpl_state );
2164 filename = g_strconcat( path, "/", file, NULL );
2165 if ( chmod( filename, mode ) == -1 ) {
2166 gchar *buf = g_strconcat( "Error changing file mode of \"", filename, "\": ",
2167 g_strerror( errno ), NULL );
2168 gtk_file_selection_fileop_error( fs, buf );
2169 gtk_widget_destroy( fs->fileop_dialog );
2170 gtk_file_selection_populate( fs, "", FALSE );
2173 gtk_file_selection_rename_file_confirmed( widget, data );
2180 gtk_file_selection_rename_file( gpointer data ){
2181 GtkFileSelection *fs = data;
2188 g_return_if_fail( fs != NULL );
2189 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2191 if ( fs->fileop_dialog ) {
2195 fs->fileop_file = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) );
2196 if ( strlen( fs->fileop_file ) < 1 ) {
2201 fs->fileop_dialog = dialog = gtk_dialog_new();
2202 gtk_signal_connect( GTK_OBJECT( dialog ), "destroy",
2203 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
2205 gtk_window_set_title( GTK_WINDOW( dialog ), _( "Rename File" ) );
2206 gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE );
2208 /* If file dialog is grabbed, grab option dialog */
2209 /* When option dialog closed, file dialog will be grabbed again */
2210 if ( GTK_WINDOW( fs )->modal ) {
2211 gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
2214 vbox = gtk_vbox_new( FALSE, 0 );
2215 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 8 );
2216 gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), vbox,
2218 gtk_widget_show( vbox );
2220 buf = g_strconcat( "Rename file \"", fs->fileop_file, "\" to:", NULL );
2221 label = gtk_label_new( buf );
2222 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.0 );
2223 gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 5 );
2224 gtk_widget_show( label );
2227 /* New filename entry */
2228 fs->fileop_entry = gtk_entry_new();
2229 gtk_box_pack_start( GTK_BOX( vbox ), fs->fileop_entry,
2231 GTK_WIDGET_SET_FLAGS( fs->fileop_entry, GTK_CAN_DEFAULT );
2232 gtk_widget_show( fs->fileop_entry );
2234 gtk_entry_set_text( GTK_ENTRY( fs->fileop_entry ), fs->fileop_file );
2235 gtk_editable_select_region( GTK_EDITABLE( fs->fileop_entry ),
2236 0, strlen( fs->fileop_file ) );
2239 button = gtk_button_new_with_label( _( "Rename" ) );
2240 gtk_signal_connect( GTK_OBJECT( button ), "clicked",
2241 (GtkSignalFunc) gtk_file_selection_rename_file_confirmed,
2243 gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
2244 button, TRUE, TRUE, 0 );
2245 GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
2246 gtk_widget_show( button );
2248 button = gtk_button_new_with_label( _( "Cancel" ) );
2249 gtk_signal_connect_object( GTK_OBJECT( button ), "clicked",
2250 (GtkSignalFunc) gtk_widget_destroy,
2251 (gpointer) dialog );
2252 gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
2253 button, TRUE, TRUE, 0 );
2254 GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
2255 gtk_widget_grab_default( button );
2256 gtk_widget_show( button );
2258 gtk_widget_show( dialog );
2262 gtk_file_selection_properties_get_mode( PropertiesPrivate* priv ){
2265 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[0] ) ) ) {
2268 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[1] ) ) ) {
2271 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[2] ) ) ) {
2274 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[3] ) ) ) {
2277 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[4] ) ) ) {
2280 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[5] ) ) ) {
2283 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[6] ) ) ) {
2286 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[7] ) ) ) {
2289 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[8] ) ) ) {
2292 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[9] ) ) ) {
2295 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[10] ) ) ) {
2298 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[11] ) ) ) {
2306 gtk_file_selection_properties_update_mode( GtkWidget *widget, gpointer data ){
2307 GtkFileSelection *fs = data;
2308 PropertiesPrivate *priv = fs->fileop_data;
2311 sprintf( str, "(%.4o)", gtk_file_selection_properties_get_mode( priv ) );
2312 gtk_label_set( GTK_LABEL( priv->mode_label ), str );
2316 gtk_file_selection_properties( gpointer data ){
2317 GtkFileSelection *fs = data;
2323 GtkWidget *notebook;
2325 GtkWidget *hseparator;
2327 GtkWidget *togglebutton;
2328 struct stat statbuf;
2334 gchar timeBuf[TIME_STRING_BUF];
2336 PropertiesPrivate *priv;
2339 g_return_if_fail( fs != NULL );
2340 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2342 if ( fs->fileop_dialog ) {
2347 fs->fileop_dialog = dialog = gtk_dialog_new();
2348 gtk_signal_connect( GTK_OBJECT( dialog ), "destroy",
2349 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
2351 priv = fs->fileop_data = g_malloc( sizeof( PropertiesPrivate ) );
2353 gtk_window_set_title( GTK_WINDOW( dialog ), ( "Properties" ) );
2354 gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE );
2356 /* If file dialog is grabbed, grab option dialog */
2357 /* When option dialog closed, file dialog will be grabbed again */
2358 if ( GTK_WINDOW( fs )->modal ) {
2359 gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
2362 /* Dialog guts go here */
2363 notebook = gtk_notebook_new();
2364 gtk_widget_show( notebook );
2365 gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), notebook, TRUE, TRUE, 0 );
2366 gtk_container_set_border_width( GTK_CONTAINER( notebook ), 8 );
2368 path = cmpl_reference_position( fs->cmpl_state );
2369 fs->fileop_file = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) );
2370 filename = g_strconcat( path, "/", fs->fileop_file, NULL );
2371 if ( strlen( fs->fileop_file ) > 0 && !( stat( filename, &statbuf ) ) ) {
2373 table = gtk_table_new( 9, 2, FALSE );
2374 gtk_widget_show( table );
2375 gtk_container_add( GTK_CONTAINER( notebook ), table );
2376 gtk_container_set_border_width( GTK_CONTAINER( table ), 5 );
2377 gtk_table_set_row_spacings( GTK_TABLE( table ), 4 );
2378 gtk_table_set_col_spacings( GTK_TABLE( table ), 6 );
2380 label = gtk_label_new( _( "Statistics" ) );
2381 gtk_widget_show( label );
2382 gtk_notebook_set_tab_label( GTK_NOTEBOOK( notebook ), gtk_notebook_get_nth_page( GTK_NOTEBOOK( notebook ), pagenum ), label );
2384 /* path and filename */
2385 label = gtk_label_new( _( "Path:" ) );
2386 gtk_widget_show( label );
2387 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2388 (GtkAttachOptions) ( GTK_FILL ),
2389 (GtkAttachOptions) ( 0 ), 0, 0 );
2390 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2392 label = gtk_label_new( _( path ) );
2393 gtk_widget_show( label );
2394 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 0, 1,
2395 (GtkAttachOptions) ( GTK_FILL ),
2396 (GtkAttachOptions) ( 0 ), 0, 0 );
2397 gtk_label_set_justify( GTK_LABEL( label ), GTK_JUSTIFY_LEFT );
2398 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2400 label = gtk_label_new( _( "File Name:" ) );
2401 gtk_widget_show( label );
2402 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2403 (GtkAttachOptions) ( GTK_FILL ),
2404 (GtkAttachOptions) ( 0 ), 0, 0 );
2405 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2407 fs->fileop_entry = entry = gtk_entry_new();
2408 gtk_widget_show( entry );
2409 gtk_table_attach( GTK_TABLE( table ), entry, 1, 2, 1, 2,
2410 (GtkAttachOptions) ( GTK_FILL ),
2411 (GtkAttachOptions) ( 0 ), 0, 0 );
2412 gtk_entry_set_text( GTK_ENTRY( entry ), fs->fileop_file );
2413 if ( access( filename, W_OK ) ) {
2414 gtk_widget_set_sensitive( GTK_WIDGET( entry ), FALSE );
2417 hseparator = gtk_hseparator_new();
2418 gtk_widget_show( hseparator );
2419 gtk_table_attach( GTK_TABLE( table ), hseparator, 0, 2, 2, 3,
2420 (GtkAttachOptions) ( GTK_FILL ),
2421 (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
2423 /* file type and size */
2424 label = gtk_label_new( _( "Type:" ) );
2425 gtk_widget_show( label );
2426 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 3, 4,
2427 (GtkAttachOptions) ( GTK_FILL ),
2428 (GtkAttachOptions) ( 0 ), 0, 0 );
2429 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2431 switch ( statbuf.st_mode & S_IFMT )
2434 buf = g_strdup( "Socket" );
2437 buf = g_strdup( "Symbolic link" );
2440 buf = g_strdup( "File" );
2443 buf = g_strdup( "Block device" );
2446 buf = g_strdup( "Directory" );
2449 buf = g_strdup( "Character device" );
2452 buf = g_strdup( "First-in/first-out pipe" );
2455 buf = g_strdup( "Unknown" );
2459 label = gtk_label_new( buf );
2460 gtk_widget_show( label );
2461 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 3, 4,
2462 (GtkAttachOptions) ( GTK_FILL ),
2463 (GtkAttachOptions) ( 0 ), 0, 0 );
2464 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2466 label = gtk_label_new( _( "Size:" ) );
2467 gtk_widget_show( label );
2468 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 4, 5,
2469 (GtkAttachOptions) ( GTK_FILL ),
2470 (GtkAttachOptions) ( 0 ), 0, 0 );
2471 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2473 label = gtk_label_new( _( g_strdup_printf( "%ld bytes", statbuf.st_size ) ) );
2474 gtk_widget_show( label );
2475 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 4, 5,
2476 (GtkAttachOptions) ( GTK_FILL ),
2477 (GtkAttachOptions) ( 0 ), 0, 0 );
2478 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2480 hseparator = gtk_hseparator_new();
2481 gtk_widget_show( hseparator );
2482 gtk_table_attach( GTK_TABLE( table ), hseparator, 0, 2, 5, 6,
2483 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2484 (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
2487 label = gtk_label_new( _( "Created:" ) );
2488 gtk_widget_show( label );
2489 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 6, 7,
2490 (GtkAttachOptions) ( GTK_FILL ),
2491 (GtkAttachOptions) ( 0 ), 0, 0 );
2492 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2494 strftime( timeBuf, TIME_STRING_BUF, "%a %b %d %X %Y", localtime( &statbuf.st_mtime ) );
2495 label = gtk_label_new( _( timeBuf ) );
2496 gtk_widget_show( label );
2497 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 6, 7,
2498 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2499 (GtkAttachOptions) ( 0 ), 0, 0 );
2500 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2504 label = gtk_label_new( _( "Modified:" ) );
2505 gtk_widget_show( label );
2506 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 7, 8,
2507 (GtkAttachOptions) ( GTK_FILL ),
2508 (GtkAttachOptions) ( 0 ), 0, 0 );
2509 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2511 strftime( timeBuf, TIME_STRING_BUF, "%a %b %d %X %Y", localtime( &statbuf.st_mtime ) );
2512 label = gtk_label_new( _( timeBuf ) );
2513 gtk_widget_show( label );
2514 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 7, 8,
2515 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2516 (GtkAttachOptions) ( 0 ), 0, 0 );
2517 gtk_label_set_justify( GTK_LABEL( label ), GTK_JUSTIFY_LEFT );
2518 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2521 label = gtk_label_new( _( "Accessed:" ) );
2522 gtk_widget_show( label );
2523 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 8, 9,
2524 (GtkAttachOptions) ( GTK_FILL ),
2525 (GtkAttachOptions) ( 0 ), 0, 0 );
2526 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2528 strftime( timeBuf, TIME_STRING_BUF, "%a %b %d %X %Y", localtime( &statbuf.st_atime ) );
2529 label = gtk_label_new( _( timeBuf ) );
2530 gtk_widget_show( label );
2531 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 8, 9,
2532 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2533 (GtkAttachOptions) ( 0 ), 0, 0 );
2534 gtk_label_set_justify( GTK_LABEL( label ), GTK_JUSTIFY_LEFT );
2535 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2537 /* permissions page */
2538 vbox = gtk_vbox_new( FALSE, 4 );
2539 gtk_widget_show( vbox );
2540 gtk_container_add( GTK_CONTAINER( notebook ), vbox );
2541 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
2543 label = gtk_label_new( _( "Permissions" ) );
2544 gtk_widget_show( label );
2545 gtk_notebook_set_tab_label( GTK_NOTEBOOK( notebook ), gtk_notebook_get_nth_page( GTK_NOTEBOOK( notebook ), pagenum ), label );
2549 table = gtk_table_new( 2, 2, FALSE );
2550 gtk_widget_show( table );
2551 gtk_box_pack_start( GTK_BOX( vbox ), table, FALSE, TRUE, 0 );
2552 gtk_table_set_row_spacings( GTK_TABLE( table ), 2 );
2553 gtk_table_set_col_spacings( GTK_TABLE( table ), 8 );
2555 label = gtk_label_new( _( "Owner:" ) );
2556 gtk_widget_show( label );
2557 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2558 (GtkAttachOptions) ( GTK_FILL ),
2559 (GtkAttachOptions) ( 0 ), 0, 0 );
2560 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2562 entry = gtk_entry_new();
2563 gtk_widget_show( entry );
2564 gtk_table_attach( GTK_TABLE( table ), entry, 1, 2, 0, 1,
2565 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2566 (GtkAttachOptions) ( 0 ), 0, 0 );
2567 if ( ( pw = getpwuid( statbuf.st_uid ) ) ) {
2568 gtk_entry_set_text( GTK_ENTRY( entry ), pw->pw_name );
2571 gtk_entry_set_text( GTK_ENTRY( entry ), (gpointer) statbuf.st_uid );
2573 if ( access( filename, W_OK ) || ( getuid() != 0 ) ) {
2574 gtk_widget_set_sensitive( GTK_WIDGET( entry ), FALSE );
2578 label = gtk_label_new( _( "Group:" ) );
2579 gtk_widget_show( label );
2580 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2581 (GtkAttachOptions) ( GTK_FILL ),
2582 (GtkAttachOptions) ( 0 ), 0, 0 );
2583 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2585 entry = gtk_entry_new();
2586 gtk_widget_show( entry );
2587 gtk_table_attach( GTK_TABLE( table ), entry, 1, 2, 1, 2,
2588 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2589 (GtkAttachOptions) ( 0 ), 0, 0 );
2590 if ( ( gp = getgrgid( statbuf.st_gid ) ) ) {
2591 gtk_entry_set_text( GTK_ENTRY( entry ), gp->gr_name );
2594 gtk_entry_set_text( GTK_ENTRY( entry ), (gpointer) statbuf.st_gid );
2596 if ( access( filename, W_OK ) || ( getuid() != 0 ) ) {
2597 gtk_widget_set_sensitive( GTK_WIDGET( entry ), FALSE );
2601 hseparator = gtk_hseparator_new();
2602 gtk_widget_show( hseparator );
2603 gtk_box_pack_start( GTK_BOX( vbox ), hseparator, FALSE, TRUE, 0 );
2606 table = gtk_table_new( 4, 5, TRUE );
2607 gtk_widget_show( table );
2608 gtk_box_pack_start( GTK_BOX( vbox ), table, FALSE, FALSE, 0 );
2609 gtk_table_set_row_spacings( GTK_TABLE( table ), 2 );
2610 gtk_table_set_col_spacings( GTK_TABLE( table ), 4 );
2611 if ( access( filename, W_OK ) || ( ( getuid() != statbuf.st_uid ) && getuid() != 0 ) ) {
2612 gtk_widget_set_sensitive( GTK_WIDGET( table ), FALSE );
2615 hbox = gtk_hbox_new( FALSE, 1 );
2616 gtk_widget_show( hbox );
2617 gtk_table_attach( GTK_TABLE( table ), hbox, 0, 1, 0, 1,
2618 (GtkAttachOptions) ( GTK_FILL ),
2619 (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
2621 priv->mode_label = label = gtk_label_new( "(0000)" );
2622 gtk_widget_show( label );
2623 gtk_box_pack_start( GTK_BOX( hbox ), label, FALSE, FALSE, 0 );
2625 label = gtk_label_new( _( "Read" ) );
2626 gtk_widget_show( label );
2627 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 0, 1,
2628 (GtkAttachOptions) ( 0 ),
2629 (GtkAttachOptions) ( 0 ), 0, 0 );
2631 label = gtk_label_new( _( "Write" ) );
2632 gtk_widget_show( label );
2633 gtk_table_attach( GTK_TABLE( table ), label, 2, 3, 0, 1,
2634 (GtkAttachOptions) ( 0 ),
2635 (GtkAttachOptions) ( 0 ), 0, 0 );
2637 label = gtk_label_new( _( "Exec" ) );
2638 gtk_widget_show( label );
2639 gtk_table_attach( GTK_TABLE( table ), label, 3, 4, 0, 1,
2640 (GtkAttachOptions) ( 0 ),
2641 (GtkAttachOptions) ( 0 ), 0, 0 );
2643 label = gtk_label_new( _( "Special" ) );
2644 gtk_widget_show( label );
2645 gtk_table_attach( GTK_TABLE( table ), label, 4, 5, 0, 1,
2646 (GtkAttachOptions) ( 0 ),
2647 (GtkAttachOptions) ( 0 ), 0, 0 );
2650 label = gtk_label_new( _( "User:" ) );
2651 gtk_widget_show( label );
2652 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2653 (GtkAttachOptions) ( GTK_FILL ),
2654 (GtkAttachOptions) ( 0 ), 0, 0 );
2655 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2657 priv->mode_buttons[0] = togglebutton = gtk_toggle_button_new_with_label( "" );
2658 gtk_widget_show( togglebutton );
2659 gtk_table_attach( GTK_TABLE( table ), togglebutton, 1, 2, 1, 2,
2660 (GtkAttachOptions) ( GTK_FILL ),
2661 (GtkAttachOptions) ( 0 ), 0, 0 );
2662 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IRUSR ) {
2663 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2666 priv->mode_buttons[1] = togglebutton = gtk_toggle_button_new_with_label( "" );
2667 gtk_widget_show( togglebutton );
2668 gtk_table_attach( GTK_TABLE( table ), togglebutton, 2, 3, 1, 2,
2669 (GtkAttachOptions) ( GTK_FILL ),
2670 (GtkAttachOptions) ( 0 ), 0, 0 );
2671 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IWUSR ) {
2672 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2675 priv->mode_buttons[2] = togglebutton = gtk_toggle_button_new_with_label( "" );
2676 gtk_widget_show( togglebutton );
2677 gtk_table_attach( GTK_TABLE( table ), togglebutton, 3, 4, 1, 2,
2678 (GtkAttachOptions) ( GTK_FILL ),
2679 (GtkAttachOptions) ( 0 ), 0, 0 );
2680 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IXUSR ) {
2681 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2684 priv->mode_buttons[3] = togglebutton = gtk_toggle_button_new_with_label( "" );
2685 gtk_widget_show( togglebutton );
2686 gtk_table_attach( GTK_TABLE( table ), togglebutton, 4, 5, 1, 2,
2687 (GtkAttachOptions) ( GTK_FILL ),
2688 (GtkAttachOptions) ( 0 ), 0, 0 );
2689 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_ISUID ) {
2690 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2695 label = gtk_label_new( _( "Group:" ) );
2696 gtk_widget_show( label );
2697 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 2, 3,
2698 (GtkAttachOptions) ( GTK_FILL ),
2699 (GtkAttachOptions) ( 0 ), 0, 0 );
2700 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2702 priv->mode_buttons[4] = togglebutton = gtk_toggle_button_new_with_label( "" );
2703 gtk_widget_show( togglebutton );
2704 gtk_table_attach( GTK_TABLE( table ), togglebutton, 1, 2, 2, 3,
2705 (GtkAttachOptions) ( GTK_FILL ),
2706 (GtkAttachOptions) ( 0 ), 0, 0 );
2707 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IRGRP ) {
2708 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2711 priv->mode_buttons[5] = togglebutton = gtk_toggle_button_new_with_label( "" );
2712 gtk_widget_show( togglebutton );
2713 gtk_table_attach( GTK_TABLE( table ), togglebutton, 2, 3, 2, 3,
2714 (GtkAttachOptions) ( GTK_FILL ),
2715 (GtkAttachOptions) ( 0 ), 0, 0 );
2716 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IWGRP ) {
2717 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2720 priv->mode_buttons[6] = togglebutton = gtk_toggle_button_new_with_label( "" );
2721 gtk_widget_show( togglebutton );
2722 gtk_table_attach( GTK_TABLE( table ), togglebutton, 3, 4, 2, 3,
2723 (GtkAttachOptions) ( GTK_FILL ),
2724 (GtkAttachOptions) ( 0 ), 0, 0 );
2725 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IXGRP ) {
2726 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2729 priv->mode_buttons[7] = togglebutton = gtk_toggle_button_new_with_label( "" );
2730 gtk_widget_show( togglebutton );
2731 gtk_table_attach( GTK_TABLE( table ), togglebutton, 4, 5, 2, 3,
2732 (GtkAttachOptions) ( GTK_FILL ),
2733 (GtkAttachOptions) ( 0 ), 0, 0 );
2734 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_ISGID ) {
2735 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2738 label = gtk_label_new( _( "Other:" ) );
2739 gtk_widget_show( label );
2740 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 3, 4,
2741 (GtkAttachOptions) ( GTK_FILL ),
2742 (GtkAttachOptions) ( 0 ), 0, 0 );
2743 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2745 priv->mode_buttons[8] = togglebutton = gtk_toggle_button_new_with_label( "" );
2746 gtk_widget_show( togglebutton );
2747 gtk_table_attach( GTK_TABLE( table ), togglebutton, 1, 2, 3, 4,
2748 (GtkAttachOptions) ( GTK_FILL ),
2749 (GtkAttachOptions) ( 0 ), 0, 0 );
2750 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IROTH ) {
2751 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2754 priv->mode_buttons[9] = togglebutton = gtk_toggle_button_new_with_label( "" );
2755 gtk_widget_show( togglebutton );
2756 gtk_table_attach( GTK_TABLE( table ), togglebutton, 2, 3, 3, 4,
2757 (GtkAttachOptions) ( GTK_FILL ),
2758 (GtkAttachOptions) ( 0 ), 0, 0 );
2759 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IWOTH ) {
2760 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2763 priv->mode_buttons[10] = togglebutton = gtk_toggle_button_new_with_label( "" );
2764 gtk_widget_show( togglebutton );
2765 gtk_table_attach( GTK_TABLE( table ), togglebutton, 3, 4, 3, 4,
2766 (GtkAttachOptions) ( GTK_FILL ),
2767 (GtkAttachOptions) ( 0 ), 0, 0 );
2768 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IXOTH ) {
2769 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2772 priv->mode_buttons[11] = togglebutton = gtk_toggle_button_new_with_label( "" );
2773 gtk_widget_show( togglebutton );
2774 gtk_table_attach( GTK_TABLE( table ), togglebutton, 4, 5, 3, 4,
2775 (GtkAttachOptions) ( GTK_FILL ),
2776 (GtkAttachOptions) ( 0 ), 0, 0 );
2777 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_ISVTX ) {
2778 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2781 for ( i = 0; i < 12; i++ )
2782 gtk_signal_connect( GTK_OBJECT( priv->mode_buttons[i] ), "toggled",
2783 GTK_SIGNAL_FUNC( gtk_file_selection_properties_update_mode ), fs );
2784 gtk_toggle_button_toggled( GTK_TOGGLE_BUTTON( priv->mode_buttons[0] ) );
2787 vbox = gtk_vbox_new( FALSE, 0 );
2788 gtk_widget_show( vbox );
2789 gtk_container_add( GTK_CONTAINER( notebook ), vbox );
2791 label = gtk_label_new( _( "Global" ) );
2792 gtk_widget_show( label );
2793 gtk_notebook_set_tab_label( GTK_NOTEBOOK( notebook ), gtk_notebook_get_nth_page( GTK_NOTEBOOK( notebook ), pagenum ), label );
2796 label = gtk_label_new( _( "dialog preferances will go here" ) );
2797 gtk_widget_show( label );
2798 gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 0 );
2800 /* end of dialog guts */
2803 button = gtk_button_new_with_label( _( "OK" ) );
2804 // gtk_signal_connect (GTK_OBJECT (button), "clicked",
2805 // (GtkSignalFunc) gtk_file_selection_rename_file_confirmed,
2807 gtk_signal_connect( GTK_OBJECT( button ), "clicked",
2808 (GtkSignalFunc) gtk_file_selection_file_mode_confirmed,
2810 gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
2811 button, TRUE, TRUE, 0 );
2812 GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
2813 gtk_widget_show( button );
2815 button = gtk_button_new_with_label( _( "Cancel" ) );
2816 gtk_signal_connect_object( GTK_OBJECT( button ), "clicked",
2817 (GtkSignalFunc) gtk_widget_destroy,
2818 (gpointer) dialog );
2819 gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
2820 button, TRUE, TRUE, 0 );
2821 GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
2822 gtk_widget_grab_default( button );
2823 gtk_widget_show( button );
2826 gtk_widget_show( dialog );
2830 gtk_file_selection_key_press( GtkWidget *widget,
2832 gpointer user_data ){
2834 GtkFileSelection *fs;
2837 g_return_val_if_fail( widget != NULL, FALSE );
2838 g_return_val_if_fail( event != NULL, FALSE );
2840 fs = GTK_FILE_SELECTION( user_data );
2842 if ( fs->saved_entry ) {
2843 gtk_clist_unselect_all( (GtkCList *) ( fs->dir_list ) );
2844 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),fs->saved_entry );
2845 g_free( fs->saved_entry );
2846 fs->saved_entry = NULL;
2848 if ( event->keyval == GDK_Tab ) {
2849 text = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) );
2851 text = g_strdup( text );
2853 gtk_file_selection_populate( fs, text, TRUE );
2857 gtk_signal_emit_stop_by_name( GTK_OBJECT( widget ), "key_press_event" );
2868 gtk_file_selection_home_button (GtkWidget *widget, gpointer data){
2871 GtkFileSelection *fs=data;
2873 list = fs->next_history;
2876 g_free (list->data);
2879 g_list_free (fs->next_history);
2880 fs->next_history = NULL;
2882 gtk_file_selection_populate (fs,"~/",FALSE);
2886 gtk_file_selection_bookmark_button( GtkWidget *widget,
2887 GtkFileSelection *fs ){
2889 g_return_if_fail( fs != NULL );
2890 g_return_if_fail( GTK_FILE_SELECTION( fs ) );
2892 gtk_menu_popup( GTK_MENU( fs->bookmark_menu ), NULL, NULL, NULL, NULL,
2898 gtk_file_selection_up_button( GtkWidget *widget, gpointer data ){
2899 GtkFileSelection *fs = data;
2902 g_return_if_fail( fs != NULL );
2903 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2905 list = fs->next_history;
2907 g_free( list->data );
2910 g_list_free( fs->next_history );
2911 fs->next_history = NULL;
2913 gtk_file_selection_populate( fs, "../", FALSE ); /*change directories. */
2918 gtk_file_selection_prev_button( GtkWidget *widget, gpointer data ){
2919 GtkFileSelection *fs = data;
2924 g_return_if_fail( fs != NULL );
2925 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2927 list = fs->prev_history;
2929 if ( list && g_list_length( list ) > 1 ) {
2930 first = list; /* get first element */
2931 list = list->next; /* pop off current directory */
2933 list->prev = NULL; /* make this the new head. */
2935 fs->prev_history = list; /* update prev_history list */
2936 fs->next_history = g_list_prepend( fs->next_history,first->data ); /* put it on next_history */
2938 first->next = NULL; /* orphan the old first node */
2939 g_list_free( first ); /* free the node (data is now in use by next_history) */
2943 path = g_malloc( strlen( list->data ) + 4 ); /* plenty of space */
2944 strcpy( path,list->data ); /* get the 2nd path in the history */
2945 strcat( path,"/" ); /* append a '/' */
2946 gtk_file_selection_populate( fs, path, FALSE ); /* change directories. */
2952 gtk_file_selection_next_button( GtkWidget *widget, gpointer data ){
2953 GtkFileSelection *fs = data;
2958 g_return_if_fail( fs != NULL );
2959 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2961 list = fs->next_history;
2963 if ( list && g_list_length( list ) > 0 ) {
2964 first = list; /*get first element*/
2965 list = list->next; /*pop off current directory*/
2971 fs->next_history = list; /*update prev_history list*/
2973 path = g_malloc( strlen( first->data ) + 4 ); /*plenty of space*/
2974 strcpy( path,first->data );
2975 strcat( path,"/" ); /*append a / */
2976 gtk_file_selection_populate( fs, path, FALSE ); /*change directories.*/
2979 first->next = NULL; /* orphan the old first node */
2980 g_list_free( first ); /* free the node (data is now in use by next_history) */
2986 gtk_file_selection_refresh_button( GtkWidget *widget, gpointer data ){
2987 GtkFileSelection *fs = data;
2989 g_return_if_fail( fs != NULL );
2990 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2992 gtk_file_selection_populate( fs,"",FALSE );
2996 gtk_file_selection_mask_entry_callback( GtkWidget *widget, gpointer data ){
2997 GtkFileSelection *fs = data;
3003 fs->mask = g_strdup( gtk_entry_get_text( GTK_ENTRY( GTK_COMBO( fs->mask_entry )->entry ) ) );
3005 if ( strlen( fs->mask ) == 0 ) {
3010 gtk_file_selection_refresh_button( widget,data );
3013 static gint gtk_file_selection_files_list_key_callback( GtkWidget *widget, GdkEventKey *event, gpointer data ){
3014 GtkFileSelection *fs = data;
3018 // g_print("Key event: %d\n",event->keyval);
3019 //we need some sort of timeout.
3021 //if the key is a normal character then
3022 //add to our saved_entry1
3023 //if it's backspace then remove one character
3024 //otherwise let it through (and erase our buffer.
3026 if ( event->keyval > GDK_space && event->keyval <= GDK_Korean_Won ) {
3028 key[0] = event->keyval;
3029 saved = fs->saved_entry1;
3030 if ( fs->saved_entry1 ) {
3031 fs->saved_entry1 = g_strconcat( saved,key,NULL );
3035 fs->saved_entry1 = g_strdup( key );
3037 g_print( "complete: %s\n",fs->saved_entry1 );
3038 /*gtk_label_set_text(GTK_LABEL(fs->completion_label), fs->saved_entry1); */
3040 saved = g_strdup( gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ) );
3041 gtk_file_selection_complete( fs,fs->saved_entry1 );
3042 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),saved );
3045 else if ( event->keyval == GDK_BackSpace ) {
3046 if ( strlen( fs->saved_entry1 ) ) {
3047 fs->saved_entry1[strlen( fs->saved_entry1 ) - 1] = 0;
3048 g_print( "complete: %s\n",fs->saved_entry1 );
3049 /*gtk_label_set_text(GTK_LABEL(fs->completion_label),fs->saved_entry1); */
3050 saved = g_strdup( gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ) );
3051 gtk_file_selection_complete( fs,fs->saved_entry1 );
3052 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),saved );
3056 else if ( event->keyval == GDK_Tab ) {
3057 saved = g_strdup( gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ) );
3058 gtk_file_selection_populate( fs,fs->saved_entry1,TRUE );
3059 g_free( fs->saved_entry1 );
3060 fs->saved_entry1 = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) );
3061 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),saved );
3064 g_print( "complete: %s\n",fs->saved_entry1 );
3065 /* gtk_label_set_text(GTK_LABEL(fs->completion_label),fs->saved_entry1);*/
3067 gtk_signal_emit_stop_by_name( GTK_OBJECT( widget ), "key_press_event" );
3070 if ( fs->saved_entry1 ) {
3071 g_free( fs->saved_entry1 );
3072 fs->saved_entry1 = NULL;
3074 /* gtk_label_set_text(GTK_LABEL(fs->completion_label)," "); */
3081 static gint gtk_file_selection_mask_entry_key_callback( GtkWidget *widget, GdkEventKey *event, gpointer data ){
3082 GtkEntry *entry = (GtkEntry *)widget;
3083 GtkFileSelection *fs = data;
3085 g_return_val_if_fail( fs != NULL,FALSE );
3086 g_return_val_if_fail( GTK_IS_FILE_SELECTION( fs ),FALSE );
3089 if ( event->keyval == GDK_Return || event->keyval == GDK_Tab ) {
3094 fs->mask = g_strdup( gtk_entry_get_text( entry ) );
3095 gtk_file_selection_refresh_button( widget,fs );
3097 if ( event->keyval == GDK_Return ) {
3098 gtk_signal_emit_stop_by_name( GTK_OBJECT( widget ), "key_press_event" );
3108 static gint gtk_file_selection_mask_entry_button_callback( GtkWidget *widget, GdkEventButton *event, gpointer data ){
3109 GtkFileSelection *fs = data;
3115 fs->mask = g_strdup( gtk_entry_get_text( GTK_ENTRY( GTK_COMBO( fs->mask_entry )->entry ) ) );
3116 gtk_file_selection_refresh_button( widget,fs );
3122 static gboolean gtk_file_selection_history_combo_list_key_handler( GtkWidget *widget,
3124 gpointer user_data ){
3126 g_print("Key pressed! \n");
3132 static gboolean gtk_file_selection_history_combo_list_callback( GtkWidget *thelist,
3133 GdkEventButton *event,
3134 gpointer user_data ){
3136 GtkFileSelection *fs = user_data;
3140 list = fs->next_history;
3142 g_free( list->data );
3145 g_list_free( fs->next_history );
3146 fs->next_history = NULL;
3148 path = g_malloc( strlen( gtk_entry_get_text( GTK_ENTRY( ( (GtkCombo *)fs->history_combo )->entry ) ) ) + 4 );
3149 strcpy( path,gtk_entry_get_text( GTK_ENTRY( ( (GtkCombo *)fs->history_combo )->entry ) ) );
3152 gtk_file_selection_populate( fs,path,TRUE );
3160 gtk_file_selection_history_combo_callback( GtkWidget *widget, GdkEventKey *event, gpointer data ){
3161 GtkEntry *entry = (GtkEntry *)widget;
3162 GtkFileSelection *fs = data;
3166 g_return_val_if_fail( fs != NULL,FALSE );
3167 g_return_val_if_fail( GTK_IS_FILE_SELECTION( fs ),FALSE );
3170 if ( event->keyval == GDK_Return ) {
3171 list = fs->next_history;
3173 g_free( list->data );
3176 g_list_free( fs->next_history );
3177 fs->next_history = NULL;
3179 path = g_malloc( strlen( gtk_entry_get_text( entry ) ) + 4 );
3180 strcpy( path,gtk_entry_get_text( entry ) );
3182 gtk_file_selection_populate( fs,path,TRUE );
3184 gtk_signal_emit_stop_by_name( GTK_OBJECT( widget ), "key_press_event" );
3195 static void gtk_file_selection_bookmark_callback( GtkWidget *widget, gpointer data ){
3196 GtkFileSelection *fs = data;
3197 BookmarkMenuStruct *item;
3200 g_return_if_fail( fs != NULL );
3201 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
3203 //g_print ("Callback\n");
3204 list = fs->bookmark_list;
3207 if ( item->menu_item == widget ) {
3208 if ( strcmp( item->path,"./" ) ) {
3209 gtk_file_selection_populate( fs, item->path, FALSE );
3218 gtk_file_selection_update_history_menu( GtkFileSelection *fs,
3219 gchar *current_directory ){
3222 g_return_if_fail( fs != NULL );
3223 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
3224 g_return_if_fail( current_directory != NULL );
3226 current_dir = g_strdup( current_directory );
3228 if ( fs->prev_history ) {
3229 if ( strcmp( ( fs->prev_history )->data,current_dir ) ) { /*if this item isn't on the top of the list */
3230 fs->prev_history = g_list_prepend( fs->prev_history,g_strdup( current_dir ) );
3234 fs->prev_history = g_list_prepend( fs->prev_history,g_strdup( current_dir ) );
3237 gtk_combo_set_popdown_strings( GTK_COMBO( fs->history_combo ),fs->prev_history );
3239 g_free( current_dir );
3243 gtk_file_selection_file_button( GtkWidget *widget,
3246 GdkEventButton *bevent,
3247 gpointer user_data ){
3248 GtkFileSelection *fs = NULL;
3249 gchar *filename, *temp = NULL;
3251 g_return_if_fail( GTK_IS_CLIST( widget ) );
3254 g_return_if_fail( fs != NULL );
3255 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
3257 gtk_clist_get_text( GTK_CLIST( fs->file_list ), row, 0, &temp );
3258 filename = g_strdup( temp );
3261 if ( fs->saved_entry ) {
3262 gtk_clist_unselect_all( (GtkCList *) ( fs->dir_list ) );
3263 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),fs->saved_entry );
3264 g_free( fs->saved_entry );
3265 fs->saved_entry = NULL;
3267 if ( fs->saved_entry1 ) {
3268 g_free( fs->saved_entry1 );
3269 fs->saved_entry1 = NULL;
3271 /* gtk_label_set_text(GTK_LABEL(fs->completion_label)," "); */
3275 switch ( bevent->type )
3277 case GDK_2BUTTON_PRESS:
3278 gtk_button_clicked( GTK_BUTTON( fs->ok_button ) );
3283 if (bevent->button && GDK_BUTTON2_MASK)
3285 g_print("Right click! -- %d\n",bevent->button);
3291 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), filename );
3297 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), filename );
3305 gtk_file_selection_dir_button( GtkWidget *widget,
3308 GdkEventButton *bevent,
3309 gpointer user_data ){
3311 GtkFileSelection *fs = NULL;
3312 gchar *filename, *temp = NULL;
3314 g_return_if_fail( GTK_IS_CLIST( widget ) );
3316 fs = GTK_FILE_SELECTION( user_data );
3317 g_return_if_fail( fs != NULL );
3318 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
3320 gtk_clist_get_text( GTK_CLIST( fs->dir_list ), row, 0, &temp );
3321 filename = g_strdup( temp );
3325 switch ( bevent->type )
3327 case GDK_2BUTTON_PRESS:
3328 list = fs->next_history;
3330 g_free( list->data );
3333 g_list_free( fs->next_history );
3334 fs->next_history = NULL;
3336 gtk_file_selection_populate( fs, filename, FALSE );
3337 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),fs->saved_entry );
3338 g_free( fs->saved_entry );
3339 fs->saved_entry = NULL;
3343 /* here we need to add the "filename" to the beginning of what's already
3344 in the entry. Save what's in the entry, then restore it on the double click
3346 if ( fs->saved_entry ) {
3347 g_free( fs->saved_entry );
3349 fs->saved_entry = g_strdup( gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ) );
3351 temp = g_strconcat( filename,fs->saved_entry,NULL );
3352 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), temp );
3359 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), filename );
3367 gtk_file_selection_undir_button( GtkWidget *widget,
3370 GdkEventButton *bevent,
3371 gpointer user_data ){
3372 GtkFileSelection *fs = NULL;
3373 gchar *filename, *temp = NULL;
3375 g_return_if_fail( GTK_IS_CLIST( widget ) );
3377 fs = GTK_FILE_SELECTION( user_data );
3378 g_return_if_fail( fs != NULL );
3379 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
3381 gtk_clist_get_text( GTK_CLIST( fs->dir_list ), row, 0, &temp );
3382 filename = g_strdup( temp );
3386 switch ( bevent->type )
3389 /* here we need to add the "filename" to the beginning of what's already
3390 in the entry. Save what's in the entry, then restore it on the double click
3392 if ( fs->saved_entry ) {
3393 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),fs->saved_entry );
3394 g_free( fs->saved_entry );
3395 fs->saved_entry = NULL;
3401 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), filename ); //?????
3409 gtk_file_selection_populate( GtkFileSelection *fs,
3411 gint try_complete ){
3412 CompletionState *cmpl_state;
3413 PossibleCompletion* poss;
3416 gchar* rem_path = rel_path;
3419 gint did_recurse = FALSE;
3420 gint possible_count = 0;
3421 gint selection_index = -1;
3422 gint file_list_width;
3423 gint dir_list_width;
3425 g_return_if_fail( fs != NULL );
3426 g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
3428 cmpl_state = (CompletionState*) fs->cmpl_state;
3429 poss = cmpl_completion_matches( rel_path, &rem_path, cmpl_state );
3431 if ( !cmpl_state_okay( cmpl_state ) ) {
3432 /* Something went wrong. */
3433 gtk_file_selection_abort( fs );
3437 g_assert( cmpl_state->reference_dir );
3439 gtk_clist_freeze( GTK_CLIST( fs->dir_list ) );
3440 gtk_clist_clear( GTK_CLIST( fs->dir_list ) );
3441 gtk_clist_freeze( GTK_CLIST( fs->file_list ) );
3442 gtk_clist_clear( GTK_CLIST( fs->file_list ) );
3444 /* Set the dir_list to include ./ and ../ */
3445 /* Actually, no let's not.
3448 row = gtk_clist_append (GTK_CLIST (fs->dir_list), text);
3451 text[0] = "../"; //Do we need ..?
3452 row = gtk_clist_append( GTK_CLIST( fs->dir_list ), text );
3454 /*reset the max widths of the lists*/
3455 dir_list_width = gdk_string_width( fs->dir_list->style->font,"../" );
3456 gtk_clist_set_column_width( GTK_CLIST( fs->dir_list ),0,dir_list_width );
3457 file_list_width = 1;
3458 gtk_clist_set_column_width( GTK_CLIST( fs->file_list ),0,file_list_width );
3462 if ( cmpl_is_a_completion( poss ) ) {
3463 possible_count += 1;
3465 filename = cmpl_this_completion( poss );
3469 if ( cmpl_is_directory( poss ) ) {
3470 if ( strcmp( filename, "./" ) != 0 &&
3471 strcmp( filename, "../" ) != 0 ) {
3472 int width = gdk_string_width( fs->dir_list->style->font,
3474 row = gtk_clist_append( GTK_CLIST( fs->dir_list ), text );
3475 if ( width > dir_list_width ) {
3476 dir_list_width = width;
3477 gtk_clist_set_column_width( GTK_CLIST( fs->dir_list ),0,
3485 if ( gtk_file_selection_match_mask( filename,fs->mask ) ) {
3486 int width = gdk_string_width( fs->file_list->style->font,
3488 row = gtk_clist_append( GTK_CLIST( fs->file_list ), text );
3489 if ( width > file_list_width ) {
3490 file_list_width = width;
3491 gtk_clist_set_column_width( GTK_CLIST( fs->file_list ),0,
3498 int width = gdk_string_width( fs->file_list->style->font,
3500 row = gtk_clist_append( GTK_CLIST( fs->file_list ), text );
3501 if ( width > file_list_width ) {
3502 file_list_width = width;
3503 gtk_clist_set_column_width( GTK_CLIST( fs->file_list ),0,
3510 poss = cmpl_next_completion( cmpl_state );
3513 gtk_clist_thaw( GTK_CLIST( fs->dir_list ) );
3514 gtk_clist_thaw( GTK_CLIST( fs->file_list ) );
3516 /* File lists are set. */
3518 g_assert( cmpl_state->reference_dir );
3520 if ( try_complete ) {
3522 /* User is trying to complete filenames, so advance the user's input
3523 * string to the updated_text, which is the common leading substring
3524 * of all possible completions, and if its a directory attempt
3525 * attempt completions in it. */
3527 if ( cmpl_updated_text( cmpl_state )[0] ) {
3529 if ( cmpl_updated_dir( cmpl_state ) ) {
3530 gchar* dir_name = g_strdup( cmpl_updated_text( cmpl_state ) );
3534 gtk_file_selection_populate( fs, dir_name, TRUE );
3540 if ( fs->selection_entry ) {
3541 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),
3542 cmpl_updated_text( cmpl_state ) );
3548 selection_index = cmpl_last_valid_char( cmpl_state ) -
3549 ( strlen( rel_path ) - strlen( rem_path ) );
3550 if ( fs->selection_entry ) {
3551 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), rem_path );
3557 if ( fs->selection_entry ) {
3558 /* Here we need to take the old filename and keep it!*/
3559 /*gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");*/
3564 if ( !did_recurse ) {
3565 if ( fs->selection_entry ) {
3566 gtk_entry_set_position( GTK_ENTRY( fs->selection_entry ), selection_index );
3569 if ( fs->selection_entry ) {
3570 sel_text = g_strconcat( _( "Selection: " ),
3571 cmpl_reference_position( cmpl_state ),
3575 gtk_label_set_text (GTK_LABEL (fs->selection_text), sel_text);
3580 gtk_file_selection_update_history_menu( fs, cmpl_reference_position( cmpl_state ) );
3586 gtk_file_selection_abort( GtkFileSelection *fs ){
3589 sprintf( err_buf, _( "Directory unreadable: %s" ), cmpl_strerror( cmpl_errno ) );
3591 /* BEEP gdk_beep(); */
3594 if (fs->selection_entry)
3595 gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
3599 /**********************************************************************/
3600 /* External Interface */
3601 /**********************************************************************/
3603 /* The four completion state selectors
3606 cmpl_updated_text( CompletionState* cmpl_state ){
3607 return cmpl_state->updated_text;
3611 cmpl_updated_dir( CompletionState* cmpl_state ){
3612 return cmpl_state->re_complete;
3616 cmpl_reference_position( CompletionState* cmpl_state ){
3617 return cmpl_state->reference_dir->fullname;
3621 cmpl_last_valid_char( CompletionState* cmpl_state ){
3622 return cmpl_state->last_valid_char;
3626 cmpl_completion_fullname( gchar* text, CompletionState* cmpl_state ){
3627 static char nothing[2] = "";
3629 if ( !cmpl_state_okay( cmpl_state ) ) {
3632 else if ( text[0] == '/' ) {
3633 strcpy( cmpl_state->updated_text, text );
3635 else if ( text[0] == '~' ) {
3639 dir = open_user_dir( text, cmpl_state );
3642 /* spencer says just return ~something, so
3643 * for now just do it. */
3644 strcpy( cmpl_state->updated_text, text );
3649 strcpy( cmpl_state->updated_text, dir->fullname );
3651 slash = strchr( text, '/' );
3654 strcat( cmpl_state->updated_text, slash );
3660 strcpy( cmpl_state->updated_text, cmpl_state->reference_dir->fullname );
3661 if ( strcmp( cmpl_state->reference_dir->fullname, "/" ) != 0 ) {
3662 strcat( cmpl_state->updated_text, "/" );
3664 strcat( cmpl_state->updated_text, text );
3667 return cmpl_state->updated_text;
3670 /* The three completion selectors
3673 cmpl_this_completion( PossibleCompletion* pc ){
3678 cmpl_is_directory( PossibleCompletion* pc ){
3679 return pc->is_directory;
3683 cmpl_is_a_completion( PossibleCompletion* pc ){
3684 return pc->is_a_completion;
3687 /**********************************************************************/
3688 /* Construction, deletion */
3689 /**********************************************************************/
3691 static CompletionState*
3692 cmpl_init_state( void ){
3693 gchar getcwd_buf[2 * MAXPATHLEN];
3694 CompletionState *new_state;
3696 new_state = g_new( CompletionState, 1 );
3698 /* We don't use getcwd() on SUNOS, because, it does a popen("pwd")
3699 * and, if that wasn't bad enough, hangs in doing so.
3701 #if defined( sun ) && !defined( __SVR4 )
3702 if ( !getwd( getcwd_buf ) )
3704 if ( !getcwd( getcwd_buf, MAXPATHLEN ) )
3707 /* Oh joy, we can't get the current directory. Um..., we should have
3708 * a root directory, right? Right? (Probably not portable to non-Unix)
3710 strcpy( getcwd_buf, "/" );
3715 new_state->reference_dir = NULL;
3716 new_state->completion_dir = NULL;
3717 new_state->active_completion_dir = NULL;
3718 new_state->directory_storage = NULL;
3719 new_state->directory_sent_storage = NULL;
3720 new_state->last_valid_char = 0;
3721 new_state->updated_text = g_new( gchar, MAXPATHLEN );
3722 new_state->updated_text_alloc = MAXPATHLEN;
3723 new_state->the_completion.text = g_new( gchar, MAXPATHLEN );
3724 new_state->the_completion.text_alloc = MAXPATHLEN;
3725 new_state->user_dir_name_buffer = NULL;
3726 new_state->user_directories = NULL;
3728 new_state->reference_dir = open_dir( getcwd_buf, new_state );
3730 if ( !new_state->reference_dir ) {
3731 /* Directories changing from underneath us, grumble */
3732 strcpy( getcwd_buf, "/" );
3740 cmpl_free_dir_list( GList* dp0 ){
3744 free_dir( dp->data );
3752 cmpl_free_dir_sent_list( GList* dp0 ){
3756 free_dir_sent( dp->data );
3764 cmpl_free_state( CompletionState* cmpl_state ){
3765 cmpl_free_dir_list( cmpl_state->directory_storage );
3766 cmpl_free_dir_sent_list( cmpl_state->directory_sent_storage );
3768 if ( cmpl_state->user_dir_name_buffer ) {
3769 g_free( cmpl_state->user_dir_name_buffer );
3771 if ( cmpl_state->user_directories ) {
3772 g_free( cmpl_state->user_directories );
3774 if ( cmpl_state->the_completion.text ) {
3775 g_free( cmpl_state->the_completion.text );
3777 if ( cmpl_state->updated_text ) {
3778 g_free( cmpl_state->updated_text );
3781 g_free( cmpl_state );
3785 free_dir( CompletionDir* dir ){
3786 g_free( dir->fullname );
3791 free_dir_sent( CompletionDirSent* sent ){
3792 g_free( sent->name_buffer );
3793 g_free( sent->entries );
3798 prune_memory_usage( CompletionState *cmpl_state ){
3799 GList* cdsl = cmpl_state->directory_sent_storage;
3800 GList* cdl = cmpl_state->directory_storage;
3804 for (; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1 )
3808 cmpl_free_dir_sent_list( cdsl->next );
3812 cmpl_state->directory_storage = NULL;
3814 if ( cdl->data == cmpl_state->reference_dir ) {
3815 cmpl_state->directory_storage = g_list_prepend( NULL, cdl->data );
3818 free_dir( cdl->data );
3823 g_list_free( cdl0 );
3826 /**********************************************************************/
3827 /* The main entrances. */
3828 /**********************************************************************/
3830 static PossibleCompletion*
3831 cmpl_completion_matches( gchar* text_to_complete,
3832 gchar** remaining_text,
3833 CompletionState* cmpl_state ){
3835 PossibleCompletion *poss;
3837 prune_memory_usage( cmpl_state );
3839 g_assert( text_to_complete != NULL );
3841 cmpl_state->user_completion_index = -1;
3842 cmpl_state->last_completion_text = text_to_complete;
3843 cmpl_state->the_completion.text[0] = 0;
3844 cmpl_state->last_valid_char = 0;
3845 cmpl_state->updated_text_len = -1;
3846 cmpl_state->updated_text[0] = 0;
3847 cmpl_state->re_complete = FALSE;
3849 first_slash = strchr( text_to_complete, '/' );
3851 if ( text_to_complete[0] == '~' && !first_slash ) {
3852 /* Text starts with ~ and there is no slash, show all the
3853 * home directory completions.
3855 poss = attempt_homedir_completion( text_to_complete, cmpl_state );
3857 update_cmpl( poss, cmpl_state );
3862 cmpl_state->reference_dir =
3863 open_ref_dir( text_to_complete, remaining_text, cmpl_state );
3865 if ( !cmpl_state->reference_dir ) {
3869 cmpl_state->completion_dir =
3870 find_completion_dir( *remaining_text, remaining_text, cmpl_state );
3872 cmpl_state->last_valid_char = *remaining_text - text_to_complete;
3874 if ( !cmpl_state->completion_dir ) {
3878 cmpl_state->completion_dir->cmpl_index = -1;
3879 cmpl_state->completion_dir->cmpl_parent = NULL;
3880 cmpl_state->completion_dir->cmpl_text = *remaining_text;
3882 cmpl_state->active_completion_dir = cmpl_state->completion_dir;
3884 cmpl_state->reference_dir = cmpl_state->completion_dir;
3886 poss = attempt_file_completion( cmpl_state );
3888 update_cmpl( poss, cmpl_state );
3893 static PossibleCompletion*
3894 cmpl_next_completion( CompletionState* cmpl_state ){
3895 PossibleCompletion* poss = NULL;
3897 cmpl_state->the_completion.text[0] = 0;
3899 if ( cmpl_state->user_completion_index >= 0 ) {
3900 poss = attempt_homedir_completion( cmpl_state->last_completion_text, cmpl_state );
3903 poss = attempt_file_completion( cmpl_state );
3906 update_cmpl( poss, cmpl_state );
3911 /**********************************************************************/
3912 /* Directory Operations */
3913 /**********************************************************************/
3915 /* Open the directory where completion will begin from, if possible. */
3916 static CompletionDir*
3917 open_ref_dir( gchar* text_to_complete,
3918 gchar** remaining_text,
3919 CompletionState* cmpl_state ){
3921 CompletionDir *new_dir;
3923 first_slash = strchr( text_to_complete, '/' );
3925 if ( text_to_complete[0] == '~' ) {
3926 new_dir = open_user_dir( text_to_complete, cmpl_state );
3929 if ( first_slash ) {
3930 *remaining_text = first_slash + 1;
3933 *remaining_text = text_to_complete + strlen( text_to_complete );
3941 else if ( text_to_complete[0] == '/' || !cmpl_state->reference_dir ) {
3942 gchar *tmp = g_strdup( text_to_complete );
3946 while ( *p && *p != '*' && *p != '?' )
3950 p = strrchr( tmp, '/' );
3958 new_dir = open_dir( tmp, cmpl_state );
3961 *remaining_text = text_to_complete +
3962 ( ( p == tmp + 1 ) ? ( p - tmp ) : ( p + 1 - tmp ) );
3967 /* If no possible candidates, use the cwd */
3968 gchar *curdir = g_get_current_dir();
3970 new_dir = open_dir( curdir, cmpl_state );
3973 *remaining_text = text_to_complete;
3983 *remaining_text = text_to_complete;
3985 new_dir = open_dir( cmpl_state->reference_dir->fullname, cmpl_state );
3989 new_dir->cmpl_index = -1;
3990 new_dir->cmpl_parent = NULL;
3996 /* open a directory by user name */
3997 static CompletionDir*
3998 open_user_dir( gchar* text_to_complete,
3999 CompletionState *cmpl_state ){
4003 g_assert( text_to_complete && text_to_complete[0] == '~' );
4005 first_slash = strchr( text_to_complete, '/' );
4007 if ( first_slash ) {
4008 cmp_len = first_slash - text_to_complete - 1;
4011 cmp_len = strlen( text_to_complete + 1 );
4016 gchar *homedir = g_get_home_dir();
4019 return open_dir( homedir, cmpl_state );
4028 char* copy = g_new( char, cmp_len + 1 );
4030 strncpy( copy, text_to_complete + 1, cmp_len );
4032 pwd = getpwnam( copy );
4039 return open_dir( pwd->pw_dir, cmpl_state );
4043 /* open a directory relative the the current relative directory */
4044 static CompletionDir*
4045 open_relative_dir( gchar* dir_name,
4047 CompletionState *cmpl_state ){
4048 gchar path_buf[2 * MAXPATHLEN];
4050 if ( dir->fullname_len + strlen( dir_name ) + 2 >= MAXPATHLEN ) {
4051 cmpl_errno = CMPL_ERRNO_TOO_LONG;
4055 strcpy( path_buf, dir->fullname );
4057 if ( dir->fullname_len > 1 ) {
4058 path_buf[dir->fullname_len] = '/';
4059 strcpy( path_buf + dir->fullname_len + 1, dir_name );
4063 strcpy( path_buf + dir->fullname_len, dir_name );
4066 return open_dir( path_buf, cmpl_state );
4069 /* after the cache lookup fails, really open a new directory */
4070 static CompletionDirSent*
4071 open_new_dir( gchar* dir_name, struct stat* sbuf, gboolean stat_subdirs ){
4072 CompletionDirSent* sent;
4075 struct dirent *dirent_ptr;
4076 gint buffer_size = 0;
4077 gint entry_count = 0;
4079 struct stat ent_sbuf;
4080 char path_buf[MAXPATHLEN * 2];
4083 sent = g_new( CompletionDirSent, 1 );
4084 sent->mtime = sbuf->st_mtime;
4085 sent->inode = sbuf->st_ino;
4086 sent->device = sbuf->st_dev;
4088 path_buf_len = strlen( dir_name );
4090 if ( path_buf_len > MAXPATHLEN ) {
4091 cmpl_errno = CMPL_ERRNO_TOO_LONG;
4095 strcpy( path_buf, dir_name );
4097 directory = opendir( dir_name );
4104 while ( ( dirent_ptr = readdir( directory ) ) != NULL )
4106 int entry_len = strlen( dirent_ptr->d_name );
4107 buffer_size += entry_len + 1;
4110 if ( path_buf_len + entry_len + 2 >= MAXPATHLEN ) {
4111 cmpl_errno = CMPL_ERRNO_TOO_LONG;
4112 closedir( directory );
4117 sent->name_buffer = g_new( gchar, buffer_size );
4118 sent->entries = g_new( CompletionDirEntry, entry_count );
4119 sent->entry_count = entry_count;
4121 buffer_ptr = sent->name_buffer;
4123 rewinddir( directory );
4125 for ( i = 0; i < entry_count; i += 1 )
4127 dirent_ptr = readdir( directory );
4129 if ( !dirent_ptr ) {
4131 closedir( directory );
4135 strcpy( buffer_ptr, dirent_ptr->d_name );
4136 sent->entries[i].entry_name = buffer_ptr;
4137 buffer_ptr += strlen( dirent_ptr->d_name );
4141 path_buf[path_buf_len] = '/';
4142 strcpy( path_buf + path_buf_len + 1, dirent_ptr->d_name );
4144 if ( stat_subdirs ) {
4145 if ( stat( path_buf, &ent_sbuf ) >= 0 && S_ISDIR( ent_sbuf.st_mode ) ) {
4146 sent->entries[i].is_dir = 1;
4149 /* stat may fail, and we don't mind, since it could be a
4150 * dangling symlink. */
4151 sent->entries[i].is_dir = 0;
4155 sent->entries[i].is_dir = 1;
4159 qsort( sent->entries, sent->entry_count, sizeof( CompletionDirEntry ), compare_cmpl_dir );
4161 closedir( directory );
4167 check_dir( gchar *dir_name, struct stat *result, gboolean *stat_subdirs ){
4168 /* A list of directories that we know only contain other directories.
4169 * Trying to stat every file in these directories would be very
4176 struct stat statbuf;
4177 } no_stat_dirs[] = {
4178 { "/afs", FALSE, { 0 } },
4179 { "/net", FALSE, { 0 } }
4182 static const gint n_no_stat_dirs = sizeof( no_stat_dirs ) / sizeof( no_stat_dirs[0] );
4183 static gboolean initialized = FALSE;
4187 if ( !initialized ) {
4189 for ( i = 0; i < n_no_stat_dirs; i++ )
4191 if ( stat( no_stat_dirs[i].name, &no_stat_dirs[i].statbuf ) == 0 ) {
4192 no_stat_dirs[i].present = TRUE;
4197 if ( stat( dir_name, result ) < 0 ) {
4202 *stat_subdirs = TRUE;
4203 for ( i = 0; i < n_no_stat_dirs; i++ )
4205 if ( no_stat_dirs[i].present &&
4206 ( no_stat_dirs[i].statbuf.st_dev == result->st_dev ) &&
4207 ( no_stat_dirs[i].statbuf.st_ino == result->st_ino ) ) {
4208 *stat_subdirs = FALSE;
4216 /* open a directory by absolute pathname */
4217 static CompletionDir*
4218 open_dir( gchar* dir_name, CompletionState* cmpl_state ){
4220 gboolean stat_subdirs;
4221 CompletionDirSent *sent;
4224 if ( !check_dir( dir_name, &sbuf, &stat_subdirs ) ) {
4228 cdsl = cmpl_state->directory_sent_storage;
4234 if ( sent->inode == sbuf.st_ino &&
4235 sent->mtime == sbuf.st_mtime &&
4236 sent->device == sbuf.st_dev ) {
4237 return attach_dir( sent, dir_name, cmpl_state );
4243 sent = open_new_dir( dir_name, &sbuf, stat_subdirs );
4246 cmpl_state->directory_sent_storage =
4247 g_list_prepend( cmpl_state->directory_sent_storage, sent );
4249 return attach_dir( sent, dir_name, cmpl_state );
4255 static CompletionDir*
4256 attach_dir( CompletionDirSent* sent, gchar* dir_name, CompletionState *cmpl_state ){
4257 CompletionDir* new_dir;
4259 new_dir = g_new( CompletionDir, 1 );
4261 cmpl_state->directory_storage =
4262 g_list_prepend( cmpl_state->directory_storage, new_dir );
4264 new_dir->sent = sent;
4265 new_dir->fullname = g_strdup( dir_name );
4266 new_dir->fullname_len = strlen( dir_name );
4272 correct_dir_fullname( CompletionDir* cmpl_dir ){
4273 gint length = strlen( cmpl_dir->fullname );
4276 if ( strcmp( cmpl_dir->fullname + length - 2, "/." ) == 0 ) {
4277 if ( length == 2 ) {
4278 strcpy( cmpl_dir->fullname, "/" );
4279 cmpl_dir->fullname_len = 1;
4283 cmpl_dir->fullname[length - 2] = 0;
4286 else if ( strcmp( cmpl_dir->fullname + length - 3, "/./" ) == 0 ) {
4287 cmpl_dir->fullname[length - 2] = 0;
4289 else if ( strcmp( cmpl_dir->fullname + length - 3, "/.." ) == 0 ) {
4290 if ( length == 3 ) {
4291 strcpy( cmpl_dir->fullname, "/" );
4292 cmpl_dir->fullname_len = 1;
4296 if ( stat( cmpl_dir->fullname, &sbuf ) < 0 ) {
4301 cmpl_dir->fullname[length - 2] = 0;
4303 if ( !correct_parent( cmpl_dir, &sbuf ) ) {
4307 else if ( strcmp( cmpl_dir->fullname + length - 4, "/../" ) == 0 ) {
4308 if ( length == 4 ) {
4309 strcpy( cmpl_dir->fullname, "/" );
4310 cmpl_dir->fullname_len = 1;
4314 if ( stat( cmpl_dir->fullname, &sbuf ) < 0 ) {
4319 cmpl_dir->fullname[length - 3] = 0;
4321 if ( !correct_parent( cmpl_dir, &sbuf ) ) {
4326 cmpl_dir->fullname_len = strlen( cmpl_dir->fullname );
4332 correct_parent( CompletionDir* cmpl_dir, struct stat *sbuf ){
4338 last_slash = strrchr( cmpl_dir->fullname, '/' );
4340 g_assert( last_slash );
4342 if ( last_slash != cmpl_dir->fullname ) { /* last_slash[0] = 0; */
4350 if ( stat( cmpl_dir->fullname, &parbuf ) < 0 ) {
4355 if ( parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev ) {
4356 /* it wasn't a link */
4364 last_slash[0] = '/'; */
4366 /* it was a link, have to figure it out the hard way */
4368 new_name = find_parent_dir_fullname( cmpl_dir->fullname );
4374 g_free( cmpl_dir->fullname );
4376 cmpl_dir->fullname = new_name;
4382 find_parent_dir_fullname( gchar* dirname ){
4383 gchar buffer[MAXPATHLEN];
4384 gchar buffer2[MAXPATHLEN];
4386 #if defined( sun ) && !defined( __SVR4 )
4387 if ( !getwd( buffer ) )
4389 if ( !getcwd( buffer, MAXPATHLEN ) )
4396 if ( chdir( dirname ) != 0 || chdir( ".." ) != 0 ) {
4401 #if defined( sun ) && !defined( __SVR4 )
4402 if ( !getwd( buffer2 ) )
4404 if ( !getcwd( buffer2, MAXPATHLEN ) )
4413 if ( chdir( buffer ) != 0 ) {
4418 return g_strdup( buffer2 );
4421 /**********************************************************************/
4422 /* Completion Operations */
4423 /**********************************************************************/
4425 static PossibleCompletion*
4426 attempt_homedir_completion( gchar* text_to_complete,
4427 CompletionState *cmpl_state ){
4430 if ( !cmpl_state->user_dir_name_buffer &&
4431 !get_pwdb( cmpl_state ) ) {
4434 length = strlen( text_to_complete ) - 1;
4436 cmpl_state->user_completion_index += 1;
4438 while ( cmpl_state->user_completion_index < cmpl_state->user_directories_len )
4440 index = first_diff_index( text_to_complete + 1,
4441 cmpl_state->user_directories
4442 [cmpl_state->user_completion_index].login );
4449 if ( cmpl_state->last_valid_char < ( index + 1 ) ) {
4450 cmpl_state->last_valid_char = index + 1;
4452 cmpl_state->user_completion_index += 1;
4456 cmpl_state->the_completion.is_a_completion = 1;
4457 cmpl_state->the_completion.is_directory = 1;
4459 append_completion_text( "~", cmpl_state );
4461 append_completion_text( cmpl_state->
4462 user_directories[cmpl_state->user_completion_index].login,
4465 return append_completion_text( "/", cmpl_state );
4468 if ( text_to_complete[1] ||
4469 cmpl_state->user_completion_index > cmpl_state->user_directories_len ) {
4470 cmpl_state->user_completion_index = -1;
4475 cmpl_state->user_completion_index += 1;
4476 cmpl_state->the_completion.is_a_completion = 1;
4477 cmpl_state->the_completion.is_directory = 1;
4479 return append_completion_text( "~/", cmpl_state );
4483 /* returns the index (>= 0) of the first differing character,
4484 * PATTERN_MATCH if the completion matches */
4486 first_diff_index( gchar* pat, gchar* text ){
4489 while ( *pat && *text && *text == *pat )
4500 return PATTERN_MATCH;
4503 static PossibleCompletion*
4504 append_completion_text( gchar* text, CompletionState* cmpl_state ){
4507 if ( !cmpl_state->the_completion.text ) {
4511 len = strlen( text ) + strlen( cmpl_state->the_completion.text ) + 1;
4513 if ( cmpl_state->the_completion.text_alloc > len ) {
4514 strcat( cmpl_state->the_completion.text, text );
4515 return &cmpl_state->the_completion;
4518 while ( i < len ) { i <<= 1; }
4520 cmpl_state->the_completion.text_alloc = i;
4522 cmpl_state->the_completion.text = (gchar*)g_realloc( cmpl_state->the_completion.text, i );
4524 if ( !cmpl_state->the_completion.text ) {
4529 strcat( cmpl_state->the_completion.text, text );
4530 return &cmpl_state->the_completion;
4534 static CompletionDir*
4535 find_completion_dir( gchar* text_to_complete,
4536 gchar** remaining_text,
4537 CompletionState* cmpl_state ){
4538 gchar* first_slash = strchr( text_to_complete, '/' );
4539 CompletionDir* dir = cmpl_state->reference_dir;
4540 CompletionDir* next;
4541 *remaining_text = text_to_complete;
4543 while ( first_slash )
4545 gint len = first_slash - *remaining_text;
4547 gchar *found_name = NULL; /* Quiet gcc */
4549 gchar* pat_buf = g_new( gchar, len + 1 );
4551 strncpy( pat_buf, *remaining_text, len );
4554 for ( i = 0; i < dir->sent->entry_count; i += 1 )
4556 if ( dir->sent->entries[i].is_dir &&
4557 fnmatch( pat_buf, dir->sent->entries[i].entry_name,
4558 FNMATCH_FLAGS ) != FNM_NOMATCH ) {
4566 found_name = dir->sent->entries[i].entry_name;
4572 /* Perhaps we are trying to open an automount directory */
4573 found_name = pat_buf;
4576 next = open_relative_dir( found_name, dir, cmpl_state );
4583 next->cmpl_parent = dir;
4587 if ( !correct_dir_fullname( dir ) ) {
4592 *remaining_text = first_slash + 1;
4593 first_slash = strchr( *remaining_text, '/' );
4602 update_cmpl( PossibleCompletion* poss, CompletionState* cmpl_state ){
4605 if ( !poss || !cmpl_is_a_completion( poss ) ) {
4609 cmpl_len = strlen( cmpl_this_completion( poss ) );
4611 if ( cmpl_state->updated_text_alloc < cmpl_len + 1 ) {
4612 cmpl_state->updated_text =
4613 (gchar*)g_realloc( cmpl_state->updated_text,
4614 cmpl_state->updated_text_alloc );
4615 cmpl_state->updated_text_alloc = 2 * cmpl_len;
4618 if ( cmpl_state->updated_text_len < 0 ) {
4619 strcpy( cmpl_state->updated_text, cmpl_this_completion( poss ) );
4620 cmpl_state->updated_text_len = cmpl_len;
4621 cmpl_state->re_complete = cmpl_is_directory( poss );
4623 else if ( cmpl_state->updated_text_len == 0 ) {
4624 cmpl_state->re_complete = FALSE;
4629 first_diff_index( cmpl_state->updated_text,
4630 cmpl_this_completion( poss ) );
4632 cmpl_state->re_complete = FALSE;
4634 if ( first_diff == PATTERN_MATCH ) {
4638 if ( first_diff > cmpl_state->updated_text_len ) {
4639 strcpy( cmpl_state->updated_text, cmpl_this_completion( poss ) );
4642 cmpl_state->updated_text_len = first_diff;
4643 cmpl_state->updated_text[first_diff] = 0;
4647 static PossibleCompletion*
4648 attempt_file_completion( CompletionState *cmpl_state ){
4649 gchar *pat_buf, *first_slash;
4650 CompletionDir *dir = cmpl_state->active_completion_dir;
4652 dir->cmpl_index += 1;
4654 if ( dir->cmpl_index == dir->sent->entry_count ) {
4655 if ( dir->cmpl_parent == NULL ) {
4656 cmpl_state->active_completion_dir = NULL;
4662 cmpl_state->active_completion_dir = dir->cmpl_parent;
4664 return attempt_file_completion( cmpl_state );
4668 g_assert( dir->cmpl_text );
4670 first_slash = strchr( dir->cmpl_text, '/' );
4672 if ( first_slash ) {
4673 gint len = first_slash - dir->cmpl_text;
4675 pat_buf = g_new( gchar, len + 1 );
4676 strncpy( pat_buf, dir->cmpl_text, len );
4681 gint len = strlen( dir->cmpl_text );
4683 pat_buf = g_new( gchar, len + 2 );
4684 strcpy( pat_buf, dir->cmpl_text );
4685 strcpy( pat_buf + len, "*" );
4688 if ( first_slash ) {
4689 if ( dir->sent->entries[dir->cmpl_index].is_dir ) {
4690 if ( fnmatch( pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
4691 FNMATCH_FLAGS ) != FNM_NOMATCH ) {
4692 CompletionDir* new_dir;
4694 new_dir = open_relative_dir( dir->sent->entries[dir->cmpl_index].entry_name,
4702 new_dir->cmpl_parent = dir;
4704 new_dir->cmpl_index = -1;
4705 new_dir->cmpl_text = first_slash + 1;
4707 cmpl_state->active_completion_dir = new_dir;
4710 return attempt_file_completion( cmpl_state );
4715 return attempt_file_completion( cmpl_state );
4721 return attempt_file_completion( cmpl_state );
4726 if ( dir->cmpl_parent != NULL ) {
4727 append_completion_text( dir->fullname +
4728 strlen( cmpl_state->completion_dir->fullname ) + 1,
4730 append_completion_text( "/", cmpl_state );
4733 append_completion_text( dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state );
4735 cmpl_state->the_completion.is_a_completion =
4736 ( fnmatch( pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
4737 FNMATCH_FLAGS ) != FNM_NOMATCH );
4739 cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
4740 if ( dir->sent->entries[dir->cmpl_index].is_dir ) {
4741 append_completion_text( "/", cmpl_state );
4745 return &cmpl_state->the_completion;
4751 get_pwdb( CompletionState* cmpl_state ){
4752 struct passwd *pwd_ptr;
4754 gint len = 0, i, count = 0;
4756 if ( cmpl_state->user_dir_name_buffer ) {
4761 while ( ( pwd_ptr = getpwent() ) != NULL )
4763 len += strlen( pwd_ptr->pw_name );
4764 len += strlen( pwd_ptr->pw_dir );
4771 cmpl_state->user_dir_name_buffer = g_new( gchar, len );
4772 cmpl_state->user_directories = g_new( CompletionUserDir, count );
4773 cmpl_state->user_directories_len = count;
4775 buf_ptr = cmpl_state->user_dir_name_buffer;
4777 for ( i = 0; i < count; i += 1 )
4779 pwd_ptr = getpwent();
4785 strcpy( buf_ptr, pwd_ptr->pw_name );
4786 cmpl_state->user_directories[i].login = buf_ptr;
4787 buf_ptr += strlen( buf_ptr );
4789 strcpy( buf_ptr, pwd_ptr->pw_dir );
4790 cmpl_state->user_directories[i].homedir = buf_ptr;
4791 buf_ptr += strlen( buf_ptr );
4795 qsort( cmpl_state->user_directories,
4796 cmpl_state->user_directories_len,
4797 sizeof( CompletionUserDir ),
4806 if ( cmpl_state->user_dir_name_buffer ) {
4807 g_free( cmpl_state->user_dir_name_buffer );
4809 if ( cmpl_state->user_directories ) {
4810 g_free( cmpl_state->user_directories );
4813 cmpl_state->user_dir_name_buffer = NULL;
4814 cmpl_state->user_directories = NULL;
4820 compare_user_dir( const void* a, const void* b ){
4821 return strcmp( ( ( (CompletionUserDir*)a ) )->login,
4822 ( ( (CompletionUserDir*)b ) )->login );
4826 compare_cmpl_dir( const void* a, const void* b ){
4827 return strcmp( ( ( (CompletionDirEntry*)a ) )->entry_name,
4828 ( ( (CompletionDirEntry*)b ) )->entry_name );
4832 cmpl_state_okay( CompletionState* cmpl_state ){
4833 return cmpl_state && cmpl_state->reference_dir;
4837 cmpl_strerror( gint err ){
4838 if ( err == CMPL_ERRNO_TOO_LONG ) {
4839 return "Name too long";
4842 return g_strerror( err );
4846 /* This is an internally used function to create pixmaps. */
4848 create_pixmap( GtkWidget *widget, const gchar *pixmap_char ){
4849 GdkPixmap *gdkpixmap;
4852 GdkColormap *colormap;
4854 colormap = gtk_widget_get_colormap( widget );
4856 gdkpixmap = gdk_pixmap_colormap_create_from_xpm_d( GTK_WIDGET( widget )->window,
4860 (gpointer) pixmap_char );
4861 if ( gdkpixmap == NULL ) {
4862 g_warning( "Error loading pixmap: %s", pixmap_char );
4865 pixmap = gtk_pixmap_new( gdkpixmap, mask );
4866 gdk_pixmap_unref( gdkpixmap );
4867 gdk_bitmap_unref( mask );
4875 /* Get the selected filename and print it to the console */
4876 void file_ok_sel( GtkWidget *w,
4877 GtkFileSelection *fs ){
4878 g_print( "%s\n", gtk_file_selection_get_filename( GTK_FILE_SELECTION( fs ) ) );
4881 void destroy( GtkWidget *widget,
4889 const gchar *masks[] = { "mp3s/playlists <*.mp3,*.m3u>",
4890 "src/hdr <*.[CcHh],*.[Cc][Cc],*.[Hh][Hh],*.cpp>",
4893 gtk_init( &argc, &argv );
4895 /* Create a new file selection widget */
4896 filew = gtk_file_selection_new( "Spiffy File Selector" );
4897 // gtk_file_selection_complete(GTK_FILE_SELECTION(filew),"bob");
4899 gtk_file_selection_set_masks( GTK_FILE_SELECTION( filew ), masks );
4901 gtk_signal_connect( GTK_OBJECT( filew ), "destroy",
4902 (GtkSignalFunc) destroy, &filew );
4903 /* Connect the ok_button to file_ok_sel function */
4904 gtk_signal_connect( GTK_OBJECT( GTK_FILE_SELECTION( filew )->ok_button ),
4905 "clicked", (GtkSignalFunc) file_ok_sel, filew );
4907 /* Connect the cancel_button to destroy the widget */
4908 gtk_signal_connect_object( GTK_OBJECT( GTK_FILE_SELECTION
4909 ( filew )->cancel_button ),
4910 "clicked", (GtkSignalFunc) gtk_widget_destroy,
4911 GTK_OBJECT( filew ) );
4914 gtk_widget_show( filew );
4917 g_print("%d",gtk_file_selection_match_mask("mask.c","m*.c"));
4918 g_print("%d",gtk_file_selection_match_mask("mask.c","m???.c"));
4919 g_print("%d",gtk_file_selection_match_mask("mask.c","m??*.c"));
4920 g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c"));
4921 g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c???"));
4922 g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c*"));
4923 g_print("%d",gtk_file_selection_match_mask("mask.cout","n*.c???"));
4924 g_print("%d",gtk_file_selection_match_mask("mask.c","[mn]*"));
4925 g_print("%d",gtk_file_selection_match_mask("COPYING","*.xpm"));