[iagno] Themes' dialog.



commit 26ceea8597ca170465e8356bce09be13412f8144
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date:   Fri Feb 13 19:28:17 2015 +0100

    Themes' dialog.

 configure.ac                       |    1 +
 data/Makefile.am                   |    7 ++-
 data/iagno-menus.ui                |    4 +
 data/iagno-themes.ui               |   80 +++++++++++++++++++++
 data/iagno.css                     |   37 ++++++++++
 data/iagno.ui                      |    2 +-
 data/org.gnome.iagno.gschema.xml   |    6 +-
 data/themes/Makefile.am            |   13 ++++
 data/themes/adwaita.theme.in       |   32 +++++++++
 data/themes/high_contrast.theme.in |   32 +++++++++
 data/themes/sun_and_star.theme.in  |   32 +++++++++
 po/POTFILES.in                     |    5 ++
 po/POTFILES.skip                   |    1 +
 src/Makefile.am                    |    2 +
 src/game-view.vala                 |  134 +++++++++++++++++++++++++++++-------
 src/iagno.gresource.xml            |    1 +
 src/iagno.vala                     |  136 +++++++++++++++++++++++++-----------
 src/themes.vala                    |  104 +++++++++++++++++++++++++++
 18 files changed, 557 insertions(+), 72 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 377180e..cdbd2e8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -65,6 +65,7 @@ data/Makefile
 data/icons/Makefile
 data/icons/hicolor/Makefile
 data/icons/HighContrast/Makefile
+data/themes/Makefile
 help/Makefile
 src/Makefile
 ])
diff --git a/data/Makefile.am b/data/Makefile.am
index 5f98865..ebe6885 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -1,6 +1,9 @@
-SUBDIRS = icons
+SUBDIRS = \
+       icons \
+       themes
 
 dist_noinst_DATA = \
+       iagno-themes.ui \
        iagno-menus.ui \
        iagno.ui \
        iagno.css \
@@ -8,7 +11,7 @@ dist_noinst_DATA = \
        light.svg \
        mark.svg
 
-themedir = $(datadir)/iagno/themes
+themedir = $(datadir)/iagno/themes/svg
 theme_DATA = \
        sun_and_star.svg \
        black_and_white.svg
diff --git a/data/iagno-menus.ui b/data/iagno-menus.ui
index 56cb8b5..82e755a 100644
--- a/data/iagno-menus.ui
+++ b/data/iagno-menus.ui
@@ -4,6 +4,10 @@
   <menu id="app-menu">
     <section>
       <item>
+        <attribute name="label" translatable="yes">_Theme</attribute>
+        <attribute name="action">app.theme</attribute>
+      </item>
+      <item>
         <attribute name="label" translatable="yes">_Sound</attribute>
         <attribute name="action">app.sound</attribute>
       </item>
diff --git a/data/iagno-themes.ui b/data/iagno-themes.ui
new file mode 100644
index 0000000..30396a3
--- /dev/null
+++ b/data/iagno-themes.ui
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.12"/>
+  <template class="ThemesDialog" parent="GtkDialog">
+    <property name="visible">False</property>
+    <property name="width-request">333</property>
+    <property name="height-request">450</property>
+    <property name="resizable">False</property>
+    <property name="title" translatable="yes">Theme</property>
+    <property name="border-width">0</property>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="visible">True</property>
+        <property name="border-width">0</property>
+        <child>
+          <object class="GtkScrolledWindow">
+            <property name="visible">True</property>
+            <property name="margin">0</property>
+            <property name="hscrollbar-policy">never</property>
+            <property name="vscrollbar-policy">automatic</property>
+            <property name="shadow-type">none</property>
+            <property name="valign">fill</property>
+            <child>
+              <object class="GtkFrame">
+                <property name="visible">True</property>
+                <property name="shadow-type">none</property>
+                <child>
+                  <object class="GtkListBox" id="listbox">
+                    <property name="visible">True</property>
+                    <property name="name">themes-listbox</property>
+                    <property name="activate-on-single-click">True</property>
+                    <property name="selection-mode">single</property>
+                    <child>
+                      <object class="GtkListBoxRow">
+                        <property name="visible">True</property>
+                        <property name="name">default</property>
+                        <property name="height-request">50</property>
+                        <child>
+                          <object class="GtkBox">
+                            <property name="visible">True</property>
+                            <property name="orientation">horizontal</property>
+                            <child>
+                              <object class="GtkImage">
+                                <property name="visible">True</property>
+                                <property name="icon-name">object-select-symbolic</property>
+                                <property name="icon-size">1</property>
+                                <property name="width-request">50</property>
+                                <style><class name="theme-image"/></style>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkLabel">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Default</property>
+                                <style><class name="italic-label"/></style>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkLabel">
+                                <property name="visible">False</property>
+                                <property name="label">default</property>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/data/iagno.css b/data/iagno.css
index 76de693..7965668 100644
--- a/data/iagno.css
+++ b/data/iagno.css
@@ -1,3 +1,40 @@
+/* -*- Mode: vala; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * Copyright (C) 2015 Arnaud Bonatti <arnaud bonatti gmail com>
+ *
+ * This file is part of a GNOME game.
+ *
+ * This application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this application. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Labels' fonts */
 GtkLabel.bold-label {
   font-weight: bold;
 }
+GtkLabel.italic-label {
+  font-style: italic;
+}
+
+/* Themes' dialog listbox */
+GtkListBox#themes-listbox GtkImage {
+  transition-duration: 200ms;
+  opacity: 0;
+}
+GtkListBox#themes-listbox GtkListBoxRow:selected GtkImage {
+  opacity: 1;
+  color:@theme_selected_fg_color;
+}
+GtkListBox#themes-listbox GtkListBoxRow:selected GtkImage:backdrop {
+  color:@theme_unfocused_selected_fg_color;
+}
diff --git a/data/iagno.ui b/data/iagno.ui
index 407bfdc..34ed20b 100644
--- a/data/iagno.ui
+++ b/data/iagno.ui
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="3.12"/>
-  <object class="GtkApplicationWindow" id="iagno-window">
+  <object class="GtkApplicationWindow" id="window">
     <property name="title" translatable="yes">Iagno</property>
     <child type="titlebar">
       <object class="GtkHeaderBar" id="headerbar">
diff --git a/data/org.gnome.iagno.gschema.xml b/data/org.gnome.iagno.gschema.xml
index 35d67d1..173340d 100644
--- a/data/org.gnome.iagno.gschema.xml
+++ b/data/org.gnome.iagno.gschema.xml
@@ -21,8 +21,10 @@
       <summary>Color to play as</summary>
       <description>Whether to play as Dark or Light. Ignored for two-player games.</description>
     </key>
-    <key name="tileset" type="s">
-      <default>'black_and_white.svg'</default>
+    <key name="theme" type="s">
+      <default>'default'</default>
+      <summary>Theme</summary>
+      <description>Filename of the theme used, or "default". Are provided "adwaita.theme", 
"high_contrast.theme" and "sun_and_star.theme".</description>
     </key>
     <key name="sound" type="b">
       <default>true</default>
diff --git a/data/themes/Makefile.am b/data/themes/Makefile.am
new file mode 100644
index 0000000..50125c2
--- /dev/null
+++ b/data/themes/Makefile.am
@@ -0,0 +1,13 @@
+themes_keydir = $(datadir)/iagno/themes/key
+themes_key_files = \
+       adwaita.theme.in \
+       high_contrast.theme.in \
+       sun_and_star.theme.in
+themes_key_DATA = $(themes_key_files:.theme.in=.theme)
+
+%.theme: %.theme.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po)
+       $(AM_V_GEN) LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache 
$(top_srcdir)/po $< $@
+
+EXTRA_DIST = $(themes_key_DATA)
+
+-include $(top_srcdir)/git.mk
diff --git a/data/themes/adwaita.theme.in b/data/themes/adwaita.theme.in
new file mode 100644
index 0000000..078d637
--- /dev/null
+++ b/data/themes/adwaita.theme.in
@@ -0,0 +1,32 @@
+[Theme]
+Id=adwaita
+_Name=Adwaita
+
+[Pieces]
+#Animated=True
+File=black_and_white.svg
+
+[Background]
+#Image=
+Red=0.3
+Green=0.6
+Blue=0.4
+
+[Border]
+Red=0.0
+Green=0.0
+Blue=0.0
+Width=3
+
+[Spacing]
+Red=0.1
+Green=0.3
+Blue=0.2
+Width=2
+
+[Margin]
+#Width=0
+
+[Sound]
+Flip=flip-piece.ogg
+GameOver=gameover.ogg
diff --git a/data/themes/high_contrast.theme.in b/data/themes/high_contrast.theme.in
new file mode 100644
index 0000000..b199b35
--- /dev/null
+++ b/data/themes/high_contrast.theme.in
@@ -0,0 +1,32 @@
+[Theme]
+Id=high_contrast
+_Name=High Contrast
+
+[Pieces]
+#Animated=True
+File=black_and_white.svg
+
+[Background]
+#Image=
+Red=1.0
+Green=1.0
+Blue=1.0
+
+[Border]
+Red=0.0
+Green=0.0
+Blue=0.0
+Width=3
+
+[Spacing]
+Red=0.0
+Green=0.0
+Blue=0.0
+Width=2
+
+[Margin]
+#Width=0
+
+[Sound]
+Flip=flip-piece.ogg
+GameOver=gameover.ogg
diff --git a/data/themes/sun_and_star.theme.in b/data/themes/sun_and_star.theme.in
new file mode 100644
index 0000000..44dbf64
--- /dev/null
+++ b/data/themes/sun_and_star.theme.in
@@ -0,0 +1,32 @@
+[Theme]
+Id=sun_and_star
+_Name=Sun and Star
+
+[Pieces]
+#Animated=True
+File=sun_and_star.svg
+
+[Background]
+#Image=
+Red=0.3
+Green=0.6
+Blue=0.4
+
+[Border]
+Red=0.0
+Green=0.0
+Blue=0.0
+Width=3
+
+[Spacing]
+Red=0.1
+Green=0.3
+Blue=0.2
+Width=2
+
+[Margin]
+#Width=0
+
+[Sound]
+Flip=flip-piece.ogg
+GameOver=gameover.ogg
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 0acece1..3995379 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -4,10 +4,15 @@
 data/iagno.appdata.xml.in
 data/iagno.desktop.in
 [type: gettext/glade]data/iagno-menus.ui
+[type: gettext/glade]data/iagno-themes.ui
 [type: gettext/glade]data/iagno.ui
+[type: gettext/ini]data/themes/adwaita.theme.in
+[type: gettext/ini]data/themes/high_contrast.theme.in
+[type: gettext/ini]data/themes/sun_and_star.theme.in
 data/org.gnome.iagno.gschema.xml
 src/computer-player.vala
 src/game.vala
 src/game-view.vala
 src/iagno.vala
 src/player.vala
+src/themes.vala
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index d0ce332..fbd1b9f 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -3,3 +3,4 @@ src/game.c
 src/game-view.c
 src/iagno.c
 src/player.c
+src/themes.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 7c52e6c..117b5e8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,6 +8,7 @@ iagno_SOURCES = \
        computer-player.vala \
        game.vala \
        game-view.vala \
+       themes.vala \
        iagno.vala \
        player.vala \
        $(BUILT_SOURCES)
@@ -28,6 +29,7 @@ iagno_VALAFLAGS = \
        --pkg librsvg-2.0 \
        --pkg libcanberra \
        --pkg libcanberra-gtk \
+       --gresources=$(builddir)/iagno.gresource.xml \
        --target-glib 2.40
 
 iagno_LDADD = $(IAGNO_LIBS)
diff --git a/src/game-view.vala b/src/game-view.vala
index cbd31fa..2d5740c 100644
--- a/src/game-view.vala
+++ b/src/game-view.vala
@@ -21,14 +21,32 @@
 public class GameView : Gtk.DrawingArea
 {
     /* Theme */
-    private const int GRID_BORDER = 3;
-    private const int GRID_SPACING = 2;
+    private string pieces_file;
+
+    private double background_red;
+    private double background_green;
+    private double background_blue;
+
+    private double border_red;
+    private double border_green;
+    private double border_blue;
+    private int border_width;
+
+    private double spacing_red;
+    private double spacing_green;
+    private double spacing_blue;
+    private int spacing_width;
+
+    // private int margin_width;
+
+    public string sound_flip     { get; private set; }
+    public string sound_gameover { get; private set; }
 
     /* Utilities, see calculate () */
     private int tile_size;
     private int board_size;
-    private int x_offset { get { return (get_allocated_width () - board_size) / 2 + GRID_BORDER; }}
-    private int y_offset { get { return (get_allocated_height () - board_size) / 2 + GRID_BORDER; }}
+    private int x_offset { get { return (get_allocated_width () - board_size) / 2 + border_width; }}
+    private int y_offset { get { return (get_allocated_height () - board_size) / 2 + border_width; }}
 
     /* Delay in milliseconds between tile flip frames */
     private const int PIXMAP_FLIP_DELAY = 20;
@@ -73,7 +91,69 @@ public class GameView : Gtk.DrawingArea
     public string? theme
     {
         get { return _theme; }
-        set { _theme = value; tiles_pattern = null; queue_draw (); }
+        set {
+            if (value == "default")
+            {
+                set_default_theme ();
+                _theme = "default";
+            }
+            else
+            {
+                var key = new GLib.KeyFile ();
+                key.load_from_file (Path.build_filename (DATA_DIRECTORY, "themes", "key", value), 
GLib.KeyFileFlags.NONE);
+
+                // TODO try ... catch { set_default_theme (); _theme="default"; }
+                load_theme (key);
+                _theme = value;
+            }
+
+            // redraw all
+            tiles_pattern = null;
+            queue_draw ();
+        }
+    }
+
+    private void set_default_theme ()
+    {
+        var defaults = Gtk.Settings.get_default ();
+        var key = new GLib.KeyFile ();
+        string filename;
+        if (defaults.gtk_theme_name == "HighContrast")
+            filename = "high_contrast.theme";
+        /* else if (defaults.gtk_application_prefer_dark_theme == true)     // TODO
+            filename = "adwaita_dark.theme"; */
+        else
+            filename = "adwaita.theme";
+        key.load_from_file (Path.build_filename (DATA_DIRECTORY, "themes", "key", filename), 
GLib.KeyFileFlags.NONE);
+        load_theme (key);
+    }
+
+    private void load_theme (GLib.KeyFile key)
+    {
+        string path = Path.build_filename (DATA_DIRECTORY, "themes", "svg");
+
+        pieces_file = Path.build_filename (path, key.get_string ("Pieces", "File"));
+        if (Path.get_dirname (pieces_file) != path)     // security
+            pieces_file = Path.build_filename (path, "black_and_white.svg");
+
+        background_red   = key.get_double  ("Background", "Red");
+        background_green = key.get_double  ("Background", "Green");
+        background_blue  = key.get_double  ("Background", "Blue");
+
+        border_red       = key.get_double  ("Border", "Red");
+        border_green     = key.get_double  ("Border", "Green");
+        border_blue      = key.get_double  ("Border", "Blue");
+        border_width     = key.get_integer ("Border", "Width");
+
+        spacing_red      = key.get_double  ("Spacing", "Red");
+        spacing_green    = key.get_double  ("Spacing", "Green");
+        spacing_blue     = key.get_double  ("Spacing", "Blue");
+        spacing_width    = key.get_integer ("Spacing", "Width");
+
+        // margin_width = key.get_integer  ("Margin", "Width");
+
+        sound_flip       = key.get_string  ("Sound", "Flip");
+        sound_gameover   = key.get_string  ("Sound", "GameOver");
     }
 
     public GameView ()
@@ -86,9 +166,9 @@ public class GameView : Gtk.DrawingArea
     {
         var size = int.min (get_allocated_width (), get_allocated_height ());
         /* tile_size includes a grid spacing */
-        tile_size = (size - 2 * GRID_BORDER + GRID_SPACING) / game.size;
+        tile_size = (size - 2 * border_width + spacing_width) / game.size;
         /* board_size includes its borders */
-        board_size = tile_size * game.size - GRID_SPACING + 2 * GRID_BORDER;
+        board_size = tile_size * game.size - spacing_width + 2 * border_width;
     }
 
     public override bool draw (Cairo.Context cr)
@@ -103,34 +183,38 @@ public class GameView : Gtk.DrawingArea
             render_size = tile_size;
             var surface = new Cairo.Surface.similar (cr.get_target (), Cairo.Content.COLOR_ALPHA, tile_size 
* 8, tile_size * 4);
             var c = new Cairo.Context (surface);
-            load_theme (c);
+            load_image (c);
             tiles_pattern = new Cairo.Pattern.for_surface (surface);
         }
 
         cr.translate (x_offset, y_offset);
 
-        /* draw border and background */
-        cr.set_source_rgba (0.3, 0.6, 0.4, 1.0);
-        cr.rectangle (-GRID_BORDER / 2.0, -GRID_BORDER / 2.0, board_size - GRID_BORDER, board_size - 
GRID_BORDER);
-        cr.fill_preserve ();
-        cr.set_source_rgba (0.0, 0.0, 0.0, 1.0);
-        cr.set_line_width (GRID_BORDER);
-        cr.stroke ();
+        /* draw background; TODO save for border */
+        cr.set_source_rgba (background_red, background_green, background_blue, 1.0);
+        cr.rectangle (-border_width / 2.0, -border_width / 2.0, board_size - border_width, board_size - 
border_width);
+        cr.fill ();
 
         /* draw lines */
-        cr.set_line_width (GRID_SPACING);
+        cr.set_source_rgba (spacing_red, spacing_green, spacing_blue, 1.0);
+        cr.set_line_width (spacing_width);
         for (var i = 1; i < game.size; i++)
         {
-            cr.move_to (i * tile_size - GRID_SPACING / 2.0, 0);
-            cr.rel_line_to (0, board_size - GRID_BORDER);
+            cr.move_to (i * tile_size - spacing_width / 2.0, 0);
+            cr.rel_line_to (0, board_size - border_width);
 
-            cr.move_to (0, i * tile_size - GRID_SPACING / 2.0);
-            cr.rel_line_to (board_size - GRID_BORDER, 0);
+            cr.move_to (0, i * tile_size - spacing_width / 2.0);
+            cr.rel_line_to (board_size - border_width, 0);
         }
         cr.stroke ();
 
+        /* draw border */
+        cr.set_source_rgba (border_red, border_green, border_blue, 1.0);
+        cr.set_line_width (border_width);
+        cr.rectangle (-border_width / 2.0, -border_width / 2.0, board_size - border_width, board_size - 
border_width);
+        cr.stroke ();
+
         /* draw pieces */
-        cr.translate (-GRID_SPACING / 2, -GRID_SPACING / 2);
+        cr.translate (-spacing_width / 2, -spacing_width / 2);
         for (var x = 0; x < game.size; x++)
         {
             for (var y = 0; y < game.size; y++)
@@ -154,14 +238,14 @@ public class GameView : Gtk.DrawingArea
         return false;
     }
 
-    private void load_theme (Cairo.Context c)
+    private void load_image (Cairo.Context c)
     {
         var width = tile_size * 8;
         var height = tile_size * 4;
 
         try
         {
-            var h = new Rsvg.Handle.from_file (theme);
+            var h = new Rsvg.Handle.from_file (pieces_file);
 
             var m = Cairo.Matrix.identity ();
             m.scale ((double) width / h.width, (double) height / h.height);
@@ -177,13 +261,13 @@ public class GameView : Gtk.DrawingArea
 
         try
         {
-            var p = new Gdk.Pixbuf.from_file_at_scale (theme, width, height, false);
+            var p = new Gdk.Pixbuf.from_file_at_scale (pieces_file, width, height, false);
             Gdk.cairo_set_source_pixbuf (c, p, 0, 0);
             c.paint ();
         }
         catch (Error e)
         {
-            warning ("Failed to load theme %s: %s", theme, e.message);
+            warning ("Failed to load theme image %s: %s", pieces_file, e.message);
         }
     }
 
diff --git a/src/iagno.gresource.xml b/src/iagno.gresource.xml
index 8260b10..7e29f66 100644
--- a/src/iagno.gresource.xml
+++ b/src/iagno.gresource.xml
@@ -7,6 +7,7 @@
   </gresource>
   <gresource prefix="/org/gnome/iagno/ui">
     <file preprocess="xml-stripblanks" alias="iagno.ui">../data/iagno.ui</file>
+    <file preprocess="xml-stripblanks" alias="themes.ui">../data/iagno-themes.ui</file>
     <file alias="iagno.css">../data/iagno.css</file>
   </gresource>
   <gresource prefix="/org/gnome/iagno/gtk">
diff --git a/src/iagno.vala b/src/iagno.vala
index cd97aa6..d14b7d0 100644
--- a/src/iagno.vala
+++ b/src/iagno.vala
@@ -49,6 +49,7 @@ public class Iagno : Gtk.Application
     private Gtk.Label dark_score_label;
     private Gtk.Label light_score_label;
     private Gtk.Stack main_stack;
+    private ThemesDialog themes_dialog;
 
     private Gtk.Button back_button;
     private Gtk.Button undo_button;
@@ -87,6 +88,7 @@ public class Iagno : Gtk.Application
         {"undo-move", undo_move_cb},
         {"back", back_cb},
 
+        {"theme", theme_cb},
         {"help", help_cb},
         {"about", about_cb},
         {"quit", quit}
@@ -209,7 +211,7 @@ public class Iagno : Gtk.Application
         color_box.sensitive = settings.get_int ("num-players") == 1;
 
         /* Window construction */
-        window = builder.get_object ("iagno-window") as Gtk.ApplicationWindow;
+        window = builder.get_object ("window") as Gtk.ApplicationWindow;
         window.size_allocate.connect (size_allocate_cb);
         window.window_state_event.connect (window_state_event_cb);
         window.set_default_size (settings.get_int ("window-width"), settings.get_int ("window-height"));
@@ -228,8 +230,7 @@ public class Iagno : Gtk.Application
         /* View construction */
         view = new GameView ();
         view.move.connect (player_move_cb);
-        var tile_set = settings.get_string ("tileset");
-        view.theme = Path.build_filename (DATA_DIRECTORY, "themes", tile_set);
+        view.theme = settings.get_string ("theme");
         view.halign = Gtk.Align.FILL;
         view.show ();
 
@@ -271,6 +272,10 @@ public class Iagno : Gtk.Application
         settings.set_boolean ("window-is-maximized", is_maximized);
     }
 
+    /*\
+    * * Window events
+    \*/
+
     private void size_allocate_cb (Gtk.Allocation allocation)
     {
         if (is_maximized || is_tiled)
@@ -289,6 +294,60 @@ public class Iagno : Gtk.Application
         return false;
     }
 
+    /*\
+    * * App-menu callbacks
+    \*/
+
+    private void theme_cb ()
+    {
+        /* Don’t permit to open more than one dialog */
+        if (themes_dialog == null)
+        {
+            themes_dialog = new ThemesDialog (settings, view);
+            themes_dialog.set_transient_for (window);
+        }
+        themes_dialog.present ();
+    }
+
+    private void help_cb ()
+    {
+        try
+        {
+            Gtk.show_uri (window.get_screen (), "help:iagno", Gtk.get_current_event_time ());
+        }
+        catch (Error e)
+        {
+            warning ("Failed to show help: %s", e.message);
+        }
+    }
+
+    private void about_cb ()
+    {
+        string[] authors = { "Ian Peters", "Robert Ancell", null };
+        string[] documenters = { "Tiffany Antopolski", null };
+
+        Gtk.show_about_dialog (window,
+                               "name", _("Iagno"),
+                               "version", VERSION,
+                               "copyright",
+                                 "Copyright © 1998–2008 Ian Peters\n"+
+                                 "Copyright © 2013–2015 Michael Catanzaro\n"+
+                                 "Copyright © 2014–2015 Arnaud Bonatti",
+                               "license-type", Gtk.License.GPL_3_0,
+                               "comments",
+                                 _("A disk flipping game derived from Reversi"),
+                               "authors", authors,
+                               "documenters", documenters,
+                               "translator-credits", _("translator-credits"),
+                               "logo-icon-name", "iagno",
+                               "website", "https://wiki.gnome.org/Apps/Iagno";,
+                               null);
+    }
+
+    /*\
+    * * Internal calls
+    \*/
+
     private void start_game_cb ()
     {
         main_stack.set_transition_type (Gtk.StackTransitionType.SLIDE_DOWN);
@@ -405,7 +464,7 @@ public class Iagno : Gtk.Application
         }
 
         update_ui ();
-        play_sound ("flip-piece");
+        play_sound (Sound.FLIP);
     }
 
     private void turn_ended_cb ()
@@ -422,7 +481,7 @@ public class Iagno : Gtk.Application
     private void prepare_move ()
     {
         /* for the move that just ended */
-        play_sound ("flip-piece");
+        play_sound (Sound.FLIP);
 
         /*
          * Get the computer to move after a delay, so it looks like it's
@@ -441,7 +500,7 @@ public class Iagno : Gtk.Application
     private void pass ()
     {
         /* for the move that just ended */
-        play_sound ("flip-piece");
+        play_sound (Sound.FLIP);
 
         game.pass ();
         if (game.current_color == Player.DARK)
@@ -475,17 +534,7 @@ public class Iagno : Gtk.Application
         }
 
         if (play_gameover_sound)
-            play_sound ("gameover");
-    }
-
-    private void play_sound (string name)
-    {
-        if (!settings.get_boolean ("sound"))
-            return;
-
-        CanberraGtk.play_for_widget (view, 0,
-                                     Canberra.PROP_MEDIA_NAME, name,
-                                     Canberra.PROP_MEDIA_FILENAME, Path.build_filename (SOUND_DIRECTORY, 
"%s.ogg".printf (name)));
+            play_sound (Sound.GAMEOVER);
     }
 
     private void player_move_cb (int x, int y)
@@ -501,35 +550,38 @@ public class Iagno : Gtk.Application
         }
     }
 
-    private void help_cb ()
+    /*\
+    * * Sound
+    \*/
+
+    private enum Sound
     {
-        try
-        {
-            Gtk.show_uri (window.get_screen (), "help:iagno", Gtk.get_current_event_time ());
-        }
-        catch (Error e)
-        {
-            warning ("Failed to show help: %s", e.message);
-        }
+        FLIP,
+        GAMEOVER;
     }
 
-    private void about_cb ()
+    private void play_sound (Sound sound)
     {
-        string[] authors = { "Ian Peters", "Robert Ancell", null };
-        string[] documenters = { "Tiffany Antopolski", null };
+        if (!settings.get_boolean ("sound"))
+            return;
 
-        Gtk.show_about_dialog (window,
-                               "name", _("Iagno"),
-                               "version", VERSION,
-                               "copyright",
-                               "Copyright © 1998–2008 Ian Peters\nCopyright © 2013–2015 Michael Catanzaro",
-                               "license-type", Gtk.License.GPL_3_0,
-                               "comments", _("A disk flipping game derived from Reversi"),
-                               "authors", authors,
-                               "documenters", documenters,
-                               "translator-credits", _("translator-credits"),
-                               "logo-icon-name", "iagno",
-                               "website", "https://wiki.gnome.org/Apps/Iagno";,
-                               null);
+        string name;
+        switch (sound)
+        {
+            case Sound.FLIP:
+                name = view.sound_flip;
+                break;
+            case Sound.GAMEOVER:
+                name = view.sound_gameover;
+                break;
+            default:
+                return;
+        }
+        string path = Path.build_filename (SOUND_DIRECTORY, name);
+        int r = CanberraGtk.play_for_widget (view, 0,
+                                             Canberra.PROP_MEDIA_NAME, name,
+                                             Canberra.PROP_MEDIA_FILENAME, path);
+        if (r != 0)
+            warning ("Error playing file: %s\nfilepath should be:%s", name, path);
     }
 }
diff --git a/src/themes.vala b/src/themes.vala
new file mode 100644
index 0000000..eb064ad
--- /dev/null
+++ b/src/themes.vala
@@ -0,0 +1,104 @@
+/* -*- Mode: vala; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * Copyright (C) 2015 Arnaud Bonatti <arnaud bonatti gmail com>
+ *
+ * This file is part of a GNOME game.
+ *
+ * This application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this application. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+using Gtk;
+
+[GtkTemplate (ui = "/org/gnome/iagno/ui/themes.ui")]
+public class ThemesDialog : Dialog
+{
+    private static const string PREFIX = "theme-";
+
+    private GameView view;
+
+    [GtkChild]
+    private ListBox listbox;
+
+    public ThemesDialog (GLib.Settings settings, GameView view)
+    {
+        Object (use_header_bar: Gtk.Settings.get_default ().gtk_dialogs_use_header ? 1 : 0);
+        this.view = view;
+        delete_event.connect (do_not_close);
+
+        /* load themes key files */
+        Dir dir;
+        string theme = settings.get_string ("theme");
+        try
+        {
+            dir = Dir.open (Path.build_filename (DATA_DIRECTORY, "themes", "key"));
+            while (true)
+            {
+                string filename = dir.read_name ();
+                if (filename == null)
+                    break;
+
+                string path = Path.build_filename (DATA_DIRECTORY, "themes", "key", filename);
+                var key = new GLib.KeyFile ();
+                try
+                {
+                    key.load_from_file (path, GLib.KeyFileFlags.NONE);
+                }
+                catch (GLib.KeyFileError e)
+                {
+                    warning ("oops: %s", e.message);
+                }
+
+                var row = new ListBoxRow ();
+                row.visible = true;
+                row.height_request = 50;
+                var box = new Box (Orientation.HORIZONTAL, 0);
+                box.visible = true;
+                var img = new Image ();
+                img.visible = true;
+                img.width_request = 50;
+                img.icon_name = "object-select-symbolic";
+                var lbl = new Label (key.get_locale_string ("Theme", "Name"));      // TODO test translation
+                lbl.visible = true;
+                lbl.xalign = 0;
+                var data = new Label (filename);
+                data.visible = false;
+
+                box.add (img);
+                box.add (lbl);
+                box.add (data);
+                row.add (box);
+                listbox.add (row);
+
+                if (filename == settings.get_string ("theme"))
+                    listbox.select_row (row);
+            }
+            // FIXME bug on <ctrl>double-click
+            listbox.row_selected.connect ((row) => {
+                    view.theme = ((Label) (((Box) row.get_child ()).get_children ().nth_data (2))).label;
+                    // TODO BETTER view.theme may have fall back to "default"
+                    settings.set_string ("theme", view.theme);
+                });
+        }
+        catch (FileError e)
+        {
+            warning ("Failed to load themes: %s", e.message);
+        }
+    }
+
+    private bool do_not_close (Widget widget, Gdk.EventAny event)
+    {
+        widget.hide ();
+        return true;
+    }
+}


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