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