gtkprintoperation-unix.c 33.8 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
2 3
 * gtkprintoperation-unix.c: Print Operation Details for Unix 
 *                           and Unix-like platforms
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 * 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, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "config.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
29 30 31
#include <errno.h>
#include <stdlib.h>       
#include <fcntl.h>
32

33
#include <glib/gstdio.h>
34 35 36
#include "gtkprintoperation-private.h"
#include "gtkmessagedialog.h"

37 38
#include <cairo-pdf.h>
#include <cairo-ps.h>
39
#include "gtkprivate.h"
40 41 42 43
#include "gtkprintunixdialog.h"
#include "gtkpagesetupunixdialog.h"
#include "gtkprintbackend.h"
#include "gtkprinter.h"
44
#include "gtkprinter-private.h"
45
#include "gtkprintjob.h"
46
#include "gtklabel.h"
47
#include "gtkintl.h"
48
#include "gtkalias.h"
49

Matthias Clasen's avatar
Matthias Clasen committed
50 51
typedef struct 
{
Matthias Clasen's avatar
Matthias Clasen committed
52
  GtkWindow *parent;        /* just in case we need to throw error dialogs */
Alexander Larsson's avatar
Alexander Larsson committed
53 54
  GMainLoop *loop;
  gboolean data_sent;
55

56
  /* Real printing (not preview) */
57 58 59 60 61
  GtkPrintJob *job;         /* the job we are sending to the printer */
  cairo_surface_t *surface;
  gulong job_status_changed_tag;

  
62 63
} GtkPrintOperationUnix;

Alexander Larsson's avatar
Alexander Larsson committed
64 65 66
typedef struct _PrinterFinder PrinterFinder;

static void printer_finder_free (PrinterFinder *finder);
Matthias Clasen's avatar
Matthias Clasen committed
67
static void find_printer        (const gchar   *printer,
Alexander Larsson's avatar
Alexander Larsson committed
68 69 70
				 GFunc          func,
				 gpointer       data);

71 72
static void
unix_start_page (GtkPrintOperation *op,
Matthias Clasen's avatar
Matthias Clasen committed
73 74
		 GtkPrintContext   *print_context,
		 GtkPageSetup      *page_setup)
75
{
76
  GtkPrintOperationUnix *op_unix;  
77 78
  GtkPaperSize *paper_size;
  cairo_surface_type_t type;
Matthias Clasen's avatar
Matthias Clasen committed
79
  gdouble w, h;
80

81 82
  op_unix = op->priv->platform_data;
  
83 84 85 86 87
  paper_size = gtk_page_setup_get_paper_size (page_setup);

  w = gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS);
  h = gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS);
  
88
  type = cairo_surface_get_type (op_unix->surface);
89

90 91 92 93
  if ((op->priv->manual_number_up < 2) ||
      (op->priv->page_position % op->priv->manual_number_up == 0))
    {
      if (type == CAIRO_SURFACE_TYPE_PS)
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
        {
          cairo_ps_surface_set_size (op_unix->surface, w, h);
          cairo_ps_surface_dsc_begin_page_setup (op_unix->surface);
          switch (gtk_page_setup_get_orientation (page_setup))
            {
              case GTK_PAGE_ORIENTATION_PORTRAIT:
              case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
                cairo_ps_surface_dsc_comment (op_unix->surface, "%%PageOrientation: Portrait");
                break;

              case GTK_PAGE_ORIENTATION_LANDSCAPE:
              case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
                cairo_ps_surface_dsc_comment (op_unix->surface, "%%PageOrientation: Landscape");
                break;
            }
         }
110
      else if (type == CAIRO_SURFACE_TYPE_PDF)
111 112 113
        {
          cairo_pdf_surface_set_size (op_unix->surface, w, h);
        }
114
    }
115 116 117 118
}

static void
unix_end_page (GtkPrintOperation *op,
Matthias Clasen's avatar
Matthias Clasen committed
119
	       GtkPrintContext   *print_context)
120 121 122
{
  cairo_t *cr;

123
  cr = gtk_print_context_get_cairo_context (print_context);
124 125 126 127 128

  if ((op->priv->manual_number_up < 2) ||
      ((op->priv->page_position + 1) % op->priv->manual_number_up == 0) ||
      (op->priv->page_position == op->priv->nr_of_pages_to_print - 1))
    cairo_show_page (cr);
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
}

static void
op_unix_free (GtkPrintOperationUnix *op_unix)
{
  if (op_unix->job)
    {
      g_signal_handler_disconnect (op_unix->job,
				   op_unix->job_status_changed_tag);
      g_object_unref (op_unix->job);
    }

  g_free (op_unix);
}

Matthias Clasen's avatar
Matthias Clasen committed
144
static gchar *
145
shell_command_substitute_file (const gchar *cmd,
146 147 148 149
			       const gchar *pdf_filename,
			       const gchar *settings_filename,
                               gboolean    *pdf_filename_replaced,
                               gboolean    *settings_filename_replaced)
150
{
Matthias Clasen's avatar
Matthias Clasen committed
151
  const gchar *inptr, *start;
152 153 154
  GString *final;

  g_return_val_if_fail (cmd != NULL, NULL);
155 156
  g_return_val_if_fail (pdf_filename != NULL, NULL);
  g_return_val_if_fail (settings_filename != NULL, NULL);
157 158 159

  final = g_string_new (NULL);

160 161
  *pdf_filename_replaced = FALSE;
  *settings_filename_replaced = FALSE;
162

163
  start = inptr = cmd;
164 165 166 167 168 169 170
  while ((inptr = strchr (inptr, '%')) != NULL) 
    {
      g_string_append_len (final, start, inptr - start);
      inptr++;
      switch (*inptr) 
        {
          case 'f':
171 172 173 174 175 176 177
            g_string_append (final, pdf_filename);
            *pdf_filename_replaced = TRUE;
            break;

          case 's':
            g_string_append (final, settings_filename);
            *settings_filename_replaced = TRUE;
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
            break;

          case '%':
            g_string_append_c (final, '%');
            break;

          default:
            g_string_append_c (final, '%');
            if (*inptr)
              g_string_append_c (final, *inptr);
            break;
        }
      if (*inptr)
        inptr++;
      start = inptr;
    }
  g_string_append (final, start);

196
  return g_string_free (final, FALSE);
197 198 199 200
}

void
_gtk_print_operation_platform_backend_launch_preview (GtkPrintOperation *op,
201
						      cairo_surface_t   *surface,
Matthias Clasen's avatar
Matthias Clasen committed
202 203
						      GtkWindow         *parent,
						      const gchar       *filename)
204
{
Matthias Clasen's avatar
Matthias Clasen committed
205
  gint argc;
206 207 208 209
  gchar **argv;
  gchar *cmd;
  gchar *preview_cmd;
  GtkSettings *settings;
Marek Kasik's avatar
Marek Kasik committed
210
  GtkPrintSettings *print_settings = NULL;
211 212 213 214
  GtkPageSetup *page_setup;
  GKeyFile *key_file = NULL;
  gchar *data = NULL;
  gsize data_len;
215
  gchar *settings_filename = NULL;
216
  gchar *quoted_filename;
217
  gchar *quoted_settings_filename;
218 219
  gboolean filename_used = FALSE;
  gboolean settings_used = FALSE;
220 221
  GdkScreen *screen;
  GError *error = NULL;
222
  gint fd;
223
  gboolean retval;
224

225
  cairo_surface_destroy (surface);
226
 
227 228 229 230 231
  if (parent)
    screen = gtk_window_get_screen (parent);
  else
    screen = gdk_screen_get_default ();

232 233 234
  fd = g_file_open_tmp ("settingsXXXXXX.ini", &settings_filename, &error);
  if (fd < 0) 
    goto out;
235

236 237
  key_file = g_key_file_new ();
  
Marek Kasik's avatar
Marek Kasik committed
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
  print_settings = gtk_print_settings_copy (gtk_print_operation_get_print_settings (op));

  if (print_settings != NULL)
    {
      gtk_print_settings_set_reverse (print_settings, FALSE);
      gtk_print_settings_set_page_set (print_settings, GTK_PAGE_SET_ALL);
      gtk_print_settings_set_scale (print_settings, 1.0);
      gtk_print_settings_set_number_up (print_settings, 1);
      gtk_print_settings_set_number_up_layout (print_settings, GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_TOP_TO_BOTTOM);

      /*  These removals are neccessary because cups-* settings have higher priority
       *  than normal settings.
       */
      gtk_print_settings_unset (print_settings, "cups-reverse");
      gtk_print_settings_unset (print_settings, "cups-page-set");
      gtk_print_settings_unset (print_settings, "cups-scale");
      gtk_print_settings_unset (print_settings, "cups-number-up");
      gtk_print_settings_unset (print_settings, "cups-number-up-layout");

      gtk_print_settings_to_key_file (print_settings, key_file, NULL);
      g_object_unref (print_settings);
    }
260 261 262

  page_setup = gtk_print_context_get_page_setup (op->priv->print_context);
  gtk_page_setup_to_key_file (page_setup, key_file, NULL);
263

264 265
  g_key_file_set_string (key_file, "Print Job", "title", op->priv->job_name);

266 267 268 269 270
  data = g_key_file_to_data (key_file, &data_len, &error);
  if (!data)
    goto out;

  retval = g_file_set_contents (settings_filename, data, data_len, &error);
271
  if (!retval)
272 273 274
    goto out;

  settings = gtk_settings_get_for_screen (screen);
275 276 277
  g_object_get (settings, "gtk-print-preview-command", &preview_cmd, NULL);

  quoted_filename = g_shell_quote (filename);
278 279
  quoted_settings_filename = g_shell_quote (settings_filename);
  cmd = shell_command_substitute_file (preview_cmd, quoted_filename, quoted_settings_filename, &filename_used, &settings_used);
280 281
  g_shell_parse_argv (cmd, &argc, &argv, &error);

282 283 284 285 286
  g_free (preview_cmd);
  g_free (quoted_filename);
  g_free (quoted_settings_filename);
  g_free (cmd);

Matthias Clasen's avatar
Matthias Clasen committed
287
  if (error != NULL)
288 289 290 291
    goto out;

  gdk_spawn_on_screen (screen, NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error);

292 293
  g_strfreev (argv);

294 295 296 297 298 299 300 301 302 303 304 305 306
  if (error != NULL)
    {
      gchar* uri;

      g_warning ("%s %s", _("Error launching preview"), error->message);

      g_error_free (error);
      error = NULL;
      uri = g_filename_to_uri (filename, NULL, NULL);
      gtk_show_uri (screen, uri, GDK_CURRENT_TIME, &error);
      g_free (uri);
    }

307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
 out:
  if (error != NULL)
    {
      GtkWidget *edialog;
      edialog = gtk_message_dialog_new (parent, 
                                        GTK_DIALOG_DESTROY_WITH_PARENT,
                                        GTK_MESSAGE_ERROR,
                                        GTK_BUTTONS_CLOSE,
                                        _("Error launching preview") /* FIXME better text */);
      gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (edialog),
                                                "%s", error->message);
      g_signal_connect (edialog, "response",
                        G_CALLBACK (gtk_widget_destroy), NULL);

      gtk_window_present (GTK_WINDOW (edialog));

323 324 325 326
      g_error_free (error);

      filename_used = FALSE; 
      settings_used = FALSE;
327 328
   } 

329 330 331 332 333 334
  if (!filename_used)
    g_unlink (filename);

  if (!settings_used)
    g_unlink (settings_filename);

335 336 337 338 339 340
  if (fd > 0)
    close (fd);
  
  if (key_file)
    g_key_file_free (key_file);
  g_free (data);
341
  g_free (settings_filename);
342 343
}

344 345
static void
unix_finish_send  (GtkPrintJob *job,
Matthias Clasen's avatar
Matthias Clasen committed
346
                   gpointer     user_data, 
Matthias Clasen's avatar
Matthias Clasen committed
347
                   GError      *error)
348
{
349 350
  GtkPrintOperation *op = (GtkPrintOperation *) user_data;
  GtkPrintOperationUnix *op_unix = op->priv->platform_data;
351 352 353 354

  if (error != NULL)
    {
      GtkWidget *edialog;
355
      edialog = gtk_message_dialog_new (op_unix->parent, 
356 357 358
                                        GTK_DIALOG_DESTROY_WITH_PARENT,
                                        GTK_MESSAGE_ERROR,
                                        GTK_BUTTONS_CLOSE,
359 360 361 362 363 364 365 366
                                        _("Error printing") /* FIXME better text */);
      gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (edialog),
                                                "%s", error->message);
      gtk_window_set_modal (GTK_WINDOW (edialog), TRUE);
      g_signal_connect (edialog, "response",
                        G_CALLBACK (gtk_widget_destroy), NULL);

      gtk_window_present (GTK_WINDOW (edialog));
367
    }
Alexander Larsson's avatar
Alexander Larsson committed
368 369

  op_unix->data_sent = TRUE;
370

Alexander Larsson's avatar
Alexander Larsson committed
371 372
  if (op_unix->loop)
    g_main_loop_quit (op_unix->loop);
373 374

  g_object_unref (op);
375 376 377
}

static void
Alexander Larsson's avatar
Alexander Larsson committed
378
unix_end_run (GtkPrintOperation *op,
379 380
	      gboolean           wait,
	      gboolean           cancelled)
381 382
{
  GtkPrintOperationUnix *op_unix = op->priv->platform_data;
Alexander Larsson's avatar
Alexander Larsson committed
383

384 385
  cairo_surface_finish (op_unix->surface);
  
386 387 388
  if (cancelled)
    return;

Alexander Larsson's avatar
Alexander Larsson committed
389 390 391
  if (wait)
    op_unix->loop = g_main_loop_new (NULL, FALSE);
  
392
  /* TODO: Check for error */
393
  if (op_unix->job != NULL)
394 395 396 397 398 399
    {
      g_object_ref (op);
      gtk_print_job_send (op_unix->job,
                          unix_finish_send, 
                          op, NULL);
    }
Alexander Larsson's avatar
Alexander Larsson committed
400 401 402

  if (wait)
    {
403
      g_object_ref (op);
Alexander Larsson's avatar
Alexander Larsson committed
404 405 406 407 408 409 410
      if (!op_unix->data_sent)
	{
	  GDK_THREADS_LEAVE ();  
	  g_main_loop_run (op_unix->loop);
	  GDK_THREADS_ENTER ();  
	}
      g_main_loop_unref (op_unix->loop);
411 412
      op_unix->loop = NULL;
      g_object_unref (op);
Alexander Larsson's avatar
Alexander Larsson committed
413
    }
414 415 416
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
417 418
job_status_changed_cb (GtkPrintJob       *job, 
		       GtkPrintOperation *op)
419 420 421 422
{
  _gtk_print_operation_set_status (op, gtk_print_job_get_status (job), NULL);
}

423

424
static void
425 426 427
print_setup_changed_cb (GtkPrintUnixDialog *print_dialog, 
                        GParamSpec         *pspec,
                        gpointer            user_data)
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
{
  GtkPageSetup             *page_setup;
  GtkPrintSettings         *print_settings;
  GtkPrintOperation        *op = user_data;
  GtkPrintOperationPrivate *priv = op->priv;

  page_setup = gtk_print_unix_dialog_get_page_setup (print_dialog);
  print_settings = gtk_print_unix_dialog_get_settings (print_dialog);

  g_signal_emit_by_name (op,
                         "update-custom-widget",
                         priv->custom_widget,
                         page_setup,
                         print_settings);
}

444 445 446
static GtkWidget *
get_print_dialog (GtkPrintOperation *op,
                  GtkWindow         *parent)
447
{
448
  GtkPrintOperationPrivate *priv = op->priv;
449
  GtkWidget *pd, *label;
Matthias Clasen's avatar
Matthias Clasen committed
450
  const gchar *custom_tab_label;
451 452 453

  pd = gtk_print_unix_dialog_new (NULL, parent);

454 455 456 457 458
  gtk_print_unix_dialog_set_manual_capabilities (GTK_PRINT_UNIX_DIALOG (pd),
						 GTK_PRINT_CAPABILITY_PAGE_SET |
						 GTK_PRINT_CAPABILITY_COPIES |
						 GTK_PRINT_CAPABILITY_COLLATE |
						 GTK_PRINT_CAPABILITY_REVERSE |
459
						 GTK_PRINT_CAPABILITY_SCALE |
460 461 462
						 GTK_PRINT_CAPABILITY_PREVIEW |
						 GTK_PRINT_CAPABILITY_NUMBER_UP |
						 GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT);
463

464
  if (priv->print_settings)
465
    gtk_print_unix_dialog_set_settings (GTK_PRINT_UNIX_DIALOG (pd),
466
					priv->print_settings);
467

468
  if (priv->default_page_setup)
469 470
    gtk_print_unix_dialog_set_page_setup (GTK_PRINT_UNIX_DIALOG (pd), 
                                          priv->default_page_setup);
471

472 473 474
  gtk_print_unix_dialog_set_embed_page_setup (GTK_PRINT_UNIX_DIALOG (pd),
                                              priv->embed_page_setup);

475 476 477
  gtk_print_unix_dialog_set_current_page (GTK_PRINT_UNIX_DIALOG (pd), 
                                          priv->current_page);

Marek Kasik's avatar
Marek Kasik committed
478 479 480 481 482 483
  gtk_print_unix_dialog_set_support_selection (GTK_PRINT_UNIX_DIALOG (pd),
                                               priv->support_selection);

  gtk_print_unix_dialog_set_has_selection (GTK_PRINT_UNIX_DIALOG (pd),
                                           priv->has_selection);

484
  g_signal_emit_by_name (op, "create-custom-widget",
Matthias Clasen's avatar
Matthias Clasen committed
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
			 &priv->custom_widget);

  if (priv->custom_widget) 
    {
      custom_tab_label = priv->custom_tab_label;
      
      if (custom_tab_label == NULL)
	{
	  custom_tab_label = g_get_application_name ();
	  if (custom_tab_label == NULL)
	    custom_tab_label = _("Application");
	}

      label = gtk_label_new (custom_tab_label);
      
      gtk_print_unix_dialog_add_custom_tab (GTK_PRINT_UNIX_DIALOG (pd),
Matthias Clasen's avatar
Matthias Clasen committed
501
					    priv->custom_widget, label);
502

503 504
      g_signal_connect (pd, "notify::selected-printer", (GCallback) print_setup_changed_cb, op);
      g_signal_connect (pd, "notify::page-setup", (GCallback) print_setup_changed_cb, op);
Matthias Clasen's avatar
Matthias Clasen committed
505
    }
506
  
507 508
  return pd;
}
509
  
Matthias Clasen's avatar
Matthias Clasen committed
510 511
typedef struct 
{
512 513
  GtkPrintOperation           *op;
  gboolean                     do_print;
514
  gboolean                     do_preview;
515 516 517
  GtkPrintOperationResult      result;
  GtkPrintOperationPrintFunc   print_cb;
  GDestroyNotify               destroy;
Alexander Larsson's avatar
Alexander Larsson committed
518 519
  GtkWindow                   *parent;
  GMainLoop                   *loop;
520 521 522 523 524 525 526 527 528 529 530 531
} PrintResponseData;

static void
print_response_data_free (gpointer data)
{
  PrintResponseData *rdata = data;

  g_object_unref (rdata->op);
  g_free (rdata);
}

static void
Alexander Larsson's avatar
Alexander Larsson committed
532
finish_print (PrintResponseData *rdata,
Matthias Clasen's avatar
Matthias Clasen committed
533 534
	      GtkPrinter        *printer,
	      GtkPageSetup      *page_setup,
535 536
	      GtkPrintSettings  *settings,
	      gboolean           page_setup_set)
537 538
{
  GtkPrintOperation *op = rdata->op;
539
  GtkPrintOperationPrivate *priv = op->priv;
Matthias Clasen's avatar
Matthias Clasen committed
540
  GtkPrintJob *job;
541
  gdouble top, bottom, left, right;
Alexander Larsson's avatar
Alexander Larsson committed
542 543
  
  if (rdata->do_print)
544 545
    {
      gtk_print_operation_set_print_settings (op, settings);
546
      priv->print_context = _gtk_print_context_new (op);
547

548 549 550 551 552 553 554 555 556 557 558 559 560
      if (gtk_print_settings_get_number_up (settings) < 2)
        {
	  if (gtk_printer_get_hard_margins (printer, &top, &bottom, &left, &right))
	    _gtk_print_context_set_hard_margins (priv->print_context, top, bottom, left, right);
	}
      else
        {
	  /* Pages do not have any unprintable area when printing n-up as each page on the
	   * sheet has been scaled down and translated to a position within the printable
	   * area of the sheet.
	   */
	  _gtk_print_context_set_hard_margins (priv->print_context, 0, 0, 0, 0);
	}
561

562 563 564
      if (page_setup != NULL &&
          (gtk_print_operation_get_default_page_setup (op) == NULL ||
           page_setup_set))
565 566
        gtk_print_operation_set_default_page_setup (op, page_setup);

567
      _gtk_print_context_set_page_setup (priv->print_context, page_setup);
568

569
      if (!rdata->do_preview)
570
        {
571 572 573
	  GtkPrintOperationUnix *op_unix;
	  cairo_t *cr;
	  
574
	  op_unix = g_new0 (GtkPrintOperationUnix, 1);
575 576 577 578 579 580 581 582
	  priv->platform_data = op_unix;
	  priv->free_platform_data = (GDestroyNotify) op_unix_free;
	  op_unix->parent = rdata->parent;
	  
	  priv->start_page = unix_start_page;
	  priv->end_page = unix_end_page;
	  priv->end_run = unix_end_run;
	  
Matthias Clasen's avatar
Matthias Clasen committed
583 584 585
	  job = gtk_print_job_new (priv->job_name, printer, settings, page_setup);
          op_unix->job = job;
          gtk_print_job_set_track_print_status (job, priv->track_print_status);
586
	  
587
	  op_unix->surface = gtk_print_job_get_surface (job, &priv->error);
Matthias Clasen's avatar
Matthias Clasen committed
588 589
	  if (op_unix->surface == NULL) 
            {
590
	      rdata->result = GTK_PRINT_OPERATION_RESULT_ERROR;
Matthias Clasen's avatar
Matthias Clasen committed
591 592 593
	      rdata->do_print = FALSE;
	      goto out;
            }
594 595
	  
	  cr = cairo_create (op_unix->surface);
Matthias Clasen's avatar
Matthias Clasen committed
596
	  gtk_print_context_set_cairo_context (priv->print_context, cr, 72, 72);
597 598
	  cairo_destroy (cr);

Matthias Clasen's avatar
Matthias Clasen committed
599
          _gtk_print_operation_set_status (op, gtk_print_job_get_status (job), NULL);
600 601
	  
          op_unix->job_status_changed_tag =
Matthias Clasen's avatar
Matthias Clasen committed
602
	    g_signal_connect (job, "status-changed",
603 604
			      G_CALLBACK (job_status_changed_cb), op);
	  
Matthias Clasen's avatar
Matthias Clasen committed
605 606 607
          priv->print_pages = job->print_pages;
          priv->page_ranges = job->page_ranges;
          priv->num_page_ranges = job->num_page_ranges;
608
	  
Matthias Clasen's avatar
Matthias Clasen committed
609 610 611 612 613 614
          priv->manual_num_copies = job->num_copies;
          priv->manual_collation = job->collate;
          priv->manual_reverse = job->reverse;
          priv->manual_page_set = job->page_set;
          priv->manual_scale = job->scale;
          priv->manual_orientation = job->rotate_to_orientation;
615 616
          priv->manual_number_up = job->number_up;
          priv->manual_number_up_layout = job->number_up_layout;
617
        }
618
    } 
619
 out:
620
  if (rdata->print_cb)
621
    rdata->print_cb (op, rdata->parent, rdata->do_print, rdata->result); 
622 623 624 625 626

  if (rdata->destroy)
    rdata->destroy (rdata);
}

627
static void 
Alexander Larsson's avatar
Alexander Larsson committed
628 629 630 631 632 633 634 635 636
handle_print_response (GtkWidget *dialog,
		       gint       response,
		       gpointer   data)
{
  GtkPrintUnixDialog *pd = GTK_PRINT_UNIX_DIALOG (dialog);
  PrintResponseData *rdata = data;
  GtkPrintSettings *settings = NULL;
  GtkPageSetup *page_setup = NULL;
  GtkPrinter *printer = NULL;
637
  gboolean page_setup_set = FALSE;
Alexander Larsson's avatar
Alexander Larsson committed
638 639 640 641

  if (response == GTK_RESPONSE_OK)
    {
      printer = gtk_print_unix_dialog_get_selected_printer (GTK_PRINT_UNIX_DIALOG (pd));
642 643 644

      rdata->result = GTK_PRINT_OPERATION_RESULT_APPLY;
      rdata->do_preview = FALSE;
645 646 647 648 649 650
      if (printer != NULL)
	rdata->do_print = TRUE;
    } 
  else if (response == GTK_RESPONSE_APPLY)
    {
      /* print preview */
651 652
      rdata->result = GTK_PRINT_OPERATION_RESULT_APPLY;
      rdata->do_preview = TRUE;
Alexander Larsson's avatar
Alexander Larsson committed
653
      rdata->do_print = TRUE;
654 655

      rdata->op->priv->action = GTK_PRINT_OPERATION_ACTION_PREVIEW;
656
    }
Alexander Larsson's avatar
Alexander Larsson committed
657

658 659
  if (rdata->do_print)
    {
Alexander Larsson's avatar
Alexander Larsson committed
660 661
      settings = gtk_print_unix_dialog_get_settings (GTK_PRINT_UNIX_DIALOG (pd));
      page_setup = gtk_print_unix_dialog_get_page_setup (GTK_PRINT_UNIX_DIALOG (pd));
662
      page_setup_set = gtk_print_unix_dialog_get_page_setup_set (GTK_PRINT_UNIX_DIALOG (pd));
663
      
664
      g_signal_emit_by_name (rdata->op, "custom-widget-apply", rdata->op->priv->custom_widget);
665 666
    }
  
667
  finish_print (rdata, printer, page_setup, settings, page_setup_set);
Alexander Larsson's avatar
Alexander Larsson committed
668 669 670

  if (settings)
    g_object_unref (settings);
671
    
Alexander Larsson's avatar
Alexander Larsson committed
672
  gtk_widget_destroy (GTK_WIDGET (pd));
673
 
Alexander Larsson's avatar
Alexander Larsson committed
674 675 676 677
}


static void
Matthias Clasen's avatar
Matthias Clasen committed
678
found_printer (GtkPrinter        *printer,
Alexander Larsson's avatar
Alexander Larsson committed
679 680 681 682 683 684 685 686 687 688
	       PrintResponseData *rdata)
{
  GtkPrintOperation *op = rdata->op;
  GtkPrintOperationPrivate *priv = op->priv;
  GtkPrintSettings *settings = NULL;
  GtkPageSetup *page_setup = NULL;
  
  if (rdata->loop)
    g_main_loop_quit (rdata->loop);

Matthias Clasen's avatar
Matthias Clasen committed
689 690
  if (printer != NULL) 
    {
691
      rdata->result = GTK_PRINT_OPERATION_RESULT_APPLY;
Alexander Larsson's avatar
Alexander Larsson committed
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708

      rdata->do_print = TRUE;

      if (priv->print_settings)
	settings = gtk_print_settings_copy (priv->print_settings);
      else
	settings = gtk_print_settings_new ();

      gtk_print_settings_set_printer (settings,
				      gtk_printer_get_name (printer));
      
      if (priv->default_page_setup)
	page_setup = gtk_page_setup_copy (priv->default_page_setup);
      else
	page_setup = gtk_page_setup_new ();
  }
  
709
  finish_print (rdata, printer, page_setup, settings, FALSE);
Alexander Larsson's avatar
Alexander Larsson committed
710 711 712 713 714 715 716 717

  if (settings)
    g_object_unref (settings);
  
  if (page_setup)
    g_object_unref (page_setup);
}

718 719
void
_gtk_print_operation_platform_backend_run_dialog_async (GtkPrintOperation          *op,
720
							gboolean                    show_dialog,
721 722 723 724 725
                                                        GtkWindow                  *parent,
							GtkPrintOperationPrintFunc  print_cb)
{
  GtkWidget *pd;
  PrintResponseData *rdata;
Matthias Clasen's avatar
Matthias Clasen committed
726
  const gchar *printer_name;
727 728 729 730

  rdata = g_new (PrintResponseData, 1);
  rdata->op = g_object_ref (op);
  rdata->do_print = FALSE;
731
  rdata->do_preview = FALSE;
732 733
  rdata->result = GTK_PRINT_OPERATION_RESULT_CANCEL;
  rdata->print_cb = print_cb;
Alexander Larsson's avatar
Alexander Larsson committed
734 735
  rdata->parent = parent;
  rdata->loop = NULL;
736
  rdata->destroy = print_response_data_free;
737
  
738
  if (show_dialog)
Alexander Larsson's avatar
Alexander Larsson committed
739 740 741
    {
      pd = get_print_dialog (op, parent);
      gtk_window_set_modal (GTK_WINDOW (pd), TRUE);
742

Alexander Larsson's avatar
Alexander Larsson committed
743 744 745 746 747 748 749 750 751 752 753
      g_signal_connect (pd, "response", 
			G_CALLBACK (handle_print_response), rdata);
      
      gtk_window_present (GTK_WINDOW (pd));
    }
  else
    {
      printer_name = NULL;
      if (op->priv->print_settings)
	printer_name = gtk_print_settings_get_printer (op->priv->print_settings);
      
Matthias Clasen's avatar
Matthias Clasen committed
754
      find_printer (printer_name, (GFunc) found_printer, rdata);
Alexander Larsson's avatar
Alexander Larsson committed
755
    }
756 757
}

Matthias Clasen's avatar
Matthias Clasen committed
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792
static cairo_status_t
write_preview (void                *closure,
               const unsigned char *data,
               unsigned int         length)
{
  gint fd = GPOINTER_TO_INT (closure);
  gssize written;
  
  while (length > 0) 
    {
      written = write (fd, data, length);

      if (written == -1)
	{
	  if (errno == EAGAIN || errno == EINTR)
	    continue;
	  
	  return CAIRO_STATUS_WRITE_ERROR;
	}    

      data += written;
      length -= written;
    }

  return CAIRO_STATUS_SUCCESS;
}

static void
close_preview (void *data)
{
  gint fd = GPOINTER_TO_INT (data);

  close (fd);
}

793 794
cairo_surface_t *
_gtk_print_operation_platform_backend_create_preview_surface (GtkPrintOperation *op,
Matthias Clasen's avatar
Matthias Clasen committed
795 796 797
							      GtkPageSetup      *page_setup,
							      gdouble           *dpi_x,
							      gdouble           *dpi_y,
798
							      gchar            **target)
799
{
Matthias Clasen's avatar
Matthias Clasen committed
800 801
  gchar *filename;
  gint fd;
802
  GtkPaperSize *paper_size;
Matthias Clasen's avatar
Matthias Clasen committed
803
  gdouble w, h;
Matthias Clasen's avatar
Matthias Clasen committed
804 805
  cairo_surface_t *surface;
  static cairo_user_data_key_t key;
806
  
Matthias Clasen's avatar
Matthias Clasen committed
807 808
  filename = g_build_filename (g_get_tmp_dir (), "previewXXXXXX.pdf", NULL);
  fd = g_mkstemp (filename);
809 810 811 812 813 814 815

  if (fd < 0)
    {
      g_free (filename);
      return NULL;
    }

Matthias Clasen's avatar
Matthias Clasen committed
816
  *target = filename;
817
  
818 819 820 821 822
  paper_size = gtk_page_setup_get_paper_size (page_setup);
  w = gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS);
  h = gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS);
    
  *dpi_x = *dpi_y = 72;
823
  surface = cairo_pdf_surface_create_for_stream (write_preview, GINT_TO_POINTER (fd), w, h);
Matthias Clasen's avatar
Matthias Clasen committed
824 825 826 827
 
  cairo_surface_set_user_data (surface, &key, GINT_TO_POINTER (fd), close_preview);

  return surface;
828 829 830 831
}

void
_gtk_print_operation_platform_backend_preview_start_page (GtkPrintOperation *op,
832 833
							  cairo_surface_t   *surface,
							  cairo_t           *cr)
834 835 836 837 838
{
}

void
_gtk_print_operation_platform_backend_preview_end_page (GtkPrintOperation *op,
839 840
							cairo_surface_t   *surface,
							cairo_t           *cr)
841 842
{
  cairo_show_page (cr);
843 844 845 846
}

void
_gtk_print_operation_platform_backend_resize_preview_surface (GtkPrintOperation *op,
Matthias Clasen's avatar
Matthias Clasen committed
847 848
							      GtkPageSetup      *page_setup,
							      cairo_surface_t   *surface)
849 850
{
  GtkPaperSize *paper_size;
Matthias Clasen's avatar
Matthias Clasen committed
851
  gdouble w, h;
852 853 854 855 856 857 858 859
  
  paper_size = gtk_page_setup_get_paper_size (page_setup);
  w = gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS);
  h = gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS);
  cairo_pdf_surface_set_size (surface, w, h);
}


860 861
GtkPrintOperationResult
_gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
862
						  gboolean           show_dialog,
863
						  GtkWindow         *parent,
864
						  gboolean          *do_print)
865 866 867 868
 {
  GtkWidget *pd;
  PrintResponseData rdata;
  gint response;  
Matthias Clasen's avatar
Matthias Clasen committed
869
  const gchar *printer_name;
870 871 872
   
  rdata.op = op;
  rdata.do_print = FALSE;
873
  rdata.do_preview = FALSE;
874 875 876
  rdata.result = GTK_PRINT_OPERATION_RESULT_CANCEL;
  rdata.print_cb = NULL;
  rdata.destroy = NULL;
Alexander Larsson's avatar
Alexander Larsson committed
877 878
  rdata.parent = parent;
  rdata.loop = NULL;
879

880
  if (show_dialog)
Alexander Larsson's avatar
Alexander Larsson committed
881 882
    {
      pd = get_print_dialog (op, parent);
883

Alexander Larsson's avatar
Alexander Larsson committed
884 885 886 887 888 889 890 891 892 893 894 895
      response = gtk_dialog_run (GTK_DIALOG (pd));
      handle_print_response (pd, response, &rdata);
    }
  else
    {
      printer_name = NULL;
      if (op->priv->print_settings)
	printer_name = gtk_print_settings_get_printer (op->priv->print_settings);
      
      rdata.loop = g_main_loop_new (NULL, FALSE);
      find_printer (printer_name,
		    (GFunc) found_printer, &rdata);
896

Alexander Larsson's avatar
Alexander Larsson committed
897 898 899
      GDK_THREADS_LEAVE ();  
      g_main_loop_run (rdata.loop);
      GDK_THREADS_ENTER ();  
900

Alexander Larsson's avatar
Alexander Larsson committed
901
      g_main_loop_unref (rdata.loop);
902
      rdata.loop = NULL;
Alexander Larsson's avatar
Alexander Larsson committed
903
    }
904

Alexander Larsson's avatar
Alexander Larsson committed
905 906
  *do_print = rdata.do_print;
  
907 908 909 910
  return rdata.result;
}


Matthias Clasen's avatar
Matthias Clasen committed
911 912
typedef struct 
{
913 914 915 916
  GtkPageSetup         *page_setup;
  GtkPageSetupDoneFunc  done_cb;
  gpointer              data;
  GDestroyNotify        destroy;
917 918 919 920 921 922 923
} PageSetupResponseData;

static void
page_setup_data_free (gpointer data)
{
  PageSetupResponseData *rdata = data;

Matthias Clasen's avatar
Matthias Clasen committed
924 925 926
  if (rdata->page_setup)
    g_object_unref (rdata->page_setup);

927 928 929 930 931 932 933 934 935 936
  g_free (rdata);
}

static void
handle_page_setup_response (GtkWidget *dialog,
			    gint       response,
			    gpointer   data)
{
  GtkPageSetupUnixDialog *psd;
  PageSetupResponseData *rdata = data;
937

938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965
  psd = GTK_PAGE_SETUP_UNIX_DIALOG (dialog);
  if (response == GTK_RESPONSE_OK)
    rdata->page_setup = gtk_page_setup_unix_dialog_get_page_setup (psd);

  gtk_widget_destroy (dialog);

  if (rdata->done_cb)
    rdata->done_cb (rdata->page_setup, rdata->data);

  if (rdata->destroy)
    rdata->destroy (rdata);
}

static GtkWidget *
get_page_setup_dialog (GtkWindow        *parent,
		       GtkPageSetup     *page_setup,
		       GtkPrintSettings *settings)
{
  GtkWidget *dialog;

  dialog = gtk_page_setup_unix_dialog_new (NULL, parent);
  if (page_setup)
    gtk_page_setup_unix_dialog_set_page_setup (GTK_PAGE_SETUP_UNIX_DIALOG (dialog),
					       page_setup);
  gtk_page_setup_unix_dialog_set_print_settings (GTK_PAGE_SETUP_UNIX_DIALOG (dialog),
						 settings);

  return dialog;
966 967
}

968 969 970 971 972 973
/**
 * gtk_print_run_page_setup_dialog:
 * @parent: transient parent, or %NULL
 * @page_setup: an existing #GtkPageSetup, or %NULL
 * @settings: a #GtkPrintSettings
 * 
974 975 976 977 978 979 980 981
 * Runs a page setup dialog, letting the user modify the values from 
 * @page_setup. If the user cancels the dialog, the returned #GtkPageSetup 
 * is identical to the passed in @page_setup, otherwise it contains the 
 * modifications done in the dialog.
 *
 * Note that this function may use a recursive mainloop to show the page
 * setup dialog. See gtk_print_run_page_setup_dialog_async() if this is 
 * a problem.
982 983 984 985 986
 * 
 * Return value: a new #GtkPageSetup
 *
 * Since: 2.10
 */
987 988 989 990 991 992
GtkPageSetup *
gtk_print_run_page_setup_dialog (GtkWindow        *parent,
				 GtkPageSetup     *page_setup,
				 GtkPrintSettings *settings)
{
  GtkWidget *dialog;
993 994
  gint response;
  PageSetupResponseData rdata;  
995
  
996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
  rdata.page_setup = NULL;
  rdata.done_cb = NULL;
  rdata.data = NULL;
  rdata.destroy = NULL;

  dialog = get_page_setup_dialog (parent, page_setup, settings);
  response = gtk_dialog_run (GTK_DIALOG (dialog));
  handle_page_setup_response (dialog, response, &rdata);
 
  if (rdata.page_setup)
    return rdata.page_setup;
  else if (page_setup)
    return gtk_page_setup_copy (page_setup);
  else
    return gtk_page_setup_new ();
1011 1012
}

1013 1014 1015 1016 1017 1018 1019 1020
/**
 * gtk_print_run_page_setup_dialog_async:
 * @parent: transient parent, or %NULL
 * @page_setup: an existing #GtkPageSetup, or %NULL
 * @settings: a #GtkPrintSettings
 * @done_cb: a function to call when the user saves the modified page setup
 * @data: user data to pass to @done_cb
 * 
Matthias Clasen's avatar
Matthias Clasen committed
1021
 * Runs a page setup dialog, letting the user modify the values from @page_setup. 
1022
 *
Matthias Clasen's avatar
Matthias Clasen committed
1023 1024 1025
 * In contrast to gtk_print_run_page_setup_dialog(), this function  returns after 
 * showing the page setup dialog on platforms that support this, and calls @done_cb 
 * from a signal handler for the ::response signal of the dialog.