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