[gnome-chess/chess-telepathy-networking-support-664946-rebase: 58/64] [gnome-chess-channel-handler] Handle channels



commit 3f37597023e0a79c963c190bcbe919ef2550f23c
Author: Chandni Verma <chandniverma2112 gmail com>
Date:   Wed Sep 26 21:02:24 2012 +0530

    [gnome-chess-channel-handler] Handle channels

 src/chess-pgn.vala                   |   10 +
 src/gnome-chess-channel-handler.vala |  560 +++++++++++++++++++++++++++++++++-
 2 files changed, 567 insertions(+), 3 deletions(-)
---
diff --git a/src/chess-pgn.vala b/src/chess-pgn.vala
index ebce1b9..04d1b0e 100644
--- a/src/chess-pgn.vala
+++ b/src/chess-pgn.vala
@@ -74,6 +74,16 @@ public class PGNGame
         get { return tags.lookup ("Time"); }
         set { tags.insert ("Time", value); }
     }
+    public string utcdate
+    {
+        get { return tags.lookup ("UTCDate"); }
+        set { tags.insert ("UTCDate", value); }
+    }
+    public string utctime
+    {
+        get { return tags.lookup ("UTCTime"); }
+        set { tags.insert ("UTCTime", value); }
+    }
     public string round
     {
         get { return tags.lookup ("Round"); }
diff --git a/src/gnome-chess-channel-handler.vala b/src/gnome-chess-channel-handler.vala
index 35cb0f5..3e86d02 100644
--- a/src/gnome-chess-channel-handler.vala
+++ b/src/gnome-chess-channel-handler.vala
@@ -40,17 +40,60 @@ public class HandlerApplication : Application
     private GamesContacts.IndividualView? individual_view = null;
     private GamesContacts.LiveSearch? search_widget;
 
+    private Gtk.TreeView channel_view;
+
     /* Objects from gnome-chess-game-window.ui */
     private Gtk.Widget view_box;
     private Gtk.Widget handler_menubar;
 
+    private bool is_activated = false;
+    private TelepathyGLib.SimpleHandler tp_handler;
+
+    private enum ChannelsColumn
+    {
+        USER_ACTION_TIME,  /* tp-time of channel creation */
+        TARGET_CONTACT,    /* target contact */
+        CHANNEL,           /* tube channel */
+        OUR_COLOUR_WHITE,  /* our piece color */
+        PGN_GAME,
+        HISTORY_MODEL,
+        HISTORY_COMBO_ACTIVE_ITER,
+        CHESS_GAME,
+        CHESS_SCENE,
+        INFO_BAR,
+        INFO_BAR_VISIBILITY,
+        GAME_NEEDS_SAVING,
+        NUM_COLS
+    }
+
+    /* Channels */
+    Gtk.ListStore channels;
+    Gtk.TreeIter? selected_channel_iter = null;
+
     public HandlerApplication ()
     {
         Object (
-            application_id: TelepathyGLib.CLIENT_BUS_NAME_BASE + "Gnome.Chess",
+            application_id: "org.gnome.gnome-chess.handler",
             flags: ApplicationFlags.FLAGS_NONE);
 
         chess_networking_init ();
+        channels = new Gtk.ListStore (ChannelsColumn.NUM_COLS,
+            typeof (int64),                  /* USER_ACTION_TIME */
+            typeof (TelepathyGLib.Contact),  /* TARGET_CONTACT */
+            typeof (TelepathyGLib.Channel),  /* CHANNEL */
+            typeof (bool),                   /* OUR_COLOUR_WHITE */
+            typeof (PGNGame),                /* PGN_GAME */
+            typeof (Gtk.TreeModel),          /* HISTORY_MODEL */
+            typeof (Gtk.TreeIter),           /* HISTORY_COMBO_ACTIVE_ITER */
+            typeof (ChessGame),              /* CHESS_GAME */
+            typeof (ChessScene),             /* CHESS_SCENE */
+            typeof (Gtk.InfoBar),            /* INFO_BAR */
+            typeof (bool),                   /* INFO_BAR_VISIBILITY */
+            typeof (bool)                    /* GAME_NEEDS_SAVING */
+           // typeof (), /*  */
+            );
+
+        (channel_view).cursor_changed.connect (cursor_changed_cb);
 
         settings = new Settings ("org.gnome.gnome-chess");
         settings_common = new Settings ("org.gnome.gnome-chess.games-common");
@@ -83,6 +126,79 @@ public class HandlerApplication : Application
         handler_window.show_all ();
     }
 
+    private bool change_view (Gtk.TreeModel model, Gtk.TreePath path, Gtk.TreeIter channel_iter)
+    {
+        ChessGame? game = null;
+        ChessScene? scene = null;
+        bool view_visible = true;
+
+        channels  get (channel_iter, ChannelsColumn.CHESS_GAME, out game, ChannelsColumn.CHESS_SCENE, out scene);
+        assert (game != null && scene != null);
+        ChessView chess_view = (game as Object).get_data<ChessView> ("chess-view");
+        if (chess_view != null)
+        {
+            view_visible = chess_view.get_visible ();
+            view_container.remove (chess_view);
+            chess_view.destroy ();
+        }
+        if (settings_common.get_boolean ("show-3d"))
+            chess_view = new ChessView3D ();
+        else
+            chess_view = new ChessView2D ();
+        chess_view.set_size_request (300, 300);
+        chess_view.scene = scene;
+        view_container.add (chess_view);
+        chess_view.set_visible (view_visible);
+
+        return false;
+    }
+
+    private void settings_changed_cb (Settings settings_common, string key)
+    {
+        if (key == "show-3d")
+        {
+            channels  foreach (change_view);
+        }
+    }
+
+    private void show_game ()
+    {
+        TelepathyGLib.Contact? remote_player = null;
+        int64 action_time = -1;
+
+        if (selected_channel_iter == null)
+        {
+            debug ("No channel iteration selected");
+            return;
+        }
+        channels  get (selected_channel_iter, ChannelsColumn.TARGET_CONTACT, out remote_player, ChannelsColumn.USER_ACTION_TIME, out action_time);
+
+        if (remote_player != null && action_time != -1)
+        {
+            debug ("Showing game with %s. Initiation timeframe: %llu", remote_player.identifier, action_time);
+        }
+    }
+
+    private void cursor_changed_cb ()
+    {
+        Gtk.TreePath? path;
+        Gtk.TreeIter iter;
+
+        channel_view.get_cursor (out path, null);
+        if (path == null)
+        {
+            debug ("Couldn't obtain path the cursor is at");
+            return;
+        }
+
+        if (channels.get_iter (out iter, path))
+        {
+            selected_channel_iter = iter;
+
+            show_game ();
+        }
+    }
+
     private void create_handler_window ()
     {
         string [] objects_from_traditional_window =
@@ -106,6 +222,7 @@ public class HandlerApplication : Application
         contacts_box = (Gtk.Widget) builder.get_object ("contacts_box");
         alignment_play = (Gtk.Widget) builder.get_object ("alignment_play");
 
+        channel_view = new Gtk.TreeView ();
 
         /* Add objects from gnome-chess-game-window.ui */
         try {
@@ -114,6 +231,7 @@ public class HandlerApplication : Application
               objects_from_traditional_window);
 
             view_box = (Gtk.Widget) builder.get_object ("view_box");
+            view_container = (Gtk.Container) builder.get_object ("view_container");
             handler_menubar = (Gtk.Widget) builder.get_object ("menubar");
 
             handler_menubar.unparent ();
@@ -134,6 +252,9 @@ public class HandlerApplication : Application
         handler_window.title = _("Chess");
 
         prepare_contact_list ();
+
+        channel_view = new Gtk.TreeView ();
+        channel_view.model = channels;
     }
 
     private void prepare_contact_list ()
@@ -168,20 +289,452 @@ public class HandlerApplication : Application
         (contacts_box as Gtk.Container).add (search_widget);
     }
 
+    [CCode (cname = "G_MODULE_EXPORT channels_clicked_cb", instance_pos=-1)]
+    public void channels_clicked_cb (Gtk.Button button)
+    {
+        scrolledwindow_list.remove (contacts_box);
+        scrolledwindow_list.add (channel_view);
+    }
+
+    [CCode (cname = "G_MODULE_EXPORT contacts_clicked_cb", instance_pos=-1)]
+    public void contacts_clicked_cb (Gtk.Button button)
+    {
+        scrolledwindow_list.remove (channel_view);
+        scrolledwindow_list.add (contacts_box);
+    }
+
+    [CCode (cname = "G_MODULE_EXPORT goa_clicked_cb", instance_pos=-1)]
+    public void goa_clicked_cb (Gtk.Button button)
+    {
+    }
+
     [CCode (cname = "G_MODULE_EXPORT quit_cb", instance_pos = -1)]
-    private new void quit_cb (Gtk.Widget widget)
+    public override void quit_cb (Gtk.Widget widget)
     {
         quit_game ();
     }
 
+    private bool save_game_and_remove_channel (Gtk.TreeModel model, Gtk.TreePath path, Gtk.TreeIter channel_iter)
+    {
+        TelepathyGLib.Channel? channel = null;
+        int64 initiation_time = -1;
+        ChessGame game;
+        PGNGame pgn_game;
+        bool game_needs_saving;
+
+        channels  get (channel_iter, ChannelsColumn.USER_ACTION_TIME, out initiation_time, ChannelsColumn.CHANNEL, out channel);
+
+        debug ("Removing chess tube channel with %s initiated at instance: %lld", channel.target_contact.identifier, initiation_time);
+
+        return false;
+    }
+
     /* Quits the application */
-    public new void quit_game ()
+    private void quit_game ()
     {
         Settings.sync ();
+        channels  foreach (save_game_and_remove_channel);
+    }
+
+    private new void game_move_cb (ChessGame game, ChessMove move)
+    {
+        Gtk.TreeIter *channel_iter;
+        ChessView chess_view;
+
+        channel_iter = (game as Object).get_data ("channel-iter");
+        chess_view = (game as Object).get_data<ChessView> ("chess-view");
+
+        /* Need to save after each move */
+        channels  set (*channel_iter, ChannelsColumn.GAME_NEEDS_SAVING, true);
+
+        if (move.number > pgn_game.moves.length ())
+            pgn_game.moves.append (move.get_san ());
+
+        Gtk.ListStore model;
+        ChessScene scene;
+        channels  get (*channel_iter, ChannelsColumn.HISTORY_MODEL, out model, ChannelsColumn.CHESS_SCENE, out scene);
+
+        Gtk.TreeIter iter;
+        model.append (out iter);
+        model.set (iter, 1, move.number, -1);
+        set_move_text (iter, move, scene, model);
+
+        /* Follow the latest move */
+        if (move.number == game.n_moves && scene.move_number == -1)
+            channels  set (*channel_iter, ChannelsColumn.HISTORY_COMBO_ACTIVE_ITER, iter);
+
+        update_history_model (game, scene, model);
+
+        /* UI updation */
+        if (selected_channel_iter == *channel_iter)
+        {
+            update_history_panel (game,scene, model);
+            update_control_buttons ();
+        }
+
+        /* TODO: Acknowledge if opponent made this move */
+
+        chess_view.queue_draw ();
+    }
+
+    private void create_and_add_game (TelepathyGLib.DBusTubeChannel tube,
+        Gtk.TreeIter iter)
+    {
+        /* Create a new game with the offered parameters */
+        Variant tube_params = tube.dup_parameters_vardict ();
+        bool our_color_white;
+        tube_params.lookup ("offerer-white", "b", out our_color_white);
+        int32 duration;
+        tube_params.lookup ("duration", "i", out duration);
+
+        var pgn_game = new PGNGame ();
+        var now = new DateTime.now_local ();
+        var now_UTC = now.to_utc ();
+        pgn_game.date = now.format ("%Y.%m.%d");
+        pgn_game.time = now.format ("%H:%M:%S");
+        pgn_game.utcdate = now_UTC.format ("%Y.%m.%d");
+        pgn_game.utctime = now_UTC.format ("%H:%M:%S");
+        if (duration > 0)
+            pgn_game.time_control = "%d".printf (duration);
+
+        var history_model = new Gtk.ListStore (2, typeof (string), typeof (int));
+
+        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");
+        }
+        var game = new ChessGame (fen, moves);
+        (game as Object).set_data<PGNGame> ("pgn-game", pgn_game);
+        (game as Object).set_data<Gtk.TreeIter*> ("channel-iter", &iter);
+
+        if (pgn_game.time_control != null)
+        {
+            var controls = pgn_game.time_control.split (":");
+            foreach (var control in controls)
+            {
+                /* We only support simple timeouts */
+                var timeout_duration = int.parse (control);
+                if (timeout_duration > 0)
+                    game.clock = new ChessClock (timeout_duration, timeout_duration);
+            }
+        }
+
+        game.moved.connect (game_move_cb);
+        game.ended.connect (game_end_cb);
+        if (game.clock != null)
+            game.clock.tick.connect (game_clock_tick_cb);
+
+        ChessScene scene = new ChessScene ();
+        scene.changed.connect (scene_changed_cb);
+        scene.choose_promotion_type.connect (show_promotion_type_selector);
+        scene.game = game;
+
+        Gtk.InfoBar info_bar;
+
+        info_bar = new Gtk.InfoBar ();
+        var content_area = (Gtk.Container) info_bar.get_content_area ();
+        (view_box as Gtk.Box).pack_start (info_bar, false, true, 0);
+        var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 6);
+        vbox.show ();
+        content_area.add (vbox);
+        var 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;
+        var info_label = new Gtk.Label ("");
+        info_label.show ();
+        vbox.pack_start (info_label, true, true, 0);
+
+        info_bar.hide ();
+
+        (game as Object).set_data<Gtk.InfoBar> ("info-bar", info_bar);
+        (game as Object).set_data<Gtk.Label> ("info-title-label", info_title_label);
+        (game as Object).set_data<Gtk.Label> ("info-label", info_label);
+
+        ChessView chess_view;
+        if (settings_common.get_boolean ("show-3d"))
+            chess_view = new ChessView3D ();
+        else
+            chess_view = new ChessView2D ();
+        chess_view.set_size_request (300, 300);
+        /* This connects scene.changed() to a handler that draws view everytime scene changes */
+        chess_view.scene = scene;
+
+        (game as Object).set_data<ChessView> ("chess-view", chess_view);
+
+        bool 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 ();
+
+        channels.insert_with_values (null, -1,
+            ChannelsColumn.OUR_COLOUR_WHITE, our_color_white,
+            ChannelsColumn.PGN_GAME, pgn_game,
+            ChannelsColumn.HISTORY_MODEL, history_model,
+            ChannelsColumn.CHESS_GAME, game,
+            ChannelsColumn.CHESS_SCENE, scene,
+            ChannelsColumn.INFO_BAR, info_bar,
+            ChannelsColumn.INFO_BAR_VISIBILITY, info_bar.visible,
+            ChannelsColumn.GAME_NEEDS_SAVING, game_needs_saving);
+    }
+
+    private void register_objects (DBusConnection connection,
+        TelepathyGLib.DBusTubeChannel tube,
+        Gtk.TreeIter channel_iter)
+    {
+      //  display_game ();
+    }
+
+    private void offer_tube (TelepathyGLib.DBusTubeChannel tube,
+        Gtk.TreeIter channel_iter)
+    {
+        DBusConnection? connection = null;
+        bool play_as_white = settings_common.get_boolean ("play-as-white");
+        Value colour_val = play_as_white;
+        int32 duration = settings_common.get_int ("duration");
+        Value duration_val = duration;
+
+        HashTable<string, Value?>? tube_params = new HashTable<string, Value?> (str_hash, str_equal);
+        tube_params.insert ("offerer-white", colour_val);
+        tube_params.insert ("duration", duration_val);
+
+        debug ("Offering tube with offerer-white:%s and duration:%ld",
+        colour_val.get_boolean () ? "true" : "false", duration_val.get_int ());
+//        debug ("Parameters passed: %s", );
+        tube.offer_async.begin ((HashTable<void*, void*>?)tube_params,
+            (obj, res)=>{
+                try
+                {
+                    connection = tube.offer_async.end (res);
+                }
+                catch (Error e)
+                {
+                    debug ("Failed to offer tube to %s: %s",
+                        (tube as TelepathyGLib.Channel).target_contact.identifier,
+                        e.message);
+                  //  display_channel_error (channel_iter, e, "Failed to offer chess game channel to %s", (tube as TelepathyGLib.Channel).target_contact.identifier);
+                    debug ("Closing channel.");
+                    (tube as TelepathyGLib.Channel).close_async.begin ((obj, res)=>
+                        {
+                            try
+                            {
+                                (tube as TelepathyGLib.Channel).close_async.end (res);
+                            }
+                            catch (Error e)
+                            {
+                                debug ("Couldn't close the channel: %s", e.message);
+                            }
+                        });
+
+
+                    /* Nothing to do */
+                    return;
+                }
+
+                assert (connection != null);
+
+                debug ("Tube opened.");
+                connection.notify["closed"].connect (
+                    (s, p)=>{
+                        debug ("Connection to %s closed unexpectedly",
+                            (tube as
+                            TelepathyGLib.Channel).target_contact.identifier);
+                    }
+                );
+                create_and_add_game (tube, channel_iter);
+                register_objects (connection, tube, channel_iter);
+              }
+            );
+    }
+
+    private void accept_tube (TelepathyGLib.DBusTubeChannel tube,
+        Gtk.TreeIter channel_iter)
+    {
+        DBusConnection? connection = null;
+
+        debug ("Accepting tube");
+        tube.accept_async.begin (
+            (obj, res)=>{
+                try
+                {
+                    connection = tube.accept_async.end (res);
+                }
+                catch (Error e)
+                {
+                    debug ("Failed to accept tube from %s: %s",
+                        (tube as TelepathyGLib.Channel).target_contact.identifier,
+                        e.message);
+//                    display_error (channel_iter, e, "Failed to offer chess game channel to %s", (tube as TelepathyGLib.Channel).target_contact.identifer);
+                    debug ("Closing channel.");
+
+                    (tube as TelepathyGLib.Channel).close_async.begin ((obj, res)=>
+                        {
+                            try
+                            {
+                                (tube as TelepathyGLib.Channel).close_async.end (res);
+                            }
+                            catch (Error e)
+                            {
+                                debug ("Couldn't close the channel: %s", e.message);
+                            }
+                        });
+
+                    /* Nothing to do */
+                    return;
+                }
+
+                assert (connection != null);
+
+                debug ("Tube opened.");
+                connection.notify["closed"].connect (
+                    (s, p)=>{
+                        debug ("Connection to %s closed unexpectedly",
+                            (tube as
+                            TelepathyGLib.Channel).target_contact.identifier);
+                    }
+                );
+                create_and_add_game (tube, channel_iter);
+              }
+            );
+
+    }
+
+    private void tube_invalidated_cb (TelepathyGLib.Proxy tube_channel,
+        uint domain,
+        int code,
+        string message)
+    {
+        debug ("Tube has been invalidated: %s", message);
+
+        channels  foreach ((model, path, iter)=>
+            {
+                TelepathyGLib.Channel stored_tube;
+                uint64 user_action_time;
+                channels  get (iter, ChannelsColumn.CHANNEL, out stored_tube,
+                    ChannelsColumn.USER_ACTION_TIME, out user_action_time);
+
+                if (stored_tube == tube_channel)
+                {
+                    debug ("Removing chess dbus-tube channel initiated at timeframe %llu with remote player %s",
+                        user_action_time, stored_tube.target_contact.identifier);
+                    channels.remove (iter);
+                    return true;
+                }
+
+                return false;
+            }
+          );
+    }
+
+    public void handle_channels (TelepathyGLib.SimpleHandler handler,
+        TelepathyGLib.Account account,
+        TelepathyGLib.Connection conn,
+        List<TelepathyGLib.Channel> channel_bundle,
+        List<TelepathyGLib.ChannelRequest> requests,
+        int64 action_time,
+        TelepathyGLib.HandleChannelsContext context)
+    {
+        Error error = new TelepathyGLib.Error.NOT_AVAILABLE (
+            "No channel to be handled");
+
+        debug ("Handling channels");
+
+        foreach (TelepathyGLib.Channel tube_channel in channel_bundle)
+        {
+          Gtk.TreeIter iter;
+
+          if (! (tube_channel is TelepathyGLib.DBusTubeChannel))
+            continue;
+          if ((tube_channel as TelepathyGLib.DBusTubeChannel).service_name !=
+              TelepathyGLib.CLIENT_BUS_NAME_BASE + "Games.Glchess")
+            continue;
+
+          /* Add channel to list of currently handled channels */
+          channels.insert_with_values (out iter, -1,
+              ChannelsColumn.USER_ACTION_TIME, action_time,
+              ChannelsColumn.TARGET_CONTACT, tube_channel.target_contact,
+              ChannelsColumn.CHANNEL, tube_channel);
+
+          if (tube_channel.requested)
+          {
+              /* We created this channel. Make a tube offer and wait for
+               * approval */
+              offer_tube (tube_channel as TelepathyGLib.DBusTubeChannel, iter);
+             // display_waiting_for_approval (iter);
+          }
+          else
+          {
+              /* This is an incoming channel request */
+              accept_tube (tube_channel as TelepathyGLib.DBusTubeChannel, iter);
+          }
+
+          (tube_channel as TelepathyGLib.Proxy).invalidated.connect (
+              tube_invalidated_cb);
+
+          context.accept ();
+          /* Only one of accept() or fail() can be called on context */
+          return;
+        }
+
+        debug ("Rejecting channels");
+        context.fail (error);
+
+        return;
     }
 
     public override void activate ()
     {
+      if (!is_activated)
+      {
+          /* Create and register our telepathy client handler as a one-off */
+
+          tp_handler =  new TelepathyGLib.SimpleHandler.with_am (
+              TelepathyGLib.AccountManager.dup (),
+              true,     /* Bypass approval */
+              true,    /* Requests */
+              "Games.Glchess",
+              false,    /* Uniquify name */
+              handle_channels);
+
+          var filter = new HashTable<string, Value?> (str_hash, str_equal);
+          filter.insert (
+              TelepathyGLib.PROP_CHANNEL_CHANNEL_TYPE,
+              TelepathyGLib.IFACE_CHANNEL_TYPE_DBUS_TUBE);
+          filter.insert (
+              TelepathyGLib.PROP_CHANNEL_TARGET_HANDLE_TYPE,
+              1);
+          filter.insert (
+              TelepathyGLib.PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME,
+                TelepathyGLib.CLIENT_BUS_NAME_BASE + "Games.Glchess");
+
+          (tp_handler as TelepathyGLib.BaseClient).add_handler_filter (filter);
+
+          try {
+              (tp_handler as TelepathyGLib.BaseClient).register ();
+          }
+          catch (Error e) {
+              debug ("Failed to register handler: %s", e.message);
+          }
+
+          debug ("Waiting for tube offer");
+
+          is_activated = true;
+      }
+
       /* Handler window has been created by constructor already. Show it. */
       handler_window.show_all ();
       handler_window.present ();
@@ -248,3 +801,4 @@ class GlChessChannelHandler
         return result;
     }
 }
+



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