using IBApi; using System; using System.Collections.Generic; using System.Data; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Windows.Forms; using System.Xml; using TradeIdeas.MiscSupport; using TradeIdeas.TIProData.Interfaces; using TradeIdeas.XML; using System.Diagnostics; using System.Globalization; using TradeIdeas.TIProData; namespace TradeIdeas.TIProGUI { public partial class ExternalLinking : Form, IFont, ISnapToGrid { private const int DEFAULT_HEIGHT = 239; private const int DEFAULT_WIDTH = 628; private List _appConfig = new List(); /// /// Integer representing the timeout in milliseconds for us to wait for the readiness of /// the IB API connection for external linking. /// private int _IBAPI_timeout_ms = 250; private bool _externalSurfing = false; /// /// True = send surfs to external links, False = don't send surfs to external links. /// public bool ExternalSurfing { get { return _externalSurfing; } set { _externalSurfing = value; } } public ExternalLinking(List appConfig) { _appConfig = appConfig; InitializeComponent(); // Comment out line below in support of Jira PRO-87. // Logic did not work for different multi-screen setups based on which screen was the main display. //StartPosition = FormStartPosition.Manual; WindowIconCache.SetIcon(this); selectTheFont(); SymbolTranslation.LoadRules(appConfig); Disposed += ExternalLinking_Disposed; FormClosed += ExternalLinking_FormClosed; SetSnapToGrid(GuiEnvironment.SnapToGrid); } /// /// Update IB API timeout based on Global Settings. /// public void UpdateIBAPITimeout() { _IBAPI_timeout_ms = GuiEnvironment.GlobalSettings.Node("EXTERNAL_LINKING").Property("IBAPI_TIMEOUT_MS", _IBAPI_timeout_ms); } private void ExternalLinking_FormClosed(object sender, FormClosedEventArgs e) { CloseLinkToIB(); } private void ExternalLinking_Disposed(object sender, EventArgs e) { CloseLinkToIB(); } [DllImport("user32.dll")] static extern IntPtr WindowFromPoint(Point point); private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount); [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int GetWindowTextLength(IntPtr hWnd); [DllImport("user32.dll")] private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam); public static string GetWindowText(IntPtr hWnd) { IntPtr lastHandle = hWnd; while (hWnd != IntPtr.Zero) { lastHandle = hWnd; hWnd = GetParent(hWnd); } return GetThisWindowText(lastHandle); } public static string GetThisWindowText(IntPtr hWnd) { IntPtr windowHandle = GetParent(hWnd); if (windowHandle == IntPtr.Zero) windowHandle = hWnd; int size = GetWindowTextLength(windowHandle); if (size++ > 0) { var builder = new StringBuilder(size); GetWindowText(windowHandle, builder, builder.Capacity); return builder.ToString(); } return String.Empty; } public static IEnumerable FindWindows() { IntPtr found = IntPtr.Zero; List windows = new List(); EnumWindows(delegate (IntPtr wnd, IntPtr param) { windows.Add(wnd); return true; }, IntPtr.Zero); return windows; } public static IEnumerable FindWindowsWithText(string titleText) { IntPtr found = IntPtr.Zero; List windows = new List(); EnumWindows(delegate (IntPtr wnd, IntPtr param) { if (GetWindowText(wnd).Contains(titleText)) { windows.Add(wnd); } return true; }, IntPtr.Zero); return windows; } [DllImport("user32.dll")] static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, UIntPtr lParam); [DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)] internal static extern int AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach); [DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)] internal static extern IntPtr SetFocus(IntPtr hwnd); [DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId); public const Int32 WM_CHAR = 0x0102; public const Int32 WM_KEYDOWN = 0x0100; public const Int32 WM_KEYUP = 0x0101; public const Int32 VK_RETURN = 0x0D; public const Int32 WM_LEFT_MOUSE_CLICK_DOWN = 0x201; public const Int32 WM_LEFT_MOUSE_CLICK_UP = 0x202; private List _externalLinks = new List(); private Dictionary _listViewItems = new Dictionary(); private void SetFocusRemote(uint remoteThread, uint ourThread, IntPtr toFocus) { AttachThreadInput(remoteThread, ourThread, true); SetFocus(toFocus); AttachThreadInput(remoteThread, ourThread, false); } private Queue _symbolQueue = new Queue(); public void AddToSymbols(string symbol, string exchange, RowData rowData, string linkChannel, string dockWindowID) { RawSymbolInfo symbolInfo = new RawSymbolInfo(symbol, exchange, rowData, linkChannel, dockWindowID); _symbolQueue.Enqueue(symbolInfo); } private void ChangeWindowState(Form form, FormWindowState formWindowState) { // Prepare the WINDOWPLACEMENT structure. NativeMethods.WINDOWPLACEMENT placement = new NativeMethods.WINDOWPLACEMENT(); NativeMethods.GetWindowPlacement(form.Handle, ref placement); // Update structure state if (formWindowState == FormWindowState.Maximized) placement.showCmd = NativeMethods.SW_SHOWMAXIMIZED; else if (formWindowState == FormWindowState.Minimized) placement.showCmd = NativeMethods.SW_SHOWMINIMIZED; else if (formWindowState == FormWindowState.Normal) placement.showCmd = NativeMethods.SW_NORMAL; else placement.showCmd = NativeMethods.SW_RESTORE; // Change window state without animation. NativeMethods.SetWindowPlacement(form.Handle, ref placement); } /// /// Class to support bypassing animations when minimizing, maximizing and restoring windows /// I copied this from "MainForm.cs. /// (Cannot access that method from this TIProGui) /// For some reason, this helped a little bit with the issues /// of the symbol linking form not appearing at the appropriate time in some of the layouts from the /// channel bar. (e.g. double clicking a toplist/alert/multstrat window that doesn't have linking enabled. /// public static class NativeMethods { //Definitions For Different Window Placement Constants public static int SW_HIDE = 0; public static int SW_SHOWNORMAL = 1; public static int SW_NORMAL = 1; public static int SW_SHOWMINIMIZED = 2; public static int SW_SHOWMAXIMIZED = 3; public static int SW_MAXIMIZE = 3; public static int SW_SHOWNOACTIVATE = 4; public static int SW_SHOW = 5; public static int SW_MINIMIZE = 6; public static int SW_SHOWMINNOACTIVE = 7; public static int SW_SHOWNA = 8; public static int SW_RESTORE = 9; // public struct WINDOWPLACEMENT { public int length; public int flags; public int showCmd; public System.Drawing.Point ptMinPosition; public System.Drawing.Point ptMaxPosition; public System.Drawing.Rectangle rcNormalPosition; } [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl); [DllImport("user32.dll")] public static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl); //This class makes accesible the SendInput native call to the SendInputKeyPressAndRelease method private static class SubNativeMethods { [DllImport("user32.dll", SetLastError = true)] public extern static void SendInput(int nInputs, Input[] pInputs, int cbsize); [DllImport("user32.dll", EntryPoint = "MapVirtualKeyA")] public extern static int MapVirtualKey(int wCode, int wMapType); } #region Structs for Native Methods /* Used by [user32.SendInput] to store information * for synthesizing input events such as keystrokes, mouse movement, and mouse clicks. * */ [StructLayout(LayoutKind.Sequential)] private struct POINT { public int x; public int y; } [StructLayout(LayoutKind.Sequential)] struct MouseInput { public int X; public int Y; public int Data; public int Flags; public int Time; public IntPtr ExtraInfo; } [StructLayout(LayoutKind.Sequential)] struct KeyboardInput { public short VirtualKey; public short ScanCode; public int Flags; public int Time; public IntPtr ExtraInfo; } [StructLayout(LayoutKind.Sequential)] struct HardwareInput { public int uMsg; public short wParamL; public short wParamH; } [StructLayout(LayoutKind.Sequential)] struct Input { public int Type; public InputUnion ui; } [StructLayout(LayoutKind.Explicit)] struct InputUnion { [FieldOffset(0)] public MouseInput Mouse; [FieldOffset(0)] public KeyboardInput Keyboard; [FieldOffset(0)] public HardwareInput Hardware; } private const int KEYEVENTF_EXTENDEDKEY = 0x0001; private const int KEYEVENTF_KEYUP = 0x0002; private const int KEYEVENTF_SCANCODE = 0x0008; private const int KEYEVENTF_UNICODE = 0x0004; private const int MAPVK_VK_TO_VSC = 0; #endregion /// /// Sends input events such as keystrokes through the Windows' native call /// /// The key that will be sent as an Input public static void SendInputKeyPressAndRelease(Keys key) { var containsShift = (key & Keys.Shift) == Keys.Shift; int vsc = SubNativeMethods.MapVirtualKey((int)key, MAPVK_VK_TO_VSC); if (containsShift) { Input[] inputs = new Input[4]; inputs[0] = new Input(); inputs[0].Type = 1; // KeyBoard = 1 inputs[0].ui.Keyboard.VirtualKey = (short)Keys.ShiftKey; inputs[0].ui.Keyboard.ScanCode = (short)vsc; inputs[0].ui.Keyboard.Flags = 0; inputs[0].ui.Keyboard.Time = 0; inputs[0].ui.Keyboard.ExtraInfo = IntPtr.Zero; inputs[1] = new Input(); inputs[1].Type = 1; // KeyBoard = 1 inputs[1].ui.Keyboard.VirtualKey = (short)key; inputs[1].ui.Keyboard.ScanCode = (short)vsc; inputs[1].ui.Keyboard.Flags = 0; inputs[1].ui.Keyboard.Time = 0; inputs[1].ui.Keyboard.ExtraInfo = IntPtr.Zero; inputs[2] = new Input(); inputs[2].Type = 1; // KeyBoard = 1 inputs[2].ui.Keyboard.VirtualKey = (short)key; inputs[2].ui.Keyboard.ScanCode = (short)vsc; inputs[2].ui.Keyboard.Flags = KEYEVENTF_KEYUP; inputs[2].ui.Keyboard.Time = 0; inputs[2].ui.Keyboard.ExtraInfo = IntPtr.Zero; inputs[3] = new Input(); inputs[3].Type = 1; // KeyBoard = 1 inputs[3].ui.Keyboard.VirtualKey = (short)Keys.ShiftKey; inputs[3].ui.Keyboard.ScanCode = (short)vsc; inputs[3].ui.Keyboard.Flags = KEYEVENTF_KEYUP; inputs[3].ui.Keyboard.Time = 0; inputs[3].ui.Keyboard.ExtraInfo = IntPtr.Zero; SubNativeMethods.SendInput(inputs.Length, inputs, Marshal.SizeOf(inputs[0])); } else { Input[] inputs = new Input[2]; inputs[0] = new Input(); inputs[0].Type = 1; // KeyBoard = 1 inputs[0].ui.Keyboard.VirtualKey = (short)key; inputs[0].ui.Keyboard.ScanCode = (short)vsc; inputs[0].ui.Keyboard.Flags = 0; inputs[0].ui.Keyboard.Time = 0; inputs[0].ui.Keyboard.ExtraInfo = IntPtr.Zero; inputs[1] = new Input(); inputs[1].Type = 1; // KeyBoard = 1 inputs[1].ui.Keyboard.VirtualKey = (short)key; inputs[1].ui.Keyboard.ScanCode = (short)vsc; inputs[1].ui.Keyboard.Flags = KEYEVENTF_KEYUP; inputs[1].ui.Keyboard.Time = 0; inputs[1].ui.Keyboard.ExtraInfo = IntPtr.Zero; SubNativeMethods.SendInput(inputs.Length, inputs, Marshal.SizeOf(inputs[0])); } } } private ExternalLinkingIBClient _ibConnection = new ExternalLinkingIBClient(); /// /// Controls callbacks for _ibClientPaper /// private ExternalLinkingIBClient _ibConnectionPaper = new ExternalLinkingIBClient(); /// /// Socket client to IB Live account - operates on port 7496 /// private EClientSocket _ibClient = null; /// /// Socket client to IB Paper account - operates on port 7497 /// private EClientSocket _ibClientPaper = null; private int _ibRequestId = 0; private int _ibRequestIdPaper = 0; private int _ibDisplayGroupRequestId = 0; private int _ibDisplayGroupRequestIdPaper = 0; private static int _ibDisplayGroup = 1; private static int _ibDisplayGroupPaper = 1; /// /// Send symbols to external linking destinations. /// /// Open the external linking form if there are no links defined or no active links. /// Symbol is coming from the surfing feature. /// The source window. public void SendSymbols(bool promptUserIfEmpty = true, bool fromSurfManager = false, string sourceWindow = "") { List toSend = new List(); while (_symbolQueue.Count > 0) { RawSymbolInfo symbolInfo = _symbolQueue.Dequeue(); toSend.Add(symbolInfo); } //bool foundLinkedWindow = false; // Internal linking has higher priority than external linking. List
listOfAllWindows = Application.OpenForms.OfType().Where(x => !x.IsDisposed).ToList(); List linkedWindows = listOfAllWindows.OfType().Where(x => x.LinkEnabled).ToList(); //List linkedWindows = Application.OpenForms.OfType().Where(x => x.LinkEnabled).ToList(); Charts foundLinkedChartWindow = null; foreach (IAcceptSymbolLinking linkedWindow in linkedWindows) { foreach (RawSymbolInfo symbolInfo in toSend) { // Check to see if this is coming from a Dock Window Channel - RVH20220304 if (symbolInfo.LinkChannel == SymbolLinkChannels.DockWindowChannel) { // Check to see if this is coming from a a Docked Window. if (!string.IsNullOrEmpty(symbolInfo.DockWindowID)) { // Get the Dock Window ID of the linked window. string linkedDockWindowID = GuiEnvironment.GetDockWindowID((Form)linkedWindow); // If both windows are in the same Dock Window, send the symbol link. if (symbolInfo.DockWindowID == linkedDockWindowID) { linkedWindow.ChangeSymbol(symbolInfo.Symbol, symbolInfo.RowData, sourceWindow); if (linkedWindow is Charts) { foundLinkedChartWindow = (Charts)linkedWindow; } } } } else { // Check the LinkChannel for the window before sending the symbol - RVH20210901 if (symbolInfo.LinkChannel == linkedWindow.LinkChannel || symbolInfo.LinkChannel == SymbolLinkChannels.DefaultLinkChannel || linkedWindow.LinkChannel == SymbolLinkChannels.DefaultLinkChannel) { linkedWindow.ChangeSymbol(symbolInfo.Symbol, symbolInfo.RowData, sourceWindow); var isDockedWindow = symbolInfo.DockWindowID.HasValue(); if (isDockedWindow) { if (linkedWindow is Charts) { foundLinkedChartWindow = (Charts)linkedWindow; } } } } } //foundLinkedWindow = true; } // External linking. if (_externalLinks.Count > 0 && (_externalSurfing || !fromSurfManager)) { List toRemove = new List(); foreach (RawSymbolInfo symbolInfo in toSend) { foreach (ExternalLink externalLink in _externalLinks.ToList()) { if (externalLink.Enabled) { IntPtr p = externalLink.TargetHandle; if ((p != null && WindowHandleExists(p)) || externalLink.RuleId == "IB" || externalLink.RuleId == "IBP") { string symbolTranslated = SymbolTranslation.TranslateSymbol(externalLink.Rule, symbolInfo.Symbol, symbolInfo.Exchange, externalLink.UseAlternateMethod); if (externalLink.RuleId == "IB") { if (!IsTWSRunning()) continue; InitializeLinkToIB(); // Wait for the the first nextValidId with a timeout. var stopWatch = new Stopwatch(); stopWatch.Start(); while (stopWatch.Elapsed < TimeSpan.FromMilliseconds(_IBAPI_timeout_ms)) { if (_ibConnection.NextValidId > 0) break; } stopWatch.Stop(); if (stopWatch.Elapsed > TimeSpan.FromMilliseconds(_IBAPI_timeout_ms)) GuiEnvironment.LogMessage("[ExternalLinking-Live] NextValidId timeout (" + _IBAPI_timeout_ms + "ms) happened. IB API may not be ready for external linking."); else if (_ibConnection.NextValidId > 0) GuiEnvironment.LogMessage("[ExternalLinking-Live] NextValidId received and IB API is ready for external linking."); // Send the linked symbol to IB. int contractDetailsRequestId = GetIBRequestId(); Contract contract = new Contract(); contract.Symbol = symbolTranslated; contract.Currency = "USD"; contract.Exchange = "SMART"; contract.SecType = "STK"; GuiEnvironment.LogMessage("[ExternalLinking-Live] Requesting contract details for " + symbolTranslated + " with requestid " + contractDetailsRequestId); _ibClient.reqContractDetails(contractDetailsRequestId, contract); } else if (externalLink.RuleId == "IBP") { if (!IsTWSRunning()) continue; InitializeLinkToIB(true); // Wait for the the first nextValidId with a timeout. var stopWatchPaper = new Stopwatch(); stopWatchPaper.Start(); while (stopWatchPaper.Elapsed < TimeSpan.FromMilliseconds(_IBAPI_timeout_ms)) { if (_ibConnectionPaper.NextValidId > 0) break; } stopWatchPaper.Stop(); if (stopWatchPaper.Elapsed > TimeSpan.FromMilliseconds(_IBAPI_timeout_ms)) GuiEnvironment.LogMessage("[ExternalLinking-Paper] NextValidId timeout (" + _IBAPI_timeout_ms + "ms) happened. IB API may not be ready for external linking."); else if (_ibConnectionPaper.NextValidId > 0) GuiEnvironment.LogMessage("[ExternalLinking-Paper] NextValidId received and IB API is ready for external linking."); // Send the linked symbol to IB. int contractDetailsRequestId = GetIBRequestId(true); Contract contract = new Contract(); contract.Symbol = symbolTranslated; contract.Currency = "USD"; contract.Exchange = "SMART"; contract.SecType = "STK"; GuiEnvironment.LogMessage("[ExternalLinking-Paper] Requesting contract details for " + symbolTranslated + " with requestid " + contractDetailsRequestId); _ibClientPaper.reqContractDetails(contractDetailsRequestId, contract); } else if (externalLink.UseAlternateMethod) { try { AlternateMethod(p, symbolTranslated); } catch (Exception ex) { //Bypass execution to the Standard Method in case that Alternate Method fails var errText = $"External Linking: Bypassed Alternate Method because of exception: {ex.Message}" + $"\n External Linking: Run Standard Method instead"; Console.WriteLine(errText); GuiEnvironment.LogMessage(errText); StandardMethod(p, symbolTranslated, externalLink); } } else { StandardMethod(p, symbolTranslated, externalLink); } } else toRemove.Add(externalLink); } } } foreach (ExternalLink externalLink in toRemove) { DisableExternalLink(externalLink); //RemoveExternalLink(externalLink); } } else _symbolQueue.Clear(); if (foundLinkedChartWindow != null) { var linkedChartDockedWindowIsInFocus = false; string linkedDockWindowID = GuiEnvironment.GetDockWindowID((Form)foundLinkedChartWindow); foreach (Form form in Application.OpenForms.Cast().Where(x => !x.IsDisposed).ToList()) { if (linkedDockWindowID == form.Handle.ToString() && Form.ActiveForm == form) { linkedChartDockedWindowIsInFocus = true; break; } } foundLinkedChartWindow.SelectAndFocusSymbolLookup(linkedChartDockedWindowIsInFocus); } // if ((_externalLinks.Count == 0 || _externalLinks.Where(x => x.Enabled).Count() == 0) && !foundLinkedWindow && promptUserIfEmpty) if ((_externalLinks.Count == 0 || _externalLinks.Where(x => x.Enabled).Count() == 0) && promptUserIfEmpty) { Visible = true; Show(); ChangeWindowState(this, WindowState = FormWindowState.Normal); //BringToFront(); } } /// /// Interprets and sends commands to the linked external plattform. /// /// The target platform's window /// The symbol's commands to be sent to the external plattform /// The current external link instance private void StandardMethod(IntPtr targetWindow, string symbolTranslated, ExternalLink externalLink) { IntPtr savedForegroundWindow = GuiEnvironment.GetForegroundWindow(); GuiEnvironment.SetForegroundWindow(targetWindow); System.Threading.Thread.Sleep(3); string[] segmentsToSend = symbolTranslated.Split(new string[] { "{PAUSE}" }, StringSplitOptions.None); bool usingLowerCase = false; bool usingUpperCase = false; for (int i = 0; i <= segmentsToSend.Length - 1; i++) { string segment = segmentsToSend[i]; if (segment.Contains("{TOLOWER}")) { usingLowerCase = true; usingUpperCase = false; segment = segment.Replace("{TOLOWER}", ""); } if (segment.Contains("{TOUPPER}")) { usingLowerCase = false; usingUpperCase = true; segment = segment.Replace("{TOUPPER}", ""); } if (segment != "") { if (segment == "{LEFTMOUSECLICK}") { if (externalLink.Point.HasValue) ClickOnPoint(externalLink.TargetHandle, externalLink.Point.Value); } else { if (usingLowerCase) segment = segment.ToLower(); else if (usingUpperCase) segment = segment.ToUpper(); try { SendKeys.SendWait(segment); } catch { } } } System.Threading.Thread.Sleep(3); } if (externalLink.RuleId != "IBOLD") { try { SendKeys.SendWait("\r"); } catch { } } GuiEnvironment.SetForegroundWindow(savedForegroundWindow); } /// /// This is an alternate method for interpreting and /// sending commands to the linked external plattform. /// /// The symbol's commands to be sent to the external plattform private void AlternateMethod(IntPtr p, string symbolTranslated) { //Save the current focused window IntPtr savedForegroundWindow = GuiEnvironment.GetForegroundWindow(); uint ourThread = GetWindowThreadProcessId(savedForegroundWindow, IntPtr.Zero); //Set the Target window to be focused before sending the input IntPtr remoteWindow = p; uint remoteThread = GetWindowThreadProcessId(remoteWindow, IntPtr.Zero); SetFocusRemote(remoteThread, ourThread, remoteWindow); System.Threading.Thread.Sleep(200); string lang = InputLanguage.CurrentInputLanguage.Culture.Name; using (var keyFinder = new KeyboardPointer(new CultureInfo(lang))) { //Sends they related keys from the translated symbol to the target Window foreach (var c in symbolTranslated) { //Try to convert the character directly to the Keys enumerable if (System.Enum.TryParse(c.ToString(), out Keys code)) NativeMethods.SendInputKeyPressAndRelease(code); //If this is not possible, KeyFinder is used to perform the conversion. else { if (keyFinder.GetKey(c, out code)) NativeMethods.SendInputKeyPressAndRelease(code); } } //Includes an ENTER hit key NativeMethods.SendInputKeyPressAndRelease(Keys.Enter); //Waits for the send input to perform before that window loses focus System.Threading.Thread.Sleep(300); //Restores the previously focused window SetFocusRemote(ourThread, remoteThread, savedForegroundWindow); } } [DllImport("user32.dll")] static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, UIntPtr dwExtraInfo); [DllImport("user32.dll")] static extern bool ClientToScreen(IntPtr hWnd, ref Point lpPoint); private void ClickOnPoint(IntPtr wndHandle, Point clientPoint) { var oldPos = Cursor.Position; /// get screen coordinates ClientToScreen(wndHandle, ref clientPoint); /// set cursor on coords, and press mouse Cursor.Position = new Point(clientPoint.X, clientPoint.Y); mouse_event(0x00000002, 0, 0, 0, UIntPtr.Zero); /// left mouse button down mouse_event(0x00000004, 0, 0, 0, UIntPtr.Zero); /// left mouse button up /// return mouse Cursor.Position = oldPos; } private void pictureBox1_GiveFeedback(object sender, GiveFeedbackEventArgs e) { e.UseDefaultCursors = false; IntPtr hIcon = TradeIdeas.TIProGUI.Properties.Resources.left_speaker_png.GetHicon(); Cursor.Current = new Cursor(hIcon); DestroyIcon(hIcon); } protected override void OnMouseDown(MouseEventArgs e) { this.DoDragDrop("example", DragDropEffects.Copy); } [System.Runtime.InteropServices.DllImport("user32.dll")] extern static bool DestroyIcon(IntPtr handle); private void buttonAdd_Click(object sender, EventArgs e) { ExternalLinkAddForm addLinkForm = new ExternalLinkAddForm(_appConfig); DialogResult result = addLinkForm.ShowDialog(); if (result == System.Windows.Forms.DialogResult.OK) { ExternalLink externalLink = addLinkForm.ExternalLink; int alternateMethod = addLinkForm.ExternalLink.UseAlternateMethod ? 1 : 0; int beginningCutoff = addLinkForm.ExternalLink.BeginningIsCutoff ? 1 : 0; GuiEnvironment.RecordUseCase("EL.Add." + externalLink.Rule + "." + alternateMethod + "." + beginningCutoff + "." + GetWindowText(externalLink.TargetHandle), GuiEnvironment.FindConnectionMaster("").SendManager); AddExternalLink(externalLink); } addLinkForm.Dispose(); } private void buttonEditLink_Click(object sender, EventArgs e) { ListViewItem listViewItem = listView.SelectedItems[0]; ExternalLink externalLink = listViewItem.Tag as ExternalLink; ExternalLinkAddForm addLinkForm = new ExternalLinkAddForm(externalLink, _appConfig); addLinkForm.WindowName = "Edit External Link"; DialogResult result = addLinkForm.ShowDialog(); if (result == System.Windows.Forms.DialogResult.OK) { ExternalLink newLink = addLinkForm.ExternalLink; if (_externalLinks.Contains(externalLink)) _externalLinks.Remove(externalLink); _externalLinks.Add(newLink); listViewItem.Tag = newLink; _listViewItems.Remove(externalLink); _listViewItems.Add(newLink, listViewItem); RefreshListViewItem(listViewItem); UpdateButtonStatus(); } addLinkForm.Dispose(); } /// /// Adds external link to the current list of active external links /// /// ExternalLink instance to add /// Set to false to prevent IB initialization private void AddExternalLink(ExternalLink externalLink, bool initialize = true) { string windowName = GetWindowText(externalLink.TargetHandle); if (windowName == "") windowName = "(No Name)"; ListViewItem listViewItem = new ListViewItem(windowName); if (externalLink.Enabled) listViewItem.SubItems.Add("Yes"); else listViewItem.SubItems.Add("No"); listViewItem.SubItems.Add(externalLink.Rule); if (externalLink.UseAlternateMethod) listViewItem.SubItems.Add("Yes"); else listViewItem.SubItems.Add("No"); listViewItem.Tag = externalLink; listView.Items.Add(listViewItem); _listViewItems.Add(externalLink, listViewItem); _externalLinks.Add(externalLink); if (externalLink.RuleId == "IB" && initialize && IsTWSRunning()) InitializeLinkToIB(); else if (externalLink.RuleId == "IBP" && initialize && IsTWSRunning()) InitializeLinkToIB(true); } private int GetIBRequestId(bool paperTrading = false) { if (paperTrading) { _ibRequestIdPaper++; return _ibRequestIdPaper; } else { _ibRequestId++; return _ibRequestId; } } private EReaderMonitorSignal _ibSignal = new EReaderMonitorSignal(); private EReaderMonitorSignal _ibSignalPaper = new EReaderMonitorSignal(); private void InitializeLinkToIB(bool paperTrading = false) { string nodeName = "EXTERNAL_LINKING_IB"; if (paperTrading) nodeName = "EXTERNAL_LINKING_IB_PAPER"; string hostname = GuiEnvironment.XmlConfig.Node(nodeName).Property("HOSTNAME", "localhost"); int port = GetPort(paperTrading); int clientId = GuiEnvironment.XmlConfig.Node(nodeName).Property("CLIENT_ID", 10); if (paperTrading) { if (null == _ibClientPaper || !_ibClientPaper.IsConnected()) { _ibConnectionPaper.DisplayGroupReceived -= _ibConnectionPaper_DisplayGroupReceived; _ibConnectionPaper.ContractDetailsReceived -= _ibConnectionPaper_ContractDetailsReceived; _ibConnectionPaper.ConnectionRefused -= _ibConnectionPaper_ConnectionRefused; _ibConnectionPaper.ConnectionAcknowledged -= _ibConnectionPaper_ConnectionAcknowledged; _ibConnectionPaper.NextValidIdReceived -= _ibConnectionPaper_NextValidIdReceived; _ibConnectionPaper.DisplayGroupReceived += _ibConnectionPaper_DisplayGroupReceived; _ibConnectionPaper.ContractDetailsReceived += _ibConnectionPaper_ContractDetailsReceived; _ibConnectionPaper.ConnectionRefused += _ibConnectionPaper_ConnectionRefused; _ibConnectionPaper.ConnectionAcknowledged += _ibConnectionPaper_ConnectionAcknowledged; _ibConnectionPaper.NextValidIdReceived += _ibConnectionPaper_NextValidIdReceived; GuiEnvironment.LogMessage("[ExternalLinking-Paper] Initializing link to IB with hostname=" + hostname + ", port=" + port + ", clientid=" + clientId); _ibConnectionPaperReady = false; _ibSignalPaper = new EReaderMonitorSignal(); _ibClientPaper = new EClientSocket(_ibConnectionPaper, _ibSignalPaper); _ibClientPaper.eConnect(hostname, port, clientId); var reader = new EReader(_ibClientPaper, _ibSignalPaper); reader.Start(); new Thread(() => { while (null != _ibClientPaper && _ibClientPaper.IsConnected()) { _ibSignalPaper.waitForSignal(); reader.processMsgs(); } }) { IsBackground = true }.Start(); } } else { if (null == _ibClient || !_ibClient.IsConnected()) { _ibConnection.DisplayGroupReceived -= _ibConnection_DisplayGroupReceived; _ibConnection.ContractDetailsReceived -= _ibConnection_ContractDetailsReceived; _ibConnection.ConnectionRefused -= _ibConnection_ConnectionRefused; _ibConnection.ConnectionAcknowledged -= _ibConnection_ConnectionAcknowledged; _ibConnection.NextValidIdReceived -= _ibConnection_NextValidIdReceived; _ibConnection.DisplayGroupReceived += _ibConnection_DisplayGroupReceived; _ibConnection.ContractDetailsReceived += _ibConnection_ContractDetailsReceived; _ibConnection.ConnectionRefused += _ibConnection_ConnectionRefused; _ibConnection.ConnectionAcknowledged += _ibConnection_ConnectionAcknowledged; _ibConnection.NextValidIdReceived += _ibConnection_NextValidIdReceived; GuiEnvironment.LogMessage("[ExternalLinking-Live] Initializing link to IB with hostname=" + hostname + ", port=" + port + ", clientid=" + clientId); _ibConnectionReady = false; _ibSignal = new EReaderMonitorSignal(); _ibClient = new EClientSocket(_ibConnection, _ibSignal); _ibClient.eConnect(hostname, port, clientId); var reader = new EReader(_ibClient, _ibSignal); reader.Start(); new Thread(() => { while (null != _ibClient && _ibClient.IsConnected()) { _ibSignal.waitForSignal(); reader.processMsgs(); } }) { IsBackground = true }.Start(); } } } /// /// Returns true if Interactive Brokers Trader Workstation (tws) is running. /// Used to determine if we should try to initialize IB external linking. /// This avoids the freezing of the application when trying to initialize the /// the IB external linking when Trader Workstation is not running. /// private Boolean IsTWSRunning() { Process[] processes = Process.GetProcessesByName("tws"); return processes.Length != 0; } private bool _ibConnectionReady = false; private void _ibConnection_NextValidIdReceived() { // The IB API is ready for requests once Connection is Acknowledged and the first NextValidId is received. if (!_ibConnectionReady) { GuiEnvironment.LogMessage("[ExternalLinking-Live] First NextValidIdReceived message received. API connection established. Querying for display groups"); _ibClient.queryDisplayGroups(GetIBRequestId()); _ibConnectionReady = true; } } private bool _ibConnectionPaperReady = false; private void _ibConnectionPaper_NextValidIdReceived() { // The IB API is ready for requests once Connection is Acknowledged and the first NextValidId is received. if (!_ibConnectionPaperReady) { GuiEnvironment.LogMessage("[ExternalLinking-Paper] First NextValidIdReceived message received. API connection established. Querying for display groups"); _ibClientPaper.queryDisplayGroups(GetIBRequestId(true)); _ibConnectionPaperReady = true; } } private void _ibConnection_ConnectionAcknowledged() { GuiEnvironment.LogMessage("[ExternalLinking-Live] ConnectionAcknowledged message received."); } private void _ibConnectionPaper_ConnectionAcknowledged() { GuiEnvironment.LogMessage("[ExternalLinking-Paper] ConnectionAcknowledged message received."); } private int GetPort(bool paperTrading = false) { int port = GuiEnvironment.XmlConfig.Node("EXTERNAL_LINKING_IB").Property("PORT", 7496); if (paperTrading) port = GuiEnvironment.XmlConfig.Node("EXTERNAL_LINKING_IB_PAPER").Property("PORT", 7497); return port; } /// /// Close network socket connection to IB TWS if it's still around for both live and paper accounts. /// public void CloseLinkToIB() { if (null != _ibClient) _ibClient.Close(); if (null != _ibConnection) { _ibConnection.DisplayGroupReceived -= _ibConnection_DisplayGroupReceived; _ibConnection.ContractDetailsReceived -= _ibConnection_ContractDetailsReceived; _ibConnection.ConnectionRefused -= _ibConnection_ConnectionRefused; _ibConnection.ConnectionAcknowledged -= _ibConnection_ConnectionAcknowledged; } _ibClient = null; _ibSignal = null; if (null != _ibClientPaper) _ibClientPaper.Close(); if (null != _ibConnectionPaper) { _ibConnectionPaper.DisplayGroupReceived -= _ibConnectionPaper_DisplayGroupReceived; _ibConnectionPaper.ContractDetailsReceived -= _ibConnectionPaper_ContractDetailsReceived; _ibConnectionPaper.ConnectionRefused -= _ibConnectionPaper_ConnectionRefused; _ibConnectionPaper.ConnectionAcknowledged -= _ibConnectionPaper_ConnectionAcknowledged; _ibConnectionPaper.DisplayGroupReceived += _ibConnectionPaper_DisplayGroupReceived; } _ibClientPaper = null; _ibSignalPaper = null; } /// /// Close network socket connection to IB TWS if it's still around for both live and paper accounts. /// /// The rule identifier. public void CloseLinkToIB(string ruleID) { if (ruleID.Equals("IB")) { if (null != _ibClient) _ibClient.Close(); _ibClient = null; _ibSignal = null; } if (ruleID.Equals("IBP")) { if (null != _ibClientPaper) _ibClientPaper.Close(); _ibClientPaper = null; _ibSignalPaper = null; } } private bool _ibConnectionRefused = false; private bool _ibConnectionDisabling = false; private void ShowConnectionRefusedMessage(string message, bool paperTrading = false) { if (!_ibConnectionRefused) { _ibConnectionRefused = true; string title = "External Linking - Trader Workstation Unavailable"; string messageBoxMessage = "The connection to Trader Workstation (TWS) was refused. This can occur due to one or both of the following reasons:\n\n"; messageBoxMessage += "ActiveX and Socket Clients\n"; messageBoxMessage += " - TWS, go to Global Config > API > Settings\n" + " - Select \"Enable ActiveX and Socket Clients\"\n\n"; messageBoxMessage += "External Linking Rule Mismatch\n"; messageBoxMessage += " - In Trade-Ideas, go to Tools > External Linking > Edit Link\n" + " - Ensure that the rule corresponds to the correct TWS use state,\n \"Live\" or \"Paper\".\n"; DialogResult result = MessageBoxEx.Show(this, messageBoxMessage, title); } else if (!_ibConnectionDisabling) { _ibConnectionDisabling = true; // Disable IB paper or live external linking after first failure. if (_externalLinks.Count > 0 || _externalLinks.Where(x => x.Enabled).Count() > 0) { foreach (ExternalLink externalLink in _externalLinks.ToList()) { if (externalLink.Enabled) { if ((externalLink.RuleId == "IB" && !paperTrading) || (externalLink.RuleId == "IBP" && paperTrading)) { externalLink.Enabled = false; } } } // Update listview item enabled state. RefreshListViewEnabled(); string title = "External Linking - Disabled Trader Workstation Link"; string messageBoxMessage = "External Linking to the Interactive Brokers entry is disabled."; messageBoxMessage += "\n\nTo enable linking to Trader Workstation go to Tools, External Linking and either enable or create the Interactive Brokers entry."; DialogResult result = MessageBoxEx.Show(this, messageBoxMessage, title); // Reset booleans. ResetIBConnectionRefused(); } } } private void ResetIBConnectionRefused(ExternalLink externalLink) { if (null == externalLink || externalLink.RuleId == "IB" || externalLink.RuleId == "IBP") { ResetIBConnectionRefused(); } } private void ResetIBConnectionRefused() { // Reset booleans. _ibConnectionRefused = false; _ibConnectionDisabling = false; } private void RefreshListViewEnabled() { // Update listView. foreach (ListViewItem listViewItem in listView.Items) { ExternalLink externalLink = listViewItem.Tag as ExternalLink; if (null != externalLink) { if (externalLink.Enabled) listViewItem.SubItems[1].Text = "Yes"; else listViewItem.SubItems[1].Text = "No"; } } } private void _ibConnectionPaper_ConnectionRefused(string message) { ShowConnectionRefusedMessage(message, true); } private void _ibConnection_ConnectionRefused(string message) { ShowConnectionRefusedMessage(message, false); } private void _ibConnectionPaper_ContractDetailsReceived(string symbol, int contractId) { GuiEnvironment.LogMessage("[ExternalLinking-Paper] Contract details received for " + symbol + ", contractid=" + contractId + ", displaygroupid=" + _ibDisplayGroupRequestIdPaper); _ibClientPaper.updateDisplayGroup(_ibDisplayGroupRequestIdPaper, contractId + "@SMART"); } private void _ibConnection_ContractDetailsReceived(string symbol, int contractId) { GuiEnvironment.LogMessage("[ExternalLinking-Live] Contract details received for " + symbol + ", contractid=" + contractId + ", displaygroupid=" + _ibDisplayGroupRequestId); _ibClient.updateDisplayGroup(_ibDisplayGroupRequestId, contractId + "@SMART"); } private void _ibConnectionPaper_DisplayGroupReceived(int displayGroup) { _ibDisplayGroupPaper = displayGroup; _ibDisplayGroupRequestIdPaper = GetIBRequestId(true); GuiEnvironment.LogMessage("[ExternalLinking-Paper] Display Group Received message: " + displayGroup + ", requestid=" + _ibDisplayGroupRequestIdPaper); _ibClientPaper.subscribeToGroupEvents(_ibDisplayGroupRequestIdPaper, _ibDisplayGroupPaper); } private void _ibConnection_DisplayGroupReceived(int displayGroup) { _ibDisplayGroup = displayGroup; _ibDisplayGroupRequestId = GetIBRequestId(); GuiEnvironment.LogMessage("[ExternalLinking-Live] Display Group Received message: " + displayGroup + ", requestid=" + _ibDisplayGroupRequestId); _ibClient.subscribeToGroupEvents(_ibDisplayGroupRequestId, _ibDisplayGroup); } private void RefreshListViewItem(ListViewItem listViewItem) { ExternalLink externalLink = listViewItem.Tag as ExternalLink; if (null != externalLink) { listViewItem.Text = GetWindowText(externalLink.TargetHandle); if (externalLink.Enabled) listViewItem.SubItems[1].Text = "Yes"; else listViewItem.SubItems[1].Text = "No"; listViewItem.SubItems[2].Text = externalLink.Rule; if (externalLink.UseAlternateMethod) listViewItem.SubItems[3].Text = "Yes"; else listViewItem.SubItems[3].Text = "No"; } } private void adjustColumnWidths() { //When changed to -2, this specifically keeps the all content in column headers visible listView.Columns[1].Width = -2; listView.Columns[2].Width = -2; listView.Columns[3].Width = -2; } private void UpdateButtonStatus() { if (listView.SelectedItems.Count > 0) { buttonEditLink.Enabled = listView.SelectedItems.Count == 1; buttonDelete.Enabled = true; bool someDisabled = false; bool someEnabled = false; foreach (ListViewItem item in listView.SelectedItems) { ExternalLink externalLink = item.Tag as ExternalLink; if (null != externalLink) { if (externalLink.Enabled) someEnabled = true; else someDisabled = true; } } buttonDisable.Enabled = someEnabled; buttonEnable.Enabled = someDisabled; } else { buttonDelete.Enabled = false; buttonEnable.Enabled = false; buttonDisable.Enabled = false; buttonEditLink.Enabled = false; } } private void listView_SelectedIndexChanged(object sender, EventArgs e) { UpdateButtonStatus(); } private void buttonDelete_Click(object sender, EventArgs e) { if (listView.SelectedItems.Count > 0) { List toDelete = new List(); foreach (ListViewItem item in listView.SelectedItems) { toDelete.Add(item); } DialogResult result = MessageBoxEx.Show(this, "Really delete " + toDelete.Count + " external links?", "Confirm Deletion", MessageBoxButtons.OKCancel); if (result == System.Windows.Forms.DialogResult.OK) { foreach (ListViewItem item in toDelete) { ExternalLink externalLink = item.Tag as ExternalLink; if (null != externalLink) RemoveExternalLink(externalLink); } } } } private void RemoveExternalLink(ExternalLink externalLink) { ListViewItem listViewItem = null; if (_listViewItems.ContainsKey(externalLink)) listViewItem = _listViewItems[externalLink]; if (_externalLinks.Contains(externalLink)) { ResetIBConnectionRefused(externalLink); CloseLinkToIB(externalLink.RuleId); _externalLinks.Remove(externalLink); } if (null != listViewItem && listView.Items.Contains(listViewItem)) listView.Items.Remove(listViewItem); } private void buttonEnable_Click(object sender, EventArgs e) { if (listView.SelectedItems.Count > 0) { var toNotify = new List(); GuiEnvironment.RecordUseCase("EL.Enable", GuiEnvironment.FindConnectionMaster("").SendManager); foreach (ListViewItem item in listView.SelectedItems) { ExternalLink externalLink = item.Tag as ExternalLink; if ((externalLink.TargetHandle == null || !WindowHandleExists(externalLink.TargetHandle)) && externalLink.RuleId != "IB" && externalLink.RuleId != "IBP") toNotify.Add(externalLink); else { externalLink.Enabled = true; item.SubItems[1].Text = "Yes"; ResetIBConnectionRefused(externalLink); } } if (toNotify.Count > 0) { string messageBoxMessage = "The following links could not be enabled because the associated window cannot be accessed."; foreach (var externalLink in toNotify) { if (_listViewItems.TryGetValue(externalLink, out ListViewItem item)) messageBoxMessage += $"\n\n- {item.Text}"; } messageBoxMessage += "\n\nTo enable them, go to edit and associate the link again with the corresponding window."; var errormsg = new ExternalLinkgEnableErrorForm(messageBoxMessage); errormsg.ShowDialog(); } } UpdateButtonStatus(); } private void DisableExternalLink(ExternalLink externalLink) { if (_listViewItems.TryGetValue(externalLink, out ListViewItem item) && externalLink.Enabled) { externalLink.Enabled = false; item.SubItems[1].Text = "No"; ResetIBConnectionRefused(externalLink); } UpdateButtonStatus(); } private void buttonDisable_Click(object sender, EventArgs e) { if (listView.SelectedItems.Count > 0) { GuiEnvironment.RecordUseCase("EL.Disable", GuiEnvironment.FindConnectionMaster("").SendManager); foreach (ListViewItem item in listView.SelectedItems) { ExternalLink externalLink = item.Tag as ExternalLink; externalLink.Enabled = false; item.SubItems[1].Text = "No"; ResetIBConnectionRefused(externalLink); } } UpdateButtonStatus(); } private void ExternalLinking_FormClosing(object sender, FormClosingEventArgs e) { Hide(); e.Cancel = true; } [DllImport("user32", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)] public static extern IntPtr GetWindow(IntPtr hwnd, int wFlag); [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] public static extern IntPtr GetParent(IntPtr hWnd); 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 { ExternalLinking externalLinking = new ExternalLinking(GuiEnvironment.AppConfig); // change parameters to accommodate dock panel changes - RVH20210402 //externalLinking.RestoreLayout(description, ignorePosition, cascadePosition); externalLinking.RestoreLayout(description, ignorePosition, cascadePosition, dockPanelMode, mainDockPanelName, mainDockPanelTitle, dockPanelID); } }); } // change parameters to accommodate dock panel changes - RVH20210402 //public void RestoreLayout(XmlNode description, bool ignorePosition, bool cascadePosition) public void RestoreLayout(XmlNode description, bool ignorePosition, bool cascadePosition, bool dockPanelMode = false, string mainDockPanelName = "", string mainDockPanelTitle = "", string dockPanelID = "") { GuiEnvironment.externalLinking = this; // change parameters to accommodate dock panel changes - RVH20210402 //LayoutManager.RestoreBase(description, this, ignorePosition, cascadePosition); // DON'T DOCK THIS FORM - RVH LayoutManager.RestoreBase(description, this, ignorePosition, cascadePosition, null, false, false, "", "", ""); //XmlNode listNode = description.Node("DEFAULT_LIST"); //foreach (XmlNode linkNode in listNode.Enum()) //{ // ExternalLink externalLink = new ExternalLink(); // externalLink.Restore(linkNode); // if (WindowHandleExists(externalLink.TargetHandle)) // AddExternalLink(externalLink); //} } private bool WindowHandleExists(IntPtr windowHandle) { IntPtr foundHandle = GetWindow(windowHandle, 0); return foundHandle != IntPtr.Zero; } public string ExternalLinksToXml() { XmlDocument document = new XmlDocument(); document.LoadXml(""); XmlNode parent = document.Node("LINKS"); SaveLinks(parent); return document.OuterXml; } public void ExternalLinksFromXml(string xml) { if (xml != "") { try { XmlDocument document = new XmlDocument(); document.LoadXml(xml); XmlNode listNode = document.Node("LINKS").Node("DEFAULT_LIST"); _externalSurfing = listNode.Property("SURFING_ENABLED", false); checkBoxSurfingEnabled.Checked = _externalSurfing; foreach (XmlNode linkNode in listNode.Enum()) { ExternalLink externalLink = new ExternalLink(); externalLink.Restore(linkNode); if (externalLink.RuleId == "IB" || externalLink.RuleId == "IBP" || WindowHandleExists(externalLink.TargetHandle)) AddExternalLink(externalLink, false); } } catch { } } } private void SaveLinks(XmlNode description) { XmlNode listNode = description.NewNode("DEFAULT_LIST"); listNode.SetProperty("SURFING_ENABLED", _externalSurfing); foreach (ExternalLink externalLink in _externalLinks.ToList()) { XmlNode linkNode = listNode.NewNode("EXTERNAL_LINK"); externalLink.Save(linkNode); } } public void SaveLayout(XmlNode parent) { List openLinkingForms = Application.OpenForms.OfType().Where(x => !x.IsDisposed).ToList(); XmlNode description = LayoutManager.SaveBase(parent, this, FORM_TYPE); } public readonly static string FORM_TYPE = "EXTERNAL_LINKING"; public static readonly WindowIconCache WindowIconCache = new WindowIconCache("LINKING"); private void buttonClose_Click(object sender, EventArgs e) { Hide(); } private void ExternalLinking_VisibleChanged(object sender, EventArgs e) { if (Visible) { selectTheFont(); // reset font since font changes while it was hidden was not received. // Comment out logic below in support of Jira PRO-87. // Logic did not work for different multi-screen setups based on which screen was the main display. //int width = SystemInformation.VirtualScreen.Width; //int height = SystemInformation.VirtualScreen.Height; //if (Left < 0 && Top >= 0 && Top <= height) //{ // Location = new Point(0, Top); //} //else if (Left >= 0 && Left <= width && Top < 0) //{ // Location = new Point(Left, 0); //} //else if (isOnScreen() == false) //check if form is completely off screen //{ // Location = new Point(0, 0); //} //if (ClientRectangle.Height == 0) //{ // Height = DEFAULT_HEIGHT; // Width = DEFAULT_WIDTH; //} } } // Comment out method below in support of Jira PRO-87. // Logic did not work for different multi-screen setups based on which screen was the main display. //private bool isOnScreen() //courtesy of http://stackoverflow.com/questions/987018/determining-if-a-form-is-completely-off-screen //{ // // Create rectangle of this external linking form... // Rectangle formRectangle = new Rectangle(Left, Top, Width, Height); // return Screen.AllScreens.Any(s => s.WorkingArea.IntersectsWith(formRectangle)); //} /* static public void RegisterLayout() { LayoutManager.Instance().AddRestoreRule(FORM_TYPE, (RestoreLayout)delegate(XmlNode description, bool ignorePosition, bool cascadePosition) { ExternalLinking externalLinking = new ExternalLinking(); externalLinking.RestoreLayout(description, ignorePosition, cascadePosition); }); } */ public void selectTheFont() { Font = GuiEnvironment.FontSettings; adjustColumnWidths(); if (listView.Right < buttonClose.Right) { this.Width = buttonClose.Right + buttonClose.Width; } } public void SetSnapToGrid(bool enabled) { formSnapper1.Enabled = enabled; if (GuiEnvironment.RunningWin10 && enabled) { formSnapper1.Win10HeightAdjustment = GuiEnvironment.HEIGHT_INCREASE; formSnapper1.Win10WidthAdjustment = GuiEnvironment.WIDTH_INCREASE; } } private void checkBoxSurfingEnabled_CheckedChanged(object sender, EventArgs e) { _externalSurfing = checkBoxSurfingEnabled.Checked; } } [Serializable()] public class ExternalLink { public IntPtr TargetHandle { get; set; } private Point? _point = null; public Point? Point { get { return _point; } set { _point = value; } } private bool _enabled = true; public bool Enabled { get { return _enabled; } set { _enabled = value; } } private bool _beginningIsCutoff = false; public bool BeginningIsCutoff { get { return _beginningIsCutoff; } set { _beginningIsCutoff = value; } } private bool _useAlternateMethod = false; public bool UseAlternateMethod { get { return _useAlternateMethod; } set { _useAlternateMethod = value; } } private string _ruleId = "NONE"; public string RuleId { get { return _ruleId; } set { _ruleId = value; } } private string _rule = "None"; public string Rule { get { return _rule; } set { _rule = value; } } public ExternalLink() { } public override string ToString() { return TargetHandle.ToString(); } public void Save(XmlNode description) { description.SetProperty("ENABLED", _enabled); description.SetProperty("BEGINNING_CUTOFF", _beginningIsCutoff); description.SetProperty("ALTERNATE_METHOD", _useAlternateMethod); description.SetProperty("RULE", _rule); description.SetProperty("RULE_ID", _ruleId); description.SetProperty("TARGET_HANDLE", TargetHandle); } public void Restore(XmlNode description) { _enabled = description.Property("ENABLED", true); _beginningIsCutoff = description.Property("BEGINNING_CUTOFF", false); _useAlternateMethod = description.Property("ALTERNATE_METHOD", false); _rule = description.Property("RULE", ""); _ruleId = description.Property("RULE_ID", ""); TargetHandle = (IntPtr)description.Property("TARGET_HANDLE", 0); } } public class RawSymbolInfo { public string Symbol { get; set; } public string Exchange { get; set; } public RowData RowData { get; set; } public string LinkChannel { get; set; } public string DockWindowID { get; set; } public RawSymbolInfo() { } public RawSymbolInfo(string symbol, string exchange, RowData rowData, string linkChannel, string dockWindowID) { Symbol = symbol; Exchange = exchange; RowData = rowData; LinkChannel = linkChannel; DockWindowID = dockWindowID; } } // new class for Symbol Link Channels - RVH20210901 public static class SymbolLinkChannels { public static string DefaultLinkChannel = "Link to All"; public static string DockWindowChannel = "Dock Window"; private static Dictionary _channels = new Dictionary(); public static Dictionary Channels { get { return _channels; } set { _channels = value; } } public static void LoadChannels() { // Load link channels from Common.xml/Custom4.xml _channels.Clear(); List linkChannelsNode = GuiEnvironment.XmlConfig.Node("SYMBOL_LINKING").Node("LINK_CHANNELS"); if (linkChannelsNode != null) { foreach (XmlNode channelNode in linkChannelsNode[0]) { _channels.Add(channelNode.Property("NAME"), ColorTranslator.FromHtml(channelNode.Property("COLOR"))); } } else { _channels.Add("Green", Color.Green); _channels.Add("Red", Color.Red); _channels.Add("Blue", Color.Blue); } } } }