#include #include #include #include #include #include #include #include #if defined(_WIN32) # include #else # include # include # include # if defined(__SOLARIS__) # include # include # elif defined(__FreeBSD__) /* # include */ # else # include # endif #endif #include #include #include "../include/string.h" #include "../include/disk.h" #include "guiutils.h" #include "pulist.h" #include "fprompt.h" #include "cdialog.h" #include "fb.h" #include "config.h" #include "images/icon_file_20x20.xpm" #include "images/icon_file_hidden_20x20.xpm" #include "images/icon_folder_closed_20x20.xpm" #include "images/icon_folder_opened_20x20.xpm" #include "images/icon_folder_parent_20x20.xpm" #include "images/icon_folder_noaccess_20x20.xpm" #include "images/icon_folder_home_20x20.xpm" #include "images/icon_folder_hidden_20x20.xpm" #include "images/icon_link_20x20.xpm" #include "images/icon_link_broken_20x20.xpm" #include "images/icon_link_hidden_20x20.xpm" #include "images/icon_pipe_20x20.xpm" #include "images/icon_socket_20x20.xpm" #include "images/icon_device_block_20x20.xpm" #include "images/icon_device_character_20x20.xpm" #include "images/icon_executable_20x20.xpm" #include "images/icon_drive_fixed_20x20.xpm" #include "images/icon_drive_root2_20x20.xpm" #include "images/icon_fb_list_standard_20x20.xpm" #include "images/icon_fb_list_vertical_20x20.xpm" #include "images/icon_fb_list_vertical_details_20x20.xpm" #include "images/icon_rename_20x20.xpm" #include "images/icon_chmod_20x20.xpm" #include "images/icon_reload_20x20.xpm" #include "images/icon_select_20x20.xpm" #include "images/icon_ok_20x20.xpm" #include "images/icon_cancel_20x20.xpm" #include "images/icon_trash_32x32.xpm" typedef struct _FileBrowserIcon FileBrowserIcon; #define FILE_BROWSER_ICON(p) ((FileBrowserIcon *)(p)) typedef struct _FileBrowserObject FileBrowserObject; #define FILE_BROWSER_OBJECT(p) ((FileBrowserObject *)(p)) typedef struct _FileBrowserColumn FileBrowserColumn; #define FILE_BROWSER_COLUMN(p) ((FileBrowserColumn *)(p)) typedef struct _FileBrowser FileBrowser; #define FILE_BROWSER(p) ((FileBrowser *)(p)) #define FILE_BROWSER_KEY "/FileBrowser" /* * Flags: */ typedef enum { FB_MAPPED = (1 << 0), FB_REALIZED = (1 << 1), FB_VSB_MAPPED = (1 << 2), FB_HSB_MAPPED = (1 << 3), FB_SHOW_HIDDEN_OBJECTS = (1 << 8), FB_RESPONSE_OK = (1 << 15) /* User clicked on OK, * otherwise, user * clicked on cancel */ } FileBrowserFlags; /* * List Formats: */ typedef enum { FB_LIST_FORMAT_STANDARD, /* Horizontal */ FB_LIST_FORMAT_VERTICAL, /* Vertical */ FB_LIST_FORMAT_VERTICAL_DETAILS /* Vertical with details */ } FileBrowserListFormat; /* * Icon Index Values: * * The values represent the index of each icon defined in * FB_ICON_DATA_LIST. */ typedef enum { FB_ICON_FILE, /* 0 */ FB_ICON_FILE_HIDDEN, FB_ICON_FOLDER_CLOSED, FB_ICON_FOLDER_OPENED, FB_ICON_FOLDER_PARENT, FB_ICON_FOLDER_NOACCESS, FB_ICON_FOLDER_HOME, FB_ICON_FOLDER_HIDDEN, FB_ICON_LINK, FB_ICON_LINK_BROKEN, /* 9 */ FB_ICON_LINK_HIDDEN, FB_ICON_PIPE, FB_ICON_SOCKET, FB_ICON_DEVICE_BLOCK, FB_ICON_DEVICE_CHARACTER, FB_ICON_EXECUTABLE, FB_ICON_DRIVE_FIXED, FB_ICON_DRIVE_ROOT /* 17 */ } FileBrowserIconIndex; /* Utilities */ static gchar **FileBrowserListDirectory( const gchar *path, gint *nobjects_rtn ); static gboolean FileBrowserIsNameHidden(const gchar *name); static gboolean FileBrowserIsPathDirectory(const gchar *path); static gboolean FileBrowserIsLPathDirectory(const gchar *path); static const gchar *FileBrowserGetExtensionFromPath(const gchar *path); static gchar *FileBrowserJoinPaths( const gchar *parent_path, const gchar *child_path ); static GList *FileBrowserDNDBufParse( const gchar *buf, const gint len ); static gchar *FileBrowserDNDBufFormat( GList *objects_list, GList *selection, gint *len_rtn ); static gboolean FileBrowserMatchName( const gchar *name, const gchar *extensions ); static gint FileBrowserDirectoryCreate(const gchar *path); static gint FileBrowserLinkCreate( const gchar *path, const gchar *target ); static gint FileBrowserUnlink(const gchar *path); /* Get Drive Paths */ static GList *FileBrowserGetDrivePaths(void); /* Busy/Ready Setting */ static void FileBrowserSetBusy( FileBrowser *fb, const gboolean busy ); /* Show Hidden Objects Setting */ static void FileBrowserSetShowHiddenObjects( FileBrowser *fb, const gboolean show ); /* List Format Setting */ static void FileBrowserSetListFormat( FileBrowser *fb, const FileBrowserListFormat list_format ); /* Location */ static void FileBrowserSetLocation( FileBrowser *fb, const gchar *path ); static void FileBrowserGoToParent(FileBrowser *fb); static const gchar *FileBrowserGetLocation(FileBrowser *fb); /* Locations Popup List */ static void FileBrowserLocationsPUListUpdate(FileBrowser *fb); /* Icons */ static FileBrowserIcon *FileBrowserIconNew(void); static void FileBrowserIconDelete(FileBrowserIcon *icon); static FileBrowserIcon *FileBrowserGetIcon(FileBrowser *fb, const gint i); static FileBrowserIcon *FileBrowserIconAppend( FileBrowser *fb, guint8 **data, const gchar *desc ); static FileBrowserIconIndex FileBrowserMatchIconNumFromPath( FileBrowser *fb, const gchar *path, struct stat *lstat_buf ); /* Objects */ static FileBrowserObject *FileBrowserObjectNew(void); static void FileBrowserObjectDelete(FileBrowserObject *o); static FileBrowserObject *FileBrowserGetObject(FileBrowser *fb, const gint i); static void FileBrowserObjectUpdateValues( FileBrowser *fb, FileBrowserObject *o ); static FileBrowserObject *FileBrowserObjectAppend( FileBrowser *fb, const gchar *name, const gchar *path, struct stat *lstat_buf ); /* List */ static void FileBrowserListUpdate( FileBrowser *fb, const gchar *filter ); static void FileBrowserListUpdatePositions(FileBrowser *fb); static void FileBrowserListSetDNDDragIcon(FileBrowser *fb); static GtkVisibility FileBrowserListObjectVisibility( FileBrowser *fb, const gint i ); static void FileBrowserListMoveToObject( FileBrowser *fb, const gint i, const gfloat coeff ); static gint FileBrowserListSelectCoordinates( FileBrowser *fb, const gint x, const gint y ); static void FileBrowserListHeaderDraw(FileBrowser *fb); static void FileBrowserListHeaderQueueDraw(FileBrowser *fb); static void FileBrowserListDraw(FileBrowser *fb); static void FileBrowserListQueueDraw(FileBrowser *fb); /* List Columns */ static FileBrowserColumn *FileBrowserColumnNew(void); static void FileBrowserColumnDelete(FileBrowserColumn *column); static FileBrowserColumn *FileBrowserListGetColumn( FileBrowser *fb, const gint i ); static FileBrowserColumn *FileBrowserListColumnAppend( FileBrowser *fb, const gchar *label, const gint width ); static void FileBrowserListColumnsClear(FileBrowser *fb); /* File Name Entry */ static void FileBrowserEntrySetSelectedObjects(FileBrowser *fb); /* Types List */ static void FileBrowserTypesListSetTypes( FileBrowser *fb, fb_type_struct **list, const gint total ); /* File Operations */ static void FileBrowserNewDirectory(FileBrowser *fb); static gint FileBrowserMoveIteration( FileBrowser *fb, const gchar *old_path, const gchar *new_path ); static gint FileBrowserCopyIteration( FileBrowser *fb, const gchar *old_path, const gchar *new_path ); static void FileBrowserMove( FileBrowser *fb, GList *paths_list, const gchar *tar_path ); static void FileBrowserCopy( FileBrowser *fb, GList *paths_list, const gchar *tar_path ); static void FileBrowserLink( FileBrowser *fb, const gchar *src_path, const gchar *tar_path ); static void FileBrowserRenameFPromptCB(gpointer data, const gchar *value); static void FileBrowserRenameQuery(FileBrowser *fb); static void FileBrowserCHModFPromptCB(gpointer data, const gchar *value); static void FileBrowserCHModQuery(FileBrowser *fb); static gint FileBrowserDeleteIteration( FileBrowser *fb, const gchar *path ); static void FileBrowserDelete(FileBrowser *fb); /* Operations */ static void FileBrowserRefresh(FileBrowser *fb); static void FileBrowserEntryActivate(FileBrowser *fb); static void FileBrowserOK(FileBrowser *fb); static void FileBrowserCancel(FileBrowser *fb); /* Callbacks */ static void FileBrowserLocationsPUListItemDestroyCB(gpointer data); static void FileBrowserDestroyCB(gpointer data); static gint FileBrowserDeleteEventCB( GtkWidget *widget, GdkEvent *event, gpointer data ); static void FileBrowserRealizeCB(GtkWidget *widget, gpointer data); static void FileBrowserListDragBeginCB( GtkWidget *widget, GdkDragContext *dc, gpointer data ); static void FileBrowserListDragDataGetCB( GtkWidget *widget, GdkDragContext *dc, GtkSelectionData *selection_data, guint info, guint t, gpointer data ); static void FileBrowserListDragDataDeleteCB( GtkWidget *widget, GdkDragContext *dc, gpointer data ); static void FileBrowserListDragEndCB( GtkWidget *widget, GdkDragContext *dc, gpointer data ); static void FileBrowserListDragLeaveCB( GtkWidget *widget, GdkDragContext *dc, guint t, gpointer data ); static gboolean FileBrowserListDragMotionCB( GtkWidget *widget, GdkDragContext *dc, gint x, gint y, guint t, gpointer data ); static void FileBrowserDragDataReceivedCB( GtkWidget *widget, GdkDragContext *dc, gint x, gint y, GtkSelectionData *selection_data, guint info, guint t, gpointer data ); static void FileBrowserShowHiddenObjectsToggleCB(GtkWidget *widget, gpointer data); static void FileBrowserListFormatToggleCB(GtkWidget *widget, gpointer data); static void FileBrowserGoToParentCB(GtkWidget *widget, gpointer data); static void FileBrowserNewDirectoryCB(GtkWidget *widget, gpointer data); static void FileBrowserRefreshCB(GtkWidget *widget, gpointer data); static void FileBrowserLocPUListChangedCB( GtkWidget *widget, const gint i, gpointer data ); static gint FileBrowserListHeaderEventCB( GtkWidget *widget, GdkEvent *event, gpointer data ); static gint FileBrowserListEventCB( GtkWidget *widget, GdkEvent *event, gpointer data ); static void FileBrowserListRealizeCB(GtkWidget *widget, gpointer data); static void FileBrowserListScrollCB(GtkAdjustment *adj, gpointer data); static void FileBrowserSelectAllCB(GtkWidget *widget, gpointer data); static void FileBrowserUnselectAllCB(GtkWidget *widget, gpointer data); static void FileBrowserInvertSelectionCB(GtkWidget *widget, gpointer data); static void FileBrowserRenameCB(GtkWidget *widget, gpointer data); static void FileBrowserCHModCB(GtkWidget *widget, gpointer data); static void FileBrowserDeleteCB(GtkWidget *widget, gpointer data); static gint FileBrowserEntryCompletePathKeyCB( GtkWidget *widget, GdkEventKey *key, gpointer data ); static void FileBrowserEntryActivateCB(GtkWidget *widget, gpointer data); static void FileBrowserTypesPUListChangedCB( GtkWidget *widget, const gint i, gpointer data ); static void FileBrowserOKCB(GtkWidget *widget, gpointer data); static void FileBrowserCancelCB(GtkWidget *widget, gpointer data); /* File Selection Dialog Front Ends */ gint FileBrowserInit(void); void FileBrowserSetStyle(GtkRcStyle *rc_style); void FileBrowserSetTransientFor(GtkWidget *w); void FileBrowserSetObjectCreatedCB( void (*cb)( const gchar *path, gpointer data ), gpointer data ); void FileBrowserSetObjectModifiedCB( void (*cb)( const gchar *old_path, const gchar *new_path, gpointer data ), gpointer data ); void FileBrowserSetObjectDeletedCB( void (*cb)( const gchar *path, gpointer data ), gpointer data ); gboolean FileBrowserIsQuery(void); void FileBrowserBreakQuery(void); GtkWidget *FileBrowserGetToplevel(void); void FileBrowserReset(void); gboolean FileBrowserGetResponse( const gchar *title, const gchar *ok_label, const gchar *cancel_label, const gchar *path, fb_type_struct **file_types_list, gint nfile_types, gchar ***paths_list_rtn, gint *npaths_rtn, fb_type_struct **file_type_rtn ); void FileBrowserMap(void); void FileBrowserUnmap(void); void FileBrowserShutdown(void); const gchar *FileBrowserGetLastLocation(void); void FileBrowserShowHiddenObjects(const gboolean show); void FileBrowserListStandard(void); void FileBrowserListDetailed(void); /* File Types List */ fb_type_struct *FileBrowserTypeNew(void); void FileBrowserTypeDelete(fb_type_struct *t); gint FileBrowserTypeListNew( fb_type_struct ***list, gint *total, const gchar *extensions, const gchar *name ); void FileBrowserDeleteTypeList( fb_type_struct **list, const gint total ); gchar *FileBrowserTypeCompleteExtension( const gchar *path, fb_type_struct *t, const gboolean always_replace ); #define FB_WIDTH 480 #define FB_HEIGHT 300 #define FB_TB_BTN_WIDTH (20 + 5) #define FB_TB_BTN_HEIGHT (20 + 5) #define FB_LOC_LIST_MAX_CHARS 25 /* Maximum characters for * each item in the * locations list */ #define FB_LIST_ITEM_MAX_CHARS 25 /* Maximum characters for * each item in the list */ #define FB_LIST_HEADER_WIDTH -1 #define FB_LIST_HEADER_HEIGHT (20 + 2) #define FB_LIST_WIDTH -1 #define FB_LIST_HEIGHT -1 #define FB_LIST_OBJECT_SPACING 1 /* Spacing between objects */ #define FB_LIST_ICON_WIDTH 20 #define FB_LIST_ICON_HEIGHT 20 #define FB_LIST_ICON_TEXT_SPACING 2 /* Spacing between icon and * text */ #define FB_DEF_TITLE "Select File" #define FB_DEF_TYPE_STR "All Files (*.*)" #define FB_NEW_DIRECTORY_NAME "new_directory" #define FB_IS_OBJECT_SELECTED(_fb_,_obj_num_) \ ((((_fb_) != NULL) && ((_obj_num_) >= 0)) ? \ ((g_list_find((_fb_)->selection, (gpointer)(_obj_num_)) != NULL) ? \ TRUE : FALSE) : FALSE) #define ATOI(s) (((s) != NULL) ? atoi(s) : 0) #define ATOL(s) (((s) != NULL) ? atol(s) : 0) #define ATOF(s) (((s) != NULL) ? atof(s) : 0.0f) #define STRDUP(s) (((s) != NULL) ? g_strdup(s) : NULL) #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #define CLIP(a,l,h) (MIN(MAX((a),(l)),(h))) #define STRLEN(s) (((s) != NULL) ? (gint)strlen(s) : 0) #define STRISEMPTY(s) (((s) != NULL) ? (*(s) == '\0') : TRUE) #ifndef ISBLANK # define ISBLANK(c) (((c) == ' ') || ((c) == '\t')) #endif /* * Icons Data List: * * The order of this list must correspond with FileBrowserIconIndex. * * Each set contains 6 gpointers, and the gpointers in the last * set are all NULL. */ #define FB_ICON_DATA_LIST { \ "file", \ icon_file_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ "file/hidden", \ icon_file_hidden_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ "directory/closed", \ icon_folder_closed_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ "directory/opened", \ icon_folder_opened_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ "directory/parent", \ icon_folder_parent_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ "directory/noaccess", \ icon_folder_noaccess_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ "directory/home", \ icon_folder_home_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ "directory/hidden", \ icon_folder_hidden_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ "link", \ icon_link_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ "link/broken", \ icon_link_broken_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ "link/hidden", \ icon_link_hidden_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ "pipe", \ icon_pipe_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ "socket", \ icon_socket_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ "device/block", \ icon_device_block_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ "device/character", \ icon_device_character_20x20_xpm,\ NULL, NULL, NULL, NULL, \ \ "executable", \ icon_executable_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ "drive/fixed", \ icon_drive_fixed_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ "drive/root", \ icon_drive_root2_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ NULL, NULL, \ NULL, NULL, NULL, NULL \ } /* * Drag Flags: */ typedef enum { FILE_BROWSER_DRAG_ACTIVE = (1 << 0), /* We initiated a drag */ FILE_BROWSER_DRAG_ACTIVE_MOVE = (1 << 1), /* Initiated drag is * currently a move */ FILE_BROWSER_DRAG_OVER = (1 << 7) /* Drag is over the * list and is accepted */ } FileBrowserDragFlags; /* * Icon: */ struct _FileBrowserIcon { GdkPixmap *pixmap; GdkBitmap *mask; gint width, height; gchar *desc; /* Used for MIME type */ }; /* * Object: */ struct _FileBrowserObject { gchar *name, *path, *displayed_name; /* Name shown on the list * (may be abbriviated) */ FileBrowserIconIndex icon_num; gint x, y, /* Position and size on list */ width, height; struct stat lstat_buf; }; /* * Column: * * Used in the FileBrowser's list when displaying things that * need columns. */ struct _FileBrowserColumn { GtkWidgetFlags flags; gint position; gchar *label; GtkJustification label_justify; /* If the member drag is TRUE then it means this column is * being dragged (resized) and the member drag_position * indicates the right or lower edge of this column */ gboolean drag; gint drag_position; gint drag_last_drawn_position; }; /* * File Selection Dialog: */ struct _FileBrowser { GtkWidget *toplevel; GtkAccelGroup *accelgrp; GdkGC *gc; gint busy_count, freeze_count, main_level; FileBrowserFlags flags; GdkBitmap *transparent_stipple_bm; GdkCursor *cur_busy, *cur_column_hresize, *cur_translate; GtkWidget *main_vbox, *location_pulistbox, *goto_parent_btn, *new_directory_btn, *rename_btn, *refresh_btn, *show_hidden_objects_tb, *show_hidden_objects_micheck, *list_format_standard_tb, *list_format_standard_micheck, *list_format_vertical_tb, *list_format_vertical_micheck, *list_format_vertical_details_tb, *list_format_vertical_details_micheck, *list_header_da, /* List Header GtkDrawingArea */ *list_da, /* List GtkDrawingArea */ *list_vsb, /* List Vertical GtkScrollBar */ *list_hsb, /* List Horizontal GtkScrollBar */ *list_menu, /* List Right Click GtkMenu */ *entry, /* File Name GtkEntry */ *types_pulistbox, *ok_btn_label, *ok_btn, *cancel_btn_label, *cancel_btn; GdkPixmap *list_pm; /* List GtkDrawingArea back * buffer */ /* Current Location */ gchar *cur_location; /* List Format */ FileBrowserListFormat list_format; /* Columns */ GList *columns_list; /* GList of FileBrowserColumn * columns */ /* Icons */ GList *icons_list; /* GList of FileBrowserIcon * icons */ gint uid, euid; gint gid, egid; gchar *home_path; /* Devices */ GList *device_paths_list; /* List of gchar * device paths */ /* Objects */ GList *objects_list; /* GList of FileBrowserObject * objects */ gint objects_per_row; /* For use with * FB_LIST_FORMAT_STANDARD */ GList *selection, *selection_end; gint focus_object, last_single_select_object; /* Current File Type Return */ fb_type_struct *current_file_type; /* Selected Paths Return */ gchar **selected_paths_list; gint nselected_paths; /* Callbacks */ void (*object_created_cb)( const gchar *path, gpointer data ); gpointer object_created_data; void (*object_modified_cb)( const gchar *old_path, const gchar *new_path, gpointer data ); gpointer object_modified_data; void (*object_deleted_cb)( const gchar *path, gpointer data ); gpointer object_deleted_data; /* Last button pressed and pointer position */ gint button; FileBrowserDragFlags drag_flags; gint drag_last_x, drag_last_y; }; static FileBrowser *file_browser = NULL; /* * Gets the complete list of objects (including special directory * notations) in a directory. * * The path specifies the directory. * * The nobjects_rtn specifies the return value for the number of * objects in the directory. * * Returns a list of dynamically allocated strings describing the * name of each object in the directory. */ static gchar **FileBrowserListDirectory( const gchar *path, gint *nobjects_rtn ) { return(GetDirEntNames2( (const char *)path, (int *)nobjects_rtn )); } /* * Checks if the name is considered hidden. */ static gboolean FileBrowserIsNameHidden(const gchar *name) { if(name == NULL) { errno = EINVAL; return(FALSE); } /* Does the name start with a '.'? */ if(name[0] != '.') return(FALSE); /* Check if it is a special directory notation which might be * confused as hidden */ if((name[1] == '\0') || (name[1] == '.')) return(FALSE); return(TRUE); } /* * Checks if the path's destination refers to a directory. */ static gboolean FileBrowserIsPathDirectory(const gchar *path) { struct stat stat_buf; if(path == NULL) { errno = EINVAL; return(FALSE); } if(stat((const char *)path, &stat_buf)) return(FALSE); #ifdef S_ISDIR if(S_ISDIR(stat_buf.st_mode)) #else if(FALSE) #endif { errno = EISDIR; return(TRUE); } else { errno = ENOTDIR; return(FALSE); } } /* * Checks if the path refers to a directory. */ static gboolean FileBrowserIsLPathDirectory(const gchar *path) { struct stat lstat_buf; if(path == NULL) { errno = EINVAL; return(FALSE); } if(lstat((const char *)path, &lstat_buf)) return(FALSE); #ifdef S_ISDIR if(S_ISDIR(lstat_buf.st_mode)) #else if(FALSE) #endif { errno = EISDIR; return(TRUE); } else { errno = ENOTDIR; return(FALSE); } } /* * Joins two paths togeather. * * The parent_path specifies the string describing the full path * to the parent. The parent may not be NULL and must be a full * path. * * The child_path specifies the string describing the child path. * If child_path is NULL then a copy of parent_path is returned. * If child_path is a full path then a copy of child_path is * returned. Otherwise a copy of the parent_path prefixed to the * child_path is returned. * * Returns a dynamically allocated string describing the * resulting path. */ static gchar *FileBrowserJoinPaths( const gchar *parent_path, const gchar *child_path ) { return(STRDUP(PrefixPaths(parent_path, child_path))); } /* * Gets the extension from the path. */ static const gchar *FileBrowserGetExtensionFromPath(const gchar *path) { const gchar *name; if(path == NULL) { errno = EINVAL; return(NULL); } name = g_basename(path); if(name == NULL) name = path; return((const gchar *)strchr( (const char *)name, '.' )); } /* * Parses the DND buffer into a list of paths. * * Returns a GList of gchar * paths or NULL on error. The calling * function must delete the list and each path. */ static GList *FileBrowserDNDBufParse( const gchar *buf, const gint len ) { const gchar *buf_ptr = buf, *buf_end = buf_ptr + len; GList *paths_list; if((buf_ptr == NULL) || (len <= 0)) { errno = EINVAL; return(NULL); } /* Iterate through the buffer */ paths_list = NULL; while(buf_ptr < buf_end) { const gchar *s = buf_ptr; gint s_len = STRLEN(s); /* Is the protocol prefix of the string one that we want? */ if(g_strcasepfx(s, "file://")) { /* Seek past protocol "file://" */ s += STRLEN("file://"); /* Seek past "username:password@host:port" * too many programs fail to do this! */ s = (s != NULL) ? (gchar *)strchr((char *)s, '/') : NULL; /* Able to seek to full path portion of the string? */ if(s != NULL) { /* Add this path to the paths list */ paths_list = g_list_append( paths_list, STRDUP(s) ); } } /* Seek to the next segment, past any '\0' * deliminator(s) */ buf_ptr += s_len; while((buf_ptr < buf_end) ? (*buf_ptr == '\0') : FALSE) buf_ptr++; } return(paths_list); } /* * Generates a DND buffer based on the list of objects that are * selected. * * The objects_list specifies the GList of FileBrowserObject * * objects. * * The selection specifies the GList of gint selections. * * The len_rtn specifies the return value for the length of the * buffer. * * Returns a null deliminator string describing the objects in * URL format or NULL on error. */ static gchar *FileBrowserDNDBufFormat( GList *objects_list, GList *selection, gint *len_rtn ) { gchar *buf = NULL; gint buf_len = 0; GList *glist; FileBrowserObject *o; /* Iterate through the selection list */ for(glist = selection; glist != NULL; glist = g_list_next(glist) ) { o = FILE_BROWSER_OBJECT(g_list_nth_data( objects_list, (guint)glist->data )); if(o == NULL) continue; if(o->path != NULL) { gchar *url = g_strconcat( "file://", o->path, NULL ); const gint url_len = STRLEN(url), last_buf_len = buf_len; /* Increase the buffer allocation for this url string * which we will append to the buffer (include the * '\0' character counted in buf_len) */ buf_len += url_len + 1; buf = (gchar *)g_realloc(buf, buf_len * sizeof(gchar)); if(buf == NULL) { buf_len = 0; break; } memcpy( buf + last_buf_len, (url != NULL) ? url : "", url_len + 1 /* Include the '\0' character */ ); g_free(url); } } if(len_rtn != NULL) *len_rtn = buf_len; return(buf); } /* * Checks if the name matches a filter. * * The name specifies the name to test. * * The extensions specifies a string describing a space-separated * list of extensions, each extension will be used as a filter to * test the name against. */ static gboolean FileBrowserMatchName( const gchar *name, const gchar *extensions ) { gboolean use_fnmatch; gchar *st, *st_end, cur_ext[80]; gint cur_ext_len, name_len; if((name == NULL) || (extensions == NULL)) { errno = EINVAL; return(FALSE); } name_len = strlen(name); while(ISBLANK(*extensions) || (*extensions == ',')) extensions++; /* Iterate through each extension in ext */ while(*extensions != '\0') { use_fnmatch = FALSE; /* Copy the current extension from the extensions list and * then to the next position */ cur_ext_len = 0; st = cur_ext; st_end = cur_ext + 79; /* Set end 1 character premature */ while((st < st_end) && !ISBLANK(*extensions) && (*extensions != ',') && (*extensions != '\0') ) { /* If there are any wildcards in this extension then we * need to use fnmatch() for this extension */ if((*extensions == '*') || (*extensions == '?')) use_fnmatch = TRUE; *st++ = *extensions++; cur_ext_len++; } *st = '\0'; /* Seek to the next extension in the list */ while(ISBLANK(*extensions) || (*extensions == ',')) extensions++; /* Check if there is a match */ #if defined(_WIN32) if(name_len >= cur_ext_len) { if(!g_strcasecmp( name + name_len - cur_ext_len, cur_ext )) return(TRUE); } #else if(use_fnmatch) { /* Use regex to match */ if(!fnmatch( (const char *)cur_ext, (const char *)name, 0 )) return(TRUE); } else { /* Check if cur_ext is a postfix of name */ if(name_len >= cur_ext_len) { if(!g_strcasecmp( name + name_len - cur_ext_len, cur_ext )) return(TRUE); } } #endif } return(FALSE); } /* * Creates a new directory. */ static gint FileBrowserDirectoryCreate(const gchar *path) { #if !defined(_WIN32) /* Get the current default permissions */ const guint m = (guint)umask(0); #endif #if !defined(_WIN32) /* Restore the default permissions */ (void)umask((mode_t)m); #endif if(STRISEMPTY(path)) { errno = EINVAL; return(-2); } #if defined(_WIN32) return((gint)mkdir((const char *)path)); #else return((gint)mkdir( (const char *)path, (mode_t)(~m) & (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH) )); #endif } /* * Creates a new link. */ static gint FileBrowserLinkCreate( const gchar *path, const gchar *target ) { if(STRISEMPTY(path)) { errno = EINVAL; return(-2); } if(target == NULL) target = ""; return((gint)symlink( (const char *)target, /* Old path (target) */ (const char *)path /* New path (link) */ )); } /* * Unlinks. */ static gint FileBrowserUnlink(const gchar *path) { if(STRISEMPTY(path)) { errno = EINVAL; return(-2); } return((gint)unlink((const char *)path)); } /* * Returns a list of strings describing the drive paths. */ static GList *FileBrowserGetDrivePaths(void) { #if defined(_WIN32) /* struct stat stat_buf; */ gchar drive_letter = 'a', drive_name[10]; GList *paths_list = NULL; for(drive_letter = 'a'; drive_letter <= 'z'; drive_letter++) { g_snprintf( drive_name, sizeof(drive_name), "%c:\\", toupper(drive_letter) ); /* TODO Get a list of all drives */ /* if(stat((const char *)drive_name, &stat_buf)) continue; */ paths_list = g_list_append( paths_list, g_strdup(drive_name) ); } return(paths_list); #elif defined(__FreeBSD__) return(NULL); #else /* UNIX */ GList *paths_list = NULL; #ifdef __SOLARIS__ struct vfstab *vfs_ptr = NULL; int mtback; #else struct mntent *mt_ptr; #endif /* Open system devices list file */ #ifdef __SOLARIS__ FILE *fp = fopen("/etc/vfstab", "rb"); #else FILE *fp = setmntent("/etc/fstab", "rb"); #endif if(fp == NULL) return(NULL); /* Begin reading system devices list file */ #ifdef __SOLARIS__ vfs_ptr = (struct vfstab *)g_malloc(sizeof(struct vfstab)); mtback = getvfsent(fp, vfs_ptr); while(mtback != 0) #else mt_ptr = getmntent(fp); while(mt_ptr != NULL) #endif { /* Get mount path as the drive path */ #ifdef __SOLARIS__ paths_list = g_list_append( paths_list, STRDUP(vfs_ptr->vfs_mountp) ); #else paths_list = g_list_append( paths_list, STRDUP(mt_ptr->mnt_dir) ); #endif /* Read next mount entry */ #ifdef __SOLARIS__ mtback = getmntent(fp, vfs_ptr); #else mt_ptr = getmntent(fp); #endif } /* Close the system devices list file */ #ifdef __SOLARIS__ (void)fclose(fp); /* fp = NULL; */ /* vfs_ptr = NULL; */ #else endmntent(fp); /* fp = NULL; */ #endif return(paths_list); #endif } /* * Sets the File Selection Dialog busy or ready. */ static void FileBrowserSetBusy( FileBrowser *fb, const gboolean busy ) { GdkCursor *cursor; GtkWidget *w; if(fb == NULL) return; w = fb->toplevel; if(w != NULL) { if(busy) { /* Increase busy count */ fb->busy_count++; /* If already busy then don't change anything */ if(fb->busy_count > 1) return; cursor = fb->cur_busy; } else { /* Reduce busy count */ fb->busy_count--; if(fb->busy_count < 0) fb->busy_count = 0; /* If still busy do not change anything */ if(fb->busy_count > 0) return; cursor = NULL; /* Use the default cursor */ } /* Update toplevel window's cursor */ if(w->window != NULL) { gdk_window_set_cursor(w->window, cursor); gdk_flush(); } } } /* * Sets the showing of hidden objects and updates the list as * needed. */ static void FileBrowserSetShowHiddenObjects( FileBrowser *fb, const gboolean show ) { if(fb == NULL) return; if(show && !(fb->flags & FB_SHOW_HIDDEN_OBJECTS)) { fb->flags |= FB_SHOW_HIDDEN_OBJECTS; FileBrowserShowHiddenObjectsToggleCB(NULL, fb); } else if(!show && (fb->flags & FB_SHOW_HIDDEN_OBJECTS)) { fb->flags &= ~FB_SHOW_HIDDEN_OBJECTS; FileBrowserShowHiddenObjectsToggleCB(NULL, fb); } } /* * Sets the list format and updates the list as needed. */ static void FileBrowserSetListFormat( FileBrowser *fb, const FileBrowserListFormat list_format ) { GtkWidget *w, *toplevel; FileBrowserColumn *column; if(fb == NULL) return; /* Skip if there is no change */ if(fb->list_format == list_format) return; fb->freeze_count++; toplevel = fb->toplevel; /* Set new list format */ fb->list_format = list_format; /* Update list format toggle buttons, make sure we freeze so the * toggle callback does not recurse */ switch(list_format) { case FB_LIST_FORMAT_VERTICAL: GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->list_format_standard_tb, FALSE ); GUIMenuItemSetCheck( fb->list_format_standard_micheck, FALSE, FALSE ); GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->list_format_vertical_tb, TRUE ); GUIMenuItemSetCheck( fb->list_format_vertical_micheck, TRUE, FALSE ); GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->list_format_vertical_details_tb, FALSE ); GUIMenuItemSetCheck( fb->list_format_vertical_details_micheck, FALSE, FALSE ); break; case FB_LIST_FORMAT_VERTICAL_DETAILS: GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->list_format_standard_tb, FALSE ); GUIMenuItemSetCheck( fb->list_format_standard_micheck, FALSE, FALSE ); GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->list_format_vertical_tb, FALSE ); GUIMenuItemSetCheck( fb->list_format_vertical_micheck, FALSE, FALSE ); GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->list_format_vertical_details_tb, TRUE ); GUIMenuItemSetCheck( fb->list_format_vertical_details_micheck, TRUE, FALSE ); break; case FB_LIST_FORMAT_STANDARD: GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->list_format_standard_tb, TRUE ); GUIMenuItemSetCheck( fb->list_format_standard_micheck, TRUE, FALSE ); GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->list_format_vertical_tb, FALSE ); GUIMenuItemSetCheck( fb->list_format_vertical_micheck, FALSE, FALSE ); GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->list_format_vertical_details_tb, FALSE ); GUIMenuItemSetCheck( fb->list_format_vertical_details_micheck, FALSE, FALSE ); break; } /* Begin updating things to recognize the new list format */ /* Update the list columns */ FileBrowserListColumnsClear(fb); switch(list_format) { case FB_LIST_FORMAT_VERTICAL_DETAILS: /* Set up list columns */ /* Name */ column = FileBrowserListColumnAppend( fb, "Name", 145 ); /* Size */ column = FileBrowserListColumnAppend( fb, "Size", 70 ); column->label_justify = GTK_JUSTIFY_RIGHT; /* Permissions */ column = FileBrowserListColumnAppend( fb, "Permissions", 78 ); /* Last Modified */ column = FileBrowserListColumnAppend( fb, "Last Modified", 160 ); /* Show the List Header GtkDrawingArea */ w = fb->list_header_da; if(w != NULL) gtk_widget_show(w); break; case FB_LIST_FORMAT_VERTICAL: case FB_LIST_FORMAT_STANDARD: /* Hide the List Header GtkDrawingArea */ w = fb->list_header_da; if(w != NULL) gtk_widget_hide(w); break; } gtk_widget_queue_resize(toplevel); /* Update the list */ FileBrowserListUpdate(fb, NULL); /* Update the File Name GtkEntry's value */ FileBrowserEntrySetSelectedObjects(fb); fb->freeze_count--; } /* * Sets the new location. * * Updates the locations popup list. * * The path specifies the full path to the new location. If * path does not lead to a directory then its parent will * be used instead. If the specified location and the current * location are the same then nothing will be done. */ static void FileBrowserSetLocation( FileBrowser *fb, const gchar *path ) { gchar *new_location, *new_name; GtkWidget *w; if((fb == NULL) || (path == NULL)) return; if(path == fb->cur_location) return; /* Make a copy of the specified path which will be modified * and checked */ new_location = STRDUP(path); new_name = NULL; /* Special notation checks */ /* Current location? */ if(!strcmp((const char *)new_location, ".")) { g_free(new_location); #if defined(_WIN32) new_location = (fb->cur_location != NULL) ? STRDUP(fb->cur_location) : STRDUP("C:"); #else new_location = (fb->cur_location != NULL) ? STRDUP(fb->cur_location) : STRDUP("/"); #endif } /* Parent? */ if(!strcmp((const char *)new_location, "..")) { g_free(new_location); #if defined(_WIN32) new_location = (fb->cur_location != NULL) ? g_dirname(fb->cur_location) : STRDUP("C:"); #else new_location = (fb->cur_location != NULL) ? g_dirname(fb->cur_location) : STRDUP("/"); #endif } /* Home directory prefix? */ if(*new_location == '~') { /* Prefix the home directory to the new location path */ const gchar *home = fb->home_path, *child = new_location + 1; gchar *s = g_strconcat( (home != NULL) ? home : "", (*child != G_DIR_SEPARATOR) ? G_DIR_SEPARATOR_S : "", child, NULL ); g_free(new_location); new_location = s; } /* At this point we need to check if the new location is an * absolute path, if it is not then the current working dir * must be prefixed to it */ if(!g_path_is_absolute(new_location)) { const gchar *cwd = g_get_current_dir(); gchar *s = g_strconcat( (cwd != NULL) ? cwd : "", G_DIR_SEPARATOR_S, new_location, NULL ); g_free(new_location); new_location = s; } /* Make sure that the new location is a directory, if it is * not then use its parent */ if(!FileBrowserIsPathDirectory(new_location)) { gchar *parent = g_dirname(new_location); const gchar *name = (const gchar *)strrchr( (const char *)new_location, G_DIR_SEPARATOR ); /* Record that we have an object name */ g_free(new_name); new_name = (name != NULL) ? STRDUP(name + 1) : NULL; /* Record parent directory */ g_free(new_location); new_location = parent; } /* Strip the tailing deliminator */ if(new_location != NULL) { #if defined(_WIN32) gchar *s = (gchar *)strrchr((char *)new_location, G_DIR_SEPARATOR); const gint prefix_len = STRLEN("x:\\"); #else gchar *s = (gchar *)strrchr((char *)new_location, G_DIR_SEPARATOR); const gint prefix_len = STRLEN("/"); #endif while(s >= (new_location + prefix_len)) { if((s[0] == G_DIR_SEPARATOR) && (s[1] == '\0') ) *s = '\0'; else break; s--; } } /* Memory allocation error? */ if(new_location == NULL) { g_free(new_name); return; } /* No change in the location? */ if(fb->cur_location != NULL) { if(!strcmp((const char *)fb->cur_location, (const char *)new_location)) { /* No change in location, but check if we still need * to update the lists if the lists were reset/cleared */ if(fb->objects_list != NULL) { /* Lists were not cleared, so no need to update */ g_free(new_location); g_free(new_name); return; } } } /* Accept and set the new location */ g_free(fb->cur_location); fb->cur_location = new_location; new_location = NULL; /* Update the file name entry */ w = fb->entry; if(w != NULL) { gtk_entry_set_text( GTK_ENTRY(w), (!STRISEMPTY(new_name)) ? new_name : "" ); gtk_entry_set_position(GTK_ENTRY(w), -1); } /* Get the listing of the objects at the new location */ FileBrowserListUpdate(fb, NULL); /* Update the locations popup list */ FileBrowserLocationsPUListUpdate(fb); g_free(new_location); g_free(new_name); } /* * Changes the location to the parent directory of the current * location. */ static void FileBrowserGoToParent(FileBrowser *fb) { const gchar *cur_location; gchar *parent; if(fb == NULL) return; cur_location = FileBrowserGetLocation(fb); if(cur_location == NULL) return; parent = g_dirname(cur_location); if(parent == NULL) return; FileBrowserSetLocation(fb, parent); g_free(parent); } /* * Gets the current location. * * Returns a statically allocated string describing the current * location. */ static const gchar *FileBrowserGetLocation(FileBrowser *fb) { #if defined(_WIN32) return((fb != NULL) ? fb->cur_location : "C:\\"); #else return((fb != NULL) ? fb->cur_location : "/"); #endif } /* * List GtkDrawingArea DND source "drag_begin" signal callback. */ static void FileBrowserListDragBeginCB( GtkWidget *widget, GdkDragContext *dc, gpointer data ) { FileBrowser *fb = FILE_BROWSER(data); if((widget == NULL) || (dc == NULL) || (fb == NULL)) return; /* Mark that we have initiated the drag */ fb->drag_flags |= FILE_BROWSER_DRAG_ACTIVE; FileBrowserListQueueDraw(fb); } /* * List GtkDrawingArea DND source "drag_data_get" signal * callback. */ static void FileBrowserListDragDataGetCB( GtkWidget *widget, GdkDragContext *dc, GtkSelectionData *selection_data, guint info, guint t, gpointer data ) { gboolean data_sent = FALSE; FileBrowser *fb = FILE_BROWSER(data); if((widget == NULL) || (fb == NULL)) return; /* Make sure the DND info is one that we support */ if((info == 0) || (info == 1) || (info == 2)) { /* Handle by destination widget */ /* List */ if(widget == fb->list_da) { gint buf_len; gchar *buf = FileBrowserDNDBufFormat( fb->objects_list, fb->selection, &buf_len ); if(buf != NULL) { /* Send out DND data */ gtk_selection_data_set( selection_data, GDK_SELECTION_TYPE_STRING, 8, /* 8 bits per character */ buf, buf_len ); data_sent = TRUE; g_free(buf); } } } /* Failed to send out DND data? */ if(!data_sent) { /* Send a response indicating error */ const gchar *s = "Error"; gtk_selection_data_set( selection_data, GDK_SELECTION_TYPE_STRING, 8, /* 8 bits per character */ s, STRLEN(s) + 1 /* Include the '\0' character */ ); data_sent = TRUE; } } /* * List GtkDrawingArea DND source "drag_data_delete" signal * callback. */ static void FileBrowserListDragDataDeleteCB( GtkWidget *widget, GdkDragContext *dc, gpointer data ) { FileBrowser *fb = FILE_BROWSER(data); if((widget == NULL) || (fb == NULL)) return; /* Typically this means that some objects have been deleted * so we need to refresh the list. */ FileBrowserRefresh(fb); } /* * List GtkDrawingArea DND source "drag_end" signal callback. */ static void FileBrowserListDragEndCB( GtkWidget *widget, GdkDragContext *dc, gpointer data ) { FileBrowser *fb = FILE_BROWSER(data); if((widget == NULL) || (dc == NULL) || (fb == NULL)) return; /* Clear the drag flags */ fb->drag_flags &= ~FILE_BROWSER_DRAG_ACTIVE; fb->drag_flags &= ~FILE_BROWSER_DRAG_ACTIVE_MOVE; FileBrowserListQueueDraw(fb); } /* * List GtkDrawingArea DND target "drag_leave" signal callback. */ static void FileBrowserListDragLeaveCB( GtkWidget *widget, GdkDragContext *dc, guint t, gpointer data ) { FileBrowser *fb = FILE_BROWSER(data); if((widget == NULL) || (dc == NULL) || (fb == NULL)) return; if(!GTK_WIDGET_SENSITIVE(widget)) return; /* Mark that the drag has left */ if(fb->drag_flags & FILE_BROWSER_DRAG_OVER) { fb->drag_flags &= ~FILE_BROWSER_DRAG_OVER; FileBrowserListQueueDraw(fb); } if(fb->focus_object > -1) { fb->focus_object = -1; FileBrowserListQueueDraw(fb); } } /* * List GtkDrawingArea DND target "drag_motion" signal callback. */ static gboolean FileBrowserListDragMotionCB( GtkWidget *widget, GdkDragContext *dc, gint x, gint y, guint t, gpointer data ) { gboolean same; gint i; gchar *target_name; GList *glist; GdkDragAction allowed_actions, default_action_same, default_action, action; GtkTargetFlags target_flags; GtkWidget *src_w; FileBrowser *fb = FILE_BROWSER(data); if((widget == NULL) || (dc == NULL) || (fb == NULL)) return(FALSE); if(!GTK_WIDGET_SENSITIVE(widget)) return(FALSE); /* Get the GtkWidget that the drag originated from */ src_w = gtk_drag_get_source_widget(dc); /* Are they the same GtkWidgets? */ same = (src_w == widget) ? TRUE : FALSE; /* Set the target flags to denote if this is the same * GtkWidget or not */ target_flags = ((same) ? GTK_TARGET_SAME_WIDGET : 0); /* Iterate through the list of targets on the drag context * and check if we will accept one of them */ for(glist = dc->targets; glist != NULL; glist = g_list_next(glist) ) { /* Get the next target name on the drag context */ target_name = gdk_atom_name((GdkAtom)glist->data); if(target_name != NULL) { /* Check if this target name is in the list of * accepted targets on the GtkWidget */ if(GUIDNDIsTargetAccepted( widget, target_name, target_flags )) { g_free(target_name); break; } g_free(target_name); } } /* Drag not accepted? */ if(glist == NULL) return(FALSE); /* Determine the appropriate drag action */ GUIDNDGetTarActions( widget, &allowed_actions, &default_action_same, &default_action ); action = GUIDNDDetermineDragAction( dc->actions, /* Requested actions */ allowed_actions, (same) ? default_action_same : default_action ); /* Did the drag originate from this list? */ if(src_w == fb->list_da) { if(action == GDK_ACTION_MOVE) { if(!(fb->drag_flags & FILE_BROWSER_DRAG_ACTIVE_MOVE)) { /* Mark that the drag we initiated is currently a move */ fb->drag_flags |= FILE_BROWSER_DRAG_ACTIVE_MOVE; FileBrowserListQueueDraw(fb); } } else { if(fb->drag_flags & FILE_BROWSER_DRAG_ACTIVE_MOVE) { /* Clear the initiated drag is a move flag */ fb->drag_flags &= ~FILE_BROWSER_DRAG_ACTIVE_MOVE; FileBrowserListQueueDraw(fb); } } } /* Mark that the drag is occuring over this widget */ if((action != 0) && !(fb->drag_flags & FILE_BROWSER_DRAG_OVER) ) { fb->drag_flags |= FILE_BROWSER_DRAG_OVER; FileBrowserListQueueDraw(fb); } else if((action == 0) && (fb->drag_flags & FILE_BROWSER_DRAG_OVER) ) { fb->drag_flags &= ~FILE_BROWSER_DRAG_OVER; FileBrowserListQueueDraw(fb); } if(action != 0) i = FileBrowserListSelectCoordinates(fb, x, y); else i = -1; if(fb->focus_object != i) { fb->focus_object = i; FileBrowserListQueueDraw(fb); } return(FALSE); } /* * List GtkDrawingArea or File Name GtkEntry DND target * "drag_data_received" signal callback. */ static void FileBrowserDragDataReceivedCB( GtkWidget *widget, GdkDragContext *dc, gint x, gint y, GtkSelectionData *selection_data, guint info, guint t, gpointer data ) { FileBrowser *fb = FILE_BROWSER(data); if((widget == NULL) || (dc == NULL) || (selection_data == NULL) || (fb == NULL) ) return; if((selection_data->data == NULL) || (selection_data->length <= 0) ) return; /* Make sure the DND info is one that we want */ if((info == 0) || (info == 1) || (info == 2)) { /* List GtkDrawingArea */ if(widget == fb->list_da) { /* When dropping to the list, first check if there is * exactly one object and if so then check if it leads * to a directory, in which case we will set the new * location. Otherwise we just set it to the file * name entry. */ GList *paths_list = FileBrowserDNDBufParse( (const gchar *)selection_data->data, selection_data->length ); if(paths_list != NULL) { gchar *tar_path; /* Get the object that was dropped on */ const gint i = FileBrowserListSelectCoordinates( fb, x, y ); FileBrowserObject *o = FileBrowserGetObject(fb, i); /* Get the target path */ if(o != NULL) { tar_path = STRDUP(o->path); if(!FileBrowserIsPathDirectory(tar_path)) { g_free(tar_path); tar_path = STRDUP(FileBrowserGetLocation(fb)); } } else { tar_path = STRDUP(FileBrowserGetLocation(fb)); } if(tar_path != NULL) { switch(dc->action) { case GDK_ACTION_COPY: FileBrowserCopy( fb, paths_list, tar_path ); break; case GDK_ACTION_MOVE: FileBrowserMove( fb, paths_list, tar_path ); break; case GDK_ACTION_LINK: FileBrowserLink( fb, (const gchar *)paths_list->data, tar_path ); break; case GDK_ACTION_DEFAULT: case GDK_ACTION_PRIVATE: case GDK_ACTION_ASK: break; } g_free(tar_path); } g_list_foreach(paths_list, (GFunc)g_free, NULL); g_list_free(paths_list); } } /* File Name GtkEntry */ else if(widget == fb->entry) { GtkEditable *editable = GTK_EDITABLE(widget); gint len = selection_data->length; const gchar *ss = (const gchar *)selection_data->data; gchar *s; if(ss[len - 1] == '\0') len--; s = (gchar *)g_malloc(len + (1 * sizeof(gchar))); if(s != NULL) { gint position; gchar *s2, *s_end; if(len > 0) memcpy(s, selection_data->data, len); s[len] = '\0'; /* Remove any null deliminators within the string */ s2 = s; s_end = s2 + len; while(s2 < s_end) { if(*s2 == '\0') *s2 = ' '; s2++; } /* Insert the string into the entry */ position = gtk_editable_get_position(editable); if(len > 0) gtk_editable_insert_text( editable, s, len, &position ); g_free(s); } } } } /* * Show hidden objects GtkToggleButton "toggled" signal callback. */ static void FileBrowserShowHiddenObjectsToggleCB(GtkWidget *widget, gpointer data) { gboolean b; FileBrowser *fb = FILE_BROWSER(data); if(fb == NULL) return; if(fb->freeze_count > 0) return; fb->freeze_count++; /* Toggle the show hidden objects flag */ if(fb->flags & FB_SHOW_HIDDEN_OBJECTS) { fb->flags &= ~FB_SHOW_HIDDEN_OBJECTS; b = FALSE; } else { fb->flags |= FB_SHOW_HIDDEN_OBJECTS; b = TRUE; } /* Update the show hidden objects GtkWidgets */ GUIMenuItemSetCheck( fb->show_hidden_objects_micheck, b, FALSE ); GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->show_hidden_objects_tb, b ); /* Update the listing */ FileBrowserListUpdate(fb, NULL); /* Update the File Name GtkEntry's value */ FileBrowserEntrySetSelectedObjects(fb); fb->freeze_count--; } /* * List format GtkToggleButton "toggled" signal callback. */ static void FileBrowserListFormatToggleCB(GtkWidget *widget, gpointer data) { FileBrowserListFormat list_format = FB_LIST_FORMAT_STANDARD; FileBrowser *fb = FILE_BROWSER(data); if((widget == NULL) || (fb == NULL)) return; if(fb->freeze_count > 0) return; fb->freeze_count++; /* Do not let toggle button become untoggled when toggling * was not frozen */ if(GTK_IS_MENU_ITEM(widget)) { if(!GUIMenuItemGetCheck(widget)) { GUIMenuItemSetCheck( widget, TRUE, FALSE ); fb->freeze_count--; return; } } else if(GTK_IS_TOGGLE_BUTTON(widget)) { GtkToggleButton *tb = GTK_TOGGLE_BUTTON(widget); if(!tb->active) { gtk_toggle_button_set_active(tb, TRUE); fb->freeze_count--; return; } } /* See which toggle button this signal was for so we know * what list format to set */ /* Standard */ if((widget == fb->list_format_standard_tb) || (widget == fb->list_format_standard_micheck) ) list_format = FB_LIST_FORMAT_STANDARD; /* Vertical */ else if((widget == fb->list_format_vertical_tb) || (widget == fb->list_format_vertical_micheck) ) list_format = FB_LIST_FORMAT_VERTICAL; /* Vertical with details */ else if((widget == fb->list_format_vertical_details_tb) || (widget == fb->list_format_vertical_details_micheck) ) list_format = FB_LIST_FORMAT_VERTICAL_DETAILS; /* Set the new list format and reget the listing */ FileBrowserSetListFormat(fb, list_format); fb->freeze_count--; } /* * Go to parent callback. */ static void FileBrowserGoToParentCB(GtkWidget *widget, gpointer data) { FileBrowserGoToParent(FILE_BROWSER(data)); } /* * New Directory callback. */ static void FileBrowserNewDirectoryCB(GtkWidget *widget, gpointer data) { FileBrowserNewDirectory(FILE_BROWSER(data)); } /* * Refresh callback. */ static void FileBrowserRefreshCB(GtkWidget *widget, gpointer data) { FileBrowserRefresh(FILE_BROWSER(data)); } /* * Updates the locations popup list based on the current * location. */ static void FileBrowserLocationsPUListUpdate(FileBrowser *fb) { struct stat stat_buf; const gchar *cur_location; GtkWidget *pulist, *pulistbox; FileBrowserIcon *icon; GtkDestroyNotify destroy_cb = FileBrowserLocationsPUListItemDestroyCB; if(fb == NULL) return; pulistbox = fb->location_pulistbox; pulist = PUListBoxGetPUList(pulistbox); if(pulist == NULL) return; FileBrowserSetBusy(fb, TRUE); /* Delete all the existing items */ PUListClear(pulist); /* Gets the icon from the path */ #if defined(_WIN32) #define GET_ICON(_path_) { \ if(stat((const char *)(_path_), &stat_buf)) \ icon = NULL; \ else \ icon = FileBrowserGetIcon( \ fb, \ FileBrowserMatchIconNumFromPath( \ fb, \ (_path_), \ &stat_buf \ ) \ ); \ } #else #define GET_ICON(_path_) { \ if(lstat((const char *)(_path_), &stat_buf)) \ icon = NULL; \ else \ icon = FileBrowserGetIcon( \ fb, \ FileBrowserMatchIconNumFromPath( \ fb, \ (_path_), \ &stat_buf \ ) \ ); \ } #endif /* Copies a shortened string as s_shortened */ #define STRING_COPY_SHORTEN(_s_) { \ if((_s_) != NULL) { \ const gint len = strlen(_s_), \ max_characters = FB_LOC_LIST_MAX_CHARS;\ \ /* Does the string need to be shortened? */ \ if(len > max_characters) \ s_shortened = g_strconcat( \ "...", \ &(_s_)[len - max_characters + 3], \ NULL \ ); \ else \ s_shortened = g_strdup(_s_); \ } else { \ s_shortened = NULL; \ } \ } /* Get the current location and set the items for it as * each of its parents */ cur_location = FileBrowserGetLocation(fb); if(cur_location != NULL) { gint i; gchar *path = g_strdup(cur_location), *path_ptr, *s_shortened; FileBrowserIconIndex icon_num; #if defined(_WIN32) /* Get the icon for the current location */ if(stat((const char *)path, &stat_buf)) icon_num = FB_ICON_FOLDER_CLOSED; else icon_num = FileBrowserMatchIconNumFromPath( fb, path, &stat_buf ); #else /* Get the icon for the current location */ if(lstat((const char *)path, &stat_buf)) icon_num = FB_ICON_FOLDER_CLOSED; else icon_num = FileBrowserMatchIconNumFromPath( fb, path, &stat_buf ); #endif if(icon_num == FB_ICON_FOLDER_CLOSED) icon_num = FB_ICON_FOLDER_OPENED; icon = FileBrowserGetIcon(fb, icon_num); STRING_COPY_SHORTEN(path); if(icon != NULL) i = PUListAddItemPixText( pulist, s_shortened, icon->pixmap, icon->mask ); else i = PUListAddItem( pulist, s_shortened ); g_free(s_shortened); PUListSetItemDataFull( pulist, i, g_strdup(path), destroy_cb ); /* Get each parent location */ path_ptr = (gchar *)strrchr( (char *)path, G_DIR_SEPARATOR ); if(path_ptr == NULL) path_ptr = path; #if defined(_WIN32) while(path_ptr > (path + 3)) #else while(path_ptr > path) #endif { /* Terminate the path string at its parent deliminator * character so that path now describes the parent * path */ *path_ptr = '\0'; GET_ICON(path); STRING_COPY_SHORTEN(path); if(icon != NULL) i = PUListAddItemPixText( pulist, s_shortened, icon->pixmap, icon->mask ); else i = PUListAddItem( pulist, s_shortened ); g_free(s_shortened); PUListSetItemDataFull( pulist, i, g_strdup(path), destroy_cb ); path_ptr = (gchar *)strrchr( (char *)path, G_DIR_SEPARATOR ); if(path_ptr == NULL) break; } /* Toplevel */ #if defined(_WIN32) /* On Win32, use s as is from the above parent fetching loop */ path_ptr = (gchar *)strchr((const char *)path, G_DIR_SEPARATOR); if(path_ptr != NULL) { if((*(path_ptr + 1) != '\0') { *(path_ptr + 1) = '\0'; GET_ICON(path); STRING_COPY_SHORTEN(path); if(icon != NULL) i = PUListAddItemPixText( pulist, s_shortened, icon->pixmap, icon->mask ); else i = PUListAddItem( pulist, s_shortened ); g_free(s_shortened); PUListSetItemDataFull( pulist, i, g_strdup(path), destroy_cb ); } } g_free(path); #else g_free(path); path = g_strdup("/"); GET_ICON(path); STRING_COPY_SHORTEN(path); if(icon != NULL) i = PUListAddItemPixText( pulist, s_shortened, icon->pixmap, icon->mask ); else i = PUListAddItem( pulist, s_shortened ); g_free(s_shortened); PUListSetItemDataFull( pulist, i, g_strdup(path), destroy_cb ); g_free(path); #endif } #if !defined(_WIN32) /* Home */ if(fb->home_path != NULL) { gint i; gchar *s_shortened; const gchar *path = fb->home_path; GET_ICON(path); STRING_COPY_SHORTEN(path); if(icon != NULL) i = PUListAddItemPixText( pulist, s_shortened, icon->pixmap, icon->mask ); else i = PUListAddItem( pulist, s_shortened ); g_free(s_shortened); PUListSetItemDataFull( pulist, i, g_strdup(path), destroy_cb ); } #endif /* !_WIN32 */ /* Drives */ if(fb->device_paths_list != NULL) { gint i; const gchar *path; gchar *s_shortened; GList *glist; for(glist = fb->device_paths_list; glist != NULL; glist = g_list_next(glist) ) { path = (const gchar *)glist->data; if(path == NULL) continue; /* Ignore the toplevel path */ if(!g_strcasecmp((const char *)path, "/")) continue; #if !defined(_WIN32) /* Ignore drives that do not have absolute paths */ if(!g_path_is_absolute(path)) continue; #endif /* !_WIN32 */ #if defined(_WIN32) icon = FileBrowserGetIcon(fb, FB_ICON_DRIVE_FIXED); #else GET_ICON(path); #endif STRING_COPY_SHORTEN(path); if(icon != NULL) i = PUListAddItemPixText( pulist, s_shortened, icon->pixmap, icon->mask ); else i = PUListAddItem( pulist, s_shortened ); g_free(s_shortened); PUListSetItemDataFull( pulist, i, g_strdup(path), destroy_cb ); } } /* Other things to be added to the popup list, like mounted drives? */ /* Make sure that no more than 10 lines are visible */ PUListBoxSetLinesVisible( pulistbox, MIN(PUListGetTotalItems(pulist), 10) ); /* Select the first item */ PUListBoxSelect(pulistbox, 0); FileBrowserSetBusy(fb, FALSE); #undef GET_ICON #undef STRING_COPY_SHORTEN } /* * Creates a new icon. */ static FileBrowserIcon *FileBrowserIconNew(void) { return(FILE_BROWSER_ICON(g_malloc0( sizeof(FileBrowserIcon) ))); } /* * Deletes the icon. */ static void FileBrowserIconDelete(FileBrowserIcon *icon) { if(icon == NULL) return; GDK_PIXMAP_UNREF(icon->pixmap); GDK_BITMAP_UNREF(icon->mask); g_free(icon->desc); g_free(icon); } /* * Returns the icon at index i or NULL on error. */ static FileBrowserIcon *FileBrowserGetIcon(FileBrowser *fb, const gint i) { if((fb == NULL) || (i < 0)) return(NULL); return(FILE_BROWSER_ICON(g_list_nth_data( fb->icons_list, (guint)i ))); } /* * Appends an icon to the list. */ static FileBrowserIcon *FileBrowserIconAppend( FileBrowser *fb, guint8 **data, const gchar *desc ) { FileBrowserIcon *icon; if((fb == NULL) || (data == NULL)) return(NULL); /* Create a new icon */ icon = FileBrowserIconNew(); if(icon == NULL) return(NULL); /* Append it to the list */ fb->icons_list = g_list_append(fb->icons_list, icon); /* Set the new icon's values */ icon->pixmap = GDK_PIXMAP_NEW_FROM_XPM_DATA(&icon->mask, data); gdk_window_get_size(icon->pixmap, &icon->width, &icon->height); icon->desc = STRDUP(desc); return(icon); } /* * Returns the icon index appropriate for the object specified by * full_path and lstat_buf. */ static FileBrowserIconIndex FileBrowserMatchIconNumFromPath( FileBrowser *fb, const gchar *path, struct stat *lstat_buf ) { mode_t m; gint uid, gid; const gchar *ext, *name; if(fb == NULL) return(FB_ICON_FILE); if(path != NULL) { const gchar *s; GList *glist; name = g_basename(path); #if !defined(_WIN32) /* Home directory? */ s = fb->home_path; if(s != NULL) { if(!strcmp((const char *)s, (const char *)path)) return(FB_ICON_FOLDER_HOME); } /* Toplevel? */ if(!g_strcasecmp(path, "/")) return(FB_ICON_DRIVE_ROOT); #endif /* !_WIN32 */ /* Drive path? */ for(glist = fb->device_paths_list; glist != NULL; glist = g_list_next(glist) ) { s = (const gchar *)glist->data; if(s == NULL) continue; if(!strcmp((const char *)s, (const char *)path)) return(FB_ICON_DRIVE_FIXED); } /* Get the extension (if any) */ ext = FileBrowserGetExtensionFromPath(path); } else { name = NULL; ext = NULL; } /* Get object's statistics */ if(lstat_buf != NULL) { m = lstat_buf->st_mode; uid = lstat_buf->st_uid; gid = lstat_buf->st_gid; } else { m = 0; uid = 0; gid = 0; } /* Directory? */ #ifdef S_ISDIR if(S_ISDIR(m)) #else if(FALSE) #endif { #if defined(_WIN32) return(FB_ICON_FOLDER_CLOSED); #else /* Accessable? */ if(fb->euid != 0) { int accessable_icon = FB_ICON_FOLDER_CLOSED; /* Hidden? */ if(FileBrowserIsNameHidden(name)) accessable_icon = FB_ICON_FOLDER_HIDDEN; /* For non-root process id's we should check if the * process or the process' group owns the object and * respectively see if the object (the directory) is * executable (accessable) and return the appropriate * icon number. */ /* This process owns object? */ if(fb->euid == uid) return((m & S_IXUSR) ? accessable_icon : FB_ICON_FOLDER_NOACCESS ); /* This process' group id owns object? */ else if(fb->egid == gid) return((m & S_IXGRP) ? accessable_icon : FB_ICON_FOLDER_NOACCESS ); /* Anonymous */ else return((m & S_IXOTH) ? accessable_icon : FB_ICON_FOLDER_NOACCESS ); } else { /* Root always owns the object, so check if the owner * has access */ if(!(m & S_IXUSR)) return(FB_ICON_FOLDER_NOACCESS); /* Hidden? */ if(FileBrowserIsNameHidden(name)) return(FB_ICON_FOLDER_HIDDEN); return(FB_ICON_FOLDER_CLOSED); } #endif } #if defined(_WIN32) /* File With Extension? */ if(ext != NULL) { /* Executable File? */ if(!g_strcasecmp(ext, ".exe") || !g_strcasecmp(ext, ".com") || !g_strcasecmp(ext, ".bat") ) return(FB_ICON_EXECUTABLE); else return(FB_ICON_FILE); } else { /* File */ return(FB_ICON_FILE); } #else #ifdef S_ISLNK /* Link? */ if(S_ISLNK(m)) { struct stat stat_buf; /* Broken Link? */ if(stat((const char *)path, &stat_buf)) { return(FB_ICON_LINK_BROKEN); } /* Hidden Link? */ if(FileBrowserIsNameHidden(name)) { return(FB_ICON_LINK_HIDDEN); } return(FB_ICON_LINK); } #endif #ifdef S_ISFIFO /* Pipe? */ if(S_ISFIFO(m)) return(FB_ICON_PIPE); #endif #ifdef S_ISSOCK /* Socket? */ if(S_ISSOCK(m)) return(FB_ICON_SOCKET); #endif #ifdef S_ISBLK /* Block Device? */ if(S_ISBLK(m)) return(FB_ICON_DEVICE_BLOCK); #endif #ifdef S_ISCHR /* Character Device? */ if(S_ISCHR(m)) return(FB_ICON_DEVICE_CHARACTER); #endif #if defined(S_IXUSR) && defined(S_IXGRP) && defined(S_IXOTH) /* Executable File? */ if((m & S_IXUSR) || (m & S_IXGRP) || (m & S_IXOTH)) return(FB_ICON_EXECUTABLE); #endif /* Hidden File? */ if(FileBrowserIsNameHidden(name)) return(FB_ICON_FILE_HIDDEN); /* File */ return(FB_ICON_FILE); #endif } /* * Creates a new object. */ static FileBrowserObject *FileBrowserObjectNew(void) { return(FILE_BROWSER_OBJECT(g_malloc0( sizeof(FileBrowserObject) ))); } /* * Deletes the object. */ static void FileBrowserObjectDelete(FileBrowserObject *o) { if(o == NULL) return; g_free(o->displayed_name); g_free(o->path); g_free(o->name); g_free(o); } /* * Returns the object at index i or NULL on error. */ static FileBrowserObject *FileBrowserGetObject(FileBrowser *fb, const gint i) { if(fb == NULL) return(NULL); if(i < 0) return(NULL); return(FILE_BROWSER_OBJECT( g_list_nth_data(fb->objects_list, (guint)i) )); } /* * Updates the Object's values. * * The name and lstat_buf must be updated prior to this call since * the information used to update the other values are based on * them. * * The updated values are; icon_num, width, and height. */ static void FileBrowserObjectUpdateValues( FileBrowser *fb, FileBrowserObject *o ) { GdkFont *font; GtkStyle *style; GtkWidget *w; FileBrowserIconIndex icon_num; FileBrowserIcon *icon; if((fb == NULL) || (o == NULL)) return; /* Get the list's GtkStyle and GdkFont */ w = fb->list_da; style = (w != NULL) ? gtk_widget_get_style(w) : NULL; font = (style != NULL) ? style->font : NULL; /* Get the icon index based on the object's path and statistics */ o->icon_num = icon_num = FileBrowserMatchIconNumFromPath( fb, o->path, &o->lstat_buf ); /* Get the icon */ icon = FileBrowserGetIcon(fb, icon_num); /* Set displayed_name from name, abbreviate it as needed */ if(o->name != NULL) { #if 0 /* Do not shorten the displayed name anymore */ const gchar *name = o->name; const gint len = STRLEN(name), max_chars = FB_LIST_ITEM_MAX_CHARS; g_free(o->displayed_name); if((len > max_chars) && (len > 3)) o->displayed_name = g_strdup_printf( "...%s", name + (len - max_chars + 3) ); else o->displayed_name = STRDUP(name); #endif g_free(o->displayed_name); o->displayed_name = STRDUP(o->name); } else { g_free(o->displayed_name); o->displayed_name = STRDUP(""); } /* Update size */ if((o->displayed_name != NULL) && (font != NULL)) { const gchar *name = o->displayed_name; const gint font_height = font->ascent + font->descent; GdkTextBounds b; gdk_string_bounds(font, name, &b); if(icon != NULL) { o->width = MAX( icon->width + FB_LIST_ICON_TEXT_SPACING + 2 + b.width, 1 ); o->height = MAX( icon->height, font_height ); } else { o->width = MAX( b.width, 1 ); o->height = MAX( font_height, 1 ); } } else { if(icon != NULL) { o->width = icon->width; o->height = icon->height; } else { o->width = 1; o->height = 1; } } } /* * Appends an object to the list. * * Returns the new object or NULL on error. The calling function * needs to calculate the position. */ static FileBrowserObject *FileBrowserObjectAppend( FileBrowser *fb, const gchar *name, const gchar *path, struct stat *lstat_buf ) { FileBrowserObject *o; if((fb == NULL) || (path == NULL)) return(NULL); /* Create a new object */ o = FileBrowserObjectNew(); if(o == NULL) return(NULL); /* Append this object to the list */ fb->objects_list = g_list_append(fb->objects_list, o); o->name = STRDUP(name); o->path = STRDUP(path); if(lstat_buf != NULL) memcpy(&o->lstat_buf, lstat_buf, sizeof(struct stat)); else memset(&o->lstat_buf, 0x00, sizeof(struct stat)); /* Update values */ FileBrowserObjectUpdateValues(fb, o); return(o); } /* * Updates the list. * * Regets the list of objects from the current location and then * calls FileBrowserListUpdatePositions() to update the scroll * bar values. The focus object and selections list will be * cleared. * * The filter specifies a string describing which objects are * to be added to the list by name. If filter is NULL then all * objects will be added. */ static void FileBrowserListUpdate( FileBrowser *fb, const gchar *filter ) { gboolean match_all_files, show_hidden_objects; gint i; const gchar *cur_location, *extensions; fb_type_struct *t_cur; if(fb == NULL) return; FileBrowserSetBusy(fb, TRUE); t_cur = fb->current_file_type; /* Show hidden objects? */ show_hidden_objects = (fb->flags & FB_SHOW_HIDDEN_OBJECTS) ? TRUE : FALSE; /* Get the extensions list from the filter (if specified) or * from the current file type */ extensions = (filter != NULL) ? filter : t_cur->extensions; if(extensions != NULL) match_all_files = (!strcmp(extensions, "*.*") || !strcmp(extensions, "*")) ? TRUE : FALSE; else match_all_files = TRUE; /* Get listing of contents in current location */ /* Delete current objects list and the selection */ /* Unselect all */ fb->focus_object = -1; g_list_free(fb->selection); fb->selection = fb->selection_end = NULL; /* Delete all the objects */ if(fb->objects_list != NULL) { g_list_foreach( fb->objects_list, (GFunc)FileBrowserObjectDelete, NULL ); g_list_free(fb->objects_list); fb->objects_list = NULL; } /* Reset the number of objects per row */ fb->objects_per_row = 0; /* Get the current location */ cur_location = FileBrowserGetLocation(fb); if(cur_location != NULL) { /* Get the listing of objects in the current location */ gint nobjs; gchar **names_list = FileBrowserListDirectory( cur_location, &nobjs ); if(names_list != NULL) { struct stat stat_buf; const gchar *name; gchar *path; StringQSort(names_list, nobjs); /* Iterate through the names list and get only the * directories */ for(i = 0; i < nobjs; i++) { name = names_list[i]; if(name == NULL) continue; /* Skip special directory notations */ if(!strcmp(name, ".") || !strcmp(name, "..") ) { g_free(names_list[i]); names_list[i] = NULL; continue; } /* Skip hidden objects? */ if(!show_hidden_objects) { if(FileBrowserIsNameHidden(name)) { g_free(names_list[i]); names_list[i] = NULL; continue; } } /* Get the full path to the object */ path = FileBrowserJoinPaths( cur_location, name ); if(path == NULL) { g_free(names_list[i]); names_list[i] = NULL; continue; } /* Get this object's destination stats */ if(stat((const char *)path, &stat_buf)) { g_free(path); /* Do not delete this object from the names list */ continue; } /* Skip this object if it's destination is not * a directory */ if(!S_ISDIR(stat_buf.st_mode)) { g_free(path); /* Do not delete this object from the names list */ continue; } /* Get this directory's local statistics */ #if !defined(_WIN32) if(lstat((const char *)path, &stat_buf)) memset(&stat_buf, 0x00, sizeof(struct stat)); #endif /* Append this directory and its stats to the list */ FileBrowserObjectAppend( fb, name, path, &stat_buf ); g_free(path); /* Delete this directory from the names list */ g_free(names_list[i]); names_list[i] = NULL; } /* Add all the other objects */ for(i = 0; i < nobjs; i++) { name = names_list[i]; if(name == NULL) continue; /* Get the full path to the object */ path = FileBrowserJoinPaths( cur_location, name ); if(path == NULL) { g_free(names_list[i]); names_list[i] = NULL; continue; } /* Filter this object's name */ if(!match_all_files) { if(!FileBrowserMatchName( name, extensions )) { g_free(path); g_free(names_list[i]); names_list[i] = NULL; continue; } } /* Get this object's local stats */ #if defined(_WIN32) if(stat((const char *)path, &stat_buf)) memset(&stat_buf, 0x00, sizeof(struct stat)); #else if(lstat((const char *)path, &stat_buf)) memset(&stat_buf, 0x00, sizeof(struct stat)); #endif /* Append this object and its stats to the list */ FileBrowserObjectAppend( fb, name, path, &stat_buf ); g_free(path); /* Delete this object from the names list */ g_free(names_list[i]); names_list[i] = NULL; } /* Delete the names list */ g_free(names_list); } } /* Update each object's position and the scroll bars values */ FileBrowserListUpdatePositions(fb); /* If the objects list is not empty then reset the focus * object */ if(fb->objects_list != NULL) fb->focus_object = 0; FileBrowserSetBusy(fb, FALSE); } /* * Updates each object's position and the scroll bar values. * * This may produce a change in the list window's size due to * the mapping or unmapping of the scroll bar, in which case * a "configure_event" signal will be queued. */ static void FileBrowserListUpdatePositions(FileBrowser *fb) { gboolean need_resize = FALSE; const gint border_major = 5, border_minor = 2, border_x = border_minor, border_y = border_minor, object_spacing = FB_LIST_OBJECT_SPACING; gint width, height, objects_per_row, cur_x = border_x, cur_y = border_y, longest_width = 0, last_longest_width = longest_width, list_x_max = 0, list_y_max = 0, list_x_inc = 0, list_x_page_inc = 0, list_y_inc = 0, list_y_page_inc = 0; GList *glist; GdkWindow *window; GtkWidget *w; FileBrowserListFormat list_format; FileBrowserObject *o = NULL; if(fb == NULL) return; list_format = fb->list_format; w = fb->list_da; if(w == NULL) return; window = w->window; if(window == NULL) return; /* Get the size of the list */ gdk_window_get_size(window, &width, &height); /* Update the object positions and get values for the scroll * bar */ switch(list_format) { case FB_LIST_FORMAT_VERTICAL_DETAILS: case FB_LIST_FORMAT_VERTICAL: for(glist = fb->objects_list; glist != NULL; glist = g_list_next(glist) ) { o = FILE_BROWSER_OBJECT(glist->data); if(o == NULL) continue; if(longest_width < o->width) last_longest_width = longest_width = o->width; o->x = cur_x; o->y = cur_y; cur_y += o->height + object_spacing; } break; case FB_LIST_FORMAT_STANDARD: objects_per_row = 0; for(glist = fb->objects_list; glist != NULL; glist = g_list_next(glist) ) { o = FILE_BROWSER_OBJECT(glist->data); if(o == NULL) continue; if(longest_width < o->width) last_longest_width = longest_width = o->width; o->x = cur_x; o->y = cur_y; cur_y += o->height + object_spacing; objects_per_row++; /* Update the objects per row if this object's y * position has exceeded the list's height */ if((cur_y + o->height) > height) { if(objects_per_row > fb->objects_per_row) fb->objects_per_row = objects_per_row; objects_per_row = 0; cur_y = border_y; cur_x += longest_width + border_major; /* border_major * as spacing * between rows */ longest_width = 0; } } break; } /* Use the last object to set the list's bounds, these * bounds will be used in updating of the scroll bar's values */ if(o != NULL) { list_x_max = o->x + last_longest_width + border_x; list_y_max = o->y + o->height + border_y; list_x_inc = (gint)(width * 0.25f); list_x_page_inc = (gint)(width * 0.5f); list_y_inc = o->height; list_y_page_inc = (gint)(height * 0.5f); } /* Show/hide the scroll bars */ switch(list_format) { case FB_LIST_FORMAT_VERTICAL_DETAILS: case FB_LIST_FORMAT_VERTICAL: w = fb->list_hsb; if(w != NULL) { if(fb->flags & FB_HSB_MAPPED) { gtk_widget_hide(w); fb->flags &= ~FB_HSB_MAPPED; need_resize = TRUE; } } w = fb->list_vsb; if(w != NULL) { GtkRange *range = GTK_RANGE(w); GtkAdjustment *adj = range->adjustment; if(adj != NULL) { adj->lower = 0.0f; adj->upper = (gfloat)list_y_max; adj->value = adj->lower; adj->step_increment = (gfloat)list_y_inc; adj->page_increment = (gfloat)list_y_page_inc; adj->page_size = (gfloat)height; gtk_signal_emit_by_name( GTK_OBJECT(adj), "changed" ); gtk_signal_emit_by_name( GTK_OBJECT(adj), "value_changed" ); /* If content size is larger than visible size then * map the scrollbar, otherwise unmap the scrollbar * since it would not be needed. */ if((adj->upper - adj->lower) > adj->page_size) { if(!(fb->flags & FB_VSB_MAPPED)) { gtk_widget_show(w); fb->flags |= FB_VSB_MAPPED; need_resize = TRUE; } } else { if(fb->flags & FB_VSB_MAPPED) { gtk_widget_hide(w); fb->flags &= ~FB_VSB_MAPPED; need_resize = TRUE; } } } } break; case FB_LIST_FORMAT_STANDARD: w = fb->list_vsb; if(w != NULL) { if(fb->flags & FB_VSB_MAPPED) { gtk_widget_hide(w); fb->flags &= ~FB_VSB_MAPPED; need_resize = TRUE; } } w = fb->list_hsb; if(w != NULL) { GtkRange *range = GTK_RANGE(w); GtkAdjustment *adj = range->adjustment; if(adj != NULL) { adj->lower = 0.0f; adj->upper = (gfloat)list_x_max; adj->value = adj->lower; adj->step_increment = (gfloat)list_x_inc; adj->page_increment = (gfloat)list_x_page_inc; adj->page_size = (gfloat)width; gtk_signal_emit_by_name( GTK_OBJECT(adj), "changed" ); gtk_signal_emit_by_name( GTK_OBJECT(adj), "value_changed" ); /* If content size is larger than visible size then * map the scrollbar, otherwise unmap the scrollbar * since it would not be needed */ if((adj->upper - adj->lower) > adj->page_size) { if(!(fb->flags & FB_HSB_MAPPED)) { gtk_widget_show(w); fb->flags |= FB_HSB_MAPPED; need_resize = TRUE; } } else { if(fb->flags & FB_HSB_MAPPED) { gtk_widget_hide(w); fb->flags &= ~FB_HSB_MAPPED; need_resize = TRUE; } } } } break; } /* Need to resize due to widgets being mapped or unmapped? */ if(need_resize) gtk_widget_queue_resize(fb->toplevel); } /* * Sets the DND drag icon based on the selected objects in * the list. */ static void FileBrowserListSetDNDDragIcon(FileBrowser *fb) { const gint nitems_visible = GUI_DND_DRAG_ICON_DEF_NITEMS_VISIBLE; gint i; GList *glist, *cells_list; GtkCellPixText *cell_pixtext; FileBrowserObject *o; /* Create a GtkCells list from the selected objects */ cells_list = NULL; for(glist = fb->selection, i = 0; (glist != NULL) && (i < nitems_visible); glist = g_list_next(glist), i++ ) { o = FileBrowserGetObject( fb, (gint)glist->data ); if(o == NULL) continue; cell_pixtext = (GtkCellPixText *)g_malloc0( sizeof(GtkCellPixText) ); if(cell_pixtext != NULL) { FileBrowserIcon *icon = FileBrowserGetIcon(fb, o->icon_num); cell_pixtext->type = GTK_CELL_PIXTEXT; cell_pixtext->text = o->displayed_name; cell_pixtext->spacing = FB_LIST_ICON_TEXT_SPACING; if(icon != NULL) { cell_pixtext->pixmap = icon->pixmap; cell_pixtext->mask = icon->mask; } cells_list = g_list_append( cells_list, cell_pixtext ); } } if(cells_list != NULL) { /* Set the DND drag icon from the GtkCells list */ GUIDNDSetDragIconFromCellsList( fb->list_da, cells_list, g_list_length(fb->selection), GTK_ORIENTATION_VERTICAL, GTK_ORIENTATION_HORIZONTAL, FB_LIST_OBJECT_SPACING ); /* Delete the GtkCells list */ g_list_foreach( cells_list, (GFunc)g_free, NULL ); g_list_free(cells_list); } } /* * Returns the visibility of the object in the list. * * The i specifies the object's index. * * Returns one of GTK_VISIBILITY_*. */ static GtkVisibility FileBrowserListObjectVisibility( FileBrowser *fb, const gint i ) { gint scroll_x = 0, scroll_y = 0; gint x, y, width, height; GdkWindow *window; GtkWidget *w; FileBrowserObject *o; if(fb == NULL) return(GTK_VISIBILITY_NONE); /* Get list's GdkWindow and its size, making sure it exists and * its size is positive */ w = fb->list_da; window = (w != NULL) ? w->window : NULL; if(window == NULL) return(GTK_VISIBILITY_NONE); gdk_window_get_size(window, &width, &height); if((width <= 0) || (height <= 0)) return(GTK_VISIBILITY_NONE); /* Get object */ o = FileBrowserGetObject(fb, i); if(o == NULL) return(GTK_VISIBILITY_NONE); /* Get current scroll position */ if(fb->list_hsb != NULL) { GtkRange *range = GTK_RANGE(fb->list_hsb); GtkAdjustment *adj = range->adjustment; scroll_x = (gint)((adj != NULL) ? adj->value : 0.0f); } if(fb->list_vsb != NULL) { GtkRange *range = GTK_RANGE(fb->list_vsb); GtkAdjustment *adj = range->adjustment; scroll_y = (gint)((adj != NULL) ? adj->value : 0.0f); } /* Check visibility by list display format */ switch(fb->list_format) { case FB_LIST_FORMAT_VERTICAL_DETAILS: case FB_LIST_FORMAT_VERTICAL: y = o->y - scroll_y; if(((y + o->height) <= 0) || (y >= height)) return(GTK_VISIBILITY_NONE); else if((y < 0) || ((y + o->height) > height)) return(GTK_VISIBILITY_PARTIAL); else return(GTK_VISIBILITY_FULL); break; case FB_LIST_FORMAT_STANDARD: x = o->x - scroll_x; if(((x + o->width) <= 0) || (x >= width)) return(GTK_VISIBILITY_NONE); else if((x < 0) || ((x + o->width) > width)) return(GTK_VISIBILITY_PARTIAL); else return(GTK_VISIBILITY_FULL); break; } return(GTK_VISIBILITY_NONE); } /* * Scrolls to the object in the list. * * The i specifies the object's index. * * The coefficient specifies the offset within the list window. */ static void FileBrowserListMoveToObject( FileBrowser *fb, const gint i, const gfloat coeff ) { gint x, y, width, height; GdkWindow *window; GtkWidget *w; FileBrowserObject *o; if(fb == NULL) return; w = fb->list_da; o = FileBrowserGetObject(fb, i); if((w == NULL) || (o == NULL)) return; window = w->window; if(window == NULL) return; gdk_window_get_size(window, &width, &height); if((width <= 0) || (height <= 0)) return; /* Move to the object based on the list format */ switch(fb->list_format) { case FB_LIST_FORMAT_VERTICAL_DETAILS: case FB_LIST_FORMAT_VERTICAL: y = o->y + (gint)(o->height * coeff); y -= (gint)(height * CLIP(coeff, 0.0f, 1.0f)); if(fb->list_vsb != NULL) { GTK_ADJUSTMENT_SET_VALUE( GTK_RANGE(fb->list_vsb)->adjustment, (gfloat)y ); } break; case FB_LIST_FORMAT_STANDARD: x = o->x + (gint)(o->width * coeff); x -= (gint)(width * CLIP(coeff, 0.0f, 1.0f)); if(fb->list_hsb != NULL) { GTK_ADJUSTMENT_SET_VALUE( GTK_RANGE(fb->list_hsb)->adjustment, (gfloat)x ); } break; } } /* * Gets the object at the coordinates within the list. * * The x and y specifies the coordinates in the list window. * * Returns the object index or -1 on failed match. */ static gint FileBrowserListSelectCoordinates( FileBrowser *fb, const gint x, const gint y ) { gint i, cx, cy, scroll_x = 0, scroll_y = 0; GList *glist; FileBrowserObject *o; if(fb == NULL) return(-1); /* Get scroll position */ if(fb->list_hsb != NULL) { GtkRange *range = GTK_RANGE(fb->list_hsb); GtkAdjustment *adj = range->adjustment; scroll_x = (gint)((adj != NULL) ? adj->value : 0.0f); } if(fb->list_vsb != NULL) { GtkRange *range = GTK_RANGE(fb->list_vsb); GtkAdjustment *adj = range->adjustment; scroll_y = (gint)((adj != NULL) ? adj->value : 0.0f); } /* Find object by list display format */ switch(fb->list_format) { case FB_LIST_FORMAT_VERTICAL_DETAILS: case FB_LIST_FORMAT_VERTICAL: for(glist = fb->objects_list, i = 0; glist != NULL; glist = g_list_next(glist), i++ ) { o = FILE_BROWSER_OBJECT(glist->data); if(o == NULL) continue; if((o->width <= 0) || (o->height <= 0)) continue; /* Calculate object position with scrolled adjust */ cx = o->x; cy = o->y - scroll_y; /* In bounds? */ if((x >= cx) && (x < (cx + o->width)) && (y >= cy) && (y < (cy + o->height)) ) return(i); } break; case FB_LIST_FORMAT_STANDARD: for(glist = fb->objects_list, i = 0; glist != NULL; glist = g_list_next(glist), i++ ) { o = FILE_BROWSER_OBJECT(glist->data); if(o == NULL) continue; if((o->width <= 0) || (o->height <= 0)) continue; /* Calculate object position with scrolled adjust */ cx = o->x - scroll_x; cy = o->y; /* In bounds? */ if((x >= cx) && (x < (cx + o->width)) && (y >= cy) && (y < (cy + o->height)) ) return(i); } break; } return(-1); } /* * Redraws the File Selection Dialog's list header. */ static void FileBrowserListHeaderDraw(FileBrowser *fb) { gint width, height, font_height; GdkFont *font; GdkDrawable *drawable; GdkWindow *window; GdkGC *gc; GtkStateType state; GtkStyle *style; GtkWidget *w; if(fb == NULL) return; gc = fb->gc; w = fb->list_header_da; if((gc == NULL) || (w == NULL)) return; if(!GTK_WIDGET_VISIBLE(w)) return; window = w->window; if(window == NULL) return; drawable = (GdkDrawable *)fb->list_pm; if(drawable == NULL) drawable = (GdkDrawable *)window; state = GTK_WIDGET_STATE(w); style = gtk_widget_get_style(w); gdk_window_get_size(window, &width, &height); if((style == NULL) || (width <= 0) || (height <= 0)) return; font = style->font; if(font == NULL) return; font_height = font->ascent + font->descent; /* Draw the background */ gdk_gc_set_function(gc, GDK_COPY); gdk_gc_set_foreground(gc, &style->bg[state]); gdk_gc_set_fill(gc, GDK_SOLID); /* Draw the frame and the background */ gtk_paint_box( style, (GdkWindow *)drawable, state, GTK_SHADOW_OUT, NULL, /* Entire area */ w, NULL, /* No detail */ 0, 0, width, height ); /* Any column headings to draw? */ if(fb->columns_list != NULL) { gint last_column_position = 0; GList *glist; GdkRectangle rect; FileBrowserColumn *column; /* Draw each column heading */ for(glist = fb->columns_list; glist != NULL; glist = g_list_next(glist) ) { column = FILE_BROWSER_COLUMN(glist->data); if(column == NULL) continue; rect.x = last_column_position; rect.y = 0; rect.width = MAX(column->position - last_column_position, 0); rect.height = height; if(rect.width == 0) continue; /* Draw the frame for this heading */ gtk_paint_box( style, (GdkWindow *)drawable, (column->flags & GTK_HAS_FOCUS) ? GTK_STATE_ACTIVE : state, (column->flags & GTK_HAS_FOCUS) ? GTK_SHADOW_IN : GTK_SHADOW_OUT, NULL, /* Entire area */ w, NULL, /* No detail */ rect.x, rect.y, rect.width, rect.height ); gdk_gc_set_foreground( gc, (column->flags & GTK_SENSITIVE) ? &style->text[state] : &style->text[GTK_STATE_INSENSITIVE] ); if((column->label != NULL) ? (*column->label != '\0') : FALSE) { const gchar *label = column->label; GdkTextBounds b; gdk_string_bounds(font, label, &b); gdk_gc_set_clip_origin(gc, 0, 0); gdk_gc_set_clip_rectangle(gc, &rect); switch(column->label_justify) { case GTK_JUSTIFY_FILL: case GTK_JUSTIFY_CENTER: gdk_draw_string( drawable, font, gc, (rect.x + ((rect.width - b.width) / 2)) - b.lbearing, (rect.y + ((rect.height - font_height) / 2)) + font->ascent, label ); break; case GTK_JUSTIFY_RIGHT: gdk_draw_string( drawable, font, gc, (rect.x + rect.width - b.width - 5) - b.lbearing, (rect.y + ((rect.height - font_height) / 2)) + font->ascent, label ); break; case GTK_JUSTIFY_LEFT: gdk_draw_string( drawable, font, gc, (rect.x + 5) - b.lbearing, (rect.y + ((rect.height - font_height) / 2)) + font->ascent, label ); break; } gdk_gc_set_clip_rectangle(gc, NULL); } last_column_position = column->position; } } /* Send drawable to window if drawable is not the window */ if(drawable != (GdkDrawable *)window) gdk_draw_pixmap( window, gc, drawable, 0, 0, 0, 0, width, height ); } /* * Queues a draw of the File Selection Dialog's list header. */ static void FileBrowserListHeaderQueueDraw(FileBrowser *fb) { if(fb == NULL) return; gtk_widget_queue_draw(fb->list_header_da); } /* * Draws the File Selection Dialog's list. */ static void FileBrowserListDraw(FileBrowser *fb) { gint scroll_x = 0, scroll_y = 0, width, height, font_height; GdkFont *font; GdkDrawable *drawable; GdkWindow *window; GdkGC *gc; GtkStateType state; GtkStyle *style; GtkWidget *w; FileBrowserDragFlags drag_flags; if(fb == NULL) return; drag_flags = fb->drag_flags; gc = fb->gc; w = fb->list_da; if((gc == NULL) || (w == NULL)) return; window = w->window; if(window == NULL) return; drawable = (GdkDrawable *)fb->list_pm; if(drawable == NULL) drawable = (GdkDrawable *)window; state = GTK_WIDGET_STATE(w); style = gtk_widget_get_style(w); gdk_window_get_size(window, &width, &height); if((style == NULL) || (width <= 0) || (height <= 0)) return; font = style->font; if(font == NULL) return; font_height = font->ascent + font->descent; /* Draw the background */ gdk_gc_set_function(gc, GDK_COPY); gdk_gc_set_fill(gc, GDK_SOLID); gdk_gc_set_foreground(gc, &style->base[state]); gdk_draw_rectangle( drawable, gc, TRUE, /* Fill */ 0, 0, width, height ); #if 0 if(style->bg_pixmap[state] != NULL) gtk_style_apply_default_background( style, drawable, FALSE, state, NULL, 0, 0, width, height ); #endif /* Get the scroll position */ if(fb->list_hsb != NULL) { GtkRange *range = GTK_RANGE(fb->list_hsb); GtkAdjustment *adj = range->adjustment; scroll_x = (gint)((adj != NULL) ? adj->value : 0.0f); } if(fb->list_vsb != NULL) { GtkRange *range = GTK_RANGE(fb->list_vsb); GtkAdjustment *adj = range->adjustment; scroll_y = (gint)((adj != NULL) ? adj->value : 0.0f); } /* Begin drawing objects by list display format */ switch(fb->list_format) { gboolean o_is_selected, o_is_dragged; gint i, x, y, icon_width; GList *glist; GdkTextBounds b; GdkColor *fg_color, *text_color; FileBrowserObject *o; FileBrowserIcon *icon; case FB_LIST_FORMAT_VERTICAL_DETAILS: /* Make sure we have enough columns to draw for this * list display format */ if(g_list_length(fb->columns_list) >= 4) { mode_t m; struct stat *lstat_buf; GdkRectangle rect; FileBrowserColumn *column[4]; /* Get the pointers to each column */ for(glist = fb->columns_list, i = 0; (glist != NULL) && (i < 4); glist = g_list_next(glist), i++ ) column[i] = FILE_BROWSER_COLUMN(glist->data); /* Draw each object */ for(glist = fb->objects_list, i = 0; glist != NULL; glist = g_list_next(glist), i++ ) { o = FILE_BROWSER_OBJECT(glist->data); if(o == NULL) continue; if((o->width <= 0) || (o->height <= 0)) continue; x = o->x; y = o->y - scroll_y; /* Is this object completely off the y axis? */ if(((y + o->height) < 0) || (y >= height)) continue; /* Get this object's values */ lstat_buf = &o->lstat_buf; m = lstat_buf->st_mode; /* Get calculate this object's boundary * rectangle */ rect.x = x - 0; rect.y = y; rect.width = MIN(o->width, column[0]->position - 0); rect.height = o->height; /* Is this object selected? */ o_is_selected = FB_IS_OBJECT_SELECTED(fb, i); if(o_is_selected) { /* Mark the object as dragged as a move if * we initiate the drag and it is a move */ if(fb->drag_flags & FILE_BROWSER_DRAG_ACTIVE) o_is_dragged = (fb->drag_flags & FILE_BROWSER_DRAG_ACTIVE_MOVE) ? TRUE : FALSE; else o_is_dragged = FALSE; /* Get the selected GCs and draw the selected * background */ gdk_gc_set_foreground(gc, &style->bg[GTK_STATE_SELECTED]); if(o_is_dragged) { /* Object is dragged as a move, draw with * the transparent stipple GdkBitmap */ gdk_gc_set_stipple( gc, (GdkPixmap *)fb->transparent_stipple_bm ); gdk_gc_set_ts_origin(gc, 0, 0); gdk_gc_set_fill(gc, GDK_STIPPLED); } if(rect.width > 0) gdk_draw_rectangle( drawable, gc, TRUE, /* Fill */ rect.x, rect.y, rect.width, rect.height ); fg_color = &style->fg[GTK_STATE_SELECTED]; text_color = &style->text[GTK_STATE_SELECTED]; } else { fg_color = &style->fg[state]; text_color = &style->text[state]; o_is_dragged = FALSE; } /* Get the icon, its width, and draw it */ icon = FileBrowserGetIcon(fb, o->icon_num); if((icon != NULL) ? (icon->pixmap != NULL) : FALSE) { GdkBitmap *mask = icon->mask; GdkPixmap *pixmap = icon->pixmap; gdk_gc_set_clip_origin(gc, x, y); gdk_gc_set_clip_mask(gc, mask); gdk_draw_pixmap( drawable, gc, (GdkDrawable *)pixmap, 0, 0, /* Source position */ x, y, /* Target position */ icon->width, icon->height /* Size */ ); gdk_gc_set_clip_mask(gc, NULL); icon_width = icon->width; } else { icon_width = 0; } /* Draw the displayed name */ if(o->displayed_name != NULL) { const gchar *s = o->displayed_name; gdk_string_bounds(font, s, &b); gdk_gc_set_clip_rectangle(gc, &rect); gdk_gc_set_foreground(gc, text_color); gdk_draw_string( drawable, font, gc, (x + icon_width + FB_LIST_ICON_TEXT_SPACING) - b.lbearing, (y + ((o->height - font_height) / 2)) + font->ascent, s ); } else { /* No name, but we still need to have the font * extents for use for the subsequent drawing */ gdk_string_bounds(font, "X", &b); } /* Stop using the clip rectangle */ gdk_gc_set_clip_rectangle(gc, NULL); /* Use the non-selected colors */ fg_color = &style->fg[state]; text_color = &style->text[state]; /* Draw the size */ if(S_ISREG(m)) { gchar s[80]; g_snprintf( s, sizeof(s), "%ld", (gulong)lstat_buf->st_size ); gdk_string_bounds(font, s, &b); rect.x = column[0]->position; rect.width = column[1]->position - column[0]->position; if(rect.width > 0) { gdk_gc_set_clip_rectangle(gc, &rect); gdk_gc_set_foreground(gc, text_color); gdk_draw_string( drawable, font, gc, (rect.x + rect.width - b.width - 2) - b.lbearing, (y + (o->height / 2) - ((font->ascent + font->descent) / 2)) + font->ascent, s ); } } /* Draw the permissions */ #ifdef S_ISLNK if(!S_ISLNK(m)) #else if(TRUE) #endif { gchar s[80]; #if defined(S_IRUSR) && defined(S_IWUSR) && defined(S_IXUSR) g_snprintf( s, sizeof(s), "%c%c%c%c%c%c%c%c%c", (m & S_IRUSR) ? 'r' : '-', (m & S_IWUSR) ? 'w' : '-', (m & S_ISUID) ? 'S' : ((m & S_IXUSR) ? 'x' : '-'), (m & S_IRGRP) ? 'r' : '-', (m & S_IWGRP) ? 'w' : '-', (m & S_ISGID) ? 'G' : ((m & S_IXGRP) ? 'x' : '-'), (m & S_IROTH) ? 'r' : '-', (m & S_IWOTH) ? 'w' : '-', (m & S_ISVTX) ? 'T' : ((m & S_IXOTH) ? 'x' : '-') ); #else strcpy(s, "rwxrwxrwx"); #endif gdk_string_bounds(font, s, &b); rect.x = column[1]->position; rect.width = column[2]->position - column[1]->position; if(rect.width > 0) { gdk_gc_set_clip_rectangle(gc, &rect); gdk_gc_set_foreground(gc, text_color); gdk_draw_string( drawable, font, gc, (rect.x + 2) - b.lbearing, (y + (o->height / 2) - ((font->ascent + font->descent) / 2)) + font->ascent, s ); } } /* Draw the modified time */ if(S_ISREG(m)) { time_t mtime = lstat_buf->st_mtime; gchar *s = STRDUP((mtime > 0) ? ctime(&mtime) : "*undefined*"); gchar *s_shortened = strchr(s, '\n'); if(s_shortened == NULL) s_shortened = strchr(s, '\r'); if(s_shortened != NULL) *s_shortened = '\0'; gdk_string_bounds(font, s, &b); rect.x = column[2]->position; rect.width = column[3]->position - column[2]->position; if(rect.width > 0) { gdk_gc_set_clip_rectangle(gc, &rect); gdk_gc_set_foreground(gc, text_color); gdk_draw_string( drawable, font, gc, (rect.x + 2) - b.lbearing, (y + (o->height / 2) - ((font->ascent + font->descent) / 2)) + font->ascent, s ); } g_free(s); } /* Stop using the clip rectangle */ gdk_gc_set_clip_rectangle(gc, NULL); /* Draw the focus rectangle around this object? */ if((GTK_WIDGET_HAS_FOCUS(w) && GTK_WIDGET_SENSITIVE(w) ) || (fb->drag_flags & FILE_BROWSER_DRAG_OVER) ) { if(i == fb->focus_object) { rect.x = x - 0; rect.width = MIN(o->width, column[0]->position - 0); gdk_gc_set_function(gc, GDK_INVERT); gdk_draw_rectangle( drawable, gc, FALSE, /* Outline */ rect.x, rect.y, rect.width - 1, rect.height - 1 ); gdk_gc_set_function(gc, GDK_COPY); } } /* Restore the graphic context */ if(o_is_dragged) { gdk_gc_set_fill(gc, GDK_SOLID); } } } break; case FB_LIST_FORMAT_VERTICAL: for(glist = fb->objects_list, i = 0; glist != NULL; glist = g_list_next(glist), i++ ) { o = FILE_BROWSER_OBJECT(glist->data); if(o == NULL) continue; if((o->width <= 0) || (o->height <= 0)) continue; x = o->x; y = o->y - scroll_y; /* Is this object completely off the y axis? */ if(((y + o->height) < 0) || (y >= height)) continue; /* Is object selected? */ o_is_selected = FB_IS_OBJECT_SELECTED(fb, i); if(o_is_selected) { /* Mark the object as dragged as a move if we * initiate the drag and it is a move */ if(fb->drag_flags & FILE_BROWSER_DRAG_ACTIVE) o_is_dragged = (fb->drag_flags & FILE_BROWSER_DRAG_ACTIVE_MOVE) ? TRUE : FALSE; else o_is_dragged = FALSE; /* Get the selected GCs and draw the selected * background */ gdk_gc_set_foreground(gc, &style->bg[GTK_STATE_SELECTED]); if(o_is_dragged) { /* Object is dragged as a move, draw with the * transparent stipple GdkBitmap */ gdk_gc_set_stipple( gc, (GdkPixmap *)fb->transparent_stipple_bm ); gdk_gc_set_ts_origin(gc, 0, 0); gdk_gc_set_fill(gc, GDK_STIPPLED); } if(o->width > 0) gdk_draw_rectangle( drawable, gc, TRUE, /* Fill */ x, y, o->width, o->height ); fg_color = &style->fg[GTK_STATE_SELECTED]; text_color = &style->text[GTK_STATE_SELECTED]; } else { fg_color = &style->fg[state]; text_color = &style->text[state]; o_is_dragged = FALSE; } /* Get the icon, its size, and draw it */ icon = FileBrowserGetIcon(fb, o->icon_num); if((icon != NULL) ? (icon->pixmap != NULL) : FALSE) { GdkBitmap *mask = icon->mask; GdkPixmap *pixmap = icon->pixmap; gdk_gc_set_clip_origin(gc, x, y); gdk_gc_set_clip_mask(gc, mask); gdk_draw_pixmap( drawable, gc, (GdkDrawable *)pixmap, 0, 0, /* Source position */ x, y, /* Target position */ icon->width, icon->height /* Size */ ); gdk_gc_set_clip_mask(gc, NULL); icon_width = icon->width; } else { icon_width = 0; } /* Draw the name */ if(o->displayed_name != NULL) { const gchar *s = o->displayed_name; gdk_string_bounds(font, s, &b); gdk_gc_set_foreground(gc, text_color); gdk_draw_string( drawable, font, gc, (x + icon_width + FB_LIST_ICON_TEXT_SPACING) - b.lbearing, (y + ((o->height - font_height) / 2)) + font->ascent, s ); } /* Draw the focus rectangle around this object? */ if((GTK_WIDGET_HAS_FOCUS(w) && GTK_WIDGET_SENSITIVE(w) ) || (fb->drag_flags & FILE_BROWSER_DRAG_OVER) ) { if(i == fb->focus_object) { gdk_gc_set_function(gc, GDK_INVERT); gdk_draw_rectangle( drawable, gc, FALSE, /* Outline */ x, y, o->width - 1, o->height - 1 ); gdk_gc_set_function(gc, GDK_COPY); } } /* Restore the graphic context */ if(o_is_dragged) { gdk_gc_set_fill(gc, GDK_SOLID); } } break; case FB_LIST_FORMAT_STANDARD: /* Draw each object */ for(glist = fb->objects_list, i = 0; glist != NULL; glist = g_list_next(glist), i++ ) { o = FILE_BROWSER_OBJECT(glist->data); if(o == NULL) continue; if((o->width <= 0) || (o->height <= 0)) continue; x = o->x - scroll_x; y = o->y; /* Is this object completely off the x axis? */ if(((x + o->width) < 0) || (x >= width)) continue; /* Is this object selected? */ o_is_selected = FB_IS_OBJECT_SELECTED(fb, i); if(o_is_selected) { /* Mark the object as dragged as a move if we * initiate the drag and it is a move */ if(fb->drag_flags & FILE_BROWSER_DRAG_ACTIVE) o_is_dragged = (fb->drag_flags & FILE_BROWSER_DRAG_ACTIVE_MOVE) ? TRUE : FALSE; else o_is_dragged = FALSE; /* Get the selected GCs and draw the selected * background */ gdk_gc_set_foreground(gc, &style->bg[GTK_STATE_SELECTED]); if(o_is_dragged) { /* Object is dragged as a move, draw with the * transparent stipple GdkBitmap */ gdk_gc_set_stipple( gc, (GdkPixmap *)fb->transparent_stipple_bm ); gdk_gc_set_ts_origin(gc, 0, 0); gdk_gc_set_fill(gc, GDK_STIPPLED); } if(o->width > 0) gdk_draw_rectangle( drawable, gc, TRUE, /* Fill */ x, y, o->width, o->height ); fg_color = &style->fg[GTK_STATE_SELECTED]; text_color = &style->text[GTK_STATE_SELECTED]; } else { fg_color = &style->fg[state]; text_color = &style->text[state]; o_is_dragged = FALSE; } /* Get the icon, its width, and draw it */ icon = FileBrowserGetIcon(fb, o->icon_num); if((icon != NULL) ? (icon->pixmap != NULL) : FALSE) { GdkBitmap *mask = icon->mask; GdkPixmap *pixmap = icon->pixmap; gdk_gc_set_clip_origin(gc, x, y); gdk_gc_set_clip_mask(gc, mask); gdk_draw_pixmap( drawable, gc, (GdkDrawable *)pixmap, 0, 0, /* Source position */ x, y, /* Target position */ icon->width, icon->height /* Size */ ); gdk_gc_set_clip_mask(gc, NULL); icon_width = icon->width; } else { icon_width = 0; } /* Draw the name */ if(o->displayed_name != NULL) { const gchar *s = o->displayed_name; gdk_string_bounds(font, s, &b); gdk_gc_set_foreground(gc, text_color); gdk_draw_string( drawable, font, gc, (x + icon_width + FB_LIST_ICON_TEXT_SPACING) - b.lbearing, (y + ((o->height - font_height) / 2)) + font->ascent, s ); } /* Draw the focus rectangle around this object? */ if((GTK_WIDGET_HAS_FOCUS(w) && GTK_WIDGET_SENSITIVE(w) ) || (fb->drag_flags & FILE_BROWSER_DRAG_OVER) ) { if(i == fb->focus_object) { gdk_gc_set_function(gc, GDK_INVERT); gdk_draw_rectangle( drawable, gc, FALSE, /* Outline */ x, y, o->width - 1, o->height - 1 ); gdk_gc_set_function(gc, GDK_COPY); } } /* Restore the GCs */ if(o_is_dragged) { gdk_gc_set_fill(gc, GDK_SOLID); } } break; } /* Is there a drag over this list? */ if((GTK_WIDGET_HAS_FOCUS(w) && GTK_WIDGET_SENSITIVE(w) ) || (drag_flags & FILE_BROWSER_DRAG_OVER) ) gtk_paint_focus( style, (GdkWindow *)drawable, NULL, /* Entire area */ w, NULL, /* No detail */ 0, 0, width - 1, height - 1 ); /* Send drawable to window if drawable is not the window */ if(drawable != (GdkDrawable *)window) gdk_draw_pixmap( (GdkDrawable *)window, gc, drawable, 0, 0, /* Source position */ 0, 0, /* Target position */ width, height /* Size */ ); } /* * Queues a redraw of the File Selection Dialog's list. */ static void FileBrowserListQueueDraw(FileBrowser *fb) { if(fb == NULL) return; gtk_widget_queue_draw(fb->list_da); } /* * Creates a new list column. */ static FileBrowserColumn *FileBrowserColumnNew(void) { return(FILE_BROWSER_COLUMN(g_malloc0( sizeof(FileBrowserColumn) ))); } /* * Deletes the list column. */ static void FileBrowserColumnDelete(FileBrowserColumn *column) { if(column == NULL) return; g_free(column->label); g_free(column); } /* * Returns the list column at index i. */ static FileBrowserColumn *FileBrowserListGetColumn( FileBrowser *fb, const gint i ) { if((fb == NULL) || (i < 0)) return(NULL); return(FILE_BROWSER_COLUMN(g_list_nth_data( fb->columns_list, (guint)i) )); } /* * Appends a new list column. */ static FileBrowserColumn *FileBrowserListColumnAppend( FileBrowser *fb, const gchar *label, const gint width ) { gint ncolumns; FileBrowserColumn *column, *column_prev; if(fb == NULL) return(NULL); /* Create a new list column */ column = FileBrowserColumnNew(); if(column == NULL) return(NULL); fb->columns_list = g_list_append(fb->columns_list, column); ncolumns = g_list_length(fb->columns_list); column->label = STRDUP(label); column_prev = FileBrowserListGetColumn(fb, ncolumns - 2); if(column_prev != NULL) column->position = column_prev->position + width; else column->position = width; column->label_justify = GTK_JUSTIFY_LEFT; column->flags = GTK_SENSITIVE | GTK_CAN_FOCUS | GTK_CAN_DEFAULT; column->drag = FALSE; column->drag_position = column->position; column->drag_last_drawn_position = column->drag_position; return(column); } /* * Deletes all the list columns. */ static void FileBrowserListColumnsClear(FileBrowser *fb) { if(fb == NULL) return; if(fb->columns_list != NULL) { g_list_foreach( fb->columns_list, (GFunc)FileBrowserColumnDelete, NULL ); g_list_free(fb->columns_list); fb->columns_list = NULL; } } /* * Updates the File Name GtkEntry with the current selected * objects. */ static void FileBrowserEntrySetSelectedObjects(FileBrowser *fb) { gchar *s, *s2; GList *glist; GtkEntry *entry; const FileBrowserObject *o; if(fb == NULL) return; entry = GTK_ENTRY(fb->entry); s = NULL; for(glist = fb->selection; glist != NULL; glist = g_list_next(glist) ) { o = FileBrowserGetObject(fb, (gint)glist->data); if(o == NULL) continue; if(o->name == NULL) continue; if(s != NULL) s2 = g_strconcat( s, ",", o->name, NULL ); else s2 = STRDUP(o->name); g_free(s); s = s2; } gtk_entry_set_text(entry, (s != NULL) ? s : ""); g_free(s); gtk_entry_set_position(entry, -1); } /* * Sets the types list. * * The list and total specifies the new types list. */ static void FileBrowserTypesListSetTypes( FileBrowser *fb, fb_type_struct **list, const gint total ) { gint i, n; gchar *label; GtkWidget *pulist, *pulistbox; fb_type_struct *t, *t2; if(fb == NULL) return; pulistbox = fb->types_pulistbox; pulist = PUListBoxGetPUList(pulistbox); if(pulist == NULL) return; fb->freeze_count++; /* Delete all the types in the list */ PUListClear(pulist); /* Set the new types list */ for(i = 0; i < total; i++) { t = list[i]; if(t == NULL) continue; if(t->extensions == NULL) continue; t2 = FB_TYPE(g_malloc0(sizeof(fb_type_struct))); if(t2 == NULL) continue; t2->extensions = STRDUP(t->extensions); t2->name = STRDUP(t->name); if(STRISEMPTY(t2->name)) label = STRDUP(t2->extensions); else label = g_strdup_printf( "%s (%s)", t2->name, t2->extensions ); n = PUListAddItem( pulist, label ); g_free(label); if(n < 0) { FileBrowserTypeDelete(t2); continue; } PUListSetItemDataFull( pulist, n, t2, (GtkDestroyNotify)FileBrowserTypeDelete ); } /* Make sure that no more than 10 items are visible */ n = PUListGetTotalItems(pulist); PUListBoxSetLinesVisible( pulistbox, CLIP(n, 1, 10) ); /* Select the first item */ PUListBoxSelect(pulistbox, 0); FileBrowserTypesPUListChangedCB(pulistbox, 0, fb); fb->freeze_count--; } /* * Create new directory. */ static void FileBrowserNewDirectory(FileBrowser *fb) { gchar *name, *path; const gchar *cur_location; GtkWidget *toplevel; if(fb == NULL) return; #if defined(PROG_LANGUAGE_SPANISH) # define TITLE_MKDIR_FAILED "Cree Guía Nueva Fallada" #elif defined(PROG_LANGUAGE_FRENCH) # define TITLE_MKDIR_FAILED "Créer Le Nouvel Annuaire Echoué" #elif defined(PROG_LANGUAGE_GERMAN) # define TITLE_MKDIR_FAILED "Schaffen Sie Neues Versagten Verzeichnis" #elif defined(PROG_LANGUAGE_ITALIAN) # define TITLE_MKDIR_FAILED "Creare L'Elenco Nuovo Fallito" #elif defined(PROG_LANGUAGE_DUTCH) # define TITLE_MKDIR_FAILED "Creëer Nieuwe Gids Verzuimde" #elif defined(PROG_LANGUAGE_PORTUGUESE) # define TITLE_MKDIR_FAILED "Crie Novo Guia Fracassado" #elif defined(PROG_LANGUAGE_NORWEGIAN) # define TITLE_MKDIR_FAILED "Skap New Directory Failed" #else # define TITLE_MKDIR_FAILED "Create New Directory Failed" #endif #define MESSAGE_MKDIR_FAILED(_msg_) { \ CDialogSetTransientFor(toplevel); \ CDialogGetResponse( \ TITLE_MKDIR_FAILED, \ (_msg_), \ NULL, \ CDIALOG_ICON_WARNING, \ CDIALOG_BTNFLAG_OK, \ CDIALOG_BTNFLAG_OK \ ); \ CDialogSetTransientFor(NULL); \ } FileBrowserSetBusy(fb, TRUE); toplevel = fb->toplevel; cur_location = FileBrowserGetLocation(fb); if(cur_location == NULL) { FileBrowserSetBusy(fb, FALSE); return; } /* Generate new directory's name and path */ name = STRDUP(FB_NEW_DIRECTORY_NAME); path = FileBrowserJoinPaths( cur_location, name ); if(path == NULL) { g_free(name); FileBrowserSetBusy(fb, FALSE); return; } /* Create the new directory */ if(FileBrowserDirectoryCreate(path)) { /* Unable to create the new directory */ gboolean created = FALSE; gint i, error_code; /* Try creating the new directory under a different name */ for(i = 2; i < 100; i++) { /* Regenerate a different new name and full path */ g_free(name); g_free(path); name = g_strdup_printf( "%s%i", FB_NEW_DIRECTORY_NAME, i ); path = FileBrowserJoinPaths( cur_location, name ); if(path == NULL) break; /* Try to create the new directory with this new name */ if(!FileBrowserDirectoryCreate(path)) { created = TRUE; break; } /* Unable to create the directory */ error_code = (gint)errno; /* Unable to create the directory for reasons other than * it already existed? */ if(error_code != EEXIST) { gchar *s = g_strdup_printf( "%s.", g_strerror(error_code) ); MESSAGE_MKDIR_FAILED(s); g_free(s); break; } } /* Did not create a new directory? */ if(!created) { g_free(name); g_free(path); FileBrowserSetBusy(fb, FALSE); return; } } /* Update the list */ FileBrowserListUpdate(fb, NULL); /* Select the newly created directory */ if(path != NULL) { gint i; GList *glist; FileBrowserObject *o; for(glist = fb->objects_list, i = 0; glist != NULL; glist = g_list_next(glist), i++ ) { o = FILE_BROWSER_OBJECT(glist->data); if(o == NULL) continue; if(o->path == NULL) continue; if(!strcmp( (const char *)o->path, (const char *)path )) { /* Select this object */ fb->focus_object = i; fb->selection = g_list_append( fb->selection, (gpointer)i ); fb->selection_end = g_list_last(fb->selection); /* Scroll to this object */ FileBrowserListMoveToObject(fb, i, 0.5f); break; } } } FileBrowserListQueueDraw(fb); /* Update the File Name GtkEntry's value */ FileBrowserEntrySetSelectedObjects(fb); /* Notify about the created directory */ if(fb->object_created_cb != NULL) fb->object_created_cb( path, fb->object_created_data ); g_free(name); g_free(path); FileBrowserSetBusy(fb, FALSE); #undef MESSAGE_MKDIR_FAILED #undef TITLE_MKDIR_FAILED } /* * Move iteration. */ static gint FileBrowserMoveIteration( FileBrowser *fb, const gchar *old_path, const gchar *new_path ) { struct stat lstat_buf; /* If the target already exists then fail */ #if defined(_WIN32) if(!stat((const char *)new_path, &lstat_buf)) #else if(!lstat((const char *)new_path, &lstat_buf)) #endif { /* Report that the target already exists */ errno = EEXIST; return(-1); } /* Move */ if(rename( (const char *)old_path, (const char *)new_path )) { /* Move failed */ const gint error_code = (gint)errno; /* Moving across devices? */ #ifdef EXDEV if(error_code == EXDEV) #else if(FALSE) #endif { /* Need to copy the source to the target and then * delete the source */ const gint status = FileBrowserCopyIteration( fb, old_path, new_path ); if(status != 0) { /* Copy failed */ return(status); } else { /* Copy successful, delete the source */ return(FileBrowserDeleteIteration(fb, old_path)); } } else { /* Move failed, other error */ return(-1); } } else { /* Move successful */ return(0); } } /* * Copy iteration. */ static gint FileBrowserCopyIteration( FileBrowser *fb, const gchar *old_path, const gchar *new_path ) { mode_t m; struct stat lstat_buf; /* If the target already exists then fail */ #if defined(_WIN32) if(!stat((const char *)new_path, &lstat_buf)) #else if(!lstat((const char *)new_path, &lstat_buf)) #endif { /* Report that the target already exists */ errno = EEXIST; return(-1); } /* Get the source object's local statistics */ #if defined(_WIN32) if(stat((const char *)old_path, &lstat_buf)) #else if(lstat((const char *)old_path, &lstat_buf)) #endif return(-1); m = lstat_buf.st_mode; /* Directory */ #ifdef S_ISDIR if(S_ISDIR(m)) #else if(FALSE) #endif { #if !defined(_WIN32) struct utimbuf utime_buf; #endif gint status, nnames; gchar **names_list; /* Create the target directory */ if(FileBrowserDirectoryCreate(new_path)) return(-1); /* Set the target directory's properties */ (void)chmod( (const char *)new_path, m ); (void)chown( (const char *)new_path, lstat_buf.st_uid, lstat_buf.st_gid ); #if !defined(_WIN32) utime_buf.actime = lstat_buf.st_atime; utime_buf.modtime = lstat_buf.st_mtime; (void)utime( (const char *)new_path, &utime_buf ); #endif /* Get the list of objects in the source directory */ status = 0; names_list = FileBrowserListDirectory( old_path, &nnames ); if(names_list != NULL) { gint i; gchar *name, *old_child_path, *new_child_path; /* Copy all the objects in the source directory */ for(i = 0; i < nnames; i++) { name = names_list[i]; if(name == NULL) continue; if(!strcmp((const char *)name, ".") || !strcmp((const char *)name, "..") ) { g_free(name); continue; } /* Get the full path to this child object */ old_child_path = FileBrowserJoinPaths( old_path, name ); new_child_path = FileBrowserJoinPaths( new_path, name ); if((old_child_path != NULL) && (new_child_path != NULL)) { /* Copy this child object */ const gint status2 = FileBrowserCopyIteration( fb, old_child_path, new_child_path ); if(status2 != 0) { if(status == 0) status = status2; } } g_free(old_child_path); g_free(new_child_path); g_free(name); } g_free(names_list); } return(status); } /* Link */ #ifdef S_ISLNK else if(S_ISLNK(m)) #else else if(FALSE) #endif { const gint target_len = (gint)lstat_buf.st_size; gint bytes_read; gchar *target = (gchar *)g_malloc( (target_len + 1) * sizeof(gchar) ); if(target == NULL) return(-3); /* Get the source link's target */ bytes_read = (gint)readlink( old_path, target, (size_t)target_len ); if(bytes_read < 0) { const gint error_code = (gint)errno; g_free(target); errno = (gint)error_code; return(-1); } if(bytes_read < target_len) target[bytes_read] = '\0'; else target[target_len] = '\0'; /* Create the target link */ if(FileBrowserLinkCreate( new_path, /* Path */ target /* Target */ )) { const gint error_code = (gint)errno; g_free(target); errno = (gint)error_code; return(-1); } /* Set the target link's properties */ (void)lchown( (const char *)new_path, lstat_buf.st_uid, lstat_buf.st_gid ); /* Delete the target value */ g_free(target); return(0); } /* File */ #ifdef S_ISREG else if(S_ISREG(m)) #else else if(FALSE) #endif { const size_t file_size = lstat_buf.st_size; size_t io_buf_len, coppied_size; #if !defined(_WIN32) struct utimbuf utime_buf; #endif struct stat tar_stat_buf; FILE *src_fp, *tar_fp; gint tar_fd; guint8 *io_buf; /* Open the source file for reading */ src_fp = fopen((const char *)old_path, "rb"); if(src_fp == NULL) return(-1); /* Create/open the target file for writing */ tar_fp = fopen((const char *)new_path, "wb"); if(tar_fp == NULL) { const gint error_code = (gint)errno; (void)fclose(src_fp); errno = (int)error_code; return(-1); } tar_fd = (gint)fileno(tar_fp); /* Calculate the size of the I/O buffer */ #if defined(_WIN32) io_buf_len = 1024l; #else io_buf_len = (size_t)lstat_buf.st_blksize; #endif /* Get the target file's statistics */ if(!fstat((int)tar_fd, &tar_stat_buf)) { /* Need to use a smaller I/O buffer? */ #if !defined(_WIN32) if((size_t)tar_stat_buf.st_blksize < io_buf_len) io_buf_len = (size_t)tar_stat_buf.st_blksize; #endif } if(io_buf_len < 1l) io_buf_len = 1l; /* Set the target file's properties to match the source * file's properties */ (void)fchmod( (int)tar_fd, m ); (void)fchown( (int)tar_fd, lstat_buf.st_uid, lstat_buf.st_gid ); coppied_size = 0l; /* Allocate the I/O buffer */ io_buf = (guint8 *)g_malloc(io_buf_len * sizeof(guint8)); if(io_buf != NULL) { size_t units_read; /* Copy */ while(!feof(src_fp)) { /* Read */ units_read = fread( io_buf, sizeof(guint8), io_buf_len, src_fp ); /* Read error? */ if(ferror(src_fp)) { const gint error_code = (gint)errno; /* Attempt to write any remaining data that was read */ if((units_read > 0l) && (units_read < io_buf_len)) { const size_t units_written = fwrite( io_buf, sizeof(guint8), units_read, tar_fp ); if(units_written > 0l) coppied_size += units_written; } (void)fclose(src_fp); (void)fclose(tar_fp); errno = (int)error_code; return(-1); } /* No data read? */ else if(units_read <= 0l) { /* Check for the EOF marker at the top of this * while() loop */ continue; } /* More than the requested number of bytes read? */ if(units_read > io_buf_len) { (void)fclose(src_fp); (void)fclose(tar_fp); errno = EIO; return(-1); } /* Write */ if(fwrite( io_buf, sizeof(guint8), units_read, tar_fp ) != units_read) { const gint error_code = (gint)errno; (void)fclose(src_fp); (void)fclose(tar_fp); errno = (int)error_code; return(-1); } /* Count the number of bytes coppied */ coppied_size += units_read; } /* Delete the I/O buffer */ g_free(io_buf); } else { const gint error_code = (gint)errno; (void)fclose(src_fp); (void)fclose(tar_fp); errno = (int)error_code; return(-3); } /* Close the source file */ (void)fclose(src_fp); /* Close the target file */ if(fclose(tar_fp)) return(-1); #if !defined(_WIN32) /* Set the target file's time stamps */ utime_buf.actime = lstat_buf.st_atime; utime_buf.modtime = lstat_buf.st_mtime; (void)utime( (const char *)new_path, &utime_buf ); #endif /* Did not copy the entire file? */ if(coppied_size < file_size) { errno = EIO; return(-1); } return(0); } /* Other */ else { /* Not supported */ errno = EINVAL; return(-2); } } /* * Move. * * The paths_list specifies a GList of gchar * paths to the * objects being moved. * * The tar_path specifies the full path to the target location. * The target location must be a directory. */ static void FileBrowserMove( FileBrowser *fb, GList *paths_list, const gchar *tar_path ) { gint i, status, response, nobjs_moved; const gchar *full_path, *old_path; gchar *msg, *new_path; GList *glist; GtkWidget *toplevel; if((fb == NULL) || (paths_list == NULL) || STRISEMPTY(tar_path) || CDialogIsQuery() ) return; #define MESSAGE_MOVE_FAILED(s) { \ CDialogSetTransientFor(toplevel); \ CDialogGetResponse( \ "Move Failed", \ (s), NULL, \ CDIALOG_ICON_WARNING, \ CDIALOG_BTNFLAG_OK, CDIALOG_BTNFLAG_OK \ ); \ CDialogSetTransientFor(NULL); \ } FileBrowserSetBusy(fb, TRUE); toplevel = fb->toplevel; /* Format the confirmation message, if only one object is * selected then confirm with its name, otherwise confirm * the number of objects to be moved */ glist = paths_list; i = g_list_length(glist); if(i == 1) { full_path = (const gchar *)glist->data; msg = g_strdup_printf( "Move:\n\ \n\ %s\n\ \n\ To:\n\ \n\ %s", full_path, tar_path ); } else { msg = g_strdup_printf( "Move %i objects to:\n\ \n\ %s", i, tar_path ); } /* Confirm move */ CDialogSetTransientFor(toplevel); response = CDialogGetResponse( "Confirm Move", msg, NULL, CDIALOG_ICON_FILE_MOVE, CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_NO, CDIALOG_BTNFLAG_YES ); g_free(msg); CDialogSetTransientFor(NULL); if(response != CDIALOG_RESPONSE_YES) { FileBrowserSetBusy(fb, FALSE); return; } /* Move the objects */ for(glist = paths_list, nobjs_moved = 0; glist != NULL; glist = g_list_next(glist) ) { old_path = (const gchar *)glist->data; if(STRISEMPTY(old_path)) continue; new_path = FileBrowserJoinPaths( tar_path, g_basename(old_path) ); if(new_path == NULL) continue; /* Move */ status = FileBrowserMoveIteration( fb, old_path, new_path ); if(status != 0) { const gint error_code = (gint)errno; gchar *msg = g_strdup_printf( "%s.", g_strerror(error_code) ); MESSAGE_MOVE_FAILED(msg); g_free(msg); } else { nobjs_moved++; /* Notify about the object being moved */ if(fb->object_created_cb != NULL) fb->object_created_cb( new_path, fb->object_created_data ); if(fb->object_deleted_cb != NULL) fb->object_deleted_cb( old_path, fb->object_deleted_data ); } g_free(new_path); } /* Refresh the list after the move */ FileBrowserRefresh(fb); FileBrowserSetBusy(fb, FALSE); #undef MESSAGE_MOVE_FAILED } /* * Copy. */ static void FileBrowserCopy( FileBrowser *fb, GList *paths_list, const gchar *tar_path ) { gint i, status, response, nobjs_coppied; const gchar *full_path, *old_path; gchar *msg, *new_path; GList *glist; GtkWidget *toplevel; if((fb == NULL) || (paths_list == NULL) || STRISEMPTY(tar_path) || CDialogIsQuery() ) return; #define MESSAGE_COPY_FAILED(s) { \ CDialogSetTransientFor(toplevel); \ CDialogGetResponse( \ "Copy Failed", \ (s), NULL, \ CDIALOG_ICON_WARNING, \ CDIALOG_BTNFLAG_OK, CDIALOG_BTNFLAG_OK \ ); \ CDialogSetTransientFor(NULL); \ } FileBrowserSetBusy(fb, TRUE); toplevel = fb->toplevel; /* Format the confirmation message, if only one object is * selected then confirm with its name, otherwise confirm * the number of objects to be coppied */ glist = paths_list; i = g_list_length(glist); if(i == 1) { full_path = (const gchar *)glist->data; msg = g_strdup_printf( "Copy:\n\ \n\ %s\n\ \n\ To:\n\ \n\ %s", full_path, tar_path ); } else { msg = g_strdup_printf( "Copy %i objects to:\n\ \n\ %s", i, tar_path ); } /* Confirm copy */ CDialogSetTransientFor(toplevel); response = CDialogGetResponse( "Confirm Copy", msg, NULL, CDIALOG_ICON_FILE_COPY, CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_NO, CDIALOG_BTNFLAG_YES ); g_free(msg); CDialogSetTransientFor(NULL); if(response != CDIALOG_RESPONSE_YES) { FileBrowserSetBusy(fb, FALSE); return; } /* Copy the objects */ for(glist = paths_list, nobjs_coppied = 0; glist != NULL; glist = g_list_next(glist) ) { old_path = (const gchar *)glist->data; if(STRISEMPTY(old_path)) continue; new_path = FileBrowserJoinPaths( tar_path, g_basename(old_path) ); if(new_path == NULL) continue; /* Copy */ status = FileBrowserCopyIteration( fb, old_path, new_path ); if(status != 0) { const gint error_code = (gint)errno; gchar *msg = g_strdup_printf( "%s.", g_strerror(error_code) ); MESSAGE_COPY_FAILED(msg); g_free(msg); } else { nobjs_coppied++; /* Notify about the object being coppied */ if(fb->object_created_cb != NULL) fb->object_created_cb( new_path, fb->object_created_data ); } g_free(new_path); } /* Refresh the list after the copy */ FileBrowserRefresh(fb); FileBrowserSetBusy(fb, FALSE); #undef MESSAGE_COPY_FAILED } /* * Link. */ static void FileBrowserLink( FileBrowser *fb, const gchar *src_path, const gchar *tar_path ) { struct stat lstat_buf; gint response; gchar *msg, *new_path; GtkWidget *toplevel; if((fb == NULL) || STRISEMPTY(src_path) || STRISEMPTY(tar_path) || CDialogIsQuery() ) return; #define MESSAGE_LINK_FAILED(s) { \ CDialogSetTransientFor(toplevel); \ CDialogGetResponse( \ "Link Failed", \ (s), NULL, \ CDIALOG_ICON_WARNING, \ CDIALOG_BTNFLAG_OK, CDIALOG_BTNFLAG_OK \ ); \ CDialogSetTransientFor(NULL); \ } FileBrowserSetBusy(fb, TRUE); toplevel = fb->toplevel; /* Format the confirmation message */ msg = g_strdup_printf( "Link:\n\ \n\ %s\n\ \n\ To:\n\ \n\ %s", src_path, tar_path ); /* Confirm link */ CDialogSetTransientFor(toplevel); response = CDialogGetResponse( "Confirm Link", msg, NULL, CDIALOG_ICON_LINK, CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_NO, CDIALOG_BTNFLAG_YES ); g_free(msg); CDialogSetTransientFor(NULL); if(response != CDIALOG_RESPONSE_YES) { FileBrowserSetBusy(fb, FALSE); return; } /* Format the path to the new link */ new_path = FileBrowserJoinPaths( tar_path, g_basename(src_path) ); if(new_path == NULL) { FileBrowserSetBusy(fb, FALSE); return; } /* Already exists? */ #if defined(_WIN32) if(!stat((const char *)new_path, &lstat_buf)) #else if(!lstat((const char *)new_path, &lstat_buf)) #endif { const gint error_code = EEXIST; gchar *msg = g_strdup_printf( "%s.", g_strerror(error_code) ); MESSAGE_LINK_FAILED(msg); g_free(msg); g_free(new_path); FileBrowserSetBusy(fb, FALSE); return; } /* Create the link */ if(FileBrowserLinkCreate( new_path, /* Path */ src_path /* Target */ )) { const gint error_code = (gint)errno; gchar *msg = g_strdup_printf( "%s.", g_strerror(error_code) ); MESSAGE_LINK_FAILED(msg); g_free(msg); } else { /* Notify about the new link being created */ if(fb->object_created_cb != NULL) fb->object_created_cb( new_path, fb->object_created_data ); } /* Delete the path to the new link */ g_free(new_path); /* Refresh the list after the link */ FileBrowserRefresh(fb); FileBrowserSetBusy(fb, FALSE); #undef MESSAGE_LINK_FAILED } /* * Used by FileBrowserRenameQuery() as the apply callback for * the floating prompt. */ static void FileBrowserRenameFPromptCB(gpointer data, const gchar *value) { gint i; GList *glist; GtkWidget *toplevel; FileBrowserObject *o; FileBrowser *fb = FILE_BROWSER(data); if((fb == NULL) || STRISEMPTY(value)) return; #if defined(PROG_LANGUAGE_SPANISH) # define TITLE_RENAME_FAILED "Reagrupe Fallado" #elif defined(PROG_LANGUAGE_FRENCH) # define TITLE_RENAME_FAILED "Renommer Echoué" #elif defined(PROG_LANGUAGE_GERMAN) # define TITLE_RENAME_FAILED "Benennen Sie Versagt Um" #elif defined(PROG_LANGUAGE_ITALIAN) # define TITLE_RENAME_FAILED "Rinominare Fallito" #elif defined(PROG_LANGUAGE_DUTCH) # define TITLE_RENAME_FAILED "Herdoop Geverzuimenene" #elif defined(PROG_LANGUAGE_PORTUGUESE) # define TITLE_RENAME_FAILED "O Rename Fracassou" #elif defined(PROG_LANGUAGE_NORWEGIAN) # define TITLE_RENAME_FAILED "Ombenevn Failed" #else # define TITLE_RENAME_FAILED "Rename Failed" #endif #define MESSAGE_RENAME_FAILED(s) { \ CDialogSetTransientFor(toplevel); \ CDialogGetResponse( \ TITLE_RENAME_FAILED, \ (s), NULL, \ CDIALOG_ICON_WARNING, \ CDIALOG_BTNFLAG_OK, \ CDIALOG_BTNFLAG_OK \ ); \ CDialogSetTransientFor(NULL); \ } FileBrowserSetBusy(fb, TRUE); toplevel = fb->toplevel; /* No directory deliminators may exist in the new value */ if(strchr((const char *)value, G_DIR_SEPARATOR) != NULL) { gchar *msg = g_strdup_printf( #if defined(PROG_LANGUAGE_SPANISH) "El nombre nuevo \"%s\" contiene deliminators de\n\ guía de '%c' que no se permiten en un nombre de objeto." #elif defined(PROG_LANGUAGE_FRENCH) "Le nouveau \"%s\" de nom contient deliminators d'annuaire\n\ de '%c' qui ne sont pas permis dans un nom de l'objet." #elif defined(PROG_LANGUAGE_GERMAN) "Der neu \"%s\" enthält '%c' Verzeichnis deliminators, das\n\ im Namen eines Objekts nicht erlaubt wird." #elif defined(PROG_LANGUAGE_ITALIAN) "Lo \"%s\" di nome nuovo contiene il deliminators\n\ di elenco di '%c' che non sono lasciati in un nome dell'oggetto." #elif defined(PROG_LANGUAGE_DUTCH) "De nieuwe naam \"%s\" bevat '%c' gids deliminators,\n\ die welk in de naam van een voorwerp niet toegestaan is." #elif defined(PROG_LANGUAGE_PORTUGUESE) "O novo \"%s\" de nome contem deliminators de guia\n\ de '%c' que nao são permitidos num nome do objeto." #elif defined(PROG_LANGUAGE_NORWEGIAN) "Den nye navne \"%s\" inneholder '%c' katalog deliminators\n\ som ikke tillater i et objekts navn." #else "The new name \"%s\" contains '%c' directory\n\ deliminators which are not allowed in an object's name" #endif , value, G_DIR_SEPARATOR ); MESSAGE_RENAME_FAILED(msg); g_free(msg); FileBrowserSetBusy(fb, FALSE); return; } /* Get the last selected object */ glist = fb->selection_end; i = (glist != NULL) ? (gint)glist->data : -1; o = FileBrowserGetObject(fb, i); if((o != NULL) ? (o->name != NULL) && (o->path != NULL) : FALSE ) { struct stat lstat_buf; const gchar *cur_location = FileBrowserGetLocation(fb); gchar *old_full_path = STRDUP(o->path), *new_full_path = FileBrowserJoinPaths( cur_location, value ); /* New name and old name the same? */ if(!strcmp((const char *)o->name, (const char *)value)) { } /* Make sure that the new name does not refer to an * existing object */ #if defined(_WIN32) else if(!stat((const char *)new_full_path, &lstat_buf)) #else else if(!lstat((const char *)new_full_path, &lstat_buf)) #endif { gchar *msg = g_strdup_printf( "An object by the name of \"%s\" already exists.", value ); MESSAGE_RENAME_FAILED(msg); g_free(msg); } /* Rename */ else if(rename( (const char *)old_full_path, (const char *)new_full_path )) { const gint error_code = (gint)errno; gchar *msg = g_strdup_printf( "%s.", g_strerror(error_code) ); MESSAGE_RENAME_FAILED(msg); g_free(msg); } else { /* Rename successful */ /* Update the list */ FileBrowserListUpdate(fb, NULL); /* Reselect the object */ for(glist = fb->objects_list, i = 0; glist != NULL; glist = g_list_next(glist), i++ ) { o = FILE_BROWSER_OBJECT(glist->data); if(o == NULL) continue; if(o->path == NULL) continue; if(!strcmp( (const char *)o->path, (const char *)new_full_path )) { /* Select this object */ fb->focus_object = i; fb->selection = g_list_append( fb->selection, (gpointer)i ); fb->selection_end = g_list_last(fb->selection); /* Scroll to this object */ FileBrowserListMoveToObject(fb, i, 0.5f); break; } } FileBrowserListQueueDraw(fb); /* Update the File Name GtkEntry's value */ FileBrowserEntrySetSelectedObjects(fb); /* Notify about the rename */ if(fb->object_modified_cb != NULL) fb->object_modified_cb( old_full_path, new_full_path, fb->object_modified_data ); } g_free(old_full_path); g_free(new_full_path); } FileBrowserSetBusy(fb, FALSE); #undef MESSAGE_RENAME_FAILED #undef TITLE_RENAME_FAILED } /* * Rename. */ static void FileBrowserRenameQuery(FileBrowser *fb) { gint i, x, y, width, height; GList *glist; GtkWidget *toplevel; FileBrowserObject *o; if((fb == NULL) || FPromptIsQuery()) return; toplevel = fb->toplevel; /* Get the last selected object */ glist = fb->selection_end; i = (glist != NULL) ? (gint)glist->data : -1; o = FileBrowserGetObject(fb, i); if(o == NULL) return; /* Calculate the fprompt's position and size */ x = o->x; y = o->y; width = MAX(o->width + 4, 100); height = -1; switch(fb->list_format) { case FB_LIST_FORMAT_VERTICAL_DETAILS: case FB_LIST_FORMAT_VERTICAL: if(fb->list_vsb != NULL) { y -= GTK_ADJUSTMENT_GET_VALUE( GTK_RANGE(fb->list_vsb)->adjustment ); } break; case FB_LIST_FORMAT_STANDARD: if(fb->list_hsb != NULL) { x -= GTK_ADJUSTMENT_GET_VALUE( GTK_RANGE(fb->list_hsb)->adjustment ); } break; } /* Get root window relative coordinates of the object for * placement of the floating prompt. */ if(fb->list_da != NULL) { GtkWidget *w = fb->list_da; GdkWindow *window = w->window; if(window != NULL) { gint rx, ry; gdk_window_get_deskrelative_origin( window, &rx, &ry ); x += rx; y += ry; } } /* At this point x and y should now be the root window * relative position for the floating prompt * * Set the fprompt's position and size, then map it */ FPromptSetTransientFor(toplevel); FPromptSetPosition(x - 2, y - 2); FPromptMapQuery( NULL, /* No label */ o->name, NULL, /* No tooltip */ FPROMPT_MAP_TO_POSITION, /* Map code */ width, height, /* Width and height */ 0, /* No buttons */ fb, /* Data */ NULL, /* No browse callback */ FileBrowserRenameFPromptCB, NULL /* No cancel callback */ ); } /* * Change permissions fprompt callback. */ static void FileBrowserCHModFPromptCB(gpointer data, const gchar *value) { gint i; GList *glist; GtkWidget *toplevel; FileBrowserObject *o; FileBrowser *fb = FILE_BROWSER(data); if((fb == NULL) || STRISEMPTY(value)) return; #if defined(PROG_LANGUAGE_SPANISH) # define TITLE_CHMOD_FAILED "Cambie El Modo Fallado" #elif defined(PROG_LANGUAGE_FRENCH) # define TITLE_CHMOD_FAILED "Changer Le Mode A échoué" #elif defined(PROG_LANGUAGE_GERMAN) # define TITLE_CHMOD_FAILED "Ändern Sie Versagten Modus" #elif defined(PROG_LANGUAGE_ITALIAN) # define TITLE_CHMOD_FAILED "Cambiare Il Modo Fallito" #elif defined(PROG_LANGUAGE_DUTCH) # define TITLE_CHMOD_FAILED "Verandeer Modus Verzuimde" #elif defined(PROG_LANGUAGE_PORTUGUESE) # define TITLE_CHMOD_FAILED "Mude Modo Fracassado" #elif defined(PROG_LANGUAGE_NORWEGIAN) # define TITLE_CHMOD_FAILED "Forandr Sviktet Modus" #else # define TITLE_CHMOD_FAILED "Change Permissions Failed" #endif #define MESSAGE_CHMOD_FAILED(s) { \ CDialogSetTransientFor(toplevel); \ CDialogGetResponse( \ TITLE_CHMOD_FAILED, \ (s), NULL, \ CDIALOG_ICON_WARNING, \ CDIALOG_BTNFLAG_OK, CDIALOG_BTNFLAG_OK \ ); \ CDialogSetTransientFor(NULL); \ } toplevel = fb->toplevel; /* Get the last selected object */ glist = fb->selection_end; i = (glist != NULL) ? (gint)glist->data : -1; o = FileBrowserGetObject(fb, i); if((o != NULL) ? (o->path != NULL) : FALSE) { #if defined(S_IRUSR) && defined(S_IWUSR) && defined(S_IXUSR) mode_t m = 0; const gchar *s = value, *path = o->path; /* Begin parsing the value string to obtain the mode value */ while(ISBLANK(*s)) s++; /* User */ /* Read */ if(*s != '\0') { if(tolower(*s) == 'r') m |= S_IRUSR; s++; } /* Write */ if(*s != '\0') { if(tolower(*s) == 'w') m |= S_IWUSR; s++; } /* Execute */ if(*s != '\0') { if(tolower(*s) == 'x') m |= S_IXUSR; else if(tolower(*s) == 's') m |= S_ISUID; s++; } /* Group */ /* Read */ if(*s != '\0') { if(tolower(*s) == 'r') m |= S_IRGRP; s++; } /* Write */ if(*s != '\0') { if(tolower(*s) == 'w') m |= S_IWGRP; s++; } /* Execute */ if(*s != '\0') { if(tolower(*s) == 'x') m |= S_IXGRP; else if(tolower(*s) == 'g') m |= S_ISGID; s++; } /* Other */ /* Read */ if(*s != '\0') { if(tolower(*s) == 'r') m |= S_IROTH; s++; } /* Write */ if(*s != '\0') { if(tolower(*s) == 'w') m |= S_IWOTH; s++; } /* Execute */ if(*s != '\0') { if(tolower(*s) == 'x') m |= S_IXOTH; else if(tolower(*s) == 't') m |= S_ISVTX; s++; } /* CHMod */ if(chmod( (const char *)path, m )) { const gint error_code = (gint)errno; gchar *s = g_strdup_printf( "%s.", g_strerror(error_code) ); MESSAGE_CHMOD_FAILED(s); g_free(s); } else { /* Statistics */ #if defined(_WIN32) if(stat((const char *)path, &o->lstat_buf)) #else if(lstat((const char *)path, &o->lstat_buf)) #endif memset(&o->lstat_buf, 0x00, sizeof(struct stat)); /* Update values */ FileBrowserObjectUpdateValues(fb, o); } #endif } /* Need to redraw and update the value in the entry * since one of the selected object's names may have * changed */ FileBrowserListQueueDraw(fb); FileBrowserEntrySetSelectedObjects(fb); #undef MESSAGE_RENAME_FAILED #undef TITLE_RENAME_FAILED } /* * Change permissions. */ static void FileBrowserCHModQuery(FileBrowser *fb) { mode_t m; gint i, x, y, width, height; gchar *value; GList *glist; GtkWidget *toplevel; FileBrowserObject *o; FileBrowserColumn *column, *column2; if((fb == NULL) || FPromptIsQuery()) return; toplevel = fb->toplevel; /* Get the last selected object */ glist = fb->selection_end; i = (glist != NULL) ? (gint)glist->data : -1; o = FileBrowserGetObject(fb, i); if(o == NULL) return; m = o->lstat_buf.st_mode; #ifdef S_ISLNK /* Permission of links may not be changed */ if(S_ISLNK(m)) { CDialogSetTransientFor(toplevel); CDialogGetResponse( "Change Permissions Failed", "Unable to change the permissions of links", NULL, CDIALOG_ICON_WARNING, CDIALOG_BTNFLAG_OK, CDIALOG_BTNFLAG_OK ); CDialogSetTransientFor(NULL); return; } #endif #if !defined(S_IRUSR) || !defined(S_IWUSR) || !defined(S_IXUSR) /* Since permissions are not supported we cannot chmod */ if(TRUE) { CDialogSetTransientFor(toplevel); CDialogGetResponse( "Change Permissions Failed", "Changing the permissions of objects is not supported on this system.", NULL, CDIALOG_ICON_WARNING, CDIALOG_BTNFLAG_OK, CDIALOG_BTNFLAG_OK ); CDialogSetTransientFor(NULL); return; } #else /* Format current value */ value = g_strdup_printf( "%c%c%c%c%c%c%c%c%c", (m & S_IRUSR) ? 'r' : '-', (m & S_IWUSR) ? 'w' : '-', (m & S_ISUID) ? 'S' : ((m & S_IXUSR) ? 'x' : '-'), (m & S_IRGRP) ? 'r' : '-', (m & S_IWGRP) ? 'w' : '-', (m & S_ISGID) ? 'G' : ((m & S_IXGRP) ? 'x' : '-'), (m & S_IROTH) ? 'r' : '-', (m & S_IWOTH) ? 'w' : '-', (m & S_ISVTX) ? 'T' : ((m & S_IXOTH) ? 'x' : '-') ); #endif /* Calculate the fprompt's position and size */ x = o->x; y = o->y; width = MAX(o->width + 4, 80); height = -1; switch(fb->list_format) { case FB_LIST_FORMAT_VERTICAL_DETAILS: case FB_LIST_FORMAT_VERTICAL: if(fb->list_vsb != NULL) { y -= GTK_ADJUSTMENT_GET_VALUE( GTK_RANGE(fb->list_vsb)->adjustment ); } column = FileBrowserListGetColumn(fb, 1); if(column != NULL) x += column->position; column2 = FileBrowserListGetColumn(fb, 2); if((column != NULL) && (column2 != NULL)) width = MAX(column2->position - column->position + 4, 80); break; case FB_LIST_FORMAT_STANDARD: if(fb->list_hsb != NULL) { x += GTK_ADJUSTMENT_GET_VALUE( GTK_RANGE(fb->list_hsb)->adjustment ); } break; } /* Get root window relative coordinates of the object for * placement of the floating prompt */ if(fb->list_da != NULL) { GtkWidget *w = fb->list_da; GdkWindow *window = w->window; if(window != NULL) { gint rx, ry; gdk_window_get_deskrelative_origin( window, &rx, &ry ); x += rx; y += ry; } } /* At this point x and y should now be the root window * relative position for the floating prompt */ FPromptSetTransientFor(toplevel); FPromptSetPosition(x - 2, y - 2); FPromptMapQuery( NULL, /* No label */ value, NULL, /* No tooltip */ FPROMPT_MAP_TO_POSITION, /* Map code */ width, height, /* Width and height */ 0, /* No buttons */ fb, /* Data */ NULL, /* No browse callback */ FileBrowserCHModFPromptCB, NULL /* No cancel callback */ ); g_free(value); } /* * Deletes the object. * * The path specifies the full path to the object. If the object * is a directory then it and all of its child objects will be * deleted. */ static gint FileBrowserDeleteIteration( FileBrowser *fb, const gchar *path ) { /* Is the object a directory? */ if(FileBrowserIsLPathDirectory(path)) { gint status = 0, nnames; /* Get the list of objects in this directory */ gchar **names_list = FileBrowserListDirectory( path, &nnames ); if(names_list != NULL) { gint i; gchar *name, *child_path; /* Delete all the objects in this directory */ for(i = 0; i < nnames; i++) { name = names_list[i]; if(name == NULL) continue; if(!strcmp((const char *)name, ".") || !strcmp((const char *)name, "..") ) { g_free(name); continue; } /* Get the full path to this child object */ child_path = FileBrowserJoinPaths( path, name ); if(child_path != NULL) { /* Delete this child object */ const gint status2 = FileBrowserDeleteIteration( fb, child_path ); if(status2 != 0) { if(status == 0) status = status2; } g_free(child_path); } g_free(name); } g_free(names_list); } /* Failed to delete a child object? */ if(status != 0) return(status); /* Delete the directory */ if(rmdir((const char *)path)) return(-1); } else { /* Delete the object */ if(FileBrowserUnlink(path)) return(-1); } return(0); } /* * Deletes. */ static void FileBrowserDelete(FileBrowser *fb) { gint i, response, nobjs_deleted; gchar *msg; GList *glist; GtkWidget *toplevel; FileBrowserObject *o; if((fb == NULL) || CDialogIsQuery()) return; #if defined(PROG_LANGUAGE_SPANISH) # define TITLE_DELETE_FAILED "Borre Fallado" #elif defined(PROG_LANGUAGE_FRENCH) # define TITLE_DELETE_FAILED "Effacer Echoué" #elif defined(PROG_LANGUAGE_GERMAN) # define TITLE_DELETE_FAILED "Löschen Sie Versagt" #elif defined(PROG_LANGUAGE_ITALIAN) # define TITLE_DELETE_FAILED "Cancellare Fallito" #elif defined(PROG_LANGUAGE_DUTCH) # define TITLE_DELETE_FAILED "Schrap Geverzuimenene" #elif defined(PROG_LANGUAGE_PORTUGUESE) # define TITLE_DELETE_FAILED "Anule Fracassado" #elif defined(PROG_LANGUAGE_NORWEGIAN) # define TITLE_DELETE_FAILED "Stryk Failed" #else # define TITLE_DELETE_FAILED "Delete Failed" #endif #define MESSAGE_DELETE_FAILED(s) { \ CDialogSetTransientFor(toplevel); \ CDialogGetResponse( \ TITLE_DELETE_FAILED, \ (s), NULL, \ CDIALOG_ICON_WARNING, \ CDIALOG_BTNFLAG_OK, CDIALOG_BTNFLAG_OK \ ); \ CDialogSetTransientFor(NULL); \ } toplevel = fb->toplevel; /* Get the selections list */ glist = fb->selection; if(glist == NULL) return; FileBrowserSetBusy(fb, TRUE); /* Format confirmation message, if only one object is selected * then confirm with its name. Otherwise confirm the number of * objects to be deleted. */ i = g_list_length(glist); if(i == 1) { i = (gint)glist->data; o = FileBrowserGetObject(fb, i); if((o != NULL) ? (o->path != NULL) : FALSE) { if(FileBrowserIsLPathDirectory(o->path)) msg = g_strdup_printf( #if defined(PROG_LANGUAGE_SPANISH) "¿Usted está seguro que usted quiere borrar \"%s\"?" #elif defined(PROG_LANGUAGE_FRENCH) "Etes-vous sûr que vous voulez effacer \"%s\"?" #elif defined(PROG_LANGUAGE_GERMAN) "Sind sie sicher sie \"%s\" wollen löschen?" #elif defined(PROG_LANGUAGE_ITALIAN) "Lei sono sicuro che lei vuole cancellare \"%s\"?" #elif defined(PROG_LANGUAGE_DUTCH) "Bent u zeker u \"%s\" wil schrappen?" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Estão seguro quer anular \"%s\"?" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Er De sikker De stryker \"%s\"?" #else "Delete directory \"%s\"?\n\ \n\ Including any contents within that directory." #endif , o->name ); else msg = g_strdup_printf( #if defined(PROG_LANGUAGE_SPANISH) "¿Usted está seguro que usted quiere borrar \"%s\"?" #elif defined(PROG_LANGUAGE_FRENCH) "Etes-vous sûr que vous voulez effacer \"%s\"?" #elif defined(PROG_LANGUAGE_GERMAN) "Sind sie sicher sie \"%s\" wollen löschen?" #elif defined(PROG_LANGUAGE_ITALIAN) "Lei sono sicuro che lei vuole cancellare \"%s\"?" #elif defined(PROG_LANGUAGE_DUTCH) "Bent u zeker u \"%s\" wil schrappen?" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Estão seguro quer anular \"%s\"?" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Er De sikker De stryker \"%s\"?" #else "Delete \"%s\"?" #endif , o->name ); } else msg = STRDUP( "Delete (unnamed) object?" ); } else { msg = g_strdup_printf( #if defined(PROG_LANGUAGE_SPANISH) "¿Borra %i objetos escogidos?" #elif defined(PROG_LANGUAGE_FRENCH) "Efface %i objets choisis?" #elif defined(PROG_LANGUAGE_GERMAN) "Löschen Sie %i ausgewählte Objekte?" #elif defined(PROG_LANGUAGE_ITALIAN) "Cancella %i oggetti scelti?" #elif defined(PROG_LANGUAGE_DUTCH) "Schrap %i geselecteerde voorwerpen?" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Anule %i objetos selecionados?" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Stryk %i valgte ut objekt?" #else "Delete %i selected objects?" #endif , i ); } /* Confirm delete */ CDialogSetTransientFor(toplevel); response = CDialogGetResponseIconData( #if defined(PROG_LANGUAGE_SPANISH) "Confirme Borre" #elif defined(PROG_LANGUAGE_FRENCH) "Confirmer Effacer" #elif defined(PROG_LANGUAGE_GERMAN) "Bestätigen Sie Löscht" #elif defined(PROG_LANGUAGE_ITALIAN) "Confermare Cancellare" #elif defined(PROG_LANGUAGE_DUTCH) "Bevestiig Schrappet" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Confirme Anula" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Bekreft Delete" #else "Confirm Delete" #endif , msg, NULL, (guint8 **)icon_trash_32x32_xpm, CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_NO, CDIALOG_BTNFLAG_NO ); g_free(msg); CDialogSetTransientFor(NULL); if(response != CDIALOG_RESPONSE_YES) { FileBrowserSetBusy(fb, FALSE); return; } /* Delete the selected objects */ nobjs_deleted = 0; while(glist != NULL) { i = (gint)glist->data; o = FileBrowserGetObject(fb, i); if((o != NULL) ? (o->path != NULL) : FALSE) { const gchar *path = o->path; /* Delete this object */ if(!FileBrowserDeleteIteration(fb, path)) { nobjs_deleted++; /* Notify about the deleted object */ if(fb->object_deleted_cb != NULL) fb->object_deleted_cb( path, fb->object_deleted_data ); } } glist = g_list_next(glist); } /* Refresh the list after the delete */ FileBrowserRefresh(fb); FileBrowserSetBusy(fb, FALSE); #undef MESSAGE_DELETE_FAILED #undef TITLE_DELETE_FAILED } /* * Refresh. */ static void FileBrowserRefresh(FileBrowser *fb) { gint last_scroll_x = 0, last_scroll_y = 0; GtkWidget *w; if(fb == NULL) return; FileBrowserSetBusy(fb, TRUE); /* Record the last scroll positions */ w = fb->list_hsb; if(w != NULL) last_scroll_x = (gint)GTK_ADJUSTMENT_GET_VALUE( GTK_RANGE(w)->adjustment ); w = fb->list_vsb; if(w != NULL) last_scroll_y = (gint)GTK_ADJUSTMENT_GET_VALUE( GTK_RANGE(w)->adjustment ); /* Begin refreshing */ /* Reget the Locations List */ FileBrowserLocationsPUListUpdate(fb); /* Reget the Objects List */ FileBrowserListUpdate(fb, NULL); /* Update the file name GtkEntry */ FileBrowserEntrySetSelectedObjects(fb); /* Restore the GtkScrollBar positions */ w = fb->list_hsb; if(w != NULL) GTK_ADJUSTMENT_SET_VALUE( GTK_RANGE(w)->adjustment, (gfloat)last_scroll_x ); w = fb->list_vsb; if(w != NULL) GTK_ADJUSTMENT_SET_VALUE( GTK_RANGE(w)->adjustment, (gfloat)last_scroll_y ); FileBrowserSetBusy(fb, FALSE); } /* * File Name GtkEntry activate. * * If the File Name GtkEntry's value is a single value specifying * a directory then the current location will be set as that * directory. Otherwise, in the case of multiple objects (',' * deliminators in the value) or an object that does not lead to * a directory (including non-existant objects), FileBrowserOK() * will be called. */ static void FileBrowserEntryActivate(FileBrowser *fb) { const gchar *s; gchar *v; GtkWidget *w; if(fb == NULL) return; w = fb->entry; if(w == NULL) return; /* Get the file name GtkEntry's value */ s = gtk_entry_get_text(GTK_ENTRY(w)); if(STRISEMPTY(s)) return; v = STRDUP(s); /* Copy the value */ /* Filter specified? */ if((strchr((const char *)v, '*') != NULL) || (strchr((const char *)v, '?') != NULL)) { /* Reget the listing with the newly specified filter */ FileBrowserListUpdate(fb, v); } /* Multiple objects specified? */ else if(strchr((const char *)v, ',') != NULL) { /* Multiple objects selected, perform the OK operation */ FileBrowserOK(fb); } /* Current directory specified? */ else if(!strcmp((const char *)v, ".")) { /* Do nothing */ } /* Parent directory specified? */ else if(!strcmp((const char *)v, "..")) { /* Go to the parent location */ FileBrowserGoToParent(fb); } /* Home directory specified? */ else if(*v == '~') { /* Go to the home directory */ FileBrowserSetLocation(fb, v); } else { /* All else assume a single object was specified, it may * or may not actually exist */ struct stat stat_buf; gchar *full_path; /* Check if the object specified is specified as a full * path and if it is not then prefix the current location * to it */ if(g_path_is_absolute(v)) { full_path = STRDUP(v); } else { const gchar *cur_location = FileBrowserGetLocation(fb); full_path = FileBrowserJoinPaths( cur_location, v ); } /* Check if the object specified by full_path exists and if * it leads to a directory */ if(stat((const char *)full_path, &stat_buf)) { /* Unable to obtain the object's statistics, this * object may not exist (in the case of the user * wanting to save to a new file that does not exist * yet), so perform the OK operation */ FileBrowserOK(fb); } else { /* If the object specified by full_path is a directory * then go to that directory, otherwise call the OK * callback */ #ifdef S_ISDIR if(S_ISDIR(stat_buf.st_mode)) #else if(FALSE) #endif FileBrowserSetLocation(fb, full_path); else FileBrowserOK(fb); } g_free(full_path); } g_free(v); } /* * OK operation. * * Checks if any dialogs or prompts are still in query and * breaks out of those queries. * * Otherwise, the FB_RESPONSE_OK flag is set, the File Name * GtkEntry value is coppied to the selected paths list, unmaps * the File Selection Dialog and breaks out of any main levels. */ static void FileBrowserOK(FileBrowser *fb) { if(fb == NULL) return; if(FPromptIsQuery()) { FPromptBreakQuery(); } else if(PUListIsQuery(PUListBoxGetPUList(fb->location_pulistbox))) { PUListBreakQuery(PUListBoxGetPUList(fb->location_pulistbox)); } else if(PUListIsQuery(PUListBoxGetPUList(fb->types_pulistbox))) { PUListBreakQuery(PUListBoxGetPUList(fb->types_pulistbox)); } else if(CDialogIsQuery()) { } else { gint i; GtkWidget *w; /* Mark that user response was OK */ fb->flags |= FB_RESPONSE_OK; /* Delete the previous list of selected paths */ for(i = 0; i < fb->nselected_paths; i++) g_free(fb->selected_paths_list[i]); g_free(fb->selected_paths_list); fb->selected_paths_list = NULL; fb->nselected_paths = 0; /* Get value in entry and explode it to the list of * return paths */ w = fb->entry; if(w != NULL) { const gchar *s = gtk_entry_get_text(GTK_ENTRY(w)); if(!STRISEMPTY(s)) { /* Explode the entry's string value at all ',' * characters and then prefix the current location * to each exploded string */ gint i, n; const gchar *cur_location = FileBrowserGetLocation(fb); gchar *name, **names_list = g_strsplit(s, ",", -1); for(i = 0; names_list[i] != NULL; i++) { name = names_list[i]; /* Do not strip the spaces in the object's name since spaces may, * in fact, be part of the object's name */ /* name = g_strstrip(name); */ /* Append a new path to the selected_path * array, the new path will be either name * (if name is an absolute path) or the * cur_location prefixed to name (if name is * not an absolute path). */ n = MAX(fb->nselected_paths, 0); fb->nselected_paths = n + 1; fb->selected_paths_list = (gchar **)g_realloc( fb->selected_paths_list, fb->nselected_paths * sizeof(gchar *) ); if(fb->selected_paths_list != NULL) { /* If name is an absolute path then copy * it to the selected_path list * * Otherwise prefix the current location to * the name */ if(g_path_is_absolute(name)) { fb->selected_paths_list[n] = g_strdup(name); } else if(*name == '~') { /* Prefix the home directory to the new location path */ const gchar *home = fb->home_path, *child = name + 1; fb->selected_paths_list[n] = g_strconcat( (home != NULL) ? home : "", (*child != G_DIR_SEPARATOR) ? G_DIR_SEPARATOR_S : "", child, NULL ); } else { fb->selected_paths_list[n] = FileBrowserJoinPaths( cur_location, name ); } } else { fb->nselected_paths = 0; } g_free(name); } g_free(names_list); } else { /* File name entry is empty, so set the current * location as the return path. */ const gchar *cur_location = FileBrowserGetLocation(fb); gint n = MAX(fb->nselected_paths, 0); fb->nselected_paths = n + 1; fb->selected_paths_list = (gchar **)g_realloc( fb->selected_paths_list, fb->nselected_paths * sizeof(gchar *) ); if(fb->selected_paths_list == NULL) { fb->nselected_paths = 0; } else { fb->selected_paths_list[n] = g_strdup(cur_location); } } } /* Unmap */ FileBrowserUnmap(); /* Break out of our main loop */ if(fb->main_level > 0) { gtk_main_quit(); fb->main_level--; } } } /* * Cancel operation. * * Checks if any dialogs or prompts are still in query and * breaks out of those queries. * * Otherwise, the File Selection Dialog is unmapped and any main levels * will be broken out of. */ static void FileBrowserCancel(FileBrowser *fb) { if(fb == NULL) return; if(FPromptIsQuery()) { FPromptBreakQuery(); } else if(PUListIsQuery(PUListBoxGetPUList(fb->location_pulistbox))) { PUListBreakQuery(PUListBoxGetPUList(fb->location_pulistbox)); } else if(PUListIsQuery(PUListBoxGetPUList(fb->types_pulistbox))) { PUListBreakQuery(PUListBoxGetPUList(fb->types_pulistbox)); } else if(CDialogIsQuery()) { } else { /* Unmap */ FileBrowserUnmap(); /* Break out of our main loop */ if(fb->main_level > 0) { gtk_main_quit(); fb->main_level--; } } } /* * Locations popup list item destroy callback. */ static void FileBrowserLocationsPUListItemDestroyCB(gpointer data) { #if 0 gchar *full_path = (gchar *)data; if(full_path == NULL) return; #endif g_free(data); } /* * Toplevel GtkWindow "destroy" signal callback. */ static void FileBrowserDestroyCB(gpointer data) { gint i; FileBrowser *fb = FILE_BROWSER(data); if(fb == NULL) return; if(fb == file_browser) file_browser = NULL; fb->freeze_count++; /* Delete the current file type */ FileBrowserTypeDelete(fb->current_file_type); fb->current_file_type = NULL; /* Delete the selected paths */ for(i = 0; i < fb->nselected_paths; i++) g_free(fb->selected_paths_list[i]); g_free(fb->selected_paths_list); fb->selected_paths_list = NULL; fb->nselected_paths = 0; /* Delete the selected objects list */ fb->focus_object = -1; g_list_free(fb->selection); fb->selection = fb->selection_end = NULL; /* Delete all the objects */ if(fb->objects_list != NULL) { g_list_foreach( fb->objects_list, (GFunc)FileBrowserObjectDelete, NULL ); g_list_free(fb->objects_list); fb->objects_list = NULL; } fb->objects_per_row = 0; /* Delete the icons */ if(fb->icons_list != NULL) { g_list_foreach( fb->icons_list, (GFunc)FileBrowserIconDelete, NULL ); g_list_free(fb->icons_list); fb->icons_list = NULL; } /* Delete the list columns */ FileBrowserListColumnsClear(fb); /* Delete the device paths list */ if(fb->device_paths_list != NULL) { g_list_foreach( fb->device_paths_list, (GFunc)g_free, NULL ); g_list_free(fb->device_paths_list); fb->device_paths_list = NULL; } gtk_accel_group_unref(fb->accelgrp); GDK_GC_UNREF(fb->gc); GDK_PIXMAP_UNREF(fb->list_pm); GDK_CURSOR_DESTROY(fb->cur_busy); GDK_CURSOR_DESTROY(fb->cur_column_hresize); GDK_CURSOR_DESTROY(fb->cur_translate); GDK_BITMAP_UNREF(fb->transparent_stipple_bm); g_free(fb->home_path); g_free(fb->cur_location); fb->freeze_count--; g_free(fb); } /* * Toplevel GtkWindow "delete_event" signal callback. */ static gint FileBrowserDeleteEventCB( GtkWidget *widget, GdkEvent *event, gpointer data ) { FileBrowserCancel(FILE_BROWSER(data)); return(TRUE); } /* * Toplevel GtkWindow "realize" signal callback. */ static void FileBrowserRealizeCB(GtkWidget *widget, gpointer data) { GdkWindow *window; FileBrowser *fb = FILE_BROWSER(data); if((widget == NULL) || (fb == NULL)) return; window = widget->window; if(window != NULL) { GdkGeometry geo; gdk_window_set_decorations( window, GDK_DECOR_BORDER | GDK_DECOR_TITLE ); gdk_window_set_functions( window, GDK_FUNC_MOVE | GDK_FUNC_CLOSE ); geo.min_width = 100; geo.min_height = 70; geo.max_width = gdk_screen_width() - 10; geo.max_height = gdk_screen_height() - 10; geo.base_width = 0; geo.base_height = 0; geo.width_inc = 1; geo.height_inc = 1; geo.min_aspect = 1.3f; geo.max_aspect = 1.3f; gdk_window_set_geometry_hints( window, &geo, GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE | GDK_HINT_BASE_SIZE | GDK_HINT_RESIZE_INC ); } /* Do not mark the File Selection Dialog as realized here, * FileBrowserListRealizeCB() will mark it as realized */ } /* * Locations popup list "changed" signal callback. */ static void FileBrowserLocPUListChangedCB( GtkWidget *widget, const gint i, gpointer data ) { const gchar *location; GtkWidget *pulist; FileBrowser *fb = FILE_BROWSER(data); if((widget == NULL) || (fb == NULL)) return; if(fb->freeze_count > 0) return; pulist = PUListBoxGetPUList(widget); if(pulist == NULL) return; location = (const gchar *)PUListGetItemData( pulist, i ); if(location == NULL) return; fb->freeze_count++; FileBrowserSetLocation(fb, location); fb->freeze_count--; } /* * List Header GtkDrawingArea event signal callback. */ static gint FileBrowserListHeaderEventCB( GtkWidget *widget, GdkEvent *event, gpointer data ) { gint status = FALSE; GdkEventConfigure *configure; GdkEventExpose *expose; GdkEventFocus *focus; GdkEventButton *button; GdkEventMotion *motion; GdkEventCrossing *crossing; GtkWidget *w; FileBrowser *fb = FILE_BROWSER(data); if((event == NULL) || (fb == NULL)) return(status); w = fb->list_header_da; if(w == NULL) return(status); /* List widget must also be valid */ if(fb->list_da == NULL) return(status); #define DRAW_DRAG_LINE(x) \ { if(column != NULL) { \ gint line_p = (gint)(x); \ GdkWindow *window1, *window2; \ GdkGC *gc = fb->gc; \ \ window1 = w->window; \ window2 = fb->list_da->window; \ \ if((window1 != NULL) && (window2 != NULL) && (gc != NULL)) { \ gint width, height; \ \ gdk_gc_set_function(gc, GDK_INVERT); \ \ /* Draw drag line on window1 */ \ gdk_window_get_size(window1, &width, &height); \ gdk_draw_line(window1, gc, line_p, 0, line_p, height); \ \ /* Draw drag line on window2 */ \ gdk_window_get_size(window2, &width, &height); \ gdk_draw_line(window2, gc, line_p, 0, line_p, height); \ \ gdk_gc_set_function(gc, GDK_COPY); \ } \ } } switch((gint)event->type) { case GDK_CONFIGURE: configure = (GdkEventConfigure *)event; status = TRUE; break; case GDK_EXPOSE: expose = (GdkEventExpose *)event; FileBrowserListHeaderDraw(fb); status = TRUE; break; case GDK_FOCUS_CHANGE: focus = (GdkEventFocus *)event; if(focus->in && !GTK_WIDGET_HAS_FOCUS(w)) { GTK_WIDGET_SET_FLAGS(w, GTK_HAS_FOCUS); FileBrowserListHeaderQueueDraw(fb); } else if(!focus->in && GTK_WIDGET_HAS_FOCUS(w)) { GTK_WIDGET_UNSET_FLAGS(w, GTK_HAS_FOCUS); FileBrowserListHeaderQueueDraw(fb); } status = TRUE; break; case GDK_BUTTON_PRESS: button = (GdkEventButton *)event; if(!GTK_WIDGET_HAS_FOCUS(w)) gtk_widget_grab_focus(w); if(fb->columns_list != NULL) { gint cp, tolor = 3; gint p = (gint)button->x; GList *glist; FileBrowserColumn *column, *prev_column; /* Iterate through the list of columns, checking a * column that the pointer is over its resizing * area */ for(glist = g_list_last(fb->columns_list); glist != NULL; glist = g_list_previous(glist) ) { column = FILE_BROWSER_COLUMN(glist->data); if(column == NULL) continue; cp = column->position; if((p >= (cp - tolor)) && (p < (cp + (2 * tolor)))) { /* Update column drag values */ column->drag = TRUE; column->drag_position = cp; /* Draw drag line on list header and list */ DRAW_DRAG_LINE(cp); column->drag_last_drawn_position = column->drag_position; break; } } /* No column was clicked on its resizing area? */ if(glist == NULL) { /* Iterate through all columns to update the focus * and reset the drag state */ prev_column = NULL; for(glist = fb->columns_list; glist != NULL; glist = g_list_next(glist) ) { column = FILE_BROWSER_COLUMN(glist->data); if(column == NULL) continue; /* Get the left edge column position */ cp = (prev_column != NULL) ? prev_column->position : 0; /* Clicked on this column? */ if((p >= (cp + tolor)) && (p < (column->position - tolor))) { /* Invert the focus */ if(column->flags & GTK_HAS_FOCUS) column->flags &= ~GTK_HAS_FOCUS; else column->flags |= GTK_HAS_FOCUS; } else { column->flags &= ~GTK_HAS_FOCUS; } column->drag = FALSE; prev_column = column; } } FileBrowserListHeaderQueueDraw(fb); } status = TRUE; break; case GDK_BUTTON_RELEASE: button = (GdkEventButton *)event; if(fb->columns_list != NULL) { gint pos_shift_delta = 0; GList *glist; FileBrowserColumn *column; /* Iterate through the columns list, checking for one * that is being dragged and set that column's new * position */ for(glist = fb->columns_list; glist != NULL; glist = g_list_next(glist) ) { column = FILE_BROWSER_COLUMN(glist->data); if(column == NULL) continue; /* This column being dragged? */ if(column->drag) { column->drag = FALSE; pos_shift_delta = column->drag_position - column->position; column->position = column->drag_position; } else { column->position += pos_shift_delta; } } /* Redraw the list header and the list */ FileBrowserListHeaderQueueDraw(fb); FileBrowserListQueueDraw(fb); } status = TRUE; break; case GDK_MOTION_NOTIFY: motion = (GdkEventMotion *)event; if(fb->columns_list != NULL) { gint tolor = 3, left_column_pos = 0; gint p = (gint)motion->x; GList *glist; GdkCursor *cursor = NULL; FileBrowserColumn *column; /* Iterate through all columns and check if one is * being dragged (resized) in which case it will be * handled accordingly. Also if a pointer has * moved into the dragging area of a column then * the new cursor will be specified. */ for(glist = fb->columns_list; glist != NULL; glist = g_list_next(glist) ) { column = FILE_BROWSER_COLUMN(glist->data); if(column == NULL) continue; /* Is this column being dragged? */ if(column->drag) { column->drag_position = CLIP( p, left_column_pos, w->allocation.width ); /* Draw drag line on list header and list */ DRAW_DRAG_LINE(column->drag_last_drawn_position); DRAW_DRAG_LINE(column->drag_position); column->drag_last_drawn_position = column->drag_position; /* Update cursor just in case */ cursor = fb->cur_column_hresize; /* No need to handle other columns after * this one since it should be the only * being dragged. */ break; } else { /* Column not being dragged, check if the * pointer has moved into the dragging * area of this column. */ const gint cp = column->position; if((p >= (cp - tolor)) && (p < (cp + (2 * tolor)))) cursor = fb->cur_column_hresize; left_column_pos = column->position; } } gdk_window_set_cursor(w->window, cursor); gdk_flush(); } status = TRUE; break; case GDK_ENTER_NOTIFY: crossing = (GdkEventCrossing *)event; status = TRUE; break; case GDK_LEAVE_NOTIFY: crossing = (GdkEventCrossing *)event; gdk_window_set_cursor(w->window, NULL); gdk_flush(); status = TRUE; break; } #undef DRAW_DRAG_LINE return(status); } /* * List GtkDrawingArea event signal callback. */ static gint FileBrowserListEventCB( GtkWidget *widget, GdkEvent *event, gpointer data ) { gboolean key_press; gint status = FALSE; guint key_state; GdkEventConfigure *configure; GdkEventExpose *expose; GdkEventFocus *focus; GdkEventKey *key; GdkEventButton *button; GdkEventMotion *motion; GdkEventCrossing *crossing; FileBrowser *fb = FILE_BROWSER(data); if((widget == NULL) || (event == NULL) || (fb == NULL)) return(status); switch((gint)event->type) { case GDK_CONFIGURE: configure = (GdkEventConfigure *)event; /* Recreate the list's GdkPixmap buffer */ GDK_PIXMAP_UNREF(fb->list_pm); if((configure->width > 0) && (configure->height > 0)) fb->list_pm = gdk_pixmap_new( configure->window, configure->width, configure->height, -1 ); else fb->list_pm = NULL; /* Update the list's GtkAdjustments and object positions */ FileBrowserListUpdatePositions(fb); status = TRUE; break; case GDK_EXPOSE: expose = (GdkEventExpose *)event; FileBrowserListDraw(fb); status = TRUE; break; case GDK_FOCUS_CHANGE: focus = (GdkEventFocus *)event; if(focus->in && !GTK_WIDGET_HAS_FOCUS(widget)) { GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS); /* Set the focus object if it was unset and there * are objects in the list */ if((fb->focus_object < 0) && (fb->objects_list != NULL)) fb->focus_object = 0; FileBrowserListQueueDraw(fb); } else if(!focus->in && GTK_WIDGET_HAS_FOCUS(widget)) { GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS); FileBrowserListQueueDraw(fb); } status = TRUE; break; case GDK_KEY_PRESS: case GDK_KEY_RELEASE: key = (GdkEventKey *)event; key_press = (key->type == GDK_KEY_PRESS) ? TRUE : FALSE; key_state = key->state; #define STOP_KEY_SIGNAL_EMIT { \ if(widget != NULL) \ gtk_signal_emit_stop_by_name( \ GTK_OBJECT(widget), \ key_press ? \ "key_press_event" : "key_release_event" \ ); \ } /* First handle key event by list format */ switch(fb->list_format) { /* Vertical List Format */ case FB_LIST_FORMAT_VERTICAL_DETAILS: case FB_LIST_FORMAT_VERTICAL: switch(key->keyval) { case GDK_Up: /* Change Focus Up */ case GDK_KP_Up: case GDK_Left: case GDK_KP_Left: if(key_press) { /* Scroll only? */ if(key_state & GDK_CONTROL_MASK) { GtkAdjustment *adj = GTK_RANGE(fb->list_vsb)->adjustment; if(adj != NULL) GTK_ADJUSTMENT_SET_VALUE( adj, adj->value - adj->step_increment ); } else if(fb->focus_object > 0) { gint n = fb->focus_object, i = n - 1; fb->focus_object = i; /* Select? */ if((key_state & GDK_SHIFT_MASK) && (fb->objects_list != NULL) ) { if(!FB_IS_OBJECT_SELECTED(fb, n)) fb->selection = g_list_append( fb->selection, (gpointer)n ); if(!FB_IS_OBJECT_SELECTED(fb, i)) fb->selection = g_list_append( fb->selection, (gpointer)i ); fb->selection_end = g_list_last(fb->selection); FileBrowserEntrySetSelectedObjects(fb); } /* Scroll if focus object is not fully visible */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 0.5f); else FileBrowserListQueueDraw(fb); } } STOP_KEY_SIGNAL_EMIT status = TRUE; break; case GDK_Down: /* Change Focus Down */ case GDK_KP_Down: case GDK_Right: case GDK_KP_Right: if(key_press) { /* Scroll only? */ const gint nobjs = g_list_length(fb->objects_list); if(key_state & GDK_CONTROL_MASK) { GtkAdjustment *adj = GTK_RANGE(fb->list_vsb)->adjustment; if(adj != NULL) GTK_ADJUSTMENT_SET_VALUE( adj, adj->value + adj->step_increment ); } else if(fb->focus_object < (nobjs - 1)) { gint n = fb->focus_object, i = n + 1; fb->focus_object = i; /* Select? */ if((key_state & GDK_SHIFT_MASK) && (nobjs > 0) ) { if(!FB_IS_OBJECT_SELECTED(fb, n)) fb->selection = g_list_append( fb->selection, (gpointer)n ); if(!FB_IS_OBJECT_SELECTED(fb, i)) fb->selection = g_list_append( fb->selection, (gpointer)i ); fb->selection_end = g_list_last(fb->selection); FileBrowserEntrySetSelectedObjects(fb); } /* Scroll if focus object is not fully visible */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 0.5f); else FileBrowserListQueueDraw(fb); } } STOP_KEY_SIGNAL_EMIT status = TRUE; break; case GDK_Page_Up: /* Page Focus Up */ case GDK_KP_Page_Up: if(key_press) { /* Scroll only? */ const gint nobjs = g_list_length(fb->objects_list); if(key_state & GDK_CONTROL_MASK) { GtkAdjustment *adj = GTK_RANGE(fb->list_vsb)->adjustment; if(adj != NULL) GTK_ADJUSTMENT_SET_VALUE( adj, adj->value - adj->page_increment ); } else { FileBrowserObject *o = FILE_BROWSER_OBJECT( g_list_nth_data(fb->objects_list, 0) ); gint i, n = fb->focus_object, row_height = (o != NULL) ? (o->height + 1) : 0; GtkRange *range = (GtkRange *)fb->list_vsb; GtkAdjustment *adj = (range != NULL) ? range->adjustment : NULL; if((row_height > 0) && (adj != NULL)) { fb->focus_object = i = MAX( fb->focus_object - (gint)(adj->page_increment / row_height), 0 ); /* Select? */ if((key_state & GDK_SHIFT_MASK) && (nobjs > 0) ) { gint j; for(j = MIN(n, nobjs - 1); j >= i; j--) { if(!FB_IS_OBJECT_SELECTED(fb, j)) fb->selection = g_list_append( fb->selection, (gpointer)j ); } fb->selection_end = g_list_last(fb->selection); FileBrowserEntrySetSelectedObjects(fb); } /* Scroll if focus object is not fully visible */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 0.0f); else FileBrowserListQueueDraw(fb); } } } status = TRUE; break; case GDK_Page_Down: /* Page Focus Down */ case GDK_KP_Page_Down: if(key_press) { /* Scroll only? */ const gint nobjs = g_list_length(fb->objects_list); if(key_state & GDK_CONTROL_MASK) { GtkAdjustment *adj = GTK_RANGE(fb->list_vsb)->adjustment; if(adj != NULL) GTK_ADJUSTMENT_SET_VALUE( adj, adj->value + adj->page_increment ); } else { FileBrowserObject *o = FILE_BROWSER_OBJECT( g_list_nth_data(fb->objects_list, 0) ); gint i, n = fb->focus_object, row_height = (o != NULL) ? (o->height + 1) : 0; GtkRange *range = (GtkRange *)fb->list_vsb; GtkAdjustment *adj = (range != NULL) ? range->adjustment : NULL; if((row_height > 0) && (adj != NULL)) { fb->focus_object = i = MIN( fb->focus_object + (gint)(adj->page_increment / row_height), (nobjs - 1) ); /* Select? */ if((key_state & GDK_SHIFT_MASK) && (nobjs > 0) ) { gint j; for(j = MAX(n, 0); j <= i; j++) { if(!FB_IS_OBJECT_SELECTED(fb, j)) fb->selection = g_list_append( fb->selection, (gpointer)j ); } fb->selection_end = g_list_last(fb->selection); FileBrowserEntrySetSelectedObjects(fb); } /* Scroll if focus object is not fully visible */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 1.0f); else FileBrowserListQueueDraw(fb); } } } status = TRUE; break; case GDK_Home: /* Focus Beginning */ case GDK_KP_Home: if(key_press) { /* Scroll only? */ if(key_state & GDK_CONTROL_MASK) { GtkAdjustment *adj = GTK_RANGE(fb->list_vsb)->adjustment; if(adj != NULL) GTK_ADJUSTMENT_SET_VALUE( adj, adj->lower ); } else if(fb->focus_object > 0) { const gint nobjs = g_list_length(fb->objects_list); gint i = 0, n = fb->focus_object; fb->focus_object = i; /* Select? */ if((key_state & GDK_SHIFT_MASK) && (nobjs > 0) ) { gint j; for(j = MIN(n, (nobjs - 1)); j >= i; j--) { if(!FB_IS_OBJECT_SELECTED(fb, j)) fb->selection = g_list_append( fb->selection, (gpointer)j ); } fb->selection_end = g_list_last(fb->selection); FileBrowserEntrySetSelectedObjects(fb); } /* Scroll if focus object is not fully visible */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 0.0f); else FileBrowserListQueueDraw(fb); } } status = TRUE; break; case GDK_End: /* Focus End */ case GDK_KP_End: if(key_press) { /* Scroll only? */ const gint nobjs = g_list_length(fb->objects_list); if(key_state & GDK_CONTROL_MASK) { GtkAdjustment *adj = GTK_RANGE(fb->list_vsb)->adjustment; if(adj != NULL) GTK_ADJUSTMENT_SET_VALUE( adj, adj->upper - adj->page_size ); } else if(fb->focus_object < (nobjs - 1)) { gint i = MAX((nobjs - 1), 0), n = fb->focus_object; fb->focus_object = i; /* Select? */ if((key_state & GDK_SHIFT_MASK) && (nobjs > 0) ) { gint j; for(j = MAX(n, 0); j <= i; j++) { if(!FB_IS_OBJECT_SELECTED(fb, j)) fb->selection = g_list_append( fb->selection, (gpointer)j ); } fb->selection_end = g_list_last(fb->selection); FileBrowserEntrySetSelectedObjects(fb); } /* Scroll if focus object is not fully visible */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 1.0f); else FileBrowserListQueueDraw(fb); } } status = TRUE; break; } break; /* Horizontal List Format */ case FB_LIST_FORMAT_STANDARD: switch(key->keyval) { case GDK_Up: /* Focus Up */ case GDK_KP_Up: if(key_press) { /* Scroll only? */ if(key_state & GDK_CONTROL_MASK) { /* No vertical scrolling */ } else if(fb->focus_object > 0) { const gint nobjs = g_list_length(fb->objects_list), n = fb->focus_object, i = n - 1; fb->focus_object = i; /* Select? */ if((key_state & GDK_SHIFT_MASK) && (nobjs > 0) ) { if(!FB_IS_OBJECT_SELECTED(fb, n)) fb->selection = g_list_append( fb->selection, (gpointer)n ); if(!FB_IS_OBJECT_SELECTED(fb, i)) fb->selection = g_list_append( fb->selection, (gpointer)i ); fb->selection_end = g_list_last(fb->selection); FileBrowserEntrySetSelectedObjects(fb); } /* Scroll if focus object is not fully visible */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 0.5f); else FileBrowserListQueueDraw(fb); } } STOP_KEY_SIGNAL_EMIT status = TRUE; break; case GDK_Down: /* Focus Down */ case GDK_KP_Down: if(key_press) { /* Scroll only? */ const gint nobjs = g_list_length(fb->objects_list); if(key_state & GDK_CONTROL_MASK) { /* No vertical scrolling */ } else if(fb->focus_object < (nobjs - 1)) { const gint n = MAX(fb->focus_object, 0), i = n + 1; fb->focus_object = i; /* Select? */ if((key_state & GDK_SHIFT_MASK) && (nobjs > 0) ) { if(!FB_IS_OBJECT_SELECTED(fb, n)) fb->selection = g_list_append( fb->selection, (gpointer)n ); if(!FB_IS_OBJECT_SELECTED(fb, i)) fb->selection = g_list_append( fb->selection, (gpointer)i ); fb->selection_end = g_list_last(fb->selection); FileBrowserEntrySetSelectedObjects(fb); } /* Scroll if focus object is not fully visible */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 0.5f); else FileBrowserListQueueDraw(fb); } } STOP_KEY_SIGNAL_EMIT status = TRUE; break; case GDK_Left: /* Focus Left */ case GDK_KP_Left: if(key_press) { /* Scroll only? */ if(key_state & GDK_CONTROL_MASK) { GtkAdjustment *adj = GTK_RANGE(fb->list_hsb)->adjustment; if(adj != NULL) GTK_ADJUSTMENT_SET_VALUE( adj, adj->value - adj->step_increment ); } else if(fb->focus_object > 0) { const gint nobjs = g_list_length(fb->objects_list), n = fb->focus_object, i = MAX( (fb->focus_object - fb->objects_per_row), 0 ); fb->focus_object = i; /* Select? */ if((key_state & GDK_SHIFT_MASK) && (nobjs > 0) ) { gint j; for(j = MIN(n, (nobjs - 1)); j >= i; j--) { if(!FB_IS_OBJECT_SELECTED(fb, j)) fb->selection = g_list_append( fb->selection, (gpointer)j ); } fb->selection_end = g_list_last(fb->selection); FileBrowserEntrySetSelectedObjects(fb); } /* Scroll if focus object is not fully visible */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 0.5f); else FileBrowserListQueueDraw(fb); } } STOP_KEY_SIGNAL_EMIT status = TRUE; break; case GDK_Right: /* Focus Right */ case GDK_KP_Right: if(key_press) { /* Scroll only? */ const gint nobjs = g_list_length(fb->objects_list); if(key_state & GDK_CONTROL_MASK) { GtkAdjustment *adj = GTK_RANGE(fb->list_hsb)->adjustment; if(adj != NULL) GTK_ADJUSTMENT_SET_VALUE( adj, adj->value + adj->step_increment ); } else if(fb->focus_object < (nobjs - 1)) { const gint n = MAX(fb->focus_object, 0), i = MIN( (n + fb->objects_per_row), (nobjs - 1) ); fb->focus_object = i; /* Select? */ if((key_state & GDK_SHIFT_MASK) && (nobjs > 0) ) { gint j; for(j = n; j <= i; j++) { if(!FB_IS_OBJECT_SELECTED(fb, j)) fb->selection = g_list_append( fb->selection, (gpointer)j ); } fb->selection_end = g_list_last(fb->selection); FileBrowserEntrySetSelectedObjects(fb); } /* Scroll if focus object is not fully visible */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 0.5f); else FileBrowserListQueueDraw(fb); } } STOP_KEY_SIGNAL_EMIT status = TRUE; break; case GDK_Page_Up: /* Page Focus Left */ case GDK_KP_Page_Up: if(key_press) { /* Scroll only? */ if(key_state & GDK_CONTROL_MASK) { GtkAdjustment *adj = GTK_RANGE(fb->list_hsb)->adjustment; if(adj != NULL) GTK_ADJUSTMENT_SET_VALUE( adj, adj->value - adj->page_increment ); } else if(fb->focus_object > 0) { const gint nobjs = g_list_length(fb->objects_list), n = fb->focus_object, i = MAX( (fb->focus_object - fb->objects_per_row), 0 ); fb->focus_object = i; /* Select? */ if((key_state & GDK_SHIFT_MASK) && (nobjs > 0) ) { gint j; for(j = MIN(n, (nobjs - 1)); j >= i; j--) { if(!FB_IS_OBJECT_SELECTED(fb, j)) fb->selection = g_list_append( fb->selection, (gpointer)j ); } fb->selection_end = g_list_last(fb->selection); FileBrowserEntrySetSelectedObjects(fb); } /* Scroll if focus object is not fully visible */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 0.0f); else FileBrowserListQueueDraw(fb); } } status = TRUE; break; case GDK_Page_Down: /* Page Focus Right */ case GDK_KP_Page_Down: if(key_press) { /* Scroll only? */ const gint nobjs = g_list_length(fb->objects_list); if(key_state & GDK_CONTROL_MASK) { GtkAdjustment *adj = GTK_RANGE(fb->list_hsb)->adjustment; if(adj != NULL) GTK_ADJUSTMENT_SET_VALUE( adj, adj->value + adj->page_increment ); } else if(fb->focus_object < (nobjs - 1)) { const gint n = fb->focus_object, i = MIN( (fb->focus_object + fb->objects_per_row), (nobjs - 1) ); fb->focus_object = i; /* Select? */ if((key_state & GDK_SHIFT_MASK) && (nobjs > 0) ) { gint j; for(j = MAX(n, 0); j <= i; j++) { if(!FB_IS_OBJECT_SELECTED(fb, j)) fb->selection = g_list_append( fb->selection, (gpointer)j ); } fb->selection_end = g_list_last(fb->selection); FileBrowserEntrySetSelectedObjects(fb); } /* Scroll if focus object is not fully visible */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 1.0f); else FileBrowserListQueueDraw(fb); } } status = TRUE; break; case GDK_Home: /* Focus Beginning */ case GDK_KP_Home: if(key_press) { /* Scroll only? */ if(key_state & GDK_CONTROL_MASK) { GtkAdjustment *adj = GTK_RANGE(fb->list_hsb)->adjustment; if(adj != NULL) GTK_ADJUSTMENT_SET_VALUE( adj, adj->lower ); } else if(fb->focus_object > 0) { const gint nobjs = g_list_length(fb->objects_list), i = 0, n = fb->focus_object; fb->focus_object = i; /* Select? */ if((key_state & GDK_SHIFT_MASK) && (nobjs > 0) ) { gint j; for(j = MIN(n, (nobjs - 1)); j >= i; j--) { if(!FB_IS_OBJECT_SELECTED(fb, j)) fb->selection = g_list_append( fb->selection, (gpointer)j ); } fb->selection_end = g_list_last(fb->selection); FileBrowserEntrySetSelectedObjects(fb); } /* Scroll if focus object is not fully visible */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 0.0f); else FileBrowserListQueueDraw(fb); } } status = TRUE; break; case GDK_End: /* Focus End */ case GDK_KP_End: if(key_press) { /* Scroll only? */ const gint nobjs = g_list_length(fb->objects_list); if(key_state & GDK_CONTROL_MASK) { GtkAdjustment *adj = GTK_RANGE(fb->list_hsb)->adjustment; if(adj != NULL) GTK_ADJUSTMENT_SET_VALUE( adj, adj->upper - adj->page_size ); } else if(fb->focus_object < (nobjs - 1)) { const gint i = MAX(nobjs - 1, 0), n = fb->focus_object; fb->focus_object = i; /* Select? */ if((key_state & GDK_SHIFT_MASK) && (nobjs > 0) ) { gint j; for(j = MAX(n, 0); j <= i; j++) { if(!FB_IS_OBJECT_SELECTED(fb, j)) fb->selection = g_list_append( fb->selection, (gpointer)j ); } fb->selection_end = g_list_last(fb->selection); FileBrowserEntrySetSelectedObjects(fb); } /* Scroll if focus object is not fully visible */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 1.0f); else FileBrowserListQueueDraw(fb); } } status = TRUE; break; } break; } /* If the key event was not handled above then we will * handle it generally independent of the list format * here */ if(!status) { switch(key->keyval) { case GDK_space: /* Toggle Select */ if(key_press) { const gint i = fb->focus_object; if(FB_IS_OBJECT_SELECTED(fb, i)) fb->selection = g_list_remove( fb->selection, (gpointer)i ); else fb->selection = g_list_append( fb->selection, (gpointer)i ); fb->selection_end = g_list_last(fb->selection); FileBrowserListQueueDraw(fb); FileBrowserEntrySetSelectedObjects(fb); } status = TRUE; break; case GDK_BackSpace: /* Go to parent */ if(key_press) FileBrowserGoToParent(fb); status = TRUE; break; case GDK_Delete: /* Delete */ if(key_press) FileBrowserDelete(fb); status = TRUE; break; default: /* Seek to the object */ if(key_press && isalnum((int)key->keyval)) { /* Seek to the object who's name starts with * the first character matching the key that * was pressed */ const gchar c = key->keyval; /* Backwards? */ if(key_state & GDK_MOD1_MASK) { gint i; GList *glist; FileBrowserObject *o; for(glist = g_list_last(fb->objects_list), i = g_list_length(fb->objects_list) - 1; glist != NULL; glist = g_list_previous(glist), i-- ) { o = FILE_BROWSER_OBJECT(glist->data); if(o == NULL) continue; if(o->name == NULL) continue; if(c == *o->name) { FileBrowserListMoveToObject(fb, i, 0.5f); break; } } } else { gint i; GList *glist; FileBrowserObject *o; for(glist = fb->objects_list, i = 0; glist != NULL; glist = g_list_next(glist), i++ ) { o = FILE_BROWSER_OBJECT(glist->data); if(o == NULL) continue; if(o->name == NULL) continue; if(c == *o->name) { FileBrowserListMoveToObject(fb, i, 0.5f); break; } } } status = TRUE; } break; } } #undef STOP_KEY_SIGNAL_EMIT break; case GDK_BUTTON_PRESS: button = (GdkEventButton *)event; /* Unmap the floating prompt as needed */ if(FPromptIsQuery()) FPromptBreakQuery(); /* Grab the focus as needed */ if(!GTK_WIDGET_HAS_FOCUS(widget)) gtk_widget_grab_focus(widget); /* Handle by the button number */ switch(button->button) { case GDK_BUTTON1: /* Select */ /* Multiple select? */ if(button->state & GDK_CONTROL_MASK) { const gint i = FileBrowserListSelectCoordinates( fb, (gint)button->x, (gint)button->y ); if(i > -1) { /* If this object is already selected then * unselect it, otherwise select it */ fb->last_single_select_object = -1; fb->focus_object = i; if(FB_IS_OBJECT_SELECTED(fb, i)) { fb->selection = g_list_remove( fb->selection, (gpointer)i ); fb->selection_end = g_list_last(fb->selection); } else { fb->selection = g_list_append( fb->selection, (gpointer)i ); fb->selection_end = g_list_last(fb->selection); /* Update the DND drag icon */ FileBrowserListSetDNDDragIcon(fb); } /* If this object is not fully visibile then * scroll to it */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 0.5f); } } /* Series select? */ else if(button->state & GDK_SHIFT_MASK) { const gint i = FileBrowserListSelectCoordinates( fb, (gint)button->x, (gint)button->y ); if(i > -1) { /* Select all objects between the last selected * object and this one */ fb->last_single_select_object = -1; fb->focus_object = i; if(fb->selection_end != NULL) { const gint n = (gint)fb->selection_end->data; if(i > n) { gint j = n + 1; while(j <= i) { if(!FB_IS_OBJECT_SELECTED(fb, j)) fb->selection = g_list_append( fb->selection, (gpointer)j ); j++; } fb->selection_end = g_list_last(fb->selection); } else if(i < n) { gint j = n - 1; while(j >= i) { if(!FB_IS_OBJECT_SELECTED(fb, j)) fb->selection = g_list_append( fb->selection, (gpointer)j ); j--; } fb->selection_end = g_list_last(fb->selection); } } else { /* No previously selected object, so just * select this one */ fb->selection = g_list_append( fb->selection, (gpointer)i ); fb->selection_end = g_list_last(fb->selection); } /* Update the DND drag icon */ FileBrowserListSetDNDDragIcon(fb); /* If this object is not fully visibile then * scroll it it */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 0.5f); } } /* Single select */ else { const gint i = FileBrowserListSelectCoordinates( fb, (gint)button->x, (gint)button->y ); if(i > -1) { /* If the last single selected object is * the same as this object then update * last_single_select_object */ if((g_list_length(fb->selection) == 1) ? ((gint)fb->selection->data == i) : FALSE ) fb->last_single_select_object = i; else fb->last_single_select_object = -1; /* Unselect all */ fb->focus_object = -1; g_list_free(fb->selection); fb->selection = fb->selection_end = NULL; /* Select this object */ fb->focus_object = i; fb->selection = g_list_append( fb->selection, (gpointer)i ); fb->selection_end = g_list_last(fb->selection); /* Update the DND drag icon */ FileBrowserListSetDNDDragIcon(fb); /* If this object is not fully visibile then * scroll to it */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 0.5f); } else { /* Unselect all */ fb->focus_object = -1; g_list_free(fb->selection); fb->selection = fb->selection_end = NULL; } } FileBrowserListQueueDraw(fb); /* Update the File Name GtkEntry's value with the * new list of selected objects */ FileBrowserEntrySetSelectedObjects(fb); status = TRUE; break; case GDK_BUTTON2: /* Start scrolling */ gdk_pointer_grab( button->window, FALSE, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK, NULL, fb->cur_translate, button->time ); status = TRUE; break; case GDK_BUTTON3: /* Right Click GtkMenu */ if(fb->list_menu != NULL) { GtkMenu *menu = GTK_MENU(fb->list_menu); const gint i = FileBrowserListSelectCoordinates( fb, (gint)button->x, (gint)button->y ); /* If ctrl or shift modifier keys are held then do * not modify selection before mapping of menu */ if((button->state & GDK_CONTROL_MASK) || (button->state & GDK_SHIFT_MASK) ) { /* Do not modify selection, just update focus * object */ fb->focus_object = i; } else if(i > -1) { /* Unselect all the objects */ fb->focus_object = -1; if(fb->selection != NULL) g_list_free(fb->selection); fb->selection = fb->selection_end = NULL; /* Select this object */ fb->focus_object = i; fb->selection = g_list_append( fb->selection, (gpointer)i ); fb->selection_end = g_list_last(fb->selection); /* Update the DND drag icon */ FileBrowserListSetDNDDragIcon(fb); /* Update the File Name GtkEntry's value with * the new list of selected objects */ FileBrowserEntrySetSelectedObjects(fb); } /* Map the Right Click GtkMenu */ gtk_menu_popup( menu, NULL, NULL, NULL, NULL, button->button, button->time ); } status = TRUE; break; case GDK_BUTTON4: /* Scroll left */ if(fb->list_format == FB_LIST_FORMAT_STANDARD) { GtkAdjustment *adj = GTK_RANGE(fb->list_hsb)->adjustment; if(adj != NULL) { const gfloat inc = MAX( (0.25f * adj->page_size), adj->step_increment ); GTK_ADJUSTMENT_SET_VALUE( adj, adj->value - inc ); } } /* Scroll up */ else { GtkAdjustment *adj = GTK_RANGE(fb->list_vsb)->adjustment; if(adj != NULL) { const gfloat inc = MAX( (0.25f * adj->page_size), adj->step_increment ); GTK_ADJUSTMENT_SET_VALUE( adj, adj->value - inc ); } } status = TRUE; break; case GDK_BUTTON5: /* Scroll right */ if(fb->list_format == FB_LIST_FORMAT_STANDARD) { GtkAdjustment *adj = GTK_RANGE(fb->list_hsb)->adjustment; if(adj != NULL) { const gfloat inc = MAX( (0.25f * adj->page_size), adj->step_increment ); GTK_ADJUSTMENT_SET_VALUE( adj, adj->value + inc ); } } /* Scroll down */ else { GtkAdjustment *adj = GTK_RANGE(fb->list_vsb)->adjustment; if(adj != NULL) { const gfloat inc = MAX( (0.25f * adj->page_size), adj->step_increment ); GTK_ADJUSTMENT_SET_VALUE( adj, adj->value + inc ); } } status = TRUE; break; } fb->button = button->button; fb->drag_last_x = (gint)button->x; fb->drag_last_y = (gint)button->y; break; case GDK_BUTTON_RELEASE: button = (GdkEventButton *)event; /* Handle by button number */ switch(button->button) { case GDK_BUTTON2: /* Stop scrolling as needed */ if(gdk_pointer_is_grabbed()) gdk_pointer_ungrab(button->time); status = TRUE; break; } fb->button = 0; break; case GDK_2BUTTON_PRESS: button = (GdkEventButton *)event; /* Handle by button number */ switch(button->button) { case GDK_BUTTON1: if(fb->last_single_select_object > -1) { /* File Name GtkEntry activate */ FileBrowserEntryActivate(fb); } status = TRUE; break; } break; case GDK_MOTION_NOTIFY: motion = (GdkEventMotion *)event; #define STOP_SIGNAL_EMIT(_w_) { \ gtk_signal_emit_stop_by_name( \ GTK_OBJECT(_w_), \ "motion_notify_event" \ ); \ } switch(fb->button) { gint dx, dy; GtkAdjustment *adj; case GDK_BUTTON1: /* Drag and drop starting */ /* Needed to start drag */ if(motion->is_hint) { gint x, y; GdkModifierType mask; gdk_window_get_pointer( motion->window, &x, &y, &mask ); } status = TRUE; break; case GDK_BUTTON2: /* Middle click scrolling */ if(motion->is_hint) { gint x, y; GdkModifierType mask; gdk_window_get_pointer( motion->window, &x, &y, &mask ); dx = x - fb->drag_last_x; dy = y - fb->drag_last_y; fb->drag_last_x = x; fb->drag_last_y = y; } else { dx = (gint)(motion->x - fb->drag_last_x); dy = (gint)(motion->y - fb->drag_last_y); fb->drag_last_x = (gint)motion->x; fb->drag_last_y = (gint)motion->y; } adj = GTK_RANGE(fb->list_hsb)->adjustment; if((adj != NULL) && (dx != 0)) GTK_ADJUSTMENT_SET_VALUE( adj, adj->value - dx ); adj = GTK_RANGE(fb->list_vsb)->adjustment; if((adj != NULL) && (dy != 0)) GTK_ADJUSTMENT_SET_VALUE( adj, adj->value - dy ); STOP_SIGNAL_EMIT(widget); status = TRUE; break; } #undef STOP_SIGNAL_EMIT break; case GDK_ENTER_NOTIFY: crossing = (GdkEventCrossing *)event; status = TRUE; break; case GDK_LEAVE_NOTIFY: crossing = (GdkEventCrossing *)event; status = TRUE; break; } return(status); } /* * List "realize" signal callback. */ static void FileBrowserListRealizeCB(GtkWidget *widget, gpointer data) { GdkWindow *window; FileBrowser *fb = FILE_BROWSER(data); if((widget == NULL) || (fb == NULL)) return; window = widget->window; if(fb->transparent_stipple_bm == NULL) { const gint width = 8, height = 8; const guint8 data[] = { 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA }; fb->transparent_stipple_bm = gdk_bitmap_create_from_data( window, (const gchar *)data, width, height ); } if(fb->gc == NULL) fb->gc = gdk_gc_new(window); /* Mark the List GtkDrawingArea as realized */ fb->flags |= FB_REALIZED; } /* * List scrollbar GtkAdjustment "value_changed" signal callback. */ static void FileBrowserListScrollCB( GtkAdjustment *adj, gpointer data ) { FileBrowser *fb = FILE_BROWSER(data); if(fb == NULL) return; FileBrowserListQueueDraw(fb); } /* * Select all callback. */ static void FileBrowserSelectAllCB(GtkWidget *widget, gpointer data) { gint i, nobjs; FileBrowser *fb = FILE_BROWSER(data); if(fb == NULL) return; if(fb->selection != NULL) g_list_free(fb->selection); fb->selection = NULL; nobjs = g_list_length(fb->objects_list); for(i = 0; i < nobjs; i++) fb->selection = g_list_append( fb->selection, (gpointer)i ); fb->selection_end = g_list_last(fb->selection); FileBrowserListQueueDraw(fb); FileBrowserEntrySetSelectedObjects(fb); } /* * Unselect all callback. */ static void FileBrowserUnselectAllCB(GtkWidget *widget, gpointer data) { FileBrowser *fb = FILE_BROWSER(data); if(fb == NULL) return; if(fb->selection != NULL) g_list_free(fb->selection); fb->selection = fb->selection_end = NULL; FileBrowserListQueueDraw(fb); FileBrowserEntrySetSelectedObjects(fb); } /* * Invert selection callback. */ static void FileBrowserInvertSelectionCB(GtkWidget *widget, gpointer data) { gint i, nobjs; GList *glist; FileBrowser *fb = FILE_BROWSER(data); if(fb == NULL) return; nobjs = g_list_length(fb->objects_list); glist = NULL; for(i = 0; i < nobjs; i++) { if(!FB_IS_OBJECT_SELECTED(fb, i)) glist = g_list_append(glist, (gpointer)i); } if(fb->selection != NULL) g_list_free(fb->selection); fb->selection = glist; fb->selection_end = g_list_last(fb->selection); FileBrowserListQueueDraw(fb); FileBrowserEntrySetSelectedObjects(fb); } /* * Rename callback. */ static void FileBrowserRenameCB(GtkWidget *widget, gpointer data) { FileBrowserRenameQuery(FILE_BROWSER(data)); } /* * Change permissions callback. */ static void FileBrowserCHModCB(GtkWidget *widget, gpointer data) { FileBrowserCHModQuery(FILE_BROWSER(data)); } /* * Delete callback. */ static void FileBrowserDeleteCB(GtkWidget *widget, gpointer data) { FileBrowserDelete(FILE_BROWSER(data)); } /* * Entry complete path "key_press_event" or "key_release_event" * signal callback. */ static gint FileBrowserEntryCompletePathKeyCB( GtkWidget *widget, GdkEventKey *key, gpointer data ) { gboolean key_press; gint status = FALSE; guint key_state; GtkEntry *entry = GTK_ENTRY(widget); FileBrowser *fb = FILE_BROWSER(data); if((entry == NULL) || (key == NULL) || (fb == NULL)) return(status); key_press = (key->type == GDK_KEY_PRESS) ? TRUE : FALSE; key_state = key->state; #define SIGNAL_EMIT_STOP { \ gtk_signal_emit_stop_by_name( \ GTK_OBJECT(widget), \ (key_press) ? \ "key_press_event" : "key_release_event" \ ); \ } switch(key->keyval) { case GDK_Tab: /* Skip this if the shift or ctrl keys are held */ if((key_state & GDK_CONTROL_MASK) || (key_state & GDK_SHIFT_MASK) ) return(status); if(key_press) { gchar *path = STRDUP(gtk_entry_get_text(entry)); if(path != NULL) { gint status; gchar *prev_path; /* Because the entry is usually blank or not an * absolute path, the path must first be made into * an absolute path before completing it */ if(!g_path_is_absolute(path) && (*path != '~')) { if(STRISEMPTY(path)) { g_free(path); path = STRDUP(FileBrowserGetLocation(fb)); } else { gchar *path2 = FileBrowserJoinPaths( FileBrowserGetLocation(fb), path ); g_free(path); path = path2; } } prev_path = STRDUP(path); /* Complete the path */ path = CompletePath(path, &status); /* Set the completed path to the entry */ gtk_entry_set_text(entry, (path != NULL) ? path : ""); gtk_entry_set_position(entry, -1); /* If there was no change in the path then beep */ if((prev_path != NULL) && (path != NULL)) { if(!strcmp( (const char *)prev_path, (const char *)path )) gdk_beep(); } g_free(prev_path); g_free(path); } } SIGNAL_EMIT_STOP status = TRUE; break; } #undef SIGNAL_EMIT_STOP return(status); } /* * File Name GtkEntry "activate" signal callback. */ static void FileBrowserEntryActivateCB(GtkWidget *widget, gpointer data) { FileBrowserEntryActivate(FILE_BROWSER(data)); } /* * Types popup list box "changed" signal callback. */ static void FileBrowserTypesPUListChangedCB( GtkWidget *widget, const gint i, gpointer data ) { GtkWidget *pulist; fb_type_struct *t_cur, *t_sel; FileBrowser *fb = FILE_BROWSER(data); if((widget == NULL) || (fb == NULL)) return; pulist = PUListBoxGetPUList(widget); if(pulist == NULL) return; t_sel = FB_TYPE(PUListGetItemData(pulist, i)); if(t_sel == NULL) return; t_cur = fb->current_file_type; g_free(t_cur->extensions); t_cur->extensions = STRDUP(t_sel->extensions); g_free(t_cur->name); t_cur->name = STRDUP(t_sel->name); /* Update the listings only if not frozen */ if(fb->freeze_count <= 0) { fb->freeze_count++; /* Update the list */ FileBrowserListUpdate(fb, NULL); fb->freeze_count--; } } /* * File Selection Dialog ok button signal callback. */ static void FileBrowserOKCB(GtkWidget *widget, gpointer data) { FileBrowserOK(FILE_BROWSER(data)); } /* * File Selection Dialog cancel button signal callback. */ static void FileBrowserCancelCB(GtkWidget *widget, gpointer data) { FileBrowserCancel(FILE_BROWSER(data)); } /* * Initializes the File Selection Dialog. */ gint FileBrowserInit(void) { const gint border_major = 5, border_minor = 2; gint i; GtkAdjustment *adj; GtkAccelGroup *accelgrp; GtkWidget *w, *parent, *parent2, *parent3, *parent4, *toplevel, *pulist; FileBrowser *fb; /* Already initialized? */ if(file_browser != NULL) return(0); /* Initialize the Floating Prompt as needed */ if(FPromptInit()) { g_printerr( "FileBrowserInit() Unable to initialize the Floating Prompt.\n" ); return(-1); } /* Initialize the Confirmation Dialog as needed */ if(CDialogInit()) { g_printerr( "FileBrowserInit() Unable to initialize the Confirmation Dialog.\n" ); return(-1); } /* Create a new File Selection Dialog */ file_browser = fb = FILE_BROWSER(g_malloc0(sizeof(FileBrowser))); if(fb == NULL) return(-3); fb->toplevel = toplevel = gtk_window_new(GTK_WINDOW_DIALOG); fb->accelgrp = accelgrp = gtk_accel_group_new(); /* fb->gc = NULL; fb->busy_count = 0; fb->freeze_count = 0; fb->main_level = 0; */ fb->flags = FB_SHOW_HIDDEN_OBJECTS; /* fb->transparent_stipple_bm = NULL; */ fb->cur_busy = gdk_cursor_new(GDK_WATCH); fb->cur_column_hresize = gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW); fb->cur_translate = gdk_cursor_new(GDK_FLEUR); /* fb->list_pm = NULL; fb->cur_location = NULL; */ fb->list_format = FB_LIST_FORMAT_STANDARD; /* fb->columns_list = NULL; fb->icons_list = NULL; */ #if defined(_WIN32) fb->uid = 0; fb->euid = 0; fb->gid = 0; fb->egid = 0; #else fb->uid = getuid(); fb->euid = geteuid(); fb->gid = getgid(); fb->egid = getegid(); #endif #if defined(_WIN32) fb->home_path = g_strdup("C:\\"); #else fb->home_path = STRDUP(g_getenv("HOME")); #endif /* fb->device_paths_list = NULL; fb->objects_list = NULL; fb->objects_per_row = 0; */ fb->focus_object = -1; fb->last_single_select_object = -1; /* fb->selection = fb->selection_end = NULL; */ fb->current_file_type = FileBrowserTypeNew(); /* fb->selected_paths_list = NULL; fb->nselected_paths = 0; fb->object_created_cb = NULL; fb->object_created_data = NULL; fb->object_modified_cb = NULL; fb->object_modified_data = NULL; fb->object_deleted_cb = NULL; fb->object_deleted_data = NULL; fb->button = 0; fb->drag_flags = 0; fb->drag_last_x = 0; fb->drag_last_y = 0; */ fb->freeze_count++; /* Toplevel GtkWindow */ w = toplevel; gtk_object_set_data_full( GTK_OBJECT(w), FILE_BROWSER_KEY, fb, FileBrowserDestroyCB ); #ifdef PROG_NAME gtk_window_set_wmclass( GTK_WINDOW(w), "fileselection", PROG_NAME ); #endif gtk_widget_set_name(w, FILE_BROWSER_TOPLEVEL_WIDGET_NAME); gtk_window_set_policy(GTK_WINDOW(w), FALSE, FALSE, FALSE); gtk_widget_set_usize( w, FB_WIDTH, FB_HEIGHT ); gtk_window_set_title( GTK_WINDOW(w), FB_DEF_TITLE ); gtk_widget_add_events( w, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK ); gtk_signal_connect( GTK_OBJECT(w), "delete_event", GTK_SIGNAL_FUNC(FileBrowserDeleteEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "realize", GTK_SIGNAL_FUNC(FileBrowserRealizeCB), fb ); gtk_window_add_accel_group(GTK_WINDOW(w), accelgrp); parent = w; /* Main GtkVBox */ fb->main_vbox = w = gtk_vbox_new(FALSE, border_major); gtk_container_border_width(GTK_CONTAINER(w), border_major); gtk_container_add(GTK_CONTAINER(parent), w); gtk_widget_show(w); parent = w; w = gtk_hbox_new(FALSE, border_major); gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0); gtk_widget_show(w); parent2 = w; /* GtkHBox for the location label and locations popup list */ w = gtk_hbox_new(FALSE, border_minor); gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0); gtk_widget_show(w); parent3 = w; w = gtk_label_new( #if defined(PROG_LANGUAGE_SPANISH) "Ubicación:" #elif defined(PROG_LANGUAGE_FRENCH) "Emplacement:" #elif defined(PROG_LANGUAGE_GERMAN) "Ort:" #elif defined(PROG_LANGUAGE_ITALIAN) "Posizione:" #elif defined(PROG_LANGUAGE_DUTCH) "Plaats:" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Localidade:" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Plassering:" #else "Location:" #endif ); gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_RIGHT); gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0); gtk_widget_show(w); /* Locations Popup List Box */ fb->location_pulistbox = w = PUListBoxNew( -1, 20 + (2 * 3) ); gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, 0); PUListBoxSetChangedCB( w, FileBrowserLocPUListChangedCB, fb ); gtk_widget_show(w); /* Go To Parent */ fb->goto_parent_btn = w = GUIButtonPixmap( (guint8 **)icon_folder_parent_20x20_xpm ); gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0); gtk_signal_connect( GTK_OBJECT(w), "clicked", GTK_SIGNAL_FUNC(FileBrowserGoToParentCB), fb ); GUISetWidgetTip( w, #if defined(PROG_LANGUAGE_SPANISH) "El Padre" #elif defined(PROG_LANGUAGE_FRENCH) "Le Parent" #elif defined(PROG_LANGUAGE_GERMAN) "Elternteil" #elif defined(PROG_LANGUAGE_ITALIAN) "Il Genitore" #elif defined(PROG_LANGUAGE_DUTCH) "Ouder" #elif defined(PROG_LANGUAGE_PORTUGUESE) "O Pai" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Mor Eller Far" #else "Parent" #endif ); gtk_widget_show(w); /* New Directory */ fb->new_directory_btn = w = GUIButtonPixmap( (guint8 **)icon_folder_closed_20x20_xpm ); gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0); gtk_signal_connect( GTK_OBJECT(w), "clicked", GTK_SIGNAL_FUNC(FileBrowserNewDirectoryCB), fb ); GUISetWidgetTip( w, #if defined(PROG_LANGUAGE_SPANISH) "La Guía Nueva" #elif defined(PROG_LANGUAGE_FRENCH) "Nouvel Annuaire" #elif defined(PROG_LANGUAGE_GERMAN) "Neues Verzeichnis" #elif defined(PROG_LANGUAGE_ITALIAN) "L'Elenco Nuovo" #elif defined(PROG_LANGUAGE_DUTCH) "Nieuwe Gids" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Novo Guia" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Ny Directory" #else "New Directory" #endif ); gtk_widget_show(w); /* Rename */ fb->rename_btn = w = GUIButtonPixmap( (guint8 **)icon_rename_20x20_xpm ); gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0); gtk_signal_connect( GTK_OBJECT(w), "clicked", GTK_SIGNAL_FUNC(FileBrowserRenameCB), fb ); GUISetWidgetTip( w, #if defined(PROG_LANGUAGE_SPANISH) "Reagrupe" #elif defined(PROG_LANGUAGE_FRENCH) "Renommer" #elif defined(PROG_LANGUAGE_GERMAN) "Benennen Sie Um" #elif defined(PROG_LANGUAGE_ITALIAN) "Rinominare" #elif defined(PROG_LANGUAGE_DUTCH) "Herdoop" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Mude Nome" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Ombenevn" #else "Rename" #endif ); gtk_widget_show(w); /* Refresh */ fb->refresh_btn = w = GUIButtonPixmap( (guint8 **)icon_reload_20x20_xpm ); gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0); gtk_signal_connect( GTK_OBJECT(w), "clicked", GTK_SIGNAL_FUNC(FileBrowserRefreshCB), fb ); GUISetWidgetTip( w, #if defined(PROG_LANGUAGE_SPANISH) "Refresque" #elif defined(PROG_LANGUAGE_FRENCH) "Rafraîchir" #elif defined(PROG_LANGUAGE_GERMAN) "Erfrischen Sie" #elif defined(PROG_LANGUAGE_ITALIAN) "Rinfrescare" #elif defined(PROG_LANGUAGE_DUTCH) "Verfris" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Refresque-se" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Forfrisk" #else "Refresh" #endif ); gtk_widget_show(w); /* Show Hidden Objects GtkToggleButton */ fb->show_hidden_objects_tb = w = GUIToggleButtonPixmap( (guint8 **)icon_file_hidden_20x20_xpm ); gtk_widget_set_usize(w, FB_TB_BTN_WIDTH, FB_TB_BTN_HEIGHT); gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0); gtk_signal_connect( GTK_OBJECT(w), "toggled", GTK_SIGNAL_FUNC(FileBrowserShowHiddenObjectsToggleCB), fb ); if(fb->flags & FB_SHOW_HIDDEN_OBJECTS) GTK_TOGGLE_BUTTON_SET_ACTIVE(w, TRUE); GUISetWidgetTip( w, "Show Hidden Objects" ); gtk_widget_show(w); /* List Format GtkHBox */ w = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0); gtk_widget_show(w); parent3 = w; /* List Format Standard GtkToggleButton */ fb->list_format_standard_tb = w = GUIToggleButtonPixmap( (guint8 **)icon_fb_list_standard_20x20_xpm ); gtk_widget_set_usize(w, FB_TB_BTN_WIDTH, FB_TB_BTN_HEIGHT); gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0); gtk_signal_connect( GTK_OBJECT(w), "toggled", GTK_SIGNAL_FUNC(FileBrowserListFormatToggleCB), fb ); GUISetWidgetTip( w, #if defined(PROG_LANGUAGE_SPANISH) "Listar De Estándar" #elif defined(PROG_LANGUAGE_FRENCH) "Enumérer De Norme" #elif defined(PROG_LANGUAGE_GERMAN) "Standard Aufführen" #elif defined(PROG_LANGUAGE_ITALIAN) "Elencare Di Norma" #elif defined(PROG_LANGUAGE_DUTCH) "Standaard Opsommen" #elif defined(PROG_LANGUAGE_PORTUGUESE) "O Alistamento De Padrão" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Normal Listing" #else "Standard Listing" #endif ); gtk_widget_show(w); /* List Format Vertical GtkToggleButton */ fb->list_format_vertical_tb = w = GUIToggleButtonPixmap( (guint8 **)icon_fb_list_vertical_20x20_xpm ); gtk_widget_set_usize(w, FB_TB_BTN_WIDTH, FB_TB_BTN_HEIGHT); gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0); gtk_signal_connect( GTK_OBJECT(w), "toggled", GTK_SIGNAL_FUNC(FileBrowserListFormatToggleCB), fb ); GUISetWidgetTip( w, #if defined(PROG_LANGUAGE_SPANISH) "Listar Detallado" #elif defined(PROG_LANGUAGE_FRENCH) "Enumérer Détaillé" #elif defined(PROG_LANGUAGE_GERMAN) "Ausführliches Aufführen" #elif defined(PROG_LANGUAGE_ITALIAN) "Elencare Dettagliato" #elif defined(PROG_LANGUAGE_DUTCH) "Gedetailleerd Opsommen" #elif defined(PROG_LANGUAGE_PORTUGUESE) "O Alistamento De Detailed" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Detaljert Listing" #else "Detailed Listing" #endif ); /* Don't offer the use of the vertical list anymore */ /* gtk_widget_show(w); */ /* List Format Vertical Details GtkToggleButton */ fb->list_format_vertical_details_tb = w = GUIToggleButtonPixmap( (guint8 **)icon_fb_list_vertical_details_20x20_xpm ); gtk_widget_set_usize(w, FB_TB_BTN_WIDTH, FB_TB_BTN_HEIGHT); gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0); gtk_signal_connect( GTK_OBJECT(w), "toggled", GTK_SIGNAL_FUNC(FileBrowserListFormatToggleCB), fb ); GUISetWidgetTip( w, #if defined(PROG_LANGUAGE_SPANISH) "Listar Detallado" #elif defined(PROG_LANGUAGE_FRENCH) "Enumérer Détaillé" #elif defined(PROG_LANGUAGE_GERMAN) "Ausführliches Aufführen" #elif defined(PROG_LANGUAGE_ITALIAN) "Elencare Dettagliato" #elif defined(PROG_LANGUAGE_DUTCH) "Gedetailleerd Opsommen" #elif defined(PROG_LANGUAGE_PORTUGUESE) "O Alistamento De Detailed" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Detaljert Listing" #else "Detailed Listing" #endif ); gtk_widget_show(w); /* Table for list */ w = gtk_table_new(2, 2, FALSE); gtk_table_set_row_spacing(GTK_TABLE(w), 0, border_minor); gtk_table_set_col_spacing(GTK_TABLE(w), 0, border_minor); gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 0); gtk_widget_show(w); parent2 = w; /* List Header GtkFrame */ w = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN); gtk_table_attach( GTK_TABLE(parent2), w, 0, 1, 0, 1, GTK_FILL | GTK_SHRINK | GTK_EXPAND, GTK_FILL | GTK_SHRINK | GTK_EXPAND, 0, 0 ); gtk_widget_show(w); parent3 = w; /* List Header GtkHBox */ w = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(parent3), w); gtk_widget_show(w); parent3 = w; /* List Header GtkDrawingArea */ fb->list_header_da = w = gtk_drawing_area_new(); gtk_widget_set_name(w, FILE_BROWSER_LIST_HEADINGS_WIDGET_NAME); gtk_widget_set_usize(w, FB_LIST_HEADER_WIDTH, FB_LIST_HEADER_HEIGHT); gtk_widget_add_events( w, GDK_STRUCTURE_MASK | GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK ); gtk_signal_connect( GTK_OBJECT(w), "configure_event", GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "expose_event", GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "button_press_event", GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "button_release_event", GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "motion_notify_event", GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "enter_notify_event", GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "leave_notify_event", GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb ); gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0); gtk_widget_show(w); /* List GtkDrawingArea */ fb->list_da = w = gtk_drawing_area_new(); gtk_widget_set_name(w, FILE_BROWSER_CONTENTS_LIST_WIDGET_NAME); gtk_widget_set_usize(w, FB_LIST_WIDTH, FB_LIST_HEIGHT); GTK_WIDGET_SET_FLAGS(w, GTK_CAN_FOCUS); gtk_widget_add_events( w, GDK_STRUCTURE_MASK | GDK_EXPOSURE_MASK | GDK_FOCUS_CHANGE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK ); gtk_signal_connect( GTK_OBJECT(w), "configure_event", GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "expose_event", GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "focus_in_event", GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "focus_out_event", GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "key_press_event", GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "key_release_event", GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "button_press_event", GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "button_release_event", GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "motion_notify_event", GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "realize", GTK_SIGNAL_FUNC(FileBrowserListRealizeCB), fb ); if(w != NULL) { const GtkTargetEntry dnd_tar_types[] = { {GUI_TARGET_NAME_TEXT_PLAIN, 0, 0}, {GUI_TARGET_NAME_TEXT_URI_LIST, 0, 1}, {GUI_TARGET_NAME_STRING, 0, 2} }; const GtkTargetEntry dnd_src_types[] = { {GUI_TARGET_NAME_TEXT_PLAIN, 0, 0}, {GUI_TARGET_NAME_TEXT_URI_LIST, 0, 1}, {GUI_TARGET_NAME_STRING, 0, 2} }; gtk_signal_connect_after( GTK_OBJECT(w), "drag_leave", GTK_SIGNAL_FUNC(FileBrowserListDragLeaveCB), fb ); gtk_signal_connect_after( GTK_OBJECT(w), "drag_motion", GTK_SIGNAL_FUNC(FileBrowserListDragMotionCB), fb ); GUIDNDSetTar( w, dnd_tar_types, sizeof(dnd_tar_types) / sizeof(GtkTargetEntry), GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK, /* Allowed actions */ GDK_ACTION_MOVE, /* Default action if same */ GDK_ACTION_MOVE, /* Default action */ FileBrowserDragDataReceivedCB, fb, FALSE /* Do not highlight */ ); GUIDNDSetSrc( w, dnd_src_types, sizeof(dnd_src_types) / sizeof(GtkTargetEntry), GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK, /* Allowed actions */ GDK_BUTTON1_MASK, /* Buttons */ FileBrowserListDragBeginCB, FileBrowserListDragDataGetCB, FileBrowserListDragDataDeleteCB, FileBrowserListDragEndCB, fb ); } gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, 0); gtk_widget_show(w); /* List vertical scrollbar */ adj = (GtkAdjustment *)gtk_adjustment_new( 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f ); fb->list_vsb = w = gtk_vscrollbar_new(adj); gtk_table_attach( GTK_TABLE(parent2), w, 1, 2, 0, 1, 0, GTK_FILL | GTK_SHRINK | GTK_EXPAND, 0, 0 ); gtk_signal_connect( GTK_OBJECT(adj), "value_changed", GTK_SIGNAL_FUNC(FileBrowserListScrollCB), fb ); /* List's horizontal GtkScrollBar */ adj = (GtkAdjustment *)gtk_adjustment_new( 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f ); fb->list_hsb = w = gtk_hscrollbar_new(adj); gtk_table_attach( GTK_TABLE(parent2), w, 0, 1, 1, 2, GTK_FILL | GTK_SHRINK | GTK_EXPAND, 0, 0, 0 ); gtk_signal_connect( GTK_OBJECT(adj), "value_changed", GTK_SIGNAL_FUNC(FileBrowserListScrollCB), fb ); /* List Right Click GtkMenu */ if(TRUE) { guint8 **icon; const gchar *label; guint accel_key, accel_mods; gpointer data = fb; void (*func_cb)(GtkWidget *w, gpointer); GtkWidget *menu = (GtkWidget *)GUIMenuCreate(); #define ADD_MENU_ITEM_LABEL { \ w = GUIMenuItemCreate( \ menu, \ GUI_MENU_ITEM_TYPE_LABEL, \ accelgrp, \ icon, label, \ accel_key, accel_mods, \ func_cb, data \ ); \ } #define ADD_MENU_ITEM_CHECK { \ w = GUIMenuItemCreate( \ menu, \ GUI_MENU_ITEM_TYPE_CHECK, \ accelgrp, \ NULL, \ label, \ accel_key, accel_mods, \ func_cb, data \ ); \ } #define ADD_MENU_ITEM_SEPARATOR { \ w = GUIMenuItemCreate( \ menu, \ GUI_MENU_ITEM_TYPE_SEPARATOR, \ NULL, \ NULL, NULL, \ 0, 0, \ NULL, NULL \ ); \ } icon = (guint8 **)icon_select_20x20_xpm; label = #if defined(PROG_LANGUAGE_SPANISH) "Selecto" #elif defined(PROG_LANGUAGE_FRENCH) "Choisir" #elif defined(PROG_LANGUAGE_GERMAN) "Erlesen" #elif defined(PROG_LANGUAGE_ITALIAN) "Scegliere" #elif defined(PROG_LANGUAGE_DUTCH) "Uitgezocht" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Selecione" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Utvalgt" #else "Select" #endif ; accel_key = GDK_Return; accel_mods = 0; func_cb = FileBrowserEntryActivateCB; ADD_MENU_ITEM_LABEL /* fb->select_mi = w; */ ADD_MENU_ITEM_SEPARATOR icon = (guint8 **)icon_folder_parent_20x20_xpm; label = #if defined(PROG_LANGUAGE_SPANISH) "El Padre" #elif defined(PROG_LANGUAGE_FRENCH) "Le Parent" #elif defined(PROG_LANGUAGE_GERMAN) "Elternteil" #elif defined(PROG_LANGUAGE_ITALIAN) "Il Genitore" #elif defined(PROG_LANGUAGE_DUTCH) "Ouder" #elif defined(PROG_LANGUAGE_PORTUGUESE) "O Pai" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Mor Eller Far" #else "Go To Parent" #endif ; accel_key = GDK_BackSpace; accel_mods = 0; func_cb = FileBrowserGoToParentCB; ADD_MENU_ITEM_LABEL /* fb->goto_parent_mi = w; */ ADD_MENU_ITEM_SEPARATOR icon = (guint8 **)icon_folder_closed_20x20_xpm; label = #if defined(PROG_LANGUAGE_SPANISH) "La Guía Nueva" #elif defined(PROG_LANGUAGE_FRENCH) "Nouvel Annuaire" #elif defined(PROG_LANGUAGE_GERMAN) "Neues Verzeichnis" #elif defined(PROG_LANGUAGE_ITALIAN) "L'Elenco Nuovo" #elif defined(PROG_LANGUAGE_DUTCH) "Nieuwe Gids" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Novo Guia" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Ny Directory" #else "New Directory" #endif ; accel_key = GDK_n; accel_mods = GDK_CONTROL_MASK; func_cb = FileBrowserNewDirectoryCB; ADD_MENU_ITEM_LABEL /* fb->new_directory_mi = w; */ icon = (guint8 **)icon_rename_20x20_xpm; label = #if defined(PROG_LANGUAGE_SPANISH) "Reagrupe" #elif defined(PROG_LANGUAGE_FRENCH) "Renommer" #elif defined(PROG_LANGUAGE_GERMAN) "Benennen Sie Um" #elif defined(PROG_LANGUAGE_ITALIAN) "Rinominare" #elif defined(PROG_LANGUAGE_DUTCH) "Herdoop" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Mude Nome" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Ombenevn" #else "Rename" #endif ; accel_key = GDK_F2; accel_mods = 0; func_cb = FileBrowserRenameCB; ADD_MENU_ITEM_LABEL /* fb->rename_mi = w; */ icon = (guint8 **)icon_chmod_20x20_xpm; label = #if defined(PROG_LANGUAGE_SPANISH) "Cambie Los Permisos" #elif defined(PROG_LANGUAGE_FRENCH) "Changer Des Permissions" #elif defined(PROG_LANGUAGE_GERMAN) "Ändern Sie Erlaubnis" #elif defined(PROG_LANGUAGE_ITALIAN) "Cambiare I Permessi" #elif defined(PROG_LANGUAGE_DUTCH) "Verandeer Toestemmingen" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Mude Permissões" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Forandr Permissions" #else "Change Permissions" #endif ; accel_key = GDK_F9; accel_mods = 0; func_cb = FileBrowserCHModCB; ADD_MENU_ITEM_LABEL /* fb->chmod_mi = w; */ icon = (guint8 **)icon_cancel_20x20_xpm; label = #if defined(PROG_LANGUAGE_SPANISH) "Borre" #elif defined(PROG_LANGUAGE_FRENCH) "Effacer" #elif defined(PROG_LANGUAGE_GERMAN) "Löschen Sie" #elif defined(PROG_LANGUAGE_ITALIAN) "Cancellare" #elif defined(PROG_LANGUAGE_DUTCH) "Schrap" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Anule" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Stryk" #else "Delete" #endif ; accel_key = GDK_Delete; accel_mods = 0; func_cb = FileBrowserDeleteCB; ADD_MENU_ITEM_LABEL /* fb->delete_mi = w; */ ADD_MENU_ITEM_SEPARATOR icon = NULL; label = #if defined(PROG_LANGUAGE_SPANISH) "Escoja Todo" #elif defined(PROG_LANGUAGE_FRENCH) "Choisir Tout" #elif defined(PROG_LANGUAGE_GERMAN) "Wählen Sie Alle Aus" #elif defined(PROG_LANGUAGE_ITALIAN) "Scegliere Tutto" #elif defined(PROG_LANGUAGE_DUTCH) "Selecteer Alle" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Selecione Todo" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Utvalgt All" #else "Select All" #endif ; accel_key = GDK_a; accel_mods = GDK_CONTROL_MASK; func_cb = FileBrowserSelectAllCB; ADD_MENU_ITEM_LABEL /* fb->select_all_mi = w; */ icon = NULL; label = #if defined(PROG_LANGUAGE_SPANISH) "Rechace Todo" #elif defined(PROG_LANGUAGE_FRENCH) "Déssélectionner Tout" #elif defined(PROG_LANGUAGE_GERMAN) "Entmarkieren Sie Alle" #elif defined(PROG_LANGUAGE_ITALIAN) "Deselezionare Tutto" #elif defined(PROG_LANGUAGE_DUTCH) "Heldere Keuze" #elif defined(PROG_LANGUAGE_PORTUGUESE) "O Deselect Todo" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Klar Selection" #else "Unselect All" #endif ; accel_key = GDK_u; accel_mods = GDK_CONTROL_MASK; func_cb = FileBrowserUnselectAllCB; ADD_MENU_ITEM_LABEL /* fb->unselect_all_mi = w; */ icon = NULL; label = #if defined(PROG_LANGUAGE_SPANISH) "Invierta La Selección" #elif defined(PROG_LANGUAGE_FRENCH) "Inverser La Sélection" #elif defined(PROG_LANGUAGE_GERMAN) "Invertieren Sie Auswahl" #elif defined(PROG_LANGUAGE_ITALIAN) "Invertire La Selezione" #elif defined(PROG_LANGUAGE_DUTCH) "Keer Keuze Om" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Inverta Seleção" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Inverter Selection" #else "Invert Selection" #endif ; accel_key = GDK_i; accel_mods = GDK_CONTROL_MASK; func_cb = FileBrowserInvertSelectionCB; ADD_MENU_ITEM_LABEL /* fb->invert_selection_mi = w; */ ADD_MENU_ITEM_SEPARATOR icon = (guint8 **)icon_reload_20x20_xpm; label = #if defined(PROG_LANGUAGE_SPANISH) "Refresque" #elif defined(PROG_LANGUAGE_FRENCH) "Rafraîchir" #elif defined(PROG_LANGUAGE_GERMAN) "Erfrischen Sie" #elif defined(PROG_LANGUAGE_ITALIAN) "Rinfrescare" #elif defined(PROG_LANGUAGE_DUTCH) "Verfris" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Refresque-se" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Forfrisk" #else "Refresh" #endif ; accel_key = GDK_F5; accel_mods = 0; func_cb = FileBrowserRefreshCB; ADD_MENU_ITEM_LABEL /* fb->refresh_mi = w; */ ADD_MENU_ITEM_SEPARATOR icon = NULL; label = "Show Hidden Objects"; accel_key = GDK_h; accel_mods = GDK_CONTROL_MASK; func_cb = FileBrowserShowHiddenObjectsToggleCB; ADD_MENU_ITEM_CHECK fb->show_hidden_objects_micheck = w; if(fb->flags & FB_SHOW_HIDDEN_OBJECTS) GUIMenuItemSetCheck(w, TRUE, FALSE); ADD_MENU_ITEM_SEPARATOR icon = NULL; label = "Standard Listing"; accel_key = GDK_1; accel_mods = GDK_CONTROL_MASK; func_cb = FileBrowserListFormatToggleCB; ADD_MENU_ITEM_CHECK fb->list_format_standard_micheck = w; icon = NULL; label = "Vertical Listing"; accel_key = 0; accel_mods = 0; func_cb = FileBrowserListFormatToggleCB; ADD_MENU_ITEM_CHECK fb->list_format_vertical_micheck = w; gtk_widget_hide(w); /* Disable */ icon = NULL; label = "Detailed Listing"; accel_key = GDK_2; accel_mods = GDK_CONTROL_MASK; func_cb = FileBrowserListFormatToggleCB; ADD_MENU_ITEM_CHECK fb->list_format_vertical_details_micheck = w; fb->list_menu = menu; #undef ADD_MENU_ITEM_LABEL #undef ADD_MENU_ITEM_CHECK #undef ADD_MENU_ITEM_SEPARATOR } w = gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0); gtk_widget_show(w); w = gtk_hbox_new(FALSE, border_major); gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0); gtk_widget_show(w); parent2 = w; /* GtkVBox for the name and type */ w = gtk_vbox_new(FALSE, border_major); gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0); gtk_widget_show(w); parent3 = w; /* GtkHBox for the name */ w = gtk_hbox_new(FALSE, border_minor); gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0); gtk_widget_show(w); parent4 = w; w = gtk_label_new( #if defined(PROG_LANGUAGE_SPANISH) "Archive Nombre:" #elif defined(PROG_LANGUAGE_FRENCH) "Nom De Fichier:" #elif defined(PROG_LANGUAGE_GERMAN) "Dateiname:" #elif defined(PROG_LANGUAGE_ITALIAN) "Schedare Nome:" #elif defined(PROG_LANGUAGE_DUTCH) "Archiveer Naam:" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Arquive Nome:" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Arkiver Name:" #else "File Name:" #endif ); gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_RIGHT); gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0); gtk_widget_show(w); fb->entry = w = gtk_entry_new(); gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, TRUE, 0); gtk_signal_connect( GTK_OBJECT(w), "key_press_event", GTK_SIGNAL_FUNC(FileBrowserEntryCompletePathKeyCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "key_release_event", GTK_SIGNAL_FUNC(FileBrowserEntryCompletePathKeyCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(FileBrowserEntryActivateCB), fb ); GUIEditableEndowPopupMenu(w, 0); gtk_widget_show(w); GUISetWidgetTip( w, #if defined(PROG_LANGUAGE_SPANISH) "Entre el nombre del objeto, usted puede especificar más\ que un objeto (separa cada nombre con un ',' el carácter)" #elif defined(PROG_LANGUAGE_FRENCH) "Entrer le nom de l'objet, vous pouvez spécifier plus\ qu'un objet (sépare chaque nom avec un ',' le caractère)" #elif defined(PROG_LANGUAGE_GERMAN) "Tragen Sie den Namen des Objekts, Sie können angeben\ mehr als ein Objekt ein (trennen Sie jeden Namen mit einem ',' charakter)" #elif defined(PROG_LANGUAGE_ITALIAN) "Entrare il nome dell'oggetto, lei può specificare più\ di un oggetto (separa ogni nome con un ',' il carattere)" #elif defined(PROG_LANGUAGE_DUTCH) "Ga de naam van het voorwerp, u zou kunnen specificeren\ meer than een voorwerp binnen (scheid elk naam met een ',' teken)" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Entre o nome do objeto, você pode especificar mais de\ um objeto (separa cada nome com um ',' caráter)" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Gå inn i navnet av objektet, De spesifiserer mere enn\ et objekt (separere hver navn med et ',' karakter)" #else "Enter the name of the object, you may specify more than\ one object (separate each name with a ',' character)" #endif ); if(w != NULL) { const GtkTargetEntry dnd_tar_types[] = { {GUI_TARGET_NAME_TEXT_PLAIN, 0, 0}, {GUI_TARGET_NAME_TEXT_URI_LIST, 0, 1}, {GUI_TARGET_NAME_STRING, 0, 2} }; GUIDNDSetTar( w, dnd_tar_types, sizeof(dnd_tar_types) / sizeof(GtkTargetEntry), GDK_ACTION_COPY, /* Actions */ GDK_ACTION_COPY, /* Default action if same */ GDK_ACTION_COPY, /* Default action */ FileBrowserDragDataReceivedCB, fb, TRUE /* Highlight */ ); } /* GtkHBox for the type */ w = gtk_hbox_new(FALSE, border_minor); gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0); gtk_widget_show(w); parent4 = w; w = gtk_label_new( #if defined(PROG_LANGUAGE_SPANISH) "Tipo Archivo:" #elif defined(PROG_LANGUAGE_FRENCH) "Type De Fichier:" #elif defined(PROG_LANGUAGE_GERMAN) "Legt Typ Ab:" #elif defined(PROG_LANGUAGE_ITALIAN) "Tipo Di File:" #elif defined(PROG_LANGUAGE_DUTCH) "Archiveer Type:" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Arquivo Tipo:" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Typen Arkivet:" #else "File Type:" #endif ); gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0); gtk_widget_show(w); /* Types Popup List Box */ fb->types_pulistbox = w = PUListBoxNew( -1, -1 ); gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, TRUE, 0); PUListBoxSetChangedCB( w, FileBrowserTypesPUListChangedCB, fb ); gtk_widget_show(w); pulist = PUListBoxGetPUList(w); i = PUListAddItem(pulist, FB_DEF_TYPE_STR); if(i > -1) { fb_type_struct *t = FB_TYPE(g_malloc0(sizeof(fb_type_struct))); if(t != NULL) { t->extensions = STRDUP("*.*"); t->name = STRDUP("All Files"); PUListSetItemDataFull( pulist, i, t, (GtkDestroyNotify)FileBrowserTypeDelete ); } PUListBoxSetLinesVisible(w, 1); PUListBoxSelect(w, i); FileBrowserTypesPUListChangedCB(w, i, fb); } /* Vbox for ok and cancel buttons */ w = gtk_vbox_new(TRUE, border_minor); gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0); gtk_widget_show(w); parent3 = w; /* OK GtkButton */ fb->ok_btn = w = GUIButtonPixmapLabelH( (guint8 **)icon_ok_20x20_xpm, "OK", &fb->ok_btn_label ); GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT); gtk_widget_set_usize( w, GUI_BUTTON_HLABEL_WIDTH_DEF, GUI_BUTTON_HLABEL_HEIGHT_DEF ); gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, FALSE, 0); gtk_signal_connect( GTK_OBJECT(w), "clicked", GTK_SIGNAL_FUNC(FileBrowserOKCB), fb ); #if 0 gtk_accel_group_add( accelgrp, GDK_Return, 0, GTK_ACCEL_VISIBLE, GTK_OBJECT(w), "clicked" ); gtk_accel_group_add( accelgrp, GDK_3270_Enter, 0, GTK_ACCEL_VISIBLE, GTK_OBJECT(w), "clicked" ); gtk_accel_group_add( accelgrp, GDK_KP_Enter, 0, GTK_ACCEL_VISIBLE, GTK_OBJECT(w), "clicked" ); gtk_accel_group_add( accelgrp, GDK_ISO_Enter, 0, GTK_ACCEL_VISIBLE, GTK_OBJECT(w), "clicked" ); #endif gtk_widget_show(w); /* Cancel GtkButton */ fb->cancel_btn = w = GUIButtonPixmapLabelH( (guint8 **)icon_cancel_20x20_xpm, "Cancel", &fb->cancel_btn_label ); GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT); gtk_widget_set_usize( w, GUI_BUTTON_HLABEL_WIDTH_DEF, GUI_BUTTON_HLABEL_HEIGHT_DEF ); gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, FALSE, 0); gtk_signal_connect( GTK_OBJECT(w), "clicked", GTK_SIGNAL_FUNC(FileBrowserCancelCB), fb ); gtk_accel_group_add( accelgrp, GDK_Escape, 0, GTK_ACCEL_VISIBLE, GTK_OBJECT(w), "clicked" ); gtk_widget_show(w); /* Load the icons, the order is important because the index * must match the FB_ICON_* values as indices */ if(fb->toplevel != NULL) { gint i = 0; gpointer icon_list[] = FB_ICON_DATA_LIST; while(icon_list[i] != NULL) { FileBrowserIconAppend( fb, (guint8 **)icon_list[i + 1], /* XPM data */ (const gchar *)icon_list[i + 0] /* Description */ ); i += 6; } } /* Force the setting of the default list format and get * listing */ fb->list_format = -1; FileBrowserSetListFormat(fb, FB_LIST_FORMAT_STANDARD); fb->freeze_count--; return(0); } /* * Sets the File Selection Dialog's style. */ void FileBrowserSetStyle(GtkRcStyle *rc_style) { GtkWidget *w; FileBrowser *fb = file_browser; if(fb == NULL) return; w = fb->toplevel; if(w != NULL) { if(rc_style != NULL) { gtk_widget_modify_style(w, rc_style); } else { rc_style = gtk_rc_style_new(); gtk_widget_modify_style_recursive(w, rc_style); GTK_RC_STYLE_UNREF(rc_style) } } } /* * Sets the File Selection Dialog to be a transient for the given * GtkWindow w. * * If w is NULL then the transient for will be unset. */ void FileBrowserSetTransientFor(GtkWidget *w) { FileBrowser *fb = file_browser; if(fb == NULL) return; if(w != NULL) { if(!GTK_IS_WINDOW(w)) return; /* Since the File Selection Dialog itself has popup windows that may need to be * set modal, we cannot set the File Selection Dialog itself modal. gtk_window_set_modal( GTK_WINDOW(fb->toplevel), TRUE ); */ gtk_window_set_transient_for( GTK_WINDOW(fb->toplevel), GTK_WINDOW(w) ); } else { /* gtk_window_set_modal( GTK_WINDOW(fb->toplevel), FALSE ); */ gtk_window_set_transient_for( GTK_WINDOW(fb->toplevel), NULL ); } } /* * Sets the object created callback. */ void FileBrowserSetObjectCreatedCB( void (*cb)( const gchar *path, gpointer data ), gpointer data ) { FileBrowser *fb = file_browser; if(fb == NULL) return; fb->object_created_cb = cb; fb->object_created_data = data; } /* * Sets the object modified callback. */ void FileBrowserSetObjectModifiedCB( void (*cb)( const gchar *old_path, const gchar *new_path, gpointer data ), gpointer data ) { FileBrowser *fb = file_browser; if(fb == NULL) return; fb->object_modified_cb = cb; fb->object_modified_data = data; } /* * Sets the object deleted callback. */ void FileBrowserSetObjectDeletedCB( void (*cb)( const gchar *path, gpointer data ), gpointer data ) { FileBrowser *fb = file_browser; if(fb == NULL) return; fb->object_deleted_cb = cb; fb->object_deleted_data = data; } /* * Returns TRUE if currently blocking for query. */ gboolean FileBrowserIsQuery(void) { FileBrowser *fb = file_browser; if(fb == NULL) return(FALSE); if(fb->main_level > 0) return(TRUE); else return(FALSE); } /* * Ends query if any and returns a not available response. */ void FileBrowserBreakQuery(void) { FileBrowser *fb = file_browser; if(fb == NULL) return; /* Clear the response OK flag */ fb->flags &= ~FB_RESPONSE_OK; /* Break out of all our main loops */ while(fb->main_level > 0) { gtk_main_quit(); fb->main_level--; } fb->main_level = 0; } /* * Returns the File Selection Dialog's toplevel GtkWidget. */ GtkWidget *FileBrowserGetToplevel(void) { FileBrowser *fb = file_browser; if(fb == NULL) return(NULL); return(fb->toplevel); } /* * Clears the lists and unset the location. */ void FileBrowserReset(void) { FileBrowser *fb = file_browser; if(fb == NULL) return; fb->freeze_count++; #if 0 /* Do not reset the current location */ g_free(fb->cur_location); fb->cur_location = NULL; #endif /* Clear the file name GtkEntry */ gtk_entry_set_text(GTK_ENTRY(fb->entry), ""); /* Clear the Locations Popup List */ PUListClear(PUListBoxGetPUList(fb->location_pulistbox)); /* Clear the Types Popup List */ PUListClear(PUListBoxGetPUList(fb->types_pulistbox)); /* Clear the objects listing */ /* Unselect all */ fb->focus_object = -1; g_list_free(fb->selection); fb->selection = fb->selection_end = NULL; /* Delete all the objects */ if(fb->objects_list != NULL) { g_list_foreach( fb->objects_list, (GFunc)FileBrowserObjectDelete, NULL ); g_list_free(fb->objects_list); fb->objects_list = NULL; } /* Reset the number of objects per row */ fb->objects_per_row = 0; fb->freeze_count--; } /* * Maps the File Selection Dialog and sets up the inital values. * * Returns TRUE if a path was selected or FALSE if user canceled. * * For most values that are set NULL, the value is left unchanged. * All given values are coppied. * * If type is set to NULL however, then the type list on the file * browser will be left empty. * * All returned pointer values should be considered statically * allocated. The returned pointer for type_rtn may point to * a structure in the input type list. */ gboolean FileBrowserGetResponse( const gchar *title, const gchar *ok_label, const gchar *cancel_label, const gchar *path, fb_type_struct **file_types_list, const gint nfile_types, gchar ***paths_list_rtn, gint *npaths_rtn, fb_type_struct **file_type_rtn ) { GtkWidget *w, *toplevel; fb_type_struct *t_cur; FileBrowser *fb = file_browser; if(fb == NULL) { errno = EINVAL; return(FALSE); } #define RESET_RETURNS { \ if(paths_list_rtn != NULL) \ *paths_list_rtn = NULL; \ if(npaths_rtn != NULL) \ *npaths_rtn = 0; \ \ if(file_type_rtn != NULL) \ *file_type_rtn = NULL; \ } /* Do not handle response if already waiting for a response, * return with a not available response code */ if(fb->main_level > 0) { RESET_RETURNS errno = EBUSY; return(FALSE); } /* Reset the values */ fb->flags &= ~FB_RESPONSE_OK; t_cur = fb->current_file_type; g_free(t_cur->extensions); t_cur->extensions = NULL; g_free(t_cur->name); t_cur->name = NULL; /* Reset the returns */ RESET_RETURNS toplevel = fb->toplevel; /* Reget the drive paths */ if(fb->device_paths_list != NULL) { g_list_foreach(fb->device_paths_list, (GFunc)g_free, NULL); g_list_free(fb->device_paths_list); } fb->device_paths_list = FileBrowserGetDrivePaths(); /* Set title */ if(title != NULL) gtk_window_set_title(GTK_WINDOW(toplevel), title); /* Set OK Button label */ w = fb->ok_btn_label; if((ok_label != NULL) && (w != NULL)) gtk_label_set_text(GTK_LABEL(w), ok_label); /* Set Cancel Button label */ w = fb->cancel_btn_label; if((cancel_label != NULL) && (w != NULL)) gtk_label_set_text(GTK_LABEL(w), cancel_label); /* Set the types list */ if((file_types_list != NULL) && (nfile_types > 0)) FileBrowserTypesListSetTypes( fb, file_types_list, nfile_types ); FileBrowserSetBusy(fb, TRUE); /* Map the File Selection Dialog * * This needs to be done now in order to allow the proper * realizing of sizes before other things can be updated * * A configure_event signal will be emitted at which the * correct sizes will be known when adding the objects to * the objects list */ FileBrowserMap(); /* No initial path specified? */ if(STRISEMPTY(path)) { /* If no initial path was specified then we need to * check if the list was previous cleared (reset), in * which case we need to reget the listing */ if(fb->objects_list == NULL) { FileBrowserListUpdate(fb, NULL); FileBrowserLocationsPUListUpdate(fb); } } else { /* Set the initial location */ FileBrowserSetLocation(fb, path); } /* If no location was set then set the initial location as * the home directory */ if(STRISEMPTY(fb->cur_location)) FileBrowserSetLocation(fb, "~"); /* If there is a first object in the list then set it in focus */ if(fb->objects_list != NULL) { if(fb->focus_object < 0) { fb->focus_object = 0; gtk_widget_queue_draw(fb->list_da); } } FileBrowserSetBusy(fb, FALSE); /* Block until user response */ fb->main_level++; gtk_main(); /* Unmap the File Selection Dialog just in case it was not unmapped * from any of the callbacks */ FileBrowserUnmap(); /* Break out of all our main loops */ while(fb->main_level > 0) { gtk_main_quit(); fb->main_level--; } fb->main_level = 0; /* Set the return values */ /* Selected paths list */ if(paths_list_rtn != NULL) *paths_list_rtn = fb->selected_paths_list; if(npaths_rtn != NULL) *npaths_rtn = fb->nselected_paths; /* Selected file type */ if(file_type_rtn != NULL) *file_type_rtn = t_cur; errno = 0; return((fb->flags & FB_RESPONSE_OK) ? TRUE : FALSE); #undef RESET_RETURNS } /* * Maps the File Selection Dialog. */ void FileBrowserMap(void) { FileBrowser *fb = file_browser; if(fb == NULL) return; gtk_widget_show_raise(fb->toplevel); fb->flags |= FB_MAPPED; gtk_widget_grab_focus(fb->list_da); } /* * Unmaps the File Selection Dialog. */ void FileBrowserUnmap(void) { FileBrowser *fb = file_browser; if(fb == NULL) return; gtk_widget_hide(fb->toplevel); fb->flags &= ~FB_MAPPED; } /* * Shuts down the File Selection Dialog. */ void FileBrowserShutdown(void) { FileBrowser *fb = file_browser; if(fb == NULL) return; /* Break out of all our main loops */ while(fb->main_level > 0) { gtk_main_quit(); fb->main_level--; } fb->main_level = 0; gtk_widget_destroy(fb->list_menu); /* Destroy the toplevel GtkWidget which will call * FileBrowserDestroyCB() to delete the rest of the File * Browser */ gtk_widget_destroy(fb->toplevel); } /* * Gets the last location. * * If no previous location was set then NULL will be returned. */ const gchar *FileBrowserGetLastLocation(void) { FileBrowser *fb = file_browser; if(fb == NULL) return(NULL); return(FileBrowserGetLocation(fb)); } /* * Show hidden objects. */ void FileBrowserShowHiddenObjects(const gboolean show) { FileBrowser *fb = file_browser; if(fb == NULL) return; FileBrowserSetShowHiddenObjects(fb, show); } /* * Sets the list format to standard. */ void FileBrowserListStandard(void) { FileBrowser *fb = file_browser; if(fb == NULL) return; FileBrowserSetListFormat(fb, FB_LIST_FORMAT_STANDARD); } /* * Sets the list format to detailed. */ void FileBrowserListDetailed(void) { FileBrowser *fb = file_browser; if(fb == NULL) return; FileBrowserSetListFormat(fb, FB_LIST_FORMAT_VERTICAL_DETAILS); } /* * Creates a new file type. */ fb_type_struct *FileBrowserTypeNew(void) { return(FB_TYPE(g_malloc0(sizeof(fb_type_struct)))); } /* * Deletes the file type. */ void FileBrowserTypeDelete(fb_type_struct *t) { if(t == NULL) return; g_free(t->extensions); g_free(t->name); g_free(t); } /* * Appends a new file type to the file types list. * * The list and total specifies the current and return value for * the file types list. * * The extensions specifies a string describing a space-separated * list of extensions, each extension must include the '.' * character, the first extension is considered the default * extension if there are multiple extensions in the list. * * The name specifies a verbose name/description of the extension. * * Returns the index of the newly appended file type in the list * or negative on error. */ gint FileBrowserTypeListNew( fb_type_struct ***list, gint *total, const gchar *extensions, const gchar *name ) { gint i; fb_type_struct *t; if((list == NULL) || (total == NULL)) { errno = EINVAL; return(-2); } /* Increase the list's allocation */ i = MAX(*total, 0); *total = i + 1; *list = (fb_type_struct **)g_realloc( *list, (*total) * sizeof(fb_type_struct *) ); if(*list == NULL) { *total = 0; return(-3); } /* Create a new file type */ (*list)[i] = t = FileBrowserTypeNew(); if(t == NULL) { *total = i; return(-3); } /* Set the new file type's values */ t->extensions = STRDUP(extensions); t->name = STRDUP(name); return(i); } /* * Deletes the file types list. * * The list and total the file types list. */ void FileBrowserDeleteTypeList( fb_type_struct **list, const gint total ) { gint i; for(i = 0; i < total; i++) FileBrowserTypeDelete(list[i]); g_free(list); } /* * Appends the default (first) extension to the path. * * The path specifies the path to append or replace the extension * to. * * The t specifies the file type containing the list of extensions, * only the default (first) extension will be used. * * If always_replace is TRUE then if the specified path already * has an extension and it is different then it will be replaced * with the default (first) extension in the file type. * * Returns a copy of path with the extension appended or replaced. */ gchar *FileBrowserTypeCompleteExtension( const gchar *path, fb_type_struct *t, const gboolean always_replace ) { const gchar *ext, *extensions; gchar **extensions_list, *ext_default, *path_rtn; if(path == NULL) { errno = EINVAL; return(NULL); } if(t == NULL) return(g_strdup(path)); /* Get the space-separated list of extensions */ extensions = t->extensions; if(extensions == NULL) return(g_strdup(path)); /* If the specified extension specifies all types then just * return a copy of the path */ if(!strcmp(extensions, "*.*") || !strcmp(extensions, "*") ) return(g_strdup(path)); /* Explode the space-separated list of extensions into a list * of single extension strings */ extensions_list = g_strsplit( extensions, " ", -1 ); if(extensions_list == NULL) return(g_strdup(path)); /* Get the default (first) extension and "clean" it by removing * any ',' characters and seeking past any wildcards */ ext_default = extensions_list[0]; if(ext_default != NULL) { /* Seek to the '.' character */ gchar *s = (gchar *)strchr((char *)ext_default, '.'); if(s != NULL) ext_default = s; /* Remove all ',' characters */ substr((char *)ext_default, ",", ""); } /* Get the specified path's extension */ ext = FileBrowserGetExtensionFromPath(path); /* Does the specified path not have an extension? */ if(ext == NULL) { /* Use the default extension */ if(ext_default != NULL) { path_rtn = g_strconcat( path, (*ext_default != '.') ? "." : "", ext_default, NULL ); g_strfreev(extensions_list); return(path_rtn); } else { /* No default extension so just return a copy of the path */ path_rtn = g_strdup(path); g_strfreev(extensions_list); return(path_rtn); } } /* Check if the path's extension is not in the list */ if(strstr((const char *)extensions, (const char *)ext) == NULL) { gint i; gchar *path_without_ext; /* If we do not need to replace the path's extension then * just return a copy of the path now */ if(!always_replace) { g_strfreev(extensions_list); return(g_strdup(path)); } /* Copy the path and remove this current extension */ path_without_ext = g_strdup(path); if(path_without_ext == NULL) { const gint error_code = (gint)errno; g_strfreev(extensions_list); errno = error_code; return(NULL); } i = (gint)strlen(path_without_ext) - (gint)strlen(ext); if(i >= 0) path_without_ext[i] = '\0'; /* Append the default extension to the path */ if(ext_default != NULL) { path_rtn = g_strconcat( path_without_ext, (*ext_default != '.') ? "." : "", ext_default, NULL ); g_strfreev(extensions_list); g_free(path_without_ext); return(path_rtn); } else { /* No default extension so just return a copy of the path */ path_rtn = g_strdup(path); g_strfreev(extensions_list); g_free(path_without_ext); return(path_rtn); } } /* The specified path has an extension and it is in the list so * just return a copy of the path */ path_rtn = g_strdup(path); g_strfreev(extensions_list); return(path_rtn); }