/* February 2008, Bastiaan Veelo, Bastiaan@SARC.nl */

#define DRAWING_IN_MM

#include <cairo.h>
#ifdef CAIRO_HAS_PDF_SURFACE
#include <cairo-pdf.h>
#endif
#include <gtk/gtk.h>

static GtkPrintSettings *settings = NULL;

void cairo_draw(cairo_t *cr, PangoLayout *pango_cairo_layout)
{
  PangoFontDescription *desc =
      pango_font_description_from_string ("Serif 26");
  pango_layout_set_font_description (pango_cairo_layout, desc);
  pango_font_description_free (desc);

  pango_layout_set_markup (pango_cairo_layout,
                           "<u>the quick brown fox jumped over the lazy dog</u>", -1);
  cairo_move_to (cr, 25, 25);
  pango_cairo_show_layout (cr, pango_cairo_layout);

  /* These lines should be 1mm thick and 100mm long in PDF and print,
     and proportional to the text on screen (due to logical resolution, not physical resolution). */
  cairo_set_line_width (cr, 1);
  cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
  cairo_move_to (cr, 25, 75);
  cairo_line_to (cr, 125, 75);
  cairo_stroke (cr);

  cairo_move_to (cr, 150, 75);
  cairo_line_to (cr, 150, 175);
  cairo_stroke (cr);

  desc = pango_font_description_from_string ("Serif 12");
  pango_layout_set_font_description (pango_cairo_layout, desc);
  pango_font_description_free (desc);

  pango_layout_set_markup (pango_cairo_layout,
                           "<u>the quick brown fox jumped over the lazy dog</u>", -1);
  cairo_move_to (cr, 25, 125);
  pango_cairo_show_layout (cr, pango_cairo_layout);
}


static gboolean
on_expose_event(GtkWidget      *widget,
                GdkEventExpose *event,
                gpointer        data)
{
  cairo_t *cr = gdk_cairo_create(GTK_LAYOUT(widget)->bin_window);

  PangoLayout *pango_cairo_layout = pango_cairo_create_layout (cr);

#ifdef DRAWING_IN_MM
  double dpi = gdk_screen_get_resolution (gdk_screen_get_default());
  if (dpi < 0) dpi = 96.;    /* Fall back to win32 default. */
  const double SCREEN_SCALE_MM = dpi / 25.4;

  cairo_scale (cr, SCREEN_SCALE_MM, SCREEN_SCALE_MM);
  pango_cairo_update_layout (cr, pango_cairo_layout);
  pango_cairo_context_set_resolution (pango_layout_get_context (pango_cairo_layout), dpi/SCREEN_SCALE_MM);
#endif /* DRAWING_IN_MM */

  cairo_draw (cr, pango_cairo_layout);

  g_object_unref (pango_cairo_layout);
  cairo_destroy(cr);

  return FALSE;
}


static void
begin_print (GtkPrintOperation *operation, GtkPrintContext *context, gpointer user_data)
{
  gtk_print_operation_set_n_pages (operation, 1);
}


static void
print_page (GtkPrintOperation *operation, GtkPrintContext *context,
            gint page_nr, gpointer user_data)
{
  cairo_t *cr = gtk_print_context_get_cairo_context (context);

  PangoLayout *pango_cairo_layout = gtk_print_context_create_pango_layout (context);
#ifdef DRAWING_IN_MM
  /* On win32 we need this to prevent wrong inter-letter spacing apparent in small fonts.
     Should this be in the GTK+ printing code some place? */
     pango_cairo_update_layout (cr, pango_cairo_layout);
#endif
  cairo_draw (cr, pango_cairo_layout);
  g_object_unref (pango_cairo_layout);
}


void
print_dialog(GtkWidget *button, gpointer parent)
{
  GtkPrintOperation *print;
  GtkPrintOperationResult result;
  GError *error;

  print = gtk_print_operation_new ();

  if (settings != NULL)
    gtk_print_operation_set_print_settings (print, settings);

  g_signal_connect (print, "begin-print", G_CALLBACK (begin_print), NULL);
  g_signal_connect (print, "draw-page", G_CALLBACK (print_page), NULL);

  gtk_print_operation_set_use_full_page (print, TRUE);

#ifdef DRAWING_IN_MM
  gtk_print_operation_set_unit (print, GTK_UNIT_MM);
#endif /*DRAWING_IN_MM */

  result = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
                                    GTK_WINDOW (parent), &error);

  if (result == GTK_PRINT_OPERATION_RESULT_ERROR)
  {
    GtkWidget *error_dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
                                                      GTK_DIALOG_DESTROY_WITH_PARENT,
                                                      GTK_MESSAGE_ERROR,
                                                      GTK_BUTTONS_CLOSE,
                                                      "Error printing file:\n%s",
                                                      error->message);
    g_signal_connect (error_dialog, "response",
                      G_CALLBACK (gtk_widget_destroy), NULL);
    gtk_widget_show (error_dialog);
    g_error_free (error);
  }
  else if (result == GTK_PRINT_OPERATION_RESULT_APPLY)
  {
    if (settings != NULL)
      g_object_unref (settings);
    settings = g_object_ref (gtk_print_operation_get_print_settings (print));
  }

  g_object_unref (print);
}

#ifdef CAIRO_HAS_PDF_SURFACE
static void
export_to_PDF (GtkButton *button, gpointer parent)
{
  GtkWidget *dialog =
      gtk_file_chooser_dialog_new ("Export PDF File",
                                   GTK_WINDOW (parent),
                                   GTK_FILE_CHOOSER_ACTION_SAVE,
                                   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                   GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
                                   NULL);
  gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);

  gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), "printout.pdf");
  GtkFileFilter *pdf_filter = gtk_file_filter_new();
  gtk_file_filter_set_name( pdf_filter, "Portable Document Format Files" );
  gtk_file_filter_add_pattern( pdf_filter, "*.pdf" );
  gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( dialog ), pdf_filter );
  GtkFileFilter *all_files_filter = gtk_file_filter_new();
  gtk_file_filter_set_name( all_files_filter, "All Files" );
  gtk_file_filter_add_pattern( all_files_filter, "*" );
  gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( dialog ), all_files_filter );

  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
  {
    gchar *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
    if (!g_str_has_suffix (filename, ".pdf"))
    {
      gchar *result = g_strconcat (filename, ".pdf", NULL);
      g_free (filename);
      filename = result;
    }

    cairo_surface_t *surface =
        cairo_pdf_surface_create (filename, 210.*72./25.4, 297.*72./25.4);
    cairo_status_t status = cairo_surface_status(surface);
    if (status == CAIRO_STATUS_SUCCESS)
    {
      cairo_t *cr = cairo_create (surface);

      PangoLayout *pango_cairo_layout = pango_cairo_create_layout (cr);

#ifdef DRAWING_IN_MM
      const double PDF_SCALE_MM = 72./25.4;
      cairo_scale (cr, PDF_SCALE_MM, PDF_SCALE_MM);
      pango_cairo_update_layout (cr, pango_cairo_layout);
      pango_cairo_context_set_resolution (pango_layout_get_context (pango_cairo_layout), 72/PDF_SCALE_MM);
#else
      pango_cairo_context_set_resolution (pango_layout_get_context (pango_cairo_layout), 72);
#endif /* DRAWING_IN_MM */

      cairo_draw (cr, pango_cairo_layout);

      g_object_unref (pango_cairo_layout);
      cairo_destroy(cr);
    }
    else
    {
      GtkWidget *error_dialog;
      if (status == CAIRO_STATUS_WRITE_ERROR)
        error_dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
                                               GTK_DIALOG_DESTROY_WITH_PARENT,
                                               GTK_MESSAGE_ERROR,
                                               GTK_BUTTONS_CLOSE,
                                               "Could not export to PDF:\n"
                                               "could not write to %s.\n"
                                               "If you have the file open for reading, please close it first.",
                                               filename);
      else
        error_dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
                                               GTK_DIALOG_DESTROY_WITH_PARENT,
                                               GTK_MESSAGE_ERROR,
                                               GTK_BUTTONS_CLOSE,
                                               "Could not export to PDF:\n%s",
                                               cairo_status_to_string (status));
      g_signal_connect (error_dialog, "response",
                        G_CALLBACK (gtk_widget_destroy), NULL);
      gtk_widget_show (error_dialog);
    }
    cairo_surface_destroy (surface);
    g_free (filename);
  }

  gtk_widget_destroy (dialog);
}
#endif /* CAIRO_HAS_PDF_SURFACE */


int
main (int argc, char *argv[])
{
  gtk_init (&argc, &argv);

  GtkWidget *window;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  g_signal_connect(window, "destroy",
                   G_CALLBACK (gtk_main_quit), NULL);

  GtkWidget *vbox = gtk_vbox_new (FALSE, 5);
  gtk_container_add (GTK_CONTAINER (window), vbox);
  GtkWidget *hbox = gtk_hbox_new (FALSE, 5);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);

  GtkWidget *print_button = gtk_button_new_from_stock (GTK_STOCK_PRINT);
  gtk_box_pack_start (GTK_BOX (hbox), print_button, FALSE, FALSE, 0);
  g_signal_connect (print_button, "clicked",
                    G_CALLBACK (print_dialog), window);

  GtkWidget *pdf_button = gtk_button_new_with_mnemonic ("_Export to PDF");
  gtk_box_pack_start (GTK_BOX (hbox), pdf_button, FALSE, FALSE, 0);
#ifdef CAIRO_HAS_PDF_SURFACE
  g_signal_connect (pdf_button, "clicked",
                    G_CALLBACK (export_to_PDF), window);
#else
  gtk_widget_set_sensitive (pdf_button, FALSE);
#endif /* CAIRO_HAS_PDF_SURFACE */

  GtkWidget *scrolled = gtk_scrolled_window_new(NULL, NULL);
  gtk_box_pack_start_defaults (GTK_BOX (vbox), scrolled);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

  GtkWidget *drawing = gtk_layout_new (gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (scrolled)),
                                       gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled)));
  gtk_container_add (GTK_CONTAINER (scrolled), drawing);
  GdkColor white;
  gdk_color_parse ("#FFF", &white);
  gtk_widget_modify_bg (drawing, GTK_STATE_NORMAL, &white);

  gtk_layout_set_size (GTK_LAYOUT (drawing), 850, 710);

  g_signal_connect(drawing, "expose-event",
                   G_CALLBACK (on_expose_event), NULL);

  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 850, 760);
  gtk_widget_set_app_paintable(window, TRUE);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}
