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