[gnome-chess/chess-telepathy-networking-support-664946-rebase: 36/64] Split out class GnomeChess and class Application in separate files
- From: Chandni Verma <vchandni src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-chess/chess-telepathy-networking-support-664946-rebase: 36/64] Split out class GnomeChess and class Application in separate files
- Date: Sun, 23 Dec 2012 03:41:39 +0000 (UTC)
commit 84cfeb98cc78ff22a6b06d478c54ce14035efecc
Author: Chandni Verma <chandniverma2112 gmail com>
Date: Fri Sep 7 09:14:40 2012 +0530
Split out class GnomeChess and class Application in separate files
This is done so that we don't get 2 main functions in 1 application
src/Makefile.am | 3 +-
src/gnome-chess-application.vala | 1601 ++++++++++++++++++++++++++++++++++++++
src/gnome-chess.vala | 1600 -------------------------------------
3 files changed, 1603 insertions(+), 1601 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index be30bd8..594e51b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -12,6 +12,7 @@ gnome_chess_SOURCES = \
glx.vapi \
3ds.vala \
gnome-chess.vala \
+ gnome-chess-application.vala \
chess-launcher.vala \
ai-profile.vala \
chess-bitboard.vala \
@@ -33,7 +34,7 @@ gnome_chess_channel_handler_SOURCES = \
glu.vapi \
glx.vapi \
3ds.vala \
- gnome-chess.vala \
+ gnome-chess-application.vala \
chess-launcher.vala \
gnome-chess-channel-handler.vala \
ai-profile.vala \
diff --git a/src/gnome-chess-application.vala b/src/gnome-chess-application.vala
new file mode 100644
index 0000000..602124c
--- /dev/null
+++ b/src/gnome-chess-application.vala
@@ -0,0 +1,1601 @@
+/* Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=647122 */
+extern void gtk_file_filter_set_name (Gtk.FileFilter filter, string name);
+
+public class Application : Gtk.Application
+{
+ protected Settings settings;
+ private History history;
+ private Gtk.Builder builder;
+ protected Gtk.Builder preferences_builder;
+ private ChessLauncher? launcher = null;
+ private Gtk.Window? window = null;
+
+ /* Chess game screen widgets */
+ protected Gtk.Widget game_vbox;
+ private Gtk.Widget save_menu;
+ private Gtk.Widget save_as_menu;
+ protected Gtk.MenuItem fullscreen_menu;
+ protected Gtk.InfoBar info_bar;
+ protected Gtk.Label info_title_label;
+ protected Gtk.Label info_label;
+ protected Gtk.Container view_container;
+ protected ChessScene scene;
+ protected ChessView view;
+ private Gtk.Widget undo_menu;
+ private Gtk.Widget undo_button;
+ protected Gtk.Widget resign_menu;
+ private Gtk.Widget resign_button;
+ protected Gtk.Widget first_move_button;
+ protected Gtk.Widget prev_move_button;
+ protected Gtk.Widget next_move_button;
+ protected Gtk.Widget last_move_button;
+ protected Gtk.ComboBox history_combo;
+ protected Gtk.Widget white_time_label;
+ protected Gtk.Widget black_time_label;
+
+ protected Gtk.Dialog? preferences_dialog = null;
+ private Gtk.FileChooserDialog? open_dialog = null;
+ private Gtk.InfoBar? open_dialog_info_bar = null;
+ private Gtk.Label? open_dialog_error_label = null;
+ private Gtk.FileChooserDialog? save_dialog = null;
+ private Gtk.InfoBar? save_dialog_info_bar = null;
+ private Gtk.Label? save_dialog_error_label = null;
+ protected Gtk.AboutDialog? about_dialog = null;
+
+ private PGNGame pgn_game;
+ protected ChessGame game;
+ private bool in_history;
+ private File game_file;
+ private bool game_needs_saving;
+ private string engines_file;
+ private List<AIProfile> ai_profiles;
+ protected ChessPlayer? opponent = null;
+ private ChessPlayer? human_player = null;
+ protected ChessEngine? opponent_engine = null;
+
+ public Application (File? game_file)
+ {
+ Object (application_id: "org.gnome.gnome-chess", flags: ApplicationFlags.FLAGS_NONE);
+ this.game_file = game_file;
+ }
+
+ public override void startup ()
+ {
+ base.startup ();
+
+ settings = new Settings ("org.gnome.gnome-chess.Settings");
+
+ var data_dir = File.new_for_path (Path.build_filename (Environment.get_user_data_dir (), "gnome-chess", null));
+ DirUtils.create_with_parents (data_dir.get_path (), 0755);
+
+ history = new History (data_dir);
+
+ builder = new Gtk.Builder ();
+ try
+ {
+ builder.add_from_file (Path.build_filename (Config.PKGDATADIR, "gnome-chess-game-window.ui", null));
+ }
+ catch (Error e)
+ {
+ warning ("Could not load UI: %s", e.message);
+ }
+
+ settings.changed.connect (settings_changed_cb);
+
+ string engines_file = Path.build_filename (Config.PKGDATADIR, "engines.conf", null);
+ ai_profiles = load_ai_profiles (engines_file);
+
+ foreach (var profile in ai_profiles)
+ message ("Detected AI profile %s in %s", profile.name, profile.path);
+
+ if (game_file == null)
+ {
+ launcher = create_launcher (engines_file, ai_profiles, history);
+
+ var unfinished = history.get_unfinished ();
+ if (unfinished != null)
+ launcher.show_game_selector ();
+ else
+ launcher.show_opponent_selector ();
+
+ /* Show top-level */
+ launcher.show ();
+ }
+ else
+ {
+ try
+ {
+ load_game (game_file, false);
+ }
+ catch (Error e)
+ {
+ stderr.printf ("Failed to load %s: %s\n", game_file.get_path (), e.message);
+ quit ();
+ }
+ }
+
+ }
+
+ private ChessLauncher create_launcher (string engines_file,
+ List<AIProfile>? ai_profiles,
+ History history)
+ {
+ var launcher = new ChessLauncher (engines_file, ai_profiles, history);
+ launcher.application = this;
+
+ launcher.start_game.connect (start_new_game);
+ launcher.load_game.connect (this.load_game_handler);
+ launcher.destroy.connect (launcher_destroy_cb);
+
+ return launcher;
+ }
+
+ private void create_game_window ()
+ {
+ window = (Gtk.Window) builder.get_object ("window_game_screen");
+
+ game_vbox = (Gtk.Widget) builder.get_object ("game_vbox");
+ save_menu = (Gtk.Widget) builder.get_object ("menu_save_item");
+ save_as_menu = (Gtk.Widget) builder.get_object ("menu_save_as_item");
+ fullscreen_menu = (Gtk.MenuItem) builder.get_object ("fullscreen_item");
+ undo_menu = (Gtk.Widget) builder.get_object ("undo_move_item");
+ undo_button = (Gtk.Widget) builder.get_object ("undo_move_button");
+ resign_menu = (Gtk.Widget) builder.get_object ("resign_item");
+ resign_button = (Gtk.Widget) builder.get_object ("resign_button");
+ first_move_button = (Gtk.Widget) builder.get_object ("first_move_button");
+ prev_move_button = (Gtk.Widget) builder.get_object ("prev_move_button");
+ next_move_button = (Gtk.Widget) builder.get_object ("next_move_button");
+ last_move_button = (Gtk.Widget) builder.get_object ("last_move_button");
+ history_combo = (Gtk.ComboBox) builder.get_object ("history_combo");
+ white_time_label = (Gtk.Widget) builder.get_object ("white_time_label");
+ black_time_label = (Gtk.Widget) builder.get_object ("black_time_label");
+ settings.bind ("show-toolbar", builder.get_object ("toolbar"), "visible", SettingsBindFlags.DEFAULT);
+ settings.bind ("show-history", builder.get_object ("navigation_box"), "visible", SettingsBindFlags.DEFAULT);
+ var view_box = (Gtk.VBox) builder.get_object ("view_box");
+ view_container = (Gtk.Container) builder.get_object ("view_container");
+
+ builder.connect_signals (this);
+
+ info_bar = new Gtk.InfoBar ();
+ var content_area = (Gtk.Container) info_bar.get_content_area ();
+ view_box.pack_start (info_bar, false, true, 0);
+ var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 6);
+ vbox.show ();
+ content_area.add (vbox);
+ info_title_label = new Gtk.Label ("");
+ info_title_label.show ();
+ vbox.pack_start (info_title_label, false, true, 0);
+ vbox.hexpand = true;
+ vbox.vexpand = false;
+ info_label = new Gtk.Label ("");
+ info_label.show ();
+ vbox.pack_start (info_label, true, true, 0);
+
+ scene = new ChessScene ();
+ scene.is_human.connect ((p) => { return p == human_player; } );
+ scene.changed.connect (scene_changed_cb);
+ scene.choose_promotion_type.connect (show_promotion_type_selector);
+ settings.bind ("show-move-hints", scene, "show-move-hints", SettingsBindFlags.GET);
+ settings.bind ("show-numbering", scene, "show-numbering", SettingsBindFlags.GET);
+ settings.bind ("piece-theme", scene, "theme-name", SettingsBindFlags.GET);
+ settings.bind ("show-3d-smooth", scene, "show-3d-smooth", SettingsBindFlags.GET);
+ settings.bind ("move-format", scene, "move-format", SettingsBindFlags.GET);
+ settings.bind ("board-side", scene, "board-side", SettingsBindFlags.GET);
+
+ settings_changed_cb (settings, "show-3d");
+ }
+
+ protected override void shutdown ()
+ {
+ base.shutdown ();
+ if (opponent_engine != null)
+ opponent_engine.stop ();
+ }
+
+ public PieceType show_promotion_type_selector ()
+ {
+ Gtk.Builder promotion_type_selector_builder;
+
+ promotion_type_selector_builder = new Gtk.Builder ();
+ try
+ {
+ promotion_type_selector_builder.add_from_file (Path.build_filename (Config.PKGDATADIR, "promotion-type-selector.ui", null));
+ }
+ catch (Error e)
+ {
+ warning ("Could not load promotion type selector UI: %s", e.message);
+ }
+
+ Gtk.Dialog promotion_type_selector_dialog = promotion_type_selector_builder.get_object ("dialog_promotion_type_selector") as Gtk.Dialog;
+
+ string color;
+ if (game.current_player.color == Color.WHITE)
+ color = "white";
+ else
+ color = "black";
+
+ var filename = Path.build_filename (Config.PKGDATADIR, "pieces", scene.theme_name, "%sQueen.svg".printf (color));
+ set_piece_image (promotion_type_selector_builder.get_object ("image_queen") as Gtk.Image, filename);
+
+ filename = Path.build_filename (Config.PKGDATADIR, "pieces", scene.theme_name, "%sKnight.svg".printf (color));
+ set_piece_image (promotion_type_selector_builder.get_object ("image_knight") as Gtk.Image, filename);
+
+ filename = Path.build_filename (Config.PKGDATADIR, "pieces", scene.theme_name, "%sRook.svg".printf (color));
+ set_piece_image (promotion_type_selector_builder.get_object ("image_rook") as Gtk.Image, filename);
+
+ filename = Path.build_filename (Config.PKGDATADIR, "pieces", scene.theme_name, "%sBishop.svg".printf (color));
+ set_piece_image (promotion_type_selector_builder.get_object ("image_bishop") as Gtk.Image, filename);
+
+ promotion_type_selector_builder.connect_signals (this);
+
+ PieceType selection;
+ int choice = promotion_type_selector_dialog.run ();
+ switch (choice)
+ {
+ case PromotionTypeSelected.QUEEN:
+ selection = PieceType.QUEEN;
+ break;
+ case PromotionTypeSelected.KNIGHT:
+ selection = PieceType.KNIGHT;
+ break;
+ case PromotionTypeSelected.ROOK:
+ selection = PieceType.ROOK;
+ break;
+ case PromotionTypeSelected.BISHOP:
+ selection = PieceType.BISHOP;
+ break;
+ default:
+ selection = PieceType.QUEEN;
+ break;
+ }
+ promotion_type_selector_dialog.destroy ();
+
+ return selection;
+ }
+
+ private void set_piece_image (Gtk.Image image, string filename)
+ {
+ int width, height;
+ if (!Gtk.icon_size_lookup (Gtk.IconSize.DIALOG, out width, out height))
+ return;
+
+ Gdk.Pixbuf pixbuf;
+ try
+ {
+ pixbuf = Rsvg.pixbuf_from_file_at_size (filename, width, height);
+ }
+ catch (Error e)
+ {
+ warning ("Failed to load image %s: %s", filename, e.message);
+ return;
+ }
+ image.set_from_pixbuf (pixbuf);
+ }
+
+ enum PromotionTypeSelected
+ {
+ QUEEN,
+ KNIGHT,
+ ROOK,
+ BISHOP
+ }
+
+ /* Quits the application */
+ public void quit_game ()
+ {
+ autosave ();
+ settings.sync ();
+ if (launcher != null)
+ {
+ launcher.destroy ();
+ launcher = null;
+ }
+ if (window != null)
+ {
+ window.destroy ();
+ window = null;
+ }
+ }
+
+ private void autosave ()
+ {
+ /* FIXME: Prompt user to save somewhere */
+ if (!in_history)
+ return;
+
+ /* Don't autosave if no moves (e.g. they have been undone) or only the computer has moved */
+ if (!game_needs_saving)
+ {
+ if (game_file != null)
+ history.remove (game_file);
+ return;
+ }
+
+ try
+ {
+ if (game_file != null)
+ history.update (game_file, "", pgn_game.result);
+ else
+ game_file = history.add (pgn_game.date, pgn_game.result);
+ debug ("Writing current game to %s", game_file.get_path ());
+ pgn_game.write (game_file);
+ }
+ catch (Error e)
+ {
+ warning ("Failed to autosave: %s", e.message);
+ }
+ }
+
+ private void settings_changed_cb (Settings settings, string key)
+ {
+ if (key == "show-3d")
+ {
+ if (view != null)
+ {
+ view_container.remove (view);
+ view.destroy ();
+ }
+ if (settings.get_boolean ("show-3d"))
+ view = new ChessView3D ();
+ else
+ view = new ChessView2D ();
+ view.set_size_request (300, 300);
+ view.scene = scene;
+ view_container.add (view);
+ view.show ();
+ }
+ }
+
+ private void update_history_panel ()
+ {
+ if (game == null)
+ return;
+
+ var move_number = scene.move_number;
+ var n_moves = (int) game.n_moves;
+ if (move_number < 0)
+ move_number += 1 + n_moves;
+
+ first_move_button.sensitive = n_moves > 0 && move_number != 0;
+ prev_move_button.sensitive = move_number > 0;
+ next_move_button.sensitive = move_number < n_moves;
+ last_move_button.sensitive = n_moves > 0 && move_number != n_moves;
+
+ /* Set move text for all moves (it may have changed format) */
+ int i = n_moves;
+ foreach (var state in game.move_stack)
+ {
+ if (state.last_move != null)
+ {
+ Gtk.TreeIter iter;
+ if (history_combo.model.iter_nth_child (out iter, null, i))
+ set_move_text (iter, state.last_move);
+ }
+ i--;
+ }
+
+ history_combo.set_active (move_number);
+ }
+
+ private void scene_changed_cb (ChessScene scene)
+ {
+ update_history_panel ();
+ }
+
+ private void start_game ()
+ {
+ if (in_history)
+ {
+ window.title = /* Title of the game window */
+ _("Chess");
+ }
+ else
+ {
+ var path = game_file.get_path ();
+ window.title = /* Title of the window when explicitly loaded a file. The first argument is the
+ * base name of the file (e.g. test.pgn), the second argument is the directory
+ * (e.g. /home/fred) */
+ _("%1$s (%2$s) - Chess").printf (Path.get_basename (path), Path.get_dirname (path));
+ }
+
+ var model = (Gtk.ListStore) history_combo.model;
+ model.clear ();
+ Gtk.TreeIter iter;
+ model.append (out iter);
+ model.set (iter, 0,
+ /* Move History Combo: Go to the start of the game */
+ _("Game Start"), 1, 0, -1);
+ history_combo.set_active_iter (iter);
+
+ string fen = ChessGame.STANDARD_SETUP;
+ string[] moves = new string[pgn_game.moves.length ()];
+ var i = 0;
+ foreach (var move in pgn_game.moves)
+ moves[i++] = move;
+
+ if (pgn_game.set_up)
+ {
+ if (pgn_game.fen != null)
+ fen = pgn_game.fen;
+ else
+ warning ("Chess game has SetUp tag but no FEN tag");
+ }
+ game = new ChessGame (fen, moves);
+
+ if (pgn_game.time_control != null)
+ {
+ var controls = pgn_game.time_control.split (":");
+ foreach (var control in controls)
+ {
+ /* We only support simple timeouts */
+ var duration = int.parse (control);
+ if (duration > 0)
+ game.clock = new ChessClock (duration, duration);
+ }
+ }
+
+ game.started.connect (game_start_cb);
+ game.turn_started.connect (game_turn_cb);
+ game.moved.connect (game_move_cb);
+ game.undo.connect (game_undo_cb);
+ game.ended.connect (game_end_cb);
+ if (game.clock != null)
+ game.clock.tick.connect (game_clock_tick_cb);
+
+ scene.game = game;
+ info_bar.hide ();
+ save_menu.sensitive = false;
+ save_as_menu.sensitive = false;
+ update_history_panel ();
+ update_control_buttons ();
+
+ // TODO: Could both be engines
+ var white_engine = pgn_game.tags.lookup ("WhiteAI");
+ var white_level = pgn_game.tags.lookup ("WhiteLevel");
+ if (white_level == null)
+ white_level = "normal";
+
+ var black_engine = pgn_game.tags.lookup ("BlackAI");
+ var black_level = pgn_game.tags.lookup ("BlackLevel");
+ if (black_level == null)
+ black_level = "normal";
+
+ opponent = null;
+ if (opponent_engine != null)
+ {
+ opponent_engine.stop ();
+ opponent_engine.ready_changed.disconnect (engine_ready_cb);
+ opponent_engine.moved.disconnect (engine_move_cb);
+ opponent_engine.stopped.disconnect (engine_stopped_cb);
+ opponent_engine = null;
+ }
+ if (white_engine != null)
+ {
+ opponent = game.white;
+ human_player = game.black;
+ opponent_engine = get_engine (white_engine, white_level);
+ }
+ else if (black_engine != null)
+ {
+ opponent = game.black;
+ human_player = game.white;
+ opponent_engine = get_engine (black_engine, black_level);
+ }
+
+ if (opponent_engine != null)
+ {
+ opponent_engine.ready_changed.connect (engine_ready_cb);
+ opponent_engine.moved.connect (engine_move_cb);
+ opponent_engine.stopped.connect (engine_stopped_cb);
+ opponent_engine.start ();
+ }
+
+ /* Replay current moves */
+ for (var j = (int) game.move_stack.length () - 2; j >= 0; j--)
+ {
+ var state = game.move_stack.nth_data (j);
+ game_move_cb (game, state.last_move);
+ }
+
+ game_needs_saving = false;
+ game.start ();
+
+ if (game.result != ChessResult.IN_PROGRESS)
+ game_end_cb (game);
+
+ white_time_label.queue_draw ();
+ black_time_label.queue_draw ();
+ }
+
+ private ChessEngine? get_engine (string name, string difficulty)
+ {
+ ChessEngine engine;
+ AIProfile? profile = null;
+
+ if (name == "human")
+ return null;
+
+ foreach (var p in ai_profiles)
+ {
+ if (name == "" || p.name == name)
+ {
+ profile = p;
+ break;
+ }
+ }
+ if (profile == null)
+ {
+ warning ("Unknown AI profile %s", name);
+ if (ai_profiles == null)
+ return null;
+ profile = ai_profiles.data;
+ }
+
+ string[] options;
+ switch (difficulty)
+ {
+ case "easy":
+ options = profile.easy_options;
+ break;
+ default:
+ case "normal":
+ options = profile.normal_options;
+ break;
+ case "hard":
+ options = profile.hard_options;
+ break;
+ }
+
+ if (profile.protocol == "cecp")
+ engine = new ChessEngineCECP (options);
+ else if (profile.protocol == "uci")
+ engine = new ChessEngineUCI (options);
+ else
+ {
+ warning ("Unknown AI protocol %s", profile.protocol);
+ return null;
+ }
+ engine.binary = profile.binary;
+
+ return engine;
+ }
+
+ public override void activate ()
+ {
+ /* Launcher or game-window would have been created by startup () already. Show it. */
+ if (launcher != null)
+ launcher.show ();
+ if (window != null)
+ window.show ();
+ }
+
+ [CCode (cname = "launcher_destroy_cb", instance_pos = -1)]
+ public void launcher_destroy_cb (Gtk.Widget widget)
+ {
+ launcher = null;
+
+ if (window == null)
+ quit_game ();
+ }
+
+ private void engine_ready_cb (ChessEngine engine)
+ {
+ if (opponent_engine.ready)
+ {
+ game.start ();
+ view.queue_draw ();
+ }
+ }
+
+ private void engine_move_cb (ChessEngine engine, string move)
+ {
+ opponent.move (move);
+ }
+
+ private void engine_stopped_cb (ChessEngine engine)
+ {
+ opponent.resign ();
+ }
+
+ private void game_start_cb (ChessGame game)
+ {
+ if (opponent_engine != null)
+ opponent_engine.start_game ();
+ }
+
+ private void game_clock_tick_cb (ChessClock clock)
+ {
+ white_time_label.queue_draw ();
+ black_time_label.queue_draw ();
+ }
+
+ private void game_turn_cb (ChessGame game, ChessPlayer player)
+ {
+ if (opponent_engine != null && player == opponent)
+ opponent_engine.request_move ();
+ }
+
+ private void set_move_text (Gtk.TreeIter iter, ChessMove move)
+ {
+ /* Note there are no move formats for pieces taking kings and this is not allowed in Chess rules */
+ const string human_descriptions[] = {/* Human Move String: Description of a white pawn moving from %1$s to %2s, e.g. 'c2 to c4' */
+ N_("White pawn moves from %1$s to %2$s"),
+ /* Human Move String: Description of a white pawn at %1$s capturing a pawn at %2$s */
+ N_("White pawn at %1$s takes the black pawn at %2$s"),
+ /* Human Move String: Description of a white pawn at %1$s capturing a rook at %2$s */
+ N_("White pawn at %1$s takes the black rook at %2$s"),
+ /* Human Move String: Description of a white pawn at %1$s capturing a knight at %2$s */
+ N_("White pawn at %1$s takes the black knight at %2$s"),
+ /* Human Move String: Description of a white pawn at %1$s capturing a bishop at %2$s */
+ N_("White pawn at %1$s takes the black bishop at %2$s"),
+ /* Human Move String: Description of a white pawn at %1$s capturing a queen at %2$s */
+ N_("White pawn at %1$s takes the black queen at %2$s"),
+ /* Human Move String: Description of a white rook moving from %1$s to %2$s, e.g. 'a1 to a5' */
+ N_("White rook moves from %1$s to %2$s"),
+ /* Human Move String: Description of a white rook at %1$s capturing a pawn at %2$s */
+ N_("White rook at %1$s takes the black pawn at %2$s"),
+ /* Human Move String: Description of a white rook at %1$s capturing a rook at %2$s */
+ N_("White rook at %1$s takes the black rook at %2$s"),
+ /* Human Move String: Description of a white rook at %1$s capturing a knight at %2$s */
+ N_("White rook at %1$s takes the black knight at %2$s"),
+ /* Human Move String: Description of a white rook at %1$s capturing a bishop at %2$s */
+ N_("White rook at %1$s takes the black bishop at %2$s"),
+ /* Human Move String: Description of a white rook at %1$s capturing a queen at %2$s" */
+ N_("White rook at %1$s takes the black queen at %2$s"),
+ /* Human Move String: Description of a white knight moving from %1$s to %2$s, e.g. 'b1 to c3' */
+ N_("White knight moves from %1$s to %2$s"),
+ /* Human Move String: Description of a white knight at %1$s capturing a pawn at %2$s */
+ N_("White knight at %1$s takes the black pawn at %2$s"),
+ /* Human Move String: Description of a white knight at %1$s capturing a rook at %2$s */
+ N_("White knight at %1$s takes the black rook at %2$s"),
+ /* Human Move String: Description of a white knight at %1$s capturing a knight at %2$s */
+ N_("White knight at %1$s takes the black knight at %2$s"),
+ /* Human Move String: Description of a white knight at %1$s capturing a bishop at %2$s */
+ N_("White knight at %1$s takes the black bishop at %2$s"),
+ /* Human Move String: Description of a white knight at %1$s capturing a queen at %2$s */
+ N_("White knight at %1$s takes the black queen at %2$s"),
+ /* Human Move String: Description of a white bishop moving from %1$s to %2$s, e.g. 'f1 to b5' */
+ N_("White bishop moves from %1$s to %2$s"),
+ /* Human Move String: Description of a white bishop at %1$s capturing a pawn at %2$s */
+ N_("White bishop at %1$s takes the black pawn at %2$s"),
+ /* Human Move String: Description of a white bishop at %1$s capturing a rook at %2$s */
+ N_("White bishop at %1$s takes the black rook at %2$s"),
+ /* Human Move String: Description of a white bishop at %1$s capturing a knight at %2$s */
+ N_("White bishop at %1$s takes the black knight at %2$s"),
+ /* Human Move String: Description of a white bishop at %1$s capturing a bishop at %2$s */
+ N_("White bishop at %1$s takes the black bishop at %2$s"),
+ /* Human Move String: Description of a white bishop at %1$s capturing a queen at %2$s */
+ N_("White bishop at %1$s takes the black queen at %2$s"),
+ /* Human Move String: Description of a white queen moving from %1$s to %2$s, e.g. 'd1 to d4' */
+ N_("White queen moves from %1$s to %2$s"),
+ /* Human Move String: Description of a white queen at %1$s capturing a pawn at %2$s */
+ N_("White queen at %1$s takes the black pawn at %2$s"),
+ /* Human Move String: Description of a white queen at %1$s capturing a rook at %2$s */
+ N_("White queen at %1$s takes the black rook at %2$s"),
+ /* Human Move String: Description of a white queen at %1$s capturing a knight at %2$s */
+ N_("White queen at %1$s takes the black knight at %2$s"),
+ /* Human Move String: Description of a white queen at %1$s capturing a bishop at %2$s */
+ N_("White queen at %1$s takes the black bishop at %2$s"),
+ /* Human Move String: Description of a white queen at %1$s capturing a queen at %2$s */
+ N_("White queen at %1$s takes the black queen at %2$s"),
+ /* Human Move String: Description of a white king moving from %1$s to %2$s, e.g. 'e1 to f1' */
+ N_("White king moves from %1$s to %2$s"),
+ /* Human Move String: Description of a white king at %1$s capturing a pawn at %2$s */
+ N_("White king at %1$s takes the black pawn at %2$s"),
+ /* Human Move String: Description of a white king at %1$s capturing a rook at %2$s */
+ N_("White king at %1$s takes the black rook at %2$s"),
+ /* Human Move String: Description of a white king at %1$s capturing a knight at %2$s */
+ N_("White king at %1$s takes the black knight at %2$s"),
+ /* Human Move String: Description of a white king at %1$s capturing a bishop at %2$s */
+ N_("White king at %1$s takes the black bishop at %2$s"),
+ /* Human Move String: Description of a white king at %1$s capturing a queen at %2$s */
+ N_("White king at %1$s takes the black queen at %2$s"),
+ /* Human Move String: Description of a black pawn moving from %1$s to %2$s, e.g. 'c8 to c6' */
+ N_("Black pawn moves from %1$s to %2$s"),
+ /* Human Move String: Description of a black pawn at %1$s capturing a pawn at %2$s */
+ N_("Black pawn at %1$s takes the white pawn at %2$s"),
+ /* Human Move String: Description of a black pawn at %1$s capturing a rook at %2$s */
+ N_("Black pawn at %1$s takes the white rook at %2$s"),
+ /* Human Move String: Description of a black pawn at %1$s capturing a knight at %2$s */
+ N_("Black pawn at %1$s takes the white knight at %2$s"),
+ /* Human Move String: Description of a black pawn at %1$s capturing a bishop at %2$s */
+ N_("Black pawn at %1$s takes the white bishop at %2$s"),
+ /* Human Move String: Description of a black pawn at %1$s capturing a queen at %2$s */
+ N_("Black pawn at %1$s takes the white queen at %2$s"),
+ /* Human Move String: Description of a black rook moving from %1$s to %2$s, e.g. 'a8 to a4' */
+ N_("Black rook moves from %1$s to %2$s"),
+ /* Human Move String: Description of a black rook at %1$s capturing a pawn at %2$s */
+ N_("Black rook at %1$s takes the white pawn at %2$s"),
+ /* Human Move String: Description of a black rook at %1$s capturing a rook at %2$s */
+ N_("Black rook at %1$s takes the white rook at %2$s"),
+ /* Human Move String: Description of a black rook at %1$s capturing a knight at %2$s */
+ N_("Black rook at %1$s takes the white knight at %2$s"),
+ /* Human Move String: Description of a black rook at %1$s capturing a bishop at %2$s */
+ N_("Black rook at %1$s takes the white bishop at %2$s"),
+ /* Human Move String: Description of a black rook at %1$s capturing a queen at %2$s */
+ N_("Black rook at %1$s takes the white queen at %2$s"),
+ /* Human Move String: Description of a black knight moving from %1$s to %2$s, e.g. 'b8 to c6' */
+ N_("Black knight moves from %1$s to %2$s"),
+ /* Human Move String: Description of a black knight at %1$s capturing a pawn at %2$s */
+ N_("Black knight at %1$s takes the white pawn at %2$s"),
+ /* Human Move String: Description of a black knight at %1$s capturing a rook at %2$s */
+ N_("Black knight at %1$s takes the white rook at %2$s"),
+ /* Human Move String: Description of a black knight at %1$s capturing a knight at %2$s */
+ N_("Black knight at %1$s takes the white knight at %2$s"),
+ /* Human Move String: Description of a black knight at %1$s capturing a bishop at %2$s */
+ N_("Black knight at %1$s takes the white bishop at %2$s"),
+ /* Human Move String: Description of a black knight at %1$s capturing a queen at %2$s */
+ N_("Black knight at %1$s takes the white queen at %2$s"),
+ /* Human Move String: Description of a black bishop moving from %1$s to %2$s, e.g. 'f8 to b3' */
+ N_("Black bishop moves from %1$s to %2$s"),
+ /* Human Move String: Description of a black bishop at %1$s capturing a pawn at %2$s */
+ N_("Black bishop at %1$s takes the white pawn at %2$s"),
+ /* Human Move String: Description of a black bishop at %1$s capturing a rook at %2$s */
+ N_("Black bishop at %1$s takes the white rook at %2$s"),
+ /* Human Move String: Description of a black bishop at %1$s capturing a knight at %2$s */
+ N_("Black bishop at %1$s takes the white knight at %2$s"),
+ /* Human Move String: Description of a black bishop at %1$s capturing a bishop at %2$s */
+ N_("Black bishop at %1$s takes the white bishop at %2$s"),
+ /* Human Move String: Description of a black bishop at %1$s capturing a queen at %2$s */
+ N_("Black bishop at %1$s takes the white queen at %2$s"),
+ /* Human Move String: Description of a black queen moving from %1$s to %2$s, e.g. 'd8 to d5' */
+ N_("Black queen moves from %1$s to %2$s"),
+ /* Human Move String: Description of a black queen at %1$s capturing a pawn at %2$s */
+ N_("Black queen at %1$s takes the white pawn at %2$s"),
+ /* Human Move String: Description of a black queen at %1$s capturing a rook at %2$s */
+ N_("Black queen at %1$s takes the white rook at %2$s"),
+ /* Human Move String: Description of a black queen at %1$s capturing a knight at %2$s */
+ N_("Black queen at %1$s takes the white knight at %2$s"),
+ /* Human Move String: Description of a black queen at %1$s capturing a bishop at %2$s */
+ N_("Black queen at %1$s takes the white bishop at %2$s"),
+ /* Human Move String: Description of a black queen at %1$s capturing a queen at %2$s */
+ N_("Black queen at %1$s takes the white queen at %2$s"),
+ /* Human Move String: Description of a black king moving from %1$s to %2$s, e.g. 'e8 to f8' */
+ N_("Black king moves from %1$s to %2$s"),
+ /* Human Move String: Description of a black king at %1$s capturing a pawn at %2$s */
+ N_("Black king at %1$s takes the white pawn at %2$s"),
+ /* Human Move String: Description of a black king at %1$s capturing a rook at %2$s */
+ N_("Black king at %1$s takes the white rook at %2$s"),
+ /* Human Move String: Description of a black king at %1$s capturing a knight at %2$s */
+ N_("Black king at %1$s takes the white knight at %2$s"),
+ /* Human Move String: Description of a black king at %1$s capturing a bishop at %2$s */
+ N_("Black king at %1$s takes the white bishop at %2$s"),
+ /* Human Move String: Description of a black king at %1$s capturing a queen at %2$s" */
+ N_("Black king at %1$s takes the white queen at %2$s")};
+
+ var move_text = "";
+ switch (scene.move_format)
+ {
+ case "human":
+ int index;
+ if (move.victim == null)
+ index = 0;
+ else
+ index = move.victim.type + 1;
+ index += move.piece.type * 6;
+ if (move.piece.player.color == Color.BLACK)
+ index += 36;
+
+ // FIXME: Use castling text e.g. "White castles kingside" (do for next release, we are in a string freeze)
+
+ var start = "%c%d".printf ('a' + move.f0, move.r0 + 1);
+ var end = "%c%d".printf ('a' + move.f1, move.r1 + 1);
+ move_text = _(human_descriptions[index]).printf (start, end);
+ break;
+
+ case "san":
+ move_text = move.get_san ();
+ break;
+
+ case "fan":
+ move_text = move.get_fan ();
+ break;
+
+ default:
+ case "lan":
+ move_text = move.get_lan ();
+ break;
+ }
+
+ var model = (Gtk.ListStore) history_combo.model;
+ var label = "%u%c. %s".printf ((move.number + 1) / 2, move.number % 2 == 0 ? 'b' : 'a', move_text);
+ model.set (iter, 0, label, -1);
+ }
+
+ private void game_move_cb (ChessGame game, ChessMove move)
+ {
+ /* Need to save after each move */
+ game_needs_saving = true;
+
+ /* If the only mover is the AI, then don't bother saving */
+ if (move.number == 1 && opponent != null && opponent.color == Color.WHITE)
+ game_needs_saving = false;
+
+ if (move.number > pgn_game.moves.length ())
+ pgn_game.moves.append (move.get_san ());
+
+ var model = (Gtk.ListStore) history_combo.model;
+ Gtk.TreeIter iter;
+ model.append (out iter);
+ model.set (iter, 1, move.number, -1);
+ set_move_text (iter, move);
+
+ /* Follow the latest move */
+ if (move.number == game.n_moves && scene.move_number == -1)
+ history_combo.set_active_iter (iter);
+
+ save_menu.sensitive = true;
+ save_as_menu.sensitive = true;
+ update_history_panel ();
+ update_control_buttons ();
+
+ if (opponent_engine != null)
+ opponent_engine.report_move (move);
+ view.queue_draw ();
+ }
+
+ private void game_undo_cb (ChessGame game)
+ {
+ /* Notify AI */
+ if (opponent_engine != null)
+ opponent_engine.undo ();
+
+ /* Remove from the PGN game */
+ pgn_game.moves.remove_link (pgn_game.moves.last ());
+
+ /* Remove from the history */
+ var model = (Gtk.ListStore) history_combo.model;
+ Gtk.TreeIter iter;
+ model.iter_nth_child (out iter, null, model.iter_n_children (null) - 1);
+ model.remove (iter);
+
+ /* If watching this move, go back one */
+ if (scene.move_number > game.n_moves || scene.move_number == -1)
+ {
+ model.iter_nth_child (out iter, null, model.iter_n_children (null) - 1);
+ history_combo.set_active_iter (iter);
+ view.queue_draw ();
+ }
+
+ update_history_panel ();
+ update_control_buttons ();
+ }
+
+ private void update_control_buttons ()
+ {
+ var can_resign = game.n_moves > 0;
+ resign_menu.sensitive = resign_button.sensitive = can_resign;
+
+ /* Can undo once the human player has made a move */
+ var can_undo = game.n_moves > 0;
+ if (opponent != null && opponent.color == Color.WHITE)
+ can_undo = game.n_moves > 1;
+
+ undo_menu.sensitive = undo_button.sensitive = can_undo;
+ }
+
+ private void game_end_cb (ChessGame game)
+ {
+ string title = "";
+ switch (game.result)
+ {
+ case ChessResult.WHITE_WON:
+ /* Message display when the white player wins */
+ title = _("White wins");
+ pgn_game.result = PGNGame.RESULT_WHITE;
+ break;
+ case ChessResult.BLACK_WON:
+ /* Message display when the black player wins */
+ title = _("Black wins");
+ pgn_game.result = PGNGame.RESULT_BLACK;
+ break;
+ case ChessResult.DRAW:
+ /* Message display when the game is drawn */
+ title = _("Game is drawn");
+ pgn_game.result = PGNGame.RESULT_DRAW;
+ break;
+ default:
+ break;
+ }
+
+ string reason = "";
+ switch (game.rule)
+ {
+ case ChessRule.CHECKMATE:
+ /* Message displayed when the game ends due to a player being checkmated */
+ reason = _("Opponent is in check and cannot move (checkmate)");
+ break;
+ case ChessRule.STALEMATE:
+ /* Message displayed when the game terminates due to a stalemate */
+ reason = _("Opponent cannot move (stalemate)");
+ break;
+ case ChessRule.FIFTY_MOVES:
+ /* Message displayed when the game is drawn due to the fifty move rule */
+ reason = _("No piece has been taken or pawn moved in the last fifty moves");
+ break;
+ case ChessRule.TIMEOUT:
+ /* Message displayed when the game ends due to one player's clock stopping */
+ reason = _("Opponent has run out of time");
+ break;
+ case ChessRule.THREE_FOLD_REPETITION:
+ /* Message displayed when the game is drawn due to the three-fold-repitition rule */
+ reason = _("The same board state has occurred three times (three fold repetition)");
+ break;
+ case ChessRule.INSUFFICIENT_MATERIAL:
+ /* Message displayed when the game is drawn due to the insufficient material rule */
+ reason = _("Neither player can cause checkmate (insufficient material)");
+ break;
+ case ChessRule.RESIGN:
+ if (game.result == ChessResult.WHITE_WON)
+ {
+ /* Message displayed when the game ends due to the black player resigning */
+ reason = _("The black player has resigned");
+ }
+ else
+ {
+ /* Message displayed when the game ends due to the white player resigning */
+ reason = _("The white player has resigned");
+ }
+ break;
+ case ChessRule.ABANDONMENT:
+ /* Message displayed when a game is abandoned */
+ reason = _("The game has been abandoned");
+ pgn_game.termination = PGNGame.TERMINATE_ABANDONED;
+ break;
+ case ChessRule.DEATH:
+ /* Message displayed when the game ends due to a player dying */
+ reason = _("One of the players has died");
+ pgn_game.termination = PGNGame.TERMINATE_DEATH;
+ break;
+ }
+
+ info_title_label.set_markup ("<big><b>%s</b></big>".printf (title));
+ info_label.set_text (reason);
+ info_bar.show ();
+
+ white_time_label.queue_draw ();
+ black_time_label.queue_draw ();
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT game_window_delete_event_cb", instance_pos = -1)]
+ public bool game_window_delete_event_cb (Gtk.Widget widget, Gdk.Event event)
+ {
+ quit_game ();
+ return false;
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT game_window_configure_event_cb", instance_pos = -1)]
+ public bool game_window_configure_event_cb (Gtk.Widget widget, Gdk.EventConfigure event)
+ {
+ if (!settings.get_boolean ("maximized") && !settings.get_boolean ("fullscreen"))
+ {
+ settings.set_int ("game-screen-width", event.width);
+ settings.set_int ("game-screen-height", event.height);
+ }
+
+ return false;
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT game_window_window_state_event_cb", instance_pos = -1)]
+ public bool game_window_window_state_event_cb (Gtk.Widget widget, Gdk.EventWindowState event)
+ {
+ if ((event.changed_mask & Gdk.WindowState.MAXIMIZED) != 0)
+ {
+ var is_maximized = (event.new_window_state & Gdk.WindowState.MAXIMIZED) != 0;
+ settings.set_boolean ("maximized", is_maximized);
+ }
+ if ((event.changed_mask & Gdk.WindowState.FULLSCREEN) != 0)
+ {
+ bool is_fullscreen = (event.new_window_state & Gdk.WindowState.FULLSCREEN) != 0;
+ settings.set_boolean ("fullscreen", is_fullscreen);
+ fullscreen_menu.label = is_fullscreen ? Gtk.Stock.LEAVE_FULLSCREEN : Gtk.Stock.FULLSCREEN;
+ }
+
+ return false;
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT new_game_cb", instance_pos = -1)]
+ public void new_game_cb (Gtk.Widget widget)
+ {
+ if (game_needs_saving || (in_history && game_file != null))
+ {
+ var dialog = new Gtk.MessageDialog.with_markup (window,
+ Gtk.DialogFlags.MODAL,
+ Gtk.MessageType.QUESTION,
+ Gtk.ButtonsType.NONE,
+ "<span weight=\"bold\" size=\"larger\">%s</span>",
+ _("Save this game before starting a new one?"));
+ dialog.add_button (Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL);
+ dialog.add_button (_("_Abandon game"), Gtk.ResponseType.NO);
+ dialog.add_button (_("_Save game for later"), Gtk.ResponseType.YES);
+ var result = dialog.run ();
+ dialog.destroy ();
+ if (result == Gtk.ResponseType.CANCEL)
+ return;
+
+ if (result == Gtk.ResponseType.NO)
+ {
+ in_history = false;
+ game_needs_saving = false;
+ }
+ }
+
+ autosave ();
+
+ /* Hide game-window and show launcher */
+ window.hide ();
+ if (launcher == null)
+ launcher = create_launcher (engines_file, ai_profiles, history);
+ launcher.present ();
+ launcher.show_opponent_selector ();
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT resign_cb", instance_pos = -1)]
+ public void resign_cb (Gtk.Widget widget)
+ {
+ game.current_player.resign ();
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT claim_draw_cb", instance_pos = -1)]
+ public void claim_draw_cb (Gtk.Widget widget)
+ {
+ game.current_player.claim_draw ();
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT undo_move_cb", instance_pos = -1)]
+ public void undo_move_cb (Gtk.Widget widget)
+ {
+ if (opponent != null)
+ human_player.undo ();
+ else
+ game.opponent.undo ();
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT quit_cb", instance_pos = -1)]
+ public void quit_cb (Gtk.Widget widget)
+ {
+ quit_game ();
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT white_time_draw_cb", instance_pos = -1)]
+ public bool white_time_draw_cb (Gtk.Widget widget, Cairo.Context c)
+ {
+ double fg[3] = { 0.0, 0.0, 0.0 };
+ double bg[3] = { 1.0, 1.0, 1.0 };
+
+ draw_time (widget, c, make_clock_text (game.clock, Color.WHITE), fg, bg);
+ return false;
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT black_time_draw_cb", instance_pos = -1)]
+ public bool black_time_draw_cb (Gtk.Widget widget, Cairo.Context c)
+ {
+ double fg[3] = { 1.0, 1.0, 1.0 };
+ double bg[3] = { 0.0, 0.0, 0.0 };
+
+ draw_time (widget, c, make_clock_text (game.clock, Color.BLACK), fg, bg);
+ return false;
+ }
+
+ private string make_clock_text (ChessClock? clock, Color color)
+ {
+ if (clock == null)
+ return "â";
+
+ int used;
+ if (color == Color.WHITE)
+ used = (int) (game.clock.white_duration / 1000 - game.clock.white_used_in_seconds);
+ else
+ used = (int) (game.clock.black_duration / 1000 - game.clock.black_used_in_seconds);
+
+ if (used >= 60)
+ return "%d:%02d".printf (used / 60, used % 60);
+ else
+ return ":%02d".printf (used);
+ }
+
+ private void draw_time (Gtk.Widget widget, Cairo.Context c, string text, double[] fg, double[] bg)
+ {
+ double alpha = 1.0;
+
+ if (widget.get_state () == Gtk.StateType.INSENSITIVE)
+ alpha = 0.5;
+ c.set_source_rgba (bg[0], bg[1], bg[2], alpha);
+ c.paint ();
+
+ c.set_source_rgba (fg[0], fg[1], fg[2], alpha);
+ c.select_font_face ("fixed", Cairo.FontSlant.NORMAL, Cairo.FontWeight.BOLD);
+ c.set_font_size (0.6 * widget.get_allocated_height ());
+ Cairo.TextExtents extents;
+ c.text_extents (text, out extents);
+ c.move_to ((widget.get_allocated_width () - extents.width) / 2 - extents.x_bearing,
+ (widget.get_allocated_height () - extents.height) / 2 - extents.y_bearing);
+ c.show_text (text);
+
+ widget.set_size_request ((int) extents.width + 6, -1);
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT history_combo_changed_cb", instance_pos = -1)]
+ public void history_combo_changed_cb (Gtk.ComboBox combo)
+ {
+ Gtk.TreeIter iter;
+ if (!combo.get_active_iter (out iter))
+ return;
+ int move_number;
+ combo.model.get (iter, 1, out move_number, -1);
+ if (game == null || move_number == game.n_moves)
+ move_number = -1;
+ scene.move_number = move_number;
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT history_latest_clicked_cb", instance_pos = -1)]
+ public void history_latest_clicked_cb (Gtk.Widget widget)
+ {
+ scene.move_number = -1;
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT history_next_clicked_cb", instance_pos = -1)]
+ public void history_next_clicked_cb (Gtk.Widget widget)
+ {
+ if (scene.move_number == -1)
+ return;
+
+ int move_number = scene.move_number + 1;
+ if (move_number >= game.n_moves)
+ scene.move_number = -1;
+ else
+ scene.move_number = move_number;
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT history_previous_clicked_cb", instance_pos = -1)]
+ public void history_previous_clicked_cb (Gtk.Widget widget)
+ {
+ if (scene.move_number == 0)
+ return;
+
+ if (scene.move_number == -1)
+ scene.move_number = (int) game.n_moves - 1;
+ else
+ scene.move_number = scene.move_number - 1;
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT history_start_clicked_cb", instance_pos = -1)]
+ public void history_start_clicked_cb (Gtk.Widget widget)
+ {
+ scene.move_number = 0;
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT toggle_fullscreen_cb", instance_pos = -1)]
+ public void toggle_fullscreen_cb (Gtk.Widget widget)
+ {
+ if (fullscreen_menu.label == Gtk.Stock.FULLSCREEN)
+ window.fullscreen ();
+ else
+ window.unfullscreen ();
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT preferences_cb", instance_pos = -1)]
+ public void preferences_cb (Gtk.Widget widget)
+ {
+ if (preferences_dialog != null)
+ {
+ preferences_dialog.present ();
+ return;
+ }
+
+ preferences_builder = new Gtk.Builder ();
+ try
+ {
+ preferences_builder.add_from_file (Path.build_filename (Config.PKGDATADIR, "preferences.ui", null));
+ }
+ catch (Error e)
+ {
+ warning ("Could not load preferences UI: %s", e.message);
+ }
+ preferences_dialog = (Gtk.Dialog) preferences_builder.get_object ("preferences");
+
+ settings.bind ("show-numbering", preferences_builder.get_object ("show_numbering_check"),
+ "active", SettingsBindFlags.DEFAULT);
+ settings.bind ("show-move-hints", preferences_builder.get_object ("show_move_hints_check"),
+ "active", SettingsBindFlags.DEFAULT);
+ settings.bind ("show-toolbar", preferences_builder.get_object ("show_toolbar_check"),
+ "active", SettingsBindFlags.DEFAULT);
+ settings.bind ("show-history", preferences_builder.get_object ("show_history_check"),
+ "active", SettingsBindFlags.DEFAULT);
+ settings.bind ("show-3d", preferences_builder.get_object ("show_3d_check"),
+ "active", SettingsBindFlags.DEFAULT);
+ settings.bind ("show-3d-smooth", preferences_builder.get_object ("show_3d_smooth_check"),
+ "active", SettingsBindFlags.DEFAULT);
+
+ var orientation_combo = (Gtk.ComboBox) preferences_builder.get_object ("orientation_combo");
+ set_combo (orientation_combo, 1, settings.get_string ("board-side"));
+
+ var move_combo = (Gtk.ComboBox) preferences_builder.get_object ("move_format_combo");
+ set_combo (move_combo, 1, settings.get_string ("move-format"));
+
+ var theme_combo = (Gtk.ComboBox) preferences_builder.get_object ("piece_style_combo");
+ set_combo (theme_combo, 1, settings.get_string ("piece-theme"));
+
+ preferences_builder.connect_signals (this);
+
+ preferences_dialog.present ();
+ }
+
+ private void set_combo (Gtk.ComboBox combo, int value_index, string value)
+ {
+ Gtk.TreeIter iter;
+ var model = combo.model;
+ if (!model.get_iter_first (out iter))
+ return;
+ do
+ {
+ string v;
+ model.get (iter, value_index, out v, -1);
+ if (v == value)
+ {
+ combo.set_active_iter (iter);
+ return;
+ }
+ } while (model.iter_next (ref iter));
+ }
+
+ private string? get_combo (Gtk.ComboBox combo, int value_index)
+ {
+ string value;
+ Gtk.TreeIter iter;
+ if (!combo.get_active_iter (out iter))
+ return null;
+ combo.model.get (iter, value_index, out value, -1);
+ return value;
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT preferences_response_cb", instance_pos = -1)]
+ public void preferences_response_cb (Gtk.Widget widget, int response_id)
+ {
+ preferences_dialog.hide ();
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT preferences_delete_event_cb", instance_pos = -1)]
+ public bool preferences_delete_event_cb (Gtk.Widget widget, Gdk.Event event)
+ {
+ preferences_response_cb (widget, Gtk.ResponseType.CANCEL);
+ return true;
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT piece_style_combo_changed_cb", instance_pos = -1)]
+ public void piece_style_combo_changed_cb (Gtk.ComboBox combo)
+ {
+ settings.set_string ("piece-theme", get_combo (combo, 1));
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT show_3d_toggle_cb", instance_pos = -1)]
+ public void show_3d_toggle_cb (Gtk.ToggleButton widget)
+ {
+ var w = (Gtk.Widget) preferences_builder.get_object ("show_3d_smooth_check");
+ w.sensitive = widget.active;
+
+ w = (Gtk.Widget) preferences_builder.get_object ("piece_style_combo");
+ w.sensitive = !widget.active;
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT move_format_combo_changed_cb", instance_pos = -1)]
+ public void move_format_combo_changed_cb (Gtk.ComboBox combo)
+ {
+ settings.set_string ("move-format", get_combo (combo, 1));
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT orientation_combo_changed_cb", instance_pos = -1)]
+ public void orientation_combo_changed_cb (Gtk.ComboBox combo)
+ {
+ settings.set_string ("board-side", get_combo (combo, 1));
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT help_cb", instance_pos = -1)]
+ public void help_cb (Gtk.Widget widget)
+ {
+ try
+ {
+ Gtk.show_uri (window.get_screen (), "help:gnome-chess", Gtk.get_current_event_time ());
+ }
+ catch (Error e)
+ {
+ warning ("Unable to open help: %s", e.message);
+ }
+ }
+
+ private const string[] authors = { "Robert Ancell <robert ancell gmail com>", null };
+ private const string[] artists = { "John-Paul Gignac (3D Models)", "Max Froumentin (2D Models)", "Hylke Bons <h bons student rug nl> (icon)", null };
+
+ [CCode (cname = "G_MODULE_EXPORT about_cb", instance_pos = -1)]
+ public void about_cb (Gtk.Widget widget)
+ {
+ if (about_dialog != null)
+ {
+ about_dialog.present ();
+ return;
+ }
+
+ about_dialog = new Gtk.AboutDialog ();
+ about_dialog.transient_for = window;
+ about_dialog.modal = true;
+ about_dialog.program_name = "Chess";
+ about_dialog.version = Config.VERSION;
+ about_dialog.copyright = "Copyright 2010 Robert Ancell <robert ancell gmail com>";
+ about_dialog.license_type = Gtk.License.GPL_2_0;
+ about_dialog.comments = _("The 2D/3D chess game for GNOME. \n\nGNOME Chess is a part of GNOME Games.");
+ about_dialog.authors = authors;
+ about_dialog.artists = artists;
+ about_dialog.translator_credits = "translator-credits";
+ about_dialog.website = "http://www.gnome.org/projects/gnome-games/";
+ about_dialog.website_label = _("GNOME Games web site");
+ about_dialog.logo_icon_name = "gnome-chess";
+ about_dialog.response.connect (about_response_cb);
+ about_dialog.show ();
+ }
+
+ private void about_response_cb (int response_id)
+ {
+ about_dialog.destroy ();
+ about_dialog = null;
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT save_game_as_cb", instance_pos = -1)]
+ public void save_game_as_cb (Gtk.Widget widget)
+ {
+ save_game ();
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT save_game_cb", instance_pos = -1)]
+ public void save_game_cb (Gtk.Widget widget)
+ {
+ save_game ();
+ }
+
+ private void add_info_bar_to_dialog (Gtk.Dialog dialog, out Gtk.InfoBar info_bar, out Gtk.Label label)
+ {
+ var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
+ vbox.show ();
+
+ info_bar = new Gtk.InfoBar ();
+ var content_area = (Gtk.Container) info_bar.get_content_area ();
+ vbox.pack_start (info_bar, false, true, 0);
+
+ label = new Gtk.Label ("");
+ content_area.add (label);
+ label.show ();
+
+ var child = (Gtk.Container) dialog.get_child ();
+ child.reparent (vbox);
+ child.border_width = dialog.border_width;
+ dialog.border_width = 0;
+ dialog.add (vbox);
+ }
+
+ private void save_game ()
+ {
+ /* Show active dialog */
+ if (save_dialog != null)
+ {
+ save_dialog.present ();
+ return;
+ }
+
+ save_dialog = new Gtk.FileChooserDialog (/* Title of save game dialog */
+ _("Save Chess Game"),
+ window, Gtk.FileChooserAction.SAVE,
+ Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
+ Gtk.Stock.SAVE, Gtk.ResponseType.OK, null);
+ add_info_bar_to_dialog (save_dialog, out save_dialog_info_bar, out save_dialog_error_label);
+
+ save_dialog.file_activated.connect (save_file_cb);
+ save_dialog.response.connect (save_cb);
+
+ /* Filter out non PGN files by default */
+ var pgn_filter = new Gtk.FileFilter ();
+ gtk_file_filter_set_name (pgn_filter,
+ /* Save Game Dialog: Name of filter to show only PGN files */
+ _("PGN files"));
+ pgn_filter.add_pattern ("*.pgn");
+ save_dialog.add_filter (pgn_filter);
+
+ var all_filter = new Gtk.FileFilter ();
+ gtk_file_filter_set_name (all_filter,
+ /* Save Game Dialog: Name of filter to show all files */
+ _("All files"));
+ all_filter.add_pattern ("*");
+ save_dialog.add_filter (all_filter);
+
+ save_dialog.present ();
+ }
+
+ private void save_file_cb ()
+ {
+ save_cb (Gtk.ResponseType.OK);
+ }
+
+ private void save_cb (int response_id)
+ {
+ if (response_id == Gtk.ResponseType.OK)
+ {
+ save_menu.sensitive = false;
+
+ try
+ {
+ pgn_game.write (save_dialog.get_file ());
+ }
+ catch (Error e)
+ {
+ save_dialog_error_label.set_text (_("Failed to save game: %s").printf (e.message));
+ save_dialog_info_bar.set_message_type (Gtk.MessageType.ERROR);
+ save_dialog_info_bar.show ();
+ return;
+ }
+ }
+
+ save_dialog.destroy ();
+ save_dialog = null;
+ save_dialog_info_bar = null;
+ save_dialog_error_label = null;
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT open_game_cb", instance_pos = -1)]
+ public void open_game_cb (Gtk.Widget widget)
+ {
+ /* Show active dialog */
+ if (open_dialog != null)
+ {
+ open_dialog.present ();
+ return;
+ }
+
+ open_dialog = new Gtk.FileChooserDialog (/* Title of load game dialog */
+ _("Load Chess Game"),
+ window, Gtk.FileChooserAction.OPEN,
+ Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
+ Gtk.Stock.OPEN, Gtk.ResponseType.OK, null);
+ add_info_bar_to_dialog (open_dialog, out open_dialog_info_bar, out open_dialog_error_label);
+
+ open_dialog.file_activated.connect (open_file_cb);
+ open_dialog.response.connect (open_cb);
+
+ /* Filter out non PGN files by default */
+ var pgn_filter = new Gtk.FileFilter ();
+ gtk_file_filter_set_name (pgn_filter,
+ /* Load Game Dialog: Name of filter to show only PGN files */
+ _("PGN files"));
+ pgn_filter.add_pattern ("*.pgn");
+ open_dialog.add_filter (pgn_filter);
+
+ var all_filter = new Gtk.FileFilter ();
+ gtk_file_filter_set_name (all_filter,
+ /* Load Game Dialog: Name of filter to show all files */
+ _("All files"));
+ all_filter.add_pattern ("*");
+ open_dialog.add_filter (all_filter);
+
+ open_dialog.present ();
+ }
+
+ private void open_file_cb ()
+ {
+ open_cb (Gtk.ResponseType.OK);
+ }
+
+ private void open_cb (int response_id)
+ {
+ if (response_id == Gtk.ResponseType.OK)
+ {
+ try
+ {
+ load_game (open_dialog.get_file (), false);
+ }
+ catch (Error e)
+ {
+ open_dialog_error_label.set_text (_("Failed to open game: %s").printf (e.message));
+ open_dialog_info_bar.set_message_type (Gtk.MessageType.ERROR);
+ open_dialog_info_bar.show ();
+ return;
+ }
+ }
+
+ open_dialog.destroy ();
+ open_dialog = null;
+ open_dialog_info_bar = null;
+ open_dialog_error_label = null;
+ }
+
+ private void display_window ()
+ {
+ if (settings.get_boolean ("fullscreen"))
+ window.fullscreen ();
+ else if (settings.get_boolean ("maximized"))
+ window.maximize ();
+
+ game_vbox.show ();
+ window.resize (settings.get_int ("game-screen-width"),
+ settings.get_int ("game-screen-height"));
+
+ window.show ();
+ }
+
+ private void start_new_game ()
+ {
+ if (window == null)
+ {
+ create_game_window ();
+ add_window (window);
+ }
+
+ in_history = true;
+ game_file = null;
+
+ pgn_game = new PGNGame ();
+ var now = new DateTime.now_local ();
+ pgn_game.date = now.format ("%Y.%m.%d");
+ pgn_game.time = now.format ("%H:%M:%S");
+ var duration = settings.get_int ("duration");
+ if (duration > 0)
+ pgn_game.time_control = "%d".printf (duration);
+
+ if (settings.get_string ("opponent-type") == "robot")
+ {
+ var engine_name = settings.get_string ("opponent");
+ var engine_level = settings.get_string ("difficulty");
+ if (engine_name != null)
+ {
+ if (settings.get_boolean ("play-as-white"))
+ {
+ pgn_game.tags.insert ("BlackAI", engine_name);
+ pgn_game.tags.insert ("BlackLevel", engine_level);
+ }
+ else
+ {
+ pgn_game.tags.insert ("WhiteAI", engine_name);
+ pgn_game.tags.insert ("WhiteLevel", engine_level);
+ }
+ }
+ }
+ start_game ();
+
+ display_window ();
+ if (launcher != null)
+ launcher.destroy ();
+ }
+
+ [CCode (cname = "load_game_handler", instance_pos = -1)]
+ private void load_game_handler (ChessLauncher launcher,
+ File file,
+ bool from_history)
+ {
+ try
+ {
+ load_game (file, from_history);
+ }
+ catch (Error e)
+ {
+ stderr.printf ("Error loading pgn from file: %s", e.message);
+ }
+ }
+
+ private void load_game (File file, bool from_history) throws Error
+ {
+ if (window == null)
+ {
+ create_game_window ();
+ add_window (window);
+ }
+
+ var pgn = new PGN.from_file (file);
+ pgn_game = pgn.games.nth_data (0);
+
+ game_file = file;
+ in_history = from_history;
+ start_game ();
+
+ display_window ();
+ if (launcher != null)
+ launcher.destroy ();
+ }
+}
+
+
diff --git a/src/gnome-chess.vala b/src/gnome-chess.vala
index e566e6c..61857d1 100644
--- a/src/gnome-chess.vala
+++ b/src/gnome-chess.vala
@@ -1,1603 +1,3 @@
-/* Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=647122 */
-extern void gtk_file_filter_set_name (Gtk.FileFilter filter, string name);
-
-public class Application : Gtk.Application
-{
- protected Settings settings;
- private History history;
- private Gtk.Builder builder;
- protected Gtk.Builder preferences_builder;
- private ChessLauncher? launcher = null;
- private Gtk.Window? window = null;
-
- /* Chess game screen widgets */
- protected Gtk.Widget game_vbox;
- private Gtk.Widget save_menu;
- private Gtk.Widget save_as_menu;
- protected Gtk.MenuItem fullscreen_menu;
- protected Gtk.InfoBar info_bar;
- protected Gtk.Label info_title_label;
- protected Gtk.Label info_label;
- protected Gtk.Container view_container;
- protected ChessScene scene;
- protected ChessView view;
- private Gtk.Widget undo_menu;
- private Gtk.Widget undo_button;
- protected Gtk.Widget resign_menu;
- private Gtk.Widget resign_button;
- protected Gtk.Widget first_move_button;
- protected Gtk.Widget prev_move_button;
- protected Gtk.Widget next_move_button;
- protected Gtk.Widget last_move_button;
- protected Gtk.ComboBox history_combo;
- protected Gtk.Widget white_time_label;
- protected Gtk.Widget black_time_label;
-
- protected Gtk.Dialog? preferences_dialog = null;
- private Gtk.FileChooserDialog? open_dialog = null;
- private Gtk.InfoBar? open_dialog_info_bar = null;
- private Gtk.Label? open_dialog_error_label = null;
- private Gtk.FileChooserDialog? save_dialog = null;
- private Gtk.InfoBar? save_dialog_info_bar = null;
- private Gtk.Label? save_dialog_error_label = null;
- protected Gtk.AboutDialog? about_dialog = null;
-
- private PGNGame pgn_game;
- protected ChessGame game;
- private bool in_history;
- private File game_file;
- private bool game_needs_saving;
- private string engines_file;
- private List<AIProfile> ai_profiles;
- protected ChessPlayer? opponent = null;
- private ChessPlayer? human_player = null;
- protected ChessEngine? opponent_engine = null;
-
- public Application (File? game_file)
- {
- Object (application_id: "org.gnome.gnome-chess", flags: ApplicationFlags.FLAGS_NONE);
- this.game_file = game_file;
- }
-
- public override void startup ()
- {
- base.startup ();
-
- settings = new Settings ("org.gnome.gnome-chess.Settings");
-
- var data_dir = File.new_for_path (Path.build_filename (Environment.get_user_data_dir (), "gnome-chess", null));
- DirUtils.create_with_parents (data_dir.get_path (), 0755);
-
- history = new History (data_dir);
-
- builder = new Gtk.Builder ();
- try
- {
- builder.add_from_file (Path.build_filename (Config.PKGDATADIR, "gnome-chess-game-window.ui", null));
- }
- catch (Error e)
- {
- warning ("Could not load UI: %s", e.message);
- }
-
- settings.changed.connect (settings_changed_cb);
-
- string engines_file = Path.build_filename (Config.PKGDATADIR, "engines.conf", null);
- ai_profiles = load_ai_profiles (engines_file);
-
- foreach (var profile in ai_profiles)
- message ("Detected AI profile %s in %s", profile.name, profile.path);
-
- if (game_file == null)
- {
- launcher = create_launcher (engines_file, ai_profiles, history);
-
- var unfinished = history.get_unfinished ();
- if (unfinished != null)
- launcher.show_game_selector ();
- else
- launcher.show_opponent_selector ();
-
- /* Show top-level */
- launcher.show ();
- }
- else
- {
- try
- {
- load_game (game_file, false);
- }
- catch (Error e)
- {
- stderr.printf ("Failed to load %s: %s\n", game_file.get_path (), e.message);
- quit ();
- }
- }
-
- }
-
- private ChessLauncher create_launcher (string engines_file,
- List<AIProfile>? ai_profiles,
- History history)
- {
- var launcher = new ChessLauncher (engines_file, ai_profiles, history);
- launcher.application = this;
-
- launcher.start_game.connect (start_new_game);
- launcher.load_game.connect (this.load_game_handler);
- launcher.destroy.connect (launcher_destroy_cb);
-
- return launcher;
- }
-
- private void create_game_window ()
- {
- window = (Gtk.Window) builder.get_object ("window_game_screen");
-
- game_vbox = (Gtk.Widget) builder.get_object ("game_vbox");
- save_menu = (Gtk.Widget) builder.get_object ("menu_save_item");
- save_as_menu = (Gtk.Widget) builder.get_object ("menu_save_as_item");
- fullscreen_menu = (Gtk.MenuItem) builder.get_object ("fullscreen_item");
- undo_menu = (Gtk.Widget) builder.get_object ("undo_move_item");
- undo_button = (Gtk.Widget) builder.get_object ("undo_move_button");
- resign_menu = (Gtk.Widget) builder.get_object ("resign_item");
- resign_button = (Gtk.Widget) builder.get_object ("resign_button");
- first_move_button = (Gtk.Widget) builder.get_object ("first_move_button");
- prev_move_button = (Gtk.Widget) builder.get_object ("prev_move_button");
- next_move_button = (Gtk.Widget) builder.get_object ("next_move_button");
- last_move_button = (Gtk.Widget) builder.get_object ("last_move_button");
- history_combo = (Gtk.ComboBox) builder.get_object ("history_combo");
- white_time_label = (Gtk.Widget) builder.get_object ("white_time_label");
- black_time_label = (Gtk.Widget) builder.get_object ("black_time_label");
- settings.bind ("show-toolbar", builder.get_object ("toolbar"), "visible", SettingsBindFlags.DEFAULT);
- settings.bind ("show-history", builder.get_object ("navigation_box"), "visible", SettingsBindFlags.DEFAULT);
- var view_box = (Gtk.VBox) builder.get_object ("view_box");
- view_container = (Gtk.Container) builder.get_object ("view_container");
-
- builder.connect_signals (this);
-
- info_bar = new Gtk.InfoBar ();
- var content_area = (Gtk.Container) info_bar.get_content_area ();
- view_box.pack_start (info_bar, false, true, 0);
- var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 6);
- vbox.show ();
- content_area.add (vbox);
- info_title_label = new Gtk.Label ("");
- info_title_label.show ();
- vbox.pack_start (info_title_label, false, true, 0);
- vbox.hexpand = true;
- vbox.vexpand = false;
- info_label = new Gtk.Label ("");
- info_label.show ();
- vbox.pack_start (info_label, true, true, 0);
-
- scene = new ChessScene ();
- scene.is_human.connect ((p) => { return p == human_player; } );
- scene.changed.connect (scene_changed_cb);
- scene.choose_promotion_type.connect (show_promotion_type_selector);
- settings.bind ("show-move-hints", scene, "show-move-hints", SettingsBindFlags.GET);
- settings.bind ("show-numbering", scene, "show-numbering", SettingsBindFlags.GET);
- settings.bind ("piece-theme", scene, "theme-name", SettingsBindFlags.GET);
- settings.bind ("show-3d-smooth", scene, "show-3d-smooth", SettingsBindFlags.GET);
- settings.bind ("move-format", scene, "move-format", SettingsBindFlags.GET);
- settings.bind ("board-side", scene, "board-side", SettingsBindFlags.GET);
-
- settings_changed_cb (settings, "show-3d");
- }
-
- protected override void shutdown ()
- {
- base.shutdown ();
- if (opponent_engine != null)
- opponent_engine.stop ();
- }
-
- public PieceType show_promotion_type_selector ()
- {
- Gtk.Builder promotion_type_selector_builder;
-
- promotion_type_selector_builder = new Gtk.Builder ();
- try
- {
- promotion_type_selector_builder.add_from_file (Path.build_filename (Config.PKGDATADIR, "promotion-type-selector.ui", null));
- }
- catch (Error e)
- {
- warning ("Could not load promotion type selector UI: %s", e.message);
- }
-
- Gtk.Dialog promotion_type_selector_dialog = promotion_type_selector_builder.get_object ("dialog_promotion_type_selector") as Gtk.Dialog;
-
- string color;
- if (game.current_player.color == Color.WHITE)
- color = "white";
- else
- color = "black";
-
- var filename = Path.build_filename (Config.PKGDATADIR, "pieces", scene.theme_name, "%sQueen.svg".printf (color));
- set_piece_image (promotion_type_selector_builder.get_object ("image_queen") as Gtk.Image, filename);
-
- filename = Path.build_filename (Config.PKGDATADIR, "pieces", scene.theme_name, "%sKnight.svg".printf (color));
- set_piece_image (promotion_type_selector_builder.get_object ("image_knight") as Gtk.Image, filename);
-
- filename = Path.build_filename (Config.PKGDATADIR, "pieces", scene.theme_name, "%sRook.svg".printf (color));
- set_piece_image (promotion_type_selector_builder.get_object ("image_rook") as Gtk.Image, filename);
-
- filename = Path.build_filename (Config.PKGDATADIR, "pieces", scene.theme_name, "%sBishop.svg".printf (color));
- set_piece_image (promotion_type_selector_builder.get_object ("image_bishop") as Gtk.Image, filename);
-
- promotion_type_selector_builder.connect_signals (this);
-
- PieceType selection;
- int choice = promotion_type_selector_dialog.run ();
- switch (choice)
- {
- case PromotionTypeSelected.QUEEN:
- selection = PieceType.QUEEN;
- break;
- case PromotionTypeSelected.KNIGHT:
- selection = PieceType.KNIGHT;
- break;
- case PromotionTypeSelected.ROOK:
- selection = PieceType.ROOK;
- break;
- case PromotionTypeSelected.BISHOP:
- selection = PieceType.BISHOP;
- break;
- default:
- selection = PieceType.QUEEN;
- break;
- }
- promotion_type_selector_dialog.destroy ();
-
- return selection;
- }
-
- private void set_piece_image (Gtk.Image image, string filename)
- {
- int width, height;
- if (!Gtk.icon_size_lookup (Gtk.IconSize.DIALOG, out width, out height))
- return;
-
- Gdk.Pixbuf pixbuf;
- try
- {
- pixbuf = Rsvg.pixbuf_from_file_at_size (filename, width, height);
- }
- catch (Error e)
- {
- warning ("Failed to load image %s: %s", filename, e.message);
- return;
- }
- image.set_from_pixbuf (pixbuf);
- }
-
- enum PromotionTypeSelected
- {
- QUEEN,
- KNIGHT,
- ROOK,
- BISHOP
- }
-
- /* Quits the application */
- public void quit_game ()
- {
- autosave ();
- settings.sync ();
- if (launcher != null)
- {
- launcher.destroy ();
- launcher = null;
- }
- if (window != null)
- {
- window.destroy ();
- window = null;
- }
- }
-
- private void autosave ()
- {
- /* FIXME: Prompt user to save somewhere */
- if (!in_history)
- return;
-
- /* Don't autosave if no moves (e.g. they have been undone) or only the computer has moved */
- if (!game_needs_saving)
- {
- if (game_file != null)
- history.remove (game_file);
- return;
- }
-
- try
- {
- if (game_file != null)
- history.update (game_file, "", pgn_game.result);
- else
- game_file = history.add (pgn_game.date, pgn_game.result);
- debug ("Writing current game to %s", game_file.get_path ());
- pgn_game.write (game_file);
- }
- catch (Error e)
- {
- warning ("Failed to autosave: %s", e.message);
- }
- }
-
- private void settings_changed_cb (Settings settings, string key)
- {
- if (key == "show-3d")
- {
- if (view != null)
- {
- view_container.remove (view);
- view.destroy ();
- }
- if (settings.get_boolean ("show-3d"))
- view = new ChessView3D ();
- else
- view = new ChessView2D ();
- view.set_size_request (300, 300);
- view.scene = scene;
- view_container.add (view);
- view.show ();
- }
- }
-
- private void update_history_panel ()
- {
- if (game == null)
- return;
-
- var move_number = scene.move_number;
- var n_moves = (int) game.n_moves;
- if (move_number < 0)
- move_number += 1 + n_moves;
-
- first_move_button.sensitive = n_moves > 0 && move_number != 0;
- prev_move_button.sensitive = move_number > 0;
- next_move_button.sensitive = move_number < n_moves;
- last_move_button.sensitive = n_moves > 0 && move_number != n_moves;
-
- /* Set move text for all moves (it may have changed format) */
- int i = n_moves;
- foreach (var state in game.move_stack)
- {
- if (state.last_move != null)
- {
- Gtk.TreeIter iter;
- if (history_combo.model.iter_nth_child (out iter, null, i))
- set_move_text (iter, state.last_move);
- }
- i--;
- }
-
- history_combo.set_active (move_number);
- }
-
- private void scene_changed_cb (ChessScene scene)
- {
- update_history_panel ();
- }
-
- private void start_game ()
- {
- if (in_history)
- {
- window.title = /* Title of the game window */
- _("Chess");
- }
- else
- {
- var path = game_file.get_path ();
- window.title = /* Title of the window when explicitly loaded a file. The first argument is the
- * base name of the file (e.g. test.pgn), the second argument is the directory
- * (e.g. /home/fred) */
- _("%1$s (%2$s) - Chess").printf (Path.get_basename (path), Path.get_dirname (path));
- }
-
- var model = (Gtk.ListStore) history_combo.model;
- model.clear ();
- Gtk.TreeIter iter;
- model.append (out iter);
- model.set (iter, 0,
- /* Move History Combo: Go to the start of the game */
- _("Game Start"), 1, 0, -1);
- history_combo.set_active_iter (iter);
-
- string fen = ChessGame.STANDARD_SETUP;
- string[] moves = new string[pgn_game.moves.length ()];
- var i = 0;
- foreach (var move in pgn_game.moves)
- moves[i++] = move;
-
- if (pgn_game.set_up)
- {
- if (pgn_game.fen != null)
- fen = pgn_game.fen;
- else
- warning ("Chess game has SetUp tag but no FEN tag");
- }
- game = new ChessGame (fen, moves);
-
- if (pgn_game.time_control != null)
- {
- var controls = pgn_game.time_control.split (":");
- foreach (var control in controls)
- {
- /* We only support simple timeouts */
- var duration = int.parse (control);
- if (duration > 0)
- game.clock = new ChessClock (duration, duration);
- }
- }
-
- game.started.connect (game_start_cb);
- game.turn_started.connect (game_turn_cb);
- game.moved.connect (game_move_cb);
- game.undo.connect (game_undo_cb);
- game.ended.connect (game_end_cb);
- if (game.clock != null)
- game.clock.tick.connect (game_clock_tick_cb);
-
- scene.game = game;
- info_bar.hide ();
- save_menu.sensitive = false;
- save_as_menu.sensitive = false;
- update_history_panel ();
- update_control_buttons ();
-
- // TODO: Could both be engines
- var white_engine = pgn_game.tags.lookup ("WhiteAI");
- var white_level = pgn_game.tags.lookup ("WhiteLevel");
- if (white_level == null)
- white_level = "normal";
-
- var black_engine = pgn_game.tags.lookup ("BlackAI");
- var black_level = pgn_game.tags.lookup ("BlackLevel");
- if (black_level == null)
- black_level = "normal";
-
- opponent = null;
- if (opponent_engine != null)
- {
- opponent_engine.stop ();
- opponent_engine.ready_changed.disconnect (engine_ready_cb);
- opponent_engine.moved.disconnect (engine_move_cb);
- opponent_engine.stopped.disconnect (engine_stopped_cb);
- opponent_engine = null;
- }
- if (white_engine != null)
- {
- opponent = game.white;
- human_player = game.black;
- opponent_engine = get_engine (white_engine, white_level);
- }
- else if (black_engine != null)
- {
- opponent = game.black;
- human_player = game.white;
- opponent_engine = get_engine (black_engine, black_level);
- }
-
- if (opponent_engine != null)
- {
- opponent_engine.ready_changed.connect (engine_ready_cb);
- opponent_engine.moved.connect (engine_move_cb);
- opponent_engine.stopped.connect (engine_stopped_cb);
- opponent_engine.start ();
- }
-
- /* Replay current moves */
- for (var j = (int) game.move_stack.length () - 2; j >= 0; j--)
- {
- var state = game.move_stack.nth_data (j);
- game_move_cb (game, state.last_move);
- }
-
- game_needs_saving = false;
- game.start ();
-
- if (game.result != ChessResult.IN_PROGRESS)
- game_end_cb (game);
-
- white_time_label.queue_draw ();
- black_time_label.queue_draw ();
- }
-
- private ChessEngine? get_engine (string name, string difficulty)
- {
- ChessEngine engine;
- AIProfile? profile = null;
-
- if (name == "human")
- return null;
-
- foreach (var p in ai_profiles)
- {
- if (name == "" || p.name == name)
- {
- profile = p;
- break;
- }
- }
- if (profile == null)
- {
- warning ("Unknown AI profile %s", name);
- if (ai_profiles == null)
- return null;
- profile = ai_profiles.data;
- }
-
- string[] options;
- switch (difficulty)
- {
- case "easy":
- options = profile.easy_options;
- break;
- default:
- case "normal":
- options = profile.normal_options;
- break;
- case "hard":
- options = profile.hard_options;
- break;
- }
-
- if (profile.protocol == "cecp")
- engine = new ChessEngineCECP (options);
- else if (profile.protocol == "uci")
- engine = new ChessEngineUCI (options);
- else
- {
- warning ("Unknown AI protocol %s", profile.protocol);
- return null;
- }
- engine.binary = profile.binary;
-
- return engine;
- }
-
- public override void activate ()
- {
- /* Launcher or game-window would have been created by startup () already. Show it. */
- if (launcher != null)
- launcher.show ();
- if (window != null)
- window.show ();
- }
-
- [CCode (cname = "launcher_destroy_cb", instance_pos = -1)]
- public void launcher_destroy_cb (Gtk.Widget widget)
- {
- launcher = null;
-
- if (window == null)
- quit_game ();
- }
-
- private void engine_ready_cb (ChessEngine engine)
- {
- if (opponent_engine.ready)
- {
- game.start ();
- view.queue_draw ();
- }
- }
-
- private void engine_move_cb (ChessEngine engine, string move)
- {
- opponent.move (move);
- }
-
- private void engine_stopped_cb (ChessEngine engine)
- {
- opponent.resign ();
- }
-
- private void game_start_cb (ChessGame game)
- {
- if (opponent_engine != null)
- opponent_engine.start_game ();
- }
-
- private void game_clock_tick_cb (ChessClock clock)
- {
- white_time_label.queue_draw ();
- black_time_label.queue_draw ();
- }
-
- private void game_turn_cb (ChessGame game, ChessPlayer player)
- {
- if (opponent_engine != null && player == opponent)
- opponent_engine.request_move ();
- }
-
- private void set_move_text (Gtk.TreeIter iter, ChessMove move)
- {
- /* Note there are no move formats for pieces taking kings and this is not allowed in Chess rules */
- const string human_descriptions[] = {/* Human Move String: Description of a white pawn moving from %1$s to %2s, e.g. 'c2 to c4' */
- N_("White pawn moves from %1$s to %2$s"),
- /* Human Move String: Description of a white pawn at %1$s capturing a pawn at %2$s */
- N_("White pawn at %1$s takes the black pawn at %2$s"),
- /* Human Move String: Description of a white pawn at %1$s capturing a rook at %2$s */
- N_("White pawn at %1$s takes the black rook at %2$s"),
- /* Human Move String: Description of a white pawn at %1$s capturing a knight at %2$s */
- N_("White pawn at %1$s takes the black knight at %2$s"),
- /* Human Move String: Description of a white pawn at %1$s capturing a bishop at %2$s */
- N_("White pawn at %1$s takes the black bishop at %2$s"),
- /* Human Move String: Description of a white pawn at %1$s capturing a queen at %2$s */
- N_("White pawn at %1$s takes the black queen at %2$s"),
- /* Human Move String: Description of a white rook moving from %1$s to %2$s, e.g. 'a1 to a5' */
- N_("White rook moves from %1$s to %2$s"),
- /* Human Move String: Description of a white rook at %1$s capturing a pawn at %2$s */
- N_("White rook at %1$s takes the black pawn at %2$s"),
- /* Human Move String: Description of a white rook at %1$s capturing a rook at %2$s */
- N_("White rook at %1$s takes the black rook at %2$s"),
- /* Human Move String: Description of a white rook at %1$s capturing a knight at %2$s */
- N_("White rook at %1$s takes the black knight at %2$s"),
- /* Human Move String: Description of a white rook at %1$s capturing a bishop at %2$s */
- N_("White rook at %1$s takes the black bishop at %2$s"),
- /* Human Move String: Description of a white rook at %1$s capturing a queen at %2$s" */
- N_("White rook at %1$s takes the black queen at %2$s"),
- /* Human Move String: Description of a white knight moving from %1$s to %2$s, e.g. 'b1 to c3' */
- N_("White knight moves from %1$s to %2$s"),
- /* Human Move String: Description of a white knight at %1$s capturing a pawn at %2$s */
- N_("White knight at %1$s takes the black pawn at %2$s"),
- /* Human Move String: Description of a white knight at %1$s capturing a rook at %2$s */
- N_("White knight at %1$s takes the black rook at %2$s"),
- /* Human Move String: Description of a white knight at %1$s capturing a knight at %2$s */
- N_("White knight at %1$s takes the black knight at %2$s"),
- /* Human Move String: Description of a white knight at %1$s capturing a bishop at %2$s */
- N_("White knight at %1$s takes the black bishop at %2$s"),
- /* Human Move String: Description of a white knight at %1$s capturing a queen at %2$s */
- N_("White knight at %1$s takes the black queen at %2$s"),
- /* Human Move String: Description of a white bishop moving from %1$s to %2$s, e.g. 'f1 to b5' */
- N_("White bishop moves from %1$s to %2$s"),
- /* Human Move String: Description of a white bishop at %1$s capturing a pawn at %2$s */
- N_("White bishop at %1$s takes the black pawn at %2$s"),
- /* Human Move String: Description of a white bishop at %1$s capturing a rook at %2$s */
- N_("White bishop at %1$s takes the black rook at %2$s"),
- /* Human Move String: Description of a white bishop at %1$s capturing a knight at %2$s */
- N_("White bishop at %1$s takes the black knight at %2$s"),
- /* Human Move String: Description of a white bishop at %1$s capturing a bishop at %2$s */
- N_("White bishop at %1$s takes the black bishop at %2$s"),
- /* Human Move String: Description of a white bishop at %1$s capturing a queen at %2$s */
- N_("White bishop at %1$s takes the black queen at %2$s"),
- /* Human Move String: Description of a white queen moving from %1$s to %2$s, e.g. 'd1 to d4' */
- N_("White queen moves from %1$s to %2$s"),
- /* Human Move String: Description of a white queen at %1$s capturing a pawn at %2$s */
- N_("White queen at %1$s takes the black pawn at %2$s"),
- /* Human Move String: Description of a white queen at %1$s capturing a rook at %2$s */
- N_("White queen at %1$s takes the black rook at %2$s"),
- /* Human Move String: Description of a white queen at %1$s capturing a knight at %2$s */
- N_("White queen at %1$s takes the black knight at %2$s"),
- /* Human Move String: Description of a white queen at %1$s capturing a bishop at %2$s */
- N_("White queen at %1$s takes the black bishop at %2$s"),
- /* Human Move String: Description of a white queen at %1$s capturing a queen at %2$s */
- N_("White queen at %1$s takes the black queen at %2$s"),
- /* Human Move String: Description of a white king moving from %1$s to %2$s, e.g. 'e1 to f1' */
- N_("White king moves from %1$s to %2$s"),
- /* Human Move String: Description of a white king at %1$s capturing a pawn at %2$s */
- N_("White king at %1$s takes the black pawn at %2$s"),
- /* Human Move String: Description of a white king at %1$s capturing a rook at %2$s */
- N_("White king at %1$s takes the black rook at %2$s"),
- /* Human Move String: Description of a white king at %1$s capturing a knight at %2$s */
- N_("White king at %1$s takes the black knight at %2$s"),
- /* Human Move String: Description of a white king at %1$s capturing a bishop at %2$s */
- N_("White king at %1$s takes the black bishop at %2$s"),
- /* Human Move String: Description of a white king at %1$s capturing a queen at %2$s */
- N_("White king at %1$s takes the black queen at %2$s"),
- /* Human Move String: Description of a black pawn moving from %1$s to %2$s, e.g. 'c8 to c6' */
- N_("Black pawn moves from %1$s to %2$s"),
- /* Human Move String: Description of a black pawn at %1$s capturing a pawn at %2$s */
- N_("Black pawn at %1$s takes the white pawn at %2$s"),
- /* Human Move String: Description of a black pawn at %1$s capturing a rook at %2$s */
- N_("Black pawn at %1$s takes the white rook at %2$s"),
- /* Human Move String: Description of a black pawn at %1$s capturing a knight at %2$s */
- N_("Black pawn at %1$s takes the white knight at %2$s"),
- /* Human Move String: Description of a black pawn at %1$s capturing a bishop at %2$s */
- N_("Black pawn at %1$s takes the white bishop at %2$s"),
- /* Human Move String: Description of a black pawn at %1$s capturing a queen at %2$s */
- N_("Black pawn at %1$s takes the white queen at %2$s"),
- /* Human Move String: Description of a black rook moving from %1$s to %2$s, e.g. 'a8 to a4' */
- N_("Black rook moves from %1$s to %2$s"),
- /* Human Move String: Description of a black rook at %1$s capturing a pawn at %2$s */
- N_("Black rook at %1$s takes the white pawn at %2$s"),
- /* Human Move String: Description of a black rook at %1$s capturing a rook at %2$s */
- N_("Black rook at %1$s takes the white rook at %2$s"),
- /* Human Move String: Description of a black rook at %1$s capturing a knight at %2$s */
- N_("Black rook at %1$s takes the white knight at %2$s"),
- /* Human Move String: Description of a black rook at %1$s capturing a bishop at %2$s */
- N_("Black rook at %1$s takes the white bishop at %2$s"),
- /* Human Move String: Description of a black rook at %1$s capturing a queen at %2$s */
- N_("Black rook at %1$s takes the white queen at %2$s"),
- /* Human Move String: Description of a black knight moving from %1$s to %2$s, e.g. 'b8 to c6' */
- N_("Black knight moves from %1$s to %2$s"),
- /* Human Move String: Description of a black knight at %1$s capturing a pawn at %2$s */
- N_("Black knight at %1$s takes the white pawn at %2$s"),
- /* Human Move String: Description of a black knight at %1$s capturing a rook at %2$s */
- N_("Black knight at %1$s takes the white rook at %2$s"),
- /* Human Move String: Description of a black knight at %1$s capturing a knight at %2$s */
- N_("Black knight at %1$s takes the white knight at %2$s"),
- /* Human Move String: Description of a black knight at %1$s capturing a bishop at %2$s */
- N_("Black knight at %1$s takes the white bishop at %2$s"),
- /* Human Move String: Description of a black knight at %1$s capturing a queen at %2$s */
- N_("Black knight at %1$s takes the white queen at %2$s"),
- /* Human Move String: Description of a black bishop moving from %1$s to %2$s, e.g. 'f8 to b3' */
- N_("Black bishop moves from %1$s to %2$s"),
- /* Human Move String: Description of a black bishop at %1$s capturing a pawn at %2$s */
- N_("Black bishop at %1$s takes the white pawn at %2$s"),
- /* Human Move String: Description of a black bishop at %1$s capturing a rook at %2$s */
- N_("Black bishop at %1$s takes the white rook at %2$s"),
- /* Human Move String: Description of a black bishop at %1$s capturing a knight at %2$s */
- N_("Black bishop at %1$s takes the white knight at %2$s"),
- /* Human Move String: Description of a black bishop at %1$s capturing a bishop at %2$s */
- N_("Black bishop at %1$s takes the white bishop at %2$s"),
- /* Human Move String: Description of a black bishop at %1$s capturing a queen at %2$s */
- N_("Black bishop at %1$s takes the white queen at %2$s"),
- /* Human Move String: Description of a black queen moving from %1$s to %2$s, e.g. 'd8 to d5' */
- N_("Black queen moves from %1$s to %2$s"),
- /* Human Move String: Description of a black queen at %1$s capturing a pawn at %2$s */
- N_("Black queen at %1$s takes the white pawn at %2$s"),
- /* Human Move String: Description of a black queen at %1$s capturing a rook at %2$s */
- N_("Black queen at %1$s takes the white rook at %2$s"),
- /* Human Move String: Description of a black queen at %1$s capturing a knight at %2$s */
- N_("Black queen at %1$s takes the white knight at %2$s"),
- /* Human Move String: Description of a black queen at %1$s capturing a bishop at %2$s */
- N_("Black queen at %1$s takes the white bishop at %2$s"),
- /* Human Move String: Description of a black queen at %1$s capturing a queen at %2$s */
- N_("Black queen at %1$s takes the white queen at %2$s"),
- /* Human Move String: Description of a black king moving from %1$s to %2$s, e.g. 'e8 to f8' */
- N_("Black king moves from %1$s to %2$s"),
- /* Human Move String: Description of a black king at %1$s capturing a pawn at %2$s */
- N_("Black king at %1$s takes the white pawn at %2$s"),
- /* Human Move String: Description of a black king at %1$s capturing a rook at %2$s */
- N_("Black king at %1$s takes the white rook at %2$s"),
- /* Human Move String: Description of a black king at %1$s capturing a knight at %2$s */
- N_("Black king at %1$s takes the white knight at %2$s"),
- /* Human Move String: Description of a black king at %1$s capturing a bishop at %2$s */
- N_("Black king at %1$s takes the white bishop at %2$s"),
- /* Human Move String: Description of a black king at %1$s capturing a queen at %2$s" */
- N_("Black king at %1$s takes the white queen at %2$s")};
-
- var move_text = "";
- switch (scene.move_format)
- {
- case "human":
- int index;
- if (move.victim == null)
- index = 0;
- else
- index = move.victim.type + 1;
- index += move.piece.type * 6;
- if (move.piece.player.color == Color.BLACK)
- index += 36;
-
- // FIXME: Use castling text e.g. "White castles kingside" (do for next release, we are in a string freeze)
-
- var start = "%c%d".printf ('a' + move.f0, move.r0 + 1);
- var end = "%c%d".printf ('a' + move.f1, move.r1 + 1);
- move_text = _(human_descriptions[index]).printf (start, end);
- break;
-
- case "san":
- move_text = move.get_san ();
- break;
-
- case "fan":
- move_text = move.get_fan ();
- break;
-
- default:
- case "lan":
- move_text = move.get_lan ();
- break;
- }
-
- var model = (Gtk.ListStore) history_combo.model;
- var label = "%u%c. %s".printf ((move.number + 1) / 2, move.number % 2 == 0 ? 'b' : 'a', move_text);
- model.set (iter, 0, label, -1);
- }
-
- private void game_move_cb (ChessGame game, ChessMove move)
- {
- /* Need to save after each move */
- game_needs_saving = true;
-
- /* If the only mover is the AI, then don't bother saving */
- if (move.number == 1 && opponent != null && opponent.color == Color.WHITE)
- game_needs_saving = false;
-
- if (move.number > pgn_game.moves.length ())
- pgn_game.moves.append (move.get_san ());
-
- var model = (Gtk.ListStore) history_combo.model;
- Gtk.TreeIter iter;
- model.append (out iter);
- model.set (iter, 1, move.number, -1);
- set_move_text (iter, move);
-
- /* Follow the latest move */
- if (move.number == game.n_moves && scene.move_number == -1)
- history_combo.set_active_iter (iter);
-
- save_menu.sensitive = true;
- save_as_menu.sensitive = true;
- update_history_panel ();
- update_control_buttons ();
-
- if (opponent_engine != null)
- opponent_engine.report_move (move);
- view.queue_draw ();
- }
-
- private void game_undo_cb (ChessGame game)
- {
- /* Notify AI */
- if (opponent_engine != null)
- opponent_engine.undo ();
-
- /* Remove from the PGN game */
- pgn_game.moves.remove_link (pgn_game.moves.last ());
-
- /* Remove from the history */
- var model = (Gtk.ListStore) history_combo.model;
- Gtk.TreeIter iter;
- model.iter_nth_child (out iter, null, model.iter_n_children (null) - 1);
- model.remove (iter);
-
- /* If watching this move, go back one */
- if (scene.move_number > game.n_moves || scene.move_number == -1)
- {
- model.iter_nth_child (out iter, null, model.iter_n_children (null) - 1);
- history_combo.set_active_iter (iter);
- view.queue_draw ();
- }
-
- update_history_panel ();
- update_control_buttons ();
- }
-
- private void update_control_buttons ()
- {
- var can_resign = game.n_moves > 0;
- resign_menu.sensitive = resign_button.sensitive = can_resign;
-
- /* Can undo once the human player has made a move */
- var can_undo = game.n_moves > 0;
- if (opponent != null && opponent.color == Color.WHITE)
- can_undo = game.n_moves > 1;
-
- undo_menu.sensitive = undo_button.sensitive = can_undo;
- }
-
- private void game_end_cb (ChessGame game)
- {
- string title = "";
- switch (game.result)
- {
- case ChessResult.WHITE_WON:
- /* Message display when the white player wins */
- title = _("White wins");
- pgn_game.result = PGNGame.RESULT_WHITE;
- break;
- case ChessResult.BLACK_WON:
- /* Message display when the black player wins */
- title = _("Black wins");
- pgn_game.result = PGNGame.RESULT_BLACK;
- break;
- case ChessResult.DRAW:
- /* Message display when the game is drawn */
- title = _("Game is drawn");
- pgn_game.result = PGNGame.RESULT_DRAW;
- break;
- default:
- break;
- }
-
- string reason = "";
- switch (game.rule)
- {
- case ChessRule.CHECKMATE:
- /* Message displayed when the game ends due to a player being checkmated */
- reason = _("Opponent is in check and cannot move (checkmate)");
- break;
- case ChessRule.STALEMATE:
- /* Message displayed when the game terminates due to a stalemate */
- reason = _("Opponent cannot move (stalemate)");
- break;
- case ChessRule.FIFTY_MOVES:
- /* Message displayed when the game is drawn due to the fifty move rule */
- reason = _("No piece has been taken or pawn moved in the last fifty moves");
- break;
- case ChessRule.TIMEOUT:
- /* Message displayed when the game ends due to one player's clock stopping */
- reason = _("Opponent has run out of time");
- break;
- case ChessRule.THREE_FOLD_REPETITION:
- /* Message displayed when the game is drawn due to the three-fold-repitition rule */
- reason = _("The same board state has occurred three times (three fold repetition)");
- break;
- case ChessRule.INSUFFICIENT_MATERIAL:
- /* Message displayed when the game is drawn due to the insufficient material rule */
- reason = _("Neither player can cause checkmate (insufficient material)");
- break;
- case ChessRule.RESIGN:
- if (game.result == ChessResult.WHITE_WON)
- {
- /* Message displayed when the game ends due to the black player resigning */
- reason = _("The black player has resigned");
- }
- else
- {
- /* Message displayed when the game ends due to the white player resigning */
- reason = _("The white player has resigned");
- }
- break;
- case ChessRule.ABANDONMENT:
- /* Message displayed when a game is abandoned */
- reason = _("The game has been abandoned");
- pgn_game.termination = PGNGame.TERMINATE_ABANDONED;
- break;
- case ChessRule.DEATH:
- /* Message displayed when the game ends due to a player dying */
- reason = _("One of the players has died");
- pgn_game.termination = PGNGame.TERMINATE_DEATH;
- break;
- }
-
- info_title_label.set_markup ("<big><b>%s</b></big>".printf (title));
- info_label.set_text (reason);
- info_bar.show ();
-
- white_time_label.queue_draw ();
- black_time_label.queue_draw ();
- }
-
- [CCode (cname = "G_MODULE_EXPORT game_window_delete_event_cb", instance_pos = -1)]
- public bool game_window_delete_event_cb (Gtk.Widget widget, Gdk.Event event)
- {
- quit_game ();
- return false;
- }
-
- [CCode (cname = "G_MODULE_EXPORT game_window_configure_event_cb", instance_pos = -1)]
- public bool game_window_configure_event_cb (Gtk.Widget widget, Gdk.EventConfigure event)
- {
- if (!settings.get_boolean ("maximized") && !settings.get_boolean ("fullscreen"))
- {
- settings.set_int ("game-screen-width", event.width);
- settings.set_int ("game-screen-height", event.height);
- }
-
- return false;
- }
-
- [CCode (cname = "G_MODULE_EXPORT game_window_window_state_event_cb", instance_pos = -1)]
- public bool game_window_window_state_event_cb (Gtk.Widget widget, Gdk.EventWindowState event)
- {
- if ((event.changed_mask & Gdk.WindowState.MAXIMIZED) != 0)
- {
- var is_maximized = (event.new_window_state & Gdk.WindowState.MAXIMIZED) != 0;
- settings.set_boolean ("maximized", is_maximized);
- }
- if ((event.changed_mask & Gdk.WindowState.FULLSCREEN) != 0)
- {
- bool is_fullscreen = (event.new_window_state & Gdk.WindowState.FULLSCREEN) != 0;
- settings.set_boolean ("fullscreen", is_fullscreen);
- fullscreen_menu.label = is_fullscreen ? Gtk.Stock.LEAVE_FULLSCREEN : Gtk.Stock.FULLSCREEN;
- }
-
- return false;
- }
-
- [CCode (cname = "G_MODULE_EXPORT new_game_cb", instance_pos = -1)]
- public void new_game_cb (Gtk.Widget widget)
- {
- if (game_needs_saving || (in_history && game_file != null))
- {
- var dialog = new Gtk.MessageDialog.with_markup (window,
- Gtk.DialogFlags.MODAL,
- Gtk.MessageType.QUESTION,
- Gtk.ButtonsType.NONE,
- "<span weight=\"bold\" size=\"larger\">%s</span>",
- _("Save this game before starting a new one?"));
- dialog.add_button (Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL);
- dialog.add_button (_("_Abandon game"), Gtk.ResponseType.NO);
- dialog.add_button (_("_Save game for later"), Gtk.ResponseType.YES);
- var result = dialog.run ();
- dialog.destroy ();
- if (result == Gtk.ResponseType.CANCEL)
- return;
-
- if (result == Gtk.ResponseType.NO)
- {
- in_history = false;
- game_needs_saving = false;
- }
- }
-
- autosave ();
-
- /* Hide game-window and show launcher */
- window.hide ();
- if (launcher == null)
- launcher = create_launcher (engines_file, ai_profiles, history);
- launcher.present ();
- launcher.show_opponent_selector ();
- }
-
- [CCode (cname = "G_MODULE_EXPORT resign_cb", instance_pos = -1)]
- public void resign_cb (Gtk.Widget widget)
- {
- game.current_player.resign ();
- }
-
- [CCode (cname = "G_MODULE_EXPORT claim_draw_cb", instance_pos = -1)]
- public void claim_draw_cb (Gtk.Widget widget)
- {
- game.current_player.claim_draw ();
- }
-
- [CCode (cname = "G_MODULE_EXPORT undo_move_cb", instance_pos = -1)]
- public void undo_move_cb (Gtk.Widget widget)
- {
- if (opponent != null)
- human_player.undo ();
- else
- game.opponent.undo ();
- }
-
- [CCode (cname = "G_MODULE_EXPORT quit_cb", instance_pos = -1)]
- public void quit_cb (Gtk.Widget widget)
- {
- quit_game ();
- }
-
- [CCode (cname = "G_MODULE_EXPORT white_time_draw_cb", instance_pos = -1)]
- public bool white_time_draw_cb (Gtk.Widget widget, Cairo.Context c)
- {
- double fg[3] = { 0.0, 0.0, 0.0 };
- double bg[3] = { 1.0, 1.0, 1.0 };
-
- draw_time (widget, c, make_clock_text (game.clock, Color.WHITE), fg, bg);
- return false;
- }
-
- [CCode (cname = "G_MODULE_EXPORT black_time_draw_cb", instance_pos = -1)]
- public bool black_time_draw_cb (Gtk.Widget widget, Cairo.Context c)
- {
- double fg[3] = { 1.0, 1.0, 1.0 };
- double bg[3] = { 0.0, 0.0, 0.0 };
-
- draw_time (widget, c, make_clock_text (game.clock, Color.BLACK), fg, bg);
- return false;
- }
-
- private string make_clock_text (ChessClock? clock, Color color)
- {
- if (clock == null)
- return "â";
-
- int used;
- if (color == Color.WHITE)
- used = (int) (game.clock.white_duration / 1000 - game.clock.white_used_in_seconds);
- else
- used = (int) (game.clock.black_duration / 1000 - game.clock.black_used_in_seconds);
-
- if (used >= 60)
- return "%d:%02d".printf (used / 60, used % 60);
- else
- return ":%02d".printf (used);
- }
-
- private void draw_time (Gtk.Widget widget, Cairo.Context c, string text, double[] fg, double[] bg)
- {
- double alpha = 1.0;
-
- if (widget.get_state () == Gtk.StateType.INSENSITIVE)
- alpha = 0.5;
- c.set_source_rgba (bg[0], bg[1], bg[2], alpha);
- c.paint ();
-
- c.set_source_rgba (fg[0], fg[1], fg[2], alpha);
- c.select_font_face ("fixed", Cairo.FontSlant.NORMAL, Cairo.FontWeight.BOLD);
- c.set_font_size (0.6 * widget.get_allocated_height ());
- Cairo.TextExtents extents;
- c.text_extents (text, out extents);
- c.move_to ((widget.get_allocated_width () - extents.width) / 2 - extents.x_bearing,
- (widget.get_allocated_height () - extents.height) / 2 - extents.y_bearing);
- c.show_text (text);
-
- widget.set_size_request ((int) extents.width + 6, -1);
- }
-
- [CCode (cname = "G_MODULE_EXPORT history_combo_changed_cb", instance_pos = -1)]
- public void history_combo_changed_cb (Gtk.ComboBox combo)
- {
- Gtk.TreeIter iter;
- if (!combo.get_active_iter (out iter))
- return;
- int move_number;
- combo.model.get (iter, 1, out move_number, -1);
- if (game == null || move_number == game.n_moves)
- move_number = -1;
- scene.move_number = move_number;
- }
-
- [CCode (cname = "G_MODULE_EXPORT history_latest_clicked_cb", instance_pos = -1)]
- public void history_latest_clicked_cb (Gtk.Widget widget)
- {
- scene.move_number = -1;
- }
-
- [CCode (cname = "G_MODULE_EXPORT history_next_clicked_cb", instance_pos = -1)]
- public void history_next_clicked_cb (Gtk.Widget widget)
- {
- if (scene.move_number == -1)
- return;
-
- int move_number = scene.move_number + 1;
- if (move_number >= game.n_moves)
- scene.move_number = -1;
- else
- scene.move_number = move_number;
- }
-
- [CCode (cname = "G_MODULE_EXPORT history_previous_clicked_cb", instance_pos = -1)]
- public void history_previous_clicked_cb (Gtk.Widget widget)
- {
- if (scene.move_number == 0)
- return;
-
- if (scene.move_number == -1)
- scene.move_number = (int) game.n_moves - 1;
- else
- scene.move_number = scene.move_number - 1;
- }
-
- [CCode (cname = "G_MODULE_EXPORT history_start_clicked_cb", instance_pos = -1)]
- public void history_start_clicked_cb (Gtk.Widget widget)
- {
- scene.move_number = 0;
- }
-
- [CCode (cname = "G_MODULE_EXPORT toggle_fullscreen_cb", instance_pos = -1)]
- public void toggle_fullscreen_cb (Gtk.Widget widget)
- {
- if (fullscreen_menu.label == Gtk.Stock.FULLSCREEN)
- window.fullscreen ();
- else
- window.unfullscreen ();
- }
-
- [CCode (cname = "G_MODULE_EXPORT preferences_cb", instance_pos = -1)]
- public void preferences_cb (Gtk.Widget widget)
- {
- if (preferences_dialog != null)
- {
- preferences_dialog.present ();
- return;
- }
-
- preferences_builder = new Gtk.Builder ();
- try
- {
- preferences_builder.add_from_file (Path.build_filename (Config.PKGDATADIR, "preferences.ui", null));
- }
- catch (Error e)
- {
- warning ("Could not load preferences UI: %s", e.message);
- }
- preferences_dialog = (Gtk.Dialog) preferences_builder.get_object ("preferences");
-
- settings.bind ("show-numbering", preferences_builder.get_object ("show_numbering_check"),
- "active", SettingsBindFlags.DEFAULT);
- settings.bind ("show-move-hints", preferences_builder.get_object ("show_move_hints_check"),
- "active", SettingsBindFlags.DEFAULT);
- settings.bind ("show-toolbar", preferences_builder.get_object ("show_toolbar_check"),
- "active", SettingsBindFlags.DEFAULT);
- settings.bind ("show-history", preferences_builder.get_object ("show_history_check"),
- "active", SettingsBindFlags.DEFAULT);
- settings.bind ("show-3d", preferences_builder.get_object ("show_3d_check"),
- "active", SettingsBindFlags.DEFAULT);
- settings.bind ("show-3d-smooth", preferences_builder.get_object ("show_3d_smooth_check"),
- "active", SettingsBindFlags.DEFAULT);
-
- var orientation_combo = (Gtk.ComboBox) preferences_builder.get_object ("orientation_combo");
- set_combo (orientation_combo, 1, settings.get_string ("board-side"));
-
- var move_combo = (Gtk.ComboBox) preferences_builder.get_object ("move_format_combo");
- set_combo (move_combo, 1, settings.get_string ("move-format"));
-
- var theme_combo = (Gtk.ComboBox) preferences_builder.get_object ("piece_style_combo");
- set_combo (theme_combo, 1, settings.get_string ("piece-theme"));
-
- preferences_builder.connect_signals (this);
-
- preferences_dialog.present ();
- }
-
- private void set_combo (Gtk.ComboBox combo, int value_index, string value)
- {
- Gtk.TreeIter iter;
- var model = combo.model;
- if (!model.get_iter_first (out iter))
- return;
- do
- {
- string v;
- model.get (iter, value_index, out v, -1);
- if (v == value)
- {
- combo.set_active_iter (iter);
- return;
- }
- } while (model.iter_next (ref iter));
- }
-
- private string? get_combo (Gtk.ComboBox combo, int value_index)
- {
- string value;
- Gtk.TreeIter iter;
- if (!combo.get_active_iter (out iter))
- return null;
- combo.model.get (iter, value_index, out value, -1);
- return value;
- }
-
- [CCode (cname = "G_MODULE_EXPORT preferences_response_cb", instance_pos = -1)]
- public void preferences_response_cb (Gtk.Widget widget, int response_id)
- {
- preferences_dialog.hide ();
- }
-
- [CCode (cname = "G_MODULE_EXPORT preferences_delete_event_cb", instance_pos = -1)]
- public bool preferences_delete_event_cb (Gtk.Widget widget, Gdk.Event event)
- {
- preferences_response_cb (widget, Gtk.ResponseType.CANCEL);
- return true;
- }
-
- [CCode (cname = "G_MODULE_EXPORT piece_style_combo_changed_cb", instance_pos = -1)]
- public void piece_style_combo_changed_cb (Gtk.ComboBox combo)
- {
- settings.set_string ("piece-theme", get_combo (combo, 1));
- }
-
- [CCode (cname = "G_MODULE_EXPORT show_3d_toggle_cb", instance_pos = -1)]
- public void show_3d_toggle_cb (Gtk.ToggleButton widget)
- {
- var w = (Gtk.Widget) preferences_builder.get_object ("show_3d_smooth_check");
- w.sensitive = widget.active;
-
- w = (Gtk.Widget) preferences_builder.get_object ("piece_style_combo");
- w.sensitive = !widget.active;
- }
-
- [CCode (cname = "G_MODULE_EXPORT move_format_combo_changed_cb", instance_pos = -1)]
- public void move_format_combo_changed_cb (Gtk.ComboBox combo)
- {
- settings.set_string ("move-format", get_combo (combo, 1));
- }
-
- [CCode (cname = "G_MODULE_EXPORT orientation_combo_changed_cb", instance_pos = -1)]
- public void orientation_combo_changed_cb (Gtk.ComboBox combo)
- {
- settings.set_string ("board-side", get_combo (combo, 1));
- }
-
- [CCode (cname = "G_MODULE_EXPORT help_cb", instance_pos = -1)]
- public void help_cb (Gtk.Widget widget)
- {
- try
- {
- Gtk.show_uri (window.get_screen (), "help:gnome-chess", Gtk.get_current_event_time ());
- }
- catch (Error e)
- {
- warning ("Unable to open help: %s", e.message);
- }
- }
-
- private const string[] authors = { "Robert Ancell <robert ancell gmail com>", null };
- private const string[] artists = { "John-Paul Gignac (3D Models)", "Max Froumentin (2D Models)", "Hylke Bons <h bons student rug nl> (icon)", null };
-
- [CCode (cname = "G_MODULE_EXPORT about_cb", instance_pos = -1)]
- public void about_cb (Gtk.Widget widget)
- {
- if (about_dialog != null)
- {
- about_dialog.present ();
- return;
- }
-
- about_dialog = new Gtk.AboutDialog ();
- about_dialog.transient_for = window;
- about_dialog.modal = true;
- about_dialog.program_name = "Chess";
- about_dialog.version = Config.VERSION;
- about_dialog.copyright = "Copyright 2010 Robert Ancell <robert ancell gmail com>";
- about_dialog.license_type = Gtk.License.GPL_2_0;
- about_dialog.comments = _("The 2D/3D chess game for GNOME. \n\nGNOME Chess is a part of GNOME Games.");
- about_dialog.authors = authors;
- about_dialog.artists = artists;
- about_dialog.translator_credits = "translator-credits";
- about_dialog.website = "http://www.gnome.org/projects/gnome-games/";
- about_dialog.website_label = _("GNOME Games web site");
- about_dialog.logo_icon_name = "gnome-chess";
- about_dialog.response.connect (about_response_cb);
- about_dialog.show ();
- }
-
- private void about_response_cb (int response_id)
- {
- about_dialog.destroy ();
- about_dialog = null;
- }
-
- [CCode (cname = "G_MODULE_EXPORT save_game_as_cb", instance_pos = -1)]
- public void save_game_as_cb (Gtk.Widget widget)
- {
- save_game ();
- }
-
- [CCode (cname = "G_MODULE_EXPORT save_game_cb", instance_pos = -1)]
- public void save_game_cb (Gtk.Widget widget)
- {
- save_game ();
- }
-
- private void add_info_bar_to_dialog (Gtk.Dialog dialog, out Gtk.InfoBar info_bar, out Gtk.Label label)
- {
- var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
- vbox.show ();
-
- info_bar = new Gtk.InfoBar ();
- var content_area = (Gtk.Container) info_bar.get_content_area ();
- vbox.pack_start (info_bar, false, true, 0);
-
- label = new Gtk.Label ("");
- content_area.add (label);
- label.show ();
-
- var child = (Gtk.Container) dialog.get_child ();
- child.reparent (vbox);
- child.border_width = dialog.border_width;
- dialog.border_width = 0;
- dialog.add (vbox);
- }
-
- private void save_game ()
- {
- /* Show active dialog */
- if (save_dialog != null)
- {
- save_dialog.present ();
- return;
- }
-
- save_dialog = new Gtk.FileChooserDialog (/* Title of save game dialog */
- _("Save Chess Game"),
- window, Gtk.FileChooserAction.SAVE,
- Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
- Gtk.Stock.SAVE, Gtk.ResponseType.OK, null);
- add_info_bar_to_dialog (save_dialog, out save_dialog_info_bar, out save_dialog_error_label);
-
- save_dialog.file_activated.connect (save_file_cb);
- save_dialog.response.connect (save_cb);
-
- /* Filter out non PGN files by default */
- var pgn_filter = new Gtk.FileFilter ();
- gtk_file_filter_set_name (pgn_filter,
- /* Save Game Dialog: Name of filter to show only PGN files */
- _("PGN files"));
- pgn_filter.add_pattern ("*.pgn");
- save_dialog.add_filter (pgn_filter);
-
- var all_filter = new Gtk.FileFilter ();
- gtk_file_filter_set_name (all_filter,
- /* Save Game Dialog: Name of filter to show all files */
- _("All files"));
- all_filter.add_pattern ("*");
- save_dialog.add_filter (all_filter);
-
- save_dialog.present ();
- }
-
- private void save_file_cb ()
- {
- save_cb (Gtk.ResponseType.OK);
- }
-
- private void save_cb (int response_id)
- {
- if (response_id == Gtk.ResponseType.OK)
- {
- save_menu.sensitive = false;
-
- try
- {
- pgn_game.write (save_dialog.get_file ());
- }
- catch (Error e)
- {
- save_dialog_error_label.set_text (_("Failed to save game: %s").printf (e.message));
- save_dialog_info_bar.set_message_type (Gtk.MessageType.ERROR);
- save_dialog_info_bar.show ();
- return;
- }
- }
-
- save_dialog.destroy ();
- save_dialog = null;
- save_dialog_info_bar = null;
- save_dialog_error_label = null;
- }
-
- [CCode (cname = "G_MODULE_EXPORT open_game_cb", instance_pos = -1)]
- public void open_game_cb (Gtk.Widget widget)
- {
- /* Show active dialog */
- if (open_dialog != null)
- {
- open_dialog.present ();
- return;
- }
-
- open_dialog = new Gtk.FileChooserDialog (/* Title of load game dialog */
- _("Load Chess Game"),
- window, Gtk.FileChooserAction.OPEN,
- Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
- Gtk.Stock.OPEN, Gtk.ResponseType.OK, null);
- add_info_bar_to_dialog (open_dialog, out open_dialog_info_bar, out open_dialog_error_label);
-
- open_dialog.file_activated.connect (open_file_cb);
- open_dialog.response.connect (open_cb);
-
- /* Filter out non PGN files by default */
- var pgn_filter = new Gtk.FileFilter ();
- gtk_file_filter_set_name (pgn_filter,
- /* Load Game Dialog: Name of filter to show only PGN files */
- _("PGN files"));
- pgn_filter.add_pattern ("*.pgn");
- open_dialog.add_filter (pgn_filter);
-
- var all_filter = new Gtk.FileFilter ();
- gtk_file_filter_set_name (all_filter,
- /* Load Game Dialog: Name of filter to show all files */
- _("All files"));
- all_filter.add_pattern ("*");
- open_dialog.add_filter (all_filter);
-
- open_dialog.present ();
- }
-
- private void open_file_cb ()
- {
- open_cb (Gtk.ResponseType.OK);
- }
-
- private void open_cb (int response_id)
- {
- if (response_id == Gtk.ResponseType.OK)
- {
- try
- {
- load_game (open_dialog.get_file (), false);
- }
- catch (Error e)
- {
- open_dialog_error_label.set_text (_("Failed to open game: %s").printf (e.message));
- open_dialog_info_bar.set_message_type (Gtk.MessageType.ERROR);
- open_dialog_info_bar.show ();
- return;
- }
- }
-
- open_dialog.destroy ();
- open_dialog = null;
- open_dialog_info_bar = null;
- open_dialog_error_label = null;
- }
-
- private void display_window ()
- {
- if (settings.get_boolean ("fullscreen"))
- window.fullscreen ();
- else if (settings.get_boolean ("maximized"))
- window.maximize ();
-
- game_vbox.show ();
- window.resize (settings.get_int ("game-screen-width"),
- settings.get_int ("game-screen-height"));
-
- window.show ();
- }
-
- private void start_new_game ()
- {
- if (window == null)
- {
- create_game_window ();
- add_window (window);
- }
-
- in_history = true;
- game_file = null;
-
- pgn_game = new PGNGame ();
- var now = new DateTime.now_local ();
- pgn_game.date = now.format ("%Y.%m.%d");
- pgn_game.time = now.format ("%H:%M:%S");
- var duration = settings.get_int ("duration");
- if (duration > 0)
- pgn_game.time_control = "%d".printf (duration);
-
- if (settings.get_string ("opponent-type") == "robot")
- {
- var engine_name = settings.get_string ("opponent");
- var engine_level = settings.get_string ("difficulty");
- if (engine_name != null)
- {
- if (settings.get_boolean ("play-as-white"))
- {
- pgn_game.tags.insert ("BlackAI", engine_name);
- pgn_game.tags.insert ("BlackLevel", engine_level);
- }
- else
- {
- pgn_game.tags.insert ("WhiteAI", engine_name);
- pgn_game.tags.insert ("WhiteLevel", engine_level);
- }
- }
- }
- start_game ();
-
- display_window ();
- if (launcher != null)
- launcher.destroy ();
- }
-
- [CCode (cname = "load_game_handler", instance_pos = -1)]
- private void load_game_handler (ChessLauncher launcher,
- File file,
- bool from_history)
- {
- try
- {
- load_game (file, from_history);
- }
- catch (Error e)
- {
- stderr.printf ("Error loading pgn from file: %s", e.message);
- }
- }
-
- private void load_game (File file, bool from_history) throws Error
- {
- if (window == null)
- {
- create_game_window ();
- add_window (window);
- }
-
- var pgn = new PGN.from_file (file);
- pgn_game = pgn.games.nth_data (0);
-
- game_file = file;
- in_history = from_history;
- start_game ();
-
- display_window ();
- if (launcher != null)
- launcher.destroy ();
- }
-}
-
class GnomeChess
{
static bool show_version;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]