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