]> Pileus Git - ~andy/gtk/blob - modules/printbackends/cups/gtkprintbackendcups.c
Bug 571015 libprintbackend-cups has unlocalized strings
[~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   { "Resolution", N_("Resolution") },
2150   { "PreFilter", N_("GhostScript pre-filtering") },
2151 };
2152
2153
2154 static const struct {
2155   const char *keyword;
2156   const char *choice;
2157   const char *translation;
2158 } cups_choice_translations[] = {
2159   { "Duplex", "None", N_("One Sided") },
2160   /* Translators: this is an option of "Two Sided" */
2161   { "Duplex", "DuplexNoTumble", N_("Long Edge (Standard)") },
2162   /* Translators: this is an option of "Two Sided" */
2163   { "Duplex", "DuplexTumble", N_("Short Edge (Flip)") },
2164   /* Translators: this is an option of "Paper Source" */
2165   { "InputSlot", "Auto", N_("Auto Select") },
2166   /* Translators: this is an option of "Paper Source" */
2167   { "InputSlot", "AutoSelect", N_("Auto Select") },
2168   /* Translators: this is an option of "Paper Source" */
2169   { "InputSlot", "Default", N_("Printer Default") },
2170   /* Translators: this is an option of "Paper Source" */
2171   { "InputSlot", "None", N_("Printer Default") },
2172   /* Translators: this is an option of "Paper Source" */
2173   { "InputSlot", "PrinterDefault", N_("Printer Default") },
2174   /* Translators: this is an option of "Paper Source" */
2175   { "InputSlot", "Unspecified", N_("Auto Select") },
2176   /* Translators: this is an option of "Resolution" */
2177   { "Resolution", "default", N_("Printer Default") },
2178   /* Translators: this is an option of "GhostScript" */
2179   { "PreFilter", "EmbedFonts", N_("Embed GhostScript fonts only") },
2180   /* Translators: this is an option of "GhostScript" */
2181   { "PreFilter", "Level1", N_("Convert to PS level 1") },
2182   /* Translators: this is an option of "GhostScript" */
2183   { "PreFilter", "Level2", N_("Convert to PS level 2") },
2184   /* Translators: this is an option of "GhostScript" */
2185   { "PreFilter", "No", N_("No pre-filtering") },
2186 };
2187
2188 static const struct {
2189   const char *name;
2190   const char *translation;
2191 } cups_group_translations[] = {
2192   { "Miscellaneous", N_("Miscellaneous") },
2193 };
2194
2195 static const struct {
2196   const char *ppd_keyword;
2197   const char *name;
2198 } option_names[] = {
2199   {"Duplex", "gtk-duplex" },
2200   {"MediaType", "gtk-paper-type"},
2201   {"InputSlot", "gtk-paper-source"},
2202   {"OutputBin", "gtk-output-tray"},
2203 };
2204
2205 /* keep sorted when changing */
2206 static const char *color_option_whitelist[] = {
2207   "BRColorEnhancement",
2208   "BRColorMatching",
2209   "BRColorMatching",
2210   "BRColorMode",
2211   "BRGammaValue",
2212   "BRImprovedGray",
2213   "BlackSubstitution",
2214   "ColorModel",
2215   "HPCMYKInks",
2216   "HPCSGraphics",
2217   "HPCSImages",
2218   "HPCSText",
2219   "HPColorSmart",
2220   "RPSBlackMode",
2221   "RPSBlackOverPrint",
2222   "Rcmyksimulation",
2223 };
2224
2225 /* keep sorted when changing */
2226 static const char *color_group_whitelist[] = {
2227   "ColorPage",
2228   "FPColorWise1",
2229   "FPColorWise2",
2230   "FPColorWise3",
2231   "FPColorWise4",
2232   "FPColorWise5",
2233   "HPColorOptionsPanel",
2234 };
2235   
2236 /* keep sorted when changing */
2237 static const char *image_quality_option_whitelist[] = {
2238   "BRDocument",
2239   "BRHalfTonePattern",
2240   "BRNormalPrt",
2241   "BRPrintQuality",
2242   "BitsPerPixel",
2243   "Darkness",
2244   "Dithering",
2245   "EconoMode",
2246   "Economode",
2247   "HPEconoMode",
2248   "HPEdgeControl",
2249   "HPGraphicsHalftone",
2250   "HPHalftone",
2251   "HPLJDensity",
2252   "HPPhotoHalftone",
2253   "OutputMode",
2254   "REt",
2255   "RPSBitsPerPixel",
2256   "RPSDitherType",
2257   "Resolution",
2258   "ScreenLock",
2259   "Smoothing",
2260   "TonerSaveMode",
2261   "UCRGCRForImage",
2262 };
2263
2264 /* keep sorted when changing */
2265 static const char *image_quality_group_whitelist[] = {
2266   "FPImageQuality1",
2267   "FPImageQuality2",
2268   "FPImageQuality3",
2269   "ImageQualityPage",
2270 };
2271
2272 /* keep sorted when changing */
2273 static const char * finishing_option_whitelist[] = {
2274   "BindColor",
2275   "BindEdge",
2276   "BindType",
2277   "BindWhen",
2278   "Booklet",
2279   "FoldType",
2280   "FoldWhen",
2281   "HPStaplerOptions",
2282   "Jog",
2283   "Slipsheet",
2284   "Sorter",
2285   "StapleLocation",
2286   "StapleOrientation",
2287   "StapleWhen",
2288   "StapleX",
2289   "StapleY",
2290 };
2291
2292 /* keep sorted when changing */
2293 static const char *finishing_group_whitelist[] = {
2294   "FPFinishing1",
2295   "FPFinishing2",
2296   "FPFinishing3",
2297   "FPFinishing4",
2298   "FinishingPage",
2299   "HPFinishingPanel",
2300 };
2301
2302 /* keep sorted when changing */
2303 static const char *cups_option_blacklist[] = {
2304   "Collate",
2305   "Copies", 
2306   "OutputOrder",
2307   "PageRegion",
2308   "PageSize",
2309 };
2310
2311 static char *
2312 get_option_text (ppd_file_t   *ppd_file, 
2313                  ppd_option_t *option)
2314 {
2315   int i;
2316   char *utf8;
2317   
2318   for (i = 0; i < G_N_ELEMENTS (cups_option_translations); i++)
2319     {
2320       if (strcmp (cups_option_translations[i].keyword, option->keyword) == 0)
2321         return g_strdup (_(cups_option_translations[i].translation));
2322     }
2323
2324   utf8 = ppd_text_to_utf8 (ppd_file, option->text);
2325
2326   /* Some ppd files have spaces in the text before the colon */
2327   g_strchomp (utf8);
2328   
2329   return utf8;
2330 }
2331
2332 static char *
2333 get_choice_text (ppd_file_t   *ppd_file, 
2334                  ppd_choice_t *choice)
2335 {
2336   int i;
2337   ppd_option_t *option = choice->option;
2338   const char *keyword = option->keyword;
2339   
2340   for (i = 0; i < G_N_ELEMENTS (cups_choice_translations); i++)
2341     {
2342       if (strcmp (cups_choice_translations[i].keyword, keyword) == 0 &&
2343           strcmp (cups_choice_translations[i].choice, choice->choice) == 0)
2344         return g_strdup (_(cups_choice_translations[i].translation));
2345     }
2346   return ppd_text_to_utf8 (ppd_file, choice->text);
2347 }
2348
2349 static gboolean
2350 group_has_option (ppd_group_t  *group, 
2351                   ppd_option_t *option)
2352 {
2353   int i;
2354
2355   if (group == NULL)
2356     return FALSE;
2357   
2358   if (group->num_options > 0 &&
2359       option >= group->options && option < group->options + group->num_options)
2360     return TRUE;
2361   
2362   for (i = 0; i < group->num_subgroups; i++)
2363     {
2364       if (group_has_option (&group->subgroups[i],option))
2365         return TRUE;
2366     }
2367   return FALSE;
2368 }
2369
2370 static void
2371 set_option_off (GtkPrinterOption *option)
2372 {
2373   /* Any of these will do, _set only applies the value
2374    * if its allowed of the option */
2375   gtk_printer_option_set (option, "False");
2376   gtk_printer_option_set (option, "Off");
2377   gtk_printer_option_set (option, "None");
2378 }
2379
2380 static gboolean
2381 value_is_off (const char *value)
2382 {
2383   return  (strcasecmp (value, "None") == 0 ||
2384            strcasecmp (value, "Off") == 0 ||
2385            strcasecmp (value, "False") == 0);
2386 }
2387
2388 static char *
2389 ppd_group_name (ppd_group_t *group)
2390 {
2391 #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) 
2392   return group->name;
2393 #else
2394   return group->text;
2395 #endif
2396 }
2397
2398 static int
2399 available_choices (ppd_file_t     *ppd,
2400                    ppd_option_t   *option,
2401                    ppd_choice_t ***available,
2402                    gboolean        keep_if_only_one_option)
2403 {
2404   ppd_option_t *other_option;
2405   int i, j;
2406   gchar *conflicts;
2407   ppd_const_t *constraint;
2408   const char *choice, *other_choice;
2409   ppd_option_t *option1, *option2;
2410   ppd_group_t *installed_options;
2411   int num_conflicts;
2412   gboolean all_default;
2413   int add_auto;
2414
2415   if (available)
2416     *available = NULL;
2417
2418   conflicts = g_new0 (char, option->num_choices);
2419
2420   installed_options = NULL;
2421   for (i = 0; i < ppd->num_groups; i++)
2422     {
2423       char *name; 
2424
2425       name = ppd_group_name (&ppd->groups[i]);
2426       if (strcmp (name, "InstallableOptions") == 0)
2427         {
2428           installed_options = &ppd->groups[i];
2429           break;
2430         }
2431     }
2432
2433   for (i = ppd->num_consts, constraint = ppd->consts; i > 0; i--, constraint++)
2434     {
2435       option1 = ppdFindOption (ppd, constraint->option1);
2436       if (option1 == NULL)
2437         continue;
2438
2439       option2 = ppdFindOption (ppd, constraint->option2);
2440       if (option2 == NULL)
2441         continue;
2442
2443       if (option == option1)
2444         {
2445           choice = constraint->choice1;
2446           other_option = option2;
2447           other_choice = constraint->choice2;
2448         }
2449       else if (option == option2)
2450         {
2451           choice = constraint->choice2;
2452           other_option = option1;
2453           other_choice = constraint->choice1;
2454         }
2455       else
2456         continue;
2457
2458       /* We only care of conflicts with installed_options and
2459          PageSize */
2460       if (!group_has_option (installed_options, other_option) &&
2461           (strcmp (other_option->keyword, "PageSize") != 0))
2462         continue;
2463
2464       if (*other_choice == 0)
2465         {
2466           /* Conflict only if the installed option is not off */
2467           if (value_is_off (other_option->defchoice))
2468             continue;
2469         }
2470       /* Conflict if the installed option has the specified default */
2471       else if (strcasecmp (other_choice, other_option->defchoice) != 0)
2472         continue;
2473
2474       if (*choice == 0)
2475         {
2476           /* Conflict with all non-off choices */
2477           for (j = 0; j < option->num_choices; j++)
2478             {
2479               if (!value_is_off (option->choices[j].choice))
2480                 conflicts[j] = 1;
2481             }
2482         }
2483       else
2484         {
2485           for (j = 0; j < option->num_choices; j++)
2486             {
2487               if (strcasecmp (option->choices[j].choice, choice) == 0)
2488                 conflicts[j] = 1;
2489             }
2490         }
2491     }
2492
2493   num_conflicts = 0;
2494   all_default = TRUE;
2495   for (j = 0; j < option->num_choices; j++)
2496     {
2497       if (conflicts[j])
2498         num_conflicts++;
2499       else if (strcmp (option->choices[j].choice, option->defchoice) != 0)
2500         all_default = FALSE;
2501     }
2502
2503   if ((all_default && !keep_if_only_one_option) ||
2504       (num_conflicts == option->num_choices))
2505     {
2506       g_free (conflicts);
2507
2508       return 0;
2509     }
2510
2511   /* Some ppds don't have a "use printer default" option for
2512    * InputSlot. This means you always have to select a particular slot,
2513    * and you can't auto-pick source based on the paper size. To support
2514    * this we always add an auto option if there isn't one already. If
2515    * the user chooses the generated option we don't send any InputSlot
2516    * value when printing. The way we detect existing auto-cases is based
2517    * on feedback from Michael Sweet of cups fame.
2518    */
2519   add_auto = 0;
2520   if (strcmp (option->keyword, "InputSlot") == 0)
2521     {
2522       gboolean found_auto = FALSE;
2523       for (j = 0; j < option->num_choices; j++)
2524         {
2525           if (!conflicts[j])
2526             {
2527               if (strcmp (option->choices[j].choice, "Auto") == 0 ||
2528                   strcmp (option->choices[j].choice, "AutoSelect") == 0 ||
2529                   strcmp (option->choices[j].choice, "Default") == 0 ||
2530                   strcmp (option->choices[j].choice, "None") == 0 ||
2531                   strcmp (option->choices[j].choice, "PrinterDefault") == 0 ||
2532                   strcmp (option->choices[j].choice, "Unspecified") == 0 ||
2533                   option->choices[j].code == NULL ||
2534                   option->choices[j].code[0] == 0)
2535                 {
2536                   found_auto = TRUE;
2537                   break;
2538                 }
2539             }
2540         }
2541
2542       if (!found_auto)
2543         add_auto = 1;
2544     }
2545   
2546   if (available)
2547     {
2548       *available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto);
2549
2550       i = 0;
2551       for (j = 0; j < option->num_choices; j++)
2552         {
2553           if (!conflicts[j])
2554             (*available)[i++] = &option->choices[j];
2555         }
2556
2557       if (add_auto) 
2558         (*available)[i++] = NULL;
2559     }
2560
2561   g_free (conflicts);
2562   
2563   return option->num_choices - num_conflicts + add_auto;
2564 }
2565
2566 static GtkPrinterOption *
2567 create_pickone_option (ppd_file_t   *ppd_file,
2568                        ppd_option_t *ppd_option,
2569                        const gchar  *gtk_name)
2570 {
2571   GtkPrinterOption *option;
2572   ppd_choice_t **available;
2573   char *label;
2574   int n_choices;
2575   int i;
2576 #ifdef HAVE_CUPS_API_1_2
2577   ppd_coption_t *coption;
2578 #endif
2579
2580   g_assert (ppd_option->ui == PPD_UI_PICKONE);
2581   
2582   option = NULL;
2583
2584   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
2585   if (n_choices > 0)
2586     {
2587       
2588       /* right now only support one parameter per custom option 
2589        * if more than one print warning and only offer the default choices
2590        */
2591
2592       label = get_option_text (ppd_file, ppd_option);
2593
2594 #ifdef HAVE_CUPS_API_1_2
2595       coption = ppdFindCustomOption (ppd_file, ppd_option->keyword);
2596
2597       if (coption)
2598         {
2599           ppd_cparam_t *cparam;
2600
2601           cparam = ppdFirstCustomParam (coption);
2602
2603           if (ppdNextCustomParam (coption) == NULL)
2604             {
2605               switch (cparam->type)
2606                 {
2607                 case PPD_CUSTOM_INT:
2608                   option = gtk_printer_option_new (gtk_name, label,
2609                                          GTK_PRINTER_OPTION_TYPE_PICKONE_INT);
2610                   break;
2611                 case PPD_CUSTOM_PASSCODE:
2612                   option = gtk_printer_option_new (gtk_name, label,
2613                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE);
2614                   break;
2615                 case PPD_CUSTOM_PASSWORD:
2616                     option = gtk_printer_option_new (gtk_name, label,
2617                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD);
2618                   break;
2619                case PPD_CUSTOM_REAL:
2620                     option = gtk_printer_option_new (gtk_name, label,
2621                                          GTK_PRINTER_OPTION_TYPE_PICKONE_REAL);
2622                   break;
2623                 case PPD_CUSTOM_STRING:
2624                   option = gtk_printer_option_new (gtk_name, label,
2625                                          GTK_PRINTER_OPTION_TYPE_PICKONE_STRING);
2626                   break;
2627 #ifdef PRINT_IGNORED_OPTIONS
2628                 case PPD_CUSTOM_POINTS: 
2629                   g_warning ("CUPS Backend: PPD Custom Points Option not supported");
2630                   break;
2631                 case PPD_CUSTOM_CURVE:
2632                   g_warning ("CUPS Backend: PPD Custom Curve Option not supported");
2633                   break;
2634                 case PPD_CUSTOM_INVCURVE:       
2635                   g_warning ("CUPS Backend: PPD Custom Inverse Curve Option not supported");
2636                   break;
2637 #endif
2638                 default: 
2639                   break;
2640                 }
2641             }
2642 #ifdef PRINT_IGNORED_OPTIONS
2643           else
2644             g_warning ("CUPS Backend: Multi-parameter PPD Custom Option not supported");
2645 #endif
2646         }
2647 #endif /* HAVE_CUPS_API_1_2 */
2648
2649       if (!option)
2650         option = gtk_printer_option_new (gtk_name, label,
2651                                          GTK_PRINTER_OPTION_TYPE_PICKONE);
2652       g_free (label);
2653       
2654       gtk_printer_option_allocate_choices (option, n_choices);
2655       for (i = 0; i < n_choices; i++)
2656         {
2657           if (available[i] == NULL)
2658             {
2659               /* This was auto-added */
2660               option->choices[i] = g_strdup ("gtk-ignore-value");
2661               option->choices_display[i] = g_strdup (_("Printer Default"));
2662             }
2663           else
2664             {
2665               option->choices[i] = g_strdup (available[i]->choice);
2666               option->choices_display[i] = get_choice_text (ppd_file, available[i]);
2667             }
2668         }
2669       gtk_printer_option_set (option, ppd_option->defchoice);
2670     }
2671 #ifdef PRINT_IGNORED_OPTIONS
2672   else
2673     g_warning ("CUPS Backend: Ignoring pickone %s\n", ppd_option->text);
2674 #endif
2675   g_free (available);
2676
2677   return option;
2678 }
2679
2680 static GtkPrinterOption *
2681 create_boolean_option (ppd_file_t   *ppd_file,
2682                        ppd_option_t *ppd_option,
2683                        const gchar  *gtk_name)
2684 {
2685   GtkPrinterOption *option;
2686   ppd_choice_t **available;
2687   char *label;
2688   int n_choices;
2689
2690   g_assert (ppd_option->ui == PPD_UI_BOOLEAN);
2691   
2692   option = NULL;
2693
2694   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
2695   if (n_choices == 2)
2696     {
2697       label = get_option_text (ppd_file, ppd_option);
2698       option = gtk_printer_option_new (gtk_name, label,
2699                                        GTK_PRINTER_OPTION_TYPE_BOOLEAN);
2700       g_free (label);
2701       
2702       gtk_printer_option_allocate_choices (option, 2);
2703       option->choices[0] = g_strdup ("True");
2704       option->choices_display[0] = g_strdup ("True");
2705       option->choices[1] = g_strdup ("False");
2706       option->choices_display[1] = g_strdup ("False");
2707       
2708       gtk_printer_option_set (option, ppd_option->defchoice);
2709     }
2710 #ifdef PRINT_IGNORED_OPTIONS
2711   else
2712     g_warning ("CUPS Backend: Ignoring boolean %s\n", ppd_option->text);
2713 #endif
2714   g_free (available);
2715
2716   return option;
2717 }
2718
2719 static gchar *
2720 get_option_name (const gchar *keyword)
2721 {
2722   int i;
2723
2724   for (i = 0; i < G_N_ELEMENTS (option_names); i++)
2725     if (strcmp (option_names[i].ppd_keyword, keyword) == 0)
2726       return g_strdup (option_names[i].name);
2727
2728   return g_strdup_printf ("cups-%s", keyword);
2729 }
2730
2731 static int
2732 strptr_cmp (const void *a, 
2733             const void *b)
2734 {
2735   char **aa = (char **)a;
2736   char **bb = (char **)b;
2737   return strcmp (*aa, *bb);
2738 }
2739
2740
2741 static gboolean
2742 string_in_table (gchar       *str, 
2743                  const gchar *table[], 
2744                  gint         table_len)
2745 {
2746   return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
2747 }
2748
2749 #define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
2750
2751 static void
2752 handle_option (GtkPrinterOptionSet *set,
2753                ppd_file_t          *ppd_file,
2754                ppd_option_t        *ppd_option,
2755                ppd_group_t         *toplevel_group,
2756                GtkPrintSettings    *settings)
2757 {
2758   GtkPrinterOption *option;
2759   char *name;
2760   int i;
2761
2762   if (STRING_IN_TABLE (ppd_option->keyword, cups_option_blacklist))
2763     return;
2764
2765   name = get_option_name (ppd_option->keyword);
2766
2767   option = NULL;
2768   if (ppd_option->ui == PPD_UI_PICKONE)
2769     {
2770       option = create_pickone_option (ppd_file, ppd_option, name);
2771     }
2772   else if (ppd_option->ui == PPD_UI_BOOLEAN)
2773     {
2774       option = create_boolean_option (ppd_file, ppd_option, name);
2775     }
2776 #ifdef PRINT_IGNORED_OPTIONS
2777   else
2778     g_warning ("CUPS Backend: Ignoring pickmany setting %s\n", ppd_option->text);
2779 #endif  
2780   
2781   if (option)
2782     {
2783       char *name;
2784
2785       name = ppd_group_name (toplevel_group);
2786       if (STRING_IN_TABLE (name,
2787                            color_group_whitelist) ||
2788           STRING_IN_TABLE (ppd_option->keyword,
2789                            color_option_whitelist))
2790         {
2791           option->group = g_strdup ("ColorPage");
2792         }
2793       else if (STRING_IN_TABLE (name,
2794                                 image_quality_group_whitelist) ||
2795                STRING_IN_TABLE (ppd_option->keyword,
2796                                 image_quality_option_whitelist))
2797         {
2798           option->group = g_strdup ("ImageQualityPage");
2799         }
2800       else if (STRING_IN_TABLE (name,
2801                                 finishing_group_whitelist) ||
2802                STRING_IN_TABLE (ppd_option->keyword,
2803                                 finishing_option_whitelist))
2804         {
2805           option->group = g_strdup ("FinishingPage");
2806         }
2807       else
2808         {
2809           for (i = 0; i < G_N_ELEMENTS (cups_group_translations); i++)
2810             {
2811               if (strcmp (cups_group_translations[i].name, toplevel_group->name) == 0)
2812                 {
2813                   option->group = g_strdup (_(cups_group_translations[i].translation));
2814                   break;
2815                 }
2816             }
2817
2818           if (i == G_N_ELEMENTS (cups_group_translations))
2819             option->group = g_strdup (toplevel_group->text);
2820         }
2821
2822       set_option_from_settings (option, settings);
2823       
2824       gtk_printer_option_set_add (set, option);
2825     }
2826   
2827   g_free (name);
2828 }
2829
2830 static void
2831 handle_group (GtkPrinterOptionSet *set,
2832               ppd_file_t          *ppd_file,
2833               ppd_group_t         *group,
2834               ppd_group_t         *toplevel_group,
2835               GtkPrintSettings    *settings)
2836 {
2837   gint i;
2838   gchar *name;
2839   
2840   /* Ignore installable options */
2841   name = ppd_group_name (toplevel_group);
2842   if (strcmp (name, "InstallableOptions") == 0)
2843     return;
2844   
2845   for (i = 0; i < group->num_options; i++)
2846     handle_option (set, ppd_file, &group->options[i], toplevel_group, settings);
2847
2848   for (i = 0; i < group->num_subgroups; i++)
2849     handle_group (set, ppd_file, &group->subgroups[i], toplevel_group, settings);
2850
2851 }
2852
2853 static GtkPrinterOptionSet *
2854 cups_printer_get_options (GtkPrinter           *printer,
2855                           GtkPrintSettings     *settings,
2856                           GtkPageSetup         *page_setup,
2857                           GtkPrintCapabilities  capabilities)
2858 {
2859   GtkPrinterOptionSet *set;
2860   GtkPrinterOption *option;
2861   ppd_file_t *ppd_file;
2862   int i;
2863   char *print_at[] = { "now", "at", "on-hold" };
2864   char *n_up[] = {"1", "2", "4", "6", "9", "16" };
2865   char *prio[] = {"100", "80", "50", "30" };
2866   /* Translators: These strings name the possible values of the 
2867    * job priority option in the print dialog
2868    */
2869   char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low") };
2870   char *n_up_layout[] = { "lrtb", "lrbt", "rltb", "rlbt", "tblr", "tbrl", "btlr", "btrl" };
2871   /* Translators: These strings name the possible arrangements of
2872    * multiple pages on a sheet when printing
2873    */
2874   char *n_up_layout_display[] = { N_("Left to right, top to bottom"), N_("Left to right, bottom to top"), 
2875                                   N_("Right to left, top to bottom"), N_("Right to left, bottom to top"), 
2876                                   N_("Top to bottom, left to right"), N_("Top to bottom, right to left"), 
2877                                   N_("Bottom to top, left to right"), N_("Bottom to top, right to left") };
2878   char *name;
2879   int num_opts;
2880   cups_option_t *opts = NULL;
2881   GtkPrintBackendCups *backend;
2882   GtkTextDirection text_direction;
2883
2884
2885   set = gtk_printer_option_set_new ();
2886
2887   /* Cups specific, non-ppd related settings */
2888
2889    /* Translators, this string is used to label the pages-per-sheet option 
2890     * in the print dialog 
2891     */
2892   option = gtk_printer_option_new ("gtk-n-up", _("Pages per Sheet"), GTK_PRINTER_OPTION_TYPE_PICKONE);
2893   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up),
2894                                          n_up, n_up);
2895   gtk_printer_option_set (option, "1");
2896   set_option_from_settings (option, settings);
2897   gtk_printer_option_set_add (set, option);
2898   g_object_unref (option);
2899
2900   if (cups_printer_get_capabilities (printer) & GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT)
2901     {
2902       for (i = 0; i < G_N_ELEMENTS (n_up_layout_display); i++)
2903         n_up_layout_display[i] = _(n_up_layout_display[i]);
2904   
2905        /* Translators, this string is used to label the option in the print 
2906         * dialog that controls in what order multiple pages are arranged 
2907         */
2908       option = gtk_printer_option_new ("gtk-n-up-layout", _("Page Ordering"), GTK_PRINTER_OPTION_TYPE_PICKONE);
2909       gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up_layout),
2910                                              n_up_layout, n_up_layout_display);
2911
2912       text_direction = gtk_widget_get_default_direction ();
2913       if (text_direction == GTK_TEXT_DIR_LTR)
2914         gtk_printer_option_set (option, "lrtb");
2915       else
2916         gtk_printer_option_set (option, "rltb");
2917
2918       set_option_from_settings (option, settings);
2919       gtk_printer_option_set_add (set, option);
2920       g_object_unref (option);
2921     }
2922
2923   for (i = 0; i < G_N_ELEMENTS(prio_display); i++)
2924     prio_display[i] = _(prio_display[i]);
2925   
2926   /* Translators, this string is used to label the job priority option 
2927    * in the print dialog 
2928    */
2929   option = gtk_printer_option_new ("gtk-job-prio", _("Job Priority"), GTK_PRINTER_OPTION_TYPE_PICKONE);
2930   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (prio),
2931                                          prio, prio_display);
2932   gtk_printer_option_set (option, "50");
2933   set_option_from_settings (option, settings);
2934   gtk_printer_option_set_add (set, option);
2935   g_object_unref (option);
2936
2937   /* Translators, this string is used to label the billing info entry
2938    * in the print dialog 
2939    */
2940   option = gtk_printer_option_new ("gtk-billing-info", _("Billing Info"), GTK_PRINTER_OPTION_TYPE_STRING);
2941   gtk_printer_option_set (option, "");
2942   set_option_from_settings (option, settings);
2943   gtk_printer_option_set_add (set, option);
2944   g_object_unref (option);
2945
2946   backend = GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer));
2947
2948   if (backend != NULL)
2949     {
2950       char *cover_default[] = {"none", "classified", "confidential", "secret", "standard", "topsecret", "unclassified" };
2951       /* Translators, these strings are names for various 'standard' cover 
2952        * pages that the printing system may support.
2953        */
2954       char *cover_display_default[] = {N_("None"), N_("Classified"), N_("Confidential"), N_("Secret"), N_("Standard"), N_("Top Secret"), N_("Unclassified"),};
2955       char **cover = NULL;
2956       char **cover_display = NULL;
2957       char **cover_display_translated = NULL;
2958       gint num_of_covers = 0;
2959       gpointer value;
2960       gint j;
2961
2962       num_of_covers = backend->number_of_covers;
2963       cover = g_new (char *, num_of_covers + 1);
2964       cover[num_of_covers] = NULL;
2965       cover_display = g_new (char *, num_of_covers + 1);
2966       cover_display[num_of_covers] = NULL;
2967       cover_display_translated = g_new (char *, num_of_covers + 1);
2968       cover_display_translated[num_of_covers] = NULL;
2969
2970       for (i = 0; i < num_of_covers; i++)
2971         {
2972           cover[i] = g_strdup (backend->covers[i]);
2973           value = NULL;
2974           for (j = 0; j < G_N_ELEMENTS (cover_default); j++)
2975             if (strcmp (cover_default[j], cover[i]) == 0)
2976               {
2977                 value = cover_display_default[j];
2978                 break;
2979               }
2980           cover_display[i] = (value != NULL) ? g_strdup (value) : g_strdup (backend->covers[i]);
2981         }
2982
2983       for (i = 0; i < num_of_covers; i++)
2984         cover_display_translated[i] = _(cover_display[i]);
2985   
2986       /* Translators, this is the label used for the option in the print 
2987        * dialog that controls the front cover page.
2988        */
2989       option = gtk_printer_option_new ("gtk-cover-before", _("Before"), GTK_PRINTER_OPTION_TYPE_PICKONE);
2990       gtk_printer_option_choices_from_array (option, num_of_covers,
2991                                          cover, cover_display_translated);
2992
2993       if (backend->default_cover_before != NULL)
2994         gtk_printer_option_set (option, backend->default_cover_before);
2995       else
2996         gtk_printer_option_set (option, "none");
2997       set_option_from_settings (option, settings);
2998       gtk_printer_option_set_add (set, option);
2999       g_object_unref (option);
3000
3001       /* Translators, this is the label used for the option in the print 
3002        * dialog that controls the back cover page.
3003        */
3004       option = gtk_printer_option_new ("gtk-cover-after", _("After"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3005       gtk_printer_option_choices_from_array (option, num_of_covers,
3006                                          cover, cover_display_translated);
3007       if (backend->default_cover_after != NULL)
3008         gtk_printer_option_set (option, backend->default_cover_after);
3009       else
3010         gtk_printer_option_set (option, "none");
3011       set_option_from_settings (option, settings);
3012       gtk_printer_option_set_add (set, option);
3013       g_object_unref (option);
3014
3015       g_strfreev (cover);
3016       g_strfreev (cover_display);
3017       g_free (cover_display_translated);
3018     }
3019
3020   /* Translators: this is the name of the option that controls when
3021    * a print job is printed. Possible values are 'now', a specified time,
3022    * or 'on hold'
3023    */
3024   option = gtk_printer_option_new ("gtk-print-time", _("Print at"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3025   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (print_at),
3026                                          print_at, print_at);
3027   gtk_printer_option_set (option, "now");
3028   set_option_from_settings (option, settings);
3029   gtk_printer_option_set_add (set, option);
3030   g_object_unref (option);
3031   
3032   /* Translators: this is the name of the option that allows the user
3033    * to specify a time when a print job will be printed.
3034    */
3035   option = gtk_printer_option_new ("gtk-print-time-text", _("Print at time"), GTK_PRINTER_OPTION_TYPE_STRING);
3036   gtk_printer_option_set (option, "");
3037   set_option_from_settings (option, settings);
3038   gtk_printer_option_set_add (set, option);
3039   g_object_unref (option);
3040   
3041   /* Printer (ppd) specific settings */
3042   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3043   if (ppd_file)
3044     {
3045       GtkPaperSize *paper_size;
3046       ppd_option_t *option;
3047       const gchar  *ppd_name;
3048
3049       ppdMarkDefaults (ppd_file);
3050
3051       paper_size = gtk_page_setup_get_paper_size (page_setup);
3052
3053       option = ppdFindOption (ppd_file, "PageSize");
3054       ppd_name = gtk_paper_size_get_ppd_name (paper_size);
3055       
3056       if (ppd_name)
3057         strncpy (option->defchoice, ppd_name, PPD_MAX_NAME);
3058       else
3059         {
3060           gchar *custom_name;
3061           char width[G_ASCII_DTOSTR_BUF_SIZE];
3062           char height[G_ASCII_DTOSTR_BUF_SIZE];
3063
3064           g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
3065           g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
3066           /* Translators: this format is used to display a custom paper
3067            * size. The two placeholders are replaced with the width and height
3068            * in points. E.g: "Custom 230.4x142.9"
3069            */
3070           custom_name = g_strdup_printf (_("Custom %sx%s"), width, height);
3071           strncpy (option->defchoice, custom_name, PPD_MAX_NAME);
3072           g_free (custom_name);
3073         }
3074
3075       for (i = 0; i < ppd_file->num_groups; i++)
3076         handle_group (set, ppd_file, &ppd_file->groups[i], &ppd_file->groups[i], settings);
3077     }
3078
3079   /* Now honor the user set defaults for this printer */
3080   num_opts = cups_get_user_options (gtk_printer_get_name (printer), 0, &opts);
3081
3082   for (i = 0; i < num_opts; i++)
3083     {
3084       if (STRING_IN_TABLE (opts[i].name, cups_option_blacklist))
3085         continue;
3086
3087       name = get_option_name (opts[i].name);
3088       option = gtk_printer_option_set_lookup (set, name);
3089       if (option)
3090         gtk_printer_option_set (option, opts[i].value);
3091       g_free (name);
3092     }
3093
3094   cupsFreeOptions (num_opts, opts);
3095
3096   return set;
3097 }
3098
3099
3100 static void
3101 mark_option_from_set (GtkPrinterOptionSet *set,
3102                       ppd_file_t          *ppd_file,
3103                       ppd_option_t        *ppd_option)
3104 {
3105   GtkPrinterOption *option;
3106   char *name = get_option_name (ppd_option->keyword);
3107
3108   option = gtk_printer_option_set_lookup (set, name);
3109
3110   if (option)
3111     ppdMarkOption (ppd_file, ppd_option->keyword, option->value);
3112   
3113   g_free (name);
3114 }
3115
3116
3117 static void
3118 mark_group_from_set (GtkPrinterOptionSet *set,
3119                      ppd_file_t          *ppd_file,
3120                      ppd_group_t         *group)
3121 {
3122   int i;
3123
3124   for (i = 0; i < group->num_options; i++)
3125     mark_option_from_set (set, ppd_file, &group->options[i]);
3126
3127   for (i = 0; i < group->num_subgroups; i++)
3128     mark_group_from_set (set, ppd_file, &group->subgroups[i]);
3129 }
3130
3131 static void
3132 set_conflicts_from_option (GtkPrinterOptionSet *set,
3133                            ppd_file_t          *ppd_file,
3134                            ppd_option_t        *ppd_option)
3135 {
3136   GtkPrinterOption *option;
3137   char *name;
3138
3139   if (ppd_option->conflicted)
3140     {
3141       name = get_option_name (ppd_option->keyword);
3142       option = gtk_printer_option_set_lookup (set, name);
3143
3144       if (option)
3145         gtk_printer_option_set_has_conflict (option, TRUE);
3146 #ifdef PRINT_IGNORED_OPTIONS
3147       else
3148         g_warning ("CUPS Backend: Ignoring conflict for option %s", ppd_option->keyword);
3149 #endif
3150       
3151       g_free (name);
3152     }
3153 }
3154
3155 static void
3156 set_conflicts_from_group (GtkPrinterOptionSet *set,
3157                           ppd_file_t          *ppd_file,
3158                           ppd_group_t         *group)
3159 {
3160   int i;
3161
3162   for (i = 0; i < group->num_options; i++)
3163     set_conflicts_from_option (set, ppd_file, &group->options[i]);
3164
3165   for (i = 0; i < group->num_subgroups; i++)
3166     set_conflicts_from_group (set, ppd_file, &group->subgroups[i]);
3167 }
3168
3169 static gboolean
3170 cups_printer_mark_conflicts (GtkPrinter          *printer,
3171                              GtkPrinterOptionSet *options)
3172 {
3173   ppd_file_t *ppd_file;
3174   int num_conflicts;
3175   int i;
3176  
3177   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3178
3179   if (ppd_file == NULL)
3180     return FALSE;
3181
3182   ppdMarkDefaults (ppd_file);
3183
3184   for (i = 0; i < ppd_file->num_groups; i++)
3185     mark_group_from_set (options, ppd_file, &ppd_file->groups[i]);
3186
3187   num_conflicts = ppdConflicts (ppd_file);
3188
3189   if (num_conflicts > 0)
3190     {
3191       for (i = 0; i < ppd_file->num_groups; i++)
3192         set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]);
3193     }
3194  
3195   return num_conflicts > 0;
3196 }
3197
3198 struct OptionData {
3199   GtkPrinter *printer;
3200   GtkPrinterOptionSet *options;
3201   GtkPrintSettings *settings;
3202   ppd_file_t *ppd_file;
3203 };
3204
3205 typedef struct {
3206   const char *cups;
3207   const char *standard;
3208 } NameMapping;
3209
3210 static void
3211 map_settings_to_option (GtkPrinterOption  *option,
3212                         const NameMapping  table[],
3213                         gint               n_elements,
3214                         GtkPrintSettings  *settings,
3215                         const gchar       *standard_name,
3216                         const gchar       *cups_name)
3217 {
3218   int i;
3219   char *name;
3220   const char *cups_value;
3221   const char *standard_value;
3222
3223   /* If the cups-specific setting is set, always use that */
3224   name = g_strdup_printf ("cups-%s", cups_name);
3225   cups_value = gtk_print_settings_get (settings, name);
3226   g_free (name);
3227   
3228   if (cups_value != NULL) 
3229     {
3230       gtk_printer_option_set (option, cups_value);
3231       return;
3232     }
3233
3234   /* Otherwise we try to convert from the general setting */
3235   standard_value = gtk_print_settings_get (settings, standard_name);
3236   if (standard_value == NULL)
3237     return;
3238
3239   for (i = 0; i < n_elements; i++)
3240     {
3241       if (table[i].cups == NULL && table[i].standard == NULL)
3242         {
3243           gtk_printer_option_set (option, standard_value);
3244           break;
3245         }
3246       else if (table[i].cups == NULL &&
3247                strcmp (table[i].standard, standard_value) == 0)
3248         {
3249           set_option_off (option);
3250           break;
3251         }
3252       else if (strcmp (table[i].standard, standard_value) == 0)
3253         {
3254           gtk_printer_option_set (option, table[i].cups);
3255           break;
3256         }
3257     }
3258 }
3259
3260 static void
3261 map_option_to_settings (const gchar       *value,
3262                         const NameMapping  table[],
3263                         gint               n_elements,
3264                         GtkPrintSettings  *settings,
3265                         const gchar       *standard_name,
3266                         const gchar       *cups_name)
3267 {
3268   int i;
3269   char *name;
3270
3271   for (i = 0; i < n_elements; i++)
3272     {
3273       if (table[i].cups == NULL && table[i].standard == NULL)
3274         {
3275           gtk_print_settings_set (settings,
3276                                   standard_name,
3277                                   value);
3278           break;
3279         }
3280       else if (table[i].cups == NULL && table[i].standard != NULL)
3281         {
3282           if (value_is_off (value))
3283             {
3284               gtk_print_settings_set (settings,
3285                                       standard_name,
3286                                       table[i].standard);
3287               break;
3288             }
3289         }
3290       else if (strcmp (table[i].cups, value) == 0)
3291         {
3292           gtk_print_settings_set (settings,
3293                                   standard_name,
3294                                   table[i].standard);
3295           break;
3296         }
3297     }
3298
3299   /* Always set the corresponding cups-specific setting */
3300   name = g_strdup_printf ("cups-%s", cups_name);
3301   gtk_print_settings_set (settings, name, value);
3302   g_free (name);
3303 }
3304
3305
3306 static const NameMapping paper_source_map[] = {
3307   { "Lower", "lower"},
3308   { "Middle", "middle"},
3309   { "Upper", "upper"},
3310   { "Rear", "rear"},
3311   { "Envelope", "envelope"},
3312   { "Cassette", "cassette"},
3313   { "LargeCapacity", "large-capacity"},
3314   { "AnySmallFormat", "small-format"},
3315   { "AnyLargeFormat", "large-format"},
3316   { NULL, NULL}
3317 };
3318
3319 static const NameMapping output_tray_map[] = {
3320   { "Upper", "upper"},
3321   { "Lower", "lower"},
3322   { "Rear", "rear"},
3323   { NULL, NULL}
3324 };
3325
3326 static const NameMapping duplex_map[] = {
3327   { "DuplexTumble", "vertical" },
3328   { "DuplexNoTumble", "horizontal" },
3329   { NULL, "simplex" }
3330 };
3331
3332 static const NameMapping output_mode_map[] = {
3333   { "Standard", "normal" },
3334   { "Normal", "normal" },
3335   { "Draft", "draft" },
3336   { "Fast", "draft" },
3337 };
3338
3339 static const NameMapping media_type_map[] = {
3340   { "Transparency", "transparency"},
3341   { "Standard", "stationery"},
3342   { NULL, NULL}
3343 };
3344
3345 static const NameMapping all_map[] = {
3346   { NULL, NULL}
3347 };
3348
3349
3350 static void
3351 set_option_from_settings (GtkPrinterOption *option,
3352                           GtkPrintSettings *settings)
3353 {
3354   const char *cups_value;
3355   char *value;
3356   
3357   if (settings == NULL)
3358     return;
3359
3360   if (strcmp (option->name, "gtk-paper-source") == 0)
3361     map_settings_to_option (option, paper_source_map, G_N_ELEMENTS (paper_source_map),
3362                              settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
3363   else if (strcmp (option->name, "gtk-output-tray") == 0)
3364     map_settings_to_option (option, output_tray_map, G_N_ELEMENTS (output_tray_map),
3365                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
3366   else if (strcmp (option->name, "gtk-duplex") == 0)
3367     map_settings_to_option (option, duplex_map, G_N_ELEMENTS (duplex_map),
3368                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
3369   else if (strcmp (option->name, "cups-OutputMode") == 0)
3370     map_settings_to_option (option, output_mode_map, G_N_ELEMENTS (output_mode_map),
3371                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
3372   else if (strcmp (option->name, "cups-Resolution") == 0)
3373     {
3374       cups_value = gtk_print_settings_get (settings, option->name);
3375       if (cups_value)
3376         gtk_printer_option_set (option, cups_value);
3377       else
3378         {
3379           int res = gtk_print_settings_get_resolution (settings);
3380           int res_x = gtk_print_settings_get_resolution_x (settings);
3381           int res_y = gtk_print_settings_get_resolution_y (settings);
3382
3383           if (res_x != res_y)
3384             {
3385               value = g_strdup_printf ("%dx%ddpi", res_x, res_y);
3386               gtk_printer_option_set (option, value);
3387               g_free (value);
3388             }
3389           else if (res != 0)
3390             {
3391               value = g_strdup_printf ("%ddpi", res);
3392               gtk_printer_option_set (option, value);
3393               g_free (value);
3394             }
3395         }
3396     }
3397   else if (strcmp (option->name, "gtk-paper-type") == 0)
3398     map_settings_to_option (option, media_type_map, G_N_ELEMENTS (media_type_map),
3399                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
3400   else if (strcmp (option->name, "gtk-n-up") == 0)
3401     {
3402       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
3403                               settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
3404     }
3405   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
3406     {
3407       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
3408                               settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
3409     }
3410   else if (strcmp (option->name, "gtk-billing-info") == 0)
3411     {
3412       cups_value = gtk_print_settings_get (settings, "cups-job-billing");
3413       if (cups_value)
3414         gtk_printer_option_set (option, cups_value);
3415     } 
3416   else if (strcmp (option->name, "gtk-job-prio") == 0)
3417     {
3418       cups_value = gtk_print_settings_get (settings, "cups-job-priority");
3419       if (cups_value)
3420         gtk_printer_option_set (option, cups_value);
3421     } 
3422   else if (strcmp (option->name, "gtk-cover-before") == 0)
3423     {
3424       cups_value = gtk_print_settings_get (settings, "cover-before");
3425       if (cups_value)
3426         gtk_printer_option_set (option, cups_value);
3427     } 
3428   else if (strcmp (option->name, "gtk-cover-after") == 0)
3429     {
3430       cups_value = gtk_print_settings_get (settings, "cover-after");
3431       if (cups_value)
3432         gtk_printer_option_set (option, cups_value);
3433     } 
3434   else if (strcmp (option->name, "gtk-print-time") == 0)
3435     {
3436       cups_value = gtk_print_settings_get (settings, "print-at");
3437       if (cups_value)
3438         gtk_printer_option_set (option, cups_value);
3439     } 
3440   else if (strcmp (option->name, "gtk-print-time-text") == 0)
3441     {
3442       cups_value = gtk_print_settings_get (settings, "print-at-time");
3443       if (cups_value)
3444         gtk_printer_option_set (option, cups_value);
3445     } 
3446   else if (g_str_has_prefix (option->name, "cups-"))
3447     {
3448       cups_value = gtk_print_settings_get (settings, option->name);
3449       if (cups_value)
3450         gtk_printer_option_set (option, cups_value);
3451     } 
3452 }
3453
3454 static void
3455 foreach_option_get_settings (GtkPrinterOption *option,
3456                              gpointer          user_data)
3457 {
3458   struct OptionData *data = user_data;
3459   GtkPrintSettings *settings = data->settings;
3460   const char *value;
3461
3462   value = option->value;
3463
3464   if (strcmp (option->name, "gtk-paper-source") == 0)
3465     map_option_to_settings (value, paper_source_map, G_N_ELEMENTS (paper_source_map),
3466                             settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
3467   else if (strcmp (option->name, "gtk-output-tray") == 0)
3468     map_option_to_settings (value, output_tray_map, G_N_ELEMENTS (output_tray_map),
3469                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
3470   else if (strcmp (option->name, "gtk-duplex") == 0)
3471     map_option_to_settings (value, duplex_map, G_N_ELEMENTS (duplex_map),
3472                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
3473   else if (strcmp (option->name, "cups-OutputMode") == 0)
3474     map_option_to_settings (value, output_mode_map, G_N_ELEMENTS (output_mode_map),
3475                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
3476   else if (strcmp (option->name, "cups-Resolution") == 0)
3477     {
3478       int res, res_x, res_y;
3479
3480       if (sscanf (value, "%dx%ddpi", &res_x, &res_y) == 2)
3481         {
3482           if (res_x != 0 && res_y != 0)
3483             gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
3484         }
3485       else if (sscanf (value, "%ddpi", &res) == 1)
3486         {
3487           if (res != 0)
3488             gtk_print_settings_set_resolution (settings, res);
3489         }
3490
3491       gtk_print_settings_set (settings, option->name, value);
3492     }
3493   else if (strcmp (option->name, "gtk-paper-type") == 0)
3494     map_option_to_settings (value, media_type_map, G_N_ELEMENTS (media_type_map),
3495                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
3496   else if (strcmp (option->name, "gtk-n-up") == 0)
3497     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
3498                             settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
3499   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
3500     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
3501                             settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
3502   else if (strcmp (option->name, "gtk-billing-info") == 0 && strlen (value) > 0)
3503     gtk_print_settings_set (settings, "cups-job-billing", value);
3504   else if (strcmp (option->name, "gtk-job-prio") == 0)
3505     gtk_print_settings_set (settings, "cups-job-priority", value);
3506   else if (strcmp (option->name, "gtk-cover-before") == 0)
3507     gtk_print_settings_set (settings, "cover-before", value);
3508   else if (strcmp (option->name, "gtk-cover-after") == 0)
3509     gtk_print_settings_set (settings, "cover-after", value);
3510   else if (strcmp (option->name, "gtk-print-time") == 0)
3511     gtk_print_settings_set (settings, "print-at", value);
3512   else if (strcmp (option->name, "gtk-print-time-text") == 0)
3513     gtk_print_settings_set (settings, "print-at-time", value);
3514   else if (g_str_has_prefix (option->name, "cups-"))
3515     gtk_print_settings_set (settings, option->name, value);
3516 }
3517
3518 static gboolean
3519 supports_am_pm (void)
3520 {
3521   struct tm tmp_tm = { 0 };
3522   char   time[8];
3523   int    length;
3524
3525   length = strftime (time, sizeof (time), "%p", &tmp_tm);
3526
3527   return length != 0;
3528 }
3529
3530 /* Converts local time to UTC time. Local time has to be in HH:MM format or
3531  * in HH:MM:SS format or in HH:MM:SS {am, pm} format or in HH:MM {am, pm} format
3532  * or in HH {am, pm} format.
3533  * Returns a newly allocated string holding UTC time in HH:MM:SS format
3534  * or NULL.
3535  */
3536 gchar *
3537 localtime_to_utctime (const char *local_time)
3538 {
3539   const char *formats_0[] = {" %I : %M : %S %p ", " %H : %M : %S ", " %I : %M %p ", " %H : %M ", " %I %p "};
3540   const char *formats_1[] = {" %H : %M : %S ", " %H : %M "};
3541   const char *end = NULL;
3542   struct tm  *actual_local_time;
3543   struct tm  *actual_utc_time;
3544   struct tm   local_print_time;
3545   struct tm   utc_print_time;
3546   struct tm   diff_time;
3547   gchar      *utc_time = NULL;
3548   int         i, n;
3549
3550   if (local_time == NULL || local_time[0] == '\0')
3551     return NULL;
3552
3553   n = supports_am_pm () ? G_N_ELEMENTS (formats_0) : G_N_ELEMENTS (formats_1);
3554
3555   for (i = 0; i < n; i++)
3556     {
3557       local_print_time.tm_hour = 0;
3558       local_print_time.tm_min  = 0;
3559       local_print_time.tm_sec  = 0;
3560
3561       if (supports_am_pm ())
3562         end = strptime (local_time, formats_0[i], &local_print_time);
3563       else
3564         end = strptime (local_time, formats_1[i], &local_print_time);
3565
3566       if (end != NULL && end[0] == '\0')
3567         break;
3568     }
3569
3570   if (end != NULL && end[0] == '\0')
3571     {
3572       time_t rawtime;
3573       time (&rawtime);
3574
3575       actual_utc_time = g_memdup (gmtime (&rawtime), sizeof (struct tm));
3576       actual_local_time = g_memdup (localtime (&rawtime), sizeof (struct tm));
3577
3578       diff_time.tm_hour = actual_utc_time->tm_hour - actual_local_time->tm_hour;
3579       diff_time.tm_min  = actual_utc_time->tm_min  - actual_local_time->tm_min;
3580       diff_time.tm_sec  = actual_utc_time->tm_sec  - actual_local_time->tm_sec;
3581
3582       utc_print_time.tm_hour = ((local_print_time.tm_hour + diff_time.tm_hour) + 24) % 24;
3583       utc_print_time.tm_min  = ((local_print_time.tm_min  + diff_time.tm_min)  + 60) % 60;
3584       utc_print_time.tm_sec  = ((local_print_time.tm_sec  + diff_time.tm_sec)  + 60) % 60;
3585
3586       utc_time = g_strdup_printf ("%02d:%02d:%02d",
3587                                   utc_print_time.tm_hour,
3588                                   utc_print_time.tm_min,
3589                                   utc_print_time.tm_sec);
3590     }
3591
3592   return utc_time;
3593 }
3594
3595 static void
3596 cups_printer_get_settings_from_options (GtkPrinter          *printer,
3597                                         GtkPrinterOptionSet *options,
3598                                         GtkPrintSettings    *settings)
3599 {
3600   struct OptionData data;
3601   const char *print_at, *print_at_time;
3602
3603   data.printer = printer;
3604   data.options = options;
3605   data.settings = settings;
3606   data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3607  
3608   if (data.ppd_file != NULL)
3609     {
3610       GtkPrinterOption *cover_before, *cover_after;
3611       
3612       gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
3613
3614       cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
3615       cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
3616       if (cover_before && cover_after)
3617         {
3618           char *value = g_strdup_printf ("%s,%s", cover_before->value, cover_after->value);
3619           gtk_print_settings_set (settings, "cups-job-sheets", value);
3620           g_free (value);
3621         }
3622
3623       print_at = gtk_print_settings_get (settings, "print-at");
3624       print_at_time = gtk_print_settings_get (settings, "print-at-time");
3625
3626       if (strcmp (print_at, "at") == 0)
3627         {
3628           gchar *utc_time = NULL;
3629           
3630           utc_time = localtime_to_utctime (print_at_time);
3631
3632           if (utc_time != NULL)
3633             {
3634               gtk_print_settings_set (settings, "cups-job-hold-until", utc_time);
3635               g_free (utc_time);
3636             }
3637           else
3638             gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time);
3639         }
3640       else if (strcmp (print_at, "on-hold") == 0)
3641         gtk_print_settings_set (settings, "cups-job-hold-until", "indefinite");
3642     }
3643 }
3644
3645 static void
3646 cups_printer_prepare_for_print (GtkPrinter       *printer,
3647                                 GtkPrintJob      *print_job,
3648                                 GtkPrintSettings *settings,
3649                                 GtkPageSetup     *page_setup)
3650 {
3651   GtkPageSet page_set;
3652   GtkPaperSize *paper_size;
3653   const char *ppd_paper_name;
3654   double scale;
3655
3656   print_job->print_pages = gtk_print_settings_get_print_pages (settings);
3657   print_job->page_ranges = NULL;
3658   print_job->num_page_ranges = 0;
3659   
3660   if (print_job->print_pages == GTK_PRINT_PAGES_RANGES)
3661     print_job->page_ranges =
3662       gtk_print_settings_get_page_ranges (settings,
3663                                           &print_job->num_page_ranges);
3664   
3665   if (gtk_print_settings_get_collate (settings))
3666     gtk_print_settings_set (settings, "cups-Collate", "True");
3667   print_job->collate = FALSE;
3668
3669   if (gtk_print_settings_get_reverse (settings))
3670     gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
3671   print_job->reverse = FALSE;
3672
3673   if (gtk_print_settings_get_n_copies (settings) > 1)
3674     gtk_print_settings_set_int (settings, "cups-copies",
3675                                 gtk_print_settings_get_n_copies (settings));
3676   print_job->num_copies = 1;
3677
3678   scale = gtk_print_settings_get_scale (settings);
3679   print_job->scale = 1.0;
3680   if (scale != 100.0)
3681     print_job->scale = scale/100.0;
3682
3683   page_set = gtk_print_settings_get_page_set (settings);
3684   if (page_set == GTK_PAGE_SET_EVEN)
3685     gtk_print_settings_set (settings, "cups-page-set", "even");
3686   else if (page_set == GTK_PAGE_SET_ODD)
3687     gtk_print_settings_set (settings, "cups-page-set", "odd");
3688   print_job->page_set = GTK_PAGE_SET_ALL;
3689
3690   paper_size = gtk_page_setup_get_paper_size (page_setup);
3691   ppd_paper_name = gtk_paper_size_get_ppd_name (paper_size);
3692   if (ppd_paper_name != NULL)
3693     gtk_print_settings_set (settings, "cups-PageSize", ppd_paper_name);
3694   else
3695     {
3696       char width[G_ASCII_DTOSTR_BUF_SIZE];
3697       char height[G_ASCII_DTOSTR_BUF_SIZE];
3698       char *custom_name;
3699
3700       g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
3701       g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
3702       custom_name = g_strdup_printf (("Custom.%sx%s"), width, height);
3703       gtk_print_settings_set (settings, "cups-PageSize", custom_name);
3704       g_free (custom_name);
3705     }
3706
3707   print_job->rotate_to_orientation = TRUE;
3708 }
3709
3710 static GtkPageSetup *
3711 create_page_setup (ppd_file_t *ppd_file,
3712                    ppd_size_t *size)
3713  {
3714    char *display_name;
3715    GtkPageSetup *page_setup;
3716    GtkPaperSize *paper_size;
3717    ppd_option_t *option;
3718    ppd_choice_t *choice;
3719
3720   display_name = NULL;
3721   option = ppdFindOption (ppd_file, "PageSize");
3722   if (option)
3723     {
3724       choice = ppdFindChoice (option, size->name);
3725       if (choice)
3726         display_name = ppd_text_to_utf8 (ppd_file, choice->text);
3727     }
3728
3729   if (display_name == NULL)
3730     display_name = g_strdup (size->name);
3731   
3732   page_setup = gtk_page_setup_new ();
3733   paper_size = gtk_paper_size_new_from_ppd (size->name,
3734                                             display_name,
3735                                             size->width,
3736                                             size->length);
3737   gtk_page_setup_set_paper_size (page_setup, paper_size);
3738   gtk_paper_size_free (paper_size);
3739   
3740   gtk_page_setup_set_top_margin (page_setup, size->length - size->top, GTK_UNIT_POINTS);
3741   gtk_page_setup_set_bottom_margin (page_setup, size->bottom, GTK_UNIT_POINTS);
3742   gtk_page_setup_set_left_margin (page_setup, size->left, GTK_UNIT_POINTS);
3743   gtk_page_setup_set_right_margin (page_setup, size->width - size->right, GTK_UNIT_POINTS);
3744   
3745   g_free (display_name);
3746
3747   return page_setup;
3748 }
3749
3750 static GList *
3751 cups_printer_list_papers (GtkPrinter *printer)
3752 {
3753   ppd_file_t *ppd_file;
3754   ppd_size_t *size;
3755   GtkPageSetup *page_setup;
3756   GList *l;
3757   int i;
3758
3759   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3760   if (ppd_file == NULL)
3761     return NULL;
3762
3763   l = NULL;
3764   
3765   for (i = 0; i < ppd_file->num_sizes; i++)
3766     {
3767       size = &ppd_file->sizes[i];      
3768
3769       page_setup = create_page_setup (ppd_file, size);
3770
3771       l = g_list_prepend (l, page_setup);
3772     }
3773
3774   return g_list_reverse (l);
3775 }
3776
3777 static GtkPageSetup *
3778 cups_printer_get_default_page_size (GtkPrinter *printer)
3779 {
3780   ppd_file_t *ppd_file;
3781   ppd_size_t *size;
3782   ppd_option_t *option;
3783
3784
3785   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3786   if (ppd_file == NULL)
3787     return NULL;
3788
3789   option = ppdFindOption (ppd_file, "PageSize");
3790   size = ppdPageSize (ppd_file, option->defchoice); 
3791
3792   return create_page_setup (ppd_file, size);
3793 }
3794
3795 static void
3796 cups_printer_get_hard_margins (GtkPrinter *printer,
3797                                gdouble    *top,
3798                                gdouble    *bottom,
3799                                gdouble    *left,
3800                                gdouble    *right)
3801 {
3802   ppd_file_t *ppd_file;
3803
3804   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3805   if (ppd_file == NULL)
3806     return;
3807
3808   *left = ppd_file->custom_margins[0];
3809   *bottom = ppd_file->custom_margins[1];
3810   *right = ppd_file->custom_margins[2];
3811   *top = ppd_file->custom_margins[3];
3812 }
3813
3814 static GtkPrintCapabilities
3815 cups_printer_get_capabilities (GtkPrinter *printer)
3816 {
3817   return
3818     GTK_PRINT_CAPABILITY_COPIES |
3819     GTK_PRINT_CAPABILITY_COLLATE |
3820     GTK_PRINT_CAPABILITY_REVERSE |
3821 #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
3822     GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT |
3823 #endif
3824     GTK_PRINT_CAPABILITY_NUMBER_UP;
3825 }