Example
- 20.8.1. Simple
20.8.1. Simple
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: examplewindow.h (For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EXAMPLEWINDOW_H #define GTKMM_EXAMPLEWINDOW_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::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; unsigned m_ContextId; Gtk::Statusbar m_Statusbar; Glib::RefPtr<Gtk::Builder> m_refBuilder; }; #endif //GTKMM_EXAMPLEWINDOW_H
File: printformoperation.h (For use with gtkmm 3, not gtkmm 2)
#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: 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.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_VBox(Gtk::ORIENTATION_VERTICAL), 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_Grid); //Arrange the widgets inside the grid: m_Grid.set_row_spacing(5); m_Grid.set_column_spacing(5); m_Grid.attach(m_NameLabel, 0, 0, 1, 1); m_Grid.attach(m_NameEntry, 1, 0, 1, 1); m_Grid.attach(m_SurnameLabel, 0, 1, 1, 1); m_Grid.attach(m_SurnameEntry, 1, 1, 1, 1); //Add the TextView, 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_Grid.attach(m_CommentsLabel, 0, 2, 1, 1); m_Grid.attach(m_ScrolledWindow, 1, 2, 1, 1); m_ScrolledWindow.set_hexpand(true); m_ScrolledWindow.set_vexpand(true); 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: Glib::RefPtr<Gio::SimpleActionGroup> 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); m_refBuilder = Gtk::Builder::create(); //TODO: add_accel_group(m_refBuilder->get_accel_group()); //Layout the actions in a menubar and toolbar: Glib::ustring ui_info = "<interface>" " <menu id='menu-example'>" " <submenu>" " <attribute name='label' translatable='yes'>_File</attribute>" " <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>" " </submenu>" " </menu>" /* TODO: " <toolbar name='ToolBar'>" " <toolitem action='New'/>" " <toolitem action='Print'/>" " <separator/>" " <toolitem action='Quit'/>" " </toolbar>" */ "</interface>"; try { m_refBuilder->add_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: Glib::RefPtr<Glib::Object> object = m_refBuilder->get_object("menu-example"); Glib::RefPtr<Gio::Menu> gmenu = Glib::RefPtr<Gio::Menu>::cast_dynamic(object); if(!gmenu) g_warning("GMenu not found"); Gtk::MenuBar* pMenubar = new Gtk::MenuBar(gmenu); m_VBox.pack_start(*pMenubar, Gtk::PACK_SHRINK); /* TODO: Gtk::Widget* pToolbar = m_refBuilder->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/application.h> int main(int argc, char *argv[]) { Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example"); ExampleWindow window; //Shows the window and returns when it is closed. return app->run(window); }
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(); 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()); }