]> Pileus Git - ~andy/gtk/blob - modules/printbackends/cups/gtkprintbackendcups.c
Improve one string
[~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) 2006, 2007 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 "config.h"
23 #include <ctype.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <stdlib.h>
28
29 #include <cups/cups.h>
30 #include <cups/language.h>
31 #include <cups/http.h>
32 #include <cups/ipp.h>
33 #include <errno.h>
34 #include <cairo.h>
35 #include <cairo-pdf.h>
36 #include <cairo-ps.h>
37
38 #include <glib/gstdio.h>
39 #include <glib/gi18n-lib.h>
40 #include <gmodule.h>
41
42 #include <gtk/gtk.h>
43 #include <gtk/gtkprintbackend.h>
44 #include <gtk/gtkunixprint.h>
45 #include <gtk/gtkprinter-private.h>
46
47 #include "gtkprintbackendcups.h"
48 #include "gtkprintercups.h"
49
50 #include "gtkcupsutils.h"
51
52
53 typedef struct _GtkPrintBackendCupsClass GtkPrintBackendCupsClass;
54
55 #define GTK_PRINT_BACKEND_CUPS_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
56 #define GTK_IS_PRINT_BACKEND_CUPS_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_CUPS))
57 #define GTK_PRINT_BACKEND_CUPS_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
58
59 #define _CUPS_MAX_ATTEMPTS 10 
60 #define _CUPS_MAX_CHUNK_SIZE 8192
61
62 /* define this to see warnings about ignored ppd options */
63 #undef PRINT_IGNORED_OPTIONS
64
65 #define _CUPS_MAP_ATTR_INT(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = attr->values[0].integer;}
66 #define _CUPS_MAP_ATTR_STR(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = attr->values[0].string.text;}
67
68 static GType print_backend_cups_type = 0;
69
70 typedef void (* GtkPrintCupsResponseCallbackFunc) (GtkPrintBackend *print_backend,
71                                                    GtkCupsResult   *result, 
72                                                    gpointer         user_data);
73
74 typedef enum 
75 {
76   DISPATCH_SETUP,
77   DISPATCH_REQUEST,
78   DISPATCH_SEND,
79   DISPATCH_CHECK,
80   DISPATCH_READ,
81   DISPATCH_ERROR
82 } GtkPrintCupsDispatchState;
83
84 typedef struct 
85 {
86   GSource source;
87
88   http_t *http;
89   GtkCupsRequest *request;
90   GPollFD *data_poll;
91   GtkPrintBackendCups *backend;
92
93 } GtkPrintCupsDispatchWatch;
94
95 struct _GtkPrintBackendCupsClass
96 {
97   GtkPrintBackendClass parent_class;
98 };
99
100 struct _GtkPrintBackendCups
101 {
102   GtkPrintBackend parent_instance;
103
104   char *default_printer;
105   
106   guint list_printers_poll;
107   guint list_printers_pending : 1;
108   guint got_default_printer   : 1;
109   guint default_printer_poll;
110   GtkCupsConnectionTest *default_printer_connection_test;
111
112   char **covers;
113   char  *default_cover_before;
114   char  *default_cover_after;
115   int    number_of_covers;
116 };
117
118 static GObjectClass *backend_parent_class;
119
120 static void                 gtk_print_backend_cups_class_init      (GtkPrintBackendCupsClass          *class);
121 static void                 gtk_print_backend_cups_init            (GtkPrintBackendCups               *impl);
122 static void                 gtk_print_backend_cups_finalize        (GObject                           *object);
123 static void                 gtk_print_backend_cups_dispose         (GObject                           *object);
124 static void                 cups_get_printer_list                  (GtkPrintBackend                   *print_backend);
125 static void                 cups_get_default_printer               (GtkPrintBackendCups               *print_backend);
126 static void                 cups_request_execute                   (GtkPrintBackendCups               *print_backend,
127                                                                     GtkCupsRequest                    *request,
128                                                                     GtkPrintCupsResponseCallbackFunc   callback,
129                                                                     gpointer                           user_data,
130                                                                     GDestroyNotify                     notify);
131 static void                 cups_printer_get_settings_from_options (GtkPrinter                        *printer,
132                                                                     GtkPrinterOptionSet               *options,
133                                                                     GtkPrintSettings                  *settings);
134 static gboolean             cups_printer_mark_conflicts            (GtkPrinter                        *printer,
135                                                                     GtkPrinterOptionSet               *options);
136 static GtkPrinterOptionSet *cups_printer_get_options               (GtkPrinter                        *printer,
137                                                                     GtkPrintSettings                  *settings,
138                                                                     GtkPageSetup                      *page_setup,
139                                                                     GtkPrintCapabilities               capabilities);
140 static void                 cups_printer_prepare_for_print         (GtkPrinter                        *printer,
141                                                                     GtkPrintJob                       *print_job,
142                                                                     GtkPrintSettings                  *settings,
143                                                                     GtkPageSetup                      *page_setup);
144 static GList *              cups_printer_list_papers               (GtkPrinter                        *printer);
145 static GtkPageSetup *       cups_printer_get_default_page_size     (GtkPrinter                        *printer);
146 static void                 cups_printer_request_details           (GtkPrinter                        *printer);
147 static gboolean             cups_request_default_printer           (GtkPrintBackendCups               *print_backend);
148 static void                 cups_request_ppd                       (GtkPrinter                        *printer);
149 static void                 cups_printer_get_hard_margins          (GtkPrinter                        *printer,
150                                                                     double                            *top,
151                                                                     double                            *bottom,
152                                                                     double                            *left,
153                                                                     double                            *right);
154 static GtkPrintCapabilities cups_printer_get_capabilities          (GtkPrinter                        *printer);
155 static void                 set_option_from_settings               (GtkPrinterOption                  *option,
156                                                                     GtkPrintSettings                  *setting);
157 static void                 cups_begin_polling_info                (GtkPrintBackendCups               *print_backend,
158                                                                     GtkPrintJob                       *job,
159                                                                     int                                job_id);
160 static gboolean             cups_job_info_poll_timeout             (gpointer                           user_data);
161 static void                 gtk_print_backend_cups_print_stream    (GtkPrintBackend                   *backend,
162                                                                     GtkPrintJob                       *job,
163                                                                     GIOChannel                        *data_io,
164                                                                     GtkPrintJobCompleteFunc            callback,
165                                                                     gpointer                           user_data,
166                                                                     GDestroyNotify                     dnotify);
167 static cairo_surface_t *    cups_printer_create_cairo_surface      (GtkPrinter                        *printer,
168                                                                     GtkPrintSettings                  *settings,
169                                                                     gdouble                            width,
170                                                                     gdouble                            height,
171                                                                     GIOChannel                        *cache_io);
172
173
174 static void
175 gtk_print_backend_cups_register_type (GTypeModule *module)
176 {
177   static const GTypeInfo print_backend_cups_info =
178   {
179     sizeof (GtkPrintBackendCupsClass),
180     NULL,               /* base_init */
181     NULL,               /* base_finalize */
182     (GClassInitFunc) gtk_print_backend_cups_class_init,
183     NULL,               /* class_finalize */
184     NULL,               /* class_data */
185     sizeof (GtkPrintBackendCups),
186     0,                  /* n_preallocs */
187     (GInstanceInitFunc) gtk_print_backend_cups_init
188   };
189
190   print_backend_cups_type = g_type_module_register_type (module,
191                                                          GTK_TYPE_PRINT_BACKEND,
192                                                          "GtkPrintBackendCups",
193                                                          &print_backend_cups_info, 0);
194 }
195
196 G_MODULE_EXPORT void 
197 pb_module_init (GTypeModule *module)
198 {
199   GTK_NOTE (PRINTING,
200             g_print ("CUPS Backend: Initializing the CUPS print backend module\n")); 
201
202   gtk_print_backend_cups_register_type (module);
203   gtk_printer_cups_register_type (module);
204 }
205
206 G_MODULE_EXPORT void 
207 pb_module_exit (void)
208 {
209
210 }
211   
212 G_MODULE_EXPORT GtkPrintBackend * 
213 pb_module_create (void)
214 {
215   return gtk_print_backend_cups_new ();
216 }
217
218 /*
219  * GtkPrintBackendCups
220  */
221 GType
222 gtk_print_backend_cups_get_type (void)
223 {
224   return print_backend_cups_type;
225 }
226
227 /**
228  * gtk_print_backend_cups_new:
229  *
230  * Creates a new #GtkPrintBackendCups object. #GtkPrintBackendCups
231  * implements the #GtkPrintBackend interface with direct access to
232  * the filesystem using Unix/Linux API calls
233  *
234  * Return value: the new #GtkPrintBackendCups object
235  */
236 GtkPrintBackend *
237 gtk_print_backend_cups_new (void)
238 {
239   GTK_NOTE (PRINTING,
240             g_print ("CUPS Backend: Creating a new CUPS print backend object\n"));
241
242   return g_object_new (GTK_TYPE_PRINT_BACKEND_CUPS, NULL);
243 }
244
245 static void
246 gtk_print_backend_cups_class_init (GtkPrintBackendCupsClass *class)
247 {
248   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
249   GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class);
250
251   backend_parent_class = g_type_class_peek_parent (class);
252
253   gobject_class->finalize = gtk_print_backend_cups_finalize;
254   gobject_class->dispose = gtk_print_backend_cups_dispose;
255
256   backend_class->request_printer_list = cups_get_printer_list; 
257   backend_class->print_stream = gtk_print_backend_cups_print_stream;
258   backend_class->printer_request_details = cups_printer_request_details;
259   backend_class->printer_create_cairo_surface = cups_printer_create_cairo_surface;
260   backend_class->printer_get_options = cups_printer_get_options;
261   backend_class->printer_mark_conflicts = cups_printer_mark_conflicts;
262   backend_class->printer_get_settings_from_options = cups_printer_get_settings_from_options;
263   backend_class->printer_prepare_for_print = cups_printer_prepare_for_print;
264   backend_class->printer_list_papers = cups_printer_list_papers;
265   backend_class->printer_get_default_page_size = cups_printer_get_default_page_size;
266   backend_class->printer_get_hard_margins = cups_printer_get_hard_margins;
267   backend_class->printer_get_capabilities = cups_printer_get_capabilities;
268 }
269
270 static cairo_status_t
271 _cairo_write_to_cups (void                *closure,
272                       const unsigned char *data,
273                       unsigned int         length)
274 {
275   GIOChannel *io = (GIOChannel *)closure;
276   gsize written;
277   GError *error;
278
279   error = NULL;
280
281   GTK_NOTE (PRINTING,
282             g_print ("CUPS Backend: Writing %i byte chunk to temp file\n", length));
283
284   while (length > 0) 
285     {
286       g_io_channel_write_chars (io, (gchar *)data, length, &written, &error);
287
288       if (error != NULL)
289         {
290           GTK_NOTE (PRINTING,
291                     g_print ("CUPS Backend: Error writing to temp file, %s\n", 
292                              error->message));
293
294           g_error_free (error);
295           return CAIRO_STATUS_WRITE_ERROR;
296         }    
297
298       GTK_NOTE (PRINTING,
299                 g_print ("CUPS Backend: Wrote %i bytes to temp file\n", written));
300
301       data += written;
302       length -= written;
303     }
304
305   return CAIRO_STATUS_SUCCESS;
306 }
307
308 static cairo_surface_t *
309 cups_printer_create_cairo_surface (GtkPrinter       *printer,
310                                    GtkPrintSettings *settings,
311                                    gdouble           width, 
312                                    gdouble           height,
313                                    GIOChannel       *cache_io)
314 {
315   cairo_surface_t *surface; 
316   ppd_file_t      *ppd_file = NULL;
317   ppd_attr_t      *ppd_attr = NULL;
318   int              level = 2;
319  
320   /* TODO: check if it is a ps or pdf printer */
321   
322   surface = cairo_ps_surface_create_for_stream  (_cairo_write_to_cups, cache_io, width, height);
323
324   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
325
326   if (ppd_file != NULL)
327     {
328       ppd_attr = ppdFindAttr (ppd_file, "LanguageLevel", NULL);
329
330       if (ppd_attr != NULL)
331         level = atoi (ppd_attr->value);
332     }
333
334   if (level == 2)
335     cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_2);
336
337   if (level == 3)
338     cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_3);
339
340   /* TODO: DPI from settings object? */
341   cairo_surface_set_fallback_resolution (surface, 300, 300);
342
343   return surface;
344 }
345
346 typedef struct {
347   GtkPrintJobCompleteFunc callback;
348   GtkPrintJob *job;
349   gpointer user_data;
350   GDestroyNotify dnotify;
351 } CupsPrintStreamData;
352
353 static void
354 cups_free_print_stream_data (CupsPrintStreamData *data)
355 {
356   GTK_NOTE (PRINTING,
357             g_print ("CUPS Backend: %s\n", G_STRFUNC));
358
359   if (data->dnotify)
360     data->dnotify (data->user_data);
361   g_object_unref (data->job);
362   g_free (data);
363 }
364
365 static void
366 cups_print_cb (GtkPrintBackendCups *print_backend,
367                GtkCupsResult       *result,
368                gpointer             user_data)
369 {
370   GError *error = NULL;
371   CupsPrintStreamData *ps = user_data;
372
373   GDK_THREADS_ENTER ();
374
375   GTK_NOTE (PRINTING,
376             g_print ("CUPS Backend: %s\n", G_STRFUNC)); 
377
378   if (gtk_cups_result_is_error (result))
379     error = g_error_new_literal (gtk_print_error_quark (),
380                                  GTK_PRINT_ERROR_INTERNAL_ERROR,
381                                  gtk_cups_result_get_error_string (result));
382
383   if (ps->callback)
384     ps->callback (ps->job, ps->user_data, error);
385
386   if (error == NULL)
387     {
388       int job_id = 0;
389       ipp_attribute_t *attr;            /* IPP job-id attribute */
390       ipp_t *response = gtk_cups_result_get_response (result);
391
392       if ((attr = ippFindAttribute (response, "job-id", IPP_TAG_INTEGER)) != NULL)
393         job_id = attr->values[0].integer;
394
395       if (!gtk_print_job_get_track_print_status (ps->job) || job_id == 0)
396         gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED);
397       else
398         {
399           gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_PENDING);
400           cups_begin_polling_info (print_backend, ps->job, job_id);
401         }
402     } 
403   else
404     gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED_ABORTED);
405
406   
407   if (error)
408     g_error_free (error);
409
410   GDK_THREADS_LEAVE ();  
411 }
412
413 static void
414 add_cups_options (const gchar *key,
415                   const gchar *value,
416                   gpointer     user_data)
417 {
418   GtkCupsRequest *request = user_data;
419
420   if (!g_str_has_prefix (key, "cups-"))
421     return;
422
423   if (strcmp (value, "gtk-ignore-value") == 0)
424     return;
425   
426   key = key + strlen ("cups-");
427
428   gtk_cups_request_encode_option (request, key, value);
429 }
430
431 static void
432 gtk_print_backend_cups_print_stream (GtkPrintBackend         *print_backend,
433                                      GtkPrintJob             *job,
434                                      GIOChannel              *data_io,
435                                      GtkPrintJobCompleteFunc  callback,
436                                      gpointer                 user_data,
437                                      GDestroyNotify           dnotify)
438 {
439   GtkPrinterCups *cups_printer;
440   CupsPrintStreamData *ps;
441   GtkCupsRequest *request;
442   GtkPrintSettings *settings;
443   const gchar *title;
444   char  printer_absolute_uri[HTTP_MAX_URI];
445
446   GTK_NOTE (PRINTING,
447             g_print ("CUPS Backend: %s\n", G_STRFUNC));   
448
449   cups_printer = GTK_PRINTER_CUPS (gtk_print_job_get_printer (job));
450   settings = gtk_print_job_get_settings (job);
451
452   request = gtk_cups_request_new (NULL,
453                                   GTK_CUPS_POST,
454                                   IPP_PRINT_JOB,
455                                   data_io,
456                                   NULL, 
457                                   cups_printer->device_uri);
458
459 #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1
460   httpAssembleURIf (HTTP_URI_CODING_ALL,
461                     printer_absolute_uri,
462                     sizeof (printer_absolute_uri),
463                     "ipp",
464                     NULL,
465                     "localhost",
466                     ippPort (),
467                     "/printers/%s",
468                     gtk_printer_get_name (gtk_print_job_get_printer (job)));
469 #else
470   g_snprintf (printer_absolute_uri,
471               sizeof (printer_absolute_uri),
472               "ipp://localhost:%d/printers/%s",
473               ippPort (),
474               gtk_printer_get_name (gtk_print_job_get_printer (job)));
475 #endif
476
477   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, 
478                                    IPP_TAG_URI, "printer-uri",
479                                    NULL, printer_absolute_uri);
480
481   title = gtk_print_job_get_title (job);
482   if (title)
483     gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, 
484                                      IPP_TAG_NAME, "job-name", 
485                                      NULL, title);
486
487   gtk_print_settings_foreach (settings, add_cups_options, request);
488   
489   ps = g_new0 (CupsPrintStreamData, 1);
490   ps->callback = callback;
491   ps->user_data = user_data;
492   ps->dnotify = dnotify;
493   ps->job = g_object_ref (job);
494
495   cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
496                         request,
497                         (GtkPrintCupsResponseCallbackFunc) cups_print_cb,
498                         ps,
499                         (GDestroyNotify)cups_free_print_stream_data);
500 }
501
502
503 static void
504 gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups)
505 {
506   backend_cups->list_printers_poll = FALSE;  
507   backend_cups->got_default_printer = FALSE;  
508   backend_cups->list_printers_pending = FALSE;
509
510   backend_cups->covers = NULL;
511   backend_cups->default_cover_before = NULL;
512   backend_cups->default_cover_after = NULL;
513   backend_cups->number_of_covers = 0;
514
515   backend_cups->default_printer_poll = 0;
516   backend_cups->default_printer_connection_test = NULL;
517
518   cups_get_default_printer (backend_cups);
519 }
520
521 static void
522 gtk_print_backend_cups_finalize (GObject *object)
523 {
524   GtkPrintBackendCups *backend_cups;
525   
526   GTK_NOTE (PRINTING,
527             g_print ("CUPS Backend: finalizing CUPS backend module\n"));
528
529   backend_cups = GTK_PRINT_BACKEND_CUPS (object);
530
531   g_free (backend_cups->default_printer);
532   backend_cups->default_printer = NULL;
533
534   g_strfreev (backend_cups->covers);
535   backend_cups->number_of_covers = 0;
536
537   g_free (backend_cups->default_cover_before);
538   g_free (backend_cups->default_cover_after);
539
540   gtk_cups_connection_test_free (backend_cups->default_printer_connection_test);
541   
542   backend_parent_class->finalize (object);
543 }
544
545 static void
546 gtk_print_backend_cups_dispose (GObject *object)
547 {
548   GtkPrintBackendCups *backend_cups;
549
550   GTK_NOTE (PRINTING,
551             g_print ("CUPS Backend: %s\n", G_STRFUNC));
552
553   backend_cups = GTK_PRINT_BACKEND_CUPS (object);
554
555   if (backend_cups->list_printers_poll > 0)
556     g_source_remove (backend_cups->list_printers_poll);
557   backend_cups->list_printers_poll = 0;
558   
559   if (backend_cups->default_printer_poll > 0)
560     g_source_remove (backend_cups->default_printer_poll);
561   backend_cups->default_printer_poll = 0;
562
563   backend_parent_class->dispose (object);
564 }
565
566
567 static gboolean
568 cups_dispatch_watch_check (GSource *source)
569 {
570   GtkPrintCupsDispatchWatch *dispatch;
571   GtkCupsPollState poll_state;
572   gboolean result;
573
574   GTK_NOTE (PRINTING,
575             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source)); 
576
577   dispatch = (GtkPrintCupsDispatchWatch *) source;
578
579   poll_state = gtk_cups_request_get_poll_state (dispatch->request);
580
581   if (dispatch->request->http != NULL)
582     {
583       if (dispatch->data_poll == NULL)
584         {
585           dispatch->data_poll = g_new0 (GPollFD, 1);
586           g_source_add_poll (source, dispatch->data_poll);
587         }
588       else
589         {
590           if (poll_state == GTK_CUPS_HTTP_READ)
591             dispatch->data_poll->events = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI;
592           else if (poll_state == GTK_CUPS_HTTP_WRITE)
593             dispatch->data_poll->events = G_IO_OUT | G_IO_ERR;
594           else
595             dispatch->data_poll->events = 0;
596         }
597
598 #ifdef HAVE_CUPS_API_1_2
599       dispatch->data_poll->fd = httpGetFd (dispatch->request->http);
600 #else
601       dispatch->data_poll->fd = dispatch->request->http->fd;
602 #endif
603     }
604     
605   if (poll_state != GTK_CUPS_HTTP_IDLE)  
606     if (!(dispatch->data_poll->revents & dispatch->data_poll->events)) 
607        return FALSE;
608   
609   result = gtk_cups_request_read_write (dispatch->request);
610   if (result && dispatch->data_poll != NULL)
611     {
612       g_source_remove_poll (source, dispatch->data_poll);
613       g_free (dispatch->data_poll);
614       dispatch->data_poll = NULL;
615     }
616   
617   return result;
618 }
619
620 static gboolean
621 cups_dispatch_watch_prepare (GSource *source,
622                              gint    *timeout_)
623 {
624   GtkPrintCupsDispatchWatch *dispatch;
625
626   dispatch = (GtkPrintCupsDispatchWatch *) source;
627
628   GTK_NOTE (PRINTING,
629             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
630
631   *timeout_ = -1;
632   
633   return gtk_cups_request_read_write (dispatch->request);
634 }
635
636 static gboolean
637 cups_dispatch_watch_dispatch (GSource     *source,
638                               GSourceFunc  callback,
639                               gpointer     user_data)
640 {
641   GtkPrintCupsDispatchWatch *dispatch;
642   GtkPrintCupsResponseCallbackFunc ep_callback;  
643   GtkCupsResult *result;
644   
645   g_assert (callback != NULL);
646
647   ep_callback = (GtkPrintCupsResponseCallbackFunc) callback;
648   
649   dispatch = (GtkPrintCupsDispatchWatch *) source;
650
651   result = gtk_cups_request_get_result (dispatch->request);
652
653   GTK_NOTE (PRINTING,
654             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
655
656   if (gtk_cups_result_is_error (result))
657     {
658       GTK_NOTE (PRINTING, 
659                 g_print("Error result: %s (type %i, status %i, code %i)\n", 
660                         gtk_cups_result_get_error_string (result),
661                         gtk_cups_result_get_error_type (result),
662                         gtk_cups_result_get_error_status (result),
663                         gtk_cups_result_get_error_code (result)));
664      }
665
666   ep_callback (GTK_PRINT_BACKEND (dispatch->backend), result, user_data);
667     
668   return FALSE;
669 }
670
671 static void
672 cups_dispatch_watch_finalize (GSource *source)
673 {
674   GtkPrintCupsDispatchWatch *dispatch;
675
676   GTK_NOTE (PRINTING,
677             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
678
679   dispatch = (GtkPrintCupsDispatchWatch *) source;
680
681   gtk_cups_request_free (dispatch->request);
682
683   if (dispatch->backend)
684     {
685       /* We need to unref this at idle time, because it might be the
686        * last reference to this module causing the code to be
687        * unloaded (including this particular function!)
688        * Update: Doing this at idle caused a deadlock taking the
689        * mainloop context lock while being in a GSource callout for
690        * multithreaded apps. So, for now we just disable unloading
691        * of print backends. See _gtk_print_backend_create for the
692        * disabling.
693        */
694       g_object_unref (dispatch->backend);
695       dispatch->backend = NULL;
696     }
697
698   g_free (dispatch->data_poll);
699 }
700
701 static GSourceFuncs _cups_dispatch_watch_funcs = {
702   cups_dispatch_watch_prepare,
703   cups_dispatch_watch_check,
704   cups_dispatch_watch_dispatch,
705   cups_dispatch_watch_finalize
706 };
707
708
709 static void
710 cups_request_execute (GtkPrintBackendCups              *print_backend,
711                       GtkCupsRequest                   *request,
712                       GtkPrintCupsResponseCallbackFunc  callback,
713                       gpointer                          user_data,
714                       GDestroyNotify                    notify)
715 {
716   GtkPrintCupsDispatchWatch *dispatch;
717
718   dispatch = (GtkPrintCupsDispatchWatch *) g_source_new (&_cups_dispatch_watch_funcs, 
719                                                          sizeof (GtkPrintCupsDispatchWatch));
720
721   GTK_NOTE (PRINTING,
722             g_print ("CUPS Backend: %s <source %p> - Executing cups request on server '%s' and resource '%s'\n", G_STRFUNC, dispatch, request->server, request->resource));
723
724   dispatch->request = request;
725   dispatch->backend = g_object_ref (print_backend);
726   dispatch->data_poll = NULL;
727
728   g_source_set_callback ((GSource *) dispatch, (GSourceFunc) callback, user_data, notify);
729
730   g_source_attach ((GSource *) dispatch, NULL);
731   g_source_unref ((GSource *) dispatch);
732 }
733
734 #if 0
735 static void
736 cups_request_printer_info_cb (GtkPrintBackendCups *backend,
737                               GtkCupsResult       *result,
738                               gpointer             user_data)
739 {
740   ipp_attribute_t *attr;
741   ipp_t *response;
742   gchar *printer_name;
743   GtkPrinterCups *cups_printer;
744   GtkPrinter *printer;
745   gchar *loc;
746   gchar *desc;
747   gchar *state_msg;
748   int job_count;
749   gboolean status_changed;  
750
751   g_assert (GTK_IS_PRINT_BACKEND_CUPS (backend));
752
753   printer_name = (gchar *)user_data;
754   printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (backend),
755                                             printer_name);
756
757   GTK_NOTE (PRINTING,
758             g_print ("CUPS Backend: %s - Got printer info for printer '%s'\n", G_STRFUNC, printer_name));
759
760   if (!printer)
761     {
762       GTK_NOTE (PRINTING,
763             g_print ("CUPS Backend: Could not find printer called '%s'\n", printer_name));
764       return;
765     }
766
767   cups_printer = GTK_PRINTER_CUPS (printer);
768   
769   if (gtk_cups_result_is_error (result))
770     {
771       if (gtk_printer_is_new (printer))
772         {
773           gtk_print_backend_remove_printer (GTK_PRINT_BACKEND (backend),
774                                             printer);
775           return;
776         }
777       else
778         return; /* TODO: mark as inactive printer */
779     }
780
781   response = gtk_cups_result_get_response (result);
782
783   /* TODO: determine printer type and use correct icon */
784   gtk_printer_set_icon_name (printer, "gtk-print");
785  
786   state_msg = "";
787   loc = "";
788   desc = "";
789   job_count = 0;
790   for (attr = response->attrs; attr != NULL; attr = attr->next) 
791     {
792       if (!attr->name)
793         continue;
794
795       _CUPS_MAP_ATTR_STR (attr, loc, "printer-location");
796       _CUPS_MAP_ATTR_STR (attr, desc, "printer-info");
797       _CUPS_MAP_ATTR_STR (attr, state_msg, "printer-state-message");
798       _CUPS_MAP_ATTR_INT (attr, cups_printer->state, "printer-state");
799       _CUPS_MAP_ATTR_INT (attr, job_count, "queued-job-count");
800     }
801
802   status_changed = gtk_printer_set_job_count (printer, job_count);
803   
804   status_changed |= gtk_printer_set_location (printer, loc);
805   status_changed |= gtk_printer_set_description (printer, desc);
806   status_changed |= gtk_printer_set_state_message (printer, state_msg);
807
808   if (status_changed)
809     g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
810                            "printer-status-changed", printer); 
811 }
812
813 static void
814 cups_request_printer_info (GtkPrintBackendCups *print_backend,
815                            const gchar         *printer_name)
816 {
817   GtkCupsRequest *request;
818   gchar *printer_uri;
819   static const char * const pattrs[] =  /* Attributes we're interested in */
820     {
821       "printer-location",
822       "printer-info",
823       "printer-state-message",
824       "printer-state",
825       "queued-job-count",
826       "job-sheets-supported",
827       "job-sheets-default"
828     };
829
830   request = gtk_cups_request_new (NULL,
831                                   GTK_CUPS_POST,
832                                   IPP_GET_PRINTER_ATTRIBUTES,
833                                   NULL,
834                                   NULL,
835                                   NULL);
836
837   printer_uri = g_strdup_printf ("ipp://localhost/printers/%s",
838                                   printer_name);
839   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
840                                    "printer-uri", NULL, printer_uri);
841
842   GTK_NOTE (PRINTING,
843             g_print ("CUPS Backend: %s - Requesting printer info for URI '%s'\n", G_STRFUNC, printer_uri));
844
845   g_free (printer_uri);
846
847   gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
848                                     "requested-attributes", G_N_ELEMENTS (pattrs),
849                                     NULL, pattrs);
850  
851   cups_request_execute (print_backend,
852                         request,
853                         (GtkPrintCupsResponseCallbackFunc) cups_request_printer_info_cb,
854                         g_strdup (printer_name),
855                         (GDestroyNotify) g_free);
856 }
857 #endif
858
859 typedef struct {
860   GtkPrintBackendCups *print_backend;
861   GtkPrintJob *job;
862   int job_id;
863   int counter;
864 } CupsJobPollData;
865
866 static void
867 job_object_died (gpointer  user_data,
868                  GObject  *where_the_object_was)
869 {
870   CupsJobPollData *data = user_data;
871   data->job = NULL;
872 }
873
874 static void
875 cups_job_poll_data_free (CupsJobPollData *data)
876 {
877   if (data->job)
878     g_object_weak_unref (G_OBJECT (data->job), job_object_died, data);
879     
880   g_free (data);
881 }
882
883 static void
884 cups_request_job_info_cb (GtkPrintBackendCups *print_backend,
885                           GtkCupsResult       *result,
886                           gpointer             user_data)
887 {
888   CupsJobPollData *data = user_data;
889   ipp_attribute_t *attr;
890   ipp_t *response;
891   int state;
892   gboolean done;
893
894   GDK_THREADS_ENTER ();
895
896   if (data->job == NULL)
897     {
898       cups_job_poll_data_free (data);
899       goto done;
900     }
901
902   data->counter++;
903   
904   response = gtk_cups_result_get_response (result);
905
906   state = 0;
907   for (attr = response->attrs; attr != NULL; attr = attr->next) 
908     {
909       if (!attr->name)
910         continue;
911       
912       _CUPS_MAP_ATTR_INT (attr, state, "job-state");
913     }
914   
915   done = FALSE;
916   switch (state)
917     {
918     case IPP_JOB_PENDING:
919     case IPP_JOB_HELD:
920     case IPP_JOB_STOPPED:
921       gtk_print_job_set_status (data->job,
922                                 GTK_PRINT_STATUS_PENDING);
923       break;
924     case IPP_JOB_PROCESSING:
925       gtk_print_job_set_status (data->job,
926                                 GTK_PRINT_STATUS_PRINTING);
927       break;
928     default:
929     case IPP_JOB_CANCELLED:
930     case IPP_JOB_ABORTED:
931       gtk_print_job_set_status (data->job,
932                                 GTK_PRINT_STATUS_FINISHED_ABORTED);
933       done = TRUE;
934       break;
935     case 0:
936     case IPP_JOB_COMPLETED:
937       gtk_print_job_set_status (data->job,
938                                 GTK_PRINT_STATUS_FINISHED);
939       done = TRUE;
940       break;
941     }
942
943   if (!done && data->job != NULL)
944     {
945       guint32 timeout;
946
947       if (data->counter < 5)
948         timeout = 100;
949       else if (data->counter < 10)
950         timeout = 500;
951       else
952         timeout = 1000;
953       
954       g_timeout_add (timeout, cups_job_info_poll_timeout, data);
955     }
956   else
957     cups_job_poll_data_free (data);    
958
959 done:
960   GDK_THREADS_LEAVE ();
961 }
962
963 static void
964 cups_request_job_info (CupsJobPollData *data)
965 {
966   GtkCupsRequest *request;
967   gchar *job_uri;
968
969   request = gtk_cups_request_new (NULL,
970                                   GTK_CUPS_POST,
971                                   IPP_GET_JOB_ATTRIBUTES,
972                                   NULL,
973                                   NULL,
974                                   NULL);
975
976   job_uri = g_strdup_printf ("ipp://localhost/jobs/%d", data->job_id);
977   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
978                                    "job-uri", NULL, job_uri);
979   g_free (job_uri);
980
981   cups_request_execute (data->print_backend,
982                         request,
983                         (GtkPrintCupsResponseCallbackFunc) cups_request_job_info_cb,
984                         data,
985                         NULL);
986 }
987
988 static gboolean
989 cups_job_info_poll_timeout (gpointer user_data)
990 {
991   CupsJobPollData *data = user_data;
992   
993   if (data->job == NULL)
994     cups_job_poll_data_free (data);
995   else
996     cups_request_job_info (data);
997   
998   return FALSE;
999 }
1000
1001 static void
1002 cups_begin_polling_info (GtkPrintBackendCups *print_backend,
1003                          GtkPrintJob         *job,
1004                          gint                 job_id)
1005 {
1006   CupsJobPollData *data;
1007
1008   data = g_new0 (CupsJobPollData, 1);
1009
1010   data->print_backend = print_backend;
1011   data->job = job;
1012   data->job_id = job_id;
1013   data->counter = 0;
1014
1015   g_object_weak_ref (G_OBJECT (job), job_object_died, data);
1016
1017   cups_request_job_info (data);
1018 }
1019
1020 static void
1021 mark_printer_inactive (GtkPrinter      *printer, 
1022                        GtkPrintBackend *backend)
1023 {
1024   gtk_printer_set_is_active (printer, FALSE);
1025   g_signal_emit_by_name (backend, "printer-removed", printer);
1026 }
1027
1028 static gint
1029 find_printer (GtkPrinter  *printer, 
1030               const gchar *find_name)
1031 {
1032   const gchar *printer_name;
1033
1034   printer_name = gtk_printer_get_name (printer);
1035   return g_ascii_strcasecmp (printer_name, find_name);
1036 }
1037
1038 static void
1039 cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
1040                               GtkCupsResult       *result,
1041                               gpointer             user_data)
1042 {
1043   GtkPrintBackend *backend = GTK_PRINT_BACKEND (cups_backend);
1044   ipp_attribute_t *attr;
1045   ipp_t *response;
1046   gboolean list_has_changed;
1047   GList *removed_printer_checklist;
1048
1049   GDK_THREADS_ENTER ();
1050
1051   list_has_changed = FALSE;
1052
1053   GTK_NOTE (PRINTING,
1054             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1055
1056   cups_backend->list_printers_pending = FALSE;
1057
1058   if (gtk_cups_result_is_error (result))
1059     {
1060       GTK_NOTE (PRINTING, 
1061                 g_warning ("CUPS Backend: Error getting printer list: %s", 
1062                            gtk_cups_result_get_error_string (result)));
1063
1064       goto done;
1065     }
1066   
1067   /* Gather the names of the printers in the current queue
1068    * so we may check to see if they were removed 
1069    */
1070   removed_printer_checklist = gtk_print_backend_get_printer_list (backend);
1071                                                                   
1072   response = gtk_cups_result_get_response (result);
1073
1074   for (attr = response->attrs; attr != NULL; attr = attr->next)
1075     {
1076       GtkPrinter *printer;
1077       const gchar *printer_name = NULL;
1078       const gchar *printer_uri = NULL;
1079       const gchar *member_uris = NULL;
1080       const gchar *location = NULL;
1081       const gchar *description = NULL;
1082       const gchar *state_msg = NULL;
1083       gint state = 0;
1084       gint job_count = 0;
1085       gboolean status_changed = FALSE;
1086       GList *node;
1087       gint i,j;
1088       const gchar *reason_msg = NULL;
1089       gchar *reason_msg_desc = NULL;
1090       gchar *tmp_msg = NULL;
1091       gchar *tmp_msg2 = NULL;
1092       gint printer_state_reason_level = 0; /* 0 - none, 1 - report, 2 - warning, 3 - error */
1093       gboolean interested_in = FALSE;
1094       gboolean found = FALSE;
1095       static const char * const reasons[] =     /* Reasons we're interested in */
1096         {
1097           "toner-low",
1098           "toner-empty",
1099           "developer-low",
1100           "developer-empty",
1101           "marker-supply-low",
1102           "marker-supply-empty",
1103           "cover-open",
1104           "door-open",
1105           "media-low",
1106           "media-empty",
1107           "offline",
1108           "connecting-to-device",
1109           "other"
1110         };
1111       static const char * reasons_descs[] =
1112         {
1113           N_("Printer '%s' is low on toner."),
1114           N_("Printer '%s' has no toner left."),
1115           /* Translators: "Developer" like on photo development context */
1116           N_("Printer '%s' is low on developer."),
1117           /* Translators: "Developer" like on photo development context */
1118           N_("Printer '%s' is out of developer."),
1119           /* Translators: "marker" is one color bin of the printer */
1120           N_("Printer '%s' is low on at least one marker supply."),
1121           /* Translators: "marker" is one color bin of the printer */
1122           N_("Printer '%s' is out of at least one marker supply."),
1123           N_("The cover is open on printer '%s'."),
1124           N_("The door is open on printer '%s'."),
1125           N_("Printer '%s' is low on paper."),
1126           N_("Printer '%s' is out of paper."),
1127           N_("Printer '%s' is currently off-line."),
1128           N_("Printer '%s' may not be connected."),
1129           N_("There is a problem on printer '%s'.")
1130         };
1131       gboolean is_paused = FALSE;
1132       gboolean is_accepting_jobs = TRUE;
1133       
1134       /* Skip leading attributes until we hit a printer...
1135        */
1136       while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
1137         attr = attr->next;
1138
1139       if (attr == NULL)
1140         break;
1141
1142       while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
1143       {
1144         if (strcmp (attr->name, "printer-name") == 0 &&
1145             attr->value_tag == IPP_TAG_NAME)
1146           printer_name = attr->values[0].string.text;
1147         else if (strcmp (attr->name, "printer-uri-supported") == 0 &&
1148                  attr->value_tag == IPP_TAG_URI)
1149           printer_uri = attr->values[0].string.text;
1150         else if (strcmp (attr->name, "member-uris") == 0 &&
1151                  attr->value_tag == IPP_TAG_URI)
1152           member_uris = attr->values[0].string.text;
1153         else if (strcmp (attr->name, "printer-location") == 0)
1154           location = attr->values[0].string.text;
1155         else if (strcmp (attr->name, "printer-info") == 0)
1156           description = attr->values[0].string.text;
1157         else if (strcmp (attr->name, "printer-state-message") == 0)
1158           state_msg = attr->values[0].string.text;
1159         else if (strcmp (attr->name, "printer-state-reasons") == 0)
1160           /* Store most important reason to reason_msg and set
1161              its importance at printer_state_reason_level */
1162           {
1163             for (i = 0; i < attr->num_values; i++)
1164               {
1165                 if (strcmp (attr->values[i].string.text, "none") != 0)
1166                   {
1167                     /* Sets is_paused flag for paused printer. */
1168                     if (strcmp (attr->values[i].string.text, "paused") == 0)
1169                       {
1170                         is_paused = TRUE;
1171                       }
1172
1173                     interested_in = FALSE;
1174                     for (j = 0; j < G_N_ELEMENTS (reasons); j++)
1175                         if (strncmp (attr->values[i].string.text, reasons[j], strlen (reasons[j])) == 0)
1176                           {
1177                             interested_in = TRUE;
1178                             break;
1179                           }
1180
1181                     if (interested_in)
1182                       {
1183                         if (g_str_has_suffix (attr->values[i].string.text, "-report"))
1184                           {
1185                             if (printer_state_reason_level <= 1)
1186                               {
1187                                 reason_msg = attr->values[i].string.text;
1188                                 printer_state_reason_level = 1;
1189                               }
1190                           }
1191                         else if (g_str_has_suffix (attr->values[i].string.text, "-warning"))
1192                           {
1193                             if (printer_state_reason_level <= 2)
1194                               {
1195                                 reason_msg = attr->values[i].string.text;
1196                                 printer_state_reason_level = 2;
1197                               }
1198                           }
1199                         else  /* It is error in the case of no suffix. */
1200                           {
1201                             reason_msg = attr->values[i].string.text;
1202                             printer_state_reason_level = 3;
1203                           }
1204                       }
1205                   }
1206               }
1207           }
1208         else if (strcmp (attr->name, "printer-state") == 0)
1209           state = attr->values[0].integer;
1210         else if (strcmp (attr->name, "queued-job-count") == 0)
1211           job_count = attr->values[0].integer;
1212         else if (strcmp (attr->name, "printer-is-accepting-jobs") == 0)
1213           {
1214             if (attr->values[0].boolean == 1)
1215               is_accepting_jobs = TRUE;
1216             else
1217               is_accepting_jobs = FALSE;
1218           }
1219         else if (strcmp (attr->name, "job-sheets-supported") == 0)
1220           {
1221             if (cups_backend->covers == NULL)
1222               {
1223                 cups_backend->number_of_covers = attr->num_values;
1224                 cups_backend->covers = g_new (char *, cups_backend->number_of_covers + 1);
1225
1226                 for (i = 0; i < cups_backend->number_of_covers; i++)
1227                   cups_backend->covers[i] = g_strdup (attr->values[i].string.text);
1228
1229                 cups_backend->covers[cups_backend->number_of_covers] = NULL;
1230               }
1231           }
1232         else if (strcmp (attr->name, "job-sheets-default") == 0)
1233           {
1234             if ( (cups_backend->default_cover_before == NULL) && (cups_backend->default_cover_after == NULL))
1235               {
1236                 if (attr->num_values == 2)
1237                   {
1238                     cups_backend->default_cover_before = g_strdup (attr->values[0].string.text);
1239                     cups_backend->default_cover_after = g_strdup (attr->values[1].string.text);
1240                   }
1241               }
1242           }
1243         else
1244           {
1245             GTK_NOTE (PRINTING,
1246                       g_print ("CUPS Backend: Attribute %s ignored", attr->name));
1247           }
1248
1249         attr = attr->next;
1250       }
1251
1252       if (printer_name == NULL ||
1253           (printer_uri == NULL && member_uris == NULL))
1254       {
1255         if (attr == NULL)
1256           break;
1257         else
1258           continue;
1259       }
1260    
1261       /* remove name from checklist if it was found */
1262       node = g_list_find_custom (removed_printer_checklist, printer_name, (GCompareFunc) find_printer);
1263       removed_printer_checklist = g_list_delete_link (removed_printer_checklist, node);
1264  
1265       printer = gtk_print_backend_find_printer (backend, printer_name);
1266       if (!printer)
1267         {
1268           GtkPrinterCups *cups_printer;
1269           char uri[HTTP_MAX_URI];       /* Printer URI */
1270           char method[HTTP_MAX_URI];    /* Method/scheme name */
1271           char username[HTTP_MAX_URI];  /* Username:password */
1272           char hostname[HTTP_MAX_URI];  /* Hostname */
1273           char resource[HTTP_MAX_URI];  /* Resource name */
1274           int  port;                    /* Port number */
1275           
1276           list_has_changed = TRUE;
1277           cups_printer = gtk_printer_cups_new (printer_name, backend);
1278
1279           cups_printer->device_uri = g_strdup_printf ("/printers/%s", printer_name);
1280
1281           /* Check to see if we are looking at a class */
1282           if (member_uris)
1283             {
1284               cups_printer->printer_uri = g_strdup (member_uris);
1285               /* TODO if member_uris is a class we need to recursivly find a printer */
1286               GTK_NOTE (PRINTING,
1287                         g_print ("CUPS Backend: Found class with printer %s\n", member_uris));
1288             }
1289           else
1290             {
1291               cups_printer->printer_uri = g_strdup (printer_uri);
1292               GTK_NOTE (PRINTING,
1293                         g_print ("CUPS Backend: Found printer %s\n", printer_uri));
1294             }
1295
1296 #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1
1297           httpSeparateURI (HTTP_URI_CODING_ALL, cups_printer->printer_uri, 
1298                            method, sizeof (method), 
1299                            username, sizeof (username),
1300                            hostname, sizeof (hostname),
1301                            &port, 
1302                            resource, sizeof (resource));
1303
1304 #else
1305           httpSeparate (cups_printer->printer_uri, 
1306                         method, 
1307                         username, 
1308                         hostname,
1309                         &port, 
1310                         resource);
1311 #endif
1312
1313           if (strncmp (resource, "/printers/", 10) == 0)
1314             {
1315               cups_printer->ppd_name = g_strdup (resource + 10);
1316               GTK_NOTE (PRINTING,
1317                         g_print ("CUPS Backend: Setting ppd name '%s' for printer/class '%s'\n", cups_printer->ppd_name, printer_name));
1318             }
1319
1320           gethostname (uri, sizeof (uri));
1321           if (strcasecmp (uri, hostname) == 0)
1322             strcpy (hostname, "localhost");
1323
1324           cups_printer->hostname = g_strdup (hostname);
1325           cups_printer->port = port;
1326           
1327           printer = GTK_PRINTER (cups_printer);
1328           
1329           if (cups_backend->default_printer != NULL &&
1330               strcmp (cups_backend->default_printer, gtk_printer_get_name (printer)) == 0)
1331             gtk_printer_set_is_default (printer, TRUE);
1332
1333           
1334           gtk_print_backend_add_printer (backend, printer);
1335         }
1336       else
1337         g_object_ref (printer);
1338
1339       gtk_printer_set_is_paused (printer, is_paused);
1340       gtk_printer_set_is_accepting_jobs (printer, is_accepting_jobs);
1341
1342       if (!gtk_printer_is_active (printer))
1343         {
1344           gtk_printer_set_is_active (printer, TRUE);
1345           gtk_printer_set_is_new (printer, TRUE);
1346           list_has_changed = TRUE;
1347         }
1348
1349       if (gtk_printer_is_new (printer))
1350         {
1351           g_signal_emit_by_name (backend, "printer-added", printer);
1352
1353           gtk_printer_set_is_new (printer, FALSE);
1354         }
1355
1356 #if 0
1357       /* Getting printer info with separate requests overwhelms cups
1358        * when the printer list has more than a handful of printers.
1359        */
1360       cups_request_printer_info (cups_backend, gtk_printer_get_name (printer));
1361 #endif
1362
1363       GTK_PRINTER_CUPS (printer)->state = state;
1364       status_changed = gtk_printer_set_job_count (printer, job_count);
1365       status_changed |= gtk_printer_set_location (printer, location);
1366       status_changed |= gtk_printer_set_description (printer, description);
1367
1368       if (state_msg != NULL && strlen (state_msg) == 0)
1369         {
1370           if (is_paused && !is_accepting_jobs)
1371                   /* Translators: this is a printer status. */
1372             tmp_msg2 = g_strdup ( N_("Paused ; Rejecting Jobs"));
1373           if (is_paused && is_accepting_jobs)
1374                   /* Translators: this is a printer status. */
1375             tmp_msg2 = g_strdup ( N_("Paused"));
1376           if (!is_paused && !is_accepting_jobs)
1377                   /* Translators: this is a printer status. */
1378             tmp_msg2 = g_strdup ( N_("Rejecting Jobs"));
1379
1380           if (tmp_msg2 != NULL)
1381             state_msg = tmp_msg2;
1382         }
1383
1384       /* Set description of the reason and combine it with printer-state-message. */
1385       if ( (reason_msg != NULL))
1386         {
1387           for (i = 0; i < G_N_ELEMENTS (reasons); i++)
1388             {
1389               if (strncmp (reason_msg, reasons[i], strlen (reasons[i])) == 0)
1390                 {
1391                   reason_msg_desc = g_strdup_printf (reasons_descs[i], printer_name);
1392                   found = TRUE;
1393                   break;
1394                 }
1395             }
1396
1397           if (!found)
1398             printer_state_reason_level = 0;
1399
1400           if (printer_state_reason_level >= 2)
1401             {
1402               if (strlen (state_msg) == 0)
1403                 state_msg = reason_msg_desc;
1404               else
1405                 {
1406                   tmp_msg = g_strjoin (" ; ", state_msg, reason_msg_desc, NULL);
1407                   state_msg = tmp_msg;
1408                 }
1409             }
1410         }
1411
1412       status_changed |= gtk_printer_set_state_message (printer, state_msg);
1413       status_changed |= gtk_printer_set_is_accepting_jobs (printer, is_accepting_jobs);
1414
1415       if (tmp_msg != NULL)
1416         g_free (tmp_msg);
1417
1418       if (tmp_msg2 != NULL)
1419         g_free (tmp_msg2);
1420
1421       if (reason_msg_desc != NULL)
1422         g_free (reason_msg_desc);
1423
1424       /* Set printer icon according to importance
1425          (none, report, warning, error - report is omitted). */
1426       if (printer_state_reason_level == 3)
1427         gtk_printer_set_icon_name (printer, "gtk-print-error");
1428       else if (printer_state_reason_level == 2)
1429         gtk_printer_set_icon_name (printer, "gtk-print-warning");
1430       else if (gtk_printer_is_paused (printer))
1431         gtk_printer_set_icon_name (printer, "gtk-print-paused");
1432       else
1433         gtk_printer_set_icon_name (printer, "gtk-print");
1434
1435       if (status_changed)
1436         g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
1437                                "printer-status-changed", printer);
1438
1439       /* The ref is held by GtkPrintBackend, in add_printer() */
1440       g_object_unref (printer);
1441       
1442       if (attr == NULL)
1443         break;
1444     }
1445
1446   /* look at the removed printers checklist and mark any printer
1447      as inactive if it is in the list, emitting a printer_removed signal */
1448   if (removed_printer_checklist != NULL)
1449     {
1450       g_list_foreach (removed_printer_checklist, (GFunc) mark_printer_inactive, backend);
1451       g_list_free (removed_printer_checklist);
1452       list_has_changed = TRUE;
1453     }
1454   
1455 done:
1456   if (list_has_changed)
1457     g_signal_emit_by_name (backend, "printer-list-changed");
1458   
1459   gtk_print_backend_set_list_done (backend);
1460
1461   GDK_THREADS_LEAVE ();
1462 }
1463
1464 static gboolean
1465 cups_request_printer_list (GtkPrintBackendCups *cups_backend)
1466 {
1467   GtkCupsRequest *request;
1468   static const char * const pattrs[] =  /* Attributes we're interested in */
1469     {
1470       "printer-name",
1471       "printer-uri-supported",
1472       "member-uris",
1473       "printer-location",
1474       "printer-info",
1475       "printer-state-message",
1476       "printer-state-reasons",
1477       "printer-state",
1478       "queued-job-count",
1479       "printer-is-accepting-jobs",
1480       "job-sheets-supported",
1481       "job-sheets-default"
1482     };
1483  
1484   if (cups_backend->list_printers_pending ||
1485       !cups_backend->got_default_printer)
1486     return TRUE;
1487
1488   cups_backend->list_printers_pending = TRUE;
1489
1490   request = gtk_cups_request_new (NULL,
1491                                   GTK_CUPS_POST,
1492                                   CUPS_GET_PRINTERS,
1493                                   NULL,
1494                                   NULL,
1495                                   NULL);
1496
1497   gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1498                                     "requested-attributes", G_N_ELEMENTS (pattrs),
1499                                     NULL, pattrs);
1500
1501   cups_request_execute (cups_backend,
1502                         request,
1503                         (GtkPrintCupsResponseCallbackFunc) cups_request_printer_list_cb,
1504                         request,
1505                         NULL);
1506
1507   return TRUE;
1508 }
1509
1510 static void
1511 cups_get_printer_list (GtkPrintBackend *backend)
1512 {
1513   GtkPrintBackendCups *cups_backend;
1514
1515   cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
1516   if (cups_backend->list_printers_poll == 0)
1517     {
1518       cups_request_printer_list (cups_backend);
1519       cups_backend->list_printers_poll = gdk_threads_add_timeout_seconds (3,
1520                                                         (GSourceFunc) cups_request_printer_list,
1521                                                         backend);
1522     }
1523 }
1524
1525 typedef struct {
1526   GtkPrinterCups *printer;
1527   GIOChannel *ppd_io;
1528   http_t *http;
1529 } GetPPDData;
1530
1531 static void
1532 get_ppd_data_free (GetPPDData *data)
1533 {
1534   GTK_NOTE (PRINTING,
1535             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1536   httpClose (data->http);
1537   g_io_channel_unref (data->ppd_io);
1538   g_object_unref (data->printer);
1539   g_free (data);
1540 }
1541
1542 static void
1543 cups_request_ppd_cb (GtkPrintBackendCups *print_backend,
1544                      GtkCupsResult       *result,
1545                      GetPPDData          *data)
1546 {
1547   ipp_t *response;
1548   GtkPrinter *printer;
1549
1550   GDK_THREADS_ENTER ();
1551
1552   GTK_NOTE (PRINTING,
1553             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1554
1555   printer = GTK_PRINTER (data->printer);
1556   GTK_PRINTER_CUPS (printer)->reading_ppd = FALSE;
1557
1558   if (gtk_cups_result_is_error (result))
1559     {
1560       gboolean success = FALSE;
1561
1562       /* if we get a 404 then it is just a raw printer without a ppd
1563          and not an error */
1564       if ((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
1565           (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))
1566         {
1567           gtk_printer_set_has_details (printer, TRUE);
1568           success = TRUE;
1569         } 
1570         
1571       g_signal_emit_by_name (printer, "details-acquired", success);
1572       goto done;
1573     }
1574
1575   response = gtk_cups_result_get_response (result);
1576
1577   /* let ppdOpenFd take over the ownership of the open file */
1578   g_io_channel_seek_position (data->ppd_io, 0, G_SEEK_SET, NULL);
1579   data->printer->ppd_file = ppdOpenFd (dup (g_io_channel_unix_get_fd (data->ppd_io)));
1580
1581   ppdMarkDefaults (data->printer->ppd_file);
1582   
1583   gtk_printer_set_has_details (printer, TRUE);
1584   g_signal_emit_by_name (printer, "details-acquired", TRUE);
1585
1586 done:
1587   GDK_THREADS_LEAVE ();
1588 }
1589
1590 static void
1591 cups_request_ppd (GtkPrinter *printer)
1592 {
1593   GError *error;
1594   GtkPrintBackend *print_backend;
1595   GtkPrinterCups *cups_printer;
1596   GtkCupsRequest *request;
1597   char *ppd_filename;
1598   gchar *resource;
1599   http_t *http;
1600   GetPPDData *data;
1601   int fd;
1602
1603   cups_printer = GTK_PRINTER_CUPS (printer);
1604
1605   error = NULL;
1606
1607   GTK_NOTE (PRINTING,
1608             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1609
1610   http = httpConnectEncrypt (cups_printer->hostname, 
1611                              cups_printer->port,
1612                              cupsEncryption ());
1613   
1614   data = g_new0 (GetPPDData, 1);
1615
1616   fd = g_file_open_tmp ("gtkprint_ppd_XXXXXX", 
1617                         &ppd_filename, 
1618                         &error);
1619
1620 #ifdef G_ENABLE_DEBUG 
1621   /* If we are debugging printing don't delete the tmp files */
1622   if (!(gtk_debug_flags & GTK_DEBUG_PRINTING))
1623     unlink (ppd_filename);
1624 #else
1625   unlink (ppd_filename);
1626 #endif /* G_ENABLE_DEBUG */
1627
1628   if (error != NULL)
1629     {
1630       GTK_NOTE (PRINTING, 
1631                 g_warning ("CUPS Backend: Failed to create temp file, %s\n", 
1632                            error->message));
1633       g_error_free (error);
1634       httpClose (http);
1635       g_free (ppd_filename);
1636       g_free (data);
1637
1638       g_signal_emit_by_name (printer, "details-acquired", FALSE);
1639       return;
1640     }
1641     
1642   data->http = http;
1643   fchmod (fd, S_IRUSR | S_IWUSR);
1644   data->ppd_io = g_io_channel_unix_new (fd);
1645   g_io_channel_set_encoding (data->ppd_io, NULL, NULL);
1646   g_io_channel_set_close_on_unref (data->ppd_io, TRUE);
1647
1648   data->printer = g_object_ref (printer);
1649
1650   resource = g_strdup_printf ("/printers/%s.ppd", 
1651                               gtk_printer_cups_get_ppd_name (GTK_PRINTER_CUPS (printer)));
1652
1653   request = gtk_cups_request_new (data->http,
1654                                   GTK_CUPS_GET,
1655                                   0,
1656                                   data->ppd_io,
1657                                   cups_printer->hostname,
1658                                   resource);
1659
1660   GTK_NOTE (PRINTING,
1661             g_print ("CUPS Backend: Requesting resource %s to be written to temp file %s\n", resource, ppd_filename));
1662
1663   g_free (resource);
1664   g_free (ppd_filename);
1665
1666   cups_printer->reading_ppd = TRUE;
1667
1668   print_backend = gtk_printer_get_backend (printer);
1669
1670   cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
1671                         request,
1672                         (GtkPrintCupsResponseCallbackFunc) cups_request_ppd_cb,
1673                         data,
1674                         (GDestroyNotify)get_ppd_data_free);
1675 }
1676
1677 /* Ordering matters for default preference */
1678 static const char *lpoptions_locations[] = {
1679   "/etc/cups/lpoptions",
1680   ".lpoptions", 
1681   ".cups/lpoptions"
1682 };
1683
1684 static void
1685 cups_parse_user_default_printer (const char  *filename,
1686                                  char       **printer_name)
1687 {
1688   FILE *fp;
1689   char line[1024], *lineptr, *defname = NULL;
1690   
1691   if ((fp = g_fopen (filename, "r")) == NULL)
1692     return;
1693
1694   while (fgets (line, sizeof (line), fp) != NULL)
1695     {
1696       if (strncasecmp (line, "default", 7) != 0 || !isspace (line[7]))
1697         continue;
1698
1699       lineptr = line + 8;
1700       while (isspace (*lineptr))
1701         lineptr++;
1702
1703       if (!*lineptr)
1704         continue;
1705
1706       defname = lineptr;
1707       while (!isspace (*lineptr) && *lineptr && *lineptr != '/')
1708         lineptr++;
1709
1710       *lineptr = '\0';
1711
1712       if (*printer_name != NULL)
1713         g_free (*printer_name);
1714
1715       *printer_name = g_strdup (defname);
1716     }
1717
1718   fclose (fp);
1719 }
1720
1721 static void
1722 cups_get_user_default_printer (char **printer_name)
1723 {
1724   int i;
1725
1726   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
1727     {
1728       if (g_path_is_absolute (lpoptions_locations[i]))
1729         {
1730           cups_parse_user_default_printer (lpoptions_locations[i],
1731                                            printer_name);
1732         }
1733       else 
1734         {
1735           char *filename;
1736
1737           filename = g_build_filename (g_get_home_dir (), 
1738                                        lpoptions_locations[i], NULL);
1739           cups_parse_user_default_printer (filename, printer_name);
1740           g_free (filename);
1741         }
1742     }
1743 }
1744
1745 static int
1746 cups_parse_user_options (const char     *filename,
1747                          const char     *printer_name,
1748                          int             num_options,
1749                          cups_option_t **options)
1750 {
1751   FILE *fp;
1752   gchar line[1024], *lineptr, *name;
1753
1754   if ((fp = g_fopen (filename, "r")) == NULL)
1755     return num_options;
1756
1757   while (fgets (line, sizeof (line), fp) != NULL)
1758     {
1759       if (strncasecmp (line, "dest", 4) == 0 && isspace (line[4]))
1760         lineptr = line + 4;
1761       else if (strncasecmp (line, "default", 7) == 0 && isspace (line[7]))
1762         lineptr = line + 7;
1763       else
1764         continue;
1765
1766       /* Skip leading whitespace */
1767       while (isspace (*lineptr))
1768         lineptr++;
1769
1770       if (!*lineptr)
1771         continue;
1772
1773       /* NUL-terminate the name, stripping the instance name */
1774       name = lineptr;
1775       while (!isspace (*lineptr) && *lineptr)
1776         {
1777           if (*lineptr == '/')
1778             *lineptr = '\0';
1779           lineptr++;
1780         }
1781
1782       if (!*lineptr)
1783         continue;
1784
1785       *lineptr++ = '\0';
1786
1787       if (strncasecmp (name, printer_name, strlen (printer_name)) != 0)
1788           continue;
1789
1790       /* We found our printer, parse the options */
1791       num_options = cupsParseOptions (lineptr, num_options, options);
1792     }
1793
1794   fclose (fp);
1795
1796   return num_options;
1797 }
1798
1799 static int
1800 cups_get_user_options (const char     *printer_name,
1801                        int             num_options,
1802                        cups_option_t **options)
1803 {
1804   int i;
1805
1806   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
1807     {
1808       if (g_path_is_absolute (lpoptions_locations[i]))
1809         { 
1810            num_options = cups_parse_user_options (lpoptions_locations[i],
1811                                                   printer_name,
1812                                                   num_options,
1813                                                   options);
1814         }
1815       else
1816         {
1817           char *filename;
1818
1819           filename = g_build_filename (g_get_home_dir (), 
1820                                        lpoptions_locations[i], NULL);
1821           num_options = cups_parse_user_options (filename, printer_name,
1822                                                  num_options, options);
1823           g_free (filename);
1824         }
1825     }
1826
1827   return num_options;
1828 }
1829
1830 /* This function requests default printer from a CUPS server in regular intervals.
1831  * In the case of unreachable CUPS server the request is repeated later.
1832  * The default printer is not requested in the case of previous success.
1833  */
1834 static void
1835 cups_get_default_printer (GtkPrintBackendCups *backend)
1836 {
1837   GtkPrintBackendCups *cups_backend;
1838
1839   cups_backend = backend;
1840
1841   cups_backend->default_printer_connection_test = gtk_cups_connection_test_new (NULL);
1842   if (cups_backend->default_printer_poll == 0)
1843     {
1844       if (cups_request_default_printer (cups_backend))
1845         cups_backend->default_printer_poll = gdk_threads_add_timeout_seconds (1,
1846                                                                               (GSourceFunc) cups_request_default_printer,
1847                                                                               backend);
1848     }
1849 }
1850
1851 static void
1852 cups_request_default_printer_cb (GtkPrintBackendCups *print_backend,
1853                                  GtkCupsResult       *result,
1854                                  gpointer             user_data)
1855 {
1856   ipp_t *response;
1857   ipp_attribute_t *attr;
1858
1859   GDK_THREADS_ENTER ();
1860
1861   response = gtk_cups_result_get_response (result);
1862   
1863   if ((attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME)) != NULL)
1864     print_backend->default_printer = g_strdup (attr->values[0].string.text);
1865
1866   print_backend->got_default_printer = TRUE;
1867
1868   /* Make sure to kick off get_printers if we are polling it, 
1869    * as we could have blocked this reading the default printer 
1870    */
1871   if (print_backend->list_printers_poll != 0)
1872     cups_request_printer_list (print_backend);
1873
1874   GDK_THREADS_LEAVE ();
1875 }
1876
1877 static gboolean
1878 cups_request_default_printer (GtkPrintBackendCups *print_backend)
1879 {
1880   GtkCupsRequest *request;
1881   const char *str;
1882   char *name = NULL;
1883
1884   if (!gtk_cups_connection_test_is_server_available (print_backend->default_printer_connection_test))
1885     return TRUE;
1886
1887   gtk_cups_connection_test_free (print_backend->default_printer_connection_test);
1888   print_backend->default_printer_connection_test = NULL;
1889
1890   if ((str = g_getenv ("LPDEST")) != NULL)
1891     {
1892       print_backend->default_printer = g_strdup (str);
1893       print_backend->got_default_printer = TRUE;
1894       return FALSE;
1895     }
1896   else if ((str = g_getenv ("PRINTER")) != NULL &&
1897            strcmp (str, "lp") != 0)
1898     {
1899       print_backend->default_printer = g_strdup (str);
1900       print_backend->got_default_printer = TRUE;
1901       return FALSE;
1902     }
1903   
1904   /* Figure out user setting for default printer */  
1905   cups_get_user_default_printer (&name);
1906   if (name != NULL)
1907     {
1908        print_backend->default_printer = name;
1909        print_backend->got_default_printer = TRUE;
1910        return FALSE;
1911     }
1912
1913   request = gtk_cups_request_new (NULL,
1914                                   GTK_CUPS_POST,
1915                                   CUPS_GET_DEFAULT,
1916                                   NULL,
1917                                   NULL,
1918                                   NULL);
1919   
1920   cups_request_execute (print_backend,
1921                         request,
1922                         (GtkPrintCupsResponseCallbackFunc) cups_request_default_printer_cb,
1923                         g_object_ref (print_backend),
1924                         g_object_unref);
1925
1926   return FALSE;
1927 }
1928
1929 static void
1930 cups_printer_request_details (GtkPrinter *printer)
1931 {
1932   GtkPrinterCups *cups_printer;
1933
1934   cups_printer = GTK_PRINTER_CUPS (printer);
1935   if (!cups_printer->reading_ppd && 
1936       gtk_printer_cups_get_ppd (cups_printer) == NULL)
1937     cups_request_ppd (printer); 
1938 }
1939
1940 static char *
1941 ppd_text_to_utf8 (ppd_file_t *ppd_file, 
1942                   const char *text)
1943 {
1944   const char *encoding = NULL;
1945   char *res;
1946   
1947   if (g_ascii_strcasecmp (ppd_file->lang_encoding, "UTF-8") == 0)
1948     {
1949       return g_strdup (text);
1950     }
1951   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin1") == 0)
1952     {
1953       encoding = "ISO-8859-1";
1954     }
1955   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin2") == 0)
1956     {
1957       encoding = "ISO-8859-2";
1958     }
1959   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin5") == 0)
1960     {
1961       encoding = "ISO-8859-5";
1962     }
1963   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "JIS83-RKSJ") == 0)
1964     {
1965       encoding = "SHIFT-JIS";
1966     }
1967   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "MacStandard") == 0)
1968     {
1969       encoding = "MACINTOSH";
1970     }
1971   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "WindowsANSI") == 0)
1972     {
1973       encoding = "WINDOWS-1252";
1974     }
1975   else 
1976     {
1977       /* Fallback, try iso-8859-1... */
1978       encoding = "ISO-8859-1";
1979     }
1980
1981   res = g_convert (text, -1, "UTF-8", encoding, NULL, NULL, NULL);
1982
1983   if (res == NULL)
1984     {
1985       GTK_NOTE (PRINTING,
1986                 g_warning ("CUPS Backend: Unable to convert PPD text\n"));
1987       res = g_strdup ("???");
1988     }
1989   
1990   return res;
1991 }
1992
1993 /* TODO: Add more translations for common settings here */
1994
1995 static const struct {
1996   const char *keyword;
1997   const char *translation;
1998 } cups_option_translations[] = {
1999   { "Duplex", N_("Two Sided") },
2000   { "MediaType", N_("Paper Type") },
2001   { "InputSlot", N_("Paper Source") },
2002   { "OutputBin", N_("Output Tray") },
2003 };
2004
2005
2006 static const struct {
2007   const char *keyword;
2008   const char *choice;
2009   const char *translation;
2010 } cups_choice_translations[] = {
2011   { "Duplex", "None", N_("One Sided") },
2012   /* Translators: this is an option of "Paper Source" */
2013   { "InputSlot", "Auto", N_("Auto Select") },
2014   /* Translators: this is an option of "Paper Source" */
2015   { "InputSlot", "AutoSelect", N_("Auto Select") },
2016   /* Translators: this is an option of "Paper Source" */
2017   { "InputSlot", "Default", N_("Printer Default") },
2018   /* Translators: this is an option of "Paper Source" */
2019   { "InputSlot", "None", N_("Printer Default") },
2020   /* Translators: this is an option of "Paper Source" */
2021   { "InputSlot", "PrinterDefault", N_("Printer Default") },
2022   /* Translators: this is an option of "Paper Source" */
2023   { "InputSlot", "Unspecified", N_("Auto Select") },
2024 };
2025
2026 static const struct {
2027   const char *ppd_keyword;
2028   const char *name;
2029 } option_names[] = {
2030   {"Duplex", "gtk-duplex" },
2031   {"MediaType", "gtk-paper-type"},
2032   {"InputSlot", "gtk-paper-source"},
2033   {"OutputBin", "gtk-output-tray"},
2034 };
2035
2036 /* keep sorted when changing */
2037 static const char *color_option_whitelist[] = {
2038   "BRColorEnhancement",
2039   "BRColorMatching",
2040   "BRColorMatching",
2041   "BRColorMode",
2042   "BRGammaValue",
2043   "BRImprovedGray",
2044   "BlackSubstitution",
2045   "ColorModel",
2046   "HPCMYKInks",
2047   "HPCSGraphics",
2048   "HPCSImages",
2049   "HPCSText",
2050   "HPColorSmart",
2051   "RPSBlackMode",
2052   "RPSBlackOverPrint",
2053   "Rcmyksimulation",
2054 };
2055
2056 /* keep sorted when changing */
2057 static const char *color_group_whitelist[] = {
2058   "ColorPage",
2059   "FPColorWise1",
2060   "FPColorWise2",
2061   "FPColorWise3",
2062   "FPColorWise4",
2063   "FPColorWise5",
2064   "HPColorOptionsPanel",
2065 };
2066   
2067 /* keep sorted when changing */
2068 static const char *image_quality_option_whitelist[] = {
2069   "BRDocument",
2070   "BRHalfTonePattern",
2071   "BRNormalPrt",
2072   "BRPrintQuality",
2073   "BitsPerPixel",
2074   "Darkness",
2075   "Dithering",
2076   "EconoMode",
2077   "Economode",
2078   "HPEconoMode",
2079   "HPEdgeControl",
2080   "HPGraphicsHalftone",
2081   "HPHalftone",
2082   "HPLJDensity",
2083   "HPPhotoHalftone",
2084   "OutputMode",
2085   "REt",
2086   "RPSBitsPerPixel",
2087   "RPSDitherType",
2088   "Resolution",
2089   "ScreenLock",
2090   "Smoothing",
2091   "TonerSaveMode",
2092   "UCRGCRForImage",
2093 };
2094
2095 /* keep sorted when changing */
2096 static const char *image_quality_group_whitelist[] = {
2097   "FPImageQuality1",
2098   "FPImageQuality2",
2099   "FPImageQuality3",
2100   "ImageQualityPage",
2101 };
2102
2103 /* keep sorted when changing */
2104 static const char * finishing_option_whitelist[] = {
2105   "BindColor",
2106   "BindEdge",
2107   "BindType",
2108   "BindWhen",
2109   "Booklet",
2110   "FoldType",
2111   "FoldWhen",
2112   "HPStaplerOptions",
2113   "Jog",
2114   "Slipsheet",
2115   "Sorter",
2116   "StapleLocation",
2117   "StapleOrientation",
2118   "StapleWhen",
2119   "StapleX",
2120   "StapleY",
2121 };
2122
2123 /* keep sorted when changing */
2124 static const char *finishing_group_whitelist[] = {
2125   "FPFinishing1",
2126   "FPFinishing2",
2127   "FPFinishing3",
2128   "FPFinishing4",
2129   "FinishingPage",
2130   "HPFinishingPanel",
2131 };
2132
2133 /* keep sorted when changing */
2134 static const char *cups_option_blacklist[] = {
2135   "Collate",
2136   "Copies", 
2137   "OutputOrder",
2138   "PageRegion",
2139   "PageSize",
2140 };
2141
2142 static char *
2143 get_option_text (ppd_file_t   *ppd_file, 
2144                  ppd_option_t *option)
2145 {
2146   int i;
2147   char *utf8;
2148   
2149   for (i = 0; i < G_N_ELEMENTS (cups_option_translations); i++)
2150     {
2151       if (strcmp (cups_option_translations[i].keyword, option->keyword) == 0)
2152         return g_strdup (_(cups_option_translations[i].translation));
2153     }
2154
2155   utf8 = ppd_text_to_utf8 (ppd_file, option->text);
2156
2157   /* Some ppd files have spaces in the text before the colon */
2158   g_strchomp (utf8);
2159   
2160   return utf8;
2161 }
2162
2163 static char *
2164 get_choice_text (ppd_file_t   *ppd_file, 
2165                  ppd_choice_t *choice)
2166 {
2167   int i;
2168   ppd_option_t *option = choice->option;
2169   const char *keyword = option->keyword;
2170   
2171   for (i = 0; i < G_N_ELEMENTS (cups_choice_translations); i++)
2172     {
2173       if (strcmp (cups_choice_translations[i].keyword, keyword) == 0 &&
2174           strcmp (cups_choice_translations[i].choice, choice->choice) == 0)
2175         return g_strdup (_(cups_choice_translations[i].translation));
2176     }
2177   return ppd_text_to_utf8 (ppd_file, choice->text);
2178 }
2179
2180 static gboolean
2181 group_has_option (ppd_group_t  *group, 
2182                   ppd_option_t *option)
2183 {
2184   int i;
2185
2186   if (group == NULL)
2187     return FALSE;
2188   
2189   if (group->num_options > 0 &&
2190       option >= group->options && option < group->options + group->num_options)
2191     return TRUE;
2192   
2193   for (i = 0; i < group->num_subgroups; i++)
2194     {
2195       if (group_has_option (&group->subgroups[i],option))
2196         return TRUE;
2197     }
2198   return FALSE;
2199 }
2200
2201 static void
2202 set_option_off (GtkPrinterOption *option)
2203 {
2204   /* Any of these will do, _set only applies the value
2205    * if its allowed of the option */
2206   gtk_printer_option_set (option, "False");
2207   gtk_printer_option_set (option, "Off");
2208   gtk_printer_option_set (option, "None");
2209 }
2210
2211 static gboolean
2212 value_is_off (const char *value)
2213 {
2214   return  (strcasecmp (value, "None") == 0 ||
2215            strcasecmp (value, "Off") == 0 ||
2216            strcasecmp (value, "False") == 0);
2217 }
2218
2219 static char *
2220 ppd_group_name (ppd_group_t *group)
2221 {
2222 #if CUPS_VERSION_MAJOR > 1 || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR > 1) || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR == 1 && CUPS_VERSION_PATCH >= 18) 
2223   return group->name;
2224 #else
2225   return group->text;
2226 #endif
2227 }
2228
2229 static int
2230 available_choices (ppd_file_t     *ppd,
2231                    ppd_option_t   *option,
2232                    ppd_choice_t ***available,
2233                    gboolean        keep_if_only_one_option)
2234 {
2235   ppd_option_t *other_option;
2236   int i, j;
2237   gchar *conflicts;
2238   ppd_const_t *constraint;
2239   const char *choice, *other_choice;
2240   ppd_option_t *option1, *option2;
2241   ppd_group_t *installed_options;
2242   int num_conflicts;
2243   gboolean all_default;
2244   int add_auto;
2245
2246   if (available)
2247     *available = NULL;
2248
2249   conflicts = g_new0 (char, option->num_choices);
2250
2251   installed_options = NULL;
2252   for (i = 0; i < ppd->num_groups; i++)
2253     {
2254       char *name; 
2255
2256       name = ppd_group_name (&ppd->groups[i]);
2257       if (strcmp (name, "InstallableOptions") == 0)
2258         {
2259           installed_options = &ppd->groups[i];
2260           break;
2261         }
2262     }
2263
2264   for (i = ppd->num_consts, constraint = ppd->consts; i > 0; i--, constraint++)
2265     {
2266       option1 = ppdFindOption (ppd, constraint->option1);
2267       if (option1 == NULL)
2268         continue;
2269
2270       option2 = ppdFindOption (ppd, constraint->option2);
2271       if (option2 == NULL)
2272         continue;
2273
2274       if (option == option1)
2275         {
2276           choice = constraint->choice1;
2277           other_option = option2;
2278           other_choice = constraint->choice2;
2279         }
2280       else if (option == option2)
2281         {
2282           choice = constraint->choice2;
2283           other_option = option1;
2284           other_choice = constraint->choice1;
2285         }
2286       else
2287         continue;
2288
2289       /* We only care of conflicts with installed_options and
2290          PageSize */
2291       if (!group_has_option (installed_options, other_option) &&
2292           (strcmp (other_option->keyword, "PageSize") != 0))
2293         continue;
2294
2295       if (*other_choice == 0)
2296         {
2297           /* Conflict only if the installed option is not off */
2298           if (value_is_off (other_option->defchoice))
2299             continue;
2300         }
2301       /* Conflict if the installed option has the specified default */
2302       else if (strcasecmp (other_choice, other_option->defchoice) != 0)
2303         continue;
2304
2305       if (*choice == 0)
2306         {
2307           /* Conflict with all non-off choices */
2308           for (j = 0; j < option->num_choices; j++)
2309             {
2310               if (!value_is_off (option->choices[j].choice))
2311                 conflicts[j] = 1;
2312             }
2313         }
2314       else
2315         {
2316           for (j = 0; j < option->num_choices; j++)
2317             {
2318               if (strcasecmp (option->choices[j].choice, choice) == 0)
2319                 conflicts[j] = 1;
2320             }
2321         }
2322     }
2323
2324   num_conflicts = 0;
2325   all_default = TRUE;
2326   for (j = 0; j < option->num_choices; j++)
2327     {
2328       if (conflicts[j])
2329         num_conflicts++;
2330       else if (strcmp (option->choices[j].choice, option->defchoice) != 0)
2331         all_default = FALSE;
2332     }
2333
2334   if ((all_default && !keep_if_only_one_option) ||
2335       (num_conflicts == option->num_choices))
2336     {
2337       g_free (conflicts);
2338
2339       return 0;
2340     }
2341
2342   /* Some ppds don't have a "use printer default" option for
2343    * InputSlot. This means you always have to select a particular slot,
2344    * and you can't auto-pick source based on the paper size. To support
2345    * this we always add an auto option if there isn't one already. If
2346    * the user chooses the generated option we don't send any InputSlot
2347    * value when printing. The way we detect existing auto-cases is based
2348    * on feedback from Michael Sweet of cups fame.
2349    */
2350   add_auto = 0;
2351   if (strcmp (option->keyword, "InputSlot") == 0)
2352     {
2353       gboolean found_auto = FALSE;
2354       for (j = 0; j < option->num_choices; j++)
2355         {
2356           if (!conflicts[j])
2357             {
2358               if (strcmp (option->choices[j].choice, "Auto") == 0 ||
2359                   strcmp (option->choices[j].choice, "AutoSelect") == 0 ||
2360                   strcmp (option->choices[j].choice, "Default") == 0 ||
2361                   strcmp (option->choices[j].choice, "None") == 0 ||
2362                   strcmp (option->choices[j].choice, "PrinterDefault") == 0 ||
2363                   strcmp (option->choices[j].choice, "Unspecified") == 0 ||
2364                   option->choices[j].code == NULL ||
2365                   option->choices[j].code[0] == 0)
2366                 {
2367                   found_auto = TRUE;
2368                   break;
2369                 }
2370             }
2371         }
2372
2373       if (!found_auto)
2374         add_auto = 1;
2375     }
2376   
2377   if (available)
2378     {
2379       *available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto);
2380
2381       i = 0;
2382       for (j = 0; j < option->num_choices; j++)
2383         {
2384           if (!conflicts[j])
2385             (*available)[i++] = &option->choices[j];
2386         }
2387
2388       if (add_auto) 
2389         (*available)[i++] = NULL;
2390     }
2391
2392   g_free (conflicts);
2393   
2394   return option->num_choices - num_conflicts + add_auto;
2395 }
2396
2397 static GtkPrinterOption *
2398 create_pickone_option (ppd_file_t   *ppd_file,
2399                        ppd_option_t *ppd_option,
2400                        const gchar  *gtk_name)
2401 {
2402   GtkPrinterOption *option;
2403   ppd_choice_t **available;
2404   char *label;
2405   int n_choices;
2406   int i;
2407 #ifdef HAVE_CUPS_API_1_2
2408   ppd_coption_t *coption;
2409 #endif
2410
2411   g_assert (ppd_option->ui == PPD_UI_PICKONE);
2412   
2413   option = NULL;
2414
2415   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
2416   if (n_choices > 0)
2417     {
2418       
2419       /* right now only support one parameter per custom option 
2420        * if more than one print warning and only offer the default choices
2421        */
2422
2423       label = get_option_text (ppd_file, ppd_option);
2424
2425 #ifdef HAVE_CUPS_API_1_2
2426       coption = ppdFindCustomOption (ppd_file, ppd_option->keyword);
2427
2428       if (coption)
2429         {
2430           ppd_cparam_t *cparam;
2431
2432           cparam = ppdFirstCustomParam (coption);
2433
2434           if (ppdNextCustomParam (coption) == NULL)
2435             {
2436               switch (cparam->type)
2437                 {
2438                 case PPD_CUSTOM_INT:
2439                   option = gtk_printer_option_new (gtk_name, label,
2440                                          GTK_PRINTER_OPTION_TYPE_PICKONE_INT);
2441                   break;
2442                 case PPD_CUSTOM_PASSCODE:
2443                   option = gtk_printer_option_new (gtk_name, label,
2444                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE);
2445                   break;
2446                 case PPD_CUSTOM_PASSWORD:
2447                     option = gtk_printer_option_new (gtk_name, label,
2448                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD);
2449                   break;
2450                case PPD_CUSTOM_REAL:
2451                     option = gtk_printer_option_new (gtk_name, label,
2452                                          GTK_PRINTER_OPTION_TYPE_PICKONE_REAL);
2453                   break;
2454                 case PPD_CUSTOM_STRING:
2455                   option = gtk_printer_option_new (gtk_name, label,
2456                                          GTK_PRINTER_OPTION_TYPE_PICKONE_STRING);
2457                   break;
2458 #ifdef PRINT_IGNORED_OPTIONS
2459                 case PPD_CUSTOM_POINTS: 
2460                   g_warning ("CUPS Backend: PPD Custom Points Option not supported");
2461                   break;
2462                 case PPD_CUSTOM_CURVE:
2463                   g_warning ("CUPS Backend: PPD Custom Curve Option not supported");
2464                   break;
2465                 case PPD_CUSTOM_INVCURVE:       
2466                   g_warning ("CUPS Backend: PPD Custom Inverse Curve Option not supported");
2467                   break;
2468 #endif
2469                 default: 
2470                   break;
2471                 }
2472             }
2473 #ifdef PRINT_IGNORED_OPTIONS
2474           else
2475             g_warning ("CUPS Backend: Multi-parameter PPD Custom Option not supported");
2476 #endif
2477         }
2478 #endif /* HAVE_CUPS_API_1_2 */
2479
2480       if (!option)
2481         option = gtk_printer_option_new (gtk_name, label,
2482                                          GTK_PRINTER_OPTION_TYPE_PICKONE);
2483       g_free (label);
2484       
2485       gtk_printer_option_allocate_choices (option, n_choices);
2486       for (i = 0; i < n_choices; i++)
2487         {
2488           if (available[i] == NULL)
2489             {
2490               /* This was auto-added */
2491               option->choices[i] = g_strdup ("gtk-ignore-value");
2492               option->choices_display[i] = g_strdup (_("Printer Default"));
2493             }
2494           else
2495             {
2496               option->choices[i] = g_strdup (available[i]->choice);
2497               option->choices_display[i] = get_choice_text (ppd_file, available[i]);
2498             }
2499         }
2500       gtk_printer_option_set (option, ppd_option->defchoice);
2501     }
2502 #ifdef PRINT_IGNORED_OPTIONS
2503   else
2504     g_warning ("CUPS Backend: Ignoring pickone %s\n", ppd_option->text);
2505 #endif
2506   g_free (available);
2507
2508   return option;
2509 }
2510
2511 static GtkPrinterOption *
2512 create_boolean_option (ppd_file_t   *ppd_file,
2513                        ppd_option_t *ppd_option,
2514                        const gchar  *gtk_name)
2515 {
2516   GtkPrinterOption *option;
2517   ppd_choice_t **available;
2518   char *label;
2519   int n_choices;
2520
2521   g_assert (ppd_option->ui == PPD_UI_BOOLEAN);
2522   
2523   option = NULL;
2524
2525   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
2526   if (n_choices == 2)
2527     {
2528       label = get_option_text (ppd_file, ppd_option);
2529       option = gtk_printer_option_new (gtk_name, label,
2530                                        GTK_PRINTER_OPTION_TYPE_BOOLEAN);
2531       g_free (label);
2532       
2533       gtk_printer_option_allocate_choices (option, 2);
2534       option->choices[0] = g_strdup ("True");
2535       option->choices_display[0] = g_strdup ("True");
2536       option->choices[1] = g_strdup ("False");
2537       option->choices_display[1] = g_strdup ("False");
2538       
2539       gtk_printer_option_set (option, ppd_option->defchoice);
2540     }
2541 #ifdef PRINT_IGNORED_OPTIONS
2542   else
2543     g_warning ("CUPS Backend: Ignoring boolean %s\n", ppd_option->text);
2544 #endif
2545   g_free (available);
2546
2547   return option;
2548 }
2549
2550 static gchar *
2551 get_option_name (const gchar *keyword)
2552 {
2553   int i;
2554
2555   for (i = 0; i < G_N_ELEMENTS (option_names); i++)
2556     if (strcmp (option_names[i].ppd_keyword, keyword) == 0)
2557       return g_strdup (option_names[i].name);
2558
2559   return g_strdup_printf ("cups-%s", keyword);
2560 }
2561
2562 static int
2563 strptr_cmp (const void *a, 
2564             const void *b)
2565 {
2566   char **aa = (char **)a;
2567   char **bb = (char **)b;
2568   return strcmp (*aa, *bb);
2569 }
2570
2571
2572 static gboolean
2573 string_in_table (gchar       *str, 
2574                  const gchar *table[], 
2575                  gint         table_len)
2576 {
2577   return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
2578 }
2579
2580 #define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
2581
2582 static void
2583 handle_option (GtkPrinterOptionSet *set,
2584                ppd_file_t          *ppd_file,
2585                ppd_option_t        *ppd_option,
2586                ppd_group_t         *toplevel_group,
2587                GtkPrintSettings    *settings)
2588 {
2589   GtkPrinterOption *option;
2590   char *name;
2591
2592   if (STRING_IN_TABLE (ppd_option->keyword, cups_option_blacklist))
2593     return;
2594
2595   name = get_option_name (ppd_option->keyword);
2596
2597   option = NULL;
2598   if (ppd_option->ui == PPD_UI_PICKONE)
2599     {
2600       option = create_pickone_option (ppd_file, ppd_option, name);
2601     }
2602   else if (ppd_option->ui == PPD_UI_BOOLEAN)
2603     {
2604       option = create_boolean_option (ppd_file, ppd_option, name);
2605     }
2606 #ifdef PRINT_IGNORED_OPTIONS
2607   else
2608     g_warning ("CUPS Backend: Ignoring pickmany setting %s\n", ppd_option->text);
2609 #endif  
2610   
2611   if (option)
2612     {
2613       char *name;
2614
2615       name = ppd_group_name (toplevel_group);
2616       if (STRING_IN_TABLE (name,
2617                            color_group_whitelist) ||
2618           STRING_IN_TABLE (ppd_option->keyword,
2619                            color_option_whitelist))
2620         {
2621           option->group = g_strdup ("ColorPage");
2622         }
2623       else if (STRING_IN_TABLE (name,
2624                                 image_quality_group_whitelist) ||
2625                STRING_IN_TABLE (ppd_option->keyword,
2626                                 image_quality_option_whitelist))
2627         {
2628           option->group = g_strdup ("ImageQualityPage");
2629         }
2630       else if (STRING_IN_TABLE (name,
2631                                 finishing_group_whitelist) ||
2632                STRING_IN_TABLE (ppd_option->keyword,
2633                                 finishing_option_whitelist))
2634         {
2635           option->group = g_strdup ("FinishingPage");
2636         }
2637       else
2638         {
2639           option->group = g_strdup (toplevel_group->text);
2640         }
2641
2642       set_option_from_settings (option, settings);
2643       
2644       gtk_printer_option_set_add (set, option);
2645     }
2646   
2647   g_free (name);
2648 }
2649
2650 static void
2651 handle_group (GtkPrinterOptionSet *set,
2652               ppd_file_t          *ppd_file,
2653               ppd_group_t         *group,
2654               ppd_group_t         *toplevel_group,
2655               GtkPrintSettings    *settings)
2656 {
2657   gint i;
2658   gchar *name;
2659   
2660   /* Ignore installable options */
2661   name = ppd_group_name (toplevel_group);
2662   if (strcmp (name, "InstallableOptions") == 0)
2663     return;
2664   
2665   for (i = 0; i < group->num_options; i++)
2666     handle_option (set, ppd_file, &group->options[i], toplevel_group, settings);
2667
2668   for (i = 0; i < group->num_subgroups; i++)
2669     handle_group (set, ppd_file, &group->subgroups[i], toplevel_group, settings);
2670
2671 }
2672
2673 static GtkPrinterOptionSet *
2674 cups_printer_get_options (GtkPrinter           *printer,
2675                           GtkPrintSettings     *settings,
2676                           GtkPageSetup         *page_setup,
2677                           GtkPrintCapabilities  capabilities)
2678 {
2679   GtkPrinterOptionSet *set;
2680   GtkPrinterOption *option;
2681   ppd_file_t *ppd_file;
2682   int i;
2683   char *print_at[] = { "now", "at", "on-hold" };
2684   char *n_up[] = {"1", "2", "4", "6", "9", "16" };
2685   char *prio[] = {"100", "80", "50", "30" };
2686   /* Translators: These strings name the possible values of the 
2687    * job priority option in the print dialog
2688    */
2689   char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low") };
2690   char *n_up_layout[] = { "lrtb", "lrbt", "rltb", "rlbt", "tblr", "tbrl", "btlr", "btrl" };
2691   /* Translators: These strings name the possible arrangements of
2692    * multiple pages on a sheet when printing
2693    */
2694   char *n_up_layout_display[] = { N_("Left to right, top to bottom"), N_("Left to right, bottom to top"), 
2695                                   N_("Right to left, top to bottom"), N_("Right to left, bottom to top"), 
2696                                   N_("Top to bottom, left to right"), N_("Top to bottom, right to left"), 
2697                                   N_("Bottom to top, left to right"), N_("Bottom to top, right to left") };
2698   char *name;
2699   int num_opts;
2700   cups_option_t *opts = NULL;
2701   GtkPrintBackendCups *backend;
2702   GtkTextDirection text_direction;
2703
2704
2705   set = gtk_printer_option_set_new ();
2706
2707   /* Cups specific, non-ppd related settings */
2708
2709    /* Translators, this string is used to label the pages-per-sheet option 
2710     * in the print dialog 
2711     */
2712   option = gtk_printer_option_new ("gtk-n-up", _("Pages per Sheet"), GTK_PRINTER_OPTION_TYPE_PICKONE);
2713   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up),
2714                                          n_up, n_up);
2715   gtk_printer_option_set (option, "1");
2716   set_option_from_settings (option, settings);
2717   gtk_printer_option_set_add (set, option);
2718   g_object_unref (option);
2719
2720   if (cups_printer_get_capabilities (printer) & GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT)
2721     {
2722       for (i = 0; i < G_N_ELEMENTS (n_up_layout_display); i++)
2723         n_up_layout_display[i] = _(n_up_layout_display[i]);
2724   
2725        /* Translators, this string is used to label the option in the print 
2726         * dialog that controls in what order multiple pages are arranged 
2727         */
2728       option = gtk_printer_option_new ("gtk-n-up-layout", _("Page Ordering"), GTK_PRINTER_OPTION_TYPE_PICKONE);
2729       gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up_layout),
2730                                              n_up_layout, n_up_layout_display);
2731
2732       text_direction = gtk_widget_get_default_direction ();
2733       if (text_direction == GTK_TEXT_DIR_LTR)
2734         gtk_printer_option_set (option, "lrtb");
2735       else
2736         gtk_printer_option_set (option, "rltb");
2737
2738       set_option_from_settings (option, settings);
2739       gtk_printer_option_set_add (set, option);
2740       g_object_unref (option);
2741     }
2742
2743   for (i = 0; i < G_N_ELEMENTS(prio_display); i++)
2744     prio_display[i] = _(prio_display[i]);
2745   
2746   /* Translators, this string is used to label the job priority option 
2747    * in the print dialog 
2748    */
2749   option = gtk_printer_option_new ("gtk-job-prio", _("Job Priority"), GTK_PRINTER_OPTION_TYPE_PICKONE);
2750   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (prio),
2751                                          prio, prio_display);
2752   gtk_printer_option_set (option, "50");
2753   set_option_from_settings (option, settings);
2754   gtk_printer_option_set_add (set, option);
2755   g_object_unref (option);
2756
2757   /* Translators, this string is used to label the billing info entry
2758    * in the print dialog 
2759    */
2760   option = gtk_printer_option_new ("gtk-billing-info", _("Billing Info"), GTK_PRINTER_OPTION_TYPE_STRING);
2761   gtk_printer_option_set (option, "");
2762   set_option_from_settings (option, settings);
2763   gtk_printer_option_set_add (set, option);
2764   g_object_unref (option);
2765
2766   backend = GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer));
2767
2768   if (backend != NULL)
2769     {
2770       char *cover_default[] = {"none", "classified", "confidential", "secret", "standard", "topsecret", "unclassified" };
2771       /* Translators, these strings are names for various 'standard' cover 
2772        * pages that the printing system may support.
2773        */
2774       char *cover_display_default[] = {N_("None"), N_("Classified"), N_("Confidential"), N_("Secret"), N_("Standard"), N_("Top Secret"), N_("Unclassified"),};
2775       char **cover = NULL;
2776       char **cover_display = NULL;
2777       char **cover_display_translated = NULL;
2778       gint num_of_covers = 0;
2779       gpointer value;
2780       gint j;
2781
2782       num_of_covers = backend->number_of_covers;
2783       cover = g_new (char *, num_of_covers + 1);
2784       cover[num_of_covers] = NULL;
2785       cover_display = g_new (char *, num_of_covers + 1);
2786       cover_display[num_of_covers] = NULL;
2787       cover_display_translated = g_new (char *, num_of_covers + 1);
2788       cover_display_translated[num_of_covers] = NULL;
2789
2790       for (i = 0; i < num_of_covers; i++)
2791         {
2792           cover[i] = g_strdup (backend->covers[i]);
2793           value = NULL;
2794           for (j = 0; j < G_N_ELEMENTS (cover_default); j++)
2795             if (strcmp (cover_default[j], cover[i]) == 0)
2796               {
2797                 value = cover_display_default[j];
2798                 break;
2799               }
2800           cover_display[i] = (value != NULL) ? g_strdup (value) : g_strdup (backend->covers[i]);
2801         }
2802
2803       for (i = 0; i < num_of_covers; i++)
2804         cover_display_translated[i] = _(cover_display[i]);
2805   
2806       /* Translators, this is the label used for the option in the print 
2807        * dialog that controls the front cover page.
2808        */
2809       option = gtk_printer_option_new ("gtk-cover-before", _("Before"), GTK_PRINTER_OPTION_TYPE_PICKONE);
2810       gtk_printer_option_choices_from_array (option, num_of_covers,
2811                                          cover, cover_display_translated);
2812
2813       if (backend->default_cover_before != NULL)
2814         gtk_printer_option_set (option, backend->default_cover_before);
2815       else
2816         gtk_printer_option_set (option, "none");
2817       set_option_from_settings (option, settings);
2818       gtk_printer_option_set_add (set, option);
2819       g_object_unref (option);
2820
2821       /* Translators, this is the label used for the option in the print 
2822        * dialog that controls the back cover page.
2823        */
2824       option = gtk_printer_option_new ("gtk-cover-after", _("After"), GTK_PRINTER_OPTION_TYPE_PICKONE);
2825       gtk_printer_option_choices_from_array (option, num_of_covers,
2826                                          cover, cover_display_translated);
2827       if (backend->default_cover_after != NULL)
2828         gtk_printer_option_set (option, backend->default_cover_after);
2829       else
2830         gtk_printer_option_set (option, "none");
2831       set_option_from_settings (option, settings);
2832       gtk_printer_option_set_add (set, option);
2833       g_object_unref (option);
2834
2835       g_strfreev (cover);
2836       g_strfreev (cover_display);
2837       g_free (cover_display_translated);
2838     }
2839
2840   /* Translators: this is the name of the option that controls when
2841    * a print job is printed. Possible values are 'now', a specified time,
2842    * or 'on hold'
2843    */
2844   option = gtk_printer_option_new ("gtk-print-time", _("Print at"), GTK_PRINTER_OPTION_TYPE_PICKONE);
2845   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (print_at),
2846                                          print_at, print_at);
2847   gtk_printer_option_set (option, "now");
2848   set_option_from_settings (option, settings);
2849   gtk_printer_option_set_add (set, option);
2850   g_object_unref (option);
2851   
2852   /* Translators: this is the name of the option that allows the user
2853    * to specify a time when a print job will be printed.
2854    */
2855   option = gtk_printer_option_new ("gtk-print-time-text", _("Print at time"), GTK_PRINTER_OPTION_TYPE_STRING);
2856   gtk_printer_option_set (option, "");
2857   set_option_from_settings (option, settings);
2858   gtk_printer_option_set_add (set, option);
2859   g_object_unref (option);
2860   
2861   /* Printer (ppd) specific settings */
2862   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
2863   if (ppd_file)
2864     {
2865       GtkPaperSize *paper_size;
2866       ppd_option_t *option;
2867       const gchar  *ppd_name;
2868
2869       ppdMarkDefaults (ppd_file);
2870
2871       paper_size = gtk_page_setup_get_paper_size (page_setup);
2872
2873       option = ppdFindOption (ppd_file, "PageSize");
2874       ppd_name = gtk_paper_size_get_ppd_name (paper_size);
2875       
2876       if (ppd_name)
2877         strncpy (option->defchoice, ppd_name, PPD_MAX_NAME);
2878       else
2879         {
2880           gchar *custom_name;
2881           char width[G_ASCII_DTOSTR_BUF_SIZE];
2882           char height[G_ASCII_DTOSTR_BUF_SIZE];
2883
2884           g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
2885           g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
2886           /* Translators: this format is used to display a custom paper
2887            * size. The two placeholders are replaced with the width and height
2888            * in points. E.g: "Custom 230.4x142.9"
2889            */
2890           custom_name = g_strdup_printf (_("Custom %sx%s"), width, height);
2891           strncpy (option->defchoice, custom_name, PPD_MAX_NAME);
2892           g_free (custom_name);
2893         }
2894
2895       for (i = 0; i < ppd_file->num_groups; i++)
2896         handle_group (set, ppd_file, &ppd_file->groups[i], &ppd_file->groups[i], settings);
2897     }
2898
2899   /* Now honor the user set defaults for this printer */
2900   num_opts = cups_get_user_options (gtk_printer_get_name (printer), 0, &opts);
2901
2902   for (i = 0; i < num_opts; i++)
2903     {
2904       if (STRING_IN_TABLE (opts[i].name, cups_option_blacklist))
2905         continue;
2906
2907       name = get_option_name (opts[i].name);
2908       option = gtk_printer_option_set_lookup (set, name);
2909       if (option)
2910         gtk_printer_option_set (option, opts[i].value);
2911       g_free (name);
2912     }
2913
2914   cupsFreeOptions (num_opts, opts);
2915
2916   return set;
2917 }
2918
2919
2920 static void
2921 mark_option_from_set (GtkPrinterOptionSet *set,
2922                       ppd_file_t          *ppd_file,
2923                       ppd_option_t        *ppd_option)
2924 {
2925   GtkPrinterOption *option;
2926   char *name = get_option_name (ppd_option->keyword);
2927
2928   option = gtk_printer_option_set_lookup (set, name);
2929
2930   if (option)
2931     ppdMarkOption (ppd_file, ppd_option->keyword, option->value);
2932   
2933   g_free (name);
2934 }
2935
2936
2937 static void
2938 mark_group_from_set (GtkPrinterOptionSet *set,
2939                      ppd_file_t          *ppd_file,
2940                      ppd_group_t         *group)
2941 {
2942   int i;
2943
2944   for (i = 0; i < group->num_options; i++)
2945     mark_option_from_set (set, ppd_file, &group->options[i]);
2946
2947   for (i = 0; i < group->num_subgroups; i++)
2948     mark_group_from_set (set, ppd_file, &group->subgroups[i]);
2949 }
2950
2951 static void
2952 set_conflicts_from_option (GtkPrinterOptionSet *set,
2953                            ppd_file_t          *ppd_file,
2954                            ppd_option_t        *ppd_option)
2955 {
2956   GtkPrinterOption *option;
2957   char *name;
2958
2959   if (ppd_option->conflicted)
2960     {
2961       name = get_option_name (ppd_option->keyword);
2962       option = gtk_printer_option_set_lookup (set, name);
2963
2964       if (option)
2965         gtk_printer_option_set_has_conflict (option, TRUE);
2966 #ifdef PRINT_IGNORED_OPTIONS
2967       else
2968         g_warning ("CUPS Backend: Ignoring conflict for option %s", ppd_option->keyword);
2969 #endif
2970       
2971       g_free (name);
2972     }
2973 }
2974
2975 static void
2976 set_conflicts_from_group (GtkPrinterOptionSet *set,
2977                           ppd_file_t          *ppd_file,
2978                           ppd_group_t         *group)
2979 {
2980   int i;
2981
2982   for (i = 0; i < group->num_options; i++)
2983     set_conflicts_from_option (set, ppd_file, &group->options[i]);
2984
2985   for (i = 0; i < group->num_subgroups; i++)
2986     set_conflicts_from_group (set, ppd_file, &group->subgroups[i]);
2987 }
2988
2989 static gboolean
2990 cups_printer_mark_conflicts (GtkPrinter          *printer,
2991                              GtkPrinterOptionSet *options)
2992 {
2993   ppd_file_t *ppd_file;
2994   int num_conflicts;
2995   int i;
2996  
2997   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
2998
2999   if (ppd_file == NULL)
3000     return FALSE;
3001
3002   ppdMarkDefaults (ppd_file);
3003
3004   for (i = 0; i < ppd_file->num_groups; i++)
3005     mark_group_from_set (options, ppd_file, &ppd_file->groups[i]);
3006
3007   num_conflicts = ppdConflicts (ppd_file);
3008
3009   if (num_conflicts > 0)
3010     {
3011       for (i = 0; i < ppd_file->num_groups; i++)
3012         set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]);
3013     }
3014  
3015   return num_conflicts > 0;
3016 }
3017
3018 struct OptionData {
3019   GtkPrinter *printer;
3020   GtkPrinterOptionSet *options;
3021   GtkPrintSettings *settings;
3022   ppd_file_t *ppd_file;
3023 };
3024
3025 typedef struct {
3026   const char *cups;
3027   const char *standard;
3028 } NameMapping;
3029
3030 static void
3031 map_settings_to_option (GtkPrinterOption  *option,
3032                         const NameMapping  table[],
3033                         gint               n_elements,
3034                         GtkPrintSettings  *settings,
3035                         const gchar       *standard_name,
3036                         const gchar       *cups_name)
3037 {
3038   int i;
3039   char *name;
3040   const char *cups_value;
3041   const char *standard_value;
3042
3043   /* If the cups-specific setting is set, always use that */
3044   name = g_strdup_printf ("cups-%s", cups_name);
3045   cups_value = gtk_print_settings_get (settings, name);
3046   g_free (name);
3047   
3048   if (cups_value != NULL) 
3049     {
3050       gtk_printer_option_set (option, cups_value);
3051       return;
3052     }
3053
3054   /* Otherwise we try to convert from the general setting */
3055   standard_value = gtk_print_settings_get (settings, standard_name);
3056   if (standard_value == NULL)
3057     return;
3058
3059   for (i = 0; i < n_elements; i++)
3060     {
3061       if (table[i].cups == NULL && table[i].standard == NULL)
3062         {
3063           gtk_printer_option_set (option, standard_value);
3064           break;
3065         }
3066       else if (table[i].cups == NULL &&
3067                strcmp (table[i].standard, standard_value) == 0)
3068         {
3069           set_option_off (option);
3070           break;
3071         }
3072       else if (strcmp (table[i].standard, standard_value) == 0)
3073         {
3074           gtk_printer_option_set (option, table[i].cups);
3075           break;
3076         }
3077     }
3078 }
3079
3080 static void
3081 map_option_to_settings (const gchar       *value,
3082                         const NameMapping  table[],
3083                         gint               n_elements,
3084                         GtkPrintSettings  *settings,
3085                         const gchar       *standard_name,
3086                         const gchar       *cups_name)
3087 {
3088   int i;
3089   char *name;
3090
3091   for (i = 0; i < n_elements; i++)
3092     {
3093       if (table[i].cups == NULL && table[i].standard == NULL)
3094         {
3095           gtk_print_settings_set (settings,
3096                                   standard_name,
3097                                   value);
3098           break;
3099         }
3100       else if (table[i].cups == NULL && table[i].standard != NULL)
3101         {
3102           if (value_is_off (value))
3103             {
3104               gtk_print_settings_set (settings,
3105                                       standard_name,
3106                                       table[i].standard);
3107               break;
3108             }
3109         }
3110       else if (strcmp (table[i].cups, value) == 0)
3111         {
3112           gtk_print_settings_set (settings,
3113                                   standard_name,
3114                                   table[i].standard);
3115           break;
3116         }
3117     }
3118
3119   /* Always set the corresponding cups-specific setting */
3120   name = g_strdup_printf ("cups-%s", cups_name);
3121   gtk_print_settings_set (settings, name, value);
3122   g_free (name);
3123 }
3124
3125
3126 static const NameMapping paper_source_map[] = {
3127   { "Lower", "lower"},
3128   { "Middle", "middle"},
3129   { "Upper", "upper"},
3130   { "Rear", "rear"},
3131   { "Envelope", "envelope"},
3132   { "Cassette", "cassette"},
3133   { "LargeCapacity", "large-capacity"},
3134   { "AnySmallFormat", "small-format"},
3135   { "AnyLargeFormat", "large-format"},
3136   { NULL, NULL}
3137 };
3138
3139 static const NameMapping output_tray_map[] = {
3140   { "Upper", "upper"},
3141   { "Lower", "lower"},
3142   { "Rear", "rear"},
3143   { NULL, NULL}
3144 };
3145
3146 static const NameMapping duplex_map[] = {
3147   { "DuplexTumble", "vertical" },
3148   { "DuplexNoTumble", "horizontal" },
3149   { NULL, "simplex" }
3150 };
3151
3152 static const NameMapping output_mode_map[] = {
3153   { "Standard", "normal" },
3154   { "Normal", "normal" },
3155   { "Draft", "draft" },
3156   { "Fast", "draft" },
3157 };
3158
3159 static const NameMapping media_type_map[] = {
3160   { "Transparency", "transparency"},
3161   { "Standard", "stationery"},
3162   { NULL, NULL}
3163 };
3164
3165 static const NameMapping all_map[] = {
3166   { NULL, NULL}
3167 };
3168
3169
3170 static void
3171 set_option_from_settings (GtkPrinterOption *option,
3172                           GtkPrintSettings *settings)
3173 {
3174   const char *cups_value;
3175   char *value;
3176   
3177   if (settings == NULL)
3178     return;
3179
3180   if (strcmp (option->name, "gtk-paper-source") == 0)
3181     map_settings_to_option (option, paper_source_map, G_N_ELEMENTS (paper_source_map),
3182                              settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
3183   else if (strcmp (option->name, "gtk-output-tray") == 0)
3184     map_settings_to_option (option, output_tray_map, G_N_ELEMENTS (output_tray_map),
3185                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
3186   else if (strcmp (option->name, "gtk-duplex") == 0)
3187     map_settings_to_option (option, duplex_map, G_N_ELEMENTS (duplex_map),
3188                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
3189   else if (strcmp (option->name, "cups-OutputMode") == 0)
3190     map_settings_to_option (option, output_mode_map, G_N_ELEMENTS (output_mode_map),
3191                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
3192   else if (strcmp (option->name, "cups-Resolution") == 0)
3193     {
3194       cups_value = gtk_print_settings_get (settings, option->name);
3195       if (cups_value)
3196         gtk_printer_option_set (option, cups_value);
3197       else
3198         {
3199           int res = gtk_print_settings_get_resolution (settings);
3200           if (res != 0)
3201             {
3202               value = g_strdup_printf ("%ddpi", res);
3203               gtk_printer_option_set (option, value);
3204               g_free (value);
3205             }
3206         }
3207     }
3208   else if (strcmp (option->name, "gtk-paper-type") == 0)
3209     map_settings_to_option (option, media_type_map, G_N_ELEMENTS (media_type_map),
3210                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
3211   else if (strcmp (option->name, "gtk-n-up") == 0)
3212     {
3213       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
3214                               settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
3215     }
3216   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
3217     {
3218       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
3219                               settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
3220     }
3221   else if (strcmp (option->name, "gtk-billing-info") == 0)
3222     {
3223       cups_value = gtk_print_settings_get (settings, "cups-job-billing");
3224       if (cups_value)
3225         gtk_printer_option_set (option, cups_value);
3226     } 
3227   else if (strcmp (option->name, "gtk-job-prio") == 0)
3228     {
3229       cups_value = gtk_print_settings_get (settings, "cups-job-priority");
3230       if (cups_value)
3231         gtk_printer_option_set (option, cups_value);
3232     } 
3233   else if (strcmp (option->name, "gtk-cover-before") == 0)
3234     {
3235       cups_value = gtk_print_settings_get (settings, "cover-before");
3236       if (cups_value)
3237         gtk_printer_option_set (option, cups_value);
3238     } 
3239   else if (strcmp (option->name, "gtk-cover-after") == 0)
3240     {
3241       cups_value = gtk_print_settings_get (settings, "cover-after");
3242       if (cups_value)
3243         gtk_printer_option_set (option, cups_value);
3244     } 
3245   else if (strcmp (option->name, "gtk-print-time") == 0)
3246     {
3247       cups_value = gtk_print_settings_get (settings, "print-at");
3248       if (cups_value)
3249         gtk_printer_option_set (option, cups_value);
3250     } 
3251   else if (strcmp (option->name, "gtk-print-time-text") == 0)
3252     {
3253       cups_value = gtk_print_settings_get (settings, "print-at-time");
3254       if (cups_value)
3255         gtk_printer_option_set (option, cups_value);
3256     } 
3257   else if (g_str_has_prefix (option->name, "cups-"))
3258     {
3259       cups_value = gtk_print_settings_get (settings, option->name);
3260       if (cups_value)
3261         gtk_printer_option_set (option, cups_value);
3262     } 
3263 }
3264
3265 static void
3266 foreach_option_get_settings (GtkPrinterOption *option,
3267                              gpointer          user_data)
3268 {
3269   struct OptionData *data = user_data;
3270   GtkPrintSettings *settings = data->settings;
3271   const char *value;
3272
3273   value = option->value;
3274
3275   if (strcmp (option->name, "gtk-paper-source") == 0)
3276     map_option_to_settings (value, paper_source_map, G_N_ELEMENTS (paper_source_map),
3277                             settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
3278   else if (strcmp (option->name, "gtk-output-tray") == 0)
3279     map_option_to_settings (value, output_tray_map, G_N_ELEMENTS (output_tray_map),
3280                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
3281   else if (strcmp (option->name, "gtk-duplex") == 0)
3282     map_option_to_settings (value, duplex_map, G_N_ELEMENTS (duplex_map),
3283                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
3284   else if (strcmp (option->name, "cups-OutputMode") == 0)
3285     map_option_to_settings (value, output_mode_map, G_N_ELEMENTS (output_mode_map),
3286                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
3287   else if (strcmp (option->name, "cups-Resolution") == 0)
3288     {
3289       int res = atoi (value);
3290       /* TODO: What if resolution is on XXXxYYYdpi form? */
3291       if (res != 0)
3292         gtk_print_settings_set_resolution (settings, res);
3293       gtk_print_settings_set (settings, option->name, value);
3294     }
3295   else if (strcmp (option->name, "gtk-paper-type") == 0)
3296     map_option_to_settings (value, media_type_map, G_N_ELEMENTS (media_type_map),
3297                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
3298   else if (strcmp (option->name, "gtk-n-up") == 0)
3299     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
3300                             settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
3301   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
3302     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
3303                             settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
3304   else if (strcmp (option->name, "gtk-billing-info") == 0 && strlen (value) > 0)
3305     gtk_print_settings_set (settings, "cups-job-billing", value);
3306   else if (strcmp (option->name, "gtk-job-prio") == 0)
3307     gtk_print_settings_set (settings, "cups-job-priority", value);
3308   else if (strcmp (option->name, "gtk-cover-before") == 0)
3309     gtk_print_settings_set (settings, "cover-before", value);
3310   else if (strcmp (option->name, "gtk-cover-after") == 0)
3311     gtk_print_settings_set (settings, "cover-after", value);
3312   else if (strcmp (option->name, "gtk-print-time") == 0)
3313     gtk_print_settings_set (settings, "print-at", value);
3314   else if (strcmp (option->name, "gtk-print-time-text") == 0)
3315     gtk_print_settings_set (settings, "print-at-time", value);
3316   else if (g_str_has_prefix (option->name, "cups-"))
3317     gtk_print_settings_set (settings, option->name, value);
3318 }
3319
3320 static void
3321 cups_printer_get_settings_from_options (GtkPrinter          *printer,
3322                                         GtkPrinterOptionSet *options,
3323                                         GtkPrintSettings    *settings)
3324 {
3325   struct OptionData data;
3326   const char *print_at, *print_at_time;
3327
3328   data.printer = printer;
3329   data.options = options;
3330   data.settings = settings;
3331   data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3332  
3333   if (data.ppd_file != NULL)
3334     {
3335       GtkPrinterOption *cover_before, *cover_after;
3336       
3337       gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
3338
3339       cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
3340       cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
3341       if (cover_before && cover_after)
3342         {
3343           char *value = g_strdup_printf ("%s,%s", cover_before->value, cover_after->value);
3344           gtk_print_settings_set (settings, "cups-job-sheets", value);
3345           g_free (value);
3346         }
3347
3348       print_at = gtk_print_settings_get (settings, "print-at");
3349       print_at_time = gtk_print_settings_get (settings, "print-at-time");
3350       if (strcmp (print_at, "at") == 0)
3351         gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time);
3352       else if (strcmp (print_at, "on-hold") == 0)
3353         gtk_print_settings_set (settings, "cups-job-hold-until", "indefinite");
3354     }
3355 }
3356
3357 static void
3358 cups_printer_prepare_for_print (GtkPrinter       *printer,
3359                                 GtkPrintJob      *print_job,
3360                                 GtkPrintSettings *settings,
3361                                 GtkPageSetup     *page_setup)
3362 {
3363   GtkPageSet page_set;
3364   GtkPaperSize *paper_size;
3365   const char *ppd_paper_name;
3366   double scale;
3367
3368   print_job->print_pages = gtk_print_settings_get_print_pages (settings);
3369   print_job->page_ranges = NULL;
3370   print_job->num_page_ranges = 0;
3371   
3372   if (print_job->print_pages == GTK_PRINT_PAGES_RANGES)
3373     print_job->page_ranges =
3374       gtk_print_settings_get_page_ranges (settings,
3375                                           &print_job->num_page_ranges);
3376   
3377   if (gtk_print_settings_get_collate (settings))
3378     gtk_print_settings_set (settings, "cups-Collate", "True");
3379   print_job->collate = FALSE;
3380
3381   if (gtk_print_settings_get_reverse (settings))
3382     gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
3383   print_job->reverse = FALSE;
3384
3385   if (gtk_print_settings_get_n_copies (settings) > 1)
3386     gtk_print_settings_set_int (settings, "cups-copies",
3387                                 gtk_print_settings_get_n_copies (settings));
3388   print_job->num_copies = 1;
3389
3390   scale = gtk_print_settings_get_scale (settings);
3391   print_job->scale = 1.0;
3392   if (scale != 100.0)
3393     print_job->scale = scale/100.0;
3394
3395   page_set = gtk_print_settings_get_page_set (settings);
3396   if (page_set == GTK_PAGE_SET_EVEN)
3397     gtk_print_settings_set (settings, "cups-page-set", "even");
3398   else if (page_set == GTK_PAGE_SET_ODD)
3399     gtk_print_settings_set (settings, "cups-page-set", "odd");
3400   print_job->page_set = GTK_PAGE_SET_ALL;
3401
3402   paper_size = gtk_page_setup_get_paper_size (page_setup);
3403   ppd_paper_name = gtk_paper_size_get_ppd_name (paper_size);
3404   if (ppd_paper_name != NULL)
3405     gtk_print_settings_set (settings, "cups-PageSize", ppd_paper_name);
3406   else
3407     {
3408       char width[G_ASCII_DTOSTR_BUF_SIZE];
3409       char height[G_ASCII_DTOSTR_BUF_SIZE];
3410       char *custom_name;
3411
3412       g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
3413       g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
3414       custom_name = g_strdup_printf (("Custom.%sx%s"), width, height);
3415       gtk_print_settings_set (settings, "cups-PageSize", custom_name);
3416       g_free (custom_name);
3417     }
3418
3419   print_job->rotate_to_orientation = TRUE;
3420 }
3421
3422 static GtkPageSetup *
3423 create_page_setup (ppd_file_t *ppd_file,
3424                    ppd_size_t *size)
3425  {
3426    char *display_name;
3427    GtkPageSetup *page_setup;
3428    GtkPaperSize *paper_size;
3429    ppd_option_t *option;
3430    ppd_choice_t *choice;
3431
3432   display_name = NULL;
3433   option = ppdFindOption (ppd_file, "PageSize");
3434   if (option)
3435     {
3436       choice = ppdFindChoice (option, size->name);
3437       if (choice)
3438         display_name = ppd_text_to_utf8 (ppd_file, choice->text);
3439     }
3440
3441   if (display_name == NULL)
3442     display_name = g_strdup (size->name);
3443   
3444   page_setup = gtk_page_setup_new ();
3445   paper_size = gtk_paper_size_new_from_ppd (size->name,
3446                                             display_name,
3447                                             size->width,
3448                                             size->length);
3449   gtk_page_setup_set_paper_size (page_setup, paper_size);
3450   gtk_paper_size_free (paper_size);
3451   
3452   gtk_page_setup_set_top_margin (page_setup, size->length - size->top, GTK_UNIT_POINTS);
3453   gtk_page_setup_set_bottom_margin (page_setup, size->bottom, GTK_UNIT_POINTS);
3454   gtk_page_setup_set_left_margin (page_setup, size->left, GTK_UNIT_POINTS);
3455   gtk_page_setup_set_right_margin (page_setup, size->width - size->right, GTK_UNIT_POINTS);
3456   
3457   g_free (display_name);
3458
3459   return page_setup;
3460 }
3461
3462 static GList *
3463 cups_printer_list_papers (GtkPrinter *printer)
3464 {
3465   ppd_file_t *ppd_file;
3466   ppd_size_t *size;
3467   GtkPageSetup *page_setup;
3468   GList *l;
3469   int i;
3470
3471   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3472   if (ppd_file == NULL)
3473     return NULL;
3474
3475   l = NULL;
3476   
3477   for (i = 0; i < ppd_file->num_sizes; i++)
3478     {
3479       size = &ppd_file->sizes[i];      
3480
3481       page_setup = create_page_setup (ppd_file, size);
3482
3483       l = g_list_prepend (l, page_setup);
3484     }
3485
3486   return g_list_reverse (l);
3487 }
3488
3489 static GtkPageSetup *
3490 cups_printer_get_default_page_size (GtkPrinter *printer)
3491 {
3492   ppd_file_t *ppd_file;
3493   ppd_size_t *size;
3494   ppd_option_t *option;
3495
3496
3497   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3498   if (ppd_file == NULL)
3499     return NULL;
3500
3501   option = ppdFindOption (ppd_file, "PageSize");
3502   size = ppdPageSize (ppd_file, option->defchoice); 
3503
3504   return create_page_setup (ppd_file, size);
3505 }
3506
3507 static void
3508 cups_printer_get_hard_margins (GtkPrinter *printer,
3509                                gdouble    *top,
3510                                gdouble    *bottom,
3511                                gdouble    *left,
3512                                gdouble    *right)
3513 {
3514   ppd_file_t *ppd_file;
3515
3516   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3517   if (ppd_file == NULL)
3518     return;
3519
3520   *left = ppd_file->custom_margins[0];
3521   *bottom = ppd_file->custom_margins[1];
3522   *right = ppd_file->custom_margins[2];
3523   *top = ppd_file->custom_margins[3];
3524 }
3525
3526 static GtkPrintCapabilities
3527 cups_printer_get_capabilities (GtkPrinter *printer)
3528 {
3529   return
3530     GTK_PRINT_CAPABILITY_COPIES |
3531     GTK_PRINT_CAPABILITY_COLLATE |
3532     GTK_PRINT_CAPABILITY_REVERSE |
3533 #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 1 && CUPS_VERSION_PATCH >= 15) || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR > 1) || CUPS_VERSION_MAJOR > 1
3534     GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT |
3535 #endif
3536     GTK_PRINT_CAPABILITY_NUMBER_UP;
3537 }