2007-11-17 14:04:27 +00:00
/*
* printing . c - this file is part of Geany , a fast and lightweight IDE
*
* Copyright 2007 Enrico Tröger < enrico . troeger @ uvena . de >
* Copyright 2007 Nick Treleaven < nick . treleaven @ btinternet . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
* $ Id $
*/
/*
* GTK 2.10 printing support
* ( basic code layout were adopted from Sylpheed ' s printing implementation , thanks )
*/
# include <math.h>
# include <time.h>
# include <string.h>
# include "geany.h"
# include "printing.h"
# include "prefs.h"
# include "document.h"
# include "sciwrappers.h"
# include "editor.h"
# include "sciwrappers.h"
# include "utils.h"
# include "support.h"
# include "dialogs.h"
# include "utils.h"
# include "msgwindow.h"
PrintingPrefs printing_prefs ;
# if GTK_CHECK_VERSION(2, 10, 0)
# define ROTATE_RGB(color) \
( ( ( color ) & 0xFF0000 ) > > 16 ) + ( ( color ) & 0x00FF00 ) + ( ( ( color ) & 0x0000FF ) < < 16 )
# define ADD_ATTR(l, a) \
pango_attr_list_insert ( ( l ) , ( a ) ) ; \
( a ) - > start_index = 0 ; \
( a ) - > end_index = - 1 ;
enum
{
FORE = 0 ,
BACK ,
BOLD ,
ITALIC ,
MAX_TYPES
} ;
// document-related variables
typedef struct
{
gint idx ;
gint font_width ;
gint lines ;
gint n_pages ;
gint lines_per_page ;
gint max_line_number_margin ;
gint cur_line ;
gint cur_pos ;
gint styles [ STYLE_MAX + 1 ] [ MAX_TYPES ] ;
gdouble line_height ;
gboolean long_line ; // whether we have a wrapped line on page end to take care of on next page
// set in begin_print() to hold the time when printing was started to ensure all printed
// pages have the same date and time (in case of slow machines and many pages where rendering
// takes more than a second)
time_t print_time ;
PangoLayout * layout ; // commonly used layout object
} DocInfo ;
// widget references for the custom widget in the print dialog
typedef struct
{
GtkWidget * check_print_linenumbers ;
GtkWidget * check_print_pagenumbers ;
GtkWidget * check_print_pageheader ;
GtkWidget * check_print_basename ;
GtkWidget * entry_print_dateformat ;
} PrintWidgets ;
static GtkPrintSettings * settings = NULL ;
static GtkPageSetup * page_setup = NULL ;
// returns the "width" (count of needed characters) for the given number
static gint get_line_numbers_arity ( gint x )
{
gint a = 0 ;
while ( ( x / = 10 ) ! = 0 )
a + + ;
return a ;
}
// split a RGB colour into the three colour components
static void get_rgb_values ( gint c , gint * r , gint * g , gint * b )
{
c = ROTATE_RGB ( c ) ;
* r = c % 256 ;
* g = ( c & - 16711936 ) / 256 ;
* b = ( c & 0xff0000 ) / 65536 ;
* r * = 257 ;
* g * = 257 ;
* b * = 257 ;
}
// creates a commonly used layout object from the given context for use in get_page_count and
// draw_page
static PangoLayout * setup_pango_layout ( GtkPrintContext * context , PangoFontDescription * desc )
{
PangoLayout * layout ;
layout = gtk_print_context_create_pango_layout ( context ) ;
pango_layout_set_wrap ( layout , PANGO_WRAP_WORD_CHAR ) ;
pango_layout_set_spacing ( layout , 0 ) ;
pango_layout_set_attributes ( layout , NULL ) ;
pango_layout_set_font_description ( layout , desc ) ;
return layout ;
}
static gint get_font_width ( GtkPrintContext * context , PangoFontDescription * desc )
{
PangoContext * pc ;
PangoFontMetrics * metrics ;
gint width ;
pc = gtk_print_context_create_pango_context ( context ) ;
metrics = pango_context_get_metrics ( pc , desc , pango_context_get_language ( pc ) ) ;
/// TODO is this the best result we can get?
// digit and char width are mostly equal for monospace fonts, char width might be
// for dual width characters(e.g. Japanese) so use digit width to get sure we get the width
// for one character
width = pango_font_metrics_get_approximate_digit_width ( metrics ) / PANGO_SCALE ;
pango_font_metrics_unref ( metrics ) ;
return width ;
}
static gint get_page_count ( GtkPrintContext * context , DocInfo * dinfo )
{
gdouble width , height ;
gint layout_h ;
gint i , j , lines_left ;
gchar * line_buf ;
if ( dinfo = = NULL )
return - 1 ;
width = gtk_print_context_get_width ( context ) ;
height = gtk_print_context_get_height ( context ) ;
if ( printing_prefs . print_line_numbers )
// remove line number margin space from overall width
width - = dinfo - > max_line_number_margin * dinfo - > font_width ;
pango_layout_set_width ( dinfo - > layout , width * PANGO_SCALE ) ;
// add test text to get line height
pango_layout_set_text ( dinfo - > layout , " Test 1 " , - 1 ) ;
pango_layout_get_size ( dinfo - > layout , NULL , & layout_h ) ;
if ( layout_h < = 0 )
{
geany_debug ( " Invalid layout_h (%d). Falling back to default height (%d) " ,
layout_h , 100 * PANGO_SCALE ) ;
layout_h = 100 * PANGO_SCALE ;
}
dinfo - > line_height = ( gdouble ) layout_h / PANGO_SCALE ;
dinfo - > lines_per_page = ceil ( ( height - dinfo - > line_height ) / dinfo - > line_height ) ;
# ifdef GEANY_PRINT_DEBUG
geany_debug ( " max lines_per_page: %d " , dinfo - > lines_per_page ) ;
# endif
if ( printing_prefs . print_page_numbers )
dinfo - > lines_per_page - = 2 ;
if ( printing_prefs . print_page_header )
dinfo - > lines_per_page - = 3 ;
lines_left = dinfo - > lines_per_page ;
i = 0 ;
for ( j = 0 ; j < dinfo - > lines ; j + + )
{
gint lines = 1 ;
gint line_width ;
line_buf = sci_get_line ( doc_list [ dinfo - > idx ] . sci , j ) ;
line_width = ( g_utf8_strlen ( line_buf , - 1 ) + 1 ) * dinfo - > font_width ;
if ( line_width > width )
lines = ceil ( line_width / width ) ;
# ifdef GEANY_PRINT_DEBUG
if ( lines ! = 1 ) geany_debug ( " %d %d " , j + 1 , lines ) ;
# endif
while ( lines_left < lines )
{
lines - = lines_left ;
lines_left = dinfo - > lines_per_page ;
i + + ;
}
lines_left - = lines ;
g_free ( line_buf ) ;
}
return i + 1 ;
}
static void add_page_header ( PangoLayout * layout , cairo_t * cr , DocInfo * dinfo , gint width , gint page_nr )
{
gint ph_height = dinfo - > line_height * 3 ;
gchar * data ;
gchar * datetime ;
2007-11-17 17:03:25 +00:00
gchar * tmp_file_name = ( doc_list [ dinfo - > idx ] . file_name ! = NULL ) ?
doc_list [ dinfo - > idx ] . file_name : GEANY_STRING_UNTITLED ;
2007-11-17 14:04:27 +00:00
gchar * file_name = ( printing_prefs . page_header_basename ) ?
2007-11-17 17:03:25 +00:00
g_path_get_basename ( tmp_file_name ) : g_strdup ( tmp_file_name ) ;
2007-11-17 14:04:27 +00:00
// draw the frame
2007-11-17 17:03:25 +00:00
cairo_set_line_width ( cr , 0.3 ) ;
2007-11-17 14:04:27 +00:00
cairo_set_source_rgb ( cr , 0 , 0 , 0 ) ;
cairo_rectangle ( cr , 2 , 2 , width - 4 , ph_height - 4 ) ;
cairo_stroke ( cr ) ;
// width - 8: 2px between doc border and frame border, 2px between frame border and text
// and this on left and right side, so (2 + 2) * 2
pango_layout_set_width ( layout , ( width - 8 ) * PANGO_SCALE ) ;
if ( ( g_utf8_strlen ( file_name , - 1 ) * dinfo - > font_width ) > = ( ( width - 4 ) - ( dinfo - > font_width * 2 ) ) )
// if the filename is wider than the available space on the line, skip parts of it
pango_layout_set_ellipsize ( layout , PANGO_ELLIPSIZE_MIDDLE ) ;
data = g_strdup_printf ( " <b>%s</b> " , file_name ) ;
pango_layout_set_markup ( layout , data , - 1 ) ;
pango_layout_set_alignment ( layout , PANGO_ALIGN_LEFT ) ;
cairo_move_to ( cr , 4 , dinfo - > line_height * 0.5 ) ;
pango_cairo_show_layout ( cr , layout ) ;
g_free ( data ) ;
g_free ( file_name ) ;
data = g_strdup_printf ( " <b>page %d of %d</b> " , page_nr + 1 , dinfo - > n_pages ) ;
pango_layout_set_markup ( layout , data , - 1 ) ;
pango_layout_set_alignment ( layout , PANGO_ALIGN_LEFT ) ;
cairo_move_to ( cr , 4 , dinfo - > line_height * 1.5 ) ;
pango_cairo_show_layout ( cr , layout ) ;
g_free ( data ) ;
datetime = utils_get_date_time ( printing_prefs . page_header_datefmt , & ( dinfo - > print_time ) ) ;
data = g_strdup_printf ( " <b>%s</b> " , datetime ) ;
pango_layout_set_markup ( layout , data , - 1 ) ;
pango_layout_set_alignment ( layout , PANGO_ALIGN_RIGHT ) ;
cairo_move_to ( cr , 2 , dinfo - > line_height * 1.5 ) ;
pango_cairo_show_layout ( cr , layout ) ;
g_free ( data ) ;
g_free ( datetime ) ;
// reset layout and re-position cairo context
pango_layout_set_alignment ( layout , PANGO_ALIGN_LEFT ) ;
pango_layout_set_ellipsize ( layout , FALSE ) ;
pango_layout_set_justify ( layout , FALSE ) ;
pango_layout_set_width ( layout , width * PANGO_SCALE ) ;
cairo_move_to ( cr , 0 , dinfo - > line_height * 3 ) ;
}
static void custom_widget_apply ( GtkPrintOperation * operation , GtkWidget * widget , gpointer user_data )
{
PrintWidgets * w = user_data ;
printing_prefs . print_line_numbers =
gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( w - > check_print_linenumbers ) ) ;
printing_prefs . print_page_numbers =
gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( w - > check_print_pagenumbers ) ) ;
printing_prefs . print_page_header =
gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( w - > check_print_pageheader ) ) ;
printing_prefs . page_header_basename =
gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( w - > check_print_basename ) ) ;
g_free ( printing_prefs . page_header_datefmt ) ;
printing_prefs . page_header_datefmt =
g_strdup ( gtk_entry_get_text ( GTK_ENTRY ( w - > entry_print_dateformat ) ) ) ;
}
static void on_page_header_toggled ( GtkToggleButton * togglebutton , gpointer user_data )
{
gboolean sens = gtk_toggle_button_get_active ( togglebutton ) ;
PrintWidgets * w = user_data ;
gtk_widget_set_sensitive ( w - > check_print_basename , sens ) ;
gtk_widget_set_sensitive ( w - > entry_print_dateformat , sens ) ;
}
static GtkWidget * create_custom_widget ( GtkPrintOperation * operation , gpointer user_data )
{ // copied from interface.c
GtkWidget * page ;
GtkWidget * frame33 ;
GtkWidget * alignment36 ;
GtkWidget * vbox30 ;
GtkWidget * hbox10 ;
GtkWidget * label203 ;
GtkTooltips * tooltips = gtk_tooltips_new ( ) ;
PrintWidgets * w = user_data ;
gtk_print_operation_set_custom_tab_label ( operation , _ ( " Document Setup " ) ) ;
page = gtk_vbox_new ( FALSE , 0 ) ;
gtk_container_set_border_width ( GTK_CONTAINER ( page ) , 5 ) ;
w - > check_print_linenumbers = gtk_check_button_new_with_mnemonic ( _ ( " Print line numbers " ) ) ;
gtk_box_pack_start ( GTK_BOX ( page ) , w - > check_print_linenumbers , FALSE , FALSE , 0 ) ;
gtk_tooltips_set_tip ( tooltips , w - > check_print_linenumbers , _ ( " Add line numbers to the printed page. " ) , NULL ) ;
gtk_button_set_focus_on_click ( GTK_BUTTON ( w - > check_print_linenumbers ) , FALSE ) ;
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( w - > check_print_linenumbers ) , printing_prefs . print_line_numbers ) ;
w - > check_print_pagenumbers = gtk_check_button_new_with_mnemonic ( _ ( " Print page numbers " ) ) ;
gtk_box_pack_start ( GTK_BOX ( page ) , w - > check_print_pagenumbers , FALSE , FALSE , 0 ) ;
gtk_tooltips_set_tip ( tooltips , w - > check_print_pagenumbers , _ ( " Add page numbers at the bottom of each page. It takes 2 lines of the page. " ) , NULL ) ;
gtk_button_set_focus_on_click ( GTK_BUTTON ( w - > check_print_pagenumbers ) , FALSE ) ;
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( w - > check_print_pagenumbers ) , printing_prefs . print_page_numbers ) ;
w - > check_print_pageheader = gtk_check_button_new_with_mnemonic ( _ ( " Print page header " ) ) ;
gtk_box_pack_start ( GTK_BOX ( page ) , w - > check_print_pageheader , FALSE , FALSE , 0 ) ;
gtk_tooltips_set_tip ( tooltips , w - > check_print_pageheader , _ ( " Adds 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. " ) , NULL ) ;
gtk_button_set_focus_on_click ( GTK_BUTTON ( w - > check_print_pageheader ) , FALSE ) ;
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( w - > check_print_pageheader ) , printing_prefs . print_page_header ) ;
g_signal_connect ( ( gpointer ) w - > check_print_pageheader , " toggled " , G_CALLBACK ( on_page_header_toggled ) , w ) ;
frame33 = gtk_frame_new ( NULL ) ;
gtk_box_pack_start ( GTK_BOX ( page ) , frame33 , FALSE , FALSE , 0 ) ;
gtk_frame_set_label_align ( GTK_FRAME ( frame33 ) , 0 , 0 ) ;
gtk_frame_set_shadow_type ( GTK_FRAME ( frame33 ) , GTK_SHADOW_NONE ) ;
alignment36 = gtk_alignment_new ( 0 , 0.5 , 1 , 1 ) ;
gtk_container_add ( GTK_CONTAINER ( frame33 ) , alignment36 ) ;
gtk_alignment_set_padding ( GTK_ALIGNMENT ( alignment36 ) , 0 , 0 , 12 , 0 ) ;
vbox30 = gtk_vbox_new ( FALSE , 1 ) ;
gtk_container_add ( GTK_CONTAINER ( alignment36 ) , vbox30 ) ;
w - > check_print_basename = gtk_check_button_new_with_mnemonic ( _ ( " Use the basename of the printed file " ) ) ;
gtk_box_pack_start ( GTK_BOX ( vbox30 ) , w - > check_print_basename , FALSE , FALSE , 0 ) ;
gtk_tooltips_set_tip ( tooltips , w - > check_print_basename , _ ( " Print only the basename(without the path) of the printed file. " ) , NULL ) ;
gtk_button_set_focus_on_click ( GTK_BUTTON ( w - > check_print_basename ) , FALSE ) ;
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( w - > check_print_basename ) , printing_prefs . page_header_basename ) ;
hbox10 = gtk_hbox_new ( FALSE , 5 ) ;
gtk_box_pack_start ( GTK_BOX ( vbox30 ) , hbox10 , TRUE , TRUE , 0 ) ;
label203 = gtk_label_new ( _ ( " Date format: " ) ) ;
gtk_box_pack_start ( GTK_BOX ( hbox10 ) , label203 , FALSE , FALSE , 0 ) ;
w - > entry_print_dateformat = gtk_entry_new ( ) ;
gtk_box_pack_start ( GTK_BOX ( hbox10 ) , w - > entry_print_dateformat , TRUE , TRUE , 0 ) ;
gtk_tooltips_set_tip ( tooltips , 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. " ) , NULL ) ;
gtk_entry_set_text ( GTK_ENTRY ( w - > entry_print_dateformat ) , printing_prefs . page_header_datefmt ) ;
on_page_header_toggled ( GTK_TOGGLE_BUTTON ( w - > check_print_pageheader ) , w ) ;
gtk_widget_show_all ( page ) ;
return page ;
}
static void end_print ( GtkPrintOperation * operation , GtkPrintContext * context , gpointer user_data )
{
DocInfo * dinfo = user_data ;
if ( dinfo = = NULL )
return ;
g_object_unref ( dinfo - > layout ) ;
}
static void begin_print ( GtkPrintOperation * operation , GtkPrintContext * context , gpointer user_data )
{
DocInfo * dinfo = user_data ;
PangoFontDescription * desc ;
gint i ;
if ( dinfo = = NULL )
return ;
desc = pango_font_description_from_string ( prefs . editor_font ) ;
// init dinfo fields
dinfo - > lines = sci_get_line_count ( doc_list [ dinfo - > idx ] . sci ) ;
dinfo - > lines_per_page = 0 ;
dinfo - > cur_line = 0 ;
dinfo - > cur_pos = 0 ;
dinfo - > long_line = FALSE ;
dinfo - > print_time = time ( NULL ) ;
dinfo - > max_line_number_margin = get_line_numbers_arity ( dinfo - > lines ) + 1 ;
// increase font width by 1 (looks better)
dinfo - > font_width = get_font_width ( context , desc ) + 1 ;
// create a PangoLayout to be commonly used in get_page_count and draw_page
dinfo - > layout = setup_pango_layout ( context , desc ) ;
// this is necessary because of possible line breaks on the printed page and then
// lines_per_page differs from document line count
dinfo - > n_pages = get_page_count ( context , dinfo ) ;
// read all styles from Scintilla
for ( i = 0 ; i < = STYLE_MAX ; i + + )
{
dinfo - > styles [ i ] [ FORE ] = ROTATE_RGB ( scintilla_send_message (
doc_list [ dinfo - > idx ] . sci , SCI_STYLEGETFORE , i , 0 ) ) ;
if ( i = = STYLE_LINENUMBER )
{ // ignore background colour for line number margin to avoid trouble with wrapped lines
dinfo - > styles [ STYLE_LINENUMBER ] [ BACK ] = ROTATE_RGB ( scintilla_send_message (
doc_list [ dinfo - > idx ] . sci , SCI_STYLEGETBACK , STYLE_DEFAULT , 0 ) ) ;
}
else
{
dinfo - > styles [ i ] [ BACK ] = ROTATE_RGB ( scintilla_send_message (
doc_list [ dinfo - > idx ] . sci , SCI_STYLEGETBACK , i , 0 ) ) ;
}
dinfo - > styles [ i ] [ BOLD ] =
scintilla_send_message ( doc_list [ dinfo - > idx ] . sci , SCI_STYLEGETBOLD , i , 0 ) ;
dinfo - > styles [ i ] [ ITALIC ] =
scintilla_send_message ( doc_list [ dinfo - > idx ] . sci , SCI_STYLEGETITALIC , i , 0 ) ;
}
if ( dinfo - > n_pages > = 0 )
gtk_print_operation_set_n_pages ( operation , dinfo - > n_pages ) ;
pango_font_description_free ( desc ) ;
}
static void draw_page ( GtkPrintOperation * operation , GtkPrintContext * context ,
gint page_nr , gpointer user_data )
{
DocInfo * dinfo = user_data ;
cairo_t * cr ;
gdouble width , height ;
gdouble x , y ;
//~ gint layout_h;
gint count ;
GString * str ;
if ( dinfo = = NULL | | page_nr > = dinfo - > n_pages )
return ;
# ifdef GEANY_PRINT_DEBUG
geany_debug ( " draw_page = %d, pages = %d, (real) lines_per_page = %d " ,
page_nr , dinfo - > n_pages , dinfo - > lines_per_page ) ;
# endif
str = g_string_sized_new ( 256 ) ;
cr = gtk_print_context_get_cairo_context ( context ) ;
width = gtk_print_context_get_width ( context ) ;
height = gtk_print_context_get_height ( context ) ;
cairo_set_source_rgb ( cr , 0 , 0 , 0 ) ;
# ifdef GEANY_PRINT_DEBUG
cairo_set_line_width ( cr , 0.2 ) ;
cairo_rectangle ( cr , 0 , 0 , width , height ) ;
cairo_stroke ( cr ) ;
# endif
cairo_move_to ( cr , 0 , 0 ) ;
pango_layout_set_width ( dinfo - > layout , width * PANGO_SCALE ) ;
if ( printing_prefs . print_page_header )
add_page_header ( dinfo - > layout , cr , dinfo , width , page_nr ) ;
count = 0 ; // the actual line counter for the current page, might be different from
// dinfo->cur_line due to possible line breaks
while ( count < dinfo - > lines_per_page )
{
gchar c = ' a ' ;
gint style = - 1 ;
PangoAttrList * layout_attr ;
PangoAttribute * attr ;
gint colours [ 3 ] = { 0 } ;
gboolean add_linenumber = TRUE ;
gboolean at_eol ;
while ( count < dinfo - > lines_per_page & & c ! = ' \0 ' )
{
at_eol = FALSE ;
g_string_erase ( str , 0 , str - > len ) ; // clear the string
// line numbers
if ( printing_prefs . print_line_numbers & & add_linenumber )
{
// if we had a wrapped line on the last page which needs to be continued, don't
// add a line number
if ( dinfo - > long_line )
{
add_linenumber = FALSE ;
}
else
{
gchar * line_number = NULL ;
gint cur_line_number_margin = get_line_numbers_arity ( dinfo - > cur_line + 1 ) ;
gchar * fill = g_strnfill (
dinfo - > max_line_number_margin - cur_line_number_margin - 1 , ' ' ) ;
line_number = g_strdup_printf ( " %s%d " , fill , dinfo - > cur_line + 1 ) ;
g_string_append ( str , line_number ) ;
dinfo - > cur_line + + ; // increase document line
add_linenumber = FALSE ;
style = STYLE_LINENUMBER ;
c = ' a ' ; // dummy value
g_free ( fill ) ;
g_free ( line_number ) ;
}
}
// data
else
{
style = sci_get_style_at ( doc_list [ dinfo - > idx ] . sci , dinfo - > cur_pos ) ;
c = sci_get_char_at ( doc_list [ dinfo - > idx ] . sci , dinfo - > cur_pos ) ;
if ( c = = ' \0 ' | | style = = - 1 )
{ // if c gets 0, we are probably out of document boundaries, so stop
count = dinfo - > lines_per_page ; // to break out of outer loop
break ;
}
dinfo - > cur_pos + + ;
if ( c = = ' \t ' )
{ // convert tabs to spaces which seems better than using Pango tabs
gchar * s = g_strnfill ( editor_prefs . tab_width , ' ' ) ;
g_string_append ( str , s ) ;
g_free ( s ) ;
}
// don't add line breaks, they are handled manually below
else if ( c = = ' \r ' | | c = = ' \n ' )
{
gchar c_next = sci_get_char_at ( doc_list [ dinfo - > idx ] . sci , dinfo - > cur_pos ) ;
at_eol = TRUE ;
if ( c = = ' \r ' & & c_next = = ' \n ' )
dinfo - > cur_pos + + ; // skip LF part of CR/LF
}
else
{
g_string_append_c ( str , c ) ; // finally add the character
// handle UTF-8: since we add char by char (better: byte by byte), we need to
// keep UTF-8 characters together(maybe two bytes for one character)
// the input is always UTF-8 and c is signed, so all non-Ascii
// characters are less than 0 and consist of all bytes less than 0.
// style doesn't change since it is only one character with multiple bytes.
while ( c < 0 )
{
c = sci_get_char_at ( doc_list [ dinfo - > idx ] . sci , dinfo - > cur_pos ) ;
g_string_append_c ( str , c ) ;
dinfo - > cur_pos + + ;
}
}
}
if ( ! at_eol )
{
// set text
pango_layout_set_text ( dinfo - > layout , str - > str , - 1 ) ;
// attributes
layout_attr = pango_attr_list_new ( ) ;
// foreground colour
get_rgb_values ( dinfo - > styles [ style ] [ FORE ] , & colours [ 0 ] , & colours [ 1 ] , & colours [ 2 ] ) ;
attr = pango_attr_foreground_new ( colours [ 0 ] , colours [ 1 ] , colours [ 2 ] ) ;
ADD_ATTR ( layout_attr , attr ) ;
// background colour
get_rgb_values ( dinfo - > styles [ style ] [ BACK ] , & colours [ 0 ] , & colours [ 1 ] , & colours [ 2 ] ) ;
attr = pango_attr_background_new ( colours [ 0 ] , colours [ 1 ] , colours [ 2 ] ) ;
ADD_ATTR ( layout_attr , attr ) ;
// bold text
if ( dinfo - > styles [ style ] [ BOLD ] )
{
attr = pango_attr_weight_new ( PANGO_WEIGHT_BOLD ) ;
ADD_ATTR ( layout_attr , attr ) ;
}
// italic text
if ( dinfo - > styles [ style ] [ ITALIC ] )
{
attr = pango_attr_style_new ( PANGO_STYLE_ITALIC ) ;
ADD_ATTR ( layout_attr , attr ) ;
}
pango_layout_set_attributes ( dinfo - > layout , layout_attr ) ;
pango_layout_context_changed ( dinfo - > layout ) ;
pango_attr_list_unref ( layout_attr ) ;
}
cairo_get_current_point ( cr , & x , & y ) ;
// normal line break at eol character in document
if ( at_eol )
{
//~ pango_layout_get_size(dinfo->layout, NULL, &layout_h);
//~ cairo_move_to(cr, 0, y + (gdouble)layout_h / PANGO_SCALE);
cairo_move_to ( cr , 0 , y + dinfo - > line_height ) ;
count + + ;
add_linenumber = TRUE ; // we added a new document line so request a new line number
}
else
{
gint x_offset = 0 ;
// maybe we need to force a line break because of too long line
if ( x > = ( width - dinfo - > font_width ) )
{
// don't start the line at horizontal origin because we need to skip the
// line number margin
if ( printing_prefs . print_line_numbers )
{
x_offset = ( dinfo - > max_line_number_margin + 1 ) * dinfo - > font_width ;
}
//~ pango_layout_get_size(dinfo->layout, NULL, &layout_h);
//~ cairo_move_to(cr, x_offset, y + (gdouble)layout_h / PANGO_SCALE);
// this is faster but not exactly the same as above
cairo_move_to ( cr , x_offset , y + dinfo - > line_height ) ;
cairo_get_current_point ( cr , & x , & y ) ;
count + + ;
}
if ( count < dinfo - > lines_per_page )
{
// str->len is counted in bytes not characters, so use g_utf8_strlen()
x_offset = ( g_utf8_strlen ( str - > str , - 1 ) * dinfo - > font_width ) ;
if ( dinfo - > long_line & & count = = 0 )
{
x_offset = ( dinfo - > max_line_number_margin + 1 ) * dinfo - > font_width ;
dinfo - > long_line = FALSE ;
}
pango_cairo_show_layout ( cr , dinfo - > layout ) ;
cairo_move_to ( cr , x + x_offset , y ) ;
}
else
// we are on a wrapped line but we are out of lines on this page, so continue
// the current line on the next page and remember to continue in current line
dinfo - > long_line = TRUE ;
}
}
}
if ( printing_prefs . print_line_numbers )
{ // print a thin line between the line number margin and the data
gint y_start = 0 ;
if ( printing_prefs . print_page_header )
y_start = ( dinfo - > line_height * 3 ) - 2 ; // "- 2": to connect the line number line to
// the page header frame
cairo_set_line_width ( cr , 0.3 ) ;
cairo_move_to ( cr , ( dinfo - > max_line_number_margin * dinfo - > font_width ) + 1 , y_start ) ;
cairo_line_to ( cr , ( dinfo - > max_line_number_margin * dinfo - > font_width ) + 1 ,
y + dinfo - > line_height ) ; // y is last added line, we reuse it
cairo_stroke ( cr ) ;
}
if ( printing_prefs . print_page_numbers )
{
gchar * line = g_strdup_printf ( " <small>- %d -</small> " , page_nr + 1 ) ;
pango_layout_set_markup ( dinfo - > layout , line , - 1 ) ;
pango_layout_set_alignment ( dinfo - > layout , PANGO_ALIGN_CENTER ) ;
cairo_move_to ( cr , 0 , height - dinfo - > line_height ) ;
pango_cairo_show_layout ( cr , dinfo - > layout ) ;
g_free ( line ) ;
# ifdef GEANY_PRINT_DEBUG
cairo_set_line_width ( cr , 0.3 ) ;
cairo_move_to ( cr , 0 , height - ( 1.25 * dinfo - > line_height ) ) ;
cairo_line_to ( cr , width - 1 , height - ( 1.25 * dinfo - > line_height ) ) ;
cairo_stroke ( cr ) ;
# endif
}
g_string_free ( str , TRUE ) ;
}
static void printing_print_gtk ( gint idx )
{
GtkPrintOperation * op ;
GtkPrintOperationResult res = GTK_PRINT_OPERATION_RESULT_ERROR ;
GError * error = NULL ;
DocInfo * dinfo ;
PrintWidgets * widgets ;
/// TODO check for monospace font, detect the widest character in the font and use it at font_width
widgets = g_new0 ( PrintWidgets , 1 ) ;
dinfo = g_new0 ( DocInfo , 1 ) ;
// all other fields are initialised in begin_print()
dinfo - > idx = idx ;
op = gtk_print_operation_new ( ) ;
gtk_print_operation_set_unit ( op , GTK_UNIT_POINTS ) ;
g_signal_connect ( op , " begin-print " , G_CALLBACK ( begin_print ) , dinfo ) ;
g_signal_connect ( op , " end-print " , G_CALLBACK ( end_print ) , dinfo ) ;
g_signal_connect ( op , " draw-page " , G_CALLBACK ( draw_page ) , dinfo ) ;
g_signal_connect ( op , " create-custom-widget " , G_CALLBACK ( create_custom_widget ) , widgets ) ;
g_signal_connect ( op , " custom-widget-apply " , G_CALLBACK ( custom_widget_apply ) , widgets ) ;
if ( settings ! = NULL )
gtk_print_operation_set_print_settings ( op , settings ) ;
if ( page_setup ! = NULL )
gtk_print_operation_set_default_page_setup ( op , page_setup ) ;
res = gtk_print_operation_run (
op , GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG , GTK_WINDOW ( app - > window ) , & error ) ;
if ( res = = GTK_PRINT_OPERATION_RESULT_APPLY )
{
if ( settings ! = NULL )
g_object_unref ( settings ) ;
settings = g_object_ref ( gtk_print_operation_get_print_settings ( op ) ) ;
msgwin_status_add ( _ ( " File %s printed. " ) , doc_list [ idx ] . file_name ) ;
}
else if ( res = = GTK_PRINT_OPERATION_RESULT_ERROR )
{
dialogs_show_msgbox ( GTK_MESSAGE_ERROR , _ ( " Printing of \" %s \" failed (%s). " ) ,
doc_list [ idx ] . file_name , error - > message ) ;
g_error_free ( error ) ;
}
g_object_unref ( op ) ;
g_free ( dinfo ) ;
g_free ( widgets ) ;
}
void printing_page_setup_gtk ( void )
{
GtkPageSetup * new_page_setup ;
if ( settings = = NULL )
settings = gtk_print_settings_new ( ) ;
new_page_setup = gtk_print_run_page_setup_dialog (
GTK_WINDOW ( app - > window ) , page_setup , settings ) ;
if ( page_setup ! = NULL )
g_object_unref ( page_setup ) ;
page_setup = new_page_setup ;
}
# endif // GTK 2.10
/* simple file print using an external tool */
static void print_external ( gint idx )
{
gchar * cmdline ;
if ( doc_list [ idx ] . file_name = = NULL )
return ;
if ( ! NZV ( printing_prefs . external_print_cmd ) )
{
dialogs_show_msgbox ( GTK_MESSAGE_ERROR ,
_ ( " Please set a print command in the preferences dialog first " ) ) ;
return ;
}
cmdline = g_strdup ( printing_prefs . external_print_cmd ) ;
cmdline = utils_str_replace ( cmdline , " %f " , doc_list [ idx ] . file_name ) ;
if ( dialogs_show_question (
_ ( " The file \" %s \" will be printed with the following command: \n \n %s " ) ,
doc_list [ idx ] . file_name , cmdline ) )
{
GError * error = NULL ;
# ifdef G_OS_WIN32
gchar * tmp_cmdline = g_strdup ( cmdline ) ;
# else
// /bin/sh -c emulates the system() call and makes complex commands possible
// but only needed on non-win32 systems due to the lack of win32's shell capabilities
gchar * tmp_cmdline = g_strconcat ( " /bin/sh -c \" " , cmdline , " \" " , NULL ) ;
# endif
if ( ! g_spawn_command_line_async ( tmp_cmdline , & error ) )
{
dialogs_show_msgbox ( GTK_MESSAGE_ERROR , _ ( " Printing of \" %s \" failed (return code: %s). " ) ,
doc_list [ idx ] . file_name , error - > message ) ;
g_error_free ( error ) ;
}
else
{
msgwin_status_add ( _ ( " File %s printed. " ) , doc_list [ idx ] . file_name ) ;
}
g_free ( tmp_cmdline ) ;
}
g_free ( cmdline ) ;
}
void printing_print_doc ( gint idx )
{
if ( ! DOC_IDX_VALID ( idx ) )
return ;
# if GTK_CHECK_VERSION(2, 10, 0)
if ( gtk_check_version ( 2 , 10 , 0 ) = = NULL & & printing_prefs . use_gtk_printing )
printing_print_gtk ( idx ) ;
else
# endif
print_external ( idx ) ;
}