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