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