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