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