using System; using System.Collections.Generic; using System.Windows.Forms; using TradeIdeas.TIProData.Configuration; using TradeIdeas.TIProData.Interfaces; using TradeIdeas.TIProGUI; namespace TIProDevExtension { /// /// This in a very simple version of the config window. The idea is that a user would start from a /// strategy that we built. We can select a base strategy, and he can do some simple things to /// customize it. This includes an "Advanced" button, that allows him to switch to the full version /// if he wants. /// /// This is typically called by SmallConfigWindowWrapper. SmallConfigWindowWrapper has some of the /// logic to make this work. In particular, the advanced button. (Perhaps SmallConfigWindow should /// be private? I don't know if that's even possible for a form.) /// public partial class SmallConfigWindow : Form { private Dictionary _customFilters = new Dictionary(); /// /// This is an output. We store the strategy here when we are done. This is also used internally /// as a workspace while we are building the strategy. /// public PrepairedStrategy Strategy { get; private set; } private ConfigurationWindowManager _configurationWindowManager; /// /// Standard constructor. This automatically and immediately requests data. /// /// /// This describes the window that we want. /// This is only used as an input. The caller should read/pull the values of /// and from this object. /// public SmallConfigWindow(ConfigWindowWrapper settings) { InitializeComponent(); if (!IsHandleCreated) CreateHandle(); _configurationWindowManager = new ConfigurationWindowManager(); _configurationWindowManager.LoadFromServer(settings.ConnectionMaster, settings.ConfigurationType, OnLoaded, settings.InitialConfig); UpdateButtons(); _customFilters["MinPrice"] = minPriceTextBox; _customFilters["MaxPrice"] = maxPriceTextBox; _customFilters["MinVol"] = minAverageVolumeTextBox; } /// /// Looks up a filter to find the corresponding value. /// We always look this up in the user's initial settings. /// /// A filter code like MaxPrice or MinVol. /// /// The value, or "" if the value was not found. /// Various types of errors are all treated as not found and return "". /// private string GetFilterValue(string code) { // TODO convert between the user's preferred number format and the server's preferred number format. PrepairedStrategy settings = _configurationWindowManager.CurrentSettings; if (null == settings) return ""; Filter filter; bool max; _configurationWindowManager.FindFilter(code, out filter, out max); if (null == filter) return ""; Dictionary values; if (max) values = settings.MaxFilters; else values = settings.MinFilters; string value; values.TryGetValue(filter, out value); return value??""; } /// /// Store the given filter settings. /// This always goes into the Strategy named in the Strategy property. /// /// A filter code like MaxPrice or MinVol. /// The value to store. Typically a number or the empty string. private void SetFilterValue(string code, string value) { // TODO convert between the user's preferred number format and the server's preferred number format. Filter filter; bool max; _configurationWindowManager.FindFilter(code, out filter, out max); if (null == filter) return; Dictionary values; if (max) values = Strategy.MaxFilters; else values = Strategy.MinFilters; if (value == "") values.Remove(filter); else values[filter] = value; } /// /// Called from the network thread. /// /// private void OnLoaded(ConfigurationWindowManager configurationWindowManager) { this.BeginInvokeIfRequired(delegate { OnLoaded(); }); } /// /// Called from the GUI thread. /// Configuration data is now valid. /// private void OnLoaded() { loaded = true; tabControl1.SelectedTab = selectBaseTabPage; tabControl1.TabPages.Remove(loadingTabPage); foreach (var kvp in _customFilters) kvp.Value.Text = GetFilterValue(kvp.Key); AddStrategyNodes(); } /// /// Configuration data is valid. /// We have received it from the server. /// private bool loaded; private void tabControl1_Selecting(object sender, TabControlCancelEventArgs e) { if (loaded) { if (e.TabPage == loadingTabPage) e.Cancel = true; } else { if (e.TabPage != loadingTabPage) e.Cancel = true; } } private void nextButton_Click(object sender, EventArgs e) { if (tabControl1.SelectedTab == nameTabPage) { UpdateResult(); DialogResult = System.Windows.Forms.DialogResult.OK; } else tabControl1.SelectedIndex = tabControl1.SelectedIndex + 1; } private void tabControl1_SelectedIndexChanged(object sender, EventArgs e) { UpdateButtons(); if (tabControl1.SelectedTab == selectBaseTabPage) // Do this so the selected item will be drawn in the selection color. baseTreeView.Focus(); } private void UpdateButtons() { nextButton.Enabled = tabControl1.SelectedTab != loadingTabPage; if (tabControl1.SelectedTab == nameTabPage) nextButton.Text = "Done"; else nextButton.Text = "Next"; } private void cancelButton_Click(object sender, EventArgs e) { Strategy = null; _configurationWindowManager.Abandon(); } private void advancedButton_Click(object sender, EventArgs e) { UpdateResult(); } private void UpdateResult() { if (null == Strategy) { // We hope this doesn't happen. But the server could send us an empty set of strategies. if (_configurationWindowManager.ConfigurationType == ConfigurationType.Alerts) Strategy = new AlertStrategy(); else Strategy = new TopListStrategy(); } Strategy.WindowName = strategyNameTextBox.Text; foreach (var kvp in _customFilters) SetFilterValue(kvp.Key, kvp.Value.Text); } private void baseTreeView_AfterSelect(object sender, TreeViewEventArgs e) { UpdateFromTree(); } private void baseTreeView_DoubleClick(object sender, EventArgs e) { tabControl1.SelectedIndex = tabControl1.SelectedIndex + 1; } private void UpdateFromTree() { StrategyNode node = baseTreeView.SelectedNode.Tag as StrategyNode; if (null == node) { strategyDescriptionLabel.Text = ""; return; } if (null != node.PrepairedStrategy) { // TODO strategyDescriptionLabel should be able to scroll, like in the traditional dialog box. strategyDescriptionLabel.Text = node.Description; Strategy = node.PrepairedStrategy; strategyNameTextBox.Text = Strategy.WindowName; } } private void AddStrategyNodes() { baseTreeView.Nodes.Clear(); strategyDescriptionLabel.Text = ""; StrategyNode top = _configurationWindowManager.StrategyTree; if (top == null) { return; } if (top.IsFolder()) { foreach (StrategyNode node in top.Children) { AddStrategyNodes(node, baseTreeView.Nodes); } } else { // We should never get here. The way our library parses the message from the // server, there has to be a folder on top. //AddStrategyNodes(top, null); System.Diagnostics.Debug.Assert(false); } } private void AddStrategyNodes(StrategyNode top, TreeNodeCollection into) { TreeNode newNode = new TreeNode(top.Name); bool topIsCurrent = (null != _configurationWindowManager.CurrentSettings) && (_configurationWindowManager.CurrentSettings == top.PrepairedStrategy); if (top.UserMustModify && !topIsCurrent) // This is a "start from scratch" type strategy. return; if (topIsCurrent) newNode.ImageIndex = 1; else switch (top.Icon) { case "+": newNode.ImageIndex = 4; break; case "-": newNode.ImageIndex = 5; break; case "*": newNode.ImageIndex = 2; break; case "folder": newNode.ImageIndex = 3; break; case ":)": newNode.ImageIndex = 6; break; default: newNode.ImageIndex = 0; break; } newNode.SelectedImageIndex = newNode.ImageIndex; newNode.Tag = top; into.Add(newNode); if (top.IsFolder()) { foreach (StrategyNode node in top.Children) { AddStrategyNodes(node, newNode.Nodes); } } else if (topIsCurrent || (null == baseTreeView.SelectedNode)) baseTreeView.SelectedNode = newNode; } private void baseTreeView_BeforeSelect(object sender, TreeViewCancelEventArgs e) { // Don't let someone select a folder. StrategyNode node = e.Node.Tag as StrategyNode; if ((null == node) || (null == node.PrepairedStrategy)) e.Cancel = true; } } public class SmallConfigWindowWrapper : ConfigWindowWrapper { public SmallConfigWindowWrapper(ConfigurationType configurationType, IConnectionMaster connectionMaster) : base(configurationType, connectionMaster) { } public static new ConfigWindowWrapper Factory(ConfigurationType configurationType, IConnectionMaster connectionMaster) { return new SmallConfigWindowWrapper(configurationType, connectionMaster); } /// /// This examines the request and decides if the SmallConfigWindow can handle it. /// /// /// True means to show the advanced window. /// False means to display the small window. /// private bool NeedAdvanced() { return OnlyShowColumns || (null != SortBy) || (null != InitialFilter) || IsSingleStock; } public override void ShowIt() { if (NeedAdvanced()) { TraditionalConfigWindowWrapper advanced = new TraditionalConfigWindowWrapper(this); advanced.ShowIt(); Strategy = advanced.Strategy; } else using (SmallConfigWindow smallConfigWindow = new SmallConfigWindow(this)) { smallConfigWindow.ShowDialog(); if (smallConfigWindow.DialogResult == System.Windows.Forms.DialogResult.OK) Strategy = smallConfigWindow.Strategy; else if (smallConfigWindow.DialogResult == DialogResult.Retry) { TraditionalConfigWindowWrapper advanced = new TraditionalConfigWindowWrapper(this); if (null != smallConfigWindow.Strategy) // Read the user's settings. Strategy will be null if the user didn't finish // loading the config info from the server. In that case we keep the original // inputs, which were copied by the constructor. advanced.InitialConfig = smallConfigWindow.Strategy.MakeConfigString(); advanced.ShowIt(); Strategy = advanced.Strategy; } else Strategy = null; } } } }