using System; using System.Collections.Generic; using System.Data; using System.Drawing; using System.Linq; using System.Windows.Forms; using System.Xml; using TradeIdeas.ServerConnection; using TradeIdeas.TIProData; using TradeIdeas.TIProData.Configuration; using TradeIdeas.TIProData.Interfaces; using TradeIdeas.XML; namespace TradeIdeas.TIProGUI.CBT { public partial class ConfigDemoUserControl : UserControl, IContainer { private Initializer _args; public void Initialize(Initializer args) { _args = args; if (ConfigDemoInWords.WordsAvailable(args.Filter.InternalCode)) AddChildWindow(new ConfigDemoInWords(this)); TabPage histogramTabPage = null; if (ConfigDemoHistogram.Available(args.Filter.InternalCode)) histogramTabPage = AddChildWindow(new ConfigDemoHistogram(this, args.ConfigurationWindowManager, args.CurrentStrategy)); //set the keystroke delays for the max and min text boxes. minTextBox.Delay = 250; maxTextBox.Delay = 250; // We should really subscribe to updates, in case this is not available temporarily. // The way things work, this is unlikely to be important and is hard to test, so I'm // not doing it now. UpdateImages(); if (_creators.ContainsKey(_args.Filter.InternalCode)) foreach (Creator creator in _creators[_args.Filter.InternalCode]) AddChildWindow(creator(this)); if (null == _showsExample) if (null != histogramTabPage) // This is a decent fallback plan. Most of the chart examples would be better, // but this does a good job of show >, <, inside range, outside range, etc. _showsExample = histogramTabPage; // This is mostly aimed at when the user changes to a new tab page. But for consistency // we'll also send the message if the tab page is initially visible. It's probably not // that interesting right now because the max and min have not yet been filled in! NotifyNewTabPage(); } string IContainer.InternalCode { get { return _args.Filter.InternalCode; } } IConnectionMaster IContainer.ConnectionMaster { get { return _args.ConnectionMaster; } } double? IContainer.MinValue { get { return TryParse(MinText); } } double? IContainer.MaxValue { get { return TryParse(MaxText); } } TextBox IContainer.MinTextBox { get { return minTextBox; } } TextBox IContainer.MaxTextBox { get { return maxTextBox; } } void IContainer.ShowExample() { if (null != _showsExample) viewsTabControl.SelectedTab = _showsExample; } private static Dictionary> _creators = new Dictionary>(); public static void Add(string internalCode, Creator creator) { if (!_creators.ContainsKey(internalCode)) _creators[internalCode] = new List(1); _creators[internalCode].Add(creator); } private static void FinishInitNow() { System.Diagnostics.Debug.Assert(CurrentInitState == InitState.XmlReceived); CurrentInitState = InitState.Done; ConfigDemoInWords.InitInWordsRulesCache(); // These are unique. We wouldn't get any value out of adding these to a config file. Add("RPD", delegate(IContainer container) { return new PositionInYesterdaysRange(container); }); Add("RD", delegate(IContainer container) { return new PositionInTodaysRange(container); }); Add("GUP", delegate(IContainer container) { return new PerCentGap(container); }); Add("GUR", delegate(IContainer container) { return new BarGap(container); }); Add("GUD", delegate(IContainer container) { return new DollarGap(container); }); Add("Beta", delegate(IContainer container) { return new Beta(container); }); Add("ConDays", delegate(IContainer container) { return new ConfigDemoConsolidationDays(container); }); Add("EarningD", delegate(IContainer container) { return new ConfigDemoEarningsDate(container); }); Add("FCP", delegate(IContainer container) { return new ConfigDemoUpFromTheClosePercent(container); }); Add("FCD", delegate(IContainer container) { return new ConfigDemoUpFromTheCloseDollar(container); }); Add("FCR", delegate(IContainer container) { return new ConfigDemoUpFromTheCloseBars(container); }); Add("FOD", delegate(IContainer container) { return new ConfigDemoUpFromTheOpenDollar(container); }); Add("FOP", delegate(IContainer container) { return new ConfigDemoUpFromTheOpenPercent(container); }); Add("FOR", delegate(IContainer container) { return new ConfigDemoUpFromTheOpenBars(container); }); Add("FOW", delegate(IContainer container) { return new ConfigDemoUpFromTheOpenATR(container); }); // These each load some settings from the XML file. Each class is responsible for multiple filters. foreach (var kvp in ConfigDemoGrowth.GetAll()) Add(kvp.Key, kvp.Value); ConfigDemoDailyRanges.SaveAll(); PositionInIntradayRange.SaveAll(); ConfigDemoPercentAboveSMA.SaveAll(); ConfigDemoBarsAboveSMA.SaveAll(); ConfigDemoUpVsDown.SaveAll(); ConfigDemoPercentUpDown.SaveAll(); // This is a good place to add more rules. I like keeping the example last. foreach (var kvp in Example.GetAll()) Add(kvp.Key, kvp.Value); } private TabPage _showsExample; private Dictionary _children = new Dictionary(); public ConfigDemoUserControl() { InitializeComponent(); } private TabPage AddChildWindow(IChild child) { TabPage tabPage = new TabPage(child.Name); viewsTabControl.TabPages.Add(tabPage); Control body = child.Body; tabPage.Controls.Add(body); body.Top = 0; body.Left = 0; body.Size = tabPage.ClientSize; body.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top; body.Visible = true; if (child.CanShowExample) _showsExample = tabPage; _children[tabPage] = child; return tabPage; } private void UpdateImages() { if (null == minPictureBox.Image) { minPictureBox.Image = _args.ConnectionMaster.ImageCacheManager.GetFilter("Min" + _args.Filter.InternalCode); } if (null == maxPictureBox.Image) { maxPictureBox.Image = _args.ConnectionMaster.ImageCacheManager.GetFilter("Max" + _args.Filter.InternalCode); } } /// /// Use this to read or set the text for the min value. /// /// Ideally this value would be copied from the config window to here when we first show this /// dialog box. And it would b copied back from here to the config window if the user hits /// OK. /// /// Note that we send text, not a number. Mostly this is to keep any user formatting in /// place. I.e. "12.00" vs "12". /// public string MinText { get { return minTextBox.Text.Trim(); } set { minTextBox.Text = value; } } /// /// Use this to read or set the text for the max value. /// /// This value should be copied from the config window to here when we first show this /// dialog box. And it should be copied back from here to the config window if the user hits /// OK. /// /// Note that we send text, not a number. Mostly this is to keep any user formatting in /// place. I.e. "12.00" vs "12". /// public string MaxText { get { return maxTextBox.Text.Trim(); } set { maxTextBox.Text = value; } } /// /// Does it make sense to display this dialog box? Can we display any useful informatio about /// the given filter. /// /// The internal code for a filter. For example "Price" or "BS". /// True if it makes sense to display this dialog box. public static bool Available(string code) { return ConfigDemoInWords.WordsAvailable(code) || ConfigDemoHistogram.Available(code) || _creators.ContainsKey(code); } private void minTextBox_TextChanged(object sender, EventArgs e) { setTextBoxColor(minTextBox); } private void maxTextBox_TextChanged(object sender, EventArgs e) { setTextBoxColor(maxTextBox); } public static readonly Color ERROR_BACKGROUND = Color.LightPink; /// /// Changes the background color of the text box depending on the contents. /// Keeps the default color if the text box is empty or contains a valid number. /// Displays a pink background color if the input is invalid. /// /// Read the value from here and then set the color of this. private void setTextBoxColor(TextBox textBox) { String text = textBox.Text.Trim(); if ((text == "") || (null != TryParse(text))) textBox.BackColor = SystemColors.Window; else textBox.BackColor = ERROR_BACKGROUND; } private static Double? TryParse(String asText) { double value; // This automatically allows commas. It should be specific to the user's settings, // i.e. commas vs. periods, and therefor consistent with the real config window. if (Double.TryParse(asText, out value)) return value; else return null; } private void viewsTabControl_SelectedIndexChanged(object sender, EventArgs e) { isChartSelectedTabPage(viewsTabControl.SelectedTab); NotifyNewTabPage(); } private void NotifyNewTabPage() { TabPage tabPage = viewsTabControl.SelectedTab; if (null != tabPage) { if (_children.ContainsKey(tabPage)) _children[tabPage].OnShow(); } } private void isChartSelectedTabPage(TabPage tabPage) { /*Here, we're determining whether the user has actually selected a Chart tab page. This could have been put in NotifyNewTabPage, but that method is also fired when the user hasn't explicitly selected a chart tab page. This method is for the customer tracking...*/ if (tabPage.Text == "Chart") { GuiEnvironment.RecordUseCase(_args.WindowPrefix + ".ConfigDemo.Chart." + _args.Filter.InternalCode, _args.ConnectionMaster.SendManager); } } /// /// Initial: Nothing has happened. Waiting for someone to give us a SendManager. /// XmlRequested: We sent a request for the data, and we are waiting for the server to respond. /// XmlReceived: We have received a response and we are ready for the correct thread to process /// it. /// Done: We have received the XML file and processed it. /// private enum InitState { Initial, XmlRequested, XmlReceived, Done }; /// /// Where are we as far as initializing the config demo? Initialization is tricky because (a) /// we need to wait for someone to give us a network connection, (b) we need to wait for the /// server to respond and (c) we need to figure out where the GUI thread is, so we can update /// certain vairables in that thread. This would be easier if (a) we were reading a local file /// or (b) we were doing this in the main form. This is static because we want to finish the /// initialization ASAP, before any ConfigDemo forms are displayed. /// static private volatile InitState CurrentInitState = InitState.Initial; static private volatile XmlNode _configDemoXml; static public XmlNode ConfigDemoXml { get { return _configDemoXml; } } /// /// We need a control to make sure that we can send something to the UI thread. /// As a library we can't easily rely on any other windows. So we create a /// control of our own. /// static private Control _initSyncControl; /// /// It is safe to call this more than once. We will ignore all but the first call. We'd /// like to initialize as soon as possible. This populates the list of choices, i.e. /// ConfigDemo.Available(). /// /// Where to get the data. static public void Init(ISendManager sendManager) { // For simplicity, always check for this. We don't really need it (yet) if we're // looking at a file. But that's only aimed at debugging, and we'll still need // the SendManager later. System.Diagnostics.Debug.Assert(null != sendManager); if (CurrentInitState != InitState.Initial) return; _initSyncControl = new Control(); IntPtr forceTheHandleToExist = _initSyncControl.Handle; CurrentInitState = InitState.XmlRequested; RequestXmlForInit(sendManager); } static private void RequestXmlForInit(ISendManager sendManager) { XmlNode FileInfo = GuiEnvironment.XmlConfig.Node("CONFIG_DEMO").Node("FILE_INFO").Select(); string location = FileInfo.Property("LOCATION"); string name = FileInfo.Property("NAME"); switch (location) { case "server": sendManager.SendMessage(TalkWithServer.CreateMessage( "command", "download", "location", "/Misc/", "file", name), InitResponse, false, sendManager); break; case "local": // We need a way to read a local file for the sake of development. // This will probably never be used for production. try { XmlDocument doc = new XmlDocument(); doc.Load(name); _configDemoXml = doc.Node(0); CurrentInitState = InitState.XmlReceived; FinishInitNow(); } catch { } break; } } static private void InitResponse(byte[] body, object sendManager) { System.Diagnostics.Debug.Assert(CurrentInitState == InitState.XmlRequested); if (null == body) // Disconnected from server before we got a result. Try again. RequestXmlForInit((SendManager)sendManager); else { _configDemoXml = XmlHelper.Get(body).Node(0); CurrentInitState = InitState.XmlReceived; _initSyncControl.BeginInvokeIfRequired(FinishInitNow); } } string IContainer.Description { get { return _args.Filter.LongDescription; } } } public struct Initializer { public Filter Filter; public IConnectionMaster ConnectionMaster; public ConfigurationWindowManager ConfigurationWindowManager; public PrepairedStrategy CurrentStrategy; public string WindowPrefix; } }