frontend/windows/MySQLWorkbench/ModelOverviewForm.cs (1,491 lines of code) (raw):

/* * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2.0, * as published by the Free Software Foundation. * * This program is designed to work with certain software (including * but not limited to OpenSSL) that is licensed under separate terms, as * designated in a particular file or component or in included license * documentation. The authors of MySQL hereby grant you an additional * permission to link the program and your derivative works with the * separately licensed software that they have either included with * the program or referenced in the documentation. * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License, version 2.0, for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ using System; using System.Collections; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Windows.Forms; using MySQL.Base; using MySQL.Controls; using MySQL.Grt; using MySQL.GUI.Workbench.Plugins; using MySQL.GUI.Workbench.Properties; using MySQL.Utilities; using MySQL.Utilities.SysUtils; using MySQL.Workbench; namespace MySQL.GUI.Workbench { public partial class ModelOverviewForm : TabDocument, IWorkbenchDocument { #region Member Variables // Flags to avoid recursive refreshes. private enum RefreshState { Attachments, Diagrams, Privileges, Schema } private class Identifier { public NodeIdWrapper id; public String objectId; public Identifier(NodeIdWrapper id) { this.id = id; } } private Hashtable states = new Hashtable(); // Keeps states of columns for each section listview we have. // Currently only column widths are stored. private Hashtable columnStates = new Hashtable(); // The Workbench context private WbContext wbContext; // The Workbench overview private Overview wbOverview; private WorkbenchMenuManager workbenchMenuManager; private List<CollapsingPanel> panelList = new List<CollapsingPanel>(); private Overview.DisplayMode currentOverviewDisplayMode = Overview.DisplayMode.SmallIcon; private Dictionary<String, CollapsingPanel> panelsByNode = new Dictionary<string, CollapsingPanel>(); private Dictionary<String, ListView> listsByNode = new Dictionary<String, ListView>(); // Mapper between element icon ids and image list indices, depending on // the view mode of the listview the item is in. private Dictionary<int, int> imageIndexMapper = new Dictionary<int, int>(); private bool overviewInvalid = false; private String lastSearchText = ""; private NodeIdWrapper lastFoundNode = null; private ModelObjectDescriptionForm modelObjectDescriptionForm; private UserDatatypesForm userDatatypesForm; private UndoHistoryForm historyForm; private IMessageFilter wheelMessageFilter; private bool splitterMovePending = false; public bool skipHeader = false; #endregion #region Constructors public ModelOverviewForm(WbContext WbContext, Overview be) { InitializeComponent(); wbContext = WbContext; wbOverview = be; UpdateTabText(); workbenchMenuManager = new WorkbenchMenuManager(wbContext); userDatatypesForm = new UserDatatypesForm(wbContext); historyForm = new UndoHistoryForm(wbContext); modelObjectDescriptionForm = new ModelObjectDescriptionForm(wbContext); wheelMessageFilter = new WheelMessageFilter(this); Application.AddMessageFilter(wheelMessageFilter); SetupSideBar(); UpdateColors(); } private void UpdateTabText() { Text = wbOverview.get_title(); String newTabText = wbOverview.get_title(); String filename = wbContext.get_filename(); if (filename.Length > 0) newTabText += " (" + Path.GetFileName(filename) + ")"; TabText = newTabText; } #endregion #region Internal classes /// <summary> /// Internal class used to forward mouse wheel messages to the overview page to be able to scroll /// all panels using the mouse wheel. /// </summary> internal class WheelMessageFilter : IMessageFilter { private ModelOverviewForm form; public WheelMessageFilter(ModelOverviewForm form) { this.form = form; } public bool PreFilterMessage(ref Message m) { if ((WM)m.Msg == WM.MOUSEWHEEL) { // Only handle messages if the control belongs to our form. Win32.POINT cursorPoint = new Win32.POINT(); Win32.GetCursorPos(ref cursorPoint); IntPtr targetWindow = Win32.WindowFromPoint(cursorPoint); Control control = Control.FromChildHandle(targetWindow); if (control != null && ControlUtilities.ContainsControl(form.contentHeaderPanel, control)) { // Forward the message to the control or the first of its parents which can have scrollbars. while (control != null && !(control is ScrollableControl)) control = control.Parent; if (control != null) { Win32.SendMessage(control.Handle, m.Msg, m.WParam, m.LParam); return true; } } } return false; } } #endregion #region IWorkbenchDocument Interface public UIForm BackendForm { get { return wbOverview.get_uiform(); } } public void RefreshGUI(RefreshType refresh, String str, IntPtr ptr) { switch (refresh) { case RefreshType.RefreshSelection: if (ptr != null && ptr.ToInt64() != 0) { UIForm form = UIForm.GetFromFixedId(ptr); modelObjectDescriptionForm.UpdateForView(form); } else modelObjectDescriptionForm.UpdateForView(null); break; case RefreshType.RefreshDocument: UpdateTabText(); break; case RefreshType.RefreshOverviewNodeChildren: RefreshNodeChildren(new NodeIdWrapper(str)); break; case RefreshType.RefreshOverviewNodeInfo: RefreshNodeInfo(new NodeIdWrapper(str)); break; case RefreshType.RefreshCloseEditor: CloseEditorsForObject(str); break; } } public void PerformCommand(String command) { switch (command) { case "view_user_datatypes": ShowUserDatatypes(); break; case "view_object_description": ShowDescriptions(); break; case "view_undo_history": ShowUndoHistory(); break; case "wb.toggleSidebar": mainSplitContainer.Panel1Collapsed = !mainSplitContainer.Panel1Collapsed; break; case "wb.toggleSecondarySidebar": mainContentSplitContainer.Panel2Collapsed = !mainContentSplitContainer.Panel2Collapsed; break; } } public void UpdateColors() { ApplyColors(this); modelObjectDescriptionForm.UpdateColors(); if (Controls.Count > 0 && Controls[0] is DrawablePanel) Controls[0].BackColor = Conversions.GetApplicationColor(ApplicationColor.AppColorMainBackground, false); else BackColor = Conversions.GetApplicationColor(ApplicationColor.AppColorMainBackground, false); mainSplitContainer.BackColor = Conversions.GetApplicationColor(ApplicationColor.AppColorMainBackground, false); contentSplitContainer.BackColor = Conversions.GetApplicationColor(ApplicationColor.AppColorMainBackground, false); sideSplitContainer.BackColor = Conversions.GetApplicationColor(ApplicationColor.AppColorMainBackground, false); scrollPanel.BackColor = Conversions.GetApplicationColor(ApplicationColor.AppColorMainBackground, false); } public DockablePlugin FindPluginOfType(Type type) { foreach (ITabDocument content in bottomTabControl.Documents) if (content is ObjectEditorView && (content as ObjectEditorView).EditorPlugin.GetType() == type) return content as DockablePlugin; return null; } public bool ClosePluginOfType(Type type) { foreach (ITabDocument content in bottomTabControl.Documents) if (content is ObjectEditorView && (content as ObjectEditorView).EditorPlugin.GetType() == type) { // Unregister plugin from back end. wbContext.close_gui_plugin((content as ObjectEditorView).EditorPlugin.GetFixedPtr()); content.Close(); if (bottomTabControl.TabCount == 0) contentSplitContainer.Panel2Collapsed = true; return true; } return false; } #endregion #region Event Handling private void bottomTabControl_TabClosing(object sender, TabClosingEventArgs e) { ITabDocument document = (sender as FlatTabControl).DocumentFromPage(e.page); if (document is IWorkbenchDocument) e.canClose = (document as IWorkbenchDocument).CanCloseDocument(); else if (document is MySQL.Forms.AppViewDockContent) { MySQL.Forms.AppViewDockContent content = document as MySQL.Forms.AppViewDockContent; e.canClose = content.CanCloseDocument(); } } private void bottomTabControl_TabClosed(object sender, MySQL.Controls.TabClosedEventArgs e) { if (bottomTabControl.TabCount == 0) contentSplitContainer.Panel2Collapsed = true; ITabDocument document = (sender as FlatTabControl).DocumentFromPage(e.page); if (document is IWorkbenchDocument) (document as IWorkbenchDocument).CloseDocument(); else if (document is MySQL.Forms.AppViewDockContent) { MySQL.Forms.AppViewDockContent content = document as MySQL.Forms.AppViewDockContent; content.CloseDocument(); } e.page.Controls.Clear(); e.page.Dispose(); GC.Collect(); } #endregion #region Public functions public void SearchAndFocusNode(String text) { if ((lastSearchText == null) || !text.StartsWith(lastSearchText)) lastFoundNode = null; lastSearchText = text; lastFoundNode = wbOverview.search_child_item_node_matching(null, lastFoundNode, text); if (lastFoundNode != null) FocusNode(lastFoundNode); } public void RefreshNodeInfo(NodeIdWrapper node) { int nodeType; // if we're refreshing a node, first check what type is it, then update it // through its container or the item itself if (wbOverview.get_field(node, (int)Overview.Columns.NodeType, out nodeType)) { wbOverview.refresh_node(node, false); switch ((Overview.NodeType)nodeType) { case Overview.NodeType.Root: // Do nothing. We refresh the entire content with the model refresh notification. break; case Overview.NodeType.Item: // find the object in its container NodeIdWrapper parent = wbOverview.get_parent(node); if (listsByNode.ContainsKey(parent.toString())) { ListView list = listsByNode[parent.toString()]; UpdateListViewNode(node, list); } break; case Overview.NodeType.Section: // a CollapsingPanel break; case Overview.NodeType.Group: { // schema tabs if (panelsByNode.ContainsKey(wbOverview.get_parent(node).toString())) { CollapsingPanel panel = panelsByNode[wbOverview.get_parent(node).toString()]; panel.Refresh(); panel.Update(); } break; } default: throw new Exception("Model overview: invalid node type found"); } } } public void RefreshNodeChildren(NodeIdWrapper node) { int nodeType; if ((null == node) || !node.is_valid()) { RebuildModelContents(); return; } if (overviewInvalid) return; // find the container and refresh it if (wbOverview.get_field(node, (int)Overview.Columns.NodeType, out nodeType)) { wbOverview.refresh_node(node, true); if (nodeType == (int)Overview.NodeType.Section) { RefreshItemList(node, (Overview.NodeType)nodeType); } else if (nodeType == (int)Overview.NodeType.Group) { CollapsingPanel panel = panelsByNode[node.toString()]; } else if (nodeType == (int)Overview.NodeType.Division) { int childNodeType; wbOverview.get_field(node, (int)Overview.Columns.ChildNodeType, out childNodeType); switch ((Overview.NodeType)childNodeType) { case Overview.NodeType.Group: // Group as children of Division means a tabview RefreshGroupTabs(node); break; case Overview.NodeType.Section: break; case Overview.NodeType.Item: RefreshItemList(node, (Overview.NodeType)nodeType); break; } } } } private void RefreshGroupTabs(NodeIdWrapper node) { if (panelsByNode.ContainsKey(node.toString())) { CollapsingPanel panel = panelsByNode[node.toString()]; panel.SuspendLayout(); panel.Controls.Clear(); PopulateSections(panel, currentOverviewDisplayMode, false); Refresh(); panel.ResumeLayout(); } } private void RefreshItemList(NodeIdWrapper node, Overview.NodeType nodeType) { if (listsByNode.ContainsKey(node.toString())) { ListView list = listsByNode[node.toString()]; if (nodeType == Overview.NodeType.Section) { int childCount = wbOverview.count_children(node) - 1; String info = "(" + childCount + ((childCount == 1) ? " item" : " items") + ")"; SetInfoLabelForList(list, info); } if (list.View == View.Details) SaveColumnStates(list); PopulateListView(node, list); if (list.View == View.Details) RestoreColumnStates(list); } } public void BeginRenameSelection() { ListView list = ActiveControl as ListView; if (list != null) { if (list.SelectedItems.Count > 0) { NodeIdWrapper itemNodeId = (list.SelectedItems[0].Tag as Identifier).id; if (itemNodeId != null && wbOverview.is_editable(itemNodeId)) list.SelectedItems[0].BeginEdit(); } } } public void RebuildModelContents() { overviewInvalid = true; // Don't reload the entire page if we are just about to refresh it anyway. if (!states.ContainsKey(RefreshState.Schema)) { scrollPanel.SuspendLayout(); NodeIdWrapper rootNodeId = wbOverview.get_root(); ResetDocument(true); int count = wbOverview.count_children(rootNodeId); for (int i = 0; i < count; i++) { NodeIdWrapper panelNodeId = wbOverview.get_child(rootNodeId, i); string caption; int expanded; Overview.DisplayMode displayMode; int temp; if (!wbOverview.get_field(panelNodeId, (int)Overview.Columns.Label, out caption)) throw new Exception("Invalid node"); wbOverview.get_field(panelNodeId, (int)Overview.Columns.Expanded, out expanded); //wbOverview.get_field(panelNodeId, (int)Overview.Columns.Height, out height); wbOverview.get_field(panelNodeId, (int)Overview.Columns.DisplayMode, out temp); displayMode = (Overview.DisplayMode)temp; bool isExpansionDisabled = wbOverview.is_expansion_disabled(); bool initialExpandState = !isExpansionDisabled; CollapsingPanel overviewPanel = new CollapsingPanel(caption, CollapsingPanelDisplayMode.Normal, CollapsingPanelStyle.Flat, initialExpandState, 0); overviewPanel.DisableExpansionIcon = isExpansionDisabled; overviewPanel.HeaderFont = new Font("Tahoma", 9.75f, FontStyle.Bold); overviewPanel.Padding = new Padding(12, 4, 0, 0); overviewPanel.BackColor = Color.White; panelsByNode[panelNodeId.toString()] = overviewPanel; overviewPanel.Tag = CreateIdentifier(panelNodeId); // Add the collapsing panel to the form. Move every panel explicitly to the top position // (moving all others down one step) to make stacking working properly. scrollPanel.Controls.Add(overviewPanel); scrollPanel.Controls.SetChildIndex(overviewPanel, 0); overviewPanel.Dock = DockStyle.Top; // Add panel to the list of panels for this model type so it can be deleted later. panelList.Add(overviewPanel); // If this is the first panel then remove its header. // The parent header panel will provide one. if (panelList.Count == 1) overviewPanel.DisplayMode = CollapsingPanelDisplayMode.NoHeader; // Check if there are child items FillPanelContent(panelNodeId, displayMode, overviewPanel); overviewPanel.Expanded = expanded == 1; if (skipHeader) { overviewPanel.DisplayTabActionButtons = false; overviewPanel.DisplayCustomButtons = false; overviewPanel.DisplayMode = CollapsingPanelDisplayMode.TabsOnly; } } scrollPanel.ResumeLayout(true); } // Update immediately so we have a complete overview page while loading other content. Update(); } #endregion #region Helper functions /// <summary> /// Creates a key that can be used for later lookup in a dictionary consisting of /// a GRT icon id and a view mode (large icon, small icon). /// </summary> /// <param name="id"></param> /// <param name="view"></param> /// <returns></returns> private int CreateIconKey(int id, View view) { switch (view) { case View.LargeIcon: return 3 * id; case View.Tile: return 3 * id + 1; case View.SmallIcon: case View.Details: return 3 * id + 2; } return 0; } /// <summary> /// Returns the image index for a given node and view mode as cached in the mapper. /// If such a mapping does not exist yet then one is created. /// /// Note: wbOverview must be assigned already to make this work. /// </summary> /// <returns> /// The index of the image within the associated image list. /// </returns> private int FindImageIndex(NodeIdWrapper id, View view) { IconSize size = IconSize.Icon16; if (view == View.LargeIcon) size = IconSize.Icon48; int iconId = wbOverview.get_field_icon(id, (int)Overview.Columns.Label, size); int iconKey = CreateIconKey(iconId, view); if (!imageIndexMapper.ContainsKey(iconKey)) { // There is no image index stored yet for this node. // Add a new mapping for it. // This is necessary as we cannot guarantee that both image indices are the same for // small and large icons (which would be necessary if we want to use the same index for // both, large and small icon view). So we have to set the index depending on the view mode. int listIndex = IconManagerWrapper.get_instance().add_icon_to_imagelist(iconId); imageIndexMapper.Add(iconKey, listIndex); } return imageIndexMapper[iconKey]; } private void SetViewMode(CollapsingPanel panel, View mode) { foreach (Control control in panel.Controls) if (control is ListView) SetViewMode(control as ListView, mode); panel.PerformLayout(); } private void SetViewMode(CollapsingPanel panel, Overview.DisplayMode mode) { foreach (Control control in panel.Controls) if (control is ListView) SetViewMode(control as ListView, mode); panel.PerformLayout(); } private View[] displayMode2ViewMode = { View.Details, View.LargeIcon, View.Tile, View.Details }; private void SetViewMode(ListView view, Overview.DisplayMode mode) { View newViewMode = displayMode2ViewMode[(int)mode]; SetViewMode(view, newViewMode); } private void SetViewMode(ListView view, View newViewMode) { // Keep the old column states if we go away from details mode. // They will be restored later when we go back to details view and have // added the columns again. if (view.View == View.Details) SaveColumnStates(view); if (view.View != newViewMode) { switch (newViewMode) { case View.Tile: view.View = newViewMode; view.TileSize = new Size(140, 17); view.LargeImageList = IconManagerWrapper.ImageList16; break; case View.LargeIcon: view.View = newViewMode; view.LargeImageList = IconManagerWrapper.ImageList48; break; case View.Details: // This call will make the scrollbars visible for a moment but works around a more serious problem. // When the listview is switched to report mode without scrollbars being visible then it will not adjust // its client area for the header and parts of the first items are hidden behind the header. view.Scrollable = true; view.View = newViewMode; view.Scrollable = false; break; } // Adjust item image indices, as they might not be the same for large and small icons. for (int i = 0; i < view.Items.Count; i++) { NodeIdWrapper id = (view.Items[i].Tag as Identifier).id; view.Items[i].ImageIndex = FindImageIndex(id, view.View); } view.PerformLayout(); } } private void FocusNode(NodeIdWrapper node) { NodeIdWrapper parent = wbOverview.get_parent(node); int index = node.end(); if (listsByNode.ContainsKey(parent.toString())) { ListView list = listsByNode[parent.toString()]; list.Focus(); list.SelectedIndices.Clear(); list.Items[index].Selected = true; } } #endregion #region Form implementation private int modelTypeCollapsingPanel_TabCountNeeded(object sender) { if (wbOverview != null) { NodeIdWrapper rootNode = wbOverview.get_root(); if (rootNode != null) return wbOverview.count_children(rootNode); } return 0; } private bool modelTypeCollapsingPanel_TabInfoNeeded(object sender, int index, out int iconId, out string caption, out string description) { iconId = 0; caption = ""; description = ""; NodeIdWrapper nodeId = wbOverview.get_root(); if (nodeId != null) { wbOverview.get_field(nodeId, (int)Overview.Columns.Label, out caption); return true; } else return false; } private Identifier CreateIdentifier(NodeIdWrapper nodeId) { Identifier identifier = new Identifier(nodeId); identifier.objectId = wbOverview.get_node_unique_id(nodeId); return identifier; } private void FillPanelContent(NodeIdWrapper panelNodeId, Overview.DisplayMode displayMode, CollapsingPanel overviewPanel) { // Check the node type of the child items int itemNodeType; wbOverview.get_field(panelNodeId, (int)Overview.Columns.ChildNodeType, out itemNodeType); switch ((Overview.NodeType)itemNodeType) { case Overview.NodeType.Group: { // If the child items are of type Group, add them as tabs. EnableTabViewForPanel(displayMode, overviewPanel); // Fill child items if (wbOverview.count_children(panelNodeId) > 0) FillPanelContent(wbOverview.get_child(panelNodeId, 0), currentOverviewDisplayMode, overviewPanel); } break; case Overview.NodeType.Section: // If they are of item type Section populate the list view with sections and items. PopulateSections(overviewPanel, displayMode, true); break; case Overview.NodeType.Item: { // If the child entry is an item thean we don't have any intermediate structure. ListView sectionListview = GetSectionListview(panelNodeId, overviewPanel, false, displayMode, "", ""); cacheListview(panelNodeId.toString(), sectionListview); PopulateListView(panelNodeId, sectionListview); break; } } } /// <summary> /// Helper class to make sorting in our list views case insensitive. /// </summary> private class InsensitiveListviewComparer : IComparer { #region IComparer Members private CaseInsensitiveComparer comparer = new CaseInsensitiveComparer(); public int column; // The column to sort on. public SortOrder order = SortOrder.None; public InsensitiveListviewComparer(int column, SortOrder order) { this.column = column; this.order = order; } public int Compare(object x, object y) { if (order == SortOrder.None) return 0; // Compare text case-insensitively, but make the default entry always the first one. if (((ListViewItem)x).Index == 0) return -1; else if (((ListViewItem)y).Index == 0) return 1; else { if (order == SortOrder.Ascending) return comparer.Compare( (x as ListViewItem).SubItems[column].Text, (y as ListViewItem).SubItems[column].Text); else return comparer.Compare( (y as ListViewItem).SubItems[column].Text, (x as ListViewItem).SubItems[column].Text); } } #endregion } private void SetInfoLabelForList(ListView list, String info) { CollapsingPanel overviewPanel = list.Parent as CollapsingPanel; if (null == overviewPanel) return; Panel panel = null; foreach (Control control in overviewPanel.Controls) { if (control == list) break; panel = control as Panel; } if (panel != null) { Label label = panel.Controls[0] as Label; if (label != null) label.Text = info; } } /// <summary> /// Returns the listview for a given section. If it does not exist yet it gets created. /// Optionally a simple panel is added as header for this section. /// </summary> private ListView GetSectionListview(NodeIdWrapper controlNodeId, CollapsingPanel overviewPanel, bool addSectionHeader, Overview.DisplayMode displayMode, String caption, String info) { // Iterate over all sections we have already and find the one with the given control id. String objectId = wbOverview.get_node_unique_id(controlNodeId); foreach (Control control in overviewPanel.Controls) { if (control.Tag != null) { String currentObjectId = (control.Tag as Identifier).objectId; if (currentObjectId != null && currentObjectId == objectId) { ListView listview = control as ListView; SetViewMode(listview, displayMode); return listview; } } } // If we come here then the section does not exist yet, so add it. Start with a header. if (addSectionHeader) { Panel panel = new Panel(); panel.BorderStyle = BorderStyle.None; panel.Padding = new Padding(5, 2, 5, 0); panel.BackgroundImage = Resources.header_bar_blue; panel.BackgroundImageLayout = ImageLayout.None; panel.Height = 24; // Insert client controls in reverse order. // Info label. Label infoLabel = new Label(); infoLabel.Text = info; infoLabel.ForeColor = Color.Gray; infoLabel.Font = overviewPanel.Font; infoLabel.AutoSize = true; infoLabel.Margin = new Padding(10, 0, 0, 0); infoLabel.Dock = DockStyle.Left; panel.Controls.Add(infoLabel); overviewPanel.Controls.Add(panel); // Caption label. Label captionLabel = new Label(); captionLabel.Text = caption; //captionLabel.Font = new Font(overviewPanel.Font, FontStyle.Bold); captionLabel.AutoSize = true; captionLabel.Dock = DockStyle.Left; captionLabel.ForeColor = Color.Black; panel.Controls.Add(captionLabel); overviewPanel.Controls.Add(panel); } // Then the content view. ListView sectionListview = new ListView(); sectionListview.BorderStyle = BorderStyle.None; sectionListview.AllowColumnReorder = true; sectionListview.ShowItemToolTips = true; // Undocumented feature: enabling AutoSize will indeed make the control automatically resize // (the docs say it wouldn't) though without considering long captions. For them to work // additionally Scrollable can be set to true to show a scrollbar, but only in this special case. sectionListview.AutoSize = true; sectionListview.MultiSelect = false; sectionListview.Scrollable = true; sectionListview.Visible = true; sectionListview.Font = overviewPanel.Font; sectionListview.Tag = CreateIdentifier(controlNodeId); sectionListview.Sorting = SortOrder.None; // We do custom sort. sectionListview.DoubleClick += new EventHandler(listViewDoubleClick); sectionListview.ItemDrag += new ItemDragEventHandler(listViewItemDrag); sectionListview.SelectedIndexChanged += new EventHandler(listViewSelectedIndexChanged); sectionListview.KeyDown += new KeyEventHandler(listViewKeyDown); sectionListview.MouseUp += new MouseEventHandler(listViewMouseUp); sectionListview.ColumnClick += new ColumnClickEventHandler(ListviewColumnClick); sectionListview.Enter += new EventHandler(ListViewEnter); // Renaming of overview nodes sectionListview.LabelEdit = true; sectionListview.AfterLabelEdit += new LabelEditEventHandler(listViewAfterLabelEdit); sectionListview.BeforeLabelEdit += new LabelEditEventHandler(listViewBeforeLabelEdit); // Add it to the panel. Usually the layout engine of the panel should take care for // the layout of the controls, but just to be sure we set here a dock style. overviewPanel.Controls.Add(sectionListview); sectionListview.Dock = DockStyle.Top; // Set Image Lists sectionListview.SmallImageList = IconManagerWrapper.ImageList16; sectionListview.LargeImageList = IconManagerWrapper.ImageList48; SetViewMode(sectionListview, displayMode); sectionListview.Update(); return sectionListview; } /// <summary> /// When entering a list view the surrounding scroll panel scrolls it into view such that its /// left upper corner is at the left upper position. This happens only if the listview is larger /// than the visible scroll panel area. Since clicking on the listview requires it to be already /// in view there's no sense in scrolling. /// We try to counter the scroll event by scrolling back to where we were before, even though /// this means slight flickering. /// </summary> void ListViewEnter(object sender, EventArgs e) { Action<Point> d = (Point p) => { scrollPanel.AutoScrollPosition = new Point(Math.Abs(p.X), Math.Abs(p.Y)); }; BeginInvoke(d, scrollPanel.AutoScrollPosition); } void ListviewColumnClick(object sender, ColumnClickEventArgs e) { ListView listview = (sender as ListView); SortOrder newOrder = SortOrder.Ascending; if (!(listview.ListViewItemSorter is InsensitiveListviewComparer)) listview.ListViewItemSorter = new InsensitiveListviewComparer(e.Column, newOrder); else { // Adjust sort order depending on which column was clicked on. // A previously not selected column always sorts ascending. if ((listview.ListViewItemSorter as InsensitiveListviewComparer).column == e.Column) { // Another click on the same column. Reverse the sort order. if ((listview.ListViewItemSorter as InsensitiveListviewComparer).order == SortOrder.Ascending) newOrder = SortOrder.Descending; } listview.ListViewItemSorter = new InsensitiveListviewComparer(e.Column, newOrder); } Win32.SetSortIcon(listview, e.Column, newOrder); } /// <summary> /// Sets the given panel up so that it shows tabs on top that can be used to switch content. /// </summary> /// <param name="displayMode"></param> /// <param name="overviewPanel"></param> private void EnableTabViewForPanel(Overview.DisplayMode displayMode, CollapsingPanel overviewPanel) { overviewPanel.TabHeaderImageList = new ImageList(); overviewPanel.TabHeaderImageList.ColorDepth = ColorDepth.Depth32Bit; overviewPanel.TabHeaderImageList.ImageSize = new Size(32, 32); overviewPanel.TabHeaderImageList = IconManagerWrapper.ImageList32; overviewPanel.DisplayMode = CollapsingPanelDisplayMode.HeaderAndTab; overviewPanel.TabCountNeeded += new TabCountHandler(overviewPanel_TabCountNeeded); overviewPanel.TabInfoNeeded += new TabInfoHandler(overviewPanel_TabInfoNeeded); overviewPanel.TabChanged += new TabChangedHandler(overviewPanel_TabChanged); overviewPanel.TabDoubleClicked += new MouseEventHandler(overviewPanel_TabDoubleClicked); overviewPanel.TabHeaderMouseUp += new MouseEventHandler(overviewPanel_TabHeaderMouseUp); // Select the first tab to populate the ListView overviewPanel.SelectedTabIndex = 0; overviewPanel.DisplayTabActionButtons = true; overviewPanel.TabAddButtonClicked += new EventHandler(overviewPanel_TabAddButtonClicked); overviewPanel.TabDelButtonClicked += new EventHandler(overviewPanel_TabDelButtonClicked); // Buttons for display mode. overviewPanel.DisplayCustomButtons = true; overviewPanel.CustomButtonClicked += new CustomButtonEvent(overviewPanel_CustomButtonClicked); overviewPanel.AddCustomButton(26, 23, "collapsing_panel_grid_large_icons", displayMode == Overview.DisplayMode.LargeIcon); overviewPanel.AddCustomButton(26, 23, "collapsing_panel_grid_small_icons", displayMode == Overview.DisplayMode.SmallIcon); overviewPanel.AddCustomButton(26, 23, "collapsing_panel_grid_details", displayMode == Overview.DisplayMode.List); } /// <summary> /// Save the state of the current columns in the given list view so it can be restored later. /// </summary> /// <param name="listview"></param> private void SaveColumnStates(ListView listview) { String id = (listview.Tag as Identifier).objectId; if (id != null) { List<int> widths = new List<int>(); for (int i = 0; i < listview.Columns.Count; i++) widths.Add(listview.Columns[i].Width); columnStates[id] = widths; } } /// <summary> /// Restores a saved column state of the given listview (if it exists). /// </summary> /// <param name="listview"></param> private void RestoreColumnStates(ListView listview) { String id = (listview.Tag as Identifier).objectId; if (id != null && columnStates.ContainsKey(id)) { List<int> widths = (List<int>)columnStates[id]; for (int i = 0; i < widths.Count && i < listview.Columns.Count; i++) listview.Columns[i].Width = widths[i]; } } /// <summary> /// Fills the given panel with a number of sections (simple panel + listview pair) per section /// entry in wbOverview. /// </summary> private void PopulateSections(CollapsingPanel overviewPanel, Overview.DisplayMode displayMode, bool setDefaultTabPage) { if (overviewPanel != null && overviewPanel.Tag != null) { overviewPanel.SuspendLayout(); try { NodeIdWrapper parentNodeId = null; // If this panel has tabs, choose the NodeId of the selected tab as parent. if (overviewPanel.DisplayMode == CollapsingPanelDisplayMode.HeaderAndTab || overviewPanel.DisplayMode == CollapsingPanelDisplayMode.TabsOnly) { // make sure the selected index is still valid int count = wbOverview.count_children((overviewPanel.Tag as Identifier).id); if (count > 0) { int default_tab_page_index = wbOverview.get_default_tab_page_index(); if (setDefaultTabPage && default_tab_page_index != -1) { overviewPanel.SelectedTabIndex = default_tab_page_index; } else if (overviewPanel.SelectedTabIndex > count - 1) { if (count > 0) overviewPanel.SelectedTabIndex = count - 1; else overviewPanel.SelectedTabIndex = 0; } parentNodeId = wbOverview.get_child((overviewPanel.Tag as Identifier).id, overviewPanel.SelectedTabIndex); // reselect the tab wbOverview.focus_node(parentNodeId); } else { overviewPanel.SelectedTabIndex = 0; parentNodeId = null; } } else // If this panel has no tabs, choose the NodeId of the panel itself as parent if (overviewPanel.DisplayMode == CollapsingPanelDisplayMode.Normal) parentNodeId = (overviewPanel.Tag as Identifier).id; if (parentNodeId != null) { int itemCount = wbOverview.count_children(parentNodeId); if (itemCount > 0) { NodeIdWrapper itemNodeId = wbOverview.get_child(parentNodeId, 0); // Check the node type of the child items int nodeType; wbOverview.get_field(itemNodeId, (int)Overview.Columns.NodeType, out nodeType); // If the node type is secion, loop over all sections and add them if (nodeType == (int)Overview.NodeType.Section) { for (int i = 0; i < itemCount; i++) { NodeIdWrapper sectionNodeId = wbOverview.get_child(parentNodeId, i); string caption; wbOverview.get_field(sectionNodeId, (int)Overview.Columns.Label, out caption); // Determine child count for info display but don't consider the default entry. int childCount = wbOverview.count_children(sectionNodeId) - 1; String info = "(" + childCount + ((childCount == 1) ? " item" : " items") + ")"; ListView sectionListview = GetSectionListview(sectionNodeId, overviewPanel, true, displayMode, caption, info); cacheListview(sectionNodeId.toString(), sectionListview); PopulateListViewColumns(sectionNodeId, sectionListview, displayMode); PopulateListView(sectionNodeId, sectionListview); if (sectionListview.View == View.Details) RestoreColumnStates(sectionListview); } } else { // If there are no sections, simple add the items. ListView sectionListview = GetSectionListview(parentNodeId, overviewPanel, false, displayMode, "", ""); cacheListview(parentNodeId.toString(), sectionListview); PopulateListView(parentNodeId, sectionListview); } } } } finally { overviewPanel.ResumeLayout(); } } } private void PopulateListViewColumns(NodeIdWrapper sectionNodeId, ListView sectionListview, Overview.DisplayMode displayMode) { // Refresh columns for this view. sectionListview.Columns.Clear(); sectionListview.Columns.Add("Name", 200); // Only add more columns if we are not in tile view mode. // We add columns also for large icon view, even though we don't see them, as we can then // quicker switch between large icon and tile view (no need to re-populate the view then). if (displayMode == Overview.DisplayMode.List) { int columnCount = wbOverview.get_details_field_count(sectionNodeId); for (int counter = 0; counter < columnCount; counter++) { String columnCaption = wbOverview.get_field_name(sectionNodeId, (int)Overview.Columns.FirstDetailField + counter); sectionListview.Columns.Add(columnCaption, 100); } } } private void PopulateListView(NodeIdWrapper parentNodeId, ListView listView) { listView.BeginUpdate(); try { int itemCount = wbOverview.count_children(parentNodeId); ListViewItem[] items = new ListViewItem[itemCount]; int detail_fields_count = wbOverview.get_details_field_count(parentNodeId); for (int i = 0; i < itemCount; i++) { NodeIdWrapper itemNodeId = wbOverview.get_child(parentNodeId, i); string caption; wbOverview.get_field(itemNodeId, (int)Overview.Columns.Label, out caption); ListViewItem item = new ListViewItem(caption, FindImageIndex(itemNodeId, listView.View)); item.Tag = CreateIdentifier(itemNodeId); item.ToolTipText = caption; // Add details for that item, if available. for (int counter = 0; counter < detail_fields_count; counter++) { String detailText; wbOverview.get_field(itemNodeId, (int)Overview.Columns.FirstDetailField + counter, out detailText); item.SubItems.Add(detailText.Replace(Environment.NewLine, " / ")); } items[i] = item; } listView.Items.Clear(); listView.Items.AddRange(items); // Setting the sorter also starts sorting. listView.ListViewItemSorter = new InsensitiveListviewComparer(0, SortOrder.Ascending); Win32.SetSortIcon(listView, 0, SortOrder.Ascending); // select stuff List<int> selected = wbOverview.get_selected_children(parentNodeId); foreach (int i in selected) listView.SelectedIndices.Add(i); } finally { listView.EndUpdate(); listView.PerformLayout(); }; } /// <summary> /// Updates all fields of the given node. /// </summary> private void UpdateListViewNode(NodeIdWrapper nodeId, ListView listView) { // Find the listview item that corresponds to the given node id and update it. // Don't wrap this with BeginUpdate/EndUpdate. That will produce more flicker than // just updating the item. String objectId = wbOverview.get_node_unique_id(nodeId); foreach (ListViewItem item in listView.Items) { String currentObjectId = (item.Tag as Identifier).objectId; if (currentObjectId != null && currentObjectId == objectId) { string caption; wbOverview.get_field(nodeId, (int)Overview.Columns.Label, out caption); item.Text = caption; item.ToolTipText = caption; // Note: the first sub item is the same as the main item. int offset = -1; foreach (ListViewItem.ListViewSubItem subItem in item.SubItems) { if (offset > -1) { String detailText; wbOverview.get_field(nodeId, (int)Overview.Columns.FirstDetailField + offset, out detailText); subItem.Text = detailText.Replace(Environment.NewLine, " / "); } offset++; } break; } } } void PopupActionHandler(object sender, EventArgs e) { ToolStripItem item = sender as ToolStripItem; if (item != null) { String name = item.Name; if (name == "builtin:rename") { if (item.Owner.Tag is CollapsingPanel) { CollapsingPanel panel = item.Owner.Tag as CollapsingPanel; } else if (item.Owner.Tag is ListView) { ListView list = item.Owner.Tag as ListView; if (list.SelectedItems.Count > 0) list.SelectedItems[0].BeginEdit(); } } else if (name == "drop") { List <NodeIdWrapper> node_ids = new List<NodeIdWrapper>(); if (item.Owner.Tag is ListView) { ListView list = item.Owner.Tag as ListView; foreach (ListViewItem list_item in list.SelectedItems) node_ids.Add((list_item.Tag as Identifier).id); } else node_ids.Add((item.Tag as Identifier).id); wbOverview.activate_popup_item_for_nodes(name, node_ids); } else wbOverview.activate_popup_item_for_nodes(name, new List<NodeIdWrapper>() { (item.Tag as Identifier).id }); } } void overviewPanel_TabHeaderMouseUp(object sender, MouseEventArgs e) { CollapsingPanel panel = sender as CollapsingPanel; if (panel != null) { if (panel != null && panel.Tag != null) { List<MySQL.Base.MenuItem> items; List<NodeIdWrapper> nodes = new List<NodeIdWrapper>(); NodeIdWrapper itemNodeId= new NodeIdWrapper(); int tab = panel.GetTabAtPosition(e.X, e.Y); if (tab >= 0) { itemNodeId = wbOverview.get_child((panel.Tag as Identifier).id, tab); nodes.Add(itemNodeId); } items = wbOverview.get_popup_items_for_nodes(nodes); if (items != null && items.Count > 0) { System.Windows.Forms.ContextMenuStrip menu; menu = workbenchMenuManager.ShowContextMenu(panel, items, e.X, e.Y, new EventHandler(PopupActionHandler)); menu.Tag = panel; SetMenuItemsTag(menu.Items, CreateIdentifier(itemNodeId)); } } } } void SetMenuItemsTag(ToolStripItemCollection menuItems, object tagObject) { foreach (ToolStripItem tsitem in menuItems) { if (tsitem is ToolStripMenuItem) { ToolStripMenuItem item = tsitem as ToolStripMenuItem; if (item.Tag != null) throw new Exception("item.Tag was expected to be unset"); if ((item.DropDownItems != null) && (item.DropDownItems.Count > 0)) SetMenuItemsTag(item.DropDownItems, tagObject); else item.Tag = tagObject; } } } void listViewKeyDown(object sender, KeyEventArgs e) { ListView listView = sender as ListView; if (e.Modifiers == Keys.None && e.KeyCode == Keys.F2) { if (listView.SelectedItems.Count > 0) listView.SelectedItems[0].BeginEdit(); } } void listViewMouseUp(object sender, MouseEventArgs e) { ListView listView = sender as ListView; if (listView != null && e.Button == MouseButtons.Right) { List<MySQL.Base.MenuItem> items = new List<MySQL.Base.MenuItem>(); List<NodeIdWrapper> item_nodes = new List<NodeIdWrapper>(); if (listView.SelectedItems.Count == 0) { item_nodes.Add((listView.Tag as Identifier).id); // nodeid for the group represented by the listview } else { foreach (ListViewItem item in listView.SelectedItems) item_nodes.Add((item.Tag as Identifier).id); } if (item_nodes.Count != 0) { items = wbOverview.get_popup_items_for_nodes(item_nodes); if (items.Count > 0) { System.Windows.Forms.ContextMenuStrip menu; menu = workbenchMenuManager.ShowContextMenu(listView, items, e.X, e.Y, new EventHandler(PopupActionHandler)); menu.Tag = listView; SetMenuItemsTag(menu.Items, CreateIdentifier(item_nodes[0])); } } } } void listViewSelectedIndexChanged(object sender, EventArgs e) { ListView listView = sender as ListView; // If the parent of the listview is null then is it currently being destroyed. // Strangely enough, listView.Disposing is still false. if (listView != null && listView.Parent != null) { wbOverview.begin_selection_marking(); foreach (ListViewItem item in listView.SelectedItems) { NodeIdWrapper itemNodeId = (item.Tag as Identifier).id; try { wbOverview.select_node(itemNodeId); } catch { } } wbOverview.end_selection_marking(); } } void listViewBeforeLabelEdit(object sender, LabelEditEventArgs e) { ListView listView = sender as ListView; ListViewItem item = listView.Items[e.Item]; if (item == null) return; if (item.Tag == null) e.CancelEdit = true; else { NodeIdWrapper itemNodeId = (item.Tag as Identifier).id; if (itemNodeId == null || !wbOverview.is_editable(itemNodeId)) e.CancelEdit = true; } } void listViewAfterLabelEdit(object sender, LabelEditEventArgs e) { ListView listView = sender as ListView; if (listView != null && e.Item >= 0 && e.Label != null && !e.Label.Equals("")) { ListViewItem item = listView.Items[e.Item]; if (item != null && item.Tag != null) { NodeIdWrapper itemNodeId = (item.Tag as Identifier).id; if (itemNodeId != null) { if (!wbOverview.set_field(itemNodeId, (int)Overview.Columns.Label, e.Label)) e.CancelEdit = true; } } } else if (e.Label != null) e.CancelEdit = true; } void listViewItemDrag(object sender, ItemDragEventArgs e) { ListView listView = sender as ListView; if (listView != null) { ListView.SelectedListViewItemCollection selItems = listView.SelectedItems; List<GrtValue> selObjects = new List<GrtValue>(); foreach (ListViewItem item in selItems) { if (item.Tag != null) { NodeIdWrapper itemNodeId = (item.Tag as Identifier).id; GrtValue val = wbOverview.get_grt_value(itemNodeId, 0); if (itemNodeId != null) selObjects.Add(val); } } if (selObjects.Count > 0) DoDragDrop(selObjects, DragDropEffects.Copy); } } void listViewDoubleClick(object sender, EventArgs e) { ListView view = sender as ListView; if (view != null && view.SelectedItems.Count > 0 && view.SelectedItems[0].Tag != null) { NodeIdWrapper nodeId = (view.SelectedItems[0].Tag as Identifier).id; if (nodeId != null) wbOverview.activate_node(nodeId); } } private void overviewPanel_TabDoubleClicked(object sender, MouseEventArgs e) { CollapsingPanel panel = sender as CollapsingPanel; if (panel != null && panel.Tag != null) wbOverview.activate_node( wbOverview.get_child((panel.Tag as Identifier).id, panel.SelectedTabIndex)); } private void overviewPanel_TabChanged(object sender, int index) { CollapsingPanel panel = sender as CollapsingPanel; if (panel != null && panel.Tag != null && index >= 0) wbOverview.select_node(wbOverview.get_child((panel.Tag as Identifier).id, panel.SelectedTabIndex)); // Delete all sections in the panel and re-populate it. SuspendLayout(); try { foreach (Control control in panel.Controls) if (control is ListView) { ListView view = control as ListView; if (view.View == View.Details) SaveColumnStates(view); } panel.Controls.Clear(); PopulateSections(panel, currentOverviewDisplayMode, false); } finally { ResumeLayout(); } } private int overviewPanel_TabCountNeeded(object sender) { CollapsingPanel panel = sender as CollapsingPanel; if (panel != null && panel.Tag != null) return wbOverview.count_children((panel.Tag as Identifier).id); else return 0; } private bool overviewPanel_TabInfoNeeded(object sender, int index, out int iconId, out string caption, out string description) { iconId = 0; description = ""; caption = ""; CollapsingPanel panel = sender as CollapsingPanel; if (panel != null && panel.Tag != null) { NodeIdWrapper tabNodeId = wbOverview.get_child((panel.Tag as Identifier).id, index); if (tabNodeId != null) { wbOverview.get_field(tabNodeId, (int)Overview.Columns.Label, out caption); description = wbOverview.get_field_description(tabNodeId, 0); int grtIconId = wbOverview.get_field_icon(tabNodeId, (int)Overview.Columns.Label, IconSize.Icon32); iconId = IconManagerWrapper.get_instance().add_icon_to_imagelist(grtIconId); } } return true; } void overviewPanel_TabDelButtonClicked(object sender, EventArgs e) { CollapsingPanel panel = sender as CollapsingPanel; if (panel != null && panel.Tag != null) { NodeIdWrapper groupNodeId = (panel.Tag as Identifier).id; // Delete section object. wbOverview.request_delete_object(wbOverview.get_focused_child(groupNodeId)); } } void overviewPanel_TabAddButtonClicked(object sender, EventArgs e) { CollapsingPanel panel = sender as CollapsingPanel; if (panel != null && panel.Tag != null) { NodeIdWrapper groupNodeId = (panel.Tag as Identifier).id; // Add section object. wbOverview.request_add_object(groupNodeId); wbOverview.refresh_node(groupNodeId, true); } } void overviewPanel_CustomButtonClicked(object sender, int index) { CollapsingPanel panel = sender as CollapsingPanel; Overview.DisplayMode newMode = (Overview.DisplayMode)(index + 1); if (newMode != currentOverviewDisplayMode) { bool fullRefresh = (newMode == Overview.DisplayMode.SmallIcon) || (currentOverviewDisplayMode == Overview.DisplayMode.SmallIcon); currentOverviewDisplayMode = newMode; // Unfortunately, we cannot just switch the view mode here as this would leave all columns // intact, which in turn means we have unwanted info in tile mode. // So we have to fully re-populate the listviews in cases where we switch from or to tile mode. if (fullRefresh) PopulateSections(panel, currentOverviewDisplayMode, false); else SetViewMode(panel, currentOverviewDisplayMode); } } private void ModelOverviewForm_Shown(object sender, EventArgs e) { LoadFormState(); } internal void ResetDocument(bool removePanels) { overviewInvalid = false; SuspendLayout(); try { foreach (CollapsingPanel panel in panelList) { panel.Controls.Clear(); if (removePanels && panel.Parent != null) panel.Parent.Controls.Remove(panel); } if (removePanels) panelList.Clear(); } finally { ResumeLayout(); } } /// <summary> /// Returns the current view mode of the listview that represents the content of the given panel. /// </summary> /// <param name="panel"></param> /// <returns></returns> private View GetViewMode(CollapsingPanel panel) { foreach (Control control in panel.Controls) if (control is ListView) return (control as ListView).View; return View.LargeIcon; } #endregion #region Other Implementation private void SetupSideBar() { mainSplitContainer.SuspendLayout(); sideSplitContainer.SuspendLayout(); try { // Rebuild side bar. sideTopTabControl.TabPages.Clear(); DockSideDocument(modelObjectDescriptionForm, true, true); sideBottomTabControl.TabPages.Clear(); DockSideDocument(userDatatypesForm, false, true); DockSideDocument(historyForm, false, false); contentSplitContainer.Panel2Collapsed = true; contentSplitContainer.SplitterWidth = 6; contentSplitContainer.Panel2MinSize = 370; } finally { mainSplitContainer.ResumeLayout(true); sideSplitContainer.ResumeLayout(true); } } private void DockSideDocument(ITabDocument document, bool top, bool activate) { if (top) { if (!sideTopTabControl.HasDocument(document)) { int index = sideTopTabControl.AddDocument(document); sideTopTabControl.TabPages[index].BackColor = Color.White; } } else { if (!sideBottomTabControl.HasDocument(document)) { int index = sideBottomTabControl.AddDocument(document); sideBottomTabControl.TabPages[index].BackColor = Color.White; } } if (activate) document.Activate(); if (sideBottomTabControl.TabCount == 0) sideSplitContainer.Panel2Collapsed = true; // This will implicitly expand panel1. else sideSplitContainer.Panel1Collapsed = false; if (sideTopTabControl.TabCount == 0) sideSplitContainer.Panel1Collapsed = true; // This will implicitly expand panel2. else sideSplitContainer.Panel2Collapsed = false; } /// <summary> /// Docks the given document to the bottom tab control (like object editors etc.). /// </summary> /// <param name="document"></param> public void DockDocument(ITabDocument document, bool activate) { if (!bottomTabControl.HasDocument(document)) bottomTabControl.AddDocument(document); if (activate) document.Activate(); if (contentSplitContainer.Panel2Collapsed) { contentSplitContainer.Panel2Collapsed = false; // Set a splitter distance or we end up at almost full display. Use a relatively small // value for panel2. The document's min height will kick in and does the right job. contentSplitContainer.SplitterDistance = contentSplitContainer.Height - 100; } } public void UndockDocument(ITabDocument document) { if (bottomTabControl.HasDocument(document)) bottomTabControl.RemoveDocument(document); } public void CloseEditorsForObject(string oid) { ITabDocument[] documents = bottomTabControl.DocumentsToArray(); // loop over all documents for (int i = documents.Length - 1; i >= 0; i--) { if (documents[i] is ObjectEditorView) { ObjectEditorView editor = documents[i] as ObjectEditorView; if (oid == "" || editor.EditorPlugin.ShouldCloseOnObjectDelete(oid)) bottomTabControl.CloseDocument(editor); } } } /// <summary> /// Determines if this document can be closed, i.e. all sub documents are queried for closing /// as well. Returns false if this form cannot be closed. /// </summary> /// <returns></returns> public bool CanCloseDocument() { foreach (ITabDocument document in bottomTabControl.Documents) if (document is IWorkbenchDocument) if (!(document as IWorkbenchDocument).CanCloseDocument()) return false; if (!BackendForm.can_close()) return false; return true; } public void CloseDocument() { foreach (ITabDocument document in bottomTabControl.Documents) if (document is IWorkbenchDocument) (document as IWorkbenchDocument).CloseDocument(); BackendForm.close(); } private void ShowUserDatatypes() { mainSplitContainer.Panel2Collapsed = false; userDatatypesForm.Activate(); } private void ShowDescriptions() { mainSplitContainer.Panel2Collapsed = false; modelObjectDescriptionForm.Activate(); } private void ShowUndoHistory() { mainSplitContainer.Panel2Collapsed = false; historyForm.Activate(); } private void LoadFormState() { int sidebarWidth = wbContext.read_state("sidebar_width", "model_overview", 200); if (sidebarWidth < 0) sidebarWidth = 200; if (sidebarWidth > mainSplitContainer.Width) sidebarWidth = mainSplitContainer.Width / 2; mainSplitContainer.SplitterDistance = sidebarWidth; sidebarWidth = wbContext.read_state("secondary_sidebar_width", "model_diagram", mainContentSplitContainer.Width - 200); if (mainContentSplitContainer.Width - sidebarWidth < 200) sidebarWidth = mainContentSplitContainer.Width - 200; if (sidebarWidth > mainContentSplitContainer.Width) sidebarWidth = mainContentSplitContainer.Width / 2; mainContentSplitContainer.SplitterDistance = sidebarWidth; NodeIdWrapper nodeId = wbOverview.get_root(); String caption; wbOverview.get_field(nodeId, (int)Overview.Columns.Label, out caption); String domain = caption + "-overview"; foreach (CollapsingPanel panel in panelList) { panel.Expanded = wbContext.read_state(panel.HeaderCaption + "-expanded", domain, panel.Expanded); View viewMode = (View)wbContext.read_state(panel.HeaderCaption + "-mode", domain, (int)GetViewMode(panel)); SetViewMode(panel, viewMode); switch (viewMode) { case View.LargeIcon: panel.ActiveCustomButton = 0; break; case View.Tile: panel.ActiveCustomButton = 1; break; case View.Details: panel.ActiveCustomButton = 2; break; } } } private void SaveFormState() { NodeIdWrapper nodeId = wbOverview.get_root(); String caption; wbOverview.get_field(nodeId, (int)Overview.Columns.Label, out caption); String domain = caption + "-overview"; foreach (CollapsingPanel panel in panelList) { wbContext.save_state(panel.HeaderCaption + "-expanded", domain, panel.Expanded); wbContext.save_state(panel.HeaderCaption + "-mode", domain, (int)GetViewMode(panel)); } } private void mainSplitContainer_SplitterMoved(object sender, SplitterEventArgs e) { if (splitterMovePending) { splitterMovePending = false; wbContext.save_state("sidebar_width", "model_overview", mainSplitContainer.SplitterDistance); } } private void mainContentSplitContainer_SplitterMoved(object sender, SplitterEventArgs e) { if (splitterMovePending) { splitterMovePending = false; wbContext.save_state("secondary_sidebar_width", "model_overview", mainSplitContainer.SplitterDistance); } } private void SplitterMoving(object sender, SplitterCancelEventArgs e) { // This event only comes up when the splitter is moved with the mouse (i.e. by the user). // We can use it to differentiate between user initiated and programmatic splitter changes. splitterMovePending = true; } private void ModelOverviewForm_FormClosing(object sender, FormClosingEventArgs e) { SaveFormState(); } /// <summary> /// Keeps an association between a node (id) and a listview for later quick access. /// Frees any resource links to the backend if a listview is removed. /// </summary> /// <param name="id"></param> private void cacheListview(String id, ListView listview) { if (listsByNode.ContainsKey(id)) { if (listsByNode[id] != listview) { listsByNode[id].SmallImageList = null; listsByNode[id].LargeImageList = null; listsByNode[id].Dispose(); listsByNode[id] = listview; } } else listsByNode[id] = listview; } private void ApplyColors(Control parent) { foreach (Control control in parent.Controls) { if (control is FlatTabControl) { FlatTabControl tabView = control as FlatTabControl; tabView.UpdateColors(); tabView.BackgroundColor = Conversions.GetApplicationColor(ApplicationColor.AppColorMainBackground, false); } else if (control is HeaderPanel) { HeaderPanel panel = control as HeaderPanel; panel.HeaderColor = Conversions.GetApplicationColor(ApplicationColor.AppColorPanelHeader, false); panel.ForeColor = Conversions.GetApplicationColor(ApplicationColor.AppColorPanelHeader, true); panel.HeaderColorFocused = Conversions.GetApplicationColor(ApplicationColor.AppColorPanelHeaderFocused, false); panel.ForeColorFocused = Conversions.GetApplicationColor(ApplicationColor.AppColorPanelHeaderFocused, true); } else if (control is ToolStrip) { ToolStrip toolStrip = control as ToolStrip; toolStrip.BackColor = Conversions.GetApplicationColor(ApplicationColor.AppColorPanelToolbar, false); toolStrip.ForeColor = Conversions.GetApplicationColor(ApplicationColor.AppColorPanelToolbar, true); } else if (control is TabPage) { TabPage page = control as TabPage; if (page.Parent is FlatTabControl) { FlatTabControl view = page.Parent as FlatTabControl; if (view.TabStyle == FlatTabControl.TabStyleType.BottomNormal) page.BackColor = Conversions.GetApplicationColor(ApplicationColor.AppColorPanelContentArea, false); } } ApplyColors(control); } } public override void Activate() { base.Activate(); if (secondarySidebarPanel.Controls.Count == 0) { Control secondarySidebar = wbContext.shared_secondary_sidebar(); secondarySidebarPanel.Controls.Add(secondarySidebar); secondarySidebar.Dock = DockStyle.Fill; } } #endregion } }