Example
| Chapter 18. Printing |

## Example

### 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.

Figure 18.1. Printing - Simple

Source Code

File: `printformoperation.h`

    #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`

    #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:

      virtual void build_main_menu();

      virtual 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.
      virtual void on_printoperation_status_changed(const Glib::RefPtr<PrintFormOperation>& operation);

      virtual void on_printoperation_done(Gtk::PrintOperationResult result, const Glib::RefPtr<PrintFormOperation>& operation);

      //Action signal handlers:
      virtual void on_menu_file_new();
      virtual void on_menu_file_page_setup();
      virtual void on_menu_file_print_preview();
      virtual void on_menu_file_print();
      virtual 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: `main.cc`

    #include <gtkmm/main.h>
    #include "examplewindow.h"

    int main(int argc, char *argv[])
    {
      Gtk::Main kit(argc, argv);

      ExampleWindow window;
      Gtk::Main::run(window); //Shows the window and returns when it is closed.

      return 0;
    }

File: `examplewindow.cc`

    #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>";

      #ifdef GLIBMM_EXCEPTIONS_ENABLED
      try
      {
        m_refUIManager->add_ui_from_string(ui_info);
      }
      catch(const Glib::Error& ex)
      {
        std::cerr << "building menus failed: " << ex.what();
      }
      #else
      std::auto_ptr<Glib::Error> ex;
      m_refUIManager->add_ui_from_string(ui_info, ex);
      if(ex.get())
      {
        std::cerr << "building menus failed: " << ex->what();
      }
      #endif //GLIBMM_EXCEPTIONS_ENABLED

      //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: `printformoperation.cc`

    #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());
    }

Preview |  | Chapter 19. Recently Used Documents