The following example demonstrates how to print some input from a user
interface. 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: printformoperation.h (For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_PRINT_FORM_OPERATION_H
#define GTKMM_PRINT_FORM_OPERATION_H
#include <pangomm.h>
#include <gtkmm.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:
virtual void on_begin_print(const Glib::RefPtr<Gtk::PrintContext>& context);
virtual void on_draw_page(const Glib::RefPtr<Gtk::PrintContext>& context, int page_nr);
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.h (For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H
#include <memory>
#include <vector>
#include <pangomm.h>
#include <gtkmm.h>
class PrintFormOperation;
class ExampleWindow : public Gtk::Window
{
public:
ExampleWindow();
virtual ~ExampleWindow();
protected:
void build_main_menu();
void print_or_preview(Gtk::PrintOperationAction 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(const Glib::RefPtr<PrintFormOperation>& operation);
void on_printoperation_done(Gtk::PrintOperationResult result, const Glib::RefPtr<PrintFormOperation>& operation);
//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;
//Child widgets:
Gtk::VBox m_VBox;
Gtk::Table m_Table;
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;
unsigned m_ContextId;
Gtk::Statusbar m_Statusbar;
Glib::RefPtr<Gtk::UIManager> m_refUIManager;
Glib::RefPtr<Gtk::ActionGroup> m_refActionGroup;
};
#endif //GTKMM_EXAMPLEWINDOW_H
File: printformoperation.cc (For use with gtkmm 3, not gtkmm 2)
#include "printformoperation.h"
PrintFormOperation::PrintFormOperation()
{
}
PrintFormOperation::~PrintFormOperation()
{
}
Glib::RefPtr<PrintFormOperation> PrintFormOperation::create()
{
return Glib::RefPtr<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:
Pango::LayoutIter iter;
m_refLayout->get_iter(iter);
double start_pos = 0;
int line_index = 0;
do
{
if (line_index >= start_page_line)
{
Glib::RefPtr<Pango::LayoutLine> layout_line = iter.get_line();
Pango::Rectangle 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());
}
File: examplewindow.cc (For use with gtkmm 3, not gtkmm 2)
#include "examplewindow.h"
#include "printformoperation.h"
#include <iostream>
#include <pangomm.h>
const Glib::ustring app_title = "gtkmm Printing Example";
ExampleWindow::ExampleWindow()
:
m_Table(3, 2),
m_NameLabel("Name"),
m_SurnameLabel("Surname"),
m_CommentsLabel("Comments")
{
m_refPageSetup = Gtk::PageSetup::create();
m_refSettings = Gtk::PrintSettings::create();
m_ContextId = m_Statusbar.get_context_id(app_title);
set_title(app_title);
set_default_size(400, 300);
add(m_VBox);
build_main_menu();
m_VBox.pack_start(m_Table);
//Arrange the widgets inside the table:
m_Table.attach(m_NameLabel, 0, 1, 0, 1);
m_Table.attach(m_NameEntry, 1, 2, 0, 1);
m_Table.attach(m_SurnameLabel, 0, 1, 1, 2, Gtk::SHRINK);
m_Table.attach(m_SurnameEntry, 1, 2, 1, 2);
//Add the TreeView, inside a ScrolledWindow:
m_ScrolledWindow.add(m_TextView);
//Only show the scrollbars when they are necessary:
m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
m_Table.attach(m_CommentsLabel, 0, 1, 2, 3, Gtk::SHRINK);
m_Table.attach(m_ScrolledWindow, 1, 2, 2, 3);
m_refTextBuffer = Gtk::TextBuffer::create();
m_TextView.set_buffer(m_refTextBuffer);
m_VBox.pack_start(m_Statusbar);
show_all_children();
}
ExampleWindow::~ExampleWindow()
{
}
void ExampleWindow::build_main_menu()
{
//Create actions for menus and toolbars:
m_refActionGroup = Gtk::ActionGroup::create();
//File menu:
m_refActionGroup->add(
Gtk::Action::create("FileMenu", "_File"));
m_refActionGroup->add(
Gtk::Action::create("New", Gtk::Stock::NEW),
sigc::mem_fun(*this, &ExampleWindow::on_menu_file_new));
m_refActionGroup->add(
Gtk::Action::create("PageSetup", "Page _Setup"),
sigc::mem_fun(*this, &ExampleWindow::on_menu_file_page_setup));
m_refActionGroup->add(
Gtk::Action::create("PrintPreview", "Print Preview"),
sigc::mem_fun(*this, &ExampleWindow::on_menu_file_print_preview));
m_refActionGroup->add(
Gtk::Action::create("Print", Gtk::Stock::PRINT),
sigc::mem_fun(*this, &ExampleWindow::on_menu_file_print));
m_refActionGroup->add(
Gtk::Action::create("Quit", Gtk::Stock::QUIT),
sigc::mem_fun(*this, &ExampleWindow::on_menu_file_quit));
m_refUIManager = Gtk::UIManager::create();
m_refUIManager->insert_action_group(m_refActionGroup);
add_accel_group(m_refUIManager->get_accel_group());
//Layout the actions in a menubar and toolbar:
Glib::ustring ui_info =
"<ui>"
" <menubar name='MenuBar'>"
" <menu action='FileMenu'>"
" <menuitem action='New'/>"
" <menuitem action='PageSetup'/>"
" <menuitem action='PrintPreview'/>"
" <menuitem action='Print'/>"
" <separator/>"
" <menuitem action='Quit'/>"
" </menu>"
" </menubar>"
" <toolbar name='ToolBar'>"
" <toolitem action='New'/>"
" <toolitem action='Print'/>"
" <separator/>"
" <toolitem action='Quit'/>"
" </toolbar>"
"</ui>";
try
{
m_refUIManager->add_ui_from_string(ui_info);
}
catch(const Glib::Error& ex)
{
std::cerr << "building menus failed: " << ex.what();
}
//Get the menubar and toolbar widgets, and add them to a container widget:
Gtk::Widget* pMenubar = m_refUIManager->get_widget("/MenuBar");
if(pMenubar)
m_VBox.pack_start(*pMenubar, Gtk::PACK_SHRINK);
Gtk::Widget* pToolbar = m_refUIManager->get_widget("/ToolBar") ;
if(pToolbar)
m_VBox.pack_start(*pToolbar, Gtk::PACK_SHRINK);
}
void ExampleWindow::on_printoperation_status_changed(
const Glib::RefPtr<PrintFormOperation>& operation)
{
Glib::ustring status_msg;
if (operation->is_finished())
{
status_msg = "Print job completed.";
}
else
{
//You could also use get_status().
status_msg = operation->get_status_string();
}
m_Statusbar.push(status_msg, m_ContextId);
}
void ExampleWindow::on_printoperation_done(Gtk::PrintOperationResult result,
const Glib::RefPtr<PrintFormOperation>& operation)
{
//Printing is "done" when the print data is spooled.
if (result == Gtk::PRINT_OPERATION_RESULT_ERROR)
{
Gtk::MessageDialog err_dialog(*this, "Error printing form", false,
Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
err_dialog.run();
}
else if (result == Gtk::PRINT_OPERATION_RESULT_APPLY)
{
//Update PrintSettings with the ones used in this PrintOperation:
m_refSettings = operation->get_print_settings();
}
if (! operation->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.
operation->signal_status_changed().connect(sigc::bind(sigc::mem_fun(*this,
&ExampleWindow::on_printoperation_status_changed),
operation));
}
}
void ExampleWindow::print_or_preview(Gtk::PrintOperationAction print_action)
{
//Create a new PrintOperation with our PageSetup and PrintSettings:
//(We use our derived PrintOperation class)
Glib::RefPtr<PrintFormOperation> print = PrintFormOperation::create();
print->set_name(m_NameEntry.get_text() + " " + m_SurnameEntry.get_text());
print->set_comments(m_refTextBuffer->get_text(false /*Don't include hidden*/));
print->set_track_print_status();
print->set_default_page_setup(m_refPageSetup);
print->set_print_settings(m_refSettings);
print->signal_done().connect(sigc::bind(sigc::mem_fun(*this,
&ExampleWindow::on_printoperation_done), print));
try
{
print->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:
Glib::RefPtr<Gtk::PageSetup> 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::PRINT_OPERATION_ACTION_PREVIEW);
}
void ExampleWindow::on_menu_file_print()
{
print_or_preview(Gtk::PRINT_OPERATION_ACTION_PRINT_DIALOG);
}
void ExampleWindow::on_menu_file_quit()
{
hide();
}
File: main.cc (For use with gtkmm 3, not gtkmm 2)
#include "examplewindow.h"
#include <gtkmm/main.h>
int main(int argc, char *argv[])
{
Gtk::Main kit(argc, argv);
ExampleWindow window;
//Shows the window and returns when it is closed.
Gtk::Main::run(window);
return 0;
}