gtkprintoperation-unix.c 36.6 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
Matthias Clasen's avatar
Matthias Clasen committed
2
 * gtkprintoperation-unix.c: Print Operation Details for Unix
3
 *                           and Unix-like platforms
4
5
6
7
8
9
10
11
12
13
14
15
16
 * 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
Javier Jardón's avatar
Javier Jardón committed
17
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18
19
20
21
22
23
24
25
26
 */

#include "config.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
27
#include <errno.h>
Matthias Clasen's avatar
Matthias Clasen committed
28
#include <stdlib.h>
29
#include <fcntl.h>
30

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

36
37
#include <cairo-pdf.h>
#include <cairo-ps.h>
38
#include "gtkprivate.h"
39
40
#include "gtkprintunixdialog.h"
#include "gtkpagesetupunixdialog.h"
41
#include "gtkprintbackendprivate.h"
42
43
#include "gtkprinter.h"
#include "gtkprintjob.h"
44
#include "gtklabel.h"
Matthias Clasen's avatar
Matthias Clasen committed
45
#include <glib/gi18n-lib.h>
46

47

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

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

Matthias Clasen's avatar
Matthias Clasen committed
59

60
61
} GtkPrintOperationUnix;

Alexander Larsson's avatar
Alexander Larsson committed
62
63
64
typedef struct _PrinterFinder PrinterFinder;

static void printer_finder_free (PrinterFinder *finder);
Benjamin Otte's avatar
Benjamin Otte committed
65
static void find_printer        (const char    *printer,
Alexander Larsson's avatar
Alexander Larsson committed
66
67
68
				 GFunc          func,
				 gpointer       data);

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

79
  op_unix = op->priv->platform_data;
Matthias Clasen's avatar
Matthias Clasen committed
80

81
82
83
84
  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);
Matthias Clasen's avatar
Matthias Clasen committed
85

86
  type = cairo_surface_get_type (op_unix->surface);
87

88
89
90
91
  if ((op->priv->manual_number_up < 2) ||
      (op->priv->page_position % op->priv->manual_number_up == 0))
    {
      if (type == CAIRO_SURFACE_TYPE_PS)
92
93
94
95
96
97
98
99
100
101
102
103
104
105
        {
          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;
106
107
              default:
                break;
108
109
            }
         }
110
      else if (type == CAIRO_SURFACE_TYPE_PDF)
111
        {
112
113
114
115
116
          if (!op->priv->manual_orientation)
            {
              w = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_POINTS);
              h = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_POINTS);
            }
117
118
          cairo_pdf_surface_set_size (op_unix->surface, w, h);
        }
119
    }
120
121
122
123
}

static void
unix_end_page (GtkPrintOperation *op,
Matthias Clasen's avatar
Matthias Clasen committed
124
	       GtkPrintContext   *print_context)
125
126
127
{
  cairo_t *cr;

128
  cr = gtk_print_context_get_cairo_context (print_context);
129
130
131
132
133

  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);
134
135
136
137
138
139
140
}

static void
op_unix_free (GtkPrintOperationUnix *op_unix)
{
  if (op_unix->job)
    {
141
142
143
      if (op_unix->job_status_changed_tag > 0)
        g_signal_handler_disconnect (op_unix->job,
				     op_unix->job_status_changed_tag);
144
145
146
147
148
149
      g_object_unref (op_unix->job);
    }

  g_free (op_unix);
}

Benjamin Otte's avatar
Benjamin Otte committed
150
151
152
153
static char *
shell_command_substitute_file (const char *cmd,
			       const char *pdf_filename,
			       const char *settings_filename,
154
155
                               gboolean    *pdf_filename_replaced,
                               gboolean    *settings_filename_replaced)
156
{
Benjamin Otte's avatar
Benjamin Otte committed
157
  const char *inptr, *start;
158
159
160
  GString *final;

  g_return_val_if_fail (cmd != NULL, NULL);
161
162
  g_return_val_if_fail (pdf_filename != NULL, NULL);
  g_return_val_if_fail (settings_filename != NULL, NULL);
163
164
165

  final = g_string_new (NULL);

166
167
  *pdf_filename_replaced = FALSE;
  *settings_filename_replaced = FALSE;
168

169
  start = inptr = cmd;
Matthias Clasen's avatar
Matthias Clasen committed
170
  while ((inptr = strchr (inptr, '%')) != NULL)
171
172
173
    {
      g_string_append_len (final, start, inptr - start);
      inptr++;
Matthias Clasen's avatar
Matthias Clasen committed
174
      switch (*inptr)
175
176
        {
          case 'f':
177
178
179
180
181
182
183
            g_string_append (final, pdf_filename);
            *pdf_filename_replaced = TRUE;
            break;

          case 's':
            g_string_append (final, settings_filename);
            *settings_filename_replaced = TRUE;
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
            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);

202
  return g_string_free (final, FALSE);
203
204
}

205
206
207
208
static void
gtk_print_operation_unix_launch_preview (GtkPrintOperation *op,
                                         cairo_surface_t   *surface,
                                         GtkWindow         *parent,
Benjamin Otte's avatar
Benjamin Otte committed
209
                                         const char        *filename)
210
{
Matthias Clasen's avatar
Matthias Clasen committed
211
  GAppInfo *appinfo;
Matthias Clasen's avatar
Matthias Clasen committed
212
  GdkAppLaunchContext *context;
Benjamin Otte's avatar
Benjamin Otte committed
213
214
  char *cmd;
  char *preview_cmd;
215
  GtkSettings *settings;
Marek Kašík's avatar
Marek Kašík committed
216
  GtkPrintSettings *print_settings = NULL;
217
218
  GtkPageSetup *page_setup;
  GKeyFile *key_file = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
219
  char *data = NULL;
220
  gsize data_len;
Benjamin Otte's avatar
Benjamin Otte committed
221
222
223
  char *settings_filename = NULL;
  char *quoted_filename;
  char *quoted_settings_filename;
224
225
  gboolean filename_used = FALSE;
  gboolean settings_used = FALSE;
226
  GdkDisplay *display;
227
  GError *error = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
228
  int fd;
229
  gboolean retval;
230

231
  cairo_surface_destroy (surface);
232

233
  if (parent)
234
    display = gtk_widget_get_display (GTK_WIDGET (parent));
235
  else
236
    display = gdk_display_get_default ();
237

238
  fd = g_file_open_tmp ("settingsXXXXXX.ini", &settings_filename, &error);
Matthias Clasen's avatar
Matthias Clasen committed
239
  if (fd < 0)
240
    goto out;
241

242
  key_file = g_key_file_new ();
Matthias Clasen's avatar
Matthias Clasen committed
243

Marek Kašík's avatar
Marek Kašík committed
244
245
246
247
248
249
250
251
252
253
  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);

254
      /*  These removals are necessary because cups-* settings have higher priority
Marek Kašík's avatar
Marek Kašík committed
255
256
257
258
259
260
261
262
263
264
265
       *  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);
    }
266
267
268

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

270
271
  g_key_file_set_string (key_file, "Print Job", "title", op->priv->job_name);

272
273
274
275
276
  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);
277
  if (!retval)
278
279
    goto out;

280
  settings = gtk_settings_get_for_display (display);
281
282
283
  g_object_get (settings, "gtk-print-preview-command", &preview_cmd, NULL);

  quoted_filename = g_shell_quote (filename);
284
285
  quoted_settings_filename = g_shell_quote (settings_filename);
  cmd = shell_command_substitute_file (preview_cmd, quoted_filename, quoted_settings_filename, &filename_used, &settings_used);
Matthias Clasen's avatar
Matthias Clasen committed
286
287
288
289
290

  appinfo = g_app_info_create_from_commandline (cmd,
                                                "Print Preview",
                                                G_APP_INFO_CREATE_NONE,
                                                &error);
291

292
293
294
295
296
  g_free (preview_cmd);
  g_free (quoted_filename);
  g_free (quoted_settings_filename);
  g_free (cmd);

Matthias Clasen's avatar
Matthias Clasen committed
297
  if (error != NULL)
298
299
    goto out;

300
  context = gdk_display_get_app_launch_context (display);
Matthias Clasen's avatar
Matthias Clasen committed
301
  g_app_info_launch (appinfo, NULL, G_APP_LAUNCH_CONTEXT (context), &error);
302

Matthias Clasen's avatar
Matthias Clasen committed
303
304
  g_object_unref (context);
  g_object_unref (appinfo);
305

306
307
  if (error != NULL)
    {
Benjamin Otte's avatar
Benjamin Otte committed
308
      char * uri;
309

310
      g_warning ("Error launching preview: %s", error->message);
311
312
313
314

      g_error_free (error);
      error = NULL;
      uri = g_filename_to_uri (filename, NULL, NULL);
315
      gtk_show_uri (parent, uri, GDK_CURRENT_TIME);
316
317
318
      g_free (uri);
    }

319
320
321
 out:
  if (error != NULL)
    {
322
323
324
325
      if (op->priv->error == NULL)
        op->priv->error = error;
      else
        g_error_free (error);
326

Matthias Clasen's avatar
Matthias Clasen committed
327
      filename_used = FALSE;
328
      settings_used = FALSE;
Matthias Clasen's avatar
Matthias Clasen committed
329
   }
330

331
332
333
334
335
336
  if (!filename_used)
    g_unlink (filename);

  if (!settings_used)
    g_unlink (settings_filename);

337
338
  if (fd > 0)
    close (fd);
Matthias Clasen's avatar
Matthias Clasen committed
339

340
341
342
  if (key_file)
    g_key_file_free (key_file);
  g_free (data);
343
  g_free (settings_filename);
344
345
}

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

354
355
  if (error != NULL && op->priv->error == NULL)
    op->priv->error = g_error_copy (error);
Alexander Larsson's avatar
Alexander Larsson committed
356
357

  op_unix->data_sent = TRUE;
358

Alexander Larsson's avatar
Alexander Larsson committed
359
360
  if (op_unix->loop)
    g_main_loop_quit (op_unix->loop);
361
362

  g_object_unref (op);
363
364
365
}

static void
Alexander Larsson's avatar
Alexander Larsson committed
366
unix_end_run (GtkPrintOperation *op,
367
368
	      gboolean           wait,
	      gboolean           cancelled)
369
370
{
  GtkPrintOperationUnix *op_unix = op->priv->platform_data;
Alexander Larsson's avatar
Alexander Larsson committed
371

372
  cairo_surface_finish (op_unix->surface);
Matthias Clasen's avatar
Matthias Clasen committed
373

374
375
376
  if (cancelled)
    return;

Alexander Larsson's avatar
Alexander Larsson committed
377
378
  if (wait)
    op_unix->loop = g_main_loop_new (NULL, FALSE);
Matthias Clasen's avatar
Matthias Clasen committed
379

380
  /* TODO: Check for error */
381
  if (op_unix->job != NULL)
382
383
384
    {
      g_object_ref (op);
      gtk_print_job_send (op_unix->job,
Matthias Clasen's avatar
Matthias Clasen committed
385
                          unix_finish_send,
386
387
                          op, NULL);
    }
Alexander Larsson's avatar
Alexander Larsson committed
388
389
390

  if (wait)
    {
391
      g_object_ref (op);
Alexander Larsson's avatar
Alexander Larsson committed
392
      if (!op_unix->data_sent)
Emmanuele Bassi's avatar
Emmanuele Bassi committed
393
	g_main_loop_run (op_unix->loop);
Alexander Larsson's avatar
Alexander Larsson committed
394
      g_main_loop_unref (op_unix->loop);
395
396
      op_unix->loop = NULL;
      g_object_unref (op);
Alexander Larsson's avatar
Alexander Larsson committed
397
    }
398
399
400
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
401
job_status_changed_cb (GtkPrintJob       *job,
Matthias Clasen's avatar
Matthias Clasen committed
402
		       GtkPrintOperation *op)
403
404
405
406
{
  _gtk_print_operation_set_status (op, gtk_print_job_get_status (job), NULL);
}

407

408
static void
Matthias Clasen's avatar
Matthias Clasen committed
409
print_setup_changed_cb (GtkPrintUnixDialog *print_dialog,
410
411
                        GParamSpec         *pspec,
                        gpointer            user_data)
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
{
  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);
}

428
429
430
static GtkWidget *
get_print_dialog (GtkPrintOperation *op,
                  GtkWindow         *parent)
431
{
432
  GtkPrintOperationPrivate *priv = op->priv;
433
  GtkWidget *pd, *label;
Benjamin Otte's avatar
Benjamin Otte committed
434
  const char *custom_tab_label;
435
436
437

  pd = gtk_print_unix_dialog_new (NULL, parent);

438
439
440
441
442
  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 |
443
						 GTK_PRINT_CAPABILITY_SCALE |
444
445
446
						 GTK_PRINT_CAPABILITY_PREVIEW |
						 GTK_PRINT_CAPABILITY_NUMBER_UP |
						 GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT);
447

448
  if (priv->print_settings)
449
    gtk_print_unix_dialog_set_settings (GTK_PRINT_UNIX_DIALOG (pd),
450
					priv->print_settings);
451

452
  if (priv->default_page_setup)
Matthias Clasen's avatar
Matthias Clasen committed
453
    gtk_print_unix_dialog_set_page_setup (GTK_PRINT_UNIX_DIALOG (pd),
454
                                          priv->default_page_setup);
455

456
457
458
  gtk_print_unix_dialog_set_embed_page_setup (GTK_PRINT_UNIX_DIALOG (pd),
                                              priv->embed_page_setup);

Matthias Clasen's avatar
Matthias Clasen committed
459
  gtk_print_unix_dialog_set_current_page (GTK_PRINT_UNIX_DIALOG (pd),
460
461
                                          priv->current_page);

Marek Kašík's avatar
Marek Kašík committed
462
463
464
465
466
467
  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);

468
  g_signal_emit_by_name (op, "create-custom-widget",
Matthias Clasen's avatar
Matthias Clasen committed
469
470
			 &priv->custom_widget);

Matthias Clasen's avatar
Matthias Clasen committed
471
  if (priv->custom_widget)
Matthias Clasen's avatar
Matthias Clasen committed
472
473
    {
      custom_tab_label = priv->custom_tab_label;
Matthias Clasen's avatar
Matthias Clasen committed
474

Matthias Clasen's avatar
Matthias Clasen committed
475
476
477
478
479
480
481
482
      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);
Matthias Clasen's avatar
Matthias Clasen committed
483

Matthias Clasen's avatar
Matthias Clasen committed
484
      gtk_print_unix_dialog_add_custom_tab (GTK_PRINT_UNIX_DIALOG (pd),
Matthias Clasen's avatar
Matthias Clasen committed
485
					    priv->custom_widget, label);
486

487
488
      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
489
    }
Matthias Clasen's avatar
Matthias Clasen committed
490

491
492
  return pd;
}
Matthias Clasen's avatar
Matthias Clasen committed
493
494

typedef struct
Matthias Clasen's avatar
Matthias Clasen committed
495
{
496
497
  GtkPrintOperation           *op;
  gboolean                     do_print;
498
  gboolean                     do_preview;
499
500
501
  GtkPrintOperationResult      result;
  GtkPrintOperationPrintFunc   print_cb;
  GDestroyNotify               destroy;
Alexander Larsson's avatar
Alexander Larsson committed
502
503
  GtkWindow                   *parent;
  GMainLoop                   *loop;
504
505
506
507
508
509
510
511
512
513
514
515
} 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
516
finish_print (PrintResponseData *rdata,
Matthias Clasen's avatar
Matthias Clasen committed
517
518
	      GtkPrinter        *printer,
	      GtkPageSetup      *page_setup,
519
520
	      GtkPrintSettings  *settings,
	      gboolean           page_setup_set)
521
522
{
  GtkPrintOperation *op = rdata->op;
523
  GtkPrintOperationPrivate *priv = op->priv;
Matthias Clasen's avatar
Matthias Clasen committed
524
  GtkPrintJob *job;
525
  double top, bottom, left, right;
Matthias Clasen's avatar
Matthias Clasen committed
526

Alexander Larsson's avatar
Alexander Larsson committed
527
  if (rdata->do_print)
528
529
    {
      gtk_print_operation_set_print_settings (op, settings);
530
      priv->print_context = _gtk_print_context_new (op);
531

532
533
      if (gtk_print_settings_get_number_up (settings) < 2)
        {
534
535
	  if (printer && (gtk_printer_get_hard_margins_for_paper_size (printer, gtk_page_setup_get_paper_size (page_setup), &top, &bottom, &left, &right) ||
			  gtk_printer_get_hard_margins (printer, &top, &bottom, &left, &right)))
536
537
538
539
540
541
542
543
544
545
	    _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);
	}
546

547
548
549
      if (page_setup != NULL &&
          (gtk_print_operation_get_default_page_setup (op) == NULL ||
           page_setup_set))
550
551
        gtk_print_operation_set_default_page_setup (op, page_setup);

552
      _gtk_print_context_set_page_setup (priv->print_context, page_setup);
553

554
      if (!rdata->do_preview)
555
        {
556
557
	  GtkPrintOperationUnix *op_unix;
	  cairo_t *cr;
Matthias Clasen's avatar
Matthias Clasen committed
558

559
	  op_unix = g_new0 (GtkPrintOperationUnix, 1);
560
561
562
	  priv->platform_data = op_unix;
	  priv->free_platform_data = (GDestroyNotify) op_unix_free;
	  op_unix->parent = rdata->parent;
Matthias Clasen's avatar
Matthias Clasen committed
563

564
565
566
	  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
567

Matthias Clasen's avatar
Matthias Clasen committed
568
569
570
	  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);
Matthias Clasen's avatar
Matthias Clasen committed
571

572
	  op_unix->surface = gtk_print_job_get_surface (job, &priv->error);
Matthias Clasen's avatar
Matthias Clasen committed
573
	  if (op_unix->surface == NULL)
Matthias Clasen's avatar
Matthias Clasen committed
574
            {
575
	      rdata->result = GTK_PRINT_OPERATION_RESULT_ERROR;
Matthias Clasen's avatar
Matthias Clasen committed
576
577
578
	      rdata->do_print = FALSE;
	      goto out;
            }
Matthias Clasen's avatar
Matthias Clasen committed
579

580
	  cr = cairo_create (op_unix->surface);
Matthias Clasen's avatar
Matthias Clasen committed
581
	  gtk_print_context_set_cairo_context (priv->print_context, cr, 72, 72);
582
583
	  cairo_destroy (cr);

Matthias Clasen's avatar
Matthias Clasen committed
584
          _gtk_print_operation_set_status (op, gtk_print_job_get_status (job), NULL);
Matthias Clasen's avatar
Matthias Clasen committed
585

586
          op_unix->job_status_changed_tag =
Matthias Clasen's avatar
Matthias Clasen committed
587
	    g_signal_connect (job, "status-changed",
588
			      G_CALLBACK (job_status_changed_cb), op);
Matthias Clasen's avatar
Matthias Clasen committed
589

590
591
592
593
594
595
596
597
598
599
          priv->print_pages = gtk_print_job_get_pages (job);
          priv->page_ranges = gtk_print_job_get_page_ranges (job, &priv->num_page_ranges);
          priv->manual_num_copies = gtk_print_job_get_num_copies (job);
          priv->manual_collation = gtk_print_job_get_collate (job);
          priv->manual_reverse = gtk_print_job_get_reverse (job);
          priv->manual_page_set = gtk_print_job_get_page_set (job);
          priv->manual_scale = gtk_print_job_get_scale (job);
          priv->manual_orientation = gtk_print_job_get_rotate (job);
          priv->manual_number_up = gtk_print_job_get_n_up (job);
          priv->manual_number_up_layout = gtk_print_job_get_n_up_layout (job);
600
        }
Matthias Clasen's avatar
Matthias Clasen committed
601
    }
602
 out:
603
  if (rdata->print_cb)
Matthias Clasen's avatar
Matthias Clasen committed
604
    rdata->print_cb (op, rdata->parent, rdata->do_print, rdata->result);
605
606
607
608
609

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

Matthias Clasen's avatar
Matthias Clasen committed
610
static void
Alexander Larsson's avatar
Alexander Larsson committed
611
handle_print_response (GtkWidget *dialog,
Benjamin Otte's avatar
Benjamin Otte committed
612
		       int        response,
Alexander Larsson's avatar
Alexander Larsson committed
613
614
615
616
617
618
619
		       gpointer   data)
{
  GtkPrintUnixDialog *pd = GTK_PRINT_UNIX_DIALOG (dialog);
  PrintResponseData *rdata = data;
  GtkPrintSettings *settings = NULL;
  GtkPageSetup *page_setup = NULL;
  GtkPrinter *printer = NULL;
620
  gboolean page_setup_set = FALSE;
Alexander Larsson's avatar
Alexander Larsson committed
621
622
623
624

  if (response == GTK_RESPONSE_OK)
    {
      printer = gtk_print_unix_dialog_get_selected_printer (GTK_PRINT_UNIX_DIALOG (pd));
625
626
627

      rdata->result = GTK_PRINT_OPERATION_RESULT_APPLY;
      rdata->do_preview = FALSE;
628
629
      if (printer != NULL)
	rdata->do_print = TRUE;
Matthias Clasen's avatar
Matthias Clasen committed
630
    }
631
632
633
  else if (response == GTK_RESPONSE_APPLY)
    {
      /* print preview */
634
635
      rdata->result = GTK_PRINT_OPERATION_RESULT_APPLY;
      rdata->do_preview = TRUE;
Alexander Larsson's avatar
Alexander Larsson committed
636
      rdata->do_print = TRUE;
637
638

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

641
642
  if (rdata->do_print)
    {
Alexander Larsson's avatar
Alexander Larsson committed
643
644
      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));
645
      page_setup_set = gtk_print_unix_dialog_get_page_setup_set (GTK_PRINT_UNIX_DIALOG (pd));
646
647
648
649
650

      /* Set new print settings now so that custom-widget options
       * can be added to the settings in the callback
       */
      gtk_print_operation_set_print_settings (rdata->op, settings);
651
      g_signal_emit_by_name (rdata->op, "custom-widget-apply", rdata->op->priv->custom_widget);
652
    }
Matthias Clasen's avatar
Matthias Clasen committed
653

654
655
  if (rdata->loop)
    g_main_loop_quit (rdata->loop);
Matthias Clasen's avatar
Matthias Clasen committed
656

657
  finish_print (rdata, printer, page_setup, settings, page_setup_set);
Alexander Larsson's avatar
Alexander Larsson committed
658
659
660

  if (settings)
    g_object_unref (settings);
Matthias Clasen's avatar
Matthias Clasen committed
661

Matthias Clasen's avatar
Matthias Clasen committed
662
  gtk_window_destroy (GTK_WINDOW (pd));
Alexander Larsson's avatar
Alexander Larsson committed
663
664
665
666
}


static void
Matthias Clasen's avatar
Matthias Clasen committed
667
found_printer (GtkPrinter        *printer,
Alexander Larsson's avatar
Alexander Larsson committed
668
669
670
671
672
673
	       PrintResponseData *rdata)
{
  GtkPrintOperation *op = rdata->op;
  GtkPrintOperationPrivate *priv = op->priv;
  GtkPrintSettings *settings = NULL;
  GtkPageSetup *page_setup = NULL;
674

Alexander Larsson's avatar
Alexander Larsson committed
675
676
677
  if (rdata->loop)
    g_main_loop_quit (rdata->loop);

Matthias Clasen's avatar
Matthias Clasen committed
678
  if (printer != NULL)
Matthias Clasen's avatar
Matthias Clasen committed
679
    {
680
      rdata->result = GTK_PRINT_OPERATION_RESULT_APPLY;
Alexander Larsson's avatar
Alexander Larsson committed
681
682
683
684
685
686
687
688
689
690

      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));
Matthias Clasen's avatar
Matthias Clasen committed
691

Alexander Larsson's avatar
Alexander Larsson committed
692
693
694
695
696
      if (priv->default_page_setup)
	page_setup = gtk_page_setup_copy (priv->default_page_setup);
      else
	page_setup = gtk_page_setup_new ();
  }
Matthias Clasen's avatar
Matthias Clasen committed
697

698
  finish_print (rdata, printer, page_setup, settings, FALSE);
Alexander Larsson's avatar
Alexander Larsson committed
699
700
701

  if (settings)
    g_object_unref (settings);
Matthias Clasen's avatar
Matthias Clasen committed
702

Alexander Larsson's avatar
Alexander Larsson committed
703
704
705
706
  if (page_setup)
    g_object_unref (page_setup);
}

707
708
709
710
711
static void
gtk_print_operation_unix_run_dialog_async (GtkPrintOperation          *op,
                                           gboolean                    show_dialog,
                                           GtkWindow                  *parent,
                                           GtkPrintOperationPrintFunc  print_cb)
712
713
714
{
  GtkWidget *pd;
  PrintResponseData *rdata;
Benjamin Otte's avatar
Benjamin Otte committed
715
  const char *printer_name;
716
717
718
719

  rdata = g_new (PrintResponseData, 1);
  rdata->op = g_object_ref (op);
  rdata->do_print = FALSE;
720
  rdata->do_preview = FALSE;
721
722
  rdata->result = GTK_PRINT_OPERATION_RESULT_CANCEL;
  rdata->print_cb = print_cb;
Alexander Larsson's avatar
Alexander Larsson committed
723
724
  rdata->parent = parent;
  rdata->loop = NULL;
725
  rdata->destroy = print_response_data_free;
Matthias Clasen's avatar
Matthias Clasen committed
726

727
  if (show_dialog)
Alexander Larsson's avatar
Alexander Larsson committed
728
729
730
    {
      pd = get_print_dialog (op, parent);
      gtk_window_set_modal (GTK_WINDOW (pd), TRUE);
731

Matthias Clasen's avatar
Matthias Clasen committed
732
      g_signal_connect (pd, "response",
Alexander Larsson's avatar
Alexander Larsson committed
733
			G_CALLBACK (handle_print_response), rdata);
734

Alexander Larsson's avatar
Alexander Larsson committed
735
736
737
738
739
740
741
      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
742

Matthias Clasen's avatar
Matthias Clasen committed
743
      find_printer (printer_name, (GFunc) found_printer, rdata);
Alexander Larsson's avatar
Alexander Larsson committed
744
    }
745
746
}

Matthias Clasen's avatar
Matthias Clasen committed
747
748
749
750
751
static cairo_status_t
write_preview (void                *closure,
               const unsigned char *data,
               unsigned int         length)
{
Benjamin Otte's avatar
Benjamin Otte committed
752
  int fd = GPOINTER_TO_INT (closure);
Matthias Clasen's avatar
Matthias Clasen committed
753
  gssize written;
Matthias Clasen's avatar
Matthias Clasen committed
754
755

  while (length > 0)
Matthias Clasen's avatar
Matthias Clasen committed
756
757
758
759
760
761
762
    {
      written = write (fd, data, length);

      if (written == -1)
	{
	  if (errno == EAGAIN || errno == EINTR)
	    continue;
Matthias Clasen's avatar
Matthias Clasen committed
763

Matthias Clasen's avatar
Matthias Clasen committed
764
	  return CAIRO_STATUS_WRITE_ERROR;
Matthias Clasen's avatar
Matthias Clasen committed
765
	}
Matthias Clasen's avatar
Matthias Clasen committed
766
767
768
769
770
771
772
773
774
775
776

      data += written;
      length -= written;
    }

  return CAIRO_STATUS_SUCCESS;
}

static void
close_preview (void *data)
{
Benjamin Otte's avatar
Benjamin Otte committed
777
  int fd = GPOINTER_TO_INT (data);
Matthias Clasen's avatar
Matthias Clasen committed
778
779
780
781

  close (fd);
}

782
783
784
static cairo_surface_t *
gtk_print_operation_unix_create_preview_surface (GtkPrintOperation *op,
                                                 GtkPageSetup      *page_setup,
785
786
                                                 double            *dpi_x,
                                                 double            *dpi_y,
Benjamin Otte's avatar
Benjamin Otte committed
787
                                                 char             **target)
788
{
Benjamin Otte's avatar
Benjamin Otte committed
789
  char *filename;
Benjamin Otte's avatar
Benjamin Otte committed
790
  int fd;
791
  GtkPaperSize *paper_size;
792
  double w, h;
Matthias Clasen's avatar
Matthias Clasen committed
793
794
  cairo_surface_t *surface;
  static cairo_user_data_key_t key;
Matthias Clasen's avatar
Matthias Clasen committed
795

Matthias Clasen's avatar
Matthias Clasen committed
796
797
  filename = g_build_filename (g_get_tmp_dir (), "previewXXXXXX.pdf", NULL);
  fd = g_mkstemp (filename);
798
799
800
801
802
803
804

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

Matthias Clasen's avatar
Matthias Clasen committed
805
  *target = filename;
Matthias Clasen's avatar
Matthias Clasen committed
806

807
808
809
  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);
Matthias Clasen's avatar
Matthias Clasen committed
810

811
  *dpi_x = *dpi_y = 72;
812
  surface = cairo_pdf_surface_create_for_stream (write_preview, GINT_TO_POINTER (fd), w, h);
Matthias Clasen's avatar
Matthias Clasen committed
813

Matthias Clasen's avatar
Matthias Clasen committed
814
815
816
  cairo_surface_set_user_data (surface, &key, GINT_TO_POINTER (fd), close_preview);

  return surface;
817
818
}

819
820
821
822
static void
gtk_print_operation_unix_preview_start_page (GtkPrintOperation *op,
                                             cairo_surface_t   *surface,
                                             cairo_t           *cr)
823
824
825
{
}

826
827
828
829
static void
gtk_print_operation_unix_preview_end_page (GtkPrintOperation *op,
                                           cairo_surface_t   *surface,
                                           cairo_t           *cr)
830
831
{
  cairo_show_page (cr);
832
833
}

834
835
836
837
static void
gtk_print_operation_unix_resize_preview_surface (GtkPrintOperation *op,
                                                 GtkPageSetup      *page_setup,
                                                 cairo_surface_t   *surface)
838
{
839
  double w, h;
Matthias Clasen's avatar
Matthias Clasen committed
840

841
842
  w = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_POINTS);
  h = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_POINTS);
843
844
845
  cairo_pdf_surface_set_size (surface, w, h);
}

846
847
848
849
850
static GtkPrintOperationResult
gtk_print_operation_unix_run_dialog (GtkPrintOperation *op,
                                     gboolean           show_dialog,
                                     GtkWindow         *parent,
                                     gboolean          *do_print)
851
852
853
 {
  GtkWidget *pd;
  PrintResponseData rdata;
Benjamin Otte's avatar
Benjamin Otte committed
854
  const char *printer_name;
Matthias Clasen's avatar
Matthias Clasen committed
855

856
857
  rdata.op = op;
  rdata.do_print = FALSE;
858
  rdata.do_preview = FALSE;
859
860
861
  rdata.result = GTK_PRINT_OPERATION_RESULT_CANCEL;
  rdata.print_cb = NULL;
  rdata.destroy = NULL;
Alexander Larsson's avatar
Alexander Larsson committed
862
863
  rdata.parent = parent;
  rdata.loop = NULL;
864

865
  if (show_dialog)
Alexander Larsson's avatar
Alexander Larsson committed
866
867
    {
      pd = get_print_dialog (op, parent);
868
869
      gtk_window_set_modal (GTK_WINDOW (pd), TRUE);

Matthias Clasen's avatar
Matthias Clasen committed
870
      g_signal_connect (pd, "response",
871
			G_CALLBACK (handle_print_response), &rdata);
872

873
874
875
876
877
878
      gtk_window_present (GTK_WINDOW (pd));

      rdata.loop = g_main_loop_new (NULL, FALSE);
      g_main_loop_run (rdata.loop);
      g_main_loop_unref (rdata.loop);
      rdata.loop = NULL;
Alexander Larsson's avatar
Alexander Larsson committed
879
880
881
882
883
884
    }
  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
885

Alexander Larsson's avatar
Alexander Larsson committed
886
887
888
      rdata.loop = g_main_loop_new (NULL, FALSE);
      find_printer (printer_name,
		    (GFunc) found_printer, &rdata);
889

Alexander Larsson's avatar
Alexander Larsson committed
890
891
      g_main_loop_run (rdata.loop);
      g_main_loop_unref (rdata.loop);
892
      rdata.loop = NULL;
Alexander Larsson's avatar
Alexander Larsson committed
893
    }
894

Alexander Larsson's avatar
Alexander Larsson committed
895
  *do_print = rdata.do_print;
Matthias Clasen's avatar
Matthias Clasen committed
896

897
898
899
900
  return rdata.result;
}


Matthias Clasen's avatar
Matthias Clasen committed
901
typedef struct
Matthias Clasen's avatar
Matthias Clasen committed
902
{
903
904
905
906
  GtkPageSetup         *page_setup;
  GtkPageSetupDoneFunc  done_cb;
  gpointer              data;
  GDestroyNotify        destroy;
907
  GMainLoop            *loop;
908
909
910
911
912
913
914
} PageSetupResponseData;

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

Matthias Clasen's avatar
Matthias Clasen committed
915
916
917
  if (rdata->page_setup)
    g_object_unref (rdata->page_setup);

918
919
920
921
922
  g_free (rdata);
}

static void
handle_page_setup_response (GtkWidget *dialog,
Benjamin Otte's avatar
Benjamin Otte committed
923
			    int        response,
924
925
926
927
			    gpointer   data)
{
  GtkPageSetupUnixDialog *psd;
  PageSetupResponseData *rdata = data;
928

929
930
931
  if (rdata->loop)
    g_main_loop_quit (rdata->loop);

932
933
934
935
  psd = GTK_PAGE_SETUP_UNIX_DIALOG (dialog);
  if (response == GTK_RESPONSE_OK)
    rdata->page_setup = gtk_page_setup_unix_dialog_get_page_setup (psd);

Matthias Clasen's avatar
Matthias Clasen committed
936
  gtk_window_destroy (GTK_WINDOW (dialog));
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959

  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;
960
961
}

962
963
/**
 * gtk_print_run_page_setup_dialog:
964
 * @parent: (nullable): transient parent
Matthias Clasen's avatar
Matthias Clasen committed
965
966
 * @page_setup: (nullable): an existing `GtkPageSetup`
 * @settings: a `GtkPrintSettings`
967
968
 *
 * Runs a page setup dialog, letting the user modify the values from
Matthias Clasen's avatar
Matthias Clasen committed
969
 * @page_setup. If the user cancels the dialog, the returned `GtkPageSetup`
Matthias Clasen's avatar
Matthias Clasen committed
970
 * is identical to the passed in @page_setup, otherwise it contains the
971
972
973
 * modifications done in the dialog.
 *
 * Note that this function may use a recursive mainloop to show the page
Matthias Clasen's avatar
Matthias Clasen committed
974
 * setup dialog. See gtk_print_run_page_setup_dialog_async() if this is
975
 * a problem.
Matthias Clasen's avatar
Matthias Clasen committed
976
 *
Matthias Clasen's avatar
Matthias Clasen committed
977
 * Returns: (transfer full): a new `GtkPageSetup`
978
 */
979
980
981