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