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