[five-or-more/vala-port] start the port to vala



commit 312e70c8384710a0fa5a9a3324eafd233e94eda6
Author: Thomas Hindoe Paaboel Andersen <phomes gmail com>
Date:   Sat Nov 3 02:34:51 2012 +0100

    start the port to vala

 configure.ac                      |   11 +
 data/five-or-more.ui              |    5 +-
 src/Makefile.am                   |   32 +-
 src/application.vala              |  157 ++++
 src/board.vala                    |  314 +++++++
 src/config.vapi                   |    8 +
 src/field.vala                    |   32 +
 src/five-or-more.gresource.xml.in |    7 +
 src/glines.c                      | 1676 -------------------------------------
 src/glines.h                      |   47 -
 src/main.vala                     |   41 +
 src/piece.vala                    |   13 +
 src/preview-queue.vala            |   31 +
 src/view-2d.vala                  |  144 ++++
 src/view-cli.vala                 |  146 ++++
 15 files changed, 934 insertions(+), 1730 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 0d61494..9626ce7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5,6 +5,7 @@ AM_MAINTAINER_MODE
 GNOME_MAINTAINER_MODE_DEFINES
 AC_CONFIG_HEADERS([config.h])
 
+AM_PROG_VALAC([0.16.0])
 AM_PROG_CC_C_O
 
 GLIB_GSETTINGS
@@ -59,14 +60,23 @@ dnl ###########################################################################
 
 GTK_REQUIRED=3.4.0
 RSVG_REQUIRED=2.32.0
+GEE_REQUIRED=0.6.0
 
 PKG_CHECK_MODULES(FIVE_OR_MORE, [
   gmodule-2.0
   gtk+-3.0 >= $GTK_REQUIRED
   librsvg-2.0 >= $RSVG_REQUIRED
+  gee-1.0 >= $GEE_REQUIRED
 ])
 
 dnl ###########################################################################
+dnl GResources
+dnl ###########################################################################
+
+GLIB_COMPILE_RESOURCES=`$PKG_CONFIG --variable glib_compile_resources gio-2.0`
+AC_SUBST(GLIB_COMPILE_RESOURCES)
+
+dnl ###########################################################################
 dnl Internationalization
 dnl ###########################################################################
 
@@ -92,4 +102,5 @@ data/icons/Makefile
 data/five-or-more.desktop.in
 help/Makefile
 src/Makefile
+src/five-or-more.gresource.xml
 ])
diff --git a/data/five-or-more.ui b/data/five-or-more.ui
index 859169d..426b771 100644
--- a/data/five-or-more.ui
+++ b/data/five-or-more.ui
@@ -2,7 +2,7 @@
 <interface>
   <!-- interface-requires gtk+ 3.0 -->
   <object class="GtkAccelGroup" id="accelgroup"/>
-  <object class="GtkWindow" id="glines_window">
+  <object class="GtkApplicationWindow" id="glines_window">
     <property name="can_focus">False</property>
     <property name="title" translatable="yes">Five or more</property>
     <property name="default_width">320</property>
@@ -11,6 +11,7 @@
     <accel-groups>
       <group name="accelgroup"/>
     </accel-groups>
+    <signal name="delete-event" handler="game_quit_callback" swapped="no"/>
     <child>
       <object class="GtkBox" id="vbox">
         <property name="visible">True</property>
@@ -18,7 +19,7 @@
         <property name="orientation">vertical</property>
         <child>
           <object class="GtkMenuBar" id="menubar">
-            <property name="visible">True</property>
+            <property name="visible">False</property>
             <property name="can_focus">False</property>
             <child>
               <object class="GtkMenuItem" id="game_menu_item">
diff --git a/src/Makefile.am b/src/Makefile.am
index 169e7b3..58f7cc1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,7 @@
 bin_PROGRAMS = five-or-more
 
+BUILT_SOURCES = five-or-more-resources.c
+
 five_or_more_SOURCES =	\
 	games-file-list.c	\
 	games-file-list.h	\
@@ -17,12 +19,20 @@ five_or_more_SOURCES =	\
 	games-scores-backend.h \
 	games-stock.c	\
 	games-stock.h	\
-	glines.c	\
-	glines.h
+	config.vapi \
+	main.vala \
+	application.vala \
+	board.vala \
+	field.vala \
+	piece.vala \
+	preview-queue.vala \
+	view-2d.vala \
+	view-cli.vala \
+	$(BUILT_SOURCES)
 
 if ENABLE_SETGID
-five_or_more_SOURCES +=		\
-	games-setgid-io.c		\
+five_or_more_SOURCES += \
+	games-setgid-io.c \
 	games-setgid-io.h
 endif # ENABLE_SETGID
 
@@ -31,8 +41,9 @@ five_or_more_CPPFLAGS = \
 	$(AM_CPPFLAGS)
 
 five_or_more_CFLAGS = \
-	-DDATA_DIRECTORY=\"$(datadir)/five-or-more\" \
 	-DLOCALEDIR=\"$(datadir)/locale\" \
+	-DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \
+	-DDATA_DIRECTORY=\"$(datadir)/five-or-more\" \
 	-DICON_THEME_DIRECTORY="\"$(datadir)/icons\"" \
 	-DSCORESDIR="\"$(scoredir)\""				\
 	$(FIVE_OR_MORE_CFLAGS)
@@ -41,6 +52,17 @@ five_or_more_LDADD =	\
 	$(FIVE_OR_MORE_LIBS) \
 	-lm
 
+five_or_more_VALAFLAGS = \
+	--pkg posix \
+	--pkg gtk+-3.0 \
+	--pkg gee-1.0
+
+five-or-more-resources.c: five-or-more.gresource.xml $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies five-or-more.gresource.xml)
+	$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --generate-source five-or-more.gresource.xml
+
+EXTRA_DIST = \
+	five-or-more.gresource.xml
+
 install-exec-hook:
 	-if test "$(setgid)" = "true"; then \
 	  chgrp $(scores_group) $(DESTDIR)$(bindir)/five-or-more && chmod 2555 $(DESTDIR)$(bindir)/five-or-more ;\
diff --git a/src/application.vala b/src/application.vala
new file mode 100644
index 0000000..f109bc1
--- /dev/null
+++ b/src/application.vala
@@ -0,0 +1,157 @@
+namespace FiveOrMore
+{
+    public class FiveOrMoreApp : Gtk.Application
+    {
+        private Gtk.ApplicationWindow window;
+        private Settings settings;
+        private Gtk.Builder builder;
+        //private GnomeGamesSupport.Scores highscores;
+
+        private Gtk.Dialog preferences_dialog;
+
+        private const GLib.ActionEntry[] action_entries =
+        {
+            { "new-game", new_game_cb },
+            { "scores", scores_cb },
+            { "preferences", preferences_cb },
+            { "help", help_cb },
+            { "about", about_cb },
+            { "quit", quit_cb }
+        };
+
+        //private const GnomeGamesSupport.ScoresCategory scorecats[] =
+        //{
+        //    { "Small",  NC_("board size", "Small") },
+        //    { "Medium", NC_("board size", "Medium") },
+        //    { "Large",  NC_("board size", "Large") }
+        //};
+    
+        private const string[] authors = { "Thomas Andersen <phomes gmail com>", "Robert Szokovacs <szo appaloosacorp hu>", "Szabolcs B\xc3\xa1n <shooby gnome hu>" };
+        //private const string[] documenters = { "Tiffany Antopolski", "Lanka Rathnayaka" };
+
+        private GlinesBoard board = new GlinesBoard(10, 10, 5, 3);
+
+        public FiveOrMoreApp ()
+        {
+            Object (application_id: "org.gnome.five-or-more", flags: ApplicationFlags.FLAGS_NONE);
+
+            add_action_entries (action_entries, this);
+        }
+
+        protected override void startup ()
+        {
+            base.startup ();
+
+            settings = new Settings ("org.gnome.five-or-more");
+
+            //highscores = new GnomeGamesSupport.Scores ("glines", scorecats, "board size", null, 0, GnomeGamesSupport.ScoreStyle.PLAIN_DESCENDING);
+
+            builder = new Gtk.Builder ();
+            try
+            {
+                 builder.add_from_resource ("/org/gnome/five-or-more/ui/glines.ui");
+                 builder.add_from_resource ("/org/gnome/five-or-more/ui/glines-preferences.ui");
+            }
+            catch (GLib.Error e)
+            {
+                GLib.warning ("Could not load UI: %s", e.message);
+            }
+
+            var menu = new Menu ();
+
+            var section = new Menu ();
+            menu.append_section (null, section);
+            section.append (_("_New Game"), "app.new-game");
+            section.append (_("_Scores"), "app.scores");
+            section.append (_("_Preferences"), "app.preferences");
+
+            section = new Menu ();
+            menu.append_section (null, section);
+            section.append (_("_Help"), "app.help");
+            section.append (_("_About"), "app.about");
+
+            section = new Menu ();
+            menu.append_section (null, section);
+            section.append (_("_Quit"), "app.quit");
+            set_app_menu (menu);
+
+            var box = (Gtk.Box) builder.get_object ("vbox");
+            var view2d = new View2D (board);
+            box.add (view2d);
+            view2d.show ();
+
+            window = (Gtk.ApplicationWindow) builder.get_object ("glines_window");
+            add_window (window);
+        }
+
+        public override void activate ()
+        {
+            //var v = new ViewCli (board);
+            //v.run();
+
+            window.present ();
+        }
+
+        private void new_game_cb ()
+        {
+            stdout.printf ("new game\n");
+        }
+
+        private void scores_cb ()
+        {
+            stdout.printf ("FIXME: Showing scores does not currently work\n");
+
+            //var scores_dialog = new GnomeGamesSupport.ScoresDialog (window, highscores, _("GNOME Five or More"));
+            //scores_dialog.set_category_description (_("_Board size:"));
+            //scores_dialog.run ();
+            //scores_dialog.destroy ();
+        }
+
+        private void preferences_cb ()
+        {
+            stdout.printf ("preferences\n");
+            if (preferences_dialog == null)
+                preferences_dialog = (Gtk.Dialog) builder.get_object ("preferences_dialog");
+            
+            preferences_dialog.run();
+            preferences_dialog.destroy ();
+        }
+
+        private void help_cb ()
+        {
+            try
+            {
+                Gtk.show_uri (window.get_screen (), "ghelp:five-or-more", Gtk.get_current_event_time ());
+            }
+            catch (GLib.Error e)
+            {
+                GLib.warning ("Unable to open help: %s", e.message);
+            }
+        }
+
+        private void about_cb ()
+        {
+            Gtk.show_about_dialog (window,
+                                   "program-name", _("Five or More"),
+                                   "logo-icon-name", "five-or-more",
+                                   "version", Config.VERSION,
+                                   "comments", _("GNOME port of the once-popular Color Lines game.\n\nFive or More is a part of GNOME Games."),
+                                   "copyright", "Copyright \xc2\xa9 1997-2012 Free Software Foundation, Inc.",
+                                   "license-type", Gtk.License.GPL_3_0,
+                                   "wrap-license", false,
+                                   "authors", authors,
+                                   //FIXME: "documenters", documenters,
+                                   "translator-credits", _("translator-credits"),
+                                   "website", "http://www.gnome.org/projects/gnome-games/";,
+                                   "website-label", _("GNOME Games web site"),
+                                   null);
+        }
+        
+        private void quit_cb ()
+        {
+            window.destroy ();
+        }
+    }
+}
+
+
diff --git a/src/board.vala b/src/board.vala
new file mode 100644
index 0000000..d859476
--- /dev/null
+++ b/src/board.vala
@@ -0,0 +1,314 @@
+using Gee;
+
+namespace FiveOrMore
+{
+    public class GlinesBoard
+    {
+        public int cols { get; private set; }
+        public int rows { get; private set; }
+
+        public GlinesField[,] fields { get; private set; }
+        public GlinesField active_field = null;
+
+        public GlinesPreviewQueue preview_queue;
+
+        public bool show_cursor { get; set; }
+        private int _cursor_x;
+        public int cursor_x
+        {
+            get { return this._cursor_x; }
+            set { this._cursor_x = this.clamp(0, value, this.cols - 1); }
+        }
+        private int _cursor_Y;
+        public int cursor_y
+        {
+            get { return this._cursor_Y; }
+            set { this._cursor_Y = this.clamp(0, value, this.rows - 1); }
+        }
+
+        public bool move_in_progress { get; set; }
+
+        public signal void changed ();
+        public signal void move (ArrayList<GlinesField> path);
+        public signal void info (string text);
+        public signal void gameover ();
+
+        public GlinesBoard(int cols, int rows, int preview_queue_size, int n_types)
+        {
+            this.cols = cols;
+            this.rows = rows;
+
+            preview_queue = new GlinesPreviewQueue(preview_queue_size, n_types);
+
+            this.fields = new GlinesField[this.cols, this.rows];
+
+            for (int x = 0; x < this.cols; x++)
+            {
+                for (int y = 0; y < this.rows; y++)
+                {
+                    this.fields[x, y] = new GlinesField();
+                }
+            }
+
+            //Connect the neigbouring fields
+            for (int x = 0; x < this.cols; x++)
+            {
+                for (int y = 0; y < this.rows; y++)
+                {
+                    var field = this.fields[x, y];
+                    if (x > 0)
+                        field.neigbour_west = this.fields[x - 1, y];
+                    if (x < this.cols - 1)
+                        field.neigbour_east = this.fields[x + 1, y];
+                    if (y > 0)
+                        field.neigbour_north = this.fields[x, y - 1];
+                    if (y < this.rows - 1)
+                        field.neigbour_south = this.fields[x, y + 1];
+                }
+            }
+
+            this.move_preview_queue_to_board();
+        }
+
+        public void select_field(int x, int y)
+        {
+            if (this.move_in_progress)
+                return;
+
+            var clicked_field = this.fields[x, y];
+
+            if (this.active_field == null && !clicked_field.has_piece)
+            {
+                info("No piece to select");
+            }
+            else if (this.active_field == null && clicked_field.has_piece)
+            {
+                //select a field
+                clicked_field.active = true;
+                this.active_field = clicked_field;
+                info("Field selected");
+            }
+            else if (this.active_field == clicked_field)
+            {
+                //deselect the active field
+                clicked_field.active = false;
+                this.active_field = null;
+                info("Field deselected");
+            }
+            else
+            {
+                //attempt to move the piece
+                if (find_route(this.active_field, clicked_field))
+                {
+                    info("moving");
+
+                    this.move_in_progress = true;
+
+                    // Let the listener do the animation.
+                    this.move(this.backtrack_route(clicked_field));
+
+                    //actually move the piece
+                    clicked_field.piece = this.active_field.piece;
+                    this.active_field.piece = null;
+
+                    this.active_field.active = false;
+                    this.active_field = null;
+
+                    move_completed();
+                }
+                else
+                {
+                    info("no route");
+                }
+            }
+
+            this.changed();
+        }
+
+        private void move_completed()
+        {
+            //remove lines to make space for adding pieces
+            this.remove_lines();
+
+            this.move_preview_queue_to_board();
+
+            //remove lines again. New once could be formed when adding the new pieces
+            this.remove_lines();
+
+            if (this.check_for_game_over())
+            {
+                gameover();
+            }
+
+            this.move_in_progress = false;
+        }
+
+        private void remove_lines()
+        {
+            //todo: check for lines to remove
+            //      + add scoring
+        }
+
+        private void move_preview_queue_to_board()
+        {
+            foreach (var piece in preview_queue.pieces)
+            {
+                this.add_piece_at_random_location(piece);
+            }
+
+            preview_queue.refill();
+        }
+
+        private void add_piece_at_random_location(GlinesPiece piece)
+        {
+            var vert = new ArrayList<int>();
+            var hori = new ArrayList<int>();
+
+            for (int i = 0; i < this.cols; i++)
+                vert.add(i);
+            for (int i = 0; i < this.rows; i++)
+                hori.add(i);
+
+            shuffle(hori);
+            shuffle(vert);
+
+            foreach(int x in vert)
+            {
+                foreach(int y in hori)
+                {
+                    if(!fields[x,y].has_piece)
+                    {
+                        fields[x, y].piece = piece;
+                        return;
+                    }
+                }
+            }
+        }
+
+        private void shuffle(ArrayList<int> list)
+        {
+            for (int from = list.size - 1; from > 0; from--)
+            {
+                int to = Random.int_range(0, from);
+                int temp = list[to];
+                list[to] = list[from];
+                list[from] = temp;
+            }
+        }
+
+        private bool check_for_game_over()
+        {
+            for (int x = 0; x < this.cols; x++)
+                for (int y = 0; y < this.rows; y++)
+                    if (!this.fields[x, y].has_piece)
+                        return false;
+
+            return true;
+        }
+
+        private void reset_path_search()
+        {
+            for (int x = 0; x < this.cols; x++)
+                for (int y = 0; y < this.rows; y++)
+                    fields[x, y].path_search = null;
+        }
+
+        //After the search has been performed a route will (if possible)
+        //exist from the target to the origin. Use the Pathsearch to follow
+        //the path.
+        public bool find_route(GlinesField from, GlinesField target)
+        {
+            reset_path_search();
+
+            var check_now = new ArrayList<GlinesField> ();
+            check_now.add (from);
+
+            while (check_now.size != 0)
+            {
+                var check_next = new ArrayList<GlinesField>();
+
+                foreach (var current in check_now)
+                {
+                    foreach (var neighbour in this.non_searched_neighbours(current))
+                    {
+                        if (neighbour == from)
+                            continue;
+
+                        neighbour.path_search = current;
+                        check_next.add(neighbour);
+
+                        // We have reached the target so there is no use in continuing
+                        if (neighbour == target)
+                            return true;
+                    }
+                }
+
+                check_now = check_next;
+            }
+
+            return false;
+        }
+
+        private ArrayList<GlinesField> non_searched_neighbours(GlinesField field)
+        {
+            var neighbours = new ArrayList<GlinesField>();
+
+            if (field.neigbour_north != null &&
+                !field.neigbour_north.has_piece &&
+                field.neigbour_north.path_search == null)
+                neighbours.add(field.neigbour_north);
+
+            if (field.neigbour_west != null &&
+                !field.neigbour_west.has_piece &&
+                field.neigbour_west.path_search == null)
+                neighbours.add(field.neigbour_west);
+
+            if (field.neigbour_south != null &&
+                !field.neigbour_south.has_piece &&
+                field.neigbour_south.path_search == null)
+                neighbours.add(field.neigbour_south);
+
+            if (field.neigbour_east != null &&
+                !field.neigbour_east.has_piece &&
+                field.neigbour_east.path_search == null)
+                neighbours.add(field.neigbour_east);
+
+            return neighbours;
+        }
+
+        public ArrayList<GlinesField> backtrack_route(GlinesField from)
+        {
+            var route = new ArrayList<GlinesField> ();
+            route.add (from);
+
+            //the routes origin may have more than one
+            while (route[route.size -1 ].path_search != null)
+                route.add(route[route.size -1 ].path_search);
+
+            return route;
+        }
+
+        public void place_cursor(int x, int y)
+        {
+            this.cursor_x = x;
+            this.cursor_y = y;
+
+            this.changed();
+        }
+
+        public void move_cursor(int x, int y)
+        {
+            this.place_cursor(this.cursor_x + x, this.cursor_y + y);
+        }
+
+        private int clamp(int min, int val, int max)
+        {
+            if (val < min)
+                return min;
+            if (val > max)
+                return max;
+            return val;
+        }
+    }
+}
+
diff --git a/src/config.vapi b/src/config.vapi
new file mode 100644
index 0000000..6908fe6
--- /dev/null
+++ b/src/config.vapi
@@ -0,0 +1,8 @@
+[CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "config.h")]
+namespace Config
+{
+  public const string DATA_DIRECTORY;
+  public const string LOCALEDIR;
+  public const string GETTEXT_PACKAGE;
+  public const string VERSION;
+}
diff --git a/src/field.vala b/src/field.vala
new file mode 100644
index 0000000..f5c2118
--- /dev/null
+++ b/src/field.vala
@@ -0,0 +1,32 @@
+namespace FiveOrMore
+{
+    public class GlinesField
+    {
+        public GlinesField()
+        {
+            this.piece = null;
+
+            this.neigbour_north = null;
+            this.neigbour_south = null;
+            this.neigbour_east = null;
+            this.neigbour_west = null;
+            this.path_search = null;
+        }
+
+        public GlinesPiece piece { get; set; }
+        public bool active { get; set; }
+
+        public GlinesField neigbour_north { get; set; }
+        public GlinesField neigbour_south { get; set; }
+        public GlinesField neigbour_east { get; set; }
+        public GlinesField neigbour_west { get; set; }
+
+        public GlinesField path_search { get; set; }
+
+        public bool has_piece
+        {
+            get { return this.piece != null; }
+        }
+    }
+}
+
diff --git a/src/five-or-more.gresource.xml.in b/src/five-or-more.gresource.xml.in
new file mode 100644
index 0000000..8ec7f1f
--- /dev/null
+++ b/src/five-or-more.gresource.xml.in
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/five-or-more/ui">
+    <file alias="five-or-more.ui" preprocess="xml-stripblanks">@top_srcdir@/data/five-or-more.ui</file>
+    <file alias="five-or-more-preferences.ui" preprocess="xml-stripblanks">@top_srcdir@/data/five-or-more-preferences.ui</file>
+  </gresource>
+</gresources>
diff --git a/src/main.vala b/src/main.vala
new file mode 100644
index 0000000..34fcdbd
--- /dev/null
+++ b/src/main.vala
@@ -0,0 +1,41 @@
+private static bool show_version;
+
+private static const OptionEntry[] options =
+{
+    { "version", 'v', 0, OptionArg.NONE, ref show_version,
+    /* Help string for command line --version flag */
+    N_("Show release version"), null},
+    { null }
+};
+
+public static int main (string[] args)
+{
+    Gtk.init (ref args);
+
+    var c = new OptionContext (/* Arguments and description for --help text */
+                                   _("[FILE] - Play Glines"));
+    c.add_main_entries (options, Config.GETTEXT_PACKAGE);
+    c.add_group (Gtk.get_option_group (true));
+    try
+    {
+        c.parse (ref args);
+    }
+    catch (Error e)
+    {
+        stderr.printf ("%s\n", e.message);
+        stderr.printf (/* Text printed out when an unknown command-line argument provided */
+                       _("Run '%s --help' to see a full list of available command line options."), args[0]);
+        stderr.printf ("\n");
+        return Posix.EXIT_FAILURE;
+    }
+    if (show_version)
+    {
+        /* Note, not translated so can be easily parsed */
+        stderr.printf ("glines %s\n", Config.VERSION);
+        return Posix.EXIT_SUCCESS;
+    }
+
+    var app = new FiveOrMore.FiveOrMoreApp ();
+    return app.run ();
+}
+
diff --git a/src/piece.vala b/src/piece.vala
new file mode 100644
index 0000000..e8bf445
--- /dev/null
+++ b/src/piece.vala
@@ -0,0 +1,13 @@
+namespace FiveOrMore
+{
+    public class GlinesPiece
+    {
+        public int id { get; private set; }
+
+        public GlinesPiece(int id)
+        {
+            this.id = id;
+        }
+    }
+}
+
diff --git a/src/preview-queue.vala b/src/preview-queue.vala
new file mode 100644
index 0000000..d4e58e6
--- /dev/null
+++ b/src/preview-queue.vala
@@ -0,0 +1,31 @@
+using Gee;
+
+namespace FiveOrMore
+{
+    public class GlinesPreviewQueue
+    {
+        public ArrayList<GlinesPiece> pieces { get; set; }
+        private int size { get; set; }
+        private int types { get; set; }
+
+        public GlinesPreviewQueue(int size, int types)
+        {
+            this.pieces = new ArrayList<GlinesPiece>();
+            this.size = size;
+            this.types = types; 
+            this.refill();
+        }
+
+        public void refill()
+        {
+            this.pieces.clear();
+
+            for (int i = 0; i < this.size; i++)
+            {
+                int id = Random.int_range(0, types);
+                this.pieces.add(new GlinesPiece(id));
+            }
+        }
+    }
+}
+
diff --git a/src/view-2d.vala b/src/view-2d.vala
new file mode 100644
index 0000000..6e8a721
--- /dev/null
+++ b/src/view-2d.vala
@@ -0,0 +1,144 @@
+using Cairo;
+using Gdk;
+using Gtk;
+
+namespace FiveOrMore
+{
+    public class View2D : DrawingArea
+    {
+        public GlinesBoard board { get; private set; }
+        public double line_width { get; set; }
+
+        public View2D(GlinesBoard board)
+        {
+            this.board = board;
+            this.line_width = 1.0;
+
+            this.vexpand = true;
+            this.hexpand = true;
+            this.add_events (Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK | Gdk.EventMask.KEY_PRESS_MASK);
+
+            this.board.changed.connect ((o) => this.queue_draw());
+            //TODO: drop this later:
+            this.board.info.connect ((o, info) => stdout.printf(info + "\n"));
+        }
+
+
+        public override bool draw(Context ctx)
+        {
+            int w = this.get_allocated_width();
+            int h = this.get_allocated_height();
+
+            double boxsize_h = (w - (1 + board.rows) * line_width) / board.rows;
+            double boxsize_v = (h - (1 + board.cols) * line_width) / board.cols;
+
+            //Draw the backgrounds first
+            for (int i = 0; i < board.cols; i++)
+                for (int j = 0; j < board.rows; j++)
+                    this.draw_field(this, ctx, j, i);
+
+            ctx.set_source_rgb(52, 95, 108);
+            ctx.set_line_width(line_width);
+
+            for (int i = 0; i < board.rows; i++)
+            {
+                ctx.move_to(0.5 + i * (boxsize_h + line_width), 0.5);
+                ctx.line_to(0.5 + i * (boxsize_h + line_width), 0.5 + h);
+            }
+
+            for (int i = 0; i < board.cols; i++)
+            {
+                ctx.move_to(0.5 + 0, 0.5 + i * (boxsize_v + line_width));
+                ctx.line_to(0.5 + w, 0.5 + i * (boxsize_v + line_width));
+            }
+
+            ctx.rectangle(0.5, 0.5, w - 0.5, h - 0.5);
+            ctx.stroke();
+
+            /* Cursor */
+            if (board.show_cursor)
+            {
+                ctx.set_source_rgb(255, 0, 0);
+                ctx.set_line_width(line_width);
+
+                ctx.rectangle(board.cursor_x * (boxsize_h + line_width) + 1.5, board.cursor_y * (boxsize_v + line_width) + 1.5, (boxsize_h + line_width) - 2.5, (boxsize_v + line_width) - 2.5);
+                ctx.stroke();
+            }
+
+            return true;
+        }
+
+        private void draw_field(Widget da, Context ctx, int x, int y)
+        {
+            int w = da.get_allocated_width();
+            int h = da.get_allocated_height();
+
+            double boxsizeH = (w - (1 + board.rows) * line_width) / board.rows;
+            double boxsizeV = (h - (1 + board.cols) * line_width) / board.cols;
+
+            var field = board.fields[x, y];
+
+            ctx.set_source_rgb(50, 50, 50);
+
+            ctx.rectangle(x * (boxsizeH + line_width) + 1.5, y * (boxsizeV + line_width) + 1.5, (boxsizeH + line_width) - 2.5, (boxsizeV + line_width) - 2.5);
+            ctx.fill();
+
+            if (field.piece != null)
+            {
+                this.draw_piece(da, ctx, field.piece, x, y);
+            }
+        }
+
+        private void draw_piece(Widget da, Context ctx, GlinesPiece p, int x, int y)
+        {
+            int w = da.get_allocated_width();
+            int h = da.get_allocated_height();
+
+            double boxsizeH = (w - (1 + board.rows) * line_width) / board.rows;
+            double boxsizeV = (h - (1 + board.cols) * line_width) / board.cols;
+
+            //TODO: look up surfaces instead. This is just for debug:
+            if (p.id == 0)
+                ctx.set_source_rgb(255, 0, 0);
+            else if (p.id == 1)
+                ctx.set_source_rgb(0, 255, 0);
+            else if (p.id == 2)
+                ctx.set_source_rgb(0, 0, 255);
+            else
+                ctx.set_source_rgb(0, 0, 0);
+
+
+            ctx.rectangle(x * (boxsizeH + line_width) + 1.5, y * (boxsizeV + line_width) + 1.5, (boxsizeH + line_width) - 2.5, (boxsizeV + line_width) - 2.5);
+            ctx.fill();
+        }
+
+        public override bool key_press_event (EventKey e)
+        {
+            var  key = e.keyval;
+
+            if (key == Gdk.Key.Up) this.board.cursor_y--;
+            if (key == Gdk.Key.Down) this.board.cursor_y++;
+            if (key == Gdk.Key.Left) this.board.cursor_x--;
+            if (key == Gdk.Key.Right) this.board.cursor_x++;
+
+            if (key == Gdk.Key.space) board.select_field(board.cursor_x, board.cursor_y);
+
+            return true;
+        }
+
+        public override bool button_press_event (EventButton e)
+        {
+            double boxsize_x = this.get_allocated_width() / (board.rows * 1.0);
+            int x = (int)(e.x / boxsize_x);
+
+            double boxsize_y = this.get_allocated_height() / (board.cols * 1.0);
+            int y = (int)(e.y / boxsize_y);
+
+            board.place_cursor(x, y);
+            board.select_field(x, y);
+
+            return false;
+        }
+    }
+}
+
diff --git a/src/view-cli.vala b/src/view-cli.vala
new file mode 100644
index 0000000..03b52cb
--- /dev/null
+++ b/src/view-cli.vala
@@ -0,0 +1,146 @@
+using Gee;
+
+namespace FiveOrMore
+{
+    class ViewCli
+    {
+        private GlinesBoard board;
+
+        private bool gameover = false;
+        private string message = "";
+        private char[] id_to_char_symbol = { 'a', 'b', 'c', 'd', 'e' };
+
+        public ViewCli(GlinesBoard board)
+        {
+            this.board = board;
+            board.show_cursor = true;
+            board.place_cursor(5,5);
+
+            board.changed.connect ((o) => this.print(null));
+            board.move.connect ((o, path) => this.animate_move(path));
+            board.info.connect ((o, info) => message = info);
+            board.gameover.connect ((o) => this.gameover = true);
+
+            this.print(null);
+        }
+
+        public void run()
+        {
+            while (!gameover)
+            {
+                var c = stdin.getc();
+
+                if (c == 49) //southwest
+                    board.move_cursor(-1, 1);
+
+                if (c == 50) //south
+                    board.move_cursor(0, 1);
+
+                if (c == 51) //southeast
+                    board.move_cursor(1, 1);
+
+                if (c == 52) //west
+                    board.move_cursor(-1, 0);
+
+                if (c == 54) //east
+                    board.move_cursor(1, 0);
+
+                if (c == 55) //northwest
+                    board.move_cursor(-1, -1);
+
+                if (c == 56) //north
+                    board.move_cursor(0, -1);
+
+                if (c == 57) //northeast
+                    board.move_cursor(1, -1);
+
+                if (c == 53) //click
+                    board.select_field(board.cursor_x, board.cursor_y);
+            }
+
+            stdout.printf("Game over.\n");
+        }
+
+        private void print(ArrayList<GlinesField>? path)
+        {
+            var sb = new StringBuilder();
+
+            sb.append("Preview: ");
+            foreach (var preview in board.preview_queue.pieces)
+                sb.append_c(id_to_char_symbol[preview.id]);
+            sb.append("\n\n");
+
+            for (int n = 0; n < board.cols + 2; n++)
+                sb.append("#");
+            sb.append("\n");
+
+            for (int y = 0; y < board.rows; y++)
+            {
+                sb.append("#");
+                for (int x = 0; x < board.cols; x++)
+                {
+                    var field = board.fields[x,y];
+
+                    bool is_cursor = board.cursor_x == x && board.cursor_y == y;
+
+                    if (field.active)
+                    {
+                        sb.append("@");
+                        continue;
+                    }
+
+                    if (!field.has_piece)
+                    {
+                        if (is_cursor) sb.append("Â");
+                        else
+                        {
+                            if (path != null && field.path_search != null && path.contains(field))
+                            {
+                                if (field.path_search == field.neigbour_north)
+                                    sb.append("â");
+                                if (field.path_search == field.neigbour_south)
+                                    sb.append("â");
+                                if (field.path_search == field.neigbour_west)
+                                    sb.append("â");
+                                if (field.path_search == field.neigbour_east)
+                                    sb.append("â");
+                            }
+                            else
+                                sb.append(" ");
+                        }
+                    }
+                    else
+                    {
+                        char symbol = id_to_char_symbol[field.piece.id];
+
+                        if (is_cursor)
+                            sb.append_c(symbol.toupper());
+                        else
+                            sb.append_c(symbol);
+                    }
+                }
+
+                sb.append("#\n");
+            }
+
+            for (int n = 0; n < board.cols + 2; n++)
+                sb.append("#");
+
+            sb.append("\n\n");
+            sb.append(message);
+            sb.append("\n");
+
+            stdout.printf(sb.str);
+        }
+
+        private void animate_move(ArrayList<GlinesField> path)
+        {
+            //"animate" the move:
+            this.message = "'Animating' the move...";
+            this.print(path);
+            this.message = "";
+            stdin.getc();
+        }
+    }
+}
+



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