{****************************************************** Trade-Ideas Alerts Symbol Lists Dispatcher (Primary Provider, Primary Controller, Primary Updater) Copyright (C) 2004-2006 Trade-Ideas LLC ******************************************************} unit tiSymbolListsDispatcher; interface uses Classes, Forms, Dialogs, SysUtils, StdCtrls, ExtCtrls, TradeIdeasWindow_TLB, tiConst, tiResources, tiTypes; type // Symbol list record TSymbolList = record // List ID ID:Integer; // List name Name:string; // Symbols of list Symbols:TStringList; end; PSymbolList = ^TSymbolList; // Symbol list cache // Now we organize cache as the list of TSymbolList records TSymbolListCache = TList; // Abstract class for symbol list listeners // Listeners are used to request, monitor and // implement changes to the symbol list cache TSymbolListChanges = class(TObject) public procedure BeginTransaction; virtual; abstract; procedure EndTransaction(Commit:Boolean); virtual; abstract; procedure CreateList(ID:Integer; Name:string); virtual; abstract; procedure RenameList(ID:Integer; NewName:string); virtual; abstract; procedure DeleteList(ID:Integer); virtual; abstract; procedure FillList(ID:Integer; NewSymbols:TStrings); virtual; abstract; procedure CopyList(DestID:Integer; DestName: string; SourceIDs: array of Integer); virtual; abstract; procedure AddSymbol(ID:Integer; Symbol:string); virtual; abstract; procedure DeleteSymbol(ID:Integer; Symbol:string); virtual; abstract; end; // Abstract class for symbol list providers // Provider refreshes the symbol list cache // by downloading lists from server (currently) or from // other data source TSymbolListProvider = class(TObject) public procedure Refresh; virtual; abstract; procedure Cancel; virtual; abstract; end; // Primary symbol list controller // Controls the primary local symbol list cache and // dispatch events to the connected listeners TPrimarySymbolListController = class(TSymbolListChanges) private // List of connected listeners FListeners:TList; protected // Procedures to dispatch events to the connected listeners procedure DispatchBeginTransaction; procedure DispatchEndTransaction(Commit:Boolean); procedure DispatchCreateList(ID:Integer; Name:string); procedure DispatchRenameList(ID:Integer; NewName:string); procedure DispatchDeleteList(ID:Integer); procedure DispatchFillList(ID:Integer; NewSymbols:TStrings); procedure DispatchCopyList(DestID:Integer; DestName: string; SourceIDs: array of Integer); procedure DispatchAddSymbol(ID:Integer; Symbol:string); procedure DispatchDeleteSymbol(ID:Integer; Symbol:string); public constructor Create; destructor Destroy; override; // Control commands procedure BeginTransaction; override; procedure EndTransaction(Commit:Boolean); override; procedure CreateList(ID:Integer; Name:string); override; procedure RenameList(ID:Integer; NewName:string); override; procedure DeleteList(ID:Integer); override; procedure FillList(ID:Integer; NewSymbols:TStrings); override; procedure CopyList(DestID:Integer; DestName: string; SourceIDs: array of Integer); override; procedure AddSymbol(ID:Integer; Symbol:string); override; procedure DeleteSymbol(ID:Integer; Symbol:string); override; // Child listeners operations procedure AddSymbolListListener(Listener:TSymbolListChanges); procedure RemoveSymbolListListener(Listener:TSymbolListChanges); end; // Primary symbol list provider // Downloads symbol lists from server to the primary local cache TPrimarySymbolListProvider = class(TSymbolListProvider) private // ActiveX control to deal with symbol lists FListOfLists:IListOfLists; // Cancel flag FCancel:Boolean; // Refresh timer FRefreshTimer:TTimer; // Refresh timer tick handler procedure OnRefreshTimer(Sender:TObject); public constructor Create; destructor Destroy; override; procedure Refresh; override; procedure Cancel; override; end; // Primary symbol list updater // Sends request to the server to update server's list // according to updated local cache TPrimarySymbolListUpdater = class(TSymbolListChanges) private // ActiveX current list control FCurrentList:ISymbolListControl; protected public constructor Create; destructor Destroy; override; procedure BeginTransaction; override; procedure EndTransaction(Commit:Boolean); override; procedure CreateList(ID:Integer; Name:string); override; procedure RenameList(ID:Integer; NewName:string); override; procedure DeleteList(ID:Integer); override; procedure FillList(ID:Integer; NewSymbols:TStrings); override; procedure CopyList(DestID:Integer; DestName: string; SourceIDs: array of Integer); override; procedure AddSymbol(ID:Integer; Symbol:string); override; procedure DeleteSymbol(ID:Integer; Symbol:string); override; end; // Procedures to create and free default provider, controller and updater procedure InitDefaultDispatcher; procedure FreeDefaultDispatcher; // Get/set primary cache pointer function GetPrimaryCache:TSymbolListCache; procedure SetPrimaryCache(Cache:TSymbolListCache); // Cache utilites procedure ClearCache(Cache:TSymbolListCache); procedure CacheToListBox(Cache:TSymbolListCache; ListBox:TListBox); procedure FillCache(Cache:TSymbolListCache; ListOfLists:IListOfLists); procedure CopyCache(Dest:TSymbolListCache; Source:TSymbolListCache); function GetCacheIndexByID(Cache:TSymbolListCache; ID:Integer):Integer; // Get/set primary symbol list provider function GetPrimarySymbolListProvider : TSymbolListProvider; procedure SetPrimarySymbolListProvider(Provider : TSymbolListProvider); // Get primary symbol list controller function GetPrimarySymbolListController : TSymbolListChanges; implementation uses tiMainForm, tiSymbolListsForm; var // Primary cache // Primary provider, controller and updater work with this PrimaryCache:TSymbolListCache; // Pointer to the primary provider, owned by this unit PrimaryProvider:TSymbolListProvider; // Pointer to the primary controller, owned by this unit PrimaryController:TSymbolListChanges; // Pointer to the primary updater, owned by this unit PrimaryUpdater:TSymbolListChanges; // Inits default primary cache, provider, controller and updater procedure InitDefaultDispatcher; begin // Create primary cache PrimaryCache:=TSymbolListCache.Create; // Create default primary provider, updater and controller PrimaryProvider:=TPrimarySymbolListProvider.Create; PrimaryController:=TPrimarySymbolListController.Create; PrimaryUpdater:=TPrimarySymbolListUpdater.Create; // Add primary updater to the list of listeners of the primary controller (PrimaryController as TPrimarySymbolListController).AddSymbolListListener(PrimaryUpdater); end; // Free primary cache, provider, controller and updater procedure FreeDefaultDispatcher; begin ClearCache(PrimaryCache); PrimaryCache.Free; PrimaryProvider.Free; PrimaryController.Free; PrimaryUpdater.Free; end; // Returns pointer to the current primary cache function GetPrimaryCache:TSymbolListCache; begin Result:=PrimaryCache; end; // Sets pointer to the new primary cache procedure SetPrimaryCache(Cache:TSymbolListCache); begin if Assigned(Cache) then begin // Clear old cache ClearCache(PrimaryCache); PrimaryCache.Free; PrimaryCache:=Cache; end; end; // Populates listbox with lists names from the cache procedure CacheToListBox(Cache:TSymbolListCache; ListBox:TListBox); var I:Integer; SymbolListPtr:PSymbolList; ItemString, CounterString:string; begin ListBox.Items.Clear; for I:=0 to Cache.Count - 1 do begin SymbolListPtr:=PSymbolList(Cache[I]); if SymbolListPtr^.Symbols.Count > 0 then CounterString := Trim(IntToStr(SymbolListPtr^.Symbols.Count)) else CounterString := SEmpty; ItemString:=Format(SListboxItemTemplate,[SymbolListPtr^.Name,CounterString]); ListBox.Items.Add(ItemString); end; end; // Gets index of list in the cache by the list's ID // Returns -1 if the given list ID is not found in the cache function GetCacheIndexByID(Cache:TSymbolListCache; ID:Integer):Integer; var I:Integer; SymbolListPtr:PSymbolList; begin Result:=-1; for I:=0 to Cache.Count - 1 do begin SymbolListPtr:=PSymbolList(Cache[I]); if SymbolListPtr^.ID = ID then begin Result:=I; Break; end; end; end; // Copies one cache to another procedure CopyCache(Dest:TSymbolListCache; Source:TSymbolListCache); var I,J:Integer; DestListPtr:PSymbolList; SourceListPtr:PSymbolList; begin if Assigned(Dest) and Assigned(Source) then begin // Clear destination cache ClearCache(Dest); for I:=0 to Source.Count - 1 do begin SourceListPtr:=PSymbolList(Source[I]); if Assigned(SourceListPtr) then begin // Copy list from source cache to the destination cache New(DestListPtr); DestListPtr^.ID:= SourceListPtr^.ID; DestListPtr^.Name := SourceListPtr^.Name; DestListPtr^.Symbols := TStringList.Create; DestListPtr^.Symbols.Duplicates := dupIgnore; DestListPtr^.Symbols.Sorted := True; // Copy symbols of the current list from source cache to the destination cache for J:=0 to SourceListPtr^.Symbols.Count -1 do DestListPtr^.Symbols.Add(SourceListPtr^.Symbols[J]); Dest.Add(DestListPtr); end; end; end; end; // Clears the cache procedure ClearCache(Cache:TSymbolListCache); var SymbolListPtr:PSymbolList; begin if Assigned(Cache) then begin // Dispose pointers and empty cache while (Cache.Count > 0) do begin SymbolListPtr:= Cache.Last; Cache.Remove(SymbolListPtr); SymbolListPtr^.Symbols.Clear; SymbolListPtr^.Symbols.Free; Dispose(SymbolListPtr); end; end; end; // Fills cache with the data from the given IListOfLists objects procedure FillCache(Cache:TSymbolListCache; ListOfLists:IListOfLists); var AList:ISymbolListControl; SymbolListPtr:PSymbolList; I, Count:Integer; begin ClearCache(Cache); // If data present if ListOfLists.DataPresent then begin // Reset pointer to the first list ListOfLists.ResetPointer; repeat AList := ListOfLists.GetNextList; if not Assigned(AList) then Break; // Create new cache list and fill it with the data New(SymbolListPtr); // Symbols in the list are sorted and not duplicated SymbolListPtr^.Symbols := TStringList.Create; SymbolListPtr^.Symbols.Duplicates := dupIgnore; SymbolListPtr^.Symbols.Sorted := True; SymbolListPtr^.ID := AList.ID; SymbolListPtr^.Name := AList.Name; // Add new list to cache Cache.Add(SymbolListPtr); // Request symbols for the current list AList.RequestFromServer; // Using polling to have controlling in one place // We don't allow to cancel operation here, as it may lead // to data inconsistency while(AList.WaitingForListFromServer) do begin Application.ProcessMessages; if Application.Terminated then Exit; end; // Populate cache list with symbols from server if AList.ServerListValid then begin Count:= AList.ServerListCount -1; for I:=0 to Count do SymbolListPtr^.Symbols.Add(AList.GetServerListItem(I)); end; until False; end; end; // Returns next cache ID (i.e. maximum ID currently in the cache +1) function GetNextCacheID(Cache:TSymbolListCache):Integer; var I:Integer; SymbolListPtr:PSymbolList; begin Result:=0; if Assigned(Cache) then begin for I:=0 to Cache.Count - 1 do begin SymbolListPtr:= Cache[I]; if SymbolListPtr^.ID > Result then Result:= SymbolListPtr^.ID; end; end; Inc(Result); end; // Returns current primary symbol list provider function GetPrimarySymbolListProvider : TSymbolListProvider; begin Result:=PrimaryProvider; end; // Sets current primary symbol list provider procedure SetPrimarySymbolListProvider(Provider : TSymbolListProvider); begin if Assigned(Provider) then PrimaryProvider:=Provider; end; // Returns current primary symbol list controller function GetPrimarySymbolListController : TSymbolListChanges; begin Result:=PrimaryController; end; // Primary symbol list controller constructor TPrimarySymbolListController.Create; begin FListeners:= TList.Create; end; destructor TPrimarySymbolListController.Destroy; begin FListeners.Free; inherited Destroy; end; // Begin transaction procedure TPrimarySymbolListController.BeginTransaction; begin // Currently nothing to do with the local primary cache // Dispatch event to the connected listeners DispatchBeginTransaction; end; procedure TPrimarySymbolListController.DispatchBeginTransaction; var I:Integer; begin // Dispatch event to the connected listeners for I:=0 to FListeners.Count - 1 do if Assigned(FListeners[I]) then TSymbolListChanges(FListeners[I]).BeginTransaction; end; // End transaction procedure TPrimarySymbolListController.EndTransaction(Commit:Boolean); begin // Currently nothing to do with the local primary cache // Dispatch event to the connected listeners DispatchEndTransaction(Commit); end; procedure TPrimarySymbolListController.DispatchEndTransaction(Commit:Boolean); var I:Integer; begin for I:=0 to FListeners.Count - 1 do if Assigned(FListeners[I]) then TSymbolListChanges(FListeners[I]).EndTransaction(Commit); end; // Create new symbol list procedure TPrimarySymbolListController.CreateList(ID:Integer; Name:string); var SymbolListPtr:PSymbolList; begin // Generate next ID for new list ID:=GetNextCacheID(GetPrimaryCache); // Create new list and populate with data New(SymbolListPtr); // Symbols in the list are sorted and not duplicated SymbolListPtr^.Symbols := TStringList.Create; SymbolListPtr^.Symbols.Duplicates := dupIgnore; SymbolListPtr^.Symbols.Sorted := True; SymbolListPtr^.ID := ID; // Check if name is empty and provide default name in this case if Length(Trim(Name)) = 0 then Name := Format(SDefaultListNameTemplate,[Trim(IntToStr(ID))]); SymbolListPtr^.Name := Name; // Add new list to cache GetPrimaryCache.Add(SymbolListPtr); // Dispatch message to the listeners DispatchCreateList(ID,Name); // Dispatch end transaction DispatchEndTransaction(True); end; procedure TPrimarySymbolListController.DispatchCreateList(ID:Integer; Name:string); var I:Integer; begin for I:=0 to FListeners.Count - 1 do if Assigned(FListeners[I]) then TSymbolListChanges(FListeners[I]).CreateList(ID, Name); end; // Rename list procedure TPrimarySymbolListController.RenameList(ID:Integer; NewName:string); var Index:Integer; SymbolListPtr:PSymbolList; begin // Get cache Index by given ID Index:=GetCacheIndexByID(GetPrimaryCache,ID); // If given ID is found (i.e. list exists in the cache) - rename it if Index <> -1 then begin SymbolListPtr:=GetPrimaryCache[Index]; // Check if name is empty and provide default name in this case if Length(Trim(NewName)) = 0 then NewName := Format(SDefaultListNameTemplate,[Trim(IntToStr(ID))]); SymbolListPtr^.Name:=NewName; end; // Dispatch events to the listeners DispatchRenameList(ID, NewName); DispatchEndTransaction(True); end; procedure TPrimarySymbolListController.DispatchRenameList(ID:Integer; NewName:string); var i:Integer; begin for i:=0 to FListeners.Count - 1 do if Assigned(FListeners[I]) then TSymbolListChanges(FListeners[I]).RenameList(ID, NewName); end; // Delete list procedure TPrimarySymbolListController.DeleteList(ID:Integer); var Index:Integer; SymbolListPtr:PSymbolList; begin // Get cache Index by given ID Index:=GetCacheIndexByID(GetPrimaryCache,ID); // If given ID is found (i.e. list exists in the cache) - delete it if Index <> -1 then begin SymbolListPtr:=GetPrimaryCache[Index]; GetPrimaryCache.Delete(Index); SymbolListPtr^.Symbols.Clear; SymbolListPtr^.Symbols.Free; Dispose(SymbolListPtr); end; // Dispatch events to the listeners DispatchDeleteList(ID); DispatchEndTransaction(True); end; procedure TPrimarySymbolListController.DispatchDeleteList(ID:Integer); var i:Integer; begin for i:=0 to FListeners.Count - 1 do if Assigned(FListeners[I]) then TSymbolListChanges(FListeners[I]).DeleteList(ID); end; // Fill list with symbols procedure TPrimarySymbolListController.FillList(ID:Integer; NewSymbols:TStrings); var Index:Integer; SymbolListPtr:PSymbolList; begin // Get cache Index by given ID Index:=GetCacheIndexByID(GetPrimaryCache,ID); // If given ID is found (i.e. list exists in the cache) - fill it with data if Index <> -1 then begin SymbolListPtr:=GetPrimaryCache[Index]; SymbolListPtr^.Symbols.Assign(NewSymbols); end; // Dispatch events to the listeners DispatchFillList(ID,NewSymbols); DispatchEndTransaction(True); end; procedure TPrimarySymbolListController.DispatchFillList(ID:Integer; NewSymbols:TStrings); var i:Integer; begin for i:=0 to FListeners.Count - 1 do if Assigned(FListeners[I]) then TSymbolListChanges(FListeners[I]).FillList(ID, NewSymbols); end; // Copy list(s) to the new list procedure TPrimarySymbolListController.CopyList(DestID:Integer; DestName: string; SourceIDs: array of Integer); var I,J:Integer; Index:Integer; DestSymbolListPtr:PSymbolList; SourceSymbolListPtr:PSymbolList; begin // Create new destination list and add it to the cache DestID:=GetNextCacheID(GetPrimaryCache); New(DestSymbolListPtr); DestSymbolListPtr^.Symbols := TStringList.Create; DestSymbolListPtr^.Symbols.Duplicates := dupIgnore; DestSymbolListPtr^.Symbols.Sorted := True; DestSymbolListPtr^.ID := DestID; // Check if name is empty and provide default name in this case if Length(Trim(DestName)) = 0 then DestName := Format(SDefaultListNameTemplate,[Trim(IntToStr(DestID))]); DestSymbolListPtr^.Name := DestName; GetPrimaryCache.Add(DestSymbolListPtr); // Copy all symbols from source list(s), checking for duplicates for I:=0 to High(SourceIDs) do begin Index:=GetCacheIndexByID(GetPrimaryCache,SourceIDs[I]); if Index <> -1 then begin SourceSymbolListPtr:=GetPrimaryCache[Index]; for J:=0 to SourceSymbolListPtr^.Symbols.Count -1 do begin if not DestSymbolListPtr^.Symbols.Find(SourceSymbolListPtr^.Symbols[J],Index) then DestSymbolListPtr^.Symbols.Add(SourceSymbolListPtr^.Symbols[J]); end; end; end; // Dispatch events to the listeners DispatchCopyList(DestID, DestName, SourceIDs); DispatchEndTransaction(True); end; procedure TPrimarySymbolListController.DispatchCopyList(DestID:Integer; DestName: string; SourceIDs: array of Integer); var I:Integer; begin for I:=0 to FListeners.Count - 1 do if Assigned(FListeners[I]) then TSymbolListChanges(FListeners[I]).CopyList(DestID, DestName, SourceIDs); end; // Add symbol to the list procedure TPrimarySymbolListController.AddSymbol(ID:Integer; Symbol:string); var Index:Integer; SymbolListPtr:PSymbolList; begin // Get cache Index by given ID Index:=GetCacheIndexByID(GetPrimaryCache,ID); // If given ID is found (i.e. list exists in the cache) - add symbol to it, // ignoring duplicate if Index <> -1 then begin SymbolListPtr:=GetPrimaryCache[Index]; if not SymbolListPtr^.Symbols.Find(Symbol,Index) then SymbolListPtr^.Symbols.Add(Symbol); end; // Dispatch events DispatchAddSymbol(ID,Symbol); DispatchEndTransaction(True); end; procedure TPrimarySymbolListController.DispatchAddSymbol(ID:Integer; Symbol:string); var I:Integer; begin for I:=0 to FListeners.Count - 1 do if Assigned(FListeners[I]) then TSymbolListChanges(FListeners[I]).AddSymbol(ID, Symbol); end; // Delete symbol from the list procedure TPrimarySymbolListController.DeleteSymbol(ID:Integer; Symbol:string); var Index:Integer; SymbolListPtr:PSymbolList; begin // Get cache Index by given ID Index:=GetCacheIndexByID(GetPrimaryCache,ID); // If given ID is found (i.e. list exists in the cache) - delete symbol from it if Index <> -1 then begin SymbolListPtr:=GetPrimaryCache[Index]; if SymbolListPtr^.Symbols.Find(Symbol,Index) then SymbolListPtr^.Symbols.Delete(Index); end; // Dispatch events DispatchDeleteSymbol(ID,Symbol); DispatchEndTransaction(True); end; procedure TPrimarySymbolListController.DispatchDeleteSymbol(ID:Integer; Symbol:string); var I:Integer; begin for I:=0 to FListeners.Count - 1 do if Assigned(FListeners[I]) then TSymbolListChanges(FListeners[I]).DeleteSymbol(ID, Symbol); end; // Add new listener to the list procedure TPrimarySymbolListController.AddSymbolListListener(Listener:TSymbolListChanges); begin if Assigned(Listener) then FListeners.Add(Listener); end; // Delete listener from the list procedure TPrimarySymbolListController.RemoveSymbolListListener(Listener:TSymbolListChanges); begin if Assigned(Listener) then FListeners.Remove(Listener); end; // Primary symbol list provider constructor TPrimarySymbolListProvider.Create; begin // Get IListOfLists interface from the main connection control FListOfLists:=MainForm.ConnectionControl.GetListOfLists; FCancel:=False; // Create refresh timer FRefreshTimer:=TTimer.Create(nil); FRefreshTimer.Enabled:=False; FRefreshTimer.Interval := 500; FRefreshTimer.OnTimer := Self.OnRefreshTimer; end; destructor TPrimarySymbolListProvider.Destroy; begin // First stop and destroy refresh timer... FRefreshTimer.Enabled:=False; FRefreshTimer.Free; // ...then release interface FListOfLists:=nil; inherited Destroy; end; // Refresh local cache from the server procedure TPrimarySymbolListProvider.Refresh; begin // Additional guard - check interface pointer if not Assigned(FListOfLists) then Exit; // Start refreshing if not FRefreshTimer.Enabled then begin // Inform listeners that we start refreshing GetPrimarySymbolListController.BeginTransaction; // Query symbol lists from server FCancel:=False; FListOfLists.DataPresent:=False; FListOfLists.RefreshData; // Start refresh timer FRefreshTimer.Enabled := True; // If not yet completed - wait for next timer tick if not FListOfLists.DataPresent then Exit; end else // Already refreshing - check current state begin if not FListOfLists.DataPresent then begin // Data is not received yet, but cancel flag was set - stop refresh timer // and rollback transaction if FCancel then begin FRefreshTimer.Enabled := False; GetPrimarySymbolListController.EndTransaction(False); end; Exit; end; end; // Response received - stop refresh timer FRefreshTimer.Enabled := False; // Populate the cache if not FCancel then FillCache(GetPrimaryCache, FListOfLists); // Inform listeners that we finished if FCancel then GetPrimarySymbolListController.EndTransaction(False) else GetPrimarySymbolListController.EndTransaction(True); end; // Check refresh status procedure TPrimarySymbolListProvider.OnRefreshTimer(Sender:TObject); begin // Exit if application is terminated if Application.Terminated then Exit; // Start refresh or check refresh state Refresh; end; procedure TPrimarySymbolListProvider.Cancel; begin // Set cancel flag FCancel:=True; end; // Primary Symbol List Updater constructor TPrimarySymbolListUpdater.Create; begin FCurrentList:=nil; end; destructor TPrimarySymbolListUpdater.Destroy; begin FCurrentList:=nil; inherited Destroy; end; // Begin and End of Transaction are not handled procedure TPrimarySymbolListUpdater.BeginTransaction; begin end; procedure TPrimarySymbolListUpdater.EndTransaction(Commit:Boolean); begin end; // Send create list request to the server procedure TPrimarySymbolListUpdater.CreateList(ID:Integer; Name:string); begin // Create new list in ActiveX with the given ID FCurrentList:=MainForm.ConnectionControl.SymbolList[ID]; if FCurrentList = nil then Exit; FCurrentList.Name := Name; // Force update FCurrentList.DeleteSymbol(SStubName); end; // Send rename list request to the server procedure TPrimarySymbolListUpdater.RenameList(ID:Integer; NewName:string); begin // Get list with the given ID from ActiveX and rename it FCurrentList:=MainForm.ConnectionControl.SymbolList[ID]; if FCurrentList = nil then Exit; FCurrentList.Name := NewName; // Force update FCurrentList.DeleteSymbol(SStubName); end; // Send delete list request to the server procedure TPrimarySymbolListUpdater.DeleteList(ID:Integer); begin // Get list with the given ID from ActiveX and delete it FCurrentList:=MainForm.ConnectionControl.SymbolList[ID]; if FCurrentList = nil then Exit; FCurrentList.DeleteList; end; // Send fill list request to the server procedure TPrimarySymbolListUpdater.FillList(ID:Integer; NewSymbols:TStrings); var I:Integer; begin // Get list with the given ID from ActiveX and fill it with data FCurrentList:=MainForm.ConnectionControl.SymbolList[ID]; if FCurrentList = nil then Exit; // Clear work list FCurrentList.ClearWorkList; for I:=0 to NewSymbols.Count -1 do begin // Populate work list with data given FCurrentList.AddToWorkList(NewSymbols[I]); end; // Send updated list to the server FCurrentList.SendWorkList; end; // Send copy list(s) request to the server procedure TPrimarySymbolListUpdater.CopyList(DestID:Integer; DestName: string; SourceIDs: array of Integer); var Index,I,J:Integer; SymbolListPtr:PSymbolList; begin // Create new list in ActiveX with the given ID FCurrentList:=MainForm.ConnectionControl.SymbolList[DestID]; if FCurrentList = nil then Exit; FCurrentList.Name := DestName; FCurrentList.ClearWorkList; // Populate new list with the symbols from the source lists in the primary cache // Don't bother about duplicates - this is handled by ActiveX for I:=0 to High(SourceIDs) do begin Index:=GetCacheIndexByID(GetPrimaryCache,SourceIDs[I]); if Index <> -1 then begin SymbolListPtr:=GetPrimaryCache[Index]; for J:=0 to SymbolListPtr^.Symbols.Count -1 do FCurrentList.AddToWorkList(SymbolListPtr^.Symbols[J]); end; end; // Send list to the server FCurrentList.SendWorkList; end; // Send add symbol request to the server procedure TPrimarySymbolListUpdater.AddSymbol(ID:Integer; Symbol:string); begin FCurrentList:=MainForm.ConnectionControl.SymbolList[ID]; if FCurrentList = nil then Exit; FCurrentList.AddSymbol(Symbol); end; // Send delete symbol request to the server procedure TPrimarySymbolListUpdater.DeleteSymbol(ID:Integer; Symbol:string); begin FCurrentList:=MainForm.ConnectionControl.SymbolList[ID]; if FCurrentList = nil then Exit; FCurrentList.DeleteSymbol(Symbol); end; end.