/*
* gedit-print-job.c
* This file is part of gedit
*
* Copyright (C) 2000-2001 Chema Celorio, Paolo Maggi
* Copyright (C) 2002-2008 Paolo Maggi
* Copyright (C) 2015 Sébastien Wilmet
*
* 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, see .
*/
#include "gedit-print-job.h"
#include
#include "gedit-print-preview.h"
#include "gedit-settings.h"
struct _GeditPrintJob
{
GObject parent_instance;
GSettings *gsettings;
TeplView *view;
GtkPrintOperation *operation;
GtkSourcePrintCompositor *compositor;
GtkWidget *preview;
gchar *status_string;
gdouble progress;
/* Widgets part of the custom print preferences widget.
* These pointers are valid just when the dialog is displayed.
*/
GtkToggleButton *syntax_checkbutton;
GtkToggleButton *page_header_checkbutton;
GtkToggleButton *line_numbers_checkbutton;
GtkSpinButton *line_numbers_spinbutton;
GtkToggleButton *text_wrapping_checkbutton;
GtkToggleButton *do_not_split_checkbutton;
GtkFontButton *body_fontbutton;
GtkFontButton *headers_fontbutton;
GtkFontButton *numbers_fontbutton;
guint is_preview : 1;
};
enum
{
PROP_0,
PROP_VIEW,
LAST_PROP
};
static GParamSpec *properties[LAST_PROP];
enum
{
PRINTING,
SHOW_PREVIEW,
DONE,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
G_DEFINE_TYPE (GeditPrintJob, gedit_print_job, G_TYPE_OBJECT)
static void
gedit_print_job_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GeditPrintJob *job = GEDIT_PRINT_JOB (object);
switch (prop_id)
{
case PROP_VIEW:
g_value_set_object (value, job->view);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gedit_print_job_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GeditPrintJob *job = GEDIT_PRINT_JOB (object);
switch (prop_id)
{
case PROP_VIEW:
job->view = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gedit_print_job_dispose (GObject *object)
{
GeditPrintJob *job = GEDIT_PRINT_JOB (object);
g_clear_object (&job->gsettings);
g_clear_object (&job->operation);
g_clear_object (&job->compositor);
g_clear_object (&job->preview);
G_OBJECT_CLASS (gedit_print_job_parent_class)->dispose (object);
}
static void
gedit_print_job_finalize (GObject *object)
{
GeditPrintJob *job = GEDIT_PRINT_JOB (object);
g_free (job->status_string);
G_OBJECT_CLASS (gedit_print_job_parent_class)->finalize (object);
}
static void
gedit_print_job_printing (GeditPrintJob *job,
GeditPrintJobStatus status)
{
}
static void
gedit_print_job_show_preview (GeditPrintJob *job,
GtkWidget *preview)
{
}
static void
gedit_print_job_done (GeditPrintJob *job,
GeditPrintJobResult result,
const GError *error)
{
}
static void
gedit_print_job_class_init (GeditPrintJobClass *klass)
{
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (klass);
object_class->get_property = gedit_print_job_get_property;
object_class->set_property = gedit_print_job_set_property;
object_class->dispose = gedit_print_job_dispose;
object_class->finalize = gedit_print_job_finalize;
properties[PROP_VIEW] =
g_param_spec_object ("view",
"view",
"",
TEPL_TYPE_VIEW,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class, LAST_PROP, properties);
signals[PRINTING] =
g_signal_new_class_handler ("printing",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_CALLBACK (gedit_print_job_printing),
NULL, NULL, NULL,
G_TYPE_NONE,
1,
G_TYPE_UINT);
signals[SHOW_PREVIEW] =
g_signal_new_class_handler ("show-preview",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_CALLBACK (gedit_print_job_show_preview),
NULL, NULL, NULL,
G_TYPE_NONE,
1,
GTK_TYPE_WIDGET);
signals[DONE] =
g_signal_new_class_handler ("done",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_CALLBACK (gedit_print_job_done),
NULL, NULL, NULL,
G_TYPE_NONE,
2,
G_TYPE_UINT,
G_TYPE_POINTER);
}
static void
gedit_print_job_init (GeditPrintJob *job)
{
job->gsettings = g_settings_new ("org.gnome.gedit.preferences.print");
job->status_string = g_strdup (_("Preparing…"));
}
static void
restore_button_clicked (GtkButton *button,
GeditPrintJob *job)
{
g_settings_reset (job->gsettings, GEDIT_SETTINGS_PRINT_FONT_BODY_PANGO);
g_settings_reset (job->gsettings, GEDIT_SETTINGS_PRINT_FONT_HEADER_PANGO);
g_settings_reset (job->gsettings, GEDIT_SETTINGS_PRINT_FONT_NUMBERS_PANGO);
}
static GObject *
create_custom_widget_cb (GtkPrintOperation *operation,
GeditPrintJob *job)
{
GtkBuilder *builder;
GtkWidget *contents;
GtkWidget *line_numbers_hbox;
GtkWidget *restore_button;
guint line_numbers;
GtkWrapMode wrap_mode;
gchar *root_objects[] = {
"adjustment1",
"contents",
NULL
};
builder = gtk_builder_new ();
gtk_builder_add_objects_from_resource (builder, "/org/gnome/gedit/ui/gedit-print-preferences.ui",
root_objects, NULL);
contents = GTK_WIDGET (gtk_builder_get_object (builder, "contents"));
g_object_ref (contents);
job->syntax_checkbutton = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "syntax_checkbutton"));
job->line_numbers_checkbutton = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "line_numbers_checkbutton"));
line_numbers_hbox = GTK_WIDGET (gtk_builder_get_object (builder, "line_numbers_hbox"));
job->line_numbers_spinbutton = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "line_numbers_spinbutton"));
job->page_header_checkbutton = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "page_header_checkbutton"));
job->text_wrapping_checkbutton = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "text_wrapping_checkbutton"));
job->do_not_split_checkbutton = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "do_not_split_checkbutton"));
job->body_fontbutton = GTK_FONT_BUTTON (gtk_builder_get_object (builder, "body_fontbutton"));
job->headers_fontbutton = GTK_FONT_BUTTON (gtk_builder_get_object (builder, "headers_fontbutton"));
job->numbers_fontbutton = GTK_FONT_BUTTON (gtk_builder_get_object (builder, "numbers_fontbutton"));
restore_button = GTK_WIDGET (gtk_builder_get_object (builder, "restore_button"));
g_object_unref (builder);
/* Syntax highlighting */
g_settings_bind (job->gsettings, GEDIT_SETTINGS_PRINT_SYNTAX_HIGHLIGHTING,
job->syntax_checkbutton, "active",
G_SETTINGS_BIND_GET);
/* Print header */
g_settings_bind (job->gsettings, GEDIT_SETTINGS_PRINT_HEADER,
job->page_header_checkbutton, "active",
G_SETTINGS_BIND_GET);
/* Line numbers */
g_settings_get (job->gsettings, GEDIT_SETTINGS_PRINT_LINE_NUMBERS,
"u", &line_numbers);
if (line_numbers > 0)
{
gtk_spin_button_set_value (job->line_numbers_spinbutton, line_numbers);
}
else
{
gtk_spin_button_set_value (job->line_numbers_spinbutton, 1);
}
gtk_toggle_button_set_active (job->line_numbers_checkbutton,
line_numbers > 0);
g_object_bind_property (job->line_numbers_checkbutton, "active",
line_numbers_hbox, "sensitive",
G_BINDING_SYNC_CREATE);
/* Fonts */
g_settings_bind (job->gsettings, GEDIT_SETTINGS_PRINT_FONT_BODY_PANGO,
job->body_fontbutton, "font-name",
G_SETTINGS_BIND_GET);
g_settings_bind (job->gsettings, GEDIT_SETTINGS_PRINT_FONT_HEADER_PANGO,
job->headers_fontbutton, "font-name",
G_SETTINGS_BIND_GET);
g_settings_bind (job->gsettings, GEDIT_SETTINGS_PRINT_FONT_NUMBERS_PANGO,
job->numbers_fontbutton, "font-name",
G_SETTINGS_BIND_GET);
/* Wrap mode */
wrap_mode = g_settings_get_enum (job->gsettings, GEDIT_SETTINGS_PRINT_WRAP_MODE);
switch (wrap_mode)
{
case GTK_WRAP_WORD:
gtk_toggle_button_set_active (job->text_wrapping_checkbutton, TRUE);
gtk_toggle_button_set_active (job->do_not_split_checkbutton, TRUE);
break;
case GTK_WRAP_CHAR:
gtk_toggle_button_set_active (job->text_wrapping_checkbutton, TRUE);
gtk_toggle_button_set_active (job->do_not_split_checkbutton, FALSE);
break;
default:
gtk_toggle_button_set_active (job->text_wrapping_checkbutton, FALSE);
break;
}
g_object_bind_property (job->text_wrapping_checkbutton, "active",
job->do_not_split_checkbutton, "sensitive",
G_BINDING_SYNC_CREATE);
g_object_bind_property (job->text_wrapping_checkbutton, "active",
job->do_not_split_checkbutton, "inconsistent",
G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE);
/* Restore */
g_signal_connect (restore_button,
"clicked",
G_CALLBACK (restore_button_clicked),
job);
return G_OBJECT (contents);
}
static void
custom_widget_apply_cb (GtkPrintOperation *operation,
GtkWidget *widget,
GeditPrintJob *job)
{
gboolean syntax;
gboolean page_header;
const gchar *body_font;
const gchar *header_font;
const gchar *numbers_font;
GtkWrapMode wrap_mode;
syntax = gtk_toggle_button_get_active (job->syntax_checkbutton);
page_header = gtk_toggle_button_get_active (job->page_header_checkbutton);
body_font = gtk_font_chooser_get_font (GTK_FONT_CHOOSER (job->body_fontbutton));
header_font = gtk_font_chooser_get_font (GTK_FONT_CHOOSER (job->headers_fontbutton));
numbers_font = gtk_font_chooser_get_font (GTK_FONT_CHOOSER (job->numbers_fontbutton));
g_settings_set_boolean (job->gsettings,
GEDIT_SETTINGS_PRINT_SYNTAX_HIGHLIGHTING,
syntax);
g_settings_set_boolean (job->gsettings,
GEDIT_SETTINGS_PRINT_HEADER,
page_header);
g_settings_set_string (job->gsettings,
GEDIT_SETTINGS_PRINT_FONT_BODY_PANGO,
body_font);
g_settings_set_string (job->gsettings,
GEDIT_SETTINGS_PRINT_FONT_HEADER_PANGO,
header_font);
g_settings_set_string (job->gsettings,
GEDIT_SETTINGS_PRINT_FONT_NUMBERS_PANGO,
numbers_font);
if (gtk_toggle_button_get_active (job->line_numbers_checkbutton))
{
gint num;
num = gtk_spin_button_get_value_as_int (job->line_numbers_spinbutton);
g_settings_set (job->gsettings,
GEDIT_SETTINGS_PRINT_LINE_NUMBERS,
"u", MAX (1, num));
}
else
{
g_settings_set (job->gsettings,
GEDIT_SETTINGS_PRINT_LINE_NUMBERS,
"u", 0);
}
if (gtk_toggle_button_get_active (job->text_wrapping_checkbutton))
{
if (gtk_toggle_button_get_active (job->do_not_split_checkbutton))
{
wrap_mode = GTK_WRAP_WORD;
}
else
{
wrap_mode = GTK_WRAP_CHAR;
}
}
else
{
wrap_mode = GTK_WRAP_NONE;
}
g_settings_set_enum (job->gsettings,
GEDIT_SETTINGS_PRINT_WRAP_MODE,
wrap_mode);
}
static void
preview_ready (GtkPrintOperationPreview *gtk_preview,
GtkPrintContext *context,
GeditPrintJob *job)
{
job->is_preview = TRUE;
g_signal_emit (job, signals[SHOW_PREVIEW], 0, job->preview);
g_clear_object (&job->preview);
}
static gboolean
preview_cb (GtkPrintOperation *op,
GtkPrintOperationPreview *gtk_preview,
GtkPrintContext *context,
GtkWindow *parent,
GeditPrintJob *job)
{
g_clear_object (&job->preview);
job->preview = gedit_print_preview_new (op, gtk_preview, context);
g_object_ref_sink (job->preview);
g_signal_connect_after (gtk_preview,
"ready",
G_CALLBACK (preview_ready),
job);
return TRUE;
}
static void
create_compositor (GeditPrintJob *job)
{
GtkSourceBuffer *buf;
gchar *print_font_body;
gchar *print_font_header;
gchar *print_font_numbers;
gboolean syntax_hl;
GtkWrapMode wrap_mode;
guint print_line_numbers;
gboolean print_header;
guint tab_width;
gdouble margin;
buf = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (job->view)));
print_font_body = g_settings_get_string (job->gsettings,
GEDIT_SETTINGS_PRINT_FONT_BODY_PANGO);
print_font_header = g_settings_get_string (job->gsettings,
GEDIT_SETTINGS_PRINT_FONT_HEADER_PANGO);
print_font_numbers = g_settings_get_string (job->gsettings,
GEDIT_SETTINGS_PRINT_FONT_NUMBERS_PANGO);
g_settings_get (job->gsettings,
GEDIT_SETTINGS_PRINT_LINE_NUMBERS,
"u", &print_line_numbers);
print_header = g_settings_get_boolean (job->gsettings,
GEDIT_SETTINGS_PRINT_HEADER);
wrap_mode = g_settings_get_enum (job->gsettings,
GEDIT_SETTINGS_PRINT_WRAP_MODE);
syntax_hl = g_settings_get_boolean (job->gsettings,
GEDIT_SETTINGS_PRINT_SYNTAX_HIGHLIGHTING);
syntax_hl &= gtk_source_buffer_get_highlight_syntax (buf);
tab_width = gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (job->view));
job->compositor = GTK_SOURCE_PRINT_COMPOSITOR (
g_object_new (GTK_SOURCE_TYPE_PRINT_COMPOSITOR,
"buffer", buf,
"tab-width", tab_width,
"highlight-syntax", syntax_hl,
"wrap-mode", wrap_mode,
"print-line-numbers", print_line_numbers,
"print-header", print_header,
"print-footer", FALSE,
"body-font-name", print_font_body,
"line-numbers-font-name", print_font_numbers,
"header-font-name", print_font_header,
NULL));
margin = g_settings_get_double (job->gsettings, GEDIT_SETTINGS_PRINT_MARGIN_LEFT);
gtk_source_print_compositor_set_left_margin (job->compositor, margin, GTK_UNIT_MM);
margin = g_settings_get_double (job->gsettings, GEDIT_SETTINGS_PRINT_MARGIN_TOP);
gtk_source_print_compositor_set_top_margin (job->compositor, margin, GTK_UNIT_MM);
margin = g_settings_get_double (job->gsettings, GEDIT_SETTINGS_PRINT_MARGIN_RIGHT);
gtk_source_print_compositor_set_right_margin (job->compositor, margin, GTK_UNIT_MM);
margin = g_settings_get_double (job->gsettings, GEDIT_SETTINGS_PRINT_MARGIN_BOTTOM);
gtk_source_print_compositor_set_bottom_margin (job->compositor, margin, GTK_UNIT_MM);
if (print_header)
{
gchar *doc_name;
gchar *name_to_display;
gchar *left;
doc_name = tepl_file_get_full_name (tepl_buffer_get_file (TEPL_BUFFER (buf)));
name_to_display = tepl_utils_str_middle_truncate (doc_name, 60);
left = g_strdup_printf (_("File: %s"), name_to_display);
gtk_source_print_compositor_set_header_format (job->compositor,
TRUE,
left,
NULL,
/* Translators: %N is the current page number, %Q is the total
* number of pages (ex. Page 2 of 10)
*/
_("Page %N of %Q"));
g_free (doc_name);
g_free (name_to_display);
g_free (left);
}
g_free (print_font_body);
g_free (print_font_header);
g_free (print_font_numbers);
}
static void
begin_print_cb (GtkPrintOperation *operation,
GtkPrintContext *context,
GeditPrintJob *job)
{
create_compositor (job);
job->progress = 0.0;
g_signal_emit (job,
signals[PRINTING],
0,
GEDIT_PRINT_JOB_STATUS_PAGINATING);
}
static gboolean
paginate_cb (GtkPrintOperation *operation,
GtkPrintContext *context,
GeditPrintJob *job)
{
gboolean finished;
finished = gtk_source_print_compositor_paginate (job->compositor, context);
if (finished)
{
gint n_pages;
n_pages = gtk_source_print_compositor_get_n_pages (job->compositor);
gtk_print_operation_set_n_pages (job->operation, n_pages);
}
job->progress = gtk_source_print_compositor_get_pagination_progress (job->compositor);
/* When previewing, the progress is just for pagination, when printing
* it's split between pagination and rendering.
*/
if (!job->is_preview)
{
job->progress /= 2.0;
}
g_signal_emit (job,
signals[PRINTING],
0,
GEDIT_PRINT_JOB_STATUS_PAGINATING);
return finished;
}
static void
draw_page_cb (GtkPrintOperation *operation,
GtkPrintContext *context,
gint page_nr,
GeditPrintJob *job)
{
/* In preview, pages are drawn on the fly, so rendering is
* not part of the progress.
*/
if (!job->is_preview)
{
gint n_pages;
n_pages = gtk_source_print_compositor_get_n_pages (job->compositor);
g_free (job->status_string);
job->status_string = g_strdup_printf (_("Rendering page %d of %d…"), page_nr + 1, n_pages);
job->progress = page_nr / (2.0 * n_pages) + 0.5;
g_signal_emit (job,
signals[PRINTING],
0,
GEDIT_PRINT_JOB_STATUS_DRAWING);
}
gtk_source_print_compositor_draw_page (job->compositor, context, page_nr);
}
static void
end_print_cb (GtkPrintOperation *operation,
GtkPrintContext *context,
GeditPrintJob *job)
{
g_clear_object (&job->compositor);
}
static void
done_cb (GtkPrintOperation *operation,
GtkPrintOperationResult result,
GeditPrintJob *job)
{
GError *error = NULL;
GeditPrintJobResult print_result;
switch (result)
{
case GTK_PRINT_OPERATION_RESULT_CANCEL:
print_result = GEDIT_PRINT_JOB_RESULT_CANCEL;
break;
case GTK_PRINT_OPERATION_RESULT_APPLY:
print_result = GEDIT_PRINT_JOB_RESULT_OK;
break;
case GTK_PRINT_OPERATION_RESULT_ERROR:
print_result = GEDIT_PRINT_JOB_RESULT_ERROR;
gtk_print_operation_get_error (operation, &error);
break;
default:
g_return_if_reached ();
}
/* Avoid that job is destroyed in the handler of the "done" message. */
g_object_ref (job);
g_signal_emit (job, signals[DONE], 0, print_result, error);
g_object_unref (job);
}
GeditPrintJob *
gedit_print_job_new (TeplView *view)
{
g_return_val_if_fail (TEPL_IS_VIEW (view), NULL);
return g_object_new (GEDIT_TYPE_PRINT_JOB,
"view", view,
NULL);
}
/* Note that gedit_print_job_print() can only be called once on a given
* GeditPrintJob.
*/
GtkPrintOperationResult
gedit_print_job_print (GeditPrintJob *job,
GtkPrintOperationAction action,
GtkPageSetup *page_setup,
GtkPrintSettings *settings,
GtkWindow *parent,
GError **error)
{
TeplBuffer *buffer;
gchar *job_name;
g_return_val_if_fail (job->operation == NULL, GTK_PRINT_OPERATION_RESULT_ERROR);
g_return_val_if_fail (job->compositor == NULL, GTK_PRINT_OPERATION_RESULT_ERROR);
job->operation = gtk_print_operation_new ();
job->is_preview = action == GTK_PRINT_OPERATION_ACTION_PREVIEW;
if (settings != NULL)
{
gtk_print_operation_set_print_settings (job->operation,
settings);
}
if (page_setup != NULL)
{
gtk_print_operation_set_default_page_setup (job->operation,
page_setup);
}
buffer = TEPL_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (job->view)));
job_name = tepl_file_get_short_name (tepl_buffer_get_file (buffer));
gtk_print_operation_set_job_name (job->operation, job_name);
g_free (job_name);
gtk_print_operation_set_embed_page_setup (job->operation, TRUE);
gtk_print_operation_set_custom_tab_label (job->operation, _("Text Editor"));
gtk_print_operation_set_allow_async (job->operation, TRUE);
g_signal_connect (job->operation,
"create-custom-widget",
G_CALLBACK (create_custom_widget_cb),
job);
g_signal_connect (job->operation,
"custom-widget-apply",
G_CALLBACK (custom_widget_apply_cb),
job);
g_signal_connect (job->operation,
"preview",
G_CALLBACK (preview_cb),
job);
g_signal_connect (job->operation,
"begin-print",
G_CALLBACK (begin_print_cb),
job);
g_signal_connect (job->operation,
"paginate",
G_CALLBACK (paginate_cb),
job);
g_signal_connect (job->operation,
"draw-page",
G_CALLBACK (draw_page_cb),
job);
g_signal_connect_object (job->operation,
"end-print",
G_CALLBACK (end_print_cb),
job,
0);
g_signal_connect_object (job->operation,
"done",
G_CALLBACK (done_cb),
job,
0);
return gtk_print_operation_run (job->operation,
action,
parent,
error);
}
void
gedit_print_job_cancel (GeditPrintJob *job)
{
g_return_if_fail (GEDIT_IS_PRINT_JOB (job));
gtk_print_operation_cancel (job->operation);
}
const gchar *
gedit_print_job_get_status_string (GeditPrintJob *job)
{
g_return_val_if_fail (GEDIT_IS_PRINT_JOB (job), NULL);
g_return_val_if_fail (job->status_string != NULL, NULL);
return job->status_string;
}
gdouble
gedit_print_job_get_progress (GeditPrintJob *job)
{
g_return_val_if_fail (GEDIT_IS_PRINT_JOB (job), 0.0);
return job->progress;
}
GtkPrintSettings *
gedit_print_job_get_print_settings (GeditPrintJob *job)
{
g_return_val_if_fail (GEDIT_IS_PRINT_JOB (job), NULL);
return gtk_print_operation_get_print_settings (job->operation);
}
GtkPageSetup *
gedit_print_job_get_page_setup (GeditPrintJob *job)
{
g_return_val_if_fail (GEDIT_IS_PRINT_JOB (job), NULL);
return gtk_print_operation_get_default_page_setup (job->operation);
}
/* ex:set ts=8 noet: */