using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TradeIdeas.TIProData;
using TradeIdeas.XML;
using System.Windows.Forms;
using System.Drawing;
using System.Xml;
namespace TradeIdeas.TIProGUI
{
[XmlSerializerInit]
public class GuiDataNode : DataNode
{
private static Control _control;
public static void Init()
{
if (null != _control)
// Already initialized.
return;
System.Diagnostics.Debug.Assert(Application.MessageLoop);
_control = new Control();
var keepTheCompilerFromComplainingIJustNeedTheSideEffects =
_control.Handle;
// CreateControl() actually works in this case. But that method comes with some
// caveats. Calling Handle is the preferred solution.
//_control.CreateControl();
//System.Diagnostics.Debug.Assert(_control.IsHandleCreated);
}
private DataNode _child;
private GuiDataNode(object args)
{
AddAutoLink(((Factory)args).Find(out _child, Forward));
// What about the initial value? Is it safe to read it from another thread?
Value = _child.Value;
//System.Diagnostics.Debug.WriteLine("GuiDataNode initializing Value to " + Value);
}
private void Forward()
{
object value = _child.Value;
//System.Diagnostics.Debug.WriteLine("GuiDataNode.Forward value = " + value);
// TODO Currently we're forwarding everything with no delay. We should
// try to combine requests.
_control.BeginInvokeIfRequired(() => Value = value);
}
///
/// Wrap the given Factory in a GUI data node Factory. The result will be a new factory
/// with the same data, but the updates will only come in the GUI thread.
///
/// In some cases we can detect that the original factory is already safe. It only
/// makes callbacks in the GUI thread. For example, maybe the input to this function
/// was the result of a previous call to this function. In this case this function
/// will return its input without modification.
///
/// If and only if the input is null, the result will be null.
///
///
///
public static Factory GetFactory(Factory input)
{
if (null == input)
return null;
object metaData = input.MetaData;
if ((metaData is GuiDataNode) || (metaData is ConstantDataNode))
// We could add a GuiDataNode Factory around this factory, but it wouldn't
// do anything. Return the origianl as is. The result would be functionally
// the same if we didn't have this if statement. This is an optimization.
return input;
else
// Create the factory as requested.
return new FactoryWithHash(args => new GuiDataNode(args), typeof(GuiDataNode), input);
}
///
/// If the input is a Factory wrapped in a GuiDataNode Factory, return the original factory.
/// If the input is some other type of Factory, return it as is.
/// If the input is anything else, return null.
///
///
///
public static Factory Deconstruct(object input)
{
FactoryWithHash original = input as FactoryWithHash;
if ((null != original) && (typeof(GuiDataNode).Equals(original.MetaData)))
// This is a GUI data node. Return the original argument.
// This is the inverse of GetFactory(), and the ideal case of Deconstruct().
return (Factory)original.Arguments;
if (input is Factory)
// This is some other type of factory. Return it as is. That's consistent with the
// way we expect people to use this. The caller has a factory and doesn't know
// if it's one of ours or not.
return (Factory)input;
else
// This is not a factory. We always want to return a factory. So return null
// to say we failed.
return null;
}
}
[XmlSerializerInit("*")]
public class GradientInfoDataNode : DataNode
{
private readonly GradientInfo _gradientInfo;
private DataNode _child;
private GradientInfoDataNode(object arguments)
{
StaticList args = (StaticList)arguments;
System.Diagnostics.Debug.Assert(args.Length == 2);
_gradientInfo = (GradientInfo)args[0];
Factory factory = (Factory)args[1];
AddAutoLink(factory.Find(out _child, Update));
Update();
}
private void Update()
{
object inputAsObject = _child.Value;
if (!(inputAsObject is Double))
Value = null;
else if (_gradientInfo.Empty())
Value = null;
else
Value = _gradientInfo.GetColor((double)inputAsObject);
}
public static Factory GetFactory(GradientInfo gradientInfo, Factory basedOn)
{
return new FactoryWithHash(args => new GradientInfoDataNode(args), typeof(GradientInfoDataNode),
StaticList.Create(gradientInfo, basedOn));
}
///
/// Perfect for use with TransformationDataNode.
///
/// If this is the background color...
/// ...this will make a nice foreground color.
public static object AltColor(object originalColor)
{
if (!(originalColor is Color))
return null;
else
return GradientInfo.AltColor((Color)originalColor);
}
private const string GRADIENT_INFO = "gradient_info";
private static void XmlSerializerInit()
{
XmlSerializer.RegisterDecoder(GRADIENT_INFO, DecodeGradientInfo);
XmlSerializer.RegisterEncoder(typeof(GradientInfo), EncodeGradientInfo);
XmlSerializer.AddKnownObject(AltColor, "ALT_COLOR");
FactoryWithHash.RegisterStandardDecoder(typeof(GradientInfoDataNode));
}
private static object DecodeGradientInfo(XmlElement parent)
{
XmlElement value = parent.Node(XmlSerializer.VALUE);
if (null == value)
throw new ArgumentException("Can't find VALUE for GradientInfo.");
else
return new GradientInfo(value);
}
private static void EncodeGradientInfo(object toEncode, XmlElement body)
{
GradientInfo gradientInfo = (GradientInfo)toEncode;
gradientInfo.SaveAs(body, XmlSerializer.VALUE);
body.SetProperty(XmlSerializer.TYPE, GRADIENT_INFO);
}
}
public static class GuiDataNodeHelper
{
public static void AutoRelease(this DataNode.Link link, System.ComponentModel.Component component)
{
component.Disposed += delegate { link.Release(); };
}
}
}