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