Compare commits

..

No commits in common. "ad29013e44e2f32029893edd977b0b555c8a50cd" and "9ba3679e89a0c760c016a541557528cfbea59f20" have entirely different histories.

2 changed files with 50 additions and 90 deletions

View File

@ -94,7 +94,6 @@ static struct key_section help_keys[] = {
{"<alt>Left", "Go back in history"}, {"<alt>Left", "Go back in history"},
{"<alt>Right", "Go forward in history"}, {"<alt>Right", "Go forward in history"},
{"<alt>Up", "Go to parent directory"}, {"<alt>Up", "Go to parent directory"},
{"<alt>Home", "Go home"},
{"F5 r <control>r", "Refresh"}, {"F5 r <control>r", "Refresh"},
{} {}
}}, }},
@ -119,8 +118,6 @@ static struct key_section help_keys[] = {
{"1...9", "Set zoom to N:1"}, {"1...9", "Set zoom to N:1"},
{"plus <control>plus", "Zoom in"}, {"plus <control>plus", "Zoom in"},
{"minus <control>minus", "Zoom out"}, {"minus <control>minus", "Zoom out"},
{"w", "Zoom to fit width if larger"},
{"h", "Zoom to fit height if larger"},
{} {}
}}, }},
{"Orientation", (struct key[]) { {"Orientation", (struct key[]) {
@ -129,12 +126,6 @@ static struct key_section help_keys[] = {
{"greater", "Rotate clockwise"}, {"greater", "Rotate clockwise"},
{} {}
}}, }},
{"Configuration", (struct key[]) {
{"x", "Toggle scale to fit if larger"},
{"i", "Toggle smooth scaling"},
{"t", "Toggle transparency highlighting"},
{}
}},
{"Control", (struct key[]) { {"Control", (struct key[]) {
{"bracketleft", "Previous page"}, {"bracketleft", "Previous page"},
{"bracketright", "Next page"}, {"bracketright", "Next page"},
@ -143,6 +134,12 @@ static struct key_section help_keys[] = {
{"space", "Toggle playback"}, {"space", "Toggle playback"},
{} {}
}}, }},
{"Configuration", (struct key[]) {
{"x", "Toggle scale to fit"},
{"i", "Toggle smooth scaling"},
{"t", "Toggle transparency highlighting"},
{}
}},
{"Tools", (struct key[]) { {"Tools", (struct key[]) {
{"<control>p", "Print..."}, {"<control>p", "Print..."},
{"<control>s", "Save page as..."}, {"<control>s", "Save page as..."},
@ -775,9 +772,6 @@ on_key_press(G_GNUC_UNUSED GtkWidget *widget, GdkEventKey *event,
g_free(parent); g_free(parent);
} }
return TRUE; return TRUE;
case GDK_KEY_Home:
load_directory(g_get_home_dir());
return TRUE;
} }
break; break;
case 0: case 0:

View File

@ -33,7 +33,6 @@
struct _FivView { struct _FivView {
GtkWidget parent_instance; GtkWidget parent_instance;
gchar *path; ///< Path to the current image (if any)
cairo_surface_t *image; ///< The loaded image (sequence) cairo_surface_t *image; ///< The loaded image (sequence)
cairo_surface_t *page; ///< Current page within image, weak cairo_surface_t *page; ///< Current page within image, weak
cairo_surface_t *frame; ///< Current frame within page, weak cairo_surface_t *frame; ///< Current frame within page, weak
@ -50,10 +49,6 @@ struct _FivView {
G_DEFINE_TYPE(FivView, fiv_view, GTK_TYPE_WIDGET) G_DEFINE_TYPE(FivView, fiv_view, GTK_TYPE_WIDGET)
struct size {
double width, height;
};
static FivIoOrientation view_left[9] = { static FivIoOrientation view_left[9] = {
[FivIoOrientationUnknown] = FivIoOrientationUnknown, [FivIoOrientationUnknown] = FivIoOrientationUnknown,
[FivIoOrientation0] = FivIoOrientation270, [FivIoOrientation0] = FivIoOrientation270,
@ -110,7 +105,6 @@ fiv_view_finalize(GObject *gobject)
{ {
FivView *self = FIV_VIEW(gobject); FivView *self = FIV_VIEW(gobject);
cairo_surface_destroy(self->image); cairo_surface_destroy(self->image);
g_free(self->path);
G_OBJECT_CLASS(fiv_view_parent_class)->finalize(gobject); G_OBJECT_CLASS(fiv_view_parent_class)->finalize(gobject);
} }
@ -178,11 +172,12 @@ fiv_view_set_property(
} }
} }
static struct size static void
get_surface_dimensions(FivView *self) get_surface_dimensions(FivView *self, double *width, double *height)
{ {
*width = *height = 0;
if (!self->image) if (!self->image)
return (struct size) {}; return;
cairo_rectangle_t extents = {}; cairo_rectangle_t extents = {};
switch (cairo_surface_get_type(self->page)) { switch (cairo_surface_get_type(self->page)) {
@ -204,18 +199,23 @@ get_surface_dimensions(FivView *self)
case FivIoOrientationMirror90: case FivIoOrientationMirror90:
case FivIoOrientation270: case FivIoOrientation270:
case FivIoOrientationMirror270: case FivIoOrientationMirror270:
return (struct size) {extents.height, extents.width}; *width = extents.height;
*height = extents.width;
return;
default: default:
return (struct size) {extents.width, extents.height}; *width = extents.width;
*height = extents.height;
} }
} }
static void static void
get_display_dimensions(FivView *self, int *width, int *height) get_display_dimensions(FivView *self, int *width, int *height)
{ {
struct size surface_dimensions = get_surface_dimensions(self); double w, h;
*width = ceil(surface_dimensions.width * self->scale); get_surface_dimensions(self, &w, &h);
*height = ceil(surface_dimensions.height * self->scale);
*width = ceil(w * self->scale);
*height = ceil(h * self->scale);
} }
static cairo_matrix_t static cairo_matrix_t
@ -263,7 +263,9 @@ fiv_view_get_preferred_height(GtkWidget *widget, gint *minimum, gint *natural)
{ {
FivView *self = FIV_VIEW(widget); FivView *self = FIV_VIEW(widget);
if (self->scale_to_fit) { if (self->scale_to_fit) {
*natural = ceil(get_surface_dimensions(self).height); double sw, sh;
get_surface_dimensions(self, &sw, &sh);
*natural = ceil(sh);
*minimum = 1; *minimum = 1;
} else { } else {
int dw, dh; int dw, dh;
@ -277,7 +279,9 @@ fiv_view_get_preferred_width(GtkWidget *widget, gint *minimum, gint *natural)
{ {
FivView *self = FIV_VIEW(widget); FivView *self = FIV_VIEW(widget);
if (self->scale_to_fit) { if (self->scale_to_fit) {
*natural = ceil(get_surface_dimensions(self).width); double sw, sh;
get_surface_dimensions(self, &sw, &sh);
*natural = ceil(sw);
*minimum = 1; *minimum = 1;
} else { } else {
int dw, dh; int dw, dh;
@ -295,13 +299,14 @@ fiv_view_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
if (!self->image || !self->scale_to_fit) if (!self->image || !self->scale_to_fit)
return; return;
struct size surface_dimensions = get_surface_dimensions(self); double w, h;
self->scale = 1; get_surface_dimensions(self, &w, &h);
if (ceil(surface_dimensions.width * self->scale) > allocation->width) self->scale = 1;
self->scale = allocation->width / surface_dimensions.width; if (ceil(w * self->scale) > allocation->width)
if (ceil(surface_dimensions.height * self->scale) > allocation->height) self->scale = allocation->width / w;
self->scale = allocation->height / surface_dimensions.height; if (ceil(h * self->scale) > allocation->height)
self->scale = allocation->height / h;
g_object_notify_by_pspec(G_OBJECT(widget), view_properties[PROP_SCALE]); g_object_notify_by_pspec(G_OBJECT(widget), view_properties[PROP_SCALE]);
} }
@ -369,7 +374,9 @@ fiv_view_draw(GtkWidget *widget, cairo_t *cr)
return TRUE; return TRUE;
int w, h; int w, h;
double sw, sh;
get_display_dimensions(self, &w, &h); get_display_dimensions(self, &w, &h);
get_surface_dimensions(self, &sw, &sh);
double x = 0; double x = 0;
double y = 0; double y = 0;
@ -378,9 +385,7 @@ fiv_view_draw(GtkWidget *widget, cairo_t *cr)
if (h < allocation.height) if (h < allocation.height)
y = round((allocation.height - h) / 2.); y = round((allocation.height - h) / 2.);
struct size surface_dimensions = get_surface_dimensions(self); cairo_matrix_t matrix = get_orientation_matrix(self->orientation, sw, sh);
cairo_matrix_t matrix = get_orientation_matrix(
self->orientation, surface_dimensions.width, surface_dimensions.height);
cairo_translate(cr, x, y); cairo_translate(cr, x, y);
if (self->checkerboard) { if (self->checkerboard) {
gtk_style_context_save(style); gtk_style_context_save(style);
@ -471,28 +476,6 @@ set_scale(FivView *self, double scale)
return set_scale_to_fit(self, false); return set_scale_to_fit(self, false);
} }
static gboolean
set_scale_to_fit_width(FivView *self)
{
double w = get_surface_dimensions(self).width;
int allocated = gtk_widget_get_allocated_width(
gtk_widget_get_parent(GTK_WIDGET(self)));
if (ceil(w * self->scale) > allocated)
return set_scale(self, allocated / w);
return TRUE;
}
static gboolean
set_scale_to_fit_height(FivView *self)
{
double h = get_surface_dimensions(self).height;
int allocated = gtk_widget_get_allocated_height(
gtk_widget_get_parent(GTK_WIDGET(self)));
if (ceil(h * self->scale) > allocated)
return set_scale(self, allocated / h);
return TRUE;
}
static gboolean static gboolean
fiv_view_scroll_event(GtkWidget *widget, GdkEventScroll *event) fiv_view_scroll_event(GtkWidget *widget, GdkEventScroll *event)
{ {
@ -664,11 +647,12 @@ static void
on_draw_page(G_GNUC_UNUSED GtkPrintOperation *operation, on_draw_page(G_GNUC_UNUSED GtkPrintOperation *operation,
GtkPrintContext *context, G_GNUC_UNUSED int page_nr, FivView *self) GtkPrintContext *context, G_GNUC_UNUSED int page_nr, FivView *self)
{ {
double surface_width_px = 0, surface_height_px = 0;
get_surface_dimensions(self, &surface_width_px, &surface_height_px);
// Any DPI will be wrong, unless we import that information from the image. // Any DPI will be wrong, unless we import that information from the image.
double scale = 1 / 96.; double scale = 1 / 96.;
struct size surface_dimensions = get_surface_dimensions(self); double w = surface_width_px * scale, h = surface_height_px * scale;
double w = surface_dimensions.width * scale;
double h = surface_dimensions.height * scale;
// Scale down to fit the print area, taking care to not divide by zero. // Scale down to fit the print area, taking care to not divide by zero.
double areaw = gtk_print_context_get_width(context); double areaw = gtk_print_context_get_width(context);
@ -679,12 +663,12 @@ on_draw_page(G_GNUC_UNUSED GtkPrintOperation *operation,
cairo_scale(cr, scale, scale); cairo_scale(cr, scale, scale);
cairo_set_source_surface(cr, self->frame, 0, 0); cairo_set_source_surface(cr, self->frame, 0, 0);
cairo_matrix_t matrix = get_orientation_matrix( cairo_matrix_t matrix = get_orientation_matrix(
self->orientation, surface_dimensions.width, surface_dimensions.height); self->orientation, surface_width_px, surface_height_px);
cairo_pattern_set_matrix(cairo_get_source(cr), &matrix); cairo_pattern_set_matrix(cairo_get_source(cr), &matrix);
cairo_paint(cr); cairo_paint(cr);
} }
static void static gboolean
print(FivView *self) print(FivView *self)
{ {
GtkPrintOperation *print = gtk_print_operation_new(); GtkPrintOperation *print = gtk_print_operation_new();
@ -711,6 +695,7 @@ print(FivView *self)
show_error_dialog(window, error); show_error_dialog(window, error);
g_object_unref(print); g_object_unref(print);
return TRUE;
} }
static gboolean static gboolean
@ -733,32 +718,21 @@ save_as(FivView *self, gboolean frame)
GtkFileFilter *webp_filter = gtk_file_filter_new(); GtkFileFilter *webp_filter = gtk_file_filter_new();
gtk_file_filter_add_mime_type(webp_filter, "image/webp"); gtk_file_filter_add_mime_type(webp_filter, "image/webp");
gtk_file_filter_add_pattern(webp_filter, "*.webp"); gtk_file_filter_add_pattern(webp_filter, "*.webp");
gtk_file_filter_set_name(webp_filter, "Lossless WebP (*.webp)"); gtk_file_filter_set_name(webp_filter, "Lossless WebP");
gtk_file_chooser_add_filter(chooser, webp_filter); gtk_file_chooser_add_filter(chooser, webp_filter);
// Note that GTK+'s save dialog is too stupid to automatically change // TODO(p): Derive it from the currently displayed filename,
// the extension when user changes the filter. Presumably, // and set the directory to the same place.
// gtk_file_chooser_set_extra_widget() can be used to circumvent this. gtk_file_chooser_set_current_name(
gchar *basename = g_filename_display_basename(self->path); chooser, frame ? "frame.webp" : "page.webp");
gchar *name =
g_strdup_printf(frame ? "%s.frame.webp" : "%s.webp", basename);
g_free(basename);
gtk_file_chooser_set_current_name(chooser, name);
g_free(name);
#endif // HAVE_LIBWEBP #endif // HAVE_LIBWEBP
gtk_file_chooser_set_do_overwrite_confirmation(chooser, TRUE);
gchar *dirname = g_path_get_dirname(self->path);
gtk_file_chooser_set_current_folder(chooser, dirname);
g_free(dirname);
// The format is supported by Exiv2 and ExifTool. // The format is supported by Exiv2 and ExifTool.
// This is mostly a developer tool. // This is mostly a developer tool.
GtkFileFilter *exv_filter = gtk_file_filter_new(); GtkFileFilter *exv_filter = gtk_file_filter_new();
gtk_file_filter_add_mime_type(exv_filter, "image/x-exv"); gtk_file_filter_add_mime_type(exv_filter, "image/x-exv");
gtk_file_filter_add_pattern(exv_filter, "*.exv"); gtk_file_filter_add_pattern(exv_filter, "*.exv");
gtk_file_filter_set_name(exv_filter, "Exiv2 metadata (*.exv)"); gtk_file_filter_set_name(exv_filter, "Exiv2 metadata");
gtk_file_chooser_add_filter(chooser, exv_filter); gtk_file_chooser_add_filter(chooser, exv_filter);
switch (gtk_dialog_run(GTK_DIALOG(dialog))) { switch (gtk_dialog_run(GTK_DIALOG(dialog))) {
@ -842,11 +816,6 @@ fiv_view_key_press_event(GtkWidget *widget, GdkEventKey *event)
case GDK_KEY_minus: case GDK_KEY_minus:
return command(self, FIV_VIEW_COMMAND_ZOOM_OUT); return command(self, FIV_VIEW_COMMAND_ZOOM_OUT);
case GDK_KEY_w:
return set_scale_to_fit_width(self);
case GDK_KEY_h:
return set_scale_to_fit_height(self);
case GDK_KEY_x: // Inspired by gThumb, which has more such modes. case GDK_KEY_x: // Inspired by gThumb, which has more such modes.
return command(self, FIV_VIEW_COMMAND_TOGGLE_SCALE_TO_FIT); return command(self, FIV_VIEW_COMMAND_TOGGLE_SCALE_TO_FIT);
case GDK_KEY_i: case GDK_KEY_i:
@ -941,7 +910,7 @@ fiv_view_init(FivView *self)
self->scale = 1.0; self->scale = 1.0;
} }
// --- Public interface -------------------------------------------------------- // --- Picture loading ---------------------------------------------------------
// TODO(p): Progressive picture loading, or at least async/cancellable. // TODO(p): Progressive picture loading, or at least async/cancellable.
gboolean gboolean
@ -958,9 +927,6 @@ fiv_view_open(FivView *self, const gchar *path, GError **error)
switch_page(self, self->image); switch_page(self, self->image);
set_scale_to_fit(self, true); set_scale_to_fit(self, true);
g_free(self->path);
self->path = g_strdup(path);
g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_HAS_IMAGE]); g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_HAS_IMAGE]);
return TRUE; return TRUE;
} }