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