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