mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-11-25 06:16:37 +01:00
d7b519a53c
Signed-off-by: Konstantina Chremmou <Konstantina.Chremmou@cloud.com>
623 lines
20 KiB
C#
623 lines
20 KiB
C#
/* Copyright (c) Cloud Software Group, Inc.
|
|
*
|
|
* 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.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Windows.Forms;
|
|
|
|
namespace XenAdmin.Controls
|
|
{
|
|
public class CustomListRow : IDisposable
|
|
{
|
|
private Color ForeColor_ = SystemColors.ControlText;
|
|
private Color BackColor_ = SystemColors.Control;
|
|
private Color BorderColor_ = SystemColors.ActiveBorder;
|
|
private Pen BorderPen = null;
|
|
private Pen ForePen = null;
|
|
|
|
public Color SelectedBackColor_ = Color.Black;
|
|
private Brush BackBrush = null;
|
|
private Brush SelectedBackBrush = null;
|
|
|
|
public bool ShowBorder = true;
|
|
public bool Enabled = true;
|
|
public bool DrawChildren = true;
|
|
private bool _selected = false;
|
|
public bool Selectable = true;
|
|
public bool DrawGroupBox = true;
|
|
public bool ShowOnlyHorizBorder = false;
|
|
|
|
public int Indent = 13; // left-indent to any child rows
|
|
public float BorderWidth = 1.0f;
|
|
public Padding Padding = new Padding(0, 3, 0, 3);
|
|
|
|
public List<ToolStripMenuItem> MenuItems = new List<ToolStripMenuItem>();
|
|
public ToolStripMenuItem DefaultMenuItem;
|
|
|
|
public CustomListRow Parent;
|
|
public CustomListPanel ParentPanel;
|
|
|
|
public List<CustomListRow> Children = new List<CustomListRow>();
|
|
public List<CustomListItem> Items = new List<CustomListItem>();
|
|
|
|
/// <summary>
|
|
/// Construct a header.
|
|
/// </summary>
|
|
public CustomListRow(object tag, Color back, Color fore, Color border, Font font)
|
|
{
|
|
this.BackColor = back;
|
|
this.ForeColor = fore;
|
|
this.BorderColor = border;
|
|
this.SelectedBackColor = back;
|
|
this.DrawGroupBox = false;
|
|
this.ShowOnlyHorizBorder = true;
|
|
|
|
CustomListItem name = new CustomListItem(tag, font, fore, new Padding(10, 3, 3, 3));
|
|
|
|
AddItem(name);
|
|
|
|
init();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Construct child rows.
|
|
/// </summary>
|
|
public CustomListRow(Color back, params CustomListItem[] items)
|
|
{
|
|
Selectable = true;
|
|
BackColor = back;
|
|
this.SelectedBackColor = back;
|
|
ShowBorder = false;
|
|
foreach (CustomListItem item in items)
|
|
{
|
|
AddItem(item);
|
|
}
|
|
|
|
init();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Construct fixed row.
|
|
/// </summary>
|
|
public CustomListRow(Color back, int height)
|
|
{
|
|
Selectable = true;
|
|
BackColor = back;
|
|
this.SelectedBackColor = back;
|
|
ShowBorder = false;
|
|
this.Padding = new Padding(0, 0, 0, height);
|
|
|
|
init();
|
|
}
|
|
|
|
private void init()
|
|
{
|
|
RecalculateBorderPen();
|
|
RecalculateForePen();
|
|
RecalculateBackBrushes();
|
|
}
|
|
|
|
public Color ForeColor
|
|
{
|
|
get { return ForeColor_; }
|
|
set
|
|
{
|
|
ForeColor_ = value;
|
|
RecalculateForePen();
|
|
}
|
|
}
|
|
|
|
public Color BackColor
|
|
{
|
|
get { return BackColor_; }
|
|
set
|
|
{
|
|
BackColor_ = value;
|
|
RecalculateBorderPen();
|
|
}
|
|
}
|
|
|
|
public Color BorderColor
|
|
{
|
|
get { return BorderColor_; }
|
|
set
|
|
{
|
|
BorderColor_ = value;
|
|
RecalculateBorderPen();
|
|
}
|
|
}
|
|
|
|
private void RecalculateBorderPen()
|
|
{
|
|
if (BorderPen != null)
|
|
BorderPen.Dispose();
|
|
BorderPen = new Pen(BorderColor, BorderWidth);
|
|
}
|
|
|
|
private void RecalculateForePen()
|
|
{
|
|
if (ForePen != null)
|
|
ForePen.Dispose();
|
|
ForePen = new Pen(ForeColor);
|
|
}
|
|
|
|
public Color SelectedBackColor
|
|
{
|
|
get { return SelectedBackColor_; }
|
|
set
|
|
{
|
|
SelectedBackColor_ = value;
|
|
RecalculateBackBrushes();
|
|
}
|
|
}
|
|
|
|
private void RecalculateBackBrushes()
|
|
{
|
|
if (BackBrush != null)
|
|
BackBrush.Dispose();
|
|
if (SelectedBackBrush != null)
|
|
SelectedBackBrush.Dispose();
|
|
BackBrush = new SolidBrush(BackColor);
|
|
SelectedBackBrush = new SolidBrush(SelectedBackColor);
|
|
}
|
|
|
|
public bool Selected
|
|
{
|
|
get
|
|
{
|
|
return _selected;
|
|
}
|
|
set
|
|
{
|
|
_selected = value;
|
|
if (!value)
|
|
foreach (CustomListRow child in Children)
|
|
child.Selected = false;
|
|
}
|
|
}
|
|
|
|
public int RowAndChildrenHeight
|
|
{
|
|
get
|
|
{
|
|
int height = Padding.Vertical + RowHeight;
|
|
|
|
if (DrawChildren)
|
|
{
|
|
foreach (CustomListRow child in Children)
|
|
{
|
|
height += child.RowAndChildrenHeight;
|
|
}
|
|
}
|
|
|
|
return height;
|
|
}
|
|
}
|
|
|
|
public int RowHeight
|
|
{
|
|
get
|
|
{
|
|
int height = 0;
|
|
int headWidth = HeaderWidth;
|
|
for (int i = 0; i < Items.Count; i++)
|
|
{
|
|
CustomListItem item = Items[i];
|
|
Size cSize = item.WrappedSize(headWidth);
|
|
if (cSize.Height > height)
|
|
{
|
|
height = cSize.Height;
|
|
}
|
|
if(ParentPanel.level1ColWidths != null && i < ParentPanel.level1ColWidths.Length)
|
|
headWidth -= ParentPanel.level1ColWidths[i];
|
|
}
|
|
return height;
|
|
}
|
|
}
|
|
|
|
public int HeaderWidth
|
|
{
|
|
get
|
|
{
|
|
if (Parent != null)
|
|
return Parent.HeaderWidth - (Padding.Horizontal + Indent);
|
|
else
|
|
return ParentPanel.Width - (ParentPanel.Padding.Horizontal + Padding.Horizontal);
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
ForePen?.Dispose();
|
|
BorderPen?.Dispose();
|
|
BackBrush?.Dispose();
|
|
SelectedBackBrush?.Dispose();
|
|
|
|
foreach (var row in Children)
|
|
{
|
|
row.Dispose();
|
|
}
|
|
}
|
|
|
|
public void DrawSelf(Graphics g, Rectangle bounds)
|
|
{
|
|
int rowHeight = RowHeight;
|
|
int headWidth = HeaderWidth;
|
|
|
|
using (Brush ParentBackBrush = new SolidBrush(ParentPanel.BackColor))
|
|
{
|
|
g.FillRectangle(ParentBackBrush, bounds);
|
|
}
|
|
|
|
int top = bounds.Top + Padding.Top;
|
|
int left = bounds.Left + Padding.Left;
|
|
|
|
for(int i = 0; i < Items.Count; i++)
|
|
{
|
|
CustomListItem item = Items[i];
|
|
Size itemSize = item.PreferredSize;
|
|
|
|
int itemTop = top;
|
|
|
|
if ((item.Anchor & AnchorStyles.Bottom) > 0)
|
|
{
|
|
itemTop += rowHeight - itemSize.Height;
|
|
}
|
|
else if ((item.Anchor & AnchorStyles.Top) == 0)
|
|
{
|
|
itemTop += Convert.ToInt32(Math.Ceiling(Convert.ToDouble(rowHeight - itemSize.Height) / 2.0d));
|
|
}
|
|
|
|
Rectangle itemBounds;
|
|
if (Parent != null && (i == Items.Count - 1 || Items[i+1].InCorrectColumn))
|
|
{
|
|
int colWidth = 1;
|
|
if (ParentPanel.level1ColWidths.Length > i)
|
|
{
|
|
colWidth = ParentPanel.level1ColWidths[i];
|
|
}
|
|
itemBounds = new Rectangle(new Point(left, itemTop), item.WrappedSize(headWidth - (left - bounds.Left)));
|
|
left += colWidth;
|
|
}
|
|
else
|
|
{
|
|
itemBounds = new Rectangle(left, itemTop, itemSize.Width, itemSize.Height);
|
|
left += itemSize.Width;
|
|
}
|
|
|
|
if (itemBounds.Right > bounds.Right - Padding.Right)
|
|
itemBounds.Width -= itemBounds.Right - (bounds.Right - Padding.Right);
|
|
if (itemBounds.Bottom > bounds.Bottom - Padding.Bottom)
|
|
itemBounds.Height -= itemBounds.Bottom - (bounds.Bottom - Padding.Bottom);
|
|
|
|
if (i > 0 && Selected && !string.IsNullOrEmpty(item.Tag.ToString()))
|
|
{
|
|
if ((i < Items.Count - 1 && !Items[i + 1].InCorrectColumn))
|
|
{
|
|
g.FillRectangle(SystemBrushes.Highlight, new Rectangle(itemBounds.Left - 2, itemBounds.Top - 3, itemBounds.Width + Items[i + 1].PreferredSize.Width + 4, Items[i + 1].PreferredSize.Height + 2));
|
|
}
|
|
else if (item.InCorrectColumn)
|
|
{
|
|
g.FillRectangle(SystemBrushes.Highlight, new Rectangle(itemBounds.Left - 2, itemBounds.Top - 1, itemBounds.Width + 4, itemBounds.Height));
|
|
}
|
|
}
|
|
|
|
item.DrawSelf(g, itemBounds, Selected && i > 0 && !(i < Items.Count - 1 && !Items[i + 1].InCorrectColumn));
|
|
}
|
|
|
|
top += rowHeight + Padding.Bottom;
|
|
|
|
if (DrawChildren)
|
|
{
|
|
foreach (CustomListRow child in Children)
|
|
{
|
|
int childHeight = child.RowAndChildrenHeight;
|
|
Rectangle childBounds = new Rectangle(bounds.Left + Padding.Left + Indent, top, bounds.Width - (Padding.Horizontal + Indent), childHeight);
|
|
child.DrawSelf(g, childBounds);
|
|
top += childHeight;
|
|
}
|
|
}
|
|
|
|
if (ShowBorder)
|
|
{
|
|
if (ShowOnlyHorizBorder)
|
|
{
|
|
int w = 1;
|
|
if(ParentPanel.level1ColWidths.Length >= 2)
|
|
w = 450;
|
|
|
|
using (Brush brush = new LinearGradientBrush(Point.Empty, new Point(w, 0), BorderColor, BackColor))
|
|
{
|
|
g.FillRectangle(brush, bounds.Left, bounds.Top + rowHeight, w, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g.DrawRectangle(BorderPen, bounds.Left, bounds.Top, headWidth, rowHeight);
|
|
}
|
|
|
|
if (DrawGroupBox)
|
|
g.DrawRectangle(BorderPen, bounds.Left, bounds.Top, headWidth, RowAndChildrenHeight);
|
|
}
|
|
}
|
|
|
|
public void AddItem(CustomListItem item)
|
|
{
|
|
item.Row = this;
|
|
this.Items.Add(item);
|
|
}
|
|
|
|
public void AddChild(CustomListRow row)
|
|
{
|
|
row.Parent = this;
|
|
if (this.Children.Count == 0)
|
|
row.Padding.Top = 10;
|
|
row.ParentPanel = ParentPanel;
|
|
Children.Add(row);
|
|
}
|
|
|
|
internal CustomListRow SelectChild(Point point, out Point rowRelativePoint)
|
|
{
|
|
int top = Padding.Vertical + RowHeight;
|
|
|
|
foreach (CustomListRow child in Children)
|
|
{
|
|
int height = child.RowAndChildrenHeight;
|
|
|
|
if (point.Y >= top && point.Y < top + height)
|
|
{
|
|
int leftPos = point.X - Padding.Left - Indent;
|
|
rowRelativePoint = new Point(leftPos, point.Y - top);
|
|
if (leftPos < 0) // off the left hand side of the subrow
|
|
return null;
|
|
return child;
|
|
}
|
|
|
|
top += height;
|
|
}
|
|
|
|
rowRelativePoint = new Point();
|
|
return null;
|
|
}
|
|
|
|
private CustomListItem GetItemFromPosition(Point point, out Point itemRelativePoint)
|
|
{
|
|
int left = Padding.Left;
|
|
for (int i = 0; i < ParentPanel.level1ColWidths.Length; i++)
|
|
{
|
|
if (i >= Items.Count)
|
|
continue;
|
|
|
|
CustomListItem item = Items[i];
|
|
|
|
if (item == null)
|
|
continue;
|
|
|
|
int width = item.itemBorder.Horizontal + ParentPanel.level1ColWidths[i];
|
|
|
|
if (point.X >= left && point.X < left + width)
|
|
{
|
|
itemRelativePoint = new Point(point.X - left, point.Y - Padding.Top);
|
|
return item;
|
|
}
|
|
|
|
left += width;
|
|
}
|
|
|
|
itemRelativePoint = new Point();
|
|
return null;
|
|
}
|
|
|
|
public void OnMouseClick(MouseEventArgs e, Point point)
|
|
{
|
|
if (point.Y > RowHeight + Padding.Vertical)
|
|
{
|
|
Point rowRelativePoint;
|
|
CustomListRow row = SelectChild(point, out rowRelativePoint);
|
|
if (row == null)
|
|
return;
|
|
|
|
row.OnMouseClick(e, rowRelativePoint);
|
|
}
|
|
else
|
|
{
|
|
Point itemRelativePoint;
|
|
CustomListItem item = GetItemFromPosition(point, out itemRelativePoint);
|
|
if (item == null)
|
|
return;
|
|
|
|
item.OnMouseClick(e, itemRelativePoint);
|
|
}
|
|
}
|
|
|
|
public void OnMouseDoubleClick(MouseEventArgs e, Point point)
|
|
{
|
|
if (e.Button == MouseButtons.Left)
|
|
{
|
|
if (DefaultMenuItem != null)
|
|
DefaultMenuItem.PerformClick();
|
|
}
|
|
|
|
if (point.Y > RowHeight + Padding.Vertical)
|
|
{
|
|
Point rowRelativePoint;
|
|
CustomListRow row = SelectChild(point, out rowRelativePoint);
|
|
if (row == null)
|
|
return;
|
|
|
|
row.OnMouseDoubleClick(e, rowRelativePoint);
|
|
}
|
|
else
|
|
{
|
|
Point itemRelativePoint;
|
|
CustomListItem item = GetItemFromPosition(point, out itemRelativePoint);
|
|
if (item == null)
|
|
return;
|
|
|
|
item.OnMouseDoubleClick(e, itemRelativePoint);
|
|
}
|
|
}
|
|
|
|
public void OnMouseDown(Point point)
|
|
{
|
|
if (point.Y > RowHeight + Padding.Vertical)
|
|
{
|
|
Point rowRelativePoint;
|
|
CustomListRow row = SelectChild(point, out rowRelativePoint);
|
|
if (row == null)
|
|
{
|
|
ParentPanel.ClearSelection();
|
|
return;
|
|
}
|
|
|
|
row.OnMouseDown(rowRelativePoint);
|
|
}
|
|
else
|
|
{
|
|
if (!Selectable)
|
|
return;
|
|
|
|
ParentPanel.ClearSelection();
|
|
|
|
Selected = true;
|
|
ParentPanel.SelectedRow = this;
|
|
ParentPanel.Invalidate();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This code is almost identical to the code for tracking mouse movements
|
|
* in the CustomGridRow.
|
|
*/
|
|
|
|
private CustomListItem currentItem;
|
|
private CustomListRow currentRow;
|
|
|
|
public virtual void OnMouseEnter(Point point)
|
|
{
|
|
System.Diagnostics.Trace.Assert(currentItem == null && currentRow == null);
|
|
|
|
if (point.Y > RowHeight + Padding.Vertical)
|
|
{
|
|
Point rowRelativePoint;
|
|
currentRow = SelectChild(point, out rowRelativePoint);
|
|
if (currentRow == null)
|
|
return;
|
|
|
|
currentRow.OnMouseEnter(rowRelativePoint);
|
|
}
|
|
else
|
|
{
|
|
Point itemRelativePoint;
|
|
currentItem = GetItemFromPosition(point, out itemRelativePoint);
|
|
if (currentItem == null)
|
|
return;
|
|
|
|
currentItem.OnMouseEnter(itemRelativePoint);
|
|
}
|
|
}
|
|
|
|
public virtual void OnMouseMove(Point point)
|
|
{
|
|
System.Diagnostics.Trace.Assert(currentItem == null || currentRow == null);
|
|
|
|
if (point.Y > RowHeight + Padding.Vertical)
|
|
{
|
|
if (currentItem != null)
|
|
{
|
|
currentItem.OnMouseLeave();
|
|
currentItem = null;
|
|
}
|
|
|
|
Point rowRelativePoint;
|
|
CustomListRow row = SelectChild(point, out rowRelativePoint);
|
|
|
|
if (row != null && currentRow == row)
|
|
{
|
|
currentRow.OnMouseMove(rowRelativePoint);
|
|
return;
|
|
}
|
|
|
|
if (currentRow != null)
|
|
currentRow.OnMouseLeave();
|
|
|
|
currentRow = row;
|
|
|
|
if (currentRow != null)
|
|
currentRow.OnMouseEnter(rowRelativePoint);
|
|
}
|
|
else
|
|
{
|
|
if(currentRow != null)
|
|
{
|
|
currentRow.OnMouseLeave();
|
|
currentRow = null;
|
|
}
|
|
|
|
Point itemRelativePoint;
|
|
CustomListItem item = GetItemFromPosition(point, out itemRelativePoint);
|
|
|
|
if (item != null && currentItem == item)
|
|
{
|
|
currentItem.OnMouseMove(itemRelativePoint);
|
|
return;
|
|
}
|
|
|
|
if (currentItem != null)
|
|
currentItem.OnMouseLeave();
|
|
|
|
currentItem = item;
|
|
|
|
if (currentItem != null)
|
|
currentItem.OnMouseEnter(itemRelativePoint);
|
|
}
|
|
}
|
|
|
|
public virtual void OnMouseLeave()
|
|
{
|
|
System.Diagnostics.Trace.Assert(currentItem == null || currentRow == null);
|
|
|
|
if (currentItem != null)
|
|
{
|
|
currentItem.OnMouseLeave();
|
|
currentItem = null;
|
|
}
|
|
|
|
if (currentRow != null)
|
|
{
|
|
currentRow.OnMouseLeave();
|
|
currentRow = null;
|
|
}
|
|
}
|
|
}
|
|
}
|