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