[longomatch] Add support for players subsitutions



commit aff71e30df100c131145e6bac2b89ac458f2f2c8
Author: Andoni Morales Alastruey <ylatuya gmail com>
Date:   Mon Sep 22 21:25:07 2014 +0200

    Add support for players subsitutions

 LongoMatch.Core/Common/Constants.cs                |    1 +
 LongoMatch.Core/Common/Enums.cs                    |    9 +
 LongoMatch.Core/Common/EventsBroker.cs             |   10 +
 LongoMatch.Core/Common/Exceptions.cs               |    6 +
 LongoMatch.Core/Handlers/Handlers.cs               |    2 +-
 LongoMatch.Core/Store/EventType.cs                 |   23 ++
 LongoMatch.Core/Store/Player.cs                    |    2 +-
 .../Store/Playlists/PlaylistPlayElement.cs         |    2 +-
 LongoMatch.Core/Store/Project.cs                   |  125 +++++++++++-
 LongoMatch.Core/Store/TimeNode.cs                  |    6 +-
 LongoMatch.Core/Store/TimelineEvent.cs             |  167 ++++++++++++----
 LongoMatch.Core/StyleConf.cs                       |    3 +
 LongoMatch.Drawing/CanvasObjects/BenchObject.cs    |    1 +
 LongoMatch.Drawing/CanvasObjects/ButtonObject.cs   |   35 ++++
 LongoMatch.Drawing/CanvasObjects/FieldObject.cs    |   40 +++--
 .../CanvasObjects/PlayersTaggerObject.cs           |  194 ++++++++++++++++--
 LongoMatch.Drawing/CanvasObjects/TimeNodeObject.cs |    5 +
 LongoMatch.Drawing/Widgets/TeamTagger.cs           |   35 +++-
 LongoMatch.GUI.Multimedia/Gui/PlayerBin.cs         |   12 +-
 LongoMatch.GUI.Multimedia/gtk-gui/objects.xml      |    2 +-
 LongoMatch.GUI/Gui/Component/CodingWidget.cs       |   11 +
 LongoMatch.GUI/Gui/Component/TeamTemplateEditor.cs |   11 +-
 LongoMatch.GUI/Gui/Dialog/SubstitutionsEditor.cs   |  216 ++++++++++++++++++++
 LongoMatch.GUI/Gui/GUIToolkit.cs                   |   20 ++-
 LongoMatch.GUI/Gui/Menu/PlaysMenu.cs               |   40 +++--
 LongoMatch.GUI/Gui/Panel/NewProjectPanel.cs        |   31 +++-
 LongoMatch.GUI/Gui/TreeView/PlaysTreeView.cs       |   32 +++-
 LongoMatch.GUI/LongoMatch.GUI.csproj               |    2 +
 LongoMatch.GUI/Makefile.am                         |    2 +
 .../LongoMatch.Gui.Dialog.SubstitutionsEditor.cs   |  140 +++++++++++++
 LongoMatch.GUI/gtk-gui/gui.stetic                  |  157 ++++++++++++++
 LongoMatch.GUI/gtk-gui/objects.xml                 |  100 +++++-----
 LongoMatch.Migration/LongoMatch.Migration.csproj   |    3 -
 LongoMatch.Services/Services/EventsManager.cs      |   17 ++
 LongoMatch.Services/Services/ProjectsManager.cs    |    1 +
 po/POTFILES.in                                     |    1 +
 36 files changed, 1293 insertions(+), 171 deletions(-)
---
diff --git a/LongoMatch.Core/Common/Constants.cs b/LongoMatch.Core/Common/Constants.cs
index 2ae737f..4bc0db1 100644
--- a/LongoMatch.Core/Common/Constants.cs
+++ b/LongoMatch.Core/Common/Constants.cs
@@ -126,5 +126,6 @@ Xavier Queralt Mateu (ca)";
                
                public static Guid PenaltyCardID = new Guid ("da4df338-3392-11e4-be8d-0811963e3880");
                public static Guid ScoreID = new Guid ("dc4df338-3392-11e4-be8d-0811963e3880");
+               public static Guid SubsID = new Guid ("db4df338-3392-11e4-be8d-0811963e3880");
        }
 }
diff --git a/LongoMatch.Core/Common/Enums.cs b/LongoMatch.Core/Common/Enums.cs
index f4d5dd6..9c36cf4 100644
--- a/LongoMatch.Core/Common/Enums.cs
+++ b/LongoMatch.Core/Common/Enums.cs
@@ -258,5 +258,14 @@ namespace LongoMatch.Core.Common
                Sorted = 8,
                Focused = 16
        }
+       
+       public enum SubstitutionReason {
+               PlayersSubstitution,
+               PositionChange,
+               BenchPositionChange,
+               Injury,
+               TemporalExclusion,
+               Exclusion,
+       }
 }
 
diff --git a/LongoMatch.Core/Common/EventsBroker.cs b/LongoMatch.Core/Common/EventsBroker.cs
index 0a1eb18..2644d90 100644
--- a/LongoMatch.Core/Common/EventsBroker.cs
+++ b/LongoMatch.Core/Common/EventsBroker.cs
@@ -22,6 +22,7 @@ using LongoMatch.Core.Store;
 using LongoMatch.Core.Store.Playlists;
 using LongoMatch.Core.Interfaces;
 using LongoMatch.Core.Interfaces.GUI;
+using LongoMatch.Core.Store.Templates;
 
 namespace LongoMatch.Core.Common
 {
@@ -39,6 +40,7 @@ namespace LongoMatch.Core.Common
                public event TagEventHandler TagEventEvent;
                public event DuplicateEventsHandler DuplicateEventsEvent;
                public event TeamsTagsChangedHandler TeamTagsChanged;
+               public event PlayersSubstitutionHandler PlayerSubstitutionEvent;
                
                /* Playlist */
                public event RenderPlaylistHandler RenderPlaylist;
@@ -377,6 +379,14 @@ namespace LongoMatch.Core.Common
                
                public void EmitPressButton (DashboardButton button) {
                }
+               
+               public void EmitSubstitutionEvent (TeamTemplate team, Player p1, Player p2,
+                                                  SubstitutionReason reason, Time time)
+               {
+                       if (PlayerSubstitutionEvent != null) {
+                               PlayerSubstitutionEvent (team, p1, p2, reason, time);
+                       }
+               }
        }
 }
 
diff --git a/LongoMatch.Core/Common/Exceptions.cs b/LongoMatch.Core/Common/Exceptions.cs
index 0f5abcf..a07707d 100644
--- a/LongoMatch.Core/Common/Exceptions.cs
+++ b/LongoMatch.Core/Common/Exceptions.cs
@@ -37,5 +37,11 @@ namespace LongoMatch.Core.Common
                {
                }
        }
+       
+       public class SubstitutionException: Exception {
+               public SubstitutionException (string error): base (error)
+               {
+               }
+       }
 }
 
diff --git a/LongoMatch.Core/Handlers/Handlers.cs b/LongoMatch.Core/Handlers/Handlers.cs
index 18f46ab..a3e92ee 100644
--- a/LongoMatch.Core/Handlers/Handlers.cs
+++ b/LongoMatch.Core/Handlers/Handlers.cs
@@ -105,7 +105,7 @@ namespace LongoMatch.Core.Handlers
        public delegate void PlayerPropertiesHandler (Player player);
        public delegate void PlayersPropertiesHandler (List<Player> players);
        /* Players selection */
-       public delegate void PlayersSubstitutionHandler (Player p1,Player p2,TeamTemplate team);
+       public delegate void PlayersSubstitutionHandler (TeamTemplate team, Player p1, Player p2, 
SubstitutionReason reason, Time time);
        public delegate void PlayersSelectionChangedHandler (List<Player> players);
        /* A list of projects have been selected */
        public delegate void ProjectsSelectedHandler (List<ProjectDescription> projects);
diff --git a/LongoMatch.Core/Store/EventType.cs b/LongoMatch.Core/Store/EventType.cs
index 9ad9a33..d937e23 100644
--- a/LongoMatch.Core/Store/EventType.cs
+++ b/LongoMatch.Core/Store/EventType.cs
@@ -166,5 +166,28 @@ namespace LongoMatch.Core.Store
                        return ID.GetHashCode ();
                }
        }
+
+       [Serializable]
+       public class SubstitutionEventType: EventType
+       {
+               public SubstitutionEventType ()
+               {
+                       ID = Constants.SubsID;
+                       Name = Catalog.GetString ("Substitution");
+               }
+
+               public override bool Equals (object obj)
+               {
+                       SubstitutionEventType sc = obj as SubstitutionEventType;
+                       if (sc == null)
+                               return false;
+                       return sc.ID == ID;
+               }
+
+               public override int GetHashCode ()
+               {
+                       return ID.GetHashCode ();
+               }
+       }
 }
 
diff --git a/LongoMatch.Core/Store/Player.cs b/LongoMatch.Core/Store/Player.cs
index 5f150cf..99c4271 100644
--- a/LongoMatch.Core/Store/Player.cs
+++ b/LongoMatch.Core/Store/Player.cs
@@ -148,7 +148,7 @@ namespace LongoMatch.Core.Store
                        } else {
                                displayName = Name + " " + LastName;
                        }
-                       return String.Format("{0} - {1}", Number, displayName);
+                       return String.Format("{0}-{1}", Number, displayName);
                }
 
                #endregion
diff --git a/LongoMatch.Core/Store/Playlists/PlaylistPlayElement.cs 
b/LongoMatch.Core/Store/Playlists/PlaylistPlayElement.cs
index 66adaae..3ab650b 100644
--- a/LongoMatch.Core/Store/Playlists/PlaylistPlayElement.cs
+++ b/LongoMatch.Core/Store/Playlists/PlaylistPlayElement.cs
@@ -61,7 +61,7 @@ namespace LongoMatch.Core.Store.Playlists
                [JsonIgnore]
                public string Description {
                        get {
-                               return Title;
+                               return Title + " " + Play.Start.ToSecondsString () + " " + 
Play.Stop.ToSecondsString ();
                        }
                }
 
diff --git a/LongoMatch.Core/Store/Project.cs b/LongoMatch.Core/Store/Project.cs
index ec173e0..0b8390f 100644
--- a/LongoMatch.Core/Store/Project.cs
+++ b/LongoMatch.Core/Store/Project.cs
@@ -49,6 +49,7 @@ namespace LongoMatch.Core.Store
        public class Project : IComparable, IIDObject
        {
                ProjectDescription description;
+               SubstitutionEventType subsType;
 
                #region Constructors
                public Project() {
@@ -160,6 +161,19 @@ namespace LongoMatch.Core.Store
                        }
                }
                
+               [JsonIgnore]
+               public SubstitutionEventType SubstitutionsEventType {
+                       get {
+                               if (subsType == null) {
+                                       subsType = EventTypes.OfType<SubstitutionEventType> ().FirstOrDefault 
();
+                                       if (subsType == null) {
+                                               subsType = new SubstitutionEventType ();
+                                               subsType.SortMethod = SortMethodType.SortByStartTime;
+                                       }
+                               }
+                               return subsType;
+                       }
+               }
                #endregion
 
                #region Public Methods
@@ -262,10 +276,117 @@ namespace LongoMatch.Core.Store
 
                public void UpdateEventTypes ()
                {
-                       IEnumerable<EventType> types = Dashboard.List.OfType<EventButton>().Select(b => 
b.EventType);
+                       IEnumerable<EventType> types = Dashboard.List.OfType<EventButton> ().Select (b => 
b.EventType);
                        EventTypes.AddRange (types.Except (EventTypes));
-                       types = Timeline.Select (t => t.EventType).Distinct().Except (EventTypes);
+                       types = Timeline.Select (t => t.EventType).Distinct ().Except (EventTypes);
                        EventTypes.AddRange (types.Except (EventTypes));
+                       if (!EventTypes.Contains (SubstitutionsEventType)) {
+                               EventTypes.Add (SubstitutionsEventType);
+                       }
+               }
+
+               public SubstitutionEvent SubsitutePlayer (TeamTemplate template, Player playerIn, Player 
playerOut,
+                                                         SubstitutionReason reason, Time subsTime)
+               {
+                       Team team;
+                       LineupEvent lineup;
+                       SubstitutionEvent se;
+                       
+                       if (template == LocalTeamTemplate) {
+                               team = Team.LOCAL;
+                       } else {
+                               team = Team.VISITOR;
+                       }
+                       lineup = Timeline.OfType<LineupEvent> ().FirstOrDefault ();
+                       if (lineup == null) {
+                               throw new SubstitutionException (Catalog.GetString ("No lineup events 
found"));
+                       }
+                       if (subsTime < lineup.EventTime) {
+                               throw new SubstitutionException (Catalog.GetString ("A substitution can't 
happen before the lineup event"));
+                       }
+                       se = new SubstitutionEvent ();
+                       se.EventType = SubstitutionsEventType;
+                       se.In = playerIn;
+                       se.Out = playerOut;
+                       se.Reason = reason;
+                       se.EventTime = subsTime;
+                       se.Team = team;
+                       Timeline.Add (se);
+                       return se;
+               }
+
+               public void CurrentLineup (Time currentTime,
+                                        out List<Player> homeFieldPlayers,
+                                        out List<Player> homeBenchPlayers,
+                                        out List<Player> awayFieldPlayers,
+                                        out List<Player> awayBenchPlayers)
+               {
+                       TeamTemplate homeTeam, awayTeam;
+                       LineupEvent lineup;
+                       List<Player> homeTeamPlayers, awayTeamPlayers;
+
+                       lineup = Timeline.OfType <LineupEvent> ().FirstOrDefault ();
+                       if (lineup == null) {
+                               lineup = CreateLineupEvent ();
+                       }
+
+                       homeTeamPlayers = lineup.HomeStartingPlayers.Concat (lineup.HomeBenchPlayers).ToList 
();
+                       awayTeamPlayers = lineup.AwayStartingPlayers.Concat (lineup.AwayBenchPlayers).ToList 
();
+
+                       foreach (SubstitutionEvent ev in Timeline.OfType<SubstitutionEvent> ().
+                                Where (e => e.EventTime <= currentTime)) {
+                               if (ev.In != null && ev.Out != null) {
+                                       if (ev.Team == Team.LOCAL) {
+                                               homeTeamPlayers.Swap (ev.In, ev.Out);
+                                       } else {
+                                               awayTeamPlayers.Swap (ev.In, ev.Out);
+                                       }
+                               }
+                       }
+
+                       homeTeam = new TeamTemplate {
+                               Formation = LocalTeamTemplate.Formation,
+                               List = homeTeamPlayers
+                       };
+                       awayTeam = new TeamTemplate {
+                               Formation = VisitorTeamTemplate.Formation,
+                               List = awayTeamPlayers
+                       };
+                       
+                       homeFieldPlayers = homeTeam.StartingPlayersList;
+                       homeBenchPlayers = homeTeam.BenchPlayersList;
+                       awayFieldPlayers = awayTeam.StartingPlayersList;
+                       awayBenchPlayers = awayTeam.BenchPlayersList;
+               }
+
+               public bool LineupChanged (Time start, Time stop)
+               {
+                       return Timeline.OfType<SubstitutionEvent> ().
+                               Count (s => s.EventTime > start && s.EventTime <= stop) > 0;
+               }
+               
+               public LineupEvent CreateLineupEvent ()
+               {
+                       Time startTime;
+                       LineupEvent lineup;
+
+                       if (Periods.Count == 0) {
+                               startTime = new Time (0);
+                       } else {
+                               startTime = Periods[0].PeriodNode.Start;
+                       }
+
+                       lineup = new LineupEvent {
+                               Name = Catalog.GetString ("Lineup ") + LocalTeamTemplate.TeamName,
+                               EventType = SubstitutionsEventType,
+                               HomeStartingPlayers = LocalTeamTemplate.StartingPlayersList,
+                               HomeBenchPlayers = LocalTeamTemplate.BenchPlayersList,
+                               AwayStartingPlayers = VisitorTeamTemplate.StartingPlayersList,
+                               AwayBenchPlayers = VisitorTeamTemplate.BenchPlayersList, 
+                               EventTime = startTime};
+                       Timeline.Add (lineup);
+
+                       return lineup;
                }
 
                public List<TimelineEvent> EventsByType (EventType evType) {
diff --git a/LongoMatch.Core/Store/TimeNode.cs b/LongoMatch.Core/Store/TimeNode.cs
index 759dae0..c288984 100644
--- a/LongoMatch.Core/Store/TimeNode.cs
+++ b/LongoMatch.Core/Store/TimeNode.cs
@@ -45,7 +45,7 @@ namespace LongoMatch.Core.Store
                /// <summary>
                /// A short description of the time node
                /// </summary>
-               public string Name {
+               public virtual string Name {
                        get;
                        set;
                }
@@ -53,7 +53,7 @@ namespace LongoMatch.Core.Store
                /// <summary>
                /// Start Time
                /// </summary>
-               public Time Start {
+               public virtual Time Start {
                        get {
                                return start;
                        }
@@ -68,7 +68,7 @@ namespace LongoMatch.Core.Store
                /// <summary>
                /// Stop time
                /// </summary>
-               public Time Stop {
+               public virtual Time Stop {
                        get {
                                return stop;
                        }
diff --git a/LongoMatch.Core/Store/TimelineEvent.cs b/LongoMatch.Core/Store/TimelineEvent.cs
index 5420138..e1476cb 100644
--- a/LongoMatch.Core/Store/TimelineEvent.cs
+++ b/LongoMatch.Core/Store/TimelineEvent.cs
@@ -17,7 +17,6 @@
 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 //
 //
-
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -28,7 +27,6 @@ using Newtonsoft.Json;
 
 namespace LongoMatch.Core.Store
 {
-
        /// <summary>
        /// Represents a Play in the game.
        /// </summary>
@@ -38,22 +36,21 @@ namespace LongoMatch.Core.Store
        {
 
                #region Constructors
-               public TimelineEvent() {
-                       Drawings = new List<FrameDrawing>();
+               public TimelineEvent ()
+               {
+                       Drawings = new List<FrameDrawing> ();
                        Players = new List<Player> ();
-                       Tags = new List<Tag>();
+                       Tags = new List<Tag> ();
                        Rate = 1.0f;
                        ID = Guid.NewGuid ();
                }
                #endregion
-
                #region Properties
-
                public Guid ID {
                        get;
                        set;
                }
-               
+
                /// <summary>
                /// Category in which this play is tagged
                /// </summary>
@@ -86,20 +83,20 @@ namespace LongoMatch.Core.Store
                        get;
                        set;
                }
-
                /* FIXME: Keep this until we support multiple drawings */
                [JsonIgnore]
                public FrameDrawing KeyFrameDrawing {
                        get {
-                               if(Drawings.Count > 0)
-                                       return Drawings.First();
+                               if (Drawings.Count > 0)
+                                       return Drawings.First ();
                                else
                                        return null;
-                       } set {
+                       }
+                       set {
                                if (Drawings.Count == 0)
                                        Drawings.Add (value);
                                else
-                                       Drawings[0] = value;
+                                       Drawings [0] = value;
                        }
                }
 
@@ -117,7 +114,7 @@ namespace LongoMatch.Core.Store
                        get;
                        set;
                }
-               
+
                public Team Team {
                        get;
                        set;
@@ -127,7 +124,7 @@ namespace LongoMatch.Core.Store
                        get;
                        set;
                }
-               
+
                public Coordinates FieldPosition {
                        get;
                        set;
@@ -137,19 +134,19 @@ namespace LongoMatch.Core.Store
                        get;
                        set;
                }
-               
+
                public Coordinates GoalPosition {
                        get;
                        set;
                }
-               
+
                [JsonIgnore]
                public virtual string Description {
                        get {
                                return 
-                                       Name + "\n" +
-                                               TagsDescription () + "\n" +
-                                               Start.ToMSecondsString() + " - " + Stop.ToMSecondsString();
+                                       (Name + "\n" +
+                                       TagsDescription () + "\n" +
+                                       TimesDesription ());
                        }
                }
 
@@ -160,14 +157,25 @@ namespace LongoMatch.Core.Store
                        }
                }
                #endregion
-
                #region Public methods
-               
-               public string TagsDescription () {
+               public string TagsDescription ()
+               {
                        return String.Join ("-", Tags.Select (t => t.Value));
                }
 
-               public void AddDefaultPositions () {
+               public string TimesDesription ()
+               {
+                       if (Start != null && Stop != null) {
+                               return Start.ToMSecondsString () + " - " + Stop.ToMSecondsString ();
+                       } else if (EventType != null) {
+                               return EventTime.ToMSecondsString ();
+                       } else {
+                               return "";
+                       }
+               }
+
+               public void AddDefaultPositions ()
+               {
                        if (EventType.TagFieldPosition) {
                                if (FieldPosition == null) {
                                        FieldPosition = new Coordinates ();
@@ -194,8 +202,9 @@ namespace LongoMatch.Core.Store
                                }
                        }
                }
-               
-               public Coordinates CoordinatesInFieldPosition (FieldPositionType pos) {
+
+               public Coordinates CoordinatesInFieldPosition (FieldPositionType pos)
+               {
                        switch (pos) {
                        case FieldPositionType.Field:
                                return FieldPosition;
@@ -206,8 +215,9 @@ namespace LongoMatch.Core.Store
                        }
                        return null;
                }
-               
-               public void UpdateCoordinates (FieldPositionType pos, List<Point> points) {
+
+               public void UpdateCoordinates (FieldPositionType pos, List<Point> points)
+               {
                        Coordinates co = new Coordinates ();
                        co.Points = points;
                        
@@ -223,14 +233,14 @@ namespace LongoMatch.Core.Store
                                break;
                        }
                }
-               
-               public override string ToString()
+
+               public override string ToString ()
                {
                        return Description;
                }
                #endregion
        }
-       
+
        [Serializable]
        public class PenaltyCardEvent: TimelineEvent
        {
@@ -246,7 +256,7 @@ namespace LongoMatch.Core.Store
                        }
                }
        }
-       
+
        [Serializable]
        public class ScoreEvent: TimelineEvent
        {
@@ -261,15 +271,102 @@ namespace LongoMatch.Core.Store
                                return Score != null ? Score.Color : EventType.Color;
                        }
                }
-               
+
                [JsonIgnore]
                public override string Description {
                        get {
                                return String.Format ("{0} - {1}\n{2}\n{3}\n", Score.Points, Name,
-                                                     TagsDescription (), Start.ToMSecondsString(),
-                                                     Stop.ToMSecondsString());
+                                                     TagsDescription (), Start.ToMSecondsString (),
+                                                     Stop.ToMSecondsString ());
                        }
                }
        }
 
+       [Serializable]
+       public class StatEvent: TimelineEvent
+       {
+               public override Time Start {
+                       get {
+                               return EventTime;
+                       }
+                       set {
+                               EventTime = value;
+                       }
+               }
+               
+               public override Time Stop {
+                       get {
+                               return EventTime;
+                       }
+                       set {
+                               EventTime = value;
+                       }
+               }
+       }
+       
+       [Serializable]
+       public class SubstitutionEvent: StatEvent
+       {
+               public Player In {
+                       get;
+                       set;
+               }
+
+               public Player Out {
+                       get;
+                       set;
+               }
+               
+               public override string Name {
+                       get {
+                               return Reason.ToString();
+                       }
+                       set {
+                       }
+               }
+
+               public SubstitutionReason Reason {
+                       get;
+                       set;
+               }
+
+               [JsonIgnore]
+               public override string Description {
+                       get {
+                               string desc = "";
+                               if (In != null && Out != null) {
+                                       desc = String.Format ("{0} ⟲ {1}", In, Out);
+                               } else if (In != null) {
+                                       desc = "↷ " + In;
+                               } else if (Out != null) {
+                                       desc = "↶ " + Out;
+                               }
+                               return desc += "\n" + EventTime.ToMSecondsString ();
+                       }
+               }
+       }
+
+       [Serializable]
+       public class LineupEvent: StatEvent
+       {
+               public List<Player> HomeStartingPlayers {
+                       get;
+                       set;
+               }
+               
+               public List<Player> HomeBenchPlayers {
+                       get;
+                       set;
+               }
+               
+               public List<Player> AwayStartingPlayers {
+                       get;
+                       set;
+               }
+               
+               public List<Player> AwayBenchPlayers {
+                       get;
+                       set;
+               }
+       }
 }
diff --git a/LongoMatch.Core/StyleConf.cs b/LongoMatch.Core/StyleConf.cs
index 68714f3..b8b41a5 100644
--- a/LongoMatch.Core/StyleConf.cs
+++ b/LongoMatch.Core/StyleConf.cs
@@ -87,6 +87,9 @@ namespace LongoMatch.Core.Common
                public const int PlayerNumberOffset  = 17;
                public const int PlayerArrowOffset = 14; 
                public const int PlayerArrowSize = 20; 
+               
+               public const string SubsLock = "hicolor/scalable/actions/longomatch-player-swap-lock.svg";
+               public const string SubsUnlock = "hicolor/scalable/actions/longomatch-player-swap-unlock.svg";
 
                public const int NotebookTabIconSize = 18;
                public const int NotebookTabSize = NotebookTabIconSize + 14;
diff --git a/LongoMatch.Drawing/CanvasObjects/BenchObject.cs b/LongoMatch.Drawing/CanvasObjects/BenchObject.cs
index d8093db..8f69de2 100644
--- a/LongoMatch.Drawing/CanvasObjects/BenchObject.cs
+++ b/LongoMatch.Drawing/CanvasObjects/BenchObject.cs
@@ -101,6 +101,7 @@ namespace LongoMatch.Drawing.CanvasObjects
                        foreach (PlayerObject po in BenchPlayers) {
                                po.Playing = false;
                                po.SubstitutionMode = SubstitutionMode;
+                               po.Size = PlayersSize;
                                po.Draw (tk, area);
                        }
                        
diff --git a/LongoMatch.Drawing/CanvasObjects/ButtonObject.cs 
b/LongoMatch.Drawing/CanvasObjects/ButtonObject.cs
index bf3251d..3dacc69 100644
--- a/LongoMatch.Drawing/CanvasObjects/ButtonObject.cs
+++ b/LongoMatch.Drawing/CanvasObjects/ButtonObject.cs
@@ -24,6 +24,13 @@ namespace LongoMatch.Drawing.CanvasObjects
 {
        public class ButtonObject: CanvasButtonObject, IMovableObject
        {
+               const int BORDER_SIZE = 4;
+
+               public ButtonObject () {
+                       BackgroundColor = Config.Style.PaletteBackground;
+                       BorderColor = Config.Style.PaletteBackgroundDark;
+               }
+               
                public virtual Point Position {
                        get;
                        set;
@@ -39,6 +46,11 @@ namespace LongoMatch.Drawing.CanvasObjects
                        set;
                }
 
+               public string Text {
+                       get;
+                       set;
+               }
+
                public virtual Color BorderColor {
                        get;
                        set;
@@ -59,6 +71,16 @@ namespace LongoMatch.Drawing.CanvasObjects
                        }
                }
 
+               public Image BackgroundImage {
+                       get;
+                       set;
+               }
+
+               public Image BackgroundImageActive {
+                       get;
+                       set;
+               }
+
                public TagMode Mode {
                        get;
                        set;
@@ -146,7 +168,20 @@ namespace LongoMatch.Drawing.CanvasObjects
 
                public override void Draw (IDrawingToolkit tk, Area area)
                {
+                       Point pos = new Point (Position.X + BORDER_SIZE / 2, Position.Y + BORDER_SIZE / 2);
+                       
                        tk.Begin ();
+                       DrawButton (tk);
+                       DrawSelectionArea (tk);
+                       
+                       if (Active && BackgroundImageActive != null) {
+                               tk.DrawImage (pos, Width - BORDER_SIZE, Height - BORDER_SIZE, 
BackgroundImageActive, true);
+                       } else if (BackgroundImage != null) {
+                               tk.DrawImage (pos, Width - BORDER_SIZE, Height - BORDER_SIZE, 
BackgroundImage, true);
+                       }
+                       if (Text != null) {
+                               tk.DrawText (Position, Width, Height, Text);
+                       }
                        tk.End ();
                }
        }
diff --git a/LongoMatch.Drawing/CanvasObjects/FieldObject.cs b/LongoMatch.Drawing/CanvasObjects/FieldObject.cs
index b18ef3e..659e846 100644
--- a/LongoMatch.Drawing/CanvasObjects/FieldObject.cs
+++ b/LongoMatch.Drawing/CanvasObjects/FieldObject.cs
@@ -28,8 +28,6 @@ namespace LongoMatch.Drawing.CanvasObjects
        {
                int[] homeFormation;
                int[] awayFormation;
-               List<PlayerObject> homePlayingPlayers;
-               List<PlayerObject> awayPlayingPlayers;
                int playerSize;
                Image background;
 
@@ -58,6 +56,16 @@ namespace LongoMatch.Drawing.CanvasObjects
                        set;
                }
 
+               public List<PlayerObject> HomePlayingPlayers {
+                       get;
+                       set;
+               }
+
+               public List<PlayerObject> AwayPlayingPlayers {
+                       get;
+                       set;
+               }
+
                public void LoadTeams (Image backgroundImg, int[] homeF, int[] awayF,
                                       List<PlayerObject> homeT, List<PlayerObject> awayT,
                                       int size, int nteams)
@@ -65,8 +73,8 @@ namespace LongoMatch.Drawing.CanvasObjects
                        background = backgroundImg;
                        homeFormation = homeF;
                        awayFormation = awayF;
-                       homePlayingPlayers = homeT;
-                       awayPlayingPlayers = awayT;
+                       HomePlayingPlayers = homeT;
+                       AwayPlayingPlayers = awayT;
                        playerSize = size;
                        NTeams = nteams;
                        Update ();
@@ -75,10 +83,10 @@ namespace LongoMatch.Drawing.CanvasObjects
                public void Update ()
                {
                        if (homeFormation != null) {
-                               UpdateTeam (homePlayingPlayers, homeFormation, Team.LOCAL);
+                               UpdateTeam (HomePlayingPlayers, homeFormation, Team.LOCAL);
                        }
                        if (awayFormation != null) {
-                               UpdateTeam (awayPlayingPlayers, awayFormation, Team.VISITOR);
+                               UpdateTeam (AwayPlayingPlayers, awayFormation, Team.VISITOR);
                        }
                }
 
@@ -135,15 +143,19 @@ namespace LongoMatch.Drawing.CanvasObjects
                        if (background != null) {
                                tk.DrawImage (background);
                        }
-                       if (homePlayingPlayers != null) {
-                               foreach (PlayerObject po in homePlayingPlayers) {
+                       if (HomePlayingPlayers != null) {
+                               foreach (PlayerObject po in HomePlayingPlayers) {
                                        po.Playing = true;
+                                       po.SubstitutionMode = SubstitutionMode;
+                                       po.Size = playerSize;
                                        po.Draw (tk, area);
                                }
                        }
-                       if (awayPlayingPlayers != null) {
-                               foreach (PlayerObject po in awayPlayingPlayers) {
+                       if (AwayPlayingPlayers != null) {
+                               foreach (PlayerObject po in AwayPlayingPlayers) {
                                        po.Playing = true;
+                                       po.SubstitutionMode = SubstitutionMode;
+                                       po.Size = playerSize;
                                        po.Draw (tk, area);
                                }
                        }
@@ -156,15 +168,15 @@ namespace LongoMatch.Drawing.CanvasObjects
 
                        point = Utils.ToUserCoords (point, Position, 1, 1);
 
-                       if (homePlayingPlayers != null) {
-                               foreach (PlayerObject po in homePlayingPlayers) {
+                       if (HomePlayingPlayers != null) {
+                               foreach (PlayerObject po in HomePlayingPlayers) {
                                        selection = po.GetSelection (point, precision);
                                        if (selection != null)
                                                break;
                                }
                        }
-                       if (selection == null && awayPlayingPlayers != null) {
-                               foreach (PlayerObject po in awayPlayingPlayers) {
+                       if (selection == null && AwayPlayingPlayers != null) {
+                               foreach (PlayerObject po in AwayPlayingPlayers) {
                                        selection = po.GetSelection (point, precision);
                                        if (selection != null)
                                                break;
diff --git a/LongoMatch.Drawing/CanvasObjects/PlayersTaggerObject.cs 
b/LongoMatch.Drawing/CanvasObjects/PlayersTaggerObject.cs
index 1761776..26e5acf 100644
--- a/LongoMatch.Drawing/CanvasObjects/PlayersTaggerObject.cs
+++ b/LongoMatch.Drawing/CanvasObjects/PlayersTaggerObject.cs
@@ -38,30 +38,39 @@ namespace LongoMatch.Drawing.CanvasObjects
                 */
                public event PlayersSubstitutionHandler PlayersSubstitutionEvent;
                public event PlayersSelectionChangedHandler PlayersSelectionChangedEvent;
+               const int SUBSTITUTION_BUTTONS_HEIGHT = 40;
+               const int SUBSTITUTION_BUTTONS_WIDTH = 60;
+               ButtonObject subPlayers, subInjury;
                /* Cached surfaces reused by player objects */
                ISurface backgroundSurface, homeNumberSurface, awayNumberSurface, photoSurface;
                ISurface homeInSurface, homeOutSurface, awayInSurface, awayOutSurface;
                TeamTemplate homeTeam, awayTeam;
                Image background;
+               Dictionary<Player, PlayerObject> playerToPlayerObject;
                List<PlayerObject> homePlayingPlayers, awayPlayingPlayers;
                List<PlayerObject> homeBenchPlayers, awayBenchPlayers;
                List <PlayerObject> homePlayers, awayPlayers;
                BenchObject homeBench, awayBench;
                PlayerObject clickedPlayer, substitutionPlayer;
+               ButtonObject clickedButton;
                FieldObject field;
                int NTeams;
                Point offset;
                bool substitutionMode;
                double scaleX, scaleY;
+               Time lastTime, currentTime;
 
                public PlayersTaggerObject ()
                {
                        Position = new Point (0, 0);
                        homeBench = new BenchObject ();
                        awayBench = new BenchObject ();
+                       playerToPlayerObject = new Dictionary<Player, PlayerObject>();
                        field = new FieldObject ();
                        SelectedPlayers = new List<Player> ();
+                       lastTime = null;
                        LoadSurfaces ();
+                       LoadSubsButtons ();
                }
 
                protected override void Dispose (bool disposing)
@@ -78,9 +87,36 @@ namespace LongoMatch.Drawing.CanvasObjects
                        awayOutSurface.Dispose ();
                        homeInSurface.Dispose ();
                        awayInSurface.Dispose ();
+                       subPlayers.Dispose ();
+                       subInjury.Dispose ();
                        base.Dispose (disposing);
                }
 
+               public Time CurrentTime {
+                       get {
+                               return currentTime;
+                       }
+                       set {
+                               currentTime = value;
+                               if (lastTime == null) {
+                                       UpdateLineup ();
+                               } else if (currentTime != lastTime && Project != null) {
+                                       Time start, stop;
+                                       if (lastTime < currentTime) {
+                                               start = lastTime;
+                                               stop = currentTime;
+                                       } else {
+                                               start = currentTime;
+                                               stop = lastTime;
+                                       }
+                                       if (Project.LineupChanged (start, stop)) {
+                                               UpdateLineup ();
+                                       }
+                               }
+                               lastTime = currentTime;
+                       }
+               }
+               
                public Point Position {
                        get;
                        set;
@@ -106,6 +142,11 @@ namespace LongoMatch.Drawing.CanvasObjects
                        set;
                }
 
+               public Project Project {
+                       get;
+                       set;
+               }
+
                public bool SubstitutionMode {
                        get {
                                return substitutionMode;
@@ -115,6 +156,16 @@ namespace LongoMatch.Drawing.CanvasObjects
                                homeBench.SubstitutionMode = awayBench.SubstitutionMode = 
field.SubstitutionMode = value;
                        }
                }
+               
+               public bool ShowSubsitutionButtons {
+                       get;
+                       set;
+               }
+               
+               public bool ShowInjurySubsitutionButton {
+                       get;
+                       set;
+               }
 
                public List<Player> SelectedPlayers {
                        get;
@@ -196,14 +247,13 @@ namespace LongoMatch.Drawing.CanvasObjects
                public void LoadTeams (TeamTemplate homeTeam, TeamTemplate awayTeam, Image background)
                {
                        int[] homeF = null, awayF = null;
-                       int playerSize, benchPlayerSize, colSize, border, benchWidth, playersPerRow;
+                       int playerSize, colSize, border;
 
                        this.homeTeam = homeTeam;
                        this.awayTeam = awayTeam;
                        this.background = background;
                        NTeams = 0;
 
-
                        if (background != null) {
                                field.Height = background.Height;
                                field.Width = background.Width;
@@ -253,6 +303,23 @@ namespace LongoMatch.Drawing.CanvasObjects
                        Update ();
                }
 
+               void UpdateLineup ()
+               {
+                       List<Player> homeFieldL, awayFieldL, homeBenchL, awayBenchL;
+                       Project.CurrentLineup (currentTime, out homeFieldL, out homeBenchL,
+                                              out awayFieldL, out awayBenchL);
+                       homePlayingPlayers = homeFieldL.Select (p => playerToPlayerObject [p]).ToList ();
+                       homeBenchPlayers = homeBenchL.Select (p => playerToPlayerObject [p]).ToList ();
+                       awayPlayingPlayers = awayFieldL.Select (p => playerToPlayerObject [p]).ToList ();
+                       awayBenchPlayers = awayBenchL.Select (p => playerToPlayerObject [p]).ToList ();
+                       homeBench.BenchPlayers = homeBenchPlayers;
+                       awayBench.BenchPlayers = awayBenchPlayers;
+                       field.HomePlayingPlayers = homePlayingPlayers;
+                       field.AwayPlayingPlayers = awayPlayingPlayers;
+                       Update ();
+                       EmitRedrawEvent (this, new Area (Position, Width, Height));
+               }
+
                void BenchWidth (int colSize, int height, int playerSize)
                {
                        int maxPlayers, playersPerColumn, playersPerRow;
@@ -297,11 +364,11 @@ namespace LongoMatch.Drawing.CanvasObjects
                                        awayPlayers = null;
                                }
                        }
+                       playerToPlayerObject.Clear ();
                }
 
                ISurface CreateSurface (string name)
                {
-                       
                        return Config.DrawingToolkit.CreateSurface (Path.Combine (Config.ImagesDir, name));
                }
 
@@ -317,6 +384,20 @@ namespace LongoMatch.Drawing.CanvasObjects
                        awayInSurface = CreateSurface (StyleConf.PlayerAwayIn);
                }
 
+               void LoadSubsButtons () {
+                       subPlayers = new ButtonObject ();
+                       string  path = Path.Combine (Config.IconsDir, StyleConf.SubsUnlock);
+                       subPlayers.BackgroundImageActive = Image.LoadFromFile (path);
+                       path = Path.Combine (Config.IconsDir, StyleConf.SubsLock);
+                       subPlayers.BackgroundImage = Image.LoadFromFile (path);
+                       subPlayers.Toggle = true;
+                       subPlayers.ClickedEvent += HandleSubsClicked;
+                       subInjury = new ButtonObject ();
+                       subInjury.Toggle = true;
+                       subInjury.ClickedEvent += HandleSubsClicked;
+                       subInjury.Visible = false;
+               }
+
                void Substitute (PlayerObject p1, PlayerObject p2,
                                 List<PlayerObject> playingPlayers,
                                 List<PlayerObject> benchPlayers)
@@ -402,13 +483,44 @@ namespace LongoMatch.Drawing.CanvasObjects
                                        SubstitutionMode =  SubstitutionMode,
                                        Photo = photoSurface
                                };
-                               po.ClickedEvent += HandleClickedEvent;
+                               po.ClickedEvent += HandlePlayerClickedEvent;
                                playerObjects.Add (po);
+                               playerToPlayerObject.Add (p, po);
                        }
                        return playerObjects;
                }
 
-               void HandleClickedEvent (CanvasObject co)
+               void EmitSubsitutionEvent (PlayerObject player1, PlayerObject player2)
+               {
+                       TeamTemplate team;
+                       List<PlayerObject> bench, field;
+
+                       if (substitutionPlayer.Team == Team.LOCAL) {
+                               team = homeTeam;
+                               bench = homeBenchPlayers;
+                       } else {
+                               team = awayTeam;
+                               bench = awayBenchPlayers;
+                       }
+                       if (PlayersSubstitutionEvent != null) {
+                               if (bench.Contains (player1) && bench.Contains (player2)) {
+                                       PlayersSubstitutionEvent (team, player1.Player, player2.Player,
+                                                                 SubstitutionReason.BenchPositionChange, 
CurrentTime);
+                               } else if (!bench.Contains (player1) && !bench.Contains (player2)) {
+                                       PlayersSubstitutionEvent (team, player1.Player, player2.Player,
+                                                                 SubstitutionReason.PositionChange, 
CurrentTime);
+                               } else if (bench.Contains (player1)) {
+                                       PlayersSubstitutionEvent (team, player1.Player, player2.Player,
+                                                                 SubstitutionReason.PlayersSubstitution, 
CurrentTime);
+                               } else {
+                                       PlayersSubstitutionEvent (team, player2.Player, player1.Player,
+                                                                 SubstitutionReason.PlayersSubstitution, 
CurrentTime);
+                               }
+                       }
+                       ResetSelection ();
+               }
+
+               void HandlePlayerClickedEvent (ICanvasObject co)
                {
                        PlayerObject player = co as PlayerObject;
 
@@ -417,16 +529,9 @@ namespace LongoMatch.Drawing.CanvasObjects
                                        substitutionPlayer = player;
                                } else {
                                        if (substitutionPlayer.Team == player.Team) {
-                                               TeamTemplate team;
-                                               if (substitutionPlayer.Team == Team.LOCAL) {
-                                                       team = homeTeam;
-                                               } else {
-                                                       team = awayTeam;
-                                               }
-                                               if (PlayersSubstitutionEvent != null) {
-                                                       PlayersSubstitutionEvent (substitutionPlayer.Player,
-                                                                                 player.Player, team);
-                                               }
+                                               EmitSubsitutionEvent (player, substitutionPlayer);
+                                       } else {
+                                               player.Active = false;
                                        }
                                }
                        } else {
@@ -441,10 +546,44 @@ namespace LongoMatch.Drawing.CanvasObjects
                        }
                }
 
+               bool ButtonClickPressed (Point point, ButtonModifier modif, params ButtonObject[] buttons)
+               {
+                       Selection sel;
+                       
+                       if (!ShowSubsitutionButtons) {
+                               return false;
+                       }
+
+                       foreach (ButtonObject button in buttons) {
+                               if (!button.Visible)
+                                       continue;
+                               sel = button.GetSelection (point, 0);
+                               if (sel != null) {
+                                       clickedButton = sel.Drawable as ButtonObject;
+                                       (sel.Drawable as ICanvasObject).ClickPressed (point, modif);
+                                       return true;
+                               }
+                       }
+                       return false;
+               }
+
+               void HandleSubsClicked (ICanvasObject co)
+               {
+                       ResetSelection ();
+                       if (PlayersSelectionChangedEvent != null) {
+                               PlayersSelectionChangedEvent (SelectedPlayers);
+                       }
+                       SubstitutionMode = !SubstitutionMode;
+               }
+
                public override void ClickPressed (Point point, ButtonModifier modif)
                {
                        Selection selection = null;
 
+                       if (ButtonClickPressed (point, modif, subPlayers, subInjury)) {
+                               return;
+                       }
+                       
                        if (!SubstitutionMode && SelectionMode != MultiSelectionMode.Multiple) {
                                if (SelectionMode == MultiSelectionMode.Single || modif == 
ButtonModifier.None) {
                                        ResetSelection ();
@@ -474,7 +613,10 @@ namespace LongoMatch.Drawing.CanvasObjects
 
                public override void ClickReleased ()
                {
-                       if (clickedPlayer != null) {
+                       if (clickedButton != null) {
+                               clickedButton.ClickReleased ();
+                               clickedButton = null;
+                       } else if (clickedPlayer != null) {
                                clickedPlayer.ClickReleased ();
                        } else {
                                ResetSelection ();
@@ -493,10 +635,28 @@ namespace LongoMatch.Drawing.CanvasObjects
                        width = homeBench.Width * NTeams + field.Width +
                                2 * NTeams * Config.Style.TeamTaggerBenchBorder; 
                        height = field.Height;
-                       Image.ScaleFactor ((int)width, (int)height, (int)Width, (int)Height,
+                       Image.ScaleFactor ((int)width, (int)height, (int)Width,
+                                          (int)Height - SUBSTITUTION_BUTTONS_HEIGHT,
                                           out scaleX, out scaleY, out offset);
+                       offset.Y += SUBSTITUTION_BUTTONS_HEIGHT;
                        tk.Begin ();
                        tk.Clear (Config.Style.PaletteBackground);
+
+                       /* Draw substitution buttons */
+                       if (ShowSubsitutionButtons) {
+                               subPlayers.Position = new Point (Width / 2 - SUBSTITUTION_BUTTONS_WIDTH / 2,
+                                                                offset.Y - SUBSTITUTION_BUTTONS_HEIGHT);
+                               subPlayers.Width = SUBSTITUTION_BUTTONS_WIDTH;
+                               subPlayers.Height = SUBSTITUTION_BUTTONS_HEIGHT;
+                               subPlayers.Draw (tk, area);
+                               
+                               //subInjury.Position = new Point (100, 0);
+                               //subInjury.Width = 100;
+                               //subInjury.Height = SUBSTITUTION_BUTTONS_HEIGHT;
+                               //subInjury.Draw (tk, area);
+                       }
+
+                       
                        tk.TranslateAndScale (Position + offset, new Point (scaleX, scaleY));
                        homeBench.Draw (tk, area);
                        awayBench.Draw (tk, area);
diff --git a/LongoMatch.Drawing/CanvasObjects/TimeNodeObject.cs 
b/LongoMatch.Drawing/CanvasObjects/TimeNodeObject.cs
index 2f0bada..24add6d 100644
--- a/LongoMatch.Drawing/CanvasObjects/TimeNodeObject.cs
+++ b/LongoMatch.Drawing/CanvasObjects/TimeNodeObject.cs
@@ -122,6 +122,11 @@ namespace LongoMatch.Drawing.CanvasObjects
                        }
                        newTime = Utils.PosToTime (p, SecondsPerPixel);
 
+                       if (TimeNode is StatEvent) {
+                               TimeNode.EventTime = newTime;
+                               return;
+                       }
+
                        switch (sel.Position) {
                        case SelectionPosition.Left:
                                {
diff --git a/LongoMatch.Drawing/Widgets/TeamTagger.cs b/LongoMatch.Drawing/Widgets/TeamTagger.cs
index 99b7d13..6126fbd 100644
--- a/LongoMatch.Drawing/Widgets/TeamTagger.cs
+++ b/LongoMatch.Drawing/Widgets/TeamTagger.cs
@@ -33,10 +33,9 @@ namespace LongoMatch.Drawing.Widgets
        {
        
                public event PlayersSelectionChangedHandler PlayersSelectionChangedEvent;
+               public event PlayersSubstitutionHandler PlayersSubstitutionEvent;
                public event PlayersPropertiesHandler ShowMenuEvent;
                PlayersTaggerObject tagger;
-               MultiSelectionMode prevMode;
-               bool inSubs;
 
                public TeamTagger (IWidget widget): base (widget)
                {
@@ -46,6 +45,7 @@ namespace LongoMatch.Drawing.Widgets
                        tagger.SelectionMode = MultiSelectionMode.Single;
                        tagger.PlayersSubstitutionEvent += HandlePlayersSubstitutionEvent;
                        tagger.PlayersSelectionChangedEvent += HandlePlayersSelectionChangedEvent;
+                       ShowSubstitutionButtons = true;
                        ObjectsCanMove = false;
                        AddObject (tagger);
                }
@@ -67,6 +67,12 @@ namespace LongoMatch.Drawing.Widgets
                        tagger.Reload ();
                        widget.ReDraw ();
                }
+
+               public Project Project {
+                       set {
+                               tagger.Project = value;
+                       }
+               }
                
                public bool Compact {
                        set {
@@ -74,12 +80,24 @@ namespace LongoMatch.Drawing.Widgets
                        }
                }
 
+               public Time CurrentTime {
+                       set {
+                               tagger.CurrentTime = value;
+                       }
+               }
+               
                public bool SubstitutionMode {
                        set {
                                tagger.SubstitutionMode = value;
                        }
                }
 
+               public bool ShowSubstitutionButtons {
+                       set {
+                               tagger.ShowSubsitutionButtons = value;
+                       }
+               }
+               
                public new MultiSelectionMode SelectionMode {
                        set {
                                tagger.SelectionMode = value;
@@ -104,6 +122,11 @@ namespace LongoMatch.Drawing.Widgets
                        widget.ReDraw ();
                }
 
+               public void Substitute (Player p1, Player p2, TeamTemplate team)
+               {
+                       tagger.Substitute (p1, p2, team);
+               }
+
                protected override void ShowMenu (Point coords)
                {
                        List<Player> players = tagger.SelectedPlayers;
@@ -122,18 +145,18 @@ namespace LongoMatch.Drawing.Widgets
                        }
                }
 
-
                void HandleSizeChangedEvent ()
                {
                        tagger.Width = widget.Width;
                        tagger.Height = widget.Height;
                }
 
-               void HandlePlayersSubstitutionEvent (Player p1, Player p2, TeamTemplate team)
+               void HandlePlayersSubstitutionEvent (TeamTemplate team, Player p1, Player p2, 
SubstitutionReason reason, Time time)
                {
-                       team.List.Swap (p1, p2);
-                       tagger.Substitute (p1, p2, team);
                        widget.ReDraw ();
+                       if (PlayersSubstitutionEvent != null) {
+                               PlayersSubstitutionEvent (team, p1, p2, reason, time);
+                       }
                }
 
                void HandlePlayersSelectionChangedEvent (List<Player> players)
diff --git a/LongoMatch.GUI.Multimedia/Gui/PlayerBin.cs b/LongoMatch.GUI.Multimedia/Gui/PlayerBin.cs
index 5b8dfdc..21848d5 100644
--- a/LongoMatch.GUI.Multimedia/Gui/PlayerBin.cs
+++ b/LongoMatch.GUI.Multimedia/Gui/PlayerBin.cs
@@ -251,12 +251,18 @@ namespace LongoMatch.Gui
                        }
                }
 
-               public void LoadPlay (MediaFile file, TimelineEvent play, Time seekTime, bool playing)
+               public void LoadPlay (MediaFile file, TimelineEvent evt, Time seekTime, bool playing)
                {
                        loadedPlaylist = null;
                        loadedPlaylistElement = null;
-                       loadedPlay = play;
-                       LoadSegment (file, play.Start, play.Stop, seekTime, playing, play.Rate);
+                       loadedPlay = evt;
+                       if (evt.Start != null && evt.Start != null) {
+                               LoadSegment (file, evt.Start, evt.Stop, seekTime, playing, evt.Rate);
+                       } else if (evt.EventTime != null) {
+                               Seek (evt.EventTime, true);
+                       } else {
+                               Log.Error ("Event does not have timing info: " + evt);
+                       }
                }
 
                public void Close ()
diff --git a/LongoMatch.GUI.Multimedia/gtk-gui/objects.xml b/LongoMatch.GUI.Multimedia/gtk-gui/objects.xml
index b8d3ed6..28c8a06 100644
--- a/LongoMatch.GUI.Multimedia/gtk-gui/objects.xml
+++ b/LongoMatch.GUI.Multimedia/gtk-gui/objects.xml
@@ -40,10 +40,10 @@
     </itemgroups>
     <signals>
       <itemgroup label="VideoWindow Signals">
+        <signal name="ReadyEvent" />
         <signal name="ExposeEvent" />
         <signal name="ButtonPressEvent" />
         <signal name="ScrollEvent" />
-        <signal name="ReadyEvent" />
       </itemgroup>
     </signals>
   </object>
diff --git a/LongoMatch.GUI/Gui/Component/CodingWidget.cs b/LongoMatch.GUI/Gui/Component/CodingWidget.cs
index 5830a7a..f3aafa6 100644
--- a/LongoMatch.GUI/Gui/Component/CodingWidget.cs
+++ b/LongoMatch.GUI/Gui/Component/CodingWidget.cs
@@ -26,6 +26,7 @@ using LongoMatch.Drawing.Widgets;
 using LongoMatch.Drawing.Cairo;
 using LongoMatch.Gui.Helpers;
 using Mono.Unix;
+using LongoMatch.Core.Store.Templates;
 
 namespace LongoMatch.Gui.Component
 {
@@ -62,6 +63,7 @@ namespace LongoMatch.Gui.Component
                        teamtagger = new TeamTagger (new WidgetWrapper (teamsdrawingarea));
                        teamtagger.SelectionMode = MultiSelectionMode.Multiple;
                        teamtagger.PlayersSelectionChangedEvent += HandlePlayersSelectionChangedEvent;
+                       teamtagger.PlayersSubstitutionEvent += HandlePlayersSubstitutionEvent;
                        teamtagger.Compact = true;
 
                        teamsdrawingarea.HeightRequest = 200;
@@ -110,8 +112,10 @@ namespace LongoMatch.Gui.Component
                        if (project != null) {
                                buttonswidget.Template = project.Dashboard;
                        }
+                       teamtagger.Project = project;
                        teamtagger.LoadTeams (project.LocalTeamTemplate, project.VisitorTeamTemplate,
                                              project.Dashboard.FieldBackground);
+                       teamtagger.CurrentTime = new Time (0);
                        if (projectType == ProjectType.FileProject) {
                                timeline.SetProject (project, filter);
                        }
@@ -229,6 +233,7 @@ namespace LongoMatch.Gui.Component
                        if (projectType != ProjectType.FileProject) {
                                timeline.CurrentTime = currentTime;
                                buttonswidget.CurrentTime = currentTime;
+                               teamtagger.CurrentTime = currentTime;
                        }
                }
 
@@ -237,6 +242,7 @@ namespace LongoMatch.Gui.Component
                        if (projectType == ProjectType.FileProject) {
                                timeline.CurrentTime = currentTime;
                                buttonswidget.CurrentTime = currentTime;
+                               teamtagger.CurrentTime = currentTime;
                        }
                }
 
@@ -258,6 +264,11 @@ namespace LongoMatch.Gui.Component
                        Config.EventsBroker.EmitNewEvent (play);
                }
 
+               void HandlePlayersSubstitutionEvent (TeamTemplate team, Player p1, Player p2,
+                                                    SubstitutionReason reason, Time time)
+               {
+                       Config.EventsBroker.EmitSubstitutionEvent (team, p1, p2, reason, time);
+               }
        }
 }
 
diff --git a/LongoMatch.GUI/Gui/Component/TeamTemplateEditor.cs 
b/LongoMatch.GUI/Gui/Component/TeamTemplateEditor.cs
index 294a175..bbf5658 100644
--- a/LongoMatch.GUI/Gui/Component/TeamTemplateEditor.cs
+++ b/LongoMatch.GUI/Gui/Component/TeamTemplateEditor.cs
@@ -52,12 +52,13 @@ namespace LongoMatch.Gui.Component
                        teamtagger = new TeamTagger (new WidgetWrapper (drawingarea));
                        teamtagger.SelectionMode = MultiSelectionMode.MultipleWithModifier;
                        teamtagger.PlayersSelectionChangedEvent += HandlePlayersSelectionChangedEvent;
+                       teamtagger.PlayersSubstitutionEvent += HandlePlayersSubstitutionEvent;
 
                        ConnectSignals ();
 
                        ClearPlayer ();
                }
-               
+
                protected override void OnDestroyed ()
                {
                        teamtagger.Dispose ();
@@ -340,6 +341,14 @@ namespace LongoMatch.Gui.Component
                                Edited = true;
                        }
                }
+               
+               void HandlePlayersSubstitutionEvent (TeamTemplate team, Player p1, Player p2,
+                                                    SubstitutionReason reason, Time time)
+               {
+                       team.List.Swap (p1, p2);
+                       teamtagger.Substitute (p1, p2, team);
+               }
+               
        }
 }
 
diff --git a/LongoMatch.GUI/Gui/Dialog/SubstitutionsEditor.cs 
b/LongoMatch.GUI/Gui/Dialog/SubstitutionsEditor.cs
new file mode 100644
index 0000000..c7a0b74
--- /dev/null
+++ b/LongoMatch.GUI/Gui/Dialog/SubstitutionsEditor.cs
@@ -0,0 +1,216 @@
+//
+//  Copyright (C) 2014 Andoni Morales Alastruey
+//
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 2 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+using System;
+using System.Linq;
+using LongoMatch.Drawing.Widgets;
+using LongoMatch.Drawing.Cairo;
+using LongoMatch.Core.Store;
+using System.Collections.Generic;
+using LongoMatch.Core.Store.Templates;
+using LongoMatch.Core.Common;
+using LongoMatch.Drawing;
+using LongoMatch.Drawing.CanvasObjects;
+using LongoMatch.Core.Interfaces.Drawing;
+
+namespace LongoMatch.Gui.Dialog
+{
+       public partial class SubstitutionsEditor : Gtk.Dialog
+       {
+               TeamTagger tagger;
+               SelectionCanvas incanvas, outcanvas;
+               PlayerObject inpo, outpo;
+               Player inPlayer, outPlayer, selectedPlayer;
+               TeamTemplate homeTeam, awayTeam;
+               LineupEvent lineup;
+               SubstitutionEvent substitution;
+               const int PLAYER_SIZE = 100;
+
+               public SubstitutionsEditor ()
+               {
+                       this.Build ();
+                       tagger = new TeamTagger (new WidgetWrapper (drawingarea));
+                       tagger.PlayersSelectionChangedEvent += HandlePlayersSelectionChangedEvent;
+                       tagger.PlayersSubstitutionEvent += HandlePlayersSubstitutionEvent;
+                       incanvas = new SelectionCanvas (new WidgetWrapper (drawingarea2));
+                       outcanvas = new SelectionCanvas (new WidgetWrapper (drawingarea3));
+                       inpo = new PlayerObject ();
+                       outpo = new PlayerObject ();
+                       inpo.ClickedEvent += HandleClickedEvent;
+                       outpo.ClickedEvent += HandleClickedEvent;
+                       inpo.Size = PLAYER_SIZE;
+                       outpo.Size = PLAYER_SIZE;
+                       inpo.Position = new Point (PLAYER_SIZE / 2, PLAYER_SIZE / 2);
+                       outpo.Position = new Point (PLAYER_SIZE / 2, PLAYER_SIZE / 2);
+                       incanvas.AddObject (inpo);
+                       outcanvas.AddObject (outpo);
+                       drawingarea2.WidthRequest = drawingarea2.HeightRequest = PLAYER_SIZE;
+                       drawingarea3.WidthRequest = drawingarea3.HeightRequest = PLAYER_SIZE;
+               }
+
+               public void SaveChanges ()
+               {
+                       if (lineup != null) {
+                               lineup.HomeStartingPlayers = homeTeam.StartingPlayersList;
+                               lineup.HomeBenchPlayers = homeTeam.BenchPlayersList;
+                               lineup.AwayStartingPlayers = awayTeam.StartingPlayersList;
+                               lineup.AwayBenchPlayers = awayTeam.BenchPlayersList;
+                       } else {
+                               substitution.In = inPlayer;
+                               substitution.Out = outPlayer;
+                       }
+               }
+
+               public void Load (Project project, StatEvent evt)
+               {
+                       if (evt is LineupEvent) {
+                               LoadLineup (project, evt as LineupEvent);
+                       } else {
+                               LoadSubstitution (project, evt as SubstitutionEvent);
+                       }
+               }
+
+               public void LoadLineup (Project project, LineupEvent lineup)
+               {
+                       this.lineup = lineup;
+                       playershbox.Visible = false;
+                       tagger.SubstitutionMode = true;
+                       tagger.ShowSubstitutionButtons = false;
+                       LoadTeams (project, lineup.HomeStartingPlayers, lineup.HomeBenchPlayers,
+                                  lineup.AwayStartingPlayers, lineup.AwayBenchPlayers);
+               }
+
+               public void LoadSubstitution (Project project, SubstitutionEvent substitution)
+               {
+                       TeamTemplate team;
+                       List<Player> hfp, hbp, afp, abp;
+
+                       this.substitution = substitution;
+                       project.CurrentLineup (substitution.EventTime, out hfp, out hbp, out afp, out abp);
+                       if (substitution.Team == Team.LOCAL) {
+                               team = homeTeam;
+                       } else {
+                               team = awayTeam;
+                       }
+                       playershbox.Visible = true;
+                       tagger.SubstitutionMode = false;
+                       tagger.ShowSubstitutionButtons = false;
+                       tagger.SelectionMode = MultiSelectionMode.Single;
+                       LoadTeams (project, hfp, hbp, null, null);
+                       SwitchPlayer (substitution.In, substitution.Out);
+               }
+
+               void LoadTeams (Project project, List<Player> homeFieldPlayers, List<Player> homeBenchPlayers,
+                             List<Player> awayFieldPlayers, List<Player> awayBenchPlayers)
+               {
+                       List<Player> homeTeamPlayers, awayTeamPlayers;
+
+                       homeTeamPlayers = homeFieldPlayers.Concat (homeBenchPlayers).ToList ();
+                       homeTeam = new TeamTemplate {
+                               ID = project.LocalTeamTemplate.ID,
+                               Formation = project.LocalTeamTemplate.Formation,
+                               List = homeTeamPlayers
+                       };
+                       
+                       if (awayFieldPlayers != null) {
+                               awayTeamPlayers = awayFieldPlayers.Concat (awayBenchPlayers).ToList ();
+                               awayTeam = new TeamTemplate {
+                                       ID = project.VisitorTeamTemplate.ID,
+                                       Formation = project.VisitorTeamTemplate.Formation,
+                                       List = awayTeamPlayers
+                               };
+                       } else {
+                               awayTeam = null;
+                       }
+
+                       tagger.LoadTeams (homeTeam, awayTeam, project.Dashboard.FieldBackground);
+               }
+
+               void SwitchPlayer (Player inPlayer, Player outPlayer)
+               {
+                       if (inPlayer != null) {
+                               this.inPlayer = inPlayer;
+                               inpo.Player = inPlayer;
+                               inpo.Active = false;
+                               drawingarea2.QueueDraw ();
+                               tagger.ResetSelection ();
+                       } else {
+                               inframe.Visible = false;
+                       }
+                       if (outPlayer != null) {
+                               this.outPlayer = outPlayer;
+                               outpo.Player = outPlayer;
+                               outpo.Active = false;
+                               drawingarea3.QueueDraw ();
+                               tagger.ResetSelection ();
+                       } else {
+                               outframe.Visible = false;
+                       }
+                       selectedPlayer = null;
+               }
+
+               void HandlePlayersSelectionChangedEvent (List<Player> players)
+               {
+                       if (players.Count == 1) {
+                               selectedPlayer = players [0];
+                               if (inpo.Active) {
+                                       SwitchPlayer (selectedPlayer, outPlayer);
+                               } else if (outpo.Active) {
+                                       SwitchPlayer (inPlayer, selectedPlayer);
+                               }
+                       } else {
+                               selectedPlayer = null;
+                       }
+               }
+               
+               void HandlePlayersSubstitutionEvent (TeamTemplate team, Player p1, Player p2, 
SubstitutionReason reason, Time time)
+               {
+                       tagger.Substitute (p1, p2, team);
+                       if (team.ID == homeTeam.ID) {
+                               homeTeam.List.Swap (p1, p2);
+                       } else {
+                               awayTeam.List.Swap (p1, p2);
+                       }
+               }
+
+               void HandleClickedEvent (ICanvasObject co)
+               {
+                       PlayerObject po = co as PlayerObject;
+                       Player player = po.Player;
+
+                       if (po == inpo) {
+                               if (outpo.Active) {
+                                       outpo.Active = false;
+                                       drawingarea3.QueueDraw ();
+                               }
+                               if (selectedPlayer != null) {
+                                       SwitchPlayer (selectedPlayer, outPlayer);
+                               }
+                       } else {
+                               if (inpo.Active) {
+                                       inpo.Active = false;
+                                       drawingarea2.QueueDraw ();
+                               }
+                               if (selectedPlayer != null) {
+                                       SwitchPlayer (inPlayer, selectedPlayer);
+                               }
+                       }
+               }
+
+       }
+}
+
diff --git a/LongoMatch.GUI/Gui/GUIToolkit.cs b/LongoMatch.GUI/Gui/GUIToolkit.cs
index 3491562..b7077a7 100644
--- a/LongoMatch.GUI/Gui/GUIToolkit.cs
+++ b/LongoMatch.GUI/Gui/GUIToolkit.cs
@@ -226,11 +226,21 @@ namespace LongoMatch.Gui
                                sd.Destroy();
                }
                
-               public void EditPlay (TimelineEvent play, Project project, bool editTags, bool editPos, bool 
editPlayers, bool editNotes) {
-                       PlayEditor dialog = new PlayEditor ();
-                       dialog.LoadPlay (play, project, editTags, editPos, editPlayers, editNotes);
-                       dialog.Run();
-                       dialog.Destroy();
+               public void EditPlay (TimelineEvent play, Project project, bool editTags, bool editPos, bool 
editPlayers, bool editNotes)
+               {
+                       if (play is StatEvent) {
+                               SubstitutionsEditor dialog = new SubstitutionsEditor ();
+                               dialog.Load (project, play as StatEvent);
+                               if (dialog.Run () == (int)ResponseType.Ok) {
+                                       dialog.SaveChanges ();
+                               }
+                               dialog.Destroy ();
+                       } else {
+                               PlayEditor dialog = new PlayEditor ();
+                               dialog.LoadPlay (play, project, editTags, editPos, editPlayers, editNotes);
+                               dialog.Run();
+                               dialog.Destroy();
+                       }
                }
 
                public void DrawingTool (Image image, TimelineEvent play, FrameDrawing drawing) {
diff --git a/LongoMatch.GUI/Gui/Menu/PlaysMenu.cs b/LongoMatch.GUI/Gui/Menu/PlaysMenu.cs
index fbcab43..bb4dd7b 100644
--- a/LongoMatch.GUI/Gui/Menu/PlaysMenu.cs
+++ b/LongoMatch.GUI/Gui/Menu/PlaysMenu.cs
@@ -60,6 +60,8 @@ namespace LongoMatch.Gui.Menus
                private void ShowMenu (Project project, List<TimelineEvent> plays, EventType eventType, Time 
time,
                                     List<EventType> eventTypes, bool editableName)
                {
+                       bool isLineup = false, isSubstitution = false;
+
                        this.plays = plays;
                        this.eventType = eventType;
                        this.time = time;
@@ -73,17 +75,28 @@ namespace LongoMatch.Gui.Menus
                                newPlay.Visible = false;
                        }
                        
-                       if (plays == null)
+                       if (plays == null) {
                                plays = new List<TimelineEvent> ();
-                       
-                       edit.Visible = editableName;
-                       snapshot.Visible = plays.Count == 1;
-                       moveCat.Visible = plays.Count == 1 && eventTypes != null;
-                       drawings.Visible = plays.Count == 1 && plays [0].Drawings.Count > 0;
-                       del.Visible = plays.Count > 0;
-                       addPLN.Visible = plays.Count > 0;
-                       render.Visible = plays.Count > 0;
-                       duplicate.Visible = plays.Count > 0;
+                       } else if (plays.Count == 1) {
+                               isLineup = plays [0] is LineupEvent;
+                               isSubstitution = plays [0] is SubstitutionEvent;
+                       }
+
+                       if (isLineup || isSubstitution) {
+                               edit.Visible = true;
+                               del.Visible = isSubstitution;
+                               snapshot.Visible = moveCat.Visible = drawings.Visible =
+                                       addPLN.Visible = render.Visible = duplicate.Visible = false;
+                       } else {
+                               edit.Visible = editableName;
+                               snapshot.Visible = plays.Count == 1;
+                               moveCat.Visible = plays.Count == 1 && eventTypes != null;
+                               drawings.Visible = plays.Count == 1 && plays [0].Drawings.Count > 0;
+                               del.Visible = plays.Count > 0;
+                               addPLN.Visible = plays.Count > 0;
+                               render.Visible = plays.Count > 0;
+                               duplicate.Visible = plays.Count > 0;
+                       }
 
                        if (plays.Count > 0) {
                                string label = String.Format ("{0} ({1})", Catalog.GetString ("Delete"), 
plays.Count);
@@ -209,9 +222,10 @@ namespace LongoMatch.Gui.Menus
 
                void HandleNePlayActivated (object sender, EventArgs e)
                {
-                       Config.EventsBroker.EmitNewTag (eventType, null, null,
-                                                       time - new Time {Seconds = 10},
-                                                       time + new Time {Seconds = 10});
+                       Config.EventsBroker.EmitNewTag (eventType,
+                                                       eventTime: time,
+                                                       start: time - new Time {Seconds = 10},
+                                                       stop: time + new Time {Seconds = 10});
                }
                
                void EmitRenderPlaylist (List<TimelineEvent> plays)
diff --git a/LongoMatch.GUI/Gui/Panel/NewProjectPanel.cs b/LongoMatch.GUI/Gui/Panel/NewProjectPanel.cs
index fd39e2b..460f396 100644
--- a/LongoMatch.GUI/Gui/Panel/NewProjectPanel.cs
+++ b/LongoMatch.GUI/Gui/Panel/NewProjectPanel.cs
@@ -142,6 +142,8 @@ namespace LongoMatch.Gui.Panel
                        teamtagger = new TeamTagger (new WidgetWrapper (drawingarea));
                        teamtagger.ShowMenuEvent += HandleShowMenuEvent;
                        teamtagger.SubstitutionMode = true;
+                       teamtagger.ShowSubstitutionButtons = false;
+                       teamtagger.PlayersSubstitutionEvent += HandlePlayersSubstitutionEvent;
                        teams = Config.TeamTemplatesProvider.Templates;
                        hometeamscombobox.Load (teams);
                        hometeamscombobox.Changed += (sender, e) => {
@@ -364,6 +366,19 @@ namespace LongoMatch.Gui.Panel
                        captureSettings.EncodingSettings = encSettings;
                        return true;
                }
+               
+               void StartProject ()
+               {
+                       if (CreateProject ()) {
+                               if (projectType == ProjectType.EditProject) {
+                                       projectType = ProjectType.FileProject;
+                                       Config.EventsBroker.EmitCreateThumbnails (project);
+                               } else {
+                                       project.CreateLineupEvent ();
+                               }
+                               Config.EventsBroker.EmitOpenNewProject (project, projectType, 
captureSettings);
+                       }
+               }
 
                void HandleEntryChanged (object sender, EventArgs e)
                {
@@ -429,17 +444,12 @@ namespace LongoMatch.Gui.Panel
                                if (projectType == ProjectType.CaptureProject ||
                                    projectType == ProjectType.FakeCaptureProject ||
                                    projectType == ProjectType.URICaptureProject) {
+                                   project.CreateLineupEvent ();
                                        Config.EventsBroker.EmitOpenNewProject (project, projectType, 
captureSettings);
                                        return;
                                }
                        } else if (notebook1.Page == PROJECT_PERIODS) {
-                               if (CreateProject ()) {
-                                       if (projectType == ProjectType.EditProject) {
-                                               projectType = ProjectType.FileProject;
-                                               Config.EventsBroker.EmitCreateThumbnails (project);
-                                       }
-                                       Config.EventsBroker.EmitOpenNewProject (project, projectType, 
captureSettings);
-                               }
+                               StartProject ();
                                return;
                        }
 
@@ -466,6 +476,13 @@ namespace LongoMatch.Gui.Panel
                        menu.ShowAll ();
                        menu.Popup ();
                }
+               
+               void HandlePlayersSubstitutionEvent (TeamTemplate team, Player p1, Player p2,
+                                                    SubstitutionReason reason, Time time)
+               {
+                       team.List.Swap (p1, p2);
+                       teamtagger.Substitute (p1, p2, team);
+               }
        }
 }
 
diff --git a/LongoMatch.GUI/Gui/TreeView/PlaysTreeView.cs b/LongoMatch.GUI/Gui/TreeView/PlaysTreeView.cs
index e39c1d1..b2550e8 100644
--- a/LongoMatch.GUI/Gui/TreeView/PlaysTreeView.cs
+++ b/LongoMatch.GUI/Gui/TreeView/PlaysTreeView.cs
@@ -194,17 +194,33 @@ namespace LongoMatch.Gui.Component
                        Model.SetSortFunc(0, SortFunction);
                }
 
-               override protected bool SelectFunction(TreeSelection selection, TreeModel model, TreePath 
path, bool selected) {
-                       // Don't allow multiselect for categories
-                       if(!selected && selection.GetSelectedRows().Length > 0) {
-                               if(selection.GetSelectedRows().Length == 1 &&
-                                               GetValueFromPath(selection.GetSelectedRows()[0]) is EventType)
+               override protected bool SelectFunction (TreeSelection selection, TreeModel model, TreePath 
path, bool selected)
+               {
+                       TreePath[] selectedRows;
+
+                       selectedRows = selection.GetSelectedRows ();
+                       if (!selected && selectedRows.Length > 0) {
+                               object currentSelected;
+                               object firstSelected;
+
+                               firstSelected = GetValueFromPath (selectedRows [0]);
+                               // No multiple selection for event types and substitution events
+                               if (selectedRows.Length == 1) {
+                                       if (firstSelected is EventType) {
+                                               return false;
+                                       } else if (firstSelected is StatEvent) {
+                                               return false;
+                                       }
+                               }
+                               
+                               currentSelected = GetValueFromPath (path);
+                               if (currentSelected is EventType || currentSelected is StatEvent) {
                                        return false;
-                               return !(GetValueFromPath(path) is EventType);
+                               }
+                               return true;
                        }
                        // Always unselect
-                       else
-                               return true;
+                       return true;
                }
 
                override protected bool OnButtonPressEvent(Gdk.EventButton evnt)
diff --git a/LongoMatch.GUI/LongoMatch.GUI.csproj b/LongoMatch.GUI/LongoMatch.GUI.csproj
index 8538bf2..d11093c 100644
--- a/LongoMatch.GUI/LongoMatch.GUI.csproj
+++ b/LongoMatch.GUI/LongoMatch.GUI.csproj
@@ -183,6 +183,8 @@
     <Compile Include="gtk-gui\LongoMatch.Gui.Panel.PanelHeader.cs" />
     <Compile Include="Gui\Dialog\PlayEditor.cs" />
     <Compile Include="gtk-gui\LongoMatch.Gui.Dialog.PlayEditor.cs" />
+    <Compile Include="Gui\Dialog\SubstitutionsEditor.cs" />
+    <Compile Include="gtk-gui\LongoMatch.Gui.Dialog.SubstitutionsEditor.cs" />
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="gtk-gui\gui.stetic">
diff --git a/LongoMatch.GUI/Makefile.am b/LongoMatch.GUI/Makefile.am
index 27b9801..652bf61 100644
--- a/LongoMatch.GUI/Makefile.am
+++ b/LongoMatch.GUI/Makefile.am
@@ -54,6 +54,7 @@ SOURCES = Gui/Cairo.cs \
        Gui/Dialog/ShortcutsHelpDialog.cs \
        Gui/Dialog/SnapshotsDialog.cs \
        Gui/Dialog/StatsViewer.cs \
+       Gui/Dialog/SubstitutionsEditor.cs \
        Gui/Dialog/UpdateDialog.cs \
        Gui/Dialog/VideoConversionTool.cs \
        Gui/Dialog/VideoEditionProperties.cs \
@@ -125,6 +126,7 @@ SOURCES = Gui/Cairo.cs \
        gtk-gui/LongoMatch.Gui.Dialog.ShortcutsHelpDialog.cs \
        gtk-gui/LongoMatch.Gui.Dialog.SnapshotsDialog.cs \
        gtk-gui/LongoMatch.Gui.Dialog.StatsViewer.cs \
+       gtk-gui/LongoMatch.Gui.Dialog.SubstitutionsEditor.cs \
        gtk-gui/LongoMatch.Gui.Dialog.UpdateDialog.cs \
        gtk-gui/LongoMatch.Gui.Dialog.VideoConversionTool.cs \
        gtk-gui/LongoMatch.Gui.Dialog.VideoEditionProperties.cs \
diff --git a/LongoMatch.GUI/gtk-gui/LongoMatch.Gui.Dialog.SubstitutionsEditor.cs 
b/LongoMatch.GUI/gtk-gui/LongoMatch.Gui.Dialog.SubstitutionsEditor.cs
new file mode 100644
index 0000000..22b33ca
--- /dev/null
+++ b/LongoMatch.GUI/gtk-gui/LongoMatch.Gui.Dialog.SubstitutionsEditor.cs
@@ -0,0 +1,140 @@
+
+// This file has been generated by the GUI designer. Do not modify.
+namespace LongoMatch.Gui.Dialog
+{
+       public partial class SubstitutionsEditor
+       {
+               private global::Gtk.VBox vbox2;
+               private global::Gtk.HBox playershbox;
+               private global::Gtk.Frame inframe;
+               private global::Gtk.Alignment GtkAlignment2;
+               private global::Gtk.DrawingArea drawingarea2;
+               private global::Gtk.Label GtkLabel2;
+               private global::Gtk.Frame outframe;
+               private global::Gtk.Alignment GtkAlignment3;
+               private global::Gtk.DrawingArea drawingarea3;
+               private global::Gtk.Label GtkLabel3;
+               private global::Gtk.DrawingArea drawingarea;
+               private global::Gtk.Button buttonCancel;
+               private global::Gtk.Button buttonOk;
+
+               protected virtual void Build ()
+               {
+                       global::Stetic.Gui.Initialize (this);
+                       // Widget LongoMatch.Gui.Dialog.SubstitutionsEditor
+                       this.Name = "LongoMatch.Gui.Dialog.SubstitutionsEditor";
+                       this.Icon = global::Stetic.IconLoader.LoadIcon (this, "longomatch", 
global::Gtk.IconSize.Menu);
+                       this.WindowPosition = ((global::Gtk.WindowPosition)(4));
+                       this.DestroyWithParent = true;
+                       this.Gravity = ((global::Gdk.Gravity)(5));
+                       this.SkipPagerHint = true;
+                       this.SkipTaskbarHint = true;
+                       // Internal child LongoMatch.Gui.Dialog.SubstitutionsEditor.VBox
+                       global::Gtk.VBox w1 = this.VBox;
+                       w1.Name = "dialog1_VBox";
+                       w1.BorderWidth = ((uint)(2));
+                       // Container child dialog1_VBox.Gtk.Box+BoxChild
+                       this.vbox2 = new global::Gtk.VBox ();
+                       this.vbox2.Name = "vbox2";
+                       this.vbox2.Spacing = 6;
+                       // Container child vbox2.Gtk.Box+BoxChild
+                       this.playershbox = new global::Gtk.HBox ();
+                       this.playershbox.Name = "playershbox";
+                       this.playershbox.Spacing = 6;
+                       // Container child playershbox.Gtk.Box+BoxChild
+                       this.inframe = new global::Gtk.Frame ();
+                       this.inframe.Name = "inframe";
+                       this.inframe.ShadowType = ((global::Gtk.ShadowType)(0));
+                       // Container child inframe.Gtk.Container+ContainerChild
+                       this.GtkAlignment2 = new global::Gtk.Alignment (0F, 0F, 1F, 1F);
+                       this.GtkAlignment2.Name = "GtkAlignment2";
+                       this.GtkAlignment2.LeftPadding = ((uint)(12));
+                       // Container child GtkAlignment2.Gtk.Container+ContainerChild
+                       this.drawingarea2 = new global::Gtk.DrawingArea ();
+                       this.drawingarea2.Name = "drawingarea2";
+                       this.GtkAlignment2.Add (this.drawingarea2);
+                       this.inframe.Add (this.GtkAlignment2);
+                       this.GtkLabel2 = new global::Gtk.Label ();
+                       this.GtkLabel2.Name = "GtkLabel2";
+                       this.GtkLabel2.LabelProp = global::Mono.Unix.Catalog.GetString ("<b>Player in</b>");
+                       this.GtkLabel2.UseMarkup = true;
+                       this.inframe.LabelWidget = this.GtkLabel2;
+                       this.playershbox.Add (this.inframe);
+                       global::Gtk.Box.BoxChild w4 = ((global::Gtk.Box.BoxChild)(this.playershbox 
[this.inframe]));
+                       w4.Position = 0;
+                       w4.Fill = false;
+                       // Container child playershbox.Gtk.Box+BoxChild
+                       this.outframe = new global::Gtk.Frame ();
+                       this.outframe.Name = "outframe";
+                       this.outframe.ShadowType = ((global::Gtk.ShadowType)(0));
+                       // Container child outframe.Gtk.Container+ContainerChild
+                       this.GtkAlignment3 = new global::Gtk.Alignment (0F, 0F, 1F, 1F);
+                       this.GtkAlignment3.Name = "GtkAlignment3";
+                       this.GtkAlignment3.LeftPadding = ((uint)(12));
+                       // Container child GtkAlignment3.Gtk.Container+ContainerChild
+                       this.drawingarea3 = new global::Gtk.DrawingArea ();
+                       this.drawingarea3.Name = "drawingarea3";
+                       this.GtkAlignment3.Add (this.drawingarea3);
+                       this.outframe.Add (this.GtkAlignment3);
+                       this.GtkLabel3 = new global::Gtk.Label ();
+                       this.GtkLabel3.Name = "GtkLabel3";
+                       this.GtkLabel3.LabelProp = global::Mono.Unix.Catalog.GetString ("<b>Player out</b>");
+                       this.GtkLabel3.UseMarkup = true;
+                       this.outframe.LabelWidget = this.GtkLabel3;
+                       this.playershbox.Add (this.outframe);
+                       global::Gtk.Box.BoxChild w7 = ((global::Gtk.Box.BoxChild)(this.playershbox 
[this.outframe]));
+                       w7.Position = 1;
+                       w7.Fill = false;
+                       this.vbox2.Add (this.playershbox);
+                       global::Gtk.Box.BoxChild w8 = ((global::Gtk.Box.BoxChild)(this.vbox2 
[this.playershbox]));
+                       w8.Position = 0;
+                       w8.Expand = false;
+                       // Container child vbox2.Gtk.Box+BoxChild
+                       this.drawingarea = new global::Gtk.DrawingArea ();
+                       this.drawingarea.Name = "drawingarea";
+                       this.vbox2.Add (this.drawingarea);
+                       global::Gtk.Box.BoxChild w9 = ((global::Gtk.Box.BoxChild)(this.vbox2 
[this.drawingarea]));
+                       w9.Position = 1;
+                       w1.Add (this.vbox2);
+                       global::Gtk.Box.BoxChild w10 = ((global::Gtk.Box.BoxChild)(w1 [this.vbox2]));
+                       w10.Position = 0;
+                       // Internal child LongoMatch.Gui.Dialog.SubstitutionsEditor.ActionArea
+                       global::Gtk.HButtonBox w11 = this.ActionArea;
+                       w11.Name = "dialog1_ActionArea";
+                       w11.Spacing = 10;
+                       w11.BorderWidth = ((uint)(5));
+                       w11.LayoutStyle = ((global::Gtk.ButtonBoxStyle)(4));
+                       // Container child dialog1_ActionArea.Gtk.ButtonBox+ButtonBoxChild
+                       this.buttonCancel = new global::Gtk.Button ();
+                       this.buttonCancel.CanDefault = true;
+                       this.buttonCancel.CanFocus = true;
+                       this.buttonCancel.Name = "buttonCancel";
+                       this.buttonCancel.UseStock = true;
+                       this.buttonCancel.UseUnderline = true;
+                       this.buttonCancel.Label = "gtk-cancel";
+                       this.AddActionWidget (this.buttonCancel, -6);
+                       global::Gtk.ButtonBox.ButtonBoxChild w12 = 
((global::Gtk.ButtonBox.ButtonBoxChild)(w11 [this.buttonCancel]));
+                       w12.Expand = false;
+                       w12.Fill = false;
+                       // Container child dialog1_ActionArea.Gtk.ButtonBox+ButtonBoxChild
+                       this.buttonOk = new global::Gtk.Button ();
+                       this.buttonOk.CanDefault = true;
+                       this.buttonOk.CanFocus = true;
+                       this.buttonOk.Name = "buttonOk";
+                       this.buttonOk.UseStock = true;
+                       this.buttonOk.UseUnderline = true;
+                       this.buttonOk.Label = "gtk-ok";
+                       this.AddActionWidget (this.buttonOk, -5);
+                       global::Gtk.ButtonBox.ButtonBoxChild w13 = 
((global::Gtk.ButtonBox.ButtonBoxChild)(w11 [this.buttonOk]));
+                       w13.Position = 1;
+                       w13.Expand = false;
+                       w13.Fill = false;
+                       if ((this.Child != null)) {
+                               this.Child.ShowAll ();
+                       }
+                       this.DefaultWidth = 726;
+                       this.DefaultHeight = 404;
+                       this.Show ();
+               }
+       }
+}
diff --git a/LongoMatch.GUI/gtk-gui/gui.stetic b/LongoMatch.GUI/gtk-gui/gui.stetic
index e1c9b2b..e599757 100644
--- a/LongoMatch.GUI/gtk-gui/gui.stetic
+++ b/LongoMatch.GUI/gtk-gui/gui.stetic
@@ -10825,4 +10825,161 @@ You can continue with the current capture, cancel it or save your project.
       </widget>
     </child>
   </widget>
+  <widget class="Gtk.Dialog" id="LongoMatch.Gui.Dialog.SubstitutionsEditor" design-size="726 404">
+    <property name="MemberName" />
+    <property name="Icon">stock:longomatch Menu</property>
+    <property name="WindowPosition">CenterOnParent</property>
+    <property name="DestroyWithParent">True</property>
+    <property name="Gravity">Center</property>
+    <property name="SkipPagerHint">True</property>
+    <property name="SkipTaskbarHint">True</property>
+    <property name="Buttons">2</property>
+    <property name="HelpButton">False</property>
+    <child internal-child="VBox">
+      <widget class="Gtk.VBox" id="dialog1_VBox">
+        <property name="MemberName" />
+        <property name="BorderWidth">2</property>
+        <child>
+          <widget class="Gtk.VBox" id="vbox2">
+            <property name="MemberName" />
+            <property name="Spacing">6</property>
+            <child>
+              <widget class="Gtk.HBox" id="playershbox">
+                <property name="MemberName" />
+                <property name="Spacing">6</property>
+                <child>
+                  <widget class="Gtk.Frame" id="inframe">
+                    <property name="MemberName" />
+                    <property name="ShadowType">None</property>
+                    <child>
+                      <widget class="Gtk.Alignment" id="GtkAlignment2">
+                        <property name="MemberName" />
+                        <property name="Xalign">0</property>
+                        <property name="Yalign">0</property>
+                        <property name="LeftPadding">12</property>
+                        <child>
+                          <widget class="Gtk.DrawingArea" id="drawingarea2">
+                            <property name="MemberName" />
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="Gtk.Label" id="GtkLabel2">
+                        <property name="MemberName" />
+                        <property name="LabelProp" translatable="yes">&lt;b&gt;Player in&lt;/b&gt;</property>
+                        <property name="UseMarkup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="Position">0</property>
+                    <property name="AutoSize">False</property>
+                    <property name="Fill">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="Gtk.Frame" id="outframe">
+                    <property name="MemberName" />
+                    <property name="ShadowType">None</property>
+                    <child>
+                      <widget class="Gtk.Alignment" id="GtkAlignment3">
+                        <property name="MemberName" />
+                        <property name="Xalign">0</property>
+                        <property name="Yalign">0</property>
+                        <property name="LeftPadding">12</property>
+                        <child>
+                          <widget class="Gtk.DrawingArea" id="drawingarea3">
+                            <property name="MemberName" />
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="Gtk.Label" id="GtkLabel3">
+                        <property name="MemberName" />
+                        <property name="LabelProp" translatable="yes">&lt;b&gt;Player 
out&lt;/b&gt;</property>
+                        <property name="UseMarkup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="Position">1</property>
+                    <property name="AutoSize">False</property>
+                    <property name="Fill">False</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="Position">0</property>
+                <property name="AutoSize">False</property>
+                <property name="Expand">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="Gtk.DrawingArea" id="drawingarea">
+                <property name="MemberName" />
+              </widget>
+              <packing>
+                <property name="Position">1</property>
+                <property name="AutoSize">True</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="Position">0</property>
+            <property name="AutoSize">True</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+    <child internal-child="ActionArea">
+      <widget class="Gtk.HButtonBox" id="dialog1_ActionArea">
+        <property name="MemberName" />
+        <property name="Spacing">10</property>
+        <property name="BorderWidth">5</property>
+        <property name="Size">2</property>
+        <property name="LayoutStyle">End</property>
+        <child>
+          <widget class="Gtk.Button" id="buttonCancel">
+            <property name="MemberName" />
+            <property name="CanDefault">True</property>
+            <property name="CanFocus">True</property>
+            <property name="UseStock">True</property>
+            <property name="Type">StockItem</property>
+            <property name="StockId">gtk-cancel</property>
+            <property name="ResponseId">-6</property>
+            <property name="label">gtk-cancel</property>
+          </widget>
+          <packing>
+            <property name="Expand">False</property>
+            <property name="Fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="Gtk.Button" id="buttonOk">
+            <property name="MemberName" />
+            <property name="CanDefault">True</property>
+            <property name="CanFocus">True</property>
+            <property name="UseStock">True</property>
+            <property name="Type">StockItem</property>
+            <property name="StockId">gtk-ok</property>
+            <property name="ResponseId">-5</property>
+            <property name="label">gtk-ok</property>
+          </widget>
+          <packing>
+            <property name="Position">1</property>
+            <property name="Expand">False</property>
+            <property name="Fill">False</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
 </stetic-interface>
\ No newline at end of file
diff --git a/LongoMatch.GUI/gtk-gui/objects.xml b/LongoMatch.GUI/gtk-gui/objects.xml
index b57a19f..3c0644f 100644
--- a/LongoMatch.GUI/gtk-gui/objects.xml
+++ b/LongoMatch.GUI/gtk-gui/objects.xml
@@ -1,8 +1,33 @@
 <objects attr-sync="on">
+  <object type="LongoMatch.Gui.Component.CategoryProperties" palette-category="General" 
allow-children="false" base-type="Gtk.Bin">
+    <itemgroups>
+      <itemgroup label="CategoryProperties Properties">
+        <property name="Edited" />
+      </itemgroup>
+    </itemgroups>
+    <signals>
+      <itemgroup label="CategoryProperties Signals">
+        <signal name="HotKeyChanged" />
+        <signal name="EditedEvent" />
+      </itemgroup>
+    </signals>
+  </object>
   <object type="LongoMatch.Gui.Popup.TransparentDrawingArea" palette-category="General" 
allow-children="false" base-type="Gtk.Window">
     <itemgroups />
     <signals />
   </object>
+  <object type="LongoMatch.Gui.Component.DashboardWidget" palette-category="General" allow-children="false" 
base-type="Gtk.Bin">
+    <itemgroups>
+      <itemgroup label="DashboardWidget Properties">
+        <property name="Edited" />
+      </itemgroup>
+    </itemgroups>
+    <signals>
+      <itemgroup label="DashboardWidget Signals">
+        <signal name="NewTagEvent" />
+      </itemgroup>
+    </signals>
+  </object>
   <object type="LongoMatch.Gui.Component.NotesWidget" palette-category="General" allow-children="false" 
base-type="Gtk.Bin">
     <itemgroups />
     <signals />
@@ -40,19 +65,6 @@
       </itemgroup>
     </signals>
   </object>
-  <object type="LongoMatch.Gui.Component.CategoryProperties" palette-category="General" 
allow-children="false" base-type="Gtk.Bin">
-    <itemgroups>
-      <itemgroup label="CategoryProperties Properties">
-        <property name="Edited" />
-      </itemgroup>
-    </itemgroups>
-    <signals>
-      <itemgroup label="CategoryProperties Signals">
-        <signal name="HotKeyChanged" />
-        <signal name="EditedEvent" />
-      </itemgroup>
-    </signals>
-  </object>
   <object type="LongoMatch.Gui.Component.PlaysListTreeWidget" palette-category="General" 
allow-children="false" base-type="Gtk.Bin">
     <itemgroups />
     <signals />
@@ -125,6 +137,10 @@
     </itemgroups>
     <signals />
   </object>
+  <object type="LongoMatch.Gui.Component.Stats.SubCategoryViewer" palette-category="General" 
allow-children="false" base-type="Gtk.Bin">
+    <itemgroups />
+    <signals />
+  </object>
   <object type="LongoMatch.Gui.Component.Stats.CategoriesViewer" palette-category="General" 
allow-children="false" base-type="Gtk.Bin">
     <itemgroups />
     <signals />
@@ -149,30 +165,6 @@
     <itemgroups />
     <signals />
   </object>
-  <object type="LongoMatch.Gui.Component.TeamTemplateEditor" palette-category="General" 
allow-children="false" base-type="Gtk.Bin">
-    <itemgroups>
-      <itemgroup label="TeamTemplateEditor Properties">
-        <property name="Edited" />
-      </itemgroup>
-    </itemgroups>
-    <signals>
-      <itemgroup label="TeamTemplateEditor Signals">
-        <signal name="TemplateSaved" />
-      </itemgroup>
-    </signals>
-  </object>
-  <object type="LongoMatch.Gui.Component.Stats.SubCategoryViewer" palette-category="General" 
allow-children="false" base-type="Gtk.Bin">
-    <itemgroups />
-    <signals />
-  </object>
-  <object type="LongoMatch.Gui.Component.Timeline" palette-category="General" allow-children="false" 
base-type="Gtk.Bin">
-    <itemgroups />
-    <signals />
-  </object>
-  <object type="LongoMatch.Gui.Component.PlaysPositionViewer" palette-category="General" 
allow-children="false" base-type="Gtk.Bin">
-    <itemgroups />
-    <signals />
-  </object>
   <object type="LongoMatch.Gui.Component.AnalysisComponent" palette-category="General" 
allow-children="false" base-type="Gtk.Bin">
     <itemgroups />
     <signals />
@@ -209,6 +201,18 @@
       </itemgroup>
     </signals>
   </object>
+  <object type="LongoMatch.Gui.Component.TeamTemplateEditor" palette-category="General" 
allow-children="false" base-type="Gtk.Bin">
+    <itemgroups>
+      <itemgroup label="TeamTemplateEditor Properties">
+        <property name="Edited" />
+      </itemgroup>
+    </itemgroups>
+    <signals>
+      <itemgroup label="TeamTemplateEditor Signals">
+        <signal name="TemplateSaved" />
+      </itemgroup>
+    </signals>
+  </object>
   <object type="LongoMatch.Gui.Panel.ProjectsManagerPanel" palette-category="General" allow-children="false" 
base-type="Gtk.Bin">
     <itemgroups />
     <signals>
@@ -233,6 +237,10 @@
       </itemgroup>
     </signals>
   </object>
+  <object type="LongoMatch.Gui.Component.Timeline" palette-category="General" allow-children="false" 
base-type="Gtk.Bin">
+    <itemgroups />
+    <signals />
+  </object>
   <object type="LongoMatch.Gui.Component.CodingWidget" palette-category="General" allow-children="false" 
base-type="Gtk.Bin">
     <itemgroups>
       <itemgroup label="CodingWidget Properties">
@@ -249,6 +257,10 @@
     <itemgroups />
     <signals />
   </object>
+  <object type="LongoMatch.Gui.Component.PlaysPositionViewer" palette-category="General" 
allow-children="false" base-type="Gtk.Bin">
+    <itemgroups />
+    <signals />
+  </object>
   <object type="LongoMatch.Gui.Component.TeamsComboBox" palette-category="General" allow-children="false" 
base-type="Gtk.ComboBox">
     <itemgroups />
     <signals />
@@ -286,18 +298,6 @@
       </itemgroup>
     </signals>
   </object>
-  <object type="LongoMatch.Gui.Component.DashboardWidget" palette-category="General" allow-children="false" 
base-type="Gtk.Bin">
-    <itemgroups>
-      <itemgroup label="DashboardWidget Properties">
-        <property name="Edited" />
-      </itemgroup>
-    </itemgroups>
-    <signals>
-      <itemgroup label="DashboardWidget Signals">
-        <signal name="NewTagEvent" />
-      </itemgroup>
-    </signals>
-  </object>
   <object type="LongoMatch.Gui.Panel.PanelHeader" palette-category="General" allow-children="false" 
base-type="Gtk.Bin">
     <itemgroups />
     <signals>
@@ -348,10 +348,10 @@
     </itemgroups>
     <signals>
       <itemgroup label="VideoWindow Signals">
+        <signal name="ReadyEvent" />
         <signal name="ExposeEvent" />
         <signal name="ButtonPressEvent" />
         <signal name="ScrollEvent" />
-        <signal name="ReadyEvent" />
       </itemgroup>
     </signals>
   </object>
diff --git a/LongoMatch.Migration/LongoMatch.Migration.csproj 
b/LongoMatch.Migration/LongoMatch.Migration.csproj
index 8df3f24..a65bb88 100644
--- a/LongoMatch.Migration/LongoMatch.Migration.csproj
+++ b/LongoMatch.Migration/LongoMatch.Migration.csproj
@@ -110,11 +110,8 @@
     <Compile Include="Common\SerializableObject.cs" />
     <Compile Include="Common\ConsoleCrayon.cs" />
     <Compile Include="Common\Config.cs" />
-    <Compile Include="Tests\CateogiresTest.cs" />
     <Compile Include="Core\Templates\SubCategoryTemplate.cs" />
-    <Compile Include="Tests\TestTeam.cs" />
     <Compile Include="Converter.cs" />
-    <Compile Include="Tests\TestProject.cs" />
     <Compile Include="DataBase.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
diff --git a/LongoMatch.Services/Services/EventsManager.cs b/LongoMatch.Services/Services/EventsManager.cs
index e41be47..07da40b 100644
--- a/LongoMatch.Services/Services/EventsManager.cs
+++ b/LongoMatch.Services/Services/EventsManager.cs
@@ -27,6 +27,7 @@ using Mono.Unix;
 using System.IO;
 using LongoMatch.Core.Interfaces.Multimedia;
 using LongoMatch.Core.Store.Playlists;
+using LongoMatch.Core.Store.Templates;
 
 namespace LongoMatch.Services
 {
@@ -93,6 +94,7 @@ namespace LongoMatch.Services
                        Config.EventsBroker.DuplicateEventsEvent += OnDuplicatePlays;
                        Config.EventsBroker.SnapshotSeries += OnSnapshotSeries;
                        Config.EventsBroker.EventLoadedEvent += HandlePlayLoaded;
+                       Config.EventsBroker.PlayerSubstitutionEvent += HandlePlayerSubstitutionEvent;
                        
                        Config.EventsBroker.ShowProjectStatsEvent += HandleShowProjectStatsEvent;
                        Config.EventsBroker.TagSubcategoriesChangedEvent += 
HandleTagSubcategoriesChangedEvent;
@@ -105,6 +107,21 @@ namespace LongoMatch.Services
                        Config.EventsBroker.ShowFullScreenEvent += HandleShowFullScreenEvent;
                }
 
+               void HandlePlayerSubstitutionEvent (TeamTemplate team, Player p1, Player p2, 
SubstitutionReason reason, Time time)
+               {
+                       if (openedProject != null) {
+                               TimelineEvent evt;
+
+                               try {
+                                       evt = openedProject.SubsitutePlayer (team, p1, p2, reason, time);
+                                       analysisWindow.AddPlay (evt);
+                                       filter.Update ();
+                               } catch (SubstitutionException ex) {
+                                       guiToolkit.ErrorMessage (ex.Message);
+                               }
+                       }
+               }
+
                void HandleShowFullScreenEvent (bool fullscreen)
                {
                        guiToolkit.FullScreen = fullscreen;
diff --git a/LongoMatch.Services/Services/ProjectsManager.cs b/LongoMatch.Services/Services/ProjectsManager.cs
index 573dfa4..55858b5 100644
--- a/LongoMatch.Services/Services/ProjectsManager.cs
+++ b/LongoMatch.Services/Services/ProjectsManager.cs
@@ -358,6 +358,7 @@ namespace LongoMatch.Services
                                Log.Debug ("Importing fake live project");
                                ToolsManager.AddVideoFile (project, true);
                        }
+                       project.UpdateEventTypes ();
                        SetProject (project, ProjectType.FileProject, new CaptureSettings ());
                }
 
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a6a6751..cbbf346 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -39,6 +39,7 @@ LongoMatch.GUI/gtk-gui/LongoMatch.Gui.Dialog.PlayEditor.cs
 LongoMatch.GUI/gtk-gui/LongoMatch.Gui.Dialog.RenderingJobsDialog.cs
 LongoMatch.GUI/gtk-gui/LongoMatch.Gui.Dialog.SnapshotsDialog.cs
 LongoMatch.GUI/gtk-gui/LongoMatch.Gui.Dialog.StatsViewer.cs
+LongoMatch.GUI/gtk-gui/LongoMatch.Gui.Dialog.SubstitutionsEditor.cs
 LongoMatch.GUI/gtk-gui/LongoMatch.Gui.Dialog.UpdateDialog.cs
 LongoMatch.GUI/gtk-gui/LongoMatch.Gui.Dialog.VideoConversionTool.cs
 LongoMatch.GUI/gtk-gui/LongoMatch.Gui.Dialog.VideoEditionProperties.cs


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