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