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