/* Copyright (C) Arnaud Houdelette 2012-2014 Copyright (C) Emmanuel Langlois 2012-2014 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. 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, see . */ using System; using System.Collections.Generic; using System.Linq; namespace DMX2 { /// /// Event data : Objet passé aux cibles d'evenements /// public struct EventData { public string id; public byte value; public byte prev_value ; } /// /// Interface pour les fournisseurs d'evenements /// public interface IEventProvider { /// /// Renvoie le nom du sous menu associé au fournisseur d'evenements. /// /// /// Le nom du menu. /// string MenuName{ get; } /// Demande au fournisseur de signaler l'evenement spécifié /// L'id de l'evenement. /// true si le fournisseur reconnais l'id bool Bind (string eventId); /// Signale au fournisseur que l'evenement n'est plus attendu /// L'id de l'evenement. void Unbind (string eventId); /// Demande la construction du sous-menu evenement au fournisseur /// Le sous menu /// Un objet qui sera devra etre stocké dans Data[EventManager.StateKey] /// La delegate qui devra etre associée aux ButtonClickedEvent Gtk.Menu GetProviderSubMenu( EventManager.EventMenuData state, Gtk.ButtonPressEventHandler handler ); /// Appelé par le Timer de la conduite /// Fonction a appeler pour chaque evenement. void ProcessEvents(EventManagerCallback callback); IFeedbackInfo GetFeedbackInfo(string eventId); } /// /// Interface pour les recepteurs d'evenements. /// public interface IEventTarget { /// Fonction appelée lors d'un evenement /// Vrai si l'evenement est traité /// Evenement bool FireEvent(EventData data); /// Indique a la cible qu'on lui associe un evenement /// l'id de l'evenement void Bind(string id); /// Indique a la cible qu'on lui des associe un evenement /// l'id de l'evenement void Unbind(string id); bool CanFeedback{ get; } void AddFeedback(IFeedbackInfo info); } public interface IFeedbackInfo { bool FeedBack(byte data); } /// /// Callback sur evenement. /// public delegate void EventManagerCallback(EventData data); /// /// Callback appele sur clic sur un bouton de menu de choix d'evenement /// public delegate void EventManagerMenuCallBack(object state, string eventId); /// /// Cible generique d'evenement. /// public class actionEventTarget : IEventTarget { public delegate bool EventAction (EventData data); EventAction action; public actionEventTarget(EventAction _action) { action=_action; } #region IEventTarget implementation bool IEventTarget.FireEvent (EventData data) { return action(data); } void IEventTarget.Bind (string id){} // Rien a faire ici void IEventTarget.Unbind (string id){} bool IEventTarget.CanFeedback { get { return false; } } void IEventTarget.AddFeedback (IFeedbackInfo info) { throw new System.NotImplementedException (); } #endregion } /// /// Cible generique d'evenement. /// public class actionEventTargetEx : IEventTarget { public delegate bool EventAction (EventData data); public delegate int GetData(); EventAction action; GetData getdata; bool canfeedback=false;// , nofeedback=false; readonly List fbInfos = new List(); public actionEventTargetEx(EventAction _action, GetData _getdata,bool feedback) { action=_action; getdata = _getdata; canfeedback = feedback; } #region IEventTarget implementation bool IEventTarget.FireEvent (EventData data) { int val = getdata(); if ( (data.prev_value < val-5 && data.value < val-5) || (data.prev_value > val+5 && data.value > val+5) ) { return true; } //nofeedback=true; bool res =action(data); //nofeedback=false; return res; } public void FeedBack () { //if (nofeedback) return; byte data = (byte)getdata (); foreach (var info in fbInfos) { info.FeedBack(data); } } void IEventTarget.Bind (string id){} // Rien a faire ici void IEventTarget.Unbind (string id){} bool IEventTarget.CanFeedback { get { return canfeedback; } } void IEventTarget.AddFeedback (IFeedbackInfo info) { fbInfos.Add(info); } #endregion } public class EventManager { Dictionary bindings = new Dictionary(); List providers = new List(); class eventBinding { List targets=new List(); public void AddTarget(IEventTarget target) { if(!targets.Contains(target)) targets.Add(target); } public void RemoveTarget(IEventTarget target) { targets.Remove(target); } public ICollection Targets { get { return targets; } } } public EventManager () { } public void RegisterProvider (IEventProvider prov) { providers.Add (prov); foreach (var bind in bindings) { if (prov.Bind (bind.Key)) { IFeedbackInfo info = prov.GetFeedbackInfo(bind.Key); if(info!=null) foreach(IEventTarget target in bind.Value.Targets) if(target.CanFeedback) target.AddFeedback(info); } } } #region Menus // Cles pour association d'etat au elements de menu static public object StateKey = new object(); static public object EventIdKey = new object(); // Objet associe aux menus afin de pourvoir retransmettre les infos au createur du menu public class EventMenuData { public EventManagerMenuCallBack CallBack; public object state; } /// Appelé par l'interface. L'objet 'state' sera retransmis tel quel lors de l'appel au callback public Gtk.Menu GetMenu (object state, EventManagerMenuCallBack callback) { Gtk.Menu menu = new Gtk.Menu (); EventMenuData evd = new EventMenuData(); evd.CallBack = callback; evd.state = state; Gtk.ButtonPressEventHandler handler = new Gtk.ButtonPressEventHandler (HandleButtonPressEvent); foreach (IEventProvider prov in providers) { Gtk.MenuItem provitem = new Gtk.MenuItem(prov.MenuName); provitem.Submenu = prov.GetProviderSubMenu(evd, handler); if(provitem.Submenu!=null) menu.Add(provitem); } // Creation de l'element 'Aucun' retournant un ID vide, pour permettre la desassociation Gtk.MenuItem itemNone = new Gtk.MenuItem("Aucun"); itemNone.Data[StateKey] = evd; itemNone.Data[EventIdKey] = ""; itemNone.ButtonPressEvent += handler; menu.Add(itemNone); return menu; } /// Traitement du clic sur les menus generes au dessus void HandleButtonPressEvent (object o, Gtk.ButtonPressEventArgs args) { Gtk.MenuItem item = o as Gtk.MenuItem; EventMenuData evd = item.Data[EventManager.StateKey] as EventMenuData; // On récupere l'objet d'etat et la fonction a rappeler string id = item.Data[EventManager.EventIdKey] as string; // ainsi que l'id evenement evd.CallBack(evd.state,id); // Appel de la fonction } #endregion /// Appelé par le Timer de la conduite ... appel le traitement pour l'ensemble des fournisseurs public void ProcessEvents () { foreach (IEventProvider prov in providers) { prov.ProcessEvents(new EventManagerCallback(EventCallBack)); } } public void UnregisterProvider (IEventProvider prov) { providers.Remove(prov); } /// Enregistrement d'une association id => cible public bool Bind (string eventId, IEventTarget target) { if(!bindings.ContainsKey(eventId)) bindings.Add (eventId,new eventBinding()); bindings[eventId].AddTarget(target); target.Bind(eventId); foreach (IEventProvider prov in providers) { if(prov.Bind(eventId)) { if(target.CanFeedback){ IFeedbackInfo info = prov.GetFeedbackInfo(eventId); if(info!=null) target.AddFeedback(info); } return true; } } return false; } /// Desenregistrement de toutes les associations d'une cible public void Unbind (IEventTarget target) { var q = from bind in bindings where bind.Value.Targets.Contains(target) select bind.Key; foreach(string id in q.ToArray()) Unbind(id,target); } /// Desenregistrement d'une association id => cible public void Unbind (string eventId, IEventTarget target) { if (!bindings.ContainsKey (eventId)) return; bindings [eventId].RemoveTarget (target); if (bindings [eventId].Targets.Count == 0) { bindings.Remove(eventId); foreach (IEventProvider prov in providers) { prov.Unbind(eventId); } } target.Unbind(eventId); } /// Fonction appelee sur evenement public void EventCallBack (EventData data) { #if DEBUG Console.WriteLine("Event {0} => {1} (last = {2})",data.id,data.value,data.prev_value); #endif if (bindings.ContainsKey (data.id)) { foreach (IEventTarget target in bindings[data.id].Targets) { // TODO : Gestion des targets expirees target.FireEvent(data); } } } public bool SaveBindings (System.Xml.XmlElement xmlParent, IEventTarget target) { bool ret=false; System.Xml.XmlElement xmlB; var q = from bind in bindings where bind.Value.Targets.Contains(target) select bind.Key; foreach (string id in q) { ret=true; xmlParent.AppendChild(xmlB = xmlParent.OwnerDocument.CreateElement("EventBinding")); xmlB.SetAttribute("id",id); } return ret; } public static string[] LoadBindings (System.Xml.XmlElement xmlParent) { var all=xmlParent.GetElementsByTagName("EventBinding"); string[] ret = new string[all.Count]; int index=0; foreach (var xb in all) { ret[index++] = (xb as System.Xml.XmlElement).GetAttribute("id"); } return ret; } } }