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