using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Xml; using System.IO; using System.Windows.Forms; using System.Drawing; using TradeIdeas.TIProData; using TradeIdeas.TIProData.Configuration; using TradeIdeas.XML; using System.Diagnostics; using TradeIdeas.MiscSupport; using System.Windows.Input; using System.Collections.Specialized; using System.Web; using TradeIdeas.TIProGUI.Surfer; using System.Globalization; using TradeIdeas.TIProData.Interfaces; using TradeIdeas.Logging; /* Notes on history: * Originally top lists would store history in the collaborate string. This would be * preserved when you collaborate directly. And duplicating or sharing a window would * implicitly use the same logic. * * Alerts are totally different. Alerts forget the history setting as soon as you * re-configure or collaborate. * * New plan for top lists: * History is explicitly stripped out when we do a collaborate and some other types of * import. We try to preserve the user's OMH settings when doing that. But we keep * history when we save (or duplicate or share) a window. */ namespace TradeIdeas.TIProGUI { public delegate void TopListSymbolClickHandler(string symbol, string exchange); public enum TopListSortType { Descending, Ascending, ServerSort, DeltaValue }; public partial class TopListForm : Form, ISaveLayout, ICultureListener, IFont, IGrid, IRowSelect, IDemoMode, ICanConfigure, IChildable, IContextMenuStrip, ISnapToGrid, ISupportLimitedMode, IAccountStatusChanged, TopListRequest.Listener, Surfer.Surfable, ICanRefreshNow, IHaveGrids, ISymbolLinkingChannel, IHandlesStrategies, ITreemapSource { private BindingList _boundList = new BindingList(); private bool _isFrozen = false; private Color _frozenHeaderTextColor = Color.FromArgb(30, 62, 158); // Sorting variables private ColumnInfo _selectedSortColumnInfo; // for restoring layouts private bool _currentlyRestoring = true; //this is for the strategy button- restoring from layout, or instantiating new form private string _importSortBy; private bool _needToRestoreSortBy = false; private bool _needToUpDateTreemapSortOptions = true; private List _serverSortedList = new List(); private bool _localSortEnabled = false; private TopListSortType _localSortType = TopListSortType.ServerSort; private string _serverSortField = ""; private Dictionary> _filterMenuItems = new Dictionary>(); private TopListRequest.Token _requestToken; private TopList _historyRequest; private List _tokenStoreList = new List(); private string _config; private TopListInfo _metaData; private List _additionalSymbolPlusColumns = new List(); private bool _addedSymbolPlusColumns = false; // for tracking currently "selected" row for modifying the right-click menu appropriately private RowData _currentRowData; private RowData _mostRecentRowData; private int _mostRecentRowIndex; private bool _symbolSpecificContextMenuOpen = false; /// /// Multi-symbol Send To Symbol List support /// private List _selectedRowsList = new List(); //specifically for when one clicks the "Go Back" link of marketing panel. We want to know whether // to go back to "Live During Market Hours" or "Always Live". private bool _selectedLiveAlways = false; private GradientColorChooser _gradientColorChooser = new GradientColorChooser(); private ColumnListState _columnListState = new ColumnListState(); private LayoutManager _layoutManager = LayoutManager.Instance(); private readonly IConnectionMaster _connectionMaster; private List _phrases; private List _sendTo; private string _fileNameSaved; private string _fileNameSavedContents; private string _shortWindowName; private const string FORM_TYPE = "TOP_LIST_WINDOW"; private const string _defaultFileName = "DEFAULT_TOPLIST.WTI"; public static readonly WindowIconCache WindowIconCache = new WindowIconCache("TOP_LIST"); private FontManager _fontManager; private const float DEFAULT_FONT_SIZE = 8.25F; private ISymbolLinkingForOwner _symbolLinking; private Dictionary _columnColors = new Dictionary(); // new variable to set that dock panel is being used - RVH20210329 private bool _dockPanelMode = true; private string _linkChannel = SymbolLinkChannels.DefaultLinkChannel; private List _dataGrids = new List(); //varible to set that the gridview has been painted private bool _gridVisible = false; private bool _formresizing = false; private bool _restoringLayout = false; private bool _adjustGridRowsHeigth = false; private bool _adjustSymbolPlus = false; /// /// Export copies of the GradientInfo objects used to display the table. /// /// /// The default colors used for all cells. The can be overriden for specific columns. /// This can be null. For the top list we don't expect this to be null, but we plan to turn /// this into an interface used by oher types of windows. For the alert window this would /// be null. /// /// /// The colors to use for specific columns. /// This can be null. /// The keys are names that will be displayed to the user. Currently they are the internal /// codes for the filters, but I think we can do better. Currently these might include colors /// for colums that are not visible. /// public void GetColors(out GradientInfo main, out Dictionary columns) { main = _gradientColorChooser.GradientInfo.DeepCopy(); columns = new Dictionary(); foreach (var kvp in _columnColors) columns[kvp.Key] = kvp.Value; } // For use in showing columns private const int MIN_COLUMN_WIDTH = 5; private const int DEFAULT_ROW_HEIGHT = 20; //viewing and hiding the top panel which contains the strategy combo private bool _isTopPanelHidden = true; //column locking ("Freezing" the symbol column) private bool _columnLocked = false; ToolStripMenuItem _lockColumnMenuItem; private bool _lockMenuItemEnabled = true; private bool _textHeaders = false; public bool TextHeaders { get { return _textHeaders; } set { _textHeaders = value; UpdateTextHeadersMenuItem(); } } private bool _isChild = false; public bool IsChild { get { return _isChild; } set { _isChild = value; } } public Rectangle? ActualSize { get; set; } public TIFormType TIFormType { get { return TIProGUI.TIFormType.TopList; } } /// /// This gets called before any other code in the SaveLayout function. Currently this is aimed at the Scottrade addin but anything can set this code. /// public Action
PreSaveLayoutCode { get; set; } /// /// This gets called after SaveBase and it passes the XmlNode that the form is being saved to. This allows extensions to add properties to the layout. Access the layout XmlNode /// from a form using RestoredLayout. /// public Action SaveLayoutCode { get; set; } /// /// This is the xml that was used to restore this form. If this form wasn't layout-restored then this will be null. /// public XmlNode RestoredLayout { get; set; } /// /// Returns the current configuration string used for collaboration. /// /// public string GetConfigString() { return _config; } public ContextMenuStrip MyContextMenuStrip { get { return contextMenuStrip1; } } /// /// Exposes the data grids. /// public List DataGrids { get { return _dataGrids; } } /// /// Show the controls context menu. /// public void ShowContextMenu() { this.InvokeIfRequired(delegate () { contextMenuStrip1.Show(System.Windows.Forms.Cursor.Position); }); } /// /// Hide the controls context menu. /// public void HideContextMenu() { this.InvokeIfRequired(delegate () { contextMenuStrip1.Hide(); }); } /// /// Make Show Chrome Dev Tools menu item visible if in Development mode. /// public void EnableShowChromeDevToolsMenuItem() { this.InvokeIfRequired(delegate () { if (GuiEnvironment.DevelopmentMode) showChromeDevToolsToolStripMenuItem.Visible = true; }); } public BindingList BoundList { get { return _boundList; } } /// /// return the class as a Form /// /// public Form GetAsForm() { return this as Form; } //for user tracking ISendManager _sendManager; private makeSendToContextMenu _sendToContextMenu; [Obsolete("This is required by the GUI designer sometimes. Do not use it in real code.")] public TopListForm() : this(new ConnectionMaster(), null) { } private ContextMenu _contextMenuCylinder = new ContextMenu(); private ColumnInfo _cylinderColumnInfo = null; private string _cylinderColumnCode = ""; private bool _historyErrorOnDemo = false; // for showing/hiding marketingPanel if history accessed in delayed data mode private DataCell.SymbolPlusLayoutType _symbolPlusLayout = DataCell.SymbolPlusLayoutType.Off; //The below array accounts for Alert , Multistrategy windows as well as Top List: public static string[] sectorColumns = { "D_Industry", "D_Sector", "D_SubSector", "D_IndGrp", "D_SubIndustry", "D_Name", "D_Desc", "Description", "Strategies", "Strategy Name", "Sub Sector", "Industry Group", "Sub Industry", "Industry", "Sector", "Company Name" }; // New browser based treemap chart control; private ChromiumTreemapControl _chromiumTreemapControl; private bool _useBrowserInterface = false; public TopListForm(IConnectionMaster connectionMaster, String config, bool? initialOmh = null, string initialHistoryTime = null, string initialCount = null) { _limitedMode = GuiEnvironment.LimitedMode; _connectionMaster = connectionMaster; _sendManager = _connectionMaster.SendManager; _symbolLinking = new SymbolLinking(this); InitializeComponent(); _dataGrids.Add(dataGridView1); // check to see if auto-size columns is set - RVH20210720 if (GuiEnvironment.autosizeColumns) dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill; // Add browser treemap control. _chromiumTreemapControl = new ChromiumTreemapControl(this) { Location = new Point(0, 0), Dock = DockStyle.Fill, Visible = false }; tableLayoutPanel1.Controls.Add(_chromiumTreemapControl, 0, 1); ExtensionMethods.DoubleBufferedDatagrid(dataGridView1, true); // Suppress "System.Windows.Data Error warning" // http://stackoverflow.com/questions/16065047/system-windows-data-error-4-in-a-dynamic-menuitem System.Diagnostics.PresentationTraceSources.DataBindingSource.Switch.Level = System.Diagnostics.SourceLevels.Critical; // Call to initialize time frame settings and clean up config string. config = InitializeTimeFrameSettings(config, initialOmh, initialHistoryTime, initialCount); saveToCloudToolStripMenuItem.Image = SaveToCloud.WindowIconCache.MenuImage; if (!IsHandleCreated) // This is required because the callbacks can come in any thread. InvokeRequired // does not work correctly before the handle is created. This is necessary // in any window which requests data in the constructor. CreateHandle(); /* Setting the data source allows us to use any type of object we * want to store the row data. If we added the row directly using * dataGridView1.Rows.Add(myRow), myRow would have to be an array * of objects. That means we'd have to parse and format the data * as we were adding it to the data. That's not completely * unreasonble, but I'd rather keep it the way it is. */ dataGridView1.DataError += new DataGridViewDataErrorEventHandler(dataGridView1_DataError); dataGridView1.DataSource = _boundList; SetConfiguration(config); WindowIconCache.SetIcon(this); // This should all come from a dialog box, and be saved as part of the layout. _gradientColorChooser.FieldInternalCode = "FCD"; // Up from close, in dollars. GradientInfo gradientInfo = new GradientInfo(); gradientInfo.Add(3, Color.LightGreen); gradientInfo.Add(-3, Color.Pink); bool lotsOfBlack = true; if (lotsOfBlack) { // Anything near 0 is very dark. gradientInfo.Add(1, Color.Green); gradientInfo.Add(0, Color.Black); gradientInfo.Add(-1, Color.Red); } else { // 0 is black. Even a very small distance below 0 turns red. gradientInfo.Add(0, Color.Red, Color.Black, Color.Green); } _gradientColorChooser.GradientInfo = gradientInfo; _gradientColorChooser.OnChange += new MethodInvoker(_gradientColorChooser_OnChange); graphicalIndicatorToolStripMenuItem.Click += Column.GraphicsMenuItemCallback; _phrases = GuiEnvironment.XmlConfig.Node("MULTI_STRATEGY_WINDOW").Node("PHRASES"); _sendTo = GuiEnvironment.XmlConfig.Node("TOPLIST_WINDOW").Node("PHRASES"); lblMessage.Text = _phrases.Node("IN_DEMO").PropertyForCulture("TEXT", "***"); linkLabel.Text = "Click here to subscribe."; LinkLabel.Link link = new LinkLabel.Link(); link.LinkData = "https://www.trade-ideas.com/login/?newaccount=1"; linkLabel.Links.Add(link); lblGoBack.Text = _phrases.Node("GO_BACK").PropertyForCulture("TEXT", "***"); PopulateStrings(); setGridLines(); chooseRowSelect(); _fontManager = new FontManager(this, DEFAULT_FONT_SIZE); AddExtraMenuItems(GuiEnvironment.GetExtraMenuItems().Where(x => x.TIFormType == TIFormType.All || x.TIFormType == TIFormType.TopList).ToList(), contextMenuStrip1.Items); AddExtensionMenuItems(); SetSnapToGrid(GuiEnvironment.SnapToGrid); comboMenu.loadTheCombo(ConfigurationType.TopList, _connectionMaster, _config, this); cbmColumns.IsColumns = true; cbmColumns.loadTheCombo(ConfigurationType.TopList, _connectionMaster, _config, this); chart1.ContextMenu = _contextMenuCylinder; chart1.Size = new Size(chart1.Size.Width, dataGridView1.RowTemplate.Height); hideTopPanel(); SurfManager.Instance().DataRefreshed(this); } private void dataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs anError) { // This method was implemented to ignore unhandled exceptions when the user was using the up and down keys // for external linking at the same time the grid row entered was in flux due to adding or removing // rowdata during normal view mode operations. anError.ThrowException = false; //System.Diagnostics.Debug.WriteLine("TopListForm: DataGridView.DataError Event handled: " + anError.Exception); } /// /// Returns cConfig string with time frame settings removed. /// Time frame settings are applied to global variables and components. /// /// /// /// /// /// private string InitializeTimeFrameSettings(string config, bool? initialOmh, string initialHistoryTime, string initialCount) { long historyTime = 0; // seconds int count = 0; if ((null == initialOmh) || (null == initialHistoryTime) || (null == initialCount)) { // I really, really hate doing this type of parsing on the client side. // When the newest client saves the windo we explicitly store HISTORY_TIME, OMH, // and MAX_RECORD_COUNT in the XML. // We only have to do this parsing if the layout was saved by an older client. // The older client would send config to the server to get it parsed. That made the // code slower and more complicated. NameValueCollection parsed = HttpUtility.ParseQueryString(config); if (null == initialOmh) initialOmh = parsed.Get("omh") == "1"; try { if (null == initialCount) { initialCount = parsed.Get("count"); if ((null != initialCount) && ("" != initialCount)) count = Convert.ToInt32(initialCount); } } catch (Exception e) { string debugView = e.ToString(); initialCount = null; } try { if (null == initialHistoryTime) { initialHistoryTime = parsed.Get("exact_time"); if ((null != initialHistoryTime) && ("" != initialHistoryTime)) historyTime = Convert.ToInt64(initialHistoryTime); } if (null == initialHistoryTime) { // Alternate time spec. // where h_d is the number of seconds in a day in Pacific time // h_h is the number of hours in Easter time // h_m is the number of minutes in Eastern time // To use ServerFormat.FromTimeT the input is in seconds Eastern time. string historyTimeSpec = parsed.Get("h_d"); if ((null != historyTimeSpec) && ("" != historyTimeSpec)) historyTime = Convert.ToInt64(historyTimeSpec); historyTimeSpec = parsed.Get("h_h"); if ((null != historyTimeSpec) && ("" != historyTimeSpec)) historyTime += Convert.ToInt64(historyTimeSpec) * 3600; historyTimeSpec = parsed.Get("h_m"); if ((null != historyTimeSpec) && ("" != historyTimeSpec)) historyTime += Convert.ToInt64(historyTimeSpec) * 60; // We must subtract 3 hours worth of seconds from historytime to // get the days data to Eastern time so we can create the equivalent exact_time data. if (historyTime != 0) historyTime -= 3 * 60 * 60; initialHistoryTime = historyTime.ToString(); if (historyTime <= 0) initialHistoryTime = null; } // Delete keys from config string parsed.Remove("omh"); parsed.Remove("hist"); parsed.Remove("exact_time"); parsed.Remove("h_d"); parsed.Remove("h_h"); parsed.Remove("h_d"); parsed.Remove("count"); config = parsed.ToString(); } catch (Exception e) { string debugView = e.ToString(); initialHistoryTime = null; } } // Apply case where initialOmh, initialHistoryTime, initialCount // were not null try { if (historyTime == 0) historyTime = Convert.ToInt64(initialHistoryTime); } catch (Exception e) { string debugView = e.ToString(); initialHistoryTime = null; } try { if (count == 0) count = Convert.ToInt32(initialCount); } catch (Exception e) { string debugView = e.ToString(); initialCount = null; } // Set time frame global variables and components if (null == initialHistoryTime) { _lastHistoricalTime = null; useHistoricalDateToolStripMenuItem.Checked = false; // Check when initialOmh is still null and set it to live always as default (Brad's preference) if (null == initialOmh) initialOmh = true; liveAlwaysToolStripMenuItem.Checked = (bool)initialOmh; _selectedLiveAlways = liveAlwaysToolStripMenuItem.Checked; liveDuringMarketHoursToolStripMenuItem.Checked = !liveAlwaysToolStripMenuItem.Checked; } else { useHistoricalDateToolStripMenuItem.Checked = true; liveAlwaysToolStripMenuItem.Checked = false; liveDuringMarketHoursToolStripMenuItem.Checked = false; _lastHistoricalTime = ServerFormats.FromTimeT(historyTime); } if (null != initialCount) _recordCount = count; return config; } private Dictionary _extensionMenuItems = new Dictionary(); private string separatorCSV_Name = "csv_separator"; private void AddExtensionMenuItems() { if (null != GuiEnvironment.RowDataExtensions && GuiEnvironment.RowDataExtensions.Count > 0) { bool addedSeparator = false; foreach (IRowDataExtension rowDataExtension in GuiEnvironment.RowDataExtensions) { ToolStripMenuItem menuItem = null; if (_extensionMenuItems.ContainsKey(rowDataExtension)) { contextMenuStrip1.Items.Remove(_extensionMenuItems[rowDataExtension]); menuItem = rowDataExtension.GetMenuItem(this); contextMenuStrip1.Items.Add(menuItem); _extensionMenuItems[rowDataExtension] = menuItem; } else { menuItem = rowDataExtension.GetMenuItem(this); if (null != menuItem) { if (!addedSeparator) { ToolStripSeparator toolStripSeparatorCSV = new ToolStripSeparator { Name = separatorCSV_Name }; contextMenuStrip1.Items.Add(toolStripSeparatorCSV); addedSeparator = true; } contextMenuStrip1.Items.Add(menuItem); _extensionMenuItems.Add(rowDataExtension, menuItem); } } } } } /// /// Method used to show/hide CSVLogger menu and separator. /// /// public void ToggleExtensionItems(bool toggle) { // Toggle CSVLogger menu foreach (ToolStripMenuItem menuItem in _extensionMenuItems.Values) menuItem.Visible = toggle; // Toggle CSVLogger separator foreach (ToolStripSeparator separator in contextMenuStrip1.Items.OfType()) { if (separator.Name == separatorCSV_Name) separator.Visible = toggle; } } private void AddExtraMenuItems(List menuItems, ToolStripItemCollection into) { foreach (TIToolStripMenuItem menuItem in menuItems) { AddExtraMenuItem(menuItem, into); } } private void AddExtraMenuItem(TIToolStripMenuItem menuItemTemplate, ToolStripItemCollection into) { TIToolStripMenuItem thisMenuItem = menuItemTemplate.Clone(); thisMenuItem.Click += new EventHandler(menuItem_Click); into.Add(thisMenuItem); if (menuItemTemplate.HasDropDownItems) AddExtraMenuItems(menuItemTemplate.DropDownItems.Cast().ToList(), thisMenuItem.DropDownItems); } void menuItem_Click(object sender, EventArgs e) { TIToolStripMenuItem menuItem = sender as TIToolStripMenuItem; if (null != menuItem) { menuItem.RowData = _mostRecentRowData; menuItem.ClickedCode(menuItem, this); } } private void ToggleSymbolSpecificMenuItems(bool symbolSpecific, string symbol = "") { foreach (TIToolStripMenuItem menuItem in contextMenuStrip1.Items.OfType().Where(x => x.IsSymbolSpecific)) { if (symbol != "") menuItem.Symbol = symbol; menuItem.Visible = symbolSpecific; } } public void selectTheFont() { var changedFontSettings = Font != GuiEnvironment.FontSettings; Font = GuiEnvironment.FontSettings; // Note that changing the form's font applies to the rows only because // we set the font property to null in the style object for each row. contextMenuStrip1.Font = Font; comboMenu.selectTheFont(this.Font.FontFamily.Name, this.Font.SizeInPoints); cbmColumns.selectTheFont(this.Font.FontFamily.Name, this.Font.SizeInPoints); elementHost1.Height = (int)(this.FontHeight); double wpfDropdown = Math.Max(comboMenu.getActualHeight(), cbmColumns.getActualHeight()); //actual height rendered of the wpf control when rendered tableLayoutPanelStrategies.Height = (int)(wpfDropdown) + 6; dataGridView1.Top = tableLayoutPanelStrategies.Bottom; if (_isTopPanelHidden) { tableLayoutPanelStrategies.Visible = false; picBoxHide.Visible = false; pnlHider.Visible = false; } //The meaning of the _isTopPanelHidden attribute, with the "Show Treemap Sort Options" option is slightly different. //If its value is false, this indicates that the top panel is not displayed in any case. //If its value is true, whether or not to show the top panel depends on the current view and the options selected for //the showStrategiesStripMenuItem.Checked and showTreemapSortMenu.Checked properties. else if ((showStrategiesStripMenuItem.Checked && !showTreemapToolStripMenuItem.Checked) || (showTreemapSortMenu.Checked && showTreemapToolStripMenuItem.Checked)) { tableLayoutPanelStrategies.Visible = true; picBoxHide.Visible = true; pnlHider.Visible = true; } DataGridViewHelper.SetRowHeight(dataGridView1, _symbolPlusLayout); if (changedFontSettings) RedrawColumns(); } public void setGridLines() { if (GuiEnvironment.ShowGridLines) { dataGridView1.CellBorderStyle = DataGridViewCellBorderStyle.Single; } else { dataGridView1.CellBorderStyle = DataGridViewCellBorderStyle.None; } } public void chooseRowSelect() { if (GuiEnvironment.HighlightGridRow) { dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect; } else { dataGridView1.SelectionMode = DataGridViewSelectionMode.CellSelect; } } /// /// Make sure we've requested enough data that we can satisfy the current color /// request. If someone says to use the price field to set the color, make sure /// we're requesting the price field. This might cause us to send a new data /// request. We assume that's expensive and try not to do it unless we have to. /// private void VerifyColorData() { // Start simple for now. Always assume we need to do the work. Presumably do more later ReRequestData(); } void _gradientColorChooser_OnChange() { if (!IsDisposed) { UpdateColors(); VerifyColorData(); } } private void UpdateColors() { if (dataGridView1.Rows.Count != _boundList.Count) return; int rowNumber = 0; foreach (RowData row in _boundList) { GradientColorChooser.ColorPair? colorPair = _gradientColorChooser.GetColors(row); { DataGridViewCellStyle style = dataGridView1.DefaultCellStyle.Clone(); style.Alignment = DataGridViewContentAlignment.NotSet; if (null != colorPair) { style.BackColor = colorPair.Value.BackGround; style.ForeColor = colorPair.Value.ForeGround; } // Set the font to null here, and then the row will use the form's font. // It seems like we should be able to say // "dataGridView1.DefaultCellStyle.Font = null", but that does nothing. // The operation is ignored, and the default font remains the same. style.Font = null; dataGridView1.Rows[rowNumber].DefaultCellStyle = style; } rowNumber++; } } public void SaveLayout(XmlNode parent) { if (null != PreSaveLayoutCode) PreSaveLayoutCode(this); XmlNode description = LayoutManager.SaveBase(parent, this, FORM_TYPE, ActualSize); if (null != SaveLayoutCode) SaveLayoutCode(this, description); if (null != _config) { // Need to add omh back to the config string for backward compatibilty. if (_selectedLiveAlways) description.SetProperty("CONFIG", _config + "&omh=1"); else description.SetProperty("CONFIG", _config); } if (_lastHistoricalTime != null) description.SetProperty("HISTORY_TIME", ServerFormats.ToTimeT(_lastHistoricalTime)); description.SetProperty("OUTSIDE_MARKET_HOURS", liveAlwaysToolStripMenuItem.Checked.ToString()); _gradientColorChooser.Save(description.NewNode("COLORS")); description.SetProperty("MAX_RECORD_COUNT", _recordCount); description.SetProperty("CONNECTION", GuiEnvironment.ConnectionName(_connectionMaster)); description.SetProperty("LOCALSORTENABLED", _localSortEnabled); if (null != _selectedSortColumnInfo) description.SetProperty("LOCALSORTCODE", _selectedSortColumnInfo.InternalCode); description.SetProperty("LOCALSORTTYPE", _localSortType.ToString()); description.SetProperty("COLUMN_LOCKED", _columnLocked.ToString()); description.SetProperty("MENU_ITEM_ENABLED", _lockMenuItemEnabled.ToString()); description.SetProperty("SMALL_BORDERS", smallBordersToolStripMenuItem.Checked.ToString()); description.SetProperty("TEXTHEADERS", _textHeaders); description.SetProperty("SELECTED_LIVE_ALWAYS", _selectedLiveAlways); description.SetProperty("PINNED", _pinned); description.SetProperty("IS_TOP_PANEL_HIDDEN", _isTopPanelHidden.ToString()); description.SetProperty("IS_STRATEGIES_COMBO_VISIBLE", showStrategiesStripMenuItem.Checked.ToString()); description.SetProperty("IS_COLUMNS_COMBO_VISIBLE", showTreemapSortMenu.Checked.ToString()); description.SetProperty("SCORE_CYLINDER_SHOWN", chart1.Visible); if (null != _cylinderColumnInfo) description.SetProperty("SCORE_CYLINDER_CODE", _cylinderColumnInfo.InternalCode); if (_cylinderThreshold.HasValue) description.SetProperty("SCORE_CYLINDER_THRESHOLD", _cylinderThreshold.Value); _columnListState.LoadFrom(dataGridView1); _columnListState.SaveTo(description.NewNode("COLUMNS")); //save the SymbolLinking settings... _symbolLinking.Save(description); XmlNode columnColors = description.NewNode("COLUMN_COLORS"); foreach (string internalCode in _columnColors.Keys) { XmlNode columnColor = columnColors.NewNode("COLUMN_COLOR"); columnColor.SetProperty("COLUMN", internalCode); GradientInfo gradientInfo = _columnColors[internalCode]; gradientInfo.Save(columnColor); } if (GuiEnvironment.RowDataExtensions.Count > 0) { XmlNode extensionsNode = description.NewNode("ROWDATA_EXTENSIONS"); foreach (IRowDataExtension rowDataExtension in GuiEnvironment.RowDataExtensions) { string extensionId = rowDataExtension.GetId(); XmlNode rowDataExtensionNode = extensionsNode.NewNode(extensionId); rowDataExtension.SaveToLayout(this, rowDataExtensionNode); } } // Save the symbol plus layout. description.SetProperty("SYMBOL_PLUS_LAYOUT", _symbolPlusLayout.ToString()); // Save the treemap chart settings. description.SetProperty("SHOW_TREEMAP_CHART", _useBrowserInterface); } private IRowDataExtension GetRowDataExtension(string id) { foreach (IRowDataExtension rowDataExtension in GuiEnvironment.RowDataExtensions) { if (rowDataExtension.GetId() == id) return rowDataExtension; } return null; } static public void RegisterLayout() { // change parameters to accommodate dock panel changes - RVH20210402 //LayoutManager.Instance().AddRestoreRule(FORM_TYPE, (RestoreLayout)delegate(XmlNode description, bool ignorePosition, bool cascadePosition) LayoutManager.Instance().AddRestoreRule(FORM_TYPE, (RestoreLayout)delegate (XmlNode description, bool ignorePosition, bool cascadePosition, bool dockPanelMode, string mainDockPanelName, string mainDockPanelTitle, string dockPanelID) { IConnectionMaster connectionMaster = GuiEnvironment.FindConnectionMaster(description.Property("CONNECTION")); if (null == connectionMaster) { // We could report an error here, but it's simpler just to do nothing. Any error message we tried to // report would probably be confusing at best to the user. } else { string config = description.Property("CONFIG", null); bool? outsideMarketHours = description.PropertyBool("OUTSIDE_MARKET_HOURS"); string initialHistoryTime = description.Property("HISTORY_TIME", null); string initialCount = description.Property("MAX_RECORD_COUNT", null); TopListForm topListForm = new TopListForm(connectionMaster, config, outsideMarketHours, initialHistoryTime, initialCount); // change parameters to accommodate dock panel changes - RVH20210402 //topListForm.Restore(description, ignorePosition, cascadePosition); topListForm.Restore(description, ignorePosition, cascadePosition, dockPanelMode, mainDockPanelName, mainDockPanelTitle, dockPanelID); topListForm.RestoredLayout = description; } }); } private bool _pinned = false; public bool Pinned { get { return _pinned; } set { _pinned = value; } } // change parameters to accommodate dock panel changes - RVH20210402 //public void Restore(XmlNode description, bool ignorePosition, bool cascadePosition) public void Restore(XmlNode description, bool ignorePosition, bool cascadePosition, bool dockPanelMode = false, string mainDockPanelName = "", string mainDockPanelTitle = "", string dockPanelID = "") { _restoringLayout = true; string config = description.Property("CONFIG", null); // change parameters to accommodate dock panel changes - RVH20210402 // LayoutManager.RestoreBase(description, this, ignorePosition, cascadePosition); LayoutManager.RestoreBase(description, this, ignorePosition, cascadePosition, null, false, dockPanelMode, mainDockPanelName, mainDockPanelTitle, dockPanelID); _dockPanelMode = dockPanelMode; // Retrieve the "small borders" settings before restoring form position must follow restore base // to avoid bug where loaded small border windows do not show the window icon in title bar // after deselecting small border setting. smallBordersToolStripMenuItem.Checked = description.Property("SMALL_BORDERS", false); // only call this if not in dock panel mode - RVH20210329 if (!_dockPanelMode) setBorders(); _gradientColorChooser.Load(description.Node("COLORS")); chart1.Visible = description.Property("SCORE_CYLINDER_SHOWN", false); showScoreCylinderToolStripMenuItem.Checked = chart1.Visible; _cylinderColumnCode = description.Property("SCORE_CYLINDER_CODE", ""); _cylinderThreshold = description.Property("SCORE_CYLINDER_THRESHOLD", 0); _columnListState.LoadFrom(description.Node("COLUMNS")); if (null != description.Property("TEXTHEADERS")) TextHeaders = description.Property("TEXTHEADERS", false); if (null != description.Property("LOCALSORTENABLED")) _localSortEnabled = bool.Parse(description.Property("LOCALSORTENABLED", "False")); if (null != description.Property("LOCALSORTTYPE")) _localSortType = (TopListSortType)Enum.Parse(typeof(TopListSortType), description.Property("LOCALSORTTYPE", "ServerSort")); if (null != description.Property("LOCALSORTCODE")) { _importSortBy = description.Property("LOCALSORTCODE"); _needToRestoreSortBy = true; } _pinned = description.Property("PINNED", false); pinnedToolStripMenuItem.Checked = _pinned; _selectedLiveAlways = description.Property("SELECTED_LIVE_ALWAYS", false); //for delayed data mode _columnLocked = description.Property("COLUMN_LOCKED", false); _lockMenuItemEnabled = description.Property("MENU_ITEM_ENABLED", false); //Now get the SymbolLinking settings.. _symbolLinking.Load(description); //restore custom column colors XmlNode columnColors = description.Node("COLUMN_COLORS"); if (null != columnColors) { foreach (XmlNode columnColor in columnColors.Enum()) { GradientInfo gradientInfo = new GradientInfo(columnColor); string internalCode = columnColor.Property("COLUMN", ""); if (_columnColors.ContainsKey(internalCode)) _columnColors[internalCode] = gradientInfo; else _columnColors.Add(internalCode, gradientInfo); } ApplyColumnColors(); } XmlNode extensionsNode = description.Node("ROWDATA_EXTENSIONS"); if (null != extensionsNode) { foreach (XmlNode extensionNode in extensionsNode.Enum()) { IRowDataExtension rowDataExtension = GetRowDataExtension(extensionNode.Name); if (null != rowDataExtension) rowDataExtension.RestoreFromLayout(this, extensionNode); } AddExtensionMenuItems(); } // Restore the symbol plus layout. Enum.TryParse(description.Property("SYMBOL_PLUS_LAYOUT", "Off"), out _symbolPlusLayout); // Restore treemap chart settings. _useBrowserInterface = description.Property("SHOW_TREEMAP_CHART", false); showTreemapToolStripMenuItem.Checked = _useBrowserInterface; _isTopPanelHidden = description.Property("IS_TOP_PANEL_HIDDEN", true); if (_isTopPanelHidden) { hideTopPanel(); } else { var isStrategiesVisible = description.Property("IS_STRATEGIES_COMBO_VISIBLE", false); var isColumnsVisible = description.Property("IS_COLUMNS_COMBO_VISIBLE", false); showTheTopPanel(isStrategiesVisible, isColumnsVisible); } if (_useBrowserInterface) ShowHideBrowserControl(); } private void ApplyColumnColors() { foreach (string internalCode in _columnColors.Keys) { ColumnInfo columnInfo = GetColumn(internalCode); if (null != columnInfo) columnInfo.GradientInfo = _columnColors[internalCode]; } } private ColumnInfo GetColumn(string internalCode) { if ((null != _metaData) && (null != _metaData.Columns)) { foreach (ColumnInfo column in _metaData.Columns) { if (column.InternalCode == internalCode) return column; } } return null; } /// /// Call this to request data from the server. The inputs get read from various /// places. _config is the collaborate string. Lots of things come directly from /// the menu items. /// private void ReRequestData() { SetConfiguration(_config); } void ICanRefreshNow.RefreshNow() { SetConfiguration(_config); } /// /// Add additional fields to config string. /// /// /// /// private string AddAdditionalFieldsToConfig(string config, List additionalColumns) { string newConfig = config; if (additionalColumns.Count > 0) { // Find last show parameter count. int showParamCt = 0; int showPOS = config.LastIndexOf("&show"); if (showPOS >= 0) { int equalPOS = config.IndexOf('=', showPOS); if (equalPOS >= 0) { string showParamNum = config.Substring(showPOS + 5, equalPOS - (showPOS + 5)); if (!int.TryParse(showParamNum, out showParamCt)) showParamCt = 0; } } // Add new fields to config string. int newParamCt = showParamCt; string newParameters = ""; foreach (ColumnInfo columnInfo in additionalColumns) { newParameters += $"&show{newParamCt}={columnInfo.InternalCode}"; newParamCt++; } // Update last column number. newConfig = newConfig.Replace($"&show{showParamCt}=", $"&show{newParamCt}="); // Insert new parameters into config string. newConfig = newConfig.Insert(showPOS, newParameters); } return newConfig; } /// /// Create a list of columns for those needed by the Symbol Plus column. /// /// private void CreateAdditionalSymbolPlusColumns(TopListInfo metaData) { _additionalSymbolPlusColumns.Clear(); // Check to see if Price is not already a field. If not, add it. if (metaData.Columns.FirstOrDefault(x => x.WireName == "c_Price") == null) { _additionalSymbolPlusColumns.Add(new ColumnInfo() { InternalCode = "Price", WireName = "c_Price", Description = "Price", Format = "N" }); } // Check to see if Change From Close Percent is not already a field. If not, add it. if (metaData.Columns.FirstOrDefault(x => x.WireName == "c_FCP") == null) { _additionalSymbolPlusColumns.Add(new ColumnInfo() { InternalCode = "FCP", WireName = "c_FCP", Description = "Change from the Close", Format = "N" }); } // Check to see if Change From Close Dollars is not already a field. If not, add it. if (metaData.Columns.FirstOrDefault(x => x.WireName == "c_FCD") == null) { _additionalSymbolPlusColumns.Add(new ColumnInfo() { InternalCode = "FCD", WireName = "c_FCD", Description = "Change from the Close", Format = "N" }); } } public void SetConfiguration(string config) { _metaData = null; if (null != _requestToken) { _requestToken.Cancel(); _requestToken = null; } if (null != _historyRequest) { _historyRequest.Stop(); _historyRequest.TopListData -= _historyRequest_TopListData; _historyRequest.TopListStatus -= _historyRequest_TopListStatus; _historyRequest = null; } if (_tokenStoreList.Any()) { ClearTokenList(); } _boundList.Clear(); _config = config; if (null == config) Text = "Top List"; else if (useHistoricalDateToolStripMenuItem.Checked) { // Append history and max count settings to config string string historyConfig = AddAdditionalFieldsToConfig(config, _additionalSymbolPlusColumns) + "&hist=1&exact_time=" + ServerFormats.ToTimeT(_lastHistoricalTime) + "&count=" + _recordCount; //string historyConfig = config + "&hist=1&exact_time=" + ServerFormats.ToTimeT(_lastHistoricalTime) + "&count=" + _recordCount; _historyRequest = _connectionMaster.TopListManager.GetTopList(historyConfig, true); _historyRequest.TopListData += new TopListData(_historyRequest_TopListData); _historyRequest.TopListStatus += new TopListStatus(_historyRequest_TopListStatus); _historyRequest.Start(); Text = ""; } else { TopListRequest topListRequest = new TopListRequest(); topListRequest.Collaborate = config; topListRequest.SaveToMru = true; topListRequest.Streaming = true; topListRequest.SkipMetaData = false; topListRequest.AddStandardRowDataColumns(); // Add additional compliment columns. foreach (ColumnInfo columnInfo in _additionalSymbolPlusColumns) { topListRequest.AddExtraColumn(columnInfo.InternalCode); } if (!_gradientColorChooser.FieldInternalCode.Equals("")) topListRequest.AddExtraColumn(_gradientColorChooser.FieldInternalCode); topListRequest.OutsideMarketHours = liveAlwaysToolStripMenuItem.Checked; topListRequest.ResultCount = _recordCount; _requestToken = topListRequest.Send(_sendManager, this); Text = ""; } } void _historyRequest_TopListData(List rows, DateTime? start, DateTime? end, TopList sender) { this.BeginInvokeIfRequired(delegate { if (_historyRequest != sender) // Old request. Ignore it. return; OnRowData(rows, start, end); }); } void TopListRequest.Listener.OnRowData(List rows, DateTime? start, DateTime? end, TopListRequest.Token requestToken) { this.BeginInvokeIfRequired((MethodInvoker)delegate { if (requestToken == _requestToken) { OnRowData(rows, start, end); requestToken.GetRequest().LastUpdate = DateTime.Now; } else if (_tokenStoreList.Any(rToken => rToken == requestToken)) { UpdateDataFromSingleSymbol(rows); requestToken.GetRequest().LastUpdate = DateTime.Now; } }); } void _historyRequest_TopListStatus(TopList sender) { this.BeginInvokeIfRequired(delegate { if (sender != _historyRequest) // Old request. Ignore it. return; OnMetaData(_historyRequest.TopListInfo); }); } void TopListRequest.Listener.OnMetaData(TopListRequest.Token requestToken) { this.BeginInvokeIfRequired((MethodInvoker)delegate { if (requestToken != _requestToken) // Old request. Ignore it. return; OnMetaData(requestToken.GetMetaData()); if (null != _config) requestToken.GetRequest().Collaborate = _config; }); } void TopListRequest.Listener.OnDisconnect(TopListRequest.Token requestToken) { this.BeginInvokeIfRequired((MethodInvoker)delegate { if (requestToken != _requestToken) // Old request. Ignore it. return; // Disconnected. Try again. _requestToken = requestToken.GetRequest().Send(_sendManager, this); }); } protected string Config { get { return _config; } set { SetConfiguration(value); } } private IList _previousColumns; private void OnMetaData(TopListInfo metaData) { //System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.fff tt") + " TopListStatus " + _topList.TopListInfo.WindowName); //GuiEnvironment.LogMessage("TopListStatus " + _topList.TopListInfo.WindowName); _metaData = metaData; if (null == _metaData) return; if (_metaData != null && _gridVisible) { foreach (Column column in dataGridView1.Columns) { updateMetadata(column); } } // Check to see if symbol plus is enabled. Add compliment columns, if it hasn't been done yet. if (GlobalDataSettings.GetDataGridColumnSetting(FORM_TYPE, "D_Symbol").Format == "symbolplus" && !_addedSymbolPlusColumns) { CreateAdditionalSymbolPlusColumns(metaData); _addedSymbolPlusColumns = true; if (_additionalSymbolPlusColumns.Count > 0) { SetConfiguration(_config); return; } } // We cannot use the metaData config string for creating the collaborate string. // The time for history from this config string if off by 3 hours in the future. if (!metaData.SameColumns(_previousColumns) || _serverSortField != metaData.ServerSort) { _serverSortField = metaData.ServerSort; _gradientColorChooser.SetColumnInfo(metaData.Columns); RedrawColumns(); if (!metaData.SameColumns(_previousColumns)) { cbmColumns.LoadColumns(_metaData.Columns.ToList(), _serverSortField); if (_needToUpDateTreemapSortOptions) { cbmColumns.UpdateSelectedOrderingColumn(_selectedSortColumnInfo, _localSortType, _serverSortField); _needToUpDateTreemapSortOptions = false; } } _previousColumns = metaData.Columns; } // Window setTitle(); } private Dictionary _originallyIcon = new Dictionary(); private void RedrawColumns() { if (null != _metaData) { if (null != GuiEnvironment.RowDataExtensions && GuiEnvironment.RowDataExtensions.Count > 0) { foreach (IRowDataExtension rowDataExtension in GuiEnvironment.RowDataExtensions) _metaData.Columns = rowDataExtension.HandleColumnInfo(this, _metaData.Columns); } _columnListState.LoadFrom(dataGridView1); dataGridView1.RowTemplate.DefaultCellStyle.Padding = new Padding(0); // Increase row height for Symbol Plus layout. DataGridViewHelper.SetRowHeight(dataGridView1, _symbolPlusLayout); dataGridView1.EnableHeadersVisualStyles = false; dataGridView1.Columns.Clear(); _filterMenuItems.Clear(); _contextMenuCylinder.MenuItems.Clear(); foreach (ColumnInfo columnInfo in _metaData.Columns) { // When in history mode, don't add the additional columns for Symbol+. if (useHistoricalDateToolStripMenuItem.Checked) { if (_additionalSymbolPlusColumns.Find(x => x.WireName == columnInfo.WireName) != null) { // Skip this column. continue; } } if (columnInfo.IsNumeric()) { MenuItem menuItemCylinder = new MenuItem(); menuItemCylinder.Text = columnInfo.Description; menuItemCylinder.Tag = columnInfo; menuItemCylinder.Click += menuItemCylinder_Click; _contextMenuCylinder.MenuItems.Add(menuItemCylinder); } if (_textHeaders) { if (!_originallyIcon.ContainsKey(columnInfo)) _originallyIcon.Add(columnInfo, columnInfo.TextHeader); columnInfo.TextHeader = true; } else { if (_originallyIcon.ContainsKey(columnInfo)) columnInfo.TextHeader = _originallyIcon[columnInfo]; } columnInfo.UseEmptyColor = false; DataGridViewColumn column = DataCell.GetColumn(columnInfo, _connectionMaster, this, null, _isFrozen, symbolPlusLayoutType: _symbolPlusLayout); column.HeaderCell.ContextMenuStrip = BuildColumnContextMenu(columnInfo); column.HeaderCell.ContextMenuStrip.Opening += ContextMenuStrip_Opening; if (_textHeaders) { column.HeaderCell.Style.WrapMode = DataGridViewTriState.True; } if (_isFrozen) { column.HeaderCell.Style.ForeColor = _frozenHeaderTextColor; } dataGridView1.Columns.Add(column); //We wish to keep the "Symbol" column frozen should user wish to do so //and *only* if it's the leftmost column. //We go by the DisplayIndex, as the DisplayIndex won't always //be equal to the column index. if (columnInfo.InternalCode == "D_Symbol" && column.DisplayIndex == 0 && _columnLocked) { column.Frozen = true; //for restoring layouts and duplicating window. } ApplyColumnColors(); } MenuItem customThresholdMenuItem = new MenuItem(); customThresholdMenuItem.Text = "Set Custom Threshold..."; customThresholdMenuItem.Click += customThresholdMenuItem_Click; _contextMenuCylinder.MenuItems.Add(customThresholdMenuItem); if (_needToRestoreSortBy) { ColumnInfo oldSortCodeImport = null; // Symbol was the only "special" column under the old format if (_importSortBy == "symbol") _importSortBy = "D_Symbol"; foreach (ColumnInfo columnInfo in _metaData.Columns) { if (columnInfo.InternalCode == _importSortBy) { _selectedSortColumnInfo = columnInfo; // Update treemap filter code and units. _chromiumTreemapControl.SetUnits(_selectedSortColumnInfo.Units); _chromiumTreemapControl.FilterCode = _selectedSortColumnInfo.WireName; break; } // Need to support old layouts that were saving the "c_" version of the codes. // It's probably not necessary but if by some miracle if both the old and new // sort codes exist we assume it's the new. else if (CommonAlertFields.GetFilterWireName(columnInfo.InternalCode) == _importSortBy) { oldSortCodeImport = columnInfo; } } _needToRestoreSortBy = false; if (_selectedSortColumnInfo == null) _selectedSortColumnInfo = oldSortCodeImport; // revert to server sort if we fail to restore the listed sort code if (_selectedSortColumnInfo != null) { cbmColumns.UpdateSelectedOrderingColumn(_selectedSortColumnInfo, _localSortType, _serverSortField); } if (_selectedSortColumnInfo == null) { _localSortEnabled = false; _localSortType = TopListSortType.ServerSort; ColumnInfo columnInfo = _metaData.Columns.FirstOrDefault(column => column.InternalCode == _serverSortField); if (columnInfo != null) cbmColumns.UpdateSelectedOrderingColumn(columnInfo, _localSortType, _serverSortField); } } if (_lockColumnMenuItem != null) { _lockColumnMenuItem.Enabled = _lockMenuItemEnabled; } // add parameter for form type - RVH20210528 // _columnListState.SaveTo(dataGridView1); _columnListState.SaveTo(dataGridView1, FORM_TYPE); FixSortMenuItems(); var symbolColumn = dataGridView1.Columns.Cast().FirstOrDefault(gridColumn => gridColumn.ColumnInfo.Format == "symbolplus"); if (symbolColumn != null) { var adjustedColumn = DataCell.GetColumn(symbolColumn.ColumnInfo, _connectionMaster, this, symbolPlusLayoutType: _symbolPlusLayout); dataGridView1.Columns[symbolColumn.Index].MinimumWidth = adjustedColumn.MinimumWidth; if (!_restoringLayout && _adjustSymbolPlus) { dataGridView1.Columns[symbolColumn.Index].Width = adjustedColumn.MinimumWidth; _adjustSymbolPlus = false; if (_metaData != null) updateMetadata(symbolColumn); } else if (_restoringLayout) { var width = _columnListState.GetColumnStateWidth(symbolColumn.ColumnInfo.InternalCode); if (width != -1) dataGridView1.Columns[symbolColumn.Index].Width = width; _restoringLayout = false; if (_metaData != null) updateMetadata(symbolColumn); } } } } void ContextMenuStrip_Opening(object sender, CancelEventArgs e) { ContextMenuStrip menuStrip = sender as ContextMenuStrip; if (null != menuStrip) GuiEnvironment.EnforceLimitedMode(menuStrip); } private double? _cylinderThreshold = 0; void customThresholdMenuItem_Click(object sender, EventArgs e) { ScoreCylinderThresholdForm scoreThresholdForm = new ScoreCylinderThresholdForm(); scoreThresholdForm.Threshold = _cylinderThreshold; DialogResult result = scoreThresholdForm.ShowDialog(); if (result == System.Windows.Forms.DialogResult.OK) { _cylinderThreshold = scoreThresholdForm.Threshold; UpdateCylinderChart(); } scoreThresholdForm.Dispose(); } void menuItemCylinder_Click(object sender, EventArgs e) { MenuItem menuItem = sender as MenuItem; if (null != menuItem) { ColumnInfo columnInfo = menuItem.Tag as ColumnInfo; if (null != columnInfo) { _cylinderColumnInfo = columnInfo; _cylinderColumnCode = columnInfo.InternalCode; _cylinderThreshold = GetScoreCylinderThreshold(columnInfo); UpdateCylinderChart(); } } } private double GetScoreCylinderThreshold(ColumnInfo columnInfo) { List filterDefaultColors = GuiEnvironment.XmlConfig.Node("FILTER_DEFAULT_COLORS"); List customColorNode = filterDefaultColors.Node("COLORS").Where(x => x.Property("COLUMN") == columnInfo.InternalCode).ToList(); if (customColorNode.Count > 0 && customColorNode.Node("COLORS").Count > 0) { List colorsNode = customColorNode.Node("COLORS"); List valueSet = new List(); foreach (XmlElement rowNode in colorsNode.Enum()) { double value = rowNode.Property("VALUE", 0.00); valueSet.Add(value); } if (valueSet.Count == 5) return valueSet[2]; } return 0; } private ToolTip toolTipForChart = new ToolTip(); private void UpdateCylinderChart() { if (null == _metaData) return; if (null == _cylinderColumnInfo && _metaData.Columns.Where(x => x.IsNumeric()).Count() > 0) { if (_cylinderColumnCode == "") _cylinderColumnInfo = _metaData.Columns.Where(x => x.IsNumeric()).First(); else if (_metaData.Columns.Where(x => x.InternalCode == _cylinderColumnCode).Count() > 0) _cylinderColumnInfo = _metaData.Columns.Where(x => x.InternalCode == _cylinderColumnCode).First(); } if (null != _cylinderColumnInfo) { double threshold = 0; if (_cylinderThreshold.HasValue) threshold = _cylinderThreshold.Value; int numberPositive = _boundList.Where(x => x.GetAsDouble(_cylinderColumnInfo.WireName, _cylinderColumnInfo.WireName + "CACHE") > threshold).Count(); int numberNegative = _boundList.Where(x => x.GetAsDouble(_cylinderColumnInfo.WireName, _cylinderColumnInfo.WireName + "CACHE") < threshold).Count(); int total = numberPositive + numberNegative; // Under some conditions (yesterday is a holiday, so no data) total is zero. Return to avoid errors in the chart library if (total == 0) return; // Catch "Object reference not set to an instance of an object." exception try { double percentPositive = (double)numberPositive / total * 100; chart1.Series[0].Points[0].YValues[0] = percentPositive; chart1.Series[1].Points[0].YValues[0] = 100 - percentPositive; chart1.Series[0].Label = "#VALY{0.##}" + "%"; chart1.Series[1].Label = "#VALY{0.##}" + "%"; string toolTipText = _cylinderColumnInfo.Description + "\nAbove: " + numberPositive + "\nBelow: " + numberNegative + "\nTotal: " + total + "\nCurrent Threshold: " + threshold; toolTipForChart.SetToolTip(chart1, toolTipText); chart1.Invalidate(); } catch (Exception ex) { string test = ex.Message; //for testing } } foreach (MenuItem menuItem in _contextMenuCylinder.MenuItems) { ColumnInfo menuColumnInfo = menuItem.Tag as ColumnInfo; if (null != menuColumnInfo && _cylinderColumnInfo == menuColumnInfo) menuItem.Checked = true; else menuItem.Checked = false; } } private void sendSymbolToToolStripMenuItem_MouseEnter(object sender, EventArgs e) { if (sendSymbolToToolStripMenuItem.DropDownItems.Count == 1) /*We have the "dummy" item there just to make it look like the "send to" item has children*/ { sendSymbolToToolStripMenuItem.DropDownItems.Clear(); _sendToContextMenu = new makeSendToContextMenu(this, this, sendSymbolToToolStripMenuItem, _mostRecentRowData, _sendManager, _config, _connectionMaster); if (_selectedRowsList.Count > 1) _sendToContextMenu.setRowDataList(_selectedRowsList); } else { if (_selectedRowsList.Count > 1) _sendToContextMenu.setRowDataList(_selectedRowsList); else { string exchange = _mostRecentRowData.GetExchange(); string symbol = _mostRecentRowData.GetSymbol(); _sendToContextMenu.setRowData(_mostRecentRowData, _config, symbol, exchange, _connectionMaster); } } } private System.Windows.Forms.ContextMenuStrip BuildColumnContextMenu(ColumnInfo columnInfo) { System.Windows.Forms.ContextMenuStrip contextMenu = new System.Windows.Forms.ContextMenuStrip(); contextMenu.Opened += new EventHandler(contextMenu_Opened); if (columnInfo.InternalCode == "D_Symbol" && columnInfo.Format == "symbolplus") { // Add symbol plus layout options. ToolStripMenuItem symbolPlusMenuItem = new ToolStripMenuItem("Symbol Plus"); symbolPlusMenuItem.Checked = _symbolPlusLayout != DataCell.SymbolPlusLayoutType.Off; symbolPlusMenuItem.DropDownOpening += SymbolPlusMenuItem_DropDownOpening; symbolPlusMenuItem.Click += delegate { if (symbolPlusMenuItem.Checked) { var selectedOption = symbolPlusMenuItem.DropDownItems.Cast().ToList().FirstOrDefault(item => item.Checked); if (selectedOption != null) selectedOption.PerformClick(); } else { var normalOption = symbolPlusMenuItem.DropDownItems.Cast().ToList().FirstOrDefault(item => item.Text == "Normal"); if (normalOption != null) normalOption.PerformClick(); } }; contextMenu.Items.Add(symbolPlusMenuItem); } if (columnInfo.IsNumeric()) { TIContentMenuItem helpLinkMenuItem = new TIContentMenuItem(columnInfo.InternalCode, GuiEnvironment.XmlConfig.Node("TOPLIST_WINDOW").Node("PHRASES").Node("CONTEXT_MENUITEM_GOTO_HELP").PropertyForCulture("TEXT", "***") + columnInfo.Description, columnInfo); helpLinkMenuItem.OnClick += new TIContextMenuItemClicked(helpLinkMenuItem_OnClick); contextMenu.Items.Add(helpLinkMenuItem); TIContentMenuItem configurationMenuItem = new TIContentMenuItem(columnInfo.InternalCode, GuiEnvironment.XmlConfig.Node("TOPLIST_WINDOW").Node("PHRASES").Node("CONTEXT_MENUITEM_CONFIGURE").PropertyForCulture("TEXT", "***") + columnInfo.Description, columnInfo); configurationMenuItem.OnClick += new TIContextMenuItemClicked(configurationMenuItem_OnClick); contextMenu.Items.Add(configurationMenuItem); TIContentMenuItem serverSortOnMenuItem = new TIContentMenuItem(columnInfo.InternalCode, GuiEnvironment.XmlConfig.Node("TOPLIST_WINDOW").Node("PHRASES").Node("CONTEXT_MENUITEM_SERVERSORT_ON").PropertyForCulture("TEXT", "***") + columnInfo.Description, columnInfo); serverSortOnMenuItem.OnClick += new TIContextMenuItemClicked(serverSortOnMenuItem_OnClick); contextMenu.Items.Add(serverSortOnMenuItem); TIContentMenuItem colorColumnMenuItem = new TIContentMenuItem(columnInfo.InternalCode, "Custom Colors for " + columnInfo.Description, columnInfo); colorColumnMenuItem.Click += colorColumnMenuItem_Click; colorColumnMenuItem.Image = TopListColorChooser.WindowIconCache.MenuImage; contextMenu.Items.Add(colorColumnMenuItem); // New menu item for Real-Time Stock Race - RVH20211112 ToolStripSeparator filterMenuSeparator1 = new ToolStripSeparator(); contextMenu.Items.Add(filterMenuSeparator1); TIContentMenuItem realTimeStockRaceColumnMenuItem = new TIContentMenuItem(columnInfo.InternalCode, "Launch Real-Time Stock Race for " + columnInfo.Description, columnInfo); realTimeStockRaceColumnMenuItem.Click += realTimeStockRaceColumnMenuItem_Click; contextMenu.Items.Add(realTimeStockRaceColumnMenuItem); ToolStripSeparator filterMenuSeparator2 = new ToolStripSeparator(); contextMenu.Items.Add(filterMenuSeparator2); } string sortDescendingMenuItemText = GuiEnvironment.XmlConfig.Node("TOPLIST_WINDOW").Node("PHRASES").Node("CONTEXT_MENUITEM_SORTBY").PropertyForCulture("TEXT", "***") + columnInfo.Description + " " + GuiEnvironment.XmlConfig.Node("TOPLIST_WINDOW").Node("PHRASES").Node("DESCENDING").PropertyForCulture("TEXT", "***"); string sortAscendingMenuItemText = GuiEnvironment.XmlConfig.Node("TOPLIST_WINDOW").Node("PHRASES").Node("CONTEXT_MENUITEM_SORTBY").PropertyForCulture("TEXT", "***") + columnInfo.Description + " " + GuiEnvironment.XmlConfig.Node("TOPLIST_WINDOW").Node("PHRASES").Node("ASCENDING").PropertyForCulture("TEXT", "***"); string serverSortMenuItemText = GuiEnvironment.XmlConfig.Node("TOPLIST_WINDOW").Node("PHRASES").Node("CONTEXT_MENUITEM_SERVERSORT").PropertyForCulture("TEXT", "***"); Dictionary sortMenuItems = new Dictionary(); if (!_filterMenuItems.ContainsKey(columnInfo.InternalCode)) { _filterMenuItems.Add(columnInfo.InternalCode, sortMenuItems); } TIContentSortMenuItem sortbyDescendingMenuItem = new TIContentSortMenuItem(sortDescendingMenuItemText, columnInfo, TopListSortType.Descending); sortbyDescendingMenuItem.OnClick += new TIContextSortMenuItemClicked(sortbyMenuItem_OnClick); sortbyDescendingMenuItem.Enabled = true; contextMenu.Items.Add(sortbyDescendingMenuItem); sortMenuItems.Add(TopListSortType.Descending, sortbyDescendingMenuItem); TIContentSortMenuItem sortbyAscendingMenuItem = new TIContentSortMenuItem(sortAscendingMenuItemText, columnInfo, TopListSortType.Ascending); sortbyAscendingMenuItem.OnClick += new TIContextSortMenuItemClicked(sortbyMenuItem_OnClick); sortbyAscendingMenuItem.Enabled = true; contextMenu.Items.Add(sortbyAscendingMenuItem); sortMenuItems.Add(TopListSortType.Ascending, sortbyAscendingMenuItem); if (columnInfo.InternalCode == "D_Symbol") { if (columnInfo.Format == "symbolplus" && (_symbolPlusLayout == DataCell.SymbolPlusLayoutType.Normal || _symbolPlusLayout == DataCell.SymbolPlusLayoutType.NoLogo)) { // Get Price ColumnInfo. ColumnInfo priceColumnInfo = _metaData.Columns.FirstOrDefault(x => x.WireName == "c_Price"); if (priceColumnInfo == null) priceColumnInfo = _additionalSymbolPlusColumns.FirstOrDefault(x => x.WireName == "c_Price"); if (priceColumnInfo != null) { Dictionary sortPriceMenuItems = new Dictionary(); if (!_filterMenuItems.ContainsKey(columnInfo.InternalCode + "|" + priceColumnInfo.InternalCode)) { _filterMenuItems.Add(columnInfo.InternalCode + "|" + priceColumnInfo.InternalCode, sortPriceMenuItems); } // Add sorting options for Price. TIContentSortMenuItem sortbyPriceDescendingMenuItem = new TIContentSortMenuItem($"Sort by {priceColumnInfo.Description} Descending", priceColumnInfo, TopListSortType.Descending); sortbyPriceDescendingMenuItem.OnClick += new TIContextSortMenuItemClicked(sortbyMenuItem_OnClick); sortbyPriceDescendingMenuItem.Enabled = true; contextMenu.Items.Add(sortbyPriceDescendingMenuItem); sortPriceMenuItems.Add(TopListSortType.Descending, sortbyPriceDescendingMenuItem); TIContentSortMenuItem sortbyPriceAscendingMenuItem = new TIContentSortMenuItem($"Sort by {priceColumnInfo.Description} Ascending", priceColumnInfo, TopListSortType.Ascending); sortbyPriceAscendingMenuItem.OnClick += new TIContextSortMenuItemClicked(sortbyMenuItem_OnClick); sortbyPriceAscendingMenuItem.Enabled = true; contextMenu.Items.Add(sortbyPriceAscendingMenuItem); sortPriceMenuItems.Add(TopListSortType.Ascending, sortbyPriceAscendingMenuItem); } // Get Change from Close ($) ColumnInfo. ColumnInfo changeFromCloseDollarsColumnInfo = _metaData.Columns.FirstOrDefault(x => x.WireName == "c_FCD"); if (changeFromCloseDollarsColumnInfo == null) changeFromCloseDollarsColumnInfo = _additionalSymbolPlusColumns.FirstOrDefault(x => x.WireName == "c_FCD"); if (changeFromCloseDollarsColumnInfo != null) { Dictionary sortChangeFromCloseDollarsMenuItems = new Dictionary(); if (!_filterMenuItems.ContainsKey(columnInfo.InternalCode + "|" + changeFromCloseDollarsColumnInfo.InternalCode)) { _filterMenuItems.Add(columnInfo.InternalCode + "|" + changeFromCloseDollarsColumnInfo.InternalCode, sortChangeFromCloseDollarsMenuItems); } // Add sorting options for Change from Close ($). TIContentSortMenuItem sortbyPriceDescendingMenuItem = new TIContentSortMenuItem($"Sort by {changeFromCloseDollarsColumnInfo.Description} ($) Descending", changeFromCloseDollarsColumnInfo, TopListSortType.Descending); sortbyPriceDescendingMenuItem.OnClick += new TIContextSortMenuItemClicked(sortbyMenuItem_OnClick); sortbyPriceDescendingMenuItem.Enabled = true; contextMenu.Items.Add(sortbyPriceDescendingMenuItem); sortChangeFromCloseDollarsMenuItems.Add(TopListSortType.Descending, sortbyPriceDescendingMenuItem); TIContentSortMenuItem sortbyPriceAscendingMenuItem = new TIContentSortMenuItem($"Sort by {changeFromCloseDollarsColumnInfo.Description} ($) Ascending", changeFromCloseDollarsColumnInfo, TopListSortType.Ascending); sortbyPriceAscendingMenuItem.OnClick += new TIContextSortMenuItemClicked(sortbyMenuItem_OnClick); sortbyPriceAscendingMenuItem.Enabled = true; contextMenu.Items.Add(sortbyPriceAscendingMenuItem); sortChangeFromCloseDollarsMenuItems.Add(TopListSortType.Ascending, sortbyPriceAscendingMenuItem); } // Get Change from Close (%) ColumnInfo. ColumnInfo changeFromClosePercentColumnInfo = _metaData.Columns.FirstOrDefault(x => x.WireName == "c_FCP"); if (changeFromClosePercentColumnInfo == null) changeFromClosePercentColumnInfo = _additionalSymbolPlusColumns.FirstOrDefault(x => x.WireName == "c_FCP"); if (changeFromClosePercentColumnInfo != null) { Dictionary sortChangeFromClosePercentMenuItems = new Dictionary(); if (!_filterMenuItems.ContainsKey(columnInfo.InternalCode + "|" + changeFromClosePercentColumnInfo.InternalCode)) { _filterMenuItems.Add(columnInfo.InternalCode + "|" + changeFromClosePercentColumnInfo.InternalCode, sortChangeFromClosePercentMenuItems); } // Add sorting options for Change from Close ($). TIContentSortMenuItem sortbyPriceDescendingMenuItem = new TIContentSortMenuItem($"Sort by {changeFromClosePercentColumnInfo.Description} (%) Descending", changeFromClosePercentColumnInfo, TopListSortType.Descending); sortbyPriceDescendingMenuItem.OnClick += new TIContextSortMenuItemClicked(sortbyMenuItem_OnClick); sortbyPriceDescendingMenuItem.Enabled = true; contextMenu.Items.Add(sortbyPriceDescendingMenuItem); sortChangeFromClosePercentMenuItems.Add(TopListSortType.Descending, sortbyPriceDescendingMenuItem); TIContentSortMenuItem sortbyPriceAscendingMenuItem = new TIContentSortMenuItem($"Sort by {changeFromClosePercentColumnInfo.Description} (%) Ascending", changeFromClosePercentColumnInfo, TopListSortType.Ascending); sortbyPriceAscendingMenuItem.OnClick += new TIContextSortMenuItemClicked(sortbyMenuItem_OnClick); sortbyPriceAscendingMenuItem.Enabled = true; contextMenu.Items.Add(sortbyPriceAscendingMenuItem); sortChangeFromClosePercentMenuItems.Add(TopListSortType.Ascending, sortbyPriceAscendingMenuItem); } } _lockColumnMenuItem = new ToolStripMenuItem(GuiEnvironment.XmlConfig.Node("TOPLIST_WINDOW").Node("PHRASES").Node("LOCK_COLUMN").PropertyForCulture("TEXT", "***")); _lockColumnMenuItem.Click += new System.EventHandler(columnLockMenuItem_OnClick); if (_columnLocked) //for restoring layouts, duplication-preservataion of state { _lockColumnMenuItem.Checked = true; } contextMenu.Items.Add(_lockColumnMenuItem); } TIContentSortMenuItem serverSortMenuItem = new TIContentSortMenuItem(serverSortMenuItemText, columnInfo, TopListSortType.ServerSort); serverSortMenuItem.OnClick += new TIContextSortMenuItemClicked(sortbyMenuItem_OnClick); serverSortMenuItem.Enabled = true; contextMenu.Items.Add(serverSortMenuItem); sortMenuItems.Add(TopListSortType.ServerSort, serverSortMenuItem); return contextMenu; } private void SymbolPlusMenuItem_DropDownOpening(object sender, EventArgs e) { ToolStripMenuItem toolStripMenuItem = sender as ToolStripMenuItem; if (toolStripMenuItem != null) { toolStripMenuItem.DropDownItems.Clear(); ToolStripMenuItem toolStripMenuItem1 = new ToolStripMenuItem("Normal"); toolStripMenuItem1.Click += delegate { if (!toolStripMenuItem1.Checked) { DataGridViewHelper.SetSymbolPlusLayout(dataGridView1, DataCell.SymbolPlusLayoutType.Normal); _symbolPlusLayout = DataCell.SymbolPlusLayoutType.Normal; } else { DataGridViewHelper.SetSymbolPlusLayout(dataGridView1, DataCell.SymbolPlusLayoutType.Off); _symbolPlusLayout = DataCell.SymbolPlusLayoutType.Off; } _adjustSymbolPlus = true; RedrawColumns(); UpdateColors(); }; ToolStripMenuItem toolStripMenuItem2 = new ToolStripMenuItem("Logo Only"); toolStripMenuItem2.Click += delegate { if (!toolStripMenuItem2.Checked) { DataGridViewHelper.SetSymbolPlusLayout(dataGridView1, DataCell.SymbolPlusLayoutType.LogoOnly); _symbolPlusLayout = DataCell.SymbolPlusLayoutType.LogoOnly; } else { DataGridViewHelper.SetSymbolPlusLayout(dataGridView1, DataCell.SymbolPlusLayoutType.Off); _symbolPlusLayout = DataCell.SymbolPlusLayoutType.Off; } _adjustSymbolPlus = true; RedrawColumns(); UpdateColors(); }; ToolStripMenuItem toolStripMenuItem3 = new ToolStripMenuItem("No Logo"); toolStripMenuItem3.Click += delegate { if (!toolStripMenuItem3.Checked) { DataGridViewHelper.SetSymbolPlusLayout(dataGridView1, DataCell.SymbolPlusLayoutType.NoLogo); _symbolPlusLayout = DataCell.SymbolPlusLayoutType.NoLogo; } else { DataGridViewHelper.SetSymbolPlusLayout(dataGridView1, DataCell.SymbolPlusLayoutType.Off); _symbolPlusLayout = DataCell.SymbolPlusLayoutType.Off; } _adjustSymbolPlus = true; RedrawColumns(); UpdateColors(); }; ToolStripMenuItem toolStripMenuItem4 = new ToolStripMenuItem("Symbol/Logo"); toolStripMenuItem4.Click += delegate { if (!toolStripMenuItem4.Checked) { DataGridViewHelper.SetSymbolPlusLayout(dataGridView1, DataCell.SymbolPlusLayoutType.SymbolLogo); _symbolPlusLayout = DataCell.SymbolPlusLayoutType.SymbolLogo; } else { DataGridViewHelper.SetSymbolPlusLayout(dataGridView1, DataCell.SymbolPlusLayoutType.Off); _symbolPlusLayout = DataCell.SymbolPlusLayoutType.Off; } _adjustSymbolPlus = true; RedrawColumns(); UpdateColors(); }; if (_symbolPlusLayout == DataCell.SymbolPlusLayoutType.Normal) toolStripMenuItem1.Checked = true; else if (_symbolPlusLayout == DataCell.SymbolPlusLayoutType.LogoOnly) toolStripMenuItem2.Checked = true; else if (_symbolPlusLayout == DataCell.SymbolPlusLayoutType.NoLogo) toolStripMenuItem3.Checked = true; else if (_symbolPlusLayout == DataCell.SymbolPlusLayoutType.SymbolLogo) toolStripMenuItem4.Checked = true; toolStripMenuItem.DropDownItems.Add(toolStripMenuItem1); toolStripMenuItem.DropDownItems.Add(toolStripMenuItem2); toolStripMenuItem.DropDownItems.Add(toolStripMenuItem3); toolStripMenuItem.DropDownItems.Add(toolStripMenuItem4); } } void colorColumnMenuItem_Click(object sender, EventArgs e) { TIContentMenuItem colorColumnMenuItem = sender as TIContentMenuItem; if (null != colorColumnMenuItem) { ColumnInfo columnInfo = colorColumnMenuItem.ColumnInfo; GradientColorChooser colorChooser = new GradientColorChooser(); GradientInfo gradientInfo = new GradientInfo(); if (_columnColors.ContainsKey(columnInfo.InternalCode)) gradientInfo = _columnColors[columnInfo.InternalCode]; colorChooser.GradientInfo = gradientInfo; TopListColorChooser configureColorsForm = new TopListColorChooser(_connectionMaster); configureColorsForm.LimitToColumn(columnInfo); configureColorsForm.GradientColorChooser = colorChooser; configureColorsForm.ShowDialog(); if (configureColorsForm.DialogResult == System.Windows.Forms.DialogResult.OK) { if (_columnColors.ContainsKey(columnInfo.InternalCode)) _columnColors[columnInfo.InternalCode] = colorChooser.GradientInfo; else _columnColors.Add(columnInfo.InternalCode, colorChooser.GradientInfo); columnInfo.GradientInfo = colorChooser.GradientInfo; UpdateColors(); dataGridView1.Invalidate(); } configureColorsForm.Dispose(); } } void realTimeStockRaceColumnMenuItem_Click(object sender, EventArgs e) { TIContentMenuItem realTimeStockRaceColumnMenuItem = sender as TIContentMenuItem; if (null != realTimeStockRaceColumnMenuItem) { ColumnInfo columnInfo = realTimeStockRaceColumnMenuItem.ColumnInfo; GuiEnvironment.OpenRealTimeStockRace(true, false, false, _config, TopListSortType.DeltaValue, _selectedSortColumnInfo != null ? _selectedSortColumnInfo.WireName : "", columnInfo.WireName, this); } } private void contextMenu_Opened(object sender, EventArgs e) { System.Windows.Forms.ContextMenuStrip test = (System.Windows.Forms.ContextMenuStrip)sender; test.Font = Font; } void sortbyMenuItem_OnClick(TIContentMenuItem menuItem, string internalCode, ColumnInfo columnInfo, TopListSortType sortType) { _localSortType = sortType; if (sortType == TopListSortType.Descending) { GuiEnvironment.RecordUseCase("TopList.ColumnHeader.RightClick.Descending", _sendManager); _localSortEnabled = true; _selectedSortColumnInfo = columnInfo; menuItem.Checked = true; } else if (sortType == TopListSortType.Ascending) { GuiEnvironment.RecordUseCase("TopList.ColumnHeader.RightClick.Ascending", _sendManager); _localSortEnabled = true; _selectedSortColumnInfo = columnInfo; menuItem.Checked = true; } else if (sortType == TopListSortType.ServerSort) { GuiEnvironment.RecordUseCase("TopList.ColumnHeader.RightClick.UseServerSort", _sendManager); _localSortEnabled = false; _selectedSortColumnInfo = columnInfo; menuItem.Checked = true; } DoSorting(); } void columnLockMenuItem_OnClick(object sender, EventArgs e) { GuiEnvironment.RecordUseCase("TopList.ColumnHeader.RightClick.LockColumn", _sendManager); // header row was single clicked on - //Column column = (Column)dataGridView1.Columns[e.ColumnIndex]; /*We want to look at the *display index*-that is the index of the column that's visible to the user. (The display index might not necessarily be equal to e.ColumnIndex). We only wish to toggle between frozen and unfrozen if the Visible Index of the Symbol column is zero -which is at the leftmost column of the TopList window. The purpose to toggle is that once the column is frozen it cannot be moved. The user might wish to move that Symbol column to another position within the datagridview. */ ToolStripMenuItem t = (ToolStripMenuItem)sender; int i = getSymbolGridPosition(); if (i != -1) { if (dataGridView1.Columns[i].Frozen == false) { dataGridView1.Columns[i].Frozen = true; t.Checked = true; _columnLocked = true; } else { dataGridView1.Columns[i].Frozen = false; t.Checked = false; _columnLocked = false; } } } private void updateLockMenuEnabled(bool enabled) { if (_lockColumnMenuItem != null) _lockColumnMenuItem.Enabled = enabled; _lockMenuItemEnabled = enabled; } private void dataGridView1_ColumnDisplayIndexChanged(object sender, DataGridViewColumnEventArgs e) { if (getSymbolGridPosition() == -1) { updateLockMenuEnabled(false); } else { updateLockMenuEnabled(true); } } private int getSymbolGridPosition() { /*Although the menu item for freezing the column is only set in the symbol column, we want to double check to make sure that it is at the proper position (far left end) of the grid before enabling the freeze option. So we look at its Display index*/ for (int i = 0; i < dataGridView1.Columns.Count; i++) { Column cI = (Column)dataGridView1.Columns[i]; if (dataGridView1.Columns[i].DisplayIndex == 0 && cI.ColumnInfo.InternalCode == "D_Symbol") { return i; } } return -1; } void DoSorting() { dataGridView1.SuspendLayout(); List rows = _boundList.ToList(); _boundList.Clear(); ClearTokenList(); UpdateBoundList(SortRows(rows, _selectedSortColumnInfo, _localSortType)); if (GlobalDataSettings.GetDataGridColumnSetting(FORM_TYPE, "D_Symbol").Format == "symbolplus") { // Symbol Plus format is specified. So reset the symbol plus column format. DataGridViewHelper.SetSymbolPlusLayout(dataGridView1, _symbolPlusLayout); } var temp = _serverSortField; FixSortMenuItems(); dataGridView1.ClearSelection(); dataGridView1.ResumeLayout(); NotifySurfers(); if (_localSortType == TopListSortType.ServerSort) { var serverSortColumnInfo = dataGridView1.Columns.Cast().Select(column => column.ColumnInfo).FirstOrDefault(columnInfo => columnInfo.InternalCode == _serverSortField); if (serverSortColumnInfo != null) _selectedSortColumnInfo = serverSortColumnInfo; } cbmColumns.UpdateSelectedOrderingColumn(_selectedSortColumnInfo, _localSortType, _serverSortField); } private void ClearTokenList() { foreach (var token in _tokenStoreList) { token.Cancel(); } _tokenStoreList.Clear(); } void FixCellHeader(string sortableCode, TopListSortType sortType) { if (null == _metaData) return; bool needsToBePressed = false; if (sortType != TopListSortType.ServerSort) needsToBePressed = true; ColumnInfo columnInfo = _metaData.Columns.Single(x => x.InternalCode == sortableCode); DataGridViewColumn column = dataGridView1.Columns.Cast().Single(x => x.ColumnInfo == columnInfo); Depressible header = column.HeaderCell as Depressible; if (null != header) { if (header.Pressed != needsToBePressed) { header.Pressed = needsToBePressed; dataGridView1.InvalidateCell(column.HeaderCell); } } } void FixSortMenuItems() { if (_localSortType == TopListSortType.ServerSort) { foreach (string sortableCode in _filterMenuItems.Keys) { Dictionary menuItems = _filterMenuItems[sortableCode]; TIContentSortMenuItem serverSortMenuItem = null; if (menuItems.ContainsKey(TopListSortType.ServerSort)) serverSortMenuItem = menuItems[TopListSortType.ServerSort]; TIContentSortMenuItem descendingSortMenuItem = menuItems[TopListSortType.Descending]; TIContentSortMenuItem ascendingSortMenuItem = menuItems[TopListSortType.Ascending]; if (serverSortMenuItem != null) serverSortMenuItem.Checked = true; descendingSortMenuItem.Checked = false; ascendingSortMenuItem.Checked = false; // Strip off secondary field for symbol plus column. string sortableCodeColumn = sortableCode; if (sortableCodeColumn.StartsWith("D_Symbol|")) sortableCodeColumn = "D_Symbol"; // are we server sorting by this column? if (_serverSortField == sortableCodeColumn) FixCellHeader(sortableCodeColumn, TopListSortType.Ascending); else FixCellHeader(sortableCodeColumn, TopListSortType.ServerSort); //if (_serverSortField == sortableCode) // FixCellHeader(sortableCode, TopListSortType.Ascending); //else // FixCellHeader(sortableCode, TopListSortType.ServerSort); } } else { foreach (string sortableCode in _filterMenuItems.Keys) { Dictionary menuItems = _filterMenuItems[sortableCode]; TIContentSortMenuItem serverSortMenuItem = null; if (menuItems.ContainsKey(TopListSortType.ServerSort)) serverSortMenuItem = menuItems[TopListSortType.ServerSort]; TIContentSortMenuItem descendingSortMenuItem = menuItems[TopListSortType.Descending]; TIContentSortMenuItem ascendingSortMenuItem = menuItems[TopListSortType.Ascending]; if (serverSortMenuItem != null) serverSortMenuItem.Checked = false; // Strip off secondary field for symbol plus column. string sortableCodeOnly = sortableCode; string sortableCodeColumn = sortableCode; bool specialSymbolPlusColumn = false; if (sortableCodeColumn.StartsWith("D_Symbol|")) { sortableCodeColumn = "D_Symbol"; sortableCodeOnly = sortableCodeOnly.Replace("D_Symbol|", ""); specialSymbolPlusColumn = true; } if (_selectedSortColumnInfo != null && sortableCodeOnly == _selectedSortColumnInfo.InternalCode) { FixCellHeader(sortableCodeColumn, _localSortType); //FixCellHeader(sortableCode, _localSortType); if (_localSortType == TopListSortType.Ascending) { descendingSortMenuItem.Checked = false; ascendingSortMenuItem.Checked = true; } else { descendingSortMenuItem.Checked = true; ascendingSortMenuItem.Checked = false; } } else { // setting cell header to "ServerSort" means no local sorting on this column if (!specialSymbolPlusColumn) FixCellHeader(sortableCodeColumn, TopListSortType.ServerSort); //FixCellHeader(sortableCode, TopListSortType.ServerSort); descendingSortMenuItem.Checked = false; ascendingSortMenuItem.Checked = false; } } } } void helpLinkMenuItem_OnClick(string internalCode) { GuiEnvironment.RecordUseCase("TopList.ColumnHeader.RightClick.Help", _sendManager); GuiEnvironment.gotoHelpForFilter(internalCode); } void configurationMenuItem_OnClick(string internalCode) { GuiEnvironment.RecordUseCase("TopList.ColumnHeader.RightClick.Configure", _sendManager); showConfigWindow(true, internalCode); } void serverSortOnMenuItem_OnClick(string internalCode) { GuiEnvironment.RecordUseCase("TopList.ColumnHeader.RightClick.ServerSortOn", _sendManager); showConfigWindow(false, internalCode); } void showConfigWindow(bool useOutsideConfig, string internalCode) { ConfigWindowWrapper configWindowWrapper = ConfigWindowWrapper.DefaultFactory(ConfigurationType.TopList, _connectionMaster); configWindowWrapper.InitialConfig = _config ?? ""; if (useOutsideConfig) { configWindowWrapper.InitialFilter = internalCode; } else { configWindowWrapper.SortBy = internalCode; } configWindowWrapper.ShowIt(); if (!configWindowWrapper.Canceled) SetConfiguration(configWindowWrapper.Strategy.MakeConfigString()); //addresses bug where a hidden column appears after having modified //filter columns. _columnListState.isNewer = true; } public void CultureChanged() { RedrawColumns(); // Why is this next line required? If I don't call this, the colors revert to the // alternatating gray and white backgrounds. It gets fixed as soon as there is // another update. UpdateColors(); PopulateStrings(); } private void PopulateStrings() { List sharedPhrases = GuiEnvironment.XmlConfig.Node("ALERT_WINDOW").Node("PHRASES"); List phrases = GuiEnvironment.XmlConfig.Node("TOPLIST_WINDOW").Node("PHRASES"); configureToolStripMenuItem.Text = sharedPhrases.Node("CONFIGURE").PropertyForCulture("TEXT", "***"); propertiesToolStripMenuItem.Text = sharedPhrases.Node("PROPERTIES").PropertyForCulture("TEXT", "***"); collaborateToolStripMenuItem.Text = sharedPhrases.Node("COLLABORATE").PropertyForCulture("TEXT", "***"); duplicateToolStripMenuItem.Text = sharedPhrases.Node("DUPLICATE").PropertyForCulture("TEXT", "***"); saveAsToolStripMenuItem.Text = sharedPhrases.Node("SAVE_AS").PropertyForCulture("TEXT", "***"); saveAsDefaultToolStripMenuItem.Text = phrases.Node("SAVE_AS_DEFAULT").PropertyForCulture("TEXT", "***"); historyToolStripMenuItem.Text = phrases.Node("HISTORY").PropertyForCulture("TEXT", "***"); liveAlwaysToolStripMenuItem.Text = phrases.Node("LIVE_ALWAYS").PropertyForCulture("TEXT", "***"); liveDuringMarketHoursToolStripMenuItem.Text = phrases.Node("LIVE_MARKET_HOURS").PropertyForCulture("TEXT", "***"); useHistoricalDateToolStripMenuItem.Text = phrases.Node("HISTORICAL_DATE").PropertyForCulture("TEXT", "***"); setRecordCountToolStripMenuItem.Text = phrases.Node("SET_RECORD_COUNT").PropertyForCulture("TEXT", "***"); selectColorsToolStripMenuItem.Text = phrases.Node("COLORS").PropertyForCulture("TEXT", "***"); selectColorsToolStripMenuItem.Image = TopListColorChooser.WindowIconCache.MenuImage; graphicalIndicatorToolStripMenuItem.Text = phrases.Node("GRAPHIAL_INDICATOR").PropertyForCulture("TEXT", "***"); saveContentsToolStripMenuItem.Text = sharedPhrases.Node("SAVE_CONTENTS").PropertyForCulture("TEXT", "***"); showStrategiesStripMenuItem.Text = sharedPhrases.Node("SHOW_STRATEGIES").PropertyForCulture("TEXT", "***"); pinnedToolStripMenuItem.Text = sharedPhrases.Node("PINNED").PropertyForCulture("TEXT", "***"); //Allows us to hide item for ScottradeELITE if (_phrases.Node("SAVE_TO_CLOUD").PropertyForCulture("TEXT", "***") == "---") { saveToCloudToolStripMenuItem.Visible = false; } else { saveToCloudToolStripMenuItem.Text = _phrases.Node("SAVE_TO_CLOUD").PropertyForCulture("TEXT", "***"); } //Allows us to hide item for ScottradeELITE if (_phrases.Node("SMALL_BORDERS").PropertyForCulture("TEXT", "***") == "---") { smallBordersToolStripMenuItem.Visible = false; } else { smallBordersToolStripMenuItem.Text = _phrases.Node("SMALL_BORDERS").PropertyForCulture("TEXT", "***"); } } /// /// This never has the delayed data disclaimer. /// private string _baseWindowTitle = "Top List"; private void setTitle(DateTime? start = null, DateTime? end = null) { StringBuilder result = new StringBuilder(); if ((null == _metaData) || (_metaData.WindowName == "")) result.Append("Top List"); else result.Append(_metaData.WindowName); // save the short windowname for file saving _shortWindowName = result.ToString(); if ((null != start) || (null != end)) { result.Append(": "); if (null != start) result.Append(((DateTime)start).ToString(GuiEnvironment.AlertTimeTopListFormat)); if ((null != start) && (null != end)) result.Append(" - "); if (null != end) result.Append(((DateTime)end).ToString(GuiEnvironment.AlertTimeTopListFormat)); DateTime date = end.HasValue ? end.Value : start.Value; result.Append(" "); try { //string dateFormat = GuiEnvironment.XmlConfig.Node("DATE_FORMAT").PropertyForCulture("TEXT", null); // Use user's current culture to format date. string dateFormat = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern; if (null != dateFormat) { if (null != end) result.Append(end.Value.ToString(dateFormat)); else result.Append(start.Value.ToString(dateFormat)); } } catch { // Presumaly a bad value in the file could lead us here. By default print nothing. } } _baseWindowTitle = result.ToString(); UpdateWindowTitle(); } private void UpdateWindowTitle() { string windowTitle = _baseWindowTitle; if (_connectionMaster.LoginManager.IsDemo) { windowTitle += GuiEnvironment.XmlConfig.Node("COMMON_PHRASES").Node("DEMO_DISCLAIMER").PropertyForCulture("TEXT", "***"); saveToCloudToolStripMenuItem.Enabled = false; } else { saveToCloudToolStripMenuItem.Enabled = true; } Text = windowTitle; } void IDemoMode.OnDemoModeChanged() { UpdateWindowTitle(); } private void resizeColumns_Click(object sender, EventArgs e) { GuiEnvironment.RecordUseCase("TopList.RightClick.ResizeColumns", _sendManager); dataGridView1.AutoResizeColumns(); } void OnRowData(List rows, DateTime? start, DateTime? end) { //System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.fff tt") + " TopListData " + rows.Count + " " + _topList.TopListInfo.WindowName); //GuiEnvironment.LogMessage("TopListData " + rows.Count + " " + _topList.TopListInfo.WindowName); // Table // If no columns are defined then exit routine if (dataGridView1.Columns.GetFirstColumn(DataGridViewElementStates.Visible) == null) { return; } dataGridView1.SuspendLayout(); // We need to keep track of the server sorted list in case the user goes back to TopListType.ServerSort // This way we can revert to the server sort without waiting for the next refresh from the server _serverSortedList = new List(rows); if (_localSortType != TopListSortType.ServerSort) rows = SortRows(rows, _selectedSortColumnInfo, _localSortType); UpdateBoundList(rows); setTitle(start, end); UpdateCylinderChart(); NotifySurfers(); dataGridView1.ResumeLayout(); } /// /// restore the previously saved cell selection after a datagrid update /// /// private void RestoreCellsSelection() { if (_savedCellSelection == null) return; for (int i = 0; i < _savedCellSelection.SelectedRows.Length; i++) { int row = _savedCellSelection.SelectedRows[i]; int col = _savedCellSelection.SelectedCols[i]; if ((row < dataGridView1.RowCount) && (col <= dataGridView1.ColumnCount)) { // Need to set both selected rows and current cell to keep the cell/row focus insync. // Make sure the cells are visible before setting the currentCell. if (_savedCellSelection.CurrentRowCell == row && _savedCellSelection.CurrentColCell == col && dataGridView1.Rows[row].Cells[col].Visible) dataGridView1.CurrentCell = dataGridView1.Rows[row].Cells[col]; dataGridView1.Rows[row].Cells[col].Selected = true; } } //Prevent the dataGridView from autoscrolling after the selected cells are restored if (dataGridView1.FirstDisplayedScrollingRowIndex != _savedCellSelection.FirstRow && _savedCellSelection.FirstRow >= 0 && _savedCellSelection.FirstRow <= dataGridView1.Rows.Count - 1) dataGridView1.FirstDisplayedScrollingRowIndex = _savedCellSelection.FirstRow; _savedCellSelection = null; } /// /// Stores the current cell selection for restoring after datagrid update /// /// The object of type SavedCellSelection with the selected cells information private void SaveCellsSelection() { if (_savedCellSelection != null) return; DataGridViewSelectedCellCollection savedSelection = dataGridView1.SelectedCells; // saved selection is mostly useless after calling _boundList.Clear(); int[] selectedRows = new int[savedSelection.Count]; int[] selectedCols = new int[savedSelection.Count]; for (int i = 0; i < savedSelection.Count; i++) { selectedRows[i] = savedSelection[i].RowIndex; selectedCols[i] = savedSelection[i].ColumnIndex; } int horizontalScrollPosition = dataGridView1.HorizontalScrollingOffset; int verticalScrollPosition = dataGridView1.VerticalScrollingOffset; int firstCol = dataGridView1.FirstDisplayedScrollingColumnIndex; int firstRow = dataGridView1.FirstDisplayedScrollingRowIndex; int currentCellRow = dataGridView1.CurrentCell?.RowIndex ?? -1; int currentCellCol = dataGridView1.CurrentCell?.ColumnIndex ?? -1; _savedCellSelection = new SavedCellSelection(horizontalScrollPosition, verticalScrollPosition, firstCol, firstRow, selectedRows, selectedCols, currentCellRow, currentCellCol); } /// /// Goes through the incoming data and refresh the table /// /// private void UpdateBoundList(List rows) { SaveCellsSelection(); dataGridView1.SuspendLayout(); var historyMode = useHistoricalDateToolStripMenuItem.Checked; if (historyMode) { _boundList.Clear(); foreach (var row in rows) _boundList.Add(row); } else { //Remove items that will not longer be on the _boundList var toRemove = _boundList.Where(r1 => !rows.Any(r2 => r2.GetSymbol() == r1.GetSymbol())).ToList(); foreach (var item in toRemove) { //System.Diagnostics.Debug.WriteLine($"Removing {item.GetSymbol()} at {_boundList.IndexOf(item)} from _boundList"); _boundList.Remove(item); } //Remove if there are tokens on the token list also var tokensToRemove = _tokenStoreList.Where(cToken => toRemove.Any(sl => sl.GetSymbol() == cToken.GetRequest().SingleSymbol)).ToList(); foreach (var token in tokensToRemove) { token.Cancel(); _tokenStoreList.Remove(token); } int index = 0; foreach (RowData row in rows) { UpdateOrAddData(row, index); index++; } UpdateSingleSymbolSubscriptions(); } if (null != GuiEnvironment.RowDataExtensions && GuiEnvironment.RowDataExtensions.Count > 0) { try { foreach (IRowDataExtension rowDataExtension in GuiEnvironment.RowDataExtensions) rowDataExtension.HandleRowData(this, _metaData.WindowName, _metaData.Columns, rows); } catch { } } UpdateColors(); dataGridView1.ClearSelection(); RestoreCellsSelection(); dataGridView1.ResumeLayout(); dataGridView1.Invalidate(); if (_useBrowserInterface) { // Update treemap chart. _chromiumTreemapControl.UpdateChart(); } } /// /// Used to update the individual values if neccesary for a single symbol /// The single symbol subscription should call this method /// /// private void UpdateDataFromSingleSymbol(List rowData) { if (rowData.Count == 1) { var row = rowData[0]; List rows = _boundList.ToList(); var index = rows.FindIndex(r => r.GetSymbol() == row.GetSymbol()); //Check if we are receiving a symbol that is no longer present on the _boundList if (index < 0) { //make sure it will not come back again var tokens = _tokenStoreList.Where(r => r.GetRequest().SingleSymbol == row.GetSymbol()).ToList(); foreach (var token in tokens) { token.Cancel(); _tokenStoreList.Remove(token); } return; } rows[index] = row; //Sort also in case that the selected sort column has new incomming values changes if (IsSortedColumnUpdated(rowData, _localSortType)) rows = SortRows(rows, _selectedSortColumnInfo, _localSortType, true); UpdateBoundList(rows); } } /// /// Detects if the selected sort column info is going to receive new values or updates to existing ones /// This allows to mindfully execute the sorting action in cases where it is needed preserving resources and performance /// /// The incoming row data set to be evaluated /// The selected sortType. Used to determine the wireName in special cases. private bool IsSortedColumnUpdated(List rows, TopListSortType sortType) { var wireName = null != _selectedSortColumnInfo ? _selectedSortColumnInfo.WireName : "c_" + HttpUtility.ParseQueryString(_config).Get("sort")?.Remove(0, 3); // The following covers the case when HttpUtility.ParseQueryString(_config).Get("sort") is null. // This happens when a user enters an Alert Collaborate string into the TopList Collaborate window. if (wireName.Equals("c_") && sortType == TopListSortType.ServerSort) wireName = "c_" + _serverSortField; else if (wireName.Equals("c_")) return false; // Case of bad wireName foreach (var rowItem in rows) { var symbol = rowItem.GetSymbol(""); var sortingColumnValue = rowItem.GetAsString(wireName); var existent = _boundList.FirstOrDefault(bRow => bRow.GetSymbol("") == symbol); //Look for updates in the selected sorted column if (existent == default || existent.GetAsString(wireName) != sortingColumnValue) return true; } return false; } /// /// If the value contained on the row data exist and has changed, the value is updated /// If the value does not exist insert it to the corresponding index /// /// /// private void UpdateOrAddData(RowData row, int index) { // Ignore update if the index is not in boundList. if (index > _boundList.Count) return; string symbol = row.GetSymbol(""); var existent = _boundList.FirstOrDefault(bRow => bRow.GetSymbol("") == symbol); try { if (existent == default) { _boundList.Insert(index, row); //System.Diagnostics.Debug.WriteLine($"Inserted {symbol} on index {_boundList.IndexOf(row)}"); } else { //Check if the data has changed if (!MatchingRowData(existent, row)) { //System.Diagnostics.Debug.WriteLine($"Updating {symbol} on {index}, old Value: {Environment.NewLine} {existent.ToString()} {Environment.NewLine} New Value: {Environment.NewLine} {row.ToString()}"); int existentIndex = _boundList.IndexOf(existent); if (existentIndex != index) { _boundList.Remove(existent); if (_boundList.Count == 0) _boundList.Insert(0, row); // Insert at the beginning of the list. else if (index >= 0 && index <= _boundList.Count - 1) _boundList.Insert(index, row); //Insert at within the list. else if (index >= 0 && index == _boundList.Count) _boundList.Add(row); // Add to the end of the list.; } else _boundList[index] = row; } } } catch (Exception e) { TILogger.Error(e.Message, e); } } /// /// Updates the single symbol subscription based on the visibility of the elements on the _boundList /// private void UpdateSingleSymbolSubscriptions() { // When the window is frozen, resizing or scrolling should not restart the single symbol subscription. if (_isFrozen) return; var firstDisplayedIndex = dataGridView1.FirstDisplayedScrollingRowIndex; var lastDisplayedIndex = dataGridView1.DisplayedRowCount(true) + firstDisplayedIndex; for (int i = 0; i < _boundList.Count; i++) { string symbol = _boundList[i].GetSymbol(""); if (i <= lastDisplayedIndex && i >= firstDisplayedIndex) { //if not in ss subscribe list insert it if (!_tokenStoreList.Any(token => token.GetRequest().SingleSymbol == symbol)) { TopListRequest request = new TopListRequest(); request.Collaborate = _config; request.SaveToMru = false; request.Streaming = true; request.SkipMetaData = false; request.AddStandardRowDataColumns(); // Add additional compliment columns. foreach (ColumnInfo columnInfo in _additionalSymbolPlusColumns) { request.AddExtraColumn(columnInfo.InternalCode); } if (!_gradientColorChooser.FieldInternalCode.Equals("")) request.AddExtraColumn(_gradientColorChooser.FieldInternalCode); request.OutsideMarketHours = liveAlwaysToolStripMenuItem.Checked; request.SingleSymbol = symbol; var token = request.Send(_connectionMaster.SendManager, this); //System.Diagnostics.Debug.WriteLine($"Added listener for {symbol} token {token} visible at {i}"); _tokenStoreList.Add(token); } } else { var token = _tokenStoreList.FirstOrDefault(cToken => cToken.GetRequest().SingleSymbol == symbol); if (token != default) { //System.Diagnostics.Debug.WriteLine($"Removed listener for {symbol} token {token.ToString()} not visible at {i}"); token.Cancel(); _tokenStoreList.Remove(token); } } } } private void NotifySurfers() { foreach (Action dataRefreshCallback in _dataRefreshCallbacks) dataRefreshCallback(this); } /// /// Checks if all parameters inside the rowData have the same values /// /// /// /// private bool MatchingRowData(RowData left, RowData right) { foreach (KeyValuePair kvp in left.Data) { if (kvp.Value == null) continue; Type type = kvp.Value.GetType(); var lValue = Convert.ChangeType(kvp.Value, type); var rValue = lValue; //ignore if key does not exist on the other object, compare only matching keys if (right.Data.ContainsKey(kvp.Key)) { rValue = Convert.ChangeType(right.Data[kvp.Key], type); } if (!lValue.Equals(rValue)) { //System.Diagnostics.Debug.WriteLine($"Value change for {left.GetSymbol()} on {kvp.Key.ToString()}, old Value: {Environment.NewLine} {lValue.ToString()} {Environment.NewLine} New Value: {Environment.NewLine} {rValue.ToString()}"); return false; } } return true; } // TODO This is UGLY. We always want nulls to be at the bottom. For doubles, we replace null with + or - // infinity. For strings, we know that "" is the smallest value, like -INF. We are using this as the // biggest value. It will probably work okay, but it's ugly. Really, we should provide our own compare // function. That function can deal with the nulls more explicitly. const string LAST_STRING = "zzzz"; // I don't know why this next line didn't work. This is a very large value for a unicode character. // I would have expected this string to come after all normal strings. But in fact it came first. //const string LAST_STRING = "\uffe0"; private List SortRows(List rows, ColumnInfo sortColumnInfo, TopListSortType sortType, bool isFastUpdate = false) { if (!_localSortEnabled && isFastUpdate) { var sortConfig = HttpUtility.ParseQueryString(_config).Get("sort"); if (sortConfig != null) { sortType = sortConfig.Substring(0, 3).ToUpperInvariant().Contains("MAX") ? TopListSortType.Descending : TopListSortType.Ascending; sortColumnInfo = new ColumnInfo { WireName = "c_" + sortConfig.Remove(0, 3) }; } } if (sortType == TopListSortType.ServerSort || sortColumnInfo == null) { return _serverSortedList; } else { if (sortColumnInfo.Format == "" || sortColumnInfo.Format == "symbolplus") // Also sort Symbol Plus like blank format. { if (sortType == TopListSortType.Descending) return rows.OrderByDescending(x => (x.Data.ContainsKey(sortColumnInfo.WireName)) ? x.GetAsString(sortColumnInfo.WireName) : "").ToList(); else return rows.OrderBy(x => (x.Data.ContainsKey(sortColumnInfo.WireName)) ? x.GetAsString(sortColumnInfo.WireName) : LAST_STRING).ToList(); } else { if (sortType == TopListSortType.Descending) return rows.OrderByDescending(x => (x.Data.ContainsKey(sortColumnInfo.WireName)) ? ParseRowElementValue(x.Data[sortColumnInfo.WireName].ToString(), double.NegativeInfinity) : double.NegativeInfinity).ToList(); else return rows.OrderBy(x => (x.Data.ContainsKey(sortColumnInfo.WireName)) ? ParseRowElementValue(x.Data[sortColumnInfo.WireName].ToString(), double.PositiveInfinity) : double.PositiveInfinity).ToList(); } } } private double ParseRowElementValue(string source, double def) { double value; if (ServerFormats.TryParse(source, out value)) return value; else return def; } private void TopListForm_FormClosed(object sender, FormClosedEventArgs e) { _dataRefreshCallbacks.Clear(); SurfManager.Instance().Remove(this); SetConfiguration(null); if (timer != null) { timer.Stop(); timer.Tick -= RunEvent; } timer.Dispose(); comboMenu.close(); cbmColumns.close(); dataGridView1.Columns.Clear(); _boundList.Clear(); ClearTokenList(); _serverSortedList.Clear(); _selectedRowsList.Clear(); _filterMenuItems.Clear(); _sendToContextMenu = null; _mostRecentRowData = null; _currentRowData = null; _contextMenuCylinder.MenuItems.Clear(); contextMenuStrip1.Opening -= LayoutManager.Instance().contextMenuStrip_Opening; // Chart cleanup. _chromiumTreemapControl.Cleanup(); } private void collaborateToolStripMenuItem_Click(object sender, EventArgs e) { GuiEnvironment.RecordUseCase("TopList.RightClick.Collaborate", _sendManager); CollaborateForm form = new CollaborateForm(); // Originally the metaData config string was used to create the collaborate string. // After some testing the time from the metaData was off by 3 hours in the future. // So now we are creating the string from _config, _lastHistoricalTime, // and the omh. This woks well. string timeFrameSettings = ""; if (null != _lastHistoricalTime && useHistoricalDateToolStripMenuItem.Checked) timeFrameSettings += "&hist=1&exact_time=" + ServerFormats.ToTimeT(_lastHistoricalTime); if (liveAlwaysToolStripMenuItem.Checked) timeFrameSettings += "&omh=1"; timeFrameSettings += "&count=" + _recordCount; if (null != _config) form.ConfigString = GuiEnvironment.XmlConfig.Node("TOPLIST_WINDOW").Node("CONFIG_PREFIX").PropertyForCulture("BASE", "***") + _config + timeFrameSettings; form.Text = GuiEnvironment.XmlConfig.Node("ALERT_WINDOW").Node("PHRASES").Node("COLLABORATE").PropertyForCulture("TEXT", "***"); form.ShowWarning = StrategyContainsPrivateList(); form.ShowDialog(this); if (form.DialogResult == System.Windows.Forms.DialogResult.OK) { if (form.ConfigString != "") { // Call to initialize time frame settings and clean up config string. string config = InitializeTimeFrameSettings(form.ConfigString, null, null, null); SetConfiguration(config); } } form.Dispose(); } private void configureToolStripMenuItem_Click(object sender, EventArgs e) { GuiEnvironment.RecordUseCase("TopList.RightClick.Configure", _sendManager); ShowConfiguration(); } public void ShowConfiguration() { ConfigWindowWrapper configWindowWrapper = ConfigWindowWrapper.DefaultFactory(ConfigurationType.TopList, _connectionMaster); configWindowWrapper.InitialConfig = _config; foreach (XmlNode asXml in GuiEnvironment.XmlConfig.Node("TOPLIST_WINDOW").Node("COLUMNS").Enum()) configWindowWrapper.ExtraColumns.Add(new DisplayOnlyField(asXml)); configWindowWrapper.ShowIt(); if (!configWindowWrapper.Canceled) { _config = configWindowWrapper.Strategy.MakeConfigString(); _addedSymbolPlusColumns = false; _additionalSymbolPlusColumns.Clear(); ReRequestData(); if (configWindowWrapper.Strategy.Name != "Current Settings") { comboMenu.setTheHeader(configWindowWrapper.Strategy.Name); } } //addresses bug where a hidden column appears after having modified //filter columns. _columnListState.isNewer = true; } private void duplicateToolStripMenuItem_Click(object sender, EventArgs e) { GuiEnvironment.RecordUseCase("TopList.RightClick.Duplicate", _sendManager); LayoutManager.Instance().Duplicate(this); } private void smallBordersToolStripMenuItem_Click(object sender, EventArgs e) { setBorders(); } private void setBorders() { if (smallBordersToolStripMenuItem.Checked) FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow; else FormBorderStyle = System.Windows.Forms.FormBorderStyle.Sizable; } private void saveAsToolStripMenuItem_Click(object sender, EventArgs e) { GuiEnvironment.RecordUseCase("TopList.RightClick.SaveAs", _sendManager); LayoutManager layoutManager = LayoutManager.Instance(); SaveFileDialog dialog = new SaveFileDialog(); if (null != layoutManager.Directory) dialog.InitialDirectory = layoutManager.Directory; dialog.Filter = GuiEnvironment.XmlConfig.Node("TOPLIST_WINDOW").Node("PHRASES").Node("WINDOW_FILTER").PropertyForCulture("TEXT", "***"); dialog.DefaultExt = "WTI"; string fileName = ""; if (_fileNameSaved == null) { fileName = GuiEnvironment.XmlConfig.Node("TOPLIST_WINDOW").Node("PHRASES").Node("FILE_PREFIX").PropertyForCulture("TEXT", "***") + _shortWindowName; } else { fileName = _fileNameSaved; } dialog.FileName = FileNameMethod.QuoteFileName(fileName); if (dialog.ShowDialog() == DialogResult.OK) { layoutManager.SaveOne(this, dialog.FileName); _fileNameSaved = Path.GetFileName(dialog.FileName); } dialog.Dispose(); } private void saveAsDefaultToolStripMenuItem_Click(object sender, EventArgs e) { GuiEnvironment.RecordUseCase("TopList.RightClick.SaveAsDefault", _sendManager); string programDirFileName = _layoutManager.Directory + "\\" + _defaultFileName; _layoutManager.SaveOne(this, programDirFileName); } private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e) { } private void selectColorsToolStripMenuItem_Click(object sender, EventArgs e) { GuiEnvironment.RecordUseCase("TopList.RightClick.SelectColors", _sendManager); /*This try-catch is for the hypothetical case where the user already has a live, populated top list showing. unbeknownwst to user(well the main form would indicate this) the connection goes out. The user then tries to open new toplist window. In this case, this new window will be blank, and a null reference exception would be thrown if user were to try to open the color chooser. Thus, the try-catch will make it so that the chooser cannot be brought up on an empty top list window.*/ try { using (TopListColorChooser form = new TopListColorChooser(_connectionMaster)) { form.GradientColorChooser = _gradientColorChooser; form.ShowDialog(); } } catch (NullReferenceException) { } } private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e) { if (e.RowIndex != -1) { if (GuiEnvironment.IntegrationSupportSingleClick) { // there has been some rare crashes that we haven't been able to reproduce but the errors // lean towards trying to access an index that is out of bounds. if (_boundList.Count > e.RowIndex) SendToExternalLinking(_currentRowData); } } } private void RecordExternalLinkUseCase(RowData rowData) { GuiEnvironment.RecordExternalLinkingUseCase(rowData, "TopList", _sendManager); } public void SendToExternalLinking(RowData rowData, bool promptUserIfEmpty = false) { if (null == rowData) return; RecordExternalLinkUseCase(rowData); string symbol = rowData.GetSymbol(); string exchange = rowData.GetExchange(); string listName = _symbolLinking.SavedSymbolListName; // Use BeginInvoke to avoid "reentrant call to the SetCurrentCellAddress" // exception when a user uses both keyboard and mouse symbol linking // simultaneously with external linking alternate method disabled. // http://stackoverflow.com/questions/26522927/how-to-evade-reentrant-call-to-setcurrentcelladdresscore BeginInvoke(new MethodInvoker(() => { GuiEnvironment.sendSymbolToExternalConnector(symbol, exchange, listName, this, rowData, promptUserIfEmpty: promptUserIfEmpty, linkChannel: _linkChannel, dockWindowID: GuiEnvironment.GetDockWindowID(this)); })); } public void SortRows(Column column) { if (column != null) { // Update treemap filter code and units. _chromiumTreemapControl.SetUnits(column.ColumnInfo.Units); _chromiumTreemapControl.FilterCode = column.ColumnInfo.WireName; if (!_localSortEnabled || (_selectedSortColumnInfo.InternalCode != column.ColumnInfo.InternalCode)) { _localSortEnabled = true; _selectedSortColumnInfo = column.ColumnInfo; _localSortType = TopListSortType.Descending; DoSorting(); } else { if (_localSortType == TopListSortType.Descending) _localSortType = TopListSortType.Ascending; else if (_localSortType == TopListSortType.Ascending) { _localSortType = TopListSortType.ServerSort; _localSortEnabled = false; } DoSorting(); } } else { _localSortType = TopListSortType.ServerSort; _localSortEnabled = false; DoSorting(); } } private void dataGridView1_CellDoubleClick(object sender, DataGridViewCellEventArgs e) { if (e.RowIndex != -1) { if (GuiEnvironment.IntegrationSupportSingleClick == false) { // there has been some rare crashes that we haven't been able to reproduce but the errors // lean towards trying to access an index that is out of bounds. if (_boundList.Count > e.RowIndex) SendToExternalLinking(_mostRecentRowData); } } else { GuiEnvironment.RecordUseCase("TopList.ColumnHeader.Sort", _sendManager); // header row was double clicked on Column column = (Column)dataGridView1.Columns[e.ColumnIndex]; // string internalCode = column.ColumnInfo.InternalCode; // string sortCode = SortCode(internalCode); SortRows(column); } } // This is needed to keep track of the last column index that was used in the most recent menustripneeded callback. // It's required because of Limited Mode which disables or enables all menu items based on the mode. That is performed // in the Opening event, but the Opening event doesn't have a column index. This is used there. private int _lastContextMenuNeededColumnIndex; private void dataGridView1_CellContextMenuStripNeeded(object sender, DataGridViewCellContextMenuStripNeededEventArgs e) { _lastContextMenuNeededColumnIndex = e.ColumnIndex; Column.UpdateGraphicsMenuItem(graphicalIndicatorToolStripMenuItem, dataGridView1, e.ColumnIndex); } private void saveContentsToolStripMenuItem_Click(object sender, EventArgs e) { GuiEnvironment.RecordUseCase("TopList.RightClick.SaveContents", _sendManager); GridContentSaver contentSaver = new GridContentSaver(); string fileNameSavedContents = ""; if (_fileNameSavedContents == null) { fileNameSavedContents = GuiEnvironment.XmlConfig.Node("TOPLIST_WINDOW").Node("PHRASES").Node("FILE_PREFIX_CONTENTS").PropertyForCulture("TEXT", "***") + _shortWindowName; } else { fileNameSavedContents = _fileNameSavedContents; } _fileNameSavedContents = contentSaver.saveGridContents(dataGridView1, fileNameSavedContents); } //the configure and collaborate menu items are greyed out for a frozen window. //If someone were to actually try to alter the configuration of a frozen window //(e.g. add or remove column, or symbol list ..., that change will automatically //unfreeze the window due to the SetConfig operation, which defeats the purpose of freezing. //Plus upon unfreezing,an exception will occur. private void freezeToolStripMenuItem1_Click(object sender, EventArgs e) { GuiEnvironment.RecordUseCase("TopList.RightClick.Freeze", _sendManager); if (_isFrozen) { configureToolStripMenuItem.Enabled = true; collaborateToolStripMenuItem.Enabled = true; _isFrozen = false; CultureChanged(); //responsible for changing colors ReRequestData(); } else { configureToolStripMenuItem.Enabled = false; collaborateToolStripMenuItem.Enabled = false; _isFrozen = true; if (null != _requestToken) _requestToken.Cancel(); if (_tokenStoreList.Any()) ClearTokenList(); CultureChanged(); } } private void dataGridView1_CellMouseEnter(object sender, DataGridViewCellEventArgs e) { int selectedRow = e.RowIndex; if (selectedRow != -1 && !_symbolSpecificContextMenuOpen) { try { _currentRowData = _boundList[selectedRow]; _mostRecentRowData = _currentRowData; _mostRecentRowIndex = selectedRow; } catch { } } } private void dataGridView1_CellMouseLeave(object sender, DataGridViewCellEventArgs e) { _currentRowData = null; } private void contextMenuStrip1_Opening(object sender, CancelEventArgs e) { _selectedRowsList = new List(); if (dataGridView1.SelectedCells.Count > 1) { IEnumerable selectedRows = dataGridView1.SelectedCells.Cast() .Select(cell => cell.OwningRow) .Distinct(); foreach (DataGridViewRow row in selectedRows.ToList()) { _selectedRowsList.Add(_boundList[row.Index]); } } if (null != _currentRowData) { if (_selectedRowsList.Count > 1) { ToggleSymbolSpecificMenuItems(false); graphicalIndicatorToolStripMenuItem.Visible = false; ShowRowSpecificMenuItems(_currentRowData); sendSymbolToToolStripMenuItem.Visible = true; } else { string symbol = _currentRowData.GetSymbol(); graphicalIndicatorToolStripMenuItem.Visible = true; ShowRowSpecificMenuItems(_currentRowData); ToggleSymbolSpecificMenuItems(true, symbol); } } else { ToggleSymbolSpecificMenuItems(false); graphicalIndicatorToolStripMenuItem.Visible = false; sendSymbolToToolStripMenuItem.Visible = false; } if (null != GuiEnvironment.RobotStrategyMenuCode) { string symbol = ""; if (null != _currentRowData && _selectedRowsList.Count < 2) symbol = _currentRowData.GetSymbol(); GuiEnvironment.RobotStrategyMenuCode(contextMenuStrip1, symbol, "TL", _currentRowData); } GuiEnvironment.HideMenuItems(contextMenuStrip1.Items); GuiEnvironment.EnforceLimitedMode(contextMenuStrip1); Column.UpdateGraphicsMenuItem(graphicalIndicatorToolStripMenuItem, dataGridView1, _lastContextMenuNeededColumnIndex); // add symbol linking channels here symbolLinkingToolStripMenuItem.DropDown.Items.Clear(); //Detect if the current window is in floating mode var floatingMode = string.IsNullOrEmpty(GuiEnvironment.GetDockWindowID(this)); foreach (KeyValuePair channel in SymbolLinkChannels.Channels) { if (channel.Key == SymbolLinkChannels.DockWindowChannel && floatingMode) continue; ToolStripMenuItem menuItem = new ToolStripMenuItem(channel.Key, null, delegate { SetLinkChannel(channel.Key); }); menuItem.BackColor = channel.Value; Color foreColor = GradientInfo.AltColor(channel.Value); menuItem.ForeColor = foreColor; menuItem.Checked = _linkChannel == channel.Key; symbolLinkingToolStripMenuItem.DropDown.Items.Add(menuItem); } showStrategiesStripMenuItem.Visible = !showTreemapToolStripMenuItem.Checked; showTreemapSortMenu.Visible = showTreemapToolStripMenuItem.Checked; } /// /// Set the symbol link channel from symbol linking /// /// The link channel void ISymbolLinkingChannel.SetLinkChannel(string linkChannel) { SetLinkChannel(linkChannel); } /// /// Set the symbol link channel /// /// The link channel private void SetLinkChannel(string linkChannel) { _linkChannel = linkChannel; // change window icon SymbolLinkingChannelsHelper.SetFormIcon(this, "T", linkChannel); } /// /// Get the symbol link channel /// /// string ISymbolLinkingChannel.GetLinkChannel() { return _linkChannel; } private void updateMetadata(Column columnData) { if (_metaData != null) { var index = _metaData.Columns.IndexOf(columnData.ColumnInfo); if (index > -1) { _metaData.Columns[index].MaximumWidth = columnData.Width; } } } private void keepColumnWidth(Column columnData) { if (_metaData != null) { var index = _metaData.Columns.IndexOf(columnData.ColumnInfo); if (index > -1) columnData.Width = _metaData.Columns[index].MaximumWidth; } } private void ShowRowSpecificMenuItems(RowData alert) { try { if (_mostRecentRowIndex != -1 && _mostRecentRowIndex < dataGridView1.Rows.Count) { string symbol = alert.GetSymbol(); if (_selectedRowsList.Count > 1) symbol = ""; _symbolSpecificContextMenuOpen = true; foreach (DataGridViewRow selectedRow in dataGridView1.SelectedRows) selectedRow.Selected = false; dataGridView1.Rows[_mostRecentRowIndex].Selected = true; string sendToText = _sendTo.Node("SEND_SYMBOL_TO").PropertyForCulture("TEXT", "***"); if (sendToText == "---") sendSymbolToToolStripMenuItem.Visible = false; else sendSymbolToToolStripMenuItem.Visible = true; sendToText = sendToText.Replace("{symbol}", symbol); sendSymbolToToolStripMenuItem.Text = sendToText; graphicalIndicatorToolStripMenuItem.Visible = true; } } catch { } // _symbolSpecificContextMenuOpen = false; } private void dataGridView1_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e) { Column c = (Column)e.Column; string test = getSectorColumn(c); if (dataGridView1.AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.Fill) { if (_gridVisible && !_formresizing) { updateMetadata(c); } } else { if (!_formresizing) { updateMetadata(c); } else if (_gridVisible) { keepColumnWidth(c); } } if (e.Column.Width <= MIN_COLUMN_WIDTH && c.ColumnInfo.InternalCode == test && test != "") { //Assigning a true value to this attribute causes the rows height to be //set to its default value when painting the grid. _adjustGridRowsHeigth = true; } else if (c.ColumnInfo.InternalCode != test) { if (isColumnWidthAtMinimum()) { return; } } else { dataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells; } _formresizing = false; } private bool isColumnWidthAtMinimum() { /*This little method retrieves the width status of a Sector column...whatever position it may be in the grid*/ foreach (DataGridViewColumn column in dataGridView1.Columns) { Column c = (Column)column; string test = TopListForm.getSectorColumn(c); if (c.ColumnInfo.InternalCode == test && column.Width <= MIN_COLUMN_WIDTH) { return true; } } return false; } public static string getSectorColumn(Column c) //This method is used by both the TopList and Alert forms... { string flag = ""; String test = c.ColumnInfo.InternalCode; foreach (string col in sectorColumns) { if (test == col) { flag = col; break; } } return flag; } WindowIconCache ISaveLayout.WindowIconCache { get { return WindowIconCache; } } private void saveToCloudToolStripMenuItem_Click(object sender, EventArgs e) { SaveToCloud.DoIt(_sendManager, this); } private void contextMenuStrip1_Closing(object sender, ToolStripDropDownClosingEventArgs e) { _symbolSpecificContextMenuOpen = false; } private void textHeaderToolStripMenuItem_Click(object sender, EventArgs e) { _textHeaders = !textHeadersToolStripMenuItem.Checked; RedrawColumns(); UpdateColors(); UpdateTextHeadersMenuItem(); } private void UpdateTextHeadersMenuItem() { textHeadersToolStripMenuItem.Checked = _textHeaders; } public void SetSnapToGrid(bool enabled) { formSnapper1.Enabled = enabled; if (GuiEnvironment.RunningWin10 && enabled) { formSnapper1.Win10HeightAdjustment = GuiEnvironment.HEIGHT_INCREASE; formSnapper1.Win10WidthAdjustment = GuiEnvironment.WIDTH_INCREASE; } } private void dataGridView1_MouseEnter(object sender, EventArgs e) { contextMenuStrip1.Enabled = true; } private void dataGridView1_MouseLeave(object sender, EventArgs e) { contextMenuStrip1.Enabled = false; } private void picBoxHide_Click(object sender, EventArgs e) { hideTopPanel(); } private void hideTopPanel() { tableLayoutPanelStrategies.Visible = false; _isTopPanelHidden = true; showStrategiesStripMenuItem.Checked = false; showTreemapSortMenu.Checked = false; } private void showStrategiesStripMenuItem_Click(object sender, EventArgs e) { showTheTopPanel(!showStrategiesStripMenuItem.Checked, showTreemapSortMenu.Checked); selectTheFont(); } private void showColumnsStripMenu_Click(object sender, EventArgs e) { showTheTopPanel(showStrategiesStripMenuItem.Checked, !showTreemapSortMenu.Checked); selectTheFont(); } private void showTheTopPanel(bool strategyMenu, bool columnsMenu) { showStrategiesStripMenuItem.Checked = GuiEnvironment.AllowStrategyMenus && strategyMenu; showTreemapSortMenu.Checked = columnsMenu; if (!strategyMenu && !columnsMenu) { hideTopPanel(); } //If the showStrategiesStripMenuItem option is not checked or the current view is Treemap and the showTreemapSortMenu //option is not checked or the Treemap view is not selected, the top panel is hidden. else if ((!showStrategiesStripMenuItem.Checked || showTreemapToolStripMenuItem.Checked) && (!showTreemapSortMenu.Checked || !showTreemapToolStripMenuItem.Checked)) tableLayoutPanelStrategies.Visible = false; //Option column widths are calculated accordingly. else { tableLayoutPanelStrategies.ColumnStyles[0].Width = showStrategiesStripMenuItem.Checked && !showTreemapToolStripMenuItem.Checked ? 100 : 0; tableLayoutPanelStrategies.ColumnStyles[1].Width = showTreemapSortMenu.Checked && showTreemapToolStripMenuItem.Checked ? 100 : 0; } //If the showStrategiesStripMenuItem option is checked and the current view is not Treemap and the //showTreemapSortMenu option is checked and the Treemap view is selected, the top panel is displayed. if ((showStrategiesStripMenuItem.Checked && !showTreemapToolStripMenuItem.Checked) || (showTreemapSortMenu.Checked && showTreemapToolStripMenuItem.Checked)) { elementHost1.Height = (int)(this.FontHeight); tableLayoutPanelStrategies.Height = elementHost1.Height; picBoxHide.Visible = true; pnlHider.Visible = true; tableLayoutPanelStrategies.Visible = true; _isTopPanelHidden = false; } if (_currentlyRestoring) { selectTheFont(); _currentlyRestoring = false; } } private void showScoreCylinderToolStripMenuItem_Click(object sender, EventArgs e) { chart1.Visible = !chart1.Visible; showScoreCylinderToolStripMenuItem.Checked = chart1.Visible; } private void dataGridView1_Scroll(object sender, ScrollEventArgs e) { bool historyMode = useHistoricalDateToolStripMenuItem.Checked; if (e.ScrollOrientation == ScrollOrientation.VerticalScroll && !historyMode) { UpdateSingleSymbolSubscriptions(); } } private void dataGridView1_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e) { string ListName = _symbolLinking.SavedSymbolListName; string symbol = ""; string exchange = ""; int currentRow = 0; if (dataGridView1.SelectedRows.Count == 1) { if (e.KeyCode == Keys.Enter) { DataGridViewRow r = dataGridView1.SelectedRows[0]; currentRow = r.Index; RowData test = _boundList[currentRow]; symbol = test.GetSymbol(); exchange = test.GetExchange(); dataGridView1.PerformLayout(); SendToExternalLinking(test); } else if (e.KeyCode == Keys.Space) { DataGridViewRow r = dataGridView1.SelectedRows[0]; int selectedRow = r.Index; currentRow = selectedRow + 1; if (currentRow <= _boundList.Count - 1) { SetCurrentCell(currentRow); dataGridView1.Rows[selectedRow].Selected = false; dataGridView1.Rows[currentRow].Selected = true; RowData test = _boundList[currentRow]; symbol = test.GetSymbol(); exchange = test.GetExchange(); if (!dataGridView1.Rows[currentRow].Displayed) { try { while (!dataGridView1.Rows[currentRow].Displayed) { int firstRow = dataGridView1.FirstDisplayedScrollingRowIndex; dataGridView1.FirstDisplayedScrollingRowIndex = firstRow + 2; } dataGridView1.PerformLayout(); } catch { } } else if (currentRow + 1 < _boundList.Count && !dataGridView1.Rows[currentRow + 1].Displayed) { try { int firstRow = dataGridView1.FirstDisplayedScrollingRowIndex; dataGridView1.FirstDisplayedScrollingRowIndex = firstRow + 1; dataGridView1.PerformLayout(); } catch { } } SendToExternalLinking(test); } } } else if (dataGridView1.SelectedCells.Count != 0 && !moreThanOneRowWithSelectedCells(dataGridView1)) //Options -turn off selected row. { if (e.KeyCode == Keys.Enter) { //find the row of the first selected cell (more that currentRow = dataGridView1.SelectedCells[0].RowIndex; RowData test = _boundList[currentRow]; symbol = test.GetSymbol(); exchange = test.GetExchange(); SendToExternalLinking(test); } else if (e.KeyCode == Keys.Space) { int selectedRow = dataGridView1.SelectedCells[0].RowIndex; int selectedColumn = dataGridView1.SelectedCells[0].ColumnIndex; currentRow = selectedRow + 1; if (currentRow <= _boundList.Count - 1) { dataGridView1.CurrentCell = this.dataGridView1[selectedColumn, currentRow]; RowData test = _boundList[currentRow]; symbol = test.GetSymbol(); exchange = test.GetExchange(); if (!dataGridView1.Rows[currentRow].Displayed) { try { while (!dataGridView1.Rows[currentRow].Displayed) { int firstRow = dataGridView1.FirstDisplayedScrollingRowIndex; dataGridView1.FirstDisplayedScrollingRowIndex = firstRow + 2; } dataGridView1.PerformLayout(); } catch { } } else if (currentRow + 1 < _boundList.Count && !dataGridView1.Rows[currentRow + 1].Displayed) { try { int firstRow = dataGridView1.FirstDisplayedScrollingRowIndex; dataGridView1.FirstDisplayedScrollingRowIndex = firstRow + 1; dataGridView1.PerformLayout(); } catch { } } SendToExternalLinking(test); } } } } /// /// Set focus by setting the CurrentCell. We need to make sure that the column used is visible /// else we get an exception "Current cell cannot be set to an invisble cell". /// /// private void SetCurrentCell(int currentRow) { /* * Setting the current cell actually sets the focus. Row (or cell) selection * is independent from focus-which does set the record pointer. * http://social.msdn.microsoft.com/Forums/windows/en-US/080e68a3-2bcd-4c1d-ae60-54b43b9c040e/datagridview-focusing-a-particular-row?forum=winformsdatacontrols */ int colIndex = 0; foreach (Column col in dataGridView1.Columns) { // CurrentCell must have a visble column. if (col.Visible) { dataGridView1.CurrentCell = this.dataGridView1[colIndex, currentRow]; // Set focus on a row with a visible column. return; } colIndex++; } } public static bool moreThanOneRowWithSelectedCells(DataGridView dgv) { //check to see if the selected cells are all for the same row, or for different rows. //if the selected cells are for different rows, then do not send to symbol linking. We do nothing. int rowIndex = 0; foreach (DataGridViewCell cell in dgv.SelectedCells) { if (rowIndex == 0) { rowIndex = cell.RowIndex; } else { if (cell.RowIndex != rowIndex) //more selected cells in more than one row { return true; } } } return false; } private bool _limitedMode = false; public bool LimitedMode { get { return _limitedMode; } set { _limitedMode = value; } } public List GetStrategyConfigs() { List toReturn = new List(); StrategyConfig strategyConfig; strategyConfig.Config = _config; strategyConfig.ConfigurationType = ConfigurationType.TopList; toReturn.Add(strategyConfig); return toReturn; } private void pinnedToolStripMenuItem_Click(object sender, EventArgs e) { _pinned = pinnedToolStripMenuItem.Checked; } private void liveAlwaysToolStripMenuItem_Click(object sender, EventArgs e) { _selectedLiveAlways = true; _historyErrorOnDemo = false; _lastHistoricalTime = null; liveDuringMarketHoursToolStripMenuItem.Checked = false; useHistoricalDateToolStripMenuItem.Checked = false; liveAlwaysToolStripMenuItem.Checked = true; ReRequestData(); } private void liveDuringMarketHoursToolStripMenuItem_Click(object sender, EventArgs e) { _selectedLiveAlways = false; _historyErrorOnDemo = false; _lastHistoricalTime = null; liveDuringMarketHoursToolStripMenuItem.Checked = true; useHistoricalDateToolStripMenuItem.Checked = false; liveAlwaysToolStripMenuItem.Checked = false; ReRequestData(); } DateTime? _lastHistoricalTime; private void useHistoricalDateToolStripMenuItem_Click(object sender, EventArgs e) { TopListHistoricalTimeFrame timeFrame = new TopListHistoricalTimeFrame(_lastHistoricalTime); timeFrame.FormClosing += new FormClosingEventHandler(timeFrame_FormClosing); timeFrame.ShowDialog(); timeFrame.Dispose(); } private void setRecordCountToolStripMenuItem_Click(object sender, EventArgs e) { TopListHistoryRecords historyRecords = new TopListHistoryRecords(_recordCount); historyRecords.FormClosing += new FormClosingEventHandler(historyRecords_FormClosing); historyRecords.ShowDialog(); historyRecords.Dispose(); } private void timeFrame_FormClosing(object sender, FormClosingEventArgs e) { TopListHistoricalTimeFrame timeFrame = (TopListHistoricalTimeFrame)sender; bool test = timeFrame.isOK; if (test) { useHistoricalDateToolStripMenuItem.Checked = true; liveAlwaysToolStripMenuItem.Checked = false; liveDuringMarketHoursToolStripMenuItem.Checked = false; DateTime time = timeFrame.getDateTimeValue(); time = time.AddMilliseconds(-time.Millisecond); time = time.AddSeconds(-time.Second); _lastHistoricalTime = time; ReRequestData(); if (_connectionMaster.LoginManager.IsDemo && test == true) { _historyErrorOnDemo = true; onAccountStatusChanged(); return; } } } private const int DEFAULT_MAX_RECORD_COUNT = 100; /// /// Pass this on to the server. Tell it that the user requested this many records. /// private int _recordCount = DEFAULT_MAX_RECORD_COUNT; private void historyRecords_FormClosing(object sender, FormClosingEventArgs e) { TopListHistoryRecords historyRecords = (TopListHistoryRecords)sender; if (historyRecords.isOK) { _recordCount = historyRecords.getMaxRecords(); ReRequestData(); } } private void dataGridView1_RowEnter(object sender, DataGridViewCellEventArgs e) { int rowIndex = e.RowIndex; if (rowIndex != -1) { _mostRecentRowIndex = rowIndex; _mostRecentRowData = _boundList[rowIndex]; _currentRowData = _boundList[rowIndex]; // Require datagrid focus so we can ignore up/down key pressed outside of our application if (GuiEnvironment.IntegrationSupportUpDownKeys && dataGridView1.Focused && (Keyboard.IsKeyDown(Key.Down) || Keyboard.IsKeyDown(Key.Up))) SendToExternalLinking(_mostRecentRowData); } } private void lblGoBack_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { hideMarketingPanel(); if (_selectedLiveAlways) { liveAlwaysToolStripMenuItem_Click(null, new EventArgs()); } else { liveDuringMarketHoursToolStripMenuItem_Click(null, new EventArgs()); } } private void linkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { Process.Start(e.Link.LinkData as string); } public void onAccountStatusChanged() { if (_connectionMaster.LoginManager.AccountStatus != AccountStatus.Good) //user gets "kicked off" { //lblMessage.Text = "No Connection"; // linkLabel.Visible = true; return; } if ((_connectionMaster.LoginManager.IsDemo && _historyErrorOnDemo == true) || (_connectionMaster.LoginManager.IsDemo && useHistoricalDateToolStripMenuItem.Checked)) { showMarketingPanel(); if (_selectedLiveAlways) { liveAlwaysToolStripMenuItem.Checked = true; liveDuringMarketHoursToolStripMenuItem.Checked = false; } else { liveAlwaysToolStripMenuItem.Checked = false; liveDuringMarketHoursToolStripMenuItem.Checked = true; } } else { hideMarketingPanel(); if (useHistoricalDateToolStripMenuItem.Checked) { liveAlwaysToolStripMenuItem.Checked = false; liveDuringMarketHoursToolStripMenuItem.Checked = false; } } } private void showMarketingPanel() { pnlMarketing.BringToFront(); pnlMarketing.Visible = true; linkLabel.Visible = true; } private void hideMarketingPanel() { pnlMarketing.Visible = false; pnlMarketing.SendToBack(); } //Once we know when the wpf control has fully rendered, we can get its height, which we use to set the //panel height of "tabLayoutPanelStrategies". This timer allows the last few pixels of the control to fully render. //If the wpf control(i.e. The strategy drop-down) hasn't fully rendered, we'd get a height = 0 result. //This timer is a one-time only thing, and is run *only* from the TopListForm_Shown method. Timer timer = new Timer(); private void TopListForm_Shown(object sender, EventArgs e) { timer.Interval = 500; timer.Tick += new EventHandler(RunEvent); timer.Start(); } private void RunEvent(object sender, System.EventArgs e) { timer.Stop(); selectTheFont(); } private void TopListForm_VisibleChanged(object sender, EventArgs e) { if (Visible) { if (GuiEnvironment.CLICKED_MAIN_FORM_MENU) { GuiEnvironment.CLICKED_MAIN_FORM_MENU = false; GuiEnvironment.SetWindowOpeningPosition(this, null); } } } public List Surf(int total) { List topRows = new List(); foreach (DataGridViewRow row in dataGridView1.Rows) { RowData rowData = row.DataBoundItem as RowData; if (null != rowData) topRows.Add(rowData); if (topRows.Count >= total) break; } return topRows; } public void SelectItem(RowData rowData) { dataGridView1.ClearSelection(); string symbol = rowData.GetSymbol(); foreach (DataGridViewRow row in dataGridView1.Rows) { RowData thisRowData = row.DataBoundItem as RowData; if (null != thisRowData && rowData.GetSymbol() == thisRowData.GetSymbol()) { row.Selected = true; foreach (DataGridViewCell cell in row.Cells) cell.Selected = true; } } int selectedCells = dataGridView1.SelectedCells.Count; int selectedRows = dataGridView1.SelectedRows.Count; Debug.WriteLine("[Surf SelectItem TopList] selected " + symbol + " total selected: " + selectedRows + " cells: " + selectedCells); } public void PostSurfingLogic() { // Add post surfing logic as required. } /// /// List of callbacks to notify surf managers that new items are available. /// private List> _dataRefreshCallbacks = new List>(); private SavedCellSelection _savedCellSelection; public void AddDataRefreshedCallback(Action callback) { if (!_dataRefreshCallbacks.Contains(callback)) _dataRefreshCallbacks.Add(callback); } public void AddNewEventCallback(Action callback) { // not implementing for top list windows } public SurfStatus SurfStatus() { return Surfer.SurfStatus.Available; } public Control GetOwner() { return this; } public string WindowTitle() { return Text; } public DataGridView GetDataGridView() { return dataGridView1; } private void TopListForm_TextChanged(object sender, EventArgs e) { // change Text of parent form if running in dock panel mode - RVH20210329 if (_dockPanelMode) { Form parent = (Form)this.Parent; if (parent != null) parent.Text = this.Text; } } private void realTimeStockRaceToolStripMenuItem_Click(object sender, EventArgs e) { GuiEnvironment.OpenRealTimeStockRace(true, false, false, _config, TopListSortType.DeltaValue, _selectedSortColumnInfo != null ? _selectedSortColumnInfo.WireName : null, null, this); } private void dataGridView1_ClientSizeChanged(object sender, EventArgs e) { bool historyMode = useHistoricalDateToolStripMenuItem.Checked; if (!historyMode) UpdateSingleSymbolSubscriptions(); } private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e) { _gridVisible = true; } private void dataGridView1_Paint(object sender, PaintEventArgs e) { if (dataGridView1.AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.Fill && _gridVisible) { dataGridView1.SuspendLayout(); foreach (Column column in dataGridView1.Columns) { keepColumnWidth(column); } dataGridView1.ResumeLayout(); } if (_adjustGridRowsHeigth) { dataGridView1.SuspendLayout(); dataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None; foreach (DataGridViewRow row in dataGridView1.Rows) { row.MinimumHeight = DEFAULT_ROW_HEIGHT; } _adjustGridRowsHeigth = false; dataGridView1.ResumeLayout(); } } private void dataGridView1_Resize(object sender, EventArgs e) { _formresizing = _gridVisible && (Control.MouseButtons & MouseButtons.Left) != 0; } public bool StrategyContainsPrivateList() { var symbolLists = GuiEnvironment.GetSymbolListsCacheManager(_connectionMaster)?.GetSymbolLists(); return HandlesStrategyUtils.StrategyConfigContainsPrivateList(_config, symbolLists); } private void showChromeDevToolsToolStripMenuItem_Click(object sender, EventArgs e) { _chromiumTreemapControl.ShowDevTools(); } private void showTreemapToolStripMenuItem_Click(object sender, EventArgs e) { _useBrowserInterface = !_useBrowserInterface; // Brad wants the TreeMap sort menu to display when the user enables the TreeMap view. showTreemapSortMenu.Checked = _useBrowserInterface; showTheTopPanel(showStrategiesStripMenuItem.Checked, showTreemapSortMenu.Checked); selectTheFont(); ShowHideBrowserControl(); contextMenuStrip1.Hide(); } /// /// Show/Hide the positions browser control. /// private void ShowHideBrowserControl() { if (_useBrowserInterface) { dataGridView1.Visible = false; _chromiumTreemapControl.Visible = true; _chromiumTreemapControl.BringToFront(); } else { dataGridView1.Visible = true; _chromiumTreemapControl.Visible = false; _chromiumTreemapControl.SendToBack(); } } private void tableLayoutPanelStrategies_VisibleChanged(object sender, EventArgs e) { if (_useBrowserInterface) { _chromiumTreemapControl.SetChartSize(true); } } private void chart1_VisibleChanged(object sender, EventArgs e) { if (_useBrowserInterface) { _chromiumTreemapControl.SetChartSize(true); } } } // This is a very specific rule for choosing colors. The algorithm for choosing // colors is to use a GradientInfo. But this takes care of issues like looking up // the value in a row, and caching the result. // // The caching part turned out not to be useful. // // Basically this class contains the name of a column from which to get the value, // a ColorChooser to interpret that value, and a callback for when one of the first // two changes. public class GradientColorChooser { public event MethodInvoker OnChange; public object Epoch { get; private set; } private void ReportChange() { Epoch = new object(); if (null != OnChange) OnChange(); } private GradientInfo _gradientInfo; public GradientInfo GradientInfo { // This is always copied on get and set, so we are sure the epoch changes every time this // changes. Presumably this is only get and set when presenting the GUI for the user to // changes these settings, so the cost should be reasonable and appropriate. get { if (null == _gradientInfo) return null; else return _gradientInfo.DeepCopy(); } set { if (null == value) _gradientInfo = null; else _gradientInfo = value.DeepCopy(); ReportChange(); } } string _fieldInternalCode = ""; public string FieldInternalCode { get { return _fieldInternalCode; } set { _fieldInternalCode = value; UpdateWireName(); } } private string _fieldWireName; private IList _columns; public void SetColumnInfo(IList columns) { _columns = columns; UpdateWireName(); } private void UpdateWireName() { string newWireName = CommonAlertFields.GetFilterWireName(_fieldInternalCode); if (newWireName != _fieldWireName) { _fieldWireName = newWireName; ReportChange(); } } public struct ColorPair { public Color ForeGround; public Color BackGround; } public ColorPair? GetColors(RowData rowData) { if (rowData.Data.ContainsKey(Epoch)) return rowData.Data[Epoch] as ColorPair?; ColorPair? result = ComputeColors(rowData); rowData.Data[Epoch] = result; return result; } private ColorPair? ComputeColors(RowData rowData) { if ((null == _fieldWireName) || (null == _gradientInfo) || (_gradientInfo.Empty())) // Not enough formatting information. return null; string valueString = rowData.GetAsString(_fieldWireName); double value; if (valueString == null || !ServerFormats.TryParse(valueString, out value)) // Invalid or missing data for this row. return null; ColorPair result; Color fg = _gradientInfo.GetColor(value); result.BackGround = fg; result.ForeGround = GradientInfo.AltColor(fg); return result; } private const string COLORS = "COLORS"; private const string COLUMN = "COLUMN"; public void Save(XmlNode node) { _gradientInfo.SaveAs(node, COLORS); if (FieldInternalCode != "") node.SetProperty(COLUMN, FieldInternalCode); } public void Load(XmlNode node) { // It might be nice to prevent this from signalling the listener twice. // Probably not that important. FieldInternalCode = node.Property(COLUMN); GradientInfo newColors = new GradientInfo(node.Node("COLORS")); GradientInfo = newColors; } } public class SavedCellSelection { public int HorizontalScrollPosition { get; } public int VerticalScrollPosition { get; } public int FirstCol { get; } public int FirstRow { get; } public int[] SelectedRows { get; } public int[] SelectedCols { get; } public int CurrentColCell { get; } public int CurrentRowCell { get; } public SavedCellSelection(int horizontalScrollPosition, int verticalScrollPosition, int firstCol, int firstRow, int[] selectedRows, int[] selectedCols, int currentColCell, int currentRowCell) { this.HorizontalScrollPosition = horizontalScrollPosition; this.VerticalScrollPosition = verticalScrollPosition; this.FirstCol = firstCol; this.FirstRow = firstRow; this.SelectedRows = selectedRows; this.SelectedCols = selectedCols; CurrentColCell = currentColCell; CurrentRowCell = currentRowCell; } } }