1 /* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
2 /* Balsa E-Mail Client
3 * Copyright (C) 1997-2013 Stuart Parmenter and others,
4 * See the file AUTHORS for a list.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 */
21
22 #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
23 # include "config.h"
24 #endif /* HAVE_CONFIG_H */
25
26 #include <string.h>
27 #include <gtk/gtk.h>
28 #include "balsa-app.h"
29 #include "print.h"
30 #include "misc.h"
31 #include "balsa-message.h"
32 #include "quote-color.h"
33 #include <glib/gi18n.h>
34 #include "balsa-print-object.h"
35 #include "balsa-print-object-decor.h"
36 #include "balsa-print-object-header.h"
37
38 #if HAVE__NL_MEASUREMENT_MEASUREMENT
39 #include <langinfo.h>
40 #endif /* HAVE__NL_MEASUREMENT_MEASUREMENT */
41
42 typedef struct {
43 GtkWidget *header_font;
44 GtkWidget *body_font;
45 GtkWidget *footer_font;
46 GtkWidget *highlight_cited;
47 GtkWidget *highlight_phrases;
48 GtkWidget *margin_top;
49 GtkWidget *margin_bottom;
50 GtkWidget *margin_left;
51 GtkWidget *margin_right;
52 } BalsaPrintPrefs;
53
54
55 typedef struct {
56 /* related message */
57 LibBalsaMessage *message;
58
59 /* print setup */
60 BalsaPrintSetup setup;
61
62 /* print data */
63 GList *print_parts;
64
65 /* header related stuff */
66 gdouble c_header_y;
67
68 /* page footer related stuff */
69 gchar *footer;
70 gdouble c_footer_y;
71 } BalsaPrintData;
72
73
74 /* print the page header and footer */
75 static void
print_header_footer(GtkPrintContext * context,cairo_t * cairo_ctx,BalsaPrintData * pdata,gint pageno)76 print_header_footer(GtkPrintContext * context, cairo_t * cairo_ctx,
77 BalsaPrintData * pdata, gint pageno)
78 {
79 PangoLayout *layout;
80 PangoFontDescription *font;
81 gchar *pagebuf;
82
83 /* page number */
84 font = pango_font_description_from_string(balsa_app.print_header_font);
85 layout = gtk_print_context_create_pango_layout(context);
86 pango_layout_set_font_description(layout, font);
87 pango_font_description_free(font);
88 pango_layout_set_width(layout, C_TO_P(pdata->setup.c_width));
89 pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT);
90 pagebuf =
91 g_strdup_printf(_("Page %d of %d"), pageno + 1, pdata->setup.page_count);
92 pango_layout_set_text(layout, pagebuf, -1);
93 g_free(pagebuf);
94 cairo_move_to(cairo_ctx, pdata->setup.c_x0, pdata->c_header_y);
95 pango_cairo_show_layout(cairo_ctx, layout);
96 g_object_unref(G_OBJECT(layout));
97
98 /* footer (if available) */
99 if (pdata->footer) {
100 font =
101 pango_font_description_from_string(balsa_app.
102 print_footer_font);
103 layout = gtk_print_context_create_pango_layout(context);
104 pango_layout_set_font_description(layout, font);
105 pango_font_description_free(font);
106 pango_layout_set_width(layout, C_TO_P(pdata->setup.c_width));
107 pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
108 pango_layout_set_text(layout, pdata->footer, -1);
109 cairo_move_to(cairo_ctx, pdata->setup.c_x0, pdata->c_footer_y);
110 pango_cairo_show_layout(cairo_ctx, layout);
111 g_object_unref(G_OBJECT(layout));
112 }
113 }
114
115
116 /*
117 * scan the body list and prepare print data according to the content type
118 */
119 static GList *
scan_body(GList * bpo_list,GtkPrintContext * context,BalsaPrintSetup * psetup,LibBalsaMessageBody * body,gboolean no_first_sep)120 scan_body(GList *bpo_list, GtkPrintContext * context, BalsaPrintSetup * psetup,
121 LibBalsaMessageBody * body, gboolean no_first_sep)
122 {
123 #ifdef HAVE_GPGME
124 gboolean add_signature;
125 gboolean have_crypto_frame;
126 #endif /* HAVE_GPGME */
127
128 while (body) {
129 gchar *conttype;
130
131 conttype = libbalsa_message_body_get_mime_type(body);
132 #ifdef HAVE_GPGME
133 add_signature = body->sig_info &&
134 g_ascii_strcasecmp(conttype, "application/pgp-signature") &&
135 g_ascii_strcasecmp(conttype, "application/pkcs7-signature") &&
136 g_ascii_strcasecmp(conttype, "application/x-pkcs7-signature");
137 if (!g_ascii_strcasecmp("multipart/signed", conttype) &&
138 body->parts && body->parts->next
139 && body->parts->next->sig_info) {
140 have_crypto_frame = TRUE;
141 bpo_list = balsa_print_object_separator(bpo_list, psetup);
142 no_first_sep = TRUE;
143 if (body->was_encrypted)
144 bpo_list = balsa_print_object_frame_begin(bpo_list,
145 _("Signed and encrypted matter"),
146 psetup);
147 else
148 bpo_list = balsa_print_object_frame_begin(bpo_list,
149 _("Signed matter"),
150 psetup);
151 } else if (!add_signature && body->was_encrypted) {
152 have_crypto_frame = TRUE;
153 bpo_list = balsa_print_object_separator(bpo_list, psetup);
154 no_first_sep = TRUE;
155 bpo_list = balsa_print_object_frame_begin(bpo_list,
156 _("Encrypted matter"),
157 psetup);
158 } else
159 have_crypto_frame = FALSE;
160 #endif /* HAVE_GPGME */
161
162 if (g_ascii_strncasecmp(conttype, "multipart/", 10)) {
163 if (no_first_sep)
164 no_first_sep = FALSE;
165 else
166 bpo_list = balsa_print_object_separator(bpo_list, psetup);
167 #ifdef HAVE_GPGME
168 if (add_signature) {
169 if (body->was_encrypted)
170 bpo_list = balsa_print_object_frame_begin(bpo_list,
171 _("Signed and encrypted matter"),
172 psetup);
173 else
174 bpo_list = balsa_print_object_frame_begin(bpo_list,
175 _("Signed matter"),
176 psetup);
177 }
178 #endif /* HAVE_GPGME */
179 bpo_list = balsa_print_objects_append_from_body(bpo_list, context,
180 body, psetup);
181 }
182
183 if (body->parts) {
184 bpo_list = scan_body(bpo_list, context, psetup, body->parts, no_first_sep);
185 no_first_sep = FALSE;
186 }
187
188 /* end the frame for an embedded message or encrypted stuff */
189 if (!g_ascii_strcasecmp(conttype, "message/rfc822")
190 #ifdef HAVE_GPGME
191 || have_crypto_frame
192 #endif
193 )
194 bpo_list = balsa_print_object_frame_end(bpo_list, psetup);
195
196 #ifdef HAVE_GPGME
197 if (add_signature) {
198 gchar *header =
199 g_strdup_printf(_("This is an inline %s signed %s message part:"),
200 body->sig_info->protocol == GPGME_PROTOCOL_OpenPGP ?
201 _("OpenPGP") : _("S/MIME"), conttype);
202 bpo_list = balsa_print_object_separator(bpo_list, psetup);
203 bpo_list = balsa_print_object_header_crypto(bpo_list, context, body, header, psetup);
204 g_free(header);
205 bpo_list = balsa_print_object_frame_end(bpo_list, psetup);
206 }
207 #endif /* HAVE_GPGME */
208 g_free(conttype);
209
210 body = body->next;
211 }
212
213 return bpo_list;
214 }
215
216
217 static void
begin_print(GtkPrintOperation * operation,GtkPrintContext * context,BalsaPrintData * pdata)218 begin_print(GtkPrintOperation * operation, GtkPrintContext * context,
219 BalsaPrintData * pdata)
220 {
221 GtkPageSetup *page_setup;
222 PangoLayout *layout;
223 PangoFontDescription *font;
224 gchar *pagebuf;
225 gchar *subject;
226 gchar *date;
227 GString *footer_string;
228
229 /* initialise the context */
230 page_setup = gtk_print_context_get_page_setup(context);
231
232 /* calculate the "real" margins */
233 pdata->setup.c_x0 = balsa_app.margin_left -
234 gtk_page_setup_get_left_margin(page_setup, GTK_UNIT_POINTS);
235 pdata->setup.c_width =
236 gtk_page_setup_get_page_width(page_setup, GTK_UNIT_POINTS) -
237 pdata->setup.c_x0 -(balsa_app.margin_right -
238 gtk_page_setup_get_right_margin(page_setup, GTK_UNIT_POINTS));
239 pdata->setup.c_y0 = balsa_app.margin_top -
240 gtk_page_setup_get_top_margin(page_setup, GTK_UNIT_POINTS);
241 pdata->setup.c_height =
242 gtk_page_setup_get_page_height(page_setup, GTK_UNIT_POINTS) -
243 pdata->setup.c_y0 -(balsa_app.margin_bottom -
244 gtk_page_setup_get_bottom_margin(page_setup, GTK_UNIT_POINTS));
245
246 pdata->setup.page_count = 1;
247
248 /* create a layout so we can do some calculations */
249 layout = gtk_print_context_create_pango_layout(context);
250 pagebuf = g_strdup_printf(_("Page %d of %d"), 17, 42);
251
252 /* basic body font height */
253 font = pango_font_description_from_string(balsa_app.print_body_font);
254 pango_layout_set_font_description(layout, font);
255 pango_font_description_free(font);
256 pdata->setup.p_body_font_height =
257 p_string_height_from_layout(layout, pagebuf);
258
259 /* basic header font and header height */
260 font = pango_font_description_from_string(balsa_app.print_header_font);
261 pango_layout_set_font_description(layout, font);
262 pango_font_description_free(font);
263 pdata->setup.p_hdr_font_height =
264 p_string_height_from_layout(layout, pagebuf);
265 g_free(pagebuf);
266
267 pdata->c_header_y = pdata->setup.c_y0;
268 pdata->setup.c_y0 += P_TO_C(pdata->setup.p_hdr_font_height) + C_HEADER_SEP;
269 pdata->setup.c_y_pos = pdata->setup.c_y0;
270 pdata->setup.c_height -=
271 P_TO_C(pdata->setup.p_hdr_font_height) + C_HEADER_SEP;
272
273 /* now create the footer string so we can reduce the height accordingly */
274 subject = g_strdup(LIBBALSA_MESSAGE_GET_SUBJECT(pdata->message));
275 libbalsa_utf8_sanitize(&subject, balsa_app.convert_unknown_8bit, NULL);
276 if (subject)
277 footer_string = g_string_new(subject);
278 else
279 footer_string = NULL;
280
281 date = libbalsa_message_date_to_utf8(pdata->message, balsa_app.date_string);
282 if (footer_string) {
283 footer_string = g_string_append(footer_string, " \342\200\224 ");
284 footer_string = g_string_append(footer_string, date);
285 } else
286 footer_string = g_string_new(date);
287 g_free(date);
288
289 if (pdata->message->headers->from) {
290 gchar *from =
291 internet_address_list_to_string(pdata->message->headers->from, FALSE);
292
293 libbalsa_utf8_sanitize(&from, balsa_app.convert_unknown_8bit,
294 NULL);
295 if (footer_string) {
296 footer_string =
297 g_string_prepend(footer_string, " \342\200\224 ");
298 footer_string = g_string_prepend(footer_string, from);
299 } else {
300 footer_string = g_string_new(from);
301 }
302 g_free(from);
303 }
304
305 /* if a footer is available, remember the string and adjust the height */
306 if (footer_string) {
307 gint p_height;
308
309 /* create a layout to calculate the height of the footer */
310 font = pango_font_description_from_string(balsa_app.print_footer_font);
311 pango_layout_set_font_description(layout, font);
312 pango_font_description_free(font);
313 pango_layout_set_width(layout, C_TO_P(pdata->setup.c_width));
314 pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
315
316 /* calculate the height and adjust the pringrid region */
317 p_height = p_string_height_from_layout(layout, footer_string->str);
318 pdata->c_footer_y =
319 pdata->setup.c_y0 + pdata->setup.c_height - P_TO_C(p_height);
320 pdata->setup.c_height -= P_TO_C(p_height) + C_HEADER_SEP;
321
322 /* remember in the context */
323 pdata->footer = g_string_free(footer_string, FALSE);
324 }
325 g_object_unref(G_OBJECT(layout));
326
327 /* add the message headers */
328 pdata->setup.c_y_pos = 0.0; /* to simplify calculating the layout... */
329 pdata->print_parts =
330 balsa_print_object_header_from_message(NULL, context, pdata->message,
331 subject, &pdata->setup);
332 g_free(subject);
333
334 /* add the mime bodies */
335 pdata->print_parts =
336 scan_body(pdata->print_parts, context, &pdata->setup,
337 pdata->message->body_list, FALSE);
338
339 /* done */
340 gtk_print_operation_set_n_pages(operation, pdata->setup.page_count);
341 }
342
343
344 static void
draw_page(GtkPrintOperation * operation,GtkPrintContext * context,gint page_nr,BalsaPrintData * print_data)345 draw_page(GtkPrintOperation * operation, GtkPrintContext * context,
346 gint page_nr, BalsaPrintData * print_data)
347 {
348 cairo_t *cairo_ctx;
349 GList * p;
350
351 /* emit a warning if we try to print a non-existing page */
352 if (page_nr >= print_data->setup.page_count) {
353 balsa_information(LIBBALSA_INFORMATION_WARNING,
354 ngettext("Cannot print page %d because "
355 "the document has only %d page.",
356 "Cannot print page %d because "
357 "the document has only %d pages.",
358 print_data->setup.page_count),
359 page_nr + 1, print_data->setup.page_count);
360 return;
361 }
362
363 /* get the cairo context */
364 cairo_ctx = gtk_print_context_get_cairo_context(context);
365
366 /* print the page header and footer */
367 print_header_footer(context, cairo_ctx, print_data, page_nr);
368
369 /* print parts */
370 p = print_data->print_parts;
371 while (p) {
372 BalsaPrintObject *po = BALSA_PRINT_OBJECT(p->data);
373
374 if (po->on_page == page_nr)
375 balsa_print_object_draw(po, context, cairo_ctx);
376
377 p = g_list_next(p);
378 }
379 }
380
381 /* setup gui related stuff */
382 /* shamelessly stolen from gtk+-2.10.6/gtk/gtkpagesetupunixdialog.c */
383 static GtkUnit
get_default_user_units(void)384 get_default_user_units(void)
385 {
386 /* Translate to the default units to use for presenting
387 * lengths to the user. Translate to default:inch if you
388 * want inches, otherwise translate to default:mm.
389 * Do *not* translate it to "predefinito:mm", if it
390 * it isn't default:mm or default:inch it will not work
391 */
392 gchar *e = _("default:mm");
393
394 #if HAVE__NL_MEASUREMENT_MEASUREMENT
395 gchar *imperial = NULL;
396
397 imperial = nl_langinfo(_NL_MEASUREMENT_MEASUREMENT);
398 if (imperial && imperial[0] == 2 )
399 return GTK_UNIT_INCH; /* imperial */
400 if (imperial && imperial[0] == 1 )
401 return GTK_UNIT_MM; /* metric */
402 #endif /* HAVE__NL_MEASUREMENT_MEASUREMENT */
403
404 if (strcmp(e, "default:inch")==0)
405 return GTK_UNIT_INCH;
406 else if (strcmp(e, "default:mm"))
407 g_warning("Whoever translated default:mm did so wrongly.\n");
408 return GTK_UNIT_MM;
409 }
410
411 static GtkWidget *
add_font_button(const gchar * text,const gchar * font,GtkGrid * grid,gint row)412 add_font_button(const gchar * text, const gchar * font, GtkGrid * grid,
413 gint row)
414 {
415 GtkWidget *label;
416 GtkWidget *font_button;
417
418 label = gtk_label_new_with_mnemonic(text);
419 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
420 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
421 gtk_widget_set_halign(label, GTK_ALIGN_START);
422 gtk_grid_attach(grid, label, 0, row, 1, 1);
423
424 font_button = gtk_font_button_new_with_font(font);
425 gtk_label_set_mnemonic_widget(GTK_LABEL(label), font_button);
426 gtk_widget_set_hexpand(font_button, TRUE);
427 gtk_grid_attach(grid, font_button, 1, row, 1, 1);
428
429 return font_button;
430 }
431
432 /* note: min and max are passed in points = 1/72" */
433 static GtkWidget *
add_margin_spinbtn(const gchar * text,gdouble min,gdouble max,gdouble dflt,GtkGrid * grid,gint row)434 add_margin_spinbtn(const gchar * text, gdouble min, gdouble max, gdouble dflt,
435 GtkGrid * grid, gint row)
436 {
437 GtkWidget *label;
438 GtkWidget *spinbtn;
439 const gchar *unit;
440
441 label = gtk_label_new_with_mnemonic(text);
442 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
443 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
444 gtk_widget_set_halign(label, GTK_ALIGN_START);
445 gtk_grid_attach(grid, label, 0, row, 1, 1);
446
447 if (get_default_user_units() == GTK_UNIT_INCH) {
448 unit = _("inch");
449 spinbtn = gtk_spin_button_new_with_range(min / 72.0,
450 max / 72.0, 0.01);
451 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbtn), dflt / 72.0);
452 gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spinbtn), 2);
453 gtk_spin_button_set_increments(GTK_SPIN_BUTTON(spinbtn), 0.1, 1.0);
454 } else {
455 unit = _("mm");
456 spinbtn = gtk_spin_button_new_with_range(min / 72.0 * 25.4,
457 max / 72.0 * 25.4, 0.1);
458 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbtn), dflt / 72.0 * 25.4);
459 gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spinbtn), 1);
460 gtk_spin_button_set_increments(GTK_SPIN_BUTTON(spinbtn), 1.0, 10.0);
461 }
462 gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(spinbtn), TRUE);
463 gtk_label_set_mnemonic_widget(GTK_LABEL(label), spinbtn);
464 gtk_grid_attach(grid, spinbtn, 1, row, 1, 1);
465
466 label = gtk_label_new(unit);
467 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
468 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
469 gtk_widget_set_halign(label, GTK_ALIGN_START);
470 gtk_grid_attach(grid, label, 2, row, 1, 1);
471
472 return spinbtn;
473 }
474
475 static void
check_margins(GtkAdjustment * adjustment,GtkAdjustment * other)476 check_margins(GtkAdjustment * adjustment, GtkAdjustment * other)
477 {
478 if (gtk_adjustment_get_value(adjustment) +
479 gtk_adjustment_get_value(other) >
480 gtk_adjustment_get_upper(adjustment))
481 gtk_adjustment_set_value(adjustment,
482 gtk_adjustment_get_upper(adjustment) -
483 gtk_adjustment_get_value(other));
484 }
485
486 static GtkWidget *
message_prefs_widget(GtkPrintOperation * operation,BalsaPrintPrefs * print_prefs)487 message_prefs_widget(GtkPrintOperation * operation,
488 BalsaPrintPrefs * print_prefs)
489 {
490 GtkWidget *page;
491 GtkWidget *group;
492 GtkWidget *label;
493 GtkWidget *hbox;
494 GtkWidget *vbox;
495 GtkWidget *grid;
496 GtkPageSetup *pg_setup;
497 gchar *markup;
498
499 gtk_print_operation_set_custom_tab_label(operation, _("Message"));
500
501 page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
502 gtk_container_set_border_width(GTK_CONTAINER(page), 12);
503
504 group = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
505 gtk_box_pack_start(GTK_BOX(page), group, FALSE, TRUE, 0);
506
507 label = gtk_label_new(NULL);
508 markup = g_strdup_printf("<b>%s</b>", _("Fonts"));
509 gtk_label_set_markup(GTK_LABEL(label), markup);
510 g_free(markup);
511 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
512 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
513 gtk_box_pack_start(GTK_BOX(group), label, FALSE, FALSE, 0);
514
515 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
516 gtk_box_pack_start(GTK_BOX(group), hbox, TRUE, TRUE, 0);
517 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" "),
518 FALSE, FALSE, 0);
519 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
520 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
521
522 grid = gtk_grid_new();
523 gtk_grid_set_row_spacing(GTK_GRID(grid), 6);
524 gtk_grid_set_column_spacing(GTK_GRID(grid), 6);
525
526 gtk_box_pack_start(GTK_BOX(vbox), grid, FALSE, TRUE, 0);
527
528 print_prefs->header_font =
529 add_font_button(_("_Header Font:"), balsa_app.print_header_font,
530 GTK_GRID(grid), 0);
531 print_prefs->body_font =
532 add_font_button(_("B_ody Font:"), balsa_app.print_body_font,
533 GTK_GRID(grid), 1);
534 print_prefs->footer_font =
535 add_font_button(_("_Footer Font:"), balsa_app.print_footer_font,
536 GTK_GRID(grid), 2);
537
538 group = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
539 gtk_box_pack_start(GTK_BOX(page), group, FALSE, TRUE, 0);
540
541 label = gtk_label_new(NULL);
542 markup = g_strdup_printf("<b>%s</b>", _("Highlighting"));
543 gtk_label_set_markup(GTK_LABEL(label), markup);
544 g_free(markup);
545 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
546 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
547 gtk_box_pack_start(GTK_BOX(group), label, FALSE, FALSE, 0);
548
549 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
550 gtk_box_pack_start(GTK_BOX(group), hbox, TRUE, TRUE, 0);
551 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" "),
552 FALSE, FALSE, 0);
553 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
554 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
555
556 print_prefs->highlight_cited =
557 gtk_check_button_new_with_mnemonic(_("Highlight _cited text"));
558 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
559 (print_prefs->highlight_cited),
560 balsa_app.print_highlight_cited);
561 gtk_box_pack_start(GTK_BOX(vbox), print_prefs->highlight_cited, FALSE,
562 TRUE, 0);
563
564 print_prefs->highlight_phrases =
565 gtk_check_button_new_with_mnemonic(_
566 ("Highlight _structured phrases"));
567 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
568 (print_prefs->highlight_phrases),
569 balsa_app.print_highlight_phrases);
570 gtk_box_pack_start(GTK_BOX(vbox), print_prefs->highlight_phrases,
571 FALSE, TRUE, 0);
572
573 group = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
574 gtk_box_pack_start(GTK_BOX(page), group, FALSE, TRUE, 0);
575
576 label = gtk_label_new(NULL);
577 markup = g_strdup_printf("<b>%s</b>", _("Margins"));
578 gtk_label_set_markup(GTK_LABEL(label), markup);
579 g_free(markup);
580 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
581 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
582 gtk_box_pack_start(GTK_BOX(group), label, FALSE, FALSE, 0);
583
584 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
585 gtk_box_pack_start(GTK_BOX(group), hbox, TRUE, TRUE, 0);
586 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" "),
587 FALSE, FALSE, 0);
588 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
589 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
590
591 grid = gtk_grid_new();
592 gtk_grid_set_row_spacing(GTK_GRID(grid), 6);
593 gtk_grid_set_column_spacing(GTK_GRID(grid), 6);
594
595 gtk_box_pack_start(GTK_BOX(vbox), grid, FALSE, TRUE, 0);
596
597 pg_setup = gtk_print_operation_get_default_page_setup(operation);
598 print_prefs->margin_top =
599 add_margin_spinbtn(_("_Top"),
600 gtk_page_setup_get_top_margin(pg_setup, GTK_UNIT_POINTS),
601 gtk_page_setup_get_page_height(pg_setup, GTK_UNIT_POINTS),
602 balsa_app.margin_top,
603 GTK_GRID(grid), 0);
604 print_prefs->margin_bottom =
605 add_margin_spinbtn(_("_Bottom"),
606 gtk_page_setup_get_bottom_margin(pg_setup, GTK_UNIT_POINTS),
607 gtk_page_setup_get_page_height(pg_setup, GTK_UNIT_POINTS),
608 balsa_app.margin_bottom,
609 GTK_GRID(grid), 1);
610 g_signal_connect(G_OBJECT(gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(print_prefs->margin_top))),
611 "value-changed", G_CALLBACK(check_margins),
612 gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(print_prefs->margin_bottom)));
613 g_signal_connect(G_OBJECT(gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(print_prefs->margin_bottom))),
614 "value-changed", G_CALLBACK(check_margins),
615 gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(print_prefs->margin_top)));
616 print_prefs->margin_left =
617 add_margin_spinbtn(_("_Left"),
618 gtk_page_setup_get_left_margin(pg_setup, GTK_UNIT_POINTS),
619 gtk_page_setup_get_page_width(pg_setup, GTK_UNIT_POINTS),
620 balsa_app.margin_left,
621 GTK_GRID(grid), 2);
622 print_prefs->margin_right =
623 add_margin_spinbtn(_("_Right"),
624 gtk_page_setup_get_right_margin(pg_setup, GTK_UNIT_POINTS),
625 gtk_page_setup_get_page_width(pg_setup, GTK_UNIT_POINTS),
626 balsa_app.margin_right,
627 GTK_GRID(grid), 3);
628 g_signal_connect(G_OBJECT(gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(print_prefs->margin_left))),
629 "value-changed", G_CALLBACK(check_margins),
630 gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(print_prefs->margin_right)));
631 g_signal_connect(G_OBJECT(gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(print_prefs->margin_right))),
632 "value-changed", G_CALLBACK(check_margins),
633 gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(print_prefs->margin_left)));
634
635 gtk_widget_show_all(page);
636
637 return page;
638 }
639
640
641 static void
message_prefs_apply(GtkPrintOperation * operation,GtkWidget * widget,BalsaPrintPrefs * print_prefs)642 message_prefs_apply(GtkPrintOperation * operation, GtkWidget * widget,
643 BalsaPrintPrefs * print_prefs)
644 {
645 g_free(balsa_app.print_header_font);
646 balsa_app.print_header_font =
647 g_strdup(gtk_font_button_get_font_name
648 (GTK_FONT_BUTTON(print_prefs->header_font)));
649 g_free(balsa_app.print_body_font);
650 balsa_app.print_body_font =
651 g_strdup(gtk_font_button_get_font_name
652 (GTK_FONT_BUTTON(print_prefs->body_font)));
653 g_free(balsa_app.print_footer_font);
654 balsa_app.print_footer_font =
655 g_strdup(gtk_font_button_get_font_name
656 (GTK_FONT_BUTTON(print_prefs->footer_font)));
657 balsa_app.print_highlight_cited =
658 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
659 (print_prefs->highlight_cited));
660 balsa_app.print_highlight_phrases =
661 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
662 (print_prefs->highlight_phrases));
663
664 balsa_app.margin_top =
665 gtk_spin_button_get_value(GTK_SPIN_BUTTON(print_prefs->margin_top)) * 72.0;
666 balsa_app.margin_bottom =
667 gtk_spin_button_get_value(GTK_SPIN_BUTTON(print_prefs->margin_bottom)) * 72.0;
668 balsa_app.margin_left =
669 gtk_spin_button_get_value(GTK_SPIN_BUTTON(print_prefs->margin_left)) * 72.0;
670 balsa_app.margin_right =
671 gtk_spin_button_get_value(GTK_SPIN_BUTTON(print_prefs->margin_right)) * 72.0;
672 if (get_default_user_units() != GTK_UNIT_INCH) {
673 /* adjust for mm */
674 balsa_app.margin_top /= 25.4;
675 balsa_app.margin_bottom /= 25.4;
676 balsa_app.margin_left /= 25.4;
677 balsa_app.margin_right /= 25.4;
678 }
679 }
680
681
682 void
message_print_page_setup(GtkWindow * parent)683 message_print_page_setup(GtkWindow * parent)
684 {
685 GtkPageSetup *new_page_setup;
686
687 if (!balsa_app.print_settings)
688 balsa_app.print_settings = gtk_print_settings_new();
689
690 new_page_setup =
691 gtk_print_run_page_setup_dialog(parent, balsa_app.page_setup,
692 balsa_app.print_settings);
693
694 if (balsa_app.page_setup)
695 g_object_unref(G_OBJECT(balsa_app.page_setup));
696
697 balsa_app.page_setup = new_page_setup;
698 }
699
700
701 void
message_print(LibBalsaMessage * msg,GtkWindow * parent)702 message_print(LibBalsaMessage * msg, GtkWindow * parent)
703 {
704 GtkPrintOperation *print;
705 GtkPrintOperationResult res;
706 BalsaPrintData *print_data;
707 BalsaPrintPrefs print_prefs;
708 GError *err = NULL;
709
710 print = gtk_print_operation_new();
711 g_assert(print != NULL);
712
713 g_object_ref(G_OBJECT(msg));
714
715 gtk_print_operation_set_n_pages(print, 1);
716 gtk_print_operation_set_unit(print, GTK_UNIT_POINTS);
717 gtk_print_operation_set_use_full_page(print, FALSE);
718
719 if (balsa_app.print_settings != NULL)
720 gtk_print_operation_set_print_settings(print,
721 balsa_app.print_settings);
722 if (balsa_app.page_setup != NULL)
723 gtk_print_operation_set_default_page_setup(print,
724 balsa_app.page_setup);
725
726 /* create a print context */
727 print_data = g_new0(BalsaPrintData, 1);
728 print_data->message = msg;
729
730 g_signal_connect(print, "begin_print", G_CALLBACK(begin_print), print_data);
731 g_signal_connect(print, "draw_page", G_CALLBACK(draw_page), print_data);
732 g_signal_connect(print, "create-custom-widget",
733 G_CALLBACK(message_prefs_widget), &print_prefs);
734 g_signal_connect(print, "custom-widget-apply",
735 G_CALLBACK(message_prefs_apply), &print_prefs);
736
737 res =
738 gtk_print_operation_run(print,
739 GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
740 parent, &err);
741
742 if (res == GTK_PRINT_OPERATION_RESULT_APPLY) {
743 if (balsa_app.print_settings != NULL)
744 g_object_unref(balsa_app.print_settings);
745 balsa_app.print_settings =
746 g_object_ref(gtk_print_operation_get_print_settings(print));
747 } else if (res == GTK_PRINT_OPERATION_RESULT_ERROR)
748 balsa_information(LIBBALSA_INFORMATION_ERROR,
749 _("Error printing message: %s"), err->message);
750
751 /* clean up */
752 if (err)
753 g_error_free(err);
754 g_list_foreach(print_data->print_parts, (GFunc) g_object_unref, NULL);
755 g_list_free(print_data->print_parts);
756 g_free(print_data->footer);
757 g_free(print_data);
758 g_object_unref(G_OBJECT(print));
759 g_object_unref(G_OBJECT(msg));
760 }
761