#include #include #include #include #include #include #include #include #ifdef WIN32 # include #else # 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 "cdialog.h" #include "pulist.h" #include "fprompt.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_link2_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_root_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; typedef struct _FileBrowserObject FileBrowserObject; typedef struct _FileBrowserColumn FileBrowserColumn; typedef struct _FileBrowser FileBrowser; /* * 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_PIPE, FB_ICON_SOCKET, /* 10 */ FB_ICON_DEVICE_BLOCK, FB_ICON_DEVICE_CHARACTER, FB_ICON_EXECUTABLE, FB_ICON_DRIVE_FIXED, FB_ICON_DRIVE_ROOT /* 15 */ } FileBrowserIconNum; /* * Icons Data List: * * The order of this list must correspond with FileBrowserIconNum. * * 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_link2_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_root_20x20_xpm, \ NULL, NULL, NULL, NULL, \ \ NULL, NULL, \ NULL, NULL, NULL, NULL \ } /* * Icon: */ struct _FileBrowserIcon { GdkPixmap *pixmap; GdkBitmap *mask; gint width, height; gchar *desc; /* Used for MIME type */ }; #define FILE_BROWSER_ICON(p) ((FileBrowserIcon *)(p)) /* * Object: */ struct _FileBrowserObject { gchar *name, *full_path, *displayed_name; /* Name shown on the list * (may be abbriviated) */ gint icon_num; gint x, y, /* Position and size on list */ width, height; struct stat lstat_buf; }; #define FILE_BROWSER_OBJECT(p) ((FileBrowserObject *)(p)) /* * 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; }; #define FILE_BROWSER_COLUMN(p) ((FileBrowserColumn *)(p)) /* * File Browser: */ struct _FileBrowser { GtkWidget *toplevel; gint busy_count, freeze_count; GtkAccelGroup *accelgrp; GdkCursor *cur_busy, *cur_column_hresize, *cur_translate; GtkWidget *main_vbox, *dir_pulist_da, *dir_pulist_btn, *goto_parent_btn, *new_directory_btn, *rename_btn, *refresh_btn, *show_hidden_objects_tb, *list_format_standard_tb, *list_format_vertical_tb, *list_format_vertical_details_tb, *list_header_da, /* List's header GtkDrawingArea */ *list_da, /* List's GtkDrawingArea */ *list_vsb, /* List's vertical GtkScrollBar */ *list_hsb, /* List's horizontal GtkScrollBar */ *list_menu, /* List's right-click GtkMenu */ *entry, /* Location GtkEntry */ *type_combo, /* File type GtkCombo */ *ok_btn_label, *ok_btn, *cancel_btn_label, *cancel_btn; gboolean vsb_map_state, hsb_map_state; GdkPixmap *list_pm; pulist_struct *dir_pulist; gchar *cur_location; /* Current directory */ gint block_loop_level; gboolean user_response; /* TRUE if user clicked on OK */ FileBrowserListFormat list_format; FileBrowserColumn **column; gint total_columns; fb_type_struct cur_type; /* Current file type */ gchar **selected_path; gint total_selected_paths; FileBrowserIcon **icon; gint total_icons; gint uid, euid; gint gid, egid; gchar *home_path; gchar **drive_path; gint total_drive_paths; gint focus_object; GList *selection, *selection_end; FileBrowserObject **object; gint total_objects; gint objects_per_row; /* For use with * FB_LIST_FORMAT_STANDARD */ gint last_single_select_object; gboolean show_hidden_objects; /* Last pointer event information in the list_da */ gint button, last_x, last_y; gulong last_button1_release_time; /* In ms */ }; #define FILE_BROWSER(p) ((FileBrowser *)(p)) static FileBrowser file_browser; /* Utilities */ static gchar **FileBrowserDNDBufParse( const gchar *buf, gint len, gint *n ); static gchar *FileBrowserDNDBufFormat( FileBrowserObject **object, gint total, GList *selection, gint *len ); static gboolean FileBrowserObjectNameFilter( const gchar *name, const gchar *full_path, const gchar *ext ); /* Get Drive Paths */ static gchar **FileBrowserGetDrivePaths(gint *n); /* Busy/Ready Setting */ static void FileBrowserSetBusy(FileBrowser *fb, gboolean busy); /* Show Hidden Objects Setting */ static void FileBrowserSetShowHiddenObjects( FileBrowser *fb, gboolean show ); /* List Format Setting */ static void FileBrowserSetListFormat( FileBrowser *fb, FileBrowserListFormat list_format ); /* Location */ static void FileBrowserSetLocation(FileBrowser *fb, const gchar *path); static const gchar *FileBrowserGetLocation(FileBrowser *fb); /* Directory List */ static void FileBrowserDirPUListUpdate(FileBrowser *fb); static void FileBrowserDirPUListDraw(FileBrowser *fb); /* Icons */ static FileBrowserIcon *FileBrowserGetIcon(FileBrowser *fb, gint i); static FileBrowserIcon *FileBrowserIconAppend( FileBrowser *fb, guint8 **data, const gchar *desc ); static gint FileBrowserMatchIconNumFromPath( FileBrowser *fb, const gchar *full_path, const struct stat *lstat_buf ); /* Objects */ static FileBrowserObject *FileBrowserGetObject(FileBrowser *fb, gint i); static void FileBrowserObjectUpdateValues( FileBrowser *fb, FileBrowserObject *o ); static FileBrowserObject *FileBrowserObjectAppend( FileBrowser *fb, const gchar *name, const gchar *full_path, struct stat *lstat_buf ); /* List */ static void FileBrowserListUpdate(FileBrowser *fb, const gchar *filter); static void FileBrowserListUpdatePositions(FileBrowser *fb); static void FileBrowserListObjectSetDNDIcon(FileBrowser *fb, gint i); static gint FileBrowserListObjectVisibility(FileBrowser *fb, gint i); static void FileBrowserListMoveToObject( FileBrowser *fb, gint i, gfloat coeff ); static gint FileBrowserListSelectCoordinates( FileBrowser *fb, gint x, gint y ); static void FileBrowserListHeaderDraw(FileBrowser *fb); static void FileBrowserListHeaderQueueDraw(FileBrowser *fb); static void FileBrowserListDraw(FileBrowser *fb); static void FileBrowserListQueueDraw(FileBrowser *fb); /* Columns */ static FileBrowserColumn *FileBrowserListGetColumn(FileBrowser *fb, gint i); static FileBrowserColumn *FileBrowserListColumnAppend( FileBrowser *fb, const gchar *label, gint width ); static void FileBrowserListColumnsClear(FileBrowser *fb); static void FileBrowserEntrySetSelectedObjects(FileBrowser *fb); /* Callbacks */ static void FileBrowserDirPUListItemDestroyCB(gpointer data); static void FileBrowserIconDestroyCB(gpointer data); static void FileBrowserObjectDestroyCB(gpointer data); static void FileBrowserColumnDestroyCB(gpointer data); static gboolean FileBrowserDragmotionCB( 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 FileBrowserDragDataGetCB( GtkWidget *widget, GdkDragContext *dc, GtkSelectionData *selection_data, guint info, guint t, gpointer data ); static void FileBrowserDragDataDeleteCB( GtkWidget *widget, GdkDragContext *dc, 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 FileBrowserOKCB(GtkWidget *widget, gpointer data); static void FileBrowserCancelCB(GtkWidget *widget, gpointer data); static gint FileBrowserDirPUListDAEventCB( GtkWidget *widget, GdkEvent *event, gpointer data ); static void FileBrowserDirPUListMapCB(GtkWidget *widget, gpointer data); static gint FileBrowserListHeaderEventCB( GtkWidget *widget, GdkEvent *event, gpointer data ); static gint FileBrowserListEventCB( GtkWidget *widget, GdkEvent *event, 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 FileBrowserRenameFPromptCB( gpointer data, const gchar *value ); static void FileBrowserRenameCB(GtkWidget *widget, gpointer data); static void FileBrowserCHModFPromptCB( gpointer data, const gchar *value ); 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 FileBrowserEntryEnterCB(GtkWidget *widget, gpointer data); static void FileBrowserTypeListChangeCB( GtkWidget *widget, gpointer data, GList *glist ); /* File Browser Front Ends */ gint FileBrowserInit(void); void FileBrowserSetStyle(GtkRcStyle *rc_style); void FileBrowserSetTransientFor(GtkWidget *w); 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 **type, gint total_types, gchar ***path_rtn, gint *path_total_rtns, fb_type_struct **type_rtn ); void FileBrowserMap(void); void FileBrowserUnmap(void); void FileBrowserShutdown(void); void FileBrowserShowHiddenObjects(gboolean show); void FileBrowserListStandard(void); void FileBrowserListDetailed(void); /* File Types List */ gint FileBrowserTypeListNew( fb_type_struct ***list, gint *total, const gchar *ext, /* Space separated list of extensions */ const gchar *name /* Descriptive name */ ); void FileBrowserDeleteTypeList( fb_type_struct **t, gint total ); #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) ? strlen(s) : 0) #define STRISEMPTY(s) (((s) != NULL) ? (*(s) == '\0') : TRUE) #ifndef ISBLANK # define ISBLANK(c) (((c) == ' ') || ((c) == '\t')) #endif #define OBJISSEL(fb,n) ((((fb) != NULL) && ((n) >= 0)) ? \ ((g_list_find(fb->selection, (gpointer)(n)) != NULL) ? TRUE : FALSE) : FALSE) #define FB_WIDTH 480 #define FB_HEIGHT -1 #define FB_TB_BTN_WIDTH (20 + 5) #define FB_TB_BTN_HEIGHT (20 + 5) #define FB_LOC_LIST_MAX_CHARS 25 /* Max chars for each item in the * the locations list */ #define FB_LIST_ITEM_MAX_CHARS 25 /* Max chars 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 150 #define FB_LIST_ICON_WIDTH 20 #define FB_LIST_ICON_HEIGHT 20 #define FB_LIST_ICON_BORDER 2 /* Border between icon and text */ #define FB_DEFAULT_TYPE_STR "All files (*.*)" #define FB_NEW_DIRECTORY_NAME "new_directory" static gchar *G_STRCAT(gchar *s, const gchar *s2) { if(s != NULL) { if(s2 != NULL) { gchar *sr = g_strconcat(s, s2, NULL); g_free(s); s = sr; } } else { if(s2 != NULL) s = STRDUP(s2); else s = STRDUP(""); } return(s); } /* * Returns a list of full paths parsed from the given DND buffer. */ static gchar **FileBrowserDNDBufParse( const gchar *buf, gint len, gint *n ) { const gchar *buf_end = buf + len; /* Record end of buffer */ gchar **strv = NULL; gint i, strc = 0; if(n != NULL) *n = strc; if((buf == NULL) || (len <= 0)) return(strv); /* Iterate through buffer that contains a list of '\0' character * separated strings */ while(buf < buf_end) { const gchar *s = buf; 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) ? strchr(s, '/') : NULL; /* Able to seek to full path portion of the string? */ if(s != NULL) { /* Got full path portion, now add it to the list of * return strings */ i = strc; strc = i + 1; strv = (gchar **)g_realloc( strv, strc * sizeof(gchar *) ); if(strv == NULL) { strc = 0; break; } strv[i] = STRDUP(s); } } /* Seek buf to next string. First seek buf past this string * then seek past any '\0' deliminator(s) until we reach * the next string or the end of the buffer */ buf += s_len; while((buf < buf_end) ? (*buf == '\0') : FALSE) buf++; } /* Update total return value */ if(n != NULL) *n = strc; return(strv); } /* * Generates a DND buffer based on the list of objects that are * selected based on the given selection. */ static gchar *FileBrowserDNDBufFormat( FileBrowserObject **object, gint total, GList *selection, gint *len ) { gchar *buf = NULL; gint i, buf_len = 0; GList *glist; FileBrowserObject *o; /* Iterate through selection */ for(glist = selection; glist != NULL; glist = g_list_next(glist)) { i = (gint)glist->data; if((i >= 0) && (i < total)) o = object[i]; else o = NULL; if((o != NULL) ? (o->full_path != NULL) : FALSE) { gchar *url = g_strdup_printf( "file://%s", o->full_path ); gint url_len = STRLEN(url); gint last_buf_len = buf_len; /* Increase allocation of buffer 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); } } /* Update return value */ if(len != NULL) *len = buf_len; return(buf); } /* * Checks if the object name specified by name and full_path * match the extensions given in ext. */ static gboolean FileBrowserObjectNameFilter( const gchar *name, const gchar *full_path, const gchar *ext ) { gboolean use_regex; gchar *st, *st_end, cur_ext[80]; gint cur_ext_len, name_len; if((name == NULL) || (full_path == NULL) || (ext == NULL)) return(FALSE); name_len = STRLEN(name); while(ISBLANK(*ext) || (*ext == ',')) ext++; /* Iterate through each extension in ext */ while(*ext != '\0') { use_regex = FALSE; /* Copy current extension string in ext to cur_ext and * seek ext to next position */ cur_ext_len = 0; st = cur_ext; st_end = cur_ext + 79; /* Set end 1 character premature */ while((st < st_end) && !ISBLANK(*ext) && (*ext != ',') && (*ext != '\0') ) { /* Use this opportunity to check if there are characters * in the extension string to warrent the use of regex */ if((*ext == '*') || (*ext == '?')) use_regex = TRUE; *st++ = *ext++; cur_ext_len++; } *st = '\0'; /* Seek ext to next extension string */ while(ISBLANK(*ext) || (*ext == ',')) ext++; /* 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_regex) { /* Use regex to match */ if(!fnmatch(cur_ext, 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); } /* * Returns a list of strings describing the drive paths. */ static gchar **FileBrowserGetDrivePaths(gint *n) { #if defined(WIN32) /* Win32 */ gchar drive_letter = 'a'; gchar drive_name[10]; gint i, strc = 0; gchar **strv = NULL; for(drive_letter = 'a'; drive_letter <= 'g'; drive_letter++) { g_snprintf( drive_name, sizeof(drive_name), "%c:\\", toupper(drive_letter) ); i = strc; strc = i + 1; strv = (gchar **)g_realloc( strv, strc * sizeof(gchar *) ); if(strv == NULL) { strc = 0; break; } strv[i] = STRDUP(drive_name); } if(n != NULL) *n = strc; return(strv); #elif defined(__FreeBSD__) /* FreeBSD */ if(n != NULL ) *n = 0; return(NULL); #else /* UNIX */ gint i, strc = 0; gchar **strv = 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) { if(n != NULL) *n = strc; return(strv); } /* 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 { i = strc; strc = i + 1; strv = (gchar **)g_realloc( strv, strc * sizeof(gchar *) ); if(strv == NULL) { strc = 0; break; } /* Get mount path as the drive path */ #ifdef __SOLARIS__ strv[i] = STRDUP(vfs_ptr->vfs_mountp); #else strv[i] = STRDUP(mt_ptr->mnt_dir); #endif /* Read next mount entry */ #ifdef __SOLARIS__ mtback = getmntent(fp, vfs_ptr); #else mt_ptr = getmntent(fp); #endif } /* Close system devices list file */ #ifdef __SOLARIS__ FClose(fp); fp = NULL; vfs_ptr = NULL; #else endmntent(fp); fp = NULL; #endif if(n != NULL) *n = strc; return(strv); #endif } /* * Sets the file browser busy or ready. */ static void FileBrowserSetBusy(FileBrowser *fb, 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 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, gboolean show ) { if(fb == NULL) return; /* Skip if there is no change */ if(fb->show_hidden_objects == show) return; fb->freeze_count++; fb->show_hidden_objects = show; GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->show_hidden_objects_tb, show ); /* Update the listing, this will reget the list of objects */ FileBrowserListUpdate(fb, NULL); fb->freeze_count--; } /* * Sets the list format and updates the list as needed. */ static void FileBrowserSetListFormat( FileBrowser *fb, 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 ); GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->list_format_vertical_tb, TRUE ); GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->list_format_vertical_details_tb, FALSE ); break; case FB_LIST_FORMAT_VERTICAL_DETAILS: GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->list_format_standard_tb, FALSE ); GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->list_format_vertical_tb, FALSE ); GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->list_format_vertical_details_tb, TRUE ); break; case FB_LIST_FORMAT_STANDARD: GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->list_format_standard_tb, TRUE ); GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->list_format_vertical_tb, FALSE ); GTK_TOGGLE_BUTTON_SET_ACTIVE( fb->list_format_vertical_details_tb, FALSE ); break; } /* Begin updating things to recognize the new list format */ /* Update 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 list header */ w = fb->list_header_da; if(w != NULL) gtk_widget_show(w); break; case FB_LIST_FORMAT_VERTICAL: case FB_LIST_FORMAT_STANDARD: /* Hide list header */ w = fb->list_header_da; if(w != NULL) gtk_widget_hide(w); break; } gtk_widget_queue_resize(toplevel); /* Update the listing, this will reget the list of objects * and update the list's GtkScrollBars */ FileBrowserListUpdate(fb, NULL); fb->freeze_count--; } /* * Sets the new location and updates the list and directory pulist. * * The new location must be a directory, if it is not then its * parent will be used instead. */ 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 given path which will be modified and * checked before (if) it is finally accepted */ new_location = STRDUP(path); new_name = NULL; /* Special notation checks */ /* Current location? */ if(!strcmp(new_location, ".")) { g_free(new_location); #ifdef 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(new_location, "..")) { g_free(new_location); #ifdef 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; gchar *s = g_strdup_printf( "%s%s", (home != NULL) ? home : "", new_location + 1 ); 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)) { gchar *s, *cwd = STRDUP(g_get_current_dir()); s = STRDUP(PrefixPaths(cwd, new_location)); g_free(new_location); new_location = s; g_free(cwd); } /* Make sure the new location is a directory, if it is not then * get its parent instead */ if(!ISPATHDIR(new_location)) { gchar *parent = g_dirname(new_location); const gchar *name = strrchr(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 tailing deliminator */ if(new_location != NULL) { #ifdef WIN32 gchar *s = strrchr(new_location, G_DIR_SEPARATOR); gint prefix_len = STRLEN("x:\\"); #else gchar *s = strrchr(new_location, G_DIR_SEPARATOR); 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--; } } /* No change in the location? */ if((fb->cur_location != NULL) ? !strcmp(fb->cur_location, new_location) : FALSE ) { /* No change in location, but check if we still need to * update the lists if the lists were reset/cleared */ if(fb->total_objects > 0) { /* Lists were not cleared, so no need to update */ g_free(new_location); g_free(new_name); return; } } /* Accept and set new location */ g_free(fb->cur_location); fb->cur_location = new_location; new_location = NULL; /* Update 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 new listing of objects due to location change */ FileBrowserListUpdate(fb, NULL); /* Update paths in the directory popup list */ FileBrowserDirPUListUpdate(fb); /* Redraw */ FileBrowserDirPUListDraw(fb); g_free(new_location); g_free(new_name); } /* * Returns the current location, never returns NULL. */ static const gchar *FileBrowserGetLocation(FileBrowser *fb) { #ifdef WIN32 return((fb != NULL) ? fb->cur_location : "C:\\"); #else return((fb != NULL) ? fb->cur_location : "/"); #endif } /* * DND "drag_motion" signal callback. */ static gboolean FileBrowserDragmotionCB( GtkWidget *widget, GdkDragContext *dc, gint x, gint y, guint t, gpointer data ) { FileBrowser *fb = FILE_BROWSER(data); if((dc == NULL) || (fb == NULL)) return(FALSE); if(dc->actions & GDK_ACTION_COPY) gdk_drag_status(dc, GDK_ACTION_COPY, t); else gdk_drag_status(dc, 0, t); return(TRUE); } /* * DND "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) || (fb == NULL)) return; if(selection_data == NULL) return; /* Make sure the DND info is one that we want */ if((info == 0) || (info == 1) || (info == 2)) { /* Handle by destination widget */ /* List */ 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. */ gint i, strc; gchar **strv = FileBrowserDNDBufParse( (const gchar *)selection_data->data, selection_data->length, &strc ); const gchar *new_location = (strc == 1) ? strv[0] : NULL; if((new_location != NULL) ? ISPATHDIR(new_location) : FALSE) { /* Exactly one object dropped and it leads to a * directory so go to the new location */ FileBrowserSetLocation(fb, new_location); } else { /* Not one object or the one object does not lead to * a directory, so just set the dropped objects to * the file name entry. */ GtkEntry *entry = GTK_ENTRY(fb->entry); gchar *s = NULL; for(i = 0; i < strc; i++) { s = G_STRCAT(s, strv[i]); if((i + 1) < strc) s = G_STRCAT(s, ","); } gtk_entry_set_text(entry, s); gtk_entry_set_position(entry, -1); g_free(s); } for(i = 0; i < strc; i++) g_free(strv[i]); g_free(strv); } /* File name entry */ else if(widget == fb->entry) { GtkEntry *entry = GTK_ENTRY(widget); gchar *s = NULL; gint i, strc; gchar **strv = FileBrowserDNDBufParse( (const gchar *)selection_data->data, selection_data->length, &strc ); for(i = 0; i < strc; i++) { s = G_STRCAT(s, strv[i]); if((i + 1) < strc) s = G_STRCAT(s, ","); g_free(strv[i]); } g_free(strv); gtk_entry_set_text(entry, s); gtk_entry_set_position(entry, -1); g_free(s); } } } /* * DND "drag_data_get" signal callback. */ static void FileBrowserDragDataGetCB( 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->object, fb->total_objects, 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; } } /* * DND "drag_data_delete" signal callback. */ static void FileBrowserDragDataDeleteCB( GtkWidget *widget, GdkDragContext *dc, gpointer data ) { FileBrowser *fb = FILE_BROWSER(data); if((widget == NULL) || (fb == NULL)) return; /* Typical this means that some objects have been deleted * so we need to refresh. */ FileBrowserRefreshCB(fb->refresh_btn, fb); } /* * Show hidden objects GtkToggleButton "toggled" signal callback. */ static void FileBrowserShowHiddenObjectsToggleCB( GtkWidget *widget, gpointer data ) { GtkToggleButton *tb = GTK_TOGGLE_BUTTON(widget); FileBrowser *fb = FILE_BROWSER(data); if((tb == NULL) || (fb == NULL)) return; if(fb->freeze_count > 0) return; fb->freeze_count++; if(fb->show_hidden_objects == gtk_toggle_button_get_active(tb)) { fb->freeze_count--; return; } fb->show_hidden_objects = tb->active; /* Set new list format and reget listing */ FileBrowserListUpdate(fb, NULL); fb->freeze_count--; } /* * List format GtkToggleButton "toggled" signal callback. */ static void FileBrowserListFormatToggleCB( GtkWidget *widget, gpointer data ) { FileBrowserListFormat list_format = FB_LIST_FORMAT_STANDARD; GtkToggleButton *tb = GTK_TOGGLE_BUTTON(widget); FileBrowser *fb = FILE_BROWSER(data); if((tb == 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(!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) list_format = FB_LIST_FORMAT_STANDARD; /* Vertical */ else if(widget == fb->list_format_vertical_tb) list_format = FB_LIST_FORMAT_VERTICAL; /* Vertical with details */ else if(widget == fb->list_format_vertical_details_tb) list_format = FB_LIST_FORMAT_VERTICAL_DETAILS; /* Set new list format and reget listing */ FileBrowserSetListFormat(fb, list_format); fb->freeze_count--; } /* * Go to parent callback. */ static void FileBrowserGoToParentCB(GtkWidget *widget, gpointer data) { const gchar *cur_location; gchar *parent; FileBrowser *fb = FILE_BROWSER(data); if(fb == NULL) return; /* Get current location, then get its parent */ cur_location = FileBrowserGetLocation(fb); parent = (cur_location != NULL) ? g_dirname(cur_location) : NULL; /* Go to the parent of the current location */ if(parent != NULL) FileBrowserSetLocation(fb, parent); g_free(parent); } /* * New Directory callback. */ static void FileBrowserNewDirectoryCB(GtkWidget *widget, gpointer data) { mode_t m; gchar *name, *full_path; const gchar *cur_location; GtkWidget *toplevel; FileBrowser *fb = FILE_BROWSER(data); 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(s) { \ CDialogSetTransientFor(toplevel); \ CDialogGetResponse( \ TITLE_MKDIR_FAILED, \ (s), NULL, \ CDIALOG_ICON_WARNING, \ CDIALOG_BTNFLAG_OK, CDIALOG_BTNFLAG_OK \ ); \ CDialogSetTransientFor(NULL); \ } toplevel = fb->toplevel; cur_location = FileBrowserGetLocation(fb); if(cur_location == NULL) return; FileBrowserSetBusy(fb, TRUE); /* Generate new name and full path */ name = STRDUP(FB_NEW_DIRECTORY_NAME); full_path = STRDUP(PrefixPaths(cur_location, name)); #if defined(S_IRUSR) && defined(S_IWUSR) && defined(S_IXUSR) m = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH; #else m = 0; #endif /* Create new directory */ #ifdef WIN32 if(mkdir(full_path)) #else if(mkdir(full_path, m)) #endif { gint i; gboolean created = FALSE; /* Failed to create, try creating it under a different * name. */ for(i = 2; i < 100; i++) { /* Regenerate new name and full path */ g_free(name); g_free(full_path); name = g_strdup_printf( "%s%i", FB_NEW_DIRECTORY_NAME, i ); full_path = STRDUP(PrefixPaths(cur_location, name)); /* Try to create new directory, if success then * break out of the loop. */ #ifdef WIN32 if(!mkdir(full_path)) #else if(!mkdir(full_path, m)) #endif { created = TRUE; break; } /* Error creating directory and it was not because it * already exists? */ if(errno != EEXIST) { gchar *buf; #if defined(PROG_LANGUAGE_SPANISH) buf = STRDUP("Incapaz de crear guía nueva."); #elif defined(PROG_LANGUAGE_FRENCH) buf = STRDUP("Incapable pour créer le nouvel annuaire."); #elif defined(PROG_LANGUAGE_GERMAN) buf = STRDUP("Unfähig, neues Verzeichnis zu schaffen."); #elif defined(PROG_LANGUAGE_ITALIAN) buf = STRDUP("Incapace per creare l'elenco nuovo."); #elif defined(PROG_LANGUAGE_DUTCH) buf = STRDUP("Onbekwaam nieuwe gids te creëren."); #elif defined(PROG_LANGUAGE_PORTUGUESE) buf = STRDUP("Incapaz de criar novo guia."); #elif defined(PROG_LANGUAGE_NORWEGIAN) buf = STRDUP("Maktesløs skape ny katalog."); #else switch(errno) { case EACCES: buf = STRDUP( "You do not have sufficient permission to create a\n\ new directory at this location." ); break; case ENAMETOOLONG: buf = STRDUP( "The name for the new directory is too long." ); break; case ENOENT: buf = STRDUP( "A compoent of the current location is a dangling symbolic link." ); break; case ENOMEM: buf = STRDUP( "The system is out of memory." ); break; case EROFS: buf = STRDUP( "The current location is at a read-only filesystem." ); break; #ifdef ELOOP case ELOOP: buf = STRDUP( "Too many symbolic links were encountered at this location." ); break; #endif case ENOSPC: buf = STRDUP( "The device is out of free space." ); break; default: buf = STRDUP( "Unable to create new directory." ); break; } #endif MESSAGE_MKDIR_FAILED(buf); g_free(buf); break; } } /* Failed to create new directory? */ if(!created) { g_free(name); g_free(full_path); FileBrowserSetBusy(fb, FALSE); return; } } /* Update list */ FileBrowserListUpdate(fb, NULL); /* Select the newly created directory */ if(name != NULL) { gint i; const FileBrowserObject *o; for(i = 0; i < fb->total_objects; i++) { o = fb->object[i]; if((o != NULL) ? (o->name == NULL) : TRUE) continue; if(!strcmp(o->name, name)) { /* 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 selected objects on entry */ FileBrowserEntrySetSelectedObjects(fb); FileBrowserSetBusy(fb, FALSE); g_free(name); g_free(full_path); #undef MESSAGE_MKDIR_FAILED #undef TITLE_MKDIR_FAILED } /* * Refresh callback. */ static void FileBrowserRefreshCB(GtkWidget *widget, gpointer data) { gint last_scroll_x = 0, last_scroll_y = 0; GtkWidget *w; FileBrowser *fb = FILE_BROWSER(data); 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 directories listing */ FileBrowserDirPUListUpdate(fb); /* Reget objects listing */ FileBrowserListUpdate(fb, NULL); /* Restore the GtkScrollBar positions */ w = fb->list_hsb; if(w != NULL) { GtkRange *range = GTK_RANGE(w); GtkAdjustment *adj = range->adjustment; if(adj != NULL) gtk_adjustment_set_value( adj, CLIP( (gfloat)last_scroll_x, adj->lower, MAX(adj->upper - adj->page_size, adj->lower) ) ); } w = fb->list_vsb; if(w != NULL) { GtkRange *range = GTK_RANGE(w); GtkAdjustment *adj = range->adjustment; if(adj != NULL) gtk_adjustment_set_value( adj, CLIP( (gfloat)last_scroll_y, adj->lower, MAX(adj->upper - adj->page_size, adj->lower) ) ); } FileBrowserSetBusy(fb, FALSE); } /* * Updates the directory popup list based on the current location. */ static void FileBrowserDirPUListUpdate(FileBrowser *fb) { const gchar *cur_location; pulist_struct *pulist; const FileBrowserIcon *icon; struct stat stat_buf; GtkDestroyNotify destroy_cb = FileBrowserDirPUListItemDestroyCB; if(fb == NULL) return; pulist = fb->dir_pulist; if(pulist == NULL) return; FileBrowserSetBusy(fb, TRUE); /* Delete any existing items in the list */ PUListClear(pulist); #define GET_ICON(p) { \ if(stat((p), &stat_buf)) \ icon = NULL; \ else \ icon = FileBrowserGetIcon( \ fb, \ FileBrowserMatchIconNumFromPath( \ fb, (p), &stat_buf \ ) \ ); \ } /* Takes string s and shortens it, allocating a new string s2 * that is a shortened (as needed) version of s. The string s2 * must be deleted */ #define ALLOC_STRING_SHORTENED \ { if(s != NULL) { \ const gint len = STRLEN(s), \ max_characters = FB_LOC_LIST_MAX_CHARS;\ \ /* Length of s is too long? */ \ if(len > max_characters) \ s2 = g_strdup_printf( \ "...%s", \ &s[len - max_characters + 3] \ ); \ else \ s2 = STRDUP(s); \ } else { \ s2 = NULL; \ } } /* Get current location and set items for it and each parent * location. */ cur_location = FileBrowserGetLocation(fb); if(cur_location != NULL) { gint i, icon_num; gchar *s = STRDUP(cur_location), *sd = strrchr(s, G_DIR_SEPARATOR), *s2; /* Current location */ if(stat(s, &stat_buf)) icon_num = -1; else icon_num = FileBrowserMatchIconNumFromPath( fb, s, &stat_buf ); if(icon_num == FB_ICON_FOLDER_CLOSED) icon_num = FB_ICON_FOLDER_OPENED; icon = FileBrowserGetIcon(fb, icon_num); ALLOC_STRING_SHORTENED if(icon != NULL) i = PUListAddItemPixText( pulist, s2, icon->pixmap, icon->mask ); else i = PUListAddItem(pulist, s2); g_free(s2); PUListSetItemDataFull(pulist, i, STRDUP(s), destroy_cb); /* Parent locations */ #ifdef WIN32 while(sd > (s + 3)) #else while(sd > s) #endif { *sd = '\0'; GET_ICON(s); ALLOC_STRING_SHORTENED if(icon != NULL) i = PUListAddItemPixText( pulist, s2, icon->pixmap, icon->mask ); else i = PUListAddItem(pulist, s2); g_free(s2); PUListSetItemDataFull(pulist, i, STRDUP(s), destroy_cb); sd = strrchr(s, G_DIR_SEPARATOR); } /* Toplevel */ #ifdef WIN32 /* On Win32, use s as is from the above parent fetching loop */ sd = strchr(s, G_DIR_SEPARATOR); if((sd != NULL) ? (*(sd + 1) != '\0') : FALSE) { *(sd + 1) = '\0'; GET_ICON(s); ALLOC_STRING_SHORTENED if(icon != NULL) i = PUListAddItemPixText( pulist, s2, icon->pixmap, icon->mask ); else i = PUListAddItem(pulist, s2); g_free(s2); PUListSetItemDataFull(pulist, i, STRDUP(s), destroy_cb); } g_free(s); #else g_free(s); s = STRDUP("/"); GET_ICON(s); ALLOC_STRING_SHORTENED if(icon != NULL) i = PUListAddItemPixText( pulist, s2, icon->pixmap, icon->mask ); else i = PUListAddItem(pulist, s2); g_free(s2); PUListSetItemDataFull(pulist, i, STRDUP(s), destroy_cb); g_free(s); #endif } #ifndef WIN32 /* Home */ if(fb->home_path != NULL) { const gchar *s = fb->home_path; gint i; gchar *s2; GET_ICON(s); ALLOC_STRING_SHORTENED if(icon != NULL) i = PUListAddItemPixText( pulist, s2, icon->pixmap, icon->mask ); else i = PUListAddItem(pulist, s2); g_free(s2); PUListSetItemDataFull(pulist, i, STRDUP(s), destroy_cb); } #endif /* Drives */ if(fb->total_drive_paths > 0) { gint i, i2; const gchar *s; gchar *s2; for(i = 0; i < fb->total_drive_paths; i++) { s = fb->drive_path[i]; if(s == NULL) continue; /* Ignore toplevel */ if(!strcmp(s, "/")) continue; #ifndef WIN32 /* Ignore drives that do not have absolute paths */ if(!g_path_is_absolute(s)) continue; #endif #if defined(WIN32) icon = FileBrowserGetIcon(fb, FB_ICON_DRIVE_FIXED); #else GET_ICON(s); #endif ALLOC_STRING_SHORTENED if(icon != NULL) i2 = PUListAddItemPixText( pulist, s2, icon->pixmap, icon->mask ); else i2 = PUListAddItem( pulist, s2 ); g_free(s2); PUListSetItemDataFull(pulist, i2, STRDUP(s), destroy_cb); } } /* Other things to be added to the popup list, like mounted drives? */ FileBrowserSetBusy(fb, FALSE); #undef GET_ICON #undef ALLOC_STRING_SHORTENED } /* * Redraws the directory popup list's drawing area. */ static void FileBrowserDirPUListDraw(FileBrowser *fb) { gint state, width, height; GdkFont *font; GdkDrawable *drawable; GdkWindow *window; GdkGC *gc; GtkStyle *style; GtkWidget *w; const gchar *cur_location; if(fb == NULL) return; w = fb->dir_pulist_da; window = (w != NULL) ? w->window : NULL; 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; /* Draw background */ gdk_draw_rectangle( drawable, style->base_gc[state], TRUE, 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 /* Draw current location */ cur_location = FileBrowserGetLocation(fb); if(cur_location != NULL) { gint x = 2; gint icon_num; const FileBrowserIcon *icon; struct stat stat_buf; /* Match icon */ if(!stat(cur_location, &stat_buf)) icon_num = FileBrowserMatchIconNumFromPath( fb, cur_location, &stat_buf ); else icon_num = -1; /* If regular folder then get the opened folder */ if(icon_num == FB_ICON_FOLDER_CLOSED) icon_num = FB_ICON_FOLDER_OPENED; icon = FileBrowserGetIcon(fb, icon_num); /* Draw icon */ gc = style->fg_gc[state]; if(icon != NULL) { gint cx = x, cy = (height / 2) - (icon->height / 2); gdk_gc_set_clip_mask(gc, icon->mask); gdk_gc_set_clip_origin(gc, cx, cy); gdk_draw_pixmap( drawable, gc, icon->pixmap, 0, 0, cx, cy, icon->width, icon->height ); gdk_gc_set_clip_mask(gc, NULL); x += icon->width + FB_LIST_ICON_BORDER; } if(TRUE) { gchar *s; GdkTextBounds b; const gint len = STRLEN(cur_location), max_characters = FB_LOC_LIST_MAX_CHARS; GdkGC *gc_text = style->text_gc[state]; if(len > max_characters) s = g_strdup_printf( "...%s", &cur_location[len - max_characters + 3] ); else s = STRDUP(cur_location); gdk_string_bounds(font, s, &b); gdk_draw_string( drawable, font, gc_text, x - b.lbearing, ((height - (font->ascent + font->descent)) / 2) + font->ascent, s ); g_free(s); } } /* Draw focus rectangle if widget is in focus */ if(GTK_WIDGET_HAS_FOCUS(w) && GTK_WIDGET_SENSITIVE(w)) #if defined(WIN32) gdk_draw_rectangle( drawable, style->fg_gc[state], FALSE, 0, 0, width - 1, height - 1 ); #else gtk_draw_focus( style, drawable, 0, 0, width - 1, height - 1 ); #endif /* Send drawable to window if drawable is not the window */ if(drawable != window) gdk_draw_pixmap( window, style->fg_gc[state], drawable, 0, 0, 0, 0, width, height ); } /* * Returns the icon at index i or NULL on error. */ static FileBrowserIcon *FileBrowserGetIcon(FileBrowser *fb, gint i) { if(fb == NULL) return(NULL); if((i < 0) || (i >= fb->total_icons)) return(NULL); else return(fb->icon[i]); } /* * Appends an icon to the list. */ static FileBrowserIcon *FileBrowserIconAppend( FileBrowser *fb, guint8 **data, const gchar *desc ) { gint i; FileBrowserIcon *icon; if((fb == NULL) || (data == NULL)) return(NULL); i = MAX(fb->total_icons, 0); fb->total_icons = i + 1; fb->icon = (FileBrowserIcon **)g_realloc( fb->icon, fb->total_icons * sizeof(FileBrowserIcon *) ); if(fb->icon == NULL) { fb->total_icons = 0; return(NULL); } fb->icon[i] = icon = FILE_BROWSER_ICON(g_malloc0( sizeof(FileBrowserIcon) )); if(icon != NULL) { 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 gint FileBrowserMatchIconNumFromPath( FileBrowser *fb, const gchar *full_path, const struct stat *lstat_buf ) { gint i, uid, gid; const gchar *ext, *name; mode_t m; if(fb == NULL) return(FB_ICON_FILE); if(full_path != NULL) { const gchar *s; name = g_basename(full_path); #ifndef WIN32 /* Home directory? */ s = fb->home_path; if((s != NULL) ? !strcmp(s, full_path) : FALSE) return(FB_ICON_FOLDER_HOME); /* Toplevel? */ if(!strcmp(full_path, "/")) return(FB_ICON_DRIVE_ROOT); #endif /* Drive path? */ for(i = 0; i < fb->total_drive_paths; i++) { s = fb->drive_path[i]; if((s != NULL) ? !strcmp(s, full_path) : FALSE) return(FB_ICON_DRIVE_FIXED); } /* Get extension (if any) */ ext = strrchr(full_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((name != NULL) ? (*name == '.') : FALSE) { if((name[2] != '\0') && (name[2] != '.')) 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 owner has * access */ if(!(m & S_IXUSR)) return(FB_ICON_FOLDER_NOACCESS); /* Hidden? */ if((name != NULL) ? (*name == '.') : FALSE) { if((name[2] != '\0') && (name[2] != '.')) return(FB_ICON_FOLDER_HIDDEN); } return(FB_ICON_FOLDER_CLOSED); } #endif } #if defined(WIN32) if(ext != NULL) { if(!g_strcasecmp(ext, ".exe") || !g_strcasecmp(ext, ".com") || !g_strcasecmp(ext, ".bat") ) return(FB_ICON_EXECUTABLE); else return(FB_ICON_FILE); } else { return(FB_ICON_FILE); } #else #ifdef S_ISLNK if(S_ISLNK(m)) return(FB_ICON_LINK); #endif #ifdef S_ISFIFO if(S_ISFIFO(m)) return(FB_ICON_PIPE); #endif #ifdef S_ISSOCK if(S_ISSOCK(m)) return(FB_ICON_SOCKET); #endif #ifdef S_ISBLK if(S_ISBLK(m)) return(FB_ICON_DEVICE_BLOCK); #endif #ifdef S_ISCHR if(S_ISCHR(m)) return(FB_ICON_DEVICE_CHARACTER); #endif #if defined(S_IXUSR) && defined(S_IXGRP) && defined(S_IXOTH) if((m & S_IXUSR) || (m & S_IXGRP) || (m & S_IXOTH)) return(FB_ICON_EXECUTABLE); #endif /* Hidden? */ if((name != NULL) ? (*name == '.') : FALSE) { if((name[2] != '\0') && (name[2] != '.')) return(FB_ICON_FILE_HIDDEN); } return(FB_ICON_FILE); #endif } /* * Returns the object at index i or NULL on error. */ static FileBrowserObject *FileBrowserGetObject(FileBrowser *fb, gint i) { if(fb == NULL) return(NULL); if((i < 0) || (i >= fb->total_objects)) return(NULL); else return(fb->object[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 ) { gint icon_num; GdkFont *font; GtkStyle *style; GtkWidget *w; FileBrowserIcon *icon; if((fb == NULL) || (o == NULL)) return; /* Get the list widget's style and font */ w = fb->list_da; style = (w != NULL) ? gtk_widget_get_style(w) : NULL; font = (style != NULL) ? style->font : NULL; /* Get icon number based on the object's path and statistics */ o->icon_num = icon_num = FileBrowserMatchIconNumFromPath( fb, o->full_path, &o->lstat_buf ); /* Get icon */ icon = FileBrowserGetIcon(fb, icon_num); /* Set displayed_name from name, abbreviate it as needed */ if(o->name != NULL) { 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); } 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_BORDER + 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 *full_path, struct stat *lstat_buf ) { gint i; FileBrowserObject *o; if((fb == NULL) || (full_path == NULL)) return(NULL); /* Allocate one more pointer for the array */ i = MAX(fb->total_objects, 0); fb->total_objects = i + 1; fb->object = (FileBrowserObject **)g_realloc( fb->object, fb->total_objects * sizeof(FileBrowserObject *) ); if(fb->object == NULL) { fb->total_objects = 0; return(NULL); } /* Create a new Object */ fb->object[i] = o = FILE_BROWSER_OBJECT(g_malloc0( sizeof(FileBrowserObject) )); if(o != NULL) { o->name = STRDUP(name); o->full_path = STRDUP(full_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 * updates the list's GtkScrollBars. */ static void FileBrowserListUpdate(FileBrowser *fb, const gchar *filter) { gboolean match_all_files, show_hidden_objects; gint i; const gchar *cur_location, *ext; if(fb == NULL) return; FileBrowserSetBusy(fb, TRUE); show_hidden_objects = fb->show_hidden_objects; /* Get the extension from the filter (if specified) or from * the current file type */ ext = (filter != NULL) ? filter : fb->cur_type.ext; if(ext != NULL) match_all_files = (!strcmp(ext, "*.*") || !strcmp(ext, "*")) ? 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 objects */ for(i = 0; i < fb->total_objects; i++) FileBrowserObjectDestroyCB(fb->object[i]); g_free(fb->object); fb->object = NULL; fb->total_objects = 0; /* Reset objects per row */ fb->objects_per_row = 0; /* Get new objects */ cur_location = FileBrowserGetLocation(fb); if(cur_location != NULL) { gint strc; gchar **strv = GetDirEntNames2(cur_location, &strc); if(strv != NULL) { gchar *s, *full_path; struct stat lstat_buf; StringQSort(strv, strc); /* We will make several passes through the strv array * to load objects in proper order, directories first * and all other types of objects afterwards */ /* Add directories */ for(i = 0; i < strc; i++) { s = strv[i]; if(s == NULL) continue; /* Skip special directory notations */ if(!strcmp(s, ".") || !strcmp(s, "..") ) { g_free(s); strv[i] = s = NULL; continue; } /* Skip hidden objects? */ if(!show_hidden_objects && (*s == '.')) { g_free(s); strv[i] = s = NULL; continue; } /* Generate full path to object */ full_path = STRDUP(PrefixPaths(cur_location, s)); /* Skip objects that are not or do not go to a directory */ if(!ISPATHDIR(full_path)) { g_free(full_path); /* Do not delete string from strv, let * subsequent loops through strv handle it */ continue; } /* Get local stats of object and append object to * the list */ #ifdef WIN32 if(stat(full_path, &lstat_buf)) #else if(lstat(full_path, &lstat_buf)) #endif memset(&lstat_buf, 0x00, sizeof(struct stat)); FileBrowserObjectAppend(fb, s, full_path, &lstat_buf); g_free(full_path); /* Delete this name so it does not get handled on * the next loop below */ g_free(s); strv[i] = s = NULL; } /* Add all other objects */ for(i = 0; i < strc; i++) { s = strv[i]; if(s == NULL) continue; /* Generate full path to object */ full_path = STRDUP(PrefixPaths(cur_location, s)); /* Check if this object name gets filtered */ if(match_all_files ? FALSE : !FileBrowserObjectNameFilter( s, full_path, ext )) { g_free(full_path); g_free(s); strv[i] = s = NULL; continue; } /* Get local stats of object and append object to * the list */ #ifdef WIN32 if(stat(full_path, &lstat_buf)) #else if(lstat(full_path, &lstat_buf)) #endif memset(&lstat_buf, 0x00, sizeof(struct stat)); FileBrowserObjectAppend(fb, s, full_path, &lstat_buf); /* Delete full path, it is no longer needed */ g_free(full_path); g_free(s); strv[i] = s = NULL; } /* No more loops, delete the strings list * * All strings remaining in strv should have been * deleted in the last loop */ g_free(strv); } } /* Set the object's positions and update the GtkScrollBars * * This may produce a change in the list's size due to the * mapping or unmapping of the GtkScrollBars */ FileBrowserListUpdatePositions(fb); FileBrowserSetBusy(fb, FALSE); } /* * Updates the list's object positions and GtkScrollBar * GtkAdjustments. */ static void FileBrowserListUpdatePositions(FileBrowser *fb) { const gint border_major = 5, border_minor = 2, border_x = border_minor, border_y = border_minor; gboolean need_resize = FALSE; gint i, width, height, total_objects, 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; 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 object positions and get values for the * GtkScrollBar GtkAdjustments */ switch(list_format) { case FB_LIST_FORMAT_VERTICAL_DETAILS: case FB_LIST_FORMAT_VERTICAL: total_objects = fb->total_objects; for(i = 0; i < total_objects; i++) { o = fb->object[i]; 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 + 1; } break; case FB_LIST_FORMAT_STANDARD: total_objects = fb->total_objects; objects_per_row = 0; for(i = 0; i < total_objects; i++) { o = fb->object[i]; 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 + 1; objects_per_row++; /* Maximum is 7 objects per "row" (column) */ if(((cur_y + o->height) > height) || (objects_per_row >= 7) ) { 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; longest_width = 0; } } break; } /* Use the last object to set the list's bounds, these * bounds will be used in updating of the GtkScrollBar * GtkAdjustments below */ 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.25); list_x_page_inc = (gint)(width * 0.5); list_y_inc = o->height; list_y_page_inc = (gint)(height * 0.5); } /* Show/hide and update the GtkScrollBars */ switch(list_format) { case FB_LIST_FORMAT_VERTICAL_DETAILS: case FB_LIST_FORMAT_VERTICAL: w = fb->list_hsb; if(w != NULL) { if(fb->hsb_map_state) { gtk_widget_hide(w); fb->hsb_map_state = FALSE; 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->vsb_map_state) { gtk_widget_show(w); fb->vsb_map_state = TRUE; need_resize = TRUE; } } else { if(fb->vsb_map_state) { gtk_widget_hide(w); fb->vsb_map_state = FALSE; need_resize = TRUE; } } } } break; case FB_LIST_FORMAT_STANDARD: w = fb->list_vsb; if(w != NULL) { if(fb->vsb_map_state) { gtk_widget_hide(w); fb->vsb_map_state = FALSE; 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->hsb_map_state) { gtk_widget_show(w); fb->hsb_map_state = TRUE; need_resize = TRUE; } } else { if(fb->hsb_map_state) { gtk_widget_hide(w); fb->hsb_map_state = FALSE; 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 icon based on the object i. */ static void FileBrowserListObjectSetDNDIcon(FileBrowser *fb, gint i) { FileBrowserIcon *icon; FileBrowserObject *o = FileBrowserGetObject(fb, i); if(fb == NULL) return; /* Get object's icon (if any) */ icon = FileBrowserGetIcon(fb, o->icon_num); if(icon == NULL) return; /* Set new DND icon if it has a pixmap */ if(icon->pixmap != NULL) GUIDNDSetDragIcon( icon->pixmap, icon->mask, icon->width / 2, icon->height / 2 ); } /* * Returns one of GTK_VISIBILITY_* based on the visibility of * object i. */ static gint FileBrowserListObjectVisibility(FileBrowser *fb, 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 i, using the given coefficient as an * offset within the list window for the final scroll position. */ static void FileBrowserListMoveToObject( FileBrowser *fb, gint i, gfloat coeff ) { gint x, y, width, height; GdkWindow *window; GtkWidget *w; FileBrowserObject *o; if(fb == NULL) return; /* Get list's GdkWindow and its size, make sure that it exists * and its size is positive */ w = fb->list_da; window = (w != NULL) ? w->window : NULL; if(window == NULL) return; gdk_window_get_size(window, &width, &height); if((width <= 0) || (height <= 0)) return; /* Get object */ o = FileBrowserGetObject(fb, i); if(o == NULL) return; /* Move to object depending on list display 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) { GtkRange *range = GTK_RANGE(fb->list_vsb); GtkAdjustment *adj = range->adjustment; if(adj != NULL) gtk_adjustment_set_value( adj, CLIP( y, adj->lower, MAX(adj->upper - adj->page_size, adj->lower) ) ); } 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) { GtkRange *range = GTK_RANGE(fb->list_hsb); GtkAdjustment *adj = range->adjustment; if(adj != NULL) gtk_adjustment_set_value( adj, CLIP( x, adj->lower, MAX(adj->upper - adj->page_size, adj->lower) ) ); } break; } } /* * Returns the object index number that is found at the given * coordinates or -1 on failed match. */ static gint FileBrowserListSelectCoordinates( FileBrowser *fb, gint x, gint y ) { gint scroll_x = 0, scroll_y = 0; 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) { gint i, cx, cy; FileBrowserObject *o; case FB_LIST_FORMAT_VERTICAL_DETAILS: case FB_LIST_FORMAT_VERTICAL: for(i = 0; i < fb->total_objects; i++) { o = fb->object[i]; 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(i = 0; i < fb->total_objects; i++) { o = fb->object[i]; 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 Browser'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 = (fb != NULL) ? fb->list_header_da : NULL; if(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 = 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 background */ gc = style->bg_gc[state]; #if 0 bg_pixmap = style->bg_pixmap[state]; if(bg_pixmap != NULL) { gdk_gc_set_fill(gc, GDK_TILED); gdk_gc_set_tile(gc, bg_pixmap); gdk_gc_set_ts_origin(gc, 0, 0); } else { gdk_gc_set_fill(gc, GDK_SOLID); gdk_gc_set_foreground(gc, &style->bg[state]); } #endif gdk_draw_rectangle( drawable, gc, TRUE, 0, 0, width, height ); #if 0 gdk_gc_set_fill(gc, GDK_SOLID); gdk_gc_set_tile(gc, NULL); #endif /* Draw frame around entire header */ gtk_draw_box( style, drawable, state, GTK_SHADOW_OUT, 0, 0, width, height ); /* Any column headings to draw? */ if(fb->total_columns > 0) { gint i, last_column_position = 0; GdkRectangle rect; FileBrowserColumn *column; GdkGC *gc_text; /* Draw each column heading */ for(i = 0; i < fb->total_columns; i++) { column = fb->column[i]; if(column == NULL) continue; rect.x = last_column_position; rect.y = 0; rect.width = MAX(column->position - last_column_position, 0); rect.height = height; gtk_draw_box( style, drawable, state, GTK_SHADOW_OUT, rect.x, rect.y, rect.width, rect.height ); gc_text = (column->flags & GTK_SENSITIVE) ? style->text_gc[state] : style->text_gc[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_text, 0, 0); gdk_gc_set_clip_rectangle(gc_text, &rect); switch(column->label_justify) { case GTK_JUSTIFY_FILL: case GTK_JUSTIFY_CENTER: gdk_draw_string( drawable, font, gc_text, (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_text, (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_text, (rect.x + 5) - b.lbearing, (rect.y + ((rect.height - font_height) / 2)) + font->ascent, label ); break; } gdk_gc_set_clip_rectangle(gc_text, NULL); } if((column->flags & GTK_SENSITIVE) && (column->flags & GTK_HAS_FOCUS) && GTK_WIDGET_HAS_FOCUS(w) && GTK_WIDGET_SENSITIVE(w) ) gdk_draw_rectangle( drawable, style->fg_gc[state], FALSE, rect.x, rect.y, rect.width - 1, rect.height - 1 ); last_column_position = column->position; } } /* Send drawable to window if drawable is not the window */ if(drawable != (GdkDrawable *)window) gdk_draw_pixmap( window, style->fg_gc[state], drawable, 0, 0, 0, 0, width, height ); } /* * Queues a redraw of the File Browser's list header. */ static void FileBrowserListHeaderQueueDraw(FileBrowser *fb) { GtkWidget *w = (fb != NULL) ? fb->list_header_da : NULL; if(w != NULL) gtk_widget_queue_draw(w); } /* * Redraws the File Browser's list. */ static void FileBrowserListDraw(FileBrowser *fb) { gint width, height, font_height; gint scroll_x = 0, scroll_y = 0; GdkFont *font; GdkDrawable *drawable; GdkWindow *window; GdkGC *gc; GtkStateType state; GtkStyle *style; GtkWidget *w = (fb != NULL) ? fb->list_da : NULL; if(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 = 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 background */ gc = style->base_gc[state]; gdk_draw_rectangle( drawable, gc, TRUE, 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 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; GdkTextBounds b; gint i, x, y, icon_width; GdkGC *gc_text; const FileBrowserObject *o; const FileBrowserIcon *icon; case FB_LIST_FORMAT_VERTICAL_DETAILS: /* Make sure we have enough columns to draw for this * list display format */ if(fb->total_columns >= 4) { mode_t m; const struct stat *lstat_buf; GdkRectangle rect; const FileBrowserColumn *column[4]; /* Get pointer to all columns */ for(i = 0; i < 4; i++) column[i] = fb->column[i]; /* Iterate through objects */ for(i = 0; i < fb->total_objects; i++) { o = fb->object[i]; if(o == NULL) continue; if((o->width <= 0) || (o->height <= 0)) continue; /* Object off screen? */ x = o->x; y = o->y - scroll_y; if(((y + o->height) < 0) || (y >= height)) continue; /* Get object values */ lstat_buf = &o->lstat_buf; m = lstat_buf->st_mode; /* Get rectangle for the limits of the object's * name area */ rect.x = x - 0; rect.y = y; rect.width = MIN(o->width, column[0]->position - 0); rect.height = o->height; /* Is object selected? */ o_is_selected = OBJISSEL(fb, i); if(o_is_selected) { gc = style->fg_gc[GTK_STATE_SELECTED]; gc_text = style->text_gc[GTK_STATE_SELECTED]; gdk_draw_rectangle( drawable, style->bg_gc[GTK_STATE_SELECTED], TRUE, rect.x, rect.y, rect.width, rect.height ); } else { gc = style->fg_gc[state]; gc_text = style->text_gc[state]; } /* Draw Icon */ 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, pixmap, 0, 0, x, y, icon->width, icon->height ); gdk_gc_set_clip_mask(gc, NULL); icon_width = icon->width; } else { icon_width = 0; } /* Set up clip rectangle for drawing of the * object's name cell */ gdk_gc_set_clip_origin(gc, 0, 0); gdk_gc_set_clip_rectangle(gc, &rect); gdk_gc_set_clip_origin(gc_text, 0, 0); gdk_gc_set_clip_rectangle(gc_text, &rect); /* Draw Name */ if(o->displayed_name != NULL) { const gchar *s = o->displayed_name; gdk_string_bounds(font, s, &b); gdk_draw_string( drawable, font, gc_text, (x + icon_width + FB_LIST_ICON_BORDER) - b.lbearing, (y + ((o->height - font_height) / 2)) + font->ascent, s ); } else { /* No name, but we still need to have font extents * for use with drawing the subsequent cells below */ gdk_string_bounds(font, "X", &b); } /* If the object was selected it means we drew the * icon and name with the selected gc's. We should * now restore the gc's and use the current state * gc's for drawing subsequent values */ if(o_is_selected) { gdk_gc_set_clip_rectangle(gc, NULL); gdk_gc_set_clip_rectangle(gc_text, NULL); gc = style->fg_gc[state]; gc_text = style->text_gc[state]; } /* Draw Size */ if(S_ISREG(m)) { gchar s[80]; g_snprintf( s, sizeof(s), "%ld", 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_text, &rect); gdk_draw_string( drawable, font, gc_text, (rect.x + rect.width - b.width - 2) - b.lbearing, (y + (o->height / 2) - ((font->ascent + font->descent) / 2)) + font->ascent, s ); } /* Draw 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_text, &rect); gdk_draw_string( drawable, font, gc_text, (rect.x + 2) - b.lbearing, (y + (o->height / 2) - ((font->ascent + font->descent) / 2)) + font->ascent, s ); } /* Draw Last Modified Date */ if(S_ISREG(m)) { time_t mtime = lstat_buf->st_mtime; gchar *s = STRDUP((mtime > 0) ? ctime(&mtime) : "*undefined*"); gchar *s2 = strchr(s, '\n'); if(s2 == NULL) s2 = strchr(s, '\r'); if(s2 != NULL) *s2 = '\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_text, &rect); gdk_draw_string( drawable, font, gc_text, (rect.x + 2) - b.lbearing, (y + (o->height / 2) - ((font->ascent + font->descent) / 2)) + font->ascent, s ); g_free(s); } /* Restore gc */ gdk_gc_set_clip_rectangle(gc_text, NULL); gdk_gc_set_clip_rectangle(gc, NULL); /* Draw focus rectangle around object? */ if((i == fb->focus_object) && GTK_WIDGET_HAS_FOCUS(w)) { GdkGCValues gcv; gdk_gc_get_values(gc, &gcv); gdk_gc_set_function(gc, GDK_INVERT); rect.x = x - 0; rect.width = MIN(o->width, column[0]->position - 0); gdk_draw_rectangle( drawable, gc, FALSE, rect.x, rect.y, rect.width - 1, rect.height - 1 ); gdk_gc_set_function(gc, gcv.function); } } } break; case FB_LIST_FORMAT_VERTICAL: for(i = 0; i < fb->total_objects; i++) { o = fb->object[i]; if(o == NULL) continue; if((o->width <= 0) || (o->height <= 0)) continue; /* Object off screen? */ x = o->x; y = o->y - scroll_y; if(((y + o->height) < 0) || (y >= height)) continue; /* Is object selected? */ o_is_selected = OBJISSEL(fb, i); if(o_is_selected) { gc = style->fg_gc[GTK_STATE_SELECTED]; gc_text = style->text_gc[GTK_STATE_SELECTED]; gdk_draw_rectangle( drawable, style->bg_gc[GTK_STATE_SELECTED], TRUE, x, y, o->width, o->height ); } else { gc = style->fg_gc[state]; gc_text = style->text_gc[state]; } /* Draw icon */ 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, pixmap, 0, 0, x, y, icon->width, icon->height ); gdk_gc_set_clip_mask(gc, NULL); icon_width = icon->width; } else { icon_width = 0; } /* Draw name */ if(o->displayed_name != NULL) { const gchar *s = o->displayed_name; gdk_string_bounds(font, s, &b); gdk_draw_string( drawable, font, gc_text, (x + icon_width + FB_LIST_ICON_BORDER) - b.lbearing, (y + ((o->height - font_height) / 2)) + font->ascent, s ); } /* Draw focus rectangle around object? */ if((i == fb->focus_object) && GTK_WIDGET_HAS_FOCUS(w)) { GdkGCValues gcv; gdk_gc_get_values(gc, &gcv); gdk_gc_set_function(gc, GDK_INVERT); gdk_draw_rectangle( drawable, gc, FALSE, x, y, o->width - 1, o->height - 1 ); gdk_gc_set_function(gc, gcv.function); } } break; case FB_LIST_FORMAT_STANDARD: for(i = 0; i < fb->total_objects; i++) { o = fb->object[i]; if(o == NULL) continue; if((o->width <= 0) || (o->height <= 0)) continue; /* Object off screen? */ x = o->x - scroll_x; y = o->y; if(((x + o->width) < 0) || (x >= width)) continue; /* Is object selected? */ o_is_selected = OBJISSEL(fb, i); if(o_is_selected) { gc = style->fg_gc[GTK_STATE_SELECTED]; gc_text = style->text_gc[GTK_STATE_SELECTED]; gdk_draw_rectangle( drawable, style->bg_gc[GTK_STATE_SELECTED], TRUE, x, y, o->width, o->height ); } else { gc = style->fg_gc[state]; gc_text = style->text_gc[state]; } /* Draw icon */ 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, pixmap, 0, 0, x, y, icon->width, icon->height ); gdk_gc_set_clip_mask(gc, NULL); icon_width = icon->width; } else { icon_width = 0; } /* Draw name */ if(o->displayed_name != NULL) { const gchar *s = o->displayed_name; gdk_string_bounds(font, s, &b); gdk_draw_string( drawable, font, gc_text, (x + icon_width + FB_LIST_ICON_BORDER) - b.lbearing, (y + ((o->height - font_height) / 2)) + font->ascent, s ); } /* Draw focus rectangle around object? */ if((i == fb->focus_object) && GTK_WIDGET_HAS_FOCUS(w)) { GdkGCValues gcv; gdk_gc_get_values(gc, &gcv); gdk_gc_set_function(gc, GDK_INVERT); gdk_draw_rectangle( drawable, gc, FALSE, x, y, o->width - 1, o->height - 1 ); gdk_gc_set_function(gc, gcv.function); } } break; } #if 0 /* Draw focus rectangle if widget is in focus */ if(GTK_WIDGET_HAS_FOCUS(w) && (state != GTK_STATE_INSENSITIVE)) #if defined(WIN32) gdk_draw_rectangle( drawable, style->fg_gc[state], FALSE, 0, 0, width - 1, height - 1 ); #else gtk_draw_focus( style, drawable, 0, 0, width - 1, height - 1 ); #endif #endif /* Send drawable to window if drawable is not the window */ if(drawable != (GdkDrawable *)window) gdk_draw_pixmap( window, style->fg_gc[state], drawable, 0, 0, 0, 0, width, height ); } /* * Queues a redraw of the File Browser's list. */ static void FileBrowserListQueueDraw(FileBrowser *fb) { GtkWidget *w = (fb != NULL) ? fb->list_da : NULL; if(w != NULL) gtk_widget_queue_draw(w); } /* * Returns the list column at index i. */ static FileBrowserColumn *FileBrowserListGetColumn(FileBrowser *fb, gint i) { if(fb == NULL) return(NULL); if((i < 0) || (i >= fb->total_columns)) return(NULL); else return(fb->column[i]); } /* * Appends a new list column. */ static FileBrowserColumn *FileBrowserListColumnAppend( FileBrowser *fb, const gchar *label, gint width ) { gint i; FileBrowserColumn *column, *column_prev; if(fb == NULL) return(NULL); i = fb->total_columns; fb->total_columns = i + 1; fb->column = (FileBrowserColumn **)g_realloc( fb->column, fb->total_columns * sizeof(FileBrowserColumn *) ); if(fb->column == NULL) { fb->total_columns = 0; return(NULL); } fb->column[i] = column = FILE_BROWSER_COLUMN(g_malloc0( sizeof(FileBrowserColumn) )); if(column == NULL) return(NULL); column->label = STRDUP(label); column_prev = FileBrowserListGetColumn(fb, i - 1); column->position = ((column_prev != NULL) ? column_prev->position : 0) + 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 list columns. */ static void FileBrowserListColumnsClear(FileBrowser *fb) { gint i; if(fb == NULL) return; for(i = 0; i < fb->total_columns; i++) FileBrowserColumnDestroyCB(fb->column[i]); g_free(fb->column); fb->column = NULL; fb->total_columns = 0; } /* * Updates the entry with the selected objects, if there are no * selected objects then the entry is blanked. */ static void FileBrowserEntrySetSelectedObjects(FileBrowser *fb) { gchar *s; GList *glist; const FileBrowserObject *o; GtkEntry *entry = (fb != NULL) ? (GtkEntry *)fb->entry : NULL; if(entry == NULL) return; /* Iterate through selected objects and generate a string s to * be set as the new entry value */ s = STRDUP(""); for(glist = fb->selection; glist != NULL; glist = g_list_next(glist) ) { o = FileBrowserGetObject(fb, (gint)glist->data); if((o != NULL) ? (o->name != NULL) : FALSE) { s = strinsstr(s, -1, o->name); /* Add deliminator to string if there is another * object after this one */ if(g_list_next(glist) != NULL) s = strinschr(s, -1, ','); } } /* Update entry value if there were objects or else clear it */ gtk_entry_set_text(entry, (s != NULL) ? s : ""); gtk_entry_set_position(entry, -1); g_free(s); } /* * Directory popup list item destroy callback. */ static void FileBrowserDirPUListItemDestroyCB(gpointer data) { gchar *full_path = (gchar *)data; if(full_path == NULL) return; g_free(full_path); } /* * File browser icon destroy callback. */ static void FileBrowserIconDestroyCB(gpointer data) { FileBrowserIcon *icon = FILE_BROWSER_ICON(data); if(icon == NULL) return; GDK_PIXMAP_UNREF(icon->pixmap); GDK_BITMAP_UNREF(icon->mask); g_free(icon->desc); g_free(icon); } /* * File browser object destroy callback. */ static void FileBrowserObjectDestroyCB(gpointer data) { FileBrowserObject *o = FILE_BROWSER_OBJECT(data); if(o == NULL) return; g_free(o->displayed_name); g_free(o->full_path); g_free(o->name); g_free(o); } /* * File browser list column destroy callback. */ static void FileBrowserColumnDestroyCB(gpointer data) { FileBrowserColumn *column = FILE_BROWSER_COLUMN(data); if(column == NULL) return; g_free(column->label); g_free(column); } /* * File browser ok button signal callback. */ static void FileBrowserOKCB(GtkWidget *widget, gpointer data) { FileBrowser *fb = FILE_BROWSER(data); if(fb == NULL) return; if(FPromptIsQuery()) { FPromptBreakQuery(); } else if(PUListIsQuery(fb->dir_pulist)) { PUListBreakQuery(fb->dir_pulist); } else if(CDialogIsQuery()) { } else { gint i; GtkWidget *w; /* Mark that user response was OK */ fb->user_response = TRUE; /* Delete previous list of selected paths */ for(i = 0; i < fb->total_selected_paths; i++) g_free(fb->selected_path[i]); g_free(fb->selected_path); fb->selected_path = NULL; fb->total_selected_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 */ /* 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->total_selected_paths, 0); fb->total_selected_paths = n + 1; fb->selected_path = (gchar **)g_realloc( fb->selected_path, fb->total_selected_paths * sizeof(gchar *) ); if(fb->selected_path != NULL) { /* If name is an absolute path then copy * it to the selected_path list * * Otherwise copy the cur_location prefixed * to name */ if(g_path_is_absolute(name)) fb->selected_path[n] = STRDUP(name); else fb->selected_path[n] = STRDUP(PrefixPaths( cur_location, name )); } else { fb->total_selected_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->total_selected_paths, 0); fb->total_selected_paths = n + 1; fb->selected_path = (gchar **)g_realloc( fb->selected_path, fb->total_selected_paths * sizeof(gchar *) ); if(fb->selected_path == NULL) { fb->total_selected_paths = 0; } else { fb->selected_path[n] = STRDUP(cur_location); } } } /* Unmap */ FileBrowserUnmap(); /* Break out of blocking loop */ if(fb->block_loop_level > 0) { gtk_main_quit(); fb->block_loop_level--; } } } /* * File browser cancel button signal callback. */ static void FileBrowserCancelCB(GtkWidget *widget, gpointer data) { FileBrowser *fb = FILE_BROWSER(data); if(fb == NULL) return; if(FPromptIsQuery()) { FPromptBreakQuery(); } else if(PUListIsQuery(fb->dir_pulist)) { PUListBreakQuery(fb->dir_pulist); } else if(CDialogIsQuery()) { } else { /* Unmap */ FileBrowserUnmap(); /* Break out of blocking loop */ if(fb->block_loop_level > 0) { gtk_main_quit(); fb->block_loop_level--; } } } /* * Toplevel GtkWindow "delete_event" signal callback. */ static gint FileBrowserDeleteEventCB( GtkWidget *widget, GdkEvent *event, gpointer data ) { FileBrowserCancelCB(widget, data); return(TRUE); } /* * Directory popup list GtkDrawingArea event signal callback. */ static gint FileBrowserDirPUListDAEventCB( GtkWidget *widget, GdkEvent *event, gpointer data ) { gint status = FALSE; gboolean key_press; GdkEventConfigure *configure; GdkEventExpose *expose; GdkEventFocus *focus; GdkEventKey *key; GdkEventButton *button; GtkWidget *w; FileBrowser *fb = FILE_BROWSER(data); if((event == NULL) || (fb == NULL)) return(status); w = fb->dir_pulist_da; if(w == NULL) return(status); switch((gint)event->type) { case GDK_CONFIGURE: configure = (GdkEventConfigure *)event; status = TRUE; break; case GDK_EXPOSE: expose = (GdkEventExpose *)event; FileBrowserDirPUListDraw(fb); status = TRUE; break; case GDK_FOCUS_CHANGE: focus = (GdkEventFocus *)event; if(focus->in) GTK_WIDGET_SET_FLAGS(w, GTK_HAS_FOCUS); else GTK_WIDGET_UNSET_FLAGS(w, GTK_HAS_FOCUS); FileBrowserDirPUListDraw(fb); status = TRUE; break; case GDK_KEY_PRESS: case GDK_KEY_RELEASE: key = (GdkEventKey *)event; key_press = (key->type == GDK_KEY_PRESS) ? TRUE : FALSE; #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" \ ); \ } switch(key->keyval) { case GDK_Return: case GDK_KP_Enter: case GDK_space: case GDK_Up: case GDK_KP_Up: case GDK_Down: case GDK_KP_Down: if(key_press) FileBrowserDirPUListMapCB(fb->dir_pulist_btn, fb); STOP_KEY_SIGNAL_EMIT status = TRUE; break; } #undef STOP_KEY_SIGNAL_EMIT break; case GDK_BUTTON_PRESS: button = (GdkEventButton *)event; if(!GTK_WIDGET_HAS_FOCUS(w)) gtk_widget_grab_focus(w); switch(button->button) { case 1: FileBrowserDirPUListMapCB(fb->dir_pulist_btn, fb); break; } status = TRUE; break; case GDK_BUTTON_RELEASE: button = (GdkEventButton *)event; status = TRUE; break; } return(status); } /* * Directory popup list button callback. */ static void FileBrowserDirPUListMapCB(GtkWidget *widget, gpointer data) { const gchar *v; pulist_struct *pulist; FileBrowser *fb = FILE_BROWSER(data); if(fb == NULL) return; pulist = fb->dir_pulist; if(pulist == NULL) return; /* Map popup list to query for new location */ v = PUListMapQuery( pulist, NULL, /* Initial value */ -1, /* Lines visible */ PULIST_RELATIVE_BELOW, fb->dir_pulist_da, widget ); /* Got new location? */ if(v != NULL) { const gchar *full_path = (const gchar *)PUListGetDataFromValue( pulist, v ); /* Go to new location */ if(full_path != NULL) FileBrowserSetLocation(fb, full_path); } } /* * List header 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; 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; \ GtkStyle *style; \ \ window1 = w->window; \ window2 = fb->list_da->window; \ style = gtk_widget_get_style(w); \ gc = (style != NULL) ? \ style->fg_gc[GTK_WIDGET_STATE(w)] : NULL; \ \ if((window1 != NULL) && (window2 != NULL) && (gc != NULL)) { \ GdkGCValues gcv; \ gint width, height; \ \ gdk_gc_get_values(gc, &gcv); \ 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, gcv.function); \ } \ } } 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->total_columns > 0) { gint i, cp, tolor = 3; gint p = (gint)button->x; FileBrowserColumn *column; #define COLUMN_POSITION(p) (((p) != NULL) ? (p)->position : 0) /* Iterate through all columns to update focus and * reset drag state. */ for(i = 0; i < fb->total_columns; i++) { column = fb->column[i]; if(column == NULL) continue; /* Get left edge column position */ cp = ((i - 1) >= 0) ? COLUMN_POSITION(fb->column[i - 1]) : 0; if((p >= (cp + tolor)) && (p < (column->position - tolor))) column->flags |= GTK_HAS_FOCUS; else column->flags &= ~GTK_HAS_FOCUS; column->drag = FALSE; } FileBrowserListHeaderQueueDraw(fb); /* Iterate through all columns, checking for one * where the pointer is over its resizing area. */ for(i = fb->total_columns - 1; i >= 0; i--) { column = fb->column[i]; 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; } } #undef COLUMN_POSITION } status = TRUE; break; case GDK_BUTTON_RELEASE: button = (GdkEventButton *)event; if(fb->total_columns > 0) { gint i, pos_shift_delta = 0; FileBrowserColumn *column; /* Iterate through all columns, checking for one that * is being dragged and set that new column's * position */ for(i = 0; i < fb->total_columns; i++) { column = fb->column[i]; 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->total_columns > 0) { gint i, tolor = 3, left_column_pos = 0; GdkCursor *cursor = NULL; gint p = (gint)motion->x; 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(i = 0; i < fb->total_columns; i++) { column = fb->column[i]; if(column == NULL) continue; /* 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. */ 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_LEAVE_NOTIFY: 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 ) { gint status = FALSE; gboolean key_press; GdkEventConfigure *configure; GdkEventExpose *expose; GdkEventFocus *focus; GdkEventKey *key; GdkEventButton *button; GdkEventMotion *motion; GtkWidget *w; FileBrowser *fb = FILE_BROWSER(data); if((event == NULL) || (fb == NULL)) return(status); w = fb->list_da; if(w == 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 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(w)) { GTK_WIDGET_SET_FLAGS(w, GTK_HAS_FOCUS); FileBrowserListQueueDraw(fb); } else if(!focus->in && GTK_WIDGET_HAS_FOCUS(w)) { GTK_WIDGET_UNSET_FLAGS(w, 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; #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) { GtkRange *range = (GtkRange *)fb->list_vsb; GtkAdjustment *adj = (range != NULL) ? range->adjustment : NULL; if(adj != NULL) gtk_adjustment_set_value( adj, MAX( adj->value - adj->step_increment, adj->lower ) ); } 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->total_objects > 0) ) { if(!OBJISSEL(fb, n)) fb->selection = g_list_append( fb->selection, (gpointer)n ); if(!OBJISSEL(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? */ if(key->state & GDK_CONTROL_MASK) { GtkRange *range = (GtkRange *)fb->list_vsb; GtkAdjustment *adj = (range != NULL) ? range->adjustment : NULL; if(adj != NULL) gtk_adjustment_set_value( adj, MIN( adj->value + adj->step_increment, MAX(adj->upper - adj->page_size, adj->lower) ) ); } else if(fb->focus_object < (fb->total_objects - 1)) { gint n = fb->focus_object, i = n + 1; fb->focus_object = i; /* Select? */ if((key->state & GDK_SHIFT_MASK) && (fb->total_objects > 0) ) { if(!OBJISSEL(fb, n)) fb->selection = g_list_append( fb->selection, (gpointer)n ); if(!OBJISSEL(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? */ if(key->state & GDK_CONTROL_MASK) { GtkRange *range = (GtkRange *)fb->list_vsb; GtkAdjustment *adj = (range != NULL) ? range->adjustment : NULL; if(adj != NULL) gtk_adjustment_set_value( adj, MAX( adj->value - adj->page_increment, adj->lower ) ); } else { const FileBrowserObject *o = (fb->total_objects > 0) ? fb->object[0] : NULL; 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) && (fb->total_objects > 0) ) { gint j; for(j = MIN(n, fb->total_objects - 1); j >= i; j--) { if(!OBJISSEL(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? */ if(key->state & GDK_CONTROL_MASK) { GtkRange *range = (GtkRange *)fb->list_vsb; GtkAdjustment *adj = (range != NULL) ? range->adjustment : NULL; if(adj != NULL) gtk_adjustment_set_value( adj, MIN( adj->value + adj->page_increment, MAX(adj->upper - adj->page_size, adj->lower) ) ); } else { const FileBrowserObject *o = (fb->total_objects > 0) ? fb->object[0] : NULL; 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), fb->total_objects - 1 ); /* Select? */ if((key->state & GDK_SHIFT_MASK) && (fb->total_objects > 0) ) { gint j; for(j = MAX(n, 0); j <= i; j++) { if(!OBJISSEL(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) { GtkRange *range = (GtkRange *)fb->list_vsb; GtkAdjustment *adj = (range != NULL) ? range->adjustment : NULL; if(adj != NULL) gtk_adjustment_set_value( adj, adj->lower ); } else if(fb->focus_object > 0) { gint i = 0, n = fb->focus_object; fb->focus_object = i; /* Select? */ if((key->state & GDK_SHIFT_MASK) && (fb->total_objects > 0) ) { gint j; for(j = MIN(n, fb->total_objects - 1); j >= i; j--) { if(!OBJISSEL(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? */ if(key->state & GDK_CONTROL_MASK) { GtkRange *range = (GtkRange *)fb->list_vsb; GtkAdjustment *adj = (range != NULL) ? range->adjustment : NULL; if(adj != NULL) gtk_adjustment_set_value( adj, MAX( adj->upper - adj->page_size, adj->lower ) ); } else if(fb->focus_object < (fb->total_objects - 1)) { gint i = MAX(fb->total_objects - 1, 0), n = fb->focus_object; fb->focus_object = i; /* Select? */ if((key->state & GDK_SHIFT_MASK) && (fb->total_objects > 0) ) { gint j; for(j = MAX(n, 0); j <= i; j++) { if(!OBJISSEL(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 n = fb->focus_object, i = n - 1; fb->focus_object = i; /* Select? */ if((key->state & GDK_SHIFT_MASK) && (fb->total_objects > 0) ) { if(!OBJISSEL(fb, n)) fb->selection = g_list_append( fb->selection, (gpointer)n ); if(!OBJISSEL(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? */ if(key->state & GDK_CONTROL_MASK) { /* No vertical scrolling */ } else if(fb->focus_object < (fb->total_objects - 1)) { const gint n = MAX(fb->focus_object, 0), i = n + 1; fb->focus_object = i; /* Select? */ if((key->state & GDK_SHIFT_MASK) && (fb->total_objects > 0) ) { if(!OBJISSEL(fb, n)) fb->selection = g_list_append( fb->selection, (gpointer)n ); if(!OBJISSEL(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) { GtkRange *range = (GtkRange *)fb->list_hsb; GtkAdjustment *adj = (range != NULL) ? range->adjustment : NULL; if(adj != NULL) gtk_adjustment_set_value( adj, MAX( adj->value - adj->step_increment, adj->lower ) ); } else if(fb->focus_object > 0) { const gint 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) && (fb->total_objects > 0) ) { gint j; for(j = MIN(n, fb->total_objects - 1); j >= i; j--) { if(!OBJISSEL(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? */ if(key->state & GDK_CONTROL_MASK) { GtkRange *range = (GtkRange *)fb->list_hsb; GtkAdjustment *adj = (range != NULL) ? range->adjustment : NULL; if(adj != NULL) gtk_adjustment_set_value( adj, MIN( adj->value + adj->step_increment, MAX(adj->upper - adj->page_size, adj->lower) ) ); } else if(fb->focus_object < (fb->total_objects - 1)) { const gint n = MAX(fb->focus_object, 0), i = MIN( n + fb->objects_per_row, fb->total_objects - 1 ); fb->focus_object = i; /* Select? */ if((key->state & GDK_SHIFT_MASK) && (fb->total_objects > 0) ) { gint j; for(j = n; j <= i; j++) { if(!OBJISSEL(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) { GtkRange *range = (GtkRange *)fb->list_hsb; GtkAdjustment *adj = (range != NULL) ? range->adjustment : NULL; if(adj != NULL) gtk_adjustment_set_value( adj, MAX( adj->value - adj->page_increment, adj->lower ) ); } else if(fb->focus_object > 0) { gint 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) && (fb->total_objects > 0) ) { gint j; for(j = MIN(n, fb->total_objects - 1); j >= i; j--) { if(!OBJISSEL(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? */ if(key->state & GDK_CONTROL_MASK) { GtkRange *range = (GtkRange *)fb->list_hsb; GtkAdjustment *adj = (range != NULL) ? range->adjustment : NULL; if(adj != NULL) gtk_adjustment_set_value( adj, MIN( adj->value + adj->page_increment, MAX(adj->upper - adj->page_size, adj->lower) ) ); } else if(fb->focus_object < (fb->total_objects - 1)) { gint n = fb->focus_object, i = MIN( fb->focus_object + fb->objects_per_row, fb->total_objects - 1 ); fb->focus_object = i; /* Select? */ if((key->state & GDK_SHIFT_MASK) && (fb->total_objects > 0) ) { gint j; for(j = MAX(n, 0); j <= i; j++) { if(!OBJISSEL(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) { GtkRange *range = (GtkRange *)fb->list_hsb; GtkAdjustment *adj = (range != NULL) ? range->adjustment : NULL; if(adj != NULL) gtk_adjustment_set_value( adj, adj->lower ); } else if(fb->focus_object > 0) { gint i = 0, n = fb->focus_object; fb->focus_object = i; /* Select? */ if((key->state & GDK_SHIFT_MASK) && (fb->total_objects > 0) ) { gint j; for(j = MIN(n, fb->total_objects - 1); j >= i; j--) { if(!OBJISSEL(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? */ if(key->state & GDK_CONTROL_MASK) { GtkRange *range = (GtkRange *)fb->list_hsb; GtkAdjustment *adj = (range != NULL) ? range->adjustment : NULL; if(adj != NULL) gtk_adjustment_set_value( adj, MAX( adj->upper - adj->page_size, adj->lower ) ); } else if(fb->focus_object < (fb->total_objects - 1)) { gint i = MAX(fb->total_objects - 1, 0), n = fb->focus_object; fb->focus_object = i; /* Select? */ if((key->state & GDK_SHIFT_MASK) && (fb->total_objects > 0) ) { gint j; for(j = MAX(n, 0); j <= i; j++) { if(!OBJISSEL(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 * handle it generally independent of the list * format here */ if(!status) { switch(key->keyval) { case GDK_Return: /* Enter */ case GDK_KP_Enter: case GDK_3270_Enter: case GDK_ISO_Enter: if(key_press) FileBrowserEntryEnterCB(fb->entry, fb); status = TRUE; break; case GDK_space: /* Toggle Select */ if(key_press) { gint i = fb->focus_object; if(OBJISSEL(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: if(key_press) FileBrowserGoToParentCB(NULL, fb); status = TRUE; break; case GDK_F5: /* Refresh */ if(key_press) FileBrowserRefreshCB(NULL, fb); status = TRUE; break; case GDK_Insert: /* New Directory */ #if 0 /* Some other accelerator seems to catch this even with this widget in * focus so we don't need to respond to it. */ if(key_press) FileBrowserNewDirectoryCB(NULL, fb); #endif status = TRUE; break; case GDK_F2: /* Rename */ if(key_press) FileBrowserRenameCB(NULL, fb); status = TRUE; break; case GDK_Delete: /* Delete */ if(key_press) FileBrowserDeleteCB(NULL, fb); status = TRUE; break; #if 0 /* Some other accelerator seems to catch this even with this widget in * focus so we don't need to respond to it. */ case 'a': /* Select All */ if(key_press && (key->state & GDK_CONTROL_MASK)) FileBrowserSelectAllCB(NULL, fb); status = TRUE; break; case 'u': /* Unelect All */ if(key_press && (key->state & GDK_CONTROL_MASK)) FileBrowserUnselectAllCB(NULL, fb); status = TRUE; break; case 'i': /* Invert Selection */ if(key_press && (key->state & GDK_CONTROL_MASK)) FileBrowserInvertSelectionCB(NULL, fb); status = TRUE; break; #endif } } #undef STOP_KEY_SIGNAL_EMIT break; case GDK_BUTTON_PRESS: button = (GdkEventButton *)event; /* Unmap floating prompt as needed */ if(FPromptIsQuery()) FPromptBreakQuery(); /* Grab focus as needed */ if(!GTK_WIDGET_HAS_FOCUS(w)) gtk_widget_grab_focus(w); /* Handle by button number */ switch(button->button) { case GDK_BUTTON1: /* Select */ /* Select one item at a time? */ if(button->state & GDK_CONTROL_MASK) { const gint i = FileBrowserListSelectCoordinates( fb, (gint)button->x, (gint)button->y ); if(i > -1) { /* If new object is already selected then unselect * it, otherwise add it to the selection */ fb->last_single_select_object = -1; fb->focus_object = i; if(OBJISSEL(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 DND icon based on the newly selected * object. */ FileBrowserListObjectSetDNDIcon(fb, i); } /* If matched object is not fully visibile then * scroll so that it is in the center of the * list. */ if(FileBrowserListObjectVisibility(fb, i) != GTK_VISIBILITY_FULL ) FileBrowserListMoveToObject(fb, i, 0.5f); } } /* Select a group of items */ 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 the current one */ fb->last_single_select_object = -1; fb->focus_object = i; if(fb->selection_end != NULL) { gint j = i, n = (gint)fb->selection_end->data; if(j > n) { while(j > n) { if(!OBJISSEL(fb, j)) fb->selection = g_list_append( fb->selection, (gpointer)j ); j--; } fb->selection_end = g_list_last(fb->selection); } else if(j < n) { while(j < n) { if(!OBJISSEL(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 DND icon based on the newly selected * object. */ FileBrowserListObjectSetDNDIcon(fb, i); /* If matched object is not fully visibile then * scroll so that it is in the center of the * list */ 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 (if any) * is the same as the newly selected 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 DND icon based on the newly selected * object */ FileBrowserListObjectSetDNDIcon(fb, i); /* If matched object is not fully visibile then * scroll so that it is in the center of the * list */ 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; } } /* Update entry with the new list of selected * objects */ FileBrowserEntrySetSelectedObjects(fb); break; case GDK_BUTTON2: /* Scroll */ /* 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 ); break; case GDK_BUTTON3: /* Right-click menu */ /* Right-click menu */ if(fb->list_menu != NULL) { GtkMenu *menu = GTK_MENU(fb->list_menu); 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 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 DND icon based on the newly selected * object */ FileBrowserListObjectSetDNDIcon(fb, i); } /* Map menu */ gtk_menu_popup( menu, NULL, NULL, NULL, NULL, button->button, button->time ); } break; } fb->button = button->button; fb->last_x = (gint)button->x; fb->last_y = (gint)button->y; FileBrowserListQueueDraw(fb); status = TRUE; break; case GDK_BUTTON_RELEASE: button = (GdkEventButton *)event; /* Handle by button number */ switch(button->button) { case GDK_BUTTON1: if(!(button->state & GDK_CONTROL_MASK) && !(button->state & GDK_SHIFT_MASK) ) { gulong double_click_int = 800, last_time = fb->last_button1_release_time, cur_time = (gulong)button->time, dt = ( (last_time > 0) && (cur_time > last_time) ) ? (cur_time - last_time) : 0l; #if 0 /* Slow double click? */ if((dt > double_click_int) && (dt <= (2 * double_click_int)) (dt != 0) ) { /* Last single selected object did not change? */ if(fb->last_single_select_object > -1) { /* Call rename callback */ FileBrowserRenameCB(NULL, fb); } } #endif /* Double click? */ if((dt <= double_click_int) && (dt != 0)) { /* Last single selected object did not change? */ if(fb->last_single_select_object > -1) { /* Call entry enter callback */ FileBrowserEntryEnterCB(fb->entry, fb); } } } fb->last_button1_release_time = (gulong)button->time; break; case GDK_BUTTON2: /* Stop scrolling as needed */ if(gdk_pointer_is_grabbed()) gdk_pointer_ungrab(button->time); break; } fb->button = 0; status = TRUE; break; case GDK_MOTION_NOTIFY: motion = (GdkEventMotion *)event; #define STOP_SIGNAL_EMIT { \ gtk_signal_emit_stop_by_name( \ GTK_OBJECT(widget), "motion_notify_event" \ ); \ } switch(fb->button) { gint dx, dy; GtkAdjustment *adj; GtkRange *range; case GDK_BUTTON1: if(motion->is_hint) { gint x, y; GdkModifierType mask; gdk_window_get_pointer( motion->window, &x, &y, &mask ); } break; case GDK_BUTTON2: if(motion->is_hint) { gint x, y; GdkModifierType mask; gdk_window_get_pointer( motion->window, &x, &y, &mask ); dx = x - fb->last_x; dy = y - fb->last_y; fb->last_x = x; fb->last_y = y; } else { dx = (gint)(motion->x - fb->last_x); dy = (gint)(motion->y - fb->last_y); fb->last_x = (gint)motion->x; fb->last_y = (gint)motion->y; } range = (GtkRange *)fb->list_hsb; adj = (range != NULL) ? range->adjustment : NULL; if((adj != NULL) && (dx != 0) && (fb->list_format == FB_LIST_FORMAT_STANDARD) ) { if((adj->upper - adj->page_size) > adj->lower) gtk_adjustment_set_value( adj, CLIP( adj->value - dx, adj->lower, adj->upper - adj->page_size ) ); } range = (GtkRange *)fb->list_vsb; adj = (range != NULL) ? range->adjustment : NULL; if((adj != NULL) && (dy != 0) && ((fb->list_format == FB_LIST_FORMAT_VERTICAL) || (fb->list_format == FB_LIST_FORMAT_VERTICAL_DETAILS) ) ) { if((adj->upper - adj->page_size) > adj->lower) gtk_adjustment_set_value( adj, CLIP( adj->value - dy, adj->lower, adj->upper - adj->page_size ) ); } STOP_SIGNAL_EMIT status = TRUE; break; } #undef STOP_SIGNAL_EMIT break; } return(status); } /* * 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; FileBrowser *fb = FILE_BROWSER(data); if(fb == NULL) return; if(fb->selection != NULL) g_list_free(fb->selection); fb->selection = NULL; for(i = 0; i < fb->total_objects; 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; GList *glist = NULL; FileBrowser *fb = FILE_BROWSER(data); if(fb == NULL) return; for(i = 0; i < fb->total_objects; i++) { if(!OBJISSEL(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); } /* * Used by FileBrowserRenameCB() as the apply callback for * the floating prompt. */ static void FileBrowserRenameFPromptCB( gpointer data, const gchar *value ) { gint i; GList *glist; 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(fb->toplevel); \ CDialogGetResponse( \ TITLE_RENAME_FAILED, \ (s), NULL, \ CDIALOG_ICON_WARNING, \ CDIALOG_BTNFLAG_OK, \ CDIALOG_BTNFLAG_OK \ ); \ CDialogSetTransientFor(NULL); \ } /* No directory deliminators may exist in the new value */ if(strchr(value, G_DIR_SEPARATOR) != NULL) { gchar *buf = 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(buf); g_free(buf); return; } /* Get last selected object */ glist = fb->selection_end; i = (glist != NULL) ? (gint)glist->data : -1; o = FileBrowserGetObject(fb, i); if((o != NULL) ? (o->full_path != NULL) : FALSE) { const gchar *cur_location = FileBrowserGetLocation(fb); gchar *old_full_path = STRDUP(o->full_path), *new_full_path = STRDUP( PrefixPaths(cur_location, value) ); struct stat lstat_buf; /* New name and old name the same? */ if(!strcmp(o->name, value)) { } /* Make that the new name does not refer to an object that * already exists */ #ifdef WIN32 else if(stat(new_full_path, &lstat_buf) == 0) #else else if(lstat(new_full_path, &lstat_buf) == 0) #endif { gchar *buf = g_strdup_printf( "An object by the name of \"%s\" already exists.", value ); MESSAGE_RENAME_FAILED(buf); g_free(buf); } /* Rename */ else if(rename(old_full_path, new_full_path)) { gchar *buf; #if defined(PROG_LANGUAGE_SPANISH) buf = g_strdup_printf( "Incapaz de reagrupar \"%s\" a \"%s\".", o->name, value ); #elif defined(PROG_LANGUAGE_FRENCH) buf = g_strdup_printf( "Incapable pour renommer \"%s\" à \"%s\".", o->name, value ); #elif defined(PROG_LANGUAGE_GERMAN) buf = g_strdup_printf( "Unfähig, \"%s\" zu \"%s\" umzubenennen.", o->name, value ); #elif defined(PROG_LANGUAGE_ITALIAN) buf = g_strdup_printf( "Incapace per rinominare \"%s\" a \"%s\".", o->name, value ); #elif defined(PROG_LANGUAGE_DUTCH) buf = g_strdup_printf( "Onbekwaam \"%s\" aan \"%s\" te herdopen.", o->name, value ); #elif defined(PROG_LANGUAGE_PORTUGUESE) buf = g_strdup_printf( "Incapaz a \"%s\" de rename a \"%s\".", o->name, value ); #elif defined(PROG_LANGUAGE_NORWEGIAN) buf = g_strdup_printf( "Maktesløs ombenevne \"%s\" til \"%s\".", o->name, value ); #else switch(errno) { case EISDIR: buf = g_strdup_printf( "\"%s\" is an existing directory, but \"%s\" is not a directory.", value, o->name ); break; case ENOTEMPTY: case EEXIST: buf = g_strdup_printf( "\"%s\" is a non-empty directory.", value ); break; case EBUSY: buf = g_strdup_printf( "\"%s\" or \"%s\" is a directory that is currently in use.", o->name, value ); break; case EINVAL: buf = g_strdup_printf( "\"%s\" contains a path prefix of \"%s\" or an attempt\n\ was made to make a directory a subdirectory of itself.", value, o->name ); break; case EMLINK: buf = g_strdup_printf( "\"%s\" already has the maximum number of links\n\ to it, or it was a directory and the directory containing\n\ \"%s\" has reached the maximum number of links.", o->name, value ); break; case ENOTDIR: buf = g_strdup_printf( "A compoent in \"%s\"\n\ is not a directory or \"%s\" is a directory and\n\ \"%s\" exists but is not a directory.", old_full_path, o->name, value ); break; case EACCES: buf = g_strdup_printf( "You do not have sufficient permission to rename\n\ \"%s\" to \"%s\".", o->name, value ); break; case ENAMETOOLONG: buf = g_strdup_printf( "The name \"%s\" is too long.", value ); break; case ENOENT: buf = g_strdup_printf( "One or more directory compoents of \"%s\" or \"%s\"\n\ is a dangling symbolic link.", value, o->name ); break; case ENOMEM: buf = STRDUP("The system is out of memory."); break; case EROFS: buf = g_strdup_printf( "The object \"%s\" is on a read-only filesystem\n\ and cannot be renamed.", o->name ); break; #ifdef ELOOP case ELOOP: buf = g_strdup_printf( "Too many symbolic links were encountered when trying to\n\ resolve \"%s\" or \"%s\".", value, o->name ); break; #endif case ENOSPC: buf = g_strdup_printf( "The device that \"%s\" is on is out of free space.", o->name ); break; default: buf = g_strdup_printf( "Unable to rename \"%s\" to \"%s\".", o->name, value ); break; } #endif MESSAGE_RENAME_FAILED(buf); g_free(buf); } else { /* Rename successful */ /* Update list */ FileBrowserListUpdate(fb, NULL); /* Reselect object */ for(i = 0; i < fb->total_objects; i++) { o = fb->object[i]; if((o != NULL) ? (o->full_path == NULL) : TRUE) continue; if(!strcmp(o->full_path, 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 selected objects on entry */ FileBrowserEntrySetSelectedObjects(fb); } g_free(old_full_path); g_free(new_full_path); } #undef MESSAGE_RENAME_FAILED #undef TITLE_RENAME_FAILED } /* * Rename callback. */ static void FileBrowserRenameCB(GtkWidget *widget, gpointer data) { gint i, x, y, width, height; const GList *glist; const FileBrowserObject *o; FileBrowser *fb = FILE_BROWSER(data); if((fb == NULL) || FPromptIsQuery()) return; /* Get last selected object */ glist = fb->selection_end; i = (glist != NULL) ? (gint)glist->data : -1; o = FileBrowserGetObject(fb, i); if(o == NULL) return; /* Get fprompt geometry based on list display format */ 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) { GtkRange *range = GTK_RANGE(fb->list_vsb); GtkAdjustment *adj = range->adjustment; y -= (gint)((adj != NULL) ? adj->value : 0.0f); } break; case FB_LIST_FORMAT_STANDARD: if(fb->list_hsb != NULL) { GtkRange *range = GTK_RANGE(fb->list_hsb); GtkAdjustment *adj = range->adjustment; x -= (gint)((adj != NULL) ? adj->value : 0.0f); } 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(fb->toplevel); FPromptSetPosition(x - 2, y - 2); FPromptMapQuery( NULL, /* No label */ o->name, NULL, /* No tooltip */ FPROMPT_MAP_NO_MOVE, /* Map code */ width, height, /* Width and height */ 0, /* No buttons */ fb, /* Data */ NULL, /* No browse callback */ FileBrowserRenameFPromptCB, NULL /* No cancel callback */ ); } /* * CHMod fprompt callback. */ static void FileBrowserCHModFPromptCB( gpointer data, const gchar *value ) { gint i; GList *glist; 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 Mode Failed" #endif #define MESSAGE_CHMOD_FAILED(s) { \ CDialogSetTransientFor(fb->toplevel); \ CDialogGetResponse( \ TITLE_CHMOD_FAILED, \ (s), NULL, \ CDIALOG_ICON_WARNING, \ CDIALOG_BTNFLAG_OK, CDIALOG_BTNFLAG_OK \ ); \ CDialogSetTransientFor(NULL); \ } /* Get last selected object */ glist = fb->selection_end; i = (glist != NULL) ? (gint)glist->data : -1; o = FileBrowserGetObject(fb, i); if((o != NULL) ? (o->full_path != NULL) : FALSE) { #if defined(S_IRUSR) && defined(S_IWUSR) && defined(S_IXUSR) mode_t m = 0; const gchar *s = value; /* Begin parsing value to obtain new 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(o->full_path, m)) { gchar *buf; #if defined(PROG_LANGUAGE_SPANISH) buf = g_strdup_printf( "Incapaz de cambiar el modo de \"%s\".", o->name ); #elif defined(PROG_LANGUAGE_FRENCH) buf = g_strdup_printf( "Incapable pour changer le mode de \"%s\".", o->name ); #elif defined(PROG_LANGUAGE_GERMAN) buf = g_strdup_printf( "Unfähig, den modus von \"%s\" zu ändern.", o->name ); #elif defined(PROG_LANGUAGE_ITALIAN) buf = g_strdup_printf( "Incapace per cambiare il modo di \"%s\".", o->name ); #elif defined(PROG_LANGUAGE_DUTCH) buf = g_strdup_printf( "Onbekwame de modus van \"%s\" te veranderen.", o->name ); #elif defined(PROG_LANGUAGE_PORTUGUESE) buf = g_strdup_printf( "Incapaz de mudar o modo de \"%s\".", o->name ); #elif defined(PROG_LANGUAGE_NORWEGIAN) buf = g_strdup_printf( "Maktesløs forandre modus av \"%s\".", o->name ); #else switch(errno) { case EPERM: buf = g_strdup_printf( "You do not have sufficient permission to change the permissions\n\ of \"%s\".", o->name ); break; case EROFS: buf = g_strdup_printf( "The object \"%s\" is on a read-only filesystem\n\ and its permissions cannot be changed.", o->name ); break; case ENAMETOOLONG: buf = g_strdup_printf( "The name \"%s\" is too long.", o->name ); break; case ENOMEM: buf = STRDUP( "The system is out of memory." ); break; case ENOTDIR: buf = STRDUP( "A compoent of the path prefix is not a directory." ); break; case EACCES: buf = STRDUP( "Search permission is denied on a compoent of the path prefix." ); break; #ifdef ELOOP case ELOOP: buf = g_strdup_printf( "Too many symbolic links were encountered when trying to\n\ resolve \"%s\".", o->name ); break; #endif default: buf = g_strdup_printf( "Unable to change the mode of \"%s\".", o->name ); break; } #endif MESSAGE_CHMOD_FAILED(buf); g_free(buf); } else { /* Statistics */ #ifdef WIN32 if(stat(o->full_path, &o->lstat_buf)) #else if(lstat(o->full_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 } /* * CHMod callback. */ static void FileBrowserCHModCB(GtkWidget *widget, gpointer data) { gint i, x, y, width, height; mode_t m; gchar *value; const GList *glist; const FileBrowserObject *o; const FileBrowserColumn *column, *column2; FileBrowser *fb = FILE_BROWSER(data); if((fb == NULL) || FPromptIsQuery()) return; /* Get 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 /* Cannot chmod links */ if(S_ISLNK(m)) { CDialogSetTransientFor(fb->toplevel); CDialogGetResponse( "Change Mode Failed", "You cannot change the mode of symbolic link objects", 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 not supported we cannot chmod */ if(TRUE) { CDialogSetTransientFor(fb->toplevel); CDialogGetResponse( "Change Mode Failed", "Changing the mode 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 /* Get fprompt geometry based on list display format */ 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) { GtkRange *range = GTK_RANGE(fb->list_vsb); GtkAdjustment *adj = range->adjustment; y -= (gint)((adj != NULL) ? adj->value : 0.0f); } 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) { GtkRange *range = GTK_RANGE(fb->list_hsb); GtkAdjustment *adj = range->adjustment; x -= (gint)((adj != NULL) ? adj->value : 0.0f); } 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(fb->toplevel); FPromptSetPosition(x - 2, y - 2); FPromptMapQuery( NULL, /* No label */ value, NULL, /* No tooltip */ FPROMPT_MAP_NO_MOVE, /* Map code */ width, height, /* Width and height */ 0, /* No buttons */ fb, /* Data */ NULL, /* No browse callback */ FileBrowserCHModFPromptCB, NULL /* No cancel callback */ ); g_free(value); } /* * List object delete callback. */ static void FileBrowserDeleteCB(GtkWidget *widget, gpointer data) { gint objects_deleted = 0; gint i, response; gchar *buf; GList *glist; FileBrowserObject *o; FileBrowser *fb = FILE_BROWSER(data); if(fb == NULL) 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(fb->toplevel); \ CDialogGetResponse( \ TITLE_DELETE_FAILED, \ (s), NULL, \ CDIALOG_ICON_WARNING, \ CDIALOG_BTNFLAG_OK, CDIALOG_BTNFLAG_OK \ ); \ CDialogSetTransientFor(NULL); \ } if(CDialogIsQuery()) return; /* Get selection */ 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->name != NULL) : FALSE) buf = 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 "Are you sure you want to delete \"%s\"?" #endif , o->name ); else buf = STRDUP( "Are you sure you want to delete this (unnamed) object?" ); } else { buf = 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?\n" #endif , i ); } /* Confirm delete */ CDialogSetTransientFor(fb->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 , buf, NULL, (guint8 **)icon_trash_32x32_xpm, CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_NO, CDIALOG_BTNFLAG_NO ); CDialogSetTransientFor(NULL); g_free(buf); if(response != CDIALOG_RESPONSE_YES) { FileBrowserSetBusy(fb, FALSE); return; } /* Iterate through selected objects */ while(glist != NULL) { i = (gint)glist->data; o = FileBrowserGetObject(fb, i); if((o != NULL) ? (o->full_path != NULL) : FALSE) { const gchar *full_path = o->full_path; if(ISLPATHDIR(full_path)) { /* Delete directory */ if(rmdir(full_path)) { const gchar *name = o->name; #if defined(PROG_LANGUAGE_SPANISH) buf = g_strdup_printf( "Incapaz de borrar guía \"%s\".", name ); #elif defined(PROG_LANGUAGE_FRENCH) buf = g_strdup_printf( "Incapable pour effacer \"%s\" d'annuaire.", name ); #elif defined(PROG_LANGUAGE_GERMAN) buf = g_strdup_printf( "Unfähig, Verzeichnis \"%s\" zu löschen.", name ); #elif defined(PROG_LANGUAGE_ITALIAN) buf = g_strdup_printf( "Incapace per cancellare \"%s\" di elenco.", name ); #elif defined(PROG_LANGUAGE_DUTCH) buf = g_strdup_printf( "Onbekwaam gids \"%s\" te schrappen.", name ); #elif defined(PROG_LANGUAGE_PORTUGUESE) buf = g_strdup_printf( "Incapaz de anular \"%s\" de guia.", name ); #elif defined(PROG_LANGUAGE_NORWEGIAN) buf = g_strdup_printf( "Maktesløs stryke katalog \"%s\".", name ); #else switch(errno) { case EPERM: buf = g_strdup_printf( "The filesystem that the directory \"%s\" is on does\n\ not permit the removal of directory objects.", name ); break; case EACCES: buf = g_strdup_printf( "You do not have sufficient permission to delete \"%s\".", name ); break; case ENAMETOOLONG: buf = g_strdup_printf( "The name of \"%s\" is too long.", name ); break; case ENOENT: buf = g_strdup_printf( "A directory compoent of \"%s\" does not exist or\n\ is a dangling symbolic link.", name ); break; case ENOTEMPTY: buf = g_strdup_printf( "The directory \"%s\" is not empty, you must\n\ make sure that the directory is empty before you can delete it.", name ); break; case EBUSY: buf = g_strdup_printf( "The directory \"%s\" is currently in use and\n\ cannot be deleted.", name ); break; case ENOMEM: buf = STRDUP( "The system is out of memory." ); break; case EROFS: buf = g_strdup_printf( "\"%s\" is located on a read-only filesystem.", name ); break; #ifdef ELOOP case ELOOP: buf = g_strdup_printf( "Too many symbolic links were encountered while resolving\n\ \"%s\".", name ); break; #endif default: buf = g_strdup_printf( "Unable to delete directory \"%s\".", name ); break; } #endif MESSAGE_DELETE_FAILED(buf); g_free(buf); } else { objects_deleted++; } } else { /* Delete object */ if(unlink((const char *)full_path)) { const gchar *name = o->name; #if defined(PROG_LANGUAGE_SPANISH) buf = g_strdup_printf( "Incapaz de borrar \"%s\".", name ); #elif defined(PROG_LANGUAGE_FRENCH) buf = g_strdup_printf( "Incapable pour effacer \"%s\".", name ); #elif defined(PROG_LANGUAGE_GERMAN) buf = g_strdup_printf( "Unfähig, \"%s\" zu löschen.", name ); #elif defined(PROG_LANGUAGE_ITALIAN) buf = g_strdup_printf( "Incapace per cancellare \"%s\".", name ); #elif defined(PROG_LANGUAGE_DUTCH) buf = g_strdup_printf( "Onbekwaam \"%s\" te schrappen.", name ); #elif defined(PROG_LANGUAGE_PORTUGUESE) buf = g_strdup_printf( "Incapaz de anular \"%s\".", name ); #elif defined(PROG_LANGUAGE_NORWEGIAN) buf = g_strdup_printf( "Maktesløs stryke \"%s\".", name ); #else switch(errno) { case EACCES: buf = g_strdup_printf( "You do not have sufficient permission to delete \"%s\".", name ); break; case EPERM: buf = g_strdup_printf( "You do not have sufficient permission to delete \"%s\".", name ); break; case ENAMETOOLONG: buf = g_strdup_printf( "The name of \"%s\" is too long.", name ); break; case ENOENT: buf = g_strdup_printf( "A directory compoent of \"%s\" does not exist or\n\ is a dangling symbolic link.", name ); break; case ENOTDIR: buf = g_strdup_printf( "A directory compoent of \"%s\" is not a directory.", name ); break; case ENOMEM: buf = STRDUP( "The system is out of memory." ); break; case EROFS: buf = g_strdup_printf( "\"%s\" is located on a read-only filesystem.", name ); break; #ifdef ELOOP case ELOOP: buf = g_strdup_printf( "Too many symbolic links were encountered while resolving\n\ \"%s\".", name ); break; #endif case EIO: buf = g_strdup_printf( "An I/O error occured while attempting to delete \"%s\".", name ); break; default: buf = g_strdup_printf( "Unable to delete \"%s\".", name ); break; } #endif MESSAGE_DELETE_FAILED(buf); g_free(buf); } else { objects_deleted++; } } } glist = g_list_next(glist); } /* Need to refresh everything due to delete */ FileBrowserRefreshCB(fb->refresh_btn, fb); FileBrowserSetBusy(fb, FALSE); #undef MESSAGE_DELETE_FAILED #undef TITLE_DELETE_FAILED } /* * Entry complete path "key_press_event" or "key_release_event" * signal callback. */ static gint FileBrowserEntryCompletePathKeyCB( GtkWidget *widget, GdkEventKey *key, gpointer data ) { gint status = FALSE; gboolean press; GtkEntry *entry = GTK_ENTRY(widget); FileBrowser *fb = FILE_BROWSER(data); if((entry == NULL) || (key == NULL) || (fb == NULL)) return(status); press = (key->type == GDK_KEY_PRESS) ? TRUE : FALSE; #define SIGNAL_EMIT_STOP { \ gtk_signal_emit_stop_by_name( \ GTK_OBJECT(widget), \ press ? \ "key_press_event" : "key_release_event" \ ); \ } switch(key->keyval) { case GDK_Tab: if(press) { gchar *s = STRDUP(gtk_entry_get_text(entry)); if(s != NULL) { gint status; /* 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(!ISPATHABSOLUTE(s)) { if(STRISEMPTY(s)) { g_free(s); s = STRDUP(FileBrowserGetLocation(fb)); } else { gchar *s2 = STRDUP(PrefixPaths( FileBrowserGetLocation(fb), s )); g_free(s); s = s2; } } s = CompletePath(s, &status); gtk_entry_set_text(entry, (s != NULL) ? s : ""); gtk_entry_set_position(entry, -1); g_free(s); switch(status) { case COMPLETE_PATH_NONE: case COMPLETE_PATH_AMBIGUOUS: gdk_beep(); break; } } } SIGNAL_EMIT_STOP status = TRUE; break; } #undef SIGNAL_EMIT_STOP return(status); } /* * Entry enter callback. * * This function is slightly different from the OK callback * since it will switch to a directory if the single value * is a directory. * * This function will call the OK callback if there are * multiple values specified (when it sees a ',' deliminator) * or the single object is specified and (if it actually exists) * does not lead to a directory. */ static void FileBrowserEntryEnterCB(GtkWidget *widget, gpointer data) { gchar *s; GtkWidget *w; FileBrowser *fb = FILE_BROWSER(data); if(fb == NULL) return; w = fb->entry; if(w == NULL) return; /* Get file name entry value */ s = gtk_entry_get_text(GTK_ENTRY(w)); if((s != NULL) ? (*s == '\0') : TRUE) return; s = STRDUP(s); /* Make a copy of s */ /* Filter specified? */ if((strchr(s, '*') != NULL) || (strchr(s, '?') != NULL)) { /* Reget listing with the newly specified filter */ FileBrowserListUpdate(fb, s); } /* Multiple objects specified? */ else if(strchr(s, ',')) { /* Multiple objects selected, just call the OK callback */ FileBrowserOKCB(fb->ok_btn, fb); } /* Current directory specified? */ else if(!strcmp(s, ".")) { /* Do nothing */ } /* Parent directory specified? */ else if(!strcmp(s, "..")) { /* Go to parent location */ const gchar *cur_location = FileBrowserGetLocation(fb); gchar *parent = (cur_location != NULL) ? g_dirname(cur_location) : NULL; FileBrowserSetLocation(fb, parent); g_free(parent); } /* Home directory specified? */ else if(*s == '~') { /* Go to home directory */ FileBrowserSetLocation(fb, s); } else { /* All else assume single object specified, it may or may * not actually exist */ gchar *full_path; struct stat stat_buf; /* 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(s)) { full_path = STRDUP(s); } else { const gchar *cur_location = FileBrowserGetLocation(fb); full_path = STRDUP(PrefixPaths(cur_location, s)); } /* Check if the object specified by full_path exists and if * it leads to a directory */ if(stat(full_path, &stat_buf)) { /* Cannot stat object (note that it may exist locally * as a symbolic link), the user may want to save as a * new object, so go ahead and call the OK callback on * this */ FileBrowserOKCB(fb->ok_btn, 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 FileBrowserOKCB(fb->ok_btn, fb); } g_free(full_path); } g_free(s); } /* * File type list selected value changed callback. */ static void FileBrowserTypeListChangeCB( GtkWidget *widget, gpointer data, GList *glist ) { const gchar *s, *ext; gchar *s2; GtkEntry *entry; GtkCombo *combo; fb_type_struct *t; FileBrowser *fb = FILE_BROWSER(data); if(fb == NULL) return; combo = (GtkCombo *)fb->type_combo; if(combo == NULL) return; entry = GTK_ENTRY(combo->entry); /* Get selected file type value */ s = gtk_entry_get_text(entry); if((s != NULL) ? (*s == '\0') : TRUE) return; /* Parse the selected file type and store the values in the * current file type structure. * * The format of s will be: * * "desc(ext)" * * "Description of Format (*.ext1 *.ext2)" */ t = &fb->cur_type; /* Get name */ g_free(t->name); t->name = STRDUP(s); s2 = strrchr(t->name, '('); if(s2 != NULL) { if(s2 > t->name) s2--; while((s2 > t->name) && ISBLANK(*s2)) s2--; *(s2 + 1) = '\0'; } /* Get extensions list */ g_free(t->ext); t->ext = NULL; ext = strrchr(s, '('); if(ext != NULL) { ext++; while(ISBLANK(*ext)) ext++; t->ext = STRDUP(ext); s2 = strchr(t->ext, ')'); if(s2 != NULL) { if(s2 > t->ext) s2--; while((s2 > t->ext) && ISBLANK(*s2)) s2--; *(s2 + 1) = '\0'; } } /* Update values that depend on file type changes only if the * file type change freeze count is 0 */ if(fb->freeze_count > 0) return; fb->freeze_count++; /* Need to update list due to type change */ FileBrowserListUpdate(fb, NULL); fb->freeze_count--; } /* * Initializes the file browser. */ gint FileBrowserInit(void) { const gint border_major = 5, border_minor = 2; gpointer w_ptr1, w_ptr2; GList *glist; GdkWindow *window; GtkAdjustment *adj; GtkAccelGroup *accelgrp; GtkWidget *w, *parent, *parent2, *parent3, *parent4, *parent5; GtkEntry *entry; GtkCombo *combo; pulist_struct *pulist; FileBrowser *fb = &file_browser; /* Reset values */ fb->busy_count = 0; fb->freeze_count = 0; fb->accelgrp = accelgrp = gtk_accel_group_new(); 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->vsb_map_state = FALSE; fb->hsb_map_state = FALSE; fb->cur_location = NULL; fb->block_loop_level = 0; fb->user_response = FALSE; fb->list_format = FB_LIST_FORMAT_STANDARD; fb->column = NULL; fb->total_columns = 0; memset(&fb->cur_type, 0x00, sizeof(fb_type_struct)); fb->selected_path = NULL; fb->total_selected_paths = 0; fb->icon = NULL; fb->total_icons = 0; #ifdef 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 #ifdef WIN32 fb->home_path = STRDUP("C:\\"); #else fb->home_path = STRDUP(g_getenv("HOME")); #endif fb->drive_path = NULL; fb->total_drive_paths = 0; fb->focus_object = -1; fb->selection = fb->selection_end = NULL; fb->object = NULL; fb->total_objects = 0; fb->objects_per_row = 0; fb->last_single_select_object = -1; fb->show_hidden_objects = TRUE; fb->button = 0; fb->last_x = 0; fb->last_y = 0; fb->last_button1_release_time = 0l; /* Toplevel */ fb->toplevel = w = gtk_window_new(GTK_WINDOW_DIALOG); gtk_widget_set_usize(w, FB_WIDTH, FB_HEIGHT); gtk_window_set_title(GTK_WINDOW(w), "Select File"); #ifdef PROG_NAME gtk_window_set_wmclass( GTK_WINDOW(w), "fileselection", PROG_NAME ); #endif gtk_widget_realize(w); window = w->window; if(window != NULL) { GdkGeometry geo; gdk_window_set_decorations( window, GDK_DECOR_BORDER | GDK_DECOR_TITLE | GDK_DECOR_MENU | GDK_DECOR_MINIMIZE ); gdk_window_set_functions( window, GDK_FUNC_MOVE | GDK_FUNC_MINIMIZE | 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 ); } gtk_signal_connect( GTK_OBJECT(w), "delete_event", GTK_SIGNAL_FUNC(FileBrowserDeleteEventCB), fb ); gtk_container_border_width(GTK_CONTAINER(w), 0); gtk_window_add_accel_group(GTK_WINDOW(w), accelgrp); parent = w; /* Main vbox */ 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; /* Hbox for directory popup list label and drawing area*/ 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_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0); gtk_widget_show(w); /* Hbox for Directory popup list and map button */ w = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, 0); gtk_widget_show(w); parent4 = w; /* Directory popup list frame and drawing area */ w = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, TRUE, 0); gtk_widget_show(w); parent5 = w; /* Drawing area */ fb->dir_pulist_da = w = gtk_drawing_area_new(); gtk_widget_set_usize(w, -1, 20 + (2 * 0)); 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 ); gtk_signal_connect( GTK_OBJECT(w), "expose_event", GTK_SIGNAL_FUNC(FileBrowserDirPUListDAEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "focus_in_event", GTK_SIGNAL_FUNC(FileBrowserDirPUListDAEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "focus_out_event", GTK_SIGNAL_FUNC(FileBrowserDirPUListDAEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "button_press_event", GTK_SIGNAL_FUNC(FileBrowserDirPUListDAEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "button_release_event", GTK_SIGNAL_FUNC(FileBrowserDirPUListDAEventCB), fb ); gtk_container_add(GTK_CONTAINER(parent5), w); gtk_widget_show(w); /* Directory popup list button */ fb->dir_pulist_btn = w = PUListNewMapButtonArrow( GTK_ARROW_DOWN, GTK_SHADOW_OUT, FileBrowserDirPUListMapCB, fb ); gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0); gtk_widget_show(w); /* Directory popup list */ fb->dir_pulist = pulist = PUListNew(); /* 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 ); #if 0 gtk_accel_group_add( accelgrp, GDK_BackSpace, 0, GTK_ACCEL_VISIBLE, GTK_OBJECT(w), "clicked" ); #endif 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 ); #if 0 gtk_accel_group_add( accelgrp, GDK_Insert, 0, GTK_ACCEL_VISIBLE, GTK_OBJECT(w), "clicked" ); #endif 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 ); #if 0 gtk_accel_group_add( accelgrp, GDK_F2, 0, GTK_ACCEL_VISIBLE, GTK_OBJECT(w), "clicked" ); #endif 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 ); #if 0 gtk_accel_group_add( accelgrp, GDK_F5, 0, GTK_ACCEL_VISIBLE, GTK_OBJECT(w), "clicked" ); #endif 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 */ fb->show_hidden_objects_tb = w = GUIToggleButtonPixmap( (guint8 **)icon_file_hidden_20x20_xpm ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(w), fb->show_hidden_objects ); 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 ); GUISetWidgetTip(w, "Show Hidden Objects" ); gtk_widget_show(w); /* Hbox for list format buttons */ w = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0); gtk_widget_show(w); parent3 = w; 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); 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); */ 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; /* Frame and vbox with in for parenting the list header and * the list. */ 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; w = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(parent3), w); gtk_widget_show(w); parent3 = w; /* List header */ fb->list_header_da = w = gtk_drawing_area_new(); gtk_widget_set_usize(w, FB_LIST_HEADER_WIDTH, FB_LIST_HEADER_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_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), "focus_in_event", GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb ); gtk_signal_connect( GTK_OBJECT(w), "focus_out_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 */ fb->list_da = w = gtk_drawing_area_new(); gtk_widget_set_usize(w, FB_LIST_WIDTH, FB_LIST_HEIGHT); GTK_WIDGET_SET_FLAGS(w, GTK_CAN_FOCUS | GTK_CAN_DEFAULT); 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 ); 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_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, 0); gtk_widget_show(w); if(w != NULL) { const GtkTargetEntry dnd_tar_types[] = { {"text/plain", 0, 0}, {"text/uri-list", 0, 1}, {"STRING", 0, 2} }; const GtkTargetEntry dnd_src_types[] = { {"text/plain", 0, 0}, {"text/uri-list", 0, 1}, {"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 ); gtk_signal_connect_after( GTK_OBJECT(w), "drag_motion", GTK_SIGNAL_FUNC(FileBrowserDragmotionCB), fb ); GUIDNDSetSrc( w, dnd_src_types, sizeof(dnd_src_types) / sizeof(GtkTargetEntry), GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK, /* Actions */ GDK_BUTTON1_MASK, /* Buttons */ NULL, FileBrowserDragDataGetCB, FileBrowserDragDataDeleteCB, NULL, fb ); } /* 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 menu */ if(TRUE) { GtkWidget *menu = (GtkWidget *)GUIMenuCreate(); guint8 **icon; const gchar *label; guint accel_key, accel_mods; gpointer mclient_data = fb; void (*func_cb)(GtkWidget *w, gpointer); #define DO_ADD_MENU_ITEM_LABEL { \ w = GUIMenuItemCreate( \ menu, GUI_MENU_ITEM_TYPE_LABEL, accelgrp, \ icon, label, accel_key, accel_mods, &w_ptr1, \ mclient_data, func_cb \ ); \ } #define DO_ADD_MENU_ITEM_CHECK { \ w = GUIMenuItemCreate( \ menu, GUI_MENU_ITEM_TYPE_CHECK, accelgrp, \ icon, label, accel_key, accel_mods, &w_ptr1, \ mclient_data, func_cb \ ); \ } #define DO_ADD_MENU_SEP { \ w = GUIMenuItemCreate( \ menu, GUI_MENU_ITEM_TYPE_SEPARATOR, NULL, \ NULL, NULL, 0, 0, NULL, \ 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 = FileBrowserEntryEnterCB; DO_ADD_MENU_ITEM_LABEL /* fb->select_mi = w; */ DO_ADD_MENU_SEP 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 "Parent" #endif ; accel_key = GDK_BackSpace; accel_mods = 0; func_cb = FileBrowserGoToParentCB; DO_ADD_MENU_ITEM_LABEL /* fb->goto_parent_mi = w; */ 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; DO_ADD_MENU_ITEM_LABEL /* fb->refresh_mi = w; */ DO_ADD_MENU_SEP 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_Insert; accel_mods = 0; func_cb = FileBrowserNewDirectoryCB; DO_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; DO_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; DO_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; DO_ADD_MENU_ITEM_LABEL /* fb->delete_mi = w; */ DO_ADD_MENU_SEP 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 = 'a'; accel_mods = GDK_CONTROL_MASK; func_cb = FileBrowserSelectAllCB; DO_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 = 'u'; accel_mods = GDK_CONTROL_MASK; func_cb = FileBrowserUnselectAllCB; DO_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 = 'i'; accel_mods = GDK_CONTROL_MASK; func_cb = FileBrowserInvertSelectionCB; DO_ADD_MENU_ITEM_LABEL /* fb->invert_selection_mi = w; */ fb->list_menu = menu; #undef DO_ADD_MENU_ITEM_LABEL #undef DO_ADD_MENU_ITEM_CHECK #undef DO_ADD_MENU_SEP } 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; /* Vbox for location entry and file type combo */ w = gtk_vbox_new(FALSE, border_minor); gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0); gtk_widget_show(w); parent3 = w; /* File name entry */ w = GUIPromptBar( NULL, #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 , &w_ptr1, &w_ptr2 ); gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0); gtk_widget_show(w); fb->entry = w = GTK_WIDGET(w_ptr2); entry = GTK_ENTRY(w); 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(FileBrowserEntryEnterCB), fb ); 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[] = { {"text/plain", 0, 0}, {"text/uri-list", 0, 1}, {"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 ); gtk_signal_connect_after( GTK_OBJECT(w), "drag_motion", GTK_SIGNAL_FUNC(FileBrowserDragmotionCB), fb ); } /* File types combo */ glist = NULL; glist = g_list_append(glist, STRDUP(FB_DEFAULT_TYPE_STR)); w = GUIComboCreate( #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 , (const gchar *)glist->data, glist, /* Initial GList */ 100, /* Maximum items */ &w_ptr1, /* GtkCombo return */ fb, NULL, FileBrowserTypeListChangeCB ); gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0); gtk_widget_show(w); g_list_foreach(glist, (GFunc)g_free, NULL); g_list_free(glist); combo = (GtkCombo *)w_ptr1; entry = GTK_ENTRY(combo->entry); fb->type_combo = GTK_WIDGET(combo); gtk_entry_set_editable(entry, FALSE); GUISetWidgetTip( GTK_WIDGET(entry), #if defined(PROG_LANGUAGE_SPANISH) "Escoja el tipo del archivo que usted buscan" #elif defined(PROG_LANGUAGE_FRENCH) "Choisir le type de fichier que vous cherchez" #elif defined(PROG_LANGUAGE_GERMAN) "Wählen Sie die Art Akte, die Sie suchen aus" #elif defined(PROG_LANGUAGE_ITALIAN) "Scegliere il tipo di file che lei cercano" #elif defined(PROG_LANGUAGE_DUTCH) "Selecteer het soort dossier dat u voor kijkt" #elif defined(PROG_LANGUAGE_PORTUGUESE) "Selecione o tipo de arquivo que você procurar" #elif defined(PROG_LANGUAGE_NORWEGIAN) "Velg ut typen arkivet som De leter etter" #else "Select the type of file that you are looking for" #endif ); /* 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 button */ 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 button */ 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 icons, the order is important because the index must * match 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); return(0); } /* * Sets the File Browser's style. */ void FileBrowserSetStyle(GtkRcStyle *rc_style) { GtkWidget *w; FileBrowser *fb = &file_browser; 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 Browser to be a transient for the given * GtkWindow w. * * If w is NULL then transient for will be unset. */ void FileBrowserSetTransientFor(GtkWidget *w) { FileBrowser *fb = &file_browser; if(fb->toplevel != NULL) { GtkWidget *toplevel = fb->toplevel; if(w != NULL) { if(!GTK_IS_WINDOW(w)) return; /* Since the file browser itself has popup windows that may need to be * set modal, we cannot set the file browser itself modal. gtk_window_set_modal( GTK_WINDOW(toplevel), TRUE ); */ gtk_window_set_transient_for( GTK_WINDOW(toplevel), GTK_WINDOW(w) ); } else { /* gtk_window_set_modal( GTK_WINDOW(toplevel), FALSE ); */ gtk_window_set_transient_for( GTK_WINDOW(toplevel), NULL ); } } } /* * Returns TRUE if currently blocking for query. */ gboolean FileBrowserIsQuery(void) { FileBrowser *fb = &file_browser; if(fb->block_loop_level > 0) return(TRUE); else return(FALSE); } /* * Ends query if any and returns a not available response. */ void FileBrowserBreakQuery(void) { FileBrowser *fb = &file_browser; fb->user_response = FALSE; /* Break out of an additional blocking loops */ while(fb->block_loop_level > 0) { gtk_main_quit(); fb->block_loop_level--; } fb->block_loop_level = 0; } /* * Returns the File Browser's toplevel GtkWidget. */ GtkWidget *FileBrowserGetToplevel(void) { FileBrowser *fb = &file_browser; return(fb->toplevel); } /* * Clears the lists and unset the location. */ void FileBrowserReset(void) { FileBrowser *fb = &file_browser; gint i; GtkEntry *entry; GtkCombo *combo; #if 0 /* Unset location */ g_free(fb->cur_location); fb->cur_location = NULL; #endif /* Unset file name */ entry = (GtkEntry *)fb->entry; if(entry != NULL) gtk_entry_set_text(entry, ""); /* Clear dir popup list */ PUListClear(fb->dir_pulist); /* Clear file types list */ combo = (GtkCombo *)fb->type_combo; if(combo != NULL) { entry = GTK_ENTRY(combo->entry); fb->freeze_count++; gtk_entry_set_text(entry, ""); GUIComboSetList(GTK_WIDGET(combo), NULL); fb->freeze_count--; } /* Clear objects listing */ /* Unselect all */ fb->focus_object = -1; g_list_free(fb->selection); fb->selection = fb->selection_end = NULL; /* Delete all objects */ for(i = 0; i < fb->total_objects; i++) FileBrowserObjectDestroyCB(fb->object[i]); g_free(fb->object); fb->object = NULL; fb->total_objects = 0; /* Reset objects per row */ fb->objects_per_row = 0; } /* * Maps the file browser 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 **type, gint total_types, gchar ***path_rtn, gint *path_total_rtns, fb_type_struct **type_rtn ) { GtkWidget *w, *toplevel; FileBrowser *fb = &file_browser; #define RESET_RETURNS { \ if(path_rtn != NULL) \ *path_rtn = NULL; \ if(path_total_rtns != NULL) \ *path_total_rtns = 0; \ \ if(type_rtn != NULL) \ *type_rtn = NULL; \ } /* Do not handle response if already waiting for a response, * return with a not available response code */ if(fb->block_loop_level > 0) { RESET_RETURNS return(FALSE); } /* Reset values */ fb->user_response = FALSE; g_free(fb->cur_type.name); g_free(fb->cur_type.ext); memset(&fb->cur_type, 0x00, sizeof(fb_type_struct)); /* Reset returns */ RESET_RETURNS toplevel = fb->toplevel; /* Reget drive paths */ strlistfree(fb->drive_path, fb->total_drive_paths); fb->drive_path = FileBrowserGetDrivePaths(&fb->total_drive_paths); /* 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 file types list if a new set of file types is given, * otherwise we keep the current file types list (if any) */ w = fb->type_combo; if((type != NULL) && (total_types > 0) && (w != NULL)) { GtkCombo *combo = GTK_COMBO(w); GtkEntry *entry = GTK_ENTRY(combo->entry); GList *glist = NULL; const gchar *first_type_s = NULL; gint i; gchar *s; const fb_type_struct *t; /* Freeze when changing file types so other things like * the list do not get modified */ fb->freeze_count++; /* Iterate through all given file types and generate the * list */ for(i = 0; i < total_types; i++) { t = type[i]; if((t != NULL) ? (t->ext == NULL) : TRUE) continue; /* Format list item string */ if(!STRISEMPTY(t->name)) s = g_strdup_printf( "%s (%s)", t->name, t->ext ); else s = STRDUP(t->ext); /* Add list item string to the list */ glist = g_list_append(glist, s); /* Record first type which is considered the initially * selected type */ if(i == 0) { fb_type_struct *t_cur = &fb->cur_type; first_type_s = s; g_free(t_cur->name); t_cur->name = STRDUP(t->name); g_free(t_cur->ext); t_cur->ext = STRDUP(t->ext); } } /* Set first file type string to the combo's entry */ if(first_type_s != NULL) gtk_entry_set_text(entry, first_type_s); /* Set file types string list to file type combo */ GUIComboSetList(GTK_WIDGET(combo), glist); glist = NULL; first_type_s = NULL; fb->freeze_count--; } FileBrowserSetBusy(fb, TRUE); /* Map File Browser * * This needs to be done now in order to allow the proper * realizing of sizes before other things can be updated */ FileBrowserMap(); #if 0 /* This is not needed any more since object positions are updated when * a "configure_event" signal is received */ while(gtk_events_pending() > 0) gtk_main_iteration(); #endif /* Set initial path (if specified) */ if(!STRISEMPTY(path)) { FileBrowserSetLocation(fb, path); } else { /* No path was given, but we still need to check if the * listings need to be updated in case they were * reset/cleared */ if(fb->total_objects <= 0) { gchar *cur_path = STRDUP(FileBrowserGetLocation(fb)); FileBrowserSetLocation(fb, cur_path); g_free(cur_path); } } /* If no location was set then set the current location to be * the home directory */ if(STRISEMPTY(fb->cur_location)) FileBrowserSetLocation(fb, "~"); /* Focus first object as needed */ if(fb->total_objects > 0) { if(fb->focus_object < 0) { fb->focus_object = 0; gtk_widget_queue_draw(fb->list_da); } } FileBrowserSetBusy(fb, FALSE); /* Block until user response */ fb->block_loop_level++; gtk_main(); /* Unmap the File Browser just in case it was not unmapped from * any of the callbacks */ FileBrowserUnmap(); /* Break out of an additional blocking loops */ while(fb->block_loop_level > 0) { gtk_main_quit(); fb->block_loop_level--; } fb->block_loop_level = 0; /* Begin setting returns */ /* Response path returns */ if(path_rtn != NULL) *path_rtn = fb->selected_path; if(path_total_rtns != NULL) *path_total_rtns = fb->total_selected_paths; /* File type */ if(type_rtn != NULL) *type_rtn = &fb->cur_type; return(fb->user_response); #undef RESET_RETURNS } /* * Maps the File Browser. */ void FileBrowserMap(void) { FileBrowser *fb = &file_browser; GtkWidget *w = fb->toplevel; /* Map toplevel */ gtk_widget_show_raise(w); /* Grab focus and default for the list */ w = fb->list_da; gtk_widget_grab_focus(w); gtk_widget_grab_default(w); } /* * Unmaps the File Browser. */ void FileBrowserUnmap(void) { FileBrowser *fb = &file_browser; GtkWidget *w = fb->toplevel; gtk_widget_hide(w); } /* * Shuts down the File Browser. */ void FileBrowserShutdown(void) { gint i; FileBrowser *fb = &file_browser; /* Begin deleting values */ /* Current file type */ g_free(fb->cur_type.name); g_free(fb->cur_type.ext); memset(&fb->cur_type, 0x00, sizeof(fb_type_struct)); /* Selected paths */ for(i = 0; i < fb->total_selected_paths; i++) g_free(fb->selected_path[i]); g_free(fb->selected_path); fb->selected_path = NULL; fb->total_selected_paths = 0; /* Selectioned objects list */ fb->focus_object = -1; g_list_free(fb->selection); fb->selection = fb->selection_end = NULL; /* Objects */ for(i = 0; i < fb->total_objects; i++) FileBrowserObjectDestroyCB(fb->object[i]); g_free(fb->object); fb->object = NULL; fb->total_objects = 0; fb->objects_per_row = 0; /* Icons */ for(i = 0; i < fb->total_icons; i++) FileBrowserIconDestroyCB(fb->icon[i]); g_free(fb->icon); fb->icon = NULL; fb->total_icons = 0; /* List columns */ FileBrowserListColumnsClear(fb); /* Drive paths */ strlistfree(fb->drive_path, fb->total_drive_paths); fb->drive_path = NULL; fb->total_drive_paths = 0; /* Break out of any additional blocking loops */ while(fb->block_loop_level > 0) { gtk_main_quit(); fb->block_loop_level--; } fb->block_loop_level = 0; /* Begin destroying widgets */ PUListDelete(fb->dir_pulist); fb->dir_pulist = NULL; GTK_WIDGET_DESTROY(fb->list_menu); fb->list_menu = NULL; GTK_WIDGET_DESTROY(fb->dir_pulist_da); fb->dir_pulist_da = NULL; GTK_WIDGET_DESTROY(fb->dir_pulist_btn); fb->dir_pulist_btn = NULL; GTK_WIDGET_DESTROY(fb->goto_parent_btn); fb->goto_parent_btn = NULL; GTK_WIDGET_DESTROY(fb->new_directory_btn); fb->new_directory_btn = NULL; GTK_WIDGET_DESTROY(fb->rename_btn); fb->rename_btn = NULL; GTK_WIDGET_DESTROY(fb->refresh_btn); fb->refresh_btn = NULL; GTK_WIDGET_DESTROY(fb->show_hidden_objects_tb); fb->show_hidden_objects_tb = NULL; GTK_WIDGET_DESTROY(fb->list_format_standard_tb); fb->list_format_standard_tb = NULL; GTK_WIDGET_DESTROY(fb->list_format_vertical_tb); fb->list_format_vertical_tb = NULL; GTK_WIDGET_DESTROY(fb->list_format_vertical_details_tb); fb->list_format_vertical_details_tb = NULL; GTK_WIDGET_DESTROY(fb->list_header_da); fb->list_header_da = NULL; GTK_WIDGET_DESTROY(fb->list_da); fb->list_da = NULL; GTK_WIDGET_DESTROY(fb->list_vsb); fb->list_vsb = NULL; GTK_WIDGET_DESTROY(fb->list_hsb); fb->list_hsb = NULL; GTK_WIDGET_DESTROY(fb->entry); fb->entry = NULL; GTK_WIDGET_DESTROY(fb->type_combo); fb->type_combo = NULL; GTK_WIDGET_DESTROY(fb->ok_btn); fb->ok_btn = NULL; GTK_WIDGET_DESTROY(fb->cancel_btn); fb->cancel_btn = NULL; GTK_WIDGET_DESTROY(fb->main_vbox); fb->main_vbox = NULL; GTK_WIDGET_DESTROY(fb->toplevel); fb->toplevel = NULL; GDK_PIXMAP_UNREF(fb->list_pm); fb->list_pm = NULL; GDK_CURSOR_DESTROY(fb->cur_busy); fb->cur_busy = NULL; GDK_CURSOR_DESTROY(fb->cur_column_hresize); fb->cur_column_hresize = NULL; GDK_CURSOR_DESTROY(fb->cur_translate); fb->cur_translate = NULL; GTK_ACCEL_GROUP_UNREF(fb->accelgrp); fb->accelgrp = NULL; g_free(fb->home_path); fb->home_path = NULL; g_free(fb->cur_location); fb->cur_location = NULL; memset(fb, 0x00, sizeof(FileBrowser)); } /* * Show hidden objects. */ void FileBrowserShowHiddenObjects(gboolean show) { FileBrowser *fb = &file_browser; FileBrowserSetShowHiddenObjects(fb, show); } /* * Sets the list format to standard. */ void FileBrowserListStandard(void) { FileBrowser *fb = &file_browser; FileBrowserSetListFormat(fb, FB_LIST_FORMAT_STANDARD); } /* * Sets the list format to detailed. */ void FileBrowserListDetailed(void) { FileBrowser *fb = &file_browser; FileBrowserSetListFormat(fb, FB_LIST_FORMAT_VERTICAL_DETAILS); } /* * Convience function to allocate a new file browser file extension * type structure and append it to the given list. The given list * will be modified and the index number of the newly allocated * structure will be returned. * * Can return -1 on error. */ gint FileBrowserTypeListNew( fb_type_struct ***list, gint *total, const gchar *ext, /* Space separated list of extensions */ const gchar *name /* Descriptive name */ ) { gint n; fb_type_struct *fb_type_ptr; if((list == NULL) || (total == NULL)) return(-1); n = MAX(*total, 0); *total = n + 1; /* Allocate more pointers */ *list = (fb_type_struct **)g_realloc( *list, (*total) * sizeof(fb_type_struct *) ); if(*list == NULL) { *total = 0; return(-1); } /* Allocate new structure */ fb_type_ptr = (fb_type_struct *)g_malloc0(sizeof(fb_type_struct)); (*list)[n] = fb_type_ptr; if(fb_type_ptr == NULL) { *total = n; return(-1); } /* Set values */ fb_type_ptr->ext = STRDUP(ext); fb_type_ptr->name = STRDUP(name); return(n); } /* * Deletes a dynamically allocated file browser file extensions * type list. */ void FileBrowserDeleteTypeList( fb_type_struct **t, gint total ) { gint i; fb_type_struct *t_ptr; for(i = 0; i < total; i++) { t_ptr = t[i]; if(t_ptr == NULL) continue; g_free(t_ptr->ext); g_free(t_ptr->name); g_free(t_ptr); } g_free(t); }