commit a715d99def2bc308a5ed6095e2f02e4a3c23d55d
Author: Parin Porecha <parinporecha@gmail.com>
Date:   Thu Nov 21 16:29:50 2013 -0900

    Implemented printing multiple sudokus feature in vala-port
    
    As saving a sudoku has not been ported yet,
    the corresponding options have been disabled in print dialog for now.

diff --git a/data/print-games.ui b/data/print-games.ui
new file mode 100644
index 0000000..09e8a74
--- /dev/null
+++ b/data/print-games.ui
@@ -0,0 +1,367 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy toplevel-contextual -->
+  <object class="GtkAdjustment" id="adjustment1">
+    <property name="lower">1</property>
+    <property name="upper">100</property>
+    <property name="value">4</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment2">
+    <property name="lower">1</property>
+    <property name="upper">9</property>
+    <property name="value">1</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkDialog" id="dialog">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="title" translatable="yes">Print Sudokus</property>
+    <property name="type_hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="cancelbutton1">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="okbutton1">
+                <property name="label">gtk-print</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVBox" id="vbox1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="border_width">12</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Print Games</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                  <attribute name="scale" value="1.2"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkAlignment" id="alignment2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="left_padding">24</property>
+                <child>
+                  <object class="GtkTable" id="table1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="n_rows">2</property>
+                    <property name="n_columns">2</property>
+                    <child>
+                      <object class="GtkLabel" id="label3">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">_Number of sudoku to print: </property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">sudokusToPrintSpinButton</property>
+                      </object>
+                      <packing>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options"/>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label2">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">_Sudokus per page: </property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">sudokusPerPageSpinButton</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options"/>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSpinButton" id="sudokusToPrintSpinButton">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="primary_icon_activatable">False</property>
+                        <property name="secondary_icon_activatable">False</property>
+                        <property name="primary_icon_sensitive">True</property>
+                        <property name="secondary_icon_sensitive">True</property>
+                        <property name="adjustment">adjustment1</property>
+                        <property name="climb_rate">1</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="y_options"/>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSpinButton" id="sudokusPerPageSpinButton">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="primary_icon_activatable">False</property>
+                        <property name="secondary_icon_activatable">False</property>
+                        <property name="primary_icon_sensitive">True</property>
+                        <property name="secondary_icon_sensitive">True</property>
+                        <property name="adjustment">adjustment2</property>
+                        <property name="climb_rate">1</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                        <property name="y_options"/>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label4">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Levels of difficulty to print</property>
+                <property name="wrap">True</property>
+                <attributes>
+                  <attribute name="style" value="italic"/>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkAlignment" id="alignment1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="left_padding">24</property>
+                <child>
+                  <object class="GtkVBox" id="vbox2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <child>
+                      <object class="GtkCheckButton" id="easyCheckButton">
+                        <property name="label" translatable="yes">_Easy</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton" id="mediumCheckButton">
+                        <property name="label" translatable="yes">_Medium</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton" id="hardCheckButton">
+                        <property name="label" translatable="yes">_Hard</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton" id="very_hardCheckButton">
+                        <property name="label" translatable="yes">_Very Hard</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">3</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label6">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Details</property>
+                <property name="wrap">True</property>
+                <attributes>
+                  <attribute name="style" value="italic"/>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">4</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkAlignment" id="alignment3">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="left_padding">24</property>
+                <child>
+                  <object class="GtkVBox" id="vbox3">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <child>
+                      <object class="GtkCheckButton" id="markAsPlayedToggle">
+                        <property name="label" translatable="yes">_Mark games as played once you've printed them.</property>
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton" id="includeOldGamesToggle">
+                        <property name="label" translatable="yes">_Include games you've already played in list of games to print</property>
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">5</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-6">cancelbutton1</action-widget>
+      <action-widget response="-5">okbutton1</action-widget>
+    </action-widgets>
+  </object>
+</interface>
diff --git a/src/gnome-sudoku.gresource.xml.in b/src/gnome-sudoku.gresource.xml.in
index f8c08ab..4865b71 100644
--- a/src/gnome-sudoku.gresource.xml.in
+++ b/src/gnome-sudoku.gresource.xml.in
@@ -3,6 +3,7 @@
   <gresource prefix="/org/gnome/gnome-sudoku/ui">
     <file alias="gnome-sudoku.ui" preprocess="xml-stripblanks">@top_srcdir@/data/gnome-sudoku.ui</file>
     <file alias="gnome-sudoku-menu.ui" preprocess="xml-stripblanks">@top_srcdir@/data/gnome-sudoku-menu.ui</file>
+    <file alias="print-games.ui" preprocess="xml-stripblanks">@top_srcdir@/data/print-games.ui</file>
   </gresource>
   <gresource prefix="/org/gnome/gnome-sudoku/puzzles">
     <file alias="easy">@top_srcdir@/data/easy</file>
diff --git a/src/gnome-sudoku.vala b/src/gnome-sudoku.vala
index 24b306b..26a84d8 100644
--- a/src/gnome-sudoku.vala
+++ b/src/gnome-sudoku.vala
@@ -460,14 +460,15 @@ public class Sudoku : Gtk.Application
     {
         if (game_box.visible)
         {
-            SudokuPrinter printer = new SudokuPrinter (game.board.clone (), ref window);
+            SudokuPrinter printer = new SudokuPrinter ({game.board.clone ()}, ref window);
             printer.print_sudoku ();
         }
     }
 
     public void print_multiple_cb ()
     {
-        stdout.printf ("TODO: Print multiple\n");
+        GamePrinter printer = new GamePrinter(sudoku_store, ref window);
+        printer.run_dialog();
     }
 
     public void possible_numbers_cb ()
diff --git a/src/sudoku-board.vala b/src/sudoku-board.vala
index 470595d..ce83122 100644
--- a/src/sudoku-board.vala
+++ b/src/sudoku-board.vala
@@ -61,6 +61,32 @@ public class SudokuBoard
         get { return _filled == _cols * _rows && !broken; }
     }
 
+    public double _difficulty_rating;
+    public double difficulty_rating
+    {
+        get { return _difficulty_rating; }
+        set { _difficulty_rating = value; }
+    }
+
+    private bool in_range (float[] range)
+    {
+        return (difficulty_rating >= range[0] && difficulty_rating < range[1]);
+    }
+
+    public DifficultyCatagory get_difficulty_catagory ()
+    {
+        if (in_range(DifficultyRating.EASY_RANGE))
+            return DifficultyCatagory.EASY;
+        else if (in_range(DifficultyRating.MEDIUM_RANGE))
+            return DifficultyCatagory.MEDIUM;
+        else if (in_range(DifficultyRating.HARD_RANGE))
+            return DifficultyCatagory.HARD;
+        else if (in_range(DifficultyRating.VERY_HARD_RANGE))
+            return DifficultyCatagory.VERY_HARD;
+        else
+            return DifficultyCatagory.EASY;
+    }
+
     public signal void completed ();
 
     /* The set of coordinates on the board which are invalid */
diff --git a/src/sudoku-printer.vala b/src/sudoku-printer.vala
index ecf08b4..797cbff 100644
--- a/src/sudoku-printer.vala
+++ b/src/sudoku-printer.vala
@@ -1,21 +1,21 @@
 using Gtk;
+using Gee;
 using Gdk;
 
 public class SudokuPrinter : GLib.Object {
 
-    private SudokuBoard board;
-    private DifficultyRating difficulty_rating;
+    private SudokuBoard[] boards;
     private ApplicationWindow window;
 
     private int margin;
     private int n_sudokus;
     private int sudokus_per_page;
 
-    private Gtk.PrintOperation print_op;
+    private PrintOperation print_op;
 
-    public void print_sudoku ()
+    public PrintOperationResult print_sudoku ()
     {
-        Gtk.PrintOperationResult result = print_op.run (Gtk.PrintOperationAction.PRINT_DIALOG, window);
+        PrintOperationResult result = print_op.run (Gtk.PrintOperationAction.PRINT_DIALOG, window);
         if (result == Gtk.PrintOperationResult.ERROR)
         {
             Gtk.MessageDialog error_dialog = new Gtk.MessageDialog (window, Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, "Error printing file\n");
@@ -24,15 +24,17 @@ public class SudokuPrinter : GLib.Object {
             });
             error_dialog.show ();
         }
+
+        return result;
     }
 
-    public SudokuPrinter (SudokuBoard board, ref  ApplicationWindow window)
+    public SudokuPrinter (SudokuBoard[] boards, ref  ApplicationWindow window, int sudokus_per_page = 1)
     {
-        this.board = board;
+        this.boards = boards;
         this.window = window;
         this.margin = 25;
-        this.n_sudokus = 1;
-        this.sudokus_per_page = 1;
+        this.n_sudokus = boards.length;
+        this.sudokus_per_page = sudokus_per_page;
 
         this.print_op = new Gtk.PrintOperation ();
         print_op.begin_print.connect (begin_print_cb);
@@ -41,7 +43,8 @@ public class SudokuPrinter : GLib.Object {
 
     private void begin_print_cb (Gtk.PrintOperation operation, Gtk.PrintContext context)
     {
-        operation.set_n_pages ((n_sudokus / sudokus_per_page) + ((n_sudokus % sudokus_per_page) & 1));
+        int remainder = (n_sudokus % sudokus_per_page) == 0 ? 0 : 1;
+        operation.set_n_pages ((n_sudokus / sudokus_per_page) + remainder);
     }
 
     private void draw_page_cb (Gtk.PrintOperation operation, Gtk.PrintContext context, int page_nr)
@@ -50,19 +53,53 @@ public class SudokuPrinter : GLib.Object {
         double width = context.get_width ();
         double height = context.get_height ();
 
-        double[] best_values = fit_squares_in_rectangle (width, height, sudokus_per_page, margin);
+        double[] best_values = fit_squares_in_rectangle (width, height, margin);
         double best_fit = best_values[0];
         double best_square_size = best_values[1];
 
         int start = page_nr * sudokus_per_page;
-        int left = margin;
-        int top = margin;
+        int end = (start + sudokus_per_page) > boards.length ? boards.length : (start + sudokus_per_page);
+        SudokuBoard[] sudokus_on_page = boards[start : end];
+
+        double left = margin;
+        double top = margin;
+        int[] pos = {1, 1};
+        string label = "";
 
-        draw_sudoku (cr, best_square_size, left, top);
+        foreach (SudokuBoard sudoku in sudokus_on_page)
+        {
+            if (n_sudokus > 1)
+                label = sudoku.get_difficulty_catagory ().to_string () + " (" + sudoku.difficulty_rating.to_string()[0:4] + ")";
+            else
+                label = "";
+            cr.set_font_size(12);
+            cr.select_font_face ("Sans", Cairo.FontSlant.NORMAL, Cairo.FontWeight.BOLD);
+            cr.set_source_rgb(0, 0, 0);
+            Cairo.TextExtents extents;
+            cr.text_extents (label, out extents);
+            cr.move_to(left, top - extents.height / 2);
+            cr.show_text(label);
+
+            draw_sudoku (cr, sudoku, best_square_size, left, top);
+
+            if (pos[0] < best_fit)
+            {
+                left = left + best_square_size + margin;
+                pos[0] += 1;
+            }
+            else
+            {
+                top = top + best_square_size + margin;
+                left = margin;
+                pos[0] = 1;
+                pos[1] += 1;
+            }
+        }
     }
 
-    private double[] fit_squares_in_rectangle (double width, double height, int n, int margin)
+    private double[] fit_squares_in_rectangle (double width, double height, int margin)
     {
+        int n = sudokus_per_page;
         double best_square_size = 0;
         double[] best_fit = {0,0};
         double square_size = 0;
@@ -93,7 +130,7 @@ public class SudokuPrinter : GLib.Object {
         return best_fit;
     }
 
-    private void draw_sudoku (Cairo.Context cr, double size, int offset_x, int offset_y)
+    private void draw_sudoku (Cairo.Context cr, SudokuBoard sudoku_board, double size, double offset_x, double offset_y)
     {
         const int SUDOKU_SIZE = 9;
         const int N_BOXES = 3;
@@ -169,7 +206,7 @@ public class SudokuPrinter : GLib.Object {
         cr.set_font_size (FONT_SIZE);
         string letter = "";
         Cairo.TextExtents extents;
-        int[,] sudoku = board.get_cells ();
+        int[,] sudoku = sudoku_board.get_cells ();
 
         for (var x = 0; x < SUDOKU_SIZE; x++)
         {
@@ -182,7 +219,7 @@ public class SudokuPrinter : GLib.Object {
                 {
                     letter = (sudoku[y,x]).to_string ();
 
-                    if (board.is_fixed[y,x])
+                    if (sudoku_board.is_fixed[y,x])
                     {
                         cr.select_font_face ("Sans", Cairo.FontSlant.NORMAL, FONT_WEIGHT);
                         cr.set_source_rgb (0, 0, 0);
@@ -200,3 +237,127 @@ public class SudokuPrinter : GLib.Object {
         }
     }
 }
+
+public class GamePrinter: GLib.Object {
+
+    private SudokuStore store;
+    private ApplicationWindow window;
+    private GLib.Settings settings;
+    private Gtk.Dialog dialog;
+    private HashMap<string, CheckButton> options_map;
+    private SpinButton sudokusToPrintSpinButton;
+    private SpinButton sudokusPerPageSpinButton;
+
+    public GamePrinter (SudokuStore store, ref  ApplicationWindow window)
+    {
+        this.store = store;
+        this.window = window;
+        this.settings = new GLib.Settings ("org.gnome.gnome-sudoku");
+        this.options_map = new HashMap<string, CheckButton> ();
+
+        Gtk.Builder builder = new Builder();
+        try
+        {
+            builder.add_from_resource ("/org/gnome/gnome-sudoku/ui/print-games.ui");
+        }
+        catch (GLib.Error e)
+        {
+            GLib.warning ("Could not load UI: %s", e.message);
+        }
+        builder.connect_signals(null);
+        this.dialog = builder.get_object ("dialog") as Dialog;
+        dialog.set_transient_for (window);
+        dialog.set_default_response (Gtk.ResponseType.OK);
+        dialog.response.connect (response_cb);
+
+        string[,] settings_to_widgets = {
+            {"mark-printed-as-played", "markAsPlayedToggle"},
+            {"print-already-played-games", "includeOldGamesToggle"},
+            {"print-easy", "easyCheckButton"},
+            {"print-medium", "mediumCheckButton"},
+            {"print-hard", "hardCheckButton"},
+            {"print-very-hard", "very_hardCheckButton"}};
+        CheckButton check_button;
+        string setting0, setting1;
+
+        for (var i=0; i<6; i++)
+        {
+            setting0 = settings_to_widgets[i,0];
+            setting1 = settings_to_widgets[i,1];
+            try
+            {
+                check_button = builder.get_object (setting1) as CheckButton;
+            }
+            catch (GLib.Error e)
+            {
+                GLib.warning ("Widget %s does not exist: %s", setting1, e.message);
+            }
+            wrap_toggle (setting0, check_button);
+            options_map.set (setting1, check_button);
+        }
+
+        sudokusToPrintSpinButton = builder.get_object ("sudokusToPrintSpinButton") as SpinButton;
+        sudokusPerPageSpinButton = builder.get_object ("sudokusPerPageSpinButton") as SpinButton;
+
+        wrap_adjustment ("print-multiple-sudokus-to-print", sudokusToPrintSpinButton.get_adjustment ());
+        wrap_adjustment ("sudokus-per-page", sudokusPerPageSpinButton.get_adjustment ());
+    }
+
+    private void wrap_toggle (string key_name, CheckButton action)
+    {
+        action.set_active (settings.get_boolean (key_name));
+        action.toggled.connect (() => settings.set_boolean (key_name, action.get_active ()));
+    }
+
+    private void wrap_adjustment (string key_name, Adjustment action)
+    {
+        action.set_value (settings.get_int (key_name));
+        action.value_changed.connect (() => settings.set_int (key_name, (int) action.get_value ()));
+    }
+
+    private void response_cb (Dialog dialog, int response)
+    {
+        if (response != Gtk.ResponseType.ACCEPT && response != Gtk.ResponseType.OK)
+        {
+            dialog.hide ();
+            return;
+        }
+
+        int nsudokus = (int) sudokusToPrintSpinButton.get_adjustment ().get_value ();
+        int sudokus_per_page = (int) sudokusPerPageSpinButton.get_adjustment ().get_value ();
+        DifficultyCatagory[] levels = {};
+
+        if (options_map.get ("easyCheckButton").get_active () == true)
+            levels += DifficultyCatagory.EASY;
+        if (options_map.get ("mediumCheckButton").get_active () == true)
+            levels += DifficultyCatagory.MEDIUM;
+        if (options_map.get ("hardCheckButton").get_active () == true)
+            levels += DifficultyCatagory.HARD;
+        if (options_map.get ("very_hardCheckButton").get_active () == true)
+            levels += DifficultyCatagory.VERY_HARD;
+
+        var boards = new ArrayList<SudokuBoard> ();
+        boards = store.get_assorted_boards (nsudokus, levels);
+
+        SudokuBoard[] sorted_boards = {};
+
+        foreach (SudokuBoard i in boards)
+            sorted_boards += i;
+
+        SudokuPrinter printer = new SudokuPrinter (sorted_boards, ref window, sudokus_per_page);
+        PrintOperationResult result = printer.print_sudoku ();
+
+        if (result == PrintOperationResult.APPLY)
+        {
+            // In future when 'markAsPlayedToggle' is made sensitive,
+            // copy the 'Tracker' portion of code from printing.py here.
+            dialog.hide ();
+        }
+    }
+
+    public void run_dialog ()
+    {
+        dialog.show();
+    }
+
+}
diff --git a/src/sudoku-solver.vala b/src/sudoku-solver.vala
index 64c02aa..1ce530b 100644
--- a/src/sudoku-solver.vala
+++ b/src/sudoku-solver.vala
@@ -753,7 +753,24 @@ public enum DifficultyCatagory {
     EASY,
     MEDIUM,
     HARD,
-    VERY_HARD
+    VERY_HARD;
+
+    public string to_string ()
+    {
+        switch (this)
+        {
+            case EASY:
+                return "Easy";
+            case MEDIUM:
+                return "Medium";
+            case HARD:
+                return "Hard";
+            case VERY_HARD:
+                return "Very hard";
+            default:
+                return "Undefined";
+        }
+    }
 }
 
 public class DifficultyRating {
diff --git a/src/sudoku-store.vala b/src/sudoku-store.vala
index 8477c35..5f28f91 100644
--- a/src/sudoku-store.vala
+++ b/src/sudoku-store.vala
@@ -1,6 +1,6 @@
 using Gee;
 
-class SudokuStore
+public class SudokuStore
 {
     private ArrayList<SudokuBoard> easy_boards = new ArrayList<SudokuBoard> ();
     private ArrayList<SudokuBoard> medium_boards = new ArrayList<SudokuBoard> ();
@@ -19,6 +19,7 @@ class SudokuStore
                 while ((line = dis.read_line (null)) != null) {
                     SudokuBoard board = new SudokuBoard();
                     board.set_from_string(line[0:161], " ");
+                    board.difficulty_rating = double.parse(line[162:line.length]);
 
                     easy_boards.add(board);
                 }
@@ -34,6 +35,7 @@ class SudokuStore
                 while ((line = dis.read_line (null)) != null) {
                     SudokuBoard board = new SudokuBoard();
                     board.set_from_string(line[0:161], " ");
+                    board.difficulty_rating = double.parse(line[162:line.length]);
 
                     medium_boards.add(board);
                 }
@@ -49,6 +51,7 @@ class SudokuStore
                 while ((line = dis.read_line (null)) != null) {
                     SudokuBoard board = new SudokuBoard();
                     board.set_from_string(line[0:161], " ");
+                    board.difficulty_rating = double.parse(line[162:line.length]);
 
                     hard_boards.add(board);
                 }
@@ -64,6 +67,7 @@ class SudokuStore
                 while ((line = dis.read_line (null)) != null) {
                     SudokuBoard board = new SudokuBoard();
                     board.set_from_string(line[0:161], " ");
+                    board.difficulty_rating = double.parse(line[162:line.length]);
 
                     very_hard_boards.add(board);
                 }
@@ -106,4 +110,38 @@ class SudokuStore
         else
             assert_not_reached();
     }
+
+    public ArrayList<SudokuBoard> get_assorted_boards(int n, owned DifficultyCatagory[] levels)
+    {
+        var boards = new ArrayList<SudokuBoard> ();
+
+        int i = 0;
+        int  il = 0;
+        int lev = 0;
+
+        if (levels.length == 0)
+            levels = {DifficultyCatagory.EASY, DifficultyCatagory.MEDIUM, DifficultyCatagory.HARD, DifficultyCatagory.VERY_HARD};
+
+        while (i < n)
+        {
+            if (il >= levels.length)
+                il = 0;
+            lev = levels[il];
+            boards.add(get_random_board((DifficultyCatagory) lev));
+
+            i += 1;
+            il += 1;
+        }
+
+        CompareDataFunc<SudokuBoard> CompareDifficultyRatings = (a, b) => {
+            if (a.difficulty_rating > b.difficulty_rating)
+                return 1;
+            if (a.difficulty_rating == b.difficulty_rating)
+                return 0;
+            return -1;
+        };
+
+        boards.sort (CompareDifficultyRatings);
+        return boards;
+    }
 }
