using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml; using System.Windows.Forms; using TradeIdeas.XML; using System.Drawing; using TradeIdeas.Logging; using WeifenLuo.WinFormsUI.Docking; using System.ComponentModel; /* Typically the main program would make one instance of the Layout Manager. * * Each window type is represented by a string. This string must be saved when you are * saving a window, so we know which procedure to call when restoring a window. The * rest is completely up to the window, but most windows will call SaveBase() and * RestoreBase() to do the common things like the size and position of the window. * The mechanism doesn't even require a form; you can choose to save other settings * as part of the layout. * * If you want to be saved as part of the layout, you have two choices. You can * implement ISaveLayout. When the user askes to save the layout, we iterate though * all open windows looking for windows that implement that interface. That is ideal * for classes which can support any number of windows. We also have our own list of * windows named SpecialSaveLayout. This is useful for windows which are unique, and * which might be hidden at the time we save the layout. These will still have the * chance save their layout. * * Note that the callback for saving your layout gives you an XML node. That is the * parent of any nodes you create. You should create one child per window that you * want to save. This gives you the opportunity to decide at runtime not to save * a specific window. Or to save more than one window in one callback. * * All restoring is done through AddRestoreRule(). The key is the name that we * stored in the XML when we saved the layout. Each key points to a delegate. For * a class which can support any number of windows, this delegate will typically * call the class's constructor, then RestoreBase(), then anything specific for * that window. For unique windows, the delegate will point directly to an existing * window object, so there is no need to call the constructor. The delegate will * immedately call RestoreBase() followed by anything specific to the object. * * Notice that we always store a list of windows. Presumably when you say to save * a window, you will get a file with only that one window. However, there is * no restriction, and when you say to load a window, you might get multiple windows. * In fact, the only difference between loading a saved window and loading a saved * layout is that before we restore an old layout, we close all the existing windows. * * Notice that there is a way to ignore the position when we restore a window. That * makes a convenient way to duplicate a window. Save the window into memory, then * restore everything but the position. Windows should ask the operating system to * put them in the default position when they are first created. (For some reason * there is no method to request the default position after a window has been * created.) When you duplicate a window, you don't want the new to be on top of * the old one. That's confusing to a user; it looks like nothing happened. The * default position will be offset by a little bit each time you create a new window * so you can see each window clearly. * * This code was specifically designed to know nothing about the specific windows * in the program. It learns about them at runtime through callbacks. So many * different main programs should all be able to use this library routine. This * code is based loosely on a similar library I wrote for Delphi. */ namespace TradeIdeas.TIProGUI { /// /// This delegate works with . This allows /// any class to participate in the layout. is an alternative. /// This delegate works well with classes that are not forms, and with forms where you /// want exactly 0 or 1 instance of each form to be visible. /// /// /// This is the whole layout file. Create one new node for each window. /// provides a good starting point for saving a window. /// public delegate void SaveLayout(XmlNode parent); /// /// A window should implement this interface if it wants to participate in the layout. In particular, /// this is used when there can be 0 or more windows of a particular type. We iterate through the /// list of visible windows looking for windows that implement this. is a /// reasonable alternative. /// public interface ISaveLayout { /// /// Add yourself to the layout. /// provides a good starting point for saving a window. /// /// /// This is the whole layout file. Create one new node for each window. /// void SaveLayout(XmlNode parent); /// /// Return the icon for the window. This should never be null. /// can provide a reasonable default /// if necessary. The icon name is probably more important than the actual /// icon, because we are more likely to store that. /// WindowIconCache WindowIconCache { get; } bool Pinned { get; set; } } public delegate void LayoutRestoreCompleteHandler(bool replacedWindows); // change parameters to accommodate dock panel changes - RVH20210402 //public delegate void RestoreLayout(XmlNode description, bool ignorePosition, bool cascadePosition); public delegate void RestoreLayout(XmlNode description, bool ignorePosition, bool cascadePosition, bool dockPanelMode, string mainDockPanelName, string mainDockPanelTitle, string dockPanelID); public enum ApplicationClosingState { LayoutSaved, LayoutNotSaved, Exception, ShuttingDown, Canceled } public interface IUpdateLayout { void UpdateLayout(XmlNode layout, ApplicationClosingState appstate); } public class LayoutManager { public static bool OddsMakerAvailable = false; public static int OddsMakerTrials = 0; public const string FORM_TYPE = "FORM_TYPE"; const string BASE = "BASE"; const int CASCADE_OFFSET = 20; // define main dock form list - RVH20210328 private static List _mainDockFormList = new List(); // init _idCount - RVH20210409 private int _idCount = 0; /// /// Controls whether multiple channels can be opened separately at the same time. /// public bool MultiChannelEnabled { get; set; } = true; private static MainDockForm _dockPanelFormFocused = null; /// /// Gets and Sets the DockPanelForm that currently has focus. /// public MainDockForm DockPanelFormFocused { get { return _dockPanelFormFocused; } set { _dockPanelFormFocused = value; } } /// /// A list of the Dock Panel Forms. /// public List MainDockFormList { get { return _mainDockFormList; } } // new method for adding forms to main dock form - RVH20210328 public static void AddFormToDockPanelForm(Form childForm, string docFormText, string mainFormName, string mainFormTitle, string docPanelID) { // find top dock panel form MainDockForm findDockForm = _dockPanelFormFocused; // find dock panel form if (findDockForm == null) findDockForm = _mainDockFormList.Find(x => x.Name == mainFormName && x.Text == mainFormTitle); // Make sure this dock window is visible. if (null != findDockForm && !findDockForm.IsDisposed && !GuiEnvironment.KeepRestoredFormsHidden) MakeFullyVisible(findDockForm); // create new main dock form if it doesn't exist if (findDockForm == null) { findDockForm = new MainDockForm(); findDockForm.Name = mainFormName; findDockForm.Text = mainFormTitle; LayoutManager.Instance().SetFormDefaultSizeAndLocation(findDockForm); findDockForm.Show(); // add to list of forms _mainDockFormList.Add(findDockForm); } else { if (findDockForm.IsDisposed) { int dockFormIndex = _mainDockFormList.IndexOf(findDockForm); findDockForm = null; findDockForm = new MainDockForm(); findDockForm.Name = mainFormName; findDockForm.Text = mainFormTitle; LayoutManager.Instance().SetFormDefaultSizeAndLocation(findDockForm); findDockForm.Show(); if (dockFormIndex < _mainDockFormList.Count && _mainDockFormList.Count > 0) _mainDockFormList[dockFormIndex] = findDockForm; else _mainDockFormList.Add(findDockForm); } else { findDockForm.Show(); } } // see if dock form is minimized and show it if (findDockForm.WindowState == FormWindowState.Minimized) { //findDockForm.MinimizeRestoreCharts(false); findDockForm.WindowState = FormWindowState.Normal; } // add child form to the dock form findDockForm.AddChildForm(childForm, docFormText, docPanelID); } // new method to create a new empty dock panel form - RVH20210421 public MainDockForm CreateDockPanelForm(string mainFormName, string mainFormTitle) { // make sure the form name is unique string tempFormName = mainFormName; bool keepLooping = true; int loopCt = 0; do { // find dock panel form MainDockForm findDockForm = _mainDockFormList.Find(x => x.Name == tempFormName && x.Text == mainFormTitle); if (findDockForm != null) tempFormName = mainFormName + loopCt++.ToString(); else keepLooping = false; } while (keepLooping); mainFormName = tempFormName; // create new dock form MainDockForm newDockForm = new MainDockForm(); newDockForm.Name = mainFormName; newDockForm.Text = mainFormTitle; SetFormDefaultSizeAndLocation(newDockForm); newDockForm.Show(); // add to list of forms _mainDockFormList.Add(newDockForm); return newDockForm; } private void SetFormDefaultSizeAndLocation(MainDockForm mainDockForm) { int left = 0; int top = 77; int widthAdjust = 100; int heightAdjust = 100; foreach (Form form in Application.OpenForms.Cast
().Where(x => !x.IsDisposed).ToList()) { if (form.Name == "MainForm") { top = form.Height - 8; left = Math.Min(form.Left, left); if (GuiEnvironment.openRelativeToMainWindow && firstDefaultLayoutCompleted) { top += GuiEnvironment.currentMainWindowTop - GuiEnvironment.newMainWindowTop; left += GuiEnvironment.currentMainWindowLeft - GuiEnvironment.newMainWindowLeft; } break; } } // Make the size of the dock window fill most of the current screen. Rectangle bounds = Screen.FromControl(mainDockForm).Bounds; mainDockForm.Location = new Point(left, top); if (GuiEnvironment.openRelativeToMainWindow && firstDefaultLayoutCompleted) { int leftAdjustment = left - (GuiEnvironment.currentMainWindowLeft - GuiEnvironment.newMainWindowLeft); int topAdjustment = top - (GuiEnvironment.currentMainWindowTop - GuiEnvironment.newMainWindowTop); mainDockForm.Size = new Size(bounds.Width - leftAdjustment - widthAdjust, bounds.Height - topAdjustment - heightAdjust); } else mainDockForm.Size = new Size(bounds.Width - left - widthAdjust, bounds.Height - top - heightAdjust); // Make sure this dock window is visible. if (!GuiEnvironment.KeepRestoredFormsHidden) MakeFullyVisible(mainDockForm); } // new method to create a new dock panel form and load the xml layout - RVH20210413 private void CreateDockPanelFormFromLayout(XmlNode description, out string mainFormName, out string mainFormTitle) { // get settings from description XmlNode baseNode = description.Node("BASE"); int top = Convert.ToInt32(baseNode.Property("Top")); int left = Convert.ToInt32(baseNode.Property("Left")); int width = Convert.ToInt32(baseNode.Property("Width")); int height = Convert.ToInt32(baseNode.Property("Height")); mainFormName = description.Property("FORM_NAME"); mainFormTitle = description.Property("FORM_TITLE"); XmlNode layoutNode = description.Node("LAYOUT"); string xmlLayout = layoutNode.Property("VALUE"); bool pinned = layoutNode.Property("PINNED", false); // make sure the form name is unique string tempFormName = mainFormName; string tempFormTitle = mainFormTitle; bool keepLooping = true; int loopCt = 0; do { // find dock panel form MainDockForm findDockForm = _mainDockFormList.Find(x => x.Name == tempFormName && x.Text == tempFormTitle); if (findDockForm != null) tempFormName = mainFormName + loopCt++.ToString(); else keepLooping = false; } while (keepLooping); mainFormName = tempFormName; // create new dock form MainDockForm newDockForm = new MainDockForm(); newDockForm.Name = mainFormName; newDockForm.Text = mainFormTitle; newDockForm.Pinned = pinned; newDockForm.Show(); newDockForm.Top = top; newDockForm.Left = left; newDockForm.Width = width; newDockForm.Height = height; newDockForm.LoadLayoutFromString(xmlLayout); // add to list of forms _mainDockFormList.Add(newDockForm); } // new method to update a new dock panel form and load the xml layout - RVH20210413 private void UpdateDockPanelFormFromLayout(XmlNode description) { // get settings from description // don't change position and size of dock panel form when updating (reloading channel) - RVH20210513 //XmlNode baseNode = description.Node("BASE"); //int top = Convert.ToInt32(baseNode.Property("Top")); //int left = Convert.ToInt32(baseNode.Property("Left")); //int width = Convert.ToInt32(baseNode.Property("Width")); //int height = Convert.ToInt32(baseNode.Property("Height")); string mainFormName = description.Property("FORM_NAME"); string mainFormTitle = description.Property("FORM_TITLE"); XmlNode layoutNode = description.Node("LAYOUT"); string xmlLayout = layoutNode.Property("VALUE"); bool pinned = layoutNode.Property("PINNED", false); // update dock form MainDockForm updateDockForm = DockPanelFormFocused; if (updateDockForm != null) { updateDockForm.Name = mainFormName; updateDockForm.Text = mainFormTitle; updateDockForm.Pinned = pinned; updateDockForm.Show(); // don't change position and size of dock panel form when updating (reloading channel) - RVH20210513 //updateDockForm.Top = top; //updateDockForm.Left = left; //updateDockForm.Width = width; //updateDockForm.Height = height; updateDockForm.LoadLayoutFromString(xmlLayout); } } // new property for getting _idCount - RVH20210413 public int IDCount { get { return _idCount; } set { _idCount = value; } } // add a parameter for the dock panel layout - RVH20210520 //public static XmlNode SaveBase(XmlNode parent, Form toSave, string formType, System.Drawing.Rectangle? actualRectangle = null) public static XmlNode SaveBase(XmlNode parent, Form toSave, string formType, System.Drawing.Rectangle? actualRectangle = null, string dockPanelLayout = null) { // Start by creating the new node for the window, as that will be a common // activity. XmlNode result = parent.NewNode("WINDOW"); // FORM_TYPE is required. result.SetProperty(FORM_TYPE, formType); // Put all of these properties in a sub tag, so we keep the namespace clear. // We might want to add new properties later, and we don't want their names // to conflict with names used by the individual windows. XmlNode baseNode = result.NewNode(BASE); // Save the position of the windows. For Mimimized and Maximized windows // use RestoreBounds property to retrieve size and position. if (null != actualRectangle) { baseNode.SetProperty("Top", actualRectangle.Value.Top); baseNode.SetProperty("Left", actualRectangle.Value.Left); baseNode.SetProperty("Height", actualRectangle.Value.Height); baseNode.SetProperty("Width", actualRectangle.Value.Width); } else if (toSave.WindowState == FormWindowState.Minimized || toSave.WindowState == FormWindowState.Maximized) { baseNode.SetProperty("Top", toSave.RestoreBounds.Top); baseNode.SetProperty("Left", toSave.RestoreBounds.Left); baseNode.SetProperty("Height", toSave.RestoreBounds.Height); baseNode.SetProperty("Width", toSave.RestoreBounds.Width); } else { baseNode.SetProperty("Top", toSave.Top); baseNode.SetProperty("Left", toSave.Left); baseNode.SetProperty("Height", toSave.Height); baseNode.SetProperty("Width", toSave.Width); } // Windows 10 changed the the left, right and bottom borders so they are invisible. // To allow us to place the windows side by side we increase the windows width and height // to use the invisble border space. So here we need to decrease by the amount we added when restoring the window. // don't make the following adjustments when saving the main dock form - RVH20210510 // also ship for docked windows. //if (GuiEnvironment.RunningWin10) if (GuiEnvironment.RunningWin10 && !(toSave is MainDockForm) && !(toSave.ParentForm is WindowDocumentDock)) { int normalHeight = baseNode.Property("Height", 0) - GuiEnvironment.HEIGHT_INCREASE; int normalWidth = baseNode.Property("Width",0) - GuiEnvironment.WIDTH_INCREASE; int normalLeft = baseNode.Property("Left", 0) + GuiEnvironment.WIDTH_INCREASE / 2; baseNode.SetProperty("Height", normalHeight); baseNode.SetProperty("Width", normalWidth); if (!GuiEnvironment.openRelativeToMainWindow) baseNode.SetProperty("Left", normalLeft); } baseNode.SetProperty("WindowState", toSave.WindowState); baseNode.SetProperty("Visible", toSave.Visible); // we need to save the windows docked state - RVH20210505 baseNode.SetProperty("Docked", toSave.ParentForm is WindowDocumentDock); // for Brokerage Plus form, save the dock panel layout if ((formType == "TRADING_STRATEGY_GRID" || formType == "SINGLE_STOCK_DOCK_WINDOW" || formType == "SINGLE_STOCK_WINDOW") && dockPanelLayout != null) baseNode.SetProperty("DockPanelLayout", dockPanelLayout); // save symbol linking channel - RVH20210929 if (toSave is ISymbolLinkingChannel) { ISymbolLinkingChannel symbolLinkingChannel = toSave as ISymbolLinkingChannel; if (symbolLinkingChannel != null) baseNode.SetProperty("LinkChannel", symbolLinkingChannel.GetLinkChannel()); } return result; } // change parameters to accommodate dock panel changes - RVH20210402 //public static void RestoreBase(XmlNode description, Form toRestore, bool ignorePosition, bool cascadePosition, object parent = null, bool startHidden = false) public static void RestoreBase(XmlNode description, Form toRestore, bool ignorePosition, bool cascadePosition, object parent = null, bool startHidden = false, bool dockPanelMode = false, string mainDockPanelName = "", string mainDockPanelTitle = "", string dockPanelID = "") { XmlNode baseNode = description.Node("BASE"); bool visible = baseNode.Property("Visible", true); // get NEW property Docked - RVH20210505 // EXPERIMENTAL CHANGE - only get the Docked property if dockPanelMode is true // This is because loading saved layouts saved while docked causes them to load as docked // even if GuiEnvironment.openInDockWindow is false - RVH20210604 bool docked = false; if (dockPanelMode) docked = baseNode.Property("Docked", dockPanelMode); //bool docked = baseNode.Property("Docked", dockPanelMode); //update the context menu for the form -RVH20210517 ContextMenuStrip contextMenuStrip = GetContextMenu(toRestore); if (contextMenuStrip != null) { contextMenuStrip.Opening += LayoutManager.Instance().contextMenuStrip_Opening; } // add a child form to the dock panel form - RVH20210328 if (docked) { // hide child form until docked toRestore.Visible = false; // if child form is channel bar form, set the Docked property to true if (toRestore is ChannelBarForm) { ((ChannelBarForm)toRestore).Docked = true; // TODO: may need to set ChannelBarForm.URL to URL in description - RVH // This may get passed back correctly from the selected channel - Discuss with Dave/Brad if it doesn't. } AddFormToDockPanelForm(toRestore, toRestore.Name, mainDockPanelName, mainDockPanelTitle, dockPanelID); } else { if (!visible || startHidden) toRestore.Visible = false; if (!ignorePosition) { if (cascadePosition) { toRestore.Top = baseNode.Property("Top", toRestore.Top) + CASCADE_OFFSET; toRestore.Left = baseNode.Property("Left", toRestore.Left) + CASCADE_OFFSET; } else { toRestore.Top = baseNode.Property("Top", toRestore.Top); toRestore.Left = baseNode.Property("Left", toRestore.Left); // When loading the first default layout ignore open relative to main window setting if (GuiEnvironment.openRelativeToMainWindow && firstDefaultLayoutCompleted) { toRestore.Top += GuiEnvironment.currentMainWindowTop - GuiEnvironment.newMainWindowTop; toRestore.Left += GuiEnvironment.currentMainWindowLeft - GuiEnvironment.newMainWindowLeft; } } } toRestore.Height = baseNode.Property("Height", toRestore.Height); toRestore.Width = baseNode.Property("Width", toRestore.Width); // Windows 10 changed the the left, right and bottom borders so they are invisible. // To allow us to place the windows side by side we increase the windows width and height // to use the invisble border space. if (GuiEnvironment.RunningWin10) { //The state of the form must be taken into account to make adjustments. if (toRestore.WindowState == FormWindowState.Normal) { toRestore.Height += GuiEnvironment.HEIGHT_INCREASE; toRestore.Width += GuiEnvironment.WIDTH_INCREASE; if (!GuiEnvironment.openRelativeToMainWindow) toRestore.Left += -GuiEnvironment.WIDTH_INCREASE / 2; } else { toRestore.Height = toRestore.RestoreBounds.Height + GuiEnvironment.HEIGHT_INCREASE; toRestore.Width = toRestore.RestoreBounds.Width + GuiEnvironment.WIDTH_INCREASE; if (!GuiEnvironment.openRelativeToMainWindow) toRestore.Left = toRestore.RestoreBounds.Left + -GuiEnvironment.WIDTH_INCREASE / 2; } } if (!GuiEnvironment.KeepRestoredFormsHidden) MakeFullyVisible(toRestore); if (GuiEnvironment.InHostedEnvironment) { IChildable childForm = toRestore as IChildable; if (null != childForm) { childForm.ActualSize = new System.Drawing.Rectangle(toRestore.Left, toRestore.Top, toRestore.Width, toRestore.Height); childForm.RestoredLayout = description; } } toRestore.WindowState = baseNode.PropertyEnum("WindowState", toRestore.WindowState); try { // TryParse requires dot net 4.0. FormWindowState windowState = (FormWindowState)Enum.Parse(typeof(FormWindowState), baseNode.Property("WindowState")); toRestore.WindowState = windowState; } catch { // by default keep the current value, i.e. do nothing. } if (visible && !GuiEnvironment.KeepRestoredFormsHidden && !startHidden) // If we want the window to be invisible, do that first. If we want to window to be // visible, do that last. If we are changing the visibity, and we are changing other // attributes (like the size or position) we want the window to be invisible while the // other changes are being made. Experience shows that things look a lot cleaner that // way. toRestore.Visible = true; if (null != GuiEnvironment.FormLayoutRestoredCode) GuiEnvironment.FormLayoutRestoredCode(toRestore, parent); } // set symbol linking channel - RVH20210929 if (toRestore is ISymbolLinkingChannel) { ISymbolLinkingChannel symbolLinkingChannel = toRestore as ISymbolLinkingChannel; if (symbolLinkingChannel != null) { // Assigned to Dock Window Channel if docked and not assigned a channel - RVH20220304 if (docked) { // Get the linking channel for the dock window. string dockWindowLinkChannel = GuiEnvironment.GetDockWindowLinkChannel(toRestore); bool dockWindowChannelBarMenuItemsAdded = GuiEnvironment.GetDockWindowChannelBarMenuItemsAdded(toRestore); // If it was possible to obtain the LinkChannel value of the interface, it is used, otherwise the one of the docked window is used. //Setting the property from the window being restored symbolLinkingChannel.SetLinkChannel(baseNode.Property("LinkChannel", dockWindowLinkChannel)); //Checking if the dockWindowLinkChannel has updated to MIXED dockWindowLinkChannel = GuiEnvironment.GetDockWindowLinkChannel(toRestore); if (dockWindowLinkChannel != "MIXED" && dockWindowChannelBarMenuItemsAdded) symbolLinkingChannel.SetLinkChannel(dockWindowLinkChannel); } else { symbolLinkingChannel.SetLinkChannel(baseNode.Property("LinkChannel", SymbolLinkChannels.DefaultLinkChannel)); } } } } public bool PreventModifyContextMenuItems { get; set; } // new methods to get the context menu from a form and update it - RVH20210517 public void contextMenuStrip_Opening(object sender, System.ComponentModel.CancelEventArgs e) { if (PreventModifyContextMenuItems) { return; } // modify context menu items for dock panel windows ModifyContextMenuItems((ContextMenuStrip)sender); e.Cancel = false; } public void toolStripMenuItemDuplicateHere_Click(object sender, EventArgs e) { ContextMenuStrip contextMenuStrip = (ContextMenuStrip)((ToolStripMenuItem)sender).GetCurrentParent(); Form childForm = contextMenuStrip.Tag as Form; LayoutManager.Instance().Duplicate((ISaveLayout)childForm, true, _dockPanelFormFocused.Name, _dockPanelFormFocused.Text); } public void toolStripMenuItemDuplicateNew_Click(object sender, EventArgs e) { ContextMenuStrip contextMenuStrip = (ContextMenuStrip)((ToolStripMenuItem)sender).GetCurrentParent(); Form childForm = contextMenuStrip.Tag as Form; CreateDockPanelForm("MainDockForm", "Trade-Ideas Pro Dock - NEW"); LayoutManager.Instance().Duplicate((ISaveLayout)childForm, true, "MainDockForm", "Trade-Ideas Pro Dock - NEW"); } public void toolStripMenuItemDuplicateInto_Click(object sender, EventArgs e) { ContextMenuStrip contextMenuStrip = (ContextMenuStrip)((ToolStripMenuItem)sender).GetCurrentParent(); Form childForm = contextMenuStrip.Tag as Form; ((MainDockForm)((ToolStripMenuItem)sender).Tag).BringToFront(); LayoutManager.Instance().Duplicate((ISaveLayout)childForm, true, ((MainDockForm)((ToolStripMenuItem)sender).Tag).Name, ((MainDockForm)((ToolStripMenuItem)sender).Tag).Text); } public static ContextMenuStrip GetContextMenu(Form form) { ContextMenuStrip contextMenuStrip = null; if (form is IContextMenuStrip) { contextMenuStrip = ((IContextMenuStrip)form).MyContextMenuStrip; contextMenuStrip.Tag = form; } return contextMenuStrip; } private void ModifyContextMenuItems(ContextMenuStrip contextMenuStrip) { // TODO menu items text should be defined in Common.xml. Also logic that uses menu items text needs to be language independent. string newDuplicateText = "Duplicate into Free Floating Window"; bool docked = false; if (contextMenuStrip.Tag != null && ((Form)contextMenuStrip.Tag).ParentForm is WindowDocumentDock) docked = true; // delete previously added menu items List itemsToRemove = new List(); foreach (var item in contextMenuStrip.Items) { if (item is ToolStripMenuItem && (((ToolStripMenuItem)item).Text.StartsWith("Duplicate into") || ((ToolStripMenuItem)item).Text.Equals("Duplicate Here")) && !((ToolStripMenuItem)item).Text.Equals(newDuplicateText)) itemsToRemove.Add((ToolStripMenuItem)item); } foreach (ToolStripMenuItem item in itemsToRemove) { contextMenuStrip.Items.Remove(item); } // Hide Pinned toolStripMenuItem if (docked) { foreach (var item in contextMenuStrip.Items) { if (item is ToolStripMenuItem && ((ToolStripMenuItem)item).Text.Contains("Pinned")) { int index = contextMenuStrip.Items.IndexOf((ToolStripMenuItem)item); contextMenuStrip.Items[index].Visible = false; } //else if (item is ToolStripMenuItem && ((ToolStripMenuItem)item).Text.Equals("Properties")) //{ // // look for Actions menu item and hide it // foreach (var subItem in ((ToolStripMenuItem)item).DropDownItems) // { // if (subItem is ToolStripMenuItem && ((ToolStripMenuItem)subItem).Text.Equals("Actions...")) // { // int index = ((ToolStripMenuItem)item).DropDownItems.IndexOf((ToolStripMenuItem)subItem); // ((ToolStripMenuItem)item).DropDownItems[index].Visible = false; // } // } //} } } // Find Duplicate menu item bool chartOtherMenu = false; int dupIndex = -1; int i = -1; foreach (var item in contextMenuStrip.Items) { i++; if (item is ToolStripMenuItem && (((ToolStripMenuItem)item).Text.Equals("Duplicate") || ((ToolStripMenuItem)item).Text.Equals(newDuplicateText))) { dupIndex = i; //break; } else if (item is ToolStripMenuItem && ((ToolStripMenuItem)item).Text.Equals("Edit Chart Buy Strategy...")) { // check to see if this menu is from the other chart menu (B or S) - RVH20210616 chartOtherMenu = true; } } // new logic to only add additional Duplicate menu items if Duplicate menu item is found - RVH20210616 if (dupIndex >= 0 && !chartOtherMenu) { if (docked) { // change text of Duplicate menu item contextMenuStrip.Items[dupIndex].Text = newDuplicateText; // create Duplicate Here context menu ToolStripMenuItem toolStripMenuItemDuplicateHere = new ToolStripMenuItem(); toolStripMenuItemDuplicateHere.Text = "Duplicate Here"; toolStripMenuItemDuplicateHere.Click += LayoutManager.Instance().toolStripMenuItemDuplicateHere_Click; //if (dupIndex >= 0) contextMenuStrip.Items.Insert(dupIndex + 1, toolStripMenuItemDuplicateHere); //else // contextMenuStrip.Items.Add(toolStripMenuItemDuplicateHere); } else { // create Duplicate Here context menu ToolStripMenuItem toolStripMenuItemDuplicateHere = new ToolStripMenuItem(); toolStripMenuItemDuplicateHere.Text = "Duplicate into New Dock Window"; toolStripMenuItemDuplicateHere.Click += LayoutManager.Instance().toolStripMenuItemDuplicateNew_Click; //if (dupIndex >= 0) contextMenuStrip.Items.Insert(dupIndex + 1, toolStripMenuItemDuplicateHere); //else // contextMenuStrip.Items.Add(toolStripMenuItemDuplicateHere); } // create addtional context menu items for current dock panel windows open foreach (MainDockForm dockForm in LayoutManager.Instance().MainDockFormList) { if (dockForm != null && !dockForm.IsDisposed && (!docked || dockForm != _dockPanelFormFocused)) { ToolStripMenuItem toolStripMenuItemDuplicateInto = new ToolStripMenuItem(); toolStripMenuItemDuplicateInto.Text = $"Duplicate into {dockForm.Text}"; toolStripMenuItemDuplicateInto.Tag = dockForm; toolStripMenuItemDuplicateInto.Click += LayoutManager.Instance().toolStripMenuItemDuplicateInto_Click; //if (dupIndex >= 0) //{ dupIndex++; contextMenuStrip.Items.Insert(dupIndex + 1, toolStripMenuItemDuplicateInto); //} //else //{ // contextMenuStrip.Items.Add(toolStripMenuItemDuplicateInto); //} } } } } // new method to Save Dock Panel Form layouts - RVH20210407 // NOT CURRENTLY USED public void SaveDockPanelFormLayouts() { foreach (MainDockForm dockForm in _mainDockFormList) { string configFile = Directory + "\\" + dockForm.Name + ".config"; dockForm.SaveLayout(configFile); } } // new method for closing all dock forms if not pinned - RVH20210409 /// /// Close all dock forms or close all unpinned dock forms. /// /// If true close all unpinned dock forms else close all dock forms. public void CloseDockPanelForms(bool honorPinned = false) { List toClose = new List(); foreach (MainDockForm dockForm in _mainDockFormList) { if (!honorPinned || !dockForm.Pinned) toClose.Add(dockForm); } foreach (MainDockForm dockForm in toClose) { _mainDockFormList.Remove(dockForm); if (_dockPanelFormFocused == dockForm) _dockPanelFormFocused = null; if (null != GuiEnvironment.FormLayoutClosedCode) GuiEnvironment.FormLayoutClosedCode(dockForm); else { dockForm.Visible = false; dockForm.Close(); dockForm.Dispose(); } } } // new method for check for a dock panel window in the XML layout - RVH20210504 public bool ContainsDockPanelForm(XmlNode all) { bool containsIt = false; foreach (XmlNode description in all.Enum()) { if (description is XmlElement && description.Property(FORM_TYPE) == "DOCK_PANEL_FORM") { containsIt = true; break; } } return containsIt; } // new method for check for a channel bar form in the XML layout - RVH20210504 private bool ContainsChannelBarForm(XmlNode all) { bool containsIt = false; foreach (XmlNode description in all.Enum()) { if (description is XmlElement && description.Property(FORM_TYPE) == "WELCOME_SCREEN") { containsIt = true; break; } } return containsIt; } public event SaveLayout SpecialSaveLayout; /// /// This is like except that the caller can specify which items /// to include in the file. Remember that a window file and a layout file have exactly /// the same format. If it'w important to know which is which, that information must /// be saved elsewhere. (The traditional "Save Layout..." and "Save Window..." options /// use the file extension to store this. The cloud puts this into a field in the /// database.) /// /// /// If this is true we save the position of the main window, the symbol list window, /// etc. The exact list of what's saved comes from , /// so it is extensible. /// /// /// The list of windows to save. /// /// An XML file describing the layout. This is in a format suitable for sending to . // modify parameter list to use a list of forms instead - RVH20210414 //public XmlDocument SaveSome(bool includeSpecial, IEnumerable windows) public XmlDocument SaveSome(bool includeSpecial, List windows) { XmlDocument result = new XmlDocument(); XmlNode container = result.CreateElement("LAYOUT"); result.AppendChild(container); // new loop code - RVH20210414 foreach (Form form in windows) { ISaveLayout saveable = form as ISaveLayout; if (null != saveable) { try { saveable.SaveLayout(container); } catch (Exception e) { string debugView = e.ToString(); } } else { // check for main dock form and store it to the layout file - RVH20210412 if (form is MainDockForm) SaveDockPanelFormLayout(container, form); } } // old loop code - RVH20210414 //foreach (ISaveLayout saveable in windows) //{ // if (null != saveable) // try // { // saveable.SaveLayout(container); // } // catch (Exception e) // { // string debugView = e.ToString(); // } //} if (includeSpecial && (null != SpecialSaveLayout)) try { SpecialSaveLayout(container); } catch { } return result; } // new method to save dock panel form layout - RVH20210412 public void SaveDockPanelFormLayout(XmlNode parent, Form form) { XmlNode description = LayoutManager.SaveBase(parent, form, "DOCK_PANEL_FORM"); description.SetProperty("FORM_NAME", form.Name); description.SetProperty("FORM_TITLE", form.Text); XmlNode layout = description.NewNode("LAYOUT"); string layoutString = ((MainDockForm)form).SaveLayout(); layout.SetProperty("VALUE", layoutString); layout.SetProperty("PINNED", ((MainDockForm)form).Pinned); } /// /// Checks the open windows that implement the IUpdateLayout interface and runs the UpdateLayout method of each of them /// /// Full name of the layout file to update /// Current application status public void UpdateDefaulLayout(string filename, ApplicationClosingState state) { try { XmlDocument currentDefaultLayout = new XmlDocument(); currentDefaultLayout.Load(filename); var layout = currentDefaultLayout.FirstChild; if (layout != null) { foreach (Form form in Application.OpenForms.Cast().Where(x => !x.IsDisposed).ToList()) { IUpdateLayout update = form as IUpdateLayout; if (null != update) { try { update.UpdateLayout(layout, state); } catch { } } } currentDefaultLayout.Save(filename); } } catch (Exception) { //Do nothing, no default layout file is updated. } } public XmlDocument SaveAll() { XmlDocument result = new XmlDocument(); XmlNode container = result.CreateElement("LAYOUT"); result.AppendChild(container); // save docked windows first - RVH20210505 foreach (Form form in Application.OpenForms.Cast().Where(x => !x.IsDisposed).ToList()) { // check for main dock form and store it to the layout file - RVH20210412 if (form is MainDockForm) { SaveDockPanelFormLayout(container, form); MainDockForm dockForm = form as MainDockForm; // save each child form foreach (Form childForm in dockForm.ChildForms) { ISaveLayout saveable = childForm as ISaveLayout; if (null != saveable) { try { saveable.SaveLayout(container); } catch { } } } } } // save undocked windows next - RVH20210505 // original method of saving forms - RVH20210420 foreach (Form form in Application.OpenForms.Cast().Where(x => !x.IsDisposed).ToList()) { ISaveLayout saveable = form as ISaveLayout; // don't save docked windows here because they are save above - RVH20210505 if (null != saveable && !(form.ParentForm is WindowDocumentDock) && !(form is Charts && form.ParentForm != null)) { try { saveable.SaveLayout(container); } catch { } } } if (null != SpecialSaveLayout) try { SpecialSaveLayout(container); } catch { } return result; } public void SaveAll(string fileName) { try { SaveAll().Save(fileName); } catch { } } // The output format of SaveOne and SaveAll are the same. The only difference // is that SaveOne has only the one window in it. But it's still set up as a // list of windows, just a list with only one entry. Presumably the main program // will use a different file extension to seperate these two cases, and that // will decide whether closeOldWindows is true or false in the call to Restore(). public void SaveOne(SaveLayout code, string fileName) { XmlDocument result = new XmlDocument(); XmlNode container = result.CreateElement("LAYOUT"); result.AppendChild(container); code(container); try { // since we are saving the layout for one window only here, we need to delete the BASE node Docked setting // This will allow the window to open according to the settings in GuiEnvironment - RVH20210625 XmlNode layoutNode = result.Node(0); XmlNode windowNode = layoutNode.Node("WINDOW"); if (windowNode != null) { XmlNode baseNode = windowNode.Node("BASE"); baseNode.Attributes.RemoveNamedItem("Docked"); } result.Save(fileName); } catch { } } public void SaveOne(ISaveLayout window, string fileName) { SaveOne(window.SaveLayout, fileName); } // Used to skp open reltive to main window setting for first layout of session. private static bool firstDefaultLayoutCompleted = false; /// /// Load a layout that was saved to XML /// /// The document element of the saved file. /// /// True if we should replace the current layout. /// False if we should add to the current layout. /// /// /// After loading the new items, stretch, shrink, and/or translate the entire layout to fit on /// the screen. If this this false, leave things exactly where they were saved. /// // change parameters to accommodate dock panel changes - RVH20210402 // public void Restore(XmlNode all, bool closeOldWindows, bool fillScreen = false) public void Restore(XmlNode all, bool closeOldWindows, bool fillScreen = false, bool dockPanelMode = false, string mainDockPanelName = "", string mainDockPanelTitle = "", int idFixedValue = -100, bool loadCloudLinkIntoDockChannel = false) { // check to see if this XML layout has a dock panel form in it // and it is launched from a dock panel window with a channel bar in it - RVH20210504 bool updateDockPanelLayout = false; bool loadedFromDockedChannelBar = false; bool containsDockPanelForm = false; MainDockForm saveMainDockForm = null; if (dockPanelMode) { MainDockForm mainDockForm = DockPanelFormFocused; if (mainDockForm != null) { saveMainDockForm = mainDockForm; loadedFromDockedChannelBar = mainDockForm.ContainsChannelBarForm(); } containsDockPanelForm = ContainsDockPanelForm(all); if (loadedFromDockedChannelBar && containsDockPanelForm) { // If not closing windows from the layout and we are loading into a dock window from a docked channel bar, // then update the dock panel layout - RVH20220329 if (!closeOldWindows) loadCloudLinkIntoDockChannel = true; closeOldWindows = true; // need to clear panels and windows if (loadCloudLinkIntoDockChannel) updateDockPanelLayout = true; // reload XML layout } } Screen destination = null; if (fillScreen && (Screen.AllScreens.Length > 1)) { using (ScreenPicker screenPicker = new ScreenPicker()) { screenPicker.ShowDialog(); destination = screenPicker.Choice; if (null == destination) // The user hit cancel. Cancel the entire operation. return; } } // only close all windows if not in multi-channel support mode - RVH20210409 //if (closeOldWindows) if (closeOldWindows && !dockPanelMode) // !MultiChannelEnabled) { // Create a copy of the list, because the list will be modified as soon // as we close the first window. List toClose = new List(); foreach (Form form in Application.OpenForms.Cast().Where(x => !x.IsDisposed).ToList()) { ISaveLayout saveable = form as ISaveLayout; // only close undocked windows - RVH20210503 //if (null != saveable && !saveable.Pinned) if ((null != saveable && !saveable.Pinned && !(form.ParentForm is WindowDocumentDock)) || ((form is MainDockForm form2) && !form2.Pinned)) toClose.Add(form); } foreach (Form form in toClose) { if (null != GuiEnvironment.FormLayoutClosedCode) GuiEnvironment.FormLayoutClosedCode(form); else { form.Close(); form.Dispose(); } } } else if (closeOldWindows && dockPanelMode && loadedFromDockedChannelBar && loadCloudLinkIntoDockChannel) { //clear child forms and panel windows MainDockForm mainDockForm = DockPanelFormFocused; if (mainDockForm != null && mainDockForm.ContainsChannelBarForm()) { mainDockForm.Text = mainDockPanelTitle; mainDockForm.CloseAllChildForms(false); mainDockForm.CloseAllDocuments(); } // Test to see if we are loading a custom channel that doesn't contain a channel bar // from a docked channel bar. If so, close the free floating windows - RVH20210605 if (!containsDockPanelForm && !ContainsChannelBarForm(all)) { List toClose = new List(); foreach (Form form in Application.OpenForms.Cast().Where(x => !x.IsDisposed).ToList()) { ISaveLayout saveable = form as ISaveLayout; // only close undocked windows - RVH20210605 if ((null != saveable && !saveable.Pinned && !(form.ParentForm is WindowDocumentDock)) || ((form is MainDockForm form2) && !form2.Pinned)) toClose.Add(form); } foreach (Form form in toClose) { if (null != GuiEnvironment.FormLayoutClosedCode) GuiEnvironment.FormLayoutClosedCode(form); else { form.Close(); form.Dispose(); } } } } else if (closeOldWindows && dockPanelMode) { // if in dock panel mode and this layout isn't opened from a docked channel bar, // it should be opened from a saved layout, so close everything and reopen it all - RVH20210505 List toClose = new List(); foreach (Form form in Application.OpenForms.Cast().Where(x => !x.IsDisposed).ToList()) { ISaveLayout saveable = form as ISaveLayout; if ((null != saveable && !saveable.Pinned && !(form.ParentForm is WindowDocumentDock)) || ((form is MainDockForm form2) && !form2.Pinned)) toClose.Add(form); } foreach (Form form in toClose) { if (null != GuiEnvironment.FormLayoutClosedCode) GuiEnvironment.FormLayoutClosedCode(form); else { form.Close(); form.Dispose(); } } CloseDockPanelForms(true); } // if not in multi-channel support mode, close all dock panel windows - RVH20210409 if (!MultiChannelEnabled && closeOldWindows) CloseDockPanelForms(true); Dictionary previouslyOpen = new Dictionary(); foreach (Form form in Application.OpenForms.Cast().Where(x => !x.IsDisposed).ToList()) previouslyOpen.Add(form, form.DesktopBounds); // Load MAIN_FORM first foreach (XmlNode description in all.Enum()) if (description is XmlElement && description.Property(FORM_TYPE) == "MAIN_FORM") try { //Restore position. No cascading // change parameters to accommodate dock panel changes - RVH20210402 //RestoreOne(description, false, false); RestoreOne(description, false, false, dockPanelMode, mainDockPanelName, mainDockPanelTitle); } catch (Exception e) { TILogger.Error(e.Message, e); } // Load all windows except MAIN_FORM // init _idCount - RVH20210409 _idCount = 0; foreach (XmlNode description in all.Enum()) //if (description is XmlElement && description.Property(FORM_TYPE) != "MAIN_FORM") if (description is XmlElement && description.Property(FORM_TYPE) != "MAIN_FORM") // && description.Property(FORM_TYPE) != "DOCK_PANEL_FORM") if (description is XmlElement && description.Property(FORM_TYPE) == "DOCK_PANEL_FORM") { try { if (updateDockPanelLayout) UpdateDockPanelFormFromLayout(description); else CreateDockPanelFormFromLayout(description, out mainDockPanelName, out mainDockPanelTitle); // reset id count _idCount = 0; } catch (Exception e) { TILogger.Error(e.Message, e); } } else { try { //Restore position. No cascading // change parameters to accommodate dock panel changes - RVH20210402 //RestoreOne(description, false, false); // idFixedValue is passed as -200 when we what a blank id to get passed to RestoreOne() string idTemp = ""; if (idFixedValue == -200) { // do nothing which leaves idTemp empty } else if (idFixedValue >= 0) { idTemp = idFixedValue.ToString(); //_idCount = idFixedValue + 1; } else { // only increment _idCount for Windows that are displayed in dock panels - RVH20210409 // checking the Visible property is NOT going to be the way to handle whether or not to increment _idCount // to correctly display in new panel. That property is false for windows that fall behind other panels. // Hardcode form types for now until we come up with a better way. //XmlNode baseNode = description.Node(BASE); //baseNode.Property("Visible") == "True" && //if (description.Property(FORM_TYPE) != "WELCOME_SCREEN" && description.Property(FORM_TYPE) != "SYMBOL_LIST_WINDOW" // && description.Property(FORM_TYPE) != "EXTERNAL_LINKING") if (description.Property(FORM_TYPE) != "SYMBOL_LIST_WINDOW" && description.Property(FORM_TYPE) != "EXTERNAL_LINKING") { idTemp = _idCount.ToString(); _idCount++; } } RestoreOne(description, false, false, dockPanelMode, mainDockPanelName, mainDockPanelTitle, idTemp); } catch (Exception e) { TILogger.Error(e.Message, e); } } if (fillScreen) { List newForms = new List(); foreach (Form form in Application.OpenForms.Cast().Where(x => !x.IsDisposed).ToList()) { bool shouldAdjust = true; Rectangle previousBounds; if (previouslyOpen.TryGetValue(form, out previousBounds)) shouldAdjust = previousBounds != form.DesktopBounds; if (shouldAdjust) // Either the form was new or moved. I.e. it was part of the layout we just loaded. // What about something that was minimized / restored? We should probably consider // that, too. What about something that was mentioned in the layout, but just // happened to be in exactly the same place before and after the load? Maybe we // could do this a better way. TODO newForms.Add(form); } if (null == destination) // Let the ScreenFiller deal with it. Items might be rearranged, but they // will stay on the same screen where they started. ScreenFiller.FillScreens(newForms); else ScreenFiller.FillScreen(destination, newForms); } // Test to see if we just loaded a custom channel that doesn't contain a channel bar // from a docked channel bar. If so, close the dock panel form that is empty - RVH20210605 if (loadedFromDockedChannelBar && !containsDockPanelForm && !ContainsChannelBarForm(all)) { if (saveMainDockForm != null && saveMainDockForm.IsEmpty()) { // Dock panel form is empty, so close it saveMainDockForm.Close(); DockPanelFormFocused = null; } } // Finished loading first default layout firstDefaultLayoutCompleted = true; if (null != LayoutRestoreComplete) LayoutRestoreComplete(closeOldWindows); } public XmlNode GetXmlFromFile(string fileName) { XmlNode body = null; try { XmlDocument document = new XmlDocument(); document.Load(fileName); body = document.DocumentElement; } catch { } return body; } // change parameters to accommodate dock panel changes - RVH20210402 //public XmlNode Restore(string fileName, bool closeOldWindows) public XmlNode Restore(string fileName, bool closeOldWindows, bool dockPanelMode = false, string mainDockPanelName = "", string mainDockPanelTitle = "", int idFixedValue = -100, bool loadOriginalFormat = false) { XmlNode body = null; try { body = GetXmlFromFile(fileName); } catch { } if (null == body) { // Almost certainly an invalid XML file. string message = GuiEnvironment.XmlConfig.Node("LAYOUT_MANAGER") .Node("PHRASES").Node("INVALID_FILE").PropertyForCulture("TEXT", "***"); string title = GuiEnvironment.XmlConfig.Node("LAYOUT_MANAGER") .Node("PHRASES").Node("ERROR").PropertyForCulture("TEXT", "***"); MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Error); } else { // Restore should not throw an exception. However, if it does, we want // to know about it. Don't mix that in with a bad xml file. // change parameters to accommodate dock panel changes - RVH20210402 // Restore(body, closeOldWindows); // check to see if layout has a dock panel in it. If not load the layout in free floating mode - RVH20210514 bool docked = dockPanelMode; if (dockPanelMode && loadOriginalFormat && !ContainsDockPanelForm(body)) docked = false; Restore(body, closeOldWindows, false, docked, mainDockPanelName, mainDockPanelTitle, idFixedValue); //Restore(body, closeOldWindows, false, dockPanelMode, mainDockPanelName, mainDockPanelTitle, idFixedValue); } // Finished loading first default layout firstDefaultLayoutCompleted = true; if (null != LayoutRestoreComplete) LayoutRestoreComplete(closeOldWindows); return body; } // change parameters to accommodate dock panel changes - RVH20210402 //private void RestoreOne(XmlNode description, bool ignorePosition, bool cascadePosition) private void RestoreOne(XmlNode description, bool ignorePosition, bool cascadePosition, bool dockPanelMode = false, string mainDockPanelName = "", string mainDockPanelTitle = "", string dockPanelID = "") { RestoreLayout code; // change parameters to accommodate dock panel changes - RVH20210402 //if (_restoreRules.TryGetValue(description.Property(FORM_TYPE), out code)) // code(description, ignorePosition, cascadePosition); if (_restoreRules.TryGetValue(description.Property(FORM_TYPE), out code)) code(description, ignorePosition, cascadePosition, dockPanelMode, mainDockPanelName, mainDockPanelTitle, dockPanelID); } private Dictionary _restoreRules = new Dictionary(); public void AddRestoreRule(string windowType, RestoreLayout code) { if (_restoreRules.ContainsKey(windowType)) throw new ArgumentException("Duplicate restore handler for " + windowType); _restoreRules[windowType] = code; } //public void RemoveAllRestoreRule() //Using this for a very special test project case. //{ // _restoreRules.Clear(); //} // modify parameters to accomodate dock panel mode when duplicating - RVH20210422 //public void Duplicate(ISaveLayout original) public void Duplicate(ISaveLayout original, bool dockPanelMode = false, string mainDockPanelName = "", string mainDockPanelTitle = "", string dockPanelID = "") { XmlDocument result = new XmlDocument(); XmlNode container = result.CreateElement("TEMP"); result.AppendChild(container); original.SaveLayout(container); // Use and cascade position of duplicate window foreach (XmlNode description in container.ChildNodes) { // modify parameters to accomodate dock panel mode when duplicating - RVH20210422 //RestoreOne(description, false, true); // set baseNode Docked property to dockPanelMode setting - RVH20210506 XmlNode baseNode = description.Node("BASE"); //When a docked window is duplicated as a free floating window, the height to be used //for the creation of the new window is modified. var originalForm = original as Form; if (originalForm != null) { var dockContent = originalForm.Parent as DockContent; if (dockContent != null && !dockPanelMode) { description.SetProperty("SMALL_BORDERS", false); var heightToUse = dockContent.Pane.Height; baseNode.SetProperty("Height", heightToUse); } } baseNode.SetProperty("Docked", dockPanelMode); // If duplicating to free floating, make sure the link channel is not dock window. string linkChannel = baseNode.Property("LinkChannel"); if (!dockPanelMode && linkChannel == SymbolLinkChannels.DockWindowChannel) baseNode.SetProperty("LinkChannel", SymbolLinkChannels.DefaultLinkChannel); RestoreOne(description, false, true, dockPanelMode, mainDockPanelName, mainDockPanelTitle, dockPanelID); } } // It seemed more convenient to have only one of these. I'm not sure if it even makes // sense to allow more than one. That's certainly not the way we plan to use it. private LayoutManager() { } static private LayoutManager _instance; static public LayoutManager Instance() { if (null == _instance) _instance = new LayoutManager(); return _instance; } public event LayoutRestoreCompleteHandler LayoutRestoreComplete; // Typically the main program would decide on a good place to save layout files, // and the library routines would use that suggestion as the default directory. // This might be null if no suggestion is available. public string Directory { get; set; } public static void MakeFullyVisible(Form form, Screen screen = null) { //The values ​​relating to the position, height and width of the form are obtained to be used in the calculations. //If the state of the form is not normal, these values ​​are taken from the RestoreBounds attribute. int formTop = form.WindowState == FormWindowState.Normal ? form.Location.Y : form.RestoreBounds.Y; int formLeft = form.WindowState == FormWindowState.Normal ? form.Location.X : form.RestoreBounds.X; int formWidth = form.WindowState == FormWindowState.Normal ? form.Width : form.RestoreBounds.Width; int formHeight = form.WindowState == FormWindowState.Normal ? form.Height: form.RestoreBounds.Height; // This is based on the function of the same name in the Delphi library. int layoutTop = formTop; int layoutLeft = formLeft; //this method "Screen.FromControl", can cause problems-"form" can get its location parameters changed from that which was saved to layout. //I note this with externalLinking,score-cylinder of toplist window and some others //as a result, although the user may save layout then re-opening the application followed by //exiting *without doing ANY changes*, the "save layout" dialog box might pop up when it's not supposed to-hence the variable "layoutTop" and "layoutLeft" if (null == screen) screen = Screen.FromControl(form); if (null == screen) screen = Screen.PrimaryScreen; // Windows 10 changed the the left, right and bottom borders so they are invisible. // We need to apply adjustments to align with the edges of the screen. int hiddenBorderWidthAdjustment = 0; int hiddenBorderHeightAdjustment = 0; if (GuiEnvironment.RunningWin10) { hiddenBorderWidthAdjustment = GuiEnvironment.WIDTH_INCREASE / 2; hiddenBorderHeightAdjustment = GuiEnvironment.HEIGHT_INCREASE; } int left = layoutLeft; int top = layoutTop; if (left + formWidth > screen.WorkingArea.Right + hiddenBorderWidthAdjustment) left = screen.WorkingArea.Right - formWidth + hiddenBorderWidthAdjustment; if (top + formHeight > screen.WorkingArea.Bottom + hiddenBorderHeightAdjustment) { top = screen.WorkingArea.Bottom - formHeight + hiddenBorderHeightAdjustment; // Uncomment code. We need to look at this a little more - Julio 8/21/15 //if (GuiEnvironment.openRelativeToMainWindow && LoadFromCloudRequest.loadingFromChannelBar) //{ // LoadFromCloudRequest.yOffSet += 50; // top = screen.WorkingArea.Bottom - form.Height - LoadFromCloudRequest.yOffSet; //} } if (top < screen.WorkingArea.Top) top = screen.WorkingArea.Top; if (left < screen.WorkingArea.Left - hiddenBorderWidthAdjustment) left = screen.WorkingArea.Left - hiddenBorderWidthAdjustment; // Check one more case what if the window is still too high for the current screen size. // In this case let's change the height of the window to fit. This case applies to the channel bar // saved in the different channel layouts. We need to apply the Windows 10 adjustments. if (top + formHeight > screen.WorkingArea.Bottom + hiddenBorderHeightAdjustment) { form.Height = screen.WorkingArea.Bottom - top + hiddenBorderHeightAdjustment; } form.Left = left; form.Top = top; } /// /// Make all child forms visible for all dock windows. /// This is necessary to make all the child forms show up in Application.OpenForm calls. /// There were issues in saving to cloud and the performance monitor with some of the /// child windows not showing in the Application.OpenForms list. /// public void MakeAllDockPanelChildWindowsVisible() { foreach (Form form in Application.OpenForms.OfType().Where(x => !x.IsDisposed).ToList()) { MainDockForm mainDockForm = form as MainDockForm; if (mainDockForm != null) mainDockForm.MakeAllChildFormsVisible(); } } /* procedure TCustomForm.MakeFullyVisible(AMonitor: TMonitor); var ALeft: Integer; ATop: Integer; begin if AMonitor = nil then AMonitor := Monitor; ALeft := Left; ATop := Top; if Left + Width > AMonitor.Left + AMonitor.Width then ALeft := AMonitor.Left + AMonitor.Width - Width; if Left < AMonitor.Left then ALeft := AMonitor.Left; if Top + Height > AMonitor.Top + AMonitor.Height then ATop := AMonitor.Top + AMonitor.Height - Height; if Top < AMonitor.Top then ATop := AMonitor.Top; SetBounds(ALeft, ATop, Width, Height); end; */ } }