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