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