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