aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2022-01-24 01:20:37 +0100
committerPřemysl Eric Janouch <p@janouch.name>2022-01-24 02:48:38 +0100
commit991e74b99bbf344effe456f71c544a284f7afe14 (patch)
tree30093c454c7b4e7874e11153aa19c52c9e72a7bf
parent38670428dacaf44525db3710c8acea1bb543eab6 (diff)
downloadfiv-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.c62
-rw-r--r--fiv-view.h3
-rw-r--r--fiv.c55
3 files changed, 84 insertions, 36 deletions
diff --git a/fiv-view.c b/fiv-view.c
index 5f57899..26be2eb 100644
--- a/fiv-view.c
+++ b/fiv-view.c
@@ -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));
diff --git a/fiv-view.h b/fiv-view.h
index 54093b7..7df00e0 100644
--- a/fiv-view.h
+++ b/fiv-view.h
@@ -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 {
diff --git a/fiv.c b/fiv.c
index 7102db0..49a04a5 100644
--- a/fiv.c
+++ b/fiv.c
@@ -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);