diff options
Diffstat (limited to 'fiv-browser.c')
| -rw-r--r-- | fiv-browser.c | 156 | 
1 files changed, 123 insertions, 33 deletions
diff --git a/fiv-browser.c b/fiv-browser.c index 01ff8f0..8c79ca6 100644 --- a/fiv-browser.c +++ b/fiv-browser.c @@ -32,6 +32,7 @@  #include "fiv-collection.h"  #include "fiv-context-menu.h"  #include "fiv-io.h" +#include "fiv-io-model.h"  #include "fiv-thumbnail.h"  // --- Widget ------------------------------------------------------------------ @@ -78,7 +79,7 @@ struct _FivBrowser {  	gboolean show_labels;               ///< Show labels underneath items  	FivIoModel *model;                  ///< Filesystem model -	GArray *entries;                    ///< []Entry +	GPtrArray *entries;                 ///< []*Entry  	GArray *layouted_rows;              ///< []Row  	const Entry *selected;              ///< Selected entry or NULL @@ -112,14 +113,25 @@ struct entry {  	FivIoModelEntry *e;                 ///< Reference to model entry  	cairo_surface_t *thumbnail;         ///< Prescaled thumbnail  	GIcon *icon;                        ///< If no thumbnail, use this icon + +	gboolean removed;                   ///< Model announced removal  }; +static Entry * +entry_new(FivIoModelEntry *e) +{ +	Entry *self = g_slice_alloc0(sizeof *self); +	self->e = e; +	return self; +} +  static void -entry_free(Entry *self) +entry_destroy(Entry *self)  {  	fiv_io_model_entry_unref(self->e);  	g_clear_pointer(&self->thumbnail, cairo_surface_destroy);  	g_clear_object(&self->icon); +	g_slice_free1(sizeof *self, self);  }  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -202,7 +214,7 @@ relayout(FivBrowser *self, int width)  	GArray *items = g_array_new(TRUE, TRUE, sizeof(Item));  	int x = 0, y = padding.top;  	for (guint i = 0; i < self->entries->len; i++) { -		const Entry *entry = &g_array_index(self->entries, Entry, i); +		const Entry *entry = self->entries->pdata[i];  		if (!entry->thumbnail)  			continue; @@ -414,6 +426,15 @@ draw_row(FivBrowser *self, cairo_t *cr, const Row *row)  			// the whole rectangle with the selection color.  		} +		// TODO(p): Come up with a better rendition. +		if (item->entry->removed) { +			cairo_move_to(cr, 0, border.top + extents.height + border.bottom); +			cairo_line_to(cr, border.left + extents.width + border.right, 0); +			cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); +			cairo_set_line_width(cr, 5); +			cairo_stroke(cr); +		} +  		if (self->show_labels) {  			gtk_style_context_save(style);  			gtk_style_context_add_class(style, "label"); @@ -522,14 +543,9 @@ entry_set_surface_user_data(const Entry *self)  		NULL);  } -static void -entry_add_thumbnail(gpointer data, gpointer user_data) +static cairo_surface_t * +entry_lookup_thumbnail(Entry *self, FivBrowser *browser)  { -	Entry *self = data; -	g_clear_object(&self->icon); -	g_clear_pointer(&self->thumbnail, cairo_surface_destroy); - -	FivBrowser *browser = FIV_BROWSER(user_data);  	cairo_surface_t *cached =  		g_hash_table_lookup(browser->thumbnail_cache, self->e->uri);  	if (cached && @@ -537,18 +553,39 @@ entry_add_thumbnail(gpointer data, gpointer user_data)  			&fiv_browser_key_mtime_msec) == (intptr_t) self->e->mtime_msec &&  		(uintptr_t) cairo_surface_get_user_data(cached,  			&fiv_browser_key_filesize) == (uintptr_t) self->e->filesize) { -		self->thumbnail = cairo_surface_reference(cached);  		// TODO(p): If this hit is low-quality, see if a high-quality thumbnail  		// hasn't been produced without our knowledge (avoid launching a minion  		// unnecessarily; we might also shift the concern there). -	} else { -		cairo_surface_t *found = fiv_thumbnail_lookup( -			entry_system_wide_uri(self), self->e->mtime_msec, self->e->filesize, -			browser->item_size); -		self->thumbnail = rescale_thumbnail(found, browser->item_height); +		return cairo_surface_reference(cached); +	} + +	cairo_surface_t *found = fiv_thumbnail_lookup( +		entry_system_wide_uri(self), self->e->mtime_msec, self->e->filesize, +		browser->item_size); +	return rescale_thumbnail(found, browser->item_height); +} + +static void +entry_add_thumbnail(gpointer data, gpointer user_data) +{ +	Entry *self = data; +	FivBrowser *browser = FIV_BROWSER(user_data); +	if (self->removed) { +		// Keep whatever size of thumbnail we had at the time up until reload. +		// g_file_query_info() fails for removed files, so keep the icon, too. +		if (self->icon) { +			g_clear_pointer(&self->thumbnail, cairo_surface_destroy); +		} else { +			self->thumbnail = +				rescale_thumbnail(self->thumbnail, browser->item_height); +		} +		return;  	} -	if (self->thumbnail) { +	g_clear_object(&self->icon); +	g_clear_pointer(&self->thumbnail, cairo_surface_destroy); + +	if ((self->thumbnail = entry_lookup_thumbnail(self, browser))) {  		// Yes, this is a pointless action in case it's been found in the cache.  		entry_set_surface_user_data(self);  		return; @@ -624,15 +661,15 @@ reload_thumbnails(FivBrowser *self)  	GThreadPool *pool = g_thread_pool_new(  		entry_add_thumbnail, self, g_get_num_processors(), FALSE, NULL);  	for (guint i = 0; i < self->entries->len; i++) -		g_thread_pool_push(pool, &g_array_index(self->entries, Entry, i), NULL); +		g_thread_pool_push(pool, self->entries->pdata[i], NULL);  	g_thread_pool_free(pool, FALSE, TRUE);  	// Once a URI disappears from the model, its thumbnail is forgotten.  	g_hash_table_remove_all(self->thumbnail_cache);  	for (guint i = 0; i < self->entries->len; i++) { -		Entry *entry = &g_array_index(self->entries, Entry, i); -		if (entry->thumbnail) { +		Entry *entry = self->entries->pdata[i]; +		if (!entry->removed && entry->thumbnail) {  			g_hash_table_insert(self->thumbnail_cache, g_strdup(entry->e->uri),  				cairo_surface_reference(entry->thumbnail));  		} @@ -790,7 +827,10 @@ thumbnailers_start(FivBrowser *self)  	GQueue lq = G_QUEUE_INIT;  	for (guint i = 0; i < self->entries->len; i++) { -		Entry *entry = &g_array_index(self->entries, Entry, i); +		Entry *entry = self->entries->pdata[i]; +		if (entry->removed) +			continue; +  		if (entry->icon)  			g_queue_push_tail(&self->thumbnailers_queue, entry);  		else if (cairo_surface_get_user_data( @@ -868,7 +908,7 @@ fiv_browser_finalize(GObject *gobject)  {  	FivBrowser *self = FIV_BROWSER(gobject);  	thumbnailers_abort(self); -	g_array_free(self->entries, TRUE); +	g_ptr_array_free(self->entries, TRUE);  	g_array_free(self->layouted_rows, TRUE);  	if (self->model) {  		g_signal_handlers_disconnect_by_data(self->model, self); @@ -1774,8 +1814,8 @@ fiv_browser_init(FivBrowser *self)  	gtk_widget_set_can_focus(GTK_WIDGET(self), TRUE);  	gtk_widget_set_has_tooltip(GTK_WIDGET(self), TRUE); -	self->entries = g_array_new(FALSE, TRUE, sizeof(Entry)); -	g_array_set_clear_func(self->entries, (GDestroyNotify) entry_free); +	self->entries = +		g_ptr_array_new_with_free_func((GDestroyNotify) entry_destroy);  	self->layouted_rows = g_array_new(FALSE, TRUE, sizeof(Row));  	g_array_set_clear_func(self->layouted_rows, (GDestroyNotify) row_free);  	abort_button_tracking(self); @@ -1810,9 +1850,8 @@ fiv_browser_init(FivBrowser *self)  // --- Public interface -------------------------------------------------------- -// TODO(p): Later implement any arguments of this FivIoModel signal.  static void -on_model_files_changed(FivIoModel *model, FivBrowser *self) +on_model_reloaded(FivIoModel *model, FivBrowser *self)  {  	g_return_if_fail(model == self->model); @@ -1821,14 +1860,14 @@ on_model_files_changed(FivIoModel *model, FivBrowser *self)  		selected_uri = g_strdup(self->selected->e->uri);  	thumbnailers_abort(self); -	g_array_set_size(self->entries, 0);  	g_array_set_size(self->layouted_rows, 0); +	g_ptr_array_set_size(self->entries, 0);  	gsize len = 0;  	FivIoModelEntry *const *files = fiv_io_model_get_files(self->model, &len);  	for (gsize i = 0; i < len; i++) { -		Entry e = {.e = fiv_io_model_entry_ref(files[i])}; -		g_array_append_val(self->entries, e); +		g_ptr_array_add( +			self->entries, entry_new(fiv_io_model_entry_ref(files[i])));  	}  	fiv_browser_select(self, selected_uri); @@ -1838,6 +1877,55 @@ on_model_files_changed(FivIoModel *model, FivBrowser *self)  	thumbnailers_start(self);  } +static void +on_model_changed(FivIoModel *model, FivIoModelEntry *old, FivIoModelEntry *new, +	FivBrowser *self) +{ +	g_return_if_fail(model == self->model); + +	// Add new entries to the end, so as to not disturb the layout. +	if (!old) { +		g_ptr_array_add( +			self->entries, entry_new(fiv_io_model_entry_ref(new))); + +		// TODO(p): Only process this one item, not everything at once. +		// (This mainly has an effect on thumbnail-less entries.) +		reload_thumbnails(self); +		thumbnailers_start(self); +		return; +	} + +	Entry *found = NULL; +	for (guint i = 0; i < self->entries->len; i++) { +		Entry *entry = self->entries->pdata[i]; +		if (entry->e == old) { +			found = entry; +			break; +		} +	} +	if (!found) +		return; + +	// Rename entries in place, so as to not disturb the layout. +	// XXX: This behaves differently from FivIoModel, and by extension fiv.c. +	if (new) { +		fiv_io_model_entry_unref(found->e); +		found->e = fiv_io_model_entry_ref(new); +		found->removed = FALSE; + +		// TODO(p): If there is a URI mismatch, don't reload thumbnails, +		// so that there's no jumping around. Or, a bit more properly, +		// move the thumbnail cache entry to the new URI. +		// TODO(p): Only process this one item, not everything at once. +		// (This mainly has an effect on thumbnail-less entries.) +		reload_thumbnails(self); +		thumbnailers_start(self); +	} else { +		found->removed = TRUE; +		gtk_widget_queue_draw(GTK_WIDGET(self)); +	} +} +  GtkWidget *  fiv_browser_new(FivIoModel *model)  { @@ -1846,9 +1934,11 @@ fiv_browser_new(FivIoModel *model)  	FivBrowser *self = g_object_new(FIV_TYPE_BROWSER, NULL);  	self->model = g_object_ref(model); -	g_signal_connect( -		self->model, "files-changed", G_CALLBACK(on_model_files_changed), self); -	on_model_files_changed(self->model, self); +	g_signal_connect(self->model, "reloaded", +		G_CALLBACK(on_model_reloaded), self); +	g_signal_connect(self->model, "files-changed", +		G_CALLBACK(on_model_changed), self); +	on_model_reloaded(self->model, self);  	return GTK_WIDGET(self);  } @@ -1877,7 +1967,7 @@ fiv_browser_select(FivBrowser *self, const char *uri)  		return;  	for (guint i = 0; i < self->entries->len; i++) { -		const Entry *entry = &g_array_index(self->entries, Entry, i); +		const Entry *entry = self->entries->pdata[i];  		if (!g_strcmp0(entry->e->uri, uri)) {  			self->selected = entry;  			scroll_to_selection(self);  | 
