Add zooming to fit width/height if larger
Also, mildly refactor get_surface_dimensions().
This commit is contained in:
		
							
								
								
									
										14
									
								
								fastiv.c
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								fastiv.c
									
									
									
									
									
								
							@@ -119,6 +119,8 @@ 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[]) {
 | 
				
			||||||
@@ -127,6 +129,12 @@ 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"},
 | 
				
			||||||
@@ -135,12 +143,6 @@ 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..."},
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										89
									
								
								fiv-view.c
									
									
									
									
									
								
							
							
						
						
									
										89
									
								
								fiv-view.c
									
									
									
									
									
								
							@@ -50,6 +50,10 @@ 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,
 | 
				
			||||||
@@ -174,12 +178,11 @@ fiv_view_set_property(
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static struct size
 | 
				
			||||||
get_surface_dimensions(FivView *self, double *width, double *height)
 | 
					get_surface_dimensions(FivView *self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	*width = *height = 0;
 | 
					 | 
				
			||||||
	if (!self->image)
 | 
						if (!self->image)
 | 
				
			||||||
		return;
 | 
							return (struct size) {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cairo_rectangle_t extents = {};
 | 
						cairo_rectangle_t extents = {};
 | 
				
			||||||
	switch (cairo_surface_get_type(self->page)) {
 | 
						switch (cairo_surface_get_type(self->page)) {
 | 
				
			||||||
@@ -201,23 +204,18 @@ get_surface_dimensions(FivView *self, double *width, double *height)
 | 
				
			|||||||
	case FivIoOrientationMirror90:
 | 
						case FivIoOrientationMirror90:
 | 
				
			||||||
	case FivIoOrientation270:
 | 
						case FivIoOrientation270:
 | 
				
			||||||
	case FivIoOrientationMirror270:
 | 
						case FivIoOrientationMirror270:
 | 
				
			||||||
		*width = extents.height;
 | 
							return (struct size) {extents.height, extents.width};
 | 
				
			||||||
		*height = extents.width;
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		*width = extents.width;
 | 
							return (struct size) {extents.width, extents.height};
 | 
				
			||||||
		*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)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	double w, h;
 | 
						struct size surface_dimensions = get_surface_dimensions(self);
 | 
				
			||||||
	get_surface_dimensions(self, &w, &h);
 | 
						*width = ceil(surface_dimensions.width * self->scale);
 | 
				
			||||||
 | 
						*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
 | 
				
			||||||
@@ -265,9 +263,7 @@ 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) {
 | 
				
			||||||
		double sw, sh;
 | 
							*natural = ceil(get_surface_dimensions(self).height);
 | 
				
			||||||
		get_surface_dimensions(self, &sw, &sh);
 | 
					 | 
				
			||||||
		*natural = ceil(sh);
 | 
					 | 
				
			||||||
		*minimum = 1;
 | 
							*minimum = 1;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		int dw, dh;
 | 
							int dw, dh;
 | 
				
			||||||
@@ -281,9 +277,7 @@ 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) {
 | 
				
			||||||
		double sw, sh;
 | 
							*natural = ceil(get_surface_dimensions(self).width);
 | 
				
			||||||
		get_surface_dimensions(self, &sw, &sh);
 | 
					 | 
				
			||||||
		*natural = ceil(sw);
 | 
					 | 
				
			||||||
		*minimum = 1;
 | 
							*minimum = 1;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		int dw, dh;
 | 
							int dw, dh;
 | 
				
			||||||
@@ -301,14 +295,13 @@ fiv_view_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
 | 
				
			|||||||
	if (!self->image || !self->scale_to_fit)
 | 
						if (!self->image || !self->scale_to_fit)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	double w, h;
 | 
						struct size surface_dimensions = get_surface_dimensions(self);
 | 
				
			||||||
	get_surface_dimensions(self, &w, &h);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	self->scale = 1;
 | 
						self->scale = 1;
 | 
				
			||||||
	if (ceil(w * self->scale) > allocation->width)
 | 
					
 | 
				
			||||||
		self->scale = allocation->width / w;
 | 
						if (ceil(surface_dimensions.width * self->scale) > allocation->width)
 | 
				
			||||||
	if (ceil(h * self->scale) > allocation->height)
 | 
							self->scale = allocation->width / surface_dimensions.width;
 | 
				
			||||||
		self->scale = allocation->height / h;
 | 
						if (ceil(surface_dimensions.height * self->scale) > allocation->height)
 | 
				
			||||||
 | 
							self->scale = allocation->height / surface_dimensions.height;
 | 
				
			||||||
	g_object_notify_by_pspec(G_OBJECT(widget), view_properties[PROP_SCALE]);
 | 
						g_object_notify_by_pspec(G_OBJECT(widget), view_properties[PROP_SCALE]);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -376,9 +369,7 @@ 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;
 | 
				
			||||||
@@ -387,7 +378,9 @@ 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.);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cairo_matrix_t matrix = get_orientation_matrix(self->orientation, sw, sh);
 | 
						struct size surface_dimensions = get_surface_dimensions(self);
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
@@ -478,6 +471,28 @@ 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)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -649,12 +664,11 @@ 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.;
 | 
				
			||||||
	double w = surface_width_px * scale, h = surface_height_px * scale;
 | 
						struct size surface_dimensions = get_surface_dimensions(self);
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
@@ -665,7 +679,7 @@ 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_width_px, surface_height_px);
 | 
							self->orientation, surface_dimensions.width, surface_dimensions.height);
 | 
				
			||||||
	cairo_pattern_set_matrix(cairo_get_source(cr), &matrix);
 | 
						cairo_pattern_set_matrix(cairo_get_source(cr), &matrix);
 | 
				
			||||||
	cairo_paint(cr);
 | 
						cairo_paint(cr);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -828,6 +842,11 @@ 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:
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user