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