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
}
}