/*
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 (byte data)
{
if (nofeedback)
return;
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) {
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;
}
}
}