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