The following example demonstrates how to print some input from a user interface
using PrintOperation
. It shows how to implement
on_begin_print
and on_draw_page
,
as well as how to track print status and update the print settings.
File: examplewindow.h
(For use with gtkmm 4)
#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H
// This file is part of the printing/simple and printing/advanced examples
#include <gtkmm.h>
class PrintFormOperation;
class ExampleWindow : public Gtk::Window
{
public:
ExampleWindow(const Glib::RefPtr<Gtk::Application>& app);
virtual ~ExampleWindow();
protected:
void build_main_menu(const Glib::RefPtr<Gtk::Application>& app);
void print_or_preview(Gtk::PrintOperation::Action print_action);
//PrintOperation signal handlers.
//We handle these so can get necessary information to update the UI or print settings.
//Our derived PrintOperation class also overrides some default signal handlers.
void on_printoperation_status_changed();
void on_printoperation_done(Gtk::PrintOperation::Result result);
//Action signal handlers:
void on_menu_file_new();
void on_menu_file_page_setup();
void on_menu_file_print_preview();
void on_menu_file_print();
void on_menu_file_quit();
//Printing-related objects:
Glib::RefPtr<Gtk::PageSetup> m_refPageSetup;
Glib::RefPtr<Gtk::PrintSettings> m_refSettings;
Glib::RefPtr<PrintFormOperation> m_refPrintFormOperation;
//Child widgets:
Gtk::Box m_VBox;
Gtk::Grid m_Grid;
Gtk::Label m_NameLabel;
Gtk::Entry m_NameEntry;
Gtk::Label m_SurnameLabel;
Gtk::Entry m_SurnameEntry;
Gtk::Label m_CommentsLabel;
Gtk::ScrolledWindow m_ScrolledWindow;
Gtk::TextView m_TextView;
Glib::RefPtr<Gtk::TextBuffer> m_refTextBuffer;
Gtk::Label m_Statusbar;
Glib::RefPtr<Gtk::Builder> m_refBuilder;
Glib::RefPtr<Gtk::AlertDialog> m_refDialog;
};
#endif //GTKMM_EXAMPLEWINDOW_H
File: printformoperation.h
(For use with gtkmm 4)
#ifndef GTKMM_PRINT_FORM_OPERATION_H
#define GTKMM_PRINT_FORM_OPERATION_H
#include <gtkmm.h>
#include <pangomm.h>
#include <vector>
//We derive our own class from PrintOperation,
//so we can put the actual print implementation here.
class PrintFormOperation : public Gtk::PrintOperation
{
public:
static Glib::RefPtr<PrintFormOperation> create();
virtual ~PrintFormOperation();
void set_name(const Glib::ustring& name) { m_Name = name; }
void set_comments(const Glib::ustring& comments) { m_Comments = comments; }
protected:
PrintFormOperation();
//PrintOperation default signal handler overrides:
void on_begin_print(const Glib::RefPtr<Gtk::PrintContext>& context) override;
void on_draw_page(const Glib::RefPtr<Gtk::PrintContext>& context, int page_nr) override;
Glib::ustring m_Name;
Glib::ustring m_Comments;
Glib::RefPtr<Pango::Layout> m_refLayout;
std::vector<int> m_PageBreaks; // line numbers where a page break occurs
};
#endif // GTKMM_PRINT_FORM_OPERATION_H
File: examplewindow.cc
(For use with gtkmm 4)
#include "examplewindow.h"
#include "printformoperation.h"
#include <iostream>
ExampleWindow::ExampleWindow(const Glib::RefPtr<Gtk::Application>& app)
: m_VBox(Gtk::Orientation::VERTICAL),
m_NameLabel("Name"),
m_SurnameLabel("Surname"),
m_CommentsLabel("Comments")
{
m_refPageSetup = Gtk::PageSetup::create();
m_refSettings = Gtk::PrintSettings::create();
set_title("gtkmm Printing Example");
set_default_size(400, 300);
set_child(m_VBox);
build_main_menu(app);
m_VBox.append(m_Grid);
//Arrange the widgets inside the grid:
m_Grid.set_expand(true);
m_Grid.set_row_spacing(5);
m_Grid.set_column_spacing(5);
m_Grid.attach(m_NameLabel, 0, 0);
m_Grid.attach(m_NameEntry, 1, 0);
m_Grid.attach(m_SurnameLabel, 0, 1);
m_Grid.attach(m_SurnameEntry, 1, 1);
//Add the TextView, inside a ScrolledWindow:
m_ScrolledWindow.set_child(m_TextView);
//Only show the scrollbars when they are necessary:
m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
m_Grid.attach(m_CommentsLabel, 0, 2);
m_Grid.attach(m_ScrolledWindow, 1, 2);
m_ScrolledWindow.set_expand(true);
m_refTextBuffer = Gtk::TextBuffer::create();
m_TextView.set_buffer(m_refTextBuffer);
m_Statusbar.set_hexpand(true);
m_VBox.append(m_Statusbar);
}
ExampleWindow::~ExampleWindow()
{
}
void ExampleWindow::build_main_menu(const Glib::RefPtr<Gtk::Application>& app)
{
//Create actions for menus and toolbars:
auto refActionGroup = Gio::SimpleActionGroup::create();
//File menu:
refActionGroup->add_action("new",
sigc::mem_fun(*this, &ExampleWindow::on_menu_file_new));
refActionGroup->add_action("pagesetup",
sigc::mem_fun(*this, &ExampleWindow::on_menu_file_page_setup));
refActionGroup->add_action("printpreview",
sigc::mem_fun(*this, &ExampleWindow::on_menu_file_print_preview));
refActionGroup->add_action("print",
sigc::mem_fun(*this, &ExampleWindow::on_menu_file_print));
refActionGroup->add_action("quit",
sigc::mem_fun(*this, &ExampleWindow::on_menu_file_quit));
insert_action_group("example", refActionGroup);
// When the menubar is a child of a Gtk::Window, keyboard accelerators are not
// automatically fetched from the Gio::Menu.
// See the examples/book/menus/main_menu example for an alternative way of
// adding the menubar when using Gtk::ApplicationWindow.
app->set_accel_for_action("example.new", "<Primary>n");
app->set_accel_for_action("example.print", "<Primary>p");
app->set_accel_for_action("example.quit", "<Primary>q");
m_refBuilder = Gtk::Builder::create();
// Layout the actions in a menubar:
Glib::ustring ui_menu_info =
"<interface>"
" <menu id='menu-example'>"
" <submenu>"
" <attribute name='label' translatable='yes'>_File</attribute>"
" <section>"
" <item>"
" <attribute name='label' translatable='yes'>_New</attribute>"
" <attribute name='action'>example.new</attribute>"
" </item>"
" </section>"
" <section>"
" <item>"
" <attribute name='label' translatable='yes'>Page _Setup...</attribute>"
" <attribute name='action'>example.pagesetup</attribute>"
" </item>"
" <item>"
" <attribute name='label' translatable='yes'>Print Preview</attribute>"
" <attribute name='action'>example.printpreview</attribute>"
" </item>"
" <item>"
" <attribute name='label' translatable='yes'>_Print...</attribute>"
" <attribute name='action'>example.print</attribute>"
" </item>"
" </section>"
" <section>"
" <item>"
" <attribute name='label' translatable='yes'>_Quit</attribute>"
" <attribute name='action'>example.quit</attribute>"
" </item>"
" </section>"
" </submenu>"
" </menu>"
"</interface>";
try
{
m_refBuilder->add_from_string(ui_menu_info);
}
catch(const Glib::Error& ex)
{
std::cerr << "building menus failed: " << ex.what();
}
// Layout the actions in a toolbar:
Glib::ustring ui_toolbar_info =
"<!-- Generated with glade 3.18.3 and then changed manually -->"
"<interface>"
"<object class='GtkBox' id='toolbar'>"
"<property name='can_focus'>False</property>"
"<property name='spacing'>3</property>"
"<child>"
"<object class='GtkButton' id='toolbutton_new'>"
"<property name='can_focus'>False</property>"
"<property name='tooltip_text' translatable='yes'>New</property>"
"<property name='action_name'>example.new</property>"
"<property name='icon_name'>document-new</property>"
"<property name='hexpand'>False</property>"
"<property name='vexpand'>False</property>"
"</object>"
"</child>"
"<child>"
"<object class='GtkButton' id='toolbutton_print'>"
"<property name='can_focus'>False</property>"
"<property name='tooltip_text' translatable='yes'>Print</property>"
"<property name='action_name'>example.print</property>"
"<property name='icon_name'>document-print</property>"
"<property name='hexpand'>False</property>"
"<property name='vexpand'>False</property>"
"</object>"
"</child>"
"<child>"
"<object class='GtkSeparator' id='separator1'>"
"<property name='can_focus'>False</property>"
"<property name='hexpand'>False</property>"
"<property name='vexpand'>False</property>"
"</object>"
"</child>"
"<child>"
"<object class='GtkButton' id='toolbutton_quit'>"
"<property name='can_focus'>False</property>"
"<property name='tooltip_text' translatable='yes'>Quit</property>"
"<property name='action_name'>example.quit</property>"
"<property name='icon_name'>application-exit</property>"
"<property name='hexpand'>False</property>"
"<property name='vexpand'>False</property>"
"</object>"
"</child>"
"</object>"
"</interface>";
try
{
m_refBuilder->add_from_string(ui_toolbar_info);
}
catch(const Glib::Error& ex)
{
std::cerr << "building toolbar failed: " << ex.what();
}
// Get the menubar and add it to a container widget:
auto object = m_refBuilder->get_object("menu-example");
auto gmenu = std::dynamic_pointer_cast<Gio::Menu>(object);
if (!gmenu)
g_warning("GMenu not found");
else
{
auto pMenuBar = Gtk::make_managed<Gtk::PopoverMenuBar>(gmenu);
// Add the PopoverMenuBar to the window:
m_VBox.append(*pMenuBar);
}
// Get the toolbar and add it to a container widget:
auto toolbar = m_refBuilder->get_widget<Gtk::Box>("toolbar");
if (!toolbar)
g_warning("toolbar not found");
else
m_VBox.append(*toolbar);
}
void ExampleWindow::on_printoperation_status_changed()
{
Glib::ustring status_msg;
if (m_refPrintFormOperation->is_finished())
{
status_msg = "Print job completed.";
}
else
{
//You could also use get_status().
status_msg = m_refPrintFormOperation->get_status_string();
}
m_Statusbar.set_text(status_msg);
}
void ExampleWindow::on_printoperation_done(Gtk::PrintOperation::Result result)
{
//Printing is "done" when the print data is spooled.
if (result == Gtk::PrintOperation::Result::ERROR)
{
if (!m_refDialog)
m_refDialog = Gtk::AlertDialog::create("Error printing form");
m_refDialog->show(*this);
}
else if (result == Gtk::PrintOperation::Result::APPLY)
{
//Update PrintSettings with the ones used in this PrintOperation:
m_refSettings = m_refPrintFormOperation->get_print_settings();
}
if (!m_refPrintFormOperation->is_finished())
{
//We will connect to the status-changed signal to track status
//and update a status bar. In addition, you can, for example,
//keep a list of active print operations, or provide a progress dialog.
m_refPrintFormOperation->signal_status_changed().connect(sigc::mem_fun(*this,
&ExampleWindow::on_printoperation_status_changed));
}
}
void ExampleWindow::print_or_preview(Gtk::PrintOperation::Action print_action)
{
//Create a new PrintOperation with our PageSetup and PrintSettings:
//(We use our derived PrintOperation class)
m_refPrintFormOperation = PrintFormOperation::create();
m_refPrintFormOperation->set_name(m_NameEntry.get_text() + " " + m_SurnameEntry.get_text());
m_refPrintFormOperation->set_comments(m_refTextBuffer->get_text(false /*Don't include hidden*/));
// In the printing/advanced example, the font will be set through a custom tab
// in the print dialog.
m_refPrintFormOperation->set_track_print_status();
m_refPrintFormOperation->set_default_page_setup(m_refPageSetup);
m_refPrintFormOperation->set_print_settings(m_refSettings);
m_refPrintFormOperation->signal_done().connect(sigc::mem_fun(*this,
&ExampleWindow::on_printoperation_done));
try
{
m_refPrintFormOperation->run(print_action /* print or preview */, *this);
}
catch (const Gtk::PrintError& ex)
{
//See documentation for exact Gtk::PrintError error codes.
std::cerr << "An error occurred while trying to run a print operation:"
<< ex.what() << std::endl;
}
}
void ExampleWindow::on_menu_file_new()
{
//Clear entries and textview:
m_NameEntry.set_text("");
m_SurnameEntry.set_text("");
m_refTextBuffer->set_text("");
m_TextView.set_buffer(m_refTextBuffer);
}
void ExampleWindow::on_menu_file_page_setup()
{
//Show the page setup dialog, asking it to start with the existing settings:
auto new_page_setup =
Gtk::run_page_setup_dialog(*this, m_refPageSetup, m_refSettings);
//Save the chosen page setup dialog for use when printing, previewing, or
//showing the page setup dialog again:
m_refPageSetup = new_page_setup;
}
void ExampleWindow::on_menu_file_print_preview()
{
print_or_preview(Gtk::PrintOperation::Action::PREVIEW);
}
void ExampleWindow::on_menu_file_print()
{
print_or_preview(Gtk::PrintOperation::Action::PRINT_DIALOG);
}
void ExampleWindow::on_menu_file_quit()
{
set_visible(false);
}
File: main.cc
(For use with gtkmm 4)
#include "examplewindow.h"
#include <gtkmm/application.h>
int main(int argc, char* argv[])
{
auto app = Gtk::Application::create("org.gtkmm.example");
// Shows the window and returns when it is closed.
return app->make_window_and_run<ExampleWindow>(argc, argv, app);
}
File: printformoperation.cc
(For use with gtkmm 4)
#include "printformoperation.h"
PrintFormOperation::PrintFormOperation()
{
}
PrintFormOperation::~PrintFormOperation()
{
}
Glib::RefPtr<PrintFormOperation> PrintFormOperation::create()
{
return Glib::make_refptr_for_instance<PrintFormOperation>(new PrintFormOperation());
}
void PrintFormOperation::on_begin_print(
const Glib::RefPtr<Gtk::PrintContext>& print_context)
{
//Create and set up a Pango layout for PrintData based on the passed
//PrintContext: We then use this to calculate the number of pages needed, and
//the lines that are on each page.
m_refLayout = print_context->create_pango_layout();
Pango::FontDescription font_desc("sans 12");
m_refLayout->set_font_description(font_desc);
const double width = print_context->get_width();
const double height = print_context->get_height();
m_refLayout->set_width(static_cast<int>(width * Pango::SCALE));
//Set and mark up the text to print:
Glib::ustring marked_up_form_text;
marked_up_form_text += "<b>Name</b>: " + m_Name + "\n\n";
marked_up_form_text += "<b>Comments</b>: " + m_Comments;
m_refLayout->set_markup(marked_up_form_text);
//Set the number of pages to print by determining the line numbers
//where page breaks occur:
const int line_count = m_refLayout->get_line_count();
Glib::RefPtr<Pango::LayoutLine> layout_line;
double page_height = 0;
for (int line = 0; line < line_count; ++line)
{
Pango::Rectangle ink_rect, logical_rect;
layout_line = m_refLayout->get_line(line);
layout_line->get_extents(ink_rect, logical_rect);
const double line_height = logical_rect.get_height() / 1024.0;
if (page_height + line_height > height)
{
m_PageBreaks.push_back(line);
page_height = 0;
}
page_height += line_height;
}
set_n_pages(m_PageBreaks.size() + 1);
}
void PrintFormOperation::on_draw_page(
const Glib::RefPtr<Gtk::PrintContext>& print_context, int page_nr)
{
//Decide which lines we need to print in order to print the specified page:
int start_page_line = 0;
int end_page_line = 0;
if(page_nr == 0)
{
start_page_line = 0;
}
else
{
start_page_line = m_PageBreaks[page_nr - 1];
}
if(page_nr < static_cast<int>(m_PageBreaks.size()))
{
end_page_line = m_PageBreaks[page_nr];
}
else
{
end_page_line = m_refLayout->get_line_count();
}
//Get a Cairo Context, which is used as a drawing board:
Cairo::RefPtr<Cairo::Context> cairo_ctx = print_context->get_cairo_context();
//We'll use black letters:
cairo_ctx->set_source_rgb(0, 0, 0);
//Render Pango LayoutLines over the Cairo context:
auto iter = m_refLayout->get_iter();
double start_pos = 0;
int line_index = 0;
do
{
if(line_index >= start_page_line)
{
auto layout_line = iter.get_line();
auto logical_rect = iter.get_line_logical_extents();
int baseline = iter.get_baseline();
if (line_index == start_page_line)
{
start_pos = logical_rect.get_y() / 1024.0;
}
cairo_ctx->move_to(logical_rect.get_x() / 1024.0,
baseline / 1024.0 - start_pos);
layout_line->show_in_cairo_context(cairo_ctx);
}
line_index++;
}
while(line_index < end_page_line && iter.next_line());
}
The following example demonstrates how to print some input from a user
interface using PrintDialog
. The user interface is
similar to the previous example.
File: examplewindow.h
(For use with gtkmm 4)
#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H
#include <gtkmm.h>
class PrintFormDialog;
class ExampleWindow : public Gtk::Window
{
public:
ExampleWindow(const Glib::RefPtr<Gtk::Application>& app);
~ExampleWindow() override;
protected:
void build_main_menu(const Glib::RefPtr<Gtk::Application>& app);
// Action signal handlers:
void on_menu_file_new();
void on_menu_file_print_setup();
void on_menu_file_print();
void on_menu_file_quit();
// Printing-related objects:
Glib::RefPtr<PrintFormDialog> m_refPrintFormDialog;
// Child widgets:
Gtk::Box m_VBox;
Gtk::Grid m_Grid;
Gtk::Label m_NameLabel;
Gtk::Entry m_NameEntry;
Gtk::Label m_SurnameLabel;
Gtk::Entry m_SurnameEntry;
Gtk::Label m_CommentsLabel;
Gtk::ScrolledWindow m_ScrolledWindow;
Gtk::TextView m_TextView;
Glib::RefPtr<Gtk::TextBuffer> m_refTextBuffer;
Glib::RefPtr<Gtk::Builder> m_refBuilder;
};
#endif //GTKMM_EXAMPLEWINDOW_H
File: printformdialog.h
(For use with gtkmm 4)
#ifndef GTKMM_PRINT_FORM_DIALOG_H
#define GTKMM_PRINT_FORM_DIALOG_H
#include <gtkmm.h>
#include <pangomm.h>
#include <vector>
#define HAS_PRINT_DIALOG GTKMM_CHECK_VERSION(4,13,1)
#if HAS_PRINT_DIALOG
// We derive our own class from PrintDialog,
// so we can put the actual print implementation here.
class PrintFormDialog : public Gtk::PrintDialog
{
public:
static Glib::RefPtr<PrintFormDialog> create();
virtual ~PrintFormDialog();
void set_name(const Glib::ustring& name) { m_Name = name; }
void set_comments(const Glib::ustring& comments) { m_Comments = comments; }
void do_setup(Gtk::Window* parent);
void do_print(Gtk::Window* parent);
protected:
PrintFormDialog();
// PrintDialog callback slots:
void on_setup_finish(const Glib::RefPtr<Gio::AsyncResult>& result);
void on_print_finish(const Glib::RefPtr<Gio::AsyncResult>& result);
Cairo::ErrorStatus write_func(const unsigned char* data, unsigned int length,
Glib::RefPtr<Gio::OutputStream>& stream);
void do_printing(const Cairo::RefPtr<Cairo::Context>& cairo_ctx);
Glib::ustring m_Name;
Glib::ustring m_Comments;
Glib::RefPtr<Pango::Layout> m_refLayout;
Glib::RefPtr<Gtk::PrintSetup> m_refPrintSetup;
};
#endif // HAS_PRINT_DIALOG
#endif // GTKMM_PRINT_FORM_DIALOG_H
File: examplewindow.cc
(For use with gtkmm 4)
#include "examplewindow.h"
#include "printformdialog.h"
#include <iostream>
ExampleWindow::ExampleWindow(const Glib::RefPtr<Gtk::Application>& app)
: m_VBox(Gtk::Orientation::VERTICAL),
m_NameLabel("Name"),
m_SurnameLabel("Surname"),
m_CommentsLabel("Comments")
{
set_title("gtkmm PrintDialog Example");
set_default_size(400, 300);
#if HAS_PRINT_DIALOG
// Create a PrintDialog. (We use our derived PrintDialog class.)
m_refPrintFormDialog = PrintFormDialog::create();
#endif // HAS_PRINT_DIALOG
set_child(m_VBox);
build_main_menu(app);
m_VBox.append(m_Grid);
// Arrange the widgets inside the grid:
m_Grid.set_expand(true);
m_Grid.set_row_spacing(5);
m_Grid.set_column_spacing(5);
m_Grid.attach(m_NameLabel, 0, 0);
m_Grid.attach(m_NameEntry, 1, 0);
m_Grid.attach(m_SurnameLabel, 0, 1);
m_Grid.attach(m_SurnameEntry, 1, 1);
// Add the TextView, inside a ScrolledWindow:
m_ScrolledWindow.set_child(m_TextView);
// Only show the scrollbars when they are necessary:
m_ScrolledWindow.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
m_Grid.attach(m_CommentsLabel, 0, 2);
m_Grid.attach(m_ScrolledWindow, 1, 2);
m_ScrolledWindow.set_expand(true);
m_refTextBuffer = Gtk::TextBuffer::create();
m_TextView.set_buffer(m_refTextBuffer);
}
ExampleWindow::~ExampleWindow()
{
}
void ExampleWindow::build_main_menu(const Glib::RefPtr<Gtk::Application>& app)
{
// Create actions for menus and toolbars:
auto refActionGroup = Gio::SimpleActionGroup::create();
// File menu:
refActionGroup->add_action("new",
sigc::mem_fun(*this, &ExampleWindow::on_menu_file_new));
refActionGroup->add_action("printsetup",
sigc::mem_fun(*this, &ExampleWindow::on_menu_file_print_setup));
refActionGroup->add_action("print",
sigc::mem_fun(*this, &ExampleWindow::on_menu_file_print));
refActionGroup->add_action("quit",
sigc::mem_fun(*this, &ExampleWindow::on_menu_file_quit));
insert_action_group("example", refActionGroup);
// When the menubar is a child of a Gtk::Window, keyboard accelerators are not
// automatically fetched from the Gio::Menu.
// See the examples/book/menus/main_menu example for an alternative way of
// adding the menubar when using Gtk::ApplicationWindow.
app->set_accel_for_action("example.new", "<Primary>n");
app->set_accel_for_action("example.print", "<Primary>p");
app->set_accel_for_action("example.quit", "<Primary>q");
m_refBuilder = Gtk::Builder::create();
// Layout the actions in a menubar:
Glib::ustring ui_menu_info =
"<interface>"
" <menu id='menu-example'>"
" <submenu>"
" <attribute name='label' translatable='yes'>_File</attribute>"
" <section>"
" <item>"
" <attribute name='label' translatable='yes'>_New</attribute>"
" <attribute name='action'>example.new</attribute>"
" </item>"
" </section>"
" <section>"
" <item>"
" <attribute name='label' translatable='yes'>Print _Setup...</attribute>"
" <attribute name='action'>example.printsetup</attribute>"
" </item>"
" <item>"
" <attribute name='label' translatable='yes'>_Print...</attribute>"
" <attribute name='action'>example.print</attribute>"
" </item>"
" </section>"
" <section>"
" <item>"
" <attribute name='label' translatable='yes'>_Quit</attribute>"
" <attribute name='action'>example.quit</attribute>"
" </item>"
" </section>"
" </submenu>"
" </menu>"
"</interface>";
try
{
m_refBuilder->add_from_string(ui_menu_info);
}
catch(const Glib::Error& ex)
{
std::cerr << "building menus failed: " << ex.what();
}
// Layout the actions in a toolbar:
Glib::ustring ui_toolbar_info =
"<!-- Generated with glade 3.18.3 and then changed manually -->"
"<interface>"
"<object class='GtkBox' id='toolbar'>"
"<property name='can_focus'>False</property>"
"<property name='spacing'>3</property>"
"<child>"
"<object class='GtkButton' id='toolbutton_new'>"
"<property name='can_focus'>False</property>"
"<property name='tooltip_text' translatable='yes'>New</property>"
"<property name='action_name'>example.new</property>"
"<property name='icon_name'>document-new</property>"
"<property name='hexpand'>False</property>"
"<property name='vexpand'>False</property>"
"</object>"
"</child>"
"<child>"
"<object class='GtkButton' id='toolbutton_print'>"
"<property name='can_focus'>False</property>"
"<property name='tooltip_text' translatable='yes'>Print</property>"
"<property name='action_name'>example.print</property>"
"<property name='icon_name'>document-print</property>"
"<property name='hexpand'>False</property>"
"<property name='vexpand'>False</property>"
"</object>"
"</child>"
"<child>"
"<object class='GtkSeparator' id='separator1'>"
"<property name='can_focus'>False</property>"
"<property name='hexpand'>False</property>"
"<property name='vexpand'>False</property>"
"</object>"
"</child>"
"<child>"
"<object class='GtkButton' id='toolbutton_quit'>"
"<property name='can_focus'>False</property>"
"<property name='tooltip_text' translatable='yes'>Quit</property>"
"<property name='action_name'>example.quit</property>"
"<property name='icon_name'>application-exit</property>"
"<property name='hexpand'>False</property>"
"<property name='vexpand'>False</property>"
"</object>"
"</child>"
"</object>"
"</interface>";
try
{
m_refBuilder->add_from_string(ui_toolbar_info);
}
catch(const Glib::Error& ex)
{
std::cerr << "building toolbar failed: " << ex.what();
}
// Get the menubar and add it to a container widget:
auto object = m_refBuilder->get_object("menu-example");
auto gmenu = std::dynamic_pointer_cast<Gio::Menu>(object);
if (!gmenu)
g_warning("GMenu not found");
else
{
auto pMenuBar = Gtk::make_managed<Gtk::PopoverMenuBar>(gmenu);
// Add the PopoverMenuBar to the window:
m_VBox.append(*pMenuBar);
}
// Get the toolbar and add it to a container widget:
auto toolbar = m_refBuilder->get_widget<Gtk::Box>("toolbar");
if (!toolbar)
g_warning("toolbar not found");
else
m_VBox.append(*toolbar);
}
void ExampleWindow::on_menu_file_new()
{
// Clear entries and textview:
m_NameEntry.set_text("");
m_SurnameEntry.set_text("");
m_refTextBuffer->set_text("");
m_TextView.set_buffer(m_refTextBuffer);
}
void ExampleWindow::on_menu_file_print_setup()
{
#if HAS_PRINT_DIALOG
m_refPrintFormDialog->do_setup(this);
#else
std::cout << "Gtk::PrintDialog is not available.\n";
#endif // HAS_PRINT_DIALOG
}
void ExampleWindow::on_menu_file_print()
{
#if HAS_PRINT_DIALOG
m_refPrintFormDialog->set_name(m_NameEntry.get_text() + " " + m_SurnameEntry.get_text());
m_refPrintFormDialog->set_comments(m_refTextBuffer->get_text(false /*Don't include hidden*/));
m_refPrintFormDialog->do_print(this);
#else
std::cout << "Gtk::PrintDialog is not available.\n";
#endif // HAS_PRINT_DIALOG
}
void ExampleWindow::on_menu_file_quit()
{
set_visible(false);
}
File: main.cc
(For use with gtkmm 4)
#include "examplewindow.h"
#include <gtkmm/application.h>
int main(int argc, char* argv[])
{
auto app = Gtk::Application::create("org.gtkmm.example");
// Shows the window and returns when it is closed.
return app->make_window_and_run<ExampleWindow>(argc, argv, app);
}
File: printformdialog.cc
(For use with gtkmm 4)
#include "printformdialog.h"
#include <iostream>
#if HAS_PRINT_DIALOG
PrintFormDialog::PrintFormDialog()
{
set_page_setup(Gtk::PageSetup::create());
set_print_settings(Gtk::PrintSettings::create());
}
PrintFormDialog::~PrintFormDialog()
{
}
Glib::RefPtr<PrintFormDialog> PrintFormDialog::create()
{
return Glib::make_refptr_for_instance<PrintFormDialog>(new PrintFormDialog());
}
void PrintFormDialog::do_setup(Gtk::Window* parent)
{
set_title("Print setup");
if (parent)
setup(*parent, sigc::mem_fun(*this, &PrintFormDialog::on_setup_finish));
else
setup(sigc::mem_fun(*this, &PrintFormDialog::on_setup_finish));
}
void PrintFormDialog::do_print(Gtk::Window* parent)
{
set_title("Printing");
if (parent)
print(*parent, sigc::mem_fun(*this, &PrintFormDialog::on_print_finish));
else
print(sigc::mem_fun(*this, &PrintFormDialog::on_print_finish));
}
void PrintFormDialog::on_setup_finish(const Glib::RefPtr<Gio::AsyncResult>& result)
{
try
{
m_refPrintSetup = setup_finish(result);
if (m_refPrintSetup)
{
set_page_setup(m_refPrintSetup->get_page_setup());
set_print_settings(m_refPrintSetup->get_print_settings());
}
}
catch (const Gtk::DialogError& err)
{
// Can be thrown by setup_finish(result).
std::cout << "No setup selected. " << err.what() << std::endl;
}
catch (const Glib::Error& err)
{
std::cout << "Unexpected exception. " << err.what() << std::endl;
}
}
void PrintFormDialog::on_print_finish(const Glib::RefPtr<Gio::AsyncResult>& result)
{
Glib::RefPtr<Gio::OutputStream> outputStream;
try
{
outputStream = print_finish(result);
}
catch (const Gtk::DialogError& err)
{
// Can be thrown by print_finish(result).
std::cout << "No output stream. " << err.what() << std::endl;
return;
}
catch (const Glib::Error& err)
{
std::cout << "Unexpected exception. " << err.what() << std::endl;
return;
}
const double paper_width = get_page_setup()->get_paper_width(Gtk::Unit::POINTS);
const double paper_height = get_page_setup()->get_paper_height(Gtk::Unit::POINTS);
auto cairo_surface = Cairo::PdfSurface::create_for_stream(
sigc::bind(sigc::mem_fun(*this, &PrintFormDialog::write_func), outputStream),
paper_width, paper_height);
auto cairo_ctx = Cairo::Context::create(cairo_surface);
do_printing(cairo_ctx);
// cairo_surface->finish() calls write_func(),
// which must be done before the stream is closed.
cairo_surface->finish();
try
{
outputStream->close();
}
catch (const Glib::Error& err)
{
std::cout << "Error closing stream. " << err.what() << std::endl;
}
}
Cairo::ErrorStatus PrintFormDialog::write_func(const unsigned char* data,
unsigned int length, Glib::RefPtr<Gio::OutputStream>& stream)
{
try
{
gsize bytes_written = 0;
stream->write_all(data, length, bytes_written);
}
catch (const Glib::Error& err)
{
std::cout << "Error writing to stream. " << err.what() << std::endl;
return CAIRO_STATUS_WRITE_ERROR;
}
return CAIRO_STATUS_SUCCESS;
}
void PrintFormDialog::do_printing(const Cairo::RefPtr<Cairo::Context>& cairo_ctx)
{
m_refLayout = Pango::Layout::create(cairo_ctx);
m_refLayout->set_font_description(Pango::FontDescription("sans 12"));
const auto page_setup = get_page_setup();
const double page_width = page_setup->get_page_width(Gtk::Unit::POINTS);
const double page_height = page_setup->get_page_height(Gtk::Unit::POINTS);
const double left_margin = page_setup->get_left_margin(Gtk::Unit::POINTS);
const double top_margin = page_setup->get_top_margin(Gtk::Unit::POINTS);
m_refLayout->set_width(static_cast<int>(page_width * Pango::SCALE));
m_refLayout->set_wrap(Pango::WrapMode::WORD_CHAR);
cairo_ctx->translate(left_margin, top_margin);
// Set and mark up the text to print:
Glib::ustring marked_up_form_text =
"<b>Name</b>: " + m_Name + "\n\n" +
"<b>Comments</b>: " + m_Comments;
m_refLayout->set_markup(marked_up_form_text);
// We'll use black letters:
cairo_ctx->set_source_rgb(0, 0, 0);
// Render Pango LayoutLines over the Cairo context:
const double inverse_pango_scale = 1.0 / Pango::SCALE;
double page_y = 0.0;
auto iter = m_refLayout->get_iter();
double start_y_pos = iter.get_line_logical_extents().get_y() * inverse_pango_scale;
do
{
const auto layout_line = iter.get_line();
const auto logical_rect = iter.get_line_logical_extents();
const double line_height = logical_rect.get_height() * inverse_pango_scale;
if (page_y + line_height > page_height)
{
// Start a new page.
cairo_ctx->show_page();
page_y = 0.0;
start_y_pos = logical_rect.get_y() * inverse_pango_scale;
}
page_y += line_height;
cairo_ctx->move_to(logical_rect.get_x() * inverse_pango_scale,
iter.get_baseline() * inverse_pango_scale - start_y_pos);
layout_line->show_in_cairo_context(cairo_ctx);
}
while (iter.next_line());
cairo_ctx->show_page();
}
#endif // HAS_PRINT_DIALOG