Include debug.h from main.h since debug macros may be used anywhere in the code.
[geeqie.git] / src / print.c
1 /*
2  * Geeqie
3  * (C) 2004 John Ellis
4  * Copyright (C) 2008 The Geeqie Team
5  *
6  * Author: John Ellis
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13
14 #include "main.h"
15 #include "print.h"
16
17 #include "filedata.h"
18 #include "image.h"
19 #include "image-load.h"
20 #include "pixbuf_util.h"
21 #include "thumb.h"
22 #include "utilops.h"
23 #include "ui_bookmark.h"
24 #include "ui_menu.h"
25 #include "ui_misc.h"
26 #include "ui_utildlg.h"
27 #include "ui_fileops.h"
28 #include "ui_spinner.h"
29 #include "ui_tabcomp.h"
30
31
32 #include <locale.h>
33 #include <signal.h>
34
35
36 #define PRINT_LPR_COMMAND "lpr"
37 #define PRINT_LPR_CUSTOM  "lpr -P %s"
38 #define PRINT_LPR_QUERY   "lpstat -p"
39
40 #define PRINT_DLG_WIDTH 600
41 #define PRINT_DLG_HEIGHT 400
42
43 #define PRINT_DLG_PREVIEW_WIDTH 270
44 #define PRINT_DLG_PREVIEW_HEIGHT -1
45
46 /* these are in point units */
47 #define PRINT_MIN_WIDTH 100
48 #define PRINT_MIN_HEIGHT 100
49 #define PRINT_MAX_WIDTH 4000
50 #define PRINT_MAX_HEIGHT 4000
51
52 #define PRINT_MARGIN_DEFAULT 36
53
54 #define PRINT_PROOF_MIN_SIZE 8
55 #define PRINT_PROOF_MAX_SIZE 720
56 #define PRINT_PROOF_DEFAULT_SIZE 144
57 #define PRINT_PROOF_MARGIN 5
58
59 /* default page size */
60 #define PAGE_LAYOUT_WIDTH 850
61 #define PAGE_LAYOUT_HEIGHT 1100
62
63 /* preview uses 1 pixel = PRINT_PREVIEW_SCALE points */
64 #define PRINT_PREVIEW_SCALE 4
65
66 /* default dpi to use for printing ps image data */
67 #define PRINT_PS_DPI_DEFAULT 300.0
68 #define PRINT_PS_DPI_MIN 150.0
69 /* method to use when scaling down image data */
70 #define PRINT_PS_MAX_INTERP GDK_INTERP_BILINEAR
71 /* color to use as mask when printing transparent images */
72 #define PRINT_PS_MASK_R 255
73 #define PRINT_PS_MASK_G 255
74 #define PRINT_PS_MASK_B 255
75
76 /* padding between objects */
77 #define PRINT_TEXT_PADDING 3.0
78
79 /* locale for postscript portability */
80 #define POSTSCRIPT_LOCALE "C"
81
82
83 /* group and keys for saving prefs */
84 #define PRINT_PREF_GROUP        "print_settings"
85
86 #define PRINT_PREF_SAVE         "save_settings"
87
88 #define PRINT_PREF_OUTPUT       "output"
89 #define PRINT_PREF_FORMAT       "format"
90 #define PRINT_PREF_DPI          "dpi"
91 #define PRINT_PREF_UNITS        "units"
92 #define PRINT_PREF_SIZE         "size"
93 #define PRINT_PREF_ORIENTATION  "orientation"
94
95 #define PRINT_PREF_CUSTOM_WIDTH         "custom_width"
96 #define PRINT_PREF_CUSTOM_HEIGHT        "custom_height"
97 #define PRINT_PREF_MARGIN_LEFT          "margin_left"
98 #define PRINT_PREF_MARGIN_RIGHT         "margin_right"
99 #define PRINT_PREF_MARGIN_TOP           "margin_top"
100 #define PRINT_PREF_MARGIN_BOTTOM        "margin_bottom"
101 #define PRINT_PREF_PROOF_WIDTH          "proof_width"
102 #define PRINT_PREF_PROOF_HEIGHT         "proof_height"
103
104 #define PRINT_PREF_PRINTERC     "custom_printer"
105
106
107 typedef enum {
108         PRINT_SOURCE_IMAGE = 0,
109         PRINT_SOURCE_SELECTION,
110         PRINT_SOURCE_ALL,
111         PRINT_SOURCE_COUNT
112 } PrintSource;
113
114 const gchar *print_source_text[] = {
115         N_("Image"),
116         N_("Selection"),
117         N_("All"),
118         NULL
119 };
120
121 typedef enum {
122         PRINT_LAYOUT_IMAGE = 0,
123         PRINT_LAYOUT_PROOF,
124         PRINT_LAYOUT_COUNT
125 } PrintLayout;
126
127 const gchar *print_layout_text[] = {
128         N_("One image per page"),
129         N_("Proof sheet"),
130         NULL
131 };
132
133 typedef enum {
134         PRINT_OUTPUT_PS_LPR = 0,
135         PRINT_OUTPUT_PS_CUSTOM,
136         PRINT_OUTPUT_PS_FILE,
137         PRINT_OUTPUT_RGB_FILE,
138         PRINT_OUTPUT_COUNT
139 } PrintOutput;
140
141 const gchar *print_output_text[] = {
142         N_("Default printer"),
143         N_("Custom printer"),
144         N_("PostScript file"),
145         N_("Image file"),
146         NULL,
147         NULL
148 };
149
150 typedef enum {
151         PRINT_FILE_JPG_LOW = 0,
152         PRINT_FILE_JPG_NORMAL,
153         PRINT_FILE_JPG_HIGH,
154         PRINT_FILE_PNG,
155         PRINT_FILE_COUNT
156 } PrintFileFormat;
157
158 const gchar *print_file_format_text[] = {
159         N_("jpeg, low quality"),
160         N_("jpeg, normal quality"),
161         N_("jpeg, high quality"),
162         "png",
163         NULL
164 };
165
166 typedef enum {
167         RENDER_FORMAT_PREVIEW,
168         RENDER_FORMAT_RGB,
169         RENDER_FORMAT_PS
170 } RenderFormat;
171
172 typedef enum {
173         TEXT_INFO_FILENAME = 1 << 0,
174         TEXT_INFO_FILEDATE = 1 << 1,
175         TEXT_INFO_FILESIZE = 1 << 2,
176         TEXT_INFO_DIMENSIONS = 1 << 3,
177         TEXT_INFO_FILEPATH = 1 << 4
178 } TextInfo;
179
180 typedef struct _PrintWindow PrintWindow;
181 struct _PrintWindow
182 {
183         GenericDialog *dialog;
184
185         FileData *source_fd;
186         GList *source_selection;
187         GList *source_list;
188
189         PrintSource source;
190         PrintLayout layout;
191         PrintOutput output;
192
193         gchar *output_path;
194         gchar *output_custom;
195
196         PrintFileFormat output_format;
197
198         gdouble max_dpi;
199
200         GtkWidget *notebook;
201
202         GtkWidget *path_entry;
203         GtkWidget *custom_entry;
204         GtkWidget *path_format_menu;
205         GtkWidget *max_dpi_menu;
206
207         ImageWindow *layout_image;
208         gdouble layout_width;
209         gdouble layout_height;
210
211         gint layout_idle_id;
212
213         gint image_scale;
214
215         GtkWidget *image_scale_spin;
216
217         gdouble proof_width;
218         gdouble proof_height;
219         gint proof_columns;
220         gint proof_rows;
221         GList *proof_point;
222         gint proof_position;
223         gint proof_page;
224
225         GtkWidget *proof_group;
226         GtkWidget *proof_width_spin;
227         GtkWidget *proof_height_spin;
228
229         GtkWidget *paper_menu;
230         GtkWidget *paper_width_spin;
231         GtkWidget *paper_height_spin;
232         GtkWidget *paper_units_menu;
233         GtkWidget *paper_orientation_menu;
234
235         GtkWidget *margin_left_spin;
236         GtkWidget *margin_right_spin;
237         GtkWidget *margin_top_spin;
238         GtkWidget *margin_bottom_spin;
239
240         gint paper_units;
241         gint paper_size;
242         gdouble paper_width;
243         gdouble paper_height;
244         gint paper_orientation;
245
246         gdouble margin_left;
247         gdouble margin_right;
248         gdouble margin_top;
249         gdouble margin_bottom;
250
251         GtkWidget *button_back;
252         GtkWidget *button_next;
253         GtkWidget *page_label;
254         GtkWidget *print_button;
255
256         gdouble single_scale;
257         gdouble single_x;
258         gdouble single_y;
259
260         GtkWidget *single_scale_spin;
261
262         TextInfo        text_fields;
263         gint            text_points;
264         guint8          text_r;
265         guint8          text_g;
266         guint8          text_b;
267
268         gint save_settings;
269
270         /* job printing */
271
272         GenericDialog   *job_dialog;
273         GtkWidget       *job_progress;
274         GtkWidget       *job_progress_label;
275
276         RenderFormat     job_format;
277         PrintOutput      job_output;
278
279         FILE            *job_file;
280         FILE            *job_pipe;
281         gchar           *job_path;
282
283         GdkPixbuf       *job_pixbuf;
284
285         gint             job_page;
286         ImageLoader     *job_loader;
287 };
288
289
290 static void print_job_throw_error(PrintWindow *pw, const gchar *message);
291 static gint print_job_start(PrintWindow *pw, RenderFormat format, PrintOutput output);
292 static void print_job_close(PrintWindow *pw, gint error);
293 static void print_window_close(PrintWindow *pw);
294
295
296 /* misc utils */
297
298 static gint clip_region(gdouble x1, gdouble y1, gdouble w1, gdouble h1,
299                         gdouble x2, gdouble y2, gdouble w2, gdouble h2,
300                         gdouble *rx, gdouble *ry, gdouble *rw, gdouble *rh)
301 {
302         if (x2 + w2 <= x1 || x2 >= x1 + w1 ||
303             y2 + h2 <= y1 || y2 >= y1 + h1)
304                 {
305                 return FALSE;
306                 }
307
308         *rx = MAX(x1, x2);
309         *rw = MIN((x1 + w1), (x2 + w2)) - *rx;
310
311         *ry = MAX(y1, y2);
312         *rh = MIN((y1 + h1), (y2 + h2)) - *ry;
313
314         return TRUE;
315 }
316
317 static const gchar *print_output_name(PrintOutput output)
318 {
319         if (output < 0 || output >= PRINT_OUTPUT_COUNT) return "";
320
321         return _(print_output_text[output]);
322 }
323
324
325 /*
326  *-----------------------------------------------------------------------------
327  * data
328  *-----------------------------------------------------------------------------
329  */
330
331
332 typedef enum {
333         PAPER_UNIT_POINTS = 0,
334         PAPER_UNIT_MM,
335         PAPER_UNIT_CM,
336         PAPER_UNIT_INCH,
337         PAPER_UNIT_PICAS,
338         PAPER_UNIT_COUNT
339 } PaperUnits;
340
341 typedef enum {
342         PAPER_ORIENTATION_PORTRAIT = 0,
343         PAPER_ORIENTATION_LANDSCAPE,
344         PAPER_ORIENTATION_COUNT
345 } PaperOrientation;
346
347 typedef struct _PaperSize PaperSize;
348 struct _PaperSize {
349         gchar *description;
350         gint width;
351         gint height;
352         PaperOrientation orientation;
353 };
354
355 const gchar *print_paper_units[] = {
356         N_("points"),
357         N_("millimeters"),
358         N_("centimeters"),
359         N_("inches"),
360         N_("picas"),
361         NULL
362 };
363
364 const gchar *print_paper_orientation[] = {
365         N_("Portrait"),
366         N_("Landscape"),
367         NULL
368 };
369
370 PaperSize print_paper_sizes[] = {
371         { N_("Custom"),         360,    720,    PAPER_ORIENTATION_PORTRAIT },
372         { N_("Letter"),         612,    792,    PAPER_ORIENTATION_PORTRAIT },   /* in 8.5 x 11 */
373         { N_("Legal"),          612,    1008,   PAPER_ORIENTATION_PORTRAIT },   /* in 8.5 x 14 */
374         { N_("Executive"),      522,    756,    PAPER_ORIENTATION_PORTRAIT },   /* in 7.25x 10.5 */
375         { "A0",                 2384,   3370,   PAPER_ORIENTATION_PORTRAIT },   /* mm 841 x 1189 */
376         { "A1",                 1684,   2384,   PAPER_ORIENTATION_PORTRAIT },   /* mm 594 x 841 */
377         { "A2",                 1191,   1684,   PAPER_ORIENTATION_PORTRAIT },   /* mm 420 x 594 */
378         { "A3",                 842,    1191,   PAPER_ORIENTATION_PORTRAIT },   /* mm 297 x 420 */
379         { "A4",                 595,    842,    PAPER_ORIENTATION_PORTRAIT },   /* mm 210 x 297 */
380         { "A5",                 420,    595,    PAPER_ORIENTATION_PORTRAIT },   /* mm 148 x 210 */
381         { "A6",                 298,    420,    PAPER_ORIENTATION_PORTRAIT },   /* mm 105 x 148 */
382         { "B3",                 1001,   1417,   PAPER_ORIENTATION_PORTRAIT },   /* mm 353 x 500 */
383         { "B4",                 709,    1001,   PAPER_ORIENTATION_PORTRAIT },   /* mm 250 x 353 */
384         { "B5",                 499,    709,    PAPER_ORIENTATION_PORTRAIT },   /* mm 176 x 250 */
385         { "B6",                 354,    499,    PAPER_ORIENTATION_PORTRAIT },   /* mm 125 x 176 */
386         { N_("Envelope #10"),   297,    684,    PAPER_ORIENTATION_LANDSCAPE },  /* in 4.125 x 9.5 */
387         { N_("Envelope #9"),    279,    639,    PAPER_ORIENTATION_LANDSCAPE },  /* in 3.875 x 8.875 */
388         { N_("Envelope C4"),    649,    918,    PAPER_ORIENTATION_LANDSCAPE },  /* mm 229 x 324 */
389         { N_("Envelope C5"),    459,    649,    PAPER_ORIENTATION_LANDSCAPE },  /* mm 162 x 229 */
390         { N_("Envelope C6"),    323,    459,    PAPER_ORIENTATION_LANDSCAPE },  /* mm 114 x 162 */
391         { N_("Photo 6x4"),      432,    288,    PAPER_ORIENTATION_PORTRAIT },   /* in 6   x 4 */
392         { N_("Photo 8x10"),     576,    720,    PAPER_ORIENTATION_PORTRAIT },   /* in 8   x 10 */
393         { N_("Postcard"),       284,    419,    PAPER_ORIENTATION_LANDSCAPE },  /* mm 100 x 148 */
394         { N_("Tabloid"),        792,    1224,   PAPER_ORIENTATION_PORTRAIT },   /* in 11  x 17 */
395         { NULL, 0, 0, 0 }
396 };
397
398
399 static PaperSize *print_paper_size_nth(gint n)
400 {
401         PaperSize *ps = NULL;
402         gint i = 0;
403
404         while (i <= n && print_paper_sizes[i].description)
405                 {
406                 ps = &print_paper_sizes[i];
407                 i++;
408                 }
409
410         return ps;
411 }
412
413 static gint print_paper_size_lookup(gint n, gdouble *width, gdouble *height)
414 {
415         PaperSize *ps;
416         gdouble w, h;
417
418         ps = print_paper_size_nth(n);
419         if (!ps) return FALSE;
420
421         if (ps->orientation == PAPER_ORIENTATION_PORTRAIT)
422                 {
423                 w = ps->width;
424                 h = ps->height;
425                 }
426         else
427                 {
428                 h = ps->width;
429                 w = ps->height;
430                 }
431
432         if (width) *width = w;
433         if (height) *height = h;
434
435         return TRUE;
436 }
437
438 static gdouble print_paper_size_convert_units(gdouble value, PaperUnits src, PaperUnits dst)
439 {
440         gdouble ret;
441
442         if (src == dst) return value;
443
444         switch (src)
445                 {
446                 case PAPER_UNIT_MM:
447                         ret = value / 25.4 * 72.0;
448                         break;
449                 case PAPER_UNIT_CM:
450                         ret = value / 2.54 * 72.0;
451                         break;
452                 case PAPER_UNIT_INCH:
453                         ret = value * 72.0;
454                         break;
455                 case PAPER_UNIT_PICAS:
456                         ret = value * 12.0;
457                         break;
458                 case PAPER_UNIT_POINTS:
459                 default:
460                         ret = value;
461                         break;
462                 }
463
464         switch (dst)
465                 {
466                 case PAPER_UNIT_MM:
467                         ret = ret / 72.0 * 25.4;
468                         break;
469                 case PAPER_UNIT_CM:
470                         ret = ret / 72.0 * 2.54;
471                         break;
472                 case PAPER_UNIT_INCH:
473                         ret = ret / 72.0;
474                         break;
475                 case PAPER_UNIT_PICAS:
476                         ret = ret / 12.0;
477                         break;
478                 case PAPER_UNIT_POINTS:
479                 default:
480                         break;
481                 }
482
483         return ret;
484 }
485
486 static PaperUnits paper_unit_default(void)
487 {
488         const char *result;
489 #if 0
490         /* this is not used because it is not even slightly portable */
491         #include <langinfo.h>
492
493         result = nl_langinfo(_NL_MEASUREMENT_MEASUREMENT);
494         if (result[0] == '2') return PAPER_UNIT_INCH;
495 #endif
496
497 #ifdef LC_MEASUREMENT
498         result = setlocale(LC_MEASUREMENT, NULL);
499 #else
500         result = setlocale(LC_ALL, NULL);
501 #endif
502         if (result &&
503             (strstr(result, "_US") || strstr(result, "_PR")) )
504                 {
505                 return PAPER_UNIT_INCH;
506                 }
507
508         return PAPER_UNIT_CM;
509 }
510
511 /*
512  *-----------------------------------------------------------------------------
513  * the layout window
514  *-----------------------------------------------------------------------------
515  */
516
517 static gint print_layout_page_count(PrintWindow *pw);
518
519
520 static gint print_preview_unit(gdouble points)
521 {
522         return (int)(points / PRINT_PREVIEW_SCALE);
523 }
524
525 static void print_proof_size(PrintWindow *pw, gdouble *width, gdouble *height)
526 {
527         if (width) *width = pw->proof_width + PRINT_PROOF_MARGIN * 2;
528         if (height)
529                 {
530                 gdouble h;
531
532                 h = pw->proof_height + PRINT_PROOF_MARGIN * 2;
533                 if (pw->text_fields != 0) h += PRINT_TEXT_PADDING;
534                 if (pw->text_fields & TEXT_INFO_FILENAME) h+= (gdouble)pw->text_points * 1.25;
535                 if (pw->text_fields & TEXT_INFO_DIMENSIONS) h+= (gdouble)pw->text_points * 1.25;
536                 if (pw->text_fields & TEXT_INFO_FILEDATE) h+= (gdouble)pw->text_points * 1.25;
537                 if (pw->text_fields & TEXT_INFO_FILESIZE) h+= (gdouble)pw->text_points * 1.25;
538                 *height = h;
539                 }
540 }
541
542 static void print_window_layout_status(PrintWindow *pw)
543 {
544         gint total;
545         gchar *buf;
546
547         total = print_layout_page_count(pw);
548         pw->proof_page = CLAMP(pw->proof_page, 0, total - 1);
549
550         buf = g_strdup_printf(_("page %d of %d"), pw->proof_page + 1, (total > 0) ? total : 1);
551         gtk_label_set_text(GTK_LABEL(pw->page_label), buf);
552         g_free(buf);
553
554         gtk_widget_set_sensitive(pw->page_label, (total > 0));
555
556         gtk_widget_set_sensitive(pw->button_back, (pw->proof_page > 0));
557         gtk_widget_set_sensitive(pw->button_next, (pw->proof_page < total - 1));
558
559         gtk_widget_set_sensitive(pw->print_button, total > 0);
560 }
561
562 static void print_window_layout_render_stop(PrintWindow *pw)
563 {
564         if (pw->layout_idle_id != -1)
565                 {
566                 g_source_remove(pw->layout_idle_id);
567                 pw->layout_idle_id = -1;
568                 }
569 }
570
571 static gboolean print_window_layout_render_idle(gpointer data)
572 {
573         PrintWindow *pw = data;
574
575         print_job_close(pw, FALSE);
576         print_job_start(pw, RENDER_FORMAT_PREVIEW, 0);
577
578         pw->layout_idle_id = -1;
579         return FALSE;
580 }
581
582 static void print_window_layout_render(PrintWindow *pw)
583 {
584         gdouble proof_w, proof_h;
585
586         print_proof_size(pw, &proof_w, &proof_h);
587         pw->proof_columns = (pw->layout_width - pw->margin_left - pw->margin_right) / proof_w;
588         pw->proof_rows = (pw->layout_height - pw->margin_top - pw->margin_bottom) / proof_h;
589
590         print_window_layout_status(pw);
591
592         if (pw->layout_idle_id == -1)
593                 {
594                 pw->layout_idle_id = g_idle_add(print_window_layout_render_idle, pw);
595                 }
596 }
597
598 static void print_window_layout_size(PrintWindow *pw)
599 {
600         GdkPixbuf *pixbuf;
601         gdouble width;
602         gdouble height;
603         gint sw, sh;
604
605         if (!pw->layout_image) return;
606
607         if (pw->paper_orientation == PAPER_ORIENTATION_LANDSCAPE)
608                 {
609                 width = pw->paper_height;
610                 height = pw->paper_width;
611                 }
612         else
613                 {
614                 width = pw->paper_width;
615                 height = pw->paper_height;
616                 }
617
618         pw->layout_width = width;
619         pw->layout_height = height;
620
621         sw = print_preview_unit(width);
622         sh = print_preview_unit(height);
623         pixbuf = image_get_pixbuf(pw->layout_image);
624         if (!pixbuf ||
625             gdk_pixbuf_get_width(pixbuf) != sw ||
626             gdk_pixbuf_get_height(pixbuf) != sh)
627                 {
628                 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, sw, sh);
629                 image_change_pixbuf(pw->layout_image, pixbuf, 0.0);
630                 g_object_unref(pixbuf);
631                 }
632
633         print_window_layout_render(pw);
634         print_window_layout_status(pw);
635 }
636
637 static gint print_layout_page_count(PrintWindow *pw)
638 {
639         gint images;
640         gint images_per_page;
641         gint pages;
642
643         if (pw->layout_width - pw->margin_left - pw->margin_right <= 0.0 ||
644             pw->layout_height - pw->margin_top - pw->margin_bottom <= 0.0)
645                 {
646                 return 0;
647                 }
648
649         switch (pw->source)
650                 {
651                 case PRINT_SOURCE_ALL:
652                         images = g_list_length(pw->source_list);
653                         break;
654                 case PRINT_SOURCE_SELECTION:
655                         images = g_list_length(pw->source_selection);
656                         break;
657                 case PRINT_SOURCE_IMAGE:
658                 default:
659                         images = (pw->source_fd) ? 1 : 0;
660                         break;
661                 }
662
663         switch (pw->layout)
664                 {
665                 case PRINT_LAYOUT_PROOF:
666                         images_per_page = pw->proof_columns * pw->proof_rows;
667                         break;
668                 case PRINT_LAYOUT_IMAGE:
669                 default:
670                         images_per_page = 1;
671                         break;
672                 }
673
674         if (images < 1 || images_per_page < 1) return 0;
675
676         pages = images / images_per_page;
677         if (pages * images_per_page < images) pages++;
678
679         return pages;
680 }
681
682 static void print_layout_page_step(PrintWindow *pw, gint step)
683 {
684         gint max;
685         gint page;
686
687         max = print_layout_page_count(pw);
688         page = pw->proof_page + step;
689
690         if (page >= max) page = max - 1;
691         if (page < 0) page = 0;
692
693         if (page == pw->proof_page) return;
694
695         pw->proof_page = page;
696         print_window_layout_size(pw);
697 }
698
699 static void print_layout_page_back_cb(GtkWidget *widget, gpointer data)
700 {
701         PrintWindow *pw = data;
702
703         print_layout_page_step(pw, -1);
704 }
705
706 static void print_layout_page_next_cb(GtkWidget *widget, gpointer data)
707 {
708         PrintWindow *pw = data;
709
710         print_layout_page_step(pw, 1);
711 }
712
713 static void print_layout_zoom_in_cb(GtkWidget *widget, gpointer data)
714 {
715         PrintWindow *pw = data;
716         image_zoom_adjust(pw->layout_image, 0.25);
717 }
718
719 static void print_layout_zoom_out_cb(GtkWidget *widget, gpointer data)
720 {
721         PrintWindow *pw = data;
722         image_zoom_adjust(pw->layout_image, -0.25);
723 }
724
725 static void print_layout_zoom_original_cb(GtkWidget *widget, gpointer data)
726 {
727         PrintWindow *pw = data;
728         gdouble zoom;
729
730         zoom = image_zoom_get(pw->layout_image);
731         image_zoom_set(pw->layout_image, (zoom == 1.0) ? 0.0 : 1.0);
732 }
733
734 static GtkWidget *print_window_layout_setup(PrintWindow *pw, GtkWidget *box)
735 {
736         GtkWidget *vbox;
737         GtkWidget *hbox;
738         GtkWidget *group;
739         GtkWidget *button;
740
741         vbox = pref_box_new(box, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
742         group = pref_frame_new(vbox, TRUE, _("Preview"), GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
743
744         pw->layout_idle_id = -1;
745
746         pw->layout_image = image_new(FALSE);
747         gtk_widget_set_size_request(pw->layout_image->widget, PRINT_DLG_PREVIEW_WIDTH, PRINT_DLG_PREVIEW_HEIGHT);
748
749         gtk_box_pack_start(GTK_BOX(group), pw->layout_image->widget, TRUE, TRUE, 0);
750         gtk_widget_show(pw->layout_image->widget);
751
752         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
753         pw->button_back = pref_button_new(hbox, GTK_STOCK_GO_BACK, NULL, TRUE,
754                                           G_CALLBACK(print_layout_page_back_cb), pw);
755         pw->button_next = pref_button_new(hbox, GTK_STOCK_GO_FORWARD, NULL, TRUE,
756                                           G_CALLBACK(print_layout_page_next_cb), pw);
757         pw->page_label = pref_label_new(hbox, "");
758
759         button = pref_button_new(NULL, GTK_STOCK_ZOOM_OUT, NULL, TRUE,
760                                  G_CALLBACK(print_layout_zoom_out_cb), pw);
761         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
762         gtk_widget_show(button);
763         button = pref_button_new(NULL, GTK_STOCK_ZOOM_IN, NULL, TRUE,
764                                  G_CALLBACK(print_layout_zoom_in_cb), pw);
765         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
766         gtk_widget_show(button);
767         button = pref_button_new(NULL, GTK_STOCK_ZOOM_100, NULL, TRUE,
768                                  G_CALLBACK(print_layout_zoom_original_cb), pw);
769         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
770         gtk_widget_show(button);
771
772         print_window_layout_size(pw);
773
774         return vbox;
775 }
776
777 static void print_window_spin_set(GtkSpinButton *spin, gpointer block_data,
778                                   gdouble value, gdouble min, gdouble max,
779                                   gdouble step, gdouble page, gint digits)
780 {
781         if (block_data) g_signal_handlers_block_matched(G_OBJECT(spin), G_SIGNAL_MATCH_DATA,
782                                                         0, 0, NULL, NULL, block_data);
783         gtk_spin_button_set_digits(spin, digits);
784         gtk_spin_button_set_increments(spin, step, page);
785         gtk_spin_button_set_range(spin, min, max);
786         gtk_spin_button_set_value(spin, value);
787
788         if (block_data) g_signal_handlers_unblock_matched(G_OBJECT(spin), G_SIGNAL_MATCH_DATA,
789                                                           0, 0, NULL, NULL, block_data);
790 }
791
792 static void print_window_layout_sync_layout(PrintWindow *pw)
793 {
794         gtk_widget_set_sensitive(pw->image_scale_spin, (pw->layout == PRINT_LAYOUT_IMAGE));
795         gtk_widget_set_sensitive(pw->proof_group, (pw->layout == PRINT_LAYOUT_PROOF));
796 }
797
798 static void print_window_layout_sync_paper(PrintWindow *pw)
799 {
800         gdouble width, height;
801         gint digits;
802         gdouble step;
803         gdouble page;
804
805         gtk_widget_set_sensitive(pw->paper_width_spin, (pw->paper_size == 0));
806         gtk_widget_set_sensitive(pw->paper_height_spin, (pw->paper_size == 0));
807
808         width = print_paper_size_convert_units((gdouble)pw->paper_width, PAPER_UNIT_POINTS, pw->paper_units);
809         height = print_paper_size_convert_units((gdouble)pw->paper_height, PAPER_UNIT_POINTS, pw->paper_units);
810
811         switch (pw->paper_units)
812                 {
813                 case PAPER_UNIT_MM:
814                         digits = 1;
815                         step = 1.0;
816                         page = 10.0;
817                         break;
818                 case PAPER_UNIT_CM:
819                         digits = 2;
820                         step = 0.5;
821                         page = 1.0;
822                         break;
823                 case PAPER_UNIT_INCH:
824                         digits = 3;
825                         step = 0.25;
826                         page = 1.0;
827                         break;
828                 case PAPER_UNIT_PICAS:
829                         digits = 2;
830                         step = 1.0;
831                         page = 6.0;
832                         break;
833                 case PAPER_UNIT_POINTS:
834                 default:
835                         digits = 1;
836                         step = 1.0;
837                         page = 10.0;
838                         break;
839                 }
840
841         print_window_spin_set(GTK_SPIN_BUTTON(pw->paper_width_spin), pw, width,
842                               print_paper_size_convert_units(PRINT_MIN_WIDTH, PAPER_UNIT_POINTS, pw->paper_units),
843                               print_paper_size_convert_units(PRINT_MAX_WIDTH, PAPER_UNIT_POINTS, pw->paper_units),
844                               step, page, digits);
845
846         print_window_spin_set(GTK_SPIN_BUTTON(pw->paper_height_spin), pw, height,
847                               print_paper_size_convert_units(PRINT_MIN_HEIGHT, PAPER_UNIT_POINTS, pw->paper_units),
848                               print_paper_size_convert_units(PRINT_MAX_HEIGHT, PAPER_UNIT_POINTS, pw->paper_units),
849                               step, page, digits);
850
851         print_window_spin_set(GTK_SPIN_BUTTON(pw->margin_left_spin), pw,
852                               print_paper_size_convert_units(pw->margin_left, PAPER_UNIT_POINTS, pw->paper_units),
853                               0.0,
854                               print_paper_size_convert_units(PRINT_MAX_WIDTH, PAPER_UNIT_POINTS, pw->paper_units),
855                               step, page, digits);
856
857         print_window_spin_set(GTK_SPIN_BUTTON(pw->margin_right_spin), pw,
858                               print_paper_size_convert_units(pw->margin_right, PAPER_UNIT_POINTS, pw->paper_units),
859                               0.0,
860                               print_paper_size_convert_units(PRINT_MAX_WIDTH, PAPER_UNIT_POINTS, pw->paper_units),
861                               step, page, digits);
862
863         print_window_spin_set(GTK_SPIN_BUTTON(pw->margin_top_spin), pw,
864                               print_paper_size_convert_units(pw->margin_top, PAPER_UNIT_POINTS, pw->paper_units),
865                               0.0,
866                               print_paper_size_convert_units(PRINT_MAX_HEIGHT, PAPER_UNIT_POINTS, pw->paper_units),
867                               step, page, digits);
868
869         print_window_spin_set(GTK_SPIN_BUTTON(pw->margin_bottom_spin), pw,
870                               print_paper_size_convert_units(pw->margin_bottom, PAPER_UNIT_POINTS, pw->paper_units),
871                               0.0,
872                               print_paper_size_convert_units(PRINT_MAX_HEIGHT, PAPER_UNIT_POINTS, pw->paper_units),
873                               step, page, digits);
874
875         print_window_spin_set(GTK_SPIN_BUTTON(pw->proof_width_spin), pw,
876                               print_paper_size_convert_units(pw->proof_width, PAPER_UNIT_POINTS, pw->paper_units),
877                               print_paper_size_convert_units(PRINT_PROOF_MIN_SIZE, PAPER_UNIT_POINTS, pw->paper_units),
878                               print_paper_size_convert_units(PRINT_PROOF_MAX_SIZE, PAPER_UNIT_POINTS, pw->paper_units),
879                               step, page, digits);
880
881         print_window_spin_set(GTK_SPIN_BUTTON(pw->proof_height_spin), pw,
882                               print_paper_size_convert_units(pw->proof_height, PAPER_UNIT_POINTS, pw->paper_units),
883                               print_paper_size_convert_units(PRINT_PROOF_MIN_SIZE, PAPER_UNIT_POINTS, pw->paper_units),
884                               print_paper_size_convert_units(PRINT_PROOF_MAX_SIZE, PAPER_UNIT_POINTS, pw->paper_units),
885                               step, page, digits);
886 }
887
888 static void print_window_layout_set_size(PrintWindow *pw, gdouble width, gdouble height)
889 {
890         pw->paper_width = width;
891         pw->paper_height = height;
892
893         print_window_layout_sync_paper(pw);
894
895         print_window_layout_size(pw);
896 }
897
898 static void print_window_layout_set_orientation(PrintWindow *pw, PaperOrientation o)
899 {
900         if (pw->paper_orientation == o) return;
901
902         pw->paper_orientation = o;
903
904         print_window_layout_size(pw);
905 }
906
907 /*
908  *-----------------------------------------------------------------------------
909  * list printers
910  *-----------------------------------------------------------------------------
911  */
912
913 static GList *print_window_list_printers(void)
914 {
915         FILE *p;
916         GList *list = NULL;
917         gchar buffer[2048];
918
919         p = popen(PRINT_LPR_QUERY, "r");
920         if (!p) return NULL;
921
922         while (fgets(buffer, sizeof(buffer), p) != NULL)
923                 {
924                 gchar *ptr;
925                 gchar *end;
926
927                 ptr = buffer;
928                 if (strncmp(ptr, "printer ", 8) != 0) continue;
929                 if (strstr(ptr, "enabled") == NULL) continue;
930                 ptr += 8;
931                 end = ptr;
932                 while (*end != '\0' && *end != '\n' && *end != ' ' && *end != '\t') end++;
933                 *end = '\0';
934                 list = g_list_append(list, g_strdup(ptr));
935                 DEBUG_1("adding printer: %s", ptr);
936                 }
937
938         pclose(p);
939
940         return list;
941 }
942
943 /*
944  *-----------------------------------------------------------------------------
945  * print ps
946  *-----------------------------------------------------------------------------
947  */
948
949 typedef struct _PipeError PipeError;
950 struct _PipeError {
951         struct sigaction old_action;
952         sig_atomic_t *error;
953 };
954
955 static sig_atomic_t pipe_handler_error = FALSE;
956 static PipeError *pipe_handler_data = NULL;
957
958 static void pipe_handler_sigpipe_cb(int fd)
959 {
960         pipe_handler_error = TRUE;
961 }
962
963 static PipeError *pipe_handler_new(void)
964 {
965         struct sigaction new_action;
966         PipeError *pe;
967
968         if (pipe_handler_data)
969                 {
970                 printf("warning SIGPIPE handler already in use\n");
971                 return NULL;
972                 }
973
974         pe = g_new0(PipeError, 1);
975
976         pipe_handler_error = FALSE;
977         pe->error = &pipe_handler_error;
978
979         new_action.sa_handler = pipe_handler_sigpipe_cb;
980         sigemptyset(&new_action.sa_mask);
981         new_action.sa_flags = 0;
982
983         /* setup our signal handler */
984         sigaction(SIGPIPE, &new_action, &pe->old_action);
985
986         pipe_handler_data = pe;
987         return pe;
988 }
989
990 static void pipe_handler_free(PipeError *pe)
991 {
992         if (!pe) return;
993         if (pe != pipe_handler_data) printf("warning SIGPIPE handler not closing same data\n");
994
995         /* restore the original signal handler */
996         sigaction(SIGPIPE, &pe->old_action, NULL);
997
998         pipe_handler_data = NULL;
999         g_free(pe);
1000 }
1001
1002 static gint pipe_handler_check(PipeError *pe)
1003 {
1004         if (!pe) return FALSE;
1005         return *pe->error;
1006 }
1007
1008 static FILE *print_job_ps_fd(PrintWindow *pw)
1009 {
1010         if (pw->job_file) return pw->job_file;
1011         if (pw->job_pipe) return pw->job_pipe;
1012         return NULL;
1013 }
1014
1015 static gint print_job_ps_init(PrintWindow *pw)
1016 {
1017         FILE *f;
1018         PipeError *pe;
1019         const gchar *cmd = NULL;
1020         const gchar *path = NULL;
1021         gchar *lc_pointer;
1022         gint ret;
1023
1024         if (pw->job_file != NULL || pw->job_pipe != NULL) return FALSE;
1025
1026         switch (pw->job_output)
1027                 {
1028                 case PRINT_OUTPUT_PS_LPR:
1029                         cmd = PRINT_LPR_COMMAND;
1030                         break;
1031                 case PRINT_OUTPUT_PS_CUSTOM:
1032                         cmd = pw->output_custom;
1033                         break;
1034                 case PRINT_OUTPUT_PS_FILE:
1035                         path = pw->output_path;
1036                         break;
1037                 default:
1038                         return FALSE;
1039                         break;
1040                 }
1041
1042         if (cmd)
1043                 {
1044                 pw->job_pipe = popen(cmd, "w");
1045
1046                 if (!pw->job_pipe)
1047                         {
1048                         gchar *buf;
1049
1050                         buf = g_strdup_printf(_("Unable to open pipe for writing.\n\"%s\""), cmd);
1051                         print_job_throw_error(pw, buf);
1052                         g_free(buf);
1053
1054                         return FALSE;
1055                         }
1056                 }
1057         else if (path)
1058                 {
1059                 gchar *pathl;
1060
1061                 if (isfile(path))
1062                         {
1063                         gchar *buf;
1064
1065                         buf = g_strdup_printf(_("A file with name %s already exists."), path);
1066                         print_job_throw_error(pw, buf);
1067                         g_free(buf);
1068
1069                         return FALSE;
1070                         }
1071
1072                 pathl = path_from_utf8(path);
1073                 pw->job_file = fopen(pathl, "w");
1074                 g_free(pathl);
1075
1076                 if (!pw->job_file)
1077                         {
1078                         gchar *buf;
1079
1080                         buf = g_strdup_printf(_("Failure writing to file %s"), path);
1081                         print_job_throw_error(pw, buf);
1082                         g_free(buf);
1083
1084                         return FALSE;
1085                         }
1086
1087                 g_free(pw->job_path);
1088                 pw->job_path = g_strdup(path);
1089                 }
1090
1091         f = print_job_ps_fd(pw);
1092         if (!f) return FALSE;
1093
1094         lc_pointer = g_strdup(setlocale(LC_NUMERIC, NULL));
1095         setlocale(LC_NUMERIC, POSTSCRIPT_LOCALE);
1096
1097         pe = pipe_handler_new();
1098
1099         /* comments, etc. */
1100         fprintf(f, "%%!PS-Adobe-3.0\n");
1101         fprintf(f, "%%%%Creator: %s Version %s\n", GQ_APPNAME, VERSION);
1102         fprintf(f, "%%%%CreationDate: \n");
1103         fprintf(f, "%%%%LanguageLevel 2\n");
1104         fprintf(f, "%%%%DocumentMedia: \n");
1105         fprintf(f, "%%%%Orientation: %s\n",
1106                 (pw->paper_orientation == PAPER_ORIENTATION_PORTRAIT) ? "Portrait" : "Landscape");
1107         fprintf(f, "%%%%BoundingBox: %f %f %f %f\n",
1108                 0.0, 0.0, pw->paper_width, pw->paper_height);
1109         fprintf(f, "%%%%Pages: %d\n", print_layout_page_count(pw));
1110         fprintf(f, "%%%%PageOrder: Ascend\n");
1111         fprintf(f, "%%%%Title:\n");
1112
1113         /* setup page size, coordinates (do we really need this?) */
1114 #if 0
1115         fprintf(f, "<<\n");
1116         fprintf(f, "/PageSize [%f %f]\n", pw->layout_width, pw->layout_height);
1117         fprintf(f, "/ImagingBBox [%f %f %f %f]\n", /* l b r t */
1118                 pw->margin_left, pw->margin_bottom,
1119                 pw->layout_width - pw->margin_right, pw->layout_height - pw->margin_top);
1120         fprintf(f, "/Orientation %d\n",
1121                 (pw->paper_orientation == PAPER_ORIENTATION_PORTRAIT) ? 0 : 1);
1122         fprintf(f, ">> setpagedevice\n");
1123 #endif
1124
1125         ret = !pipe_handler_check(pe);
1126         pipe_handler_free(pe);
1127
1128         if (lc_pointer)
1129                 {
1130                 setlocale(LC_NUMERIC, lc_pointer);
1131                 g_free(lc_pointer);
1132                 }
1133
1134         if (!ret) print_job_throw_error(pw, _("SIGPIPE error writing to printer."));
1135
1136         return ret;
1137 }
1138
1139 static gint print_job_ps_page_new(PrintWindow *pw, gint page)
1140 {
1141         FILE *f;
1142         PipeError *pe;
1143         gchar *lc_pointer;
1144         gint ret;
1145
1146         f= print_job_ps_fd(pw);
1147         if (!f) return FALSE;
1148
1149         lc_pointer = g_strdup(setlocale(LC_NUMERIC, NULL));
1150         setlocale(LC_NUMERIC, POSTSCRIPT_LOCALE);
1151
1152         pe = pipe_handler_new();
1153
1154         fprintf(f, "%%%% page %d\n", page + 1);
1155
1156         if (pw->paper_orientation == PAPER_ORIENTATION_LANDSCAPE)
1157                 {
1158                 fprintf(f, "/pagelevel save def\n");
1159                 fprintf(f, "%d 0 translate 90 rotate\n", (gint)pw->layout_height);
1160                 }
1161
1162         ret = !pipe_handler_check(pe);
1163         pipe_handler_free(pe);
1164
1165         if (lc_pointer)
1166                 {
1167                 setlocale(LC_NUMERIC, lc_pointer);
1168                 g_free(lc_pointer);
1169                 }
1170
1171         if (!ret) print_job_throw_error(pw, _("SIGPIPE error writing to printer."));
1172
1173         return ret;
1174 }
1175
1176 static gint print_job_ps_page_done(PrintWindow *pw)
1177 {
1178         FILE *f;
1179         PipeError *pe;
1180         gchar *lc_pointer;
1181         gint ret;
1182
1183         f = print_job_ps_fd(pw);
1184         if (!f) return FALSE;
1185
1186         lc_pointer = g_strdup(setlocale(LC_NUMERIC, NULL));
1187         setlocale(LC_NUMERIC, POSTSCRIPT_LOCALE);
1188
1189         pe = pipe_handler_new();
1190
1191         if (pw->paper_orientation == PAPER_ORIENTATION_LANDSCAPE)
1192                 {
1193                 fprintf(f, "pagelevel restore\n");
1194                 }
1195
1196         fprintf(f, "showpage\n");
1197
1198         ret = !pipe_handler_check(pe);
1199         pipe_handler_free(pe);
1200
1201         if (lc_pointer)
1202                 {
1203                 setlocale(LC_NUMERIC, lc_pointer);
1204                 g_free(lc_pointer);
1205                 }
1206
1207         if (!ret) print_job_throw_error(pw, _("SIGPIPE error writing to printer."));
1208
1209         return ret;
1210 }
1211
1212 static void print_job_ps_page_image_pixel(FILE *f, guchar *pix)
1213 {
1214         static gchar hex_digits[] = "0123456789abcdef";
1215         gchar text[8];
1216         gint i;
1217
1218         for (i = 0; i < 3; i++)
1219                 {
1220                 text[i*2] = hex_digits[pix[i] >> 4];
1221                 text[i*2+1] = hex_digits[pix[i] & 0xf];
1222                 }
1223         text[6] = '\0';
1224
1225         fprintf(f, text);
1226 }
1227 static gint print_job_ps_page_image(PrintWindow *pw, GdkPixbuf *pixbuf,
1228                                     gdouble x, gdouble y, gdouble w, gdouble h,
1229                                     gdouble offx, gdouble offy)
1230 {
1231         FILE *f;
1232         PipeError *pe;
1233         gchar *lc_pointer;
1234         gint sw, sh;
1235         gint bps;
1236         gint rowstride;
1237         guchar *pix;
1238         gint i, j;
1239         gint c;
1240         guchar *p;
1241         guchar bps_buf[3];
1242         gint ret;
1243
1244         if (!pixbuf) return TRUE;
1245
1246         f = print_job_ps_fd(pw);
1247         if (!f) return FALSE;
1248
1249         sw = gdk_pixbuf_get_width(pixbuf);
1250         sh = gdk_pixbuf_get_height(pixbuf);
1251
1252         if (pw->max_dpi >= PRINT_PS_DPI_MIN &&
1253             sw / pw->max_dpi > w / 72.0)
1254                 {
1255                 pixbuf = gdk_pixbuf_scale_simple(pixbuf,
1256                                                 (gint)(w / 72.0 * pw->max_dpi),
1257                                                 (gint)(h / 72.0 * pw->max_dpi),
1258                                                 PRINT_PS_MAX_INTERP);
1259                 sw = gdk_pixbuf_get_width(pixbuf);
1260                 sh = gdk_pixbuf_get_height(pixbuf);
1261                 }
1262         else
1263                 {
1264                 g_object_ref(G_OBJECT(pixbuf));
1265                 }
1266
1267         bps = (gdk_pixbuf_get_has_alpha(pixbuf)) ? 4 : 3;
1268         rowstride = gdk_pixbuf_get_rowstride(pixbuf);
1269         pix = gdk_pixbuf_get_pixels(pixbuf);
1270
1271         lc_pointer = g_strdup(setlocale(LC_NUMERIC, NULL));
1272         setlocale(LC_NUMERIC, POSTSCRIPT_LOCALE);
1273
1274         pe = pipe_handler_new();
1275
1276         fprintf(f, "gsave\n");
1277         fprintf(f, "[%f 0 0 %f %f %f] concat\n", w, h, x, pw->layout_height - h - y);
1278         fprintf(f, "/buf %d string def\n", sw * 3);
1279         fprintf(f, "%d %d %d\n", sw, sh, 8);
1280         fprintf(f, "[%d 0 0 -%d 0 %d]\n", sw, sh, sh);
1281         fprintf(f, "{ currentfile buf readhexstring pop }\n");
1282         fprintf(f, "false %d colorimage\n", 3);
1283
1284         c = 0;
1285         for (j = 0; j < sh; j++)
1286                 {
1287                 p = pix + j * rowstride;
1288                 for (i = 0; i < sw; i++)
1289                         {
1290                         if (bps == 3)
1291                                 {
1292                                 print_job_ps_page_image_pixel(f, p);
1293                                 }
1294                         else
1295                                 {
1296                                 bps_buf[0] = (p[0] * p[3] + PRINT_PS_MASK_R * (256 - p[3])) >> 8;
1297                                 bps_buf[1] = (p[1] * p[3] + PRINT_PS_MASK_G * (256 - p[3])) >> 8;
1298                                 bps_buf[2] = (p[2] * p[3] + PRINT_PS_MASK_B * (256 - p[3])) >> 8;
1299                                 print_job_ps_page_image_pixel(f, bps_buf);
1300                                 }
1301                         p+=bps;
1302                         c++;
1303                         if (c > 11)
1304                                 {
1305                                 fprintf(f, "\n");
1306                                 c = 0;
1307                                 }
1308                         }
1309                 }
1310         if (c > 0) fprintf(f, "\n");
1311         fprintf(f, "grestore\n");
1312
1313         ret = !pipe_handler_check(pe);
1314         pipe_handler_free(pe);
1315
1316         if (lc_pointer)
1317                 {
1318                 setlocale(LC_NUMERIC, lc_pointer);
1319                 g_free(lc_pointer);
1320                 }
1321
1322         g_object_unref(G_OBJECT(pixbuf));
1323
1324         if (!ret) print_job_throw_error(pw, _("SIGPIPE error writing to printer."));
1325
1326         return ret;
1327 }
1328
1329 static const gchar *ps_text_to_hex_array(FILE *f, const gchar *text, gdouble x, gdouble y)
1330 {
1331         static gchar hex_digits[] = "0123456789abcdef";
1332         const gchar *p;
1333
1334         if (!text) return NULL;
1335
1336         fprintf(f, "%f %f moveto\n", x, y);
1337         fprintf(f, "<");
1338
1339         /* fixme: convert utf8 to ascii or proper locale string.. */
1340
1341         p = text;
1342         while (*p != '\0' && *p != '\n')
1343                 {
1344                 gchar text[3];
1345
1346                 text[0] = hex_digits[*p >> 4];
1347                 text[1] = hex_digits[*p & 0xf];
1348                 text[2] = '\0';
1349
1350                 fprintf(f, text);
1351
1352                 p++;
1353                 }
1354
1355         fprintf(f, ">\n");
1356         fprintf(f, "dup stringwidth pop 2 div neg 0 rmoveto show\n");
1357
1358         return p;
1359 }
1360
1361 static void ps_text_parse(FILE *f, const gchar *text, gdouble x, gdouble y, gdouble point_size)
1362 {
1363         const gchar *p;
1364
1365         if (!text) return;
1366
1367         fprintf(f, "newpath\n");
1368
1369         p = text;
1370         while (p && *p != '\0')
1371                 {
1372                 p = ps_text_to_hex_array(f, p, x, y);
1373                 if (p && *p == '\n') p++;
1374                 y -= point_size;
1375                 }
1376
1377         fprintf(f, "closepath\n");
1378 }
1379
1380 static gint print_job_ps_page_text(PrintWindow *pw, const gchar *text, gdouble point_size,
1381                                    gdouble x, gdouble y, gdouble width,
1382                                    guint8 r, guint8 g, guint8 b)
1383 {
1384         FILE *f;
1385         PipeError *pe;
1386         gchar *lc_pointer;
1387         gint ret;
1388
1389         if (!text) return TRUE;
1390
1391         f = print_job_ps_fd(pw);
1392         if (!f) return FALSE;
1393
1394         lc_pointer = g_strdup(setlocale(LC_NUMERIC, NULL));
1395         setlocale(LC_NUMERIC, POSTSCRIPT_LOCALE);
1396
1397         pe = pipe_handler_new();
1398
1399         fprintf(f, "/Sans findfont\n");
1400         fprintf(f, "%f scalefont\n", point_size);
1401         fprintf(f, "setfont\n");
1402
1403         fprintf(f, "%f %f %f setrgbcolor\n", (gdouble)r / 255.0, (gdouble)g / 255.0, (gdouble)b / 255.0);
1404         ps_text_parse(f, text, x, pw->layout_height - y - point_size, point_size);
1405
1406         ret = !pipe_handler_check(pe);
1407         pipe_handler_free(pe);
1408
1409         if (lc_pointer)
1410                 {
1411                 setlocale(LC_NUMERIC, lc_pointer);
1412                 g_free(lc_pointer);
1413                 }
1414
1415         if (!ret) print_job_throw_error(pw, _("SIGPIPE error writing to printer."));
1416
1417         return ret;
1418 }
1419
1420 static gint print_job_ps_end(PrintWindow *pw)
1421 {
1422         FILE *f;
1423         PipeError *pe;
1424         gchar *lc_pointer;
1425         gint ret;
1426
1427         f = print_job_ps_fd(pw);
1428         if (!f) return FALSE;
1429
1430         lc_pointer = g_strdup(setlocale(LC_NUMERIC, NULL));
1431         setlocale(LC_NUMERIC, POSTSCRIPT_LOCALE);
1432
1433         pe = pipe_handler_new();
1434
1435         fprintf(f, "%%%%EOF\n");
1436
1437         ret = !pipe_handler_check(pe);
1438         pipe_handler_free(pe);
1439
1440         if (lc_pointer)
1441                 {
1442                 setlocale(LC_NUMERIC, lc_pointer);
1443                 g_free(lc_pointer);
1444                 }
1445
1446         if (!ret) print_job_throw_error(pw, _("SIGPIPE error writing to printer."));
1447
1448         return ret;
1449 }
1450
1451 /*
1452  *-----------------------------------------------------------------------------
1453  * print rgb
1454  *-----------------------------------------------------------------------------
1455  */
1456
1457 static gint print_job_rgb_page_new(PrintWindow *pw, gint page)
1458 {
1459         gint total;
1460
1461         if (pw->job_pixbuf)
1462                 {
1463                 pixbuf_set_rect_fill(pw->job_pixbuf, 0, 0,
1464                                      gdk_pixbuf_get_width(pw->job_pixbuf),
1465                                      gdk_pixbuf_get_height(pw->job_pixbuf),
1466                                      255, 255, 255, 255);
1467                 }
1468
1469         g_free(pw->job_path);
1470         pw->job_path = NULL;
1471
1472         total = print_layout_page_count(pw);
1473
1474         if (!pw->output_path ||
1475             page < 0 || page >= total) return FALSE;
1476
1477         if (total > 1)
1478                 {
1479                 const gchar *ext;
1480                 gchar *base;
1481
1482                 ext = extension_from_path(pw->output_path);
1483
1484                 if (ext)
1485                         {
1486                         base = g_strndup(pw->output_path, ext - pw->output_path);
1487                         }
1488                 else
1489                         {
1490                         base = g_strdup(pw->output_path);
1491                         ext = "";
1492                         }
1493                 pw->job_path = g_strdup_printf("%s_%03d%s", base, page + 1, ext);
1494                 g_free(base);
1495                 }
1496         else
1497                 {
1498                 pw->job_path = g_strdup(pw->output_path);
1499                 }
1500
1501         if (isfile(pw->job_path))
1502                 {
1503                 gchar *buf;
1504
1505                 buf = g_strdup_printf(_("A file with name %s already exists."), pw->job_path);
1506                 print_job_throw_error(pw, buf);
1507                 g_free(buf);
1508
1509                 g_free(pw->job_path);
1510                 pw->job_path = NULL;
1511                 }
1512
1513         return (pw->job_path != NULL);
1514 }
1515
1516 static gint print_job_rgb_page_done(PrintWindow *pw)
1517 {
1518         gchar *pathl;
1519         gint ret = FALSE;
1520
1521         if (!pw->job_pixbuf) return FALSE;
1522
1523         pathl = path_from_utf8(pw->job_path);
1524
1525         if (pw->output_format == PRINT_FILE_PNG)
1526                 {
1527                 ret = pixbuf_to_file_as_png(pw->job_pixbuf, pathl);
1528                 }
1529         else
1530                 {
1531                 gint quality = 0;
1532
1533                 switch (pw->output_format)
1534                         {
1535                         case PRINT_FILE_JPG_LOW:
1536                                 quality = 65;
1537                                 break;
1538                         case PRINT_FILE_JPG_NORMAL:
1539                                 quality = 80;
1540                                 break;
1541                         case PRINT_FILE_JPG_HIGH:
1542                                 quality = 95;
1543                                 break;
1544                         default:
1545                                 break;
1546                         }
1547
1548                 if (quality > 0)
1549                         {
1550                         ret = pixbuf_to_file_as_jpg(pw->job_pixbuf, pathl, quality);
1551                         }
1552                 }
1553
1554         g_free(pathl);
1555
1556         if (!ret)
1557                 {
1558                 gchar *buf;
1559
1560                 buf = g_strdup_printf(_("Failure writing to file %s"), pw->job_path);
1561                 print_job_throw_error(pw, buf);
1562                 g_free(buf);
1563                 }
1564
1565         return ret;
1566 }
1567
1568 static gint print_job_rgb_page_image(PrintWindow *pw, GdkPixbuf *pixbuf,
1569                                      gdouble x, gdouble y, gdouble w, gdouble h,
1570                                      gdouble offx, gdouble offy)
1571 {
1572         gdouble sw, sh;
1573         gdouble dw, dh;
1574         gdouble rx, ry, rw, rh;
1575
1576         if (!pw->job_pixbuf) return FALSE;
1577         if (!pixbuf) return TRUE;
1578
1579         sw = (gdouble)gdk_pixbuf_get_width(pixbuf);
1580         sh = (gdouble)gdk_pixbuf_get_height(pixbuf);
1581
1582         dw = (gdouble)gdk_pixbuf_get_width(pw->job_pixbuf);
1583         dh = (gdouble)gdk_pixbuf_get_height(pw->job_pixbuf);
1584
1585         if (clip_region(x, y, w, h,
1586                         0.0, 0.0, dw, dh,
1587                         &rx, &ry, &rw, &rh))
1588                 {
1589                 gdk_pixbuf_composite(pixbuf, pw->job_pixbuf, rx, ry, rw, rh,
1590                                      x + offx, y + offy,
1591                                      w / sw, h / sh,
1592                                      (w / sw < 0.01 || h / sh < 0.01) ? GDK_INTERP_NEAREST : GDK_INTERP_BILINEAR, 255);
1593                 }
1594
1595         return TRUE;
1596 }
1597
1598 static gdouble convert_pango_dpi(gdouble points)
1599 {
1600         static gdouble dpi = 0.0;
1601
1602         if (dpi == 0.0)
1603                 {
1604                 GtkSettings *settings;
1605                 GObjectClass *klass;
1606
1607                 settings = gtk_settings_get_default();
1608                 klass = G_OBJECT_CLASS(GTK_SETTINGS_GET_CLASS(settings));
1609                 if (g_object_class_find_property(klass, "gtk-xft-dpi"))
1610                         {
1611                         int int_dpi;
1612                         g_object_get(settings, "gtk-xft-dpi", &int_dpi, NULL);
1613                         dpi = (gdouble)int_dpi / PANGO_SCALE;
1614                         }
1615
1616                 if (dpi < 25.0)
1617                         {
1618                         static gint warned = FALSE;
1619                         gdouble fallback_dpi = 96.0;
1620
1621                         if (!warned)
1622                                 {
1623                                 if (dpi == 0.0)
1624                                         {
1625                                         printf("pango dpi unknown, assuming %.0f\n", fallback_dpi);
1626                                         }
1627                                 else
1628                                         {
1629                                         printf("pango dpi reported as %.0f ignored, assuming %.0f\n", dpi, fallback_dpi);
1630                                         }
1631                                 warned = TRUE;
1632                                 }
1633
1634                         dpi = fallback_dpi;
1635                         }
1636                 }
1637
1638         if (dpi == 0) return points;
1639         return points * 72.0 / dpi;
1640 }
1641
1642 static gint print_job_rgb_page_text(PrintWindow *pw, const gchar *text, gdouble point_size,
1643                                     gdouble x, gdouble y, gdouble width,
1644                                     guint8 r, guint8 g, guint8 b)
1645 {
1646         PangoLayout *layout;
1647         PangoFontDescription *desc;
1648         gint lw, lh;
1649
1650         if (!pw->job_pixbuf) return FALSE;
1651
1652         layout = gtk_widget_create_pango_layout(pw->dialog->dialog, NULL);
1653
1654         desc = pango_font_description_new();
1655         pango_font_description_set_size(desc, convert_pango_dpi(point_size) * PANGO_SCALE);
1656         pango_layout_set_font_description(layout, desc);
1657         pango_font_description_free(desc);
1658
1659         pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
1660         pango_layout_set_text(layout, text, -1);
1661
1662         pango_layout_get_pixel_size(layout, &lw, &lh);
1663         x = x - (gdouble)lw / 2.0;
1664
1665         pixbuf_draw_layout(pw->job_pixbuf, layout, pw->dialog->dialog, x, y, r, g, b, 255);
1666         g_object_unref(G_OBJECT(layout));
1667
1668         return TRUE;
1669 }
1670
1671 static gint print_job_rgb_init(PrintWindow *pw)
1672 {
1673         if (pw->job_pixbuf) g_object_unref(pw->job_pixbuf);
1674         pw->job_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
1675                                         (gint)pw->layout_width, (gint)pw->layout_height);
1676
1677         return print_job_rgb_page_new(pw, pw->job_page);
1678 }
1679
1680 /*
1681  *-----------------------------------------------------------------------------
1682  * print preview
1683  *-----------------------------------------------------------------------------
1684  */
1685
1686 static gint print_job_preview_page_new(PrintWindow *pw, gint page)
1687 {
1688         GdkPixbuf *pixbuf;
1689         gint w, h;
1690         gint l, r, t, b;
1691
1692         pixbuf = pw->job_pixbuf;
1693         if (!pixbuf) return FALSE;
1694
1695         w = print_preview_unit(pw->layout_width);
1696         h = print_preview_unit(pw->layout_height);
1697         l = print_preview_unit(pw->margin_left);
1698         r = print_preview_unit(pw->margin_right);
1699         t = print_preview_unit(pw->margin_top);
1700         b = print_preview_unit(pw->margin_bottom);
1701
1702         /* fill background */
1703         pixbuf_set_rect_fill(pixbuf, 0, 0, w, h,
1704                              255, 255, 255, 255);
1705
1706         /* draw cm or inch grid */
1707         if (TRUE)
1708                 {
1709                 gdouble i;
1710                 gdouble grid;
1711                 PaperUnits units;
1712
1713                 units = (pw->paper_units == PAPER_UNIT_MM ||
1714                          pw->paper_units == PAPER_UNIT_CM) ? PAPER_UNIT_CM : PAPER_UNIT_INCH;
1715
1716                 grid = print_paper_size_convert_units(1.0, units, PAPER_UNIT_POINTS);
1717                 for (i = grid ; i < pw->layout_width; i += grid)
1718                         {
1719                         pixbuf_draw_rect_fill(pixbuf, print_preview_unit(i), 0, 1, h, 0, 0, 0, 16);
1720                         }
1721                 for (i = grid; i < pw->layout_height; i += grid)
1722                         {
1723                         pixbuf_draw_rect_fill(pixbuf, 0, print_preview_unit(i), w, 1, 0, 0, 0, 16);
1724                         }
1725                 }
1726
1727         /* proof sheet grid */
1728         if (pw->layout == PRINT_LAYOUT_PROOF)
1729                 {
1730                 gdouble i, j;
1731                 gdouble proof_w, proof_h;
1732                 gint uw, uh;
1733
1734                 print_proof_size(pw, &proof_w, &proof_h);
1735                 uw = print_preview_unit(proof_w + PRINT_PREVIEW_SCALE - 0.1);
1736                 uh = print_preview_unit(proof_h + PRINT_PREVIEW_SCALE - 0.1);
1737
1738                 for (i = 0; i < pw->proof_columns; i++)
1739                     for (j = 0; j < pw->proof_rows; j++)
1740                         {
1741                         gint x, y;
1742
1743                         x = pw->margin_left + (pw->layout_width - pw->margin_left - pw->margin_right - (pw->proof_columns * proof_w)) / 2 + i * proof_w;
1744                         y = pw->margin_top + j * proof_h;
1745
1746                         pixbuf_draw_rect(pixbuf, print_preview_unit(x), print_preview_unit(y), uw, uh,
1747                                          255, 0, 0, 64, 1, 1, 1, 1);
1748                         }
1749                 }
1750
1751         /* non-printable region (margins) */
1752         pixbuf_draw_rect(pixbuf, 0, 0, w, h,
1753                          0, 0, 0, 16,
1754                          l, r, t, b);
1755
1756         /* margin lines */
1757         pixbuf_draw_rect(pixbuf, l, 0, w - l - r, h,
1758                          0, 0, 255, 128,
1759                          1, 1, 0, 0);
1760         pixbuf_draw_rect(pixbuf, 0, t, w, h - t - b,
1761                          0, 0, 255, 128,
1762                          0, 0, 1, 1);
1763
1764         /* border */
1765         pixbuf_draw_rect(pixbuf, 0, 0, w, h,
1766                          0, 0, 0, 255,
1767                          1, 1, 1, 1);
1768
1769         image_area_changed(pw->layout_image, 0, 0, w, h);
1770
1771         return TRUE;
1772 }
1773
1774 static gint print_job_preview_page_done(PrintWindow *pw)
1775 {
1776         return TRUE;
1777 }
1778
1779 static gint print_job_preview_page_image(PrintWindow *pw, GdkPixbuf *pixbuf,
1780                                          gdouble x, gdouble y, gdouble w, gdouble h,
1781                                          gdouble offx, gdouble offy)
1782 {
1783         gdouble sw, sh;
1784         gdouble dw, dh;
1785         gdouble rx, ry, rw, rh;
1786
1787         if (!pw->job_pixbuf) return FALSE;
1788         if (!pixbuf) return TRUE;
1789
1790         sw = (gdouble)gdk_pixbuf_get_width(pixbuf);
1791         sh = (gdouble)gdk_pixbuf_get_height(pixbuf);
1792
1793         dw = (gdouble)gdk_pixbuf_get_width(pw->job_pixbuf);
1794         dh = (gdouble)gdk_pixbuf_get_height(pw->job_pixbuf);
1795
1796         x = print_preview_unit(x);
1797         y = print_preview_unit(y);
1798         w = print_preview_unit(w);
1799         h = print_preview_unit(h);
1800         offx = print_preview_unit(offx);
1801         offy = print_preview_unit(offy);
1802
1803         if (clip_region(x, y, w, h,
1804                         0.0, 0.0, dw, dh,
1805                         &rx, &ry, &rw, &rh))
1806                 {
1807                 gdk_pixbuf_composite(pixbuf, pw->job_pixbuf, rx, ry, rw, rh,
1808                                      x + offx, y + offy,
1809                                      w / sw, h / sh,
1810                                      (w / sw < 0.01 || h / sh < 0.01) ? GDK_INTERP_NEAREST : GDK_INTERP_BILINEAR, 255);
1811
1812                 image_area_changed(pw->layout_image, rx, ry, rw, rh);
1813                 }
1814
1815         return TRUE;
1816 }
1817
1818 static gint print_job_preview_page_text(PrintWindow *pw, const gchar *text, gdouble point_size,
1819                                         gdouble x, gdouble y, gdouble width,
1820                                         guint8 r, guint8 g, guint8 b)
1821 {
1822         PangoLayout *layout;
1823         PangoFontDescription *desc;
1824         gint lw, lh;
1825         GdkPixbuf *pixbuf;
1826
1827         if (!pw->job_pixbuf) return FALSE;
1828
1829         layout = gtk_widget_create_pango_layout(pw->dialog->dialog, NULL);
1830
1831         desc = pango_font_description_new();
1832         pango_font_description_set_size(desc, convert_pango_dpi(point_size) * PANGO_SCALE);
1833         pango_layout_set_font_description(layout, desc);
1834         pango_font_description_free(desc);
1835
1836         pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
1837         pango_layout_set_text(layout, text, -1);
1838
1839         pango_layout_get_pixel_size(layout, &lw, &lh);
1840         x = x - (gdouble)lw / 2.0;
1841
1842         pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, lw, lh);
1843         pixbuf_set_rect_fill(pixbuf, 0, 0, lw, lh, 0, 0, 0, 0);
1844         pixbuf_draw_layout(pixbuf, layout, pw->dialog->dialog, 0, 0, r, g, b, 255);
1845         g_object_unref(G_OBJECT(layout));
1846
1847         print_job_preview_page_image(pw, pixbuf, x, y, (gdouble)lw, (gdouble)lh, 0, 0);
1848         g_object_unref(pixbuf);
1849
1850         return TRUE;
1851 }
1852
1853 static gint print_job_preview_init(PrintWindow *pw)
1854 {
1855         if (pw->job_pixbuf) g_object_unref(pw->job_pixbuf);
1856         pw->job_pixbuf = image_get_pixbuf(pw->layout_image);
1857         g_object_ref(pw->job_pixbuf);
1858
1859         return print_job_preview_page_new(pw, pw->job_page);
1860 }
1861
1862
1863 /*
1864  *-----------------------------------------------------------------------------
1865  * wrappers
1866  *-----------------------------------------------------------------------------
1867  */
1868
1869 static gint print_job_page_new(PrintWindow *pw)
1870 {
1871         switch (pw->job_format)
1872                 {
1873                 case RENDER_FORMAT_RGB:
1874                         return print_job_rgb_page_new(pw, pw->job_page);
1875                 case RENDER_FORMAT_PS:
1876                         return print_job_ps_page_new(pw, pw->job_page);
1877                 case RENDER_FORMAT_PREVIEW:
1878                         return print_job_preview_page_new(pw, pw->job_page);
1879                 }
1880
1881         return FALSE;
1882 }
1883
1884 static gint print_job_page_done(PrintWindow *pw)
1885 {
1886         switch (pw->job_format)
1887                 {
1888                 case RENDER_FORMAT_RGB:
1889                         return print_job_rgb_page_done(pw);
1890                 case RENDER_FORMAT_PS:
1891                         return print_job_ps_page_done(pw);
1892                 case RENDER_FORMAT_PREVIEW:
1893                         return print_job_preview_page_done(pw);
1894                 }
1895
1896         return FALSE;
1897 }
1898
1899 static gint print_job_page_image(PrintWindow *pw, GdkPixbuf *pixbuf,
1900                                  gdouble x, gdouble y, gdouble w, gdouble h,
1901                                  gdouble offx, gdouble offy)
1902 {
1903         gint success = FALSE;
1904
1905         if (w <= 0.0 || h <= 0.0) return TRUE;
1906
1907         switch (pw->job_format)
1908                 {
1909                 case RENDER_FORMAT_RGB:
1910                         success = print_job_rgb_page_image(pw, pixbuf, x, y, w, h, offx, offy);
1911                         break;
1912                 case RENDER_FORMAT_PS:
1913                         success = print_job_ps_page_image(pw, pixbuf, x, y, w, h, offx, offy);
1914                         break;
1915                 case RENDER_FORMAT_PREVIEW:
1916                         success = print_job_preview_page_image(pw, pixbuf, x, y, w, h, offx, offy);
1917                         break;
1918                 }
1919
1920         return success;
1921 }
1922
1923 static gint print_job_page_text(PrintWindow *pw, const gchar *text, gdouble point_size,
1924                                 gdouble x, gdouble y, gdouble width,
1925                                 guint8 r, guint8 g, guint8 b)
1926 {
1927         gint success = TRUE;
1928
1929         if (!text) return TRUE;
1930
1931         switch (pw->job_format)
1932                 {
1933                 case RENDER_FORMAT_RGB:
1934                         success = print_job_rgb_page_text(pw, text, point_size, x, y, width, r, g, b);
1935                         break;
1936                 case RENDER_FORMAT_PS:
1937                         success = print_job_ps_page_text(pw, text, point_size, x, y, width, r, g, b);
1938                         break;
1939                 case RENDER_FORMAT_PREVIEW:
1940                         success = print_job_preview_page_text(pw, text, point_size, x, y, width, r, g, b);
1941                         break;
1942                 }
1943
1944         return success;
1945 }
1946
1947 /*
1948  *-----------------------------------------------------------------------------
1949  * print ?
1950  *-----------------------------------------------------------------------------
1951  */
1952
1953 static gint print_job_render_image(PrintWindow *pw);
1954 static gint print_job_render_proof(PrintWindow *pw);
1955
1956
1957 static void print_job_status(PrintWindow *pw)
1958 {
1959         gdouble value;
1960         gint page;
1961         gint total;
1962         gchar *buf;
1963
1964         if (!pw->job_progress) return;
1965
1966         page = pw->job_page;
1967         total = print_layout_page_count(pw);
1968
1969         if (pw->layout == PRINT_LAYOUT_PROOF && pw->proof_point)
1970                 {
1971                 GList *start;
1972
1973                 start = g_list_first(pw->proof_point);
1974                 value = (gdouble)g_list_position(start, pw->proof_point) / g_list_length(start);
1975                 }
1976         else
1977                 {
1978                 value = (total > 0) ? (gdouble)page / total : 0.0;
1979                 }
1980
1981         buf = g_strdup_printf(_("Page %d"), page + 1);
1982         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(pw->job_progress), buf);
1983         g_free(buf);
1984
1985         if (pw->job_path && pw->job_progress_label)
1986                 {
1987                 gtk_label_set_text(GTK_LABEL(pw->job_progress_label), pw->job_path);
1988                 }
1989
1990         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pw->job_progress), value);
1991 }
1992
1993 static void print_job_throw_error(PrintWindow *pw, const gchar *message)
1994 {
1995         GenericDialog *gd;
1996         GtkWidget *parent = NULL;
1997         GtkWidget *group;
1998         GtkWidget *label;
1999         gchar *buf;
2000
2001         if (GTK_WIDGET_VISIBLE(pw->dialog->dialog)) parent = pw->dialog->dialog;
2002
2003         gd = generic_dialog_new(_("Printing error"), GQ_WMCLASS, "print_warning",
2004                                 parent, TRUE, NULL, NULL);
2005         generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, NULL, TRUE);
2006
2007         buf = g_strdup_printf(_("An error occured printing to %s."), print_output_name(pw->output));
2008         generic_dialog_add_message(gd, GTK_STOCK_DIALOG_ERROR, _("Printing error"), buf);
2009         g_free(buf);
2010
2011         group = pref_group_new(gd->vbox, FALSE, _("Details"), GTK_ORIENTATION_VERTICAL);
2012         label = pref_label_new(group, message);
2013         gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2014
2015         gtk_widget_show(gd->dialog);
2016 }
2017
2018 static void print_job_done(PrintWindow *pw)
2019 {
2020         print_job_close(pw, FALSE);
2021 }
2022
2023 static gint print_job_text_image(PrintWindow *pw, const gchar *path,
2024                                  gdouble x, gdouble y, gdouble width,
2025                                  gint sw, gint sh, gint proof)
2026 {
2027         GString *string;
2028         gint space = FALSE;
2029         gint newline = FALSE;
2030         gint ret;
2031
2032         if (pw->text_fields == 0) return TRUE;
2033
2034         string = g_string_new("");
2035         path = pw->job_loader->fd->path;
2036
2037         if (pw->text_fields & TEXT_INFO_FILENAME)
2038                 {
2039                 if (pw->text_fields & TEXT_INFO_FILEPATH)
2040                         g_string_append(string, path);
2041                 else
2042                         g_string_append(string, filename_from_path(path));
2043                 newline = TRUE;
2044                 }
2045         else if (pw->text_fields & TEXT_INFO_FILEPATH)
2046                 {
2047                 gchar *dirname = g_path_get_dirname(path);
2048
2049                 g_string_append_printf(string, "%s%s", dirname, G_DIR_SEPARATOR_S);
2050                 g_free(dirname);
2051                 newline = TRUE;
2052                 }
2053         if (pw->text_fields & TEXT_INFO_DIMENSIONS)
2054                 {
2055                 if (newline) g_string_append(string, "\n");
2056                 g_string_append_printf(string, "%d x %d", (gint)sw, (gint)sh);
2057                 newline = proof;
2058                 space = !proof;
2059                 }
2060         if (pw->text_fields & TEXT_INFO_FILEDATE)
2061                 {
2062                 if (newline)  g_string_append(string, "\n");
2063                 if (space) g_string_append(string, " - ");
2064                 g_string_append(string, text_from_time(filetime(pw->job_loader->fd->path)));
2065                 newline = proof;
2066                 space = !proof;
2067                 }
2068         if (pw->text_fields & TEXT_INFO_FILESIZE)
2069                 {
2070                 gchar *size;
2071
2072                 if (newline)  g_string_append(string, "\n");
2073                 if (space) g_string_append(string, " - ");
2074                 size = text_from_size_abrev(filesize(pw->job_loader->fd->path));
2075                 g_string_append(string, size);
2076                 g_free(size);
2077                 }
2078
2079         ret = print_job_page_text(pw, string->str, pw->text_points, x, y, width,
2080                                   pw->text_r, pw->text_g, pw->text_b);
2081
2082         g_string_free(string, TRUE);
2083
2084         return ret;
2085 }
2086
2087 static void print_job_render_image_loader_done(ImageLoader *il, gpointer data)
2088 {
2089         PrintWindow *pw = data;
2090         GdkPixbuf *pixbuf;
2091         gint success = TRUE;
2092
2093         pixbuf = image_loader_get_pixbuf(il);
2094         if (pixbuf)
2095                 {
2096                 gdouble sw, sh;
2097                 gdouble dw, dh;
2098                 gdouble x, y, w, h;
2099                 gdouble scale;
2100                 gdouble offx, offy;
2101
2102                 sw = (gdouble)gdk_pixbuf_get_width(pixbuf);
2103                 sh = (gdouble)gdk_pixbuf_get_height(pixbuf);
2104
2105                 dw = pw->layout_width - pw->margin_left - pw->margin_right;
2106                 dh = pw->layout_height - pw->margin_top - pw->margin_bottom;
2107
2108                 if (dw / sw < dh / sh)
2109                         {
2110                         w = dw;
2111                         h = dw / sw * sh;
2112                         scale = w / sw;
2113                         }
2114                 else
2115                         {
2116                         h = dh;
2117                         w = dh / sh *sw;
2118                         scale = h / sh;
2119                         }
2120
2121                 if (pw->image_scale >= 5)
2122                         {
2123                         w = w * (gdouble)pw->image_scale / 100.0;
2124                         h = h * (gdouble)pw->image_scale / 100.0;
2125                         }
2126
2127                 x = pw->margin_left + (dw / 2) - (w / 2);
2128                 y = pw->margin_top + (dh / 2) - (h / 2);
2129
2130                 offx = offy = 0;
2131
2132                 if (x < 0)
2133                         {
2134                         w += x;
2135                         offx = x;
2136                         x = 0;
2137                         }
2138                 if (x + w >= pw->layout_width) w = pw->layout_width - x;
2139
2140                 if (y < 0)
2141                         {
2142                         h += y;
2143                         offy = y;
2144                         y = 0;
2145                         }
2146                 if (y + h >= pw->layout_height) h = pw->layout_height - y;
2147
2148                 success = (success &&
2149                            print_job_page_image(pw, pixbuf, x, y, w, h, offx, offy));
2150
2151                 x = x + w / 2;
2152                 y = y + h + PRINT_TEXT_PADDING;
2153
2154                 success = (success &&
2155                            print_job_text_image(pw, pw->job_loader->fd->path, x, y, dw, sw, sh, FALSE));
2156                 }
2157
2158         image_loader_free(pw->job_loader);
2159         pw->job_loader = NULL;
2160
2161         if (pw->job_format == RENDER_FORMAT_PREVIEW)
2162                 {
2163                 print_job_done(pw);
2164                 return;
2165                 }
2166
2167         success = (success && print_job_page_done(pw));
2168         if (!success)
2169                 {
2170                 print_job_close(pw, TRUE);
2171                 return;
2172                 }
2173
2174         pw->job_page++;
2175         print_job_status(pw);
2176
2177         if (print_job_render_image(pw))
2178                 {
2179                 if (!print_job_page_new(pw)) print_job_close(pw, TRUE);
2180                 }
2181         else
2182                 {
2183                 print_job_done(pw);
2184                 }
2185 }
2186
2187 static gint print_job_render_image(PrintWindow *pw)
2188 {
2189         FileData *fd = NULL;
2190
2191         switch (pw->source)
2192                 {
2193                 case PRINT_SOURCE_SELECTION:
2194                         fd = g_list_nth_data(pw->source_selection, pw->job_page);
2195                         break;
2196                 case PRINT_SOURCE_ALL:
2197                         fd = g_list_nth_data(pw->source_list, pw->job_page);
2198                         break;
2199                 case PRINT_SOURCE_IMAGE:
2200                 default:
2201                         if (pw->job_page == 0) fd = pw->source_fd;
2202                         break;
2203                 }
2204
2205         image_loader_free(pw->job_loader);
2206         pw->job_loader = NULL;
2207
2208         if (!fd) return FALSE;
2209
2210         pw->job_loader = image_loader_new(fd);
2211         if (!image_loader_start(pw->job_loader, print_job_render_image_loader_done, pw))
2212                 {
2213                 image_loader_free(pw->job_loader);
2214                 pw->job_loader= NULL;
2215                 }
2216
2217         return TRUE;
2218 }
2219
2220 static void print_job_render_proof_loader_done(ImageLoader *il, gpointer data)
2221 {
2222         PrintWindow *pw = data;
2223         GdkPixbuf *pixbuf;
2224         gdouble x, y;
2225         gdouble w, h;
2226         gdouble proof_w, proof_h;
2227         gdouble icon_w, icon_h;
2228         gdouble scale;
2229         gint success = TRUE;
2230
2231         if (pw->proof_columns < 1 || pw->proof_rows < 1)
2232                 {
2233                 image_loader_free(pw->job_loader);
2234                 pw->job_loader = NULL;
2235
2236                 print_job_done(pw);
2237
2238                 return;
2239                 }
2240
2241         pixbuf = image_loader_get_pixbuf(il);
2242
2243         w = gdk_pixbuf_get_width(pixbuf);
2244         h = gdk_pixbuf_get_height(pixbuf);
2245
2246         if (pw->proof_width / w < pw->proof_height / h)
2247                 {
2248                 icon_w = pw->proof_width;
2249                 icon_h = pw->proof_width / w * h;
2250                 scale = icon_w / w;
2251                 }
2252         else
2253                 {
2254                 icon_h = pw->proof_height;
2255                 icon_w = pw->proof_height / h * w;
2256                 scale = icon_h / h;
2257                 }
2258
2259         y = pw->proof_position / pw->proof_columns;
2260         x = pw->proof_position - (y * pw->proof_columns);
2261
2262         print_proof_size(pw, &proof_w, &proof_h);
2263
2264         x *= proof_w;
2265         y *= proof_h;
2266         x += pw->margin_left + (pw->layout_width - pw->margin_left - pw->margin_right - (pw->proof_columns * proof_w)) / 2 + (proof_w - icon_w) / 2;
2267         y += pw->margin_top + PRINT_PROOF_MARGIN + (pw->proof_height - icon_h) / 2;
2268
2269         success = (success &&
2270                    print_job_page_image(pw, pixbuf, x, y, icon_w, icon_h, 0, 0));
2271
2272         x = x + icon_w / 2;
2273         y = y + icon_h + (pw->proof_height - icon_h) / 2 + PRINT_TEXT_PADDING;
2274
2275         success = (success &&
2276                    print_job_text_image(pw, pw->job_loader->fd->path, x, y, icon_w + PRINT_PROOF_MARGIN * 2, w, h, TRUE));
2277
2278         if (!success)
2279                 {
2280                 print_job_close(pw, TRUE);
2281                 return;
2282                 }
2283
2284         if (pw->proof_point) pw->proof_point = pw->proof_point->next;
2285
2286         pw->proof_position++;
2287         if (pw->proof_position >= pw->proof_columns * pw->proof_rows)
2288                 {
2289                 if (pw->job_format == RENDER_FORMAT_PREVIEW)
2290                         {
2291                         print_job_done(pw);
2292                         return;
2293                         }
2294
2295                 if (!print_job_page_done(pw))
2296                         {
2297                         print_job_close(pw, TRUE);
2298                         return;
2299                         }
2300
2301                 pw->proof_position = 0;
2302                 pw->job_page++;
2303                 if (print_job_render_proof(pw))
2304                         {
2305                         if (!print_job_page_new(pw))
2306                                 {
2307                                 print_job_close(pw, TRUE);
2308                                 return;
2309                                 }
2310                         print_job_status(pw);
2311                         }
2312                 else
2313                         {
2314                         print_job_done(pw);
2315                         }
2316                 }
2317         else
2318                 {
2319                 if (print_job_render_proof(pw))
2320                         {
2321                         print_job_status(pw);
2322                         }
2323                 else
2324                         {
2325                         if (print_job_page_done(pw))
2326                                 {
2327                                 print_job_done(pw);
2328                                 }
2329                         else
2330                                 {
2331                                 print_job_close(pw, TRUE);
2332                                 }
2333                         }
2334                 }
2335 }
2336
2337 static gint print_job_render_proof(PrintWindow *pw)
2338 {
2339         FileData *fd = NULL;
2340
2341         if (pw->proof_columns < 1 || pw->proof_rows < 1) return FALSE;
2342
2343         if (!pw->proof_point && pw->proof_position == 0 && pw->source == PRINT_SOURCE_IMAGE)
2344                 {
2345                 fd = pw->source_fd;
2346                 }
2347         else if (pw->proof_point &&
2348                  pw->proof_position < pw->proof_columns * pw->proof_rows)
2349                 {
2350                 fd = pw->proof_point->data;
2351                 }
2352
2353         if (!fd) return FALSE;
2354
2355         image_loader_free(pw->job_loader);
2356         pw->job_loader = image_loader_new(fd);
2357         if (!image_loader_start(pw->job_loader, print_job_render_proof_loader_done, pw))
2358                 {
2359                 image_loader_free(pw->job_loader);
2360                 pw->job_loader = NULL;
2361                 }
2362
2363         return TRUE;
2364 }
2365
2366 static void print_job_render(PrintWindow *pw)
2367 {
2368         gdouble proof_w, proof_h;
2369         gint finished;
2370
2371         pw->proof_position = 0;
2372
2373         switch (pw->source)
2374                 {
2375                 case PRINT_SOURCE_SELECTION:
2376                         pw->proof_point = pw->source_selection;
2377                         break;
2378                 case PRINT_SOURCE_ALL:
2379                         pw->proof_point = pw->source_list;
2380                         break;
2381                 case PRINT_SOURCE_IMAGE:
2382                 default:
2383                         pw->proof_point = NULL;
2384                         break;
2385                 }
2386
2387         print_proof_size(pw, &proof_w, &proof_h);
2388         pw->proof_columns = (pw->layout_width - pw->margin_left - pw->margin_right) / proof_w;
2389         pw->proof_rows = (pw->layout_height - pw->margin_top - pw->margin_bottom) / proof_h;
2390
2391         if (pw->job_format == RENDER_FORMAT_PREVIEW)
2392                 {
2393                 gint total;
2394
2395                 total = print_layout_page_count(pw);
2396                 if (pw->job_page < 0 || pw->job_page >= total)
2397                         {
2398                         print_job_done(pw);
2399                         return;
2400                         }
2401
2402                 if (pw->proof_point && pw->job_page > 0)
2403                         {
2404                         pw->proof_point = g_list_nth(pw->proof_point, pw->job_page * pw->proof_columns * pw->proof_rows);
2405                         }
2406                 }
2407
2408         if (!print_job_page_new(pw))
2409                 {
2410                 print_job_close(pw, TRUE);
2411                 return;
2412                 }
2413
2414         if (pw->layout == PRINT_LAYOUT_IMAGE)
2415                 {
2416                 finished = !print_job_render_image(pw);
2417                 }
2418         else
2419                 {
2420                 finished = !print_job_render_proof(pw);
2421                 }
2422
2423         if (finished) print_job_done(pw);
2424 }
2425
2426 static gint print_job_init(PrintWindow *pw)
2427 {
2428         gint success = FALSE;
2429
2430         pw->job_page = 0;
2431
2432         switch (pw->job_format)
2433                 {
2434                 case RENDER_FORMAT_RGB:
2435                         success = print_job_rgb_init(pw);
2436                         break;
2437                 case RENDER_FORMAT_PS:
2438                         success = print_job_ps_init(pw);
2439                         break;
2440                 case RENDER_FORMAT_PREVIEW:
2441                         pw->job_page = pw->proof_page;
2442                         success = print_job_preview_init(pw);
2443                         break;
2444                 }
2445
2446         return success;
2447 }
2448
2449 static gint print_job_finish(PrintWindow *pw)
2450 {
2451         gint success = FALSE;
2452
2453         switch (pw->job_format)
2454                 {
2455                 case RENDER_FORMAT_RGB:
2456                         success = TRUE;
2457                         break;
2458                 case RENDER_FORMAT_PS:
2459                         print_job_ps_end(pw);
2460                         break;
2461                 case RENDER_FORMAT_PREVIEW:
2462                         success = TRUE;
2463                         break;
2464                 }
2465
2466         return success;
2467 }
2468
2469 static void print_job_close_file(PrintWindow *pw)
2470 {
2471         if (pw->job_file)
2472                 {
2473                 fclose(pw->job_file);
2474                 pw->job_file = NULL;
2475                 }
2476
2477         if (pw->job_pipe)
2478                 {
2479                 PipeError *pe;
2480
2481                 pe = pipe_handler_new();
2482                 pclose(pw->job_pipe);
2483                 pipe_handler_free(pe);
2484
2485                 pw->job_pipe = NULL;
2486                 }
2487 }
2488
2489 static gboolean print_job_close_finish_cb(gpointer data)
2490 {
2491         PrintWindow *pw = data;
2492
2493         print_window_close(pw);
2494         return FALSE;
2495 }
2496
2497 static void print_job_close(PrintWindow *pw, gint error)
2498 {
2499         if (!error) print_job_finish(pw);
2500
2501         print_job_close_file(pw);
2502         g_free(pw->job_path);
2503         pw->job_path = NULL;
2504
2505         if (pw->job_dialog)
2506                 {
2507                 generic_dialog_close(pw->job_dialog);
2508                 pw->job_dialog = NULL;
2509                 pw->job_progress = NULL;
2510                 }
2511
2512         image_loader_free(pw->job_loader);
2513         pw->job_loader = NULL;
2514
2515         if (pw->job_pixbuf)
2516                 {
2517                 g_object_unref(pw->job_pixbuf);
2518                 pw->job_pixbuf = NULL;
2519                 }
2520
2521         if (pw->dialog && !GTK_WIDGET_VISIBLE(pw->dialog->dialog))
2522                 {
2523                 g_idle_add_full(G_PRIORITY_HIGH_IDLE, print_job_close_finish_cb, pw, NULL);
2524                 }
2525 }
2526
2527 static void print_job_cancel_cb(GenericDialog *gd, gpointer data)
2528 {
2529         PrintWindow *pw = data;
2530
2531         print_job_close(pw, FALSE);
2532 }
2533
2534 static void print_pref_store(PrintWindow *pw)
2535 {
2536
2537         pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_SAVE, pw->save_settings);
2538
2539         if (!pw->save_settings) return;
2540
2541         /* only store values that are actually used in this print job, hence the if()s */
2542
2543         pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_OUTPUT, pw->output);
2544
2545         if (pw->output == PRINT_OUTPUT_RGB_FILE)
2546                 {
2547                 pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_FORMAT, pw->output_format);
2548                 }
2549
2550         if (pw->job_format == RENDER_FORMAT_PS)
2551                 {
2552                 pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_DPI, pw->max_dpi);
2553                 }
2554
2555         pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_UNITS, pw->paper_units);
2556         pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_SIZE, pw->paper_size);
2557         pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_ORIENTATION, pw->paper_orientation);
2558
2559         if (pw->paper_size == 0)
2560                 {
2561                 pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_CUSTOM_WIDTH, pw->paper_width);
2562                 pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_CUSTOM_HEIGHT, pw->paper_height);
2563                 }
2564
2565         pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_MARGIN_LEFT, pw->margin_left);
2566         pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_MARGIN_RIGHT, pw->margin_right);
2567         pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_MARGIN_TOP, pw->margin_top);
2568         pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_MARGIN_BOTTOM, pw->margin_bottom);
2569
2570         if (pw->layout == PRINT_LAYOUT_PROOF)
2571                 {
2572                 pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_PROOF_WIDTH, pw->proof_width);
2573                 pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_PROOF_HEIGHT, pw->proof_height);
2574                 }
2575
2576         if (pw->output == PRINT_OUTPUT_PS_CUSTOM)
2577                 {
2578                 pref_list_string_set(PRINT_PREF_GROUP, PRINT_PREF_PRINTERC, pw->output_custom);
2579                 }
2580
2581         if (pw->output == PRINT_OUTPUT_RGB_FILE ||
2582             pw->output == PRINT_OUTPUT_PS_FILE)
2583                 {
2584                 tab_completion_append_to_history(pw->path_entry, pw->output_path);
2585                 }
2586 }
2587
2588 static gint print_job_start(PrintWindow *pw, RenderFormat format, PrintOutput output)
2589 {
2590         GtkWidget *hbox;
2591         GtkWidget *spinner;
2592         gchar *msg;
2593
2594         if (pw->job_dialog) return FALSE;
2595
2596         pw->job_format = format;
2597         pw->job_output = output;
2598
2599         if (!print_job_init(pw))
2600                 {
2601                 print_job_close(pw, TRUE);
2602                 return FALSE;
2603                 }
2604
2605         if (format == RENDER_FORMAT_PREVIEW)
2606                 {
2607                 print_job_render(pw);
2608                 return TRUE;
2609                 }
2610
2611         print_pref_store(pw);
2612
2613         gtk_widget_hide(pw->dialog->dialog);
2614
2615         {
2616         gchar *title = g_strdup_printf("%s - %s", _("Print"), GQ_APPNAME);
2617         pw->job_dialog = file_util_gen_dlg(title, GQ_WMCLASS, "print_job_dialog",
2618                                            (GtkWidget *)gtk_window_get_transient_for(GTK_WINDOW(pw->dialog->dialog)), FALSE,
2619                                            print_job_cancel_cb, pw);
2620         g_free(title);
2621         }
2622
2623         msg = g_strdup_printf(_("Printing %d pages to %s."), print_layout_page_count(pw), print_output_name(pw->output));
2624         generic_dialog_add_message(pw->job_dialog, NULL, msg, NULL);
2625         g_free(msg);
2626
2627         if (pw->job_output == PRINT_OUTPUT_PS_FILE ||
2628             pw->job_output == PRINT_OUTPUT_RGB_FILE)
2629                 {
2630                 hbox = pref_box_new(pw->job_dialog->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2631                 pref_label_new(hbox, _("Filename:"));
2632
2633                 pw->job_progress_label = pref_label_new(hbox, "");
2634                 }
2635         else
2636                 {
2637                 pw->job_progress_label = NULL;
2638                 }
2639
2640         pref_spacer(pw->job_dialog->vbox, PREF_PAD_SPACE);
2641
2642         hbox = pref_box_new(pw->job_dialog->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2643
2644         pw->job_progress = gtk_progress_bar_new();
2645         gtk_box_pack_start(GTK_BOX(hbox), pw->job_progress, TRUE, TRUE, 0);
2646         gtk_widget_show(pw->job_progress);
2647
2648         spinner = spinner_new(NULL, SPINNER_SPEED);
2649         gtk_box_pack_start(GTK_BOX(hbox), spinner, FALSE, FALSE, 0);
2650         gtk_widget_show(spinner);
2651
2652         gtk_widget_show(pw->job_dialog->dialog);
2653
2654         print_job_render(pw);
2655         print_job_status(pw);
2656
2657         return TRUE;
2658 }
2659
2660 static void print_window_print_start(PrintWindow *pw)
2661 {
2662         RenderFormat format;
2663
2664         switch(pw->output)
2665                 {
2666                 case PRINT_OUTPUT_RGB_FILE:
2667                         format = RENDER_FORMAT_RGB;
2668                         break;
2669                 case PRINT_OUTPUT_PS_FILE:
2670                 case PRINT_OUTPUT_PS_CUSTOM:
2671                 case PRINT_OUTPUT_PS_LPR:
2672                 default:
2673                         format = RENDER_FORMAT_PS;
2674                         break;
2675                 }
2676
2677         print_job_start(pw, format, pw->output);
2678 }
2679
2680
2681 /*
2682  *-----------------------------------------------------------------------------
2683  * combo box util
2684  *-----------------------------------------------------------------------------
2685  */
2686
2687 static GtkWidget *print_combo_menu(const gchar *text[], gint count, gint preferred,
2688                                    GCallback func, gpointer data)
2689 {
2690         GtkWidget *combo;
2691         gint i;
2692
2693         combo = gtk_combo_box_new_text();
2694
2695         for (i = 0 ; i < count; i++)
2696                 {
2697                 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _(text[i]));
2698                 }
2699
2700         if (preferred >= 0 && preferred < count)
2701                 {
2702                 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), preferred);
2703                 }
2704
2705         if (func) g_signal_connect(G_OBJECT(combo), "changed", func, data);
2706
2707         return combo;
2708 }
2709
2710
2711 /*
2712  *-----------------------------------------------------------------------------
2713  * paper selection
2714  *-----------------------------------------------------------------------------
2715  */
2716
2717 static GtkWidget *print_paper_menu(GtkWidget *table, gint column, gint row,
2718                                    gint preferred, GCallback func, gpointer data)
2719 {
2720         GtkWidget *combo;
2721         gint i;
2722
2723         pref_table_label(table, column, row, (_("Format:")), 1.0);
2724
2725         combo = gtk_combo_box_new_text();
2726
2727         i = 0;
2728         while (print_paper_sizes[i].description)
2729                 {
2730                 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _(print_paper_sizes[i].description));
2731                 i++;
2732                 }
2733
2734         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), preferred);
2735         if (func) g_signal_connect(G_OBJECT(combo), "changed", func, data);
2736
2737         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
2738                          GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
2739         gtk_widget_show(combo);
2740
2741         return combo;
2742 }
2743
2744 static void print_paper_select_cb(GtkWidget *combo, gpointer data)
2745 {
2746         PrintWindow *pw = data;
2747         PaperSize *ps;
2748         gint n;
2749
2750         n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
2751         ps = print_paper_size_nth(n);
2752
2753         if (!ps) return;
2754
2755         pw->paper_size = n;
2756
2757         if (pw->paper_size == 0)
2758                 {
2759                 print_window_layout_sync_paper(pw);
2760                 return;
2761                 }
2762
2763         if (ps->orientation == PAPER_ORIENTATION_PORTRAIT)
2764                 {
2765                 print_window_layout_set_size(pw, ps->width, ps->height);
2766                 }
2767         else
2768                 {
2769                 print_window_layout_set_size(pw, ps->height, ps->width);
2770                 }
2771 }
2772
2773 static void print_paper_size_cb(GtkWidget *spin, gpointer data)
2774 {
2775         PrintWindow *pw = data;
2776         gdouble value;
2777
2778         value = print_paper_size_convert_units(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)),
2779                                                pw->paper_units, PAPER_UNIT_POINTS);
2780
2781         if (spin == pw->paper_width_spin)
2782                 {
2783                 pw->paper_width = value;
2784                 }
2785         else
2786                 {
2787                 pw->paper_height = value;
2788                 }
2789
2790         print_window_layout_set_size(pw, pw->paper_width, pw->paper_height);
2791 }
2792
2793 static GtkWidget *print_paper_units_menu(GtkWidget *table, gint column, gint row,
2794                                          PaperUnits units, GCallback func, gpointer data)
2795 {
2796         GtkWidget *combo;
2797
2798         pref_table_label(table, column, row, (_("Units:")), 1.0);
2799
2800         combo = print_combo_menu(print_paper_units, PAPER_UNIT_COUNT, units, func, data);
2801
2802         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
2803                          GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
2804         gtk_widget_show(combo);
2805
2806         return combo;
2807 }
2808
2809 static void print_paper_units_set(PrintWindow *pw, PaperUnits units)
2810 {
2811         PaperUnits old_units;
2812
2813         if (units < 0 || units >= PAPER_UNIT_COUNT) return;
2814
2815         old_units = pw->paper_units;
2816         pw->paper_units = units;
2817         print_window_layout_sync_paper(pw);
2818
2819         if ((units == PAPER_UNIT_MM || units == PAPER_UNIT_CM) !=
2820             (old_units == PAPER_UNIT_MM || old_units == PAPER_UNIT_CM))
2821                 {
2822                 print_window_layout_render(pw);
2823                 }
2824 }
2825
2826 static void print_paper_units_cb(GtkWidget *combo, gpointer data)
2827 {
2828         PrintWindow *pw = data;
2829         PaperUnits units;
2830
2831         units = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
2832
2833         print_paper_units_set(pw, units);
2834 }
2835
2836 static GtkWidget *print_paper_orientation_menu(GtkWidget *table, gint column, gint row,
2837                                                PaperOrientation preferred,
2838                                                GCallback func, gpointer data)
2839 {
2840         GtkWidget *combo;
2841
2842         pref_table_label(table, column, row, (_("Orientation:")), 1.0);
2843
2844         combo = print_combo_menu(print_paper_orientation, PAPER_ORIENTATION_COUNT, preferred, func, data);
2845
2846         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
2847                          GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
2848         gtk_widget_show(combo);
2849
2850         return combo;
2851 }
2852
2853 static void print_paper_orientation_cb(GtkWidget *combo, gpointer data)
2854 {
2855         PrintWindow *pw = data;
2856         PaperOrientation o;
2857
2858         o = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
2859
2860         print_window_layout_set_orientation(pw, o);
2861 }
2862
2863 static void print_paper_margin_cb(GtkWidget *spin, gpointer data)
2864 {
2865         PrintWindow *pw = data;
2866         gdouble value;
2867
2868         value = print_paper_size_convert_units(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)),
2869                                                pw->paper_units, PAPER_UNIT_POINTS);
2870
2871         if (spin == pw->margin_left_spin)
2872                 {
2873                 pw->margin_left = CLAMP(value, 0.0, pw->paper_width);
2874                 }
2875         else if (spin == pw->margin_right_spin)
2876                 {
2877                 pw->margin_right = CLAMP(value, 0.0, pw->paper_width);
2878                 }
2879         else if (spin == pw->margin_top_spin)
2880                 {
2881                 pw->margin_top = CLAMP(value, 0.0, pw->paper_height);
2882                 }
2883         else if (spin == pw->margin_bottom_spin)
2884                 {
2885                 pw->margin_bottom = CLAMP(value, 0.0, pw->paper_height);
2886                 }
2887
2888         print_window_layout_set_size(pw, pw->paper_width, pw->paper_height);
2889 }
2890
2891 static GtkWidget *print_misc_menu(GtkWidget *parent_box, gint preferred,
2892                                   const gchar *title, const gchar *key,
2893                                   gint count, const gchar **text,
2894                                   GCallback func, gpointer data)
2895 {
2896         GtkWidget *box;
2897         GtkWidget *button = NULL;
2898         gint i;
2899
2900         box = pref_group_new(parent_box, FALSE, title, GTK_ORIENTATION_VERTICAL);
2901
2902         for (i = 0; i < count; i++)
2903                 {
2904                 button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button), _(text[i]));
2905                 if (i == preferred)
2906                         {
2907                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
2908                         }
2909                 g_object_set_data(G_OBJECT(button), key, GINT_TO_POINTER(i));
2910                 if (func) g_signal_connect(G_OBJECT(button), "clicked", func, data);
2911                 gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0);
2912                 gtk_widget_show(button);
2913                 }
2914
2915         return box;
2916 }
2917
2918 static void print_source_select_cb(GtkWidget *widget, gpointer data)
2919 {
2920         PrintWindow *pw = data;
2921
2922         if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) return;
2923
2924         pw->source = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "print_source"));
2925         print_window_layout_size(pw);
2926 }
2927
2928 static void print_layout_select_cb(GtkWidget *widget, gpointer data)
2929 {
2930         PrintWindow *pw = data;
2931
2932         if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) return;
2933
2934         pw->layout = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "print_layout"));
2935
2936         print_window_layout_sync_layout(pw);
2937         print_window_layout_size(pw);
2938 }
2939
2940 static void print_image_scale_cb(GtkWidget *spin, gpointer data)
2941 {
2942         PrintWindow *pw = data;
2943
2944         pw->image_scale = (gint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin));
2945
2946         print_window_layout_set_size(pw, pw->paper_width, pw->paper_height);
2947 }
2948
2949 static void print_proof_size_cb(GtkWidget *spin, gpointer data)
2950 {
2951         PrintWindow *pw = data;
2952         gdouble value;
2953
2954         value = print_paper_size_convert_units(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)),
2955                                                pw->paper_units, PAPER_UNIT_POINTS);
2956
2957         if (spin == pw->proof_width_spin)
2958                 {
2959                 pw->proof_width = value;
2960                 }
2961         else
2962                 {
2963                 pw->proof_height = value;
2964                 }
2965
2966         print_window_layout_render(pw);
2967 }
2968
2969 static GtkWidget *print_output_menu(GtkWidget *table, gint column, gint row,
2970                                     PrintOutput preferred, GCallback func, gpointer data)
2971 {
2972         GtkWidget *combo;
2973
2974         pref_table_label(table, column, row, (_("Destination:")), 1.0);
2975
2976         combo = print_combo_menu(print_output_text, PRINT_OUTPUT_COUNT, preferred, func, data);
2977
2978         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
2979                          GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
2980         gtk_widget_show(combo);
2981
2982         return combo;
2983 }
2984
2985 static void print_custom_entry_set(PrintWindow *pw, GtkWidget *combo)
2986 {
2987         GtkListStore *store;
2988         const gchar *text;
2989         GList *list;
2990         GList *work;
2991
2992         store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo)));
2993         gtk_list_store_clear(store);
2994
2995         list = print_window_list_printers();
2996         work = list;
2997         while (work)
2998                 {
2999                 gchar *name;
3000                 gchar *buf;
3001
3002                 name = work->data;
3003                 work = work->next;
3004
3005                 buf = g_strdup_printf(PRINT_LPR_CUSTOM, name);
3006                 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), buf);
3007                 g_free(buf);
3008                 }
3009         string_list_free(list);
3010
3011         if (pref_list_string_get(PRINT_PREF_GROUP, PRINT_PREF_PRINTERC, &text))
3012                 {
3013                 gtk_entry_set_text(GTK_ENTRY(pw->custom_entry), text);
3014                 }
3015         else
3016                 {
3017                 text = gtk_entry_get_text(GTK_ENTRY(pw->custom_entry));
3018                 if (!text || strlen(text) == 0)
3019                         {
3020                         gchar *buf;
3021
3022                         buf = g_strdup_printf(PRINT_LPR_CUSTOM, _("<printer name>"));
3023                         gtk_entry_set_text(GTK_ENTRY(pw->custom_entry), buf);
3024                         g_free(buf);
3025                         }
3026                 }
3027 }
3028
3029 static void print_output_set(PrintWindow *pw, PrintOutput output)
3030 {
3031         gint use_file = FALSE;
3032         gint use_custom = FALSE;
3033         gint use_format = FALSE;
3034
3035         pw->output = output;
3036
3037         switch (pw->output)
3038                 {
3039                 case PRINT_OUTPUT_RGB_FILE:
3040                         use_file = TRUE;
3041                         use_format = TRUE;
3042                         break;
3043                 case PRINT_OUTPUT_PS_FILE:
3044                         use_file = TRUE;
3045                         break;
3046                 case PRINT_OUTPUT_PS_CUSTOM:
3047                         use_custom = TRUE;
3048                         break;
3049                 case PRINT_OUTPUT_PS_LPR:
3050                 default:
3051                         break;
3052                 }
3053
3054         gtk_widget_set_sensitive(gtk_widget_get_parent(pw->path_entry), use_file);
3055         gtk_widget_set_sensitive(gtk_widget_get_parent(pw->custom_entry), use_custom);
3056         gtk_widget_set_sensitive(pw->path_format_menu, use_format);
3057         gtk_widget_set_sensitive(pw->max_dpi_menu, !use_format);
3058 }
3059
3060 static void print_output_cb(GtkWidget *combo, gpointer data)
3061 {
3062         PrintWindow *pw = data;
3063         PrintOutput output;
3064
3065         output = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3066
3067         print_output_set(pw, output);
3068 }
3069
3070 static GtkWidget *print_output_format_menu(GtkWidget * table, gint column, gint row,
3071                                            PrintFileFormat preferred, GCallback func, gpointer data)
3072 {
3073         GtkWidget *combo;
3074
3075         combo = print_combo_menu(print_file_format_text, PRINT_FILE_COUNT, preferred, func, data);
3076
3077         gtk_table_attach(GTK_TABLE(table), combo, column, column + 1, row, row + 1,
3078                          GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
3079         gtk_widget_show(combo);
3080
3081         return combo;
3082 }
3083
3084 static void print_output_format_cb(GtkWidget *combo, gpointer data)
3085 {
3086         PrintWindow *pw = data;
3087
3088         pw->output_format = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
3089 }
3090
3091 static GtkWidget *print_output_dpi_menu(GtkWidget * table, gint column, gint row,
3092                                         gdouble dpi, GCallback func, gpointer data)
3093 {
3094         static gint dpilist[] = { 150, 300, 600, 1200, 0, -1};
3095         GtkWidget *combo;
3096         GtkListStore *store;
3097         GtkCellRenderer *renderer;
3098         gint current = 1;
3099         gint i;
3100
3101         store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
3102
3103         i = 0;
3104         while (dpilist[i] != -1)
3105                 {
3106                 GtkTreeIter iter;
3107                 gchar *text;
3108
3109                 if (dpilist[i] == 0)
3110                         {
3111                         text = g_strdup(_("Unlimited"));
3112                         }
3113                 else
3114                         {
3115                         text = g_strdup_printf("%d", dpilist[i]);
3116                         }
3117
3118                 gtk_list_store_append(store, &iter);
3119                 gtk_list_store_set(store, &iter, 0, text, 1, dpilist[i], -1);
3120                 g_free(text);
3121
3122                 if (dpi == (gdouble)dpilist[i]) current = i;
3123
3124                 i++;
3125                 }
3126
3127         combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
3128         g_object_unref(store);
3129
3130         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3131         if (func) g_signal_connect(G_OBJECT(combo), "changed", func, data);
3132
3133         renderer = gtk_cell_renderer_text_new();
3134         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
3135         gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer, "text", 0, NULL);
3136
3137         gtk_table_attach(GTK_TABLE(table), combo, column, column + 1, row, row + 1,
3138                          GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
3139         gtk_widget_show(combo);
3140
3141         return combo;
3142 }
3143
3144 static void print_output_dpi_cb(GtkWidget *combo, gpointer data)
3145 {
3146         PrintWindow *pw = data;
3147         GtkTreeModel *store;
3148         GtkTreeIter iter;
3149         gint n = -1;
3150
3151         store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
3152         if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter)) return;
3153         gtk_tree_model_get(store, &iter, 1, &n, -1);
3154
3155         pw->max_dpi = (gdouble)n;
3156 }
3157
3158 static void print_text_field_set(PrintWindow *pw, TextInfo field, gint active)
3159 {
3160         if (active)
3161                 {
3162                 pw->text_fields |= field;
3163                 }
3164         else
3165                 {
3166                 pw->text_fields &= ~field;
3167                 }
3168
3169         print_window_layout_render(pw);
3170 }
3171
3172 static void print_text_cb_name(GtkWidget *widget, gpointer data)
3173 {
3174         PrintWindow *pw = data;
3175         gint active;
3176
3177         active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
3178         print_text_field_set(pw, TEXT_INFO_FILENAME, active);
3179 }
3180
3181 static void print_text_cb_path(GtkWidget *widget, gpointer data)
3182 {
3183         PrintWindow *pw = data;
3184         gint active;
3185
3186         active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
3187         print_text_field_set(pw, TEXT_INFO_FILEPATH, active);
3188 }
3189
3190 static void print_text_cb_date(GtkWidget *widget, gpointer data)
3191 {
3192         PrintWindow *pw = data;
3193         gint active;
3194
3195         active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
3196         print_text_field_set(pw, TEXT_INFO_FILEDATE, active);
3197 }
3198
3199 static void print_text_cb_size(GtkWidget *widget, gpointer data)
3200 {
3201         PrintWindow *pw = data;
3202         gint active;
3203
3204         active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
3205         print_text_field_set(pw, TEXT_INFO_FILESIZE, active);
3206 }
3207
3208 static void print_text_cb_dims(GtkWidget *widget, gpointer data)
3209 {
3210         PrintWindow *pw = data;
3211         gint active;
3212
3213         active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
3214         print_text_field_set(pw, TEXT_INFO_DIMENSIONS, active);
3215 }
3216
3217 static void print_text_cb_points(GtkWidget *widget, gpointer data)
3218 {
3219         PrintWindow *pw = data;
3220
3221         pw->text_points = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
3222         print_window_layout_render(pw);
3223 }
3224
3225 static void print_text_menu(GtkWidget *box, PrintWindow *pw)
3226 {
3227         GtkWidget *group;
3228
3229         group = pref_group_new(box, FALSE, _("Show"), GTK_ORIENTATION_VERTICAL);
3230
3231         pref_checkbox_new(group, _("Name"), (pw->text_fields & TEXT_INFO_FILENAME),
3232                           G_CALLBACK(print_text_cb_name), pw);
3233         pref_checkbox_new(group, _("Path"), (pw->text_fields & TEXT_INFO_FILEPATH),
3234                           G_CALLBACK(print_text_cb_path), pw);
3235         pref_checkbox_new(group, _("Date"), (pw->text_fields & TEXT_INFO_FILEDATE),
3236                           G_CALLBACK(print_text_cb_date), pw);
3237         pref_checkbox_new(group, _("Size"), (pw->text_fields & TEXT_INFO_FILESIZE),
3238                           G_CALLBACK(print_text_cb_size), pw);
3239         pref_checkbox_new(group, _("Dimensions"), (pw->text_fields & TEXT_INFO_DIMENSIONS),
3240                           G_CALLBACK(print_text_cb_dims), pw);
3241
3242         group = pref_group_new(box, FALSE, _("Font"), GTK_ORIENTATION_VERTICAL);
3243
3244         pref_spin_new(group, _("Size:"), _("points"),
3245                       8.0, 100.0, 1.0, 0, pw->text_points,
3246                       G_CALLBACK(print_text_cb_points), pw);
3247
3248 #if 0
3249         button = color_selection_new();
3250         gtk_box_pack_start(GTK_BOX(group), button, FALSE, FALSE, 0);
3251         gtk_widget_show(button);
3252 #endif
3253 }
3254
3255 /*
3256  *-----------------------------------------------------------------------------
3257  * print window
3258  *-----------------------------------------------------------------------------
3259  */
3260
3261 static void print_window_close(PrintWindow *pw)
3262 {
3263         print_window_layout_render_stop(pw);
3264
3265         generic_dialog_close(pw->dialog);
3266         pw->dialog = NULL;
3267
3268         print_job_close(pw, FALSE);
3269
3270         file_data_unref(pw->source_fd);
3271         filelist_free(pw->source_selection);
3272         filelist_free(pw->source_list);
3273
3274         g_free(pw->output_path);
3275         g_free(pw->output_custom);
3276
3277         g_free(pw);
3278 }
3279
3280 static void print_window_print_cb(GenericDialog *gd, gpointer data)
3281 {
3282         PrintWindow *pw = data;
3283
3284         switch (pw->output)
3285                 {
3286                 case PRINT_OUTPUT_RGB_FILE:
3287                 case PRINT_OUTPUT_PS_FILE:
3288                         g_free(pw->output_path);
3289                         pw->output_path = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry)));
3290                         break;
3291                 case PRINT_OUTPUT_PS_CUSTOM:
3292                         g_free(pw->output_custom);
3293                         pw->output_custom = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->custom_entry)));
3294                         break;
3295                 case PRINT_OUTPUT_PS_LPR:
3296                 default:
3297                         break;
3298                 }
3299
3300         print_window_print_start(pw);
3301 }
3302
3303 static void print_window_cancel_cb(GenericDialog *gd, gpointer data)
3304 {
3305         PrintWindow *pw = data;
3306
3307         print_window_close(pw);
3308 }
3309
3310 static gint print_pref_int(const gchar *key, gint fallback)
3311 {
3312         gint value;
3313
3314         if (pref_list_int_get(PRINT_PREF_GROUP, key, &value)) return value;
3315         return fallback;
3316 }
3317
3318 static gdouble print_pref_double(const gchar *key, gdouble fallback)
3319 {
3320         gdouble value;
3321
3322         if (pref_list_double_get(PRINT_PREF_GROUP, key, &value)) return value;
3323         return fallback;
3324 }
3325
3326 void print_window_new(FileData *fd, GList *selection, GList *list, GtkWidget *parent)
3327 {
3328         PrintWindow *pw;
3329         GdkGeometry geometry;
3330         GtkWidget *main_box;
3331         GtkWidget *vbox;
3332         GtkWidget *label;
3333         GtkWidget *combo;
3334         GtkWidget *box;
3335         GtkWidget *table;
3336
3337         pw = g_new0(PrintWindow, 1);
3338
3339         pw->source_fd = file_data_ref(fd);
3340         pw->source_selection = selection;
3341         pw->source_list = list;
3342
3343         pw->source = PRINT_SOURCE_SELECTION;
3344         pw->layout = PRINT_LAYOUT_IMAGE;
3345
3346         pw->output = print_pref_int(PRINT_PREF_OUTPUT, PRINT_OUTPUT_PS_LPR);
3347         pw->output_format = print_pref_int(PRINT_PREF_FORMAT, PRINT_FILE_JPG_NORMAL);
3348
3349         pw->max_dpi = print_pref_double(PRINT_PREF_DPI, PRINT_PS_DPI_DEFAULT);
3350
3351         pw->paper_units = print_pref_int(PRINT_PREF_UNITS, paper_unit_default());
3352         pw->paper_size = print_pref_int(PRINT_PREF_SIZE, 1);
3353         if (pw->paper_size == 0 ||
3354             !print_paper_size_lookup(pw->paper_size, &pw->paper_width, &pw->paper_height))
3355                 {
3356                 pw->paper_width = print_pref_double(PRINT_PREF_CUSTOM_WIDTH, 360.0);
3357                 pw->paper_height = print_pref_double(PRINT_PREF_CUSTOM_HEIGHT, 720.0);
3358                 }
3359         pw->paper_orientation = print_pref_int(PRINT_PREF_ORIENTATION, PAPER_ORIENTATION_PORTRAIT);
3360
3361         pw->margin_left = print_pref_double(PRINT_PREF_MARGIN_LEFT, PRINT_MARGIN_DEFAULT);
3362         pw->margin_right = print_pref_double(PRINT_PREF_MARGIN_RIGHT, PRINT_MARGIN_DEFAULT);
3363         pw->margin_top = print_pref_double(PRINT_PREF_MARGIN_TOP, PRINT_MARGIN_DEFAULT);
3364         pw->margin_bottom = print_pref_double(PRINT_PREF_MARGIN_BOTTOM, PRINT_MARGIN_DEFAULT);
3365
3366         pw->proof_width = print_pref_double(PRINT_PREF_PROOF_WIDTH, PRINT_PROOF_DEFAULT_SIZE);
3367         pw->proof_height = print_pref_double(PRINT_PREF_PROOF_HEIGHT, PRINT_PROOF_DEFAULT_SIZE);
3368
3369         pw->text_fields = TEXT_INFO_FILENAME;
3370         pw->text_points = 10;
3371         pw->text_r = pw->text_g = pw->text_b = 0;
3372
3373         pw->save_settings = print_pref_int(PRINT_PREF_SAVE, TRUE);
3374
3375         {
3376         gchar *title = g_strdup_printf("%s - %s", _("Print"), GQ_APPNAME);
3377         pw->dialog = file_util_gen_dlg(title, GQ_WMCLASS, "print_dialog",
3378                                        parent, FALSE,
3379                                        print_window_cancel_cb, pw);
3380         g_free(title);
3381         }
3382
3383         geometry.min_width = 32;
3384         geometry.min_height = 32;
3385         geometry.base_width = PRINT_DLG_WIDTH;
3386         geometry.base_height = PRINT_DLG_HEIGHT;
3387         gtk_window_set_geometry_hints(GTK_WINDOW(pw->dialog->dialog), NULL, &geometry,
3388                                       GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE);
3389
3390         pw->print_button = generic_dialog_add_button(pw->dialog, GTK_STOCK_PRINT, NULL, print_window_print_cb, TRUE);
3391
3392         main_box = pref_box_new(pw->dialog->vbox, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP);
3393
3394         pw->notebook = gtk_notebook_new();
3395         gtk_notebook_set_tab_pos(GTK_NOTEBOOK(pw->notebook), GTK_POS_TOP);
3396         gtk_box_pack_start(GTK_BOX(main_box), pw->notebook, FALSE, FALSE, 0);
3397
3398         /* layout tab */
3399
3400         vbox = gtk_vbox_new(FALSE, 0);
3401         gtk_container_set_border_width(GTK_CONTAINER(vbox), PREF_PAD_BORDER);
3402         gtk_widget_show(vbox);
3403         label = gtk_label_new(_("Layout"));
3404         gtk_notebook_append_page(GTK_NOTEBOOK(pw->notebook), vbox, label);
3405
3406         print_misc_menu(vbox, pw->source, _("Source"), "print_source",
3407                         PRINT_SOURCE_COUNT, print_source_text,
3408                         G_CALLBACK(print_source_select_cb), pw);
3409
3410         box = print_misc_menu(vbox, pw->layout, _("Layout"), "print_layout",
3411                               PRINT_LAYOUT_COUNT, print_layout_text,
3412                               G_CALLBACK(print_layout_select_cb), pw);
3413
3414         pref_spacer(box, PREF_PAD_GROUP);
3415
3416         table = pref_table_new(box, 2, 2, FALSE, FALSE);
3417
3418         pw->image_scale_spin = pref_table_spin(table, 0, 0, _("Image size:"), "%",
3419                                                5.0, 100.0, 1.0, 0, 100.0,
3420                                                G_CALLBACK(print_image_scale_cb), pw);
3421
3422         label = pref_table_label(table, 0, 1, _("Proof size:"), 1.0);
3423         pw->proof_group = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
3424         pref_link_sensitivity(label, pw->proof_group);
3425
3426         pw->proof_width_spin = pref_spin_new(pw->proof_group, NULL, NULL,
3427                                              0.0, 50.0, 0.1, 3, 0.0,
3428                                              G_CALLBACK(print_proof_size_cb), pw);
3429         pw->proof_height_spin = pref_spin_new(pw->proof_group, "x", NULL,
3430                                               0.0, 50.0, 0.1, 3, 0.0,
3431                                               G_CALLBACK(print_proof_size_cb), pw);
3432
3433         /* text tab */
3434
3435         vbox = gtk_vbox_new(FALSE, 0);
3436         gtk_container_set_border_width(GTK_CONTAINER(vbox), PREF_PAD_BORDER);
3437         gtk_widget_show(vbox);
3438         label = gtk_label_new(_("Text"));
3439         gtk_notebook_append_page(GTK_NOTEBOOK(pw->notebook), vbox, label);
3440
3441         print_text_menu(vbox, pw);
3442
3443         /* paper tab */
3444
3445         vbox = gtk_vbox_new(FALSE, 0);
3446         gtk_container_set_border_width(GTK_CONTAINER(vbox), PREF_PAD_BORDER);
3447         gtk_widget_show(vbox);
3448         label = gtk_label_new(_("Paper"));
3449         gtk_notebook_append_page(GTK_NOTEBOOK(pw->notebook), vbox, label);
3450
3451         table = pref_table_new(vbox, 2, 4, FALSE, FALSE);
3452
3453         print_paper_menu(table, 0, 0, pw->paper_size, G_CALLBACK(print_paper_select_cb), pw);
3454
3455         label = pref_table_label(table, 0, 1, (_("Size:")), 1.0);
3456         box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
3457         pw->paper_width_spin = pref_spin_new(box, NULL, NULL,
3458                                              1.0, 10000.0, 1.0, 2, 66,
3459                                              G_CALLBACK(print_paper_size_cb), pw);
3460         pw->paper_height_spin = pref_spin_new(box, "x", NULL,
3461                                               1.0, 10000.0, 1.0, 2, 66,
3462                                               G_CALLBACK(print_paper_size_cb), pw);
3463         pref_link_sensitivity(label, pw->paper_width_spin);
3464
3465         pw->paper_units_menu = print_paper_units_menu(table, 0, 2, pw->paper_units,
3466                                         G_CALLBACK(print_paper_units_cb), pw);
3467
3468         print_paper_orientation_menu(table, 0, 3, pw->paper_orientation,
3469                                      G_CALLBACK(print_paper_orientation_cb), pw);
3470
3471         box = pref_group_new(vbox, FALSE, _("Margins"), GTK_ORIENTATION_VERTICAL);
3472         table = pref_table_new(box, 4, 2, FALSE, FALSE);
3473         pw->margin_left_spin = pref_table_spin(table, 0, 0, _("Left:"), NULL,
3474                                         0.0, 50.0, 0.1, 3, 0.0,
3475                                         G_CALLBACK(print_paper_margin_cb), pw);
3476         pw->margin_right_spin = pref_table_spin(table, 2, 0, _("Right:"), NULL,
3477                                         0.0, 50.0, 0.1, 3, 0.0,
3478                                         G_CALLBACK(print_paper_margin_cb), pw);
3479         pw->margin_top_spin = pref_table_spin(table, 0, 1, _("Top:"), NULL,
3480                                         0.0, 50.0, 0.1, 3, 0.0,
3481                                         G_CALLBACK(print_paper_margin_cb), pw);
3482         pw->margin_bottom_spin = pref_table_spin(table, 2, 1, _("Bottom:"), NULL,
3483                                         0.0, 50.0, 0.1, 3, 0.0,
3484                                         G_CALLBACK(print_paper_margin_cb), pw);
3485
3486         /* printer tab */
3487
3488         vbox = gtk_vbox_new(FALSE, 0);
3489         gtk_container_set_border_width(GTK_CONTAINER(vbox), PREF_PAD_BORDER);
3490         gtk_widget_show(vbox);
3491         label = gtk_label_new(_("Printer"));
3492         gtk_notebook_append_page(GTK_NOTEBOOK(pw->notebook), vbox, label);
3493
3494         table = pref_table_new(vbox, 2, 5, FALSE, FALSE);
3495         print_output_menu(table, 0, 0, pw->output, G_CALLBACK(print_output_cb), pw);
3496
3497         label = pref_table_label(table, 0, 1, _("Custom printer:"), 1.0);
3498         combo = history_combo_new(&pw->custom_entry, NULL, "print_custom", -1);
3499         print_custom_entry_set(pw, combo);
3500         gtk_table_attach(GTK_TABLE(table), combo, 1, 2, 1, 2,
3501                          GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
3502         gtk_widget_show(combo);
3503
3504         pref_link_sensitivity(label, combo);
3505
3506         label = pref_table_label(table, 0, 2, _("File:"), 1.0);
3507         combo = tab_completion_new_with_history(&pw->path_entry, NULL, "print_path", -1, NULL, pw);
3508         tab_completion_add_select_button(pw->path_entry, NULL, FALSE);
3509         gtk_table_attach(GTK_TABLE(table), combo, 1, 2, 2, 3,
3510                          GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
3511         gtk_widget_show(combo);
3512
3513         pref_link_sensitivity(label, combo);
3514
3515         label = pref_table_label(table, 0, 3, _("File format:"), 1.0);
3516         pw->path_format_menu = print_output_format_menu(table, 1, 3, pw->output_format,
3517                                                         G_CALLBACK(print_output_format_cb), pw);
3518         pref_link_sensitivity(label, pw->path_format_menu);
3519
3520         label = pref_table_label(table, 0, 4, _("DPI:"), 1.0);
3521         pw->max_dpi_menu = print_output_dpi_menu(table, 1, 4, pw->max_dpi,
3522                                                  G_CALLBACK(print_output_dpi_cb), pw);
3523         pref_link_sensitivity(label, pw->max_dpi_menu);
3524
3525         print_output_set(pw, pw->output);
3526
3527         vbox = print_window_layout_setup(pw, main_box);
3528         pref_checkbox_new_int(vbox, _("Remember print settings"), pw->save_settings, &pw->save_settings);
3529
3530         print_window_layout_sync_layout(pw);
3531         print_window_layout_sync_paper(pw);
3532
3533         gtk_widget_show(pw->notebook);
3534         gtk_widget_show(pw->dialog->dialog);
3535 }