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