gtkprintoperation-unix.c 27.2 KB
Newer Older
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 34 35 36

#include "gtkprintoperation-private.h"
#include "gtkmarshal.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

  if (type == CAIRO_SURFACE_TYPE_PS)
91
    cairo_ps_surface_set_size (op_unix->surface, w, h);
92
  else if (type == CAIRO_SURFACE_TYPE_PDF)
93
    cairo_pdf_surface_set_size (op_unix->surface, w, h);
94 95 96 97
}

static void
unix_end_page (GtkPrintOperation *op,
Matthias Clasen's avatar
Matthias Clasen committed
98
	       GtkPrintContext   *print_context)
99 100 101
{
  cairo_t *cr;

102
  cr = gtk_print_context_get_cairo_context (print_context);
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
  cairo_show_page (cr);
}

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
119
static gchar *
120 121 122
shell_command_substitute_file (const gchar *cmd,
			       const gchar *filename)
{
Matthias Clasen's avatar
Matthias Clasen committed
123 124
  const gchar *inptr, *start;
  gchar *result;
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
  GString *final;

  g_return_val_if_fail (cmd != NULL, NULL);
  g_return_val_if_fail (filename != NULL, NULL);

  result = NULL;
  final = g_string_new (NULL);

  start = inptr = cmd;

  while ((inptr = strchr (inptr, '%')) != NULL) 
    {
      g_string_append_len (final, start, inptr - start);
      inptr++;
      switch (*inptr) 
        {
          case 'f':
            g_string_append (final, filename ? filename : "");
            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);

  result = final->str;

  g_string_free (final, FALSE);

  return result;
}

void
_gtk_print_operation_platform_backend_launch_preview (GtkPrintOperation *op,
170
						      cairo_surface_t   *surface,
Matthias Clasen's avatar
Matthias Clasen committed
171 172
						      GtkWindow         *parent,
						      const gchar       *filename)
173
{
Matthias Clasen's avatar
Matthias Clasen committed
174
  gint argc;
175 176 177 178 179 180 181
  gchar **argv;
  gchar *cmd;
  gchar *preview_cmd;
  GtkSettings *settings;
  gchar *quoted_filename;
  GdkScreen *screen;
  GError *error = NULL;
182

183
  cairo_surface_destroy (surface);
184 185 186 187 188 189 190 191
 
  settings = gtk_settings_get_default ();
  g_object_get (settings, "gtk-print-preview-command", &preview_cmd, NULL);

  quoted_filename = g_shell_quote (filename);
  cmd = shell_command_substitute_file (preview_cmd, quoted_filename);
  g_shell_parse_argv (cmd, &argc, &argv, &error);

Matthias Clasen's avatar
Matthias Clasen committed
192
  if (error != NULL)
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
    goto out;

  if (parent)
    screen = gtk_window_get_screen (parent);
  else
    screen = gdk_screen_get_default ();
  
  gdk_spawn_on_screen (screen, NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error);

 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);
      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));

      g_error_free (error); 
   } 

  g_free (cmd);
  g_free (quoted_filename);
  g_free (preview_cmd);
  g_strfreev (argv);
}

228 229
static void
unix_finish_send  (GtkPrintJob *job,
Matthias Clasen's avatar
Matthias Clasen committed
230
                   gpointer     user_data, 
Matthias Clasen's avatar
Matthias Clasen committed
231
                   GError      *error)
232 233 234 235 236 237 238 239
{
  GtkPrintOperationUnix *op_unix;

  op_unix = (GtkPrintOperationUnix *) user_data;

  if (error != NULL)
    {
      GtkWidget *edialog;
240
      edialog = gtk_message_dialog_new (op_unix->parent, 
241 242 243
                                        GTK_DIALOG_DESTROY_WITH_PARENT,
                                        GTK_MESSAGE_ERROR,
                                        GTK_BUTTONS_CLOSE,
244 245 246 247 248 249 250 251
                                        _("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));
252
    }
Alexander Larsson's avatar
Alexander Larsson committed
253 254

  op_unix->data_sent = TRUE;
255

Alexander Larsson's avatar
Alexander Larsson committed
256 257
  if (op_unix->loop)
    g_main_loop_quit (op_unix->loop);
258 259 260
}

static void
Alexander Larsson's avatar
Alexander Larsson committed
261
unix_end_run (GtkPrintOperation *op,
262 263
	      gboolean           wait,
	      gboolean           cancelled)
264 265
{
  GtkPrintOperationUnix *op_unix = op->priv->platform_data;
Alexander Larsson's avatar
Alexander Larsson committed
266

267 268
  cairo_surface_finish (op_unix->surface);
  
269 270 271
  if (cancelled)
    return;

Alexander Larsson's avatar
Alexander Larsson committed
272 273 274
  if (wait)
    op_unix->loop = g_main_loop_new (NULL, FALSE);
  
275
  /* TODO: Check for error */
276 277 278
  if (op_unix->job != NULL)
    gtk_print_job_send (op_unix->job,
                        unix_finish_send, 
279
                        op_unix, NULL);
Alexander Larsson's avatar
Alexander Larsson committed
280 281 282

  if (wait)
    {
283
      g_object_ref (op);
Alexander Larsson's avatar
Alexander Larsson committed
284 285 286 287 288 289 290
      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);
291 292
      op_unix->loop = NULL;
      g_object_unref (op);
Alexander Larsson's avatar
Alexander Larsson committed
293
    }
294 295 296
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
297 298
job_status_changed_cb (GtkPrintJob       *job, 
		       GtkPrintOperation *op)
299 300 301 302
{
  _gtk_print_operation_set_status (op, gtk_print_job_get_status (job), NULL);
}

303 304 305 306

static GtkWidget *
get_print_dialog (GtkPrintOperation *op,
                  GtkWindow         *parent)
307
{
308
  GtkPrintOperationPrivate *priv = op->priv;
309
  GtkWidget *pd, *label;
310
  GtkPageSetup *page_setup;
Matthias Clasen's avatar
Matthias Clasen committed
311
  const gchar *custom_tab_label;
312 313 314

  pd = gtk_print_unix_dialog_new (NULL, parent);

315 316 317 318 319
  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 |
320
						 GTK_PRINT_CAPABILITY_SCALE |
321
						 GTK_PRINT_CAPABILITY_PREVIEW);
322

323
  if (priv->print_settings)
324
    gtk_print_unix_dialog_set_settings (GTK_PRINT_UNIX_DIALOG (pd),
325 326 327
					priv->print_settings);
  if (priv->default_page_setup)
    page_setup = gtk_page_setup_copy (priv->default_page_setup);
328 329 330 331 332 333
  else
    page_setup = gtk_page_setup_new ();

  gtk_print_unix_dialog_set_page_setup (GTK_PRINT_UNIX_DIALOG (pd), 
                                        page_setup);
  g_object_unref (page_setup);
334

335
  g_signal_emit_by_name (op, "create-custom-widget",
Matthias Clasen's avatar
Matthias Clasen committed
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
			 &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
352
					    priv->custom_widget, label);
Matthias Clasen's avatar
Matthias Clasen committed
353
    }
354
  
355 356
  return pd;
}
357
  
Matthias Clasen's avatar
Matthias Clasen committed
358 359
typedef struct 
{
360 361
  GtkPrintOperation           *op;
  gboolean                     do_print;
362
  gboolean                     do_preview;
363 364 365
  GtkPrintOperationResult      result;
  GtkPrintOperationPrintFunc   print_cb;
  GDestroyNotify               destroy;
Alexander Larsson's avatar
Alexander Larsson committed
366 367
  GtkWindow                   *parent;
  GMainLoop                   *loop;
368 369 370 371 372 373 374 375 376 377 378 379
} 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
380
finish_print (PrintResponseData *rdata,
Matthias Clasen's avatar
Matthias Clasen committed
381 382 383
	      GtkPrinter        *printer,
	      GtkPageSetup      *page_setup,
	      GtkPrintSettings  *settings)
384 385
{
  GtkPrintOperation *op = rdata->op;
386
  GtkPrintOperationPrivate *priv = op->priv;
Matthias Clasen's avatar
Matthias Clasen committed
387
  GtkPrintJob *job;
Alexander Larsson's avatar
Alexander Larsson committed
388 389
  
  if (rdata->do_print)
390 391
    {
      gtk_print_operation_set_print_settings (op, settings);
392 393
      priv->print_context = _gtk_print_context_new (op);
      _gtk_print_context_set_page_setup (priv->print_context, page_setup);
394

395
      if (!rdata->do_preview)
396
        {
397 398 399
	  GtkPrintOperationUnix *op_unix;
	  cairo_t *cr;
	  
400
	  op_unix = g_new0 (GtkPrintOperationUnix, 1);
401 402 403 404 405 406 407 408
	  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
409 410 411
	  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);
412
	  
413
	  op_unix->surface = gtk_print_job_get_surface (job, &priv->error);
Matthias Clasen's avatar
Matthias Clasen committed
414 415
	  if (op_unix->surface == NULL) 
            {
416
	      rdata->result = GTK_PRINT_OPERATION_RESULT_ERROR;
Matthias Clasen's avatar
Matthias Clasen committed
417 418 419
	      rdata->do_print = FALSE;
	      goto out;
            }
420 421
	  
	  cr = cairo_create (op_unix->surface);
Matthias Clasen's avatar
Matthias Clasen committed
422
	  gtk_print_context_set_cairo_context (priv->print_context, cr, 72, 72);
423 424
	  cairo_destroy (cr);

Matthias Clasen's avatar
Matthias Clasen committed
425
          _gtk_print_operation_set_status (op, gtk_print_job_get_status (job), NULL);
426 427
	  
          op_unix->job_status_changed_tag =
Matthias Clasen's avatar
Matthias Clasen committed
428
	    g_signal_connect (job, "status-changed",
429 430
			      G_CALLBACK (job_status_changed_cb), op);
	  
Matthias Clasen's avatar
Matthias Clasen committed
431 432 433
          priv->print_pages = job->print_pages;
          priv->page_ranges = job->page_ranges;
          priv->num_page_ranges = job->num_page_ranges;
434
	  
Matthias Clasen's avatar
Matthias Clasen committed
435 436 437 438 439 440
          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;
441
        }
442
    } 
443
 out:
444
  if (rdata->print_cb)
445
    rdata->print_cb (op, rdata->parent, rdata->do_print, rdata->result); 
446 447 448 449 450

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

451
static void 
Alexander Larsson's avatar
Alexander Larsson committed
452 453 454 455 456 457 458 459 460 461 462 463 464
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;

  if (response == GTK_RESPONSE_OK)
    {
      printer = gtk_print_unix_dialog_get_selected_printer (GTK_PRINT_UNIX_DIALOG (pd));
465 466 467

      rdata->result = GTK_PRINT_OPERATION_RESULT_APPLY;
      rdata->do_preview = FALSE;
468 469 470 471 472 473
      if (printer != NULL)
	rdata->do_print = TRUE;
    } 
  else if (response == GTK_RESPONSE_APPLY)
    {
      /* print preview */
474 475
      rdata->result = GTK_PRINT_OPERATION_RESULT_APPLY;
      rdata->do_preview = TRUE;
Alexander Larsson's avatar
Alexander Larsson committed
476
      rdata->do_print = TRUE;
477 478

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

481 482
  if (rdata->do_print)
    {
Alexander Larsson's avatar
Alexander Larsson committed
483 484
      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));
485
      
486
      g_signal_emit_by_name (rdata->op, "custom-widget-apply", rdata->op->priv->custom_widget);
487 488
    }
  
Alexander Larsson's avatar
Alexander Larsson committed
489 490 491 492
  finish_print (rdata, printer, page_setup, settings);

  if (settings)
    g_object_unref (settings);
493
    
Alexander Larsson's avatar
Alexander Larsson committed
494
  gtk_widget_destroy (GTK_WIDGET (pd));
495
 
Alexander Larsson's avatar
Alexander Larsson committed
496 497 498 499
}


static void
Matthias Clasen's avatar
Matthias Clasen committed
500
found_printer (GtkPrinter        *printer,
Alexander Larsson's avatar
Alexander Larsson committed
501 502 503 504 505 506 507 508 509 510
	       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
511 512
  if (printer != NULL) 
    {
513
      rdata->result = GTK_PRINT_OPERATION_RESULT_APPLY;
Alexander Larsson's avatar
Alexander Larsson committed
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539

      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 ();
  }
  
  finish_print (rdata, printer, page_setup, settings);

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

540 541
void
_gtk_print_operation_platform_backend_run_dialog_async (GtkPrintOperation          *op,
542
							gboolean                    show_dialog,
543 544 545 546 547
                                                        GtkWindow                  *parent,
							GtkPrintOperationPrintFunc  print_cb)
{
  GtkWidget *pd;
  PrintResponseData *rdata;
Matthias Clasen's avatar
Matthias Clasen committed
548
  const gchar *printer_name;
549 550 551 552

  rdata = g_new (PrintResponseData, 1);
  rdata->op = g_object_ref (op);
  rdata->do_print = FALSE;
553
  rdata->do_preview = FALSE;
554 555
  rdata->result = GTK_PRINT_OPERATION_RESULT_CANCEL;
  rdata->print_cb = print_cb;
Alexander Larsson's avatar
Alexander Larsson committed
556 557
  rdata->parent = parent;
  rdata->loop = NULL;
558
  rdata->destroy = print_response_data_free;
559
  
560
  if (show_dialog)
Alexander Larsson's avatar
Alexander Larsson committed
561 562 563
    {
      pd = get_print_dialog (op, parent);
      gtk_window_set_modal (GTK_WINDOW (pd), TRUE);
564

Alexander Larsson's avatar
Alexander Larsson committed
565 566 567 568 569 570 571 572 573 574 575
      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
576
      find_printer (printer_name, (GFunc) found_printer, rdata);
Alexander Larsson's avatar
Alexander Larsson committed
577
    }
578 579
}

Matthias Clasen's avatar
Matthias Clasen committed
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
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);
}

615 616
cairo_surface_t *
_gtk_print_operation_platform_backend_create_preview_surface (GtkPrintOperation *op,
Matthias Clasen's avatar
Matthias Clasen committed
617 618 619
							      GtkPageSetup      *page_setup,
							      gdouble           *dpi_x,
							      gdouble           *dpi_y,
620
							      gchar            **target)
621
{
Matthias Clasen's avatar
Matthias Clasen committed
622 623
  gchar *filename;
  gint fd;
624
  GtkPaperSize *paper_size;
Matthias Clasen's avatar
Matthias Clasen committed
625
  gdouble w, h;
Matthias Clasen's avatar
Matthias Clasen committed
626 627
  cairo_surface_t *surface;
  static cairo_user_data_key_t key;
628
  
Matthias Clasen's avatar
Matthias Clasen committed
629 630 631
  filename = g_build_filename (g_get_tmp_dir (), "previewXXXXXX.pdf", NULL);
  fd = g_mkstemp (filename);
  *target = filename;
632
  
Matthias Clasen's avatar
Matthias Clasen committed
633 634
  g_print ("target is %s\n", filename);

635 636 637 638 639
  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;
Matthias Clasen's avatar
Matthias Clasen committed
640 641 642 643 644
  surface = cairo_pdf_surface_create_for_stream (write_preview, GINT_TO_POINTER(fd), w, h);
 
  cairo_surface_set_user_data (surface, &key, GINT_TO_POINTER (fd), close_preview);

  return surface;
645 646 647 648
}

void
_gtk_print_operation_platform_backend_preview_start_page (GtkPrintOperation *op,
649 650
							  cairo_surface_t   *surface,
							  cairo_t           *cr)
651 652 653 654 655
{
}

void
_gtk_print_operation_platform_backend_preview_end_page (GtkPrintOperation *op,
656 657
							cairo_surface_t   *surface,
							cairo_t           *cr)
658 659
{
  cairo_show_page (cr);
660 661 662 663
}

void
_gtk_print_operation_platform_backend_resize_preview_surface (GtkPrintOperation *op,
Matthias Clasen's avatar
Matthias Clasen committed
664 665
							      GtkPageSetup      *page_setup,
							      cairo_surface_t   *surface)
666 667
{
  GtkPaperSize *paper_size;
Matthias Clasen's avatar
Matthias Clasen committed
668
  gdouble w, h;
669 670 671 672 673 674 675 676
  
  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);
}


677 678
GtkPrintOperationResult
_gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
679
						  gboolean           show_dialog,
680
						  GtkWindow         *parent,
681
						  gboolean          *do_print)
682 683 684 685
 {
  GtkWidget *pd;
  PrintResponseData rdata;
  gint response;  
Matthias Clasen's avatar
Matthias Clasen committed
686
  const gchar *printer_name;
687 688 689
   
  rdata.op = op;
  rdata.do_print = FALSE;
690
  rdata.do_preview = FALSE;
691 692 693
  rdata.result = GTK_PRINT_OPERATION_RESULT_CANCEL;
  rdata.print_cb = NULL;
  rdata.destroy = NULL;
Alexander Larsson's avatar
Alexander Larsson committed
694 695
  rdata.parent = parent;
  rdata.loop = NULL;
696

697
  if (show_dialog)
Alexander Larsson's avatar
Alexander Larsson committed
698 699
    {
      pd = get_print_dialog (op, parent);
700

Alexander Larsson's avatar
Alexander Larsson committed
701 702 703 704 705 706 707 708 709 710 711 712
      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);
713

Alexander Larsson's avatar
Alexander Larsson committed
714 715 716
      GDK_THREADS_LEAVE ();  
      g_main_loop_run (rdata.loop);
      GDK_THREADS_ENTER ();  
717

Alexander Larsson's avatar
Alexander Larsson committed
718
      g_main_loop_unref (rdata.loop);
719
      rdata.loop = NULL;
Alexander Larsson's avatar
Alexander Larsson committed
720
    }
721

Alexander Larsson's avatar
Alexander Larsson committed
722 723
  *do_print = rdata.do_print;
  
724 725 726 727
  return rdata.result;
}


Matthias Clasen's avatar
Matthias Clasen committed
728 729
typedef struct 
{
730 731 732 733
  GtkPageSetup         *page_setup;
  GtkPageSetupDoneFunc  done_cb;
  gpointer              data;
  GDestroyNotify        destroy;
734 735 736 737 738 739 740
} PageSetupResponseData;

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

Matthias Clasen's avatar
Matthias Clasen committed
741 742 743
  if (rdata->page_setup)
    g_object_unref (rdata->page_setup);

744 745 746 747 748 749 750 751 752 753
  g_free (rdata);
}

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

755 756 757 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
  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;
783 784
}

785 786 787 788 789 790
/**
 * gtk_print_run_page_setup_dialog:
 * @parent: transient parent, or %NULL
 * @page_setup: an existing #GtkPageSetup, or %NULL
 * @settings: a #GtkPrintSettings
 * 
791 792 793 794 795 796 797 798
 * 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.
799 800 801 802 803
 * 
 * Return value: a new #GtkPageSetup
 *
 * Since: 2.10
 */
804 805 806 807 808 809
GtkPageSetup *
gtk_print_run_page_setup_dialog (GtkWindow        *parent,
				 GtkPageSetup     *page_setup,
				 GtkPrintSettings *settings)
{
  GtkWidget *dialog;
810 811
  gint response;
  PageSetupResponseData rdata;  
812
  
813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
  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 ();
828 829
}

830 831 832 833 834 835 836 837
/**
 * 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
838
 * Runs a page setup dialog, letting the user modify the values from @page_setup. 
839
 *
Matthias Clasen's avatar
Matthias Clasen committed
840 841 842
 * 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.
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870
 *
 * Since: 2.10
 */
void
gtk_print_run_page_setup_dialog_async (GtkWindow            *parent,
				       GtkPageSetup         *page_setup,
				       GtkPrintSettings     *settings,
				       GtkPageSetupDoneFunc  done_cb,
				       gpointer              data)
{
  GtkWidget *dialog;
  PageSetupResponseData *rdata;
  
  dialog = get_page_setup_dialog (parent, page_setup, settings);
  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
  
  rdata = g_new (PageSetupResponseData, 1);
  rdata->page_setup = NULL;
  rdata->done_cb = done_cb;
  rdata->data = data;
  rdata->destroy = page_setup_data_free;

  g_signal_connect (dialog, "response",
		    G_CALLBACK (handle_page_setup_response), rdata);
 
  gtk_window_present (GTK_WINDOW (dialog));
 }

Matthias Clasen's avatar
Matthias Clasen committed
871 872
struct _PrinterFinder 
{
Alexander Larsson's avatar
Alexander Larsson committed
873 874 875
  gboolean found_printer;
  GFunc func;
  gpointer data;
Matthias Clasen's avatar
Matthias Clasen committed
876
  gchar *printer_name;
Alexander Larsson's avatar
Alexander Larsson committed
877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910
  GList *backends;
  guint timeout_tag;
  GtkPrinter *printer;
  GtkPrinter *default_printer;
  GtkPrinter *first_printer;
};

static gboolean
find_printer_idle (gpointer data)
{
  PrinterFinder *finder = data;
  GtkPrinter *printer;

  if (finder->printer != NULL)
    printer = finder->printer;
  else if (finder->default_printer != NULL)
    printer = finder->default_printer;
  else if (finder->first_printer != NULL)
    printer = finder->first_printer;
  else
    printer = NULL;

  finder->func (printer, finder->data);
  
  printer_finder_free (finder);

  return FALSE;
}

static void
printer_added_cb (GtkPrintBackend *backend, 
                  GtkPrinter      *printer, 
		  PrinterFinder   *finder)
{
911 912 913 914 915
  if (finder->found_printer)
    return;

  /* FIXME this skips "Print to PDF" - is this intentional ? */
  if (gtk_printer_is_virtual (printer))
Alexander Larsson's avatar
Alexander Larsson committed
916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955
    return;

  if (finder->printer_name != NULL &&
      strcmp (gtk_printer_get_name (printer), finder->printer_name) == 0)
    {
      finder->printer = g_object_ref (printer);
      finder->found_printer = TRUE;
    }
  else if (finder->default_printer == NULL &&
	   gtk_printer_is_default (printer))
    {
      finder->default_printer = g_object_ref (printer);
      if (finder->printer_name == NULL)
	finder->found_printer = TRUE;
    }
  else
    if (finder->first_printer == NULL)
      finder->first_printer = g_object_ref (printer);
  
  if (finder->found_printer)
    g_idle_add (find_printer_idle, finder);
}

static void
printer_list_done_cb (GtkPrintBackend *backend, 
		      PrinterFinder   *finder)
{
  finder->backends = g_list_remove (finder->backends, backend);
  
  g_signal_handlers_disconnect_by_func (backend, printer_added_cb, finder);
  g_signal_handlers_disconnect_by_func (backend, printer_list_done_cb, finder);
  
  gtk_print_backend_destroy (backend);
  g_object_unref (backend);

  if (finder->backends == NULL && !finder->found_printer)
    g_idle_add (find_printer_idle, finder);
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
956
find_printer_init (PrinterFinder   *finder,
Alexander Larsson's avatar
Alexander Larsson committed
957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983
		   GtkPrintBackend *backend)
{
  GList *list;
  GList *node;

  list = gtk_print_backend_get_printer_list (backend);

  node = list;
  while (node != NULL)
    {
      printer_added_cb (backend, node->data, finder);
      node = node->next;

      if (finder->found_printer)
	break;
    }

  g_list_free (list);

  if (gtk_print_backend_printer_list_is_done (backend))
    {
      finder->backends = g_list_remove (finder->backends, backend);
      gtk_print_backend_destroy (backend);
      g_object_unref (backend);
    }
  else
    {
Matthias Clasen's avatar
Matthias Clasen committed
984
      g_signal_connect (backend, "printer-added", 
Alexander Larsson's avatar
Alexander Larsson committed
985 986
			(GCallback) printer_added_cb, 
			finder);
Matthias Clasen's avatar
Matthias Clasen committed
987
      g_signal_connect (backend, "printer-list-done", 
Alexander Larsson's avatar
Alexander Larsson committed
988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
			(GCallback) printer_list_done_cb, 
			finder);
    }

}

static void
printer_finder_free (PrinterFinder *finder)
{
  GList *l;
  
  g_free (finder->printer_name);
  
  if (finder->printer)
    g_object_unref (finder->printer);
  
  if (finder->default_printer)
    g_object_unref (finder->default_printer);
  
  if (finder->first_printer)
    g_object_unref (finder->first_printer);

  for (l = finder->backends; l != NULL; l = l->next)
    {
      GtkPrintBackend *backend = l->data;
      g_signal_handlers_disconnect_by_func (backend, printer_added_cb, finder);
      g_signal_handlers_disconnect_by_func (backend, printer_list_done_cb, finder);
      gtk_print_backend_destroy (backend);
      g_object_unref (backend);
    }
  
  g_list_free (finder->backends);
  
  g_free (finder);
}

static void 
Matthias Clasen's avatar
Matthias Clasen committed
1025 1026 1027
find_printer (const gchar *printer,
	      GFunc        func,
	      gpointer     data)
Alexander Larsson's avatar
Alexander Larsson committed
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
{
  GList *node, *next;
  PrinterFinder *finder;

  finder = g_new0 (PrinterFinder, 1);

  finder->printer_name = g_strdup (printer);
  finder->func = func;
  finder->data = data;
  
  finder->backends = NULL;
  if (g_module_supported ())
    finder->backends = gtk_print_backend_load_modules ();

  for (node = finder->backends; !finder->found_printer && node != NULL; node = next)
    {
      next = node->next;
      find_printer_init (finder, GTK_PRINT_BACKEND (node->data));
    }

  if (finder->backends == NULL && !finder->found_printer)
    g_idle_add (find_printer_idle, finder);
}
1051 1052 1053

#define __GTK_PRINT_OPERATION_UNIX_C__
#include "gtkaliasdef.c"