using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using TradeIdeas.TIProData; using TradeIdeas.TIProData.Interfaces; namespace TradeIdeas.TIProGUI { public delegate void SymbolListsReadyUpdate(SymbolListsCacheManager sender); // Inheriting from UserControl solely to make sure this all runs in the GUI // thread. Maybe there is a better way, seems like overkill. public class SymbolListsCacheManager : UserControl { private IConnectionMaster _connectionMaster; private int _maxId = 0; private Dictionary> _allSymbolLists; public event SymbolListsReadyUpdate SymbolListsReadyUpdate; public bool Ready { get; private set; } public SymbolListsCacheManager(IConnectionMaster connectionMaster) { _allSymbolLists = new Dictionary>(); _connectionMaster = connectionMaster; Ready = false; } /// /// The ID of the newly created list. /// public int CreateList(string listName) { if (!Ready) return -1; ListManager.List list; // Note: The smallest id number we will create is 1. There are some special lists with // ids that are less than 0, but those cannot be created here. Positive numbers are // reserved for this routine and others like it in GWT, PHP, etc. Negative numbers are // reserved for things like the favorites list. The favorites list will have the same // id number for all users. 0 will never be legal. It is reserved for things like // error messages. for (int i = Math.Max(_maxId, 0) + 1; true; i++) { list = _connectionMaster.ListManager.Find(i); if (!_allSymbolLists.ContainsKey(list)) { if (listName.Length == 0) listName = "unnamed list #" + i; list.Name = listName; list.DeleteSymbol("~~~~~~"); _allSymbolLists[list] = new HashSet(); _maxId = i; break; } } ReadyUpdate(true); return list.Id; } public void RenameList(int id, string listName) { if (!Ready) return; ListManager.List list = _connectionMaster.ListManager.Find(id); if (_allSymbolLists.ContainsKey(list)) { list.Name = listName; list.DeleteSymbol("~~~~~~"); ReadyUpdate(true); } else { // renaming a list that doesn't already exist, error? } } public string GetListName(int id, string defaultName = "") { if (!Ready) return defaultName; ListManager.List list = _connectionMaster.ListManager.Find(id); if (!_allSymbolLists.ContainsKey(list)) return defaultName; return list.Name; } public void DeleteList(int id) { if (!Ready) return; ListManager.List list = _connectionMaster.ListManager.Find(id); if (_allSymbolLists.ContainsKey(list)) { list.DeleteList(); _allSymbolLists.Remove(list); ReadyUpdate(true); } } public void AddSymbol(int id, string symbol) { if (!Ready) return; ListManager.List list = _connectionMaster.ListManager.Find(id); if (_allSymbolLists.ContainsKey(list)) { list.AddSymbol(symbol); _allSymbolLists[list].Add(symbol); ReadyUpdate(true); } } public void DeleteSymbol(int id, string symbol) { ListManager.List list = _connectionMaster.ListManager.Find(id); if (_allSymbolLists.ContainsKey(list)) { list.DeleteSymbol(symbol); _allSymbolLists[list].Remove(symbol); ReadyUpdate(true); } } public HashSet GetSymbols(int id) { ListManager.List list = _connectionMaster.ListManager.Find(id); HashSet returnValue; if (_allSymbolLists.ContainsKey(list)) returnValue = new HashSet(_allSymbolLists[list]); else returnValue = new HashSet(); return returnValue; } public void SetAllSymbols(int id, HashSet allSymbols) { ListManager.List list = _connectionMaster.ListManager.Find(id); if (_allSymbolLists.ContainsKey(list)) { list.SetAllSymbols(allSymbols); _allSymbolLists[list] = new HashSet(allSymbols); ReadyUpdate(true); } } public class SymbolListInfo : IComparable { private ListManager.List _list; public int Id { get { return _list.Id; } } public HashSet Symbols { get; private set; } public int CompareTo(object obj) { return _list.Id.CompareTo((obj as SymbolListInfo)._list.Id); } public SymbolListInfo(ListManager.List list, HashSet symbols) { _list = list; Symbols = symbols; } /// /// The complete name of the list. It might include "|" or other special items. /// We don't do any special processing. We just copy it from the database. /// public string Name { get { return _list.Name; } } /// /// This returns the standard suffix, like " [4]" or " [Empty]". /// This can return "" in some special cases. In normal use we don't expect this to happen. /// It's just a safety in case we don't have the relevant information. /// public string SizeSuffix { get { if (null == Symbols) return ""; if (Symbols.Count == 0) return " [Empty]"; return " [" + Symbols.Count + "]"; } } /// /// This is designed specifically for use in a list box in the TI Pro list of lists window. /// /// Something like "My Symbols [4]" or "Recent downgrades [Empty]" public override string ToString() { return Name + SizeSuffix; } } public List GetSymbolLists() { List returnValue = new List(); foreach (ListManager.List list in _allSymbolLists.Keys) { SymbolListInfo symbolListInfo = new SymbolListInfo(list, _allSymbolLists[list]); // We keep this list sorted at all times. If BinarySearch // does not find the list, which it never should, it will // return the binary complement of the position to insert. int index = returnValue.BinarySearch(symbolListInfo); Debug.Assert(index < 0); returnValue.Insert(~index, symbolListInfo); } return returnValue; } public void RefreshFromServer() { _allSymbolLists.Clear(); _connectionMaster.ListManager.GetListOfLists(ListOfListsCallback); ReadyUpdate(false); } private void ReadyUpdate(bool ready) { Ready = ready; SymbolListsReadyUpdate callback = SymbolListsReadyUpdate; if (callback != null) try { callback(this); } catch { //keep working } } private void ListOfListsCallback(List lists) { if (InvokeRequired) BeginInvoke((MethodInvoker)delegate { ListOfListsCallback(lists); }); else { if (IsDisposed) return; //createNewListButton.Enabled = true; //refreshButton.Enabled = true; //symbolListsListBox.Enabled = true; //listInfoBindingList.Clear(); foreach (ListManager.List list in lists) { _allSymbolLists[list] = null; list.RequestFromServer(OnListReturned); } //setEnabledListSpecificButtons(); if (lists.Count == 0) { ReadyUpdate(true); } } } private void OnListReturned(ListManager.List list, HashSet symbols) { if (InvokeRequired) BeginInvoke((MethodInvoker)delegate { OnListReturned(list, symbols); }); else { if (IsDisposed) return; if (list.Id > _maxId) _maxId = list.Id; // save a copy of the symbols _allSymbolLists[list] = new HashSet(symbols); bool done = true; // If we are still waiting for other lists then we will see a null // cached symbols. If not we are done and we send the callback to // report that the symbol lists are ready. foreach(HashSet cachedSymbols in _allSymbolLists.Values) { if (cachedSymbols == null) { done = false; break; } } if (done) { Ready = true; SymbolListsReadyUpdate callback = SymbolListsReadyUpdate; if (callback != null) try { callback(this); } catch { //keep working } } } } } }