[longomatch] Add support for creating action links in the dashboard manager



commit 5316cf18d77d83295871f11143e948e391fe0ba6
Author: Andoni Morales Alastruey <ylatuya gmail com>
Date:   Mon Apr 13 22:14:26 2015 +0200

    Add support for creating action links in the dashboard manager

 LongoMatch.Core/Common/Enums.cs                    |    6 +
 LongoMatch.Core/Common/ExtensionMethods.cs         |   10 +
 LongoMatch.Core/Handlers/Handlers.cs               |   11 +-
 LongoMatch.Core/Interfaces/Drawing/ICanvas.cs      |   16 +-
 LongoMatch.Core/Store/ActionLink.cs                |    6 +
 LongoMatch.Core/Store/Templates/Dashboard.cs       |    1 +
 LongoMatch.Drawing/Canvas.cs                       |   26 ++-
 LongoMatch.Drawing/CanvasObjects/ButtonObject.cs   |   12 +-
 .../CanvasObjects/Dashboard/ActionLinkObject.cs    |  125 ++++++++++
 .../CanvasObjects/Dashboard/CardObject.cs          |    3 +
 .../CanvasObjects/Dashboard/CategoryObject.cs      |  104 +++++++-
 .../Dashboard/DashboardButtonObject.cs             |   54 ++++-
 .../CanvasObjects/Dashboard/LinkAnchorObject.cs    |  105 ++++++++
 .../CanvasObjects/Dashboard/TimerObject.cs         |    2 +-
 LongoMatch.Drawing/LongoMatch.Drawing.csproj       |    2 +
 LongoMatch.Drawing/Makefile.am                     |    2 +
 LongoMatch.Drawing/Widgets/DashboardCanvas.cs      |  249 ++++++++++++++++----
 LongoMatch.GUI/Gui/Component/CodingWidget.cs       |    4 +-
 LongoMatch.GUI/Gui/Component/DashboardWidget.cs    |  107 ++++++---
 LongoMatch.GUI/Gui/Panel/SportsTemplatesPanel.cs   |    2 +-
 Tests/Core/Store/TestActionLink.cs                 |    2 +-
 21 files changed, 720 insertions(+), 129 deletions(-)
---
diff --git a/LongoMatch.Core/Common/Enums.cs b/LongoMatch.Core/Common/Enums.cs
index 94434a1..eecd1a7 100644
--- a/LongoMatch.Core/Common/Enums.cs
+++ b/LongoMatch.Core/Common/Enums.cs
@@ -440,4 +440,10 @@ namespace LongoMatch.Core.Common
                Keep,
                Invert
        }
+
+       public enum DashboardMode
+       {
+               Code,
+               Edit,
+       }
 }
diff --git a/LongoMatch.Core/Common/ExtensionMethods.cs b/LongoMatch.Core/Common/ExtensionMethods.cs
index 888225d..9189fcf 100644
--- a/LongoMatch.Core/Common/ExtensionMethods.cs
+++ b/LongoMatch.Core/Common/ExtensionMethods.cs
@@ -49,6 +49,16 @@ namespace LongoMatch.Core.Common
                        return dict.SingleOrDefault (x => x.Value.Equals (value)).Key;
                }
 
+               public static void  RemoveKeysByValue<TKey, TValue> (this Dictionary<TKey, TValue> dict, 
TValue value)
+               {
+                       foreach (var item in dict.Where(k => k.Value.Equals(value)).ToList()) {
+                               try {
+                                       dict.Remove (item.Key);
+                               } catch {
+                               }
+                       }
+               }
+
                public static bool SequenceEqualSafe<T> (this List<T> first, List<T> second)
                {
                        if (first == null && second == null) {
diff --git a/LongoMatch.Core/Handlers/Handlers.cs b/LongoMatch.Core/Handlers/Handlers.cs
index 8b9c3d0..da979bc 100644
--- a/LongoMatch.Core/Handlers/Handlers.cs
+++ b/LongoMatch.Core/Handlers/Handlers.cs
@@ -58,10 +58,15 @@ namespace LongoMatch.Core.Handlers
        public delegate void DashboardEditedHandler ();
        
        /* Dashboard buttons selected */
-       public delegate void ButtonsSelectedHandlers (List<DashboardButton> taggerbuttons);
+       public delegate void ButtonsSelectedHandler (List<DashboardButton> taggerbuttons);
        public delegate void ButtonSelectedHandler (DashboardButton taggerbutton);
-       /* Show dashborad menu */
-       public delegate void ShowButtonsTaggerMenuHandler (DashboardButton taggerbutton,Tag tag);
+
+       /* Dashboard link selected */
+       public delegate void ActionLinksSelectedHandler (List<ActionLink> actionLink);
+
+       /* Show dashboard menu */
+       public delegate void ShowDashboardMenuHandler (List<DashboardButton> selectedButtons,List<ActionLink> 
selectedLinks);
+
        /* The players tagged in an event have changed */
        public delegate void TeamsTagsChangedHandler ();
        /* Project Events */
diff --git a/LongoMatch.Core/Interfaces/Drawing/ICanvas.cs b/LongoMatch.Core/Interfaces/Drawing/ICanvas.cs
index 606232e..160f60f 100644
--- a/LongoMatch.Core/Interfaces/Drawing/ICanvas.cs
+++ b/LongoMatch.Core/Interfaces/Drawing/ICanvas.cs
@@ -25,25 +25,29 @@ namespace LongoMatch.Core.Interfaces.Drawing
 
        public interface ICanvas: IDisposable
        {
-                void Draw (IContext context, Area area);
+               void Draw (IContext context, Area area);
        }
-       
+
        public interface ICanvasObject: IDisposable
        {
                event CanvasHandler ClickedEvent;
                event RedrawHandler RedrawEvent;
 
                void Draw (IDrawingToolkit tk, Area area);
-               bool Visible {set; get;}
-               string Description {set; get;}
+
+               bool Visible { set; get; }
+
+               string Description { set; get; }
+
                void ClickPressed (Point p, ButtonModifier modif);
+
                void ClickReleased ();
        }
-       
+
        public interface ICanvasSelectableObject: ICanvasObject, IMovableObject
        {
        }
-       
+
        public interface ICanvasDrawableObject: ICanvasSelectableObject
        {
                IBlackboardObject IDrawableObject {
diff --git a/LongoMatch.Core/Store/ActionLink.cs b/LongoMatch.Core/Store/ActionLink.cs
index 5233401..600f0e8 100644
--- a/LongoMatch.Core/Store/ActionLink.cs
+++ b/LongoMatch.Core/Store/ActionLink.cs
@@ -141,6 +141,12 @@ namespace LongoMatch.Core.Store
                {
                        return !(l1 == l2);
                }
+
+               public override string ToString ()
+               {
+                       return string.Format ("{0}->{1}", SourceButton != null ? SourceButton.Name : "empty",
+                               DestinationButton != null ? DestinationButton.Name : "empty");
+               }
        }
 }
 
diff --git a/LongoMatch.Core/Store/Templates/Dashboard.cs b/LongoMatch.Core/Store/Templates/Dashboard.cs
index 1c9b10f..a206ada 100644
--- a/LongoMatch.Core/Store/Templates/Dashboard.cs
+++ b/LongoMatch.Core/Store/Templates/Dashboard.cs
@@ -296,6 +296,7 @@ namespace LongoMatch.Core.Store.Templates
                        Dashboard template = new Dashboard ();
                        
                        template.FillDefaultTemplate (count);
+
                        periods.Add ("1");
                        periods.Add ("2");
                        template.GamePeriods = periods; 
diff --git a/LongoMatch.Drawing/Canvas.cs b/LongoMatch.Drawing/Canvas.cs
index 3d44b7c..8476c39 100644
--- a/LongoMatch.Drawing/Canvas.cs
+++ b/LongoMatch.Drawing/Canvas.cs
@@ -23,11 +23,12 @@ using LongoMatch.Core.Interfaces;
 using LongoMatch.Core.Common;
 using LongoMatch.Core.Store.Drawables;
 using LongoMatch.Drawing.CanvasObjects;
+using LongoMatch.Drawing.CanvasObjects.Dashboard;
 
 namespace LongoMatch.Drawing
 {
        /// <summary>
-       /// A canvas stores <see cref="ICanvasObjects"/>'s and draws them.
+       /// A canvas stores <see cref="ICanvasObject"/>'s and draws them.
        /// </summary>
        public class Canvas: ICanvas
        {
@@ -114,6 +115,7 @@ namespace LongoMatch.Drawing
                {
                        co.RedrawEvent -= HandleRedrawEvent;
                        Objects.Remove (co);
+                       co.Dispose ();
                }
 
                /// <summary>
@@ -382,6 +384,9 @@ namespace LongoMatch.Drawing
                {
                }
 
+               /// <summary>
+               /// Reset the list of select objects
+               /// </summary>
                public void ClearSelection ()
                {
                        foreach (Selection sel in Selections) {
@@ -439,20 +444,27 @@ namespace LongoMatch.Drawing
                        }
                }
 
-               Selection GetSelection (Point coords, bool inMotion = false)
+               protected virtual Selection GetSelection (Point coords, bool inMotion = false, bool 
skipSelected = false)
                {
                        Selection sel = null;
+                       Selection selected = null;
 
-                       /* Try with the selected item first */
                        if (Selections.Count > 0) {
-                               sel = Selections.LastOrDefault ().Drawable.GetSelection (coords, Accuracy, 
inMotion);
+                               selected = Selections.LastOrDefault ();
+                               /* Try with the selected item first */
+                               if (!skipSelected)
+                                       sel = selected.Drawable.GetSelection (coords, Accuracy, inMotion);
                        }
+
+                       /* Iterate over all the objects now */
                        if (sel == null) {
                                foreach (ICanvasSelectableObject co in Objects) {
                                        sel = co.GetSelection (coords, Accuracy, inMotion);
-                                       if (sel != null) {
-                                               break;
-                                       }
+                                       if (sel == null)
+                                               continue;
+                                       if (skipSelected && selected != null && sel.Drawable == 
selected.Drawable)
+                                               continue;
+                                       break;
                                }
                        }
                        return sel;
diff --git a/LongoMatch.Drawing/CanvasObjects/ButtonObject.cs 
b/LongoMatch.Drawing/CanvasObjects/ButtonObject.cs
index e04fc8a..6189588 100644
--- a/LongoMatch.Drawing/CanvasObjects/ButtonObject.cs
+++ b/LongoMatch.Drawing/CanvasObjects/ButtonObject.cs
@@ -104,6 +104,11 @@ namespace LongoMatch.Drawing.CanvasObjects
                        set;
                }
 
+               public virtual bool DrawsSelectionArea {
+                       get {
+                               return true;
+                       }
+               }
 
                public virtual Area Area {
                        get {
@@ -112,11 +117,6 @@ namespace LongoMatch.Drawing.CanvasObjects
                        }
                }
 
-               public TagMode Mode {
-                       get;
-                       set;
-               }
-
                public override void ReDraw ()
                {
                        ResetBackbuffer ();
@@ -187,7 +187,7 @@ namespace LongoMatch.Drawing.CanvasObjects
 
                protected void DrawSelectionArea (IDrawingToolkit tk)
                {
-                       if (!Selected || Mode != TagMode.Edit) {
+                       if (!Selected || !DrawsSelectionArea) {
                                return;
                        }
                        tk.StrokeColor = Constants.SELECTION_INDICATOR_COLOR;
diff --git a/LongoMatch.Drawing/CanvasObjects/Dashboard/ActionLinkObject.cs 
b/LongoMatch.Drawing/CanvasObjects/Dashboard/ActionLinkObject.cs
new file mode 100644
index 0000000..834db23
--- /dev/null
+++ b/LongoMatch.Drawing/CanvasObjects/Dashboard/ActionLinkObject.cs
@@ -0,0 +1,125 @@
+//
+//  Copyright (C) 2015 Fluendo S.A.
+//
+//  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 LongoMatch.Core.Common;
+using LongoMatch.Core.Handlers.Drawing;
+using LongoMatch.Core.Interfaces.Drawing;
+using LongoMatch.Core.Store;
+using LongoMatch.Core.Store.Drawables;
+
+namespace LongoMatch.Drawing.CanvasObjects.Dashboard
+{
+       public class ActionLinkObject: CanvasObject, ICanvasSelectableObject
+       {
+               Line line;
+               Point stop;
+               int selectionSize = 4;
+
+               public ActionLinkObject (LinkAnchorObject source,
+                                        LinkAnchorObject destination,
+                                        ActionLink link)
+               {
+                       Link = link;
+                       Source = source;
+                       Destination = destination;
+                       if (destination == null) {
+                               stop = source.Position;
+                       } else {
+                               stop = destination.Position;
+                       }
+                       line = new Line ();
+                       line.Start = source.Position;
+                       line.Stop = stop;
+               }
+
+               public LinkAnchorObject Source {
+                       get;
+                       set;
+               }
+
+               public ActionLink Link {
+                       get;
+                       set;
+               }
+
+               public LinkAnchorObject Destination {
+                       get;
+                       set;
+               }
+
+               public virtual Area Area {
+                       get {
+                               line.Start = Source.Position;
+                               if (Destination != null) {
+                                       line.Stop = Destination.Position;
+                               } else {
+                                       line.Stop = this.stop;
+                               }
+                               Area area = line.Area;
+                               area.Start.X -= selectionSize + 2;
+                               area.Start.Y -= selectionSize + 2;
+                               area.Width += selectionSize * 2 + 4;
+                               area.Height += selectionSize * 2 + 4;
+                               return area;
+                       }
+               }
+
+               public Selection GetSelection (Point point, double precision, bool inMotion = false)
+               {
+                       Selection sel = line.GetSelection (point, precision, inMotion);
+                       if (sel != null) {
+                               sel.Drawable = this;
+                       }
+                       return sel;
+               }
+
+               public void Move (Selection s, Point dst, Point start)
+               {
+                       line.Move (s, dst, start);
+                       stop = line.Stop;
+               }
+
+               public override void Draw (IDrawingToolkit tk, Area area)
+               {
+                       Color lineColor;
+                       int lineWidth = 4;
+
+                       if (!UpdateDrawArea (tk, area, Area)) {
+                               return;
+                       }
+
+                       if (Highlighted) {
+                               lineColor = Config.Style.PaletteActive;
+                       } else if (Selected) {
+                               lineColor = Config.Style.PaletteSelected;
+                       } else {
+                               lineColor = Color.Yellow;
+                       }
+
+                       tk.Begin ();
+                       tk.FillColor = lineColor;
+                       tk.StrokeColor = lineColor;
+                       tk.LineWidth = lineWidth;
+                       tk.LineStyle = LineStyle.Normal;
+                       tk.DrawLine (line.Start, line.Stop);
+                       tk.DrawArrow (line.Start, line.Stop, 2, 0.3, true);
+                       tk.End ();
+               }
+       }
+}
+
diff --git a/LongoMatch.Drawing/CanvasObjects/Dashboard/CardObject.cs 
b/LongoMatch.Drawing/CanvasObjects/Dashboard/CardObject.cs
index cc7e5b5..a06e241 100644
--- a/LongoMatch.Drawing/CanvasObjects/Dashboard/CardObject.cs
+++ b/LongoMatch.Drawing/CanvasObjects/Dashboard/CardObject.cs
@@ -78,6 +78,9 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                                tk.DrawText (Position, Button.Width, Button.Height, Button.PenaltyCard.Name);
                        }
                        DrawSelectionArea (tk);
+                       if (ShowLinks) {
+                               GetAnchor (null).Draw (tk, area);
+                       }
                        tk.End ();
                }
        }
diff --git a/LongoMatch.Drawing/CanvasObjects/Dashboard/CategoryObject.cs 
b/LongoMatch.Drawing/CanvasObjects/Dashboard/CategoryObject.cs
index 261f52e..ae0fc04 100644
--- a/LongoMatch.Drawing/CanvasObjects/Dashboard/CategoryObject.cs
+++ b/LongoMatch.Drawing/CanvasObjects/Dashboard/CategoryObject.cs
@@ -16,6 +16,7 @@
 //  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 //
 using System;
+using System.Linq;
 using System.Collections.Generic;
 using System.IO;
 using LongoMatch.Core.Common;
@@ -47,6 +48,7 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                object applyButton = new object ();
                Rectangle editRect, cancelRect, applyRect;
                double catWidth, heightPerRow;
+               Dictionary <Tag, LinkAnchorObject> subcatAnchors, cachedAnchors;
 
                public CategoryObject (AnalysisEventButton category) : base (category)
                {
@@ -79,6 +81,10 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                        }
                        MinWidth = 100;
                        MinHeight = HeaderHeight * 2;
+                       subcatAnchors = new Dictionary<Tag, LinkAnchorObject> ();
+                       foreach (Tag tag in category.AnalysisEventType.Tags) {
+                               AddSubcatAnchor (tag, new Point (0, 0));
+                       }
                }
 
                protected override void Dispose (bool disposing)
@@ -87,6 +93,9 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                                timer.Dispose ();
                                timer = null;
                        }
+                       foreach (LinkAnchorObject anchor in subcatAnchors.Values.ToList()) {
+                               RemoveAnchor (anchor);
+                       }
                        base.Dispose (disposing);
                }
 
@@ -111,7 +120,7 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                        get {
                                return ShowTags && tagsByGroup.Count > 1
                                && Button.TagMode == TagMode.Predefined
-                               && Mode != TagMode.Edit;
+                               && Mode != DashboardMode.Edit;
                        }
                }
 
@@ -175,6 +184,12 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                        SelectedTags.Clear ();
                }
 
+               void RemoveAnchor (LinkAnchorObject anchor)
+               {
+                       anchor.Dispose ();
+                       subcatAnchors.RemoveKeysByValue (anchor);
+               }
+
                void EmitCreateEvent ()
                {
                        EmitClickEvent ();
@@ -190,7 +205,7 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
 
                void DelayTagClicked ()
                {
-                       if (tagsByGroup.Keys.Count == 1 || Mode == TagMode.Edit) {
+                       if (tagsByGroup.Keys.Count == 1 || Mode == DashboardMode.Edit) {
                                TimerCallback (null);
                                return;
                        }
@@ -242,6 +257,22 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                        set;
                }
 
+               void AddSubcatAnchor (Tag tag, Point point)
+               {
+                       LinkAnchorObject anchor;
+
+                       if (subcatAnchors.ContainsKey (tag)) {
+                               anchor = subcatAnchors [tag];
+                               anchor.RelativePosition = point;
+                       } else {
+                               anchor = new LinkAnchorObject (this, new List<Tag> { tag }, point);
+                               anchor.RedrawEvent += (co, area) => {
+                                       EmitRedrawEvent (anchor, area);
+                               };
+                               subcatAnchors.Add (tag, anchor);
+                       }
+               }
+
                bool CheckRect (Point p, Rectangle rect, object obj)
                {
                        Selection subsel;
@@ -267,9 +298,32 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                        return false;
                }
 
+               public override Selection GetSelection (Point p, double precision, bool inMotion = false)
+               {
+                       Selection sel = anchor.GetSelection (p, precision, inMotion);
+                       if (sel != null)
+                               return sel;
+                       foreach (LinkAnchorObject subcatAnchor in subcatAnchors.Values) {
+                               sel = subcatAnchor.GetSelection (p, precision, inMotion);
+                               if (sel != null)
+                                       return sel;
+                       }
+                       return base.GetSelection (p, precision, inMotion);
+               }
+
+               public override LinkAnchorObject GetAnchor (List<Tag> sourceTags)
+               {
+                       /* Only one tag is supported for now */
+                       if (sourceTags == null || sourceTags.Count == 0) {
+                               return base.GetAnchor (sourceTags);
+                       } else {
+                               return subcatAnchors [sourceTags [0]];
+                       }
+               }
+
                public override void ClickPressed (Point p, ButtonModifier modif)
                {
-                       if (Mode == TagMode.Edit) {
+                       if (Mode == DashboardMode.Edit) {
                                editClicked = CheckRect (p, editRect, editbutton);
                                return;
                        }
@@ -330,6 +384,12 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                                col = i % tagsPerRow;
                                pos = new Point (start.X + col * rowwidth,
                                        start.Y + yptr + row * heightPerRow);
+                               tag = tags [i];
+
+                               AddSubcatAnchor (tag, new Point (pos.X - Position.X, pos.Y - Position.Y));
+                               if (!Button.ShowSubcategories) {
+                                       continue;
+                               }
 
                                tk.StrokeColor = Button.DarkColor;
                                tk.LineWidth = 1;
@@ -357,7 +417,6 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                                        tk.LineStyle = LineStyle.Normal;
                                }
                                tk.StrokeColor = Button.TextColor;
-                               tag = tags [i];
                                tk.DrawText (pos, rowwidth, heightPerRow, tag.Value);
                                rects.Add (new Rectangle (pos, rowwidth, heightPerRow), tag);
                        }
@@ -412,7 +471,7 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                        Color c;
                        double width, height;
 
-                       if (Mode != TagMode.Edit) {
+                       if (Mode != DashboardMode.Edit || ShowLinks || !Button.ShowSubcategories) {
                                return;
                        }
 
@@ -436,7 +495,7 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
 
                void DrawSelectedTags (IDrawingToolkit tk)
                {
-                       if (Mode == TagMode.Edit) {
+                       if (Mode == DashboardMode.Edit) {
                                return;
                        }
                        foreach (Rectangle r in rects.Keys) {
@@ -456,7 +515,7 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
 
                void DrawRecordTime (IDrawingToolkit tk)
                {
-                       if (Recording && Mode != TagMode.Edit) {
+                       if (Recording && Mode != DashboardMode.Edit) {
                                if (ShowTags) {
                                        tk.FontSize = 12;
                                        tk.FontWeight = FontWeight.Normal;
@@ -536,6 +595,16 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                        }
                }
 
+               void DrawAnchors (IDrawingToolkit tk)
+               {
+                       if (!ShowLinks)
+                               return;
+                       DrawAnchor (tk, null);
+                       foreach (LinkAnchorObject anchor in subcatAnchors.Values) {
+                               anchor.Draw (tk, null);
+                       }
+               }
+
                new void DrawButton (IDrawingToolkit tk)
                {
                        if (!ShowTags) {
@@ -564,6 +633,7 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                void DrawBackbuffer (IDrawingToolkit tk)
                {
                        Point pos;
+                       double yptr = 0;
 
                        rects.Clear ();
                        buttonsRects.Clear ();
@@ -584,13 +654,22 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                        DrawHeader (tk);
                        DrawRecordButton (tk);
 
-                       if (Button.ShowSubcategories) {
-                               double yptr = 0;
-                               foreach (List<Tag> tags in tagsByGroup.Values) {
-                                       DrawTagsGroup (tk, tags, ref yptr);
+                       foreach (List<Tag> tags in tagsByGroup.Values) {
+                               DrawTagsGroup (tk, tags, ref yptr);
+                       }
+
+                       /* Remove anchor object that where not reused
+                                * eg: after removinga a subcategory tag */
+                       foreach (Tag tag in subcatAnchors.Keys.ToList ()) {
+                               if (!Button.AnalysisEventType.Tags.Contains (tag)) {
+                                       RemoveAnchor (subcatAnchors [tag]);
                                }
                        }
-                       DrawEditButton (tk);
+                       if (!ShowLinks) {
+                               DrawEditButton (tk);
+                       }
+                       if (Button.ShowSubcategories) {
+                       }
                        tk.End ();
                }
 
@@ -637,6 +716,7 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                        DrawRecordTime (tk);
                        DrawApplyButton (tk);
                        DrawSelectionArea (tk);
+                       DrawAnchors (tk);
                        tk.End ();
                }
        }
diff --git a/LongoMatch.Drawing/CanvasObjects/Dashboard/DashboardButtonObject.cs 
b/LongoMatch.Drawing/CanvasObjects/Dashboard/DashboardButtonObject.cs
index 4d94ace..0eb4333 100644
--- a/LongoMatch.Drawing/CanvasObjects/Dashboard/DashboardButtonObject.cs
+++ b/LongoMatch.Drawing/CanvasObjects/Dashboard/DashboardButtonObject.cs
@@ -20,15 +20,31 @@ using System.Collections.Generic;
 using LongoMatch.Core.Common;
 using LongoMatch.Core.Interfaces.Drawing;
 using LongoMatch.Core.Store;
+using LongoMatch.Core.Store.Drawables;
 
 namespace LongoMatch.Drawing.CanvasObjects.Dashboard
 {
        public class DashboardButtonObject: ButtonObject, ICanvasSelectableObject
        {
+               protected LinkAnchorObject anchor;
 
                public DashboardButtonObject (DashboardButton tagger)
                {
                        Button = tagger;
+                       anchor = new LinkAnchorObject (this, null, new Point (5, 5));
+                       anchor.RedrawEvent += (co, area) => {
+                               EmitRedrawEvent (anchor, area);
+                       };
+               }
+
+               public DashboardMode Mode {
+                       get;
+                       set;
+               }
+
+               public bool ShowLinks {
+                       get;
+                       set;
                }
 
                public DashboardButton Button {
@@ -36,6 +52,17 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                        set;
                }
 
+               public bool EditActionLinks {
+                       get;
+                       set;
+               }
+
+               public override bool DrawsSelectionArea {
+                       get {
+                               return Mode == DashboardMode.Edit;
+                       }
+               }
+
                public override Point Position {
                        get {
                                return Button.Position;
@@ -109,7 +136,7 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                                return base.Active;
                        }
                        set {
-                               if (Mode != TagMode.Edit) {
+                               if (Mode != DashboardMode.Edit) {
                                        base.Active = value;
                                }
                        }
@@ -121,9 +148,30 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
                        }
                }
 
-               public virtual Point GetLinkAnchor (List<Tag> tag)
+               public virtual LinkAnchorObject GetAnchor (List<Tag> sourceTags)
+               {
+                       return anchor;
+               }
+
+               protected void DrawAnchor (IDrawingToolkit tk, Area area)
+               {
+                       if (ShowLinks) {
+                               anchor.Draw (tk, area);
+                       }
+               }
+
+               public override void Draw (IDrawingToolkit tk, Area area)
+               {
+                       base.Draw (tk, area);
+                       DrawAnchor (tk, area);
+               }
+
+               public override Selection GetSelection (Point p, double precision, bool inMotion = false)
                {
-                       return new Point (Position.X + 5, Position.Y + 5);
+                       Selection sel = anchor.GetSelection (p, precision, inMotion);
+                       if (sel != null)
+                               return sel;
+                       return base.GetSelection (p, precision, inMotion);
                }
        }
 
diff --git a/LongoMatch.Drawing/CanvasObjects/Dashboard/LinkAnchorObject.cs 
b/LongoMatch.Drawing/CanvasObjects/Dashboard/LinkAnchorObject.cs
new file mode 100644
index 0000000..82d39f0
--- /dev/null
+++ b/LongoMatch.Drawing/CanvasObjects/Dashboard/LinkAnchorObject.cs
@@ -0,0 +1,105 @@
+//
+//  Copyright (C) 2015 Fluendo S.A.
+//
+//  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.Collections.Generic;
+using LongoMatch.Core.Common;
+using LongoMatch.Core.Interfaces.Drawing;
+using LongoMatch.Core.Store;
+using LongoMatch.Core.Store.Drawables;
+
+namespace LongoMatch.Drawing.CanvasObjects.Dashboard
+{
+       public class LinkAnchorObject: CanvasObject, ICanvasSelectableObject
+       {
+
+               public LinkAnchorObject (DashboardButtonObject button, List<Tag> tags, Point relPos)
+               {
+                       RelativePosition = relPos;
+                       Button = button;
+                       if (tags == null)
+                               tags = new List<Tag> ();
+                       Tags = tags;
+               }
+
+               public DashboardButtonObject Button {
+                       get;
+                       set;
+               }
+
+               public Point RelativePosition {
+                       get;
+                       set;
+               }
+
+               public List<Tag> Tags {
+                       get;
+                       set;
+               }
+
+               public Point Position {
+                       get {
+                               return Button.Position + RelativePosition;
+                       }
+               }
+
+               public bool CanLink (LinkAnchorObject anchor)
+               {
+                       if (anchor == null)
+                               return false;
+                       else if (this == anchor)
+                               return false;
+                       else if (Button is TimerObject && anchor.Button is TimerObject)
+                               return true;
+                       else if (Button is TagObject && anchor.Button is TagObject)
+                               return true;
+                       else if (Button.Button is EventButton && anchor.Button.Button is EventButton)
+                               return true;
+                       return false;
+               }
+
+               public Selection GetSelection (Point point, double precision, bool inMotion = false)
+               {
+                       Point pos = Position;
+
+                       if (Math.Abs (pos.X - point.X) < 2 && Math.Abs (pos.Y - point.Y) < 2) {
+                               return new Selection (this, SelectionPosition.All, 0);
+                       }
+                       return null;
+               }
+
+               public void Move (Selection s, Point dst, Point start)
+               {
+               }
+
+               public override void Draw (IDrawingToolkit tk, Area area)
+               {
+                       Color color = Color.Red1;
+                       if (Highlighted) {
+                               color = Config.Style.PaletteActive;
+                       }
+
+                       tk.Begin ();
+                       tk.LineWidth = 2;
+                       tk.FillColor = color;
+                       tk.StrokeColor = color;
+                       tk.DrawCircle (Position, 2);
+                       tk.End ();
+               }
+       }
+}
+
diff --git a/LongoMatch.Drawing/CanvasObjects/Dashboard/TimerObject.cs 
b/LongoMatch.Drawing/CanvasObjects/Dashboard/TimerObject.cs
index 9903f2c..9dcecca 100644
--- a/LongoMatch.Drawing/CanvasObjects/Dashboard/TimerObject.cs
+++ b/LongoMatch.Drawing/CanvasObjects/Dashboard/TimerObject.cs
@@ -127,7 +127,7 @@ namespace LongoMatch.Drawing.CanvasObjects.Dashboard
 
                        tk.Begin ();
                        
-                       if (Active && Mode != TagMode.Edit) {
+                       if (Active && Mode != DashboardMode.Edit) {
                                tk.LineWidth = StyleConf.ButtonLineWidth;
                                tk.StrokeColor = Button.BackgroundColor;
                                tk.FillColor = Button.BackgroundColor;
diff --git a/LongoMatch.Drawing/LongoMatch.Drawing.csproj b/LongoMatch.Drawing/LongoMatch.Drawing.csproj
index 5ba4f0a..2393896 100644
--- a/LongoMatch.Drawing/LongoMatch.Drawing.csproj
+++ b/LongoMatch.Drawing/LongoMatch.Drawing.csproj
@@ -57,6 +57,7 @@
     <Compile Include="CanvasObjects\Dashboard\CardObject.cs" />
     <Compile Include="CanvasObjects\Dashboard\DashboardButtonObject.cs" />
     <Compile Include="CanvasObjects\Dashboard\CategoryObject.cs" />
+    <Compile Include="CanvasObjects\Dashboard\ActionLinkObject.cs" />
     <Compile Include="CanvasObjects\Dashboard\ScoreObject.cs" />
     <Compile Include="CanvasObjects\Dashboard\TagObject.cs" />
     <Compile Include="CanvasObjects\Dashboard\TimerObject.cs" />
@@ -77,6 +78,7 @@
     <Compile Include="CanvasObjects\Teams\PlayerObject.cs" />
     <Compile Include="CanvasObjects\Teams\PlayersTaggerObject.cs" />
     <Compile Include="CanvasObjects\Teams\FieldObject.cs" />
+    <Compile Include="CanvasObjects\Dashboard\LinkAnchorObject.cs" />
   </ItemGroup>
   <ItemGroup>
     <Reference Include="System" />
diff --git a/LongoMatch.Drawing/Makefile.am b/LongoMatch.Drawing/Makefile.am
index 84cfb7f..294338e 100644
--- a/LongoMatch.Drawing/Makefile.am
+++ b/LongoMatch.Drawing/Makefile.am
@@ -15,9 +15,11 @@ SOURCES = ../AssemblyInfo/AssemblyInfo.cs \
        CanvasObjects/Blackboard/TextObject.cs \
        CanvasObjects/ButtonObject.cs \
        CanvasObjects/CanvasObject.cs \
+       CanvasObjects/Dashboard/ActionLinkObject.cs \
        CanvasObjects/Dashboard/CardObject.cs \
        CanvasObjects/Dashboard/CategoryObject.cs \
        CanvasObjects/Dashboard/DashboardButtonObject.cs \
+       CanvasObjects/Dashboard/LinkAnchorObject.cs \
        CanvasObjects/Dashboard/ScoreObject.cs \
        CanvasObjects/Dashboard/TagObject.cs \
        CanvasObjects/Dashboard/TimerObject.cs \
diff --git a/LongoMatch.Drawing/Widgets/DashboardCanvas.cs b/LongoMatch.Drawing/Widgets/DashboardCanvas.cs
index 88c62c5..a750ef3 100644
--- a/LongoMatch.Drawing/Widgets/DashboardCanvas.cs
+++ b/LongoMatch.Drawing/Widgets/DashboardCanvas.cs
@@ -25,32 +25,38 @@ using LongoMatch.Core.Store;
 using LongoMatch.Core.Store.Drawables;
 using LongoMatch.Core.Store.Templates;
 using LongoMatch.Drawing.CanvasObjects.Dashboard;
+using LongoMatch.Drawing.CanvasObjects;
 
 namespace LongoMatch.Drawing.Widgets
 {
        public class DashboardCanvas: SelectionCanvas
        {
        
-               public event ButtonsSelectedHandlers TaggersSelectedEvent;
+               public event ButtonsSelectedHandler ButtonsSelectedEvent;
                public event ButtonSelectedHandler EditButtonTagsEvent;
-               public event ShowButtonsTaggerMenuHandler ShowMenuEvent;
+               public event ActionLinksSelectedHandler ActionLinksSelectedEvent;
+               public event ShowDashboardMenuHandler ShowMenuEvent;
                public event NewEventHandler NewTagEvent;
 
                Dashboard template;
-               TagMode tagMode;
+               DashboardMode mode;
                Time currentTime;
                int templateWidth, templateHeight;
                FitMode fitMode;
-               bool modeChanged;
+               bool modeChanged, showLinks;
+               ActionLinkObject movingLink;
+               LinkAnchorObject destAnchor;
+               Dictionary<DashboardButton, DashboardButtonObject> buttonsDict;
 
                public DashboardCanvas (IWidget widget) : base (widget)
                {
                        Accuracy = 5;
-                       TagMode = TagMode.Edit;
+                       Mode = DashboardMode.Edit;
                        widget.SizeChangedEvent += SizeChanged;
                        FitMode = FitMode.Fit;
                        CurrentTime = new Time (0);
                        AddTag = new Tag ("", "");
+                       buttonsDict = new Dictionary<DashboardButton, DashboardButtonObject> ();
                }
 
                public Project Project {
@@ -95,18 +101,37 @@ namespace LongoMatch.Drawing.Widgets
                        }
                }
 
-               public TagMode TagMode {
+               public DashboardMode Mode {
                        set {
                                modeChanged = true;
-                               tagMode = value;
-                               ObjectsCanMove = tagMode == TagMode.Edit;
-                               foreach (DashboardButtonObject to in Objects) {
+                               mode = value;
+                               ObjectsCanMove = mode == DashboardMode.Edit;
+                               foreach (DashboardButtonObject to in Objects.OfType<DashboardButtonObject> 
()) {
                                        to.Mode = value;
                                }
                                ClearSelection ();
                        }
                        get {
-                               return tagMode;
+                               return mode;
+                       }
+               }
+
+               public bool ShowLinks {
+                       set {
+                               showLinks = value;
+                               foreach (DashboardButtonObject to in Objects.OfType<DashboardButtonObject> 
()) {
+                                       to.ShowLinks = showLinks;
+                                       to.ResetDrawArea ();
+                               }
+                               foreach (ActionLinkObject ao in Objects.OfType<ActionLinkObject> ()) {
+                                       ao.Visible = showLinks;
+                                       ao.ResetDrawArea ();
+                               }
+                               ClearSelection ();
+                               widget.ReDraw ();
+                       }
+                       get {
+                               return showLinks;
                        }
                }
 
@@ -148,7 +173,8 @@ namespace LongoMatch.Drawing.Widgets
                        }
                        
                        LoadTemplate ();
-                       to = (DashboardButtonObject)Objects.FirstOrDefault (o => (o as 
DashboardButtonObject).Button == b);
+                       to = Objects.OfType<DashboardButtonObject> ().
+                               FirstOrDefault (o => o.Button == b);
                        if (to != null) {
                                UpdateSelection (new Selection (to, SelectionPosition.All, 0));
                        }
@@ -156,52 +182,153 @@ namespace LongoMatch.Drawing.Widgets
 
                protected override void ShowMenu (Point coords)
                {
-                       Selection sel;
-                       if (ShowMenuEvent == null)
+                       List<DashboardButton> buttons;
+                       List<ActionLink> links;
+
+                       if (ShowMenuEvent == null || Selections.Count == 0)
                                return;
-                       
-                       sel = Selections.LastOrDefault ();
-                       if (sel != null) {
-                               DashboardButtonObject to = sel.Drawable as DashboardButtonObject;
-                               ShowMenuEvent (to.Button, null);
+
+                       buttons = Selections.Where (s => s.Drawable is DashboardButtonObject).
+                               Select (s => (s.Drawable as DashboardButtonObject).Button).ToList ();
+                       links = Selections.Where (s => s.Drawable is ActionLinkObject).
+                               Select (s => (s.Drawable as ActionLinkObject).Link).ToList ();
+                       ShowMenuEvent (buttons, links);
+               }
+
+               protected override Selection GetSelection (Point coords, bool inMotion = false, bool 
skipSelected = false)
+               {
+                       Selection sel = null;
+                       Selection selected = null;
+
+                       /* Regular GetSelection */
+                       if (!ShowLinks)
+                               return base.GetSelection (coords, inMotion, skipSelected);
+
+                       /* With ShowLinks, only links and anchor can be selected */
+                       if (Selections.Count > 0) {
+                               selected = Selections.LastOrDefault ();
                        }
+
+                       foreach (ICanvasSelectableObject co in Objects) {
+                               sel = co.GetSelection (coords, Accuracy, inMotion);
+                               if (sel == null || sel.Drawable is DashboardButtonObject)
+                                       continue;
+                               if (skipSelected && selected != null && sel.Drawable == selected.Drawable)
+                                       continue;
+                               break;
+                       }
+                       return sel;
                }
 
                protected override void SelectionMoved (Selection sel)
                {
-                       SizeChanged ();
-                       Edited = true;
+                       if (sel.Drawable is DashboardButtonObject) {
+                               SizeChanged ();
+                               Edited = true;
+                       } else if (sel.Drawable is ActionLinkObject) {
+                               ActionLinkObject link = sel.Drawable as ActionLinkObject;
+                               LinkAnchorObject anchor = null;
+                               Selection destSel;
+
+                               destSel = GetSelection (start, true, true);
+                               if (destSel != null && destSel.Drawable is LinkAnchorObject) { 
+                                       anchor = destSel.Drawable as LinkAnchorObject;
+                               }
+                               /* Toggled highlited state */
+                               if (anchor != destAnchor) {
+                                       if (destAnchor != null) {
+                                               destAnchor.Highlighted = false;
+                                       }
+                                       /* Only highlight valid targets */
+                                       if (link.Source.CanLink (anchor)) {
+                                               anchor.Highlighted = true;
+                                       }
+                                       destAnchor = anchor;
+                               }
+                       }
                        base.SelectionMoved (sel);
                }
 
                protected override void SelectionChanged (List<Selection> sel)
                {
-                       List<DashboardButton> taggers;
-                       
-                       taggers = sel.Select (s => (s.Drawable as DashboardButtonObject).Button).ToList ();
-                       if (TagMode == TagMode.Edit) {
-                               if (TaggersSelectedEvent != null) {
-                                       TaggersSelectedEvent (taggers);
+                       if (sel.Count == 0) {
+                               return;
+                       }
+
+                       if (sel [0].Drawable is DashboardButtonObject) {
+                               List<DashboardButton> buttons;
+
+                               buttons = sel.Select (s => (s.Drawable as 
DashboardButtonObject).Button).ToList ();
+                               if (Mode == DashboardMode.Edit) {
+                                       if (ButtonsSelectedEvent != null) {
+                                               ButtonsSelectedEvent (buttons);
+                                       }
+                               }
+                       } else if (sel [0].Drawable is ActionLinkObject) {
+                               List<ActionLink> links;
+
+                               links = sel.Select (s => (s.Drawable as ActionLinkObject).Link).ToList ();
+                               if (Mode == DashboardMode.Edit) {
+                                       if (ActionLinksSelectedEvent != null) {
+                                               ActionLinksSelectedEvent (links);
+                                       }
                                }
                        }
                        base.SelectionChanged (sel);
                }
 
+               protected override void StartMove (Selection sel)
+               {
+                       if (sel != null && sel.Drawable is LinkAnchorObject) {
+                               LinkAnchorObject anchor = sel.Drawable as LinkAnchorObject;
+                               ActionLink link = new ActionLink {
+                                       SourceButton = anchor.Button.Button,
+                                       SourceTags = anchor.Tags
+                               }; 
+                               movingLink = new ActionLinkObject (anchor, null, link);
+                               AddObject (movingLink);
+                               ClearSelection ();
+                               UpdateSelection (new Selection (movingLink, SelectionPosition.LineStop, 0), 
false);
+                       }
+                       base.StartMove (sel);
+               }
+
                protected override void StopMove (bool moved)
                {
                        Selection sel = Selections.FirstOrDefault ();
-                       
-                       if (sel != null && moved) {
-                               int i = Constants.CATEGORY_TPL_GRID;
-                               DashboardButton tb = (sel.Drawable as DashboardButtonObject).Button;
-                               tb.Position.X = Utils.Round (tb.Position.X, i);
-                               tb.Position.Y = Utils.Round (tb.Position.Y, i);
-                               tb.Width = (int)Utils.Round (tb.Width, i);
-                               tb.Height = (int)Utils.Round (tb.Height, i);
-                               (sel.Drawable as DashboardButtonObject).ResetDrawArea ();
-                               widget.ReDraw ();
+
+                       if (movingLink != null) {
+                               if (destAnchor != null) {
+                                       ActionLink link = movingLink.Link;
+                                       link.DestinationButton = destAnchor.Button.Button;
+                                       link.DestionationTags = destAnchor.Tags;
+                                       link.SourceButton.ActionLinks.Add (link);
+                                       movingLink.Destination = destAnchor;
+                                       destAnchor.Highlighted = false;
+                                       Edited = true;
+                               } else {
+                                       RemoveObject (movingLink);
+                                       widget.ReDraw ();
+                               }
+                               ClearSelection ();
+                               movingLink = null;
+                               destAnchor = null;
+                               return;
                        }
 
+                       if (sel != null && moved) {
+                               if (sel.Drawable is DashboardButtonObject) {
+                                       /* Round the position of the button to match a corner in the grid */
+                                       int i = Constants.CATEGORY_TPL_GRID;
+                                       DashboardButton tb = (sel.Drawable as DashboardButtonObject).Button;
+                                       tb.Position.X = Utils.Round (tb.Position.X, i);
+                                       tb.Position.Y = Utils.Round (tb.Position.Y, i);
+                                       tb.Width = (int)Utils.Round (tb.Width, i);
+                                       tb.Height = (int)Utils.Round (tb.Height, i);
+                                       (sel.Drawable as DashboardButtonObject).ResetDrawArea ();
+                                       widget.ReDraw ();
+                               }
+                       }
                        base.StopMove (moved);
                }
 
@@ -210,7 +337,7 @@ namespace LongoMatch.Drawing.Widgets
                        tk.Context = context;
                        tk.Begin ();
                        tk.Clear (Config.Style.PaletteBackground);
-                       if (TagMode == TagMode.Edit) {
+                       if (Mode != DashboardMode.Code) {
                                tk.TranslateAndScale (Translation, new Point (ScaleX, ScaleY));
                                /* Draw grid */
                                tk.LineWidth = 1;
@@ -230,40 +357,49 @@ namespace LongoMatch.Drawing.Widgets
                        base.Draw (context, area);
                }
 
+               public void AddButton (DashboardButtonObject button)
+               {
+                       button.ShowLinks = ShowLinks;
+                       AddObject (button);
+                       buttonsDict.Add (button.Button, button);
+               }
+
                void LoadTemplate ()
                {
                        ClearObjects ();
+                       buttonsDict.Clear ();
+
                        foreach (TagButton tag in template.List.OfType<TagButton>()) {
                                TagObject to = new TagObject (tag);
                                to.ClickedEvent += HandleTaggerClickedEvent;
-                               to.Mode = TagMode;
-                               AddObject (to);
+                               to.Mode = Mode;
+                               AddButton (to);
                        }
                        
                        foreach (AnalysisEventButton cat in template.List.OfType<AnalysisEventButton>()) {
                                CategoryObject co = new CategoryObject (cat);
                                co.ClickedEvent += HandleTaggerClickedEvent;
                                co.EditButtonTagsEvent += (t) => EditButtonTagsEvent (t);
-                               co.Mode = TagMode;
-                               AddObject (co);
+                               co.Mode = Mode;
+                               AddButton (co);
                        }
                        foreach (PenaltyCardButton c in template.List.OfType<PenaltyCardButton>()) {
                                CardObject co = new CardObject (c);
                                co.ClickedEvent += HandleTaggerClickedEvent;
-                               co.Mode = TagMode;
-                               AddObject (co);
+                               co.Mode = Mode;
+                               AddButton (co);
                        }
                        foreach (ScoreButton s in template.List.OfType<ScoreButton>()) {
                                ScoreObject co = new ScoreObject (s);
                                co.ClickedEvent += HandleTaggerClickedEvent;
-                               co.Mode = TagMode;
-                               AddObject (co);
+                               co.Mode = Mode;
+                               AddButton (co);
                        }
 
                        foreach (TimerButton t in template.List.OfType<TimerButton>()) {
                                TimerObject to = new TimerObject (t);
                                to.ClickedEvent += HandleTaggerClickedEvent;
-                               to.Mode = TagMode;
+                               to.Mode = Mode;
                                if (Project != null && t.BackgroundImage == null) {
                                        if (t.Timer.Team == TeamType.LOCAL) {
                                                to.TeamImage = Project.LocalTeamTemplate.Shield;
@@ -271,7 +407,20 @@ namespace LongoMatch.Drawing.Widgets
                                                to.TeamImage = Project.VisitorTeamTemplate.Shield;
                                        }
                                }
-                               AddObject (to);
+                               AddButton (to);
+                       }
+
+                       foreach (DashboardButtonObject buttonObject in buttonsDict.Values) {
+                               foreach (ActionLink link in buttonObject.Button.ActionLinks) {
+                                       LinkAnchorObject sourceAnchor = buttonObject.GetAnchor 
(link.SourceTags);
+                                       LinkAnchorObject destAnchor = buttonsDict [link.DestinationButton].
+                                               GetAnchor (link.DestionationTags);
+                                       ActionLinkObject linkObject = new ActionLinkObject (sourceAnchor,
+                                                                             destAnchor, link);
+                                       link.SourceButton = buttonObject.Button;
+                                       linkObject.Visible = ShowLinks;
+                                       AddObject (linkObject);
+                               }
                        }
                        Edited = false;
                        SizeChanged ();
@@ -309,8 +458,8 @@ namespace LongoMatch.Drawing.Widgets
                        }
                        if (modeChanged) {
                                modeChanged = false;
-                               foreach (DashboardButtonObject to in Objects) {
-                                       to.ResetDrawArea ();
+                               foreach (CanvasObject co in Objects) {
+                                       co.ResetDrawArea ();
                                }
                        }
                        widget.ReDraw ();
@@ -346,7 +495,7 @@ namespace LongoMatch.Drawing.Widgets
 
                        button = tagger.Button as EventButton;
                        
-                       if (TagMode == TagMode.Edit) {
+                       if (Mode == DashboardMode.Edit) {
                                return;
                        }
                        
diff --git a/LongoMatch.GUI/Gui/Component/CodingWidget.cs b/LongoMatch.GUI/Gui/Component/CodingWidget.cs
index 2e9a8c1..2df2349 100644
--- a/LongoMatch.GUI/Gui/Component/CodingWidget.cs
+++ b/LongoMatch.GUI/Gui/Component/CodingWidget.cs
@@ -82,7 +82,7 @@ namespace LongoMatch.Gui.Component
                        ;
                        LongoMatch.Gui.Helpers.Misc.SetFocus (this, false);
                        
-                       buttonswidget.Mode = TagMode.Free;
+                       buttonswidget.Mode = DashboardMode.Code;
                        buttonswidget.FitMode = FitMode.Fit;
                        buttonswidget.ButtonsVisible = true;
                        buttonswidget.NewTagEvent += HandleNewTagEvent;
@@ -169,7 +169,7 @@ namespace LongoMatch.Gui.Component
                        if (project != null) {
                                buttonswidget.Project = project;
                        }
-                       buttonswidget.Mode = TagMode.Predefined;
+                       buttonswidget.Mode = DashboardMode.Code;
                        teamtagger.Project = project;
                        teamtagger.LoadTeams (project.LocalTeamTemplate, project.VisitorTeamTemplate,
                                project.Dashboard.FieldBackground);
diff --git a/LongoMatch.GUI/Gui/Component/DashboardWidget.cs b/LongoMatch.GUI/Gui/Component/DashboardWidget.cs
index 0bb2bd7..22e21ae 100644
--- a/LongoMatch.GUI/Gui/Component/DashboardWidget.cs
+++ b/LongoMatch.GUI/Gui/Component/DashboardWidget.cs
@@ -41,12 +41,12 @@ namespace LongoMatch.Gui.Component
        {
                public event NewEventHandler NewTagEvent;
 
-               TagMode tagMode;
+               DashboardMode mode;
                DashboardCanvas tagger;
                Dashboard template;
                DashboardButton selected;
-               Gtk.Image editimage;
-               ToggleToolButton editbutton, popupbutton;
+               Gtk.Image editimage, linksimage;
+               ToggleToolButton editbutton, linksbutton, popupbutton;
                RadioToolButton d11button, fillbutton, fitbutton;
                bool internalButtons, edited, ignoreChanges;
                Project project;
@@ -63,7 +63,7 @@ namespace LongoMatch.Gui.Component
                        applyimage.Pixbuf = Helpers.Misc.LoadIcon ("longomatch-apply", IconSize.Button);
 
                        tagger = new DashboardCanvas (new WidgetWrapper (drawingarea));
-                       tagger.TaggersSelectedEvent += HandleTaggersSelectedEvent;
+                       tagger.ButtonsSelectedEvent += HandleTaggersSelectedEvent;
                        tagger.ShowMenuEvent += HandleShowMenuEvent;
                        tagger.NewTagEvent += HandleNewTagEvent;
                        tagger.EditButtonTagsEvent += HandleAddNewTagEvent;
@@ -86,7 +86,7 @@ namespace LongoMatch.Gui.Component
                        FillToolbar ();
                        FitMode = FitMode.Original;
                        Edited = false;
-                       Mode = TagMode.Predefined;
+                       Mode = DashboardMode.Code;
                        // Initialize to a sane default value.
                        propertiesnotebook.Page = 1;
                }
@@ -165,26 +165,24 @@ namespace LongoMatch.Gui.Component
                        }
                }
 
-               public TagMode Mode {
+               public DashboardMode Mode {
                        set {
                                ignoreChanges = true;
-                               tagMode = value;
-                               tagger.TagMode = value;
+                               mode = value;
+                               tagger.Mode = value;
                                // Properties only visible in edit mode
-                               rightbox.Visible = tagMode == TagMode.Edit;
+                               rightbox.Visible = mode == DashboardMode.Edit;
                                // Add buttons for cards/tags/etc.. can be handled remotely.
-                               hbuttonbox2.Visible = tagMode == TagMode.Edit && internalButtons;
-                               editbutton.Active = value == TagMode.Edit;
-                               if (value == TagMode.Edit) {
-                                       editimage.Pixbuf = Helpers.Misc.LoadIcon 
("longomatch-dash-edit_active",
-                                               22, IconLookupFlags.ForceSvg);
+                               hbuttonbox2.Visible = mode == DashboardMode.Edit && internalButtons;
+                               editbutton.Active = value == DashboardMode.Edit;
+                               if (value == DashboardMode.Edit) {
+                                       editimage.Pixbuf = Helpers.Misc.LoadIcon 
("longomatch-dash-edit_active", 22);
                                } else {
-                                       editimage.Pixbuf = Helpers.Misc.LoadIcon ("longomatch-dash-edit",
-                                               22, IconLookupFlags.ForceSvg);
+                                       editimage.Pixbuf = Helpers.Misc.LoadIcon ("longomatch-dash-edit", 22);
                                }
-                               LongoMatch.Gui.Helpers.Misc.SetFocus (this, value == TagMode.Edit);
+                               LongoMatch.Gui.Helpers.Misc.SetFocus (this, value == DashboardMode.Edit);
                                if (project != null) {
-                                       if (value == TagMode.Edit) {
+                                       if (value == DashboardMode.Edit) {
                                                Edited = false;
                                        } else {
                                                if (Edited)
@@ -195,14 +193,14 @@ namespace LongoMatch.Gui.Component
                                ignoreChanges = false;
                        }
                        get {
-                               return tagMode;
+                               return mode;
                        }
                }
 
                public bool ButtonsVisible {
                        set {
                                internalButtons = value;
-                               Mode = tagMode;
+                               Mode = mode;
                        }
                }
 
@@ -257,14 +255,24 @@ namespace LongoMatch.Gui.Component
                        }
                }
 
+               void RemoveLink (ActionLink link)
+               {
+                       string msg = string.Format ("{0} {1} ?",
+                                            Catalog.GetString ("Do you want to delete: "), link);
+                       if (Config.GUIToolkit.QuestionMessage (msg, null, this)) {
+                               link.SourceButton.ActionLinks.Remove (link);
+                               Edited = true;
+                               Refresh ();
+                       }
+               }
+
                void FillToolbar ()
                {
                        Toolbar toolbar = new Toolbar ();
                        toolbar.Orientation = Orientation.Vertical;
                        toolbar.ToolbarStyle = ToolbarStyle.Icons;
                        
-                       editimage = new Gtk.Image (Helpers.Misc.LoadIcon ("longomatch-dash-edit_active",
-                               22, IconLookupFlags.ForceSvg));
+                       editimage = new Gtk.Image (Helpers.Misc.LoadIcon ("longomatch-dash-edit_active", 22));
                        editbutton = new ToggleToolButton ();
                        editbutton.IconWidget = editimage;
                        editbutton.Active = true;
@@ -272,10 +280,18 @@ namespace LongoMatch.Gui.Component
                        editbutton.TooltipText = Catalog.GetString ("Edit dashboard");
                        toolbar.Add (editbutton);
                        toolbar.Add (new SeparatorToolItem ());
-                       
+
+                       editimage = new Gtk.Image (Helpers.Misc.LoadIcon ("longomatch-dash-edit_active", 22));
+                       linksbutton = new ToggleToolButton ();
+                       linksbutton.IconWidget = editimage;
+                       linksbutton.Active = false;
+                       linksbutton.Toggled += HandleLinksToggled;
+                       linksbutton.TooltipText = Catalog.GetString ("Edit actions links");
+                       toolbar.Add (linksbutton);
+                       toolbar.Add (new SeparatorToolItem ());
+
                        popupbutton = new ToggleToolButton ();
-                       popupbutton.IconWidget = new Gtk.Image (Helpers.Misc.LoadIcon
-                                                               ("longomatch-popup", 22));
+                       popupbutton.IconWidget = new Gtk.Image (Helpers.Misc.LoadIcon ("longomatch-popup", 
22));
                        popupbutton.Active = true;
                        popupbutton.Toggled += HandlePopupToggled;
                        popupbutton.TooltipText = Catalog.GetString ("Disable popup window");
@@ -283,17 +299,17 @@ namespace LongoMatch.Gui.Component
                        toolbar.Add (new SeparatorToolItem ());
                        
                        fitbutton = new RadioToolButton ((GLib.SList)null);
-                       fitbutton.IconName = "longomatch-dash-fit";
+                       fitbutton.IconWidget = new Gtk.Image (Helpers.Misc.LoadIcon ("longomatch-dash-fit", 
22));
                        fitbutton.Toggled += HandleFitModeToggled;
                        fitbutton.TooltipText = Catalog.GetString ("Fit dashboard");
                        toolbar.Add (fitbutton);
                        fillbutton = new RadioToolButton (fitbutton);
-                       fillbutton.IconName = "longomatch-dash-fill";
+                       fillbutton.IconWidget = new Gtk.Image (Helpers.Misc.LoadIcon ("longomatch-dash-fill", 
22));
                        fillbutton.Toggled += HandleFitModeToggled;
                        fillbutton.TooltipText = Catalog.GetString ("Fill dashboard");
                        toolbar.Add (fillbutton);
                        d11button = new RadioToolButton (fitbutton);
-                       d11button.IconName = "longomatch-dash-11";
+                       d11button.IconWidget = new Gtk.Image (Helpers.Misc.LoadIcon ("longomatch-dash-11", 
22));
                        d11button.Toggled += HandleFitModeToggled;
                        d11button.TooltipText = Catalog.GetString ("1:1 dashboard");
                        toolbar.Add (d11button);
@@ -330,12 +346,22 @@ namespace LongoMatch.Gui.Component
                                return;
                        }
                        if (editbutton.Active) {
-                               Mode = TagMode.Edit;
+                               Mode = DashboardMode.Edit;
+                               linksbutton.Visible = true;
                        } else {
-                               Mode = TagMode.Predefined;
+                               Mode = DashboardMode.Code;
+                               linksbutton.Visible = false;
                        }
                }
 
+               void HandleLinksToggled (object sender, EventArgs e)
+               {
+                       if (ignoreChanges) {
+                               return;
+                       }
+                       tagger.ShowLinks = linksbutton.Active;
+               }
+
                void HandleTaggersSelectedEvent (List<DashboardButton> taggers)
                {
                        if (taggers.Count == 1) {
@@ -383,21 +409,28 @@ namespace LongoMatch.Gui.Component
                        }
                }
 
-               void HandleShowMenuEvent (DashboardButton taggerbutton, Tag tag)
+               void HandleShowMenuEvent (List<DashboardButton> buttons, List<ActionLink> links)
                {
                        Menu menu;
                        MenuItem delbut;
                        
-                       if (Mode != TagMode.Edit) {
+                       if (Mode != DashboardMode.Edit) {
                                return;
                        }
                        
                        menu = new Menu ();
-                       delbut = new MenuItem (Catalog.GetString ("Delete"));
-                       delbut.Activated += (sender, e) => {
-                               RemoveButton (taggerbutton);
-                       };
-                       menu.Add (delbut);
+                       foreach (DashboardButton button in buttons) {
+                               delbut = new MenuItem (string.Format ("{0}: {1}",
+                                       Catalog.GetString ("Delete"), button.Name));
+                               delbut.Activated += (sender, e) => RemoveButton (button);
+                               menu.Add (delbut);
+                       }
+                       foreach (ActionLink link in links) {
+                               delbut = new MenuItem (string.Format ("{0}: {1}",
+                                       Catalog.GetString ("Delete"), link));
+                               delbut.Activated += (sender, e) => RemoveLink (link);
+                               menu.Add (delbut);
+                       }
                        menu.ShowAll ();
                        menu.Popup ();
                }
diff --git a/LongoMatch.GUI/Gui/Panel/SportsTemplatesPanel.cs 
b/LongoMatch.GUI/Gui/Panel/SportsTemplatesPanel.cs
index 79f0f7f..e60a6bf 100644
--- a/LongoMatch.GUI/Gui/Panel/SportsTemplatesPanel.cs
+++ b/LongoMatch.GUI/Gui/Panel/SportsTemplatesPanel.cs
@@ -122,7 +122,7 @@ namespace LongoMatch.Gui.Panel
                        
                        buttonswidget.Sensitive = false;
                        buttonswidget.ButtonsVisible = false;
-                       buttonswidget.Mode = TagMode.Edit;
+                       buttonswidget.Mode = DashboardMode.Edit;
                        newtemplatebutton.Visible = true;
                        deletetemplatebutton.Visible = false;
                        
diff --git a/Tests/Core/Store/TestActionLink.cs b/Tests/Core/Store/TestActionLink.cs
index 9962226..398b098 100644
--- a/Tests/Core/Store/TestActionLink.cs
+++ b/Tests/Core/Store/TestActionLink.cs
@@ -51,7 +51,7 @@ namespace Tests.Core.Store
                        link = CreateLink ();
 
                        ActionLink link2 = Utils.SerializeDeserialize (link);
-                       Assert.AreNotEqual (link.SourceTags, link2.SourceTags);
+                       Assert.AreEqual (link.SourceTags, link2.SourceTags);
                        Assert.AreEqual (link.DestionationTags, link2.DestionationTags);
                        Assert.AreEqual (link.Action, link2.Action);
                        Assert.AreEqual (link.TeamAction, link2.TeamAction);


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