using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Xml; using TradeIdeas.TIProData; using TradeIdeas.TIProGUI; namespace TIProDevExtension.ValueEditor { public class ValueSelector : Button { public enum Types { All, ColorDataNode, ColorConstant, DoubleDataNode, ControlFactory, SymbolList } // TODO, more types public Types LegalTypes { get; set; } private readonly ValueBroadcaster _value = new ValueBroadcaster(); public ValueBroadcaster ValueBroadcaster { get { return _value; } } protected override void Dispose(bool disposing) { Value = null; base.Dispose(disposing); } public ValueSelector() { Height = 42; // TODO this first call to UpdateValue() is often ignored. This will set the // text property based on the Value, which is currently null. However, if you // create one of these in the form designer, the automatically generated code // will overwrite Text with some random value. UpdateValue(); } public ValueSelector(Types legalTypes) : this() { LegalTypes = legalTypes; } Dictionary _sampleData; public Dictionary SampleData { get { return _sampleData; } set { _sampleData = value; UpdateValue(); } } private DataNode _dataNode; private DataNode.Link _dataNodeLink; private ValueBroadcaster _source; public ValueBroadcaster Source { get { return _source; } set { SetSource(value); if (null == value) SetValue(null); else SetValue(_source.Value); } } private void SetSource(ValueBroadcaster newSource) { if (_source == newSource) return; if (null != _source) _source.ValueChanged -= UpdateFromSource; _source = newSource; if (null != _source) _source.ValueChanged += UpdateFromSource; } private void UpdateFromSource(object newValue) { SetValue(newValue); } private void SetValue(object value) { if (value == _value.Value) return; _value.Value = value; UpdateValue(); } public object Value { get { return _value.Value; } set { SetSource(null); SetValue(value); } } private void UpdateValue() { DataNode.Link oldLink = _dataNodeLink; _dataNodeLink = null; if (Value is DataNode.Factory) { try { DataNode.Factory factory = (DataNode.Factory)Value; factory = GuiDataNode.GetFactory(factory); if (null != SampleData) factory = (DataNode.Factory)factory.ReplaceWith(SampleData); _dataNodeLink = factory.Find(out _dataNode, DataNodeFired); ShowValue(_dataNode.Value); } catch (Exception ex) { ShowValue(ex); } } else ShowValue(Value); if (null != oldLink) oldLink.Release(); } public event Action ValueChanged { add { _value.ValueChanged += value; } remove { _value.ValueChanged -= value; } } private void DataNodeFired() { try { ShowValue(_dataNode.Value); } catch (Exception ex) { ShowValue(ex); } } private static string SmartName(Color color) { if (color.IsNamedColor) return color.Name; if (color.A != 255) return '#' + color.Name; else return '#' + color.R.ToString("X2") + color.G.ToString("X2") + color.B.ToString("X2"); } private void ShowValue(object value) { BackColor = SystemColors.Control; ForeColor = SystemColors.ControlText; Text = ""; if (null == value) { Text = "null"; } else if ((value is Color)) { Color color = (Color)value; Text = SmartName(color); color = Color.FromArgb(255, color); // Remove any alpha BackColor = color; ForeColor = GradientInfo.AltColor(color); } else if ((value is Exception)) { Text = ((Exception)value).Message; BackColor = Color.Red; ForeColor = Color.White; } else { Text = value.ToString(); } } protected override void OnClick(EventArgs e) { base.OnClick(e); ContextMenuStrip menu = new ContextMenuStrip(); Color? initialColor = Value as Color?; if (null == initialColor) initialColor = ConstantDataNode.TryDeconstruct(Value); ToolStripItemCollection colorMenu; if (LegalTypes == Types.All) { // If all types are allowed, group the colors in their own submenu. ToolStripMenuItem submenu = (ToolStripMenuItem)menu.Items.Add("Color"); colorMenu = submenu.DropDownItems; } else if ((LegalTypes == Types.ColorDataNode) || (LegalTypes == Types.ColorConstant)) // If only colors are allowed, put them directly the original menu. colorMenu = menu.Items; else // Some type other than colors. Don't show the color options at all. colorMenu = null; if (null != colorMenu) { ToolStripMenuItem constantColorQuick = (ToolStripMenuItem)colorMenu.Add("Constant Color"); constantColorQuick.Click += delegate { using (ColorDialog dialog = new ColorDialog()) { dialog.Color = initialColor ?? ConstantEditor.DEFAULT_COLOR; dialog.FullOpen = true; if (dialog.ShowDialog() == DialogResult.OK) if (LegalTypes == Types.ColorConstant) Value = dialog.Color; else Value = ConstantDataNode.GetFactory(dialog.Color); } }; constantColorQuick.Checked = initialColor.HasValue; if (LegalTypes != Types.ColorConstant) { ToolStripMenuItem compareToZero = (ToolStripMenuItem)colorMenu.Add("Compare to Zero (Color)"); compareToZero.Click += delegate { using (CompareToZeroEditor editor = new CompareToZeroEditor(Value)) { editor.OutputTypes = Types.ColorConstant; if (editor.ShowDialog() == DialogResult.OK) Value = editor.CreateOutput(); } }; compareToZero.Checked = CompareToZeroDataNode.TryDeconstruct(Value); ToolStripMenuItem gradient = (ToolStripMenuItem)colorMenu.Add("Gradient Color"); gradient.Click += delegate { using (GradientEditor editor = new GradientEditor(Value)) { editor.ShowDialog(); Value = editor.Result; } }; } } ToolStripItemCollection doubleMenu; if (LegalTypes == Types.All) { // If all types are allowed, group these in their own submenu. ToolStripMenuItem submenu = (ToolStripMenuItem)menu.Items.Add("Floating Point Number"); doubleMenu = submenu.DropDownItems; } else if (LegalTypes == Types.DoubleDataNode) // If this type is allowed, put these items directly the original menu. doubleMenu = menu.Items; else // Some other type. Don't show these options at all. doubleMenu = null; if (null != doubleMenu) { double? initialDouble = Value as double?; if (null == initialDouble) initialDouble = ConstantDataNode.TryDeconstruct(Value); ToolStripMenuItem constant = (ToolStripMenuItem)doubleMenu.Add("Constant"); constant.Click += delegate { using (ConstantEditor editor = new ConstantEditor(initialDouble ?? 0.0)) { editor.WrapInDataNodeFactory = true; editor.UserControlsWrap = false; editor.UserControlsType = false; if (editor.ShowDialog() == DialogResult.OK) Value = editor.Result; } }; constant.Checked = initialDouble.HasValue; ToolStripItem topListFormula = doubleMenu.Add("Top List Formula"); topListFormula.Click += delegate { using (FormulaEditor editor = new FormulaEditor(Value)) { // TODO Limit output type to double. // TODO Add a check mark and set the initial value, when possible. if (editor.ShowDialog() == DialogResult.OK) Value = editor.Result; } }; } ToolStripItemCollection controlMenu; if (LegalTypes == Types.All) { // If all types are allowed, group the controls in their own submenu. ToolStripMenuItem submenu = (ToolStripMenuItem)menu.Items.Add("Controls"); controlMenu = submenu.DropDownItems; } else if (LegalTypes == Types.ControlFactory) // If only controls are allowed, put them directly the original menu. controlMenu = menu.Items; else // Some type other than controls. Don't show the control options at all. controlMenu = null; if (null != controlMenu) { ToolStripMenuItem simpleControl = (ToolStripMenuItem)controlMenu.Add("Simple Control"); SimpleDataNodeControl.Factory controlFactory = Value as SimpleDataNodeControl.Factory; DataNodeViewer originalViewer; if (null == controlFactory) originalViewer = null; else originalViewer = controlFactory.Viewer; simpleControl.Checked = ViewerEditor.CanEdit(originalViewer); simpleControl.Click += delegate { using (ViewerEditor dialog = new ViewerEditor(originalViewer)) if (dialog.ShowDialog() == DialogResult.OK) { DataNodeViewer result = dialog.Result; if (null == result) Value = null; else Value = new SimpleDataNodeControl.Factory(result); } }; ToolStripMenuItem grid = (ToolStripMenuItem)controlMenu.Add("Grid"); grid.Enabled = false; // TODO, implement this! ToolStripMenuItem gridWithDetailView = (ToolStripMenuItem)controlMenu.Add("Grid with Detail View"); gridWithDetailView.Enabled = false; // TODO, implement this! ToolStripMenuItem chart = (ToolStripMenuItem)controlMenu.Add("Chart"); chart.Enabled = false; // TODO, implement this! ToolStripMenuItem compoundControl = (ToolStripMenuItem)controlMenu.Add("Compound Control"); compoundControl.Click += delegate { using (PanelEditor dialog = new PanelEditor()) { dialog.ShowDialog(); // TODO, implement this! } }; } ToolStripItemCollection symbolListMenu; if (LegalTypes == Types.All) { // If all types are allowed, group these in their own submenu. ToolStripMenuItem submenu = (ToolStripMenuItem)menu.Items.Add("Symbol Lists"); symbolListMenu = submenu.DropDownItems; } else if (LegalTypes == Types.SymbolList) // If this type is allowed, put these items directly the original menu. symbolListMenu = menu.Items; else // Some other type. Don't show these options at all. symbolListMenu = null; if (null != symbolListMenu) { ToolStripMenuItem constantList = (ToolStripMenuItem)symbolListMenu.Add("Constant list"); constantList.Enabled = false; // TODO, implement this! ToolStripMenuItem sharedSymbolList = (ToolStripMenuItem)symbolListMenu.Add("Shared Symbol List"); sharedSymbolList.Enabled = false; // TODO, implement this! ToolStripMenuItem personalSymbolList = (ToolStripMenuItem)symbolListMenu.Add("Personal Symbol List"); personalSymbolList.Enabled = false; // TODO, implement this! ToolStripMenuItem fromTopList = (ToolStripMenuItem)symbolListMenu.Add("From Top List"); fromTopList.Enabled = false; // TODO, implement this! } if (menu.Items.Count > 0) menu.Items.Add("-"); // Null. Currently this always returns exactly null. // Do we ever want a constant data node that always returns null, instead? // By default a viewer will treat a null factory just like a factory that always returns nulls. // However, a viewer can and sometimes does look for a null value here and treat that // differently than a constant data node. // GuiDataNode.GetFactory() is smart enough to pass on a null without changing it. // Is there any place where we really need a null constant data node? ToolStripMenuItem nullItem = (ToolStripMenuItem)menu.Items.Add("null"); nullItem.Click += delegate { Value = null; }; if (null == Value) { nullItem.Checked = true; nullItem.Enabled = false; } ToolStripMenuItem xml = (ToolStripMenuItem)menu.Items.Add("XML"); xml.Click += delegate { using (XmlEditor editor = new XmlEditor(Value)) { editor.ShowDialog(); Value = editor.Result; } }; xml.Checked = XmlEditor.CanSerialize(Value); { // Probably all temporary stuff. Things that haven't been set up right yet. ToolStripItemCollection miscMenu = ((ToolStripMenuItem)menu.Items.Add("Misc")).DropDownItems; ToolStripMenuItem constant = (ToolStripMenuItem)miscMenu.Add("Constant"); constant.Click += delegate { using (ConstantEditor editor = new ConstantEditor()) if (editor.ShowDialog() == DialogResult.OK) Value = editor.Result; }; ToolStripItem topListFormula = miscMenu.Add("Top List Formula"); topListFormula.Click += delegate { using (FormulaEditor editor = new FormulaEditor(Value)) { if (editor.ShowDialog() == DialogResult.OK) Value = editor.Result; } }; } menu.Items.Add("-"); ToolStripItem copy = menu.Items.Add("Copy"); XmlDocument document = new XmlDocument(); try { document.AppendChild(document.CreateElement("COPY_DATA_NODE")); XmlSerializer.Encode(Value, document.DocumentElement); string xmlAsString = document.OuterXml; copy.Click += delegate { Clipboard.SetText(xmlAsString); }; } catch { copy.Enabled = false; } ToolStripMenuItem paste = (ToolStripMenuItem)menu.Items.Add("Paste"); paste.Enabled = false; try { string xmlAsString = Clipboard.GetText(); // Clipboard.GetText() returns "" if there is no text in the clipboard, // e.g. if you copied a bitmap. document = new XmlDocument(); document.LoadXml(xmlAsString); object result = XmlSerializer.Decode(document.DocumentElement); paste.Click += delegate { Value = result; }; paste.Enabled = true; } catch { } Point ptLowerLeft = new Point(0, Height); ptLowerLeft = PointToScreen(ptLowerLeft); menu.Show(ptLowerLeft); } private void Menu_VisibleChanged(object sender, EventArgs e) { throw new NotImplementedException(); } } }