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