using System; using System.Collections; using System.Collections.Generic; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml; using TradeIdeas.MiscSupport; using TradeIdeas.ServerConnection; using TradeIdeas.TIProData; using TradeIdeas.TIProData.Interfaces; using TradeIdeas.XML; // TODO: Better testing of the event handlers. If we select a lot of items up top, and we change the import // text, what happens? Does the callback for a change to the selected items come at the end, after all items // have been unseleted, or does it call the callback after each item has been unseleted? // Does the selection callback happen often enough? What if we go from selecting 5 items to 6 items? Does it // get called? Does it need to get called? Does this matter? // What if we go from two items to none? Will the selected item be -1 in both cases? Does this matter? namespace TradeIdeas.TIProGUI { public partial class LoadFromCloud : Form { public static readonly WindowIconCache WindowIconCache = new WindowIconCache("LOAD_FROM_CLOUD"); private readonly ISendManager _sendManager; private IConnectionMaster _connectionMaster; private volatile bool _aborted; private string _baseWindowName; private string _fileSeekComboBox = ""; private bool _inConstructor = true; private bool _comboBoxInAction = false; /*Items come off of the server in Ascending order(date). However, when this window is first brought up, we wish the dates to be with the most current date on top (descending order). Hence the use for the below bool, _windowFirstLoaded.*/ private bool _windowFirstLoaded = true; private bool _createLayoutOnLoad = true; // new variable to set dock panel mode - RVH20210414 private bool _dockPanelMode = false; private const string FORM_TYPE = "FORM_TYPE"; public bool CreateLayoutOnLoad { get { return _createLayoutOnLoad; } set { _createLayoutOnLoad = value; } } public XmlDocument ItemDefinition { get; set; } //the below List is used in conjunction with search text box code //It also shall serve as the "server sorted" list (as opposed to ascending or descending list) private List _tempCurrentList = new List(); Dictionary _iconDictionary = new Dictionary(); bool _selectFromCloud = false; // change parameters to accommodate dock panel changes - RVH20210414 //public LoadFromCloud(SendManager sendManager, bool selectFromCloud = false) public LoadFromCloud(ISendManager sendManager, bool selectFromCloud = false, bool dockPanelMode = false) { _sendManager = sendManager; _connectionMaster = GuiEnvironment.FindConnectionMaster(""); InitializeComponent(); // set dock panel mode - RVH20210414 _dockPanelMode = dockPanelMode; //set the combo-box to item 0 "all file types" cboFileType.SelectedIndex = 0; WindowIconCache.SetIcon(this); _baseWindowName = Text; Text = _baseWindowName + " (Loading)"; DescribeSelectedItems(); if (!IsHandleCreated) CreateHandle(); RequestList(); _inConstructor = false; //Set default list view listView1.View = View.Details; detailsToolStripMenuItem.Checked = true; //Allow scrollbars to longDescriptionTextBox longDescriptionTextBox.ScrollBars = ScrollBars.Vertical; Font = GuiEnvironment.FontSettings; CheckClipboardForCloudLink(); _selectFromCloud = selectFromCloud; if (_selectFromCloud) { _baseWindowName = "Select from Cloud"; loadButton.Text = "Select"; loadButton.DialogResult = DialogResult.OK; } } private void CheckClipboardForCloudLink() { string clipboardText = Clipboard.GetText().Trim(); // Need to check for both http: and https: cloud links. if (clipboardText.StartsWith("http://www.trade-ideas.com/Cloud.html?code=") || clipboardText.StartsWith("https://www.trade-ideas.com/Cloud.html?code=")) importFileTextBox.Text = clipboardText; } /* From: Philip Smolen Date: Thu, Dec 15, 2016 at 10:22 AM Subject: Slowness in Brad's Load from Cloud To: Brad Williams , Julio Ortiz Hi. Brad says it's sometimes very slow when he opens the Load from Cloud window in his account. His account has a lot of items in this window. I tried this myself. I was able to reproduce it with his account. One time the request took almost 15 seconds on the server side. Most of the time it took about 1/100 that amount of time! The slowest time was the second time I tried. I was seeing about a half second of work on the client side, which I didn't measure precisely. I also tried this with my account. I was seeing server times of 0.068 - 0.08 seconds for the server. The client & server together were so fast that I never could see an empty window. I have 102 entries in my list, and Brad has 2,058 in hist list. After some research, I believe this is a database caching issue. We moved the "Misc database" tasks to their own process recently. That made it easier for me to make more database threads available. So more people can make requests at the same time. More tellers at the bank or more clerks at the grocery store. So one slow customer never has a big impact on anyone else. And if we do have too many requests in total, we can easily add capacity. Of course, this adds some confusion and related issues. We have two of these machines running at once. Your first request is routed to one of these at random. If you have multiple requests, and they are all sent around the same time, you'll probably stay on the same machine. Each machine has four threads. You get assigned a thread somewhat randomly. Like a lot of banks, there's just one line, and you get the next thread that is available. Mostly this is good, but it means that we might be routing these requests to 8 different database machines, somewhat randomly. So if you make the same request 4 times in a row, it's possible that the first one would be fast, but the next three would be slow because those machines don't have the data in their cache. I looked at the logs. I'm pretty sure this is what's happening. These processes are idle a lot. It is possible that the machines were just overloaded, but I don't think so. Other tasks using the same resources: Load a config window, load from cloud, and load the profile tab for the single stock window. I think the last one gets cached on the client side, so you might need to pick a different stock symbol each time. We handle a few more requests, but all in the background, so those would be harder for a user to see. My plan is to ignore this. He's asking for a lot of data that will eventually be in the cache. This is part of a dialog box, not a streaming request. Let me know if you see anything more. In particular, let me know if the problem is worse than I think it is. */ //private System.Diagnostics.Stopwatch _requestTimer = new System.Diagnostics.Stopwatch(); private void RequestList() { //System.Diagnostics.Debug.WriteLine("RequestList() started."); //_requestTimer.Restart(); _sendManager.SendMessage(TalkWithServer.CreateMessage("command", "cloud_list"), RequestListResponse); } public static void AutoResizeColumns(ListView lv) //courtesy of http://stackoverflow.com/questions/14133225/listview-autoresizecolumns-based-on-both-column-content-and-header { lv.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent); ListView.ColumnHeaderCollection cc = lv.Columns; for (int i = 0; i < cc.Count; i++) { int colWidth = TextRenderer.MeasureText(cc[i].Text, lv.Font).Width + 5; if (colWidth > cc[i].Width) { cc[i].Width = colWidth; } } } /// /// Called directly from communications library. Probably not in GUI thread. /// /// From server, or null if there is a communications error. /// Unused. private void RequestListResponse(byte[] body, object unused) { if (_aborted) return; if (null == body) RequestList(); else this.BeginInvokeIfRequired(delegate { RequestListResponse(body); }); } /// /// Called in GUI thread. /// /// Received from server. This will not be null. private void RequestListResponse(byte[] body) { //_requestTimer.Stop(); //System.Diagnostics.Debug.WriteLine("RequestListResponse() called after " + (_requestTimer.ElapsedMilliseconds / 1000.0) + " seconds"); if (_aborted) return; _tempCurrentList.Clear(); XmlDocument document = XmlHelper.Get(body); foreach (XmlNode node in document.Node(0).Node("ROWS").Enum()) { CloudLayoutItem dataItem = new CloudLayoutItem(node); ListViewItem guiItem = new ListViewItem(); guiItem.Text = dataItem.ShortDescription; guiItem.SubItems.Add(dataItem.Creation); guiItem.SubItems.Add(dataItem.FileType); guiItem.SubItems.Add(dataItem.WindowCount); guiItem.ImageIndex = GetIconId(dataItem.Icon); guiItem.Tag = dataItem; if (dataItem.FileType == _fileSeekComboBox || _fileSeekComboBox == "") { listView1.Items.Add(guiItem); _tempCurrentList.Add(guiItem); } } if (_windowFirstLoaded) //for the "Time" column when this window is first loaded... { _tempCurrentList.Clear(); doInitialSort(1, SortOrder.Descending, "d"); //The "Time" column //set the little caret arrow to point downward. listView1.ShowHeaderIcon(listView1, 1, SortOrder.Descending); listView1.Columns[1].Tag = "Ascending"; _windowFirstLoaded = false; /*We're reloading the _tempList when the window is first loaded. This is to address a weird bug whereby user clicks on the search bar and the item order changes.*/ foreach (ListViewItem item in listView1.Items) { _tempCurrentList.Add(item); } } if (listView1.Items.Count < 1) Text = _baseWindowName + " (No Data)"; else { Text = _baseWindowName; AutoResizeColumns(listView1); } DescribeSelectedItems(); if (_comboBoxInAction) { _comboBoxInAction = false; searchIt(_tempCurrentList); //This if-block accessed from the combobox. findActiveColumnAndSort(); } } private int GetIconId(string iconName) { //does dictionary contain the icon name? if (_iconDictionary.ContainsKey(iconName)) { return _iconDictionary[iconName]; //the value..which is the image index } else { WindowIconCache source = new WindowIconCache(iconName); Icon icon = source.Icon; try { // This is required to get a high quality version of the icon. If you // add the icon directly to the image list, the icon will be resized // automatically, but it won't look as good. // Need to convert icons to bitmaps before adding them to the image list. // Doing so will avoid a dark border around the image. using (Icon smallIcon = new Icon(icon, smallImageList.ImageSize)) { smallImageList.Images.Add(smallIcon.ToBitmap()); } using (Icon largeIcon = new Icon(icon, largeImageList.ImageSize)) { largeImageList.Images.Add(largeIcon.ToBitmap()); } } catch { // Is it possible that the image was added to one list but not the other? while (smallImageList.Images.Count > largeImageList.Images.Count) smallImageList.Images.RemoveAt(largeImageList.Images.Count); } _iconDictionary.Add(iconName, smallImageList.Images.Count - 1); return smallImageList.Images.Count - 1; } } private void largeIconToolStripMenuItem_Click(object sender, EventArgs e) { listView1.View = View.LargeIcon; largeIconToolStripMenuItem.Checked = true; detailsToolStripMenuItem.Checked = smallIconToolStripMenuItem.Checked = listToolStripMenuItem.Checked = tileToolStripMenuItem.Checked = false; } private void detailsToolStripMenuItem_Click(object sender, EventArgs e) { listView1.View = View.Details; detailsToolStripMenuItem.Checked = true; largeIconToolStripMenuItem.Checked = smallIconToolStripMenuItem.Checked = listToolStripMenuItem.Checked = tileToolStripMenuItem.Checked = false; } private void smallIconToolStripMenuItem_Click(object sender, EventArgs e) { listView1.View = View.SmallIcon; smallIconToolStripMenuItem.Checked = true; largeIconToolStripMenuItem.Checked = detailsToolStripMenuItem.Checked = listToolStripMenuItem.Checked = tileToolStripMenuItem.Checked = false; } private void listToolStripMenuItem_Click(object sender, EventArgs e) { listView1.View = View.List; listToolStripMenuItem.Checked = true; largeIconToolStripMenuItem.Checked = detailsToolStripMenuItem.Checked = smallIconToolStripMenuItem.Checked = tileToolStripMenuItem.Checked = false; } private void tileToolStripMenuItem_Click(object sender, EventArgs e) { listView1.View = View.Tile; tileToolStripMenuItem.Checked = true; largeIconToolStripMenuItem.Checked = detailsToolStripMenuItem.Checked = smallIconToolStripMenuItem.Checked = listToolStripMenuItem.Checked = false; } private void listView1_ItemActivate(object sender, EventArgs e) { loadButton.PerformClick(); } /// /// If we are in the process of handing a user change, and we make another change to the GUI, /// we don't want to keep firing events. We only want to fire events when the user changes /// the GUI. We use thise flag to mask secondary changes. /// private bool _ignoreChanges; private void listView1_SelectedIndexChanged(object sender, EventArgs e) { if (_ignoreChanges) return; if (listView1.SelectedItems.Count > 0) { _ignoreChanges = true; importFileTextBox.Clear(); _ignoreChanges = false; } DescribeSelectedItems(); } private void DescribeSelectedItems() { if (_loadInProgress) { // Disable everything but cancel. Usually we are in this state for a very short // amount of time, and you can't even see anything. But if it is slow, I want to // make sure it looks good. It should be obvious that you hit something. You // shouldn't be able to load a second layout as that might be confusing. And // starting anything else seems like a bad idea because as soon as the layout is // loaded, the other operations will be aborted. listView1.Enabled = false; importFileTextBox.ReadOnly = true; loadButton.Enabled = false; deleteButton.Enabled = false; shareButton.Enabled = false; revokeShartingButton.Enabled = false; return; } int selectedCount = listView1.SelectedItems.Count; if (importFileTextBox.Text != "") longDescriptionTextBox.Text = "Import window or layout from another user."; else if (selectedCount == 1) longDescriptionTextBox.Text = (listView1.SelectedItems[0].Tag as CloudLayoutItem).LongDescription; else longDescriptionTextBox.Text = "Select a window or layout."; if (_selectFromCloud) { loadButton.Enabled = (selectedCount == 1) || (importFileTextBox.Text != ""); deleteButton.Enabled = false; shareButton.Enabled = false; revokeShartingButton.Enabled = false; // Get the cloud link of selected item if (listView1.SelectedItems.Count < 1) // This should not happen! return; string code = (listView1.SelectedItems[0].Tag as CloudLayoutItem).Code; if (null == code) // This should not happen! return; // Modify _code to use https:. code = code.Replace("http:", "https:"); // We need to keep the selected item when we populate the cloud link text box. _ignoreChanges = true; importFileTextBox.Text = code; _ignoreChanges = false; } else { loadButton.Enabled = (selectedCount == 1) || (importFileTextBox.Text != ""); deleteButton.Enabled = selectedCount > 0; shareButton.Enabled = (selectedCount == 1) && (null != (listView1.SelectedItems[0].Tag as CloudLayoutItem).Code); // I want to say revokable = listView1.SelectedItems.Any(item => null != (current.Tag as CloudLayoutItem).Code) // But Any() doesn't work with this type of list. bool revokable = false; foreach (ListViewItem current in listView1.SelectedItems) { if (null != (current.Tag as CloudLayoutItem).Code) { revokable = true; break; } } revokeShartingButton.Enabled = revokable; } } // change parameters to accommodate dock panel changes - RVH20210414 //public static void DoIt(SendManager sendManager, bool selectFromCloud = false) public static void DoIt(ISendManager sendManager, bool selectFromCloud = false, bool dockPanelMode = false) { using (LoadFromCloud dialog = new LoadFromCloud(sendManager, selectFromCloud, dockPanelMode)) { dialog.StartPosition = FormStartPosition.CenterParent; dialog.ShowDialog(); } } private string _selectedCloudLink = ""; public string SelectedCloudLink() { return _selectedCloudLink; } private void importFileTextBox_TextChanged(object sender, EventArgs e) { if (_ignoreChanges) return; if (importFileTextBox.Text != "") { _ignoreChanges = true; listView1.SelectedItems.Clear(); _ignoreChanges = false; } DescribeSelectedItems(); } private void LoadFromCloud_FormClosed(object sender, FormClosedEventArgs e) { _aborted = true; } private void deleteButton_Click(object sender, EventArgs e) { // Create a copy of the list. As soon as we start deleting things from the GUI, the // original list will change and the iterators will probably be invalidated. List toDelete = new List(listView1.SelectedItems.Count); foreach (ListViewItem item in listView1.SelectedItems) { SendDeleteCommand((item.Tag as CloudLayoutItem).ID); toDelete.Add(item); } foreach (ListViewItem item in toDelete) { listView1.Items.Remove(item); _tempCurrentList.Remove(item); } } private void SendDeleteCommand(string id) { var command = TalkWithServer.CreateMessage("command", "cloud_delete", "id", id); _sendManager.SendMessage(command, DeleteCommandResponse, false, command); } private void DeleteCommandResponse(byte[] response, object originalCommand) { if (_aborted) // For simplicity we stop retrying anything when the user closes the window. That style // is helpful with other commands because they have a GUI component to their reaction. // Here I'm just repeating that style. It is tempting to create a separate object that // remembers these requests separate from the GUI, like the symbol list manager and several // other objects, but that might be overkill. In the worst case the user just has to // click the delete button again. return; if (null == response) // On communications failure resend the same command. _sendManager.SendMessage(originalCommand as Dictionary, DeleteCommandResponse, false, originalCommand); // else... report success? That seems painful and confusing at best. We actually have a // separate window to say please wait and to eventually report success or failure when // saving a strategy. That is currently ugly to say the least. } private void shareButton_Click(object sender, EventArgs e) { if (listView1.SelectedItems.Count < 1) // This should not happen! return; string code = (listView1.SelectedItems[0].Tag as CloudLayoutItem).Code; if (null == code) // This should not happen! return; // Modify _code to use https:. code = code.Replace("http:", "https:"); try { Clipboard.SetText(code); } catch { } //MessageBox.Show("A link to these settings has been copied to the clipboard.\r\n" // + "You can paste that link into an email or IM.", // "Share from Cloud"); //Open collaborate form CollaborateForm form = new CollaborateForm(); form.ConfigString = code; form.Text = "Load from Cloud"; form.SetCloudOptions(); form.ShowDialog(this); form.Dispose(); } private void revokeShartingButton_Click(object sender, EventArgs e) { foreach (ListViewItem listViewItem in listView1.SelectedItems) { CloudLayoutItem cloudLayoutItem = listViewItem.Tag as CloudLayoutItem; if (null != cloudLayoutItem.Code) { cloudLayoutItem.InvalidateCode(); SendRevokeCommand(cloudLayoutItem.ID); } } DescribeSelectedItems(); } private void SendRevokeCommand(string id) { var message = TalkWithServer.CreateMessage("command", "cloud_revoke", "id", id); _sendManager.SendMessage(message, RevokeCommandResponse, false, id); } private void RevokeCommandResponse(byte[] body, object clientId) { if (_aborted) // See the notes in DeleteCommandResponse. We stop trying when someone closes the window. return; if (null == body) // Communications error. Retry. SendRevokeCommand((string)clientId); } private bool _loadInProgress; private void loadButton_Click(object sender, EventArgs e) { if (_selectFromCloud && !String.IsNullOrEmpty(importFileTextBox.Text)) { _selectedCloudLink = importFileTextBox.Text.Trim(); } else { if (importFileTextBox.Text.Contains("code") || importFileTextBox.Text.Trim() == "") { Text = _baseWindowName + " (Loading)"; _loadInProgress = true; DescribeSelectedItems(); Dictionary command; if (listView1.SelectedItems.Count > 0) command = TalkWithServer.CreateMessage("command", "cloud_load", "id", (listView1.SelectedItems[0].Tag as CloudLayoutItem).ID); else { string scrubbedCloudLink = ScrubCloudLink(importFileTextBox.Text); command = TalkWithServer.CreateMessage("command", "cloud_import", "code", scrubbedCloudLink); } SendLoadImportCommand(command); } else { loadCollaborateLinks((importFileTextBox.Text).Trim()); } } } /// /// Scrubs the cloud link, doing any trimming and modifications required before sending the code to the server. /// /// /// public static string ScrubCloudLink(string rawCloudLink) { string scrubbedLink = rawCloudLink.Trim(); System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex("code=([^=^&]+)&?"); var match = regex.Match(scrubbedLink); if (match.Success) return "https://www.trade-ideas.com/Cloud.html?code=" + match.Groups[1].Value; return scrubbedLink; } private void loadCollaborateLinks(string candidate) { Text = _baseWindowName + " (Loading)"; string [] data = getConfigArray(candidate); if (data.Length != 2) { MessageBox.Show("Unable to read this collaborate link. If you're loading a collaborate link please include the entire URL string.", "Unreadable Collaborate Link"); } else { if (data[0].Contains("_TopListResult.html")) //toplist { TopListForm tForm = new TopListForm(_connectionMaster, data[1]); tForm.Show(); GuiEnvironment.SetWindowOpeningPosition(tForm, this); DialogResult = System.Windows.Forms.DialogResult.OK; } else if (data[0].Contains("View.php")) //alert (or multistrategy with single alert..cant tell) default to alert { AlertForm aForm = new AlertForm(_connectionMaster, data[1]); aForm.Show(); GuiEnvironment.SetWindowOpeningPosition(aForm, this); DialogResult = System.Windows.Forms.DialogResult.OK; } else if (data[0].Contains("MultiStrategy.html")) { MultiStrategy mForm = new MultiStrategy(_connectionMaster); mForm.SetConfiguration(data[1]); mForm.Show(); GuiEnvironment.SetWindowOpeningPosition(mForm, this); DialogResult = System.Windows.Forms.DialogResult.OK; } else { MessageBox.Show("Unable to read this collaborate link.", "Unreadable Collaborate Link"); } } Text = _baseWindowName; } private string[] getConfigArray(string data) { string[] retval = data.Split('?'); return retval; } private void SendLoadImportCommand(object command) { _sendManager.SendMessage((Dictionary)command, LoadImportCommandResponse, false, command); } private void LoadImportCommandResponse(byte[] body, object clientId) { if (_aborted) return; if (null == body) SendLoadImportCommand(clientId); else this.BeginInvokeIfRequired(delegate { LoadImportCommandResponse(body); }); } /// /// This is always called in the GUI thread. /// /// Never null. private void LoadImportCommandResponse(byte[] body) { XmlNode wrapper = XmlHelper.Get(body).Node(0).Node("LAYOUT"); bool clearPrevious = wrapper.Property("CLEAR_PREVIOUS", false); bool fillScreen = wrapper.Property("FILL_SCREEN", false); /* Bug / TODO: * Currently fill screen works pretty well if you are making things bigger. * If you a trying to move things to a smaller screen you have problems. * First we do a normal load layout, then we adjust the layout. But the normal * load layout will see that some windows are off your screen, and will adjust * them before ScreenFiller gets a chance to look at them. Maybe we need a * special flag to tell the normal layout manager not to fix those windows that * are off screen. */ XmlDocument layout = XmlHelper.Get(Encoding.UTF8.GetBytes(wrapper.Text())); if (null == layout) { StringBuilder sb = new StringBuilder("Unable to load this item."); if (wrapper.Text() == "") // This isn't necessarily right. // When a layout is too big it will also appear this way. // The server knows the difference, but it doesn't tell the client. sb.Append(" (Not found.)"); else sb.Append(" (Corrupted.)"); MessageBox.Show(sb.ToString(), "Load from Cloud Failure"); } else { ItemDefinition = layout; if (_createLayoutOnLoad) { // add dock panel mode parameter - RVH20210414 //LayoutManager.Instance().Restore(layout.DocumentElement, clearPrevious, fillScreen); bool docked = false; // check for dock panel form to determine how to display the windows - RVH20210504 // modify this code to only use the logic of whether or not a layout contains a dock panel form // to determine if it should be loaded in docked mode - RVH20210616 //if (_dockPanelMode) //{ docked = LayoutManager.Instance().ContainsDockPanelForm(layout.DocumentElement); if (docked) LayoutManager.Instance().DockPanelFormFocused = null; // keep LayoutManager.Restore from thinking this layout was loaded from a docked channel bar //} LayoutManager.Instance().Restore(layout.DocumentElement, clearPrevious, fillScreen, docked); } // GuiEnvironment.showPurchasePopup(_sendManager.isDemo()); } DialogResult = System.Windows.Forms.DialogResult.OK; } private void findActiveColumnAndSort() { /*This method is for sorting the columns appropriately when one has used the fileType combo box. The "activated" column is the column whose tag is either "Ascending" or "Descending" Those that have a tag of "None" don't have the little graphical "caret" showing in the column header*/ string colType = ""; SortOrder s = SortOrder.None; int colIndex = 0; Boolean isMatch = false; for (int i = 0; i < listView1.Columns.Count; i++) { switch (listView1.Columns[i].Tag.ToString()) { case "None": break; case "Ascending": colType = getColumnType(i); s = SortOrder.Descending; colIndex = i; if (colIndex == listView1.getColumnIconIndex()) { isMatch = true; } break; case "Descending": colType = getColumnType(i); s = SortOrder.Ascending; colIndex = i; if (colIndex == listView1.getColumnIconIndex()) { isMatch = true; } break; } if (isMatch) { break; } } ItemComparer sorter = listView1.ListViewItemSorter as ItemComparer; sorter = new ItemComparer(colIndex, s, colType); listView1.ListViewItemSorter = sorter; listView1.Sort(); } private String getColumnType(int index) { //"t" is a textual column, "n" is numeric column, "d" is datetime column string colType = ""; switch (index) { case 0: colType = "t"; break; case 1: colType = "d"; break; case 2: colType = "t"; break; case 3: colType = "n"; break; default: colType = "t"; break; } return colType; } /*The method below is fired first in "InitializeComponent()" within the constructor; hence the reason behind using a boolean called "_inConstructor"*/ private void cboFileType_SelectedIndexChanged(object sender, EventArgs e) { switch (cboFileType.SelectedIndex) { case 1: _fileSeekComboBox = "Layout"; break; case 2: _fileSeekComboBox = "Alert Window"; break; case 3: _fileSeekComboBox = "Top List Window"; break; case 4: _fileSeekComboBox = "Multi Strategy Window"; break; case 5: _fileSeekComboBox = "Compare Count Window"; break; case 6: _fileSeekComboBox = "Single Stock Window"; break; case 7: _fileSeekComboBox = "Chart Window"; break; case 8: _fileSeekComboBox = "RBI/GBI Window"; break; case 9: _fileSeekComboBox = "Price Alerts Window"; break; case 10: _fileSeekComboBox = "Price Alerts"; break; case 11: _fileSeekComboBox = "Brokerage Plus Window"; break; case 12: _fileSeekComboBox = "Market Explorer Window"; break; case 13: _fileSeekComboBox = "Real-Time Stock Race Window"; break; default: _fileSeekComboBox = ""; break; } if (_inConstructor == false) { _comboBoxInAction = true; listView1.Items.Clear(); DescribeSelectedItems(); RequestList(); } } private void txtSearch_TextChanged(object sender, EventArgs e) { searchIt(_tempCurrentList); } private void searchIt(List tempCurrentList) { //code inspired from http://www.worldbestlearningcenter.com/index_files/csharp-Sample-Dictionary-workable-search-box.htm List searchHits = new List(); try { for (int i = 0; i <= tempCurrentList.Count - 1; i++) { if ( tempCurrentList[i].Text.ToUpper().Contains(txtSearch.Text.ToUpper())) { searchHits.Add(tempCurrentList[i]); } } listView1.Items.Clear(); foreach (ListViewItem lV in searchHits) { listView1.Items.Add(lV); } } catch { } } private void listView1_ColumnClick(object sender, ColumnClickEventArgs e) { int columnIndex = e.Column; string colType = getColumnType(columnIndex); if ((string)listView1.Columns[columnIndex].Tag == "Descending") { doSorting(e, SortOrder.Ascending, colType); } else if ((string)listView1.Columns[columnIndex].Tag == "Ascending") { doSorting(e, SortOrder.Descending, colType); } } private void doSorting(ColumnClickEventArgs f, SortOrder s, string colVal) { ItemComparer sorter = listView1.ListViewItemSorter as ItemComparer; sorter = new ItemComparer(f.Column, s, colVal); listView1.ListViewItemSorter = sorter; listView1.Sort(); } /// /// The doInitialSort is used for when the loadfromcloud window is /// first instantiated. The items are initially entered in ascending /// order by date from the server. however we wish to have the date /// with the most recent on top in the ListView (descending order). /// /// /// /// private void doInitialSort(int colIndex, SortOrder s, string colVal) { ItemComparer sorter = listView1.ListViewItemSorter as ItemComparer; sorter = new ItemComparer(colIndex, s, colVal); listView1.ListViewItemSorter = sorter; listView1.Sort(); listView1.ListViewItemSorter = null; } } // Implements the manual sorting of items by columns. //Code courtesy of:http://www.c-sharpcorner.com/uploadfile/nipuntomar/sort-a-multicolumn-listview-in-C-Sharp/ //Some minor tweaking done..Wanted to get rid of the "goto" from the original code in the above link... public class ItemComparer : IComparer { public int Column { get; set; } //Order of sorting public SortOrder Order = SortOrder.None; public string _valType; public ItemComparer(int colIndex,SortOrder s,string valType) { Column = colIndex; Order = s; _valType = valType; } public int Compare(object a, object b) { int result = 0; ListViewItem itemA = a as ListViewItem; ListViewItem itemB = b as ListViewItem; if (itemA == null && itemB == null) result = 0; else if (itemA == null) result = -1; else if (itemB == null) result = 1; if (itemA == itemB) result = 0; // datetime comparison if (_valType == "d") //dates { DateTime x1, y1; // Parse the two objects passed as a parameter as a DateTime. if (!DateTime.TryParse(itemA.SubItems[Column].Text, out x1)) x1 = DateTime.MinValue; if (!DateTime.TryParse(itemB.SubItems[Column].Text, out y1)) y1 = DateTime.MinValue; result = DateTime.Compare(x1, y1); } //numeric comparison if (_valType == "n") //numbers { decimal x2, y2; if (!Decimal.TryParse(itemA.SubItems[Column].Text, out x2)) x2 = Decimal.MinValue; if (!Decimal.TryParse(itemB.SubItems[Column].Text, out y2)) y2 = Decimal.MinValue; result = Decimal.Compare(x2, y2); } //alphabetic comparison if (_valType == "t") { result = String.Compare(itemA.SubItems[Column].Text, itemB.SubItems[Column].Text); } // if sort order is descending. if (Order == SortOrder.Descending) // Invert the value returned by Compare. result *= -1; return result; } } public class CloudLayoutItem { /// /// This says we should load the file like a traditional layout. I.e. we should start /// by closing old windows before opening the new ones described in here. This particular /// copy is only to display on the screen for the end user's information. We will get /// another copy when we ask for more details about the specific strategy. /// public bool ClearPreviousLayout { get; private set; } /// /// This says that after loading the layout we should try to resize the forms to fit /// perfectly on the current user's screen. /// public bool FillScreen { get; private set; } /// /// This is the code that you can give to other people so they can use your settings. /// /// This can be null to say that we don't know the code. /// public string Code { get; private set; } /// /// Mark the code as unknown or unavailable. This goes with the revoke button. Revoke /// actually just changes the value, rather than removing it. But only the server knows /// the new code. For simplicity we don't automatically grab the new one from the server. /// The user would have to close and reopen the window to see the new code. /// public void InvalidateCode() { Code = null; } /// /// A string suitable for submission to /// public string Icon { get; private set; } /// /// This is a unique id which never changes and is never reused. Currently it's an integer /// but there's no reason it couldn't change to a long integer or something stranger in the /// future. No reason to try to interpret it here. /// public string ID { get; private set; } /// /// To display in the big text box. /// public string LongDescription { get; private set; } /// /// To display in the list of items, next to the icon. /// public string ShortDescription { get; private set; } /// /// For the user's info, only. No real value. Display it for him. /// public string WindowCount { get; private set; } /// /// A user friendly string to display for the user. This takes and /// into account. /// public string FileType { get; private set; } /// /// This is the creation date of the item in question /// The time comes off a string in "Server time" which must be converted to DateTime? /// public string Creation { get; private set; } /// /// Load this from the server. /// /// public CloudLayoutItem(XmlNode source) { ClearPreviousLayout = source.Property("CLEAR_PREVIOUS_LAYOUT") == "1"; FillScreen = source.Property("FILL_SCREEN") == "1"; Code = source.Property("CODE"); Icon = source.Property("ICON"); ID = source.Property("ID"); LongDescription = source.Property("LONG_DESCRIPTION"); ShortDescription = source.Property("SHORT_DESCRIPTION"); WindowCount = source.Property("WINDOW_COUNT"); string serverTime = source.Property("CREATION"); DateTime? test = ServerFormats.DecodeServerTime(serverTime); if (test == null) { Creation = ""; } else { DateTime result = (DateTime)test; // string datePart = result.ToShortDateString(); string datePart = result.ToString(); Creation = datePart; } // Creation = Convert.ToDateTime(source.Property("CREATION")); if (WindowCount == "0") WindowCount = ""; if (ClearPreviousLayout) FileType = "Layout"; else switch (Icon) { case "ALERTS": FileType = "Alert Window"; break; case "TOP_LIST": FileType = "Top List Window"; break; case "MULTI_STRATEGY": FileType = "Multi Strategy Window"; break; case "COMPARE_COUNT": FileType = "Compare Count Window"; break; case "SINGLE_SYMBOL_WINDOW": FileType = "Single Stock Window"; break; case "CHARTS": FileType = "Chart Window"; break; case "END_OF_CANDLE": FileType = "RBI/GBI Window"; break; case "LIMIT_ALERTS_FORM": FileType = "Price Alerts Window"; break; case "LIMIT_ALERTS": FileType = "Price Alerts"; break; case "TRADING_STRATEGY_GRID": FileType = "Brokerage Plus Window"; break; case "MARKET_EXPLORER_FORM": FileType = "Market Explorer Window"; break; case "REAL_TIME_STOCK_RACE_WINDOW": FileType = "Real-Time Stock Race Window"; break; default: FileType = ""; break; } } } }