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