diff options
| author | Přemysl Eric Janouch <p@janouch.name> | 2022-01-24 01:20:37 +0100 | 
|---|---|---|
| committer | Přemysl Eric Janouch <p@janouch.name> | 2022-01-24 02:48:38 +0100 | 
| commit | 991e74b99bbf344effe456f71c544a284f7afe14 (patch) | |
| tree | 30093c454c7b4e7874e11153aa19c52c9e72a7bf | |
| parent | 38670428dacaf44525db3710c8acea1bb543eab6 (diff) | |
| download | fiv-991e74b99bbf344effe456f71c544a284f7afe14.tar.gz fiv-991e74b99bbf344effe456f71c544a284f7afe14.tar.xz fiv-991e74b99bbf344effe456f71c544a284f7afe14.zip  | |
Redirect image open failure messages
Pop-up dialogs are quite annoying, as is not being able to
iterate over broken images.
This will also be useful for warnings and asynchronous loading.
| -rw-r--r-- | fiv-view.c | 62 | ||||
| -rw-r--r-- | fiv-view.h | 3 | ||||
| -rw-r--r-- | fiv.c | 55 | 
3 files changed, 84 insertions, 36 deletions
@@ -50,6 +50,8 @@ fiv_view_command_get_type(void)  struct _FivView {  	GtkWidget parent_instance; + +	gchar *messages;                    ///< Image load information  	gchar *uri;                         ///< Path to the current image (if any)  	cairo_surface_t *image;             ///< The loaded image (sequence)  	cairo_surface_t *page;              ///< Current page within image, weak @@ -114,7 +116,8 @@ static FivIoOrientation view_right[9] = {  };  enum { -	PROP_SCALE = 1, +	PROP_MESSAGES = 1, +	PROP_SCALE,  	PROP_SCALE_TO_FIT,  	PROP_ENABLE_CMS,  	PROP_FILTER, @@ -147,6 +150,7 @@ fiv_view_finalize(GObject *gobject)  	g_clear_pointer(&self->image, cairo_surface_destroy);  	g_clear_pointer(&self->page_scaled, cairo_surface_destroy);  	g_free(self->uri); +	g_free(self->messages);  	G_OBJECT_CLASS(fiv_view_parent_class)->finalize(gobject);  } @@ -157,6 +161,9 @@ fiv_view_get_property(  {  	FivView *self = FIV_VIEW(object);  	switch (property_id) { +	case PROP_MESSAGES: +		g_value_set_string(value, self->messages); +		break;  	case PROP_SCALE:  		g_value_set_double(value, self->scale);  		break; @@ -253,8 +260,8 @@ fiv_view_get_preferred_height(GtkWidget *widget, gint *minimum, gint *natural)  {  	FivView *self = FIV_VIEW(widget);  	if (self->scale_to_fit) { -		*natural = ceil(get_surface_dimensions(self).height);  		*minimum = 1; +		*natural = MAX(*minimum, ceil(get_surface_dimensions(self).height));  	} else {  		int dw, dh;  		get_display_dimensions(self, &dw, &dh); @@ -267,8 +274,8 @@ fiv_view_get_preferred_width(GtkWidget *widget, gint *minimum, gint *natural)  {  	FivView *self = FIV_VIEW(widget);  	if (self->scale_to_fit) { -		*natural = ceil(get_surface_dimensions(self).width);  		*minimum = 1; +		*natural = MAX(*minimum, ceil(get_surface_dimensions(self).width));  	} else {  		int dw, dh;  		get_display_dimensions(self, &dw, &dh); @@ -279,9 +286,9 @@ fiv_view_get_preferred_width(GtkWidget *widget, gint *minimum, gint *natural)  static void  prescale_page(FivView *self)  { -	FivIoRenderClosure *closure = -		cairo_surface_get_user_data(self->page, &fiv_io_key_render); -	if (!closure) +	FivIoRenderClosure *closure = NULL; +	if (!self->image || !(closure = +			cairo_surface_get_user_data(self->page, &fiv_io_key_render)))  		return;  	// TODO(p): Restart the animation. No vector formats currently animate. @@ -678,8 +685,9 @@ switch_page(FivView *self, cairo_surface_t *page)  	self->frame = self->page = page;  	prescale_page(self); -	if ((self->orientation = (uintptr_t) cairo_surface_get_user_data( -			self->page, &fiv_io_key_orientation)) == FivIoOrientationUnknown) +	if (!self->page || +		(self->orientation = (uintptr_t) cairo_surface_get_user_data( +			 self->page, &fiv_io_key_orientation)) == FivIoOrientationUnknown)  		self->orientation = FivIoOrientation0;  	start_animating(self); @@ -1044,6 +1052,9 @@ fiv_view_class_init(FivViewClass *klass)  	object_class->get_property = fiv_view_get_property;  	object_class->set_property = fiv_view_set_property; +	view_properties[PROP_MESSAGES] = g_param_spec_string( +		"messages", "Messages", "Informative messages from the last image load", +		NULL, G_PARAM_READABLE);  	view_properties[PROP_SCALE] = g_param_spec_double(  		"scale", "Scale", "Zoom level",  		0, G_MAXDOUBLE, 1.0, G_PARAM_READABLE); @@ -1151,15 +1162,8 @@ fiv_view_init(FivView *self)  // TODO(p): Progressive picture loading, or at least async/cancellable.  gboolean -fiv_view_open(FivView *self, const gchar *uri, GError **error) +fiv_view_set_uri(FivView *self, const gchar *uri)  { -	cairo_surface_t *surface = fiv_io_open( -		uri, self->enable_cms ? self->screen_cms_profile : NULL, FALSE, error); -	if (!surface) -		return FALSE; -	if (self->image) -		cairo_surface_destroy(self->image); -  	// This is extremely expensive, and only works sometimes.  	g_clear_pointer(&self->enhance_swap, cairo_surface_destroy);  	if (self->enhance) { @@ -1168,6 +1172,17 @@ fiv_view_open(FivView *self, const gchar *uri, GError **error)  			G_OBJECT(self), view_properties[PROP_ENHANCE]);  	} +	GError *error = NULL; +	cairo_surface_t *surface = fiv_io_open( +		uri, self->enable_cms ? self->screen_cms_profile : NULL, FALSE, &error); + +	g_clear_pointer(&self->messages, g_free); +	g_clear_pointer(&self->image, cairo_surface_destroy); +	if (error) { +		self->messages = g_strdup(error->message); +		g_error_free(error); +	} +  	self->frame = self->page = NULL;  	self->image = surface;  	switch_page(self, self->image); @@ -1176,8 +1191,9 @@ fiv_view_open(FivView *self, const gchar *uri, GError **error)  	g_free(self->uri);  	self->uri = g_strdup(uri); +	g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_MESSAGES]);  	g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_HAS_IMAGE]); -	return TRUE; +	return surface != NULL;  }  static void @@ -1208,11 +1224,17 @@ reload(FivView *self)  	cairo_surface_t *surface = fiv_io_open(self->uri,  		self->enable_cms ? self->screen_cms_profile : NULL, self->enhance,  		&error); -	if (!surface) { -		show_error_dialog(get_toplevel(GTK_WIDGET(self)), error); -		return FALSE; + +	g_clear_pointer(&self->messages, g_free); +	if (error) { +		self->messages = g_strdup(error->message); +		g_error_free(error);  	} +	g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_MESSAGES]); +	if (error) +		return FALSE; +  	g_clear_pointer(&self->image, cairo_surface_destroy);  	g_clear_pointer(&self->enhance_swap, cairo_surface_destroy);  	switch_page(self, (self->image = surface)); @@ -23,7 +23,8 @@  G_DECLARE_FINAL_TYPE(FivView, fiv_view, FIV, VIEW, GtkWidget)  /// Try to open the given file, synchronously, to be displayed by the widget. -gboolean fiv_view_open(FivView *self, const gchar *uri, GError **error); +/// The current image is cleared on failure. +gboolean fiv_view_set_uri(FivView *self, const gchar *uri);  // And this is how you avoid glib-mkenums.  typedef enum _FivViewCommand { @@ -542,6 +542,8 @@ struct {  	GtkWidget *view_box;  	GtkWidget *view_toolbar; +	GtkWidget *view_info; +	GtkWidget *view_info_label;  	GtkWidget *toolbar[TOOLBAR_COUNT];  	GtkWidget *view;  } g; @@ -718,32 +720,38 @@ on_sort_direction(G_GNUC_UNUSED GtkMenuItem *item, gpointer data)  }  static void +on_notify_view_messages(FivView *view, G_GNUC_UNUSED gpointer user_data) +{ +	gchar *messages = NULL; +	g_object_get(view, "messages", &messages, NULL); +	if (messages) { +		gchar *escaped = g_markup_escape_text(messages, -1); +		g_free(messages); +		gchar *message = g_strdup_printf("<b>Error:</b> %s", escaped); +		g_free(escaped); +		gtk_label_set_markup(GTK_LABEL(g.view_info_label), message); +		g_free(message); +		gtk_widget_show(g.view_info); +	} else { +		gtk_widget_hide(g.view_info); +	} +} + +static void  open(const gchar *uri)  {  	GFile *file = g_file_new_for_uri(uri); +	if (fiv_view_set_uri(FIV_VIEW(g.view), uri)) +		gtk_recent_manager_add_item(gtk_recent_manager_get_default(), uri); -	GError *error = NULL; -	if (!fiv_view_open(FIV_VIEW(g.view), uri, &error)) { -		const char *path = g_file_peek_path(file); -		// TODO(p): Use g_file_info_get_display_name(). -		gchar *base = g_filename_display_basename(path ? path : uri); -		g_object_unref(file); - -		g_prefix_error(&error, "%s: ", base); -		show_error_dialog(error); -		g_free(base); -		return; -	} - -	gtk_recent_manager_add_item(gtk_recent_manager_get_default(), uri);  	g_list_free_full(g.directory_forward, g_free);  	g.directory_forward = NULL; -  	g_free(g.uri);  	g.uri = g_strdup(uri);  	// So that load_directory() itself can be used for reloading.  	gchar *parent = parent_uri(file); +	g_object_unref(file);  	if (!g.files->len /* hack to always load the directory after launch */ ||  		!g.directory || strcmp(parent, g.directory))  		load_directory_without_switching(parent); @@ -1785,9 +1793,26 @@ main(int argc, char *argv[])  	gtk_box_pack_start(GTK_BOX(g.view_toolbar),  		gtk_separator_new(GTK_ORIENTATION_VERTICAL), FALSE, FALSE, 0); +	g.view_info = gtk_info_bar_new(); +	// The button cannot be made flat or reflect the message type under Adwaita. +	gtk_info_bar_set_show_close_button(GTK_INFO_BAR(g.view_info), TRUE); +	// Do not use gtk_info_bar_set_revealed(), as it animates. +	gtk_info_bar_set_message_type(GTK_INFO_BAR(g.view_info), GTK_MESSAGE_ERROR); +	g_signal_connect(g.view_info, "response", +		G_CALLBACK(gtk_widget_hide), NULL); + +	g.view_info_label = gtk_label_new(NULL); +	gtk_label_set_line_wrap(GTK_LABEL(g.view_info_label), TRUE); +	gtk_container_add( +		GTK_CONTAINER(gtk_info_bar_get_content_area(GTK_INFO_BAR(g.view_info))), +		g.view_info_label); +	g_signal_connect(g.view, "notify::messages", +		G_CALLBACK(on_notify_view_messages), NULL); +  	// Need to put the toolbar at the top, because of the horizontal scrollbar.  	g.view_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);  	gtk_box_pack_start(GTK_BOX(g.view_box), g.view_toolbar, FALSE, FALSE, 0); +	gtk_box_pack_start(GTK_BOX(g.view_box), g.view_info, FALSE, FALSE, 0);  	gtk_box_pack_start(GTK_BOX(g.view_box), view_scroller, TRUE, TRUE, 0);  	g.browser_scroller = gtk_scrolled_window_new(NULL, NULL);  | 
