This combines the following patches for ROX-Filer 1.3.5 ghistory history mimetypenames selbutton sortbutton Apply this patch and you are running the same filer that I am. Stephen Watson, stephen@kerofin.demon.co.uk Index:rox/ROX-Filer/src/display.c *** rox-1.3.5/ROX-Filer/src/display.c Wed Nov 20 14:17:29 2002 --- rox/ROX-Filer/src/display.c Sat Dec 7 12:37:12 2002 *************** *** 383,388 **** --- 383,389 ---- if (o_large_width.has_changed || o_small_width.has_changed) flags |= VIEW_UPDATE_NAME; /* Recreate PangoLayout */ + view_style_changed(filer_window->view, flags); } } *************** *** 439,448 **** MIME_type *type = item->mime_type; if (!scanned) ! return g_strdup("(application/octet-stream)"); ! buf = g_strdup_printf("(%s/%s)", ! type->media_type, type->subtype); } else if (filer_window->details_type == DETAILS_TIMES) { --- 440,449 ---- MIME_type *type = item->mime_type; if (!scanned) ! return g_strdup("application/octet-stream"); ! buf = g_strdup_printf("%s/%s", ! type->media_type, type->subtype); } else if (filer_window->details_type == DETAILS_TIMES) { Index:rox/ROX-Filer/src/filer.c *** rox-1.3.5/ROX-Filer/src/filer.c Wed Dec 4 12:56:23 2002 --- rox/ROX-Filer/src/filer.c Sat Dec 7 12:36:08 2002 *************** *** 74,79 **** --- 74,81 ---- static FilerWindow *window_with_primary = NULL; + static GHashTable *global_history=NULL; + /* Static prototypes */ static void attach(FilerWindow *filer_window); static void detach(FilerWindow *filer_window); *************** *** 96,101 **** --- 98,108 ---- static void filer_options_changed(void); static void set_style_by_number_of_items(FilerWindow *filer_window); + static gboolean check_history(gpointer key, gpointer value, gpointer udata); + static void history_list(gpointer key, gpointer value, gpointer udata); + static void add_to_history(FilerWindow *filer_window); + static void purge_history(void); + static GdkCursor *busy_cursor = NULL; static GdkCursor *crosshair = NULL; *************** *** 153,158 **** --- 160,169 ---- } g_free(dpyhost); + + global_history=g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, NULL); + /*printf("made global_history (%p)\n", global_history);*/ } static gboolean if_deleted(gpointer item, gpointer removed) *************** *** 324,329 **** --- 335,341 ---- dir_attach(filer_window->directory, (DirCallback) update_display, filer_window); filer_set_title(filer_window); + add_to_history(filer_window); } static void detach(FilerWindow *filer_window) *************** *** 398,403 **** --- 410,420 ---- if (filer_window->thumb_queue) destroy_glist(&filer_window->thumb_queue); + if(filer_window->history) { + g_slist_foreach(filer_window->history, (GFunc) g_free, NULL); + g_slist_free(filer_window->history); + } + tooltip_show(NULL); g_free(filer_window->auto_select); *************** *** 988,995 **** from_dup = from && *from ? g_strdup(from) : NULL; detach(filer_window); g_free(filer_window->real_path); - g_free(filer_window->sym_path); filer_window->real_path = real_path; filer_window->sym_path = sym_path; tidy_sympath(filer_window->sym_path); --- 1005,1020 ---- from_dup = from && *from ? g_strdup(from) : NULL; detach(filer_window); + /* Compare pointers, so we detect when we are going back */ + if(filer_window->history && filer_window->history->data==path) { + /* We are going one back in the history, do not push this onto + the stack */ + g_free(filer_window->sym_path); + } else { + filer_window->history=g_slist_prepend(filer_window->history, + filer_window->sym_path); + } g_free(filer_window->real_path); filer_window->real_path = real_path; filer_window->sym_path = sym_path; tidy_sympath(filer_window->sym_path); *************** *** 1020,1025 **** --- 1045,1083 ---- filer_window); } + void filer_change_to_previous(FilerWindow *filer_window) + { + GSList *tmp; + + g_return_if_fail(filer_window!=NULL); + + if(!filer_window->history) { + gdk_beep(); + return; + } + + filer_change_to(filer_window, (gchar *) filer_window->history->data, + NULL); + + tmp=filer_window->history; + filer_window->history=g_slist_remove_link(filer_window->history, tmp); + g_free(tmp->data); + g_slist_free(tmp); + } + + FilerWindow *filer_open_previous(FilerWindow *filer_window) + { + g_return_val_if_fail(filer_window!=NULL, NULL); + + if(!filer_window->history) { + gdk_beep(); + return NULL; + } + + return filer_opendir((gchar *) filer_window->history->data, + NULL, NULL); + } + /* Returns a list containing the full (sym) pathname of every selected item. * You must g_free() each item in the list. */ *************** *** 1098,1103 **** --- 1156,1162 ---- filer_window->target_cb = NULL; filer_window->mini_type = MINI_NONE; filer_window->selection_state = GTK_STATE_INSENSITIVE; + filer_window->history = NULL; filer_window->toolbar = NULL; filer_window->toplevel_vbox = NULL; *************** *** 1740,1745 **** --- 1799,1863 ---- filer_set_title(filer_window); } } + } + + static gboolean check_history(gpointer key, gpointer value, gpointer udata) + { + time_t oldest=(time_t) GPOINTER_TO_INT(udata); + time_t when=(time_t) GPOINTER_TO_INT(value); + + /*printf("%s: %ld<%ld %d\n", (char *) key, when, oldest, whensym_path); + /*printf("key=%s, hash=0x%x (size=%u)\n", key, g_str_hash(key), + g_hash_table_size(global_history));*/ + g_hash_table_replace(global_history, key, GINT_TO_POINTER(now)); + /*printf("added %s (%p) for %ld (size=%u)\n", key, key, now, + g_hash_table_size(global_history));*/ + } + + static void purge_history(void) + { + time_t now, oldest; + + time(&now); + oldest=now-60*60; + + /*printf("begin purge, size=%u\n", g_hash_table_size(global_history));*/ + g_hash_table_foreach_remove(global_history, check_history, + GINT_TO_POINTER(oldest)); + /*printf("end purge, size=%u\n", g_hash_table_size(global_history));*/ + } + + GSList *filer_global_history(void) + { + GSList *dirs=NULL; + + purge_history(); + g_hash_table_foreach(global_history, history_list, &dirs); + dirs=g_slist_sort(dirs, (GCompareFunc) strcmp); + + return dirs; } /* Change to Large or Small icons depending on the number of items Index:rox/ROX-Filer/src/filer.h *** rox-1.3.5/ROX-Filer/src/filer.h Wed Dec 4 12:56:24 2002 --- rox/ROX-Filer/src/filer.h Sat Dec 7 12:36:50 2002 *************** *** 85,90 **** --- 85,92 ---- GList *thumb_queue; /* paths to thumbnail */ GtkWidget *thumb_bar, *thumb_progress; int max_thumbs; /* total for this batch */ + + GSList *history; /* List of dirs visited by this window */ }; extern FilerWindow *window_with_focus; *************** *** 127,131 **** --- 129,136 ---- gboolean filer_window_delete(GtkWidget *window, GdkEvent *unused, FilerWindow *filer_window); + extern GSList *filer_global_history(void); + extern void filer_change_to_previous(FilerWindow *filer_window); + extern FilerWindow *filer_open_previous(FilerWindow *filer_window); #endif /* _FILER_H */ Index:rox/ROX-Filer/src/infobox.c *** rox-1.3.5/ROX-Filer/src/infobox.c Tue Dec 3 14:51:13 2002 --- rox/ROX-Filer/src/infobox.c Sat Dec 7 12:37:12 2002 *************** *** 365,370 **** --- 365,377 ---- add_row(store, _("Type:"), pretty_type(item, path)); + if(item->mime_type) { + const char *comment=mime_type_comment(item->mime_type); + if(comment) { + add_row(store, "", comment); + } + } + if (item->base_type != TYPE_DIRECTORY) add_row_and_free(store, _("Run action:"), describe_current_command(item->mime_type)); Index:rox/ROX-Filer/src/menu.c *** rox-1.3.5/ROX-Filer/src/menu.c Tue Dec 3 12:30:47 2002 --- rox/ROX-Filer/src/menu.c Sat Dec 7 12:36:08 2002 *************** *** 119,124 **** --- 119,125 ---- static void do_send_to(gchar *templ); static void show_send_to_menu(GList *paths, GdkEvent *event); static GList *set_keys_button(Option *option, xmlNode *node, guchar *label); + static void reopen(GtkWidget *widget, gpointer data); /* Note that for most of these callbacks none of the arguments are used. */ *************** *** 147,152 **** --- 148,155 ---- static void open_parent_same(gpointer data, guint action, GtkWidget *widget); static void open_parent(gpointer data, guint action, GtkWidget *widget); + static void open_previous_same(gpointer data, guint action, GtkWidget *widget); + static void open_previous(gpointer data, guint action, GtkWidget *widget); static void home_directory(gpointer data, guint action, GtkWidget *widget); static void show_bookmarks(gpointer data, guint action, GtkWidget *widget); static void new_window(gpointer data, guint action, GtkWidget *widget); *************** *** 169,174 **** --- 172,178 ---- static GtkWidget *filer_new_window; /* The New Window item */ static GtkWidget *filer_new_menu; /* The New submenu */ static GtkWidget *filer_follow_sym; /* Follow symbolic links item */ + static GtkWidget *filer_reopen; /* Global history item */ #undef N_ #define N_(x) x *************** *** 235,242 **** --- 239,249 ---- {N_("Window"), NULL, NULL, 0, ""}, {">" N_("Parent, New Window"), NULL, open_parent, 0, NULL}, {">" N_("Parent, Same Window"), NULL, open_parent_same, 0, NULL}, + {">" N_("Previous, New Window"), NULL, open_previous, 0, NULL}, + {">" N_("Previous, Same Window"), NULL, open_previous_same, 0, NULL}, {">" N_("New Window"), NULL, new_window, 0, NULL}, {">" N_("Home Directory"), NULL, home_directory, 0, NULL}, + {">" N_("Re-open"), NULL, NULL, 0, NULL}, {">" N_("Show Bookmarks"), "B", show_bookmarks, 0, NULL}, {">" N_("Follow Symbolic Links"), NULL, follow_symlinks, 0, NULL}, {">" N_("Resize Window"), NULL, resize, 0, NULL}, *************** *** 296,301 **** --- 303,309 ---- GET_SMENU_ITEM(filer_new_menu, "filer", "New"); GET_SSMENU_ITEM(item, "filer", "Window", "Follow Symbolic Links"); filer_follow_sym = GTK_BIN(item)->child; + GET_SSMENU_ITEM(filer_reopen, "filer", "Window", "Re-open"); /* File '' label... */ items = gtk_container_get_children(GTK_CONTAINER(filer_menu)); *************** *** 588,593 **** --- 596,633 ---- gtk_widget_show_all(filer_new_menu); } + static void reopen(GtkWidget *widget, gpointer data) + { + gchar *dir=(gchar *) data; + + filer_opendir(dir, NULL, NULL); + } + + static void update_reopen_menu(GtkWidget *master) + { + GSList *dirs=filer_global_history(), *rover; + GtkWidget *menu; + GtkWidget *item; + + menu=gtk_menu_new(); + + for(rover=dirs; rover; rover=g_slist_next(rover)) { + gchar *dir=g_strdup((gchar *) rover->data); + + /*printf("add %s\n", dir);*/ + item=gtk_menu_item_new_with_label(dir); + g_signal_connect(G_OBJECT(item), "activate", + G_CALLBACK(reopen), dir); + g_signal_connect_swapped(G_OBJECT(item), "destroy", + G_CALLBACK(g_free), dir); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + } + gtk_widget_show_all(menu); + + gtk_menu_item_set_submenu(GTK_MENU_ITEM(master), menu); + g_slist_free(dirs); + } + /* 'item' is the number of the item to appear under the pointer. */ void show_popup_menu(GtkWidget *menu, GdkEvent *event, int item) { *************** *** 789,794 **** --- 829,835 ---- } update_new_files_menu(get_menu_icon_style()); + update_reopen_menu(filer_reopen); gtk_widget_set_sensitive(filer_new_window, !o_unique_filer_windows.int_value); *************** *** 1568,1573 **** --- 1609,1628 ---- g_return_if_fail(window_with_focus != NULL); change_to_parent(window_with_focus); + } + + static void open_previous(gpointer data, guint action, GtkWidget *widget) + { + g_return_if_fail(window_with_focus != NULL); + + (void) filer_open_previous(window_with_focus); + } + + static void open_previous_same(gpointer data, guint action, GtkWidget *widget) + { + g_return_if_fail(window_with_focus != NULL); + + filer_change_to_previous(window_with_focus); } static void resize(gpointer data, guint action, GtkWidget *widget) Index:rox/ROX-Filer/src/toolbar.c *** rox-1.3.5/ROX-Filer/src/toolbar.c Thu Dec 5 12:37:18 2002 --- rox/ROX-Filer/src/toolbar.c Sat Dec 7 12:37:28 2002 *************** *** 42,47 **** --- 42,48 ---- #include "diritem.h" #include "view_iface.h" #include "bookmarks.h" + #include "gui_support.h" typedef struct _Tool Tool; *************** *** 83,88 **** --- 84,93 ---- FilerWindow *filer_window); static void toolbar_hidden_clicked(GtkWidget *widget, FilerWindow *filer_window); + static void toolbar_select_clicked(GtkWidget *widget, + FilerWindow *filer_window); + static void toolbar_sort_clicked(GtkWidget *widget, + FilerWindow *filer_window); static GtkWidget *add_button(GtkWidget *bar, Tool *tool, FilerWindow *filer_window); static GtkWidget *create_toolbar(FilerWindow *filer_window); *************** *** 133,142 **** --- 138,155 ---- toolbar_details_clicked, DROP_NONE, TRUE, FALSE}, + {N_("Sort"), GTK_STOCK_SORT_ASCENDING, N_("Change sort criteria"), + toolbar_sort_clicked, DROP_NONE, TRUE, + FALSE}, + {N_("Hidden"), ROX_STOCK_SHOW_HIDDEN, N_("Show/hide hidden files"), toolbar_hidden_clicked, DROP_NONE, TRUE, FALSE}, + {N_("Select"), GTK_STOCK_COPY, N_("Select all/invert selection"), + toolbar_select_clicked, DROP_NONE, TRUE, + FALSE}, + {N_("Help"), GTK_STOCK_HELP, N_("Show ROX-Filer help"), toolbar_help_clicked, DROP_NONE, TRUE, FALSE}, *************** *** 387,398 **** GdkEvent *event; event = gtk_get_current_event(); ! if (event->type == GDK_BUTTON_RELEASE && NEW_WIN_BUTTON(event)) ! { ! filer_open_parent(filer_window); } - else - change_to_parent(filer_window); } static void toolbar_size_clicked(GtkWidget *widget, FilerWindow *filer_window) --- 400,420 ---- GdkEvent *event; event = gtk_get_current_event(); ! if(event->type == GDK_BUTTON_RELEASE) { ! GdkEventButton *button=(GdkEventButton *) event; ! gboolean control=(button->state & GDK_CONTROL_MASK); ! if (NEW_WIN_BUTTON(event)){ ! if(control) ! filer_open_previous(filer_window); ! else ! filer_open_parent(filer_window); ! } else { ! if(control) ! filer_change_to_previous(filer_window); ! else ! change_to_parent(filer_window); ! } } } static void toolbar_size_clicked(GtkWidget *widget, FilerWindow *filer_window) *************** *** 405,410 **** --- 427,472 ---- bev->type == GDK_BUTTON_RELEASE && bev->button == 1); } + /* This typedef makes things a lot easier... */ + typedef int (*sort_fn_t)(const void *a, const void *b); + + static void toolbar_sort_clicked(GtkWidget *widget, FilerWindow *filer_window) + { + GdkEventButton *bev; + int i, current, next; + gboolean adjust; + + static struct sorts { + sort_fn_t fn; + const char *name; + } sort_fns[]={ + {sort_by_name, "Sort by name"}, + {sort_by_type, "Sort by type"}, + {sort_by_date, "Sort by date"}, + {sort_by_size, "Sort by size"} + }; + #define NSORT (sizeof(sort_fns)/sizeof(struct sorts)) + + bev = (GdkEventButton *) gtk_get_current_event(); + adjust=(bev->button == 2) && bev->type == GDK_BUTTON_RELEASE; + + current=-1; + for(i=0; isort_fn==sort_fns[i].fn) { + current=i; + break; + } + if(current==-1) + next=0; + else if(adjust) + next=(current+NSORT-1)%NSORT; + else + next=(current+1)%NSORT; + + display_set_sort_fn(filer_window, sort_fns[next].fn); + tooltip_show(sort_fns[next].name); + } + static void toolbar_details_clicked(GtkWidget *widget, FilerWindow *filer_window) { *************** *** 422,427 **** --- 484,513 ---- FilerWindow *filer_window) { display_set_hidden(filer_window, !filer_window->show_hidden); + } + + static gboolean invert_cb(ViewIter *iter, gpointer data) + { + return !view_get_selected((ViewIface *) data, iter); + } + + static void toolbar_select_clicked(GtkWidget *widget, FilerWindow *filer_window) + { + GdkEvent *event; + + event = gtk_get_current_event(); + if (event->type == GDK_BUTTON_RELEASE && + ((GdkEventButton *) event)->button==2) + { + view_select_if(filer_window->view, invert_cb, + filer_window->view); + } + else if (event->type == GDK_BUTTON_RELEASE && + ((GdkEventButton *) event)->button==1) + { + view_select_all(filer_window->view); + } + filer_window->temp_item_selected = FALSE; } static GtkWidget *create_toolbar(FilerWindow *filer_window) Index:rox/ROX-Filer/src/type.c *** rox-1.3.5/ROX-Filer/src/type.c Tue Dec 3 14:51:16 2002 --- rox/ROX-Filer/src/type.c Sat Dec 7 12:37:12 2002 *************** *** 56,61 **** --- 56,64 ---- #include "options.h" #include "filer.h" #include "action.h" /* (for action_chmod) */ + #include "xml.h" + + #define TYPE_NAMESPACE "http://www.freedesktop.org/standards/shared-mime-info" /* Colours for file types (same order as base types) */ static gchar *opt_type_colours[][2] = { *************** *** 185,190 **** --- 188,194 ---- mtype->media_type = g_strndup(type_name, slash - type_name); mtype->subtype = g_strdup(slash + 1); mtype->image = NULL; + mtype->comment=NULL; g_hash_table_insert(type_hash, g_strdup(type_name), mtype); *************** *** 742,747 **** --- 746,752 ---- { guchar *tmp; gchar *handler; + const gchar *comment; GtkDialog *dialog; GtkWidget *frame, *entry, *label; GtkWidget *radio, *eb, *hbox; *************** *** 751,756 **** --- 756,763 ---- g_return_if_fail(type != NULL); + comment=mime_type_comment(type); + dialog = GTK_DIALOG(gtk_dialog_new()); gtk_dialog_set_has_separator(dialog, FALSE); gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE); *************** *** 765,772 **** g_free(tmp); g_object_set_data(G_OBJECT(dialog), "set_for_all", radio); ! tmp = g_strdup_printf(_("Only for the type `%s/%s'"), type->media_type, ! type->subtype); gtk_box_pack_start(GTK_BOX(dialog->vbox), radio, FALSE, TRUE, 0); radio = gtk_radio_button_new_with_label( gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio)), --- 772,783 ---- g_free(tmp); g_object_set_data(G_OBJECT(dialog), "set_for_all", radio); ! if(comment) ! tmp=g_strdup_printf(_("Only for the type `%s' (%s/%s)"), ! comment, type->media_type, type->subtype); ! else ! tmp = g_strdup_printf(_("Only for the type `%s/%s'"), ! type->media_type, type->subtype); gtk_box_pack_start(GTK_BOX(dialog->vbox), radio, FALSE, TRUE, 0); radio = gtk_radio_button_new_with_label( gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio)), *************** *** 1243,1246 **** --- 1254,1327 ---- } filer_update_all(); + } + + /* Find the media/subtype.xml file for a MIME_type and load it, returning + * an XML document (NULL==not found) + * + * g_object_unref() the result. + */ + static XMLwrapper *load_type_file(MIME_type *type) + { + static char *dirs[]={ + /* Defined by version 0.8 of the Shared MIME-info Database spec */ + "~/.mime", "/usr/local/share/mime", "/usr/share/mime", + NULL + }; + char *fname, *home; + int i; + XMLwrapper *doc; + + /* Substitute ~ with the home directory */ + home=getenv("HOME"); + if(home) { + for(i=0; dirs[i]; i++) { + if(dirs[i][0]=='~') + dirs[i]=g_strconcat(home, dirs+1, NULL); + } + } + + for(i=0; dirs[i]; i++) { + fname=g_strconcat(dirs[i], "/", type->media_type, "/", type->subtype, + ".xml", NULL); + doc=xml_cache_load(fname); + /*printf("%s -> %p\n", fname, doc);*/ + g_free(fname); + if(doc) + return doc; + } + + return NULL; + } + + static void find_comment(MIME_type *type) + { + XMLwrapper *typedoc=load_type_file(type); + xmlNode *node; + + if(!typedoc) + return; + + node=xml_get_section(typedoc, TYPE_NAMESPACE, "comment"); + /*printf("node=%p\n", node);*/ + if(node) { + gchar *str; + + str=xmlNodeListGetString(node->doc, node->xmlChildrenNode, 1); + /*printf("%s\n", str);*/ + if(type->comment) + g_free(type->comment); + type->comment=str; + + } + + g_object_unref(typedoc); + } + + const char *mime_type_comment(MIME_type *type) + { + if(!type->comment) + find_comment(type); + + return type->comment; } Index:rox/ROX-Filer/src/type.h *** rox-1.3.5/ROX-Filer/src/type.h Tue Aug 13 11:21:18 2002 --- rox/ROX-Filer/src/type.h Sat Dec 7 12:37:12 2002 *************** *** 44,49 **** --- 44,50 ---- char *subtype; MaskedPixmap *image; /* NULL => not loaded yet */ time_t image_time; /* When we loaded the image */ + char *comment; /* Name in local language, can be NULL */ }; /* Prototypes */ *************** *** 62,66 **** --- 63,68 ---- gchar *describe_current_command(MIME_type *type); GdkColor *type_get_colour(DirItem *item, GdkColor *normal); void reread_mime_files(void); + extern const char *mime_type_comment(MIME_type *type); #endif /* _TYPE_H */ Index:rox/ROX-Filer/src/usericons.c *** rox-1.3.5/ROX-Filer/src/usericons.c Thu Nov 21 14:42:49 2002 --- rox/ROX-Filer/src/usericons.c Sat Dec 7 12:37:12 2002 *************** *** 249,254 **** --- 249,255 ---- { struct stat info; guchar *tmp; + const gchar *comment; GtkDialog *dialog; GtkWidget *frame, *hbox, *vbox2; GtkWidget *entry, *label, *button, *align, *icon; *************** *** 262,267 **** --- 263,269 ---- g_return_if_fail(item != NULL && path != NULL); gi = g_hash_table_lookup(glob_icons, path); + comment=mime_type_comment(item->mime_type); dialog = GTK_DIALOG(gtk_dialog_new()); gtk_dialog_set_has_separator(dialog, FALSE); *************** *** 284,292 **** _("Use a copy of the image as the default for all " "files of these MIME types."), NULL); ! tmp = g_strdup_printf(_("Only for the type `%s/%s'"), ! item->mime_type->media_type, ! item->mime_type->subtype); radio[SET_TYPE] = gtk_radio_button_new_with_label_from_widget(group, tmp); gtk_box_pack_start(GTK_BOX(dialog->vbox), radio[SET_TYPE], --- 286,300 ---- _("Use a copy of the image as the default for all " "files of these MIME types."), NULL); ! if(comment) ! tmp = g_strdup_printf(_("Only for the type `%s' (%s/%s)"), ! comment, ! item->mime_type->media_type, ! item->mime_type->subtype); ! else ! tmp = g_strdup_printf(_("Only for the type `%s/%s'"), ! item->mime_type->media_type, ! item->mime_type->subtype); radio[SET_TYPE] = gtk_radio_button_new_with_label_from_widget(group, tmp); gtk_box_pack_start(GTK_BOX(dialog->vbox), radio[SET_TYPE],