using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; using TradeIdeas.TIProData.Configuration; using TradeIdeas.TIProData.Interfaces; namespace TradeIdeas.TIProGUI { /// /// 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; public bool isDefaultStrategy = false; /*Dictionary for mapping a node name to its assignmed integral image index. We will need this when using the orange arrow to select strategies. Must be mutually exclusive*/ private Dictionary _dict = new Dictionary(); private int _nodeNameTally = 0; private string _nodeName; //Start location of Config window when first brought up... private static int _configLocation_X = 50; private static int _configLocation_Y = 50; //Start size of Config window when *first* brought up (default size) private static int _configWindowHeight = -1; private static int _configWindowWidth = -1; private bool _raceConfigMode = false; // Font support. private const float DEFAULT_FONT_SIZE = 8.25F; private FontManager _fontManager; /// /// 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(); StartPosition = FormStartPosition.CenterParent; MinimizeBox = false; if (!IsHandleCreated) CreateHandle(); // Font support. _fontManager = new FontManager(this, DEFAULT_FONT_SIZE); _fontManager.selectTheFont(); // Take this code out since it messes with font support. //if ((_configWindowHeight == -1 && _configWindowWidth == -1)) //{ // _configWindowWidth = Width; // _configWindowHeight = Height; //} _configurationWindowManager = new ConfigurationWindowManager(); _configurationWindowManager.LoadFromServer(settings.ConnectionMaster, settings.ConfigurationType, OnLoaded, settings.InitialConfig); UpdateButtons(); _customFilters["MinPrice"] = minPriceTextBox; _customFilters["MaxPrice"] = maxPriceTextBox; _customFilters["MinVol"] = minAverageVolumeTextBox; _customFilters["MinTV"] = minVolumeTodayTextBox; Icon = GuiEnvironment.Icon; } /// /// Set Race Config Mode to true when called from RTSR. /// In this mode, the "Select Base" tab is hidden. /// public bool RaceConfigMode { get { return _raceConfigMode; } set { _raceConfigMode = value; if (_raceConfigMode) { Text = "Configure Your Race"; } } } /// /// 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; if (_raceConfigMode) { // Remove the "Select Base" tab and make selected tab "Customize". tabControl1.SelectedTab = customizeTabPage; tabControl1.TabPages.Remove(selectBaseTabPage); } else tabControl1.SelectedTab = selectBaseTabPage; tabControl1.TabPages.Remove(loadingTabPage); foreach (var kvp in _customFilters) kvp.Value.Text = GetFilterValue(kvp.Key); AddStrategyNodes(); baseTreeView.CollapseAll(); } /// /// 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() { advancedButton.Enabled = tabControl1.SelectedTab != loadingTabPage; 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) { StrategyNode node = e.Node.Tag as StrategyNode; TreeNode newNode = e.Node as TreeNode; //if (node.IsFolder() && newNode.IsSelected) if (node.IsFolder()) { if (baseTreeView.SelectedNode.IsExpanded) { baseTreeView.SelectedNode.Collapse(); } else { baseTreeView.SelectedNode.Expand(); } return; } UpdateFromTree(); } /// /// Updates the Strategy from the treeview node. /// /// Node to get the Strategy from. If null, use Selected Node. private void UpdateFromTree(StrategyNode node = null) { if (node == null) node = baseTreeView.SelectedNode.Tag as StrategyNode; //StrategyNode node = baseTreeView.SelectedNode.Tag as StrategyNode; bool topIsCurrent = (null != _configurationWindowManager.CurrentSettings) && (_configurationWindowManager.CurrentSettings == node.PrepairedStrategy); if (null == node) { strategyDescriptionLabel.Text = ""; return; } if (null != node.PrepairedStrategy) { // TODO strategyDescriptionLabel should be able to scroll, like in the traditional dialog box. Strategy = node.PrepairedStrategy; if (isDefaultStrategy && topIsCurrent) { strategyDescriptionLabel.Text = "These are your default settings." + Environment.NewLine + Environment.NewLine + "Strategy Name: " + Environment.NewLine + Strategy.WindowName; } else if (topIsCurrent) { strategyDescriptionLabel.Text = node.Description + Environment.NewLine + Environment.NewLine + "Strategy Name: " + Environment.NewLine + Strategy.WindowName; } else { strategyDescriptionLabel.Text = node.Description; } Strategy = node.PrepairedStrategy; strategyNameTextBox.Text = Strategy.WindowName; } UpdateTreeSelectionImage(); } private void UpdateTreeSelectionImage() { TreeNode selected = baseTreeView.SelectedNode as TreeNode; if (null == selected) return; StrategyNode selectedStrategy = baseTreeView.SelectedNode.Tag as StrategyNode; // Orage arrow icon do not apply to folders. if (!selectedStrategy.IsFolder()) { selected.SelectedImageIndex = 1; //orange arrow icon selected.ImageIndex = 1; //orange arrow icon _nodeName = selected.Name; TreeNodeCollection nodes = baseTreeView.Nodes; loadSetRecursive(nodes); } } private void loadSetRecursive(TreeNodeCollection nodes) { /*This method is affiliated with the orange arrow placement - and traverses the whole tree... makes sure that no other nodes other than the selected node have an orange arrow icon*/ foreach (TreeNode n in nodes) { if (n.Name != _nodeName) { foreach (var pair in _dict) { if (pair.Key == n.Name) { n.SelectedImageIndex = n.ImageIndex = pair.Value; } } } loadSetRecursive(n.Nodes); } } 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); // If Race Config Mode, set the Strategy to the "Current Settings" node since the "Select Base" tab will be hidden/removed. if (_raceConfigMode && node.Name == "Current Settings") UpdateFromTree(node); } } 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 && isDefaultStrategy) { newNode.Text = "Default Settings"; } switch (top.Icon) { case "+": newNode.ImageIndex = 4; newNode.Name = _nodeNameTally.ToString(); _dict.Add(newNode.Name, newNode.ImageIndex); break; case "-": newNode.ImageIndex = 5; newNode.Name = _nodeNameTally.ToString(); _dict.Add(newNode.Name, newNode.ImageIndex); break; case "*": newNode.ImageIndex = 2; newNode.Name = _nodeNameTally.ToString(); _dict.Add(newNode.Name, newNode.ImageIndex); break; case "folder":; newNode.ImageIndex = 3; newNode.Name = _nodeNameTally.ToString(); _dict.Add(newNode.Name, newNode.ImageIndex); break; case ":)": newNode.ImageIndex = 6; newNode.Name = _nodeNameTally.ToString(); _dict.Add(newNode.Name, newNode.ImageIndex); break; default: newNode.ImageIndex = 0; newNode.Name = _nodeNameTally.ToString(); _dict.Add(newNode.Name, newNode.ImageIndex); break; } newNode.SelectedImageIndex = newNode.ImageIndex; newNode.Tag = top; into.Add(newNode); _nodeNameTally++; 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_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) { if (e.Node.Bounds.Contains(e.Location)) { StrategyNode node = e.Node.Tag as StrategyNode; TreeNode newNode = e.Node as TreeNode; if (node.IsFolder() && newNode.IsSelected) { if (baseTreeView.SelectedNode.IsExpanded) { baseTreeView.SelectedNode.Collapse(); } else { baseTreeView.SelectedNode.Expand(); } return; } } } private void baseTreeView_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e) { //Select and advanced strategies to the next tab. Ignore folders. if (e.Node.Bounds.Contains(e.Location)) { StrategyNode node = e.Node.Tag as StrategyNode; if (node.IsFolder()) { return; } // Update new selection UpdateFromTree(); // Advance to next tab tabControl1.SelectedIndex = tabControl1.SelectedIndex + 1; } } private void SmallConfigWindow_VisibleChanged(object sender, EventArgs e) { //Here we retain the location whin this window is created again if (!this.Visible) { _configLocation_X = Location.X; _configLocation_Y = Location.Y; _configWindowHeight = Height; _configWindowWidth = Width; } else { if (GuiEnvironment.ConfigWindowRemembersLocation) Location = new Point(_configLocation_X, _configLocation_Y); // Take this code out since it messes with font support. //Width = _configWindowWidth; //Height = _configWindowHeight; } } } 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.isDefaultStrategy = this.IsDefaultStrategy; smallConfigWindow.RaceConfigMode = this.RaceConfigMode; 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; } } } }