]> Pileus Git - ~andy/gtk/blob - modules/printbackends/cups/gtkprintbackendcups.c
Add a forgotten hunk
[~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       "job-sheets-supported",
1462       "job-sheets-default"
1463     };
1464  
1465   if (cups_backend->list_printers_pending ||
1466       !cups_backend->got_default_printer)
1467     return TRUE;
1468
1469   cups_backend->list_printers_pending = TRUE;
1470
1471   request = gtk_cups_request_new (NULL,
1472                                   GTK_CUPS_POST,
1473                                   CUPS_GET_PRINTERS,
1474                                   NULL,
1475                                   NULL,
1476                                   NULL);
1477
1478   gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1479                                     "requested-attributes", G_N_ELEMENTS (pattrs),
1480                                     NULL, pattrs);
1481
1482   cups_request_execute (cups_backend,
1483                         request,
1484                         (GtkPrintCupsResponseCallbackFunc) cups_request_printer_list_cb,
1485                         request,
1486                         NULL);
1487
1488   return TRUE;
1489 }
1490
1491 static void
1492 cups_get_printer_list (GtkPrintBackend *backend)
1493 {
1494   GtkPrintBackendCups *cups_backend;
1495
1496   cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
1497   if (cups_backend->list_printers_poll == 0)
1498     {
1499       cups_request_printer_list (cups_backend);
1500       cups_backend->list_printers_poll = gdk_threads_add_timeout (3000,
1501                                                         (GSourceFunc) cups_request_printer_list,
1502                                                         backend);
1503     }
1504 }
1505
1506 typedef struct {
1507   GtkPrinterCups *printer;
1508   GIOChannel *ppd_io;
1509   http_t *http;
1510 } GetPPDData;
1511
1512 static void
1513 get_ppd_data_free (GetPPDData *data)
1514 {
1515   GTK_NOTE (PRINTING,
1516             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1517   httpClose (data->http);
1518   g_io_channel_unref (data->ppd_io);
1519   g_object_unref (data->printer);
1520   g_free (data);
1521 }
1522
1523 static void
1524 cups_request_ppd_cb (GtkPrintBackendCups *print_backend,
1525                      GtkCupsResult       *result,
1526                      GetPPDData          *data)
1527 {
1528   ipp_t *response;
1529   GtkPrinter *printer;
1530
1531   GDK_THREADS_ENTER ();
1532
1533   GTK_NOTE (PRINTING,
1534             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1535
1536   printer = GTK_PRINTER (data->printer);
1537   GTK_PRINTER_CUPS (printer)->reading_ppd = FALSE;
1538
1539   if (gtk_cups_result_is_error (result))
1540     {
1541       gboolean success = FALSE;
1542
1543       /* if we get a 404 then it is just a raw printer without a ppd
1544          and not an error */
1545       if ((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
1546           (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))
1547         {
1548           gtk_printer_set_has_details (printer, TRUE);
1549           success = TRUE;
1550         } 
1551         
1552       g_signal_emit_by_name (printer, "details-acquired", success);
1553       goto done;
1554     }
1555
1556   response = gtk_cups_result_get_response (result);
1557
1558   /* let ppdOpenFd take over the ownership of the open file */
1559   g_io_channel_seek_position (data->ppd_io, 0, G_SEEK_SET, NULL);
1560   data->printer->ppd_file = ppdOpenFd (dup (g_io_channel_unix_get_fd (data->ppd_io)));
1561
1562   ppdMarkDefaults (data->printer->ppd_file);
1563   
1564   gtk_printer_set_has_details (printer, TRUE);
1565   g_signal_emit_by_name (printer, "details-acquired", TRUE);
1566
1567 done:
1568   GDK_THREADS_LEAVE ();
1569 }
1570
1571 static void
1572 cups_request_ppd (GtkPrinter *printer)
1573 {
1574   GError *error;
1575   GtkPrintBackend *print_backend;
1576   GtkPrinterCups *cups_printer;
1577   GtkCupsRequest *request;
1578   char *ppd_filename;
1579   gchar *resource;
1580   http_t *http;
1581   GetPPDData *data;
1582   int fd;
1583
1584   cups_printer = GTK_PRINTER_CUPS (printer);
1585
1586   error = NULL;
1587
1588   GTK_NOTE (PRINTING,
1589             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1590
1591   http = httpConnectEncrypt (cups_printer->hostname, 
1592                              cups_printer->port,
1593                              cupsEncryption ());
1594   
1595   data = g_new0 (GetPPDData, 1);
1596
1597   fd = g_file_open_tmp ("gtkprint_ppd_XXXXXX", 
1598                         &ppd_filename, 
1599                         &error);
1600
1601 #ifdef G_ENABLE_DEBUG 
1602   /* If we are debugging printing don't delete the tmp files */
1603   if (!(gtk_debug_flags & GTK_DEBUG_PRINTING))
1604     unlink (ppd_filename);
1605 #else
1606   unlink (ppd_filename);
1607 #endif /* G_ENABLE_DEBUG */
1608
1609   if (error != NULL)
1610     {
1611       GTK_NOTE (PRINTING, 
1612                 g_warning ("CUPS Backend: Failed to create temp file, %s\n", 
1613                            error->message));
1614       g_error_free (error);
1615       httpClose (http);
1616       g_free (ppd_filename);
1617       g_free (data);
1618
1619       g_signal_emit_by_name (printer, "details-acquired", FALSE);
1620       return;
1621     }
1622     
1623   data->http = http;
1624   fchmod (fd, S_IRUSR | S_IWUSR);
1625   data->ppd_io = g_io_channel_unix_new (fd);
1626   g_io_channel_set_encoding (data->ppd_io, NULL, NULL);
1627   g_io_channel_set_close_on_unref (data->ppd_io, TRUE);
1628
1629   data->printer = g_object_ref (printer);
1630
1631   resource = g_strdup_printf ("/printers/%s.ppd", 
1632                               gtk_printer_cups_get_ppd_name (GTK_PRINTER_CUPS (printer)));
1633
1634   request = gtk_cups_request_new (data->http,
1635                                   GTK_CUPS_GET,
1636                                   0,
1637                                   data->ppd_io,
1638                                   cups_printer->hostname,
1639                                   resource);
1640
1641   GTK_NOTE (PRINTING,
1642             g_print ("CUPS Backend: Requesting resource %s to be written to temp file %s\n", resource, ppd_filename));
1643
1644   g_free (resource);
1645   g_free (ppd_filename);
1646
1647   cups_printer->reading_ppd = TRUE;
1648
1649   print_backend = gtk_printer_get_backend (printer);
1650
1651   cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
1652                         request,
1653                         (GtkPrintCupsResponseCallbackFunc) cups_request_ppd_cb,
1654                         data,
1655                         (GDestroyNotify)get_ppd_data_free);
1656 }
1657
1658 /* Ordering matters for default preference */
1659 static const char *lpoptions_locations[] = {
1660   "/etc/cups/lpoptions",
1661   ".lpoptions", 
1662   ".cups/lpoptions"
1663 };
1664
1665 static void
1666 cups_parse_user_default_printer (const char  *filename,
1667                                  char       **printer_name)
1668 {
1669   FILE *fp;
1670   char line[1024], *lineptr, *defname = NULL;
1671   
1672   if ((fp = g_fopen (filename, "r")) == NULL)
1673     return;
1674
1675   while (fgets (line, sizeof (line), fp) != NULL)
1676     {
1677       if (strncasecmp (line, "default", 7) != 0 || !isspace (line[7]))
1678         continue;
1679
1680       lineptr = line + 8;
1681       while (isspace (*lineptr))
1682         lineptr++;
1683
1684       if (!*lineptr)
1685         continue;
1686
1687       defname = lineptr;
1688       while (!isspace (*lineptr) && *lineptr && *lineptr != '/')
1689         lineptr++;
1690
1691       *lineptr = '\0';
1692
1693       if (*printer_name != NULL)
1694         g_free (*printer_name);
1695
1696       *printer_name = g_strdup (defname);
1697     }
1698
1699   fclose (fp);
1700 }
1701
1702 static void
1703 cups_get_user_default_printer (char **printer_name)
1704 {
1705   int i;
1706
1707   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
1708     {
1709       if (g_path_is_absolute (lpoptions_locations[i]))
1710         {
1711           cups_parse_user_default_printer (lpoptions_locations[i],
1712                                            printer_name);
1713         }
1714       else 
1715         {
1716           char *filename;
1717
1718           filename = g_build_filename (g_get_home_dir (), 
1719                                        lpoptions_locations[i], NULL);
1720           cups_parse_user_default_printer (filename, printer_name);
1721           g_free (filename);
1722         }
1723     }
1724 }
1725
1726 static int
1727 cups_parse_user_options (const char     *filename,
1728                          const char     *printer_name,
1729                          int             num_options,
1730                          cups_option_t **options)
1731 {
1732   FILE *fp;
1733   gchar line[1024], *lineptr, *name;
1734
1735   if ((fp = g_fopen (filename, "r")) == NULL)
1736     return num_options;
1737
1738   while (fgets (line, sizeof (line), fp) != NULL)
1739     {
1740       if (strncasecmp (line, "dest", 4) == 0 && isspace (line[4]))
1741         lineptr = line + 4;
1742       else if (strncasecmp (line, "default", 7) == 0 && isspace (line[7]))
1743         lineptr = line + 7;
1744       else
1745         continue;
1746
1747       /* Skip leading whitespace */
1748       while (isspace (*lineptr))
1749         lineptr++;
1750
1751       if (!*lineptr)
1752         continue;
1753
1754       /* NUL-terminate the name, stripping the instance name */
1755       name = lineptr;
1756       while (!isspace (*lineptr) && *lineptr)
1757         {
1758           if (*lineptr == '/')
1759             *lineptr = '\0';
1760           lineptr++;
1761         }
1762
1763       if (!*lineptr)
1764         continue;
1765
1766       *lineptr++ = '\0';
1767
1768       if (strncasecmp (name, printer_name, strlen (printer_name)) != 0)
1769           continue;
1770
1771       /* We found our printer, parse the options */
1772       num_options = cupsParseOptions (lineptr, num_options, options);
1773     }
1774
1775   fclose (fp);
1776
1777   return num_options;
1778 }
1779
1780 static int
1781 cups_get_user_options (const char     *printer_name,
1782                        int             num_options,
1783                        cups_option_t **options)
1784 {
1785   int i;
1786
1787   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
1788     {
1789       if (g_path_is_absolute (lpoptions_locations[i]))
1790         { 
1791            num_options = cups_parse_user_options (lpoptions_locations[i],
1792                                                   printer_name,
1793                                                   num_options,
1794                                                   options);
1795         }
1796       else
1797         {
1798           char *filename;
1799
1800           filename = g_build_filename (g_get_home_dir (), 
1801                                        lpoptions_locations[i], NULL);
1802           num_options = cups_parse_user_options (filename, printer_name,
1803                                                  num_options, options);
1804           g_free (filename);
1805         }
1806     }
1807
1808   return num_options;
1809 }
1810
1811 static void
1812 cups_request_default_printer_cb (GtkPrintBackendCups *print_backend,
1813                                  GtkCupsResult       *result,
1814                                  gpointer             user_data)
1815 {
1816   ipp_t *response;
1817   ipp_attribute_t *attr;
1818
1819   response = gtk_cups_result_get_response (result);
1820   
1821   if ((attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME)) != NULL)
1822     print_backend->default_printer = g_strdup (attr->values[0].string.text);
1823
1824   print_backend->got_default_printer = TRUE;
1825
1826   /* Make sure to kick off get_printers if we are polling it, 
1827    * as we could have blocked this reading the default printer 
1828    */
1829   if (print_backend->list_printers_poll != 0)
1830     cups_request_printer_list (print_backend);
1831 }
1832
1833 static void
1834 cups_request_default_printer (GtkPrintBackendCups *print_backend)
1835 {
1836   GtkCupsRequest *request;
1837   const char *str;
1838   char *name = NULL;
1839
1840   if ((str = g_getenv ("LPDEST")) != NULL)
1841     {
1842       print_backend->default_printer = g_strdup (str);
1843       print_backend->got_default_printer = TRUE;
1844       return;
1845     }
1846   else if ((str = g_getenv ("PRINTER")) != NULL &&
1847            strcmp (str, "lp") != 0)
1848     {
1849       print_backend->default_printer = g_strdup (str);
1850       print_backend->got_default_printer = TRUE;
1851       return;
1852     }
1853   
1854   /* Figure out user setting for default printer */  
1855   cups_get_user_default_printer (&name);
1856   if (name != NULL)
1857     {
1858        print_backend->default_printer = name;
1859        print_backend->got_default_printer = TRUE;
1860        return;
1861     }
1862
1863   request = gtk_cups_request_new (NULL,
1864                                   GTK_CUPS_POST,
1865                                   CUPS_GET_DEFAULT,
1866                                   NULL,
1867                                   NULL,
1868                                   NULL);
1869   
1870   cups_request_execute (print_backend,
1871                         request,
1872                         (GtkPrintCupsResponseCallbackFunc) cups_request_default_printer_cb,
1873                         g_object_ref (print_backend),
1874                         g_object_unref);
1875 }
1876
1877 static void
1878 cups_printer_request_details (GtkPrinter *printer)
1879 {
1880   GtkPrinterCups *cups_printer;
1881
1882   cups_printer = GTK_PRINTER_CUPS (printer);
1883   if (!cups_printer->reading_ppd && 
1884       gtk_printer_cups_get_ppd (cups_printer) == NULL)
1885     cups_request_ppd (printer); 
1886 }
1887
1888 static char *
1889 ppd_text_to_utf8 (ppd_file_t *ppd_file, 
1890                   const char *text)
1891 {
1892   const char *encoding = NULL;
1893   char *res;
1894   
1895   if (g_ascii_strcasecmp (ppd_file->lang_encoding, "UTF-8") == 0)
1896     {
1897       return g_strdup (text);
1898     }
1899   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin1") == 0)
1900     {
1901       encoding = "ISO-8859-1";
1902     }
1903   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin2") == 0)
1904     {
1905       encoding = "ISO-8859-2";
1906     }
1907   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin5") == 0)
1908     {
1909       encoding = "ISO-8859-5";
1910     }
1911   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "JIS83-RKSJ") == 0)
1912     {
1913       encoding = "SHIFT-JIS";
1914     }
1915   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "MacStandard") == 0)
1916     {
1917       encoding = "MACINTOSH";
1918     }
1919   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "WindowsANSI") == 0)
1920     {
1921       encoding = "WINDOWS-1252";
1922     }
1923   else 
1924     {
1925       /* Fallback, try iso-8859-1... */
1926       encoding = "ISO-8859-1";
1927     }
1928
1929   res = g_convert (text, -1, "UTF-8", encoding, NULL, NULL, NULL);
1930
1931   if (res == NULL)
1932     {
1933       GTK_NOTE (PRINTING,
1934                 g_warning ("CUPS Backend: Unable to convert PPD text\n"));
1935       res = g_strdup ("???");
1936     }
1937   
1938   return res;
1939 }
1940
1941 /* TODO: Add more translations for common settings here */
1942
1943 static const struct {
1944   const char *keyword;
1945   const char *translation;
1946 } cups_option_translations[] = {
1947   { "Duplex", N_("Two Sided") },
1948   { "MediaType", N_("Paper Type") },
1949   { "InputSlot", N_("Paper Source") },
1950   { "OutputBin", N_("Output Tray") },
1951 };
1952
1953
1954 static const struct {
1955   const char *keyword;
1956   const char *choice;
1957   const char *translation;
1958 } cups_choice_translations[] = {
1959   { "Duplex", "None", N_("One Sided") },
1960   { "InputSlot", "Auto", N_("Auto Select") },
1961   { "InputSlot", "AutoSelect", N_("Auto Select") },
1962   { "InputSlot", "Default", N_("Printer Default") },
1963   { "InputSlot", "None", N_("Printer Default") },
1964   { "InputSlot", "PrinterDefault", N_("Printer Default") },
1965   { "InputSlot", "Unspecified", N_("Auto Select") },
1966 };
1967
1968 static const struct {
1969   const char *ppd_keyword;
1970   const char *name;
1971 } option_names[] = {
1972   {"Duplex", "gtk-duplex" },
1973   {"MediaType", "gtk-paper-type"},
1974   {"InputSlot", "gtk-paper-source"},
1975   {"OutputBin", "gtk-output-tray"},
1976 };
1977
1978 /* keep sorted when changing */
1979 static const char *color_option_whitelist[] = {
1980   "BRColorEnhancement",
1981   "BRColorMatching",
1982   "BRColorMatching",
1983   "BRColorMode",
1984   "BRGammaValue",
1985   "BRImprovedGray",
1986   "BlackSubstitution",
1987   "ColorModel",
1988   "HPCMYKInks",
1989   "HPCSGraphics",
1990   "HPCSImages",
1991   "HPCSText",
1992   "HPColorSmart",
1993   "RPSBlackMode",
1994   "RPSBlackOverPrint",
1995   "Rcmyksimulation",
1996 };
1997
1998 /* keep sorted when changing */
1999 static const char *color_group_whitelist[] = {
2000   "ColorPage",
2001   "FPColorWise1",
2002   "FPColorWise2",
2003   "FPColorWise3",
2004   "FPColorWise4",
2005   "FPColorWise5",
2006   "HPColorOptionsPanel",
2007 };
2008   
2009 /* keep sorted when changing */
2010 static const char *image_quality_option_whitelist[] = {
2011   "BRDocument",
2012   "BRHalfTonePattern",
2013   "BRNormalPrt",
2014   "BRPrintQuality",
2015   "BitsPerPixel",
2016   "Darkness",
2017   "Dithering",
2018   "EconoMode",
2019   "Economode",
2020   "HPEconoMode",
2021   "HPEdgeControl",
2022   "HPGraphicsHalftone",
2023   "HPHalftone",
2024   "HPLJDensity",
2025   "HPPhotoHalftone",
2026   "OutputMode",
2027   "REt",
2028   "RPSBitsPerPixel",
2029   "RPSDitherType",
2030   "Resolution",
2031   "ScreenLock",
2032   "Smoothing",
2033   "TonerSaveMode",
2034   "UCRGCRForImage",
2035 };
2036
2037 /* keep sorted when changing */
2038 static const char *image_quality_group_whitelist[] = {
2039   "FPImageQuality1",
2040   "FPImageQuality2",
2041   "FPImageQuality3",
2042   "ImageQualityPage",
2043 };
2044
2045 /* keep sorted when changing */
2046 static const char * finishing_option_whitelist[] = {
2047   "BindColor",
2048   "BindEdge",
2049   "BindType",
2050   "BindWhen",
2051   "Booklet",
2052   "FoldType",
2053   "FoldWhen",
2054   "HPStaplerOptions",
2055   "Jog",
2056   "Slipsheet",
2057   "Sorter",
2058   "StapleLocation",
2059   "StapleOrientation",
2060   "StapleWhen",
2061   "StapleX",
2062   "StapleY",
2063 };
2064
2065 /* keep sorted when changing */
2066 static const char *finishing_group_whitelist[] = {
2067   "FPFinishing1",
2068   "FPFinishing2",
2069   "FPFinishing3",
2070   "FPFinishing4",
2071   "FinishingPage",
2072   "HPFinishingPanel",
2073 };
2074
2075 /* keep sorted when changing */
2076 static const char *cups_option_blacklist[] = {
2077   "Collate",
2078   "Copies", 
2079   "OutputOrder",
2080   "PageRegion",
2081   "PageSize",
2082 };
2083
2084 static char *
2085 get_option_text (ppd_file_t   *ppd_file, 
2086                  ppd_option_t *option)
2087 {
2088   int i;
2089   char *utf8;
2090   
2091   for (i = 0; i < G_N_ELEMENTS (cups_option_translations); i++)
2092     {
2093       if (strcmp (cups_option_translations[i].keyword, option->keyword) == 0)
2094         return g_strdup (_(cups_option_translations[i].translation));
2095     }
2096
2097   utf8 = ppd_text_to_utf8 (ppd_file, option->text);
2098
2099   /* Some ppd files have spaces in the text before the colon */
2100   g_strchomp (utf8);
2101   
2102   return utf8;
2103 }
2104
2105 static char *
2106 get_choice_text (ppd_file_t   *ppd_file, 
2107                  ppd_choice_t *choice)
2108 {
2109   int i;
2110   ppd_option_t *option = choice->option;
2111   const char *keyword = option->keyword;
2112   
2113   for (i = 0; i < G_N_ELEMENTS (cups_choice_translations); i++)
2114     {
2115       if (strcmp (cups_choice_translations[i].keyword, keyword) == 0 &&
2116           strcmp (cups_choice_translations[i].choice, choice->choice) == 0)
2117         return g_strdup (_(cups_choice_translations[i].translation));
2118     }
2119   return ppd_text_to_utf8 (ppd_file, choice->text);
2120 }
2121
2122 static gboolean
2123 group_has_option (ppd_group_t  *group, 
2124                   ppd_option_t *option)
2125 {
2126   int i;
2127
2128   if (group == NULL)
2129     return FALSE;
2130   
2131   if (group->num_options > 0 &&
2132       option >= group->options && option < group->options + group->num_options)
2133     return TRUE;
2134   
2135   for (i = 0; i < group->num_subgroups; i++)
2136     {
2137       if (group_has_option (&group->subgroups[i],option))
2138         return TRUE;
2139     }
2140   return FALSE;
2141 }
2142
2143 static void
2144 set_option_off (GtkPrinterOption *option)
2145 {
2146   /* Any of these will do, _set only applies the value
2147    * if its allowed of the option */
2148   gtk_printer_option_set (option, "False");
2149   gtk_printer_option_set (option, "Off");
2150   gtk_printer_option_set (option, "None");
2151 }
2152
2153 static gboolean
2154 value_is_off (const char *value)
2155 {
2156   return  (strcasecmp (value, "None") == 0 ||
2157            strcasecmp (value, "Off") == 0 ||
2158            strcasecmp (value, "False") == 0);
2159 }
2160
2161 static char *
2162 ppd_group_name (ppd_group_t *group)
2163 {
2164 #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) 
2165   return group->name;
2166 #else
2167   return group->text;
2168 #endif
2169 }
2170
2171 static int
2172 available_choices (ppd_file_t     *ppd,
2173                    ppd_option_t   *option,
2174                    ppd_choice_t ***available,
2175                    gboolean        keep_if_only_one_option)
2176 {
2177   ppd_option_t *other_option;
2178   int i, j;
2179   gchar *conflicts;
2180   ppd_const_t *constraint;
2181   const char *choice, *other_choice;
2182   ppd_option_t *option1, *option2;
2183   ppd_group_t *installed_options;
2184   int num_conflicts;
2185   gboolean all_default;
2186   int add_auto;
2187
2188   if (available)
2189     *available = NULL;
2190
2191   conflicts = g_new0 (char, option->num_choices);
2192
2193   installed_options = NULL;
2194   for (i = 0; i < ppd->num_groups; i++)
2195     {
2196       char *name; 
2197
2198       name = ppd_group_name (&ppd->groups[i]);
2199       if (strcmp (name, "InstallableOptions") == 0)
2200         {
2201           installed_options = &ppd->groups[i];
2202           break;
2203         }
2204     }
2205
2206   for (i = ppd->num_consts, constraint = ppd->consts; i > 0; i--, constraint++)
2207     {
2208       option1 = ppdFindOption (ppd, constraint->option1);
2209       if (option1 == NULL)
2210         continue;
2211
2212       option2 = ppdFindOption (ppd, constraint->option2);
2213       if (option2 == NULL)
2214         continue;
2215
2216       if (option == option1)
2217         {
2218           choice = constraint->choice1;
2219           other_option = option2;
2220           other_choice = constraint->choice2;
2221         }
2222       else if (option == option2)
2223         {
2224           choice = constraint->choice2;
2225           other_option = option1;
2226           other_choice = constraint->choice1;
2227         }
2228       else
2229         continue;
2230
2231       /* We only care of conflicts with installed_options and
2232          PageSize */
2233       if (!group_has_option (installed_options, other_option) &&
2234           (strcmp (other_option->keyword, "PageSize") != 0))
2235         continue;
2236
2237       if (*other_choice == 0)
2238         {
2239           /* Conflict only if the installed option is not off */
2240           if (value_is_off (other_option->defchoice))
2241             continue;
2242         }
2243       /* Conflict if the installed option has the specified default */
2244       else if (strcasecmp (other_choice, other_option->defchoice) != 0)
2245         continue;
2246
2247       if (*choice == 0)
2248         {
2249           /* Conflict with all non-off choices */
2250           for (j = 0; j < option->num_choices; j++)
2251             {
2252               if (!value_is_off (option->choices[j].choice))
2253                 conflicts[j] = 1;
2254             }
2255         }
2256       else
2257         {
2258           for (j = 0; j < option->num_choices; j++)
2259             {
2260               if (strcasecmp (option->choices[j].choice, choice) == 0)
2261                 conflicts[j] = 1;
2262             }
2263         }
2264     }
2265
2266   num_conflicts = 0;
2267   all_default = TRUE;
2268   for (j = 0; j < option->num_choices; j++)
2269     {
2270       if (conflicts[j])
2271         num_conflicts++;
2272       else if (strcmp (option->choices[j].choice, option->defchoice) != 0)
2273         all_default = FALSE;
2274     }
2275
2276   if ((all_default && !keep_if_only_one_option) ||
2277       (num_conflicts == option->num_choices))
2278     {
2279       g_free (conflicts);
2280
2281       return 0;
2282     }
2283
2284   /* Some ppds don't have a "use printer default" option for
2285    * InputSlot. This means you always have to select a particular slot,
2286    * and you can't auto-pick source based on the paper size. To support
2287    * this we always add an auto option if there isn't one already. If
2288    * the user chooses the generated option we don't send any InputSlot
2289    * value when printing. The way we detect existing auto-cases is based
2290    * on feedback from Michael Sweet of cups fame.
2291    */
2292   add_auto = 0;
2293   if (strcmp (option->keyword, "InputSlot") == 0)
2294     {
2295       gboolean found_auto = FALSE;
2296       for (j = 0; j < option->num_choices; j++)
2297         {
2298           if (!conflicts[j])
2299             {
2300               if (strcmp (option->choices[j].choice, "Auto") == 0 ||
2301                   strcmp (option->choices[j].choice, "AutoSelect") == 0 ||
2302                   strcmp (option->choices[j].choice, "Default") == 0 ||
2303                   strcmp (option->choices[j].choice, "None") == 0 ||
2304                   strcmp (option->choices[j].choice, "PrinterDefault") == 0 ||
2305                   strcmp (option->choices[j].choice, "Unspecified") == 0 ||
2306                   option->choices[j].code == NULL ||
2307                   option->choices[j].code[0] == 0)
2308                 {
2309                   found_auto = TRUE;
2310                   break;
2311                 }
2312             }
2313         }
2314
2315       if (!found_auto)
2316         add_auto = 1;
2317     }
2318   
2319   if (available)
2320     {
2321       *available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto);
2322
2323       i = 0;
2324       for (j = 0; j < option->num_choices; j++)
2325         {
2326           if (!conflicts[j])
2327             (*available)[i++] = &option->choices[j];
2328         }
2329
2330       if (add_auto) 
2331         (*available)[i++] = NULL;
2332     }
2333
2334   g_free (conflicts);
2335   
2336   return option->num_choices - num_conflicts + add_auto;
2337 }
2338
2339 static GtkPrinterOption *
2340 create_pickone_option (ppd_file_t   *ppd_file,
2341                        ppd_option_t *ppd_option,
2342                        const gchar  *gtk_name)
2343 {
2344   GtkPrinterOption *option;
2345   ppd_choice_t **available;
2346   char *label;
2347   int n_choices;
2348   int i;
2349 #ifdef HAVE_CUPS_API_1_2
2350   ppd_coption_t *coption;
2351 #endif
2352
2353   g_assert (ppd_option->ui == PPD_UI_PICKONE);
2354   
2355   option = NULL;
2356
2357   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
2358   if (n_choices > 0)
2359     {
2360       
2361       /* right now only support one parameter per custom option 
2362        * if more than one print warning and only offer the default choices
2363        */
2364
2365       label = get_option_text (ppd_file, ppd_option);
2366
2367 #ifdef HAVE_CUPS_API_1_2
2368       coption = ppdFindCustomOption (ppd_file, ppd_option->keyword);
2369
2370       if (coption)
2371         {
2372           ppd_cparam_t *cparam;
2373
2374           cparam = ppdFirstCustomParam (coption);
2375
2376           if (ppdNextCustomParam (coption) == NULL)
2377             {
2378               switch (cparam->type)
2379                 {
2380                 case PPD_CUSTOM_INT:
2381                   option = gtk_printer_option_new (gtk_name, label,
2382                                          GTK_PRINTER_OPTION_TYPE_PICKONE_INT);
2383                   break;
2384                 case PPD_CUSTOM_PASSCODE:
2385                   option = gtk_printer_option_new (gtk_name, label,
2386                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE);
2387                   break;
2388                 case PPD_CUSTOM_PASSWORD:
2389                     option = gtk_printer_option_new (gtk_name, label,
2390                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD);
2391                   break;
2392                case PPD_CUSTOM_REAL:
2393                     option = gtk_printer_option_new (gtk_name, label,
2394                                          GTK_PRINTER_OPTION_TYPE_PICKONE_REAL);
2395                   break;
2396                 case PPD_CUSTOM_STRING:
2397                   option = gtk_printer_option_new (gtk_name, label,
2398                                          GTK_PRINTER_OPTION_TYPE_PICKONE_STRING);
2399                   break;
2400 #ifdef PRINT_IGNORED_OPTIONS
2401                 case PPD_CUSTOM_POINTS: 
2402                   g_warning ("CUPS Backend: PPD Custom Points Option not supported");
2403                   break;
2404                 case PPD_CUSTOM_CURVE:
2405                   g_warning ("CUPS Backend: PPD Custom Curve Option not supported");
2406                   break;
2407                 case PPD_CUSTOM_INVCURVE:       
2408                   g_warning ("CUPS Backend: PPD Custom Inverse Curve Option not supported");
2409                   break;
2410 #endif
2411                 default: 
2412                   break;
2413                 }
2414             }
2415 #ifdef PRINT_IGNORED_OPTIONS
2416           else
2417             g_warning ("CUPS Backend: Multi-parameter PPD Custom Option not supported");
2418 #endif
2419         }
2420 #endif /* HAVE_CUPS_API_1_2 */
2421
2422       if (!option)
2423         option = gtk_printer_option_new (gtk_name, label,
2424                                          GTK_PRINTER_OPTION_TYPE_PICKONE);
2425       g_free (label);
2426       
2427       gtk_printer_option_allocate_choices (option, n_choices);
2428       for (i = 0; i < n_choices; i++)
2429         {
2430           if (available[i] == NULL)
2431             {
2432               /* This was auto-added */
2433               option->choices[i] = g_strdup ("gtk-ignore-value");
2434               option->choices_display[i] = g_strdup (_("Printer Default"));
2435             }
2436           else
2437             {
2438               option->choices[i] = g_strdup (available[i]->choice);
2439               option->choices_display[i] = get_choice_text (ppd_file, available[i]);
2440             }
2441         }
2442       gtk_printer_option_set (option, ppd_option->defchoice);
2443     }
2444 #ifdef PRINT_IGNORED_OPTIONS
2445   else
2446     g_warning ("CUPS Backend: Ignoring pickone %s\n", ppd_option->text);
2447 #endif
2448   g_free (available);
2449
2450   return option;
2451 }
2452
2453 static GtkPrinterOption *
2454 create_boolean_option (ppd_file_t   *ppd_file,
2455                        ppd_option_t *ppd_option,
2456                        const gchar  *gtk_name)
2457 {
2458   GtkPrinterOption *option;
2459   ppd_choice_t **available;
2460   char *label;
2461   int n_choices;
2462
2463   g_assert (ppd_option->ui == PPD_UI_BOOLEAN);
2464   
2465   option = NULL;
2466
2467   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
2468   if (n_choices == 2)
2469     {
2470       label = get_option_text (ppd_file, ppd_option);
2471       option = gtk_printer_option_new (gtk_name, label,
2472                                        GTK_PRINTER_OPTION_TYPE_BOOLEAN);
2473       g_free (label);
2474       
2475       gtk_printer_option_allocate_choices (option, 2);
2476       option->choices[0] = g_strdup ("True");
2477       option->choices_display[0] = g_strdup ("True");
2478       option->choices[1] = g_strdup ("False");
2479       option->choices_display[1] = g_strdup ("False");
2480       
2481       gtk_printer_option_set (option, ppd_option->defchoice);
2482     }
2483 #ifdef PRINT_IGNORED_OPTIONS
2484   else
2485     g_warning ("CUPS Backend: Ignoring boolean %s\n", ppd_option->text);
2486 #endif
2487   g_free (available);
2488
2489   return option;
2490 }
2491
2492 static gchar *
2493 get_option_name (const gchar *keyword)
2494 {
2495   int i;
2496
2497   for (i = 0; i < G_N_ELEMENTS (option_names); i++)
2498     if (strcmp (option_names[i].ppd_keyword, keyword) == 0)
2499       return g_strdup (option_names[i].name);
2500
2501   return g_strdup_printf ("cups-%s", keyword);
2502 }
2503
2504 static int
2505 strptr_cmp (const void *a, 
2506             const void *b)
2507 {
2508   char **aa = (char **)a;
2509   char **bb = (char **)b;
2510   return strcmp (*aa, *bb);
2511 }
2512
2513
2514 static gboolean
2515 string_in_table (gchar       *str, 
2516                  const gchar *table[], 
2517                  gint         table_len)
2518 {
2519   return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
2520 }
2521
2522 #define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
2523
2524 static void
2525 handle_option (GtkPrinterOptionSet *set,
2526                ppd_file_t          *ppd_file,
2527                ppd_option_t        *ppd_option,
2528                ppd_group_t         *toplevel_group,
2529                GtkPrintSettings    *settings)
2530 {
2531   GtkPrinterOption *option;
2532   char *name;
2533
2534   if (STRING_IN_TABLE (ppd_option->keyword, cups_option_blacklist))
2535     return;
2536
2537   name = get_option_name (ppd_option->keyword);
2538
2539   option = NULL;
2540   if (ppd_option->ui == PPD_UI_PICKONE)
2541     {
2542       option = create_pickone_option (ppd_file, ppd_option, name);
2543     }
2544   else if (ppd_option->ui == PPD_UI_BOOLEAN)
2545     {
2546       option = create_boolean_option (ppd_file, ppd_option, name);
2547     }
2548 #ifdef PRINT_IGNORED_OPTIONS
2549   else
2550     g_warning ("CUPS Backend: Ignoring pickmany setting %s\n", ppd_option->text);
2551 #endif  
2552   
2553   if (option)
2554     {
2555       char *name;
2556
2557       name = ppd_group_name (toplevel_group);
2558       if (STRING_IN_TABLE (name,
2559                            color_group_whitelist) ||
2560           STRING_IN_TABLE (ppd_option->keyword,
2561                            color_option_whitelist))
2562         {
2563           option->group = g_strdup ("ColorPage");
2564         }
2565       else if (STRING_IN_TABLE (name,
2566                                 image_quality_group_whitelist) ||
2567                STRING_IN_TABLE (ppd_option->keyword,
2568                                 image_quality_option_whitelist))
2569         {
2570           option->group = g_strdup ("ImageQualityPage");
2571         }
2572       else if (STRING_IN_TABLE (name,
2573                                 finishing_group_whitelist) ||
2574                STRING_IN_TABLE (ppd_option->keyword,
2575                                 finishing_option_whitelist))
2576         {
2577           option->group = g_strdup ("FinishingPage");
2578         }
2579       else
2580         {
2581           option->group = g_strdup (toplevel_group->text);
2582         }
2583
2584       set_option_from_settings (option, settings);
2585       
2586       gtk_printer_option_set_add (set, option);
2587     }
2588   
2589   g_free (name);
2590 }
2591
2592 static void
2593 handle_group (GtkPrinterOptionSet *set,
2594               ppd_file_t          *ppd_file,
2595               ppd_group_t         *group,
2596               ppd_group_t         *toplevel_group,
2597               GtkPrintSettings    *settings)
2598 {
2599   gint i;
2600   gchar *name;
2601   
2602   /* Ignore installable options */
2603   name = ppd_group_name (toplevel_group);
2604   if (strcmp (name, "InstallableOptions") == 0)
2605     return;
2606   
2607   for (i = 0; i < group->num_options; i++)
2608     handle_option (set, ppd_file, &group->options[i], toplevel_group, settings);
2609
2610   for (i = 0; i < group->num_subgroups; i++)
2611     handle_group (set, ppd_file, &group->subgroups[i], toplevel_group, settings);
2612
2613 }
2614
2615 static GtkPrinterOptionSet *
2616 cups_printer_get_options (GtkPrinter           *printer,
2617                           GtkPrintSettings     *settings,
2618                           GtkPageSetup         *page_setup,
2619                           GtkPrintCapabilities  capabilities)
2620 {
2621   GtkPrinterOptionSet *set;
2622   GtkPrinterOption *option;
2623   ppd_file_t *ppd_file;
2624   int i;
2625   char *print_at[] = { "now", "at", "on-hold" };
2626   char *n_up[] = {"1", "2", "4", "6", "9", "16" };
2627   char *prio[] = {"100", "80", "50", "30" };
2628   char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low") };
2629   char *name;
2630   int num_opts;
2631   cups_option_t *opts = NULL;
2632   GtkPrintBackendCups *backend;
2633
2634
2635   set = gtk_printer_option_set_new ();
2636
2637   /* Cups specific, non-ppd related settings */
2638
2639   option = gtk_printer_option_new ("gtk-n-up", "Pages Per Sheet", GTK_PRINTER_OPTION_TYPE_PICKONE);
2640   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up),
2641                                          n_up, n_up);
2642   gtk_printer_option_set (option, "1");
2643   set_option_from_settings (option, settings);
2644   gtk_printer_option_set_add (set, option);
2645   g_object_unref (option);
2646
2647   for (i = 0; i < G_N_ELEMENTS(prio_display); i++)
2648     prio_display[i] = _(prio_display[i]);
2649   
2650   option = gtk_printer_option_new ("gtk-job-prio", "Job Priority", GTK_PRINTER_OPTION_TYPE_PICKONE);
2651   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (prio),
2652                                          prio, prio_display);
2653   gtk_printer_option_set (option, "50");
2654   set_option_from_settings (option, settings);
2655   gtk_printer_option_set_add (set, option);
2656   g_object_unref (option);
2657
2658   option = gtk_printer_option_new ("gtk-billing-info", "Billing Info", GTK_PRINTER_OPTION_TYPE_STRING);
2659   gtk_printer_option_set (option, "");
2660   set_option_from_settings (option, settings);
2661   gtk_printer_option_set_add (set, option);
2662   g_object_unref (option);
2663
2664   backend = GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer));
2665
2666   if (backend != NULL)
2667     {
2668       char *cover_default[] = {"none", "classified", "confidential", "secret", "standard", "topsecret", "unclassified" };
2669       char *cover_display_default[] = {N_("None"), N_("Classified"), N_("Confidential"), N_("Secret"), N_("Standard"), N_("Top Secret"), N_("Unclassified"),};
2670       char **cover = NULL;
2671       char **cover_display = NULL;
2672       gint num_of_covers = 0;
2673       gpointer value;
2674       gint j;
2675
2676       num_of_covers = backend->number_of_covers;
2677       cover = g_new (char *, num_of_covers + 1);
2678       cover[num_of_covers] = NULL;
2679       cover_display = g_new (char *, num_of_covers + 1);
2680       cover_display[num_of_covers] = NULL;
2681
2682       for (i = 0; i < num_of_covers; i++)
2683         {
2684           cover[i] = g_strdup (backend->covers[i]);
2685           value = NULL;
2686           for (j = 0; j < G_N_ELEMENTS (cover_default); j++)
2687             if (strcmp (cover_default[j], cover[i]) == 0)
2688               {
2689                 value = cover_display_default[j];
2690                 break;
2691               }
2692           cover_display[i] = (value != NULL) ? g_strdup (value) : g_strdup (backend->covers[i]);
2693         }
2694
2695       for (i = 0; i < num_of_covers; i++)
2696         cover_display[i] = _(cover_display[i]);
2697   
2698       option = gtk_printer_option_new ("gtk-cover-before", "Before", GTK_PRINTER_OPTION_TYPE_PICKONE);
2699       gtk_printer_option_choices_from_array (option, num_of_covers,
2700                                          cover, cover_display);
2701
2702       if (backend->default_cover_before != NULL)
2703         gtk_printer_option_set (option, backend->default_cover_before);
2704       else
2705         gtk_printer_option_set (option, "none");
2706       set_option_from_settings (option, settings);
2707       gtk_printer_option_set_add (set, option);
2708       g_object_unref (option);
2709
2710       option = gtk_printer_option_new ("gtk-cover-after", "After", GTK_PRINTER_OPTION_TYPE_PICKONE);
2711       gtk_printer_option_choices_from_array (option, num_of_covers,
2712                                          cover, cover_display);
2713       if (backend->default_cover_after != NULL)
2714         gtk_printer_option_set (option, backend->default_cover_after);
2715       else
2716         gtk_printer_option_set (option, "none");
2717       set_option_from_settings (option, settings);
2718       gtk_printer_option_set_add (set, option);
2719       g_object_unref (option);
2720
2721       g_strfreev (cover);
2722       g_strfreev (cover_display);
2723     }
2724
2725   option = gtk_printer_option_new ("gtk-print-time", "Print at", GTK_PRINTER_OPTION_TYPE_PICKONE);
2726   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (print_at),
2727                                          print_at, print_at);
2728   gtk_printer_option_set (option, "now");
2729   set_option_from_settings (option, settings);
2730   gtk_printer_option_set_add (set, option);
2731   g_object_unref (option);
2732   
2733   option = gtk_printer_option_new ("gtk-print-time-text", "Print at time", GTK_PRINTER_OPTION_TYPE_STRING);
2734   gtk_printer_option_set (option, "");
2735   set_option_from_settings (option, settings);
2736   gtk_printer_option_set_add (set, option);
2737   g_object_unref (option);
2738   
2739   /* Printer (ppd) specific settings */
2740   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
2741   if (ppd_file)
2742     {
2743       GtkPaperSize *paper_size;
2744       ppd_option_t *option;
2745       const gchar  *ppd_name;
2746
2747       ppdMarkDefaults (ppd_file);
2748
2749       paper_size = gtk_page_setup_get_paper_size (page_setup);
2750
2751       option = ppdFindOption (ppd_file, "PageSize");
2752       ppd_name = gtk_paper_size_get_ppd_name (paper_size);
2753       
2754       if (ppd_name)
2755         strncpy (option->defchoice, ppd_name, PPD_MAX_NAME);
2756       else
2757         {
2758           gchar *custom_name;
2759           char width[G_ASCII_DTOSTR_BUF_SIZE];
2760           char height[G_ASCII_DTOSTR_BUF_SIZE];
2761
2762           g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
2763           g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
2764           custom_name = g_strdup_printf (_("Custom %sx%s"), width, height);
2765           strncpy (option->defchoice, custom_name, PPD_MAX_NAME);
2766           g_free (custom_name);
2767         }
2768
2769       for (i = 0; i < ppd_file->num_groups; i++)
2770         handle_group (set, ppd_file, &ppd_file->groups[i], &ppd_file->groups[i], settings);
2771     }
2772
2773   /* Now honor the user set defaults for this printer */
2774   num_opts = cups_get_user_options (gtk_printer_get_name (printer), 0, &opts);
2775
2776   for (i = 0; i < num_opts; i++)
2777     {
2778       if (STRING_IN_TABLE (opts[i].name, cups_option_blacklist))
2779         continue;
2780
2781       name = get_option_name (opts[i].name);
2782       option = gtk_printer_option_set_lookup (set, name);
2783       if (option)
2784         gtk_printer_option_set (option, opts[i].value);
2785       g_free (name);
2786     }
2787
2788   cupsFreeOptions (num_opts, opts);
2789
2790   return set;
2791 }
2792
2793
2794 static void
2795 mark_option_from_set (GtkPrinterOptionSet *set,
2796                       ppd_file_t          *ppd_file,
2797                       ppd_option_t        *ppd_option)
2798 {
2799   GtkPrinterOption *option;
2800   char *name = get_option_name (ppd_option->keyword);
2801
2802   option = gtk_printer_option_set_lookup (set, name);
2803
2804   if (option)
2805     ppdMarkOption (ppd_file, ppd_option->keyword, option->value);
2806   
2807   g_free (name);
2808 }
2809
2810
2811 static void
2812 mark_group_from_set (GtkPrinterOptionSet *set,
2813                      ppd_file_t          *ppd_file,
2814                      ppd_group_t         *group)
2815 {
2816   int i;
2817
2818   for (i = 0; i < group->num_options; i++)
2819     mark_option_from_set (set, ppd_file, &group->options[i]);
2820
2821   for (i = 0; i < group->num_subgroups; i++)
2822     mark_group_from_set (set, ppd_file, &group->subgroups[i]);
2823 }
2824
2825 static void
2826 set_conflicts_from_option (GtkPrinterOptionSet *set,
2827                            ppd_file_t          *ppd_file,
2828                            ppd_option_t        *ppd_option)
2829 {
2830   GtkPrinterOption *option;
2831   char *name;
2832
2833   if (ppd_option->conflicted)
2834     {
2835       name = get_option_name (ppd_option->keyword);
2836       option = gtk_printer_option_set_lookup (set, name);
2837
2838       if (option)
2839         gtk_printer_option_set_has_conflict (option, TRUE);
2840 #ifdef PRINT_IGNORED_OPTIONS
2841       else
2842         g_warning ("CUPS Backend: Ignoring conflict for option %s", ppd_option->keyword);
2843 #endif
2844       
2845       g_free (name);
2846     }
2847 }
2848
2849 static void
2850 set_conflicts_from_group (GtkPrinterOptionSet *set,
2851                           ppd_file_t          *ppd_file,
2852                           ppd_group_t         *group)
2853 {
2854   int i;
2855
2856   for (i = 0; i < group->num_options; i++)
2857     set_conflicts_from_option (set, ppd_file, &group->options[i]);
2858
2859   for (i = 0; i < group->num_subgroups; i++)
2860     set_conflicts_from_group (set, ppd_file, &group->subgroups[i]);
2861 }
2862
2863 static gboolean
2864 cups_printer_mark_conflicts (GtkPrinter          *printer,
2865                              GtkPrinterOptionSet *options)
2866 {
2867   ppd_file_t *ppd_file;
2868   int num_conflicts;
2869   int i;
2870  
2871   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
2872
2873   if (ppd_file == NULL)
2874     return FALSE;
2875
2876   ppdMarkDefaults (ppd_file);
2877
2878   for (i = 0; i < ppd_file->num_groups; i++)
2879     mark_group_from_set (options, ppd_file, &ppd_file->groups[i]);
2880
2881   num_conflicts = ppdConflicts (ppd_file);
2882
2883   if (num_conflicts > 0)
2884     {
2885       for (i = 0; i < ppd_file->num_groups; i++)
2886         set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]);
2887     }
2888  
2889   return num_conflicts > 0;
2890 }
2891
2892 struct OptionData {
2893   GtkPrinter *printer;
2894   GtkPrinterOptionSet *options;
2895   GtkPrintSettings *settings;
2896   ppd_file_t *ppd_file;
2897 };
2898
2899 typedef struct {
2900   const char *cups;
2901   const char *standard;
2902 } NameMapping;
2903
2904 static void
2905 map_settings_to_option (GtkPrinterOption  *option,
2906                         const NameMapping  table[],
2907                         gint               n_elements,
2908                         GtkPrintSettings  *settings,
2909                         const gchar       *standard_name,
2910                         const gchar       *cups_name)
2911 {
2912   int i;
2913   char *name;
2914   const char *cups_value;
2915   const char *standard_value;
2916
2917   /* If the cups-specific setting is set, always use that */
2918   name = g_strdup_printf ("cups-%s", cups_name);
2919   cups_value = gtk_print_settings_get (settings, name);
2920   g_free (name);
2921   
2922   if (cups_value != NULL) 
2923     {
2924       gtk_printer_option_set (option, cups_value);
2925       return;
2926     }
2927
2928   /* Otherwise we try to convert from the general setting */
2929   standard_value = gtk_print_settings_get (settings, standard_name);
2930   if (standard_value == NULL)
2931     return;
2932
2933   for (i = 0; i < n_elements; i++)
2934     {
2935       if (table[i].cups == NULL && table[i].standard == NULL)
2936         {
2937           gtk_printer_option_set (option, standard_value);
2938           break;
2939         }
2940       else if (table[i].cups == NULL &&
2941                strcmp (table[i].standard, standard_value) == 0)
2942         {
2943           set_option_off (option);
2944           break;
2945         }
2946       else if (strcmp (table[i].standard, standard_value) == 0)
2947         {
2948           gtk_printer_option_set (option, table[i].cups);
2949           break;
2950         }
2951     }
2952 }
2953
2954 static void
2955 map_option_to_settings (const gchar       *value,
2956                         const NameMapping  table[],
2957                         gint               n_elements,
2958                         GtkPrintSettings  *settings,
2959                         const gchar       *standard_name,
2960                         const gchar       *cups_name)
2961 {
2962   int i;
2963   char *name;
2964
2965   for (i = 0; i < n_elements; i++)
2966     {
2967       if (table[i].cups == NULL && table[i].standard == NULL)
2968         {
2969           gtk_print_settings_set (settings,
2970                                   standard_name,
2971                                   value);
2972           break;
2973         }
2974       else if (table[i].cups == NULL && table[i].standard != NULL)
2975         {
2976           if (value_is_off (value))
2977             {
2978               gtk_print_settings_set (settings,
2979                                       standard_name,
2980                                       table[i].standard);
2981               break;
2982             }
2983         }
2984       else if (strcmp (table[i].cups, value) == 0)
2985         {
2986           gtk_print_settings_set (settings,
2987                                   standard_name,
2988                                   table[i].standard);
2989           break;
2990         }
2991     }
2992
2993   /* Always set the corresponding cups-specific setting */
2994   name = g_strdup_printf ("cups-%s", cups_name);
2995   gtk_print_settings_set (settings, name, value);
2996   g_free (name);
2997 }
2998
2999
3000 static const NameMapping paper_source_map[] = {
3001   { "Lower", "lower"},
3002   { "Middle", "middle"},
3003   { "Upper", "upper"},
3004   { "Rear", "rear"},
3005   { "Envelope", "envelope"},
3006   { "Cassette", "cassette"},
3007   { "LargeCapacity", "large-capacity"},
3008   { "AnySmallFormat", "small-format"},
3009   { "AnyLargeFormat", "large-format"},
3010   { NULL, NULL}
3011 };
3012
3013 static const NameMapping output_tray_map[] = {
3014   { "Upper", "upper"},
3015   { "Lower", "lower"},
3016   { "Rear", "rear"},
3017   { NULL, NULL}
3018 };
3019
3020 static const NameMapping duplex_map[] = {
3021   { "DuplexTumble", "vertical" },
3022   { "DuplexNoTumble", "horizontal" },
3023   { NULL, "simplex" }
3024 };
3025
3026 static const NameMapping output_mode_map[] = {
3027   { "Standard", "normal" },
3028   { "Normal", "normal" },
3029   { "Draft", "draft" },
3030   { "Fast", "draft" },
3031 };
3032
3033 static const NameMapping media_type_map[] = {
3034   { "Transparency", "transparency"},
3035   { "Standard", "stationery"},
3036   { NULL, NULL}
3037 };
3038
3039 static const NameMapping all_map[] = {
3040   { NULL, NULL}
3041 };
3042
3043
3044 static void
3045 set_option_from_settings (GtkPrinterOption *option,
3046                           GtkPrintSettings *settings)
3047 {
3048   const char *cups_value;
3049   char *value;
3050   
3051   if (settings == NULL)
3052     return;
3053
3054   if (strcmp (option->name, "gtk-paper-source") == 0)
3055     map_settings_to_option (option, paper_source_map, G_N_ELEMENTS (paper_source_map),
3056                              settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
3057   else if (strcmp (option->name, "gtk-output-tray") == 0)
3058     map_settings_to_option (option, output_tray_map, G_N_ELEMENTS (output_tray_map),
3059                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
3060   else if (strcmp (option->name, "gtk-duplex") == 0)
3061     map_settings_to_option (option, duplex_map, G_N_ELEMENTS (duplex_map),
3062                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
3063   else if (strcmp (option->name, "cups-OutputMode") == 0)
3064     map_settings_to_option (option, output_mode_map, G_N_ELEMENTS (output_mode_map),
3065                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
3066   else if (strcmp (option->name, "cups-Resolution") == 0)
3067     {
3068       cups_value = gtk_print_settings_get (settings, option->name);
3069       if (cups_value)
3070         gtk_printer_option_set (option, cups_value);
3071       else
3072         {
3073           int res = gtk_print_settings_get_resolution (settings);
3074           if (res != 0)
3075             {
3076               value = g_strdup_printf ("%ddpi", res);
3077               gtk_printer_option_set (option, value);
3078               g_free (value);
3079             }
3080         }
3081     }
3082   else if (strcmp (option->name, "gtk-paper-type") == 0)
3083     map_settings_to_option (option, media_type_map, G_N_ELEMENTS (media_type_map),
3084                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
3085   else if (strcmp (option->name, "gtk-n-up") == 0)
3086     {
3087       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
3088                               settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
3089     }
3090   else if (strcmp (option->name, "gtk-billing-info") == 0)
3091     {
3092       cups_value = gtk_print_settings_get (settings, "cups-job-billing");
3093       if (cups_value)
3094         gtk_printer_option_set (option, cups_value);
3095     } 
3096   else if (strcmp (option->name, "gtk-job-prio") == 0)
3097     {
3098       cups_value = gtk_print_settings_get (settings, "cups-job-priority");
3099       if (cups_value)
3100         gtk_printer_option_set (option, cups_value);
3101     } 
3102   else if (strcmp (option->name, "gtk-cover-before") == 0)
3103     {
3104       cups_value = gtk_print_settings_get (settings, "cover-before");
3105       if (cups_value)
3106         gtk_printer_option_set (option, cups_value);
3107     } 
3108   else if (strcmp (option->name, "gtk-cover-after") == 0)
3109     {
3110       cups_value = gtk_print_settings_get (settings, "cover-after");
3111       if (cups_value)
3112         gtk_printer_option_set (option, cups_value);
3113     } 
3114   else if (strcmp (option->name, "gtk-print-time") == 0)
3115     {
3116       cups_value = gtk_print_settings_get (settings, "print-at");
3117       if (cups_value)
3118         gtk_printer_option_set (option, cups_value);
3119     } 
3120   else if (strcmp (option->name, "gtk-print-time-text") == 0)
3121     {
3122       cups_value = gtk_print_settings_get (settings, "print-at-time");
3123       if (cups_value)
3124         gtk_printer_option_set (option, cups_value);
3125     } 
3126   else if (g_str_has_prefix (option->name, "cups-"))
3127     {
3128       cups_value = gtk_print_settings_get (settings, option->name);
3129       if (cups_value)
3130         gtk_printer_option_set (option, cups_value);
3131     } 
3132 }
3133
3134 static void
3135 foreach_option_get_settings (GtkPrinterOption *option,
3136                              gpointer          user_data)
3137 {
3138   struct OptionData *data = user_data;
3139   GtkPrintSettings *settings = data->settings;
3140   const char *value;
3141
3142   value = option->value;
3143
3144   if (strcmp (option->name, "gtk-paper-source") == 0)
3145     map_option_to_settings (value, paper_source_map, G_N_ELEMENTS (paper_source_map),
3146                             settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
3147   else if (strcmp (option->name, "gtk-output-tray") == 0)
3148     map_option_to_settings (value, output_tray_map, G_N_ELEMENTS (output_tray_map),
3149                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
3150   else if (strcmp (option->name, "gtk-duplex") == 0)
3151     map_option_to_settings (value, duplex_map, G_N_ELEMENTS (duplex_map),
3152                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
3153   else if (strcmp (option->name, "cups-OutputMode") == 0)
3154     map_option_to_settings (value, output_mode_map, G_N_ELEMENTS (output_mode_map),
3155                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
3156   else if (strcmp (option->name, "cups-Resolution") == 0)
3157     {
3158       int res = atoi (value);
3159       /* TODO: What if resolution is on XXXxYYYdpi form? */
3160       if (res != 0)
3161         gtk_print_settings_set_resolution (settings, res);
3162       gtk_print_settings_set (settings, option->name, value);
3163     }
3164   else if (strcmp (option->name, "gtk-paper-type") == 0)
3165     map_option_to_settings (value, media_type_map, G_N_ELEMENTS (media_type_map),
3166                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
3167   else if (strcmp (option->name, "gtk-n-up") == 0)
3168     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
3169                             settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
3170   else if (strcmp (option->name, "gtk-billing-info") == 0 && strlen (value) > 0)
3171     gtk_print_settings_set (settings, "cups-job-billing", value);
3172   else if (strcmp (option->name, "gtk-job-prio") == 0)
3173     gtk_print_settings_set (settings, "cups-job-priority", value);
3174   else if (strcmp (option->name, "gtk-cover-before") == 0)
3175     gtk_print_settings_set (settings, "cover-before", value);
3176   else if (strcmp (option->name, "gtk-cover-after") == 0)
3177     gtk_print_settings_set (settings, "cover-after", value);
3178   else if (strcmp (option->name, "gtk-print-time") == 0)
3179     gtk_print_settings_set (settings, "print-at", value);
3180   else if (strcmp (option->name, "gtk-print-time-text") == 0)
3181     gtk_print_settings_set (settings, "print-at-time", value);
3182   else if (g_str_has_prefix (option->name, "cups-"))
3183     gtk_print_settings_set (settings, option->name, value);
3184 }
3185
3186 static void
3187 cups_printer_get_settings_from_options (GtkPrinter          *printer,
3188                                         GtkPrinterOptionSet *options,
3189                                         GtkPrintSettings    *settings)
3190 {
3191   struct OptionData data;
3192   const char *print_at, *print_at_time;
3193
3194   data.printer = printer;
3195   data.options = options;
3196   data.settings = settings;
3197   data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3198  
3199   if (data.ppd_file != NULL)
3200     {
3201       GtkPrinterOption *cover_before, *cover_after;
3202       
3203       gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
3204
3205       cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
3206       cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
3207       if (cover_before && cover_after)
3208         {
3209           char *value = g_strdup_printf ("%s,%s", cover_before->value, cover_after->value);
3210           gtk_print_settings_set (settings, "cups-job-sheets", value);
3211           g_free (value);
3212         }
3213
3214       print_at = gtk_print_settings_get (settings, "print-at");
3215       print_at_time = gtk_print_settings_get (settings, "print-at-time");
3216       if (strcmp (print_at, "at") == 0)
3217         gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time);
3218       else if (strcmp (print_at, "on-hold") == 0)
3219         gtk_print_settings_set (settings, "cups-job-hold-until", "indefinite");
3220     }
3221 }
3222
3223 static void
3224 cups_printer_prepare_for_print (GtkPrinter       *printer,
3225                                 GtkPrintJob      *print_job,
3226                                 GtkPrintSettings *settings,
3227                                 GtkPageSetup     *page_setup)
3228 {
3229   GtkPageSet page_set;
3230   GtkPaperSize *paper_size;
3231   const char *ppd_paper_name;
3232   double scale;
3233
3234   print_job->print_pages = gtk_print_settings_get_print_pages (settings);
3235   print_job->page_ranges = NULL;
3236   print_job->num_page_ranges = 0;
3237   
3238   if (print_job->print_pages == GTK_PRINT_PAGES_RANGES)
3239     print_job->page_ranges =
3240       gtk_print_settings_get_page_ranges (settings,
3241                                           &print_job->num_page_ranges);
3242   
3243   if (gtk_print_settings_get_collate (settings))
3244     gtk_print_settings_set (settings, "cups-Collate", "True");
3245   print_job->collate = FALSE;
3246
3247   if (gtk_print_settings_get_reverse (settings))
3248     gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
3249   print_job->reverse = FALSE;
3250
3251   if (gtk_print_settings_get_n_copies (settings) > 1)
3252     gtk_print_settings_set_int (settings, "cups-copies",
3253                                 gtk_print_settings_get_n_copies (settings));
3254   print_job->num_copies = 1;
3255
3256   scale = gtk_print_settings_get_scale (settings);
3257   print_job->scale = 1.0;
3258   if (scale != 100.0)
3259     print_job->scale = scale/100.0;
3260
3261   page_set = gtk_print_settings_get_page_set (settings);
3262   if (page_set == GTK_PAGE_SET_EVEN)
3263     gtk_print_settings_set (settings, "cups-page-set", "even");
3264   else if (page_set == GTK_PAGE_SET_ODD)
3265     gtk_print_settings_set (settings, "cups-page-set", "odd");
3266   print_job->page_set = GTK_PAGE_SET_ALL;
3267
3268   paper_size = gtk_page_setup_get_paper_size (page_setup);
3269   ppd_paper_name = gtk_paper_size_get_ppd_name (paper_size);
3270   if (ppd_paper_name != NULL)
3271     gtk_print_settings_set (settings, "cups-PageSize", ppd_paper_name);
3272   else
3273     {
3274       char width[G_ASCII_DTOSTR_BUF_SIZE];
3275       char height[G_ASCII_DTOSTR_BUF_SIZE];
3276       char *custom_name;
3277
3278       g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
3279       g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
3280       custom_name = g_strdup_printf (("Custom.%sx%s"), width, height);
3281       gtk_print_settings_set (settings, "cups-PageSize", custom_name);
3282       g_free (custom_name);
3283     }
3284
3285   print_job->rotate_to_orientation = TRUE;
3286 }
3287
3288 static GtkPageSetup *
3289 create_page_setup (ppd_file_t *ppd_file,
3290                    ppd_size_t *size)
3291  {
3292    char *display_name;
3293    GtkPageSetup *page_setup;
3294    GtkPaperSize *paper_size;
3295    ppd_option_t *option;
3296    ppd_choice_t *choice;
3297
3298   display_name = NULL;
3299   option = ppdFindOption (ppd_file, "PageSize");
3300   if (option)
3301     {
3302       choice = ppdFindChoice (option, size->name);
3303       if (choice)
3304         display_name = ppd_text_to_utf8 (ppd_file, choice->text);
3305     }
3306
3307   if (display_name == NULL)
3308     display_name = g_strdup (size->name);
3309   
3310   page_setup = gtk_page_setup_new ();
3311   paper_size = gtk_paper_size_new_from_ppd (size->name,
3312                                             display_name,
3313                                             size->width,
3314                                             size->length);
3315   gtk_page_setup_set_paper_size (page_setup, paper_size);
3316   gtk_paper_size_free (paper_size);
3317   
3318   gtk_page_setup_set_top_margin (page_setup, size->length - size->top, GTK_UNIT_POINTS);
3319   gtk_page_setup_set_bottom_margin (page_setup, size->bottom, GTK_UNIT_POINTS);
3320   gtk_page_setup_set_left_margin (page_setup, size->left, GTK_UNIT_POINTS);
3321   gtk_page_setup_set_right_margin (page_setup, size->width - size->right, GTK_UNIT_POINTS);
3322   
3323   g_free (display_name);
3324
3325   return page_setup;
3326 }
3327
3328 static GList *
3329 cups_printer_list_papers (GtkPrinter *printer)
3330 {
3331   ppd_file_t *ppd_file;
3332   ppd_size_t *size;
3333   GtkPageSetup *page_setup;
3334   GList *l;
3335   int i;
3336
3337   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3338   if (ppd_file == NULL)
3339     return NULL;
3340
3341   l = NULL;
3342   
3343   for (i = 0; i < ppd_file->num_sizes; i++)
3344     {
3345       size = &ppd_file->sizes[i];      
3346
3347       page_setup = create_page_setup (ppd_file, size);
3348
3349       l = g_list_prepend (l, page_setup);
3350     }
3351
3352   return g_list_reverse (l);
3353 }
3354
3355 static GtkPageSetup *
3356 cups_printer_get_default_page_size (GtkPrinter *printer)
3357 {
3358   ppd_file_t *ppd_file;
3359   ppd_size_t *size;
3360   ppd_option_t *option;
3361
3362
3363   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3364   if (ppd_file == NULL)
3365     return NULL;
3366
3367   option = ppdFindOption (ppd_file, "PageSize");
3368   size = ppdPageSize (ppd_file, option->defchoice); 
3369
3370   return create_page_setup (ppd_file, size);
3371 }
3372
3373 static void
3374 cups_printer_get_hard_margins (GtkPrinter *printer,
3375                                gdouble    *top,
3376                                gdouble    *bottom,
3377                                gdouble    *left,
3378                                gdouble    *right)
3379 {
3380   ppd_file_t *ppd_file;
3381
3382   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3383   if (ppd_file == NULL)
3384     return;
3385
3386   *left = ppd_file->custom_margins[0];
3387   *bottom = ppd_file->custom_margins[1];
3388   *right = ppd_file->custom_margins[2];
3389   *top = ppd_file->custom_margins[3];
3390 }
3391
3392 static GtkPrintCapabilities
3393 cups_printer_get_capabilities (GtkPrinter *printer)
3394 {
3395   return
3396     GTK_PRINT_CAPABILITY_COPIES |
3397     GTK_PRINT_CAPABILITY_COLLATE |
3398     GTK_PRINT_CAPABILITY_REVERSE |
3399     GTK_PRINT_CAPABILITY_NUMBER_UP;
3400 }