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