]> git.street.me.uk Git - andy/viking.git/commitdiff
New feature: map printing.
authorQuy Tonthat <qtonthat@gmail.com>
Wed, 7 Nov 2007 13:09:24 +0000 (13:09 +0000)
committerQuy Tonthat <qtonthat@gmail.com>
Wed, 7 Nov 2007 13:09:24 +0000 (13:09 +0000)
Signed-off-by: Quy Tonthat <qtonthat@gmail.com>
src/Makefile.am
src/menu.xml.h
src/print-preview.c [new file with mode: 0644]
src/print-preview.h [new file with mode: 0644]
src/print.c [new file with mode: 0644]
src/print.h [new file with mode: 0644]
src/viktrwlayer_propwin.c
src/vikwindow.c

index 27c74c1967657b60bcfe6bd49f110aeef85761ea..78741ccee91dab53738f0f639bc550a9a78d6d55 100644 (file)
@@ -64,7 +64,9 @@ viking_SOURCES = main.c \
        vikdemlayer_pixmap.h \
        vikfilelist.c vikfilelist.h \
        dems.c dems.h \
-       srtm_continent.c
+       srtm_continent.c \
+       print-preview.c print-preview.h \
+       print.c print.h
 
 if GOOGLE
 viking_SOURCES += \
index 287b14fbbf3e47cff54325c9ac10dce1a23d9df6..d385f1f4856f1d5491e75800d8692c63c5f12556 100644 (file)
@@ -21,6 +21,7 @@ static const char *menu_xml =
        "      <separator/>"
        "      <menuitem action='GenImg'/>"
        "      <menuitem action='GenImgDir'/>"
+       "      <menuitem action='Print'/>"
        "      <separator/>"
        "      <menuitem action='SaveExit'/>"
        "      <menuitem action='Exit'/>"
@@ -92,6 +93,7 @@ static const char *menu_xml =
        "      <toolitem name='New' action='New'/>"
        "      <toolitem name='Open' action='Open'/>"
        "      <toolitem name='Save' action='Save'/>"
+       "      <toolitem name='Print' action='Print'/>"
        "      <toolitem name='Exit' action='Exit'/>"
        "      <separator/>"
        "    </placeholder>"
diff --git a/src/print-preview.c b/src/print-preview.c
new file mode 100644 (file)
index 0000000..acfb99a
--- /dev/null
@@ -0,0 +1,588 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+/*
+ * Modified for viking by Quy Tonthat <qtonthat@gmail.com>
+ */
+
+#include <gtk/gtk.h>
+
+#include "print-preview.h"
+
+
+#define DRAWING_AREA_SIZE 200
+
+
+enum
+{
+  OFFSETS_CHANGED,
+  LAST_SIGNAL
+};
+
+
+static void      vik_print_preview_finalize         (GObject          *object);
+
+static void      vik_print_preview_size_allocate    (GtkWidget        *widget,
+                                                      GtkAllocation    *allocation,
+                                                      VikPrintPreview *preview);
+static void      vik_print_preview_realize          (GtkWidget        *widget);
+static gboolean  vik_print_preview_event            (GtkWidget        *widget,
+                                                      GdkEvent         *event,
+                                                      VikPrintPreview *preview);
+
+static gboolean  vik_print_preview_expose_event     (GtkWidget        *widget,
+                                                      GdkEventExpose   *eevent,
+                                                      VikPrintPreview *preview);
+
+static gdouble   vik_print_preview_get_scale        (VikPrintPreview *preview);
+
+static void      vik_print_preview_get_page_margins (VikPrintPreview *preview,
+                                                      gdouble          *left_margin,
+                                                      gdouble          *right_margin,
+                                                      gdouble          *top_margin,
+                                                      gdouble          *bottom_margin);
+
+static void      print_preview_queue_draw            (VikPrintPreview *preview);
+
+
+G_DEFINE_TYPE (VikPrintPreview, vik_print_preview, GTK_TYPE_ASPECT_FRAME)
+
+#define parent_class vik_print_preview_parent_class
+
+static guint vik_print_preview_signals[LAST_SIGNAL] = { 0 };
+
+
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+
+static void
+marshal_VOID__DOUBLE_DOUBLE (GClosure     *closure,
+                             GValue       *return_value,
+                             guint         n_param_values,
+                             const GValue *param_values,
+                             gpointer      invocation_hint,
+                             gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__DOUBLE_DOUBLE) (gpointer     data1,
+                                                    gdouble      arg_1,
+                                                    gdouble      arg_2,
+                                                    gpointer     data2);
+  register GMarshalFunc_VOID__DOUBLE_DOUBLE callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 3);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+
+  callback = (GMarshalFunc_VOID__DOUBLE_DOUBLE) (marshal_data ?
+                                                 marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_double (param_values + 1),
+            g_marshal_value_peek_double (param_values + 2),
+            data2);
+}
+
+static void
+vik_print_preview_class_init (VikPrintPreviewClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  vik_print_preview_signals[OFFSETS_CHANGED] =
+    g_signal_new ("offsets-changed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (VikPrintPreviewClass, offsets_changed),
+                  NULL, NULL,
+                  marshal_VOID__DOUBLE_DOUBLE,
+                  G_TYPE_NONE, 2,
+                  G_TYPE_DOUBLE,
+                  G_TYPE_DOUBLE);
+
+  object_class->finalize = vik_print_preview_finalize;
+
+  klass->offsets_changed = NULL;
+}
+
+static void
+vik_print_preview_init (VikPrintPreview *preview)
+{
+  preview->page               = NULL;
+  preview->pixbuf             = NULL;
+  preview->dragging           = FALSE;
+  preview->image_offset_x     = 0.0;
+  preview->image_offset_y     = 0.0;
+  preview->image_offset_x_max = 0.0;
+  preview->image_offset_y_max = 0.0;
+  preview->image_xres         = 230.0; // 1.0
+  preview->image_yres         = 230.0;
+  preview->use_full_page      = FALSE;
+
+  preview->area = gtk_drawing_area_new();
+  gtk_container_add (GTK_CONTAINER (preview), preview->area);
+  gtk_widget_show (preview->area);
+
+  gtk_widget_add_events (GTK_WIDGET (preview->area), GDK_BUTTON_PRESS_MASK);
+
+  g_signal_connect (preview->area, "size-allocate",
+                    G_CALLBACK (vik_print_preview_size_allocate),
+                    preview);
+  g_signal_connect (preview->area, "realize",
+                    G_CALLBACK (vik_print_preview_realize),
+                    NULL);
+  g_signal_connect (preview->area, "event",
+                    G_CALLBACK (vik_print_preview_event),
+                    preview);
+  g_signal_connect (preview->area, "expose-event",
+                    G_CALLBACK (vik_print_preview_expose_event),
+                    preview);
+}
+
+
+static void
+vik_print_preview_finalize (GObject *object)
+{
+  VikPrintPreview *preview = VIK_PRINT_PREVIEW (object);
+
+  if (preview->drawable)
+    {
+      preview->drawable = NULL;
+    }
+
+  if (preview->pixbuf)
+    {
+      g_object_unref (preview->pixbuf);
+      preview->pixbuf = NULL;
+    }
+
+  if (preview->page)
+    {
+      g_object_unref (preview->page);
+      preview->page = NULL;
+    }
+
+  G_OBJECT_CLASS (vik_print_preview_parent_class)->finalize (object);
+}
+
+/**
+ * vik_print_preview_new:
+ * @page: page setup
+ * @drawable_id: the drawable to print
+ *
+ * Creates a new #VikPrintPreview widget.
+ *
+ * Return value: the new #VikPrintPreview widget.
+ **/
+GtkWidget *
+vik_print_preview_new (GtkPageSetup *page,
+                        GdkDrawable        *drawable)
+{
+  VikPrintPreview *preview;
+  gfloat            ratio;
+
+  preview = g_object_new (VIK_TYPE_PRINT_PREVIEW, NULL);
+
+  preview->drawable = drawable;
+
+  if (page != NULL)
+    preview->page = gtk_page_setup_copy (page);
+  else
+    preview->page = gtk_page_setup_new ();
+
+  ratio = (gtk_page_setup_get_paper_width (preview->page, GTK_UNIT_POINTS) /
+           gtk_page_setup_get_paper_height (preview->page, GTK_UNIT_POINTS));
+
+  gtk_aspect_frame_set (GTK_ASPECT_FRAME (preview), 0.5, 0.5, ratio, FALSE);
+
+  gtk_widget_set_size_request (preview->area,
+                               DRAWING_AREA_SIZE, DRAWING_AREA_SIZE);
+
+  return GTK_WIDGET (preview);
+}
+
+/**
+ * vik_print_preview_set_image_dpi:
+ * @preview: a #VikPrintPreview.
+ * @xres: the X resolution
+ * @yres: the Y resolution
+ *
+ * Sets the resolution of the image/drawable displayed by the
+ * #VikPrintPreview.
+ **/
+void
+vik_print_preview_set_image_dpi (VikPrintPreview *preview,
+                                  gdouble           xres,
+                                  gdouble           yres)
+{
+  g_return_if_fail (VIK_IS_PRINT_PREVIEW (preview));
+
+  if (preview->image_xres != xres || preview->image_yres != yres)
+    {
+      preview->image_xres = xres;
+      preview->image_yres = yres;
+
+      print_preview_queue_draw (preview);
+    }
+}
+
+/**
+ * vik_print_preview_set_page_setup:
+ * @preview: a #VikPrintPreview.
+ * @page: the page setup to use
+ *
+ * Sets the page setup to use by the #VikPrintPreview.
+ **/
+void
+vik_print_preview_set_page_setup (VikPrintPreview *preview,
+                                   GtkPageSetup     *page)
+{
+  gfloat ratio;
+
+  if (preview->page)
+    g_object_unref (preview->page);
+
+  preview->page = gtk_page_setup_copy (page);
+
+  ratio = (gtk_page_setup_get_paper_width (page, GTK_UNIT_POINTS) /
+           gtk_page_setup_get_paper_height (page, GTK_UNIT_POINTS));
+
+  gtk_aspect_frame_set (GTK_ASPECT_FRAME (preview), 0.5, 0.5, ratio, FALSE);
+
+  print_preview_queue_draw (preview);
+}
+
+/**
+ * vik_print_preview_set_image_offsets:
+ * @preview: a #VikPrintPreview.
+ * @offset_x: the X offset
+ * @offset_y: the Y offset
+ *
+ * Sets the offsets of the image/drawable displayed by the #VikPrintPreview.
+ * It does not emit the "offsets-changed" signal.
+ **/
+void
+vik_print_preview_set_image_offsets (VikPrintPreview *preview,
+                                      gdouble           offset_x,
+                                      gdouble           offset_y)
+{
+  g_return_if_fail (VIK_IS_PRINT_PREVIEW (preview));
+
+  preview->image_offset_x = offset_x;
+  preview->image_offset_y = offset_y;
+
+  print_preview_queue_draw (preview);
+}
+
+/**
+ * vik_print_preview_set_image_offsets_max:
+ * @preview: a #VikPrintPreview.
+ * @offset_x_max: the maximum X offset allowed
+ * @offset_y_max: the maximum Y offset allowed
+ *
+ * Sets the maximum offsets of the image/drawable displayed by the
+ * #VikPrintPreview.  It does not emit the "offsets-changed" signal.
+ **/
+void
+vik_print_preview_set_image_offsets_max (VikPrintPreview *preview,
+                                          gdouble           offset_x_max,
+                                          gdouble           offset_y_max)
+{
+  g_return_if_fail (VIK_IS_PRINT_PREVIEW (preview));
+
+  preview->image_offset_x_max = offset_x_max;
+  preview->image_offset_y_max = offset_y_max;
+
+  print_preview_queue_draw (preview);
+}
+
+/**
+ * vik_print_preview_set_use_full_page:
+ * @preview: a #VikPrintPreview.
+ * @full_page: TRUE to ignore the page margins
+ *
+ * If @full_page is TRUE, the page margins are ignored and the full page
+ * can be used to setup printing.
+ **/
+void
+vik_print_preview_set_use_full_page (VikPrintPreview *preview,
+                                      gboolean          full_page)
+{
+  g_return_if_fail (VIK_IS_PRINT_PREVIEW (preview));
+
+  preview->use_full_page = full_page;
+
+  print_preview_queue_draw (preview);
+}
+
+static void
+vik_print_preview_realize (GtkWidget *widget)
+{
+  GdkCursor *cursor;
+
+  cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+                                       GDK_FLEUR);
+  gdk_window_set_cursor (widget->window, cursor);
+  gdk_cursor_unref (cursor);
+}
+
+static gboolean
+vik_print_preview_event (GtkWidget        *widget,
+                          GdkEvent         *event,
+                          VikPrintPreview *preview)
+{
+  static gdouble orig_offset_x = 0.0;
+  static gdouble orig_offset_y = 0.0;
+  static gint    start_x       = 0;
+  static gint    start_y       = 0;
+
+  gdouble        offset_x;
+  gdouble        offset_y;
+  gdouble        scale;
+
+  switch (event->type)
+    {
+    case GDK_BUTTON_PRESS:
+      gdk_pointer_grab (widget->window, FALSE,
+                        (GDK_BUTTON1_MOTION_MASK |
+                         GDK_BUTTON_RELEASE_MASK),
+                        NULL, NULL, event->button.time);
+
+      orig_offset_x = preview->image_offset_x;
+      orig_offset_y = preview->image_offset_y;
+
+      start_x = event->button.x;
+      start_y = event->button.y;
+
+      preview->dragging = TRUE;
+      break;
+
+    case GDK_MOTION_NOTIFY:
+      scale = vik_print_preview_get_scale (preview);
+
+      offset_x = (orig_offset_x + (event->motion.x - start_x) / scale);
+      offset_y = (orig_offset_y + (event->motion.y - start_y) / scale);
+
+      offset_x = CLAMP (offset_x, 0, preview->image_offset_x_max);
+      offset_y = CLAMP (offset_y, 0, preview->image_offset_y_max);
+
+      if (preview->image_offset_x != offset_x ||
+          preview->image_offset_y != offset_y)
+        {
+          vik_print_preview_set_image_offsets (preview, offset_x, offset_y);
+
+          g_signal_emit (preview,
+                         vik_print_preview_signals[OFFSETS_CHANGED], 0,
+                         preview->image_offset_x, preview->image_offset_y);
+        }
+      break;
+
+    case GDK_BUTTON_RELEASE:
+      gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
+                                  event->button.time);
+      start_x = start_y = 0;
+      preview->dragging = FALSE;
+
+      print_preview_queue_draw (preview);
+      break;
+
+    default:
+      break;
+    }
+
+  return FALSE;
+}
+
+static GdkPixbuf *get_thumbnail(GdkDrawable *drawable, gint thumb_width, gint thumb_height)
+{
+  gint width, height;
+  GdkPixbuf *pixbuf;
+  GdkPixbuf *thumbnail;
+
+  gdk_drawable_get_size(drawable, &width, &height);
+  pixbuf = gdk_pixbuf_get_from_drawable(NULL, drawable,
+                                           NULL, 0, 0, 0, 0, width, height);
+  thumbnail = gdk_pixbuf_scale_simple(pixbuf, thumb_width, thumb_height,
+                                      GDK_INTERP_BILINEAR);
+  g_object_unref(pixbuf);
+  return thumbnail;
+}
+
+static gboolean
+vik_print_preview_expose_event (GtkWidget        *widget,
+                                 GdkEventExpose   *eevent,
+                                 VikPrintPreview *preview)
+{
+  gdouble  paper_width;
+  gdouble  paper_height;
+  gdouble  left_margin;
+  gdouble  right_margin;
+  gdouble  top_margin;
+  gdouble  bottom_margin;
+  gdouble  scale;
+  cairo_t *cr;
+
+  paper_width = gtk_page_setup_get_paper_width (preview->page,
+                                                GTK_UNIT_POINTS);
+  paper_height = gtk_page_setup_get_paper_height (preview->page,
+                                                  GTK_UNIT_POINTS);
+  vik_print_preview_get_page_margins (preview,
+                                       &left_margin,
+                                       &right_margin,
+                                       &top_margin,
+                                       &bottom_margin);
+
+  cr = gdk_cairo_create (widget->window);
+
+  scale = vik_print_preview_get_scale (preview);
+
+  /* draw background */
+  cairo_scale (cr, scale, scale);
+  gdk_cairo_set_source_color (cr, &widget->style->white);
+  cairo_rectangle (cr, 0, 0, paper_width, paper_height);
+  cairo_fill (cr);
+
+  /* draw page_margins */
+  gdk_cairo_set_source_color (cr, &widget->style->black);
+  cairo_rectangle (cr,
+                   left_margin,
+                   top_margin,
+                   paper_width - left_margin - right_margin,
+                   paper_height - top_margin - bottom_margin);
+  cairo_stroke (cr);
+
+  if (preview->dragging)
+    {
+      gint width, height;
+      gdk_drawable_get_size(preview->drawable, &width, &height);
+      cairo_rectangle (cr,
+                       left_margin + preview->image_offset_x,
+                       top_margin  + preview->image_offset_y,
+                       (gdouble) width  * 72.0 / preview->image_xres,
+                       (gdouble) height * 72.0 / preview->image_yres);
+      cairo_stroke (cr);
+    }
+  else
+    {
+      GdkDrawable *drawable = preview->drawable;
+
+      /* draw image */
+      cairo_translate (cr,
+                       left_margin + preview->image_offset_x,
+                       top_margin  + preview->image_offset_y);
+
+      if (preview->pixbuf == NULL)
+        {
+          gint width  = MIN (widget->allocation.width, 1024);
+          gint height = MIN (widget->allocation.height, 1024);
+
+          preview->pixbuf = get_thumbnail(drawable, width, height);
+        }
+
+      if (preview->pixbuf != NULL)
+        {
+          gint width, height;
+          gdk_drawable_get_size(drawable, &width, &height);
+
+          gdouble scale_x = ((gdouble) width /
+                             gdk_pixbuf_get_width (preview->pixbuf));
+          gdouble scale_y = ((gdouble) height /
+                             gdk_pixbuf_get_height (preview->pixbuf));
+
+          scale_x = scale_x * 72.0 / preview->image_xres;
+          scale_y = scale_y * 72.0 / preview->image_yres;
+
+          cairo_scale (cr, scale_x, scale_y);
+
+          gdk_cairo_set_source_pixbuf (cr, preview->pixbuf, 0, 0);
+
+          cairo_paint (cr);
+        }
+    }
+
+  cairo_destroy (cr);
+
+  return FALSE;
+}
+
+static gdouble
+vik_print_preview_get_scale (VikPrintPreview* preview)
+{
+  gdouble scale_x;
+  gdouble scale_y;
+
+  scale_x = ((gdouble) preview->area->allocation.width /
+             gtk_page_setup_get_paper_width (preview->page, GTK_UNIT_POINTS));
+
+  scale_y = ((gdouble) preview->area->allocation.height /
+             gtk_page_setup_get_paper_height (preview->page, GTK_UNIT_POINTS));
+
+  return MIN (scale_x, scale_y);
+}
+
+static void
+vik_print_preview_size_allocate (GtkWidget        *widget,
+                                  GtkAllocation    *allocation,
+                                  VikPrintPreview *preview)
+{
+  if (preview->pixbuf != NULL)
+    {
+      g_object_unref (preview->pixbuf);
+      preview->pixbuf = NULL;
+    }
+}
+
+static void
+vik_print_preview_get_page_margins (VikPrintPreview *preview,
+                                     gdouble          *left_margin,
+                                     gdouble          *right_margin,
+                                     gdouble          *top_margin,
+                                     gdouble          *bottom_margin)
+{
+  if (preview->use_full_page)
+    {
+      *left_margin   = 0.0;
+      *right_margin  = 0.0;
+      *top_margin    = 0.0;
+      *bottom_margin = 0.0;
+    }
+  else
+    {
+      *left_margin   = gtk_page_setup_get_left_margin (preview->page,
+                                                       GTK_UNIT_POINTS);
+      *right_margin  = gtk_page_setup_get_right_margin (preview->page,
+                                                        GTK_UNIT_POINTS);
+      *top_margin    = gtk_page_setup_get_top_margin (preview->page,
+                                                      GTK_UNIT_POINTS);
+      *bottom_margin = gtk_page_setup_get_bottom_margin (preview->page,
+                                                         GTK_UNIT_POINTS);
+    }
+}
+
+static void
+print_preview_queue_draw (VikPrintPreview *preview)
+{
+  gtk_widget_queue_draw (GTK_WIDGET (preview->area));
+}
diff --git a/src/print-preview.h b/src/print-preview.h
new file mode 100644 (file)
index 0000000..623ff5b
--- /dev/null
@@ -0,0 +1,91 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __VIK_PRINT_PREVIEW_H__
+#define __VIK_PRINT_PREVIEW_H__
+
+G_BEGIN_DECLS
+
+
+#define VIK_TYPE_PRINT_PREVIEW            (vik_print_preview_get_type ())
+#define VIK_PRINT_PREVIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIK_TYPE_PRINT_PREVIEW, VikPrintPreview))
+#define VIK_PRINT_PREVIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), VIK_TYPE_PRINT_PREVIEW, VikPrintPreviewClass))
+#define VIK_IS_PRINT_PREVIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIK_TYPE_PRINT_PREVIEW))
+#define VIK_IS_PRINT_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VIK_TYPE_PRINT_PREVIEW))
+#define VIK_PRINT_PREVIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), VIK_TYPE_PRINT_PREVIEW, VikPrintPreviewClass))
+
+typedef struct _VikPrintPreview       VikPrintPreview;
+typedef struct _VikPrintPreviewClass  VikPrintPreviewClass;
+
+struct _VikPrintPreview
+{
+  GtkAspectFrame  parent_instance;
+
+  GtkWidget      *area;
+  GtkPageSetup   *page;
+  GdkPixbuf      *pixbuf;
+  gboolean        dragging;
+
+  GdkDrawable    *drawable;
+
+  gdouble         image_offset_x;
+  gdouble         image_offset_y;
+  gdouble         image_offset_x_max;
+  gdouble         image_offset_y_max;
+  gdouble         image_xres;
+  gdouble         image_yres;
+
+  gboolean        use_full_page;
+};
+
+struct _VikPrintPreviewClass
+{
+  GtkAspectFrameClass  parent_class;
+
+  void (* offsets_changed)  (VikPrintPreview *print_preview,
+                             gint              offset_x,
+                             gint              offset_y);
+};
+
+
+GType       vik_print_preview_get_type          (void) G_GNUC_CONST;
+
+GtkWidget * vik_print_preview_new               (GtkPageSetup     *page,
+                                                  GdkDrawable     *drawable);
+
+void        vik_print_preview_set_image_dpi     (VikPrintPreview *preview,
+                                                  gdouble           xres,
+                                                  gdouble           yres);
+
+void        vik_print_preview_set_page_setup    (VikPrintPreview *preview,
+                                                  GtkPageSetup     *page);
+
+void        vik_print_preview_set_image_offsets (VikPrintPreview *preview,
+                                                  gdouble           offset_x,
+                                                  gdouble           offset_y);
+
+void        vik_print_preview_set_image_offsets_max (VikPrintPreview *preview,
+                                                      gdouble           offset_x_max,
+                                                      gdouble           offset_y_max);
+
+void        vik_print_preview_set_use_full_page (VikPrintPreview *preview,
+                                                  gboolean          full_page);
+
+G_END_DECLS
+
+#endif /* __VIK_PRINT_PREVIEW_H__ */
diff --git a/src/print.c b/src/print.c
new file mode 100644 (file)
index 0000000..d6fb00c
--- /dev/null
@@ -0,0 +1,666 @@
+/*
+ * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
+ *
+ * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
+ *
+ * print.c
+ * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include <string.h>
+#include <glib/gprintf.h>
+#include <gtk/gtk.h>
+
+#include "viking.h"
+#include "print.h"
+#include "print-preview.h"
+
+typedef enum
+{
+  VIK_PRINT_CENTER_NONE         = 0,
+  VIK_PRINT_CENTER_HORIZONTALLY,
+  VIK_PRINT_CENTER_VERTICALLY,
+  VIK_PRINT_CENTER_BOTH,
+} PrintCenterMode;
+
+typedef struct {
+  gchar *name;
+  PrintCenterMode mode;
+} PrintCenterName;
+
+static const PrintCenterName center_modes[] = {
+  {"None",          VIK_PRINT_CENTER_NONE},
+  {"Horizontally",  VIK_PRINT_CENTER_HORIZONTALLY},
+  {"Vertically",    VIK_PRINT_CENTER_VERTICALLY},
+  {"Both",          VIK_PRINT_CENTER_BOTH},
+  {NULL,            -1}
+};
+
+typedef struct {
+  gint                num_pages;
+  gboolean            show_info_header;
+  VikWindow           *vw;
+  VikViewport         *vvp;
+  gdouble             xmpp, ympp;  /* zoom level (meters/pixel) */
+  gdouble             xres;
+  gdouble             yres;
+  gint                width;
+  gint                height;
+  gdouble             offset_x;
+  gdouble             offset_y;
+  PrintCenterMode     center;
+  gboolean            use_full_page;
+  GtkPrintOperation  *operation;
+} PrintData;
+
+static GtkWidget *create_custom_widget_cb(GtkPrintOperation *operation, PrintData *data);
+static void begin_print(GtkPrintOperation *operation, GtkPrintContext *context, PrintData *data);
+static void draw_page(GtkPrintOperation *print, GtkPrintContext *context, gint page_nr, PrintData *data);
+static void end_print(GtkPrintOperation *operation, GtkPrintContext *context,  PrintData *data);
+
+void a_print(VikWindow *vw, VikViewport *vvp)
+{
+  /* TODO: make print_settings non-static when saving_settings_to_file is
+   * implemented. Keep it static for now to retain settings for each
+   * viking session
+   */
+  static GtkPrintSettings *print_settings = NULL;
+
+  GtkPrintOperation *print_oper;
+  GtkPrintOperationResult res;
+  PrintData data;
+
+  print_oper = gtk_print_operation_new ();
+
+  data.num_pages     = 1;
+  data.vw            = vw;
+  data.vvp           = vvp;
+  data.offset_x      = 0;
+  data.offset_y      = 0;
+  data.center        = VIK_PRINT_CENTER_BOTH;
+  data.use_full_page = FALSE;
+  data.operation     = print_oper;
+
+  data.xmpp          = vik_viewport_get_xmpp(vvp);
+  data.ympp          = vik_viewport_get_ympp(vvp);
+  data.width         = vik_viewport_get_width(vvp);
+  data.height        = vik_viewport_get_height(vvp);
+
+  data.xres = data.yres = 230;   /* FIXME */
+
+  if (print_settings != NULL) 
+    gtk_print_operation_set_print_settings (print_oper, print_settings);
+
+  g_signal_connect (print_oper, "begin_print", G_CALLBACK (begin_print), &data);
+  g_signal_connect (print_oper, "draw_page", G_CALLBACK (draw_page), &data);
+  g_signal_connect (print_oper, "end-print", G_CALLBACK (end_print), &data);
+  g_signal_connect (print_oper, "create-custom-widget", G_CALLBACK (create_custom_widget_cb), &data);
+
+  gtk_print_operation_set_custom_tab_label (print_oper, "Image Settings");
+
+  res = gtk_print_operation_run (print_oper,
+                                 GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
+                                 GTK_WINDOW (vw), NULL);
+
+  if (res == GTK_PRINT_OPERATION_RESULT_APPLY) {
+    if (print_settings != NULL)
+      g_object_unref (print_settings);
+    print_settings = g_object_ref (gtk_print_operation_get_print_settings (print_oper));
+  }
+
+  g_object_unref (print_oper);
+}
+
+static void begin_print(GtkPrintOperation *operation,
+                        GtkPrintContext   *context,
+                        PrintData         *data)
+{
+  // fputs("DEBUG: begin_print() called\n", stderr);
+  gtk_print_operation_set_n_pages (operation, data->num_pages);
+  gtk_print_operation_set_use_full_page (operation, data->use_full_page);
+
+}
+
+static void end_print(GtkPrintOperation *operation,
+                      GtkPrintContext   *context,
+                      PrintData *data)
+{
+  // fputs("DEBUG: end_print() called\n", stderr);
+
+}
+
+static void copy_row_from_rgb(guchar *surface_pixels, guchar *pixbuf_pixels, gint width)
+{
+  guint32 *cairo_data = (guint32 *) surface_pixels;
+  guchar  *p;
+  gint     i;
+
+  for (i = 0, p = pixbuf_pixels; i < width; i++) {
+    guint32 r = *p++;
+    guint32 g = *p++;
+    guint32 b = *p++;
+    cairo_data[i] = 0xFF000000 | (r << 16) | (g << 8) | b;
+  }
+}
+
+#define INT_MULT(a,b,t)  ((t) = (a) * (b) + 0x80, ((((t) >> 8) + (t)) >> 8))
+#define INT_BLEND(a,b,alpha,tmp)  (INT_MULT((a) - (b), alpha, tmp) + (b))
+static void copy_row_from_rgba(guchar *surface_pixels, guchar *pixbuf_pixels, gint width)
+{
+  guint32 *cairo_data = (guint32 *) surface_pixels;
+  guchar  *p;
+  gint     i;
+
+  for (i = 0, p = pixbuf_pixels; i < width; i++) {
+    guint32 r = *p++;
+    guint32 g = *p++;
+    guint32 b = *p++;
+    guint32 a = *p++;
+
+    if (a != 255) {
+      guint32 tmp;
+      /* composite on a white background */
+      r = INT_BLEND (r, 255, a, tmp);
+      g = INT_BLEND (g, 255, a, tmp);
+      b = INT_BLEND (b, 255, a, tmp);
+    }
+    cairo_data[i] = 0xFF000000 | (r << 16) | (g << 8) | b;
+  }
+}
+
+static void draw_page_cairo(GtkPrintContext *context, PrintData *data)
+{
+  cairo_t         *cr;
+  GdkPixbuf       *pixbuf_to_draw; 
+  cairo_surface_t *surface;
+  guchar          *surface_pixels;
+  guchar          *pixbuf_pixels;
+  gint             stride;
+  gint             pixbuf_stride;
+  gint             pixbuf_n_channels;
+  gdouble          cr_width;
+  gdouble          cr_height;
+  gdouble          cr_dpi_x;
+  gdouble          cr_dpi_y;
+  gdouble          scale_x;
+  gdouble          scale_y;
+  gint             y;
+
+  cr = gtk_print_context_get_cairo_context(context);
+  pixbuf_to_draw = gdk_pixbuf_get_from_drawable(NULL,
+                               GDK_DRAWABLE(vik_viewport_get_pixmap(data->vvp)),
+                               NULL, 0, 0, 0, 0, data->width, data->height);
+  surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
+                                       data->width, data->height);
+  
+  cr_width  = gtk_print_context_get_width  (context);
+  cr_height = gtk_print_context_get_height (context);
+  cr_dpi_x  = gtk_print_context_get_dpi_x  (context);
+  cr_dpi_y  = gtk_print_context_get_dpi_y  (context);
+
+  scale_x = cr_dpi_x / data->xres;
+  scale_y = cr_dpi_y / data->yres;
+
+  cairo_translate (cr,
+                   data->offset_x / cr_dpi_x * 72.0,
+                   data->offset_y / cr_dpi_y * 72.0);
+  cairo_scale (cr, scale_x, scale_y);
+
+  surface_pixels = cairo_image_surface_get_data (surface);
+  stride = cairo_image_surface_get_stride (surface);
+  pixbuf_pixels = gdk_pixbuf_get_pixels (pixbuf_to_draw);
+  pixbuf_stride = gdk_pixbuf_get_rowstride(pixbuf_to_draw);
+  pixbuf_n_channels = gdk_pixbuf_get_n_channels(pixbuf_to_draw);
+
+  // fprintf(stderr, "DEBUG: %s() surface_pixels=%p pixbuf_pixels=%p size=%d surface_width=%d surface_height=%d stride=%d data_height=%d pixmap_stride=%d pixmap_nchannels=%d pixmap_bit_per_Sample=%d\n", __PRETTY_FUNCTION__, surface_pixels, pixbuf_pixels, stride * data->height, cairo_image_surface_get_width(surface), cairo_image_surface_get_height(surface), stride, data->height, gdk_pixbuf_get_rowstride(pixbuf_to_draw), gdk_pixbuf_get_n_channels(pixbuf_to_draw), gdk_pixbuf_get_bits_per_sample(pixbuf_to_draw));
+
+  /* Assume the pixbuf has 8 bits per channel */
+  for (y = 0; y < data->height; y++, surface_pixels += stride, pixbuf_pixels += pixbuf_stride) {
+    switch (pixbuf_n_channels) {
+      case 3:
+        copy_row_from_rgb (surface_pixels, pixbuf_pixels, data->width);
+        break;
+      case 4:
+        copy_row_from_rgba (surface_pixels, pixbuf_pixels, data->width);
+        break;
+    }
+  }
+
+  g_object_unref(G_OBJECT(pixbuf_to_draw));
+
+  cairo_set_source_surface(cr, surface, 0, 0);
+  cairo_rectangle(cr, 0, 0, data->width, data->height);
+  cairo_fill(cr);
+  cairo_surface_destroy(surface);
+}
+
+static void draw_page(GtkPrintOperation *print,
+                      GtkPrintContext   *context,
+                      gint               page_nr,
+                      PrintData         *data)
+{
+  // fprintf(stderr, "DEBUG: draw_page() page_nr=%d\n", page_nr);
+  draw_page_cairo(context, data);
+
+}
+
+/*********************** page layout gui *********************/
+typedef struct
+{
+  PrintData       *data;
+  GtkWidget       *center_combo;
+  GtkWidget       *scale;
+  GtkWidget       *scale_label;
+  GtkWidget       *preview;
+} CustomWidgetInfo;
+
+enum
+{
+  BOTTOM,
+  TOP,
+  RIGHT,
+  LEFT,
+  WIDTH,
+  HEIGHT
+};
+
+static gboolean scale_change_value_cb(GtkRange *range, GtkScrollType scroll, gdouble value, CustomWidgetInfo *pinfo);
+static void get_page_dimensions (CustomWidgetInfo *info, gdouble *page_width, gdouble *page_height, GtkUnit unit);
+static void center_changed_cb (GtkWidget *combo, CustomWidgetInfo *info);
+static void get_max_offsets (CustomWidgetInfo *info, gdouble *offset_x_max, gdouble *offset_y_max);
+static void update_offsets (CustomWidgetInfo *info);
+
+static void set_scale_label(CustomWidgetInfo *pinfo, gdouble scale_val)
+{
+  static const gdouble inch_to_mm = 25.4;
+  gchar label_text[64];
+
+  g_snprintf(label_text, sizeof(label_text), "<i>%.0fx%0.f mm (%.0f%%)</i>",
+      inch_to_mm * pinfo->data->width / pinfo->data->xres,
+      inch_to_mm * pinfo->data->height / pinfo->data->yres,
+      scale_val);
+  gtk_label_set_markup (GTK_LABEL (pinfo->scale_label), label_text);
+}
+
+static void set_scale_value(CustomWidgetInfo *pinfo)
+{
+  gdouble width;
+  gdouble height;
+  gdouble ratio, ratio_w, ratio_h;
+
+
+  get_page_dimensions (pinfo, &width, &height, GTK_UNIT_INCH);
+  ratio_w = 100 * pinfo->data->width / pinfo->data->xres / width;
+  ratio_h = 100 * pinfo->data->height / pinfo->data->yres / height;
+
+  ratio = MAX(ratio_w, ratio_h);
+  g_signal_handlers_block_by_func(GTK_RANGE(pinfo->scale), scale_change_value_cb, pinfo);
+  gtk_range_set_value(GTK_RANGE(pinfo->scale), ratio);
+  g_signal_handlers_unblock_by_func(GTK_RANGE(pinfo->scale), scale_change_value_cb, pinfo);
+  set_scale_label(pinfo, ratio);
+}
+
+static void update_page_setup (CustomWidgetInfo *pinfo)
+{
+  gdouble paper_width;
+  gdouble paper_height;
+  gdouble offset_x_max, offset_y_max;
+  PrintData    *data = pinfo->data;
+
+  get_page_dimensions (pinfo, &paper_width, &paper_height, GTK_UNIT_INCH);
+  if ((paper_width < (pinfo->data->width / data->xres)) ||
+      (paper_height < (pinfo->data->height / data->yres))) {
+    gdouble xres, yres;
+    xres = (gdouble) pinfo->data->width / paper_width;
+    yres = (gdouble) pinfo->data->height / paper_height;
+    data->xres = data->yres = MAX(xres, yres);
+    vik_print_preview_set_image_dpi (VIK_PRINT_PREVIEW (pinfo->preview),
+                                  data->xres, data->yres);
+  }
+  get_max_offsets (pinfo, &offset_x_max, &offset_y_max);
+  vik_print_preview_set_image_offsets_max (VIK_PRINT_PREVIEW (pinfo->preview),
+                                            offset_x_max, offset_y_max);
+  update_offsets (pinfo);
+  set_scale_value(pinfo);
+  if (pinfo->preview)
+    vik_print_preview_set_image_offsets (VIK_PRINT_PREVIEW (pinfo->preview),
+                                     pinfo->data->offset_x, pinfo->data->offset_y);
+
+}
+
+static void page_setup_cb (GtkWidget *widget, CustomWidgetInfo *info)
+{
+  PrintData *data = info->data;
+  GtkPrintOperation *operation = data->operation;
+  GtkPrintSettings  *settings;
+  GtkPageSetup      *page_setup;
+  GtkWidget         *toplevel;
+
+  toplevel = gtk_widget_get_toplevel (widget);
+  if (! GTK_WIDGET_TOPLEVEL (toplevel))
+    toplevel = NULL;
+
+  settings = gtk_print_operation_get_print_settings (operation);
+  if (! settings)
+    settings = gtk_print_settings_new ();
+
+  page_setup = gtk_print_operation_get_default_page_setup (operation);
+
+  page_setup = gtk_print_run_page_setup_dialog (GTK_WINDOW (toplevel),
+                                                page_setup, settings);
+
+  gtk_print_operation_set_default_page_setup (operation, page_setup);
+
+  vik_print_preview_set_page_setup (VIK_PRINT_PREVIEW (info->preview),
+                                     page_setup);
+
+  update_page_setup (info);
+
+}
+
+static void full_page_toggled_cb (GtkWidget *widget, CustomWidgetInfo *pinfo)
+{
+  gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+
+  pinfo->data->use_full_page = active;
+  update_page_setup (pinfo);
+  vik_print_preview_set_use_full_page (VIK_PRINT_PREVIEW(pinfo->preview),
+                                        active);
+}
+
+static void set_center_none (CustomWidgetInfo *info)
+{
+  info->data->center = VIK_PRINT_CENTER_NONE;
+
+  if (info->center_combo) {
+    g_signal_handlers_block_by_func (info->center_combo,
+                                       center_changed_cb, info);
+
+    info->data->center = VIK_PRINT_CENTER_NONE;
+    gtk_combo_box_set_active(GTK_COMBO_BOX(info->center_combo), info->data->center);
+    g_signal_handlers_unblock_by_func (info->center_combo,
+                                         center_changed_cb, info);
+  }
+}
+
+static void preview_offsets_changed_cb (GtkWidget *widget,
+                                        gdouble    offset_x, gdouble    offset_y,
+                                        CustomWidgetInfo *info)
+{
+  set_center_none (info);
+
+  info->data->offset_x = offset_x;
+  info->data->offset_y = offset_y;
+
+  update_offsets (info);
+
+}
+
+static void get_page_dimensions (CustomWidgetInfo *info,
+                                     gdouble       *page_width,
+                                     gdouble       *page_height,
+                                     GtkUnit        unit)
+{
+  GtkPageSetup *setup;
+
+  setup = gtk_print_operation_get_default_page_setup (info->data->operation);
+
+  *page_width = gtk_page_setup_get_paper_width (setup, unit);
+  *page_height = gtk_page_setup_get_paper_height (setup, unit);
+
+  if (!info->data->use_full_page) {
+    gdouble left_margin = gtk_page_setup_get_left_margin (setup, unit);
+    gdouble right_margin = gtk_page_setup_get_right_margin (setup, unit);
+    gdouble top_margin = gtk_page_setup_get_top_margin (setup, unit);
+    gdouble bottom_margin = gtk_page_setup_get_bottom_margin (setup, unit);
+
+    *page_width -= left_margin + right_margin;
+    *page_height -= top_margin + bottom_margin;
+  }
+
+}
+
+static void get_max_offsets (CustomWidgetInfo *info,
+                                       gdouble *offset_x_max,
+                                       gdouble *offset_y_max)
+{
+  gdouble width;
+  gdouble height;
+
+  get_page_dimensions (info, &width, &height, GTK_UNIT_POINTS);
+
+  *offset_x_max = width - 72.0 * info->data->width / info->data->xres;
+  *offset_x_max = MAX (0, *offset_x_max);
+
+  *offset_y_max = height - 72.0 * info->data->height / info->data->yres;
+  *offset_y_max = MAX (0, *offset_y_max);
+}
+
+static void update_offsets (CustomWidgetInfo *info)
+{
+  PrintData *data = info->data;
+  gdouble    offset_x_max;
+  gdouble    offset_y_max;
+
+  get_max_offsets (info, &offset_x_max, &offset_y_max);
+
+  switch (data->center) {
+    case VIK_PRINT_CENTER_NONE:
+      if (data->offset_x > offset_x_max)
+        data->offset_x = offset_x_max;
+      if (data->offset_y > offset_y_max)
+        data->offset_y = offset_y_max;
+      break;
+
+    case VIK_PRINT_CENTER_HORIZONTALLY:
+      data->offset_x = offset_x_max / 2.0;
+      break;
+
+    case VIK_PRINT_CENTER_VERTICALLY:
+      data->offset_y = offset_y_max / 2.0;
+      break;
+
+    case VIK_PRINT_CENTER_BOTH:
+      data->offset_x = offset_x_max / 2.0;
+      data->offset_y = offset_y_max / 2.0;
+      break;
+    }
+}
+
+static void center_changed_cb (GtkWidget *combo, CustomWidgetInfo *info)
+{
+  info->data->center = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
+  update_offsets (info);
+
+  if (info->preview)
+    vik_print_preview_set_image_offsets (VIK_PRINT_PREVIEW (info->preview),
+                                     info->data->offset_x, info->data->offset_y);
+}
+
+static gboolean scale_change_value_cb(GtkRange     *range,
+                                 GtkScrollType scroll,
+                                 gdouble       value,
+                                 CustomWidgetInfo  *pinfo)
+{
+  gdouble paper_width;
+  gdouble paper_height;
+  gdouble xres, yres, res;
+  gdouble offset_x_max, offset_y_max;
+  gdouble scale = CLAMP(value, 1, 100);
+
+  get_page_dimensions (pinfo, &paper_width, &paper_height, GTK_UNIT_INCH);
+  xres = pinfo->data->width * 100 / paper_width / scale;
+  yres = pinfo->data->height * 100 / paper_height / scale;
+  res = MAX(xres, yres);
+  pinfo->data->xres = pinfo->data->yres = res;
+  get_max_offsets (pinfo, &offset_x_max, &offset_y_max);
+  update_offsets (pinfo);
+  if (pinfo->preview) {
+    vik_print_preview_set_image_dpi (VIK_PRINT_PREVIEW (pinfo->preview),
+                                  pinfo->data->xres, pinfo->data->yres);
+    vik_print_preview_set_image_offsets (VIK_PRINT_PREVIEW (pinfo->preview),
+                                  pinfo->data->offset_x, pinfo->data->offset_y);
+    vik_print_preview_set_image_offsets_max (VIK_PRINT_PREVIEW (pinfo->preview),
+                                            offset_x_max, offset_y_max);
+  }
+
+  set_scale_label(pinfo, scale);
+
+  return FALSE;
+}
+
+static void custom_widgets_cleanup(CustomWidgetInfo *info)
+{
+  g_free(info);
+}
+
+static GtkWidget *create_custom_widget_cb(GtkPrintOperation *operation, PrintData *data)
+{
+  GtkWidget    *layout;
+  GtkWidget    *main_hbox;
+  GtkWidget    *main_vbox;
+  GtkWidget    *hbox;
+  GtkWidget    *vbox;
+  GtkWidget    *button;
+  GtkWidget    *label;
+  GtkPageSetup *setup;
+
+  CustomWidgetInfo  *info = g_malloc0(sizeof(CustomWidgetInfo));
+  g_signal_connect_swapped (data->operation, "done", G_CALLBACK (custom_widgets_cleanup), info);
+
+
+  info->data = data;
+
+  setup = gtk_print_operation_get_default_page_setup (data->operation);
+  if (! setup) {
+    setup = gtk_page_setup_new ();
+    gtk_print_operation_set_default_page_setup (data->operation, setup);
+  }
+
+  layout = gtk_vbox_new (FALSE, 6);
+  gtk_container_set_border_width (GTK_CONTAINER (layout), 12);
+
+  /*  main hbox  */
+  main_hbox = gtk_hbox_new (FALSE, 12);
+  gtk_box_pack_start (GTK_BOX (layout), main_hbox, TRUE, TRUE, 0);
+  gtk_widget_show (main_hbox);
+
+  /*  main vbox  */
+  main_vbox = gtk_vbox_new (FALSE, 12);
+  gtk_box_pack_start (GTK_BOX (main_hbox), main_vbox, FALSE, FALSE, 0);
+  gtk_widget_show (main_vbox);
+
+  vbox = gtk_vbox_new (FALSE, 6);
+  gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0);
+  gtk_widget_show (vbox);
+
+  /* Page Size */
+  button = gtk_button_new_with_mnemonic ("_Adjust Page Size "
+                                           "and Orientation");
+  gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
+  g_signal_connect (G_OBJECT (button), "clicked",
+                    G_CALLBACK (page_setup_cb),
+                    info);
+  gtk_widget_show (button);
+
+  /* Center */
+  GtkWidget *combo;
+  const PrintCenterName *center;
+
+  hbox = gtk_hbox_new (FALSE, 6);
+  gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
+  gtk_widget_show (hbox);
+
+  label = gtk_label_new_with_mnemonic ("C_enter:");
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+  gtk_widget_show (label);
+
+  combo = gtk_combo_box_new_text ();
+  for (center = center_modes; center->name; center++) {
+    gtk_combo_box_append_text(GTK_COMBO_BOX(combo), center->name);
+  }
+  gtk_combo_box_set_active(GTK_COMBO_BOX(combo), VIK_PRINT_CENTER_BOTH);
+  gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
+  gtk_widget_show (combo);
+  gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+  g_signal_connect(combo, "changed",
+                   G_CALLBACK(center_changed_cb), info);
+  info->center_combo = combo;
+
+  /* ignore page margins */
+  button = gtk_check_button_new_with_mnemonic ("Ignore Page _Margins");
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+                                data->use_full_page);
+  gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
+  g_signal_connect (button, "toggled",
+                    G_CALLBACK (full_page_toggled_cb),
+                    info);
+  gtk_widget_show (button);
+
+  /* scale */
+  vbox = gtk_vbox_new (FALSE, 1);
+  gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0);
+  gtk_widget_show (vbox);
+
+  hbox = gtk_hbox_new (FALSE, 6);
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+  gtk_widget_show (hbox);
+
+  label = gtk_label_new_with_mnemonic ("Image S_ize:");
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+  gtk_widget_show (label);
+
+  label = gtk_label_new (NULL);
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+  info->scale_label = label;
+  gtk_box_pack_start (GTK_BOX (hbox), info->scale_label, TRUE, TRUE, 0);
+  gtk_widget_show (info->scale_label);
+
+  info->scale = gtk_hscale_new_with_range(1, 100, 1);
+  gtk_box_pack_start (GTK_BOX (vbox), info->scale, TRUE, TRUE, 0);
+  gtk_scale_set_draw_value(GTK_SCALE(info->scale), FALSE);
+  gtk_widget_show (info->scale);
+  gtk_label_set_mnemonic_widget (GTK_LABEL (label), info->scale);
+
+  g_signal_connect(info->scale, "change_value",
+                   G_CALLBACK(scale_change_value_cb), info);
+
+
+  info->preview = vik_print_preview_new (setup, GDK_DRAWABLE(vik_viewport_get_pixmap(data->vvp)));
+  vik_print_preview_set_use_full_page (VIK_PRINT_PREVIEW(info->preview),
+                                        data->use_full_page);
+  gtk_box_pack_start (GTK_BOX (main_hbox), info->preview, TRUE, TRUE, 0);
+  gtk_widget_show (info->preview);
+
+  g_signal_connect (info->preview, "offsets-changed",
+                    G_CALLBACK (preview_offsets_changed_cb),
+                    info);
+
+  update_page_setup (info);
+
+  gdouble offset_x_max, offset_y_max;
+  get_max_offsets (info, &offset_x_max, &offset_y_max);
+  vik_print_preview_set_image_offsets_max (VIK_PRINT_PREVIEW (info->preview),
+                                            offset_x_max, offset_y_max);
+
+  set_scale_value(info);
+  
+  return layout;
+}
diff --git a/src/print.h b/src/print.h
new file mode 100644 (file)
index 0000000..9dd87ba
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
+ *
+ * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
+ *
+ * print.h
+ * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#ifndef __VIKING_PRINT_H
+#define __VIKING_PRINT_H
+
+void a_print(VikWindow *vw, VikViewport *vvp);
+
+#endif /*__VIKING_PRINT_H*/
index 4bdf3bcb82ba57dcd97c90163ec800404a9a77c6..2b769f54da168ef50dac69f90423240b997ca338 100644 (file)
@@ -509,13 +509,8 @@ GtkWidget *vik_trw_layer_create_vtdiag ( GtkWidget *window, VikTrack *tr, gpoint
 #undef MARGIN
 #undef LINES
 
-/* Notes: first and third arguments are swapped around compared to the manuals */
-//static void propwin_response_cb( GtkDialog *widgets, gint resp, PropWidgets *widgets)
-static void propwin_response_cb( gpointer p_widgets, gint resp, gpointer p_dialog)
+static void propwin_response_cb( GtkDialog *dialog, gint resp, PropWidgets *widgets)
 {
-  PropWidgets *widgets = (PropWidgets *) p_widgets;
-  GtkDialog * dialog = p_dialog;
-
   VikTrack *tr = widgets->tr;
   VikTrwLayer *vtl = widgets->vtl;
 
@@ -607,7 +602,7 @@ void vik_trw_layer_propwin_run ( GtkWindow *parent, VikTrwLayer *vtl, VikTrack *
                          GTK_STOCK_OK,     GTK_RESPONSE_ACCEPT,
                          NULL);
   g_free(title);
-  g_signal_connect_swapped(dialog, "response", G_CALLBACK(propwin_response_cb), widgets);
+  g_signal_connect(dialog, "response", G_CALLBACK(propwin_response_cb), widgets);
   //fprintf(stderr, "DEBUG: dialog=0x%p\n", dialog);
   GtkTable *table;
   gdouble tr_len;
index 4ce502efb35a102c9edd6b0ebebeb8e0b0006d23..1e7419aa6e8a84b4b01bbe01e11bc3c93376ed90 100644 (file)
@@ -24,6 +24,7 @@
 #include "datasources.h"
 #include "googlesearch.h"
 #include "dems.h"
+#include "print.h"
 
 #define VIKING_TITLE " - Viking"
 
@@ -1672,6 +1673,11 @@ static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
   gtk_widget_hide ( vw->save_img_dir_dia );
 }
 
+static void print_cb ( GtkAction *a, VikWindow *vw )
+{
+  a_print(vw, vw->viking_vvp);
+}
+
 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
 {
@@ -1776,6 +1782,7 @@ static GtkActionEntry entries[] = {
   { "SaveAs",    GTK_STOCK_SAVE_AS,      "Save _As",                      NULL,         "Save the file under different name",           (GCallback)save_file_as          },
   { "GenImg",    GTK_STOCK_CLEAR,        "_Generate Image File",          NULL,         "Save a snapshot of the workspace into a file", (GCallback)draw_to_image_file_cb },
   { "GenImgDir", GTK_STOCK_DND_MULTIPLE, "Generate _Directory of Images", NULL,         "FIXME:IMGDIR",                                 (GCallback)draw_to_image_dir_cb  },
+  { "Print",    GTK_STOCK_PRINT,        "_Print...",          NULL,         "Print maps", (GCallback)print_cb },
   { "Exit",      GTK_STOCK_QUIT,         "E_xit",                         "<control>W", "Exit the program",                             (GCallback)window_close          },
   { "SaveExit",  GTK_STOCK_QUIT,         "Save and Exit",                 NULL, "Save and Exit the program",                             (GCallback)save_file_and_exit          },