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