r5162 | eht16 | 2010-08-15 13:53:09 +0100 (Sun, 15 Aug 2010) | 1 line
[geany-mirror.git] / src / printing.c
blob00bce33fe657ba932ed46917c6acd8f1c32b6b1a
1 /*
2 * printing.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2007-2010 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2007-2010 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * $Id$
26 * GTK 2.10 printing support
27 * (basic code layout were adopted from Sylpheed's printing implementation, thanks)
30 #include <math.h>
31 #include <time.h>
32 #include <string.h>
34 #include "geany.h"
35 #include "printing.h"
36 #include "document.h"
37 #include "sciwrappers.h"
38 #include "editor.h"
39 #include "sciwrappers.h"
40 #include "utils.h"
41 #include "support.h"
42 #include "dialogs.h"
43 #include "utils.h"
44 #include "ui_utils.h"
45 #include "msgwindow.h"
48 PrintingPrefs printing_prefs;
51 #if GTK_CHECK_VERSION(2, 10, 0)
54 #define ROTATE_RGB(color) \
55 (((color) & 0xFF0000) >> 16) + ((color) & 0x00FF00) + (((color) & 0x0000FF) << 16)
56 #define ADD_ATTR(l, a) \
57 pango_attr_list_insert((l), (a)); \
58 (a)->start_index = 0; \
59 (a)->end_index = -1;
62 enum
64 FORE = 0,
65 BACK,
66 BOLD,
67 ITALIC,
68 MAX_TYPES
72 /* document-related variables */
73 typedef struct
75 GeanyDocument *doc;
76 gint font_width;
77 gint lines;
78 gint n_pages;
79 gint lines_per_page;
80 gint max_line_number_margin;
81 gint cur_line;
82 gint cur_pos;
83 gint styles[STYLE_MAX + 1][MAX_TYPES];
84 gdouble line_height;
85 /* whether we have a wrapped line on page end to take care of on next page */
86 gboolean long_line;
87 /* set in begin_print() to hold the time when printing was started to ensure all printed
88 * pages have the same date and time (in case of slow machines and many pages where rendering
89 * takes more than a second) */
90 time_t print_time;
91 PangoLayout *layout; /* commonly used layout object */
92 } DocInfo;
94 /* widget references for the custom widget in the print dialog */
95 typedef struct
97 GtkWidget *check_print_linenumbers;
98 GtkWidget *check_print_pagenumbers;
99 GtkWidget *check_print_pageheader;
100 GtkWidget *check_print_basename;
101 GtkWidget *entry_print_dateformat;
102 } PrintWidgets;
105 static GtkPrintSettings *settings = NULL;
106 static GtkPageSetup *page_setup = NULL;
110 /* returns the "width" (count of needed characters) for the given number */
111 static gint get_line_numbers_arity(gint x)
113 gint a = 0;
114 while ((x /= 10) != 0)
115 a++;
116 return a;
120 /* split a RGB colour into the three colour components */
121 static void get_rgb_values(gint c, gint *r, gint *g, gint *b)
123 c = ROTATE_RGB(c);
124 if (interface_prefs.highlighting_invert_all)
125 c = utils_invert_color(c);
127 *r = c % 256;
128 *g = (c & - 16711936) / 256;
129 *b = (c & 0xff0000) / 65536;
131 *r *= 257;
132 *g *= 257;
133 *b *= 257;
137 /* creates a commonly used layout object from the given context for use in get_page_count and
138 * draw_page */
139 static PangoLayout *setup_pango_layout(GtkPrintContext *context, PangoFontDescription *desc)
141 PangoLayout *layout;
143 layout = gtk_print_context_create_pango_layout(context);
144 pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
145 pango_layout_set_spacing(layout, 0);
146 pango_layout_set_attributes(layout, NULL);
147 pango_layout_set_font_description(layout, desc);
149 return layout;
153 static gboolean utils_font_desc_check_monospace(PangoContext *pc, PangoFontDescription *desc)
155 PangoFontFamily **families;
156 gint n_families, i;
157 const gchar *font;
158 gboolean ret = TRUE;
160 font = pango_font_description_get_family(desc);
161 pango_context_list_families(pc, &families, &n_families);
162 for (i = 0; i < n_families; i++)
164 if (utils_str_equal(font, pango_font_family_get_name(families[i])))
166 if (!pango_font_family_is_monospace(families[i]))
168 ret = FALSE;
172 g_free(families);
173 return ret;
177 /* We don't support variable width fonts (yet) */
178 static gint get_font_width(GtkPrintContext *context, PangoFontDescription *desc)
180 PangoContext *pc;
181 PangoFontMetrics *metrics;
182 gint width;
184 pc = gtk_print_context_create_pango_context(context);
186 if (!utils_font_desc_check_monospace(pc, desc))
187 dialogs_show_msgbox_with_secondary(GTK_MESSAGE_WARNING,
188 _("The editor font is not a monospaced font!"),
189 _("Text will be wrongly spaced."));
191 metrics = pango_context_get_metrics(pc, desc, pango_context_get_language(pc));
192 /** TODO is this the best result we can get? */
193 /* digit and char width are mostly equal for monospace fonts, char width might be
194 * for dual width characters(e.g. Japanese) so use digit width to get sure we get the width
195 * for one character */
196 width = pango_font_metrics_get_approximate_digit_width(metrics) / PANGO_SCALE;
198 pango_font_metrics_unref(metrics);
199 g_object_unref(pc);
201 return width;
205 static gint get_page_count(GtkPrintContext *context, DocInfo *dinfo)
207 gdouble width, height;
208 gint layout_h;
209 gint i, j, lines_left;
210 gchar *line_buf;
212 if (dinfo == NULL)
213 return -1;
215 width = gtk_print_context_get_width(context);
216 height = gtk_print_context_get_height(context);
218 if (printing_prefs.print_line_numbers)
219 /* remove line number margin space from overall width */
220 width -= dinfo->max_line_number_margin * dinfo->font_width;
222 pango_layout_set_width(dinfo->layout, width * PANGO_SCALE);
224 /* add test text to get line height */
225 pango_layout_set_text(dinfo->layout, "Test 1", -1);
226 pango_layout_get_size(dinfo->layout, NULL, &layout_h);
227 if (layout_h <= 0)
229 geany_debug("Invalid layout_h (%d). Falling back to default height (%d)",
230 layout_h, 100 * PANGO_SCALE);
231 layout_h = 100 * PANGO_SCALE;
233 dinfo->line_height = (gdouble)layout_h / PANGO_SCALE;
234 dinfo->lines_per_page = ceil((height - dinfo->line_height) / dinfo->line_height);
235 #ifdef GEANY_PRINT_DEBUG
236 geany_debug("max lines_per_page: %d", dinfo->lines_per_page);
237 #endif
238 if (printing_prefs.print_page_numbers)
239 dinfo->lines_per_page -= 2;
240 if (printing_prefs.print_page_header)
241 dinfo->lines_per_page -= 3;
243 lines_left = dinfo->lines_per_page;
245 i = 0;
246 for (j = 0; j < dinfo->lines; j++)
248 gint lines = 1;
249 gint line_width;
251 line_buf = sci_get_line(dinfo->doc->editor->sci, j);
252 line_width = (g_utf8_strlen(line_buf, -1) + 1) * dinfo->font_width;
253 if (line_width > width)
254 lines = ceil(line_width / width);
255 #ifdef GEANY_PRINT_DEBUG
256 if (lines != 1) geany_debug("%d %d", j+1, lines);
257 #endif
259 while (lines_left < lines)
261 lines -= lines_left;
262 lines_left = dinfo->lines_per_page;
263 i++;
265 lines_left -= lines;
266 g_free(line_buf);
269 return i + 1;
273 static void add_page_header(DocInfo *dinfo, cairo_t *cr, gint width, gint page_nr)
275 gint ph_height = dinfo->line_height * 3;
276 gchar *data;
277 gchar *datetime;
278 gchar *tmp_file_name = (dinfo->doc->file_name != NULL) ?
279 dinfo->doc->file_name : GEANY_STRING_UNTITLED;
280 gchar *file_name = (printing_prefs.page_header_basename) ?
281 g_path_get_basename(tmp_file_name) : g_strdup(tmp_file_name);
282 PangoLayout *layout = dinfo->layout;
284 /* draw the frame */
285 cairo_set_line_width(cr, 0.3);
286 cairo_set_source_rgb(cr, 0, 0, 0);
287 cairo_rectangle(cr, 2, 2, width - 4, ph_height - 4);
288 cairo_stroke(cr);
290 /* width - 8: 2px between doc border and frame border, 2px between frame border and text
291 * and this on left and right side, so (2 + 2) * 2 */
292 pango_layout_set_width(layout, (width - 8) * PANGO_SCALE);
294 if ((g_utf8_strlen(file_name, -1) * dinfo->font_width) >= ((width - 4) - (dinfo->font_width * 2)))
295 /* if the filename is wider than the available space on the line, skip parts of it */
296 pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_MIDDLE);
298 data = g_strdup_printf("<b>%s</b>", file_name);
299 pango_layout_set_markup(layout, data, -1);
300 pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
301 cairo_move_to(cr, 4, dinfo->line_height * 0.5);
302 pango_cairo_show_layout(cr, layout);
303 g_free(data);
304 g_free(file_name);
306 data = g_strdup_printf(_("<b>Page %d of %d</b>"), page_nr + 1, dinfo->n_pages);
307 pango_layout_set_markup(layout, data, -1);
308 pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
309 cairo_move_to(cr, 4, dinfo->line_height * 1.5);
310 pango_cairo_show_layout(cr, layout);
311 g_free(data);
313 datetime = utils_get_date_time(printing_prefs.page_header_datefmt, &(dinfo->print_time));
314 if (NZV(datetime))
316 data = g_strdup_printf("<b>%s</b>", datetime);
317 pango_layout_set_markup(layout, data, -1);
318 pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT);
319 cairo_move_to(cr, 2, dinfo->line_height * 1.5);
320 pango_cairo_show_layout(cr, layout);
321 g_free(data);
323 g_free(datetime);
325 /* reset layout and re-position cairo context */
326 pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
327 pango_layout_set_ellipsize(layout, FALSE);
328 pango_layout_set_justify(layout, FALSE);
329 pango_layout_set_width(layout, width * PANGO_SCALE);
330 cairo_move_to(cr, 0, dinfo->line_height * 3);
334 static void custom_widget_apply(GtkPrintOperation *operation, GtkWidget *widget, gpointer user_data)
336 PrintWidgets *w = user_data;
338 printing_prefs.print_line_numbers =
339 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->check_print_linenumbers));
341 printing_prefs.print_page_numbers =
342 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->check_print_pagenumbers));
344 printing_prefs.print_page_header =
345 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->check_print_pageheader));
347 printing_prefs.page_header_basename =
348 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->check_print_basename));
350 g_free(printing_prefs.page_header_datefmt);
351 printing_prefs.page_header_datefmt =
352 g_strdup(gtk_entry_get_text(GTK_ENTRY(w->entry_print_dateformat)));
356 static void on_page_header_toggled(GtkToggleButton *togglebutton, gpointer user_data)
358 gboolean sens = gtk_toggle_button_get_active(togglebutton);
359 PrintWidgets *w = user_data;
361 gtk_widget_set_sensitive(w->check_print_basename, sens);
362 gtk_widget_set_sensitive(w->entry_print_dateformat, sens);
366 static GtkWidget *create_custom_widget(GtkPrintOperation *operation, gpointer user_data)
367 { /* copied from interface.c */
368 GtkWidget *page;
369 GtkWidget *frame33;
370 GtkWidget *alignment36;
371 GtkWidget *vbox30;
372 GtkWidget *hbox10;
373 GtkWidget *label203;
374 PrintWidgets *w = user_data;
376 gtk_print_operation_set_custom_tab_label(operation, _("Document Setup"));
378 page = gtk_vbox_new(FALSE, 0);
379 gtk_container_set_border_width(GTK_CONTAINER(page), 5);
381 w->check_print_linenumbers = gtk_check_button_new_with_mnemonic(_("Print line numbers"));
382 gtk_box_pack_start(GTK_BOX(page), w->check_print_linenumbers, FALSE, FALSE, 0);
383 ui_widget_set_tooltip_text(w->check_print_linenumbers, _("Add line numbers to the printed page"));
384 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->check_print_linenumbers), printing_prefs.print_line_numbers);
386 w->check_print_pagenumbers = gtk_check_button_new_with_mnemonic(_("Print page numbers"));
387 gtk_box_pack_start(GTK_BOX(page), w->check_print_pagenumbers, FALSE, FALSE, 0);
388 ui_widget_set_tooltip_text(w->check_print_pagenumbers, _("Add page numbers at the bottom of each page. It takes 2 lines of the page."));
389 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->check_print_pagenumbers), printing_prefs.print_page_numbers);
391 w->check_print_pageheader = gtk_check_button_new_with_mnemonic(_("Print page header"));
392 gtk_box_pack_start(GTK_BOX(page), w->check_print_pageheader, FALSE, FALSE, 0);
393 ui_widget_set_tooltip_text(w->check_print_pageheader, _("Add a little header to every page containing the page number, the filename and the current date (see below). It takes 3 lines of the page."));
394 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->check_print_pageheader), printing_prefs.print_page_header);
395 g_signal_connect(w->check_print_pageheader, "toggled", G_CALLBACK(on_page_header_toggled), w);
397 frame33 = gtk_frame_new(NULL);
398 gtk_box_pack_start(GTK_BOX(page), frame33, FALSE, FALSE, 0);
399 gtk_frame_set_label_align(GTK_FRAME(frame33), 0, 0);
400 gtk_frame_set_shadow_type(GTK_FRAME(frame33), GTK_SHADOW_NONE);
402 alignment36 = gtk_alignment_new(0, 0.5, 1, 1);
403 gtk_container_add(GTK_CONTAINER(frame33), alignment36);
404 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment36), 0, 0, 12, 0);
406 vbox30 = gtk_vbox_new(FALSE, 1);
407 gtk_container_add(GTK_CONTAINER(alignment36), vbox30);
409 w->check_print_basename = gtk_check_button_new_with_mnemonic(_("Use the basename of the printed file"));
410 gtk_box_pack_start(GTK_BOX(vbox30), w->check_print_basename, FALSE, FALSE, 0);
411 ui_widget_set_tooltip_text(w->check_print_basename, _("Print only the basename(without the path) of the printed file"));
412 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->check_print_basename), printing_prefs.page_header_basename);
414 hbox10 = gtk_hbox_new(FALSE, 5);
415 gtk_box_pack_start(GTK_BOX(vbox30), hbox10, TRUE, TRUE, 0);
417 label203 = gtk_label_new(_("Date format:"));
418 gtk_box_pack_start(GTK_BOX(hbox10), label203, FALSE, FALSE, 0);
420 w->entry_print_dateformat = gtk_entry_new();
421 ui_entry_add_clear_icon(GTK_ENTRY(w->entry_print_dateformat));
422 gtk_box_pack_start(GTK_BOX(hbox10), w->entry_print_dateformat, TRUE, TRUE, 0);
423 ui_widget_set_tooltip_text(w->entry_print_dateformat, _("Specify a format for the date and time stamp which is added to the page header on each page. You can use any conversion specifiers which can be used with the ANSI C strftime function."));
424 gtk_entry_set_text(GTK_ENTRY(w->entry_print_dateformat), printing_prefs.page_header_datefmt);
426 on_page_header_toggled(GTK_TOGGLE_BUTTON(w->check_print_pageheader), w);
427 gtk_widget_show_all(page);
428 return page;
432 static void end_print(GtkPrintOperation *operation, GtkPrintContext *context, gpointer user_data)
434 DocInfo *dinfo = user_data;
436 if (dinfo == NULL)
437 return;
439 gtk_widget_hide(main_widgets.progressbar);
440 g_object_unref(dinfo->layout);
444 static void begin_print(GtkPrintOperation *operation, GtkPrintContext *context, gpointer user_data)
446 DocInfo *dinfo = user_data;
447 PangoFontDescription *desc;
448 gint i;
449 gint style_max;
451 if (dinfo == NULL)
452 return;
454 gtk_widget_show(main_widgets.progressbar);
456 desc = pango_font_description_from_string(interface_prefs.editor_font);
458 /* init dinfo fields */
459 dinfo->lines = sci_get_line_count(dinfo->doc->editor->sci);
460 dinfo->lines_per_page = 0;
461 dinfo->cur_line = 0;
462 dinfo->cur_pos = 0;
463 dinfo->long_line = FALSE;
464 dinfo->print_time = time(NULL);
465 dinfo->max_line_number_margin = get_line_numbers_arity(dinfo->lines) + 1;
466 /* increase font width by 1 (looks better) */
467 dinfo->font_width = get_font_width(context, desc) + 1;
468 /* create a PangoLayout to be commonly used in get_page_count and draw_page */
469 dinfo->layout = setup_pango_layout(context, desc);
470 /* this is necessary because of possible line breaks on the printed page and then
471 * lines_per_page differs from document line count */
472 dinfo->n_pages = get_page_count(context, dinfo);
474 /* read all styles from Scintilla */
475 style_max = pow(2, scintilla_send_message(dinfo->doc->editor->sci, SCI_GETSTYLEBITS, 0, 0));
476 /* if the lexer uses only the first 32 styles(style bits = 5),
477 * we need to add the pre-defined styles */
478 if (style_max == 32)
479 style_max = STYLE_LASTPREDEFINED;
480 for (i = 0; i < style_max; i++)
482 dinfo->styles[i][FORE] = ROTATE_RGB(scintilla_send_message(
483 dinfo->doc->editor->sci, SCI_STYLEGETFORE, i, 0));
484 if (i == STYLE_LINENUMBER)
485 { /* ignore background colour for line number margin to avoid trouble with wrapped lines */
486 dinfo->styles[STYLE_LINENUMBER][BACK] = ROTATE_RGB(scintilla_send_message(
487 dinfo->doc->editor->sci, SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
489 else
491 dinfo->styles[i][BACK] = ROTATE_RGB(scintilla_send_message(
492 dinfo->doc->editor->sci, SCI_STYLEGETBACK, i, 0));
494 /* use white background color unless foreground is white to save ink */
495 if (dinfo->styles[i][FORE] != 0xffffff)
496 dinfo->styles[i][BACK] = 0xffffff;
497 dinfo->styles[i][BOLD] =
498 scintilla_send_message(dinfo->doc->editor->sci, SCI_STYLEGETBOLD, i, 0);
499 dinfo->styles[i][ITALIC] =
500 scintilla_send_message(dinfo->doc->editor->sci, SCI_STYLEGETITALIC, i, 0);
503 if (dinfo->n_pages >= 0)
504 gtk_print_operation_set_n_pages(operation, dinfo->n_pages);
506 pango_font_description_free(desc);
510 static void draw_page(GtkPrintOperation *operation, GtkPrintContext *context,
511 gint page_nr, gpointer user_data)
513 DocInfo *dinfo = user_data;
514 GeanyEditor *editor;
515 cairo_t *cr;
516 gdouble width, height;
517 gdouble x, y;
518 /*gint layout_h;*/
519 gint count;
520 GString *str;
522 if (dinfo == NULL || page_nr >= dinfo->n_pages)
523 return;
525 editor = dinfo->doc->editor;
527 if (dinfo->n_pages > 0)
529 gdouble fraction = (page_nr + 1) / (gdouble) dinfo->n_pages;
530 gchar *text = g_strdup_printf(_("Page %d of %d"), page_nr, dinfo->n_pages);
531 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(main_widgets.progressbar), fraction);
532 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(main_widgets.progressbar), text);
533 g_free(text);
536 #ifdef GEANY_PRINT_DEBUG
537 geany_debug("draw_page = %d, pages = %d, (real) lines_per_page = %d",
538 page_nr, dinfo->n_pages, dinfo->lines_per_page);
539 #endif
541 str = g_string_sized_new(256);
542 cr = gtk_print_context_get_cairo_context(context);
543 width = gtk_print_context_get_width(context);
544 height = gtk_print_context_get_height(context);
546 cairo_set_source_rgb(cr, 0, 0, 0);
547 #ifdef GEANY_PRINT_DEBUG
548 cairo_set_line_width(cr, 0.2);
549 cairo_rectangle(cr, 0, 0, width, height);
550 cairo_stroke(cr);
551 #endif
552 cairo_move_to(cr, 0, 0);
554 pango_layout_set_width(dinfo->layout, width * PANGO_SCALE);
555 pango_layout_set_alignment(dinfo->layout, PANGO_ALIGN_LEFT);
556 pango_layout_set_ellipsize(dinfo->layout, FALSE);
557 pango_layout_set_justify(dinfo->layout, FALSE);
559 if (printing_prefs.print_page_header)
560 add_page_header(dinfo, cr, width, page_nr);
562 count = 0; /* the actual line counter for the current page, might be different from
563 * dinfo->cur_line due to possible line breaks */
564 while (count < dinfo->lines_per_page)
566 gchar c = 'a';
567 gint style = -1;
568 PangoAttrList *layout_attr;
569 PangoAttribute *attr;
570 gint colours[3] = { 0 };
571 gboolean add_linenumber = TRUE;
572 gboolean at_eol;
574 while (count < dinfo->lines_per_page && c != '\0')
576 at_eol = FALSE;
578 g_string_erase(str, 0, str->len); /* clear the string */
580 /* line numbers */
581 if (printing_prefs.print_line_numbers && add_linenumber)
583 /* if we had a wrapped line on the last page which needs to be continued, don't
584 * add a line number */
585 if (dinfo->long_line)
587 add_linenumber = FALSE;
589 else
591 gchar *line_number = NULL;
592 gint cur_line_number_margin = get_line_numbers_arity(dinfo->cur_line + 1);
593 gchar *fill = g_strnfill(
594 dinfo->max_line_number_margin - cur_line_number_margin - 1, ' ');
596 line_number = g_strdup_printf("%s%d ", fill, dinfo->cur_line + 1);
597 g_string_append(str, line_number);
598 dinfo->cur_line++; /* increase document line */
599 add_linenumber = FALSE;
600 style = STYLE_LINENUMBER;
601 c = 'a'; /* dummy value */
602 g_free(fill);
603 g_free(line_number);
606 /* data */
607 else
609 style = sci_get_style_at(dinfo->doc->editor->sci, dinfo->cur_pos);
610 c = sci_get_char_at(dinfo->doc->editor->sci, dinfo->cur_pos);
611 if (c == '\0' || style == -1)
612 { /* if c gets 0, we are probably out of document boundaries,
613 * so stop to break out of outer loop */
614 count = dinfo->lines_per_page;
615 break;
617 dinfo->cur_pos++;
619 /* convert tabs to spaces which seems to be better than using Pango tabs */
620 if (c == '\t')
622 gint tab_width = sci_get_tab_width(editor->sci);
623 gchar *s = g_strnfill(tab_width, ' ');
624 g_string_append(str, s);
625 g_free(s);
627 /* don't add line breaks, they are handled manually below */
628 else if (c == '\r' || c == '\n')
630 gchar c_next = sci_get_char_at(dinfo->doc->editor->sci, dinfo->cur_pos);
631 at_eol = TRUE;
632 if (c == '\r' && c_next == '\n')
633 dinfo->cur_pos++; /* skip LF part of CR/LF */
635 else
637 g_string_append_c(str, c); /* finally add the character */
639 /* handle UTF-8: since we add char by char (better: byte by byte), we need to
640 * keep UTF-8 characters together(e.g. two bytes for one character)
641 * the input is always UTF-8 and c is signed, so all non-Ascii
642 * characters are less than 0 and consist of all bytes less than 0.
643 * style doesn't change since it is only one character with multiple bytes. */
644 while (c < 0)
646 c = sci_get_char_at(dinfo->doc->editor->sci, dinfo->cur_pos);
647 if (c < 0)
648 { /* only add the byte when it is part of the UTF-8 character
649 * otherwise we could add e.g. a '\n' and it won't be visible in the
650 * printed document */
651 g_string_append_c(str, c);
652 dinfo->cur_pos++;
658 if (! at_eol)
660 /* set text */
661 pango_layout_set_text(dinfo->layout, str->str, -1);
662 /* attributes */
663 layout_attr = pango_attr_list_new();
664 /* foreground colour */
665 get_rgb_values(dinfo->styles[style][FORE], &colours[0], &colours[1], &colours[2]);
666 attr = pango_attr_foreground_new(colours[0], colours[1], colours[2]);
667 ADD_ATTR(layout_attr, attr);
668 /* background colour */
669 get_rgb_values(dinfo->styles[style][BACK], &colours[0], &colours[1], &colours[2]);
670 attr = pango_attr_background_new(colours[0], colours[1], colours[2]);
671 ADD_ATTR(layout_attr, attr);
672 /* bold text */
673 if (dinfo->styles[style][BOLD])
675 attr = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
676 ADD_ATTR(layout_attr, attr);
678 /* italic text */
679 if (dinfo->styles[style][ITALIC])
681 attr = pango_attr_style_new(PANGO_STYLE_ITALIC);
682 ADD_ATTR(layout_attr, attr);
684 pango_layout_set_attributes(dinfo->layout, layout_attr);
685 pango_layout_context_changed(dinfo->layout);
686 pango_attr_list_unref(layout_attr);
689 cairo_get_current_point(cr, &x, &y);
692 /* normal line break at eol character in document */
693 if (at_eol)
695 /*pango_layout_get_size(dinfo->layout, NULL, &layout_h);*/
696 /*cairo_move_to(cr, 0, y + (gdouble)layout_h / PANGO_SCALE);*/
697 cairo_move_to(cr, 0, y + dinfo->line_height);
699 count++;
700 /* we added a new document line so request a new line number */
701 add_linenumber = TRUE;
703 else
705 gint x_offset = 0;
706 /* maybe we need to force a line break because of too long line */
707 if (x >= (width - dinfo->font_width))
709 /* don't start the line at horizontal origin because we need to skip the
710 * line number margin */
711 if (printing_prefs.print_line_numbers)
713 x_offset = (dinfo->max_line_number_margin + 1) * dinfo->font_width;
716 /*pango_layout_get_size(dinfo->layout, NULL, &layout_h);*/
717 /*cairo_move_to(cr, x_offset, y + (gdouble)layout_h / PANGO_SCALE);*/
718 /* this is faster but not exactly the same as above */
719 cairo_move_to(cr, x_offset, y + dinfo->line_height);
720 cairo_get_current_point(cr, &x, &y);
721 count++;
723 if (count < dinfo->lines_per_page)
725 /* str->len is counted in bytes not characters, so use g_utf8_strlen() */
726 x_offset = (g_utf8_strlen(str->str, -1) * dinfo->font_width);
728 if (dinfo->long_line && count == 0)
730 x_offset = (dinfo->max_line_number_margin + 1) * dinfo->font_width;
731 dinfo->long_line = FALSE;
734 pango_cairo_show_layout(cr, dinfo->layout);
735 cairo_move_to(cr, x + x_offset, y);
737 else
738 /* we are on a wrapped line but we are out of lines on this page, so continue
739 * the current line on the next page and remember to continue in current line */
740 dinfo->long_line = TRUE;
745 if (printing_prefs.print_line_numbers)
746 { /* print a thin line between the line number margin and the data */
747 gint y_start = 0;
749 if (printing_prefs.print_page_header)
750 y_start = (dinfo->line_height * 3) - 2; /* "- 2": to connect the line number line to
751 * the page header frame */
753 cairo_set_line_width(cr, 0.3);
754 cairo_move_to(cr, (dinfo->max_line_number_margin * dinfo->font_width) + 1, y_start);
755 cairo_line_to(cr, (dinfo->max_line_number_margin * dinfo->font_width) + 1,
756 y + dinfo->line_height); /* y is last added line, we reuse it */
757 cairo_stroke(cr);
760 if (printing_prefs.print_page_numbers)
762 gchar *line = g_strdup_printf("<small>- %d -</small>", page_nr + 1);
763 pango_layout_set_markup(dinfo->layout, line, -1);
764 pango_layout_set_alignment(dinfo->layout, PANGO_ALIGN_CENTER);
765 cairo_move_to(cr, 0, height - dinfo->line_height);
766 pango_cairo_show_layout(cr, dinfo->layout);
767 g_free(line);
769 #ifdef GEANY_PRINT_DEBUG
770 cairo_set_line_width(cr, 0.3);
771 cairo_move_to(cr, 0, height - (1.25 * dinfo->line_height));
772 cairo_line_to(cr, width - 1, height - (1.25 * dinfo->line_height));
773 cairo_stroke(cr);
774 #endif
776 g_string_free(str, TRUE);
780 static void status_changed(GtkPrintOperation *op, gpointer data)
782 gchar *filename = (data != NULL) ? data : GEANY_STRING_UNTITLED;
783 if (gtk_print_operation_get_status(op) == GTK_PRINT_STATUS_FINISHED_ABORTED)
784 msgwin_status_add(_("Did not send document %s to the printing subsystem."), filename);
785 else if (gtk_print_operation_get_status(op) == GTK_PRINT_STATUS_FINISHED)
786 msgwin_status_add(_("Document %s was sent to the printing subsystem."), filename);
790 static void printing_print_gtk(GeanyDocument *doc)
792 GtkPrintOperation *op;
793 GtkPrintOperationResult res = GTK_PRINT_OPERATION_RESULT_ERROR;
794 GError *error = NULL;
795 DocInfo *dinfo;
796 PrintWidgets *widgets;
798 /** TODO check for monospace font, detect the widest character in the font and
799 * use it at font_width */
801 widgets = g_new0(PrintWidgets, 1);
802 dinfo = g_new0(DocInfo, 1);
803 /* all other fields are initialised in begin_print() */
804 dinfo->doc = doc;
806 op = gtk_print_operation_new();
808 gtk_print_operation_set_unit(op, GTK_UNIT_POINTS);
809 gtk_print_operation_set_show_progress(op, TRUE);
810 #if GTK_CHECK_VERSION(2, 18, 0)
811 gtk_print_operation_set_embed_page_setup(op, TRUE);
812 #endif
814 g_signal_connect(op, "begin-print", G_CALLBACK(begin_print), dinfo);
815 g_signal_connect(op, "end-print", G_CALLBACK(end_print), dinfo);
816 g_signal_connect(op, "draw-page", G_CALLBACK(draw_page), dinfo);
817 g_signal_connect(op, "status-changed", G_CALLBACK(status_changed), doc->file_name);
818 g_signal_connect(op, "create-custom-widget", G_CALLBACK(create_custom_widget), widgets);
819 g_signal_connect(op, "custom-widget-apply", G_CALLBACK(custom_widget_apply), widgets);
821 if (settings != NULL)
822 gtk_print_operation_set_print_settings(op, settings);
823 if (page_setup != NULL)
824 gtk_print_operation_set_default_page_setup(op, page_setup);
826 res = gtk_print_operation_run(
827 op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, GTK_WINDOW(main_widgets.window), &error);
829 if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
831 if (settings != NULL)
832 g_object_unref(settings);
833 settings = g_object_ref(gtk_print_operation_get_print_settings(op));
834 /* status message is printed in the status-changed handler */
836 else if (res == GTK_PRINT_OPERATION_RESULT_ERROR)
838 dialogs_show_msgbox(GTK_MESSAGE_ERROR, _("Printing of %s failed (%s)."),
839 doc->file_name, error->message);
840 g_error_free(error);
843 g_object_unref(op);
844 g_free(dinfo);
845 g_free(widgets);
849 void printing_page_setup_gtk(void)
851 GtkPageSetup *new_page_setup;
853 if (settings == NULL)
854 settings = gtk_print_settings_new();
856 new_page_setup = gtk_print_run_page_setup_dialog(
857 GTK_WINDOW(main_widgets.window), page_setup, settings);
859 if (page_setup != NULL)
860 g_object_unref(page_setup);
862 page_setup = new_page_setup;
864 #endif /* GTK 2.10 */
867 /* simple file print using an external tool */
868 static void print_external(GeanyDocument *doc)
870 gchar *cmdline;
872 if (doc->file_name == NULL)
873 return;
875 if (! NZV(printing_prefs.external_print_cmd))
877 dialogs_show_msgbox(GTK_MESSAGE_ERROR,
878 _("Please set a print command in the preferences dialog first."));
879 return;
882 cmdline = g_strdup(printing_prefs.external_print_cmd);
883 utils_str_replace_all(&cmdline, "%f", doc->file_name);
885 if (dialogs_show_question(
886 _("The file \"%s\" will be printed with the following command:\n\n%s"),
887 doc->file_name, cmdline))
889 GError *error = NULL;
891 #ifdef G_OS_WIN32
892 gchar *tmp_cmdline = g_strdup(cmdline);
893 #else
894 /* /bin/sh -c emulates the system() call and makes complex commands possible
895 * but only needed on non-win32 systems due to the lack of win32's shell capabilities */
896 gchar *tmp_cmdline = g_strconcat("/bin/sh -c \"", cmdline, "\"", NULL);
897 #endif
899 if (! g_spawn_command_line_async(tmp_cmdline, &error))
901 dialogs_show_msgbox(GTK_MESSAGE_ERROR,
902 _("Printing of \"%s\" failed (return code: %s)."),
903 doc->file_name, error->message);
904 g_error_free(error);
906 else
908 msgwin_status_add(_("File %s printed."), doc->file_name);
910 g_free(tmp_cmdline);
912 g_free(cmdline);
916 void printing_print_doc(GeanyDocument *doc)
918 if (doc == NULL)
919 return;
921 #if GTK_CHECK_VERSION(2, 10, 0)
922 if (gtk_check_version(2, 10, 0) == NULL && printing_prefs.use_gtk_printing)
923 printing_print_gtk(doc);
924 else
925 #endif
926 print_external(doc);