From 2f14975fab4ff2e8d559aab6dbf4fb89def7fa78 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Thu, 6 Jun 2002 18:41:38 +0000 Subject: [PATCH] r1553: Created View interface and started moving collection specific stuff into view_collection.c --- ROX-Filer/src/Makefile.in | 4 +- ROX-Filer/src/collection.c | 57 +- ROX-Filer/src/collection.h | 34 +- ROX-Filer/src/display.c | 762 +-------------------- ROX-Filer/src/display.h | 5 +- ROX-Filer/src/dnd.c | 5 +- ROX-Filer/src/filer.c | 728 +++----------------- ROX-Filer/src/filer.h | 7 + ROX-Filer/src/global.h | 3 + ROX-Filer/src/gui_support.c | 96 +++ ROX-Filer/src/gui_support.h | 2 + ROX-Filer/src/tasklist.c | 2 + ROX-Filer/src/view_collection.c | 1437 +++++++++++++++++++++++++++++++++++++++ ROX-Filer/src/view_collection.h | 23 + ROX-Filer/src/view_iface.c | 136 ++++ ROX-Filer/src/view_iface.h | 59 ++ 16 files changed, 1938 insertions(+), 1422 deletions(-) create mode 100644 ROX-Filer/src/view_collection.c create mode 100644 ROX-Filer/src/view_collection.h create mode 100644 ROX-Filer/src/view_iface.c create mode 100644 ROX-Filer/src/view_iface.h diff --git a/ROX-Filer/src/Makefile.in b/ROX-Filer/src/Makefile.in index 14391848..f3865056 100644 --- a/ROX-Filer/src/Makefile.in +++ b/ROX-Filer/src/Makefile.in @@ -25,7 +25,7 @@ SRCS = abox.c action.c appinfo.c appmenu.c bind.c choices.c \ main.c menu.c minibuffer.c modechange.c mount.c options.c \ panel.c pinboard.c pixmaps.c remote.c rox_gettext.c run.c \ sc.c session.c support.c tasklist.c toolbar.c type.c \ - usericons.c wrapped.c xml.c + usericons.c view_collection.c view_iface.c wrapped.c xml.c OBJECTS = abox.o action.o appinfo.o appmenu.o bind.o choices.o \ collection.o dir.o diritem.o display.o dnd.o filer.o find.o \ @@ -33,7 +33,7 @@ OBJECTS = abox.o action.o appinfo.o appmenu.o bind.o choices.o \ main.o menu.o minibuffer.o modechange.o mount.o options.o \ panel.o pinboard.o pixmaps.o remote.o rox_gettext.o run.o \ sc.o session.o support.o tasklist.o toolbar.o type.o \ - usericons.o wrapped.o xml.o + usericons.o view_collection.o view_iface.o wrapped.o xml.o ############ Things to keep the same diff --git a/ROX-Filer/src/collection.c b/ROX-Filer/src/collection.c index 0c297075..03eb3860 100644 --- a/ROX-Filer/src/collection.c +++ b/ROX-Filer/src/collection.c @@ -158,8 +158,7 @@ static void draw_one_item(Collection *collection, int item, GdkRectangle *area) { collection->draw_item((GtkWidget *) collection, &collection->items[item], - area, - collection->cb_user_data); + area, collection->cb_user_data); } if (item == collection->cursor_item) @@ -280,7 +279,6 @@ static void collection_init(GTypeInstance *instance, gpointer g_class) object->item_width = 64; object->item_height = 64; object->vadj = NULL; - object->bg_gc = NULL; object->items = g_new(CollectionItem, MINIMUM_ITEMS); object->cursor_item = -1; @@ -353,12 +351,6 @@ static void collection_destroy(GtkObject *object) collection->auto_scroll = -1; } - if (collection->bg_gc) - { - g_object_unref(collection->bg_gc); - collection->bg_gc = NULL; - } - if (collection->vadj) { g_object_unref(G_OBJECT(collection->vadj)); @@ -614,10 +606,10 @@ static gint collection_expose(GtkWidget *widget, GdkEventExpose *event) return FALSE; } -static void default_draw_item( GtkWidget *widget, - CollectionItem *item, - GdkRectangle *area, - gpointer user_data) +static void default_draw_item(GtkWidget *widget, + CollectionItem *item, + GdkRectangle *area, + gpointer user_data) { gdk_draw_arc(widget->window, item->selected ? widget->style->white_gc @@ -1464,6 +1456,13 @@ void collection_set_item_size(Collection *collection, int width, int height) gtk_widget_queue_resize(GTK_WIDGET(collection)); } +static int (*cmp_callback)(const void *a, const void *b) = NULL; +static int collection_cmp(const void *a, const void *b) +{ + return cmp_callback(((CollectionItem *) a)->data, + ((CollectionItem *) b)->data); +} + /* Cursor is positioned on item with the same data as before the sort. * Same for the wink item. */ @@ -1480,6 +1479,7 @@ void collection_qsort(Collection *collection, g_return_if_fail(collection != NULL); g_return_if_fail(IS_COLLECTION(collection)); g_return_if_fail(compar != NULL); + g_return_if_fail(cmp_callback == NULL); /* Check to see if it needs sorting (saves redrawing) */ if (collection->number_of_items < 2) @@ -1488,7 +1488,7 @@ void collection_qsort(Collection *collection, array = collection->items; for (i = 1; i < collection->number_of_items; i++) { - if (compar(&array[i - 1], &array[i]) > 0) + if (compar(array[i - 1].data, array[i].data) > 0) break; } if (i == collection->number_of_items) @@ -1520,7 +1520,10 @@ void collection_qsort(Collection *collection, else cursor = -1; - qsort(collection->items, items, sizeof(collection->items[0]), compar); + cmp_callback = compar; + qsort(collection->items, items, sizeof(collection->items[0]), + collection_cmp); + cmp_callback = NULL; if (cursor > -1 || wink > -1 || wink_on_map > -1) { @@ -1566,7 +1569,7 @@ int collection_find_item(Collection *collection, gpointer data, i = (lower + upper) >> 1; - cmp = compar(&collection->items[i].data, &data); + cmp = compar(collection->items[i].data, data); if (cmp == 0) return i; @@ -1606,20 +1609,18 @@ int collection_get_item(Collection *collection, int x, int y) width = collection->item_width; item = col + row * collection->columns; - if (item >= collection->number_of_items - || - !collection->test_point(collection, - x - col * collection->item_width, - y - row * collection->item_height, - &collection->items[item], - width, - collection->item_height, - collection->cb_user_data)) - { + if (item >= collection->number_of_items) return -1; - } - return item; + x -= col * collection->item_width; + y -= row * collection->item_height; + + if (collection->test_point(collection, x, y, + &collection->items[item], width, collection->item_height, + collection->cb_user_data)) + return item; + + return -1; } int collection_selected_item_number(Collection *collection) diff --git a/ROX-Filer/src/collection.h b/ROX-Filer/src/collection.h index 03e94d54..e5fdea00 100644 --- a/ROX-Filer/src/collection.h +++ b/ROX-Filer/src/collection.h @@ -21,7 +21,7 @@ extern "C" { #endif /* __cplusplus */ #define COLLECTION(obj) GTK_CHECK_CAST((obj), collection_get_type(), Collection) -#define COLLECTION_CLASS(klass) GTK_CHECK_CLASS_CAST((klass), \ +#define COLLECTION_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS((obj), \ collection_get_type(), CollectionClass) #define IS_COLLECTION(obj) \ G_TYPE_CHECK_INSTANCE_TYPE((obj), collection_get_type()) @@ -61,18 +61,16 @@ struct _Collection { GtkWidget parent_widget; - /* For gtk+-1.2, the adjustment is used for redrawing in the right - * place. With 2.0, the collection is in a Viewport, and this is - * used only to force scrolling, etc. + /* With 2.0, the collection is in a Viewport, and this is used only to + * force scrolling, etc. */ GtkAdjustment *vadj; CollectionDrawFunc draw_item; CollectionTestFunc test_point; CollectionFreeFunc free_item; - gpointer cb_user_data; /* Passed to above two functions */ - GdkGC *bg_gc; /* NULL unless pixmap background */ - + gpointer cb_user_data; /* Passed to above functions */ + gboolean lasso_box; /* Is the box drawn? */ int drag_box_x[2]; /* Index 0 is the fixed corner */ int drag_box_y[2]; @@ -103,11 +101,23 @@ struct _CollectionClass GtkWidgetClass parent_class; void (*gain_selection)(Collection *collection, - gint time); + gint time); void (*lose_selection)(Collection *collection, - gint time); + gint time); void (*selection_changed)(Collection *collection, - gint time); + gint time); + + void (*draw_item)(GtkWidget *widget, + CollectionItem *item, + GdkRectangle *area); + + gboolean (*test_point)(Collection *collection, + int point_x, int point_y, + CollectionItem *item, + int width, int height); + + void (*free_item)(Collection *collection, + CollectionItem *item); }; GType collection_get_type (void); @@ -126,10 +136,6 @@ void collection_clear_selection (Collection *collection); void collection_invert_selection (Collection *collection); void collection_draw_item (Collection *collection, gint item, gboolean blank); -void collection_set_functions (Collection *collection, - CollectionDrawFunc draw_item, - CollectionTestFunc test_point, - gpointer user_data); void collection_set_item_size (Collection *collection, int width, int height); void collection_qsort (Collection *collection, diff --git a/ROX-Filer/src/display.c b/ROX-Filer/src/display.c index 94326c76..cf03fd75 100644 --- a/ROX-Filer/src/display.c +++ b/ROX-Filer/src/display.c @@ -41,7 +41,6 @@ #include "main.h" #include "display.h" -#include "collection.h" #include "support.h" #include "gui_support.h" #include "filer.h" @@ -57,13 +56,7 @@ #include "dir.h" #include "diritem.h" #include "fscache.h" - -#define ROW_HEIGHT_SMALL 20 -#define ROW_HEIGHT_FULL_INFO 44 -#define MIN_ITEM_WIDTH 64 - -#define MIN_TRUNCATE 0 -#define MAX_TRUNCATE 250 +#include "view_iface.h" #define HUGE_WRAP (1.5 * o_large_width.int_value) @@ -74,65 +67,16 @@ Option o_display_size; Option o_display_details; Option o_display_sort_by; static Option o_large_width; -static Option o_small_width; +Option o_small_width; Option o_display_show_hidden; Option o_display_show_thumbs; Option o_display_inherit_options; -/* GC for drawing colour filenames */ -static GdkGC *type_gc = NULL; - -typedef struct _Template Template; - -struct _Template { - GdkRectangle icon; - GdkRectangle leafname; - GdkRectangle details; -}; - -#define SHOW_RECT(ite, template) \ - g_print("%s: %dx%d+%d+%d %dx%d+%d+%d\n", \ - item->leafname, \ - (template)->leafname.width, (template)->leafname.height,\ - (template)->leafname.x, (template)->leafname.y, \ - (template)->icon.width, (template)->icon.height, \ - (template)->icon.x, (template)->icon.y) - /* Static prototypes */ -static void fill_template(GdkRectangle *area, CollectionItem *item, - FilerWindow *filer_window, Template *template); -static void huge_template(GdkRectangle *area, CollectionItem *colitem, - FilerWindow *filer_window, Template *template); -static void large_template(GdkRectangle *area, CollectionItem *colitem, - FilerWindow *filer_window, Template *template); -static void small_template(GdkRectangle *area, CollectionItem *colitem, - FilerWindow *filer_window, Template *template); -static void huge_full_template(GdkRectangle *area, CollectionItem *colitem, - FilerWindow *filer_window, Template *template); -static void large_full_template(GdkRectangle *area, CollectionItem *colitem, - FilerWindow *filer_window, Template *template); -static void small_full_template(GdkRectangle *area, CollectionItem *colitem, - FilerWindow *filer_window, Template *template); -static void draw_item(GtkWidget *widget, - CollectionItem *item, - GdkRectangle *area, - FilerWindow *filer_window); -static gboolean test_point(Collection *collection, - int point_x, int point_y, - CollectionItem *item, - int width, int height, - FilerWindow *filer_window); static void display_details_set(FilerWindow *filer_window, DetailsType details); static void display_style_set(FilerWindow *filer_window, DisplayStyle style); static void options_changed(void); static char *details(FilerWindow *filer_window, DirItem *item); -static void draw_string(GtkWidget *widget, - PangoLayout *layout, - GdkRectangle *area, /* Area available on screen */ - int width, /* Width of the full string */ - GtkStateType selection_state, - gboolean selected, - gboolean box); enum { SORT_BY_NAME = 0, @@ -162,168 +106,6 @@ void display_init() option_add_notify(options_changed); } -/* A template contains the locations of the three rectangles (for the icon, - * name and extra details). - * Fill in the empty 'template' with the rectanges for this item. - */ -static void fill_template(GdkRectangle *area, CollectionItem *colitem, - FilerWindow *filer_window, Template *template) -{ - DisplayStyle style = filer_window->display_style; - ViewData *view = (ViewData *) colitem->view_data; - - if (view->details) - { - template->details.width = view->details_width; - template->details.height = view->details_height; - - if (style == SMALL_ICONS) - small_full_template(area, colitem, - filer_window, template); - else if (style == LARGE_ICONS) - large_full_template(area, colitem, - filer_window, template); - else - huge_full_template(area, colitem, - filer_window, template); - } - else - { - if (style == HUGE_ICONS) - huge_template(area, colitem, filer_window, template); - else if (style == LARGE_ICONS) - large_template(area, colitem, filer_window, template); - else - small_template(area, colitem, filer_window, template); - } -} - -/* Return the size needed for this item */ -void calc_size(FilerWindow *filer_window, CollectionItem *colitem, - int *width, int *height) -{ - int pix_width, pix_height; - int w; - DisplayStyle style = filer_window->display_style; - ViewData *view = (ViewData *) colitem->view_data; - - if (filer_window->details_type == DETAILS_NONE) - { - if (style == HUGE_ICONS) - { - if (view->image) - { - if (!view->image->huge_pixbuf) - pixmap_make_huge(view->image); - pix_width = view->image->huge_width; - pix_height = view->image->huge_height; - } - else - { - pix_width = HUGE_WIDTH * 3 / 2; - pix_height = HUGE_HEIGHT * 3 / 2; - } - *width = MAX(pix_width, view->name_width) + 4; - *height = view->name_height + pix_height + 4; - } - else if (style == SMALL_ICONS) - { - w = MIN(view->name_width, o_small_width.int_value); - *width = SMALL_WIDTH + 12 + w; - *height = MAX(view->name_height, SMALL_HEIGHT) + 4; - } - else - { - if (view->image) - pix_width = view->image->width; - else - pix_width = ICON_WIDTH; - *width = MAX(pix_width, view->name_width) + 4; - *height = view->name_height + ICON_HEIGHT + 2; - } - } - else - { - w = view->details_width; - if (style == HUGE_ICONS) - { - *width = HUGE_WIDTH + 12 + MAX(w, view->name_width); - *height = HUGE_HEIGHT - 4; - } - else if (style == SMALL_ICONS) - { - int text_height; - - *width = SMALL_WIDTH + view->name_width + 12 + w; - text_height = MAX(view->name_height, - view->details_height); - *height = MAX(text_height, SMALL_HEIGHT) + 4; - } - else - { - *width = ICON_WIDTH + 12 + MAX(w, view->name_width); - *height = ICON_HEIGHT; - } - } -} - -/* Draw this icon (including any symlink or mount symbol) inside the - * given rectangle. - */ -void draw_huge_icon(GtkWidget *widget, - GdkRectangle *area, - DirItem *item, - MaskedPixmap *image, - gboolean selected) -{ - int width, height; - int image_x; - int image_y; - - if (!image) - return; - - width = image->huge_width; - height = image->huge_height; - image_x = area->x + ((area->width - width) >> 1); - image_y = MAX(0, area->height - height - 6); - - gdk_pixbuf_render_to_drawable_alpha( - selected ? image->huge_pixbuf_lit - : image->huge_pixbuf, - widget->window, - 0, 0, /* src */ - image_x, area->y + image_y, /* dest */ - width, height, - GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */ - GDK_RGB_DITHER_NORMAL, 0, 0); - - if (item->flags & ITEM_FLAG_SYMLINK) - { - gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf, - widget->window, - 0, 0, /* src */ - image_x, area->y + 2, /* dest */ - -1, -1, - GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */ - GDK_RGB_DITHER_NORMAL, 0, 0); - } - else if (item->flags & ITEM_FLAG_MOUNT_POINT) - { - MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED - ? im_mounted - : im_unmounted; - - gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf, - widget->window, - 0, 0, /* src */ - image_x, area->y + 2, /* dest */ - -1, -1, - GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */ - GDK_RGB_DITHER_NORMAL, 0, 0); - } -} - /* Draw this icon (including any symlink or mount symbol) inside the * given rectangle. */ @@ -398,8 +180,8 @@ void draw_large_icon(GtkWidget *widget, int sort_by_name(const void *item1, const void *item2) { - const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data; - const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data; + const DirItem *i1 = (DirItem *) item1; + const DirItem *i2 = (DirItem *) item2; char *n1 = i1->leafname_collate; char *n2 = i2->leafname_collate; @@ -459,8 +241,8 @@ int sort_by_name(const void *item1, const void *item2) int sort_by_type(const void *item1, const void *item2) { - const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data; - const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data; + const DirItem *i1 = (DirItem *) item1; + const DirItem *i2 = (DirItem *) item2; MIME_type *m1, *m2; int diff = i1->base_type - i2->base_type; @@ -493,8 +275,8 @@ int sort_by_type(const void *item1, const void *item2) int sort_by_date(const void *item1, const void *item2) { - const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data; - const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data; + const DirItem *i1 = (DirItem *) item1; + const DirItem *i2 = (DirItem *) item2; SORT_DIRS; @@ -505,8 +287,8 @@ int sort_by_date(const void *item1, const void *item2) int sort_by_size(const void *item1, const void *item2) { - const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data; - const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data; + const DirItem *i1 = (DirItem *) item1; + const DirItem *i2 = (DirItem *) item2; SORT_DIRS; @@ -515,28 +297,6 @@ int sort_by_size(const void *item1, const void *item2) sort_by_name(item1, item2); } -/* Make the items as small as possible */ -void shrink_grid(FilerWindow *filer_window) -{ - int i; - Collection *col = filer_window->collection; - int width = MIN_ITEM_WIDTH; - int height = SMALL_HEIGHT; - - for (i = 0; i < col->number_of_items; i++) - { - int w, h; - - calc_size(filer_window, &col->items[i], &w, &h); - if (w > width) - width = w; - if (h > height) - height = h; - } - - collection_set_item_size(col, width, height); -} - void display_set_sort_fn(FilerWindow *filer_window, int (*fn)(const void *a, const void *b)) { @@ -545,8 +305,7 @@ void display_set_sort_fn(FilerWindow *filer_window, filer_window->sort_fn = fn; - collection_qsort(filer_window->collection, - filer_window->sort_fn); + view_sort(VIEW(filer_window->view)); } void display_set_layout(FilerWindow *filer_window, @@ -558,7 +317,7 @@ void display_set_layout(FilerWindow *filer_window, display_style_set(filer_window, style); display_details_set(filer_window, details); - shrink_grid(filer_window); + view_style_changed(VIEW(filer_window->view), 0); if (o_filer_auto_resize.int_value != RESIZE_NEVER) filer_window_autosize(filer_window, TRUE); @@ -572,9 +331,7 @@ void display_set_thumbs(FilerWindow *filer_window, gboolean thumbs) filer_window->show_thumbs = thumbs; - display_update_views(filer_window); - - gtk_widget_queue_draw(GTK_WIDGET(filer_window->collection)); + view_style_changed(VIEW(filer_window->view), VIEW_UPDATE_VIEWDATA); if (!thumbs) filer_cancel_thumbnails(filer_window); @@ -601,61 +358,20 @@ void display_set_hidden(FilerWindow *filer_window, gboolean hidden) */ void display_set_autoselect(FilerWindow *filer_window, const gchar *leaf) { - Collection *col; - int i; - - g_return_if_fail(filer_window != NULL); - col = filer_window->collection; + gchar *new; - for (i = 0; i < col->number_of_items; i++) - { - DirItem *item = (DirItem *) col->items[i].data; + g_return_if_fail(filer_window != NULL); + g_return_if_fail(leaf != NULL); + + new = g_strdup(leaf); /* leaf == old value sometimes */ - if (strcmp(item->leafname, leaf) == 0) - { - if (col->cursor_item != -1) - collection_set_cursor_item(col, i); - else - collection_wink_item(col, i); - leaf = NULL; - goto out; - } - } - -out: g_free(filer_window->auto_select); - if (leaf) - filer_window->auto_select = g_strdup(leaf); - else - filer_window->auto_select = NULL; -} + filer_window->auto_select = NULL; -gboolean display_is_truncated(FilerWindow *filer_window, int i) -{ - Template template; - Collection *collection = filer_window->collection; - CollectionItem *colitem = &collection->items[i]; - int col = i % collection->columns; - int row = i / collection->columns; - GdkRectangle area; - ViewData *view = (ViewData *) colitem->view_data; - - if (filer_window->display_style == LARGE_ICONS || - filer_window->display_style == HUGE_ICONS) - return FALSE; /* These wrap rather than truncate */ - - area.x = col * collection->item_width; - area.y = row * collection->item_height; - area.height = collection->item_height; - - if (col == collection->columns - 1) - area.width = GTK_WIDGET(collection)->allocation.width - area.x; + if (view_autoselect(VIEW(filer_window->view), new)) + g_free(new); else - area.width = collection->item_width; - - fill_template(&area, colitem, filer_window, &template); - - return template.leafname.width < view->name_width; + filer_window->auto_select = new; } /* Change the icon size (wraps) */ @@ -696,44 +412,6 @@ ViewData *display_create_viewdata(FilerWindow *filer_window, DirItem *item) return view; } -void display_free_colitem(Collection *collection, CollectionItem *colitem) -{ - ViewData *view = (ViewData *) colitem->view_data; - - if (!view) - return; - - if (view->layout) - { - g_object_unref(G_OBJECT(view->layout)); - view->layout = NULL; - } - if (view->details) - g_object_unref(G_OBJECT(view->details)); - - if (view->image) - g_object_unref(view->image); - - g_free(view); -} - -/* Recalculate all the ViewData structs for this window. - * Useful when the display style has changed. - */ -void display_update_views(FilerWindow *filer_window) -{ - Collection *collection = filer_window->collection; - int i; - - for (i = 0; i < collection->number_of_items; i++) - { - CollectionItem *ci = &collection->items[i]; - - display_update_view(filer_window, (DirItem *) ci->data, - (ViewData *) ci->view_data, TRUE); - } -} - /**************************************************************** * INTERNAL FUNCTIONS * ****************************************************************/ @@ -745,285 +423,16 @@ static void options_changed(void) for (next = all_filer_windows; next; next = next->next) { FilerWindow *filer_window = (FilerWindow *) next->data; - - if (o_large_width.has_changed || o_small_width.has_changed) - { - /* Recreate PangoLayout */ - display_update_views(filer_window); - } + int flags = 0; if (o_intelligent_sort.has_changed || o_display_dirs_first.has_changed) - { - collection_qsort(filer_window->collection, - filer_window->sort_fn); - } - shrink_grid(filer_window); - gtk_widget_queue_draw(filer_window->window); - } -} - -static void huge_template(GdkRectangle *area, CollectionItem *colitem, - FilerWindow *filer_window, Template *template) -{ - int col_width = filer_window->collection->item_width; - int text_x, text_y; - ViewData *view = (ViewData *) colitem->view_data; - MaskedPixmap *image = view->image; - - if (image) - { - if (!image->huge_pixbuf) - pixmap_make_huge(image); - template->icon.width = image->huge_width; - template->icon.height = image->huge_height; - } - else - { - template->icon.width = HUGE_WIDTH * 3 / 2; - template->icon.height = HUGE_HEIGHT; - } - - template->leafname.width = view->name_width; - template->leafname.height = view->name_height; - - text_x = area->x + ((col_width - template->leafname.width) >> 1); - text_y = area->y + area->height - template->leafname.height; - - template->leafname.x = text_x; - template->leafname.y = text_y; - - template->icon.x = area->x + ((col_width - template->icon.width) >> 1); - template->icon.y = template->leafname.y - template->icon.height - 2; -} - -static void large_template(GdkRectangle *area, CollectionItem *colitem, - FilerWindow *filer_window, Template *template) -{ - int col_width = filer_window->collection->item_width; - int iwidth, iheight; - int image_x; - int image_y; - ViewData *view = (ViewData *) colitem->view_data; - MaskedPixmap *image = view->image; - - int text_x, text_y; - - if (image) - { - iwidth = MIN(image->width, ICON_WIDTH); - iheight = MIN(image->height + 6, ICON_HEIGHT); - } - else - { - iwidth = ICON_WIDTH; - iheight = ICON_HEIGHT; - } - image_x = area->x + ((col_width - iwidth) >> 1); - - template->leafname.width = view->name_width; - template->leafname.height = view->name_height; - - text_x = area->x + ((col_width - template->leafname.width) >> 1); - text_y = area->y + ICON_HEIGHT + 2; - - template->leafname.x = text_x; - template->leafname.y = text_y; - - image_y = text_y - iheight; - image_y = MAX(area->y, image_y); - - template->icon.x = image_x; - template->icon.y = image_y; - template->icon.width = iwidth; - template->icon.height = MIN(ICON_HEIGHT, iheight); -} - -static void small_template(GdkRectangle *area, CollectionItem *colitem, - FilerWindow *filer_window, Template *template) -{ - int text_x = area->x + SMALL_WIDTH + 4; - int low_text_y; - int max_text_width = area->width - SMALL_WIDTH - 4; - ViewData *view = (ViewData *) colitem->view_data; - - low_text_y = area->y + area->height / 2 - view->name_height / 2; - - template->leafname.x = text_x; - template->leafname.y = low_text_y; - template->leafname.width = MIN(max_text_width, view->name_width); - template->leafname.height = view->name_height; - - template->icon.x = area->x; - template->icon.y = area->y + 1; - template->icon.width = SMALL_WIDTH; - template->icon.height = SMALL_HEIGHT; -} + view_sort(VIEW(filer_window->view)); -static void huge_full_template(GdkRectangle *area, CollectionItem *colitem, - FilerWindow *filer_window, Template *template) -{ - int max_text_width = area->width - HUGE_WIDTH - 4; - ViewData *view = (ViewData *) colitem->view_data; - MaskedPixmap *image = view->image; - - if (image) - { - if (!image->huge_pixbuf) - pixmap_make_huge(image); - template->icon.width = image->huge_width; - template->icon.height = image->huge_height; - } - else - { - template->icon.width = HUGE_WIDTH * 3 / 2; - template->icon.height = HUGE_HEIGHT; - } - - template->icon.x = area->x + (HUGE_WIDTH - template->icon.width) / 2; - template->icon.y = area->y + (area->height - template->icon.height) / 2; - - template->leafname.x = area->x + HUGE_WIDTH + 4; - template->leafname.y = area->y + area->height / 2 - - (view->name_height + 2 + view->details_height) / 2; - template->leafname.width = MIN(max_text_width, view->name_width); - template->leafname.height = view->name_height; - - if (!image) - return; /* Not scanned yet */ - - template->details.x = template->leafname.x; - template->details.y = template->leafname.y + view->name_height + 2; -} - -static void large_full_template(GdkRectangle *area, CollectionItem *colitem, - FilerWindow *filer_window, Template *template) -{ - int max_text_width = area->width - ICON_WIDTH - 4; - ViewData *view = (ViewData *) colitem->view_data; - MaskedPixmap *image = view->image; - - if (image) - { - template->icon.width = image->width; - template->icon.height = image->height; - } - else - { - template->icon.width = ICON_WIDTH; - template->icon.height = ICON_HEIGHT; - } - - template->icon.x = area->x + (ICON_WIDTH - template->icon.width) / 2; - template->icon.y = area->y + (area->height - template->icon.height) / 2; - - - template->leafname.x = area->x + ICON_WIDTH + 4; - template->leafname.y = area->y + area->height / 2 - - (view->name_height + 2 + view->details_height) / 2; - template->leafname.width = MIN(max_text_width, view->name_width); - template->leafname.height = view->name_height; - - if (!image) - return; /* Not scanned yet */ - - template->details.x = template->leafname.x; - template->details.y = template->leafname.y + view->name_height + 2; -} - -static void small_full_template(GdkRectangle *area, CollectionItem *colitem, - FilerWindow *filer_window, Template *template) -{ - int col_width = filer_window->collection->item_width; - ViewData *view = (ViewData *) colitem->view_data; - - small_template(area, colitem, filer_window, template); - - if (!view->image) - return; /* Not scanned yet */ - - template->details.x = area->x + col_width - template->details.width; - template->details.y = area->y + area->height / 2 - \ - view->details_height / 2; -} - -#define INSIDE(px, py, area) \ - (px >= area.x && py >= area.y && \ - px <= area.x + area.width && py <= area.y + area.height) - -static gboolean test_point(Collection *collection, - int point_x, int point_y, - CollectionItem *colitem, - int width, int height, - FilerWindow *filer_window) -{ - Template template; - GdkRectangle area; - ViewData *view = (ViewData *) colitem->view_data; - - area.x = 0; - area.y = 0; - area.width = width; - area.height = height; - - fill_template(&area, colitem, filer_window, &template); - - return INSIDE(point_x, point_y, template.leafname) || - INSIDE(point_x, point_y, template.icon) || - (view->details && INSIDE(point_x, point_y, template.details)); -} - -static void draw_small_icon(GtkWidget *widget, - GdkRectangle *area, - DirItem *item, - MaskedPixmap *image, - gboolean selected) -{ - int width, height, image_x, image_y; - - if (!image) - return; - - if (!image->sm_pixbuf) - pixmap_make_small(image); - - width = MIN(image->sm_width, SMALL_WIDTH); - height = MIN(image->sm_height, SMALL_HEIGHT); - image_x = area->x + ((area->width - width) >> 1); - image_y = MAX(0, SMALL_HEIGHT - image->sm_height); - - gdk_pixbuf_render_to_drawable_alpha( - selected ? image->sm_pixbuf_lit : image->sm_pixbuf, - widget->window, - 0, 0, /* src */ - image_x, area->y + image_y, /* dest */ - width, height, - GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */ - GDK_RGB_DITHER_NORMAL, 0, 0); + if (o_large_width.has_changed || o_small_width.has_changed) + flags |= VIEW_UPDATE_NAME; /* Recreate PangoLayout */ - if (item->flags & ITEM_FLAG_SYMLINK) - { - gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf, - widget->window, - 0, 0, /* src */ - image_x, area->y + 8, /* dest */ - -1, -1, - GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */ - GDK_RGB_DITHER_NORMAL, 0, 0); - } - else if (item->flags & ITEM_FLAG_MOUNT_POINT) - { - MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED - ? im_mounted - : im_unmounted; - - gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf, - widget->window, - 0, 0, /* src */ - image_x + 2, area->y + 2, /* dest */ - -1, -1, - GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */ - GDK_RGB_DITHER_NORMAL, 0, 0); + view_style_changed(VIEW(filer_window->view), flags); } } @@ -1133,83 +542,21 @@ static char *details(FilerWindow *filer_window, DirItem *item) return buf; } -static void draw_item(GtkWidget *widget, - CollectionItem *colitem, - GdkRectangle *area, - FilerWindow *filer_window) -{ - DirItem *item = (DirItem *) colitem->data; - gboolean selected = colitem->selected; - Template template; - ViewData *view = (ViewData *) colitem->view_data; - - g_return_if_fail(view != NULL); - - fill_template(area, colitem, filer_window, &template); - - /* Set up GC for coloured file types */ - if (!type_gc) - type_gc = gdk_gc_new(widget->window); - - gdk_gc_set_foreground(type_gc, type_get_colour(item, - &widget->style->fg[GTK_STATE_NORMAL])); - - if (template.icon.width <= SMALL_WIDTH && - template.icon.height <= SMALL_HEIGHT) - { - draw_small_icon(widget, &template.icon, - item, view->image, selected); - } - else if (template.icon.width <= ICON_WIDTH && - template.icon.height <= ICON_HEIGHT) - { - draw_large_icon(widget, &template.icon, - item, view->image, selected); - } - else - { - draw_huge_icon(widget, &template.icon, - item, view->image, selected); - } - - draw_string(widget, view->layout, - &template.leafname, - view->name_width, - filer_window->selection_state, - selected, TRUE); - if (view->details) - draw_string(widget, view->details, - &template.details, - template.details.width, - filer_window->selection_state, - selected, TRUE); -} - -/* Note: Call shrink_grid after this */ +/* Note: Call style_changed after this */ static void display_details_set(FilerWindow *filer_window, DetailsType details) { if (filer_window->details_type == details) return; filer_window->details_type = details; - display_update_views(filer_window); - - gtk_widget_queue_draw(GTK_WIDGET(filer_window->collection)); } -/* Note: Call shrink_grid after this */ +/* Note: Call style_changed after this */ static void display_style_set(FilerWindow *filer_window, DisplayStyle style) { if (filer_window->display_style == style) return; filer_window->display_style = style; - - display_update_views(filer_window); - - collection_set_functions(filer_window->collection, - (CollectionDrawFunc) draw_item, - (CollectionTestFunc) test_point, - filer_window); } void display_update_view(FilerWindow *filer_window, @@ -1341,54 +688,3 @@ void display_update_view(FilerWindow *filer_window, view->name_height = h / PANGO_SCALE; } -/* 'box' renders a background box if the string is also selected */ -static void draw_string(GtkWidget *widget, - PangoLayout *layout, - GdkRectangle *area, /* Area available on screen */ - int width, /* Width of the full string */ - GtkStateType selection_state, - gboolean selected, - gboolean box) -{ - GdkGC *gc = selected - ? widget->style->fg_gc[selection_state] - : type_gc; - - if (selected && box) - gtk_paint_flat_box(widget->style, widget->window, - selection_state, GTK_SHADOW_NONE, - NULL, widget, "text", - area->x, area->y, - MIN(width, area->width), - area->height); - - if (width > area->width) - { - gdk_gc_set_clip_origin(gc, 0, 0); - gdk_gc_set_clip_rectangle(gc, area); - } - - gdk_draw_layout(widget->window, gc, area->x, area->y, layout); - - if (width > area->width) - { - static GdkGC *red_gc = NULL; - - if (!red_gc) - { - gboolean success; - GdkColor red = {0, 0xffff, 0, 0}; - - red_gc = gdk_gc_new(widget->window); - gdk_colormap_alloc_colors( - gtk_widget_get_colormap(widget), - &red, 1, FALSE, TRUE, &success); - gdk_gc_set_foreground(red_gc, &red); - } - gdk_draw_rectangle(widget->window, red_gc, TRUE, - area->x + area->width - 1, area->y, - 1, area->height); - gdk_gc_set_clip_rectangle(gc, NULL); - } -} - diff --git a/ROX-Filer/src/display.h b/ROX-Filer/src/display.h index 1cfc9433..059361e5 100644 --- a/ROX-Filer/src/display.h +++ b/ROX-Filer/src/display.h @@ -32,6 +32,7 @@ struct _ViewData extern Option o_display_inherit_options, o_display_sort_by; extern Option o_display_size, o_display_details, o_display_show_hidden; extern Option o_display_show_thumbs; +extern Option o_small_width; /* Prototypes */ void display_init(); @@ -47,9 +48,6 @@ int sort_by_size(const void *item1, const void *item2); void display_set_sort_fn(FilerWindow *filer_window, int (*fn)(const void *a, const void *b)); void display_set_autoselect(FilerWindow *filer_window, const gchar *leaf); -void shrink_grid(FilerWindow *filer_window); -void calc_size(FilerWindow *filer_window, CollectionItem *colitem, - int *width, int *height); void draw_large_icon(GtkWidget *widget, GdkRectangle *area, @@ -60,7 +58,6 @@ gboolean display_is_truncated(FilerWindow *filer_window, int i); void display_change_size(FilerWindow *filer_window, gboolean bigger); ViewData *display_create_viewdata(FilerWindow *filer_window, DirItem *item); -void display_free_colitem(Collection *collection, CollectionItem *colitem); void display_update_view(FilerWindow *filer_window, DirItem *item, ViewData *view, diff --git a/ROX-Filer/src/dnd.c b/ROX-Filer/src/dnd.c index 341f1419..ad1d0d0b 100644 --- a/ROX-Filer/src/dnd.c +++ b/ROX-Filer/src/dnd.c @@ -536,7 +536,10 @@ static gboolean drag_motion(GtkWidget *widget, !(item->flags & ITEM_FLAG_APPDIR)) { #if 0 - /* XXX: Do we still need this under 2.0? */ + /* XXX: This is needed so that directories don't + * spring open while we scroll. Should go in + * collection.c, I think. + */ GtkObject *vadj = GTK_OBJECT(filer_window->collection->vadj); /* Subdir: prepare for spring-open */ diff --git a/ROX-Filer/src/filer.c b/ROX-Filer/src/filer.c index a1e4570e..8cdf997f 100644 --- a/ROX-Filer/src/filer.c +++ b/ROX-Filer/src/filer.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -62,6 +61,8 @@ #include "appinfo.h" #include "mount.h" #include "xml.h" +#include "view_iface.h" +#include "view_collection.h" static XMLwrapper *groups = NULL; @@ -70,18 +71,11 @@ GList *all_filer_windows = NULL; static FilerWindow *window_with_primary = NULL; -/* Item we are about to display a tooltip for */ -static DirItem *tip_item = NULL; -static GtkWidget *tip_widget = NULL; -static gint tip_timeout = 0; -static time_t tip_time = 0; /* Time tip widget last closed */ - /* Static prototypes */ static void attach(FilerWindow *filer_window); static void detach(FilerWindow *filer_window); static void filer_window_destroyed(GtkWidget *widget, FilerWindow *filer_window); -static void add_item(FilerWindow *filer_window, DirItem *item); static void update_display(Directory *dir, DirAction action, GPtrArray *items, @@ -90,24 +84,12 @@ static void set_scanning_display(FilerWindow *filer_window, gboolean scanning); static gboolean may_rescan(FilerWindow *filer_window, gboolean warning); static gboolean minibuffer_show_cb(FilerWindow *filer_window); static FilerWindow *find_filer_window(const char *sym_path, FilerWindow *diff); -static gint coll_button_release(GtkWidget *widget, - GdkEventButton *event, - FilerWindow *filer_window); -static gint coll_button_press(GtkWidget *widget, - GdkEventButton *event, - FilerWindow *filer_window); -static gint coll_motion_notify(GtkWidget *widget, - GdkEventMotion *event, - FilerWindow *filer_window); -static void perform_action(FilerWindow *filer_window, GdkEventButton *event); static void filer_add_widgets(FilerWindow *filer_window); static void filer_add_signals(FilerWindow *filer_window); -static void filer_tooltip_prime(FilerWindow *filer_window, DirItem *item); -static void show_tooltip(guchar *text); static void filer_size_for(FilerWindow *filer_window, int w, int h, int n, gboolean allow_shrink); -static void set_selection_state(FilerWindow *collection, gboolean normal); +static void set_selection_state(FilerWindow *filer_window, gboolean normal); static void filer_next_thumb(GObject *window, const gchar *path); static void start_thumb_scanning(FilerWindow *filer_window); static void filer_options_changed(void); @@ -186,42 +168,6 @@ static gboolean if_deleted(gpointer item, gpointer removed) return FALSE; } -static void update_item(FilerWindow *filer_window, DirItem *item) -{ - char *leafname = item->leafname; - Collection *collection = filer_window->collection; - int old_w = collection->item_width; - int old_h = collection->item_height; - int w, h, i; - CollectionItem *colitem; - - if (leafname[0] == '.' && filer_window->show_hidden == FALSE) - return; - - i = collection_find_item(collection, item, filer_window->sort_fn); - - if (i < 0) - { - g_warning("Failed to find '%s'\n", leafname); - return; - } - - colitem = &collection->items[i]; - - display_update_view(filer_window, - (DirItem *) colitem->data, - (ViewData *) colitem->view_data, - FALSE); - - calc_size(filer_window, colitem, &w, &h); - if (w > old_w || h > old_h) - collection_set_item_size(collection, - MAX(old_w, w), - MAX(old_h, h)); - - collection_draw_item(collection, i, TRUE); -} - /* Resize the filer window to w x h pixels, plus border (not clamped) */ static void filer_window_set_size(FilerWindow *filer_window, int w, int h, @@ -389,7 +335,7 @@ static void filer_size_for(FilerWindow *filer_window, */ static gint open_filer_window(FilerWindow *filer_window) { - shrink_grid(filer_window); + view_style_changed(VIEW(filer_window->view), 0); if (filer_window->open_timeout) { @@ -412,30 +358,18 @@ static void update_display(Directory *dir, GPtrArray *items, FilerWindow *filer_window) { - int old_num; - int i; Collection *collection = filer_window->collection; + ViewIface *view = (ViewIface *) filer_window->view; switch (action) { case DIR_ADD: - old_num = collection->number_of_items; - for (i = 0; i < items->len; i++) - { - DirItem *item = (DirItem *) items->pdata[i]; - - add_item(filer_window, item); - } - - if (old_num != collection->number_of_items) - collection_qsort(collection, - filer_window->sort_fn); - + view_add_items(view, items); /* Open and resize if currently hidden */ open_filer_window(filer_window); break; case DIR_REMOVE: - collection_delete_if(collection, if_deleted, items); + view_delete_if(view, if_deleted, items); break; case DIR_START_SCAN: set_scanning_display(filer_window, TRUE); @@ -468,14 +402,7 @@ static void update_display(Directory *dir, start_thumb_scanning(filer_window); break; case DIR_UPDATE: - collection_qsort(collection, filer_window->sort_fn); - - for (i = 0; i < items->len; i++) - { - DirItem *item = (DirItem *) items->pdata[i]; - - update_item(filer_window, item); - } + view_update_items(view, items); break; } } @@ -483,7 +410,7 @@ static void update_display(Directory *dir, static void attach(FilerWindow *filer_window) { gdk_window_set_cursor(filer_window->window->window, busy_cursor); - collection_clear(filer_window->collection); + view_clear(VIEW(filer_window->view)); filer_window->scanning = TRUE; dir_attach(filer_window->directory, (DirCallback) update_display, filer_window); @@ -531,7 +458,7 @@ static void filer_window_destroyed(GtkWidget *widget, g_list_free(filer_window->thumb_queue); } - filer_tooltip_prime(NULL, NULL); + tooltip_show(NULL); g_free(filer_window->auto_select); g_free(filer_window->real_path); @@ -541,38 +468,6 @@ static void filer_window_destroyed(GtkWidget *widget, one_less_window(); } -/* Add a single object to a directory display */ -static void add_item(FilerWindow *filer_window, DirItem *item) -{ - char *leafname = item->leafname; - Collection *collection = filer_window->collection; - int old_w = collection->item_width; - int old_h = collection->item_height; - int w, h, i; - - if (leafname[0] == '.') - { - if (!filer_window->show_hidden) - return; - - if (leafname[1] == '\0') - return; /* Never show '.' */ - - if (leafname[1] == '.' && leafname[2] == '\0') - return; /* Never show '..' */ - } - - i = collection_insert(collection, item, - display_create_viewdata(filer_window, item)); - - calc_size(filer_window, &collection->items[i], &w, &h); - - if (w > old_w || h > old_h) - collection_set_item_size(collection, - MAX(old_w, w), - MAX(old_h, h)); -} - /* Returns TRUE iff the directory still exists. */ static gboolean may_rescan(FilerWindow *filer_window, gboolean warning) { @@ -603,19 +498,26 @@ static gboolean may_rescan(FilerWindow *filer_window, gboolean warning) return TRUE; } -/* The collection widget has lost the primary selection */ -static gint collection_lose_selection(GtkWidget *widget, - GdkEventSelection *event) +/* No items are now selected. This might be because another app claimed + * the selection or because the user unselected all the items. + */ +void filer_lost_selection(FilerWindow *filer_window, gint time) +{ + if (window_with_primary == filer_window) + { + window_with_primary = NULL; + gtk_selection_owner_set(NULL, GDK_SELECTION_PRIMARY, time); + } +} + +/* Another app has claimed the primary selection */ +void filer_lost_primary(FilerWindow *filer_window) { - if (window_with_primary && - window_with_primary->collection == COLLECTION(widget)) + if (window_with_primary && window_with_primary == filer_window) { - FilerWindow *filer_window = window_with_primary; window_with_primary = NULL; set_selection_state(filer_window, FALSE); } - - return FALSE; } /* Someone wants us to send them the selection */ @@ -676,41 +578,19 @@ static void selection_get(GtkWidget *widget, g_string_free(header, TRUE); } -/* No items are now selected. This might be because another app claimed - * the selection or because the user unselected all the items. - */ -static void lose_selection(Collection *collection, - guint time, - gpointer user_data) +void filer_selection_changed(FilerWindow *filer_window, gint time) { - FilerWindow *filer_window = (FilerWindow *) user_data; - - if (window_with_primary == filer_window) - { - window_with_primary = NULL; - gtk_selection_owner_set(NULL, - GDK_SELECTION_PRIMARY, - time); - } -} - -static void selection_changed(Collection *collection, - gint time, - gpointer user_data) -{ - FilerWindow *filer_window = (FilerWindow *) user_data; - /* Selection has been changed -- try to grab the primary selection * if we don't have it. */ if (window_with_primary == filer_window) return; /* Already got it */ - if (!collection->number_selected) + if (!filer_window->collection->number_selected) return; /* Nothing selected */ if (filer_window->temp_item_selected == FALSE && - gtk_selection_owner_set(GTK_WIDGET(collection), + gtk_selection_owner_set(GTK_WIDGET(filer_window->collection), GDK_SELECTION_PRIMARY, time)) { @@ -789,7 +669,7 @@ static gint pointer_out(GtkWidget *widget, GdkEventCrossing *event, FilerWindow *filer_window) { - filer_tooltip_prime(NULL, NULL); + tooltip_show(NULL); return FALSE; } @@ -1069,7 +949,7 @@ static gint key_press_event(GtkWidget *widget, change_to_parent(filer_window); break; case GDK_backslash: - filer_tooltip_prime(NULL, NULL); + tooltip_show(NULL); show_filer_menu(filer_window, (GdkEvent *) event, filer_window->collection->cursor_item); break; @@ -1147,7 +1027,7 @@ void filer_change_to(FilerWindow *filer_window, filer_cancel_thumbnails(filer_window); - filer_tooltip_prime(NULL, NULL); + tooltip_show(NULL); sym_path = g_strdup(path); real_path = pathdup(path); @@ -1241,37 +1121,6 @@ DirItem *filer_selected_item(FilerWindow *filer_window) return NULL; } -/* Append all the URIs in the selection to the string */ -static void create_uri_list(FilerWindow *filer_window, GString *string) -{ - Collection *collection = filer_window->collection; - GString *leader; - int i, num_selected; - - leader = g_string_new("file://"); - g_string_append(leader, our_host_name_for_dnd()); - g_string_append(leader, filer_window->sym_path); - if (leader->str[leader->len - 1] != '/') - g_string_append_c(leader, '/'); - - num_selected = collection->number_selected; - - for (i = 0; num_selected > 0; i++) - { - if (collection->items[i].selected) - { - DirItem *item = (DirItem *) collection->items[i].data; - - g_string_append(string, leader->str); - g_string_append(string, item->leafname); - g_string_append(string, "\r\n"); - num_selected--; - } - } - - g_string_free(leader, TRUE); -} - /* Creates and shows a new filer window. * If src_win != NULL then display options can be taken from that source window. * Returns the new filer window, or NULL on error. @@ -1417,9 +1266,6 @@ static void filer_add_widgets(FilerWindow *filer_window) filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); filer_set_title(filer_window); - /* The collection is the area that actually displays the files */ - collection = collection_new(); - /* This property is cleared when the window is destroyed. * You can thus ref filer_window->window and use this to see * if the window no longer exists. @@ -1427,10 +1273,11 @@ static void filer_add_widgets(FilerWindow *filer_window) g_object_set_data(G_OBJECT(filer_window->window), "filer_window", filer_window); + /* The view is the area that actually displays the files */ + filer_window->view = view_collection_new(filer_window); + gtk_widget_show(filer_window->view); + collection = GTK_WIDGET(filer_window->collection); /* XXX */ g_object_set_data(G_OBJECT(collection), "filer_window", filer_window); - filer_window->collection = COLLECTION(collection); - - filer_window->collection->free_item = display_free_colitem; /* Scrollbar on the right, everything else on the left */ hbox = gtk_hbox_new(FALSE, 0); @@ -1457,23 +1304,9 @@ static void filer_add_widgets(FilerWindow *filer_window) /* Now add the area for displaying the files. * The collection is one huge window that goes in a Viewport. */ - { - GtkWidget *viewport; - GtkAdjustment *adj; - - adj = filer_window->collection->vadj; - viewport = gtk_viewport_new(NULL, adj); - gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), - GTK_SHADOW_NONE); - gtk_container_add(GTK_CONTAINER(viewport), collection); - gtk_widget_show_all(viewport); - gtk_box_pack_start_defaults(GTK_BOX(vbox), viewport); - filer_window->scrollbar = gtk_vscrollbar_new(adj); - gtk_widget_set_size_request(viewport, 4, 4); - - gtk_container_set_resize_mode(GTK_CONTAINER(viewport), - GTK_RESIZE_IMMEDIATE); - } + gtk_box_pack_start_defaults(GTK_BOX(vbox), filer_window->view); + filer_window->scrollbar = + gtk_vscrollbar_new(filer_window->collection->vadj); /* And the minibuffer (hidden to start with) */ create_minibuffer(filer_window); @@ -1546,14 +1379,6 @@ static void filer_add_signals(FilerWindow *filer_window) GDK_BUTTON3_MOTION_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); - g_signal_connect_swapped(collection, "style_set", - G_CALLBACK(display_update_views), filer_window); - g_signal_connect(collection, "lose_selection", - G_CALLBACK(lose_selection), filer_window); - g_signal_connect(collection, "selection_changed", - G_CALLBACK(selection_changed), filer_window); - g_signal_connect(collection, "selection_clear_event", - G_CALLBACK(collection_lose_selection), NULL); g_signal_connect(collection, "selection_get", G_CALLBACK(selection_get), NULL); gtk_selection_add_targets(GTK_WIDGET(collection), GDK_SELECTION_PRIMARY, @@ -1562,12 +1387,6 @@ static void filer_add_signals(FilerWindow *filer_window) g_signal_connect(collection, "key_press_event", G_CALLBACK(key_press_event), filer_window); - g_signal_connect(collection, "button-release-event", - G_CALLBACK(coll_button_release), filer_window); - g_signal_connect(collection, "button-press-event", - G_CALLBACK(coll_button_press), filer_window); - g_signal_connect(collection, "motion-notify-event", - G_CALLBACK(coll_motion_notify), filer_window); /* Drag and drop events */ g_signal_connect(collection, "drag_data_get", @@ -1797,229 +1616,6 @@ void filer_detach_rescan(FilerWindow *filer_window) attach(filer_window); } -static gint coll_button_release(GtkWidget *widget, - GdkEventButton *event, - FilerWindow *filer_window) -{ - if (dnd_motion_release(event)) - { - if (motion_buttons_pressed == 0 && - filer_window->collection->lasso_box) - { - collection_end_lasso(filer_window->collection, - event->button == 1 ? GDK_SET : GDK_INVERT); - } - return FALSE; - } - - perform_action(filer_window, event); - - return FALSE; -} - -static void perform_action(FilerWindow *filer_window, GdkEventButton *event) -{ - Collection *collection = filer_window->collection; - DirItem *dir_item; - int item; - BindAction action; - gboolean press = event->type == GDK_BUTTON_PRESS; - gboolean selected = FALSE; - OpenFlags flags = 0; - - if (event->button > 3) - return; - - item = collection_get_item(collection, event->x, event->y); - - if (item != -1 && event->button == 1 && - collection->items[item].selected && - filer_window->selection_state == GTK_STATE_INSENSITIVE) - { - selection_changed(collection, event->time, filer_window); - return; - } - - if (filer_window->target_cb) - { - dnd_motion_ungrab(); - if (item != -1 && press && event->button == 1) - filer_window->target_cb(filer_window, item, - filer_window->target_data); - filer_target_mode(filer_window, NULL, NULL, NULL); - - return; - } - - action = bind_lookup_bev( - item == -1 ? BIND_DIRECTORY : BIND_DIRECTORY_ICON, - event); - - if (item != -1) - { - dir_item = (DirItem *) collection->items[item].data; - selected = collection->items[item].selected; - } - else - dir_item = NULL; - - switch (action) - { - case ACT_CLEAR_SELECTION: - collection_clear_selection(collection); - break; - case ACT_TOGGLE_SELECTED: - collection_toggle_item(collection, item); - break; - case ACT_SELECT_EXCL: - collection_clear_except(collection, item); - break; - case ACT_EDIT_ITEM: - flags |= OPEN_SHIFT; - /* (no break) */ - case ACT_OPEN_ITEM: - if (event->button != 1) - flags |= OPEN_CLOSE_WINDOW; - else - flags |= OPEN_SAME_WINDOW; - if (o_new_button_1.int_value) - flags ^= OPEN_SAME_WINDOW; - if (event->type == GDK_2BUTTON_PRESS) - collection_unselect_item(collection, item); - dnd_motion_ungrab(); - filer_openitem(filer_window, item, flags); - break; - case ACT_POPUP_MENU: - dnd_motion_ungrab(); - filer_tooltip_prime(NULL, NULL); - show_filer_menu(filer_window, (GdkEvent *) event, item); - break; - case ACT_PRIME_AND_SELECT: - if (!selected) - collection_clear_except(collection, item); - dnd_motion_start(MOTION_READY_FOR_DND); - break; - case ACT_PRIME_AND_TOGGLE: - collection_toggle_item(collection, item); - dnd_motion_start(MOTION_READY_FOR_DND); - break; - case ACT_PRIME_FOR_DND: - dnd_motion_start(MOTION_READY_FOR_DND); - break; - case ACT_IGNORE: - if (press && event->button < 4) - { - if (item) - collection_wink_item(collection, item); - dnd_motion_start(MOTION_NONE); - } - break; - case ACT_LASSO_CLEAR: - collection_clear_selection(collection); - /* (no break) */ - case ACT_LASSO_MODIFY: - collection_lasso_box(collection, event->x, event->y); - break; - case ACT_RESIZE: - filer_window_autosize(filer_window, TRUE); - break; - default: - g_warning("Unsupported action : %d\n", action); - break; - } -} - -static gint coll_button_press(GtkWidget *widget, - GdkEventButton *event, - FilerWindow *filer_window) -{ - collection_set_cursor_item(filer_window->collection, -1); - - if (dnd_motion_press(widget, event)) - perform_action(filer_window, event); - - return FALSE; -} - -static gint coll_motion_notify(GtkWidget *widget, - GdkEventMotion *event, - FilerWindow *filer_window) -{ - Collection *collection = filer_window->collection; - int i; - - i = collection_get_item(collection, event->x, event->y); - - if (i == -1) - filer_tooltip_prime(NULL, NULL); - else - filer_tooltip_prime(filer_window, - (DirItem *) collection->items[i].data); - - if (motion_state != MOTION_READY_FOR_DND) - return FALSE; - - if (!dnd_motion_moved(event)) - return FALSE; - - i = collection_get_item(collection, - event->x - (event->x_root - drag_start_x), - event->y - (event->y_root - drag_start_y)); - if (i == -1) - return FALSE; - - collection_wink_item(collection, -1); - - if (!collection->items[i].selected) - { - if (event->state & GDK_BUTTON1_MASK) - { - /* Select just this one */ - filer_window->temp_item_selected = TRUE; - collection_clear_except(collection, i); - } - else - { - if (collection->number_selected == 0) - filer_window->temp_item_selected = TRUE; - collection_select_item(collection, i); - } - } - - g_return_val_if_fail(collection->number_selected > 0, TRUE); - - if (collection->number_selected == 1) - { - DirItem *item = (DirItem *) collection->items[i].data; - ViewData *view = (ViewData *) collection->items[i].view_data; - - if (!item->image) - item = dir_update_item(filer_window->directory, - item->leafname); - - if (!item) - { - report_error(_("Item no longer exists!")); - return FALSE; - } - - drag_one_item(widget, event, - make_path(filer_window->sym_path, item->leafname)->str, - item, view ? view->image : NULL); - } - else - { - GString *uris; - - uris = g_string_new(NULL); - create_uri_list(filer_window, uris); - drag_selection(widget, event, uris->str); - g_string_free(uris, TRUE); - } - - return FALSE; -} - /* Puts the filer window into target mode. When an item is chosen, * fn(filer_window, item, data) is called. 'reason' will be displayed * on the toolbar while target mode is active. @@ -2056,204 +1652,6 @@ void filer_target_mode(FilerWindow *filer_window, gtk_label_set_text(GTK_LABEL(filer_window->toolbar_text), ""); } -/* Draw the black border */ -static gint filer_tooltip_draw(GtkWidget *w) -{ - gdk_draw_rectangle(w->window, w->style->fg_gc[w->state], FALSE, 0, 0, - w->allocation.width - 1, w->allocation.height - 1); - - return FALSE; -} - -/* When the tips window closed, record the time. If we try to open another - * tip soon, it will appear more quickly. - */ -static void tip_destroyed(gpointer data) -{ - time(&tip_time); -} - -/* It's time to make the tooltip appear. If we're not over the item any - * more, or the item doesn't need a tooltip, do nothing. - */ -static gboolean filer_tooltip_activate(FilerWindow *filer_window) -{ - Collection *collection; - gint x, y; - int i; - GString *tip = NULL; - guchar *fullpath = NULL; - - g_return_val_if_fail(tip_item != NULL, 0); - - tip_timeout = 0; - - show_tooltip(NULL); - - if (!filer_exists(filer_window)) - return FALSE; - - collection = filer_window->collection; - gdk_window_get_pointer(GTK_WIDGET(collection)->window, &x, &y, NULL); - i = collection_get_item(collection, x, y); - if (i == -1 || ((DirItem *) collection->items[i].data) != tip_item) - return FALSE; /* Not still under the pointer */ - - /* OK, the filer window still exists and the pointer is still - * over the same item. Do we need to show a tip? - */ - - tip = g_string_new(NULL); - - if (display_is_truncated(filer_window, i)) - { - g_string_append(tip, tip_item->leafname); - g_string_append_c(tip, '\n'); - } - - fullpath = make_path(filer_window->real_path, tip_item->leafname)->str; - - if (tip_item->flags & ITEM_FLAG_SYMLINK) - { - char *target; - - target = readlink_dup(fullpath); - if (target) - { - g_string_append(tip, _("Symbolic link to ")); - g_string_append(tip, target); - g_string_append_c(tip, '\n'); - g_free(target); - } - } - - if (tip_item->flags & ITEM_FLAG_APPDIR) - { - XMLwrapper *info; - xmlNode *node; - - info = appinfo_get(fullpath, tip_item); - if (info && ((node = xml_get_section(info, NULL, "Summary")))) - { - guchar *str; - str = xmlNodeListGetString(node->doc, - node->xmlChildrenNode, 1); - if (str) - { - g_string_append(tip, str); - g_string_append_c(tip, '\n'); - g_free(str); - } - } - if (info) - g_object_unref(info); - } - - if (!g_utf8_validate(tip_item->leafname, -1, NULL)) - g_string_append(tip, - _("This filename is not valid UTF-8. " - "You should rename it.\n")); - - if (tip->len > 1) - { - g_string_truncate(tip, tip->len - 1); - show_tooltip(tip->str); - } - - g_string_free(tip, TRUE); - - return FALSE; -} - -/* Display a tooltip-like widget near the pointer with 'text'. If 'text' is - * NULL, close any current tooltip. - */ -static void show_tooltip(guchar *text) -{ - GtkWidget *label; - int x, y, py; - int w, h; - - if (tip_widget) - { - gtk_widget_destroy(tip_widget); - tip_widget = NULL; - } - - if (!text) - return; - - /* Show the tip */ - tip_widget = gtk_window_new(GTK_WINDOW_POPUP); - gtk_widget_set_app_paintable(tip_widget, TRUE); - gtk_widget_set_name(tip_widget, "gtk-tooltips"); - - g_signal_connect_swapped(tip_widget, "expose_event", - G_CALLBACK(filer_tooltip_draw), tip_widget); - - label = gtk_label_new(text); - gtk_misc_set_padding(GTK_MISC(label), 4, 2); - gtk_container_add(GTK_CONTAINER(tip_widget), label); - gtk_widget_show(label); - gtk_widget_realize(tip_widget); - - w = tip_widget->allocation.width; - h = tip_widget->allocation.height; - gdk_window_get_pointer(NULL, &x, &py, NULL); - - x -= w / 2; - y = py + 12; /* I don't know the pointer height so I use a constant */ - - /* Now check for screen boundaries */ - x = CLAMP(x, 0, screen_width - w); - y = CLAMP(y, 0, screen_height - h); - - /* And again test if pointer is over the tooltip window */ - if (py >= y && py <= y + h) - y = py - h- 2; - gtk_window_move(GTK_WINDOW(tip_widget), x, y); - gtk_widget_show(tip_widget); - - g_signal_connect_swapped(tip_widget, "destroy", - G_CALLBACK(tip_destroyed), NULL); - time(&tip_time); -} - -/* Display a tooltip for 'item' after a while (if item is not NULL). - * Cancel any previous tooltip. - */ -static void filer_tooltip_prime(FilerWindow *filer_window, DirItem *item) -{ - time_t now; - - time(&now); - - if (item == tip_item) - return; - - if (tip_timeout) - { - gtk_timeout_remove(tip_timeout); - tip_timeout = 0; - tip_item = NULL; - } - if (tip_widget) - { - gtk_widget_destroy(tip_widget); - tip_widget = NULL; - } - - tip_item = item; - if (filer_window && item) - { - int delay = now - tip_time > 2 ? 1000 : 200; - - tip_timeout = gtk_timeout_add(delay, - (GtkFunction) filer_tooltip_activate, - filer_window); - } -} - static void set_selection_state(FilerWindow *filer_window, gboolean normal) { GtkStateType old_state = filer_window->selection_state; @@ -2440,3 +1838,53 @@ static void set_style_by_number_of_items(FilerWindow *filer_window) display_set_layout(filer_window, LARGE_ICONS, filer_window->details_type); } + +/* Append interesting information to this GString */ +void filer_add_tip_details(FilerWindow *filer_window, + GString *tip, DirItem *item) +{ + guchar *fullpath = NULL; + + fullpath = make_path(filer_window->real_path, item->leafname)->str; + + if (item->flags & ITEM_FLAG_SYMLINK) + { + char *target; + + target = readlink_dup(fullpath); + if (target) + { + g_string_append(tip, _("Symbolic link to ")); + g_string_append(tip, target); + g_string_append_c(tip, '\n'); + g_free(target); + } + } + + if (item->flags & ITEM_FLAG_APPDIR) + { + XMLwrapper *info; + xmlNode *node; + + info = appinfo_get(fullpath, item); + if (info && ((node = xml_get_section(info, NULL, "Summary")))) + { + guchar *str; + str = xmlNodeListGetString(node->doc, + node->xmlChildrenNode, 1); + if (str) + { + g_string_append(tip, str); + g_string_append_c(tip, '\n'); + g_free(str); + } + } + if (info) + g_object_unref(info); + } + + if (!g_utf8_validate(item->leafname, -1, NULL)) + g_string_append(tip, + _("This filename is not valid UTF-8. " + "You should rename it.\n")); +} diff --git a/ROX-Filer/src/filer.h b/ROX-Filer/src/filer.h index b55d8dcd..679d0ec0 100644 --- a/ROX-Filer/src/filer.h +++ b/ROX-Filer/src/filer.h @@ -41,6 +41,7 @@ struct _FilerWindow gchar *sym_path; /* Path the user sees */ gchar *real_path; /* realpath(sym_path) */ Collection *collection; + GtkWidget *view; gboolean temp_item_selected; gboolean show_hidden; FilerFlags flags; @@ -116,5 +117,11 @@ void filer_create_thumb(FilerWindow *filer_window, const gchar *pathname); void filer_cancel_thumbnails(FilerWindow *filer_window); void filer_set_title(FilerWindow *filer_window); void filer_create_thumbs(FilerWindow *filer_window); +void filer_add_tip_details(FilerWindow *filer_window, + GString *tip, DirItem *item); +void filer_lost_selection(FilerWindow *filer_window, gint time); +void filer_lost_primary(FilerWindow *filer_window); +void filer_selection_changed(FilerWindow *filer_window, gint time); +void show_filer_menu(FilerWindow *filer_window, GdkEvent *event, int item); #endif /* _FILER_H */ diff --git a/ROX-Filer/src/global.h b/ROX-Filer/src/global.h index 850bad9f..b2ecff7b 100644 --- a/ROX-Filer/src/global.h +++ b/ROX-Filer/src/global.h @@ -45,6 +45,9 @@ typedef struct _Collection Collection; */ typedef struct _CollectionItem CollectionItem; +/* A viewport containing a Collection which also handles redraw */ +typedef struct _ViewCollection ViewCollection; + /* This contains the pixbufs for an image, in various sizes */ typedef struct _MaskedPixmap MaskedPixmap; diff --git a/ROX-Filer/src/gui_support.c b/ROX-Filer/src/gui_support.c index 8eaecd55..7fabce52 100644 --- a/ROX-Filer/src/gui_support.c +++ b/ROX-Filer/src/gui_support.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,10 @@ static GdkAtom xa_cardinal; static GtkWidget *current_dialog = NULL; +static GtkWidget *tip_widget = NULL; +static time_t tip_time = 0; /* Time tip widget last closed */ +static gint tip_timeout = 0; /* When primed */ + void gui_support_init() { xa_cardinal = gdk_atom_intern("CARDINAL", FALSE); @@ -811,3 +816,94 @@ void fixed_move_fast(GtkFixed *fixed, GtkWidget *widget, int x, int y) gtk_widget_size_allocate(child->widget, &child_allocation); } } + +/* Draw the black border */ +static gint tooltip_draw(GtkWidget *w) +{ + gdk_draw_rectangle(w->window, w->style->fg_gc[w->state], FALSE, 0, 0, + w->allocation.width - 1, w->allocation.height - 1); + + return FALSE; +} + +/* When the tips window closed, record the time. If we try to open another + * tip soon, it will appear more quickly. + */ +static void tooltip_destroyed(gpointer data) +{ + time(&tip_time); +} + +/* Display a tooltip-like widget near the pointer with 'text'. If 'text' is + * NULL, close any current tooltip. + */ +void tooltip_show(guchar *text) +{ + GtkWidget *label; + int x, y, py; + int w, h; + + if (tip_timeout) + { + gtk_timeout_remove(tip_timeout); + tip_timeout = 0; + } + + if (tip_widget) + { + gtk_widget_destroy(tip_widget); + tip_widget = NULL; + } + + if (!text) + return; + + /* Show the tip */ + tip_widget = gtk_window_new(GTK_WINDOW_POPUP); + gtk_widget_set_app_paintable(tip_widget, TRUE); + gtk_widget_set_name(tip_widget, "gtk-tooltips"); + + g_signal_connect_swapped(tip_widget, "expose_event", + G_CALLBACK(tooltip_draw), tip_widget); + + label = gtk_label_new(text); + gtk_misc_set_padding(GTK_MISC(label), 4, 2); + gtk_container_add(GTK_CONTAINER(tip_widget), label); + gtk_widget_show(label); + gtk_widget_realize(tip_widget); + + w = tip_widget->allocation.width; + h = tip_widget->allocation.height; + gdk_window_get_pointer(NULL, &x, &py, NULL); + + x -= w / 2; + y = py + 12; /* I don't know the pointer height so I use a constant */ + + /* Now check for screen boundaries */ + x = CLAMP(x, 0, screen_width - w); + y = CLAMP(y, 0, screen_height - h); + + /* And again test if pointer is over the tooltip window */ + if (py >= y && py <= y + h) + y = py - h- 2; + gtk_window_move(GTK_WINDOW(tip_widget), x, y); + gtk_widget_show(tip_widget); + + g_signal_connect_swapped(tip_widget, "destroy", + G_CALLBACK(tooltip_destroyed), NULL); + time(&tip_time); +} + +/* Call callback(user_data) after a while, unless cancelled */ +void tooltip_prime(GtkFunction callback, gpointer user_data) +{ + time_t now; + int delay; + + g_return_if_fail(tip_timeout == 0); + + time(&now); + delay = now - tip_time > 2 ? 1000 : 200; + + tip_timeout = gtk_timeout_add(delay, (GtkFunction) callback, user_data); +} diff --git a/ROX-Filer/src/gui_support.h b/ROX-Filer/src/gui_support.h index 5d988236..91a1982e 100644 --- a/ROX-Filer/src/gui_support.h +++ b/ROX-Filer/src/gui_support.h @@ -49,5 +49,7 @@ GtkWidget *button_new_mixed(const char *stock, const char *message); void entry_set_error(GtkWidget *entry, gboolean error); void window_put_just_above(GdkWindow *higher, GdkWindow *lower); void fixed_move_fast(GtkFixed *fixed, GtkWidget *widget, int x, int y); +void tooltip_show(guchar *text); +void tooltip_prime(GtkFunction callback, gpointer user_data); #endif /* _GUI_SUPPORT_H */ diff --git a/ROX-Filer/src/tasklist.c b/ROX-Filer/src/tasklist.c index f8bab0f0..15f1d175 100644 --- a/ROX-Filer/src/tasklist.c +++ b/ROX-Filer/src/tasklist.c @@ -357,6 +357,8 @@ static void add_window(Window win) PropertyChangeMask); gdk_flush(); + gdk_flush(); + if (gdk_error_trap_pop() != Success) return; diff --git a/ROX-Filer/src/view_collection.c b/ROX-Filer/src/view_collection.c new file mode 100644 index 00000000..e6e92ab7 --- /dev/null +++ b/ROX-Filer/src/view_collection.c @@ -0,0 +1,1437 @@ +/* + * $Id$ + * + * ROX-Filer, filer for the ROX desktop project + * Copyright (C) 2002, the ROX-Filer team. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* view_collection.c - a subclass of Collection, used for displaying files */ + +#include "config.h" + +#include +#include + +#include "global.h" + +#include "collection.h" +#include "view_iface.h" +#include "view_collection.h" +#include "type.h" +#include "pixmaps.h" +#include "dir.h" +#include "diritem.h" +#include "gui_support.h" +#include "support.h" +#include "dnd.h" +#include "bind.h" +#include "options.h" + +#include "display.h" /* XXX */ +#include "filer.h" /* XXX */ + +#define MIN_ITEM_WIDTH 64 + +/* Item we are about to display a tooltip for */ +static DirItem *tip_item = NULL; + +static gpointer parent_class = NULL; + +struct _ViewCollectionClass { + GtkViewportClass parent; +}; + +struct _ViewCollection { + GtkViewport viewport; + + Collection *collection; + FilerWindow *filer_window; /* Used for styles, etc */ +}; + +typedef struct _Template Template; + +struct _Template { + GdkRectangle icon; + GdkRectangle leafname; + GdkRectangle details; +}; + +/* GC for drawing colour filenames */ +static GdkGC *type_gc = NULL; + +/* Static prototypes */ +static void view_collection_finialize(GObject *object); +static void view_collection_class_init(gpointer gclass, gpointer data); +static void view_collection_init(GTypeInstance *object, gpointer gclass); + +static void draw_item(GtkWidget *widget, + CollectionItem *item, + GdkRectangle *area, + gpointer user_data); +static void fill_template(GdkRectangle *area, CollectionItem *item, + FilerWindow *filer_window, Template *template); +static void huge_template(GdkRectangle *area, CollectionItem *colitem, + FilerWindow *filer_window, Template *template); +static void large_template(GdkRectangle *area, CollectionItem *colitem, + FilerWindow *filer_window, Template *template); +static void small_template(GdkRectangle *area, CollectionItem *colitem, + FilerWindow *filer_window, Template *template); +static void huge_full_template(GdkRectangle *area, CollectionItem *colitem, + FilerWindow *filer_window, Template *template); +static void large_full_template(GdkRectangle *area, CollectionItem *colitem, + FilerWindow *filer_window, Template *template); +static void small_full_template(GdkRectangle *area, CollectionItem *colitem, + FilerWindow *filer_window, Template *template); +static gboolean test_point(Collection *collection, + int point_x, int point_y, + CollectionItem *item, + int width, int height, + gpointer user_data); +static void draw_string(GtkWidget *widget, + PangoLayout *layout, + GdkRectangle *area, /* Area available on screen */ + int width, /* Width of the full string */ + GtkStateType selection_state, + gboolean selected, + gboolean box); +static void draw_small_icon(GtkWidget *widget, + GdkRectangle *area, + DirItem *item, + MaskedPixmap *image, + gboolean selected); +static void draw_huge_icon(GtkWidget *widget, + GdkRectangle *area, + DirItem *item, + MaskedPixmap *image, + gboolean selected); +static void view_collection_iface_init(gpointer giface, gpointer iface_data); +static gboolean name_is_truncated(FilerWindow *filer_window, int i); +static gint coll_motion_notify(GtkWidget *widget, + GdkEventMotion *event, + ViewCollection *view_collection); +static gint coll_button_release(GtkWidget *widget, + GdkEventButton *event, + ViewCollection *view_collection); +static gint coll_button_press(GtkWidget *widget, + GdkEventButton *event, + ViewCollection *view_collection); +static void create_uri_list(FilerWindow *filer_window, GString *string); +static void perform_action(ViewCollection *view_collection, + GdkEventButton *event); +static gint collection_lost_primary(GtkWidget *widget, + GdkEventSelection *event, + gpointer user_data); +static void style_set(Collection *collection, + GtkStyle *style, + ViewCollection *view_collection); +static void lost_selection(Collection *collection, + guint time, + gpointer user_data); +static void selection_changed(Collection *collection, + gint time, + gpointer user_data); +static void display_free_colitem(Collection *collection, + CollectionItem *colitem); +static void calc_size(FilerWindow *filer_window, CollectionItem *colitem, + int *width, int *height); + +static void view_collection_sort(ViewIface *view); +static void view_collection_style_changed(ViewIface *view, int flags); +static gboolean view_collection_autoselect(ViewIface *view, const gchar *leaf); +static void view_collection_add_items(ViewIface *view, GPtrArray *items); +static void view_collection_update_items(ViewIface *view, GPtrArray *items); +static void view_collection_delete_if(ViewIface *view, + gboolean (*test)(gpointer item, gpointer data), + gpointer data); +static void view_collection_clear(ViewIface *view); + + +/**************************************************************** + * EXTERNAL INTERFACE * + ****************************************************************/ + +GtkWidget *view_collection_new(FilerWindow *filer_window) +{ + ViewCollection *view_collection; + + view_collection = g_object_new(view_collection_get_type(), NULL); + view_collection->filer_window = filer_window; + filer_window->collection = view_collection->collection; /* XXX */ + + return GTK_WIDGET(view_collection); +} + +GType view_collection_get_type(void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo info = + { + sizeof (ViewCollectionClass), + NULL, /* base_init */ + NULL, /* base_finalise */ + view_collection_class_init, + NULL, /* class_finalise */ + NULL, /* class_data */ + sizeof(ViewCollection), + 0, /* n_preallocs */ + view_collection_init + }; + static const GInterfaceInfo iface_info = + { + view_collection_iface_init, NULL, NULL + }; + + type = g_type_register_static(gtk_viewport_get_type(), + "ViewCollection", &info, 0); + g_type_add_interface_static(type, VIEW_TYPE_IFACE, &iface_info); + } + + return type; +} + +/**************************************************************** + * INTERNAL FUNCTIONS * + ****************************************************************/ + +static void view_collection_finialize(GObject *object) +{ + /* ViewCollection *view_collection = (ViewCollection *) object; */ + + G_OBJECT_CLASS(parent_class)->finalize(object); +} + +static void view_collection_class_init(gpointer gclass, gpointer data) +{ + GObjectClass *object = (GObjectClass *) gclass; + + parent_class = g_type_class_peek_parent(gclass); + + object->finalize = view_collection_finialize; +} + +static void view_collection_init(GTypeInstance *object, gpointer gclass) +{ + ViewCollection *view_collection = (ViewCollection *) object; + GtkViewport *viewport = (GtkViewport *) object; + GtkWidget *collection; + GtkAdjustment *adj; + + collection = collection_new(); + view_collection->collection = COLLECTION(collection); + + adj = view_collection->collection->vadj; + gtk_viewport_set_vadjustment(viewport, adj); + gtk_viewport_set_shadow_type(viewport, GTK_SHADOW_NONE); + gtk_container_add(GTK_CONTAINER(object), collection); + gtk_widget_show(collection); + gtk_widget_set_size_request(GTK_WIDGET(view_collection), 4, 4); + + gtk_container_set_resize_mode(GTK_CONTAINER(viewport), + GTK_RESIZE_IMMEDIATE); + + view_collection->collection->free_item = display_free_colitem; + view_collection->collection->draw_item = draw_item; + view_collection->collection->test_point = test_point; + view_collection->collection->cb_user_data = view_collection; + + g_signal_connect(collection, "style_set", + G_CALLBACK(style_set), + view_collection); + g_signal_connect(collection, "lose_selection", + G_CALLBACK(lost_selection), view_collection); + g_signal_connect(collection, "selection_changed", + G_CALLBACK(selection_changed), view_collection); + g_signal_connect(collection, "selection_clear_event", + G_CALLBACK(collection_lost_primary), view_collection); + g_signal_connect(collection, "button-release-event", + G_CALLBACK(coll_button_release), view_collection); + g_signal_connect(collection, "button-press-event", + G_CALLBACK(coll_button_press), view_collection); + g_signal_connect(collection, "motion-notify-event", + G_CALLBACK(coll_motion_notify), view_collection); +} + +static void draw_item(GtkWidget *widget, + CollectionItem *colitem, + GdkRectangle *area, + gpointer user_data) +{ + DirItem *item = (DirItem *) colitem->data; + gboolean selected = colitem->selected; + Template template; + ViewData *view = (ViewData *) colitem->view_data; + ViewCollection *view_collection = (ViewCollection *) user_data; + FilerWindow *filer_window = view_collection->filer_window; + + g_return_if_fail(view != NULL); + + fill_template(area, colitem, filer_window, &template); + + /* Set up GC for coloured file types */ + if (!type_gc) + type_gc = gdk_gc_new(widget->window); + + gdk_gc_set_foreground(type_gc, type_get_colour(item, + &widget->style->fg[GTK_STATE_NORMAL])); + + if (template.icon.width <= SMALL_WIDTH && + template.icon.height <= SMALL_HEIGHT) + { + draw_small_icon(widget, &template.icon, + item, view->image, selected); + } + else if (template.icon.width <= ICON_WIDTH && + template.icon.height <= ICON_HEIGHT) + { + draw_large_icon(widget, &template.icon, + item, view->image, selected); + } + else + { + draw_huge_icon(widget, &template.icon, + item, view->image, selected); + } + + draw_string(widget, view->layout, + &template.leafname, + view->name_width, + filer_window->selection_state, + selected, TRUE); + if (view->details) + draw_string(widget, view->details, + &template.details, + template.details.width, + filer_window->selection_state, + selected, TRUE); +} + +/* A template contains the locations of the three rectangles (for the icon, + * name and extra details). + * Fill in the empty 'template' with the rectanges for this item. + */ +static void fill_template(GdkRectangle *area, CollectionItem *colitem, + FilerWindow *filer_window, Template *template) +{ + DisplayStyle style = filer_window->display_style; + ViewData *view = (ViewData *) colitem->view_data; + + if (view->details) + { + template->details.width = view->details_width; + template->details.height = view->details_height; + + if (style == SMALL_ICONS) + small_full_template(area, colitem, + filer_window, template); + else if (style == LARGE_ICONS) + large_full_template(area, colitem, + filer_window, template); + else + huge_full_template(area, colitem, + filer_window, template); + } + else + { + if (style == HUGE_ICONS) + huge_template(area, colitem, filer_window, template); + else if (style == LARGE_ICONS) + large_template(area, colitem, filer_window, template); + else + small_template(area, colitem, filer_window, template); + } +} + +static void huge_template(GdkRectangle *area, CollectionItem *colitem, + FilerWindow *filer_window, Template *template) +{ + int col_width = filer_window->collection->item_width; + int text_x, text_y; + ViewData *view = (ViewData *) colitem->view_data; + MaskedPixmap *image = view->image; + + if (image) + { + if (!image->huge_pixbuf) + pixmap_make_huge(image); + template->icon.width = image->huge_width; + template->icon.height = image->huge_height; + } + else + { + template->icon.width = HUGE_WIDTH * 3 / 2; + template->icon.height = HUGE_HEIGHT; + } + + template->leafname.width = view->name_width; + template->leafname.height = view->name_height; + + text_x = area->x + ((col_width - template->leafname.width) >> 1); + text_y = area->y + area->height - template->leafname.height; + + template->leafname.x = text_x; + template->leafname.y = text_y; + + template->icon.x = area->x + ((col_width - template->icon.width) >> 1); + template->icon.y = template->leafname.y - template->icon.height - 2; +} + +static void large_template(GdkRectangle *area, CollectionItem *colitem, + FilerWindow *filer_window, Template *template) +{ + int col_width = filer_window->collection->item_width; + int iwidth, iheight; + int image_x; + int image_y; + ViewData *view = (ViewData *) colitem->view_data; + MaskedPixmap *image = view->image; + + int text_x, text_y; + + if (image) + { + iwidth = MIN(image->width, ICON_WIDTH); + iheight = MIN(image->height + 6, ICON_HEIGHT); + } + else + { + iwidth = ICON_WIDTH; + iheight = ICON_HEIGHT; + } + image_x = area->x + ((col_width - iwidth) >> 1); + + template->leafname.width = view->name_width; + template->leafname.height = view->name_height; + + text_x = area->x + ((col_width - template->leafname.width) >> 1); + text_y = area->y + ICON_HEIGHT + 2; + + template->leafname.x = text_x; + template->leafname.y = text_y; + + image_y = text_y - iheight; + image_y = MAX(area->y, image_y); + + template->icon.x = image_x; + template->icon.y = image_y; + template->icon.width = iwidth; + template->icon.height = MIN(ICON_HEIGHT, iheight); +} + +static void small_template(GdkRectangle *area, CollectionItem *colitem, + FilerWindow *filer_window, Template *template) +{ + int text_x = area->x + SMALL_WIDTH + 4; + int low_text_y; + int max_text_width = area->width - SMALL_WIDTH - 4; + ViewData *view = (ViewData *) colitem->view_data; + + low_text_y = area->y + area->height / 2 - view->name_height / 2; + + template->leafname.x = text_x; + template->leafname.y = low_text_y; + template->leafname.width = MIN(max_text_width, view->name_width); + template->leafname.height = view->name_height; + + template->icon.x = area->x; + template->icon.y = area->y + 1; + template->icon.width = SMALL_WIDTH; + template->icon.height = SMALL_HEIGHT; +} + +static void huge_full_template(GdkRectangle *area, CollectionItem *colitem, + FilerWindow *filer_window, Template *template) +{ + int max_text_width = area->width - HUGE_WIDTH - 4; + ViewData *view = (ViewData *) colitem->view_data; + MaskedPixmap *image = view->image; + + if (image) + { + if (!image->huge_pixbuf) + pixmap_make_huge(image); + template->icon.width = image->huge_width; + template->icon.height = image->huge_height; + } + else + { + template->icon.width = HUGE_WIDTH * 3 / 2; + template->icon.height = HUGE_HEIGHT; + } + + template->icon.x = area->x + (HUGE_WIDTH - template->icon.width) / 2; + template->icon.y = area->y + (area->height - template->icon.height) / 2; + + template->leafname.x = area->x + HUGE_WIDTH + 4; + template->leafname.y = area->y + area->height / 2 + - (view->name_height + 2 + view->details_height) / 2; + template->leafname.width = MIN(max_text_width, view->name_width); + template->leafname.height = view->name_height; + + if (!image) + return; /* Not scanned yet */ + + template->details.x = template->leafname.x; + template->details.y = template->leafname.y + view->name_height + 2; +} + +static void large_full_template(GdkRectangle *area, CollectionItem *colitem, + FilerWindow *filer_window, Template *template) +{ + int max_text_width = area->width - ICON_WIDTH - 4; + ViewData *view = (ViewData *) colitem->view_data; + MaskedPixmap *image = view->image; + + if (image) + { + template->icon.width = image->width; + template->icon.height = image->height; + } + else + { + template->icon.width = ICON_WIDTH; + template->icon.height = ICON_HEIGHT; + } + + template->icon.x = area->x + (ICON_WIDTH - template->icon.width) / 2; + template->icon.y = area->y + (area->height - template->icon.height) / 2; + + + template->leafname.x = area->x + ICON_WIDTH + 4; + template->leafname.y = area->y + area->height / 2 + - (view->name_height + 2 + view->details_height) / 2; + template->leafname.width = MIN(max_text_width, view->name_width); + template->leafname.height = view->name_height; + + if (!image) + return; /* Not scanned yet */ + + template->details.x = template->leafname.x; + template->details.y = template->leafname.y + view->name_height + 2; +} + +static void small_full_template(GdkRectangle *area, CollectionItem *colitem, + FilerWindow *filer_window, Template *template) +{ + int col_width = filer_window->collection->item_width; + ViewData *view = (ViewData *) colitem->view_data; + + small_template(area, colitem, filer_window, template); + + if (!view->image) + return; /* Not scanned yet */ + + template->details.x = area->x + col_width - template->details.width; + template->details.y = area->y + area->height / 2 - \ + view->details_height / 2; +} + +#define INSIDE(px, py, area) \ + (px >= area.x && py >= area.y && \ + px <= area.x + area.width && py <= area.y + area.height) + +static gboolean test_point(Collection *collection, + int point_x, int point_y, + CollectionItem *colitem, + int width, int height, + gpointer user_data) +{ + Template template; + GdkRectangle area; + ViewData *view = (ViewData *) colitem->view_data; + ViewCollection *view_collection = (ViewCollection *) user_data; + FilerWindow *filer_window = view_collection->filer_window; + + area.x = 0; + area.y = 0; + area.width = width; + area.height = height; + + fill_template(&area, colitem, filer_window, &template); + + return INSIDE(point_x, point_y, template.leafname) || + INSIDE(point_x, point_y, template.icon) || + (view->details && INSIDE(point_x, point_y, template.details)); +} + +/* 'box' renders a background box if the string is also selected */ +static void draw_string(GtkWidget *widget, + PangoLayout *layout, + GdkRectangle *area, /* Area available on screen */ + int width, /* Width of the full string */ + GtkStateType selection_state, + gboolean selected, + gboolean box) +{ + GdkGC *gc = selected + ? widget->style->fg_gc[selection_state] + : type_gc; + + if (selected && box) + gtk_paint_flat_box(widget->style, widget->window, + selection_state, GTK_SHADOW_NONE, + NULL, widget, "text", + area->x, area->y, + MIN(width, area->width), + area->height); + + if (width > area->width) + { + gdk_gc_set_clip_origin(gc, 0, 0); + gdk_gc_set_clip_rectangle(gc, area); + } + + gdk_draw_layout(widget->window, gc, area->x, area->y, layout); + + if (width > area->width) + { + static GdkGC *red_gc = NULL; + + if (!red_gc) + { + gboolean success; + GdkColor red = {0, 0xffff, 0, 0}; + + red_gc = gdk_gc_new(widget->window); + gdk_colormap_alloc_colors( + gtk_widget_get_colormap(widget), + &red, 1, FALSE, TRUE, &success); + gdk_gc_set_foreground(red_gc, &red); + } + gdk_draw_rectangle(widget->window, red_gc, TRUE, + area->x + area->width - 1, area->y, + 1, area->height); + gdk_gc_set_clip_rectangle(gc, NULL); + } +} + +static void draw_small_icon(GtkWidget *widget, + GdkRectangle *area, + DirItem *item, + MaskedPixmap *image, + gboolean selected) +{ + int width, height, image_x, image_y; + + if (!image) + return; + + if (!image->sm_pixbuf) + pixmap_make_small(image); + + width = MIN(image->sm_width, SMALL_WIDTH); + height = MIN(image->sm_height, SMALL_HEIGHT); + image_x = area->x + ((area->width - width) >> 1); + image_y = MAX(0, SMALL_HEIGHT - image->sm_height); + + gdk_pixbuf_render_to_drawable_alpha( + selected ? image->sm_pixbuf_lit : image->sm_pixbuf, + widget->window, + 0, 0, /* src */ + image_x, area->y + image_y, /* dest */ + width, height, + GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */ + GDK_RGB_DITHER_NORMAL, 0, 0); + + if (item->flags & ITEM_FLAG_SYMLINK) + { + gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf, + widget->window, + 0, 0, /* src */ + image_x, area->y + 8, /* dest */ + -1, -1, + GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */ + GDK_RGB_DITHER_NORMAL, 0, 0); + } + else if (item->flags & ITEM_FLAG_MOUNT_POINT) + { + MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED + ? im_mounted + : im_unmounted; + + gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf, + widget->window, + 0, 0, /* src */ + image_x + 2, area->y + 2, /* dest */ + -1, -1, + GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */ + GDK_RGB_DITHER_NORMAL, 0, 0); + } +} + +/* Draw this icon (including any symlink or mount symbol) inside the + * given rectangle. + */ +static void draw_huge_icon(GtkWidget *widget, + GdkRectangle *area, + DirItem *item, + MaskedPixmap *image, + gboolean selected) +{ + int width, height; + int image_x; + int image_y; + + if (!image) + return; + + width = image->huge_width; + height = image->huge_height; + image_x = area->x + ((area->width - width) >> 1); + image_y = MAX(0, area->height - height - 6); + + gdk_pixbuf_render_to_drawable_alpha( + selected ? image->huge_pixbuf_lit + : image->huge_pixbuf, + widget->window, + 0, 0, /* src */ + image_x, area->y + image_y, /* dest */ + width, height, + GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */ + GDK_RGB_DITHER_NORMAL, 0, 0); + + if (item->flags & ITEM_FLAG_SYMLINK) + { + gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf, + widget->window, + 0, 0, /* src */ + image_x, area->y + 2, /* dest */ + -1, -1, + GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */ + GDK_RGB_DITHER_NORMAL, 0, 0); + } + else if (item->flags & ITEM_FLAG_MOUNT_POINT) + { + MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED + ? im_mounted + : im_unmounted; + + gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf, + widget->window, + 0, 0, /* src */ + image_x, area->y + 2, /* dest */ + -1, -1, + GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */ + GDK_RGB_DITHER_NORMAL, 0, 0); + } +} + +/* Create the handers for the View interface */ +static void view_collection_iface_init(gpointer giface, gpointer iface_data) +{ + ViewIfaceClass *iface = giface; + + g_assert(G_TYPE_FROM_INTERFACE(iface) == VIEW_TYPE_IFACE); + + /* override stuff */ + iface->sort = view_collection_sort; + iface->style_changed = view_collection_style_changed; + iface->autoselect = view_collection_autoselect; + iface->add_items = view_collection_add_items; + iface->update_items = view_collection_update_items; + iface->delete_if = view_collection_delete_if; + iface->clear = view_collection_clear; +} + +/* It's time to make the tooltip appear. If we're not over the item any + * more, or the item doesn't need a tooltip, do nothing. + */ +static gboolean tooltip_activate(FilerWindow *filer_window) +{ + Collection *collection; + gint x, y; + int i; + GString *tip = NULL; + + g_return_val_if_fail(tip_item != NULL, 0); + + tooltip_show(NULL); + + g_return_val_if_fail(filer_exists(filer_window), FALSE); + + collection = filer_window->collection; + gdk_window_get_pointer(GTK_WIDGET(collection)->window, &x, &y, NULL); + i = collection_get_item(collection, x, y); + if (i == -1 || ((DirItem *) collection->items[i].data) != tip_item) + return FALSE; /* Not still under the pointer */ + + /* OK, the filer window still exists and the pointer is still + * over the same item. Do we need to show a tip? + */ + + tip = g_string_new(NULL); + + if (name_is_truncated(filer_window, i)) + { + g_string_append(tip, tip_item->leafname); + g_string_append_c(tip, '\n'); + } + + filer_add_tip_details(filer_window, tip, tip_item); + + if (tip->len > 1) + { + g_string_truncate(tip, tip->len - 1); + + tooltip_show(tip->str); + } + + g_string_free(tip, TRUE); + + return FALSE; +} + +static gboolean name_is_truncated(FilerWindow *filer_window, int i) +{ + Template template; + Collection *collection = filer_window->collection; + CollectionItem *colitem = &collection->items[i]; + int col = i % collection->columns; + int row = i / collection->columns; + GdkRectangle area; + ViewData *view = (ViewData *) colitem->view_data; + + /* TODO: What if the window is narrower than 1 column? */ + if (filer_window->display_style == LARGE_ICONS || + filer_window->display_style == HUGE_ICONS) + return FALSE; /* These wrap rather than truncate */ + + area.x = col * collection->item_width; + area.y = row * collection->item_height; + area.height = collection->item_height; + + if (col == collection->columns - 1) + area.width = GTK_WIDGET(collection)->allocation.width - area.x; + else + area.width = collection->item_width; + + fill_template(&area, colitem, filer_window, &template); + + return template.leafname.width < view->name_width; +} + +static gint coll_motion_notify(GtkWidget *widget, + GdkEventMotion *event, + ViewCollection *view_collection) +{ + Collection *collection = view_collection->collection; + FilerWindow *filer_window; + int i; + + filer_window = g_object_get_data(G_OBJECT(collection), "filer_window"); + g_return_val_if_fail(filer_window != NULL, FALSE); + + i = collection_get_item(collection, event->x, event->y); + + if (i == -1) + tooltip_show(NULL); + else + { + DirItem *item = (DirItem *) collection->items[i].data; + + if (item != tip_item) + { + tooltip_show(NULL); + + tip_item = item; + if (item) + tooltip_prime((GtkFunction) tooltip_activate, + filer_window); + } + } + + if (motion_state != MOTION_READY_FOR_DND) + return FALSE; + + if (!dnd_motion_moved(event)) + return FALSE; + + i = collection_get_item(collection, + event->x - (event->x_root - drag_start_x), + event->y - (event->y_root - drag_start_y)); + if (i == -1) + return FALSE; + + collection_wink_item(collection, -1); + + if (!collection->items[i].selected) + { + if (event->state & GDK_BUTTON1_MASK) + { + /* Select just this one */ + filer_window->temp_item_selected = TRUE; + collection_clear_except(collection, i); + } + else + { + if (collection->number_selected == 0) + filer_window->temp_item_selected = TRUE; + collection_select_item(collection, i); + } + } + + g_return_val_if_fail(collection->number_selected > 0, TRUE); + + if (collection->number_selected == 1) + { + DirItem *item = (DirItem *) collection->items[i].data; + ViewData *view = (ViewData *) collection->items[i].view_data; + + if (!item->image) + item = dir_update_item(filer_window->directory, + item->leafname); + + if (!item) + { + report_error(_("Item no longer exists!")); + return FALSE; + } + + drag_one_item(widget, event, + make_path(filer_window->sym_path, item->leafname)->str, + item, view ? view->image : NULL); + } + else + { + GString *uris; + + uris = g_string_new(NULL); + create_uri_list(filer_window, uris); + drag_selection(widget, event, uris->str); + g_string_free(uris, TRUE); + } + + return FALSE; +} + +/* Append all the URIs in the selection to the string */ +static void create_uri_list(FilerWindow *filer_window, GString *string) +{ + Collection *collection = filer_window->collection; + GString *leader; + int i, num_selected; + + leader = g_string_new("file://"); + g_string_append(leader, our_host_name_for_dnd()); + g_string_append(leader, filer_window->sym_path); + if (leader->str[leader->len - 1] != '/') + g_string_append_c(leader, '/'); + + num_selected = collection->number_selected; + + for (i = 0; num_selected > 0; i++) + { + if (collection->items[i].selected) + { + DirItem *item = (DirItem *) collection->items[i].data; + + g_string_append(string, leader->str); + g_string_append(string, item->leafname); + g_string_append(string, "\r\n"); + num_selected--; + } + } + + g_string_free(leader, TRUE); +} + +static gint coll_button_release(GtkWidget *widget, + GdkEventButton *event, + ViewCollection *view_collection) +{ + if (dnd_motion_release(event)) + { + if (motion_buttons_pressed == 0 && + view_collection->collection->lasso_box) + { + collection_end_lasso(view_collection->collection, + event->button == 1 ? GDK_SET : GDK_INVERT); + } + return FALSE; + } + + perform_action(view_collection, event); + + return FALSE; +} + +static gint coll_button_press(GtkWidget *widget, + GdkEventButton *event, + ViewCollection *view_collection) +{ + collection_set_cursor_item(view_collection->collection, -1); + + if (dnd_motion_press(widget, event)) + perform_action(view_collection, event); + + return FALSE; +} + +static void perform_action(ViewCollection *view_collection, + GdkEventButton *event) +{ + Collection *collection = view_collection->collection; + DirItem *dir_item; + int item; + gboolean press = event->type == GDK_BUTTON_PRESS; + gboolean selected = FALSE; + OpenFlags flags = 0; + BindAction action; + FilerWindow *filer_window = view_collection->filer_window; + + if (event->button > 3) + return; + + item = collection_get_item(collection, event->x, event->y); + + if (item != -1 && event->button == 1 && + collection->items[item].selected && + filer_window->selection_state == GTK_STATE_INSENSITIVE) + { + filer_selection_changed(filer_window, event->time); + return; + } + + if (filer_window->target_cb) + { + dnd_motion_ungrab(); + if (item != -1 && press && event->button == 1) + filer_window->target_cb(filer_window, item, + filer_window->target_data); + filer_target_mode(filer_window, NULL, NULL, NULL); + + return; + } + + action = bind_lookup_bev( + item == -1 ? BIND_DIRECTORY : BIND_DIRECTORY_ICON, + event); + + if (item != -1) + { + dir_item = (DirItem *) collection->items[item].data; + selected = collection->items[item].selected; + } + else + dir_item = NULL; + + switch (action) + { + case ACT_CLEAR_SELECTION: + collection_clear_selection(collection); + break; + case ACT_TOGGLE_SELECTED: + collection_toggle_item(collection, item); + break; + case ACT_SELECT_EXCL: + collection_clear_except(collection, item); + break; + case ACT_EDIT_ITEM: + flags |= OPEN_SHIFT; + /* (no break) */ + case ACT_OPEN_ITEM: + if (event->button != 1) + flags |= OPEN_CLOSE_WINDOW; + else + flags |= OPEN_SAME_WINDOW; + if (o_new_button_1.int_value) + flags ^= OPEN_SAME_WINDOW; + if (event->type == GDK_2BUTTON_PRESS) + collection_unselect_item(collection, item); + dnd_motion_ungrab(); + filer_openitem(filer_window, item, flags); + break; + case ACT_POPUP_MENU: + dnd_motion_ungrab(); + tooltip_show(NULL); + show_filer_menu(filer_window, (GdkEvent *) event, item); + break; + case ACT_PRIME_AND_SELECT: + if (!selected) + collection_clear_except(collection, item); + dnd_motion_start(MOTION_READY_FOR_DND); + break; + case ACT_PRIME_AND_TOGGLE: + collection_toggle_item(collection, item); + dnd_motion_start(MOTION_READY_FOR_DND); + break; + case ACT_PRIME_FOR_DND: + dnd_motion_start(MOTION_READY_FOR_DND); + break; + case ACT_IGNORE: + if (press && event->button < 4) + { + if (item) + collection_wink_item(collection, item); + dnd_motion_start(MOTION_NONE); + } + break; + case ACT_LASSO_CLEAR: + collection_clear_selection(collection); + /* (no break) */ + case ACT_LASSO_MODIFY: + collection_lasso_box(collection, event->x, event->y); + break; + case ACT_RESIZE: + filer_window_autosize(filer_window, TRUE); + break; + default: + g_warning("Unsupported action : %d\n", action); + break; + } +} + +/* Another app took the selection - shade everything */ +static gint collection_lost_primary(GtkWidget *widget, + GdkEventSelection *event, + gpointer user_data) +{ + ViewCollection *view_collection = VIEW_COLLECTION(user_data); + + filer_lost_primary(view_collection->filer_window); + + return FALSE; +} + +/* Nothing is selected anymore - give up primary */ +static void lost_selection(Collection *collection, + guint time, + gpointer user_data) +{ + ViewCollection *view_collection = VIEW_COLLECTION(user_data); + + filer_lost_selection(view_collection->filer_window, time); +} + +static void selection_changed(Collection *collection, + gint time, + gpointer user_data) +{ + ViewCollection *view_collection = VIEW_COLLECTION(user_data); + + filer_selection_changed(view_collection->filer_window, time); +} + +static void display_free_colitem(Collection *collection, + CollectionItem *colitem) +{ + ViewData *view = (ViewData *) colitem->view_data; + + if (!view) + return; + + if (view->layout) + { + g_object_unref(G_OBJECT(view->layout)); + view->layout = NULL; + } + if (view->details) + g_object_unref(G_OBJECT(view->details)); + + if (view->image) + g_object_unref(view->image); + + g_free(view); +} + +static void add_item(ViewCollection *view_collection, DirItem *item) +{ + Collection *collection = view_collection->collection; + FilerWindow *filer_window = view_collection->filer_window; + int old_w = collection->item_width; + int old_h = collection->item_height; + int w, h, i; + + i = collection_insert(collection, item, + display_create_viewdata(filer_window, item)); + + calc_size(filer_window, &collection->items[i], &w, &h); + + if (w > old_w || h > old_h) + collection_set_item_size(collection, + MAX(old_w, w), + MAX(old_h, h)); +} + +static void style_set(Collection *collection, + GtkStyle *style, + ViewCollection *view_collection) +{ + view_collection_style_changed(VIEW(view_collection), + VIEW_UPDATE_VIEWDATA | VIEW_UPDATE_NAME); +} + +/* Return the size needed for this item */ +static void calc_size(FilerWindow *filer_window, CollectionItem *colitem, + int *width, int *height) +{ + int pix_width, pix_height; + int w; + DisplayStyle style = filer_window->display_style; + ViewData *view = (ViewData *) colitem->view_data; + + if (filer_window->details_type == DETAILS_NONE) + { + if (style == HUGE_ICONS) + { + if (view->image) + { + if (!view->image->huge_pixbuf) + pixmap_make_huge(view->image); + pix_width = view->image->huge_width; + pix_height = view->image->huge_height; + } + else + { + pix_width = HUGE_WIDTH * 3 / 2; + pix_height = HUGE_HEIGHT * 3 / 2; + } + *width = MAX(pix_width, view->name_width) + 4; + *height = view->name_height + pix_height + 4; + } + else if (style == SMALL_ICONS) + { + w = MIN(view->name_width, o_small_width.int_value); + *width = SMALL_WIDTH + 12 + w; + *height = MAX(view->name_height, SMALL_HEIGHT) + 4; + } + else + { + if (view->image) + pix_width = view->image->width; + else + pix_width = ICON_WIDTH; + *width = MAX(pix_width, view->name_width) + 4; + *height = view->name_height + ICON_HEIGHT + 2; + } + } + else + { + w = view->details_width; + if (style == HUGE_ICONS) + { + *width = HUGE_WIDTH + 12 + MAX(w, view->name_width); + *height = HUGE_HEIGHT - 4; + } + else if (style == SMALL_ICONS) + { + int text_height; + + *width = SMALL_WIDTH + view->name_width + 12 + w; + text_height = MAX(view->name_height, + view->details_height); + *height = MAX(text_height, SMALL_HEIGHT) + 4; + } + else + { + *width = ICON_WIDTH + 12 + MAX(w, view->name_width); + *height = ICON_HEIGHT; + } + } +} + +static void update_item(FilerWindow *filer_window, int i) +{ + Collection *collection = filer_window->collection; + int old_w = collection->item_width; + int old_h = collection->item_height; + int w, h; + CollectionItem *colitem; + + g_return_if_fail(i >= 0 && i < collection->number_of_items); + + colitem = &collection->items[i]; + + display_update_view(filer_window, + (DirItem *) colitem->data, + (ViewData *) colitem->view_data, + FALSE); + + calc_size(filer_window, colitem, &w, &h); + if (w > old_w || h > old_h) + collection_set_item_size(collection, + MAX(old_w, w), + MAX(old_h, h)); + + collection_draw_item(collection, i, TRUE); +} + +/* Implementations of the View interface. See view_iface.c for comments. */ + +static void view_collection_style_changed(ViewIface *view, int flags) +{ + ViewCollection *view_collection = VIEW_COLLECTION(view); + FilerWindow *filer_window = view_collection->filer_window; + int i; + Collection *col = view_collection->collection; + int width = MIN_ITEM_WIDTH; + int height = SMALL_HEIGHT; + + /* Recalculate all the ViewData structs for this window + * (needed if the text or image has changed in any way) and + * get the size of each item. + */ + for (i = 0; i < col->number_of_items; i++) + { + CollectionItem *ci = &col->items[i]; + int w, h; + + if (flags & (VIEW_UPDATE_VIEWDATA | VIEW_UPDATE_NAME)) + display_update_view(filer_window, + (DirItem *) ci->data, + (ViewData *) ci->view_data, + (flags & VIEW_UPDATE_NAME) != 0); + + calc_size(filer_window, ci, &w, &h); + if (w > width) + width = w; + if (h > height) + height = h; + } + + collection_set_item_size(col, width, height); + + gtk_widget_queue_draw(GTK_WIDGET(view_collection)); +} + +static void view_collection_sort(ViewIface *view) +{ + ViewCollection *view_collection = VIEW_COLLECTION(view); + + collection_qsort(view_collection->collection, + view_collection->filer_window->sort_fn); +} + +static gboolean view_collection_autoselect(ViewIface *view, const gchar *leaf) +{ + ViewCollection *view_collection = VIEW_COLLECTION(view); + Collection *col = view_collection->collection; + int i; + + for (i = 0; i < col->number_of_items; i++) + { + DirItem *item = (DirItem *) col->items[i].data; + + if (strcmp(item->leafname, leaf) == 0) + { + if (col->cursor_item != -1) + collection_set_cursor_item(col, i); + else + collection_wink_item(col, i); + return TRUE; + } + } + + return FALSE; +} + +static void view_collection_add_items(ViewIface *view, GPtrArray *items) +{ + ViewCollection *view_collection = VIEW_COLLECTION(view); + Collection *collection = view_collection->collection; + FilerWindow *filer_window = view_collection->filer_window; + int old_num, i; + + old_num = collection->number_of_items; + for (i = 0; i < items->len; i++) + { + DirItem *item = (DirItem *) items->pdata[i]; + char *leafname = item->leafname; + + if (leafname[0] == '.') + { + if (!filer_window->show_hidden) + continue; + + if (leafname[1] == '\0') + continue; /* Never show '.' */ + + if (leafname[1] == '.' && + leafname[2] == '\0') + continue; /* Never show '..' */ + } + + add_item(view_collection, item); + } + + if (old_num != collection->number_of_items) + view_collection_sort(view); +} + +static void view_collection_update_items(ViewIface *view, GPtrArray *items) +{ + ViewCollection *view_collection = VIEW_COLLECTION(view); + Collection *collection = view_collection->collection; + FilerWindow *filer_window = view_collection->filer_window; + int i; + int *is; + + g_return_if_fail(items->len > 0); + + collection_qsort(collection, filer_window->sort_fn); + + /* Find all the items first, then update since updating may + * change the sort order... + */ + + is = g_malloc(sizeof(int) * items->len); + for (i = 0; i < items->len; i++) + { + DirItem *item = (DirItem *) items->pdata[i]; + const gchar *leafname = item->leafname; + + if (leafname[0] == '.' && filer_window->show_hidden == FALSE) + is[i] = -1; + else + { + is[i] = collection_find_item(collection, item, + filer_window->sort_fn); + + if (is[i] < 0) + g_warning("Failed to find '%s'\n", leafname); + } + } + + for (i = 0; i < items->len; i++) + if (is[i] >= 0) + update_item(filer_window, is[i]); + + g_free(is); + + if (filer_window->sort_fn != sort_by_name) + collection_qsort(collection, filer_window->sort_fn); +} + +static void view_collection_delete_if(ViewIface *view, + gboolean (*test)(gpointer item, gpointer data), + gpointer data) +{ + ViewCollection *view_collection = VIEW_COLLECTION(view); + Collection *collection = view_collection->collection; + + collection_delete_if(collection, test, data); +} + +static void view_collection_clear(ViewIface *view) +{ + ViewCollection *view_collection = VIEW_COLLECTION(view); + Collection *collection = view_collection->collection; + + collection_clear(collection); +} diff --git a/ROX-Filer/src/view_collection.h b/ROX-Filer/src/view_collection.h new file mode 100644 index 00000000..6be94c12 --- /dev/null +++ b/ROX-Filer/src/view_collection.h @@ -0,0 +1,23 @@ +/* + * $Id$ + * + * + * ROX-Filer, filer for the ROX desktop project + * By Thomas Leonard, . + */ + +#ifndef __VIEW_COLLECTION_H__ +#define __VIEW_COLLECTION_H__ + +#include + +typedef struct _ViewCollectionClass ViewCollectionClass; + +#define VIEW_COLLECTION(obj) \ + (GTK_CHECK_CAST((obj), view_collection_get_type(), ViewCollection)) + +GtkWidget *view_collection_new(FilerWindow *filer_window); +GType view_collection_get_type(void); +GtkWidget *view_collection_get_collection(ViewCollection *view); + +#endif /* __VIEW_COLLECTION_H__ */ diff --git a/ROX-Filer/src/view_iface.c b/ROX-Filer/src/view_iface.c new file mode 100644 index 00000000..25728994 --- /dev/null +++ b/ROX-Filer/src/view_iface.c @@ -0,0 +1,136 @@ +/* + * $Id$ + * + * ROX-Filer, filer for the ROX desktop project + * Copyright (C) 2002, the ROX-Filer team. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* view_iface.c - operations supported by all views */ + +#include "config.h" + +#include "global.h" + +#include "view_iface.h" + +/* A word about interfaces: + * + * gobject's documentation's explanation of interfaces leaves something[1] to + * be desired, so I'd better explain here... + * + * [1] Like, eg, an explanation. + * + * A ViewIfaceClass is a struct which contains a number of function + * pointers. Each class that implements the View interface creates its + * own ViewIfaceClass with pointers to its implementation. This is stored + * with the class. + * + * When you want to call a method (eg, sort()) on a View, you call + * view_sort(object) here, which gets the class of object and then looks + * for that class's implementation of the View interface, and then calls + * the actual function through that. + */ + +/**************************************************************** + * EXTERNAL INTERFACE * + ****************************************************************/ + +GType view_iface_get_type(void) +{ + static GType iface_type = 0; + + if (!iface_type) + { + static const GTypeInfo iface_info = + { + sizeof (ViewIfaceClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + }; + + iface_type = g_type_register_static(G_TYPE_INTERFACE, + "ViewIface", &iface_info, 0); + + /* Actually, all Views should be GTK_TYPE_WIDGETs, to be more + * accurate, but including gtk.h takes so long, and noone's + * going to get this wrong ;-) + */ + g_type_interface_add_prerequisite(iface_type, G_TYPE_OBJECT); + } + + return iface_type; +} + +/* The sort function has changed -- resort */ +void view_sort(ViewIface *obj) +{ + g_return_if_fail(VIEW_IS_IFACE(obj)); + VIEW_IFACE_GET_CLASS(obj)->sort(obj); +} + +/* The style has changed -- shrink the grid and redraw. + * Also update ViewData (and name layout too) if appropriate + * flags are set. + */ +void view_style_changed(ViewIface *obj, int flags) +{ + g_return_if_fail(VIEW_IS_IFACE(obj)); + VIEW_IFACE_GET_CLASS(obj)->style_changed(obj, flags); +} + +/* Wink or move the cursor to this item, if present. Return TRUE on + * success (iff leaf was present). + */ +gboolean view_autoselect(ViewIface *obj, const gchar *leaf) +{ + g_return_val_if_fail(VIEW_IS_IFACE(obj), FALSE); + g_return_val_if_fail(leaf != NULL, FALSE); + + return VIEW_IFACE_GET_CLASS(obj)->autoselect(obj, leaf); +} + +/* Scanning has turned up some new items... */ +void view_add_items(ViewIface *obj, GPtrArray *items) +{ + VIEW_IFACE_GET_CLASS(obj)->add_items(obj, items); +} + +/* These items are already known, but have changed... */ +void view_update_items(ViewIface *obj, GPtrArray *items) +{ + VIEW_IFACE_GET_CLASS(obj)->update_items(obj, items); +} + +/* Call test(item) for each item in the view and delete all those for + * which it returns TRUE. + */ +void view_delete_if(ViewIface *obj, + gboolean (*test)(gpointer item, gpointer data), + gpointer data) +{ + g_return_if_fail(VIEW_IS_IFACE(obj)); + + VIEW_IFACE_GET_CLASS(obj)->delete_if(obj, test, data); +} + +/* Remove all items from the view (used when changing directory) */ +void view_clear(ViewIface *obj) +{ + g_return_if_fail(VIEW_IS_IFACE(obj)); + + VIEW_IFACE_GET_CLASS(obj)->clear(obj); +} diff --git a/ROX-Filer/src/view_iface.h b/ROX-Filer/src/view_iface.h new file mode 100644 index 00000000..c6243369 --- /dev/null +++ b/ROX-Filer/src/view_iface.h @@ -0,0 +1,59 @@ +/* + * $Id$ + * + * + * ROX-Filer, filer for the ROX desktop project + * By Thomas Leonard, . + */ + +#ifndef __VIEW_IFACE_H__ +#define __VIEW_IFACE_H__ + +#include + +typedef struct _ViewIface ViewIface; +typedef struct _ViewIfaceClass ViewIfaceClass; +struct _ViewIfaceClass +{ + GTypeInterface base_iface; + + void (*sort)(ViewIface *obj); + void (*style_changed)(ViewIface *obj, int flags); + gboolean (*autoselect)(ViewIface *obj, const gchar *leaf); + void (*add_items)(ViewIface *obj, GPtrArray *items); + void (*update_items)(ViewIface *obj, GPtrArray *items); + void (*delete_if)(ViewIface *obj, + gboolean (*test)(gpointer item, gpointer data), + gpointer data); + void (*clear)(ViewIface *obj); +}; + +#define VIEW_TYPE_IFACE (view_iface_get_type()) + +#define VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + VIEW_TYPE_IFACE, ViewIface)) + +#define VIEW_IS_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + VIEW_TYPE_IFACE)) + +#define VIEW_IFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), \ + VIEW_TYPE_IFACE, ViewIfaceClass)) + +/* Flags for view_style_changed() */ +enum { + VIEW_UPDATE_VIEWDATA = 1 << 0, + VIEW_UPDATE_NAME = 1 << 1, +}; + +GType view_iface_get_type(void); +void view_sort(ViewIface *obj); +void view_style_changed(ViewIface *obj, int flags); +gboolean view_autoselect(ViewIface *obj, const gchar *leaf); +void view_add_items(ViewIface *obj, GPtrArray *items); +void view_update_items(ViewIface *obj, GPtrArray *items); +void view_delete_if(ViewIface *obj, + gboolean (*test)(gpointer item, gpointer data), + gpointer data); +void view_clear(ViewIface *obj); + +#endif /* __VIEW_IFACE_H__ */ -- 2.11.4.GIT