/* GTK - The GIMP Toolkit * gtkpagesetup.c: Page Setup * Copyright (C) 2006, Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "config.h" #include "gtkpagesetup.h" #include "gtkprintutils.h" #include "gtkprintoperation.h" /* for GtkPrintError */ #include "gtkintl.h" #include "gtktypebuiltins.h" /** * GtkPageSetup: * * A `GtkPageSetup` object stores the page size, orientation and margins. * * The idea is that you can get one of these from the page setup dialog * and then pass it to the `GtkPrintOperation` when printing. * The benefit of splitting this out of the `GtkPrintSettings` is that * these affect the actual layout of the page, and thus need to be set * long before user prints. * * ## Margins * * The margins specified in this object are the “print margins”, i.e. the * parts of the page that the printer cannot print on. These are different * from the layout margins that a word processor uses; they are typically * used to determine the minimal size for the layout margins. * * To obtain a `GtkPageSetup` use [ctor@Gtk.PageSetup.new] to get the defaults, * or use [func@Gtk.print_run_page_setup_dialog] to show the page setup dialog * and receive the resulting page setup. * * ## A page setup dialog * * ```c * static GtkPrintSettings *settings = NULL; * static GtkPageSetup *page_setup = NULL; * * static void * do_page_setup (void) * { * GtkPageSetup *new_page_setup; * * if (settings == NULL) * settings = gtk_print_settings_new (); * * new_page_setup = gtk_print_run_page_setup_dialog (GTK_WINDOW (main_window), * page_setup, settings); * * if (page_setup) * g_object_unref (page_setup); * * page_setup = new_page_setup; * } * ``` */ #define KEYFILE_GROUP_NAME "Page Setup" typedef struct _GtkPageSetupClass GtkPageSetupClass; #define GTK_IS_PAGE_SETUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PAGE_SETUP)) #define GTK_PAGE_SETUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PAGE_SETUP, GtkPageSetupClass)) #define GTK_PAGE_SETUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PAGE_SETUP, GtkPageSetupClass)) struct _GtkPageSetup { GObject parent_instance; GtkPageOrientation orientation; GtkPaperSize *paper_size; /* These are stored in mm */ double top_margin, bottom_margin, left_margin, right_margin; }; struct _GtkPageSetupClass { GObjectClass parent_class; }; G_DEFINE_TYPE (GtkPageSetup, gtk_page_setup, G_TYPE_OBJECT) static void gtk_page_setup_finalize (GObject *object) { GtkPageSetup *setup = GTK_PAGE_SETUP (object); gtk_paper_size_free (setup->paper_size); G_OBJECT_CLASS (gtk_page_setup_parent_class)->finalize (object); } static void gtk_page_setup_init (GtkPageSetup *setup) { setup->paper_size = gtk_paper_size_new (NULL); setup->orientation = GTK_PAGE_ORIENTATION_PORTRAIT; setup->top_margin = gtk_paper_size_get_default_top_margin (setup->paper_size, GTK_UNIT_MM); setup->bottom_margin = gtk_paper_size_get_default_bottom_margin (setup->paper_size, GTK_UNIT_MM); setup->left_margin = gtk_paper_size_get_default_left_margin (setup->paper_size, GTK_UNIT_MM); setup->right_margin = gtk_paper_size_get_default_right_margin (setup->paper_size, GTK_UNIT_MM); } static void gtk_page_setup_class_init (GtkPageSetupClass *class) { GObjectClass *gobject_class = (GObjectClass *)class; gobject_class->finalize = gtk_page_setup_finalize; } /** * gtk_page_setup_new: * * Creates a new `GtkPageSetup`. * * Returns: a new `GtkPageSetup`. */ GtkPageSetup * gtk_page_setup_new (void) { return g_object_new (GTK_TYPE_PAGE_SETUP, NULL); } /** * gtk_page_setup_copy: * @other: the `GtkPageSetup` to copy * * Copies a `GtkPageSetup`. * * Returns: (transfer full): a copy of @other */ GtkPageSetup * gtk_page_setup_copy (GtkPageSetup *other) { GtkPageSetup *copy; copy = gtk_page_setup_new (); copy->orientation = other->orientation; gtk_paper_size_free (copy->paper_size); copy->paper_size = gtk_paper_size_copy (other->paper_size); copy->top_margin = other->top_margin; copy->bottom_margin = other->bottom_margin; copy->left_margin = other->left_margin; copy->right_margin = other->right_margin; return copy; } /** * gtk_page_setup_get_orientation: * @setup: a `GtkPageSetup` * * Gets the page orientation of the `GtkPageSetup`. * * Returns: the page orientation */ GtkPageOrientation gtk_page_setup_get_orientation (GtkPageSetup *setup) { return setup->orientation; } /** * gtk_page_setup_set_orientation: * @setup: a `GtkPageSetup` * @orientation: a `GtkPageOrientation` value * * Sets the page orientation of the `GtkPageSetup`. */ void gtk_page_setup_set_orientation (GtkPageSetup *setup, GtkPageOrientation orientation) { setup->orientation = orientation; } /** * gtk_page_setup_get_paper_size: * @setup: a `GtkPageSetup` * * Gets the paper size of the `GtkPageSetup`. * * Returns: (transfer none): the paper size */ GtkPaperSize * gtk_page_setup_get_paper_size (GtkPageSetup *setup) { g_return_val_if_fail (GTK_IS_PAGE_SETUP (setup), NULL); return setup->paper_size; } /** * gtk_page_setup_set_paper_size: * @setup: a `GtkPageSetup` * @size: a `GtkPaperSize` * * Sets the paper size of the `GtkPageSetup` without * changing the margins. * * See [method@Gtk.PageSetup.set_paper_size_and_default_margins]. */ void gtk_page_setup_set_paper_size (GtkPageSetup *setup, GtkPaperSize *size) { GtkPaperSize *old_size; g_return_if_fail (GTK_IS_PAGE_SETUP (setup)); g_return_if_fail (size != NULL); old_size = setup->paper_size; setup->paper_size = gtk_paper_size_copy (size); if (old_size) gtk_paper_size_free (old_size); } /** * gtk_page_setup_set_paper_size_and_default_margins: * @setup: a `GtkPageSetup` * @size: a `GtkPaperSize` * * Sets the paper size of the `GtkPageSetup` and modifies * the margins according to the new paper size. */ void gtk_page_setup_set_paper_size_and_default_margins (GtkPageSetup *setup, GtkPaperSize *size) { gtk_page_setup_set_paper_size (setup, size); setup->top_margin = gtk_paper_size_get_default_top_margin (setup->paper_size, GTK_UNIT_MM); setup->bottom_margin = gtk_paper_size_get_default_bottom_margin (setup->paper_size, GTK_UNIT_MM); setup->left_margin = gtk_paper_size_get_default_left_margin (setup->paper_size, GTK_UNIT_MM); setup->right_margin = gtk_paper_size_get_default_right_margin (setup->paper_size, GTK_UNIT_MM); } /** * gtk_page_setup_get_top_margin: * @setup: a `GtkPageSetup` * @unit: the unit for the return value * * Gets the top margin in units of @unit. * * Returns: the top margin */ double gtk_page_setup_get_top_margin (GtkPageSetup *setup, GtkUnit unit) { return _gtk_print_convert_from_mm (setup->top_margin, unit); } /** * gtk_page_setup_set_top_margin: * @setup: a `GtkPageSetup` * @margin: the new top margin in units of @unit * @unit: the units for @margin * * Sets the top margin of the `GtkPageSetup`. */ void gtk_page_setup_set_top_margin (GtkPageSetup *setup, double margin, GtkUnit unit) { setup->top_margin = _gtk_print_convert_to_mm (margin, unit); } /** * gtk_page_setup_get_bottom_margin: * @setup: a `GtkPageSetup` * @unit: the unit for the return value * * Gets the bottom margin in units of @unit. * * Returns: the bottom margin */ double gtk_page_setup_get_bottom_margin (GtkPageSetup *setup, GtkUnit unit) { return _gtk_print_convert_from_mm (setup->bottom_margin, unit); } /** * gtk_page_setup_set_bottom_margin: * @setup: a `GtkPageSetup` * @margin: the new bottom margin in units of @unit * @unit: the units for @margin * * Sets the bottom margin of the `GtkPageSetup`. */ void gtk_page_setup_set_bottom_margin (GtkPageSetup *setup, double margin, GtkUnit unit) { setup->bottom_margin = _gtk_print_convert_to_mm (margin, unit); } /** * gtk_page_setup_get_left_margin: * @setup: a `GtkPageSetup` * @unit: the unit for the return value * * Gets the left margin in units of @unit. * * Returns: the left margin */ double gtk_page_setup_get_left_margin (GtkPageSetup *setup, GtkUnit unit) { return _gtk_print_convert_from_mm (setup->left_margin, unit); } /** * gtk_page_setup_set_left_margin: * @setup: a `GtkPageSetup` * @margin: the new left margin in units of @unit * @unit: the units for @margin * * Sets the left margin of the `GtkPageSetup`. */ void gtk_page_setup_set_left_margin (GtkPageSetup *setup, double margin, GtkUnit unit) { setup->left_margin = _gtk_print_convert_to_mm (margin, unit); } /** * gtk_page_setup_get_right_margin: * @setup: a `GtkPageSetup` * @unit: the unit for the return value * * Gets the right margin in units of @unit. * * Returns: the right margin */ double gtk_page_setup_get_right_margin (GtkPageSetup *setup, GtkUnit unit) { return _gtk_print_convert_from_mm (setup->right_margin, unit); } /** * gtk_page_setup_set_right_margin: * @setup: a `GtkPageSetup` * @margin: the new right margin in units of @unit * @unit: the units for @margin * * Sets the right margin of the `GtkPageSetup`. */ void gtk_page_setup_set_right_margin (GtkPageSetup *setup, double margin, GtkUnit unit) { setup->right_margin = _gtk_print_convert_to_mm (margin, unit); } /** * gtk_page_setup_get_paper_width: * @setup: a `GtkPageSetup` * @unit: the unit for the return value * * Returns the paper width in units of @unit. * * Note that this function takes orientation, * but not margins into consideration. * See [method@Gtk.PageSetup.get_page_width]. * * Returns: the paper width. */ double gtk_page_setup_get_paper_width (GtkPageSetup *setup, GtkUnit unit) { if (setup->orientation == GTK_PAGE_ORIENTATION_PORTRAIT || setup->orientation == GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT) return gtk_paper_size_get_width (setup->paper_size, unit); else return gtk_paper_size_get_height (setup->paper_size, unit); } /** * gtk_page_setup_get_paper_height: * @setup: a `GtkPageSetup` * @unit: the unit for the return value * * Returns the paper height in units of @unit. * * Note that this function takes orientation, * but not margins into consideration. * See [method@Gtk.PageSetup.get_page_height]. * * Returns: the paper height. */ double gtk_page_setup_get_paper_height (GtkPageSetup *setup, GtkUnit unit) { if (setup->orientation == GTK_PAGE_ORIENTATION_PORTRAIT || setup->orientation == GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT) return gtk_paper_size_get_height (setup->paper_size, unit); else return gtk_paper_size_get_width (setup->paper_size, unit); } /** * gtk_page_setup_get_page_width: * @setup: a `GtkPageSetup` * @unit: the unit for the return value * * Returns the page width in units of @unit. * * Note that this function takes orientation * and margins into consideration. * See [method@Gtk.PageSetup.get_paper_width]. * * Returns: the page width. */ double gtk_page_setup_get_page_width (GtkPageSetup *setup, GtkUnit unit) { double width; width = gtk_page_setup_get_paper_width (setup, GTK_UNIT_MM); if (setup->orientation == GTK_PAGE_ORIENTATION_PORTRAIT || setup->orientation == GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT) width -= setup->left_margin + setup->right_margin; else width -= setup->top_margin + setup->bottom_margin; return _gtk_print_convert_from_mm (width, unit); } /** * gtk_page_setup_get_page_height: * @setup: a `GtkPageSetup` * @unit: the unit for the return value * * Returns the page height in units of @unit. * * Note that this function takes orientation * and margins into consideration. * See [method@Gtk.PageSetup.get_paper_height]. * * Returns: the page height. */ double gtk_page_setup_get_page_height (GtkPageSetup *setup, GtkUnit unit) { double height; height = gtk_page_setup_get_paper_height (setup, GTK_UNIT_MM); if (setup->orientation == GTK_PAGE_ORIENTATION_PORTRAIT || setup->orientation == GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT) height -= setup->top_margin + setup->bottom_margin; else height -= setup->left_margin + setup->right_margin; return _gtk_print_convert_from_mm (height, unit); } /** * gtk_page_setup_load_file: * @setup: a `GtkPageSetup` * @file_name: (type filename): the filename to read the page setup from * @error: (nullable): return location for an error * * Reads the page setup from the file @file_name. * * See [method@Gtk.PageSetup.to_file]. * * Returns: %TRUE on success */ gboolean gtk_page_setup_load_file (GtkPageSetup *setup, const char *file_name, GError **error) { gboolean retval = FALSE; GKeyFile *key_file; g_return_val_if_fail (GTK_IS_PAGE_SETUP (setup), FALSE); g_return_val_if_fail (file_name != NULL, FALSE); key_file = g_key_file_new (); if (g_key_file_load_from_file (key_file, file_name, 0, error) && gtk_page_setup_load_key_file (setup, key_file, NULL, error)) retval = TRUE; g_key_file_free (key_file); return retval; } /** * gtk_page_setup_new_from_file: * @file_name: (type filename): the filename to read the page setup from * @error: (nullable): return location for an error * * Reads the page setup from the file @file_name. * * Returns a new `GtkPageSetup` object with the restored * page setup, or %NULL if an error occurred. * See [method@Gtk.PageSetup.to_file]. * * Returns: the restored `GtkPageSetup` */ GtkPageSetup * gtk_page_setup_new_from_file (const char *file_name, GError **error) { GtkPageSetup *setup = gtk_page_setup_new (); if (!gtk_page_setup_load_file (setup, file_name, error)) { g_object_unref (setup); setup = NULL; } return setup; } /* something like this should really be in gobject! */ static guint string_to_enum (GType type, const char *enum_string) { GEnumClass *enum_class; const GEnumValue *value; guint retval = 0; g_return_val_if_fail (enum_string != NULL, 0); enum_class = g_type_class_ref (type); value = g_enum_get_value_by_nick (enum_class, enum_string); if (value) retval = value->value; g_type_class_unref (enum_class); return retval; } /** * gtk_page_setup_load_key_file: * @setup: a `GtkPageSetup` * @key_file: the `GKeyFile` to retrieve the page_setup from * @group_name: (nullable): the name of the group in the key_file to read * to use the default name “Page Setup” * @error: (nullable): return location for an error * * Reads the page setup from the group @group_name in the key file * @key_file. * * Returns: %TRUE on success */ gboolean gtk_page_setup_load_key_file (GtkPageSetup *setup, GKeyFile *key_file, const char *group_name, GError **error) { GtkPaperSize *paper_size; double top, bottom, left, right; char *orientation = NULL, *freeme = NULL; gboolean retval = FALSE; GError *err = NULL; g_return_val_if_fail (GTK_IS_PAGE_SETUP (setup), FALSE); g_return_val_if_fail (key_file != NULL, FALSE); if (!group_name) group_name = KEYFILE_GROUP_NAME; if (!g_key_file_has_group (key_file, group_name)) { g_set_error_literal (error, GTK_PRINT_ERROR, GTK_PRINT_ERROR_INVALID_FILE, _("Not a valid page setup file")); goto out; } #define GET_DOUBLE(kf, group, name, v) \ v = g_key_file_get_double (kf, group, name, &err); \ if (err != NULL) \ { \ g_propagate_error (error, err);\ goto out;\ } GET_DOUBLE (key_file, group_name, "MarginTop", top); GET_DOUBLE (key_file, group_name, "MarginBottom", bottom); GET_DOUBLE (key_file, group_name, "MarginLeft", left); GET_DOUBLE (key_file, group_name, "MarginRight", right); #undef GET_DOUBLE paper_size = gtk_paper_size_new_from_key_file (key_file, group_name, &err); if (!paper_size) { g_propagate_error (error, err); goto out; } gtk_page_setup_set_paper_size (setup, paper_size); gtk_paper_size_free (paper_size); gtk_page_setup_set_top_margin (setup, top, GTK_UNIT_MM); gtk_page_setup_set_bottom_margin (setup, bottom, GTK_UNIT_MM); gtk_page_setup_set_left_margin (setup, left, GTK_UNIT_MM); gtk_page_setup_set_right_margin (setup, right, GTK_UNIT_MM); orientation = g_key_file_get_string (key_file, group_name, "Orientation", NULL); if (orientation) { gtk_page_setup_set_orientation (setup, string_to_enum (GTK_TYPE_PAGE_ORIENTATION, orientation)); g_free (orientation); } retval = TRUE; out: g_free (freeme); return retval; } /** * gtk_page_setup_new_from_key_file: * @key_file: the `GKeyFile` to retrieve the page_setup from * @group_name: (nullable): the name of the group in the key_file to read * to use the default name “Page Setup” * @error: (nullable): return location for an error * * Reads the page setup from the group @group_name in the key file * @key_file. * * Returns a new `GtkPageSetup` object with the restored * page setup, or %NULL if an error occurred. * * Returns: the restored `GtkPageSetup` */ GtkPageSetup * gtk_page_setup_new_from_key_file (GKeyFile *key_file, const char *group_name, GError **error) { GtkPageSetup *setup = gtk_page_setup_new (); if (!gtk_page_setup_load_key_file (setup, key_file, group_name, error)) { g_object_unref (setup); setup = NULL; } return setup; } /** * gtk_page_setup_to_file: * @setup: a `GtkPageSetup` * @file_name: (type filename): the file to save to * @error: (nullable): return location for errors * * This function saves the information from @setup to @file_name. * * Returns: %TRUE on success */ gboolean gtk_page_setup_to_file (GtkPageSetup *setup, const char *file_name, GError **error) { GKeyFile *key_file; gboolean retval = FALSE; char *data = NULL; gsize len; g_return_val_if_fail (GTK_IS_PAGE_SETUP (setup), FALSE); g_return_val_if_fail (file_name != NULL, FALSE); key_file = g_key_file_new (); gtk_page_setup_to_key_file (setup, key_file, NULL); data = g_key_file_to_data (key_file, &len, error); if (!data) goto out; retval = g_file_set_contents (file_name, data, len, error); out: g_key_file_free (key_file); g_free (data); return retval; } /* something like this should really be in gobject! */ static char * enum_to_string (GType type, guint enum_value) { GEnumClass *enum_class; GEnumValue *value; char *retval = NULL; enum_class = g_type_class_ref (type); value = g_enum_get_value (enum_class, enum_value); if (value) retval = g_strdup (value->value_nick); g_type_class_unref (enum_class); return retval; } /** * gtk_page_setup_to_key_file: * @setup: a `GtkPageSetup` * @key_file: the `GKeyFile` to save the page setup to * @group_name: (nullable): the group to add the settings to in @key_file, * or %NULL to use the default name “Page Setup” * * This function adds the page setup from @setup to @key_file. */ void gtk_page_setup_to_key_file (GtkPageSetup *setup, GKeyFile *key_file, const char *group_name) { GtkPaperSize *paper_size; char *orientation; g_return_if_fail (GTK_IS_PAGE_SETUP (setup)); g_return_if_fail (key_file != NULL); if (!group_name) group_name = KEYFILE_GROUP_NAME; paper_size = gtk_page_setup_get_paper_size (setup); g_assert (paper_size != NULL); gtk_paper_size_to_key_file (paper_size, key_file, group_name); g_key_file_set_double (key_file, group_name, "MarginTop", gtk_page_setup_get_top_margin (setup, GTK_UNIT_MM)); g_key_file_set_double (key_file, group_name, "MarginBottom", gtk_page_setup_get_bottom_margin (setup, GTK_UNIT_MM)); g_key_file_set_double (key_file, group_name, "MarginLeft", gtk_page_setup_get_left_margin (setup, GTK_UNIT_MM)); g_key_file_set_double (key_file, group_name, "MarginRight", gtk_page_setup_get_right_margin (setup, GTK_UNIT_MM)); orientation = enum_to_string (GTK_TYPE_PAGE_ORIENTATION, gtk_page_setup_get_orientation (setup)); g_key_file_set_string (key_file, group_name, "Orientation", orientation); g_free (orientation); } /** * gtk_page_setup_to_gvariant: * @setup: a `GtkPageSetup` * * Serialize page setup to an a{sv} variant. * * Return: (transfer none): a new, floating, `GVariant` */ GVariant * gtk_page_setup_to_gvariant (GtkPageSetup *setup) { GtkPaperSize *paper_size; GVariant *variant; int i; GVariantBuilder builder; char *orientation; g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); paper_size = gtk_page_setup_get_paper_size (setup); variant = g_variant_ref_sink (gtk_paper_size_to_gvariant (paper_size)); for (i = 0; i < g_variant_n_children (variant); i++) g_variant_builder_add_value (&builder, g_variant_get_child_value (variant, i)); g_variant_unref (variant); g_variant_builder_add (&builder, "{sv}", "MarginTop", g_variant_new_double (gtk_page_setup_get_top_margin (setup, GTK_UNIT_MM))); g_variant_builder_add (&builder, "{sv}", "MarginBottom", g_variant_new_double (gtk_page_setup_get_bottom_margin (setup, GTK_UNIT_MM))); g_variant_builder_add (&builder, "{sv}", "MarginLeft", g_variant_new_double (gtk_page_setup_get_left_margin (setup, GTK_UNIT_MM))); g_variant_builder_add (&builder, "{sv}", "MarginRight", g_variant_new_double (gtk_page_setup_get_right_margin (setup, GTK_UNIT_MM))); orientation = enum_to_string (GTK_TYPE_PAGE_ORIENTATION, gtk_page_setup_get_orientation (setup)); g_variant_builder_add (&builder, "{sv}", "Orientation", g_variant_new_take_string (orientation)); return g_variant_builder_end (&builder); } /** * gtk_page_setup_new_from_gvariant: * @variant: an a{sv} `GVariant` * * Desrialize a page setup from an a{sv} variant. * * The variant must be in the format produced by * [method@Gtk.PageSetup.to_gvariant]. * * Returns: (transfer full): a new `GtkPageSetup` object */ GtkPageSetup * gtk_page_setup_new_from_gvariant (GVariant *variant) { GtkPageSetup *setup; const char *orientation; double margin; GtkPaperSize *paper_size; g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT), NULL); setup = gtk_page_setup_new (); paper_size = gtk_paper_size_new_from_gvariant (variant); if (paper_size) { gtk_page_setup_set_paper_size (setup, paper_size); gtk_paper_size_free (paper_size); } if (g_variant_lookup (variant, "MarginTop", "d", &margin)) gtk_page_setup_set_top_margin (setup, margin, GTK_UNIT_MM); if (g_variant_lookup (variant, "MarginBottom", "d", &margin)) gtk_page_setup_set_bottom_margin (setup, margin, GTK_UNIT_MM); if (g_variant_lookup (variant, "MarginLeft", "d", &margin)) gtk_page_setup_set_left_margin (setup, margin, GTK_UNIT_MM); if (g_variant_lookup (variant, "MarginRight", "d", &margin)) gtk_page_setup_set_right_margin (setup, margin, GTK_UNIT_MM); if (g_variant_lookup (variant, "Orientation", "&s", &orientation)) gtk_page_setup_set_orientation (setup, string_to_enum (GTK_TYPE_PAGE_ORIENTATION, orientation)); return setup; }