gtkprintoperation-unix.c 33.3 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
 * 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 28 29
#include <errno.h>
#include <stdlib.h>       
#include <fcntl.h>
30

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

35 36
#include <cairo-pdf.h>
#include <cairo-ps.h>
37
#include "gtkprivate.h"
38 39 40 41 42
#include "gtkprintunixdialog.h"
#include "gtkpagesetupunixdialog.h"
#include "gtkprintbackend.h"
#include "gtkprinter.h"
#include "gtkprintjob.h"
43
#include "gtklabel.h"
44
#include "gtkintl.h"
45

46

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

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

  
59 60
} GtkPrintOperationUnix;

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

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

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

78 79
  op_unix = op->priv->platform_data;
  
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);
  
85
  type = cairo_surface_get_type (op_unix->surface);
86

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

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

125
  cr = gtk_print_context_get_cairo_context (print_context);
126 127 128 129 130

  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);
131 132 133 134 135 136 137
}

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

  g_free (op_unix);
}

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

  g_return_val_if_fail (cmd != NULL, NULL);
158 159
  g_return_val_if_fail (pdf_filename != NULL, NULL);
  g_return_val_if_fail (settings_filename != NULL, NULL);
160 161 162

  final = g_string_new (NULL);

163 164
  *pdf_filename_replaced = FALSE;
  *settings_filename_replaced = FALSE;
165

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

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

199
  return g_string_free (final, FALSE);
200 201 202 203
}

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

228
  cairo_surface_destroy (surface);
229
 
230 231 232 233 234
  if (parent)
    screen = gtk_window_get_screen (parent);
  else
    screen = gdk_screen_get_default ();

235 236 237
  fd = g_file_open_tmp ("settingsXXXXXX.ini", &settings_filename, &error);
  if (fd < 0) 
    goto out;
238

239 240
  key_file = g_key_file_new ();
  
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
  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);
    }
263 264 265

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

267 268
  g_key_file_set_string (key_file, "Print Job", "title", op->priv->job_name);

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

  settings = gtk_settings_get_for_screen (screen);
278 279 280
  g_object_get (settings, "gtk-print-preview-command", &preview_cmd, NULL);

  quoted_filename = g_shell_quote (filename);
281 282
  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
283 284 285 286 287

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

289 290 291 292 293
  g_free (preview_cmd);
  g_free (quoted_filename);
  g_free (quoted_settings_filename);
  g_free (cmd);

Matthias Clasen's avatar
Matthias Clasen committed
294
  if (error != NULL)
295 296
    goto out;

Matthias Clasen's avatar
Matthias Clasen committed
297
  context = gdk_display_get_app_launch_context (gdk_screen_get_display (screen));
298 299
  gdk_app_launch_context_set_screen (context, screen);
  g_app_info_launch (appinfo, NULL, G_APP_LAUNCH_CONTEXT (context), &error);
300

Matthias Clasen's avatar
Matthias Clasen committed
301 302
  g_object_unref (context);
  g_object_unref (appinfo);
303

304 305 306 307 308 309 310 311 312 313 314 315 316
  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);
    }

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

Matthias Clasen's avatar
Matthias Clasen committed
325
      filename_used = FALSE;
326
      settings_used = FALSE;
Matthias Clasen's avatar
Matthias Clasen committed
327
   }
328

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

  if (!settings_used)
    g_unlink (settings_filename);

335 336
  if (fd > 0)
    close (fd);
Matthias Clasen's avatar
Matthias Clasen committed
337

338 339 340
  if (key_file)
    g_key_file_free (key_file);
  g_free (data);
341
  g_free (settings_filename);
342 343
}

344
static void
345 346 347
unix_finish_send  (GtkPrintJob  *job,
                   gpointer      user_data, 
                   const GError *error)
348
{
349 350
  GtkPrintOperation *op = (GtkPrintOperation *) user_data;
  GtkPrintOperationUnix *op_unix = op->priv->platform_data;
351

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

  op_unix->data_sent = TRUE;
356

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

  g_object_unref (op);
361 362 363
}

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

370 371
  cairo_surface_finish (op_unix->surface);
  
372 373 374
  if (cancelled)
    return;

Alexander Larsson's avatar
Alexander Larsson committed
375 376 377
  if (wait)
    op_unix->loop = g_main_loop_new (NULL, FALSE);
  
378
  /* TODO: Check for error */
379
  if (op_unix->job != NULL)
380 381 382 383 384 385
    {
      g_object_ref (op);
      gtk_print_job_send (op_unix->job,
                          unix_finish_send, 
                          op, NULL);
    }
Alexander Larsson's avatar
Alexander Larsson committed
386 387 388

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

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

409

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

430 431 432
static GtkWidget *
get_print_dialog (GtkPrintOperation *op,
                  GtkWindow         *parent)
433
{
434
  GtkPrintOperationPrivate *priv = op->priv;
435
  GtkWidget *pd, *label;
Matthias Clasen's avatar
Matthias Clasen committed
436
  const gchar *custom_tab_label;
437 438 439

  pd = gtk_print_unix_dialog_new (NULL, parent);

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

450
  if (priv->print_settings)
451
    gtk_print_unix_dialog_set_settings (GTK_PRINT_UNIX_DIALOG (pd),
452
					priv->print_settings);
453

454
  if (priv->default_page_setup)
455 456
    gtk_print_unix_dialog_set_page_setup (GTK_PRINT_UNIX_DIALOG (pd), 
                                          priv->default_page_setup);
457

458 459 460
  gtk_print_unix_dialog_set_embed_page_setup (GTK_PRINT_UNIX_DIALOG (pd),
                                              priv->embed_page_setup);

461 462 463
  gtk_print_unix_dialog_set_current_page (GTK_PRINT_UNIX_DIALOG (pd), 
                                          priv->current_page);

464 465 466 467 468 469
  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);

470
  g_signal_emit_by_name (op, "create-custom-widget",
Matthias Clasen's avatar
Matthias Clasen committed
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
			 &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
487
					    priv->custom_widget, label);
488

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

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

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

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

555
      if (!rdata->do_preview)
556
        {
557 558 559
	  GtkPrintOperationUnix *op_unix;
	  cairo_t *cr;
	  
560
	  op_unix = g_new0 (GtkPrintOperationUnix, 1);
561 562 563 564 565 566 567 568
	  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
569 570 571
	  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);
572
	  
573
	  op_unix->surface = gtk_print_job_get_surface (job, &priv->error);
Matthias Clasen's avatar
Matthias Clasen committed
574 575
	  if (op_unix->surface == NULL) 
            {
576
	      rdata->result = GTK_PRINT_OPERATION_RESULT_ERROR;
Matthias Clasen's avatar
Matthias Clasen committed
577 578 579
	      rdata->do_print = FALSE;
	      goto out;
            }
580 581
	  
	  cr = cairo_create (op_unix->surface);
Matthias Clasen's avatar
Matthias Clasen committed
582
	  gtk_print_context_set_cairo_context (priv->print_context, cr, 72, 72);
583 584
	  cairo_destroy (cr);

Matthias Clasen's avatar
Matthias Clasen committed
585
          _gtk_print_operation_set_status (op, gtk_print_job_get_status (job), NULL);
586 587
	  
          op_unix->job_status_changed_tag =
Matthias Clasen's avatar
Matthias Clasen committed
588
	    g_signal_connect (job, "status-changed",
589 590
			      G_CALLBACK (job_status_changed_cb), op);
	  
591 592 593 594 595 596 597 598 599 600
          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);
601
        }
602
    } 
603
 out:
604
  if (rdata->print_cb)
605
    rdata->print_cb (op, rdata->parent, rdata->do_print, rdata->result); 
606 607 608 609 610

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

611
static void 
Alexander Larsson's avatar
Alexander Larsson committed
612 613 614 615 616 617 618 619 620
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;
621
  gboolean page_setup_set = FALSE;
Alexander Larsson's avatar
Alexander Larsson committed
622 623 624 625

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

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

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

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

      /* 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);
652
      g_signal_emit_by_name (rdata->op, "custom-widget-apply", rdata->op->priv->custom_widget);
653 654
    }
  
655
  finish_print (rdata, printer, page_setup, settings, page_setup_set);
Alexander Larsson's avatar
Alexander Larsson committed
656 657 658

  if (settings)
    g_object_unref (settings);
659
    
Alexander Larsson's avatar
Alexander Larsson committed
660
  gtk_widget_destroy (GTK_WIDGET (pd));
661
 
Alexander Larsson's avatar
Alexander Larsson committed
662 663 664 665
}


static void
Matthias Clasen's avatar
Matthias Clasen committed
666
found_printer (GtkPrinter        *printer,
Alexander Larsson's avatar
Alexander Larsson committed
667 668 669 670 671 672 673 674 675 676
	       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
677 678
  if (printer != NULL) 
    {
679
      rdata->result = GTK_PRINT_OPERATION_RESULT_APPLY;
Alexander Larsson's avatar
Alexander Larsson committed
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696

      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 ();
  }
  
697
  finish_print (rdata, printer, page_setup, settings, FALSE);
Alexander Larsson's avatar
Alexander Larsson committed
698 699 700 701 702 703 704 705

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

706 707
void
_gtk_print_operation_platform_backend_run_dialog_async (GtkPrintOperation          *op,
708
							gboolean                    show_dialog,
709 710 711 712 713
                                                        GtkWindow                  *parent,
							GtkPrintOperationPrintFunc  print_cb)
{
  GtkWidget *pd;
  PrintResponseData *rdata;
Matthias Clasen's avatar
Matthias Clasen committed
714
  const gchar *printer_name;
715 716 717 718

  rdata = g_new (PrintResponseData, 1);
  rdata->op = g_object_ref (op);
  rdata->do_print = FALSE;
719
  rdata->do_preview = FALSE;
720 721
  rdata->result = GTK_PRINT_OPERATION_RESULT_CANCEL;
  rdata->print_cb = print_cb;
Alexander Larsson's avatar
Alexander Larsson committed
722 723
  rdata->parent = parent;
  rdata->loop = NULL;
724
  rdata->destroy = print_response_data_free;
725
  
726
  if (show_dialog)
Alexander Larsson's avatar
Alexander Larsson committed
727 728 729
    {
      pd = get_print_dialog (op, parent);
      gtk_window_set_modal (GTK_WINDOW (pd), TRUE);
730

Alexander Larsson's avatar
Alexander Larsson committed
731 732 733 734 735 736 737 738 739 740 741
      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
742
      find_printer (printer_name, (GFunc) found_printer, rdata);
Alexander Larsson's avatar
Alexander Larsson committed
743
    }
744 745
}

Matthias Clasen's avatar
Matthias Clasen committed
746 747 748 749 750 751 752 753 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
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);
}

781 782
cairo_surface_t *
_gtk_print_operation_platform_backend_create_preview_surface (GtkPrintOperation *op,
Matthias Clasen's avatar
Matthias Clasen committed
783 784 785
							      GtkPageSetup      *page_setup,
							      gdouble           *dpi_x,
							      gdouble           *dpi_y,
786
							      gchar            **target)
787
{
Matthias Clasen's avatar
Matthias Clasen committed
788 789
  gchar *filename;
  gint fd;
790
  GtkPaperSize *paper_size;
Matthias Clasen's avatar
Matthias Clasen committed
791
  gdouble w, h;
Matthias Clasen's avatar
Matthias Clasen committed
792 793
  cairo_surface_t *surface;
  static cairo_user_data_key_t key;
794
  
Matthias Clasen's avatar
Matthias Clasen committed
795 796
  filename = g_build_filename (g_get_tmp_dir (), "previewXXXXXX.pdf", NULL);
  fd = g_mkstemp (filename);
797 798 799 800 801 802 803

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

Matthias Clasen's avatar
Matthias Clasen committed
804
  *target = filename;
805
  
806 807 808 809 810
  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;
811
  surface = cairo_pdf_surface_create_for_stream (write_preview, GINT_TO_POINTER (fd), w, h);
Matthias Clasen's avatar
Matthias Clasen committed
812 813 814 815
 
  cairo_surface_set_user_data (surface, &key, GINT_TO_POINTER (fd), close_preview);

  return surface;
816 817 818 819
}

void
_gtk_print_operation_platform_backend_preview_start_page (GtkPrintOperation *op,
820 821
							  cairo_surface_t   *surface,
							  cairo_t           *cr)
822 823 824 825 826
{
}

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

void
_gtk_print_operation_platform_backend_resize_preview_surface (GtkPrintOperation *op,
Matthias Clasen's avatar
Matthias Clasen committed
835 836
							      GtkPageSetup      *page_setup,
							      cairo_surface_t   *surface)
837
{
Matthias Clasen's avatar
Matthias Clasen committed
838
  gdouble w, h;
839
  
840 841
  w = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_POINTS);
  h = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_POINTS);
842 843 844 845
  cairo_pdf_surface_set_size (surface, w, h);
}


846 847
GtkPrintOperationResult
_gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
848
						  gboolean           show_dialog,
849
						  GtkWindow         *parent,
850
						  gboolean          *do_print)
851 852 853 854
 {
  GtkWidget *pd;
  PrintResponseData rdata;
  gint response;  
Matthias Clasen's avatar
Matthias Clasen committed
855
  const gchar *printer_name;
856 857 858
   
  rdata.op = op;
  rdata.do_print = FALSE;
859
  rdata.do_preview = FALSE;
860 861 862
  rdata.result = GTK_PRINT_OPERATION_RESULT_CANCEL;
  rdata.print_cb = NULL;
  rdata.destroy = NULL;
Alexander Larsson's avatar
Alexander Larsson committed
863 864
  rdata.parent = parent;
  rdata.loop = NULL;
865

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

Alexander Larsson's avatar
Alexander Larsson committed
870 871 872 873 874 875 876 877 878 879 880 881
      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);
882

883
      gdk_threads_leave ();  
Alexander Larsson's avatar
Alexander Larsson committed
884
      g_main_loop_run (rdata.loop);
885
      gdk_threads_enter ();  
886

Alexander Larsson's avatar
Alexander Larsson committed
887
      g_main_loop_unref (rdata.loop);
888
      rdata.loop = NULL;
Alexander Larsson's avatar
Alexander Larsson committed
889
    }
890

Alexander Larsson's avatar
Alexander Larsson committed
891 892
  *do_print = rdata.do_print;
  
893 894 895 896
  return rdata.result;
}


Matthias Clasen's avatar
Matthias Clasen committed
897 898
typedef struct 
{
899 900 901 902
  GtkPageSetup         *page_setup;
  GtkPageSetupDoneFunc  done_cb;
  gpointer              data;
  GDestroyNotify        destroy;
903 904 905 906 907 908 909
} PageSetupResponseData;

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

Matthias Clasen's avatar
Matthias Clasen committed
910 911 912
  if (rdata->page_setup)
    g_object_unref (rdata->page_setup);

913 914 915 916 917 918 919 920 921 922
  g_free (rdata);
}

static void
handle_page_setup_response (GtkWidget *dialog,
			    gint       response,
			    gpointer   data)
{
  GtkPageSetupUnixDialog *psd;
  PageSetupResponseData *rdata = data;
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
  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;
952 953
}

954 955
/**
 * gtk_print_run_page_setup_dialog:
956 957
 * @parent: (allow-none): transient parent
 * @page_setup: (allow-none): an existing #GtkPageSetup
958
 * @settings: a #GtkPrintSettings
959 960 961
 *
 * Runs a page setup dialog, letting the user modify the values from
 * @page_setup. If the user cancels the dialog, the returned #GtkPageSetup
962 963 964 965 966 967
 * 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.
968
 * 
969
 * Returns: (transfer full): a new #GtkPageSetup
970 971 972
 *
 * Since: 2.10
 */
973 974 975 976 977 978
GtkPageSetup *
gtk_print_run_page_setup_dialog (GtkWindow        *parent,
				 GtkPageSetup     *page_setup,
				 GtkPrintSettings *settings)
{
  GtkWidget *dialog;
979 980
  gint response;
  PageSetupResponseData rdata;  
981
  
982 983 984 985 986 987 988 989 990 991 992 993 994 995 996
  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 ();
997 998
}

999 1000
/**
 * gtk_print_run_page_setup_dialog_async:
1001 1002
 * @parent: (allow-none): transient parent, or %NULL
 * @page_setup: (allow-none): an existing #GtkPageSetup, or %NULL
1003
 * @settings: a #GtkPrintSettings
1004 1005
 * @done_cb: (scope async): a function to call when the user saves
 *           the modified page setup
1006 1007
 * @data: user data to pass to @done_cb
 * 
Matthias Clasen's avatar
Matthias Clasen committed
1008
 * Runs a page setup dialog, letting the user modify the values from @page_setup. 
1009
 *
Matthias Clasen's avatar
Matthias Clasen committed
1010 1011 1012
 * 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.
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
 *
 * 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
1041 1042
struct _PrinterFinder 
{
Alexander Larsson's avatar
Alexander Larsson committed
1043 1044 1045
  gboolean found_printer;
  GFunc func;
  gpointer data;
Matthias Clasen's avatar
Matthias Clasen committed
1046
  gchar *printer_name;
Alexander Larsson's avatar
Alexander Larsson committed
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072
  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);

1073
  return G_SOURCE_REMOVE;
Alexander Larsson's avatar
Alexander Larsson committed
1074 1075 1076 1077 1078 1079 1080
}

static void
printer_added_cb (GtkPrintBackend *backend, 
                  GtkPrinter      *printer, 
		  PrinterFinder   *finder)
{
1081 1082 1083 1084 1085
  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
1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125
    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
1126
find_printer_init (PrinterFinder   *finder,
Alexander Larsson's avatar
Alexander Larsson committed
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153
		   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
1154
      g_signal_connect (backend, "printer-added", 
Alexander Larsson's avatar
Alexander Larsson committed
1155 1156
			(GCallback) printer_added_cb, 
			finder);
Matthias Clasen's avatar
Matthias Clasen committed
1157
      g_signal_connect (backend, "printer-list-done", 
Alexander Larsson's avatar
Alexander Larsson committed
1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194
			(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
1195 1196 1197
find_printer (const gchar *printer,
	      GFunc        func,
	      gpointer     data)
Alexander Larsson's avatar
Alexander Larsson committed
1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220
{
  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);
}