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