[gnome-sudoku/vala-port] Implemented printing multiple sudokus feature



commit 19ad23c31ae565a923fe05dba91e836d742b54f8
Author: Parin Porecha <parinporecha gmail com>
Date:   Sun Nov 24 18:35:00 2013 +0000

    Implemented printing multiple sudokus feature
    
    https://bugzilla.gnome.org/show_bug.cgi?id=633464

 TODO                              |   11 +-
 data/print-games.ui               |  367 +++++++++++++++++++++++++++++++++++++
 src/gnome-sudoku.gresource.xml.in |    1 +
 src/gnome-sudoku.vala             |    5 +-
 src/sudoku-board.vala             |   28 +++-
 src/sudoku-printer.vala           |  233 ++++++++++++++++++++----
 src/sudoku-solver.vala            |   19 ++-
 src/sudoku-store.vala             |   36 +++-
 8 files changed, 651 insertions(+), 49 deletions(-)
---
diff --git a/TODO b/TODO
index e768429..05842d8 100644
--- a/TODO
+++ b/TODO
@@ -6,8 +6,6 @@ about dialog: artwork has "placeholder"
 "show possible numbers" results in critical warning. No result
 "warn about unfillable squares" results in critical warning. No result
 
-Print multiple: just writes TODO to terminal
-
 Undo/redo are clickable, even before starting a game
 
 "Saved game" shown in start screen, even if none exist
@@ -19,6 +17,13 @@ The "Back" back seems to function like undo. Is that the case?
 The ingame Help: (I like it!)
   Should maybe have different name. Help is the common word for opening the help manual.
   Left align the text
-  Perhaps start with a general description of the method, instead of repeating the same sentance for each 
line.
+  Perhaps start with a general description of the method, instead of repeating the same sentence for each 
line.
 
 No way to save a game
+
+No way to know if a game has already been played or not:
+  For this SudokuTracker class needs to be ported. Once it is complete, enable the corresponding options -
+  'MarkAsPlayedToggle' and 'includeOldGamesToggle' in data/print_games.ui (for multiple print dialog)
+  
+Currently we only read sudokus from data files:
+  We need to complete the port of SudokuGenerator class to be able to generate sudokus as desired.
diff --git a/data/print-games.ui b/data/print-games.ui
new file mode 100644
index 0000000..5c4e415
--- /dev/null
+++ b/data/print-games.ui
@@ -0,0 +1,367 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.0"/>
+  <!-- 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..28957b5 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..7cdaed1 100644
--- a/src/sudoku-board.vala
+++ b/src/sudoku-board.vala
@@ -61,6 +61,27 @@ public class SudokuBoard
         get { return _filled == _cols * _rows && !broken; }
     }
 
+    public double difficulty_rating { get; private set; }
+
+    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 */
@@ -170,7 +191,7 @@ public class SudokuBoard
         return board;
     }
 
-    public void set_from_string (string s, string delimiter = "", string empty_value = "0")
+    public void set_from_string (string s, string delimiter = "", string empty_value = "0", string 
rating_delimiter = "\t")
     {
         //stdout.printf("Processing %s\n", s);
 
@@ -178,7 +199,10 @@ public class SudokuBoard
 
         string[] cells = s.split (delimiter, number_of_cells);
 
-        //stdout.printf("Cells %d %d\n", number_of_cells, cells.length);
+        string[] rating = cells[cells.length -1].split (rating_delimiter, 2);
+        cells[cells.length - 1] = rating[0];
+        difficulty_rating = double.parse (rating[1]);
+
         for (int i = 0; i < number_of_cells; i++)
         {
             string cell = cells[i];
diff --git a/src/sudoku-printer.vala b/src/sudoku-printer.vala
index ecf08b4..ab28c84 100644
--- a/src/sudoku-printer.vala
+++ b/src/sudoku-printer.vala
@@ -1,38 +1,40 @@
 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");
             error_dialog.response.connect ((w) => {
-                error_dialog.destroy();
+                error_dialog.destroy ();
             });
             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,31 +43,66 @@ 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)
     {
         Cairo.Context cr = context.get_cairo_context ();
-        double width = context.get_width ();
-        double height = context.get_height ();
+        var width = context.get_width ();
+        var height = context.get_height ();
+
+        var best_values = fit_squares_in_rectangle (width, height, margin);
+        var best_fit = best_values[0];
+        var best_square_size = best_values[1];
 
-        double[] best_values = fit_squares_in_rectangle (width, height, sudokus_per_page, margin);
-        double best_fit = best_values[0];
-        double best_square_size = best_values[1];
+        var start = page_nr * sudokus_per_page;
+        var end = (start + sudokus_per_page) > boards.length ? boards.length : (start + sudokus_per_page);
+        SudokuBoard[] sudokus_on_page = boards[start : end];
 
-        int start = page_nr * sudokus_per_page;
-        int left = margin;
-        int top = margin;
+        double left = margin;
+        double top = margin;
+        int[] pos = {1, 1};
+        var 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)
     {
-        double best_square_size = 0;
+        var n = sudokus_per_page;
+        var best_square_size = 0.0;
         double[] best_fit = {0,0};
-        double square_size = 0;
+        var square_size = 0.0;
 
         for (var n_across = 1; n_across <= n; n_across++)
         {
@@ -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;
@@ -103,13 +140,13 @@ public class SudokuPrinter : GLib.Object {
         const double[] BORDER_COLOR = {1.0, 1.0, 1.0};
         const double[] LINE_COLOR = {0.0, 0.0, 0.0};
 
-        double THIN = size / 500.0;
-        double THICK = THIN * 5;
-        double BORDER  = THICK;
-        double WHITE_SPACE = (size - (2 * BORDER)- (2 * THICK) - ((N_BOXES -1) * THICK) - ((N_BOXES * 2) * 
THIN));
-        double SQUARE_SIZE = WHITE_SPACE / SUDOKU_SIZE;
+        var THIN = size / 500.0;
+        var THICK = THIN * 5;
+        var BORDER  = THICK;
+        var WHITE_SPACE = (size - (2 * BORDER)- (2 * THICK) - ((N_BOXES -1) * THICK) - ((N_BOXES * 2) * 
THIN));
+        var SQUARE_SIZE = WHITE_SPACE / SUDOKU_SIZE;
 
-        int FONT_SIZE = (int) SQUARE_SIZE / 2;
+        var FONT_SIZE = (int) SQUARE_SIZE / 2;
         const Cairo.FontWeight FONT_WEIGHT = Cairo.FontWeight.NORMAL;
 
         double[] outer = {offset_x, offset_x + size, offset_y, offset_y + size};  // left, right, top, bottom
@@ -138,10 +175,10 @@ public class SudokuPrinter : GLib.Object {
         cr.set_source_rgb (LINE_COLOR[0], LINE_COLOR[1], LINE_COLOR[2]);
         cr.stroke ();
 
-        double[] pos = new double[SUDOKU_SIZE+1];
-        double position = BORDER + THICK;
+        var pos = new double[SUDOKU_SIZE+1];
+        var position = BORDER + THICK;
         pos[0] = position + SQUARE_SIZE / 2.0;
-        double last_line = 0;
+        var last_line = 0.0;
 
         for (var n = 1; n <= SUDOKU_SIZE; n++)
         {
@@ -167,9 +204,9 @@ public class SudokuPrinter : GLib.Object {
         }
 
         cr.set_font_size (FONT_SIZE);
-        string letter = "";
+        var letter = "";
         Cairo.TextExtents extents;
-        int[,] sudoku = board.get_cells ();
+        var 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;
+        }
+
+        var nsudokus = (int) sudokusToPrintSpinButton.get_adjustment ().get_value ();
+        var 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..63d8b7d 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> ();
@@ -18,7 +18,7 @@ class SudokuStore
                 // Read lines until end of file (null) is reached
                 while ((line = dis.read_line (null)) != null) {
                     SudokuBoard board = new SudokuBoard();
-                    board.set_from_string(line[0:161], " ");
+                    board.set_from_string(line, " ");
 
                     easy_boards.add(board);
                 }
@@ -33,7 +33,7 @@ class SudokuStore
                 // Read lines until end of file (null) is reached
                 while ((line = dis.read_line (null)) != null) {
                     SudokuBoard board = new SudokuBoard();
-                    board.set_from_string(line[0:161], " ");
+                    board.set_from_string(line, " ");
 
                     medium_boards.add(board);
                 }
@@ -48,7 +48,7 @@ class SudokuStore
                 // Read lines until end of file (null) is reached
                 while ((line = dis.read_line (null)) != null) {
                     SudokuBoard board = new SudokuBoard();
-                    board.set_from_string(line[0:161], " ");
+                    board.set_from_string(line, " ");
 
                     hard_boards.add(board);
                 }
@@ -63,7 +63,7 @@ class SudokuStore
                 // Read lines until end of file (null) is reached
                 while ((line = dis.read_line (null)) != null) {
                     SudokuBoard board = new SudokuBoard();
-                    board.set_from_string(line[0:161], " ");
+                    board.set_from_string(line, " ");
 
                     very_hard_boards.add(board);
                 }
@@ -106,4 +106,30 @@ 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;
+
+        if (levels.length == 0)
+            levels = {DifficultyCatagory.EASY, DifficultyCatagory.MEDIUM, DifficultyCatagory.HARD, 
DifficultyCatagory.VERY_HARD};
+
+        while (i < n)
+        {
+            // In future, modify this while loop to accomodate 'exclude' and 'new' parameters
+            boards.add (get_random_board ((DifficultyCatagory) levels[i++ % levels.length]));
+        }
+
+        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;
+    }
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]