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