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