xenadmin/XenAdmin/Controls/XenSearch/QueryPanel.cs
Konstantina Chremmou edea57ebf3 Simplified the logic updating the TabPages.
- The new logic of adding/removing tabs in the ChangeToNewTabs method also fixes
  tab-title flickering when we move from the Home node to a disconnected host.
- Renamed enum members Tab.Overview to Tab.Search and Tab.Settings to Tab.General
  since the pages mapped to them are TabPageSearch and TabPageGeneral respectively.
- Do not call directly UpdateToolbarsCore but rather UpdateToolbars since the
  latter takes account of the queued requested updates.

Signed-off-by: Konstantina Chremmou <konstantina.chremmou@citrix.com>
2018-10-08 11:10:44 +01:00

759 lines
27 KiB
C#

/* Copyright (c) Citrix Systems, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* * Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using XenAdmin.CustomFields;
using XenAdmin.XenSearch;
using XenAPI;
using System.Resources;
using XenAdmin.Core;
using XenAdmin.Controls.CustomGridView;
using XenAdmin.Dialogs;
using XenAdmin.Model;
namespace XenAdmin.Controls.XenSearch
{
public class QueryPanel : GridView
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private const int ROW_HEIGHT = 27;
private const int FOLDER_ROW_HEIGHT = 14;
public static Brush TextBrush = SystemBrushes.ControlText;
public static Brush RedBrush = Brushes.Red;
public static Brush LinkBrush = Brushes.Blue;
public static Brush GreyBrush = SystemBrushes.GrayText;
public static Brush DarkGreyBrush = new SolidBrush(Color.FromArgb(255, 100, 100, 100));
private static readonly Font HeaderBoldFont = new Font(Program.DefaultFont.FontFamily, Program.DefaultFont.Size + 0.2f, FontStyle.Bold);
private static readonly Font HeaderSmallFont = new Font(Program.DefaultFont.FontFamily, Program.DefaultFont.Size);
private static MetricUpdater MetricUpdater;
private Search search;
public event Action SearchChanged;
private readonly UpdateManager listUpdateManager = new UpdateManager();
static readonly ResourceManager resources = new ResourceManager("XenAdmin.Controls.XenSearch.QueryPanel", typeof(QueryPanel).Assembly);
public QueryPanel()
{
this.SuspendLayout();
MetricUpdater = PropertyAccessorHelper.MetricUpdater; // new MetricUpdater();
MetricUpdater.MetricsUpdated += MetricsUpdated;
this.ResumeLayout(false);
base.HasLeftExpanders = false;
VerticalScroll.Visible = true;
SetupHeaderRow();
CustomFieldsManager.CustomFieldsChanged += CustomFields_CustomFieldsChanged;
listUpdateManager.Update += listUpdateManager_Update;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (MetricUpdater != null)
MetricUpdater.MetricsUpdated -= MetricsUpdated;
CustomFieldsManager.CustomFieldsChanged -= CustomFields_CustomFieldsChanged;
listUpdateManager.Update -= listUpdateManager_Update;
listUpdateManager.Dispose();
}
base.Dispose(disposing);
}
private void OnSearchChanged()
{
if (SearchChanged != null)
SearchChanged();
}
void CustomFields_CustomFieldsChanged()
{
SetupCustomColumns();
}
#region Column Handling Code
private GridHeaderRow GridHeaderRow = null;
private readonly Dictionary<String, bool> Columns = new Dictionary<String, bool>();
private static readonly String[] DEFAULT_COLUMNS = new String[]{ "name",
"cpu", "memory", "disks", "network", "ip", "ha", "uptime" };
private static bool IsDefaultColumn(String column)
{
return Array.IndexOf<String>(DEFAULT_COLUMNS, column) > -1;
}
private bool IsMovableColumn(string column)
{
if (GridHeaderRow == null || !GridHeaderRow.Items.ContainsKey(column))
return true;
GridHeaderItem item = GridHeaderRow.Items[column] as GridHeaderItem;
if (item == null)
return true;
return !item.Immovable;
}
private void ShowColumn(String column)
{
Columns[column] = true;
if (column == "ha" && !HelpersGUI.HAEnabledOnAtLeastOnePool)
{
HideColumn("ha");
return;
}
if (!GridHeaderRow.Columns.Contains(column))
{
if (IsDefaultColumn(column))
{
GridHeaderRow.AddItem(column, GetDefaultColumn(column));
}
else
{
String text = column.StartsWith(CustomFieldsManager.CUSTOM_FIELD) ?
column.Substring(CustomFieldsManager.CUSTOM_FIELD.Length) : column;
GridHeaderRow.AddItem(column, NewHeaderItem(text, SortOrder.Ascending, 100, 100));
}
}
}
private void HideColumn(String column)
{
Columns[column] = false;
GridHeaderRow.RemoveItem(column);
}
private void ToggleColumn(String column)
{
if (Columns[column])
HideColumn(column);
else
ShowColumn(column);
}
private void RemoveColumn(String column)
{
if (IsDefaultColumn(column))
return;
HideColumn(column);
Columns.Remove(column);
}
private void SetupHeaderRow()
{
GridHeaderRow = new GridHeaderRow(ROW_HEIGHT);
foreach (string s in DEFAULT_COLUMNS)
ShowColumn(s);
SetHeaderRow(GridHeaderRow);
}
private static GridItemBase GetDefaultColumn(string s)
{
if (s == "name")
{
GridHeaderItem nameHeaderItem = NewHeaderItem(Messages.OVERVIEW_NAME, SortOrder.Ascending, 250, 250);
nameHeaderItem.IsDefaultSortColumn = true;
nameHeaderItem.Immovable = true;
return nameHeaderItem;
}
if (s == "cpu")
{
return NewHeaderItem(Messages.OVERVIEW_CPU_USAGE, SortOrder.Descending, 115, 115);
}
if (s == "memory")
{
return NewHeaderItem(Messages.OVERVIEW_MEMORY_USAGE, SortOrder.Descending, 125, 125);
}
if (s == "disks")
{
int colWidth;
Int32.TryParse(resources.GetString("DISKS_OVERVIEW_COL_WIDTH"), out colWidth);
return NewGridHeaderItem(Messages.OVERVIEW_DISKS, SortOrder.Descending, colWidth, colWidth);
}
if (s == "network")
{
int colWidth;
Int32.TryParse(resources.GetString("NETWORK_OVERVIEW_COL_WIDTH"), out colWidth);
return NewGridHeaderItem(Messages.OVERVIEW_NETWORK, SortOrder.Descending, colWidth, colWidth);
}
if (s == "ha")
{
return NewHeaderItem(Messages.HA, SortOrder.Ascending, 120, 120);
}
if (s == "ip")
{
return NewHeaderItem(Messages.ADDRESS, SortOrder.Ascending, 120, 120);
}
if (s == "uptime")
{
return NewHeaderItem(Messages.UPTIME, SortOrder.Descending, 170, 170);
}
// dont be a muppet, only ask for a default column
return NewHeaderItem(s, SortOrder.Ascending, 100, 100);
}
private void ShowColumns(List<KeyValuePair<String, int>> columns)
{
List<String> columnKeys = new List<String>(Columns.Keys);
if (columns == null)
{
// Show all columns
foreach (String column in columnKeys)
ShowColumn(column);
foreach (CustomFieldDefinition definition in CustomFieldsManager.GetCustomFields())
ShowColumn(CustomFieldsManager.CUSTOM_FIELD + definition.Name);
return;
}
// Hide all columns which are currently visible but not in columns
// (do not hide default columns)
foreach (String column in columnKeys)
{
if (!IsMovableColumn(column))
continue;
if (columns.Exists(delegate(KeyValuePair<String, int> kvp)
{
return kvp.Key == column;
}))
continue;
HideColumn(column);
}
ShowColumn("ha"); // force decision to show ha
// Show appropriate columns
int i = 0;
foreach (GridHeaderItem item in HeaderRow.Items.Values)
{
if (!item.Immovable)
break;
i++;
}
foreach (KeyValuePair<String, int> column in columns)
{
if (!IsMovableColumn(column.Key))
continue;
String key =
(column.Key.StartsWith(CustomFieldsManager.CUSTOM_FIELD) || IsDefaultColumn(column.Key)) ?
column.Key : CustomFieldsManager.CUSTOM_FIELD + column.Key;
ShowColumn(key);
if (!HeaderRow.Items.ContainsKey(key))
continue;
GridHeaderItem item = HeaderRow.Items[key] as GridHeaderItem;
if (item == null)
continue;
// Make the column the correct width;
item.Width = column.Value;
// Move the column to the correct place;
GridHeaderRow.Columns.Remove(column.Key);
GridHeaderRow.Columns.Insert(i, column.Key);
i++;
}
}
public override void OpenChooseColumnsMenu(Point point)
{
ContextMenu contextMenu = new ContextMenu();
foreach (String column in Columns.Keys)
{
if (!IsMovableColumn(column))
continue;
if (column == "ha" && !Columns[column] && !HelpersGUI.HAEnabledOnAtLeastOnePool) // it's off, and we can't turn it on
continue;
String columnCopy = column;
MenuItem item = new MenuItem(GetI18nColumnName(column).Ellipsise(30));
item.Checked = Columns[column];
item.Enabled = IsMovableColumn(column);
item.Click += delegate(object sender, EventArgs e)
{
item.Checked = !item.Checked;
ToggleColumn(columnCopy);
Refresh();
};
contextMenu.MenuItems.Add(item);
}
contextMenu.Show(this, point);
}
public List<ToolStripMenuItem> GetChooseColumnsMenu()
{
List<ToolStripMenuItem> menuItems = new List<ToolStripMenuItem>();
foreach (String column in Columns.Keys)
{
if (!IsMovableColumn(column))
continue;
if (column == "ha" && !Columns[column] && !HelpersGUI.HAEnabledOnAtLeastOnePool) // it's off, and we can't turn it on
continue;
String columnCopy = column;
ToolStripMenuItem item = new ToolStripMenuItem(GetI18nColumnName(column).Ellipsise(30));
item.Checked = Columns[column];
item.Enabled = IsMovableColumn(column);
item.Click += delegate(object sender, EventArgs e)
{
item.Checked = !item.Checked;
ToggleColumn(columnCopy);
Refresh();
};
menuItems.Add(item);
}
return menuItems;
}
private static string GetI18nColumnName(string column)
{
switch (column)
{
case "name":
return Messages.OVERVIEW_NAME;
case "cpu":
return Messages.OVERVIEW_CPU_USAGE;
case "memory":
return Messages.OVERVIEW_MEMORY_USAGE;
case "disks":
return Messages.OVERVIEW_DISKS;
case "network":
return Messages.OVERVIEW_NETWORK;
case "ha":
return Messages.HA;
case "ip":
return Messages.ADDRESS;
case "uptime":
return Messages.UPTIME;
default:
return column.StartsWith(CustomFieldsManager.CUSTOM_FIELD) ?
column.Substring(CustomFieldsManager.CUSTOM_FIELD.Length) : column;
}
}
/// <summary>
/// Add and/or remove custom columns to the grid.
/// </summary>
private void SetupCustomColumns()
{
List<CustomFieldDefinition> customFieldDefinitions = CustomFieldsManager.GetCustomFields();
// Did the user remove a custom field?
String[] columns = new String[Columns.Keys.Count];
Columns.Keys.CopyTo(columns, 0);
foreach (String column in columns)
{
// Skip default columns
if (IsDefaultColumn(column))
continue;
// Does the custom column in table exist in the custom fields
// collection? If not remove.
if (!customFieldDefinitions.Exists(delegate(CustomFieldDefinition definition)
{
return definition.Name == column;
}))
RemoveColumn(column);
}
// Add any new columns
foreach (CustomFieldDefinition customFieldDefinition in customFieldDefinitions)
{
if (Columns.ContainsKey(customFieldDefinition.Name))
continue;
ShowColumn(CustomFieldsManager.CUSTOM_FIELD + customFieldDefinition.Name);
}
BuildList();
}
#endregion
[Browsable(false)]
public Search Search
{
set
{
search = value;
if (search != null)
{
ShowColumns(search.Columns);
Sorting = search.Sorting;
BuildList();
}
}
}
internal override void Sort()
{
LastClickedRow = null;
OnSearchChanged();
}
public Sort[] Sorting
{
get
{
List<Sort> sorting = new List<Sort>();
foreach (SortParams sp in HeaderRow.CompareOrder)
sorting.Add(new Sort(sp.Column, sp.SortOrder == SortOrder.Ascending));
return sorting.ToArray();
}
set
{
List<SortParams> order = new List<SortParams>();
foreach (Sort sort in value)
order.Add(new SortParams(sort.Column, sort.Ascending ? SortOrder.Ascending : SortOrder.Descending));
HeaderRow.UpdateCellSorts(order);
}
}
// Are any of the sort columns based on one of the performance metrics?
public bool SortingByMetrics
{
get
{
foreach (SortParams sp in HeaderRow.CompareOrder)
{
if (sp.Column == "cpu" || sp.Column == "memory" || sp.Column == "disks" ||
sp.Column == "network" || sp.Column == "uptime")
return true;
}
return false;
}
}
private void listUpdateManager_Update(object sender, EventArgs e)
{
GridRow root = new GridRow(-1);
RowGroupAcceptor acceptor = new RowGroupAcceptor(root);
CollectionAcceptor ca = new CollectionAcceptor();
bool addedAny = search.PopulateAdapters(acceptor, ca);
Program.Invoke(Program.MainWindow, delegate
{
SaveRowStates();
Clear();
if (!addedAny)
{
AddNoResultsRow();
}
foreach (GridRow row in root.Rows)
{
AddRow(row);
}
MetricUpdater.SetXenObjects(ca.XenObjects.ToArray());
RestoreRowStates();
Refresh();
});
}
public void BuildList()
{
Program.AssertOnEventThread();
if (search == null)
return;
listUpdateManager.RequestUpdate();
}
private class RowGroupAcceptor : IAcceptGroups
{
private readonly GridRow _gridRow;
public RowGroupAcceptor(GridRow gridRow)
{
_gridRow = gridRow;
}
public IAcceptGroups Add(Grouping grouping, object group, int indent)
{
GridRow gridRow = CreateRow(grouping, group, indent);
if (gridRow != null)
{
_gridRow.AddRow(gridRow);
return new RowGroupAcceptor(gridRow);
}
return null;
}
public void FinishedInThisGroup(bool defaultExpand)
{
}
}
private void AddNoResultsRow()
{
GridRow row = new GridRow(ROW_HEIGHT);
GridStringItem resultsItem = new GridStringItem(Messages.OVERVIEW_NO_RESULTS, HorizontalAlignment.Left, VerticalAlignment.Middle, false, false, TextBrush, Program.DefaultFont, 6);
row.AddItem("name", resultsItem);
AddRow(row);
}
private static GridHeaderArrayItem NewGridHeaderItem(string label, SortOrder defaultSortOrder, int width, int minwidth)
{
return new GridHeaderArrayItem(
new GridStringItem[] { new GridStringItem(label, HorizontalAlignment.Center, VerticalAlignment.Middle, false, false, TextBrush, HeaderBoldFont, GreyBrush, HeaderBoldFont),
new GridStringItem(Messages.OVERVIEW_UNITS, HorizontalAlignment.Center, VerticalAlignment.Middle, false, false, TextBrush, HeaderSmallFont, GreyBrush, HeaderSmallFont) },
HorizontalAlignment.Center, VerticalAlignment.Middle, TextBrush, HeaderBoldFont, defaultSortOrder, width, minwidth);
}
private static GridHeaderItem NewHeaderItem(string label, SortOrder defaultSortOrder, int width, int minwidth)
{
return new GridHeaderItem(HorizontalAlignment.Center, VerticalAlignment.Middle,
TextBrush, HeaderBoldFont, label, defaultSortOrder, width, minwidth, GreyBrush);
}
private static void AddCustomFieldsToRow(IXenObject o, GridRow row)
{
foreach (CustomFieldDefinition customFieldDefinition in CustomFieldsManager.GetCustomFields())
{
GridStringItem customFieldItem = new GridStringItem(
new CustomFieldWrapper(o, customFieldDefinition),
HorizontalAlignment.Center, VerticalAlignment.Middle,
false, false, TextBrush, Program.DefaultFont,
new EventHandler(delegate
{
using (PropertiesDialog dialog = new PropertiesDialog(o))
{
dialog.SelectCustomFieldsEditPage();
dialog.ShowDialog();
}
}));
row.AddItem(CustomFieldsManager.CUSTOM_FIELD + customFieldDefinition.Name, customFieldItem);
}
}
private static GridVerticalArrayItem NewDoubleRowItem(Grouping grouping, object group)
{
string line1 = (grouping is BoolGrouping ? (grouping as BoolGrouping).GroupingName2(group) : grouping.GroupingName); // exception for Boolean groups: CA-165366
object line2 = (group is DateTime ? group : grouping.GetGroupName(group)); // exception for DateTime: CA-46983
List<GridStringItem> items = new List<GridStringItem>();
if (line1 != null)
items.Add(new GridStringItem(line1, HorizontalAlignment.Left, VerticalAlignment.Middle, false, true, TextBrush, Program.DefaultFont));
if (line2 != null)
items.Add(new GridStringItem(line2, HorizontalAlignment.Left, VerticalAlignment.Middle, false, false, DarkGreyBrush, Program.DefaultFont));
return new GridVerticalArrayItem(items.ToArray(), false);
}
private static GridRow NewGroupRow(string opaqueref, object tag, int rowHeight, int priority)
{
GridRow row = new GridRow(rowHeight);
row.OpaqueRef = opaqueref;
row.Tag = tag;
row.Expanded = true;
row.Priority = priority;
return row;
}
private static GridHorizontalArrayItem NewNameItem(GridItemBase iconItem, GridItemBase dataItem, int iconWidth, int indent)
{
GridTreeExpanderItem expander = new GridTreeExpanderItem();
GridItemBase[] items = { expander, iconItem, dataItem };
int[] widths = { 12, iconWidth };
return new GridHorizontalArrayItem(indent, items, widths, false);
}
private static GridRow CreateRow(Grouping grouping, Object o, int indent)
{
IXenObject ixmo = o as IXenObject;
if (ixmo != null)
{
bool isFolderRow = (o is Folder);
GridRow _row = NewGroupRow(ixmo.opaque_ref, ixmo, isFolderRow ? FOLDER_ROW_HEIGHT : ROW_HEIGHT, 0);
foreach (ColumnNames column in Enum.GetValues(typeof(ColumnNames)))
{
GridItemBase item = ColumnAccessors.Get(column).GetGridItem(ixmo);
if (item != null)
{
if (column == XenAdmin.XenSearch.ColumnNames.name)
{
EventHandler onDoubleClickDelegate = isFolderRow ?
(EventHandler)delegate
{
Program.MainWindow.DoSearch(Search.SearchForFolder(ixmo.opaque_ref));
} :
(EventHandler)delegate
{
if (Program.MainWindow.SelectObject(ixmo)
&& Program.MainWindow.TheTabControl.TabPages.Contains(Program.MainWindow.TabPageGeneral))
Program.MainWindow.SwitchToTab(MainWindow.Tab.General);
};
GridImageItem _statusItem = new GridImageItem(
"foo",
new ImageDelegate(delegate()
{
return Images.GetImage16For(ixmo);
}),
HorizontalAlignment.Left, VerticalAlignment.Top, true,
onDoubleClickDelegate);
_row.AddItem("name", NewNameItem(_statusItem, item, 16, indent));
}
else
_row.AddItem(column.ToString(), item);
}
}
AddCustomFieldsToRow(ixmo, _row);
return _row;
}
if (grouping == null)
return null;
GridRow row = NewGroupRow(String.Format("{0}: {1}", grouping.GroupingName, o), null, ROW_HEIGHT, 0);
GridImageItem statusItem = new GridImageItem(
grouping.GroupingName,
new ImageDelegate(delegate()
{
return Images.GetImage16For(grouping.GetGroupIcon(o));
}),
HorizontalAlignment.Left, VerticalAlignment.Top, true);
GridVerticalArrayItem nameItem = NewDoubleRowItem(grouping, o);
row.AddItem("name", NewNameItem(statusItem, nameItem, 16, indent));
return row;
}
public override bool IsDraggableRow(GridRow row)
{
IXenObject o = row.Tag as IXenObject;
return o != null;
}
private void MetricsUpdated(object o, EventArgs e)
{
Program.Invoke(Program.MainWindow, delegate
{
if (SortingByMetrics)
Sort();
else
Refresh();
});
}
public static void PanelShown()
{
MetricUpdater.Start();
}
public static void PanelHidden()
{
MetricUpdater.Pause();
}
internal static void Prod()
{
MetricUpdater.Prod();
}
}
public class CollectionAcceptor : IAcceptGroups
{
public List<IXenObject> XenObjects = new List<IXenObject>();
public IAcceptGroups Add(Grouping grouping, object group, int indent)
{
if (group is Host || group is VM)
XenObjects.Add((IXenObject)group);
return this;
}
public void FinishedInThisGroup(bool defaultExpand)
{
}
}
}