]> Pileus Git - ~andy/gtk/blob - modules/printbackends/cups/gtkprintbackendcups.c
Fix some IPP compliance issues. (#391523, Albrecht Dress)
[~andy/gtk] / modules / printbackends / cups / gtkprintbackendcups.c
1 /* GTK - The GIMP Toolkit
2  * gtkprintbackendcups.h: Default implementation of GtkPrintBackend 
3  * for the Common Unix Print System (CUPS)
4  * Copyright (C) 2003, Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include <unistd.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <stdlib.h>
26
27 #include <config.h>
28 #include <cups/cups.h>
29 #include <cups/language.h>
30 #include <cups/http.h>
31 #include <cups/ipp.h>
32 #include <errno.h>
33 #include <cairo.h>
34 #include <cairo-pdf.h>
35 #include <cairo-ps.h>
36
37 #include <glib/gi18n-lib.h>
38 #include <gmodule.h>
39
40 #include <gtk/gtkprintoperation.h>
41 #include <gtk/gtkprintsettings.h>
42 #include <gtk/gtkprintbackend.h>
43 #include <gtk/gtkprinter.h>
44 #include <gtk/gtkprinter-private.h>
45
46 #include "gtkprintbackendcups.h"
47 #include "gtkprintercups.h"
48
49 #include "gtkcupsutils.h"
50 #include "gtkdebug.h"
51
52
53 typedef struct _GtkPrintBackendCupsClass GtkPrintBackendCupsClass;
54
55 #define GTK_PRINT_BACKEND_CUPS_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
56 #define GTK_IS_PRINT_BACKEND_CUPS_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_CUPS))
57 #define GTK_PRINT_BACKEND_CUPS_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
58
59 #define _CUPS_MAX_ATTEMPTS 10 
60 #define _CUPS_MAX_CHUNK_SIZE 8192
61
62 #define _CUPS_MAP_ATTR_INT(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = attr->values[0].integer;}
63 #define _CUPS_MAP_ATTR_STR(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = attr->values[0].string.text;}
64
65 static GType print_backend_cups_type = 0;
66
67 typedef void (* GtkPrintCupsResponseCallbackFunc) (GtkPrintBackend *print_backend,
68                                                    GtkCupsResult   *result, 
69                                                    gpointer         user_data);
70
71 typedef enum 
72 {
73   DISPATCH_SETUP,
74   DISPATCH_REQUEST,
75   DISPATCH_SEND,
76   DISPATCH_CHECK,
77   DISPATCH_READ,
78   DISPATCH_ERROR
79 } GtkPrintCupsDispatchState;
80
81 typedef struct 
82 {
83   GSource source;
84
85   http_t *http;
86   GtkCupsRequest *request;
87   GPollFD *data_poll;
88   GtkPrintBackendCups *backend;
89
90 } GtkPrintCupsDispatchWatch;
91
92 struct _GtkPrintBackendCupsClass
93 {
94   GtkPrintBackendClass parent_class;
95 };
96
97 struct _GtkPrintBackendCups
98 {
99   GtkPrintBackend parent_instance;
100
101   char *default_printer;
102   
103   guint list_printers_poll;
104   guint list_printers_pending : 1;
105   guint got_default_printer   : 1;
106 };
107
108 static GObjectClass *backend_parent_class;
109
110 static void                 gtk_print_backend_cups_class_init      (GtkPrintBackendCupsClass          *class);
111 static void                 gtk_print_backend_cups_init            (GtkPrintBackendCups               *impl);
112 static void                 gtk_print_backend_cups_finalize        (GObject                           *object);
113 static void                 gtk_print_backend_cups_dispose         (GObject                           *object);
114 static void                 cups_get_printer_list                  (GtkPrintBackend                   *print_backend);
115 static void                 cups_request_execute                   (GtkPrintBackendCups               *print_backend,
116                                                                     GtkCupsRequest                    *request,
117                                                                     GtkPrintCupsResponseCallbackFunc   callback,
118                                                                     gpointer                           user_data,
119                                                                     GDestroyNotify                     notify);
120 static void                 cups_printer_get_settings_from_options (GtkPrinter                        *printer,
121                                                                     GtkPrinterOptionSet               *options,
122                                                                     GtkPrintSettings                  *settings);
123 static gboolean             cups_printer_mark_conflicts            (GtkPrinter                        *printer,
124                                                                     GtkPrinterOptionSet               *options);
125 static GtkPrinterOptionSet *cups_printer_get_options               (GtkPrinter                        *printer,
126                                                                     GtkPrintSettings                  *settings,
127                                                                     GtkPageSetup                      *page_setup,
128                                                                     GtkPrintCapabilities               capabilities);
129 static void                 cups_printer_prepare_for_print         (GtkPrinter                        *printer,
130                                                                     GtkPrintJob                       *print_job,
131                                                                     GtkPrintSettings                  *settings,
132                                                                     GtkPageSetup                      *page_setup);
133 static GList *              cups_printer_list_papers               (GtkPrinter                        *printer);
134 static void                 cups_printer_request_details           (GtkPrinter                        *printer);
135 static void                 cups_request_default_printer           (GtkPrintBackendCups               *print_backend);
136 static void                 cups_request_ppd                       (GtkPrinter                        *printer);
137 static void                 cups_printer_get_hard_margins          (GtkPrinter                        *printer,
138                                                                     double                            *top,
139                                                                     double                            *bottom,
140                                                                     double                            *left,
141                                                                     double                            *right);
142 static GtkPrintCapabilities cups_printer_get_capabilities          (GtkPrinter                        *printer);
143 static void                 set_option_from_settings               (GtkPrinterOption                  *option,
144                                                                     GtkPrintSettings                  *setting);
145 static void                 cups_begin_polling_info                (GtkPrintBackendCups               *print_backend,
146                                                                     GtkPrintJob                       *job,
147                                                                     int                                job_id);
148 static gboolean             cups_job_info_poll_timeout             (gpointer                           user_data);
149 static void                 gtk_print_backend_cups_print_stream    (GtkPrintBackend                   *backend,
150                                                                     GtkPrintJob                       *job,
151                                                                     GIOChannel                        *data_io,
152                                                                     GtkPrintJobCompleteFunc            callback,
153                                                                     gpointer                           user_data,
154                                                                     GDestroyNotify                     dnotify);
155 static cairo_surface_t *    cups_printer_create_cairo_surface      (GtkPrinter                        *printer,
156                                                                     GtkPrintSettings                  *settings,
157                                                                     gdouble                            width,
158                                                                     gdouble                            height,
159                                                                     GIOChannel                        *cache_io);
160
161
162 static void
163 gtk_print_backend_cups_register_type (GTypeModule *module)
164 {
165   static const GTypeInfo print_backend_cups_info =
166   {
167     sizeof (GtkPrintBackendCupsClass),
168     NULL,               /* base_init */
169     NULL,               /* base_finalize */
170     (GClassInitFunc) gtk_print_backend_cups_class_init,
171     NULL,               /* class_finalize */
172     NULL,               /* class_data */
173     sizeof (GtkPrintBackendCups),
174     0,                  /* n_preallocs */
175     (GInstanceInitFunc) gtk_print_backend_cups_init
176   };
177
178   print_backend_cups_type = g_type_module_register_type (module,
179                                                          GTK_TYPE_PRINT_BACKEND,
180                                                          "GtkPrintBackendCups",
181                                                          &print_backend_cups_info, 0);
182 }
183
184 G_MODULE_EXPORT void 
185 pb_module_init (GTypeModule *module)
186 {
187   GTK_NOTE (PRINTING,
188             g_print ("CUPS Backend: Initializing the CUPS print backend module\n")); 
189
190   gtk_print_backend_cups_register_type (module);
191   gtk_printer_cups_register_type (module);
192 }
193
194 G_MODULE_EXPORT void 
195 pb_module_exit (void)
196 {
197
198 }
199   
200 G_MODULE_EXPORT GtkPrintBackend * 
201 pb_module_create (void)
202 {
203   return gtk_print_backend_cups_new ();
204 }
205
206 /*
207  * GtkPrintBackendCups
208  */
209 GType
210 gtk_print_backend_cups_get_type (void)
211 {
212   return print_backend_cups_type;
213 }
214
215 /**
216  * gtk_print_backend_cups_new:
217  *
218  * Creates a new #GtkPrintBackendCups object. #GtkPrintBackendCups
219  * implements the #GtkPrintBackend interface with direct access to
220  * the filesystem using Unix/Linux API calls
221  *
222  * Return value: the new #GtkPrintBackendCups object
223  */
224 GtkPrintBackend *
225 gtk_print_backend_cups_new (void)
226 {
227   GTK_NOTE (PRINTING,
228             g_print ("CUPS Backend: Creating a new CUPS print backend object\n"));
229
230   return g_object_new (GTK_TYPE_PRINT_BACKEND_CUPS, NULL);
231 }
232
233 static void
234 gtk_print_backend_cups_class_init (GtkPrintBackendCupsClass *class)
235 {
236   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
237   GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class);
238
239   backend_parent_class = g_type_class_peek_parent (class);
240
241   gobject_class->finalize = gtk_print_backend_cups_finalize;
242   gobject_class->dispose = gtk_print_backend_cups_dispose;
243
244   backend_class->request_printer_list = cups_get_printer_list; 
245   backend_class->print_stream = gtk_print_backend_cups_print_stream;
246   backend_class->printer_request_details = cups_printer_request_details;
247   backend_class->printer_create_cairo_surface = cups_printer_create_cairo_surface;
248   backend_class->printer_get_options = cups_printer_get_options;
249   backend_class->printer_mark_conflicts = cups_printer_mark_conflicts;
250   backend_class->printer_get_settings_from_options = cups_printer_get_settings_from_options;
251   backend_class->printer_prepare_for_print = cups_printer_prepare_for_print;
252   backend_class->printer_list_papers = cups_printer_list_papers;
253   backend_class->printer_get_hard_margins = cups_printer_get_hard_margins;
254   backend_class->printer_get_capabilities = cups_printer_get_capabilities;
255 }
256
257 static cairo_status_t
258 _cairo_write_to_cups (void                *closure,
259                       const unsigned char *data,
260                       unsigned int         length)
261 {
262   GIOChannel *io = (GIOChannel *)closure;
263   gsize written;
264   GError *error;
265
266   error = NULL;
267
268   GTK_NOTE (PRINTING,
269             g_print ("CUPS Backend: Writting %i byte chunk to temp file\n", length));
270
271   while (length > 0) 
272     {
273       g_io_channel_write_chars (io, (gchar *)data, length, &written, &error);
274
275       if (error != NULL)
276         {
277           GTK_NOTE (PRINTING,
278                     g_print ("CUPS Backend: Error writting to temp file, %s\n", error->message));
279
280           g_error_free (error);
281           return CAIRO_STATUS_WRITE_ERROR;
282         }    
283
284       GTK_NOTE (PRINTING,
285                 g_print ("CUPS Backend: Wrote %i bytes to temp file\n", written));
286
287       data += written;
288       length -= written;
289     }
290
291   return CAIRO_STATUS_SUCCESS;
292 }
293
294 static cairo_surface_t *
295 cups_printer_create_cairo_surface (GtkPrinter       *printer,
296                                    GtkPrintSettings *settings,
297                                    gdouble           width, 
298                                    gdouble           height,
299                                    GIOChannel       *cache_io)
300 {
301   cairo_surface_t *surface; 
302  
303   /* TODO: check if it is a ps or pdf printer */
304   
305   surface = cairo_ps_surface_create_for_stream  (_cairo_write_to_cups, cache_io, width, height);
306
307   /* TODO: DPI from settings object? */
308   cairo_surface_set_fallback_resolution (surface, 300, 300);
309
310   return surface;
311 }
312
313 typedef struct {
314   GtkPrintJobCompleteFunc callback;
315   GtkPrintJob *job;
316   gpointer user_data;
317   GDestroyNotify dnotify;
318 } CupsPrintStreamData;
319
320 static void
321 cups_free_print_stream_data (CupsPrintStreamData *data)
322 {
323   GTK_NOTE (PRINTING,
324             g_print ("CUPS Backend: %s\n", G_STRFUNC));
325
326   if (data->dnotify)
327     data->dnotify (data->user_data);
328   g_object_unref (data->job);
329   g_free (data);
330 }
331
332 static void
333 cups_print_cb (GtkPrintBackendCups *print_backend,
334                GtkCupsResult       *result,
335                gpointer             user_data)
336 {
337   GError *error = NULL;
338   CupsPrintStreamData *ps = user_data;
339
340   GTK_NOTE (PRINTING,
341             g_print ("CUPS Backend: %s\n", G_STRFUNC)); 
342
343   if (gtk_cups_result_is_error (result))
344     error = g_error_new_literal (gtk_print_error_quark (),
345                                  GTK_PRINT_ERROR_INTERNAL_ERROR,
346                                  gtk_cups_result_get_error_string (result));
347
348   if (ps->callback)
349     ps->callback (ps->job, ps->user_data, error);
350
351   if (error == NULL)
352     {
353       int job_id = 0;
354       ipp_attribute_t *attr;            /* IPP job-id attribute */
355       ipp_t *response = gtk_cups_result_get_response (result);
356
357       if ((attr = ippFindAttribute (response, "job-id", IPP_TAG_INTEGER)) != NULL)
358         job_id = attr->values[0].integer;
359
360       if (!gtk_print_job_get_track_print_status (ps->job) || job_id == 0)
361         gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED);
362       else
363         {
364           gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_PENDING);
365           cups_begin_polling_info (print_backend, ps->job, job_id);
366         }
367     } 
368   else
369     gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED_ABORTED);
370
371   
372   if (error)
373     g_error_free (error);
374   
375 }
376
377 static void
378 add_cups_options (const gchar *key,
379                   const gchar *value,
380                   gpointer     user_data)
381 {
382   GtkCupsRequest *request = user_data;
383
384   if (!g_str_has_prefix (key, "cups-"))
385     return;
386
387   if (strcmp (value, "gtk-ignore-value") == 0)
388     return;
389   
390   key = key + strlen ("cups-");
391
392   gtk_cups_request_encode_option (request, key, value);
393 }
394
395 static void
396 gtk_print_backend_cups_print_stream (GtkPrintBackend         *print_backend,
397                                      GtkPrintJob             *job,
398                                      GIOChannel              *data_io,
399                                      GtkPrintJobCompleteFunc  callback,
400                                      gpointer                 user_data,
401                                      GDestroyNotify           dnotify)
402 {
403   GtkPrinterCups *cups_printer;
404   CupsPrintStreamData *ps;
405   GtkCupsRequest *request;
406   GtkPrintSettings *settings;
407   const gchar *title;
408
409   GTK_NOTE (PRINTING,
410             g_print ("CUPS Backend: %s\n", G_STRFUNC));   
411
412   cups_printer = GTK_PRINTER_CUPS (gtk_print_job_get_printer (job));
413   settings = gtk_print_job_get_settings (job);
414
415   request = gtk_cups_request_new (NULL,
416                                   GTK_CUPS_POST,
417                                   IPP_PRINT_JOB,
418                                   data_io,
419                                   NULL,
420                                   cups_printer->device_uri);
421
422   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
423                                    NULL, cups_printer->printer_uri);
424
425   title = gtk_print_job_get_title (job);
426   if (title)
427     gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
428                                      title);
429
430   gtk_print_settings_foreach (settings, add_cups_options, request);
431   
432   ps = g_new0 (CupsPrintStreamData, 1);
433   ps->callback = callback;
434   ps->user_data = user_data;
435   ps->dnotify = dnotify;
436   ps->job = g_object_ref (job);
437
438   cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
439                         request,
440                         (GtkPrintCupsResponseCallbackFunc) cups_print_cb,
441                         ps,
442                         (GDestroyNotify)cups_free_print_stream_data);
443 }
444
445
446 static void
447 gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups)
448 {
449   backend_cups->list_printers_poll = FALSE;  
450   backend_cups->got_default_printer = FALSE;  
451   backend_cups->list_printers_pending = FALSE;
452
453   cups_request_default_printer (backend_cups);
454 }
455
456 static void
457 gtk_print_backend_cups_finalize (GObject *object)
458 {
459   GtkPrintBackendCups *backend_cups;
460   
461   GTK_NOTE (PRINTING,
462             g_print ("CUPS Backend: finalizing CUPS backend module\n"));
463
464   backend_cups = GTK_PRINT_BACKEND_CUPS (object);
465
466   g_free (backend_cups->default_printer);
467   backend_cups->default_printer = NULL;
468   
469   backend_parent_class->finalize (object);
470 }
471
472 static void
473 gtk_print_backend_cups_dispose (GObject *object)
474 {
475   GtkPrintBackendCups *backend_cups;
476
477   GTK_NOTE (PRINTING,
478             g_print ("CUPS Backend: %s\n", G_STRFUNC));
479
480   backend_cups = GTK_PRINT_BACKEND_CUPS (object);
481
482   if (backend_cups->list_printers_poll > 0)
483     g_source_remove (backend_cups->list_printers_poll);
484   backend_cups->list_printers_poll = 0;
485   
486   backend_parent_class->dispose (object);
487 }
488
489
490 static gboolean
491 cups_dispatch_watch_check (GSource *source)
492 {
493   GtkPrintCupsDispatchWatch *dispatch;
494   GtkCupsPollState poll_state;
495   gboolean result;
496
497   GTK_NOTE (PRINTING,
498             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source)); 
499
500   dispatch = (GtkPrintCupsDispatchWatch *) source;
501
502   poll_state = gtk_cups_request_get_poll_state (dispatch->request);
503
504   if (dispatch->request->http != NULL)
505     {
506       if (dispatch->data_poll == NULL)
507         {
508           dispatch->data_poll = g_new0 (GPollFD, 1);
509           g_source_add_poll (source, dispatch->data_poll);
510         }
511       else
512         {
513           if (poll_state == GTK_CUPS_HTTP_READ)
514             dispatch->data_poll->events = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI;
515           else if (poll_state == GTK_CUPS_HTTP_WRITE)
516             dispatch->data_poll->events = G_IO_OUT | G_IO_ERR;
517           else
518             dispatch->data_poll->events = 0;
519         }
520       dispatch->data_poll->fd = httpGetFd (dispatch->request->http);
521     }
522     
523   if (poll_state != GTK_CUPS_HTTP_IDLE)  
524     if (!(dispatch->data_poll->revents & dispatch->data_poll->events)) 
525        return FALSE;
526   
527   result = gtk_cups_request_read_write (dispatch->request);
528   if (result && dispatch->data_poll != NULL)
529     {
530       g_source_remove_poll (source, dispatch->data_poll);
531       g_free (dispatch->data_poll);
532       dispatch->data_poll = NULL;
533     }
534   
535   return result;
536 }
537
538 static gboolean
539 cups_dispatch_watch_prepare (GSource *source,
540                              gint    *timeout_)
541 {
542   GtkPrintCupsDispatchWatch *dispatch;
543
544   dispatch = (GtkPrintCupsDispatchWatch *) source;
545
546   GTK_NOTE (PRINTING,
547             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
548
549   *timeout_ = -1;
550   
551   return gtk_cups_request_read_write (dispatch->request);
552 }
553
554 static gboolean
555 cups_dispatch_watch_dispatch (GSource     *source,
556                               GSourceFunc  callback,
557                               gpointer     user_data)
558 {
559   GtkPrintCupsDispatchWatch *dispatch;
560   GtkPrintCupsResponseCallbackFunc ep_callback;  
561   GtkCupsResult *result;
562   
563   g_assert (callback != NULL);
564
565   ep_callback = (GtkPrintCupsResponseCallbackFunc) callback;
566   
567   dispatch = (GtkPrintCupsDispatchWatch *) source;
568
569   result = gtk_cups_request_get_result (dispatch->request);
570
571   GTK_NOTE (PRINTING,
572             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
573
574   if (gtk_cups_result_is_error (result))
575     g_warning ("Error result: %s", gtk_cups_result_get_error_string (result));
576
577   ep_callback (GTK_PRINT_BACKEND (dispatch->backend), result, user_data);
578
579   return FALSE;
580 }
581
582 static void
583 cups_dispatch_watch_finalize (GSource *source)
584 {
585   GtkPrintCupsDispatchWatch *dispatch;
586
587   GTK_NOTE (PRINTING,
588             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
589
590   dispatch = (GtkPrintCupsDispatchWatch *) source;
591
592   gtk_cups_request_free (dispatch->request);
593
594   if (dispatch->backend)
595     {
596       /* We need to unref this at idle time, because it might be the
597        * last reference to this module causing the code to be
598        * unloaded (including this particular function!)
599        * Update: Doing this at idle caused a deadlock taking the
600        * mainloop context lock while being in a GSource callout for
601        * multithreaded apps. So, for now we just disable unloading
602        * of print backends. See _gtk_print_backend_create for the
603        * disabling.
604        */
605       g_object_unref (dispatch->backend);
606       dispatch->backend = NULL;
607     }
608
609   if (dispatch->data_poll != NULL)
610     g_free (dispatch->data_poll);
611 }
612
613 static GSourceFuncs _cups_dispatch_watch_funcs = {
614   cups_dispatch_watch_prepare,
615   cups_dispatch_watch_check,
616   cups_dispatch_watch_dispatch,
617   cups_dispatch_watch_finalize
618 };
619
620
621 static void
622 cups_request_execute (GtkPrintBackendCups              *print_backend,
623                       GtkCupsRequest                   *request,
624                       GtkPrintCupsResponseCallbackFunc  callback,
625                       gpointer                          user_data,
626                       GDestroyNotify                    notify)
627 {
628   GtkPrintCupsDispatchWatch *dispatch;
629
630   dispatch = (GtkPrintCupsDispatchWatch *) g_source_new (&_cups_dispatch_watch_funcs, 
631                                                          sizeof (GtkPrintCupsDispatchWatch));
632
633   GTK_NOTE (PRINTING,
634             g_print ("CUPS Backend: %s <source %p> - Executing cups request on server '%s' and resource '%s'\n", G_STRFUNC, dispatch, request->server, request->resource));
635
636   dispatch->request = request;
637   dispatch->backend = g_object_ref (print_backend);
638   dispatch->data_poll = NULL;
639
640   g_source_set_callback ((GSource *) dispatch, (GSourceFunc) callback, user_data, notify);
641
642   g_source_attach ((GSource *) dispatch, NULL);
643   g_source_unref ((GSource *) dispatch);
644 }
645
646 static void
647 cups_request_printer_info_cb (GtkPrintBackendCups *backend,
648                               GtkCupsResult       *result,
649                               gpointer             user_data)
650 {
651   ipp_attribute_t *attr;
652   ipp_t *response;
653   gchar *printer_name;
654   GtkPrinterCups *cups_printer;
655   GtkPrinter *printer;
656   gchar *loc;
657   gchar *desc;
658   gchar *state_msg;
659   int job_count;
660   gboolean status_changed;  
661
662   g_assert (GTK_IS_PRINT_BACKEND_CUPS (backend));
663
664   printer_name = (gchar *)user_data;
665   printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (backend),
666                                             printer_name);
667
668   GTK_NOTE (PRINTING,
669             g_print ("CUPS Backend: %s - Got printer info for printer '%s'\n", G_STRFUNC, printer_name));
670
671   if (!printer)
672     {
673       GTK_NOTE (PRINTING,
674             g_print ("CUPS Backend: Could not find printer called '%s'\n", printer_name));
675       return;
676     }
677
678   cups_printer = GTK_PRINTER_CUPS (printer);
679   
680   if (gtk_cups_result_is_error (result))
681     {
682       if (gtk_printer_is_new (printer))
683         {
684           gtk_print_backend_remove_printer (GTK_PRINT_BACKEND (backend),
685                                             printer);
686           return;
687         }
688       else
689         return; /* TODO: mark as inactive printer */
690     }
691
692   response = gtk_cups_result_get_response (result);
693
694   /* TODO: determine printer type and use correct icon */
695   gtk_printer_set_icon_name (printer, "gtk-print");
696  
697   state_msg = "";
698   loc = "";
699   desc = "";
700   job_count = 0;
701   for (attr = response->attrs; attr != NULL; attr = attr->next) 
702     {
703       if (!attr->name)
704         continue;
705
706       _CUPS_MAP_ATTR_STR (attr, loc, "printer-location");
707       _CUPS_MAP_ATTR_STR (attr, desc, "printer-info");
708       _CUPS_MAP_ATTR_STR (attr, state_msg, "printer-state-message");
709       _CUPS_MAP_ATTR_INT (attr, cups_printer->state, "printer-state");
710       _CUPS_MAP_ATTR_INT (attr, job_count, "queued-job-count");
711     }
712
713   status_changed = gtk_printer_set_job_count (printer, job_count);
714   
715   status_changed |= gtk_printer_set_location (printer, loc);
716   status_changed |= gtk_printer_set_description (printer, desc);
717   status_changed |= gtk_printer_set_state_message (printer, state_msg);
718
719   if (status_changed)
720     g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
721                            "printer-status-changed", printer); 
722 }
723
724 static void
725 cups_request_printer_info (GtkPrintBackendCups *print_backend,
726                            const gchar         *printer_name)
727 {
728   GtkCupsRequest *request;
729   gchar *printer_uri;
730   static const char * const pattrs[] =  /* Attributes we're interested in */
731     {
732       "printer-location",
733       "printer-info",
734       "printer-state-message",
735       "printer-state",
736       "queued-job-count"
737     };
738
739   request = gtk_cups_request_new (NULL,
740                                   GTK_CUPS_POST,
741                                   IPP_GET_PRINTER_ATTRIBUTES,
742                                   NULL,
743                                   NULL,
744                                   NULL);
745
746   printer_uri = g_strdup_printf ("ipp://localhost/printers/%s",
747                                   printer_name);
748   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
749                                    "printer-uri", NULL, printer_uri);
750
751   GTK_NOTE (PRINTING,
752             g_print ("CUPS Backend: %s - Requesting printer info for URI '%s'\n", G_STRFUNC, printer_uri));
753
754   g_free (printer_uri);
755
756   gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
757                                     "requested-attributes", G_N_ELEMENTS (pattrs),
758                                     NULL, pattrs);
759  
760   cups_request_execute (print_backend,
761                         request,
762                         (GtkPrintCupsResponseCallbackFunc) cups_request_printer_info_cb,
763                         g_strdup (printer_name),
764                         (GDestroyNotify) g_free);
765 }
766
767
768 typedef struct {
769   GtkPrintBackendCups *print_backend;
770   GtkPrintJob *job;
771   int job_id;
772   int counter;
773 } CupsJobPollData;
774
775 static void
776 job_object_died (gpointer  user_data,
777                  GObject  *where_the_object_was)
778 {
779   CupsJobPollData *data = user_data;
780   data->job = NULL;
781 }
782
783 static void
784 cups_job_poll_data_free (CupsJobPollData *data)
785 {
786   if (data->job)
787     g_object_weak_unref (G_OBJECT (data->job), job_object_died, data);
788     
789   g_free (data);
790 }
791
792 static void
793 cups_request_job_info_cb (GtkPrintBackendCups *print_backend,
794                           GtkCupsResult       *result,
795                           gpointer             user_data)
796 {
797   CupsJobPollData *data = user_data;
798   ipp_attribute_t *attr;
799   ipp_t *response;
800   int state;
801   gboolean done;
802
803   if (data->job == NULL)
804     {
805       cups_job_poll_data_free (data);
806       return;
807     }
808
809   data->counter++;
810   
811   response = gtk_cups_result_get_response (result);
812
813   state = 0;
814   for (attr = response->attrs; attr != NULL; attr = attr->next) 
815     {
816       if (!attr->name)
817         continue;
818       
819       _CUPS_MAP_ATTR_INT (attr, state, "job-state");
820     }
821   
822   done = FALSE;
823   switch (state)
824     {
825     case IPP_JOB_PENDING:
826     case IPP_JOB_HELD:
827     case IPP_JOB_STOPPED:
828       gtk_print_job_set_status (data->job,
829                                 GTK_PRINT_STATUS_PENDING);
830       break;
831     case IPP_JOB_PROCESSING:
832       gtk_print_job_set_status (data->job,
833                                 GTK_PRINT_STATUS_PRINTING);
834       break;
835     default:
836     case IPP_JOB_CANCELLED:
837     case IPP_JOB_ABORTED:
838       gtk_print_job_set_status (data->job,
839                                 GTK_PRINT_STATUS_FINISHED_ABORTED);
840       done = TRUE;
841       break;
842     case 0:
843     case IPP_JOB_COMPLETED:
844       gtk_print_job_set_status (data->job,
845                                 GTK_PRINT_STATUS_FINISHED);
846       done = TRUE;
847       break;
848     }
849
850   if (!done && data->job != NULL)
851     {
852       guint32 timeout;
853
854       if (data->counter < 5)
855         timeout = 100;
856       else if (data->counter < 10)
857         timeout = 500;
858       else
859         timeout = 1000;
860       
861       g_timeout_add (timeout, cups_job_info_poll_timeout, data);
862     }
863   else
864     cups_job_poll_data_free (data);    
865 }
866
867 static void
868 cups_request_job_info (CupsJobPollData *data)
869 {
870   GtkCupsRequest *request;
871   gchar *printer_uri;
872
873   request = gtk_cups_request_new (NULL,
874                                   GTK_CUPS_POST,
875                                   IPP_GET_JOB_ATTRIBUTES,
876                                   NULL,
877                                   NULL,
878                                   NULL);
879
880   printer_uri = g_strdup_printf ("ipp://localhost/jobs/%d", data->job_id);
881   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
882                                    "job-uri", NULL, printer_uri);
883   g_free (printer_uri);
884
885   cups_request_execute (data->print_backend,
886                         request,
887                         (GtkPrintCupsResponseCallbackFunc) cups_request_job_info_cb,
888                         data,
889                         NULL);
890 }
891
892 static gboolean
893 cups_job_info_poll_timeout (gpointer user_data)
894 {
895   CupsJobPollData *data = user_data;
896   
897   if (data->job == NULL)
898     cups_job_poll_data_free (data);
899   else
900     cups_request_job_info (data);
901   
902   return FALSE;
903 }
904
905 static void
906 cups_begin_polling_info (GtkPrintBackendCups *print_backend,
907                          GtkPrintJob         *job,
908                          gint                 job_id)
909 {
910   CupsJobPollData *data;
911
912   data = g_new0 (CupsJobPollData, 1);
913
914   data->print_backend = print_backend;
915   data->job = job;
916   data->job_id = job_id;
917   data->counter = 0;
918
919   g_object_weak_ref (G_OBJECT (job), job_object_died, data);
920
921   cups_request_job_info (data);
922 }
923
924 static void
925 mark_printer_inactive (GtkPrinter      *printer, 
926                        GtkPrintBackend *backend)
927 {
928   gtk_printer_set_is_active (printer, FALSE);
929   g_signal_emit_by_name (backend, "printer-removed", printer);
930 }
931
932 static gint
933 find_printer (GtkPrinter  *printer, 
934               const gchar *find_name)
935 {
936   const gchar *printer_name;
937
938   printer_name = gtk_printer_get_name (printer);
939   return g_ascii_strcasecmp (printer_name, find_name);
940 }
941
942 static void
943 cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
944                               GtkCupsResult       *result,
945                               gpointer             user_data)
946 {
947   GtkPrintBackend *backend = GTK_PRINT_BACKEND (cups_backend);
948   ipp_attribute_t *attr;
949   ipp_t *response;
950   gboolean list_has_changed;
951   GList *removed_printer_checklist;
952
953   list_has_changed = FALSE;
954
955   GTK_NOTE (PRINTING,
956             g_print ("CUPS Backend: %s\n", G_STRFUNC));
957
958   cups_backend->list_printers_pending = FALSE;
959
960   if (gtk_cups_result_is_error (result))
961     {
962       g_warning ("Error getting printer list: %s", gtk_cups_result_get_error_string (result));
963
964       goto done;
965     }
966   
967   /* Gather the names of the printers in the current queue
968    * so we may check to see if they were removed 
969    */
970   removed_printer_checklist = gtk_print_backend_get_printer_list (backend);
971                                                                   
972   response = gtk_cups_result_get_response (result);
973
974   for (attr = response->attrs; attr != NULL; attr = attr->next)
975     {
976       GtkPrinter *printer;
977       const gchar *printer_name;
978       const gchar *printer_uri;
979       const gchar *member_uris;
980       GList *node;
981       
982       /* Skip leading attributes until we hit a printer...
983        */
984       while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
985         attr = attr->next;
986
987       if (attr == NULL)
988         break;
989
990       printer_name = NULL;
991       printer_uri = NULL;
992       member_uris = NULL;
993       while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
994       {
995         if (!strcmp (attr->name, "printer-name") &&
996             attr->value_tag == IPP_TAG_NAME)
997           printer_name = attr->values[0].string.text;
998         else if (!strcmp (attr->name, "printer-uri-supported") &&
999                  attr->value_tag == IPP_TAG_URI)
1000           printer_uri = attr->values[0].string.text;
1001         else if (!strcmp (attr->name, "member-uris") &&
1002                  attr->value_tag == IPP_TAG_URI)
1003           member_uris = attr->values[0].string.text;
1004         else
1005           {
1006             GTK_NOTE (PRINTING,
1007                       g_print ("CUPS Backend: Attribute %s ignored", attr->name));
1008           }
1009
1010         attr = attr->next;
1011       }
1012
1013       if (printer_name == NULL ||
1014           (printer_uri == NULL && member_uris == NULL))
1015       {
1016         if (attr == NULL)
1017           break;
1018         else
1019           continue;
1020       }
1021    
1022       /* remove name from checklist if it was found */
1023       node = g_list_find_custom (removed_printer_checklist, printer_name, (GCompareFunc) find_printer);
1024       removed_printer_checklist = g_list_delete_link (removed_printer_checklist, node);
1025  
1026       printer = gtk_print_backend_find_printer (backend, printer_name);
1027       if (!printer)
1028         {
1029           GtkPrinterCups *cups_printer;
1030           char uri[HTTP_MAX_URI];       /* Printer URI */
1031           char method[HTTP_MAX_URI];    /* Method/scheme name */
1032           char username[HTTP_MAX_URI];  /* Username:password */
1033           char hostname[HTTP_MAX_URI];  /* Hostname */
1034           char resource[HTTP_MAX_URI];  /* Resource name */
1035           int  port;                    /* Port number */
1036           
1037           list_has_changed = TRUE;
1038           cups_printer = gtk_printer_cups_new (printer_name, backend);
1039
1040           cups_printer->device_uri = g_strdup_printf ("/printers/%s", printer_name);
1041
1042           /* Check to see if we are looking at a class */
1043           if (member_uris)
1044             {
1045               cups_printer->printer_uri = g_strdup (member_uris);
1046               /* TODO if member_uris is a class we need to recursivly find a printer */
1047               GTK_NOTE (PRINTING,
1048                         g_print ("CUPS Backend: Found class with printer %s\n", member_uris));
1049             }
1050           else
1051             {
1052               cups_printer->printer_uri = g_strdup (printer_uri);
1053               GTK_NOTE (PRINTING,
1054                         g_print ("CUPS Backend: Found printer %s\n", printer_uri));
1055             }
1056
1057 #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1
1058           httpSeparateURI (HTTP_URI_CODING_ALL, cups_printer->printer_uri, 
1059                            method, sizeof (method), 
1060                            username, sizeof (username),
1061                            hostname, sizeof (hostname),
1062                            &port, 
1063                            resource, sizeof (resource));
1064
1065 #else
1066           httpSeparate (cups_printer->printer_uri, 
1067                         method, 
1068                         username, 
1069                         hostname,
1070                         &port, 
1071                         resource);
1072 #endif
1073
1074           if (!strncmp (resource, "/printers/", 10))
1075             {
1076               cups_printer->ppd_name = g_strdup (resource + 10);
1077               GTK_NOTE (PRINTING,
1078                         g_print ("CUPS Backend: Setting ppd name '%s' for printer/class '%s'\n", cups_printer->ppd_name, printer_name));
1079             }
1080
1081           gethostname (uri, sizeof(uri));
1082           if (strcasecmp (uri, hostname) == 0)
1083             strcpy (hostname, "localhost");
1084
1085           cups_printer->hostname = g_strdup (hostname);
1086           cups_printer->port = port;
1087           
1088           printer = GTK_PRINTER (cups_printer);
1089           
1090           if (cups_backend->default_printer != NULL &&
1091               strcmp (cups_backend->default_printer, gtk_printer_get_name (printer)) == 0)
1092             gtk_printer_set_is_default (printer, TRUE);
1093
1094           
1095           gtk_print_backend_add_printer (backend, printer);
1096         }
1097       else
1098         g_object_ref (printer);
1099
1100       if (!gtk_printer_is_active (printer))
1101         {
1102           gtk_printer_set_is_active (printer, TRUE);
1103           gtk_printer_set_is_new (printer, TRUE);
1104           list_has_changed = TRUE;
1105         }
1106
1107       if (gtk_printer_is_new (printer))
1108         {
1109           g_signal_emit_by_name (backend, "printer-added", printer);
1110
1111           gtk_printer_set_is_new (printer, FALSE);
1112         }
1113
1114       cups_request_printer_info (cups_backend, gtk_printer_get_name (printer));
1115
1116       /* The ref is held by GtkPrintBackend, in add_printer() */
1117       g_object_unref (printer);
1118
1119       
1120       if (attr == NULL)
1121         break;
1122     }
1123
1124   /* look at the removed printers checklist and mark any printer
1125      as inactive if it is in the list, emitting a printer_removed signal */
1126   if (removed_printer_checklist != NULL)
1127     {
1128       g_list_foreach (removed_printer_checklist, (GFunc) mark_printer_inactive, backend);
1129       g_list_free (removed_printer_checklist);
1130       list_has_changed = TRUE;
1131     }
1132   
1133 done:
1134   if (list_has_changed)
1135     g_signal_emit_by_name (backend, "printer-list-changed");
1136   
1137   gtk_print_backend_set_list_done (backend);
1138 }
1139
1140 static gboolean
1141 cups_request_printer_list (GtkPrintBackendCups *cups_backend)
1142 {
1143   GtkCupsRequest *request;
1144   static const char * const pattrs[] =  /* Attributes we're interested in */
1145     {
1146       "printer-name",
1147       "printer-uri-supported",
1148       "member-uris"
1149     };
1150  
1151   if (cups_backend->list_printers_pending ||
1152       !cups_backend->got_default_printer)
1153     return TRUE;
1154
1155   g_object_ref (cups_backend);
1156   GDK_THREADS_LEAVE ();
1157
1158   cups_backend->list_printers_pending = TRUE;
1159
1160   request = gtk_cups_request_new (NULL,
1161                                   GTK_CUPS_POST,
1162                                   CUPS_GET_PRINTERS,
1163                                   NULL,
1164                                   NULL,
1165                                   NULL);
1166
1167   gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1168                                     "requested-attributes", G_N_ELEMENTS (pattrs),
1169                                     NULL, pattrs);
1170
1171   cups_request_execute (cups_backend,
1172                         request,
1173                         (GtkPrintCupsResponseCallbackFunc) cups_request_printer_list_cb,
1174                         request,
1175                         NULL);
1176   GDK_THREADS_ENTER ();
1177   g_object_unref (cups_backend);
1178
1179   return TRUE;
1180 }
1181
1182 static void
1183 cups_get_printer_list (GtkPrintBackend *backend)
1184 {
1185   GtkPrintBackendCups *cups_backend;
1186
1187   cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
1188   if (cups_backend->list_printers_poll == 0)
1189     {
1190       cups_request_printer_list (cups_backend);
1191       cups_backend->list_printers_poll = gdk_threads_add_timeout (3000,
1192                                                         (GSourceFunc) cups_request_printer_list,
1193                                                         backend);
1194     }
1195 }
1196
1197 typedef struct {
1198   GtkPrinterCups *printer;
1199   GIOChannel *ppd_io;
1200 } GetPPDData;
1201
1202 static void
1203 get_ppd_data_free (GetPPDData *data)
1204 {
1205   GTK_NOTE (PRINTING,
1206             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1207
1208   g_io_channel_unref (data->ppd_io);
1209   g_object_unref (data->printer);
1210   g_free (data);
1211 }
1212
1213 static void
1214 cups_request_ppd_cb (GtkPrintBackendCups *print_backend,
1215                      GtkCupsResult       *result,
1216                      GetPPDData          *data)
1217 {
1218   ipp_t *response;
1219   GtkPrinter *printer;
1220
1221   GTK_NOTE (PRINTING,
1222             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1223
1224   printer = GTK_PRINTER (data->printer);
1225   GTK_PRINTER_CUPS (printer)->reading_ppd = FALSE;
1226
1227   if (gtk_cups_result_is_error (result))
1228     {
1229       g_signal_emit_by_name (printer, "details-acquired", FALSE);
1230       return;
1231     }
1232
1233   response = gtk_cups_result_get_response (result);
1234
1235   /* let ppdOpenFd take over the ownership of the open file */
1236   g_io_channel_seek_position (data->ppd_io, 0, G_SEEK_SET, NULL);
1237   data->printer->ppd_file = ppdOpenFd (dup (g_io_channel_unix_get_fd (data->ppd_io)));
1238   
1239   gtk_printer_set_has_details (printer, TRUE);
1240   g_signal_emit_by_name (printer, "details-acquired", TRUE);
1241 }
1242
1243 static void
1244 cups_request_ppd (GtkPrinter *printer)
1245 {
1246   GError *error;
1247   GtkPrintBackend *print_backend;
1248   GtkPrinterCups *cups_printer;
1249   GtkCupsRequest *request;
1250   char *ppd_filename;
1251   gchar *resource;
1252   http_t *http;
1253   GetPPDData *data;
1254   int fd;
1255
1256   cups_printer = GTK_PRINTER_CUPS (printer);
1257
1258   error = NULL;
1259
1260   GTK_NOTE (PRINTING,
1261             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1262
1263   /* FIXME this can return NULL! */
1264   http = httpConnectEncrypt (cups_printer->hostname, 
1265                              cups_printer->port,
1266                              cupsEncryption ());
1267
1268   data = g_new0 (GetPPDData, 1);
1269
1270   fd = g_file_open_tmp ("gtkprint_ppd_XXXXXX", 
1271                         &ppd_filename, 
1272                         &error);
1273
1274 #ifdef G_ENABLE_DEBUG 
1275   /* If we are debugging printing don't delete the tmp files */
1276   if (!(gtk_debug_flags & GTK_DEBUG_PRINTING))
1277     unlink (ppd_filename);
1278 #else
1279   unlink (ppd_filename);
1280 #endif /* G_ENABLE_DEBUG */
1281
1282   if (error != NULL)
1283     {
1284       g_warning ("%s", error->message);
1285       g_error_free (error);
1286       httpClose (http);
1287       g_free (ppd_filename);
1288       g_free (data);
1289
1290       g_signal_emit_by_name (printer, "details-acquired", FALSE);
1291       return;
1292     }
1293     
1294   fchmod (fd, S_IRUSR | S_IWUSR);
1295   data->ppd_io = g_io_channel_unix_new (fd);
1296   g_io_channel_set_encoding (data->ppd_io, NULL, NULL);
1297   g_io_channel_set_close_on_unref (data->ppd_io, TRUE);
1298
1299   data->printer = g_object_ref (printer);
1300
1301   resource = g_strdup_printf ("/printers/%s.ppd", 
1302                               gtk_printer_cups_get_ppd_name (GTK_PRINTER_CUPS(printer)));
1303   request = gtk_cups_request_new (http,
1304                                   GTK_CUPS_GET,
1305                                   0,
1306                                   data->ppd_io,
1307                                   cups_printer->hostname,
1308                                   resource);
1309
1310   GTK_NOTE (PRINTING,
1311             g_print ("CUPS Backend: Requesting resource %s to be written to temp file %s\n", resource, ppd_filename));
1312
1313   g_free (resource);
1314   g_free (ppd_filename);
1315
1316   cups_printer->reading_ppd = TRUE;
1317
1318   print_backend = gtk_printer_get_backend (printer);
1319
1320   cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
1321                         request,
1322                         (GtkPrintCupsResponseCallbackFunc) cups_request_ppd_cb,
1323                         data,
1324                         (GDestroyNotify)get_ppd_data_free);
1325 }
1326
1327
1328 static void
1329 cups_request_default_printer_cb (GtkPrintBackendCups *print_backend,
1330                                  GtkCupsResult       *result,
1331                                  gpointer             user_data)
1332 {
1333   ipp_t *response;
1334   ipp_attribute_t *attr;
1335
1336   response = gtk_cups_result_get_response (result);
1337   
1338   if ((attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME)) != NULL)
1339     print_backend->default_printer = g_strdup (attr->values[0].string.text);
1340
1341   print_backend->got_default_printer = TRUE;
1342
1343   /* Make sure to kick off get_printers if we are polling it, 
1344    * as we could have blocked this reading the default printer 
1345    */
1346   if (print_backend->list_printers_poll != 0)
1347     cups_request_printer_list (print_backend);
1348 }
1349
1350 static void
1351 cups_request_default_printer (GtkPrintBackendCups *print_backend)
1352 {
1353   GtkCupsRequest *request;
1354   const char *str;
1355
1356   if ((str = g_getenv ("LPDEST")) != NULL)
1357     {
1358       print_backend->default_printer = g_strdup (str);
1359       print_backend->got_default_printer = TRUE;
1360       return;
1361     }
1362   else if ((str = g_getenv ("PRINTER")) != NULL &&
1363            strcmp (str, "lp") != 0)
1364     {
1365       print_backend->default_printer = g_strdup (str);
1366       print_backend->got_default_printer = TRUE;
1367       return;
1368     }
1369   
1370   request = gtk_cups_request_new (NULL,
1371                                   GTK_CUPS_POST,
1372                                   CUPS_GET_DEFAULT,
1373                                   NULL,
1374                                   NULL,
1375                                   NULL);
1376   
1377   cups_request_execute (print_backend,
1378                         request,
1379                         (GtkPrintCupsResponseCallbackFunc) cups_request_default_printer_cb,
1380                         g_object_ref (print_backend),
1381                         g_object_unref);
1382 }
1383
1384 static void
1385 cups_printer_request_details (GtkPrinter *printer)
1386 {
1387   GtkPrinterCups *cups_printer;
1388
1389   cups_printer = GTK_PRINTER_CUPS (printer);
1390   if (!cups_printer->reading_ppd && 
1391       gtk_printer_cups_get_ppd (cups_printer) == NULL)
1392     cups_request_ppd (printer); 
1393 }
1394
1395 static char *
1396 ppd_text_to_utf8 (ppd_file_t *ppd_file, 
1397                   const char *text)
1398 {
1399   const char *encoding = NULL;
1400   char *res;
1401   
1402   if (g_ascii_strcasecmp (ppd_file->lang_encoding, "UTF-8") == 0)
1403     {
1404       return g_strdup (text);
1405     }
1406   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin1") == 0)
1407     {
1408       encoding = "ISO-8859-1";
1409     }
1410   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin2") == 0)
1411     {
1412       encoding = "ISO-8859-2";
1413     }
1414   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin5") == 0)
1415     {
1416       encoding = "ISO-8859-5";
1417     }
1418   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "JIS83-RKSJ") == 0)
1419     {
1420       encoding = "SHIFT-JIS";
1421     }
1422   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "MacStandard") == 0)
1423     {
1424       encoding = "MACINTOSH";
1425     }
1426   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "WindowsANSI") == 0)
1427     {
1428       encoding = "WINDOWS-1252";
1429     }
1430   else 
1431     {
1432       /* Fallback, try iso-8859-1... */
1433       encoding = "ISO-8859-1";
1434     }
1435
1436   res = g_convert (text, -1, "UTF-8", encoding, NULL, NULL, NULL);
1437
1438   if (res == NULL)
1439     {
1440       g_warning ("unable to convert PPD text");
1441       res = g_strdup ("???");
1442     }
1443   
1444   return res;
1445 }
1446
1447 /* TODO: Add more translations for common settings here */
1448
1449 static const struct {
1450   const char *keyword;
1451   const char *translation;
1452 } cups_option_translations[] = {
1453   { "Duplex", N_("Two Sided") },
1454   { "MediaType", N_("Paper Type") },
1455   { "InputSlot", N_("Paper Source") },
1456   { "OutputBin", N_("Output Tray") },
1457 };
1458
1459
1460 static const struct {
1461   const char *keyword;
1462   const char *choice;
1463   const char *translation;
1464 } cups_choice_translations[] = {
1465   { "Duplex", "None", N_("One Sided") },
1466   { "InputSlot", "Auto", N_("Auto Select") },
1467   { "InputSlot", "AutoSelect", N_("Auto Select") },
1468   { "InputSlot", "Default", N_("Printer Default") },
1469   { "InputSlot", "None", N_("Printer Default") },
1470   { "InputSlot", "PrinterDefault", N_("Printer Default") },
1471   { "InputSlot", "Unspecified", N_("Auto Select") },
1472 };
1473
1474 static const struct {
1475   const char *ppd_keyword;
1476   const char *name;
1477 } option_names[] = {
1478   {"Duplex", "gtk-duplex" },
1479   {"MediaType", "gtk-paper-type"},
1480   {"InputSlot", "gtk-paper-source"},
1481   {"OutputBin", "gtk-output-tray"},
1482 };
1483
1484 /* keep sorted when changing */
1485 static const char *color_option_whitelist[] = {
1486   "BRColorEnhancement",
1487   "BRColorMatching",
1488   "BRColorMatching",
1489   "BRColorMode",
1490   "BRGammaValue",
1491   "BRImprovedGray",
1492   "BlackSubstitution",
1493   "ColorModel",
1494   "HPCMYKInks",
1495   "HPCSGraphics",
1496   "HPCSImages",
1497   "HPCSText",
1498   "HPColorSmart",
1499   "RPSBlackMode",
1500   "RPSBlackOverPrint",
1501   "Rcmyksimulation",
1502 };
1503
1504 /* keep sorted when changing */
1505 static const char *color_group_whitelist[] = {
1506   "ColorPage",
1507   "FPColorWise1",
1508   "FPColorWise2",
1509   "FPColorWise3",
1510   "FPColorWise4",
1511   "FPColorWise5",
1512   "HPColorOptionsPanel",
1513 };
1514   
1515 /* keep sorted when changing */
1516 static const char *image_quality_option_whitelist[] = {
1517   "BRDocument",
1518   "BRHalfTonePattern",
1519   "BRNormalPrt",
1520   "BRPrintQuality",
1521   "BitsPerPixel",
1522   "Darkness",
1523   "Dithering",
1524   "EconoMode",
1525   "Economode",
1526   "HPEconoMode",
1527   "HPEdgeControl",
1528   "HPGraphicsHalftone",
1529   "HPHalftone",
1530   "HPLJDensity",
1531   "HPPhotoHalftone",
1532   "OutputMode",
1533   "REt",
1534   "RPSBitsPerPixel",
1535   "RPSDitherType",
1536   "Resolution",
1537   "ScreenLock",
1538   "Smoothing",
1539   "TonerSaveMode",
1540   "UCRGCRForImage",
1541 };
1542
1543 /* keep sorted when changing */
1544 static const char *image_quality_group_whitelist[] = {
1545   "FPImageQuality1",
1546   "FPImageQuality2",
1547   "FPImageQuality3",
1548   "ImageQualityPage",
1549 };
1550
1551 /* keep sorted when changing */
1552 static const char * finishing_option_whitelist[] = {
1553   "BindColor",
1554   "BindEdge",
1555   "BindType",
1556   "BindWhen",
1557   "Booklet",
1558   "FoldType",
1559   "FoldWhen",
1560   "HPStaplerOptions",
1561   "Jog",
1562   "Slipsheet",
1563   "Sorter",
1564   "StapleLocation",
1565   "StapleOrientation",
1566   "StapleWhen",
1567   "StapleX",
1568   "StapleY",
1569 };
1570
1571 /* keep sorted when changing */
1572 static const char *finishing_group_whitelist[] = {
1573   "FPFinishing1",
1574   "FPFinishing2",
1575   "FPFinishing3",
1576   "FPFinishing4",
1577   "FinishingPage",
1578   "HPFinishingPanel",
1579 };
1580
1581 /* keep sorted when changing */
1582 static const char *cups_option_blacklist[] = {
1583   "Collate",
1584   "Copies", 
1585   "OutputOrder",
1586   "PageRegion",
1587   "PageSize",
1588 };
1589
1590 static char *
1591 get_option_text (ppd_file_t   *ppd_file, 
1592                  ppd_option_t *option)
1593 {
1594   int i;
1595   char *utf8;
1596   
1597   for (i = 0; i < G_N_ELEMENTS (cups_option_translations); i++)
1598     {
1599       if (strcmp (cups_option_translations[i].keyword, option->keyword) == 0)
1600         return g_strdup (_(cups_option_translations[i].translation));
1601     }
1602
1603   utf8 = ppd_text_to_utf8 (ppd_file, option->text);
1604
1605   /* Some ppd files have spaces in the text before the colon */
1606   g_strchomp (utf8);
1607   
1608   return utf8;
1609 }
1610
1611 static char *
1612 get_choice_text (ppd_file_t   *ppd_file, 
1613                  ppd_choice_t *choice)
1614 {
1615   int i;
1616   ppd_option_t *option = choice->option;
1617   const char *keyword = option->keyword;
1618   
1619   for (i = 0; i < G_N_ELEMENTS (cups_choice_translations); i++)
1620     {
1621       if (strcmp (cups_choice_translations[i].keyword, keyword) == 0 &&
1622           strcmp (cups_choice_translations[i].choice, choice->choice) == 0)
1623         return g_strdup (_(cups_choice_translations[i].translation));
1624     }
1625   return ppd_text_to_utf8 (ppd_file, choice->text);
1626 }
1627
1628 static gboolean
1629 group_has_option (ppd_group_t  *group, 
1630                   ppd_option_t *option)
1631 {
1632   int i;
1633
1634   if (group == NULL)
1635     return FALSE;
1636   
1637   if (group->num_options > 0 &&
1638       option >= group->options && option < group->options + group->num_options)
1639     return TRUE;
1640   
1641   for (i = 0; i < group->num_subgroups; i++)
1642     {
1643       if (group_has_option (&group->subgroups[i],option))
1644         return TRUE;
1645     }
1646   return FALSE;
1647 }
1648
1649 static void
1650 set_option_off (GtkPrinterOption *option)
1651 {
1652   /* Any of these will do, _set only applies the value
1653    * if its allowed of the option */
1654   gtk_printer_option_set (option, "False");
1655   gtk_printer_option_set (option, "Off");
1656   gtk_printer_option_set (option, "None");
1657 }
1658
1659 static gboolean
1660 value_is_off (const char *value)
1661 {
1662   return  (strcasecmp (value, "None") == 0 ||
1663            strcasecmp (value, "Off") == 0 ||
1664            strcasecmp (value, "False") == 0);
1665 }
1666
1667 static char *
1668 ppd_group_name (ppd_group_t *group)
1669 {
1670 #if CUPS_VERSION_MAJOR > 1 || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR > 1) || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR == 1 && CUPS_VERSION_PATCH >= 18) 
1671   return group->name;
1672 #else
1673   return group->text;
1674 #endif
1675 }
1676
1677 static int
1678 available_choices (ppd_file_t     *ppd,
1679                    ppd_option_t   *option,
1680                    ppd_choice_t ***available,
1681                    gboolean        keep_if_only_one_option)
1682 {
1683   ppd_option_t *other_option;
1684   int i, j;
1685   gchar *conflicts;
1686   ppd_const_t *constraint;
1687   const char *choice, *other_choice;
1688   ppd_option_t *option1, *option2;
1689   ppd_group_t *installed_options;
1690   int num_conflicts;
1691   gboolean all_default;
1692   int add_auto;
1693
1694   if (available)
1695     *available = NULL;
1696
1697   conflicts = g_new0 (char, option->num_choices);
1698
1699   installed_options = NULL;
1700   for (i = 0; i < ppd->num_groups; i++)
1701     {
1702       char *name; 
1703
1704       name = ppd_group_name (&ppd->groups[i]);
1705       if (strcmp (name, "InstallableOptions") == 0)
1706         {
1707           installed_options = &ppd->groups[i];
1708           break;
1709         }
1710     }
1711
1712   for (i = ppd->num_consts, constraint = ppd->consts; i > 0; i--, constraint++)
1713     {
1714       option1 = ppdFindOption (ppd, constraint->option1);
1715       if (option1 == NULL)
1716         continue;
1717
1718       option2 = ppdFindOption (ppd, constraint->option2);
1719       if (option2 == NULL)
1720         continue;
1721
1722       if (option == option1)
1723         {
1724           choice = constraint->choice1;
1725           other_option = option2;
1726           other_choice = constraint->choice2;
1727         }
1728       else if (option == option2)
1729         {
1730           choice = constraint->choice2;
1731           other_option = option1;
1732           other_choice = constraint->choice1;
1733         }
1734       else
1735         continue;
1736
1737       /* We only care of conflicts with installed_options and
1738          PageSize */
1739       if (!group_has_option (installed_options, other_option) &&
1740           (strcmp (other_option->keyword, "PageSize") != 0))
1741         continue;
1742
1743       if (*other_choice == 0)
1744         {
1745           /* Conflict only if the installed option is not off */
1746           if (value_is_off (other_option->defchoice))
1747             continue;
1748         }
1749       /* Conflict if the installed option has the specified default */
1750       else if (strcasecmp (other_choice, other_option->defchoice) != 0)
1751         continue;
1752
1753       if (*choice == 0)
1754         {
1755           /* Conflict with all non-off choices */
1756           for (j = 0; j < option->num_choices; j++)
1757             {
1758               if (!value_is_off (option->choices[j].choice))
1759                 conflicts[j] = 1;
1760             }
1761         }
1762       else
1763         {
1764           for (j = 0; j < option->num_choices; j++)
1765             {
1766               if (strcasecmp (option->choices[j].choice, choice) == 0)
1767                 conflicts[j] = 1;
1768             }
1769         }
1770     }
1771
1772   num_conflicts = 0;
1773   all_default = TRUE;
1774   for (j = 0; j < option->num_choices; j++)
1775     {
1776       if (conflicts[j])
1777         num_conflicts++;
1778       else if (strcmp (option->choices[j].choice, option->defchoice) != 0)
1779         all_default = FALSE;
1780     }
1781
1782   if (all_default && !keep_if_only_one_option)
1783     return 0;
1784   
1785   if (num_conflicts == option->num_choices)
1786     return 0;
1787
1788
1789   /* Some ppds don't have a "use printer default" option for
1790    * InputSlot. This means you always have to select a particular slot,
1791    * and you can't auto-pick source based on the paper size. To support
1792    * this we always add an auto option if there isn't one already. If
1793    * the user chooses the generated option we don't send any InputSlot
1794    * value when printing. The way we detect existing auto-cases is based
1795    * on feedback from Michael Sweet of cups fame.
1796    */
1797   add_auto = 0;
1798   if (strcmp (option->keyword, "InputSlot") == 0)
1799     {
1800       gboolean found_auto = FALSE;
1801       for (j = 0; j < option->num_choices; j++)
1802         {
1803           if (!conflicts[j])
1804             {
1805               if (strcmp (option->choices[j].choice, "Auto") == 0 ||
1806                   strcmp (option->choices[j].choice, "AutoSelect") == 0 ||
1807                   strcmp (option->choices[j].choice, "Default") == 0 ||
1808                   strcmp (option->choices[j].choice, "None") == 0 ||
1809                   strcmp (option->choices[j].choice, "PrinterDefault") == 0 ||
1810                   strcmp (option->choices[j].choice, "Unspecified") == 0 ||
1811                   option->choices[j].code == NULL ||
1812                   option->choices[j].code[0] == 0)
1813                 {
1814                   found_auto = TRUE;
1815                   break;
1816                 }
1817             }
1818         }
1819
1820       if (!found_auto)
1821         add_auto = 1;
1822     }
1823   
1824   if (available)
1825     {
1826       
1827       *available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto);
1828
1829       i = 0;
1830       for (j = 0; j < option->num_choices; j++)
1831         {
1832           if (!conflicts[j])
1833             (*available)[i++] = &option->choices[j];
1834         }
1835
1836       if (add_auto) 
1837         (*available)[i++] = NULL;
1838     }
1839   
1840   return option->num_choices - num_conflicts + add_auto;
1841 }
1842
1843 static GtkPrinterOption *
1844 create_pickone_option (ppd_file_t   *ppd_file,
1845                        ppd_option_t *ppd_option,
1846                        const gchar  *gtk_name)
1847 {
1848   GtkPrinterOption *option;
1849   ppd_choice_t **available;
1850   char *label;
1851   int n_choices;
1852   int i;
1853 #ifdef HAVE_CUPS_API_1_2
1854   ppd_coption_t *coption;
1855 #endif
1856
1857   g_assert (ppd_option->ui == PPD_UI_PICKONE);
1858   
1859   option = NULL;
1860
1861   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
1862   if (n_choices > 0)
1863     {
1864       
1865       /* right now only support one parameter per custom option 
1866        * if more than one print warning and only offer the default choices
1867        */
1868
1869       label = get_option_text (ppd_file, ppd_option);
1870
1871 #ifdef HAVE_CUPS_API_1_2
1872       coption = ppdFindCustomOption (ppd_file, ppd_option->keyword);
1873
1874       if (coption)
1875         {
1876           ppd_cparam_t *cparam;
1877
1878           cparam = ppdFirstCustomParam (coption);
1879
1880           if (ppdNextCustomParam (coption) == NULL)
1881             {
1882               switch (cparam->type)
1883                 {
1884                 case PPD_CUSTOM_INT:
1885                   option = gtk_printer_option_new (gtk_name, label,
1886                                          GTK_PRINTER_OPTION_TYPE_PICKONE_INT);
1887                   break;
1888                 case PPD_CUSTOM_PASSCODE:
1889                   option = gtk_printer_option_new (gtk_name, label,
1890                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE);
1891                   break;
1892                 case PPD_CUSTOM_PASSWORD:
1893                     option = gtk_printer_option_new (gtk_name, label,
1894                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD);
1895                   break;
1896                case PPD_CUSTOM_REAL:
1897                     option = gtk_printer_option_new (gtk_name, label,
1898                                          GTK_PRINTER_OPTION_TYPE_PICKONE_REAL);
1899                   break;
1900                 case PPD_CUSTOM_STRING:
1901                   option = gtk_printer_option_new (gtk_name, label,
1902                                          GTK_PRINTER_OPTION_TYPE_PICKONE_STRING);
1903                   break;
1904                 case PPD_CUSTOM_POINTS: 
1905                   g_warning ("Not Supported: PPD Custom Points Option");
1906                   break;
1907                 case PPD_CUSTOM_CURVE:
1908                   g_warning ("Not Supported: PPD Custom Curve Option");
1909                   break;
1910                 case PPD_CUSTOM_INVCURVE:       
1911                   g_warning ("Not Supported: PPD Custom Inverse Curve Option");
1912                   break;
1913                 }
1914             }
1915           else
1916             g_warning ("Not Supported: PPD Custom Option has more than one parameter");
1917         }
1918 #endif /* HAVE_CUPS_API_1_2 */
1919
1920       if (!option)
1921         option = gtk_printer_option_new (gtk_name, label,
1922                                          GTK_PRINTER_OPTION_TYPE_PICKONE);
1923       g_free (label);
1924       
1925       gtk_printer_option_allocate_choices (option, n_choices);
1926       for (i = 0; i < n_choices; i++)
1927         {
1928           if (available[i] == NULL)
1929             {
1930               /* This was auto-added */
1931               option->choices[i] = g_strdup ("gtk-ignore-value");
1932               option->choices_display[i] = g_strdup (_("Printer Default"));
1933             }
1934           else
1935             {
1936               option->choices[i] = g_strdup (available[i]->choice);
1937               option->choices_display[i] = get_choice_text (ppd_file, available[i]);
1938             }
1939         }
1940       gtk_printer_option_set (option, ppd_option->defchoice);
1941     }
1942 #ifdef PRINT_IGNORED_OPTIONS
1943   else
1944     g_warning ("Ignoring pickone %s\n", ppd_option->text);
1945 #endif
1946   g_free (available);
1947
1948   return option;
1949 }
1950
1951 static GtkPrinterOption *
1952 create_boolean_option (ppd_file_t   *ppd_file,
1953                        ppd_option_t *ppd_option,
1954                        const gchar  *gtk_name)
1955 {
1956   GtkPrinterOption *option;
1957   ppd_choice_t **available;
1958   char *label;
1959   int n_choices;
1960
1961   g_assert (ppd_option->ui == PPD_UI_BOOLEAN);
1962   
1963   option = NULL;
1964
1965   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
1966   if (n_choices == 2)
1967     {
1968       label = get_option_text (ppd_file, ppd_option);
1969       option = gtk_printer_option_new (gtk_name, label,
1970                                        GTK_PRINTER_OPTION_TYPE_BOOLEAN);
1971       g_free (label);
1972       
1973       gtk_printer_option_allocate_choices (option, 2);
1974       option->choices[0] = g_strdup ("True");
1975       option->choices_display[0] = g_strdup ("True");
1976       option->choices[1] = g_strdup ("False");
1977       option->choices_display[1] = g_strdup ("False");
1978       
1979       gtk_printer_option_set (option, ppd_option->defchoice);
1980     }
1981 #ifdef PRINT_IGNORED_OPTIONS
1982   else
1983     g_warning ("Ignoring boolean %s\n", ppd_option->text);
1984 #endif
1985   g_free (available);
1986
1987   return option;
1988 }
1989
1990 static gchar *
1991 get_option_name (const gchar *keyword)
1992 {
1993   int i;
1994
1995   for (i = 0; i < G_N_ELEMENTS (option_names); i++)
1996     if (strcmp (option_names[i].ppd_keyword, keyword) == 0)
1997       return g_strdup (option_names[i].name);
1998
1999   return g_strdup_printf ("cups-%s", keyword);
2000 }
2001
2002 static int
2003 strptr_cmp (const void *a, 
2004             const void *b)
2005 {
2006   char **aa = (char **)a;
2007   char **bb = (char **)b;
2008   return strcmp (*aa, *bb);
2009 }
2010
2011
2012 static gboolean
2013 string_in_table (gchar       *str, 
2014                  const gchar *table[], 
2015                  gint         table_len)
2016 {
2017   return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
2018 }
2019
2020 #define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
2021
2022 static void
2023 handle_option (GtkPrinterOptionSet *set,
2024                ppd_file_t          *ppd_file,
2025                ppd_option_t        *ppd_option,
2026                ppd_group_t         *toplevel_group,
2027                GtkPrintSettings    *settings)
2028 {
2029   GtkPrinterOption *option;
2030   char *name;
2031
2032   if (STRING_IN_TABLE (ppd_option->keyword, cups_option_blacklist))
2033     return;
2034
2035   name = get_option_name (ppd_option->keyword);
2036
2037   option = NULL;
2038   if (ppd_option->ui == PPD_UI_PICKONE)
2039     {
2040       option = create_pickone_option (ppd_file, ppd_option, name);
2041     }
2042   else if (ppd_option->ui == PPD_UI_BOOLEAN)
2043     {
2044       option = create_boolean_option (ppd_file, ppd_option, name);
2045     }
2046   else
2047     g_warning ("Ignored pickmany setting %s\n", ppd_option->text);
2048   
2049   
2050   if (option)
2051     {
2052       char *name;
2053
2054       name = ppd_group_name (toplevel_group);
2055       if (STRING_IN_TABLE (name,
2056                            color_group_whitelist) ||
2057           STRING_IN_TABLE (ppd_option->keyword,
2058                            color_option_whitelist))
2059         {
2060           option->group = g_strdup ("ColorPage");
2061         }
2062       else if (STRING_IN_TABLE (name,
2063                                 image_quality_group_whitelist) ||
2064                STRING_IN_TABLE (ppd_option->keyword,
2065                                 image_quality_option_whitelist))
2066         {
2067           option->group = g_strdup ("ImageQualityPage");
2068         }
2069       else if (STRING_IN_TABLE (name,
2070                                 finishing_group_whitelist) ||
2071                STRING_IN_TABLE (ppd_option->keyword,
2072                                 finishing_option_whitelist))
2073         {
2074           option->group = g_strdup ("FinishingPage");
2075         }
2076       else
2077         {
2078           option->group = g_strdup (toplevel_group->text);
2079         }
2080
2081       set_option_from_settings (option, settings);
2082       
2083       gtk_printer_option_set_add (set, option);
2084     }
2085   
2086   g_free (name);
2087 }
2088
2089 static void
2090 handle_group (GtkPrinterOptionSet *set,
2091               ppd_file_t          *ppd_file,
2092               ppd_group_t         *group,
2093               ppd_group_t         *toplevel_group,
2094               GtkPrintSettings    *settings)
2095 {
2096   gint i;
2097   gchar *name;
2098   
2099   /* Ignore installable options */
2100   name = ppd_group_name (toplevel_group);
2101   if (strcmp (name, "InstallableOptions") == 0)
2102     return;
2103   
2104   for (i = 0; i < group->num_options; i++)
2105     handle_option (set, ppd_file, &group->options[i], toplevel_group, settings);
2106
2107   for (i = 0; i < group->num_subgroups; i++)
2108     handle_group (set, ppd_file, &group->subgroups[i], toplevel_group, settings);
2109
2110 }
2111
2112 static GtkPrinterOptionSet *
2113 cups_printer_get_options (GtkPrinter           *printer,
2114                           GtkPrintSettings     *settings,
2115                           GtkPageSetup         *page_setup,
2116                           GtkPrintCapabilities  capabilities)
2117 {
2118   GtkPrinterOptionSet *set;
2119   GtkPrinterOption *option;
2120   ppd_file_t *ppd_file;
2121   int i;
2122   char *print_at[] = { "now", "at", "on-hold" };
2123   char *n_up[] = {"1", "2", "4", "6", "9", "16" };
2124   char *prio[] = {"100", "80", "50", "30" };
2125   char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low") };
2126   char *cover[] = {"none", "classified", "confidential", "secret", "standard", "topsecret", "unclassified" };
2127   char *cover_display[] = {N_("None"), N_("Classified"), N_("Confidential"), N_("Secret"), N_("Standard"), N_("Top Secret"), N_("Unclassified"),};
2128
2129
2130   set = gtk_printer_option_set_new ();
2131
2132   /* Cups specific, non-ppd related settings */
2133
2134   option = gtk_printer_option_new ("gtk-n-up", "Pages Per Sheet", GTK_PRINTER_OPTION_TYPE_PICKONE);
2135   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up),
2136                                          n_up, n_up);
2137   gtk_printer_option_set (option, "1");
2138   set_option_from_settings (option, settings);
2139   gtk_printer_option_set_add (set, option);
2140   g_object_unref (option);
2141
2142   for (i = 0; i < G_N_ELEMENTS(prio_display); i++)
2143     prio_display[i] = _(prio_display[i]);
2144   
2145   option = gtk_printer_option_new ("gtk-job-prio", "Job Priority", GTK_PRINTER_OPTION_TYPE_PICKONE);
2146   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (prio),
2147                                          prio, prio_display);
2148   gtk_printer_option_set (option, "50");
2149   set_option_from_settings (option, settings);
2150   gtk_printer_option_set_add (set, option);
2151   g_object_unref (option);
2152
2153   option = gtk_printer_option_new ("gtk-billing-info", "Billing Info", GTK_PRINTER_OPTION_TYPE_STRING);
2154   gtk_printer_option_set (option, "");
2155   set_option_from_settings (option, settings);
2156   gtk_printer_option_set_add (set, option);
2157   g_object_unref (option);
2158
2159   for (i = 0; i < G_N_ELEMENTS(cover_display); i++)
2160     cover_display[i] = _(cover_display[i]);
2161   
2162   option = gtk_printer_option_new ("gtk-cover-before", "Before", GTK_PRINTER_OPTION_TYPE_PICKONE);
2163   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (cover),
2164                                          cover, cover_display);
2165   gtk_printer_option_set (option, "none");
2166   set_option_from_settings (option, settings);
2167   gtk_printer_option_set_add (set, option);
2168   g_object_unref (option);
2169
2170   option = gtk_printer_option_new ("gtk-cover-after", "After", GTK_PRINTER_OPTION_TYPE_PICKONE);
2171   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (cover),
2172                                          cover, cover_display);
2173   gtk_printer_option_set (option, "none");
2174   set_option_from_settings (option, settings);
2175   gtk_printer_option_set_add (set, option);
2176   g_object_unref (option);
2177
2178   option = gtk_printer_option_new ("gtk-print-time", "Print at", GTK_PRINTER_OPTION_TYPE_PICKONE);
2179   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (print_at),
2180                                          print_at, print_at);
2181   gtk_printer_option_set (option, "now");
2182   set_option_from_settings (option, settings);
2183   gtk_printer_option_set_add (set, option);
2184   g_object_unref (option);
2185   
2186   option = gtk_printer_option_new ("gtk-print-time-text", "Print at time", GTK_PRINTER_OPTION_TYPE_STRING);
2187   gtk_printer_option_set (option, "");
2188   set_option_from_settings (option, settings);
2189   gtk_printer_option_set_add (set, option);
2190   g_object_unref (option);
2191   
2192   /* Printer (ppd) specific settings */
2193   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
2194   if (ppd_file)
2195     {
2196       GtkPaperSize *paper_size;
2197       ppd_option_t *option;
2198       const gchar  *ppd_name;
2199
2200       ppdMarkDefaults (ppd_file);
2201
2202       paper_size = gtk_page_setup_get_paper_size (page_setup);
2203
2204       option = ppdFindOption (ppd_file, "PageSize");
2205       ppd_name = gtk_paper_size_get_ppd_name (paper_size);
2206       
2207       if (ppd_name)
2208         strncpy (option->defchoice, ppd_name, PPD_MAX_NAME);
2209       else
2210         {
2211           gchar *custom_name;
2212
2213           custom_name = g_strdup_printf (_("Custom.%2fx%.2f"),
2214                                          gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS),
2215                                          gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
2216           strncpy (option->defchoice, custom_name, PPD_MAX_NAME);
2217           g_free (custom_name);
2218         }
2219
2220       for (i = 0; i < ppd_file->num_groups; i++)
2221         handle_group (set, ppd_file, &ppd_file->groups[i], &ppd_file->groups[i], settings);
2222     }
2223
2224   return set;
2225 }
2226
2227
2228 static void
2229 mark_option_from_set (GtkPrinterOptionSet *set,
2230                       ppd_file_t          *ppd_file,
2231                       ppd_option_t        *ppd_option)
2232 {
2233   GtkPrinterOption *option;
2234   char *name = get_option_name (ppd_option->keyword);
2235
2236   option = gtk_printer_option_set_lookup (set, name);
2237
2238   if (option)
2239     ppdMarkOption (ppd_file, ppd_option->keyword, option->value);
2240   
2241   g_free (name);
2242 }
2243
2244
2245 static void
2246 mark_group_from_set (GtkPrinterOptionSet *set,
2247                      ppd_file_t          *ppd_file,
2248                      ppd_group_t         *group)
2249 {
2250   int i;
2251
2252   for (i = 0; i < group->num_options; i++)
2253     mark_option_from_set (set, ppd_file, &group->options[i]);
2254
2255   for (i = 0; i < group->num_subgroups; i++)
2256     mark_group_from_set (set, ppd_file, &group->subgroups[i]);
2257 }
2258
2259 static void
2260 set_conflicts_from_option (GtkPrinterOptionSet *set,
2261                            ppd_file_t          *ppd_file,
2262                            ppd_option_t        *ppd_option)
2263 {
2264   GtkPrinterOption *option;
2265   char *name;
2266
2267   if (ppd_option->conflicted)
2268     {
2269       name = get_option_name (ppd_option->keyword);
2270       option = gtk_printer_option_set_lookup (set, name);
2271
2272       if (option)
2273         gtk_printer_option_set_has_conflict (option, TRUE);
2274       else
2275         g_warning ("conflict for option %s ignored", ppd_option->keyword);
2276       
2277       g_free (name);
2278     }
2279 }
2280
2281 static void
2282 set_conflicts_from_group (GtkPrinterOptionSet *set,
2283                           ppd_file_t          *ppd_file,
2284                           ppd_group_t         *group)
2285 {
2286   int i;
2287
2288   for (i = 0; i < group->num_options; i++)
2289     set_conflicts_from_option (set, ppd_file, &group->options[i]);
2290
2291   for (i = 0; i < group->num_subgroups; i++)
2292     set_conflicts_from_group (set, ppd_file, &group->subgroups[i]);
2293 }
2294
2295 static gboolean
2296 cups_printer_mark_conflicts (GtkPrinter          *printer,
2297                              GtkPrinterOptionSet *options)
2298 {
2299   ppd_file_t *ppd_file;
2300   int num_conflicts;
2301   int i;
2302  
2303   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
2304
2305   if (ppd_file == NULL)
2306     return FALSE;
2307
2308   ppdMarkDefaults (ppd_file);
2309
2310   for (i = 0; i < ppd_file->num_groups; i++)
2311     mark_group_from_set (options, ppd_file, &ppd_file->groups[i]);
2312
2313   num_conflicts = ppdConflicts (ppd_file);
2314
2315   if (num_conflicts > 0)
2316     {
2317       for (i = 0; i < ppd_file->num_groups; i++)
2318         set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]);
2319     }
2320  
2321   return num_conflicts > 0;
2322 }
2323
2324 struct OptionData {
2325   GtkPrinter *printer;
2326   GtkPrinterOptionSet *options;
2327   GtkPrintSettings *settings;
2328   ppd_file_t *ppd_file;
2329 };
2330
2331 typedef struct {
2332   const char *cups;
2333   const char *standard;
2334 } NameMapping;
2335
2336 static void
2337 map_settings_to_option (GtkPrinterOption  *option,
2338                         const NameMapping  table[],
2339                         gint               n_elements,
2340                         GtkPrintSettings  *settings,
2341                         const gchar       *standard_name,
2342                         const gchar       *cups_name)
2343 {
2344   int i;
2345   char *name;
2346   const char *cups_value;
2347   const char *standard_value;
2348
2349   /* If the cups-specific setting is set, always use that */
2350   name = g_strdup_printf ("cups-%s", cups_name);
2351   cups_value = gtk_print_settings_get (settings, name);
2352   g_free (name);
2353   
2354   if (cups_value != NULL) 
2355     {
2356       gtk_printer_option_set (option, cups_value);
2357       return;
2358     }
2359
2360   /* Otherwise we try to convert from the general setting */
2361   standard_value = gtk_print_settings_get (settings, standard_name);
2362   if (standard_value == NULL)
2363     return;
2364
2365   for (i = 0; i < n_elements; i++)
2366     {
2367       if (table[i].cups == NULL && table[i].standard == NULL)
2368         {
2369           gtk_printer_option_set (option, standard_value);
2370           break;
2371         }
2372       else if (table[i].cups == NULL &&
2373                strcmp (table[i].standard, standard_value) == 0)
2374         {
2375           set_option_off (option);
2376           break;
2377         }
2378       else if (strcmp (table[i].standard, standard_value) == 0)
2379         {
2380           gtk_printer_option_set (option, table[i].cups);
2381           break;
2382         }
2383     }
2384 }
2385
2386 static void
2387 map_option_to_settings (const gchar       *value,
2388                         const NameMapping  table[],
2389                         gint               n_elements,
2390                         GtkPrintSettings  *settings,
2391                         const gchar       *standard_name,
2392                         const gchar       *cups_name)
2393 {
2394   int i;
2395   char *name;
2396
2397   for (i = 0; i < n_elements; i++)
2398     {
2399       if (table[i].cups == NULL && table[i].standard == NULL)
2400         {
2401           gtk_print_settings_set (settings,
2402                                   standard_name,
2403                                   value);
2404           break;
2405         }
2406       else if (table[i].cups == NULL && table[i].standard != NULL)
2407         {
2408           if (value_is_off (value))
2409             {
2410               gtk_print_settings_set (settings,
2411                                       standard_name,
2412                                       table[i].standard);
2413               break;
2414             }
2415         }
2416       else if (strcmp (table[i].cups, value) == 0)
2417         {
2418           gtk_print_settings_set (settings,
2419                                   standard_name,
2420                                   table[i].standard);
2421           break;
2422         }
2423     }
2424
2425   /* Always set the corresponding cups-specific setting */
2426   name = g_strdup_printf ("cups-%s", cups_name);
2427   gtk_print_settings_set (settings, name, value);
2428   g_free (name);
2429 }
2430
2431
2432 static const NameMapping paper_source_map[] = {
2433   { "Lower", "lower"},
2434   { "Middle", "middle"},
2435   { "Upper", "upper"},
2436   { "Rear", "rear"},
2437   { "Envelope", "envelope"},
2438   { "Cassette", "cassette"},
2439   { "LargeCapacity", "large-capacity"},
2440   { "AnySmallFormat", "small-format"},
2441   { "AnyLargeFormat", "large-format"},
2442   { NULL, NULL}
2443 };
2444
2445 static const NameMapping output_tray_map[] = {
2446   { "Upper", "upper"},
2447   { "Lower", "lower"},
2448   { "Rear", "rear"},
2449   { NULL, NULL}
2450 };
2451
2452 static const NameMapping duplex_map[] = {
2453   { "DuplexTumble", "vertical" },
2454   { "DuplexNoTumble", "horizontal" },
2455   { NULL, "simplex" }
2456 };
2457
2458 static const NameMapping output_mode_map[] = {
2459   { "Standard", "normal" },
2460   { "Normal", "normal" },
2461   { "Draft", "draft" },
2462   { "Fast", "draft" },
2463 };
2464
2465 static const NameMapping media_type_map[] = {
2466   { "Transparency", "transparency"},
2467   { "Standard", "stationery"},
2468   { NULL, NULL}
2469 };
2470
2471 static const NameMapping all_map[] = {
2472   { NULL, NULL}
2473 };
2474
2475
2476 static void
2477 set_option_from_settings (GtkPrinterOption *option,
2478                           GtkPrintSettings *settings)
2479 {
2480   const char *cups_value;
2481   char *value;
2482   
2483   if (settings == NULL)
2484     return;
2485
2486   if (strcmp (option->name, "gtk-paper-source") == 0)
2487     map_settings_to_option (option, paper_source_map, G_N_ELEMENTS (paper_source_map),
2488                              settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
2489   else if (strcmp (option->name, "gtk-output-tray") == 0)
2490     map_settings_to_option (option, output_tray_map, G_N_ELEMENTS (output_tray_map),
2491                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
2492   else if (strcmp (option->name, "gtk-duplex") == 0)
2493     map_settings_to_option (option, duplex_map, G_N_ELEMENTS (duplex_map),
2494                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
2495   else if (strcmp (option->name, "cups-OutputMode") == 0)
2496     map_settings_to_option (option, output_mode_map, G_N_ELEMENTS (output_mode_map),
2497                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
2498   else if (strcmp (option->name, "cups-Resolution") == 0)
2499     {
2500       cups_value = gtk_print_settings_get (settings, option->name);
2501       if (cups_value)
2502         gtk_printer_option_set (option, cups_value);
2503       else
2504         {
2505           int res = gtk_print_settings_get_resolution (settings);
2506           if (res != 0)
2507             {
2508               value = g_strdup_printf ("%ddpi", res);
2509               gtk_printer_option_set (option, value);
2510               g_free (value);
2511             }
2512         }
2513     }
2514   else if (strcmp (option->name, "gtk-paper-type") == 0)
2515     map_settings_to_option (option, media_type_map, G_N_ELEMENTS (media_type_map),
2516                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
2517   else if (strcmp (option->name, "gtk-n-up") == 0)
2518     {
2519       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
2520                               settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
2521     }
2522   else if (strcmp (option->name, "gtk-billing-info") == 0)
2523     {
2524       cups_value = gtk_print_settings_get (settings, "cups-job-billing");
2525       if (cups_value)
2526         gtk_printer_option_set (option, cups_value);
2527     } 
2528   else if (strcmp (option->name, "gtk-job-prio") == 0)
2529     {
2530       cups_value = gtk_print_settings_get (settings, "cups-job-priority");
2531       if (cups_value)
2532         gtk_printer_option_set (option, cups_value);
2533     } 
2534   else if (strcmp (option->name, "gtk-cover-before") == 0)
2535     {
2536       cups_value = gtk_print_settings_get (settings, "cover-before");
2537       if (cups_value)
2538         gtk_printer_option_set (option, cups_value);
2539     } 
2540   else if (strcmp (option->name, "gtk-cover-after") == 0)
2541     {
2542       cups_value = gtk_print_settings_get (settings, "cover-after");
2543       if (cups_value)
2544         gtk_printer_option_set (option, cups_value);
2545     } 
2546   else if (strcmp (option->name, "gtk-print-time") == 0)
2547     {
2548       cups_value = gtk_print_settings_get (settings, "print-at");
2549       if (cups_value)
2550         gtk_printer_option_set (option, cups_value);
2551     } 
2552   else if (strcmp (option->name, "gtk-print-time-text") == 0)
2553     {
2554       cups_value = gtk_print_settings_get (settings, "print-at-time");
2555       if (cups_value)
2556         gtk_printer_option_set (option, cups_value);
2557     } 
2558   else if (g_str_has_prefix (option->name, "cups-"))
2559     {
2560       cups_value = gtk_print_settings_get (settings, option->name);
2561       if (cups_value)
2562         gtk_printer_option_set (option, cups_value);
2563     } 
2564 }
2565
2566 static void
2567 foreach_option_get_settings (GtkPrinterOption *option,
2568                              gpointer          user_data)
2569 {
2570   struct OptionData *data = user_data;
2571   GtkPrintSettings *settings = data->settings;
2572   const char *value;
2573
2574   value = option->value;
2575
2576   if (strcmp (option->name, "gtk-paper-source") == 0)
2577     map_option_to_settings (value, paper_source_map, G_N_ELEMENTS (paper_source_map),
2578                             settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
2579   else if (strcmp (option->name, "gtk-output-tray") == 0)
2580     map_option_to_settings (value, output_tray_map, G_N_ELEMENTS (output_tray_map),
2581                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
2582   else if (strcmp (option->name, "gtk-duplex") == 0)
2583     map_option_to_settings (value, duplex_map, G_N_ELEMENTS (duplex_map),
2584                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
2585   else if (strcmp (option->name, "cups-OutputMode") == 0)
2586     map_option_to_settings (value, output_mode_map, G_N_ELEMENTS (output_mode_map),
2587                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
2588   else if (strcmp (option->name, "cups-Resolution") == 0)
2589     {
2590       int res = atoi (value);
2591       /* TODO: What if resolution is on XXXxYYYdpi form? */
2592       if (res != 0)
2593         gtk_print_settings_set_resolution (settings, res);
2594       gtk_print_settings_set (settings, option->name, value);
2595     }
2596   else if (strcmp (option->name, "gtk-paper-type") == 0)
2597     map_option_to_settings (value, media_type_map, G_N_ELEMENTS (media_type_map),
2598                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
2599   else if (strcmp (option->name, "gtk-n-up") == 0)
2600     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
2601                             settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
2602   else if (strcmp (option->name, "gtk-billing-info") == 0 && strlen (value) > 0)
2603     gtk_print_settings_set (settings, "cups-job-billing", value);
2604   else if (strcmp (option->name, "gtk-job-prio") == 0)
2605     gtk_print_settings_set (settings, "cups-job-priority", value);
2606   else if (strcmp (option->name, "gtk-cover-before") == 0)
2607     gtk_print_settings_set (settings, "cover-before", value);
2608   else if (strcmp (option->name, "gtk-cover-after") == 0)
2609     gtk_print_settings_set (settings, "cover-after", value);
2610   else if (strcmp (option->name, "gtk-print-time") == 0)
2611     gtk_print_settings_set (settings, "print-at", value);
2612   else if (strcmp (option->name, "gtk-print-time-text") == 0)
2613     gtk_print_settings_set (settings, "print-at-time", value);
2614   else if (g_str_has_prefix (option->name, "cups-"))
2615     gtk_print_settings_set (settings, option->name, value);
2616 }
2617
2618 static void
2619 cups_printer_get_settings_from_options (GtkPrinter          *printer,
2620                                         GtkPrinterOptionSet *options,
2621                                         GtkPrintSettings    *settings)
2622 {
2623   struct OptionData data;
2624   const char *print_at, *print_at_time;
2625
2626   data.printer = printer;
2627   data.options = options;
2628   data.settings = settings;
2629   data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
2630  
2631   if (data.ppd_file != NULL)
2632     {
2633       GtkPrinterOption *cover_before, *cover_after;
2634       
2635       gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
2636
2637       cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
2638       cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
2639       if (cover_before && cover_after)
2640         {
2641           char *value = g_strdup_printf ("%s,%s", cover_before->value, cover_after->value);
2642           gtk_print_settings_set (settings, "cups-job-sheets", value);
2643           g_free (value);
2644         }
2645
2646       print_at = gtk_print_settings_get (settings, "print-at");
2647       print_at_time = gtk_print_settings_get (settings, "print-at-time");
2648       if (strcmp (print_at, "at") == 0)
2649         gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time);
2650       else if (strcmp (print_at, "on-hold") == 0)
2651         gtk_print_settings_set (settings, "cups-job-hold-until", "indefinite");
2652     }
2653 }
2654
2655 static void
2656 cups_printer_prepare_for_print (GtkPrinter       *printer,
2657                                 GtkPrintJob      *print_job,
2658                                 GtkPrintSettings *settings,
2659                                 GtkPageSetup     *page_setup)
2660 {
2661   GtkPageSet page_set;
2662   GtkPaperSize *paper_size;
2663   const char *ppd_paper_name;
2664   double scale;
2665
2666   print_job->print_pages = gtk_print_settings_get_print_pages (settings);
2667   print_job->page_ranges = NULL;
2668   print_job->num_page_ranges = 0;
2669   
2670   if (print_job->print_pages == GTK_PRINT_PAGES_RANGES)
2671     print_job->page_ranges =
2672       gtk_print_settings_get_page_ranges (settings,
2673                                           &print_job->num_page_ranges);
2674   
2675   if (gtk_print_settings_get_collate (settings))
2676     gtk_print_settings_set (settings, "cups-Collate", "True");
2677   print_job->collate = FALSE;
2678
2679   if (gtk_print_settings_get_reverse (settings))
2680     gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
2681   print_job->reverse = FALSE;
2682
2683   if (gtk_print_settings_get_n_copies (settings) > 1)
2684     gtk_print_settings_set_int (settings, "cups-copies",
2685                                 gtk_print_settings_get_n_copies (settings));
2686   print_job->num_copies = 1;
2687
2688   scale = gtk_print_settings_get_scale (settings);
2689   print_job->scale = 1.0;
2690   if (scale != 100.0)
2691     print_job->scale = scale/100.0;
2692
2693   page_set = gtk_print_settings_get_page_set (settings);
2694   if (page_set == GTK_PAGE_SET_EVEN)
2695     gtk_print_settings_set (settings, "cups-page-set", "even");
2696   else if (page_set == GTK_PAGE_SET_ODD)
2697     gtk_print_settings_set (settings, "cups-page-set", "odd");
2698   print_job->page_set = GTK_PAGE_SET_ALL;
2699
2700   paper_size = gtk_page_setup_get_paper_size (page_setup);
2701   ppd_paper_name = gtk_paper_size_get_ppd_name (paper_size);
2702   if (ppd_paper_name != NULL)
2703     gtk_print_settings_set (settings, "cups-PageSize", ppd_paper_name);
2704   else
2705     {
2706       char *custom_name = g_strdup_printf ("Custom.%2fx%.2f",
2707                                            gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS),
2708                                            gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
2709       gtk_print_settings_set (settings, "cups-PageSize", custom_name);
2710       g_free (custom_name);
2711     }
2712
2713   print_job->rotate_to_orientation = TRUE;
2714 }
2715
2716 static GList *
2717 cups_printer_list_papers (GtkPrinter *printer)
2718 {
2719   ppd_file_t *ppd_file;
2720   ppd_size_t *size;
2721   char *display_name;
2722   GtkPageSetup *page_setup;
2723   GtkPaperSize *paper_size;
2724   ppd_option_t *option;
2725   ppd_choice_t *choice;
2726   GList *l;
2727   int i;
2728
2729   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
2730   if (ppd_file == NULL)
2731     return NULL;
2732
2733   l = NULL;
2734   
2735   for (i = 0; i < ppd_file->num_sizes; i++)
2736     {
2737       size = &ppd_file->sizes[i];
2738
2739       display_name = NULL;
2740       option = ppdFindOption (ppd_file, "PageSize");
2741       if (option)
2742         {
2743           choice = ppdFindChoice (option, size->name);
2744           if (choice)
2745             display_name = ppd_text_to_utf8 (ppd_file, choice->text);
2746         }
2747       if (display_name == NULL)
2748         display_name = g_strdup (size->name);
2749
2750       page_setup = gtk_page_setup_new ();
2751       paper_size = gtk_paper_size_new_from_ppd (size->name,
2752                                                 display_name,
2753                                                 size->width,
2754                                                 size->length);
2755       gtk_page_setup_set_paper_size (page_setup, paper_size);
2756       gtk_paper_size_free (paper_size);
2757
2758       gtk_page_setup_set_top_margin (page_setup, size->length - size->top, GTK_UNIT_POINTS);
2759       gtk_page_setup_set_bottom_margin (page_setup, size->bottom, GTK_UNIT_POINTS);
2760       gtk_page_setup_set_left_margin (page_setup, size->left, GTK_UNIT_POINTS);
2761       gtk_page_setup_set_right_margin (page_setup, size->width - size->right, GTK_UNIT_POINTS);
2762         
2763       g_free (display_name);
2764
2765       l = g_list_prepend (l, page_setup);
2766     }
2767
2768   return g_list_reverse (l);
2769 }
2770
2771 static void
2772 cups_printer_get_hard_margins (GtkPrinter *printer,
2773                                gdouble    *top,
2774                                gdouble    *bottom,
2775                                gdouble    *left,
2776                                gdouble    *right)
2777 {
2778   ppd_file_t *ppd_file;
2779
2780   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
2781   if (ppd_file == NULL)
2782     return;
2783
2784   *left = ppd_file->custom_margins[0];
2785   *bottom = ppd_file->custom_margins[1];
2786   *right = ppd_file->custom_margins[2];
2787   *top = ppd_file->custom_margins[3];
2788 }
2789
2790 static GtkPrintCapabilities
2791 cups_printer_get_capabilities (GtkPrinter *printer)
2792 {
2793   return
2794     GTK_PRINT_CAPABILITY_COPIES |
2795     GTK_PRINT_CAPABILITY_COLLATE |
2796     GTK_PRINT_CAPABILITY_REVERSE;
2797 }