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