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