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