/* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ #define LEO #ifdef LEO #define _( a ) a static char * back_xpm[] = { "14 14 33 1", " c None", ". c #000000", "+ c #C6D7C6", "@ c #E7EBE7", "# c #FFFFFF", "$ c #DEEBDE", "% c #F7F7F7", "& c #DEE7DE", "* c #EFF3EF", "= c #101810", "- c #B5C7AD", "; c #EFEFEF", "> c #D6E3D6", ", c #213021", "' c #315931", ") c #52824A", "! c #739A6B", "~ c #84A673", "{ c #7BA673", "] c #84AA73", "^ c #84AA7B", "/ c #84AE7B", "( c #63925A", "_ c #526D4A", ": c #4A7D42", "< c #739E6B", "[ c #739A63", "} c #4A7539", "| c #638E52", "1 c #427139", "2 c #6BA663", "3 c #5A8A52", "4 c #315929", " ..", " ..+.", " ..@#+.", " ..$#%%+.", " ..&#%*%%+.", " .=&#******+.", "..-#;>&@****+,", "..')!~{]^/^/(.", " .._:<^~^/^(.", " ..':[]~/(.", " ..}:[~|.", " ..123.", " ..4.", " .." }; static char * up_xpm[] = { "14 14 36 1", " c None", ". c #000000", "+ c #181C18", "@ c #D6DBD6", "# c #94AA8C", "$ c #000400", "% c #DEDFDE", "& c #94AA84", "* c #E7E3E7", "= c #94B28C", "- c #6B865A", "; c #EFEBEF", "> c #9CB694", ", c #8CA684", "' c #EFEFEF", ") c #F7EFF7", "! c #9CB68C", "~ c #63865A", "{ c #94B684", "] c #94AE84", "^ c #739263", "/ c #F7F3F7", "( c #94B284", "_ c #849E73", ": c #8CAE7B", "< c #8CAA84", "[ c #7B966B", "} c #8CA67B", "| c #DEDBD6", "1 c #E7E7E7", "2 c #8CAE84", "3 c #8CAA7B", "4 c #738E63", "5 c #BDBEB5", "6 c #BDC3BD", "7 c #637D52", " .. ", " .. ", " +@#$ ", " .%&. ", " .**=-. ", " .;;>,. ", " .*')!&~. ", " .;)){]^. ", " .*')/(]_-. ", " .;)//::<[. ", " .*')//:::}-. ", " .|1;;12]3}4. ", ".556666^^^^-7.", ".............." }; static char * forward_xpm[] = { "14 14 36 1", " c None", ". c #000000", "+ c #E7EBDE", "@ c #FFFFFF", "# c #F7F7EF", "$ c #D6E3D6", "% c #F7F7F7", "& c #EFF3EF", "* c #CEDFCE", "= c #CEDBC6", "- c #E7EFE7", "; c #181818", "> c #292829", ", c #E7EBE7", "' c #DEE7DE", ") c #B5C7AD", "! c #9CBA94", "~ c #8CAE84", "{ c #84AA7B", "] c #7BA673", "^ c #84A67B", "/ c #739A6B", "( c #5A824A", "_ c #395931", ": c #9CBA8C", "< c #84AE7B", "[ c #739E6B", "} c #527D4A", "| c #425942", "1 c #84A673", "2 c #4A7142", "3 c #94B284", "4 c #395D31", "5 c #5A8652", "6 c #315929", "7 c #396531", ".. ", ".+.. ", ".@#$.. ", ".@%&#*.. ", ".@%%&&%=.. ", ".@&&&&&-#=;. ", ">@&&&&,'$'&)..", ".!~{~{{]^/(_..", ".:{<{^{[}|.. ", ".:<1{/}2.. ", ".31/}4.. ", ".{56.. ", ".7.. ", ".. " }; static char * refresh_xpm[] = { "16 16 11 1", " c None", ". c #000000", "+ c #526942", "@ c #4A6139", "# c #526542", "$ c #5A7142", "% c #425531", "& c #314529", "* c #425131", "= c #425931", "- c #5A754A", " . ", " .. ", " .+@... ", " .#$##@%.. ", " .+#...%%. ", " . .. .&. ", " . . .&. ", " .. .. ", " .. .. ", " .*. . . ", " .*. .. . ", " .%@...#=. ", " ..##-#@#. ", " ...@%. ", " .. ", " . " }; #endif #ifndef LEO #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fnmatch.h" #if ( defined TORRIE_DEBUG || defined LEO ) #include #include #include #include "gtkfilesel-linux.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #else #include "gdk/gdkkeysyms.h" #include "gtkbutton.h" #include "gtkentry.h" #include "gtkfilesel.h" #include "gtkhbox.h" #include "gtkhbbox.h" #include "gtklabel.h" #include "gtklist.h" #include "gtklistitem.h" #include "gtkmain.h" #include "gtkscrolledwindow.h" #include "gtksignal.h" #include "gtkvbox.h" #include "gtkmenu.h" #include "gtkmenuitem.h" #include "gtkoptionmenu.h" #include "gtkclist.h" #include "gtkdialog.h" #include "gtkcombo.h" #include "gtkframe.h" #include "gtkhpaned.h" #include "gtktable.h" #include "gtkpixmap.h" #include "gtknotebook.h" #include "gtkhseparator.h" #include "gtktogglebutton.h" #endif #ifndef LEO #include "gtkintl.h" #include "back.xpm" #include "up.xpm" #include "forward.xpm" #include "refresh.xpm" #endif #define DIR_LIST_WIDTH 180 #define DIR_LIST_HEIGHT 180 #define FILE_LIST_WIDTH 180 #define FILE_LIST_HEIGHT 180 #define BOOKMARK_FILE "/.gtkfilesel_bookmarks" #define MASK_FILE "/.gtkfilesel_masks" #define TIME_STRING_BUF 50 /* I've put this here so it doesn't get confused with the * file completion interface */ typedef struct _HistoryCallbackArg HistoryCallbackArg; struct _HistoryCallbackArg { gchar *directory; GtkWidget *menu_item; }; typedef struct _BookmarkMenuStruct BookmarkMenuStruct; struct _BookmarkMenuStruct { GtkWidget *menu_item; gchar *desc; gchar *path; }; typedef struct _CompletionState CompletionState; typedef struct _CompletionDir CompletionDir; typedef struct _CompletionDirSent CompletionDirSent; typedef struct _CompletionDirEntry CompletionDirEntry; typedef struct _CompletionUserDir CompletionUserDir; typedef struct _PossibleCompletion PossibleCompletion; /* Non-external file completion decls and structures */ /* A contant telling PRCS how many directories to cache. Its actually * kept in a list, so the geometry isn't important. */ #define CMPL_DIRECTORY_CACHE_SIZE 10 /* A constant used to determine whether a substring was an exact * match by first_diff_index() */ #define PATTERN_MATCH -1 /* The arguments used by all fnmatch() calls below */ #define FNMATCH_FLAGS ( FNM_PATHNAME | FNM_PERIOD ) #define CMPL_ERRNO_TOO_LONG ( ( 1 << 16 ) - 1 ) /* This structure contains all the useful information about a directory * for the purposes of filename completion. These structures are cached * in the CompletionState struct. CompletionDir's are reference counted. */ struct _CompletionDirSent { ino_t inode; time_t mtime; dev_t device; gint entry_count; gchar *name_buffer; /* memory segment containing names of all entries */ struct _CompletionDirEntry *entries; }; struct _CompletionDir { CompletionDirSent *sent; gchar *fullname; gint fullname_len; struct _CompletionDir *cmpl_parent; gint cmpl_index; gchar *cmpl_text; }; /* This structure contains pairs of directory entry names with a flag saying * whether or not they are a valid directory. NOTE: This information is used * to provide the caller with information about whether to update its completions * or try to open a file. Since directories are cached by the directory mtime, * a symlink which points to an invalid file (which will not be a directory), * will not be reevaluated if that file is created, unless the containing * directory is touched. I consider this case to be worth ignoring (josh). */ struct _CompletionDirEntry { gint is_dir; gchar *entry_name; }; struct _CompletionUserDir { gchar *login; gchar *homedir; }; struct _PossibleCompletion { /* accessible fields, all are accessed externally by functions * declared above */ gchar *text; gint is_a_completion; gint is_directory; gint file_size; gint file_time; gint uid; gint gid; /* Private fields */ gint text_alloc; }; struct _CompletionState { gint last_valid_char; gchar *updated_text; gint updated_text_len; gint updated_text_alloc; gint re_complete; gchar *user_dir_name_buffer; gint user_directories_len; gchar *last_completion_text; gint user_completion_index; /* if >= 0, currently completing ~user */ struct _CompletionDir *completion_dir; /* directory completing from */ struct _CompletionDir *active_completion_dir; struct _PossibleCompletion the_completion; struct _CompletionDir *reference_dir; /* initial directory */ GList* directory_storage; GList* directory_sent_storage; struct _CompletionUserDir *user_directories; }; /* Widgets from the Properties Dialog */ typedef struct _PropertiesPrivate PropertiesPrivate; struct _PropertiesPrivate { GtkWidget *mode_label; GtkWidget *mode_buttons[12]; }; /* pixmap creation function */ GtkWidget* create_pixmap( GtkWidget *widget, const gchar *pixmap_char ); /* File completion functions which would be external, were they used * outside of this file. */ static CompletionState* cmpl_init_state( void ); static void cmpl_free_state( CompletionState *cmpl_state ); static gint cmpl_state_okay( CompletionState* cmpl_state ); static gchar* cmpl_strerror( gint ); static PossibleCompletion* cmpl_completion_matches( gchar *text_to_complete, gchar **remaining_text, CompletionState *cmpl_state ); /* Returns a name for consideration, possibly a completion, this name * will be invalid after the next call to cmpl_next_completion. */ static char* cmpl_this_completion( PossibleCompletion* ); /* True if this completion matches the given text. Otherwise, this * output can be used to have a list of non-completions. */ static gint cmpl_is_a_completion( PossibleCompletion* ); /* True if the completion is a directory */ static gint cmpl_is_directory( PossibleCompletion* ); /* Obtains the next completion, or NULL */ static PossibleCompletion* cmpl_next_completion( CompletionState* ); /* Updating completions: the return value of cmpl_updated_text() will * be text_to_complete completed as much as possible after the most * recent call to cmpl_completion_matches. For the present * application, this is the suggested replacement for the user's input * string. You must CALL THIS AFTER ALL cmpl_text_completions have * been received. */ static gchar* cmpl_updated_text( CompletionState* cmpl_state ); /* After updating, to see if the completion was a directory, call * this. If it was, you should consider re-calling completion_matches. */ static gint cmpl_updated_dir( CompletionState* cmpl_state ); /* Current location: if using file completion, return the current * directory, from which file completion begins. More specifically, * the cwd concatenated with all exact completions up to the last * directory delimiter('/'). */ static gchar* cmpl_reference_position( CompletionState* cmpl_state ); /* backing up: if cmpl_completion_matches returns NULL, you may query * the index of the last completable character into cmpl_updated_text. */ static gint cmpl_last_valid_char( CompletionState* cmpl_state ); /* When the user selects a non-directory, call cmpl_completion_fullname * to get the full name of the selected file. */ static gchar* cmpl_completion_fullname( gchar*, CompletionState* cmpl_state ); /* Directory operations. */ static CompletionDir* open_ref_dir( gchar* text_to_complete, gchar** remaining_text, CompletionState* cmpl_state ); static gboolean check_dir( gchar *dir_name, struct stat *result, gboolean *stat_subdirs ); static CompletionDir* open_dir( gchar* dir_name, CompletionState* cmpl_state ); static CompletionDir* open_user_dir( gchar* text_to_complete, CompletionState *cmpl_state ); static CompletionDir* open_relative_dir( gchar* dir_name, CompletionDir* dir, CompletionState *cmpl_state ); static CompletionDirSent* open_new_dir( gchar* dir_name, struct stat* sbuf, gboolean stat_subdirs ); static gint correct_dir_fullname( CompletionDir* cmpl_dir ); static gint correct_parent( CompletionDir* cmpl_dir, struct stat *sbuf ); static gchar* find_parent_dir_fullname( gchar* dirname ); static CompletionDir* attach_dir( CompletionDirSent* sent, gchar* dir_name, CompletionState *cmpl_state ); static void free_dir_sent( CompletionDirSent* sent ); static void free_dir( CompletionDir *dir ); static void prune_memory_usage( CompletionState *cmpl_state ); /* Completion operations */ static PossibleCompletion* attempt_homedir_completion( gchar* text_to_complete, CompletionState *cmpl_state ); static PossibleCompletion* attempt_file_completion( CompletionState *cmpl_state ); static CompletionDir* find_completion_dir( gchar* text_to_complete, gchar** remaining_text, CompletionState* cmpl_state ); static PossibleCompletion* append_completion_text( gchar* text, CompletionState* cmpl_state ); static gint get_pwdb( CompletionState* cmpl_state ); static gint first_diff_index( gchar* pat, gchar* text ); static gint compare_user_dir( const void* a, const void* b ); static gint compare_cmpl_dir( const void* a, const void* b ); static void update_cmpl( PossibleCompletion* poss, CompletionState* cmpl_state ); static void gtk_file_selection_class_init( GtkFileSelectionClass *klass ); static void gtk_file_selection_init( GtkFileSelection *filesel ); static void gtk_file_selection_realize( GtkWidget *widget ); static void gtk_file_selection_destroy( GtkObject *object ); static gint gtk_file_selection_key_press( GtkWidget *widget, GdkEventKey *event, gpointer user_data ); static void gtk_file_selection_file_button( GtkWidget *widget, gint row, gint column, GdkEventButton *bevent, gpointer user_data ); static void gtk_file_selection_dir_button( GtkWidget *widget, gint row, gint column, GdkEventButton *bevent, gpointer data ); static void gtk_file_selection_undir_button( GtkWidget *widget, gint row, gint column, GdkEventButton *bevent, gpointer data ); static void gtk_file_selection_populate( GtkFileSelection *fs, gchar *rel_path, gint try_complete ); static void gtk_file_selection_abort( GtkFileSelection *fs ); static void gtk_file_selection_update_history_menu( GtkFileSelection *fs, gchar *current_dir ); static void gtk_file_selection_create_dir( gpointer data ); static void gtk_file_selection_delete_file( gpointer data ); static void gtk_file_selection_rename_file( gpointer data ); static void gtk_file_selection_properties( gpointer data ); static void gtk_file_selection_properties_update_mode( GtkWidget *widget, gpointer data ); static mode_t gtk_file_selection_properties_get_mode( PropertiesPrivate* private ); static gboolean gtk_file_selection_history_combo_callback( GtkWidget *widget, GdkEventKey *event, gpointer data ); static gboolean gtk_file_selection_history_combo_list_key_handler( GtkWidget *widget, GdkEventKey *event, gpointer user_data ); static gboolean gtk_file_selection_history_combo_list_callback( GtkWidget *thelist, GdkEventButton *event, gpointer user_data ); static void gtk_file_selection_bookmark_callback( GtkWidget *widget, gpointer data ); static void gtk_file_selection_mask_entry_callback( GtkWidget *widget, gpointer data ); static gint gtk_file_selection_mask_entry_key_callback( GtkWidget *widget, GdkEventKey *event, gpointer data ); static gint gtk_file_selection_mask_entry_button_callback( GtkWidget *widget, GdkEventButton *event, gpointer data ); //static void gtk_file_selection_home_button (GtkWidget *widget, gpointer data); static void gtk_file_selection_bookmark_button( GtkWidget *widget, GtkFileSelection *fs ); static void gtk_file_selection_up_button( GtkWidget *widget, gpointer data ); static void gtk_file_selection_prev_button( GtkWidget *widget, gpointer data ); static void gtk_file_selection_next_button( GtkWidget *widget, gpointer data ); static void gtk_file_selection_refresh_button( GtkWidget *widget, gpointer data ); static gint gtk_file_selection_files_list_key_callback( GtkWidget *widget, GdkEventKey *event, gpointer data ); static gint gtk_file_selection_match_char( gchar, gchar *mask ); static gint gtk_file_selection_match_mask( gchar *,gchar * ); static void gtk_file_selection_load_bookmarks( GtkFileSelection *fs ); static void gtk_file_selection_add_bookmark( GtkFileSelection *fs, gchar *desc, gchar *path ); gint gtk_file_selection_save_bookmarks( GtkFileSelection *fs ); static void gtk_file_selection_load_masks( GtkFileSelection *fs ); static gint gtk_file_selection_show_fileop_menu( GtkCList *clist, GdkEvent *event, GtkFileSelection *fs ); static GtkWindowClass *parent_class = NULL; /* Saves errno when something cmpl does fails. */ static gint cmpl_errno; #ifdef G_WITH_CYGWIN /* * Take the path currently in the file selection * entry field and translate as necessary from * a WIN32 style to CYGWIN32 style path. For * instance translate: * x:\somepath\file.jpg * to: * //x/somepath/file.jpg * * Replace the path in the selection text field. * Return a boolean value concerning whether a * translation had to be made. */ int translate_win32_path( GtkFileSelection *filesel ){ int updated = 0; gchar *path; /* * Retrieve the current path */ path = gtk_entry_get_text( GTK_ENTRY( filesel->selection_entry ) ); /* * Translate only if this looks like a DOS-ish * path... First handle any drive letters. */ if ( isalpha( path[0] ) && ( path[1] == ':' ) ) { /* * This part kind of stinks... It isn't possible * to know if there is enough space in the current * string for the extra character required in this * conversion. Assume that there isn't enough space * and use the set function on the text field to * set the newly created string. */ gchar *newPath = g_strdup_printf( "//%c/%s", path[0], ( path + 3 ) ); gtk_entry_set_text( GTK_ENTRY( filesel->selection_entry ), newPath ); path = newPath; updated = 1; } /* * Now, replace backslashes with forward slashes * if necessary. */ if ( strchr( path, '\\' ) ) { int index; for ( index = 0; path[index] != '\0'; index++ ) if ( path[index] == '\\' ) { path[index] = '/'; } updated = 1; } return updated; } #endif /* General notes: * Make prev and next inactive if their respective * * histories are empty. * Add facilities for handling hidden files and * * directories * * Add an api to access the mask, and hidden files * * check box? (prob not in 1.2.x series) * */ /* Routine for applying mask to filenames * * Need to be optimized to minimize recursion * * help the for loop by looking for the next * * instance of the mask character following * * the '*'. ei *.c -- look for '.' * * Also, swap all *? pairs (-> ?*), as that * * will make it possible to look ahead (? * * makes it very nondeterministic as in *?.c * * which really is ?*.c * * * */ static gint gtk_file_selection_match_char( gchar text, gchar *mask ){ gchar *maskc; gint x; gint s; gchar lastc; gchar nextc; if ( mask[0] == '[' ) { if ( !strchr( mask,']' ) ) { return 0; } lastc = 0; maskc = g_strdup( mask + 1 ); /* get the portion of mask inside []*/ ( *( strchr( maskc + 1,']' ) ) ) = 0; s = strlen( (char *)maskc ); for ( x = 0 ; x < s ; x++ ) { if ( maskc[x] == '-' ) { if ( x == s ) { return 1; } nextc = maskc[x + 1]; if ( nextc > lastc ) { if ( ( lastc <= text ) && ( nextc >= text ) ) { g_free( maskc ); return s + 2; } } else if ( ( lastc >= text ) && ( nextc <= text ) ) { g_free( maskc ); return s + 2; } } else if ( text == maskc[x] ) { g_free( maskc ); return s + 2; } lastc = maskc[x]; } g_free( maskc ); return 0; } if ( mask[0] == '?' ) { return 1; } if ( mask[0] == text ) { return 1; } return 0; } static gint gtk_file_selection_match_mask1( gchar *text, gchar *mask ){ int mc; int tc; tc = 0; mc = 0; if ( mask[0] == 0 && text[0] == 0 ) { return 1; } if ( mask[0] == '*' ) { for ( tc = 0; tc <= strlen( text ); tc++ ) { if ( gtk_file_selection_match_mask1( text + tc, mask + 1 ) ) { return 1; } } return 0; } mc = gtk_file_selection_match_char( text[0], mask ); if ( mc ) { return gtk_file_selection_match_mask1( text + 1, mask + mc ); } else{ return 0; } } static gint gtk_file_selection_match_mask( gchar *text, gchar *mask ){ gchar *masks; gchar *bmask; gchar *emask; masks = g_strdup( mask ); emask = strchr( masks,'<' ); if ( emask ) { bmask = emask + 1; emask = strchr( bmask,'>' ); if ( emask ) { *emask = 0; } } else{ bmask = masks; } do { if ( ( emask = strchr( bmask,',' ) ) || ( emask = strchr( bmask,';' ) ) ) { *emask = 0; if ( gtk_file_selection_match_mask1( text, bmask ) ) { g_free( masks ); return 1; } bmask = emask + 1; } } while ( emask ); if ( gtk_file_selection_match_mask1( text, bmask ) ) { g_free( masks ); return 1; } g_free( masks ); return 0; } static void gtk_file_selection_load_bookmarks( GtkFileSelection *fs ){ GList *list; gchar *bookmark_file; gchar *bookmark_data; struct stat file_info; gint file; gint lp; gint cp; BookmarkMenuStruct *item; if ( fs->bookmark_list ) { //erase list = fs->bookmark_list; while ( list ) { item = list->data; g_free( item->desc ); g_free( item->path ); g_free( item ); list = list->next; } g_list_free( fs->bookmark_list ); fs->bookmark_list = NULL; gtk_widget_destroy( fs->bookmark_menu ); } fs->bookmark_menu = gtk_menu_new(); /* spacer */ item = g_malloc( sizeof( item ) ); item->menu_item = gtk_menu_item_new(); gtk_widget_show( item->menu_item ); gtk_menu_append( GTK_MENU( fs->bookmark_menu ), item->menu_item ); item = g_malloc( sizeof( item ) ); item->desc = g_strdup( "Add bookmark" ); item->path = g_strdup( "." ); item->menu_item = gtk_menu_item_new_with_label( item->desc ); gtk_widget_show( item->menu_item ); //fs->bookmark_list=g_list_append(fs->bookmark_list,item); //set signal here!! gtk_menu_append( GTK_MENU( fs->bookmark_menu ), item->menu_item ); item = g_malloc( sizeof( item ) ); item->desc = g_strdup( "Edit bookmark" ); item->path = g_strdup( "." ); item->menu_item = gtk_menu_item_new_with_label( item->desc ); gtk_widget_show( item->menu_item ); //fs->bookmark_list=g_list_append(fs->bookmark_list,item); //set signal here!! gtk_menu_append( GTK_MENU( fs->bookmark_menu ), item->menu_item ); bookmark_file = g_strconcat( g_get_home_dir(), BOOKMARK_FILE,NULL ); if ( !stat( bookmark_file,&file_info ) && ( file = open( bookmark_file, O_RDONLY ) ) > 0 ) { if ( file_info.st_size < 65536 ) { bookmark_data = g_malloc( file_info.st_size ); if ( file && read( file, bookmark_data, file_info.st_size ) ) { cp = lp = 0; while ( cp < file_info.st_size ) { while ( cp < file_info.st_size && bookmark_data[cp] != '<' ) cp++; bookmark_data[cp] = 0; item = g_malloc( sizeof( BookmarkMenuStruct ) ); item->desc = g_strdup( bookmark_data + lp ); lp = ++cp; while ( cp < file_info.st_size && bookmark_data[cp] != '>' ) cp++; bookmark_data[cp] = 0; //create menu items item->path = g_strdup( bookmark_data + lp ); gtk_file_selection_add_bookmark( (gpointer) fs, (gpointer) item->desc, (gpointer) item->path ); cp++; while ( cp < file_info.st_size && bookmark_data[cp] < 33 ) cp++; lp = cp; } } close( file ); } } else { /* Add some default items, then save off to bookmarks file */ gtk_file_selection_add_bookmark( (gpointer) fs, "Home", "~/" ); gtk_file_selection_add_bookmark( (gpointer) fs, "Root", "/" ); gtk_file_selection_save_bookmarks( (gpointer) fs ); } } static void gtk_file_selection_add_bookmark( GtkFileSelection *fs, gchar *desc, gchar *path ){ /* Add item to menu */ BookmarkMenuStruct *item; item = g_malloc( sizeof( item ) ); item->desc = (gpointer) desc; item->path = (gpointer) path; item->menu_item = gtk_menu_item_new_with_label( item->desc ); gtk_widget_show( item->menu_item ); fs->bookmark_list = g_list_append( fs->bookmark_list,item ); gtk_signal_connect( GTK_OBJECT( item->menu_item ), "activate", (GtkSignalFunc) gtk_file_selection_bookmark_callback, (gpointer) fs ); gtk_menu_insert( GTK_MENU( fs->bookmark_menu ), item->menu_item, g_list_length( fs->bookmark_list ) - 1 ); } gint gtk_file_selection_save_bookmarks( GtkFileSelection *fs ){ BookmarkMenuStruct *item; gchar *bookmark_file; gchar *item_data; gint file; GList *list; bookmark_file = g_strconcat( g_get_home_dir(), BOOKMARK_FILE,NULL ); if ( ( file = open( bookmark_file, O_CREAT | O_WRONLY | O_TRUNC, 0600 ) ) > 0 ) { for ( list = g_list_first( fs->bookmark_list ); list != NULL; list = g_list_next( list ) ) { item = list->data; item_data = g_strconcat( item->desc, " <", item->path, ">\n", NULL ); if ( write( file, item_data, strlen( item_data ) ) != strlen( item_data ) ) { return TRUE; } g_free( item_data ); } close( file ); } else { return TRUE; } return FALSE; } static void gtk_file_selection_load_masks( GtkFileSelection *fs ){ /* GList *list; gchar *masks_file; gchar *masks_data; struct stat file_info; gint file; gint lp; gint cp; if(fs->masks){ list=fs->masks; while(list){ g_free(list->data); list=list->next; } fs->masks = NULL; } masks_file=g_strconcat(g_get_home_dir(), MASK_FILE,NULL); //put in #define if(!stat(masks_file,&file_info)) { if(file_info.st_size <65536 ) { masks_data=g_malloc(file_info.st_size); file = open(masks_file, O_RDONLY ); if(file && read(file, masks_data, file_info.st_size)) { cp=lp=0; while (cp < file_info.st_size) { while (cp < file_info.st_size && masks_data[cp] != '>' ) cp++; masks_data[++cp]=0; if (masks_data[lp]=='<') { //if there was no description, strip off brackets lp++; masks_data[cp-1]=0; } // g_print("%s\n",masks_data+lp); fs->masks = g_list_append(fs->masks, g_strdup(masks_data+lp)); while(cp < file_info.st_size && masks_data[cp] < 33 ) cp++; lp=cp; } } close(file); } } */ if ( !fs->masks ) { /* masks is still null, fill it with default data... */ /* fs->masks = g_list_append(fs->masks, "all files <*>"); fs->masks = g_list_append(fs->masks, "mp3s/playlists <*.mp3,*.m3u>"); fs->masks = g_list_append(fs->masks, "src/hdr <*.[CcHh],*.[Cc][Cc],*.[Hh][Hh],*.cpp>"); fs->masks = g_list_append(fs->masks, "html docs <*.html,*.htm,*.HTM,*.php*,*.inc>"); fs->masks = g_list_append(fs->masks, "images <*.png,*.jpg,*.jpeg,*.gif,*.xpm,*.tiff>"); fs->masks = g_list_append(fs->masks, "package <*.rpm,*.deb>"); fs->masks = g_list_append(fs->masks, "archive <*.tgz,*.tb2,*.tar*,*.zip,*.rar>"); fs->masks = g_list_append(fs->masks, "compressed <*.Z,*.gz,*.bz2>"); */ } } void gtk_file_selection_clear_masks( GtkFileSelection *filesel ){ GList *list; g_return_if_fail( filesel != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) ); list = filesel->masks; while ( list ) { g_free( list->data ); list = list->next; } filesel->masks = NULL; gtk_list_clear_items( GTK_LIST( GTK_COMBO( filesel->mask_entry )->list ), 0, -1 ); } void gtk_file_selection_set_masks( GtkFileSelection *filesel, const gchar **masks ){ g_return_if_fail( filesel != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) ); while ( *masks ) { filesel->masks = g_list_append( filesel->masks, ( gpointer ) * masks ); masks++; } if ( filesel->masks ) { gtk_combo_set_popdown_strings( GTK_COMBO( filesel->mask_entry ), filesel->masks ); } } GtkType gtk_file_selection_get_type( void ){ static GtkType file_selection_type = 0; if ( !file_selection_type ) { static const GtkTypeInfo filesel_info = { "GtkFileSelection", sizeof( GtkFileSelection ), sizeof( GtkFileSelectionClass ), (GtkClassInitFunc) gtk_file_selection_class_init, (GtkObjectInitFunc) gtk_file_selection_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; file_selection_type = gtk_type_unique( GTK_TYPE_WINDOW, &filesel_info ); } return file_selection_type; } static void gtk_file_selection_class_init( GtkFileSelectionClass *class ){ GtkObjectClass *object_class; GtkWidgetClass *widget_class; object_class = (GtkObjectClass*) class; parent_class = gtk_type_class( GTK_TYPE_WINDOW ); widget_class = GTK_WIDGET_CLASS( class ); widget_class->realize = gtk_file_selection_realize; object_class->destroy = gtk_file_selection_destroy; } static void gtk_file_selection_init( GtkFileSelection *filesel ){ GtkWidget *entry_vbox; GtkWidget *label; GtkWidget *list_vbox; GtkWidget *confirm_area; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *hbox2; GtkWidget *table; GtkWidget *pulldown_hbox; GtkWidget *scrolled_win; GtkWidget *mask_label; GtkWidget *bigframe; GtkWidget *button; GtkWidget *hpaned; GtkWidget *menu_item; GtkWidget *pixmap; char *dir_title [2]; char *file_title [2]; filesel->cmpl_state = cmpl_init_state(); filesel->mask = NULL; filesel->prev_history = NULL; filesel->next_history = NULL; filesel->saved_entry = NULL; filesel->bookmark_list = NULL; filesel->masks = NULL; filesel->selection_text = NULL; filesel->fileop_data = NULL; gtk_file_selection_load_masks( filesel ); gtk_file_selection_load_bookmarks( filesel ); /* The dialog-sized vertical box */ filesel->main_vbox = gtk_vbox_new( FALSE, 0 ); gtk_container_set_border_width( GTK_CONTAINER( filesel ), 0 ); gtk_container_add( GTK_CONTAINER( filesel ), filesel->main_vbox ); gtk_widget_show( filesel->main_vbox ); /* hbox for pulldown menu */ pulldown_hbox = gtk_hbox_new( FALSE, 0 ); gtk_box_pack_start( GTK_BOX( filesel->main_vbox ), pulldown_hbox, FALSE, FALSE, 0 ); gtk_widget_show( pulldown_hbox ); /* The horizontal box containing create, rename etc. buttons */ /* filesel->button_area = gtk_hbutton_box_new (); gtk_button_box_set_layout(GTK_BUTTON_BOX(filesel->button_area), GTK_BUTTONBOX_START); gtk_button_box_set_spacing(GTK_BUTTON_BOX(filesel->button_area), 0); gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->button_area, FALSE, FALSE, 0); gtk_button_box_set_child_size(GTK_BUTTON_BOX(filesel->button_area),0,0); gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX(filesel->button_area),0,0); */ filesel->button_area = gtk_hbox_new( TRUE,0 ); //gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->button_area, // FALSE, FALSE, 0); //gtk_widget_show (filesel->button_area); gtk_file_selection_show_fileop_buttons( filesel ); /* frame to put the following hbox in */ bigframe = gtk_frame_new( NULL ); gtk_box_pack_start( GTK_BOX( filesel->main_vbox ), bigframe, TRUE, TRUE, 0 ); gtk_frame_set_shadow_type( GTK_FRAME( bigframe ), GTK_SHADOW_OUT ); gtk_widget_show( bigframe ); list_vbox = gtk_vbox_new( FALSE,3 ); gtk_widget_show( list_vbox ); gtk_container_add( GTK_CONTAINER( bigframe ), list_vbox ); gtk_container_set_border_width( GTK_CONTAINER( list_vbox ),2 ); gtk_widget_show( list_vbox ); /* The horizontal box containing the directory and file listboxes */ // list_hbox = gtk_hbox_new (FALSE, 3); //gtk_container_add (GTK_CONTAINER(bigframe), list_hbox); //gtk_container_set_border_width (GTK_CONTAINER (list_hbox), 3); // gtk_box_pack_start(GTK_BOX(list_vbox), list_hbox, FALSE,FALSE,0); // gtk_widget_show (list_hbox); hpaned = gtk_hpaned_new(); gtk_widget_show( hpaned ); gtk_container_set_border_width( GTK_CONTAINER( hpaned ), 1 ); gtk_paned_set_gutter_size( GTK_PANED( hpaned ), 10 ); gtk_box_pack_start( GTK_BOX( list_vbox ), hpaned,TRUE,TRUE,0 ); /* vbox to put the buttons and directory listing in */ vbox = gtk_vbox_new( FALSE, 3 ); gtk_widget_show( vbox ); gtk_container_add( GTK_CONTAINER( hpaned ),vbox ); //gtk_box_pack_start (GTK_BOX (hpaned), vbox, FALSE, FALSE, 0); hbox = gtk_hbox_new( FALSE, 4 ); gtk_widget_show( hbox ); gtk_box_pack_start( GTK_BOX( vbox ), hbox, FALSE, FALSE, 0 ); // home_button = gtk_button_new_with_label (_("Home")); // gtk_widget_show (home_button); // gtk_signal_connect (GTK_OBJECT (home_button), "clicked", // (GtkSignalFunc) gtk_file_selection_home_button, // (gpointer) filesel); // gtk_box_pack_start (GTK_BOX (hbox), home_button, TRUE,TRUE, 0); /* Here we add the bookmark menu button */ #define If we 're going to make bookmark a menu, we don't need #define to keep it in the filesel structure button = gtk_button_new_with_label( _( "Bookmarks" ) ); gtk_widget_show( button ); gtk_box_pack_start( GTK_BOX( hbox ), button, FALSE,FALSE,0 ); gtk_signal_connect( GTK_OBJECT( button ), "clicked", (GtkSignalFunc) gtk_file_selection_bookmark_button, (gpointer) filesel ); hbox2 = gtk_hbox_new( FALSE, 2 ); gtk_box_pack_start( GTK_BOX( hbox ), hbox2, FALSE, FALSE, 0 ); gtk_widget_show( hbox2 ); /* Prev button */ button = gtk_button_new(); gtk_signal_connect( GTK_OBJECT( button ), "clicked", (GtkSignalFunc) gtk_file_selection_prev_button, (gpointer) filesel ); gtk_widget_show( button ); gtk_box_pack_start( GTK_BOX( hbox2 ), button, FALSE,FALSE, 0 ); pixmap = create_pixmap( filesel->main_vbox, (gpointer) back_xpm ); gtk_widget_show( pixmap ); gtk_container_add( GTK_CONTAINER( button ), pixmap ); /* Up button */ button = gtk_button_new(); gtk_signal_connect( GTK_OBJECT( button ), "clicked", (GtkSignalFunc) gtk_file_selection_up_button, (gpointer) filesel ); gtk_widget_show( button ); gtk_box_pack_start( GTK_BOX( hbox2 ), button, FALSE,FALSE, 0 ); pixmap = create_pixmap( filesel->main_vbox, (gpointer) up_xpm ); gtk_widget_show( pixmap ); gtk_container_add( GTK_CONTAINER( button ), pixmap ); /* next button */ button = gtk_button_new(); gtk_widget_show( button ); gtk_signal_connect( GTK_OBJECT( button ), "clicked", (GtkSignalFunc) gtk_file_selection_next_button, (gpointer) filesel ); gtk_box_pack_start( GTK_BOX( hbox2 ), button, FALSE,FALSE, 0 ); pixmap = create_pixmap( filesel->main_vbox, (gpointer) forward_xpm ); gtk_widget_show( pixmap ); gtk_container_add( GTK_CONTAINER( button ), pixmap ); /* refresh button */ button = gtk_button_new(); gtk_widget_show( button ); gtk_signal_connect( GTK_OBJECT( button ), "clicked", (GtkSignalFunc) gtk_file_selection_refresh_button, (gpointer) filesel ); gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE,FALSE, 0 ); pixmap = create_pixmap( filesel->main_vbox, (gpointer) refresh_xpm ); gtk_widget_show( pixmap ); gtk_container_add( GTK_CONTAINER( button ), pixmap ); /* menu for right click file operations */ filesel->fileop_menu = gtk_menu_new(); menu_item = gtk_menu_item_new_with_label( _( "Rename..." ) ); gtk_widget_show( menu_item ); gtk_signal_connect_object( GTK_OBJECT( menu_item ), "activate", (GtkSignalFunc) gtk_file_selection_rename_file, (gpointer) filesel ); gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item ); menu_item = gtk_menu_item_new_with_label( _( "Delete" ) ); gtk_widget_show( menu_item ); gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item ); gtk_signal_connect_object( GTK_OBJECT( menu_item ), "activate", (GtkSignalFunc) gtk_file_selection_delete_file, (gpointer) filesel ); menu_item = gtk_menu_item_new(); gtk_widget_show( menu_item ); gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item ); menu_item = gtk_menu_item_new_with_label( _( "Create Directory..." ) ); gtk_signal_connect_object( GTK_OBJECT( menu_item ), "activate", (GtkSignalFunc) gtk_file_selection_create_dir, (gpointer) filesel ); gtk_widget_show( menu_item ); gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item ); menu_item = gtk_menu_item_new(); gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item ); gtk_widget_show( menu_item ); menu_item = gtk_menu_item_new_with_label( _( "Properties..." ) ); gtk_signal_connect_object( GTK_OBJECT( menu_item ), "activate", (GtkSignalFunc) gtk_file_selection_properties, (gpointer) filesel ); gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item ); gtk_widget_show( menu_item ); /* The directories clist */ dir_title[0] = _( "Directories" ); dir_title[1] = NULL; filesel->dir_list = gtk_clist_new_with_titles( 1, (gchar**) dir_title ); gtk_widget_set_usize( filesel->dir_list, DIR_LIST_WIDTH, DIR_LIST_HEIGHT ); gtk_signal_connect( GTK_OBJECT( filesel->dir_list ), "select_row", (GtkSignalFunc) gtk_file_selection_dir_button, (gpointer) filesel ); gtk_signal_connect( GTK_OBJECT( filesel->dir_list ), "unselect_row", (GtkSignalFunc) gtk_file_selection_undir_button, (gpointer) filesel ); gtk_signal_connect( GTK_OBJECT( filesel->dir_list ), "button_press_event", GTK_SIGNAL_FUNC( gtk_file_selection_show_fileop_menu ), (gpointer) filesel ); gtk_clist_column_titles_passive( GTK_CLIST( filesel->dir_list ) ); scrolled_win = gtk_scrolled_window_new( NULL, NULL ); gtk_container_add( GTK_CONTAINER( scrolled_win ), filesel->dir_list ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolled_win ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); gtk_box_pack_start( GTK_BOX( vbox ), scrolled_win, TRUE,TRUE, 0 ); //gtk_container_add(GTK_CONTAINER(hpaned), scrolled_win); gtk_widget_show( filesel->dir_list ); gtk_widget_show( scrolled_win ); vbox = gtk_vbox_new( FALSE, 3 ); gtk_widget_show( vbox ); gtk_container_add( GTK_CONTAINER( hpaned ),vbox ); /* vbox area for mask entry and files clist */ hbox = gtk_hbox_new( FALSE, 2 ); gtk_widget_show( hbox ); gtk_box_pack_start( GTK_BOX( vbox ), hbox, FALSE, FALSE, 0 ); mask_label = gtk_label_new( _( "Mask:" ) ); gtk_widget_show( mask_label ); gtk_box_pack_start( GTK_BOX( hbox ), mask_label, FALSE, FALSE, 2 ); /* filesel->mask_entry = gtk_entry_new (); gtk_widget_show (filesel->mask_entry); gtk_signal_connect(GTK_OBJECT(filesel->mask_entry),"activate", (GtkSignalFunc) gtk_file_4_mask_entry_callback, (gpointer) filesel); gtk_box_pack_start (GTK_BOX (hbox),filesel->mask_entry, TRUE, TRUE, 0); */ filesel->mask_entry = gtk_combo_new(); gtk_widget_show( filesel->mask_entry ); gtk_combo_set_value_in_list( GTK_COMBO( filesel->mask_entry ),FALSE,FALSE ); gtk_signal_connect( GTK_OBJECT( GTK_COMBO( filesel->mask_entry )->entry ),"activate", (GtkSignalFunc) gtk_file_selection_mask_entry_callback, (gpointer) filesel ); gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->mask_entry )->entry ),"key-press-event", (GtkSignalFunc) gtk_file_selection_mask_entry_key_callback, (gpointer) filesel ); gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->mask_entry )->list ),"button-release-event", (GtkSignalFunc) gtk_file_selection_mask_entry_button_callback, (gpointer) filesel ); gtk_box_pack_start( GTK_BOX( hbox ),filesel->mask_entry, TRUE, TRUE, 0 ); if ( filesel->masks ) { gtk_combo_set_popdown_strings( GTK_COMBO( filesel->mask_entry ), filesel->masks ); } /* The files clist */ file_title[0] = _( "Files" ); file_title[1] = NULL; filesel->file_list = gtk_clist_new_with_titles( 1, (gchar**) file_title ); gtk_widget_set_usize( filesel->file_list, FILE_LIST_WIDTH, FILE_LIST_HEIGHT ); gtk_signal_connect( GTK_OBJECT( filesel->file_list ), "select_row", (GtkSignalFunc) gtk_file_selection_file_button, (gpointer) filesel ); gtk_signal_connect( GTK_OBJECT( filesel->file_list ), "key-press-event", (GtkSignalFunc) gtk_file_selection_files_list_key_callback, (gpointer) filesel ); gtk_signal_connect( GTK_OBJECT( filesel->file_list ), "button_press_event", GTK_SIGNAL_FUNC( gtk_file_selection_show_fileop_menu ), (gpointer) filesel ); gtk_clist_column_titles_passive( GTK_CLIST( filesel->file_list ) ); scrolled_win = gtk_scrolled_window_new( NULL, NULL ); gtk_container_add( GTK_CONTAINER( scrolled_win ), filesel->file_list ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolled_win ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); gtk_box_pack_start( GTK_BOX( vbox ), scrolled_win, TRUE, TRUE, 0 ); gtk_widget_show( filesel->file_list ); gtk_widget_show( scrolled_win ); /* action area for packing buttons into. */ filesel->action_area = gtk_hbox_new( TRUE, 0 ); gtk_box_pack_start( GTK_BOX( filesel->main_vbox ), filesel->action_area, FALSE, FALSE, 2 ); gtk_widget_show( filesel->action_area ); /* hbox=gtk_hbox_new(FALSE,0); gtk_box_pack_end (GTK_BOX (filesel->main_vbox), hbox, FALSE,FALSE, 0); gtk_widget_show (hbox); */ /* The selection entry widget */ entry_vbox = gtk_vbox_new( FALSE, 0 ); gtk_box_pack_end( GTK_BOX( filesel->main_vbox ), entry_vbox, FALSE, FALSE, 0 ); gtk_widget_show( entry_vbox ); table = gtk_table_new( 2, 2, FALSE ); gtk_box_pack_start( GTK_BOX( entry_vbox ), table, TRUE, TRUE, 0 ); gtk_container_set_border_width( GTK_CONTAINER( table ), 4 ); gtk_table_set_row_spacings( GTK_TABLE( table ), 2 ); gtk_table_set_col_spacings( GTK_TABLE( table ), 4 ); label = gtk_label_new( _( "Selection:" ) ); gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1, (GtkAttachOptions) ( 0 ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_widget_show( label ); filesel->selection_entry = gtk_entry_new(); gtk_signal_connect( GTK_OBJECT( filesel->selection_entry ), "key_press_event", (GtkSignalFunc) gtk_file_selection_key_press, filesel ); gtk_table_attach( GTK_TABLE( table ), filesel->selection_entry, 1, 2, 0, 1, (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_widget_show( filesel->selection_entry ); label = gtk_label_new( _( "Directory:" ) ); gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2, (GtkAttachOptions) ( 0 ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_widget_show( label ); filesel->history_combo = gtk_combo_new(); gtk_combo_set_value_in_list( GTK_COMBO( filesel->history_combo ),FALSE,FALSE ); gtk_table_attach( GTK_TABLE( table ), filesel->history_combo, 1, 2, 1, 2, (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_widget_show( filesel->history_combo ); gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->history_combo )->entry ),"key-press-event", (GtkSignalFunc) gtk_file_selection_history_combo_callback, (gpointer) filesel ); gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->history_combo )->list ),"button-press-event", (GtkSignalFunc) gtk_file_selection_history_combo_list_callback, (gpointer) filesel ); gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->history_combo )->list ),"key-press-event", (GtkSignalFunc) gtk_file_selection_history_combo_list_key_handler, (gpointer) filesel ); filesel->selection_text = NULL; /* The OK/Cancel button area */ confirm_area = gtk_hbutton_box_new(); gtk_button_box_set_layout( GTK_BUTTON_BOX( confirm_area ), GTK_BUTTONBOX_END ); gtk_button_box_set_spacing( GTK_BUTTON_BOX( confirm_area ), 5 ); gtk_box_pack_end( GTK_BOX( entry_vbox ), confirm_area, FALSE, FALSE, 0 ); gtk_widget_show( confirm_area ); /* The OK button */ filesel->ok_button = gtk_button_new_with_label( _( "OK" ) ); GTK_WIDGET_SET_FLAGS( filesel->ok_button, GTK_CAN_DEFAULT ); gtk_box_pack_start( GTK_BOX( confirm_area ), filesel->ok_button, TRUE, TRUE, 0 ); gtk_signal_connect_object( GTK_OBJECT( filesel->selection_entry ), "focus_in_event", (GtkSignalFunc) gtk_widget_grab_default, GTK_OBJECT( filesel->ok_button ) ); gtk_signal_connect_object( GTK_OBJECT( filesel->selection_entry ), "activate", (GtkSignalFunc) gtk_button_clicked, GTK_OBJECT( filesel->ok_button ) ); gtk_widget_grab_default( filesel->ok_button ); gtk_widget_show( filesel->ok_button ); /* The Cancel button */ filesel->cancel_button = gtk_button_new_with_label( _( "Cancel" ) ); GTK_WIDGET_SET_FLAGS( filesel->cancel_button, GTK_CAN_DEFAULT ); gtk_box_pack_start( GTK_BOX( confirm_area ), filesel->cancel_button, TRUE, TRUE, 0 ); gtk_widget_show( filesel->cancel_button ); gtk_widget_show( table ); /* filesel->selection_text = label = gtk_label_new (""); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_box_pack_start (GTK_BOX (entry_vbox), label, FALSE, FALSE, 0); gtk_widget_show (label); */ if ( !cmpl_state_okay( filesel->cmpl_state ) ) { gchar err_buf[256]; sprintf( err_buf, _( "Directory unreadable: %s" ), cmpl_strerror( cmpl_errno ) ); /* gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf); */ } else { gtk_file_selection_populate( filesel, "", FALSE ); } gtk_widget_grab_focus( filesel->selection_entry ); } GtkWidget* gtk_file_selection_new( const gchar *title ){ GtkFileSelection *filesel; filesel = gtk_type_new( GTK_TYPE_FILE_SELECTION ); gtk_window_set_title( GTK_WINDOW( filesel ), title ); /* !!! put check here to figure out if screen > 640x480, if true We need to make the file selection dialog bigger. much bigger.. or maybe we should keep it at a certan percentage of the screen size? */ gtk_window_set_default_size( GTK_WINDOW( filesel ), 520, 420 ); return GTK_WIDGET( filesel ); } void gtk_file_selection_show_fileop_buttons( GtkFileSelection *filesel ){ g_return_if_fail( filesel != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) ); return; /* delete, create directory, and rename */ /* if (!filesel->fileop_c_dir) { filesel->fileop_c_dir = gtk_button_new_with_label (_("MkDir")); gtk_signal_connect (GTK_OBJECT (filesel->fileop_c_dir), "clicked", (GtkSignalFunc) gtk_file_selection_create_dir, (gpointer) filesel); gtk_box_pack_start (GTK_BOX (filesel->button_area), filesel->fileop_c_dir, TRUE,TRUE, 0); gtk_widget_show (filesel->fileop_c_dir); } if (!filesel->fileop_del_file) { filesel->fileop_del_file = gtk_button_new_with_label (_("Delete")); gtk_signal_connect (GTK_OBJECT (filesel->fileop_del_file), "clicked", (GtkSignalFunc) gtk_file_selection_delete_file, (gpointer) filesel); gtk_box_pack_start (GTK_BOX (filesel->button_area), filesel->fileop_del_file, TRUE,TRUE, 0); gtk_widget_show (filesel->fileop_del_file); } if (!filesel->fileop_ren_file) { filesel->fileop_ren_file = gtk_button_new_with_label (_("Rename")); gtk_signal_connect (GTK_OBJECT (filesel->fileop_ren_file), "clicked", (GtkSignalFunc) gtk_file_selection_rename_file, (gpointer) filesel); gtk_box_pack_start (GTK_BOX (filesel->button_area), filesel->fileop_ren_file, TRUE,TRUE, 0); gtk_widget_show (filesel->fileop_ren_file); } gtk_widget_queue_resize(GTK_WIDGET(filesel)); */ } void gtk_file_selection_hide_fileop_buttons( GtkFileSelection *filesel ){ g_return_if_fail( filesel != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) ); return; /* if (filesel->fileop_ren_file) { gtk_widget_destroy (filesel->fileop_ren_file); filesel->fileop_ren_file = NULL; } if (filesel->fileop_del_file) { gtk_widget_destroy (filesel->fileop_del_file); filesel->fileop_del_file = NULL; } if (filesel->fileop_c_dir) { gtk_widget_destroy (filesel->fileop_c_dir); filesel->fileop_c_dir = NULL; } */ } void gtk_file_selection_set_filename( GtkFileSelection *filesel, const gchar *filename ){ char buf[MAXPATHLEN]; const char *name, *last_slash; g_return_if_fail( filesel != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) ); g_return_if_fail( filename != NULL ); last_slash = strrchr( filename, '/' ); if ( !last_slash ) { buf[0] = 0; name = filename; } else { gint len = MIN( MAXPATHLEN - 1, last_slash - filename + 1 ); strncpy( buf, filename, len ); buf[len] = 0; name = last_slash + 1; } gtk_file_selection_populate( filesel, buf, FALSE ); if ( filesel->selection_entry ) { gtk_entry_set_text( GTK_ENTRY( filesel->selection_entry ), name ); } } gchar* gtk_file_selection_get_filename( GtkFileSelection *filesel ){ static char nothing[2] = ""; char *text; char *filename; g_return_val_if_fail( filesel != NULL, nothing ); g_return_val_if_fail( GTK_IS_FILE_SELECTION( filesel ), nothing ); text = gtk_entry_get_text( GTK_ENTRY( filesel->selection_entry ) ); if ( text ) { filename = cmpl_completion_fullname( text, filesel->cmpl_state ); return filename; } return nothing; } void gtk_file_selection_complete( GtkFileSelection *filesel, const gchar *pattern ){ gchar *new_pattern; gint x; g_return_if_fail( filesel != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) ); g_return_if_fail( pattern != NULL ); if ( filesel->selection_entry ) { gtk_entry_set_text( GTK_ENTRY( filesel->selection_entry ), pattern ); } if ( strchr( pattern,'*' ) || strchr( pattern,'?' ) ) { for ( x = strlen( pattern ); x >= 0; x-- ) { if ( pattern[x] == '/' ) { break; } } gtk_entry_set_text( GTK_ENTRY( filesel->mask_entry ),g_strdup( pattern + x + 1 ) ); if ( filesel->mask ) { g_free( filesel->mask ); } filesel->mask = g_strdup( pattern + x + 1 ); new_pattern = g_strdup( pattern ); new_pattern[x + 1] = 0; gtk_file_selection_populate( filesel, (gchar*) new_pattern, TRUE ); g_free( new_pattern ); } else { gtk_file_selection_populate( filesel, (gchar*) pattern, TRUE ); } } static void gtk_file_selection_realize( GtkWidget *widget ){ GtkFileSelection *filesel; const gchar *masks[] = { "All Files <*>", NULL }; g_return_if_fail( widget != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( widget ) ); filesel = GTK_FILE_SELECTION( widget ); /* make sure that we have at least one mask */ if ( !filesel->masks ) { gtk_file_selection_set_masks( filesel, masks ); } filesel->mask = g_strdup( (gchar*) filesel->masks->data ); gtk_file_selection_populate( filesel, "", FALSE ); if ( GTK_WIDGET_CLASS( parent_class )->realize ) { ( *GTK_WIDGET_CLASS( parent_class )->realize )( widget ); } } static void gtk_file_selection_destroy( GtkObject *object ){ GtkFileSelection *filesel; GList *list; g_return_if_fail( object != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( object ) ); filesel = GTK_FILE_SELECTION( object ); if ( filesel->fileop_dialog ) { gtk_widget_destroy( filesel->fileop_dialog ); } if ( filesel->next_history ) { list = filesel->next_history; while ( list ) { g_free( list->data ); list = list->next; } } g_list_free( filesel->next_history ); filesel->next_history = NULL; if ( filesel->prev_history ) { list = filesel->prev_history; while ( list ) { g_free( list->data ); list = list->next; } } g_list_free( filesel->prev_history ); filesel->prev_history = NULL; if ( filesel->mask ) { g_free( filesel->mask ); filesel->mask = NULL; } cmpl_free_state( filesel->cmpl_state ); filesel->cmpl_state = NULL; if ( GTK_OBJECT_CLASS( parent_class )->destroy ) { ( *GTK_OBJECT_CLASS( parent_class )->destroy )( object ); } } /* Begin file operations callbacks */ static gint gtk_file_selection_show_fileop_menu( GtkCList *clist, GdkEvent *event, GtkFileSelection *fs ){ GdkEventButton *event_button; g_return_val_if_fail( clist != NULL, FALSE ); g_return_val_if_fail( GTK_IS_CLIST( clist ), FALSE ); g_return_val_if_fail( event != NULL, FALSE ); g_return_val_if_fail( fs != NULL, FALSE ); g_return_val_if_fail( GTK_FILE_SELECTION( fs ), FALSE ); if ( event->type == GDK_BUTTON_PRESS ) { event_button = (GdkEventButton *) event; if ( event_button->button == 3 ) { gtk_menu_popup( GTK_MENU( fs->fileop_menu ), NULL, NULL, NULL, NULL, event_button->button, event_button->time ); return TRUE; } } return FALSE; } static void gtk_file_selection_fileop_error( GtkFileSelection *fs, gchar *error_message ){ GtkWidget *label; GtkWidget *vbox; GtkWidget *button; GtkWidget *dialog; g_return_if_fail( error_message != NULL ); /* main dialog */ dialog = gtk_dialog_new(); /* gtk_signal_connect (GTK_OBJECT (dialog), "destroy", (GtkSignalFunc) gtk_file_selection_fileop_destroy, (gpointer) fs); */ gtk_window_set_title( GTK_WINDOW( dialog ), _( "Error" ) ); gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE ); /* If file dialog is grabbed, make this dialog modal too */ /* When error dialog is closed, file dialog will be grabbed again */ if ( GTK_WINDOW( fs )->modal ) { gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE ); } vbox = gtk_vbox_new( FALSE, 0 ); gtk_container_set_border_width( GTK_CONTAINER( vbox ), 8 ); gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), vbox, FALSE, FALSE, 0 ); gtk_widget_show( vbox ); label = gtk_label_new( error_message ); gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.0 ); gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 5 ); gtk_widget_show( label ); /* yes, we free it */ g_free( error_message ); /* close button */ button = gtk_button_new_with_label( _( "Close" ) ); gtk_signal_connect_object( GTK_OBJECT( button ), "clicked", (GtkSignalFunc) gtk_widget_destroy, (gpointer) dialog ); gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ), button, TRUE, TRUE, 0 ); GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT ); gtk_widget_grab_default( button ); gtk_widget_show( button ); gtk_widget_show( dialog ); } static void gtk_file_selection_fileop_destroy( GtkWidget *widget, gpointer data ){ GtkFileSelection *fs = data; g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); fs->fileop_dialog = NULL; g_free( fs->fileop_data ); fs->fileop_data = NULL; } static void gtk_file_selection_create_dir_confirmed( GtkWidget *widget, gpointer data ){ GtkFileSelection *fs = data; gchar *dirname; gchar *path; gchar *full_path; gchar *buf; CompletionState *cmpl_state; g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); dirname = gtk_entry_get_text( GTK_ENTRY( fs->fileop_entry ) ); cmpl_state = (CompletionState*) fs->cmpl_state; path = cmpl_reference_position( cmpl_state ); full_path = g_strconcat( path, "/", dirname, NULL ); if ( ( mkdir( full_path, 0755 ) < 0 ) ) { buf = g_strconcat( "Error creating directory \"", dirname, "\": ", g_strerror( errno ), NULL ); gtk_file_selection_fileop_error( fs, buf ); } g_free( full_path ); gtk_widget_destroy( fs->fileop_dialog ); gtk_file_selection_populate( fs, "", FALSE ); } static void gtk_file_selection_create_dir( gpointer data ){ GtkFileSelection *fs = data; GtkWidget *label; GtkWidget *dialog; GtkWidget *vbox; GtkWidget *button; g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); if ( fs->fileop_dialog ) { return; } /* main dialog */ fs->fileop_dialog = dialog = gtk_dialog_new(); gtk_signal_connect( GTK_OBJECT( dialog ), "destroy", (GtkSignalFunc) gtk_file_selection_fileop_destroy, (gpointer) fs ); gtk_window_set_title( GTK_WINDOW( dialog ), _( "Create Directory" ) ); gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE ); /* If file dialog is grabbed, grab option dialog */ /* When option dialog is closed, file dialog will be grabbed again */ if ( GTK_WINDOW( fs )->modal ) { gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE ); } vbox = gtk_vbox_new( FALSE, 0 ); gtk_container_set_border_width( GTK_CONTAINER( vbox ), 8 ); gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), vbox, FALSE, FALSE, 0 ); gtk_widget_show( vbox ); label = gtk_label_new( _( "Directory name:" ) ); gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.0 ); gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 5 ); gtk_widget_show( label ); /* The directory entry widget */ fs->fileop_entry = gtk_entry_new(); gtk_box_pack_start( GTK_BOX( vbox ), fs->fileop_entry, TRUE, TRUE, 5 ); GTK_WIDGET_SET_FLAGS( fs->fileop_entry, GTK_CAN_DEFAULT ); gtk_widget_show( fs->fileop_entry ); /* buttons */ button = gtk_button_new_with_label( _( "Create" ) ); gtk_signal_connect( GTK_OBJECT( button ), "clicked", (GtkSignalFunc) gtk_file_selection_create_dir_confirmed, (gpointer) fs ); gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ), button, TRUE, TRUE, 0 ); GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT ); gtk_widget_show( button ); button = gtk_button_new_with_label( _( "Cancel" ) ); gtk_signal_connect_object( GTK_OBJECT( button ), "clicked", (GtkSignalFunc) gtk_widget_destroy, (gpointer) dialog ); gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ), button, TRUE, TRUE, 0 ); GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT ); gtk_widget_grab_default( button ); gtk_widget_show( button ); gtk_widget_show( dialog ); } static void gtk_file_selection_delete_file_confirmed( GtkWidget *widget, gpointer data ){ GtkFileSelection *fs = data; CompletionState *cmpl_state; gchar *path; gchar *full_path; gchar *buf; g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); cmpl_state = (CompletionState*) fs->cmpl_state; path = cmpl_reference_position( cmpl_state ); full_path = g_strconcat( path, "/", fs->fileop_file, NULL ); if ( ( unlink( full_path ) < 0 ) ) { buf = g_strconcat( "Error deleting file \"", fs->fileop_file, "\": ", g_strerror( errno ), NULL ); gtk_file_selection_fileop_error( fs, buf ); } g_free( full_path ); gtk_widget_destroy( fs->fileop_dialog ); gtk_file_selection_populate( fs, "", FALSE ); } static void gtk_file_selection_delete_file( gpointer data ){ GtkFileSelection *fs = data; GtkWidget *label; GtkWidget *vbox; GtkWidget *button; GtkWidget *dialog; gchar *filename; gchar *buf; g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); if ( fs->fileop_dialog ) { return; } filename = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ); if ( strlen( filename ) < 1 ) { return; } fs->fileop_file = filename; /* main dialog */ fs->fileop_dialog = dialog = gtk_dialog_new(); gtk_signal_connect( GTK_OBJECT( dialog ), "destroy", (GtkSignalFunc) gtk_file_selection_fileop_destroy, (gpointer) fs ); gtk_window_set_title( GTK_WINDOW( dialog ), _( "Delete File" ) ); gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE ); /* If file dialog is grabbed, grab option dialog */ /* When option dialog is closed, file dialog will be grabbed again */ if ( GTK_WINDOW( fs )->modal ) { gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE ); } vbox = gtk_vbox_new( FALSE, 0 ); gtk_container_set_border_width( GTK_CONTAINER( vbox ), 8 ); gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), vbox, FALSE, FALSE, 0 ); gtk_widget_show( vbox ); buf = g_strconcat( "Really delete file \"", filename, "\" ?", NULL ); label = gtk_label_new( buf ); gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.0 ); gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 5 ); gtk_widget_show( label ); g_free( buf ); /* buttons */ button = gtk_button_new_with_label( _( "Delete" ) ); gtk_signal_connect( GTK_OBJECT( button ), "clicked", (GtkSignalFunc) gtk_file_selection_delete_file_confirmed, (gpointer) fs ); gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ), button, TRUE, TRUE, 0 ); GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT ); gtk_widget_show( button ); button = gtk_button_new_with_label( _( "Cancel" ) ); gtk_signal_connect_object( GTK_OBJECT( button ), "clicked", (GtkSignalFunc) gtk_widget_destroy, (gpointer) dialog ); gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ), button, TRUE, TRUE, 0 ); GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT ); gtk_widget_grab_default( button ); gtk_widget_show( button ); gtk_widget_show( dialog ); } static void gtk_file_selection_rename_file_confirmed( GtkWidget *widget, gpointer data ){ GtkFileSelection *fs = data; gchar *buf; gchar *file; gchar *path; gchar *new_filename; gchar *old_filename; CompletionState *cmpl_state; g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); file = gtk_entry_get_text( GTK_ENTRY( fs->fileop_entry ) ); cmpl_state = (CompletionState*) fs->cmpl_state; path = cmpl_reference_position( cmpl_state ); new_filename = g_strconcat( path, "/", file, NULL ); old_filename = g_strconcat( path, "/", fs->fileop_file, NULL ); if ( strcmp( new_filename, old_filename ) ) { if ( ( rename( old_filename, new_filename ) ) < 0 ) { buf = g_strconcat( "Error renaming file \"", file, "\": ", g_strerror( errno ), NULL ); gtk_file_selection_fileop_error( fs, buf ); } } g_free( new_filename ); g_free( old_filename ); gtk_widget_destroy( fs->fileop_dialog ); gtk_file_selection_populate( fs, "", FALSE ); } static void gtk_file_selection_file_mode_confirmed( GtkWidget *widget, gpointer data ){ GtkFileSelection *fs = data; PropertiesPrivate *priv = fs->fileop_data; CompletionState *cmpl_state; gchar *filename, *file, *path; mode_t mode; mode = gtk_file_selection_properties_get_mode( priv ); file = gtk_entry_get_text( GTK_ENTRY( fs->fileop_entry ) ); cmpl_state = (CompletionState*) fs->cmpl_state; path = cmpl_reference_position( cmpl_state ); filename = g_strconcat( path, "/", file, NULL ); if ( chmod( filename, mode ) == -1 ) { gchar *buf = g_strconcat( "Error changing file mode of \"", filename, "\": ", g_strerror( errno ), NULL ); gtk_file_selection_fileop_error( fs, buf ); gtk_widget_destroy( fs->fileop_dialog ); gtk_file_selection_populate( fs, "", FALSE ); } else{ gtk_file_selection_rename_file_confirmed( widget, data ); } g_free( filename ); } static void gtk_file_selection_rename_file( gpointer data ){ GtkFileSelection *fs = data; GtkWidget *label; GtkWidget *dialog; GtkWidget *vbox; GtkWidget *button; gchar *buf; g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); if ( fs->fileop_dialog ) { return; } fs->fileop_file = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ); if ( strlen( fs->fileop_file ) < 1 ) { return; } /* main dialog */ fs->fileop_dialog = dialog = gtk_dialog_new(); gtk_signal_connect( GTK_OBJECT( dialog ), "destroy", (GtkSignalFunc) gtk_file_selection_fileop_destroy, (gpointer) fs ); gtk_window_set_title( GTK_WINDOW( dialog ), _( "Rename File" ) ); gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE ); /* If file dialog is grabbed, grab option dialog */ /* When option dialog closed, file dialog will be grabbed again */ if ( GTK_WINDOW( fs )->modal ) { gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE ); } vbox = gtk_vbox_new( FALSE, 0 ); gtk_container_set_border_width( GTK_CONTAINER( vbox ), 8 ); gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), vbox, FALSE, FALSE, 0 ); gtk_widget_show( vbox ); buf = g_strconcat( "Rename file \"", fs->fileop_file, "\" to:", NULL ); label = gtk_label_new( buf ); gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.0 ); gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 5 ); gtk_widget_show( label ); g_free( buf ); /* New filename entry */ fs->fileop_entry = gtk_entry_new(); gtk_box_pack_start( GTK_BOX( vbox ), fs->fileop_entry, TRUE, TRUE, 5 ); GTK_WIDGET_SET_FLAGS( fs->fileop_entry, GTK_CAN_DEFAULT ); gtk_widget_show( fs->fileop_entry ); gtk_entry_set_text( GTK_ENTRY( fs->fileop_entry ), fs->fileop_file ); gtk_editable_select_region( GTK_EDITABLE( fs->fileop_entry ), 0, strlen( fs->fileop_file ) ); /* buttons */ button = gtk_button_new_with_label( _( "Rename" ) ); gtk_signal_connect( GTK_OBJECT( button ), "clicked", (GtkSignalFunc) gtk_file_selection_rename_file_confirmed, (gpointer) fs ); gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ), button, TRUE, TRUE, 0 ); GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT ); gtk_widget_show( button ); button = gtk_button_new_with_label( _( "Cancel" ) ); gtk_signal_connect_object( GTK_OBJECT( button ), "clicked", (GtkSignalFunc) gtk_widget_destroy, (gpointer) dialog ); gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ), button, TRUE, TRUE, 0 ); GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT ); gtk_widget_grab_default( button ); gtk_widget_show( button ); gtk_widget_show( dialog ); } static mode_t gtk_file_selection_properties_get_mode( PropertiesPrivate* priv ){ mode_t mode = 0; if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[0] ) ) ) { mode |= S_IRUSR; } if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[1] ) ) ) { mode |= S_IWUSR; } if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[2] ) ) ) { mode |= S_IXUSR; } if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[3] ) ) ) { mode |= S_ISUID; } if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[4] ) ) ) { mode |= S_IRGRP; } if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[5] ) ) ) { mode |= S_IWGRP; } if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[6] ) ) ) { mode |= S_IXGRP; } if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[7] ) ) ) { mode |= S_ISGID; } if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[8] ) ) ) { mode |= S_IROTH; } if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[9] ) ) ) { mode |= S_IWOTH; } if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[10] ) ) ) { mode |= S_IXOTH; } if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[11] ) ) ) { mode |= S_ISVTX; } return mode; } static void gtk_file_selection_properties_update_mode( GtkWidget *widget, gpointer data ){ GtkFileSelection *fs = data; PropertiesPrivate *priv = fs->fileop_data; gchar str[8]; sprintf( str, "(%.4o)", gtk_file_selection_properties_get_mode( priv ) ); gtk_label_set( GTK_LABEL( priv->mode_label ), str ); } static void gtk_file_selection_properties( gpointer data ){ GtkFileSelection *fs = data; GtkWidget *label; GtkWidget *dialog; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *button; GtkWidget *notebook; GtkWidget *table; GtkWidget *hseparator; GtkWidget *entry; GtkWidget *togglebutton; struct stat statbuf; struct passwd *pw; struct group *gp; gchar *buf; gchar *path; gchar *filename; gchar timeBuf[TIME_STRING_BUF]; gint pagenum = 0; PropertiesPrivate *priv; int i; g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); if ( fs->fileop_dialog ) { return; } /* main dialog */ fs->fileop_dialog = dialog = gtk_dialog_new(); gtk_signal_connect( GTK_OBJECT( dialog ), "destroy", (GtkSignalFunc) gtk_file_selection_fileop_destroy, (gpointer) fs ); priv = fs->fileop_data = g_malloc( sizeof( PropertiesPrivate ) ); gtk_window_set_title( GTK_WINDOW( dialog ), ( "Properties" ) ); gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE ); /* If file dialog is grabbed, grab option dialog */ /* When option dialog closed, file dialog will be grabbed again */ if ( GTK_WINDOW( fs )->modal ) { gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE ); } /* Dialog guts go here */ notebook = gtk_notebook_new(); gtk_widget_show( notebook ); gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), notebook, TRUE, TRUE, 0 ); gtk_container_set_border_width( GTK_CONTAINER( notebook ), 8 ); path = cmpl_reference_position( fs->cmpl_state ); fs->fileop_file = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ); filename = g_strconcat( path, "/", fs->fileop_file, NULL ); if ( strlen( fs->fileop_file ) > 0 && !( stat( filename, &statbuf ) ) ) { /* stats page */ table = gtk_table_new( 9, 2, FALSE ); gtk_widget_show( table ); gtk_container_add( GTK_CONTAINER( notebook ), table ); gtk_container_set_border_width( GTK_CONTAINER( table ), 5 ); gtk_table_set_row_spacings( GTK_TABLE( table ), 4 ); gtk_table_set_col_spacings( GTK_TABLE( table ), 6 ); label = gtk_label_new( _( "Statistics" ) ); gtk_widget_show( label ); gtk_notebook_set_tab_label( GTK_NOTEBOOK( notebook ), gtk_notebook_get_nth_page( GTK_NOTEBOOK( notebook ), pagenum ), label ); pagenum++; /* path and filename */ label = gtk_label_new( _( "Path:" ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); label = gtk_label_new( _( path ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 0, 1, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_label_set_justify( GTK_LABEL( label ), GTK_JUSTIFY_LEFT ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); label = gtk_label_new( _( "File Name:" ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); fs->fileop_entry = entry = gtk_entry_new(); gtk_widget_show( entry ); gtk_table_attach( GTK_TABLE( table ), entry, 1, 2, 1, 2, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_entry_set_text( GTK_ENTRY( entry ), fs->fileop_file ); if ( access( filename, W_OK ) ) { gtk_widget_set_sensitive( GTK_WIDGET( entry ), FALSE ); } hseparator = gtk_hseparator_new(); gtk_widget_show( hseparator ); gtk_table_attach( GTK_TABLE( table ), hseparator, 0, 2, 2, 3, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( GTK_FILL ), 0, 0 ); /* file type and size */ label = gtk_label_new( _( "Type:" ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 3, 4, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); switch ( statbuf.st_mode & S_IFMT ) { case S_IFSOCK: buf = g_strdup( "Socket" ); break; case S_IFLNK: buf = g_strdup( "Symbolic link" ); break; case S_IFREG: buf = g_strdup( "File" ); break; case S_IFBLK: buf = g_strdup( "Block device" ); break; case S_IFDIR: buf = g_strdup( "Directory" ); break; case S_IFCHR: buf = g_strdup( "Character device" ); break; case S_IFIFO: buf = g_strdup( "First-in/first-out pipe" ); break; default: buf = g_strdup( "Unknown" ); } label = gtk_label_new( buf ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 3, 4, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); label = gtk_label_new( _( "Size:" ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 4, 5, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); label = gtk_label_new( _( g_strdup_printf( "%ld bytes", statbuf.st_size ) ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 4, 5, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); hseparator = gtk_hseparator_new(); gtk_widget_show( hseparator ); gtk_table_attach( GTK_TABLE( table ), hseparator, 0, 2, 5, 6, (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), (GtkAttachOptions) ( GTK_FILL ), 0, 0 ); /* file dates */ label = gtk_label_new( _( "Created:" ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 6, 7, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); strftime( timeBuf, TIME_STRING_BUF, "%a %b %d %X %Y", localtime( &statbuf.st_mtime ) ); label = gtk_label_new( _( timeBuf ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 6, 7, (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); label = gtk_label_new( _( "Modified:" ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 7, 8, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); strftime( timeBuf, TIME_STRING_BUF, "%a %b %d %X %Y", localtime( &statbuf.st_mtime ) ); label = gtk_label_new( _( timeBuf ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 7, 8, (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_label_set_justify( GTK_LABEL( label ), GTK_JUSTIFY_LEFT ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); label = gtk_label_new( _( "Accessed:" ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 8, 9, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); strftime( timeBuf, TIME_STRING_BUF, "%a %b %d %X %Y", localtime( &statbuf.st_atime ) ); label = gtk_label_new( _( timeBuf ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 8, 9, (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_label_set_justify( GTK_LABEL( label ), GTK_JUSTIFY_LEFT ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); /* permissions page */ vbox = gtk_vbox_new( FALSE, 4 ); gtk_widget_show( vbox ); gtk_container_add( GTK_CONTAINER( notebook ), vbox ); gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 ); label = gtk_label_new( _( "Permissions" ) ); gtk_widget_show( label ); gtk_notebook_set_tab_label( GTK_NOTEBOOK( notebook ), gtk_notebook_get_nth_page( GTK_NOTEBOOK( notebook ), pagenum ), label ); pagenum++; /* owner / group */ table = gtk_table_new( 2, 2, FALSE ); gtk_widget_show( table ); gtk_box_pack_start( GTK_BOX( vbox ), table, FALSE, TRUE, 0 ); gtk_table_set_row_spacings( GTK_TABLE( table ), 2 ); gtk_table_set_col_spacings( GTK_TABLE( table ), 8 ); label = gtk_label_new( _( "Owner:" ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); entry = gtk_entry_new(); gtk_widget_show( entry ); gtk_table_attach( GTK_TABLE( table ), entry, 1, 2, 0, 1, (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); if ( ( pw = getpwuid( statbuf.st_uid ) ) ) { gtk_entry_set_text( GTK_ENTRY( entry ), pw->pw_name ); } else{ gtk_entry_set_text( GTK_ENTRY( entry ), (gpointer) statbuf.st_uid ); } if ( access( filename, W_OK ) || ( getuid() != 0 ) ) { gtk_widget_set_sensitive( GTK_WIDGET( entry ), FALSE ); } label = gtk_label_new( _( "Group:" ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); entry = gtk_entry_new(); gtk_widget_show( entry ); gtk_table_attach( GTK_TABLE( table ), entry, 1, 2, 1, 2, (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); if ( ( gp = getgrgid( statbuf.st_gid ) ) ) { gtk_entry_set_text( GTK_ENTRY( entry ), gp->gr_name ); } else{ gtk_entry_set_text( GTK_ENTRY( entry ), (gpointer) statbuf.st_gid ); } if ( access( filename, W_OK ) || ( getuid() != 0 ) ) { gtk_widget_set_sensitive( GTK_WIDGET( entry ), FALSE ); } hseparator = gtk_hseparator_new(); gtk_widget_show( hseparator ); gtk_box_pack_start( GTK_BOX( vbox ), hseparator, FALSE, TRUE, 0 ); /* permissions */ table = gtk_table_new( 4, 5, TRUE ); gtk_widget_show( table ); gtk_box_pack_start( GTK_BOX( vbox ), table, FALSE, FALSE, 0 ); gtk_table_set_row_spacings( GTK_TABLE( table ), 2 ); gtk_table_set_col_spacings( GTK_TABLE( table ), 4 ); if ( access( filename, W_OK ) || ( ( getuid() != statbuf.st_uid ) && getuid() != 0 ) ) { gtk_widget_set_sensitive( GTK_WIDGET( table ), FALSE ); } hbox = gtk_hbox_new( FALSE, 1 ); gtk_widget_show( hbox ); gtk_table_attach( GTK_TABLE( table ), hbox, 0, 1, 0, 1, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( GTK_FILL ), 0, 0 ); priv->mode_label = label = gtk_label_new( "(0000)" ); gtk_widget_show( label ); gtk_box_pack_start( GTK_BOX( hbox ), label, FALSE, FALSE, 0 ); label = gtk_label_new( _( "Read" ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 0, 1, (GtkAttachOptions) ( 0 ), (GtkAttachOptions) ( 0 ), 0, 0 ); label = gtk_label_new( _( "Write" ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 2, 3, 0, 1, (GtkAttachOptions) ( 0 ), (GtkAttachOptions) ( 0 ), 0, 0 ); label = gtk_label_new( _( "Exec" ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 3, 4, 0, 1, (GtkAttachOptions) ( 0 ), (GtkAttachOptions) ( 0 ), 0, 0 ); label = gtk_label_new( _( "Special" ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 4, 5, 0, 1, (GtkAttachOptions) ( 0 ), (GtkAttachOptions) ( 0 ), 0, 0 ); label = gtk_label_new( _( "User:" ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); priv->mode_buttons[0] = togglebutton = gtk_toggle_button_new_with_label( "" ); gtk_widget_show( togglebutton ); gtk_table_attach( GTK_TABLE( table ), togglebutton, 1, 2, 1, 2, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IRUSR ) { gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE ); } priv->mode_buttons[1] = togglebutton = gtk_toggle_button_new_with_label( "" ); gtk_widget_show( togglebutton ); gtk_table_attach( GTK_TABLE( table ), togglebutton, 2, 3, 1, 2, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IWUSR ) { gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE ); } priv->mode_buttons[2] = togglebutton = gtk_toggle_button_new_with_label( "" ); gtk_widget_show( togglebutton ); gtk_table_attach( GTK_TABLE( table ), togglebutton, 3, 4, 1, 2, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IXUSR ) { gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE ); } priv->mode_buttons[3] = togglebutton = gtk_toggle_button_new_with_label( "" ); gtk_widget_show( togglebutton ); gtk_table_attach( GTK_TABLE( table ), togglebutton, 4, 5, 1, 2, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_ISUID ) { gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE ); } label = gtk_label_new( _( "Group:" ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 2, 3, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); priv->mode_buttons[4] = togglebutton = gtk_toggle_button_new_with_label( "" ); gtk_widget_show( togglebutton ); gtk_table_attach( GTK_TABLE( table ), togglebutton, 1, 2, 2, 3, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IRGRP ) { gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE ); } priv->mode_buttons[5] = togglebutton = gtk_toggle_button_new_with_label( "" ); gtk_widget_show( togglebutton ); gtk_table_attach( GTK_TABLE( table ), togglebutton, 2, 3, 2, 3, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IWGRP ) { gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE ); } priv->mode_buttons[6] = togglebutton = gtk_toggle_button_new_with_label( "" ); gtk_widget_show( togglebutton ); gtk_table_attach( GTK_TABLE( table ), togglebutton, 3, 4, 2, 3, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IXGRP ) { gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE ); } priv->mode_buttons[7] = togglebutton = gtk_toggle_button_new_with_label( "" ); gtk_widget_show( togglebutton ); gtk_table_attach( GTK_TABLE( table ), togglebutton, 4, 5, 2, 3, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_ISGID ) { gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE ); } label = gtk_label_new( _( "Other:" ) ); gtk_widget_show( label ); gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 3, 4, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); priv->mode_buttons[8] = togglebutton = gtk_toggle_button_new_with_label( "" ); gtk_widget_show( togglebutton ); gtk_table_attach( GTK_TABLE( table ), togglebutton, 1, 2, 3, 4, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IROTH ) { gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE ); } priv->mode_buttons[9] = togglebutton = gtk_toggle_button_new_with_label( "" ); gtk_widget_show( togglebutton ); gtk_table_attach( GTK_TABLE( table ), togglebutton, 2, 3, 3, 4, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IWOTH ) { gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE ); } priv->mode_buttons[10] = togglebutton = gtk_toggle_button_new_with_label( "" ); gtk_widget_show( togglebutton ); gtk_table_attach( GTK_TABLE( table ), togglebutton, 3, 4, 3, 4, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IXOTH ) { gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE ); } priv->mode_buttons[11] = togglebutton = gtk_toggle_button_new_with_label( "" ); gtk_widget_show( togglebutton ); gtk_table_attach( GTK_TABLE( table ), togglebutton, 4, 5, 3, 4, (GtkAttachOptions) ( GTK_FILL ), (GtkAttachOptions) ( 0 ), 0, 0 ); if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_ISVTX ) { gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE ); } for ( i = 0; i < 12; i++ ) gtk_signal_connect( GTK_OBJECT( priv->mode_buttons[i] ), "toggled", GTK_SIGNAL_FUNC( gtk_file_selection_properties_update_mode ), fs ); gtk_toggle_button_toggled( GTK_TOGGLE_BUTTON( priv->mode_buttons[0] ) ); } /* global page */ vbox = gtk_vbox_new( FALSE, 0 ); gtk_widget_show( vbox ); gtk_container_add( GTK_CONTAINER( notebook ), vbox ); label = gtk_label_new( _( "Global" ) ); gtk_widget_show( label ); gtk_notebook_set_tab_label( GTK_NOTEBOOK( notebook ), gtk_notebook_get_nth_page( GTK_NOTEBOOK( notebook ), pagenum ), label ); pagenum++; label = gtk_label_new( _( "dialog preferances will go here" ) ); gtk_widget_show( label ); gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 0 ); /* end of dialog guts */ /* buttons */ button = gtk_button_new_with_label( _( "OK" ) ); // gtk_signal_connect (GTK_OBJECT (button), "clicked", // (GtkSignalFunc) gtk_file_selection_rename_file_confirmed, // (gpointer) fs); gtk_signal_connect( GTK_OBJECT( button ), "clicked", (GtkSignalFunc) gtk_file_selection_file_mode_confirmed, (gpointer) fs ); gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ), button, TRUE, TRUE, 0 ); GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT ); gtk_widget_show( button ); button = gtk_button_new_with_label( _( "Cancel" ) ); gtk_signal_connect_object( GTK_OBJECT( button ), "clicked", (GtkSignalFunc) gtk_widget_destroy, (gpointer) dialog ); gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ), button, TRUE, TRUE, 0 ); GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT ); gtk_widget_grab_default( button ); gtk_widget_show( button ); g_free( filename ); gtk_widget_show( dialog ); } static gint gtk_file_selection_key_press( GtkWidget *widget, GdkEventKey *event, gpointer user_data ){ GtkFileSelection *fs; char *text; g_return_val_if_fail( widget != NULL, FALSE ); g_return_val_if_fail( event != NULL, FALSE ); fs = GTK_FILE_SELECTION( user_data ); if ( fs->saved_entry ) { gtk_clist_unselect_all( (GtkCList *) ( fs->dir_list ) ); gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),fs->saved_entry ); g_free( fs->saved_entry ); fs->saved_entry = NULL; } if ( event->keyval == GDK_Tab ) { text = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ); text = g_strdup( text ); gtk_file_selection_populate( fs, text, TRUE ); g_free( text ); gtk_signal_emit_stop_by_name( GTK_OBJECT( widget ), "key_press_event" ); return TRUE; } return FALSE; } /* static void gtk_file_selection_home_button (GtkWidget *widget, gpointer data){ GList *list; GtkFileSelection *fs=data; list = fs->next_history; if (list) { g_free (list->data); list = list->next; } g_list_free (fs->next_history); fs->next_history = NULL; gtk_file_selection_populate (fs,"~/",FALSE); } */ static void gtk_file_selection_bookmark_button( GtkWidget *widget, GtkFileSelection *fs ){ g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_FILE_SELECTION( fs ) ); gtk_menu_popup( GTK_MENU( fs->bookmark_menu ), NULL, NULL, NULL, NULL, 0, 0 ); } static void gtk_file_selection_up_button( GtkWidget *widget, gpointer data ){ GtkFileSelection *fs = data; GList *list; g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); list = fs->next_history; if ( list ) { g_free( list->data ); list = list->next; } g_list_free( fs->next_history ); fs->next_history = NULL; gtk_file_selection_populate( fs, "../", FALSE ); /*change directories. */ } static void gtk_file_selection_prev_button( GtkWidget *widget, gpointer data ){ GtkFileSelection *fs = data; GList *list; GList *first; gchar *path; g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); list = fs->prev_history; if ( list && g_list_length( list ) > 1 ) { first = list; /* get first element */ list = list->next; /* pop off current directory */ list->prev = NULL; /* make this the new head. */ fs->prev_history = list; /* update prev_history list */ fs->next_history = g_list_prepend( fs->next_history,first->data ); /* put it on next_history */ first->next = NULL; /* orphan the old first node */ g_list_free( first ); /* free the node (data is now in use by next_history) */ path = g_malloc( strlen( list->data ) + 4 ); /* plenty of space */ strcpy( path,list->data ); /* get the 2nd path in the history */ strcat( path,"/" ); /* append a '/' */ gtk_file_selection_populate( fs, path, FALSE ); /* change directories. */ g_free( path ); } } static void gtk_file_selection_next_button( GtkWidget *widget, gpointer data ){ GtkFileSelection *fs = data; GList *list; GList *first; gchar *path; g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); list = fs->next_history; if ( list && g_list_length( list ) > 0 ) { first = list; /*get first element*/ list = list->next; /*pop off current directory*/ if ( list ) { list->prev = NULL; } fs->next_history = list; /*update prev_history list*/ path = g_malloc( strlen( first->data ) + 4 ); /*plenty of space*/ strcpy( path,first->data ); strcat( path,"/" ); /*append a / */ gtk_file_selection_populate( fs, path, FALSE ); /*change directories.*/ g_free( path ); first->next = NULL; /* orphan the old first node */ g_list_free( first ); /* free the node (data is now in use by next_history) */ } } void static gtk_file_selection_refresh_button( GtkWidget *widget, gpointer data ){ GtkFileSelection *fs = data; g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); gtk_file_selection_populate( fs,"",FALSE ); } static void gtk_file_selection_mask_entry_callback( GtkWidget *widget, gpointer data ){ GtkFileSelection *fs = data; if ( fs->mask ) { g_free( fs->mask ); } fs->mask = g_strdup( gtk_entry_get_text( GTK_ENTRY( GTK_COMBO( fs->mask_entry )->entry ) ) ); if ( strlen( fs->mask ) == 0 ) { g_free( fs->mask ); fs->mask = NULL; } gtk_file_selection_refresh_button( widget,data ); } static gint gtk_file_selection_files_list_key_callback( GtkWidget *widget, GdkEventKey *event, gpointer data ){ GtkFileSelection *fs = data; gchar *saved; gchar key[2]; // g_print("Key event: %d\n",event->keyval); //we need some sort of timeout. //if the key is a normal character then //add to our saved_entry1 //if it's backspace then remove one character //otherwise let it through (and erase our buffer. if ( event->keyval > GDK_space && event->keyval <= GDK_Korean_Won ) { key[1] = 0; key[0] = event->keyval; saved = fs->saved_entry1; if ( fs->saved_entry1 ) { fs->saved_entry1 = g_strconcat( saved,key,NULL ); g_free( saved ); } else{ fs->saved_entry1 = g_strdup( key ); } g_print( "complete: %s\n",fs->saved_entry1 ); /*gtk_label_set_text(GTK_LABEL(fs->completion_label), fs->saved_entry1); */ saved = g_strdup( gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ) ); gtk_file_selection_complete( fs,fs->saved_entry1 ); gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),saved ); g_free( saved ); } else if ( event->keyval == GDK_BackSpace ) { if ( strlen( fs->saved_entry1 ) ) { fs->saved_entry1[strlen( fs->saved_entry1 ) - 1] = 0; g_print( "complete: %s\n",fs->saved_entry1 ); /*gtk_label_set_text(GTK_LABEL(fs->completion_label),fs->saved_entry1); */ saved = g_strdup( gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ) ); gtk_file_selection_complete( fs,fs->saved_entry1 ); gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),saved ); g_free( saved ); } } else if ( event->keyval == GDK_Tab ) { saved = g_strdup( gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ) ); gtk_file_selection_populate( fs,fs->saved_entry1,TRUE ); g_free( fs->saved_entry1 ); fs->saved_entry1 = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ); gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),saved ); g_free( saved ); g_print( "complete: %s\n",fs->saved_entry1 ); /* gtk_label_set_text(GTK_LABEL(fs->completion_label),fs->saved_entry1);*/ gtk_signal_emit_stop_by_name( GTK_OBJECT( widget ), "key_press_event" ); } else { if ( fs->saved_entry1 ) { g_free( fs->saved_entry1 ); fs->saved_entry1 = NULL; } /* gtk_label_set_text(GTK_LABEL(fs->completion_label)," "); */ } return TRUE; } static gint gtk_file_selection_mask_entry_key_callback( GtkWidget *widget, GdkEventKey *event, gpointer data ){ GtkEntry *entry = (GtkEntry *)widget; GtkFileSelection *fs = data; g_return_val_if_fail( fs != NULL,FALSE ); g_return_val_if_fail( GTK_IS_FILE_SELECTION( fs ),FALSE ); if ( event->keyval == GDK_Return || event->keyval == GDK_Tab ) { if ( fs->mask ) { g_free( fs->mask ); } fs->mask = g_strdup( gtk_entry_get_text( entry ) ); gtk_file_selection_refresh_button( widget,fs ); if ( event->keyval == GDK_Return ) { gtk_signal_emit_stop_by_name( GTK_OBJECT( widget ), "key_press_event" ); } return TRUE; } else { return FALSE; } } static gint gtk_file_selection_mask_entry_button_callback( GtkWidget *widget, GdkEventButton *event, gpointer data ){ GtkFileSelection *fs = data; if ( fs->mask ) { g_free( fs->mask ); } fs->mask = g_strdup( gtk_entry_get_text( GTK_ENTRY( GTK_COMBO( fs->mask_entry )->entry ) ) ); gtk_file_selection_refresh_button( widget,fs ); return TRUE; } static gboolean gtk_file_selection_history_combo_list_key_handler( GtkWidget *widget, GdkEventKey *event, gpointer user_data ){ /* g_print("Key pressed! \n"); */ return TRUE; } static gboolean gtk_file_selection_history_combo_list_callback( GtkWidget *thelist, GdkEventButton *event, gpointer user_data ){ GtkFileSelection *fs = user_data; GList *list; gchar *path; list = fs->next_history; if ( list ) { g_free( list->data ); list = list->next; } g_list_free( fs->next_history ); fs->next_history = NULL; path = g_malloc( strlen( gtk_entry_get_text( GTK_ENTRY( ( (GtkCombo *)fs->history_combo )->entry ) ) ) + 4 ); strcpy( path,gtk_entry_get_text( GTK_ENTRY( ( (GtkCombo *)fs->history_combo )->entry ) ) ); strcat( path,"/" ); gtk_file_selection_populate( fs,path,TRUE ); g_free( path ); return TRUE; } static gboolean gtk_file_selection_history_combo_callback( GtkWidget *widget, GdkEventKey *event, gpointer data ){ GtkEntry *entry = (GtkEntry *)widget; GtkFileSelection *fs = data; GList *list; gchar *path; g_return_val_if_fail( fs != NULL,FALSE ); g_return_val_if_fail( GTK_IS_FILE_SELECTION( fs ),FALSE ); if ( event->keyval == GDK_Return ) { list = fs->next_history; if ( list ) { g_free( list->data ); list = list->next; } g_list_free( fs->next_history ); fs->next_history = NULL; path = g_malloc( strlen( gtk_entry_get_text( entry ) ) + 4 ); strcpy( path,gtk_entry_get_text( entry ) ); strcat( path,"/" ); gtk_file_selection_populate( fs,path,TRUE ); g_free( path ); gtk_signal_emit_stop_by_name( GTK_OBJECT( widget ), "key_press_event" ); return TRUE; } else { return FALSE; } } static void gtk_file_selection_bookmark_callback( GtkWidget *widget, gpointer data ){ GtkFileSelection *fs = data; BookmarkMenuStruct *item; GList *list; g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); //g_print ("Callback\n"); list = fs->bookmark_list; while ( list ) { item = list->data; if ( item->menu_item == widget ) { if ( strcmp( item->path,"./" ) ) { gtk_file_selection_populate( fs, item->path, FALSE ); } break; } list = list->next; } } static void gtk_file_selection_update_history_menu( GtkFileSelection *fs, gchar *current_directory ){ gchar *current_dir; g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); g_return_if_fail( current_directory != NULL ); current_dir = g_strdup( current_directory ); if ( fs->prev_history ) { if ( strcmp( ( fs->prev_history )->data,current_dir ) ) { /*if this item isn't on the top of the list */ fs->prev_history = g_list_prepend( fs->prev_history,g_strdup( current_dir ) ); } } else { fs->prev_history = g_list_prepend( fs->prev_history,g_strdup( current_dir ) ); } gtk_combo_set_popdown_strings( GTK_COMBO( fs->history_combo ),fs->prev_history ); g_free( current_dir ); } static void gtk_file_selection_file_button( GtkWidget *widget, gint row, gint column, GdkEventButton *bevent, gpointer user_data ){ GtkFileSelection *fs = NULL; gchar *filename, *temp = NULL; g_return_if_fail( GTK_IS_CLIST( widget ) ); fs = user_data; g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); gtk_clist_get_text( GTK_CLIST( fs->file_list ), row, 0, &temp ); filename = g_strdup( temp ); if ( filename ) { if ( fs->saved_entry ) { gtk_clist_unselect_all( (GtkCList *) ( fs->dir_list ) ); gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),fs->saved_entry ); g_free( fs->saved_entry ); fs->saved_entry = NULL; } if ( fs->saved_entry1 ) { g_free( fs->saved_entry1 ); fs->saved_entry1 = NULL; } /* gtk_label_set_text(GTK_LABEL(fs->completion_label)," "); */ if ( bevent ) { switch ( bevent->type ) { case GDK_2BUTTON_PRESS: gtk_button_clicked( GTK_BUTTON( fs->ok_button ) ); break; default: /* if (bevent->button && GDK_BUTTON2_MASK) { g_print("Right click! -- %d\n",bevent->button); } else { */ gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), filename ); /*}*/ break; } } else{ gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), filename ); } g_free( filename ); } } static void gtk_file_selection_dir_button( GtkWidget *widget, gint row, gint column, GdkEventButton *bevent, gpointer user_data ){ GList *list; GtkFileSelection *fs = NULL; gchar *filename, *temp = NULL; g_return_if_fail( GTK_IS_CLIST( widget ) ); fs = GTK_FILE_SELECTION( user_data ); g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); gtk_clist_get_text( GTK_CLIST( fs->dir_list ), row, 0, &temp ); filename = g_strdup( temp ); if ( filename ) { if ( bevent ) { switch ( bevent->type ) { case GDK_2BUTTON_PRESS: list = fs->next_history; if ( list ) { g_free( list->data ); list = list->next; } g_list_free( fs->next_history ); fs->next_history = NULL; gtk_file_selection_populate( fs, filename, FALSE ); gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),fs->saved_entry ); g_free( fs->saved_entry ); fs->saved_entry = NULL; break; default: /* here we need to add the "filename" to the beginning of what's already in the entry. Save what's in the entry, then restore it on the double click */ if ( fs->saved_entry ) { g_free( fs->saved_entry ); } fs->saved_entry = g_strdup( gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ) ); temp = g_strconcat( filename,fs->saved_entry,NULL ); gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), temp ); g_free( temp ); break; } } else{ gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), filename ); } g_free( filename ); } } static void gtk_file_selection_undir_button( GtkWidget *widget, gint row, gint column, GdkEventButton *bevent, gpointer user_data ){ GtkFileSelection *fs = NULL; gchar *filename, *temp = NULL; g_return_if_fail( GTK_IS_CLIST( widget ) ); fs = GTK_FILE_SELECTION( user_data ); g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); gtk_clist_get_text( GTK_CLIST( fs->dir_list ), row, 0, &temp ); filename = g_strdup( temp ); if ( filename ) { if ( bevent ) { switch ( bevent->type ) { default: /* here we need to add the "filename" to the beginning of what's already in the entry. Save what's in the entry, then restore it on the double click */ if ( fs->saved_entry ) { gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),fs->saved_entry ); g_free( fs->saved_entry ); fs->saved_entry = NULL; } break; } } else{ gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), filename ); //????? } g_free( filename ); } } static void gtk_file_selection_populate( GtkFileSelection *fs, gchar *rel_path, gint try_complete ){ CompletionState *cmpl_state; PossibleCompletion* poss; gchar* filename; gint row; gchar* rem_path = rel_path; gchar* sel_text; gchar* text[2]; gint did_recurse = FALSE; gint possible_count = 0; gint selection_index = -1; gint file_list_width; gint dir_list_width; g_return_if_fail( fs != NULL ); g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) ); cmpl_state = (CompletionState*) fs->cmpl_state; poss = cmpl_completion_matches( rel_path, &rem_path, cmpl_state ); if ( !cmpl_state_okay( cmpl_state ) ) { /* Something went wrong. */ gtk_file_selection_abort( fs ); return; } g_assert( cmpl_state->reference_dir ); gtk_clist_freeze( GTK_CLIST( fs->dir_list ) ); gtk_clist_clear( GTK_CLIST( fs->dir_list ) ); gtk_clist_freeze( GTK_CLIST( fs->file_list ) ); gtk_clist_clear( GTK_CLIST( fs->file_list ) ); /* Set the dir_list to include ./ and ../ */ /* Actually, no let's not. text[1] = NULL; text[0] = "./"; row = gtk_clist_append (GTK_CLIST (fs->dir_list), text); */ text[0] = "../"; //Do we need ..? row = gtk_clist_append( GTK_CLIST( fs->dir_list ), text ); /*reset the max widths of the lists*/ dir_list_width = gdk_string_width( fs->dir_list->style->font,"../" ); gtk_clist_set_column_width( GTK_CLIST( fs->dir_list ),0,dir_list_width ); file_list_width = 1; gtk_clist_set_column_width( GTK_CLIST( fs->file_list ),0,file_list_width ); while ( poss ) { if ( cmpl_is_a_completion( poss ) ) { possible_count += 1; filename = cmpl_this_completion( poss ); text[0] = filename; if ( cmpl_is_directory( poss ) ) { if ( strcmp( filename, "./" ) != 0 && strcmp( filename, "../" ) != 0 ) { int width = gdk_string_width( fs->dir_list->style->font, filename ); row = gtk_clist_append( GTK_CLIST( fs->dir_list ), text ); if ( width > dir_list_width ) { dir_list_width = width; gtk_clist_set_column_width( GTK_CLIST( fs->dir_list ),0, width ); } } } else { if ( fs->mask ) { if ( gtk_file_selection_match_mask( filename,fs->mask ) ) { int width = gdk_string_width( fs->file_list->style->font, filename ); row = gtk_clist_append( GTK_CLIST( fs->file_list ), text ); if ( width > file_list_width ) { file_list_width = width; gtk_clist_set_column_width( GTK_CLIST( fs->file_list ),0, width ); } } } else { int width = gdk_string_width( fs->file_list->style->font, filename ); row = gtk_clist_append( GTK_CLIST( fs->file_list ), text ); if ( width > file_list_width ) { file_list_width = width; gtk_clist_set_column_width( GTK_CLIST( fs->file_list ),0, width ); } } } } poss = cmpl_next_completion( cmpl_state ); } gtk_clist_thaw( GTK_CLIST( fs->dir_list ) ); gtk_clist_thaw( GTK_CLIST( fs->file_list ) ); /* File lists are set. */ g_assert( cmpl_state->reference_dir ); if ( try_complete ) { /* User is trying to complete filenames, so advance the user's input * string to the updated_text, which is the common leading substring * of all possible completions, and if its a directory attempt * attempt completions in it. */ if ( cmpl_updated_text( cmpl_state )[0] ) { if ( cmpl_updated_dir( cmpl_state ) ) { gchar* dir_name = g_strdup( cmpl_updated_text( cmpl_state ) ); did_recurse = TRUE; gtk_file_selection_populate( fs, dir_name, TRUE ); g_free( dir_name ); } else { if ( fs->selection_entry ) { gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), cmpl_updated_text( cmpl_state ) ); } } } else { selection_index = cmpl_last_valid_char( cmpl_state ) - ( strlen( rel_path ) - strlen( rem_path ) ); if ( fs->selection_entry ) { gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), rem_path ); } } } else { if ( fs->selection_entry ) { /* Here we need to take the old filename and keep it!*/ /*gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");*/ ; } } if ( !did_recurse ) { if ( fs->selection_entry ) { gtk_entry_set_position( GTK_ENTRY( fs->selection_entry ), selection_index ); } if ( fs->selection_entry ) { sel_text = g_strconcat( _( "Selection: " ), cmpl_reference_position( cmpl_state ), NULL ); /* gtk_label_set_text (GTK_LABEL (fs->selection_text), sel_text); */ g_free( sel_text ); } gtk_file_selection_update_history_menu( fs, cmpl_reference_position( cmpl_state ) ); } } static void gtk_file_selection_abort( GtkFileSelection *fs ){ gchar err_buf[256]; sprintf( err_buf, _( "Directory unreadable: %s" ), cmpl_strerror( cmpl_errno ) ); /* BEEP gdk_beep(); */ /* if (fs->selection_entry) gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf); */ } /**********************************************************************/ /* External Interface */ /**********************************************************************/ /* The four completion state selectors */ static gchar* cmpl_updated_text( CompletionState* cmpl_state ){ return cmpl_state->updated_text; } static gint cmpl_updated_dir( CompletionState* cmpl_state ){ return cmpl_state->re_complete; } static gchar* cmpl_reference_position( CompletionState* cmpl_state ){ return cmpl_state->reference_dir->fullname; } static gint cmpl_last_valid_char( CompletionState* cmpl_state ){ return cmpl_state->last_valid_char; } static gchar* cmpl_completion_fullname( gchar* text, CompletionState* cmpl_state ){ static char nothing[2] = ""; if ( !cmpl_state_okay( cmpl_state ) ) { return nothing; } else if ( text[0] == '/' ) { strcpy( cmpl_state->updated_text, text ); } else if ( text[0] == '~' ) { CompletionDir* dir; char* slash; dir = open_user_dir( text, cmpl_state ); if ( !dir ) { /* spencer says just return ~something, so * for now just do it. */ strcpy( cmpl_state->updated_text, text ); } else { strcpy( cmpl_state->updated_text, dir->fullname ); slash = strchr( text, '/' ); if ( slash ) { strcat( cmpl_state->updated_text, slash ); } } } else { strcpy( cmpl_state->updated_text, cmpl_state->reference_dir->fullname ); if ( strcmp( cmpl_state->reference_dir->fullname, "/" ) != 0 ) { strcat( cmpl_state->updated_text, "/" ); } strcat( cmpl_state->updated_text, text ); } return cmpl_state->updated_text; } /* The three completion selectors */ static gchar* cmpl_this_completion( PossibleCompletion* pc ){ return pc->text; } static gint cmpl_is_directory( PossibleCompletion* pc ){ return pc->is_directory; } static gint cmpl_is_a_completion( PossibleCompletion* pc ){ return pc->is_a_completion; } /**********************************************************************/ /* Construction, deletion */ /**********************************************************************/ static CompletionState* cmpl_init_state( void ){ gchar getcwd_buf[2 * MAXPATHLEN]; CompletionState *new_state; new_state = g_new( CompletionState, 1 ); /* We don't use getcwd() on SUNOS, because, it does a popen("pwd") * and, if that wasn't bad enough, hangs in doing so. */ #if defined( sun ) && !defined( __SVR4 ) if ( !getwd( getcwd_buf ) ) #else if ( !getcwd( getcwd_buf, MAXPATHLEN ) ) #endif { /* Oh joy, we can't get the current directory. Um..., we should have * a root directory, right? Right? (Probably not portable to non-Unix) */ strcpy( getcwd_buf, "/" ); } tryagain: new_state->reference_dir = NULL; new_state->completion_dir = NULL; new_state->active_completion_dir = NULL; new_state->directory_storage = NULL; new_state->directory_sent_storage = NULL; new_state->last_valid_char = 0; new_state->updated_text = g_new( gchar, MAXPATHLEN ); new_state->updated_text_alloc = MAXPATHLEN; new_state->the_completion.text = g_new( gchar, MAXPATHLEN ); new_state->the_completion.text_alloc = MAXPATHLEN; new_state->user_dir_name_buffer = NULL; new_state->user_directories = NULL; new_state->reference_dir = open_dir( getcwd_buf, new_state ); if ( !new_state->reference_dir ) { /* Directories changing from underneath us, grumble */ strcpy( getcwd_buf, "/" ); goto tryagain; } return new_state; } static void cmpl_free_dir_list( GList* dp0 ){ GList *dp = dp0; while ( dp ) { free_dir( dp->data ); dp = dp->next; } g_list_free( dp0 ); } static void cmpl_free_dir_sent_list( GList* dp0 ){ GList *dp = dp0; while ( dp ) { free_dir_sent( dp->data ); dp = dp->next; } g_list_free( dp0 ); } static void cmpl_free_state( CompletionState* cmpl_state ){ cmpl_free_dir_list( cmpl_state->directory_storage ); cmpl_free_dir_sent_list( cmpl_state->directory_sent_storage ); if ( cmpl_state->user_dir_name_buffer ) { g_free( cmpl_state->user_dir_name_buffer ); } if ( cmpl_state->user_directories ) { g_free( cmpl_state->user_directories ); } if ( cmpl_state->the_completion.text ) { g_free( cmpl_state->the_completion.text ); } if ( cmpl_state->updated_text ) { g_free( cmpl_state->updated_text ); } g_free( cmpl_state ); } static void free_dir( CompletionDir* dir ){ g_free( dir->fullname ); g_free( dir ); } static void free_dir_sent( CompletionDirSent* sent ){ g_free( sent->name_buffer ); g_free( sent->entries ); g_free( sent ); } static void prune_memory_usage( CompletionState *cmpl_state ){ GList* cdsl = cmpl_state->directory_sent_storage; GList* cdl = cmpl_state->directory_storage; GList* cdl0 = cdl; gint len = 0; for (; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1 ) cdsl = cdsl->next; if ( cdsl ) { cmpl_free_dir_sent_list( cdsl->next ); cdsl->next = NULL; } cmpl_state->directory_storage = NULL; while ( cdl ) { if ( cdl->data == cmpl_state->reference_dir ) { cmpl_state->directory_storage = g_list_prepend( NULL, cdl->data ); } else{ free_dir( cdl->data ); } cdl = cdl->next; } g_list_free( cdl0 ); } /**********************************************************************/ /* The main entrances. */ /**********************************************************************/ static PossibleCompletion* cmpl_completion_matches( gchar* text_to_complete, gchar** remaining_text, CompletionState* cmpl_state ){ gchar* first_slash; PossibleCompletion *poss; prune_memory_usage( cmpl_state ); g_assert( text_to_complete != NULL ); cmpl_state->user_completion_index = -1; cmpl_state->last_completion_text = text_to_complete; cmpl_state->the_completion.text[0] = 0; cmpl_state->last_valid_char = 0; cmpl_state->updated_text_len = -1; cmpl_state->updated_text[0] = 0; cmpl_state->re_complete = FALSE; first_slash = strchr( text_to_complete, '/' ); if ( text_to_complete[0] == '~' && !first_slash ) { /* Text starts with ~ and there is no slash, show all the * home directory completions. */ poss = attempt_homedir_completion( text_to_complete, cmpl_state ); update_cmpl( poss, cmpl_state ); return poss; } cmpl_state->reference_dir = open_ref_dir( text_to_complete, remaining_text, cmpl_state ); if ( !cmpl_state->reference_dir ) { return NULL; } cmpl_state->completion_dir = find_completion_dir( *remaining_text, remaining_text, cmpl_state ); cmpl_state->last_valid_char = *remaining_text - text_to_complete; if ( !cmpl_state->completion_dir ) { return NULL; } cmpl_state->completion_dir->cmpl_index = -1; cmpl_state->completion_dir->cmpl_parent = NULL; cmpl_state->completion_dir->cmpl_text = *remaining_text; cmpl_state->active_completion_dir = cmpl_state->completion_dir; cmpl_state->reference_dir = cmpl_state->completion_dir; poss = attempt_file_completion( cmpl_state ); update_cmpl( poss, cmpl_state ); return poss; } static PossibleCompletion* cmpl_next_completion( CompletionState* cmpl_state ){ PossibleCompletion* poss = NULL; cmpl_state->the_completion.text[0] = 0; if ( cmpl_state->user_completion_index >= 0 ) { poss = attempt_homedir_completion( cmpl_state->last_completion_text, cmpl_state ); } else{ poss = attempt_file_completion( cmpl_state ); } update_cmpl( poss, cmpl_state ); return poss; } /**********************************************************************/ /* Directory Operations */ /**********************************************************************/ /* Open the directory where completion will begin from, if possible. */ static CompletionDir* open_ref_dir( gchar* text_to_complete, gchar** remaining_text, CompletionState* cmpl_state ){ gchar* first_slash; CompletionDir *new_dir; first_slash = strchr( text_to_complete, '/' ); if ( text_to_complete[0] == '~' ) { new_dir = open_user_dir( text_to_complete, cmpl_state ); if ( new_dir ) { if ( first_slash ) { *remaining_text = first_slash + 1; } else{ *remaining_text = text_to_complete + strlen( text_to_complete ); } } else { return NULL; } } else if ( text_to_complete[0] == '/' || !cmpl_state->reference_dir ) { gchar *tmp = g_strdup( text_to_complete ); gchar *p; p = tmp; while ( *p && *p != '*' && *p != '?' ) p++; *p = '\0'; p = strrchr( tmp, '/' ); if ( p ) { if ( p == tmp ) { p++; } *p = '\0'; new_dir = open_dir( tmp, cmpl_state ); if ( new_dir ) { *remaining_text = text_to_complete + ( ( p == tmp + 1 ) ? ( p - tmp ) : ( p + 1 - tmp ) ); } } else { /* If no possible candidates, use the cwd */ gchar *curdir = g_get_current_dir(); new_dir = open_dir( curdir, cmpl_state ); if ( new_dir ) { *remaining_text = text_to_complete; } g_free( curdir ); } g_free( tmp ); } else { *remaining_text = text_to_complete; new_dir = open_dir( cmpl_state->reference_dir->fullname, cmpl_state ); } if ( new_dir ) { new_dir->cmpl_index = -1; new_dir->cmpl_parent = NULL; } return new_dir; } /* open a directory by user name */ static CompletionDir* open_user_dir( gchar* text_to_complete, CompletionState *cmpl_state ){ gchar *first_slash; gint cmp_len; g_assert( text_to_complete && text_to_complete[0] == '~' ); first_slash = strchr( text_to_complete, '/' ); if ( first_slash ) { cmp_len = first_slash - text_to_complete - 1; } else{ cmp_len = strlen( text_to_complete + 1 ); } if ( !cmp_len ) { /* ~/ */ gchar *homedir = g_get_home_dir(); if ( homedir ) { return open_dir( homedir, cmpl_state ); } else{ return NULL; } } else { /* ~user/ */ char* copy = g_new( char, cmp_len + 1 ); struct passwd *pwd; strncpy( copy, text_to_complete + 1, cmp_len ); copy[cmp_len] = 0; pwd = getpwnam( copy ); g_free( copy ); if ( !pwd ) { cmpl_errno = errno; return NULL; } return open_dir( pwd->pw_dir, cmpl_state ); } } /* open a directory relative the the current relative directory */ static CompletionDir* open_relative_dir( gchar* dir_name, CompletionDir* dir, CompletionState *cmpl_state ){ gchar path_buf[2 * MAXPATHLEN]; if ( dir->fullname_len + strlen( dir_name ) + 2 >= MAXPATHLEN ) { cmpl_errno = CMPL_ERRNO_TOO_LONG; return NULL; } strcpy( path_buf, dir->fullname ); if ( dir->fullname_len > 1 ) { path_buf[dir->fullname_len] = '/'; strcpy( path_buf + dir->fullname_len + 1, dir_name ); } else { strcpy( path_buf + dir->fullname_len, dir_name ); } return open_dir( path_buf, cmpl_state ); } /* after the cache lookup fails, really open a new directory */ static CompletionDirSent* open_new_dir( gchar* dir_name, struct stat* sbuf, gboolean stat_subdirs ){ CompletionDirSent* sent; DIR* directory; gchar *buffer_ptr; struct dirent *dirent_ptr; gint buffer_size = 0; gint entry_count = 0; gint i; struct stat ent_sbuf; char path_buf[MAXPATHLEN * 2]; gint path_buf_len; sent = g_new( CompletionDirSent, 1 ); sent->mtime = sbuf->st_mtime; sent->inode = sbuf->st_ino; sent->device = sbuf->st_dev; path_buf_len = strlen( dir_name ); if ( path_buf_len > MAXPATHLEN ) { cmpl_errno = CMPL_ERRNO_TOO_LONG; return NULL; } strcpy( path_buf, dir_name ); directory = opendir( dir_name ); if ( !directory ) { cmpl_errno = errno; return NULL; } while ( ( dirent_ptr = readdir( directory ) ) != NULL ) { int entry_len = strlen( dirent_ptr->d_name ); buffer_size += entry_len + 1; entry_count += 1; if ( path_buf_len + entry_len + 2 >= MAXPATHLEN ) { cmpl_errno = CMPL_ERRNO_TOO_LONG; closedir( directory ); return NULL; } } sent->name_buffer = g_new( gchar, buffer_size ); sent->entries = g_new( CompletionDirEntry, entry_count ); sent->entry_count = entry_count; buffer_ptr = sent->name_buffer; rewinddir( directory ); for ( i = 0; i < entry_count; i += 1 ) { dirent_ptr = readdir( directory ); if ( !dirent_ptr ) { cmpl_errno = errno; closedir( directory ); return NULL; } strcpy( buffer_ptr, dirent_ptr->d_name ); sent->entries[i].entry_name = buffer_ptr; buffer_ptr += strlen( dirent_ptr->d_name ); *buffer_ptr = 0; buffer_ptr += 1; path_buf[path_buf_len] = '/'; strcpy( path_buf + path_buf_len + 1, dirent_ptr->d_name ); if ( stat_subdirs ) { if ( stat( path_buf, &ent_sbuf ) >= 0 && S_ISDIR( ent_sbuf.st_mode ) ) { sent->entries[i].is_dir = 1; } else{ /* stat may fail, and we don't mind, since it could be a * dangling symlink. */ sent->entries[i].is_dir = 0; } } else{ sent->entries[i].is_dir = 1; } } qsort( sent->entries, sent->entry_count, sizeof( CompletionDirEntry ), compare_cmpl_dir ); closedir( directory ); return sent; } static gboolean check_dir( gchar *dir_name, struct stat *result, gboolean *stat_subdirs ){ /* A list of directories that we know only contain other directories. * Trying to stat every file in these directories would be very * expensive. */ static struct { gchar *name; gboolean present; struct stat statbuf; } no_stat_dirs[] = { { "/afs", FALSE, { 0 } }, { "/net", FALSE, { 0 } } }; static const gint n_no_stat_dirs = sizeof( no_stat_dirs ) / sizeof( no_stat_dirs[0] ); static gboolean initialized = FALSE; gint i; if ( !initialized ) { initialized = TRUE; for ( i = 0; i < n_no_stat_dirs; i++ ) { if ( stat( no_stat_dirs[i].name, &no_stat_dirs[i].statbuf ) == 0 ) { no_stat_dirs[i].present = TRUE; } } } if ( stat( dir_name, result ) < 0 ) { cmpl_errno = errno; return FALSE; } *stat_subdirs = TRUE; for ( i = 0; i < n_no_stat_dirs; i++ ) { if ( no_stat_dirs[i].present && ( no_stat_dirs[i].statbuf.st_dev == result->st_dev ) && ( no_stat_dirs[i].statbuf.st_ino == result->st_ino ) ) { *stat_subdirs = FALSE; break; } } return TRUE; } /* open a directory by absolute pathname */ static CompletionDir* open_dir( gchar* dir_name, CompletionState* cmpl_state ){ struct stat sbuf; gboolean stat_subdirs; CompletionDirSent *sent; GList* cdsl; if ( !check_dir( dir_name, &sbuf, &stat_subdirs ) ) { return NULL; } cdsl = cmpl_state->directory_sent_storage; while ( cdsl ) { sent = cdsl->data; if ( sent->inode == sbuf.st_ino && sent->mtime == sbuf.st_mtime && sent->device == sbuf.st_dev ) { return attach_dir( sent, dir_name, cmpl_state ); } cdsl = cdsl->next; } sent = open_new_dir( dir_name, &sbuf, stat_subdirs ); if ( sent ) { cmpl_state->directory_sent_storage = g_list_prepend( cmpl_state->directory_sent_storage, sent ); return attach_dir( sent, dir_name, cmpl_state ); } return NULL; } static CompletionDir* attach_dir( CompletionDirSent* sent, gchar* dir_name, CompletionState *cmpl_state ){ CompletionDir* new_dir; new_dir = g_new( CompletionDir, 1 ); cmpl_state->directory_storage = g_list_prepend( cmpl_state->directory_storage, new_dir ); new_dir->sent = sent; new_dir->fullname = g_strdup( dir_name ); new_dir->fullname_len = strlen( dir_name ); return new_dir; } static gint correct_dir_fullname( CompletionDir* cmpl_dir ){ gint length = strlen( cmpl_dir->fullname ); struct stat sbuf; if ( strcmp( cmpl_dir->fullname + length - 2, "/." ) == 0 ) { if ( length == 2 ) { strcpy( cmpl_dir->fullname, "/" ); cmpl_dir->fullname_len = 1; return TRUE; } else { cmpl_dir->fullname[length - 2] = 0; } } else if ( strcmp( cmpl_dir->fullname + length - 3, "/./" ) == 0 ) { cmpl_dir->fullname[length - 2] = 0; } else if ( strcmp( cmpl_dir->fullname + length - 3, "/.." ) == 0 ) { if ( length == 3 ) { strcpy( cmpl_dir->fullname, "/" ); cmpl_dir->fullname_len = 1; return TRUE; } if ( stat( cmpl_dir->fullname, &sbuf ) < 0 ) { cmpl_errno = errno; return FALSE; } cmpl_dir->fullname[length - 2] = 0; if ( !correct_parent( cmpl_dir, &sbuf ) ) { return FALSE; } } else if ( strcmp( cmpl_dir->fullname + length - 4, "/../" ) == 0 ) { if ( length == 4 ) { strcpy( cmpl_dir->fullname, "/" ); cmpl_dir->fullname_len = 1; return TRUE; } if ( stat( cmpl_dir->fullname, &sbuf ) < 0 ) { cmpl_errno = errno; return FALSE; } cmpl_dir->fullname[length - 3] = 0; if ( !correct_parent( cmpl_dir, &sbuf ) ) { return FALSE; } } cmpl_dir->fullname_len = strlen( cmpl_dir->fullname ); return TRUE; } static gint correct_parent( CompletionDir* cmpl_dir, struct stat *sbuf ){ struct stat parbuf; gchar *last_slash; gchar *new_name; gchar c = 0; last_slash = strrchr( cmpl_dir->fullname, '/' ); g_assert( last_slash ); if ( last_slash != cmpl_dir->fullname ) { /* last_slash[0] = 0; */ } else { c = last_slash[1]; last_slash[1] = 0; } if ( stat( cmpl_dir->fullname, &parbuf ) < 0 ) { cmpl_errno = errno; return FALSE; } if ( parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev ) { /* it wasn't a link */ return TRUE; } if ( c ) { last_slash[1] = c; } /* else last_slash[0] = '/'; */ /* it was a link, have to figure it out the hard way */ new_name = find_parent_dir_fullname( cmpl_dir->fullname ); if ( !new_name ) { return FALSE; } g_free( cmpl_dir->fullname ); cmpl_dir->fullname = new_name; return TRUE; } static gchar* find_parent_dir_fullname( gchar* dirname ){ gchar buffer[MAXPATHLEN]; gchar buffer2[MAXPATHLEN]; #if defined( sun ) && !defined( __SVR4 ) if ( !getwd( buffer ) ) #else if ( !getcwd( buffer, MAXPATHLEN ) ) #endif { cmpl_errno = errno; return NULL; } if ( chdir( dirname ) != 0 || chdir( ".." ) != 0 ) { cmpl_errno = errno; return NULL; } #if defined( sun ) && !defined( __SVR4 ) if ( !getwd( buffer2 ) ) #else if ( !getcwd( buffer2, MAXPATHLEN ) ) #endif { chdir( buffer ); cmpl_errno = errno; return NULL; } if ( chdir( buffer ) != 0 ) { cmpl_errno = errno; return NULL; } return g_strdup( buffer2 ); } /**********************************************************************/ /* Completion Operations */ /**********************************************************************/ static PossibleCompletion* attempt_homedir_completion( gchar* text_to_complete, CompletionState *cmpl_state ){ gint index, length; if ( !cmpl_state->user_dir_name_buffer && !get_pwdb( cmpl_state ) ) { return NULL; } length = strlen( text_to_complete ) - 1; cmpl_state->user_completion_index += 1; while ( cmpl_state->user_completion_index < cmpl_state->user_directories_len ) { index = first_diff_index( text_to_complete + 1, cmpl_state->user_directories [cmpl_state->user_completion_index].login ); switch ( index ) { case PATTERN_MATCH: break; default: if ( cmpl_state->last_valid_char < ( index + 1 ) ) { cmpl_state->last_valid_char = index + 1; } cmpl_state->user_completion_index += 1; continue; } cmpl_state->the_completion.is_a_completion = 1; cmpl_state->the_completion.is_directory = 1; append_completion_text( "~", cmpl_state ); append_completion_text( cmpl_state-> user_directories[cmpl_state->user_completion_index].login, cmpl_state ); return append_completion_text( "/", cmpl_state ); } if ( text_to_complete[1] || cmpl_state->user_completion_index > cmpl_state->user_directories_len ) { cmpl_state->user_completion_index = -1; return NULL; } else { cmpl_state->user_completion_index += 1; cmpl_state->the_completion.is_a_completion = 1; cmpl_state->the_completion.is_directory = 1; return append_completion_text( "~/", cmpl_state ); } } /* returns the index (>= 0) of the first differing character, * PATTERN_MATCH if the completion matches */ static gint first_diff_index( gchar* pat, gchar* text ){ gint diff = 0; while ( *pat && *text && *text == *pat ) { pat += 1; text += 1; diff += 1; } if ( *pat ) { return diff; } return PATTERN_MATCH; } static PossibleCompletion* append_completion_text( gchar* text, CompletionState* cmpl_state ){ gint len, i = 1; if ( !cmpl_state->the_completion.text ) { return NULL; } len = strlen( text ) + strlen( cmpl_state->the_completion.text ) + 1; if ( cmpl_state->the_completion.text_alloc > len ) { strcat( cmpl_state->the_completion.text, text ); return &cmpl_state->the_completion; } while ( i < len ) { i <<= 1; } cmpl_state->the_completion.text_alloc = i; cmpl_state->the_completion.text = (gchar*)g_realloc( cmpl_state->the_completion.text, i ); if ( !cmpl_state->the_completion.text ) { return NULL; } else { strcat( cmpl_state->the_completion.text, text ); return &cmpl_state->the_completion; } } static CompletionDir* find_completion_dir( gchar* text_to_complete, gchar** remaining_text, CompletionState* cmpl_state ){ gchar* first_slash = strchr( text_to_complete, '/' ); CompletionDir* dir = cmpl_state->reference_dir; CompletionDir* next; *remaining_text = text_to_complete; while ( first_slash ) { gint len = first_slash - *remaining_text; gint found = 0; gchar *found_name = NULL; /* Quiet gcc */ gint i; gchar* pat_buf = g_new( gchar, len + 1 ); strncpy( pat_buf, *remaining_text, len ); pat_buf[len] = 0; for ( i = 0; i < dir->sent->entry_count; i += 1 ) { if ( dir->sent->entries[i].is_dir && fnmatch( pat_buf, dir->sent->entries[i].entry_name, FNMATCH_FLAGS ) != FNM_NOMATCH ) { if ( found ) { g_free( pat_buf ); return dir; } else { found = 1; found_name = dir->sent->entries[i].entry_name; } } } if ( !found ) { /* Perhaps we are trying to open an automount directory */ found_name = pat_buf; } next = open_relative_dir( found_name, dir, cmpl_state ); if ( !next ) { g_free( pat_buf ); return NULL; } next->cmpl_parent = dir; dir = next; if ( !correct_dir_fullname( dir ) ) { g_free( pat_buf ); return NULL; } *remaining_text = first_slash + 1; first_slash = strchr( *remaining_text, '/' ); g_free( pat_buf ); } return dir; } static void update_cmpl( PossibleCompletion* poss, CompletionState* cmpl_state ){ gint cmpl_len; if ( !poss || !cmpl_is_a_completion( poss ) ) { return; } cmpl_len = strlen( cmpl_this_completion( poss ) ); if ( cmpl_state->updated_text_alloc < cmpl_len + 1 ) { cmpl_state->updated_text = (gchar*)g_realloc( cmpl_state->updated_text, cmpl_state->updated_text_alloc ); cmpl_state->updated_text_alloc = 2 * cmpl_len; } if ( cmpl_state->updated_text_len < 0 ) { strcpy( cmpl_state->updated_text, cmpl_this_completion( poss ) ); cmpl_state->updated_text_len = cmpl_len; cmpl_state->re_complete = cmpl_is_directory( poss ); } else if ( cmpl_state->updated_text_len == 0 ) { cmpl_state->re_complete = FALSE; } else { gint first_diff = first_diff_index( cmpl_state->updated_text, cmpl_this_completion( poss ) ); cmpl_state->re_complete = FALSE; if ( first_diff == PATTERN_MATCH ) { return; } if ( first_diff > cmpl_state->updated_text_len ) { strcpy( cmpl_state->updated_text, cmpl_this_completion( poss ) ); } cmpl_state->updated_text_len = first_diff; cmpl_state->updated_text[first_diff] = 0; } } static PossibleCompletion* attempt_file_completion( CompletionState *cmpl_state ){ gchar *pat_buf, *first_slash; CompletionDir *dir = cmpl_state->active_completion_dir; dir->cmpl_index += 1; if ( dir->cmpl_index == dir->sent->entry_count ) { if ( dir->cmpl_parent == NULL ) { cmpl_state->active_completion_dir = NULL; return NULL; } else { cmpl_state->active_completion_dir = dir->cmpl_parent; return attempt_file_completion( cmpl_state ); } } g_assert( dir->cmpl_text ); first_slash = strchr( dir->cmpl_text, '/' ); if ( first_slash ) { gint len = first_slash - dir->cmpl_text; pat_buf = g_new( gchar, len + 1 ); strncpy( pat_buf, dir->cmpl_text, len ); pat_buf[len] = 0; } else { gint len = strlen( dir->cmpl_text ); pat_buf = g_new( gchar, len + 2 ); strcpy( pat_buf, dir->cmpl_text ); strcpy( pat_buf + len, "*" ); } if ( first_slash ) { if ( dir->sent->entries[dir->cmpl_index].is_dir ) { if ( fnmatch( pat_buf, dir->sent->entries[dir->cmpl_index].entry_name, FNMATCH_FLAGS ) != FNM_NOMATCH ) { CompletionDir* new_dir; new_dir = open_relative_dir( dir->sent->entries[dir->cmpl_index].entry_name, dir, cmpl_state ); if ( !new_dir ) { g_free( pat_buf ); return NULL; } new_dir->cmpl_parent = dir; new_dir->cmpl_index = -1; new_dir->cmpl_text = first_slash + 1; cmpl_state->active_completion_dir = new_dir; g_free( pat_buf ); return attempt_file_completion( cmpl_state ); } else { g_free( pat_buf ); return attempt_file_completion( cmpl_state ); } } else { g_free( pat_buf ); return attempt_file_completion( cmpl_state ); } } else { if ( dir->cmpl_parent != NULL ) { append_completion_text( dir->fullname + strlen( cmpl_state->completion_dir->fullname ) + 1, cmpl_state ); append_completion_text( "/", cmpl_state ); } append_completion_text( dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state ); cmpl_state->the_completion.is_a_completion = ( fnmatch( pat_buf, dir->sent->entries[dir->cmpl_index].entry_name, FNMATCH_FLAGS ) != FNM_NOMATCH ); cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir; if ( dir->sent->entries[dir->cmpl_index].is_dir ) { append_completion_text( "/", cmpl_state ); } g_free( pat_buf ); return &cmpl_state->the_completion; } } static gint get_pwdb( CompletionState* cmpl_state ){ struct passwd *pwd_ptr; gchar* buf_ptr; gint len = 0, i, count = 0; if ( cmpl_state->user_dir_name_buffer ) { return TRUE; } setpwent(); while ( ( pwd_ptr = getpwent() ) != NULL ) { len += strlen( pwd_ptr->pw_name ); len += strlen( pwd_ptr->pw_dir ); len += 2; count += 1; } setpwent(); cmpl_state->user_dir_name_buffer = g_new( gchar, len ); cmpl_state->user_directories = g_new( CompletionUserDir, count ); cmpl_state->user_directories_len = count; buf_ptr = cmpl_state->user_dir_name_buffer; for ( i = 0; i < count; i += 1 ) { pwd_ptr = getpwent(); if ( !pwd_ptr ) { cmpl_errno = errno; goto error; } strcpy( buf_ptr, pwd_ptr->pw_name ); cmpl_state->user_directories[i].login = buf_ptr; buf_ptr += strlen( buf_ptr ); buf_ptr += 1; strcpy( buf_ptr, pwd_ptr->pw_dir ); cmpl_state->user_directories[i].homedir = buf_ptr; buf_ptr += strlen( buf_ptr ); buf_ptr += 1; } qsort( cmpl_state->user_directories, cmpl_state->user_directories_len, sizeof( CompletionUserDir ), compare_user_dir ); endpwent(); return TRUE; error: if ( cmpl_state->user_dir_name_buffer ) { g_free( cmpl_state->user_dir_name_buffer ); } if ( cmpl_state->user_directories ) { g_free( cmpl_state->user_directories ); } cmpl_state->user_dir_name_buffer = NULL; cmpl_state->user_directories = NULL; return FALSE; } static gint compare_user_dir( const void* a, const void* b ){ return strcmp( ( ( (CompletionUserDir*)a ) )->login, ( ( (CompletionUserDir*)b ) )->login ); } static gint compare_cmpl_dir( const void* a, const void* b ){ return strcmp( ( ( (CompletionDirEntry*)a ) )->entry_name, ( ( (CompletionDirEntry*)b ) )->entry_name ); } static gint cmpl_state_okay( CompletionState* cmpl_state ){ return cmpl_state && cmpl_state->reference_dir; } static gchar* cmpl_strerror( gint err ){ if ( err == CMPL_ERRNO_TOO_LONG ) { return "Name too long"; } else{ return g_strerror( err ); } } /* This is an internally used function to create pixmaps. */ GtkWidget* create_pixmap( GtkWidget *widget, const gchar *pixmap_char ){ GdkPixmap *gdkpixmap; GdkBitmap *mask; GtkWidget *pixmap; GdkColormap *colormap; colormap = gtk_widget_get_colormap( widget ); gdkpixmap = gdk_pixmap_colormap_create_from_xpm_d( GTK_WIDGET( widget )->window, colormap, &mask, NULL, (gpointer) pixmap_char ); if ( gdkpixmap == NULL ) { g_warning( "Error loading pixmap: %s", pixmap_char ); return NULL; } pixmap = gtk_pixmap_new( gdkpixmap, mask ); gdk_pixmap_unref( gdkpixmap ); gdk_bitmap_unref( mask ); return pixmap; } /* Testing area */ #ifdef TORRIE_DEBUG /* Get the selected filename and print it to the console */ void file_ok_sel( GtkWidget *w, GtkFileSelection *fs ){ g_print( "%s\n", gtk_file_selection_get_filename( GTK_FILE_SELECTION( fs ) ) ); } void destroy( GtkWidget *widget, gpointer data ){ gtk_main_quit(); } int main( int argc, char *argv[] ){ GtkWidget *filew; const gchar *masks[] = { "mp3s/playlists <*.mp3,*.m3u>", "src/hdr <*.[CcHh],*.[Cc][Cc],*.[Hh][Hh],*.cpp>", NULL }; gtk_init( &argc, &argv ); /* Create a new file selection widget */ filew = gtk_file_selection_new( "Spiffy File Selector" ); // gtk_file_selection_complete(GTK_FILE_SELECTION(filew),"bob"); gtk_file_selection_set_masks( GTK_FILE_SELECTION( filew ), masks ); gtk_signal_connect( GTK_OBJECT( filew ), "destroy", (GtkSignalFunc) destroy, &filew ); /* Connect the ok_button to file_ok_sel function */ gtk_signal_connect( GTK_OBJECT( GTK_FILE_SELECTION( filew )->ok_button ), "clicked", (GtkSignalFunc) file_ok_sel, filew ); /* Connect the cancel_button to destroy the widget */ gtk_signal_connect_object( GTK_OBJECT( GTK_FILE_SELECTION ( filew )->cancel_button ), "clicked", (GtkSignalFunc) gtk_widget_destroy, GTK_OBJECT( filew ) ); gtk_widget_show( filew ); /* g_print("%d",gtk_file_selection_match_mask("mask.c","m*.c")); g_print("%d",gtk_file_selection_match_mask("mask.c","m???.c")); g_print("%d",gtk_file_selection_match_mask("mask.c","m??*.c")); g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c")); g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c???")); g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c*")); g_print("%d",gtk_file_selection_match_mask("mask.cout","n*.c???")); g_print("%d",gtk_file_selection_match_mask("mask.c","[mn]*")); g_print("%d",gtk_file_selection_match_mask("COPYING","*.xpm")); */ gtk_main(); return 0; } /* example-end */ #endif