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