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