416 lines
14 KiB
C#
416 lines
14 KiB
C#
using CaeGlobals;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Windows.Forms;
|
|
|
|
namespace AutocompleteMenuNS
|
|
{
|
|
[System.ComponentModel.ToolboxItem(false)]
|
|
public class AutocompleteListView : UserControl, IAutocompleteListView
|
|
{
|
|
private readonly ToolTip toolTip = new ToolTip();
|
|
public int HighlightedItemIndex { get; set; }
|
|
private int oldItemCount;
|
|
private int selectedItemIndex = -1;
|
|
private IList<AutocompleteItem> visibleItems;
|
|
private MouseHook mouseHook;
|
|
private bool inside;
|
|
|
|
/// <summary>
|
|
/// Duration (ms) of tooltip showing
|
|
/// </summary>
|
|
public int ToolTipDuration { get; set; }
|
|
|
|
/// <summary>
|
|
/// Occurs when user selected item for inserting into text
|
|
/// </summary>
|
|
public event EventHandler ItemSelected;
|
|
|
|
/// <summary>
|
|
/// Occurs when current hovered item is changing
|
|
/// </summary>
|
|
public event EventHandler<HoveredEventArgs> ItemHovered;
|
|
|
|
/// <summary>
|
|
/// Occurs when mouse click is outside the controls area
|
|
/// </summary>
|
|
public event EventHandler OutsideMouseClick;
|
|
|
|
/// <summary>
|
|
/// Colors
|
|
/// </summary>
|
|
public Colors Colors { get; set; }
|
|
|
|
internal AutocompleteListView()
|
|
{
|
|
SetStyle(
|
|
ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
|
|
base.Font = new Font(FontFamily.GenericSansSerif, 9);
|
|
ItemHeight = Font.Height + 2;
|
|
VerticalScroll.SmallChange = ItemHeight;
|
|
BackColor = Color.White;
|
|
LeftPadding = 18;
|
|
ToolTipDuration = 3000;
|
|
Colors = new Colors();
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
toolTip.Dispose();
|
|
}
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
|
|
private int itemHeight;
|
|
|
|
public int ItemHeight
|
|
{
|
|
get { return itemHeight; }
|
|
set {
|
|
itemHeight = value;
|
|
VerticalScroll.SmallChange = value;
|
|
oldItemCount = -1;
|
|
AdjustScroll();
|
|
}
|
|
}
|
|
|
|
public override Font Font
|
|
{
|
|
get { return base.Font; }
|
|
set
|
|
{
|
|
base.Font = value;
|
|
ItemHeight = Font.Height + 2;
|
|
}
|
|
}
|
|
|
|
public int LeftPadding { get; set; }
|
|
|
|
public ImageList ImageList { get; set; }
|
|
|
|
public IList<AutocompleteItem> VisibleItems
|
|
{
|
|
get { return visibleItems; }
|
|
set
|
|
{
|
|
visibleItems = value;
|
|
SelectedItemIndex = -1;
|
|
AdjustScroll();
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
public int SelectedItemIndex
|
|
{
|
|
get { return selectedItemIndex; }
|
|
set
|
|
{
|
|
AutocompleteItem item = null;
|
|
if (value >= 0 && value < VisibleItems.Count)
|
|
item = VisibleItems[value];
|
|
|
|
selectedItemIndex = value;
|
|
|
|
OnItemHovered(new HoveredEventArgs() { Item = item });
|
|
|
|
if (item != null)
|
|
{
|
|
ShowToolTip(item, this);
|
|
ScrollToSelected();
|
|
}
|
|
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
private void OnItemHovered(HoveredEventArgs e)
|
|
{
|
|
if (ItemHovered != null)
|
|
ItemHovered(this, e);
|
|
}
|
|
|
|
private void AdjustScroll()
|
|
{
|
|
if (VisibleItems == null)
|
|
return;
|
|
if (oldItemCount == VisibleItems.Count)
|
|
return;
|
|
// Height
|
|
int needHeight = ItemHeight*VisibleItems.Count + 1;
|
|
Height = Math.Min(needHeight, MaximumSize.Height);
|
|
AutoScrollMinSize = new Size(0, needHeight);
|
|
oldItemCount = VisibleItems.Count;
|
|
// Width
|
|
int maxWidth = 0;
|
|
using (Graphics g = this.CreateGraphics())
|
|
{
|
|
foreach (var item in VisibleItems)
|
|
{
|
|
string text = item.ToString();
|
|
SizeF size = g.MeasureString(text, Font);
|
|
maxWidth = Math.Max(maxWidth, (int)Math.Ceiling(size.Width));
|
|
}
|
|
}
|
|
int padding = 35;
|
|
Width = Math.Min(MaximumSize.Width, maxWidth + padding);
|
|
}
|
|
|
|
private void ScrollToSelected()
|
|
{
|
|
int y = SelectedItemIndex*ItemHeight - VerticalScroll.Value;
|
|
if (y < 0)
|
|
VerticalScroll.Value = SelectedItemIndex*ItemHeight;
|
|
if (y > ClientSize.Height - ItemHeight)
|
|
VerticalScroll.Value = Math.Min(VerticalScroll.Maximum,
|
|
SelectedItemIndex*ItemHeight - ClientSize.Height + ItemHeight);
|
|
//some magic for update scrolls
|
|
AutoScrollMinSize -= new Size(1, 0);
|
|
AutoScrollMinSize += new Size(1, 0);
|
|
}
|
|
|
|
public Rectangle GetItemRectangle(int itemIndex)
|
|
{
|
|
var y = itemIndex * ItemHeight - VerticalScroll.Value;
|
|
return new Rectangle(0, y, ClientSize.Width - 1, ItemHeight - 1);
|
|
}
|
|
|
|
protected override void OnPaintBackground(PaintEventArgs e)
|
|
{
|
|
e.Graphics.Clear(Colors.BackColor);
|
|
}
|
|
|
|
protected override void OnPaint(PaintEventArgs e)
|
|
{
|
|
bool rtl = RightToLeft == RightToLeft.Yes;
|
|
AdjustScroll();
|
|
int startI = VerticalScroll.Value/ItemHeight - 1;
|
|
int finishI = (VerticalScroll.Value + ClientSize.Height)/ItemHeight + 1;
|
|
startI = Math.Max(startI, 0);
|
|
finishI = Math.Min(finishI, VisibleItems.Count);
|
|
int y = 0;
|
|
|
|
for (int i = startI; i < finishI; i++)
|
|
{
|
|
y = i*ItemHeight - VerticalScroll.Value;
|
|
|
|
if (ImageList != null && VisibleItems[i].ImageIndex >= 0)
|
|
if (rtl)
|
|
e.Graphics.DrawImage(ImageList.Images[VisibleItems[i].ImageIndex], Width - 1 - LeftPadding, y);
|
|
else
|
|
e.Graphics.DrawImage(ImageList.Images[VisibleItems[i].ImageIndex], 1, y);
|
|
|
|
var textRect = new Rectangle(LeftPadding, y, ClientSize.Width - 1 - LeftPadding, ItemHeight - 1);
|
|
if (rtl)
|
|
textRect = new Rectangle(1, y, ClientSize.Width - 1 - LeftPadding, ItemHeight - 1);
|
|
|
|
if (i == SelectedItemIndex)
|
|
{
|
|
Brush selectedBrush = new LinearGradientBrush(new Point(0, y - 3), new Point(0, y + ItemHeight),
|
|
Colors.SelectedBackColor2, Colors.SelectedBackColor);
|
|
e.Graphics.FillRectangle(selectedBrush, textRect);
|
|
using(var pen = new Pen(Colors.SelectedBackColor2))
|
|
e.Graphics.DrawRectangle(pen, textRect);
|
|
}
|
|
|
|
if (i == HighlightedItemIndex)
|
|
using (var pen = new Pen(Colors.HighlightingColor))
|
|
e.Graphics.DrawRectangle(pen, textRect);
|
|
|
|
var sf = new StringFormat();
|
|
if (rtl)
|
|
sf.FormatFlags = StringFormatFlags.DirectionRightToLeft;
|
|
|
|
var args = new PaintItemEventArgs(e.Graphics, e.ClipRectangle)
|
|
{
|
|
Font = Font,
|
|
TextRect = new RectangleF(textRect.Location, textRect.Size),
|
|
StringFormat = sf,
|
|
IsSelected = i == SelectedItemIndex,
|
|
IsHovered = i == HighlightedItemIndex,
|
|
Colors = Colors
|
|
};
|
|
//call drawing
|
|
VisibleItems[i].OnPaint(args);
|
|
}
|
|
}
|
|
|
|
protected override void OnScroll(ScrollEventArgs se)
|
|
{
|
|
base.OnScroll(se);
|
|
Invalidate(true);
|
|
}
|
|
|
|
protected override void OnMouseClick(MouseEventArgs e)
|
|
{
|
|
base.OnMouseClick(e);
|
|
|
|
if (e.Button == MouseButtons.Left)
|
|
{
|
|
SelectedItemIndex = PointToItemIndex(e.Location);
|
|
ScrollToSelected();
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
private Point mouseEnterPoint;
|
|
|
|
protected override void OnMouseEnter(EventArgs e)
|
|
{
|
|
base.OnMouseEnter(e);
|
|
//
|
|
inside = true;
|
|
mouseEnterPoint = Control.MousePosition;
|
|
}
|
|
|
|
protected override void OnMouseLeave(EventArgs e)
|
|
{
|
|
base.OnMouseLeave(e);
|
|
//
|
|
inside = false;
|
|
}
|
|
|
|
protected override void OnMouseMove(MouseEventArgs e)
|
|
{
|
|
base.OnMouseMove(e);
|
|
|
|
if (mouseEnterPoint != Control.MousePosition)
|
|
{
|
|
HighlightedItemIndex = PointToItemIndex(e.Location);
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
protected override void OnMouseDoubleClick(MouseEventArgs e)
|
|
{
|
|
base.OnMouseDoubleClick(e);
|
|
|
|
SelectedItemIndex = PointToItemIndex(e.Location);
|
|
Invalidate();
|
|
OnItemSelected();
|
|
}
|
|
|
|
private void OnItemSelected()
|
|
{
|
|
if (ItemSelected != null)
|
|
ItemSelected(this, EventArgs.Empty);
|
|
}
|
|
|
|
private int PointToItemIndex(Point p)
|
|
{
|
|
return (p.Y + VerticalScroll.Value)/ItemHeight;
|
|
}
|
|
|
|
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
|
|
{
|
|
var host = Parent as AutocompleteMenuHost;
|
|
if (host != null)
|
|
if (host.Menu.ProcessKey((char) keyData, Keys.None))
|
|
return true;
|
|
|
|
return base.ProcessCmdKey(ref msg, keyData);
|
|
}
|
|
|
|
public void SelectItem(int itemIndex)
|
|
{
|
|
SelectedItemIndex = itemIndex;
|
|
ScrollToSelected();
|
|
Invalidate();
|
|
}
|
|
|
|
public void SetItems(List<AutocompleteItem> items)
|
|
{
|
|
VisibleItems = items;
|
|
SelectedItemIndex = -1;
|
|
AdjustScroll();
|
|
Invalidate();
|
|
}
|
|
|
|
public void ShowToolTip(AutocompleteItem autocompleteItem, Control control = null)
|
|
{
|
|
string title = autocompleteItem.ToolTipTitle;
|
|
string text = autocompleteItem.ToolTipText;
|
|
if (control == null)
|
|
control = this;
|
|
|
|
if (string.IsNullOrEmpty(title))
|
|
{
|
|
toolTip.ToolTipTitle = null;
|
|
toolTip.SetToolTip(control, null);
|
|
return;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(text))
|
|
{
|
|
toolTip.ToolTipTitle = null;
|
|
toolTip.Show(title, control, Width + 3, 0, ToolTipDuration);
|
|
}
|
|
else
|
|
{
|
|
toolTip.ToolTipTitle = title;
|
|
toolTip.Show(text, control, Width + 3, 0, ToolTipDuration);
|
|
}
|
|
}
|
|
// Mouse hook
|
|
public void InstallMouseHook()
|
|
{
|
|
mouseHook = new MouseHook();
|
|
mouseHook.LeftButtonDown += MouseHook_LeftButtonDown;
|
|
mouseHook.DoubleClick += MouseHook_DoubleClick;
|
|
mouseHook.RightButtonDown += MouseHook_RightButtonDown;
|
|
mouseHook.MiddleButtonDown += MouseHook_MiddleButtonDown;
|
|
mouseHook.Install();
|
|
}
|
|
public void UninstallMouseHook()
|
|
{
|
|
if (mouseHook != null)
|
|
{
|
|
mouseHook.LeftButtonDown -= MouseHook_LeftButtonDown;
|
|
mouseHook.DoubleClick -= MouseHook_DoubleClick;
|
|
mouseHook.RightButtonDown -= MouseHook_RightButtonDown;
|
|
mouseHook.MiddleButtonDown -= MouseHook_MiddleButtonDown;
|
|
mouseHook.Uninstall();
|
|
mouseHook = null;
|
|
}
|
|
}
|
|
private bool MouseHook_LeftButtonDown(MouseHook.MSLLHOOKSTRUCT mouseStruct)
|
|
{
|
|
if (inside)
|
|
{
|
|
Point p = new Point(mouseStruct.pt.x, mouseStruct.pt.y);
|
|
p = PointToClient(p);
|
|
MouseEventArgs mea = new MouseEventArgs(MouseButtons.Left, 1, p.X, p.Y, 0);
|
|
OnMouseClick(mea);
|
|
}
|
|
else if (OutsideMouseClick != null) OutsideMouseClick(null, null);
|
|
//
|
|
return false;
|
|
}
|
|
private bool MouseHook_DoubleClick(MouseHook.MSLLHOOKSTRUCT mouseStruct)
|
|
{
|
|
if (inside)
|
|
{
|
|
Point p = new Point(mouseStruct.pt.x, mouseStruct.pt.y);
|
|
p = PointToClient(p);
|
|
MouseEventArgs mea = new MouseEventArgs(MouseButtons.Left, 2, p.X, p.Y, 0);
|
|
OnMouseDoubleClick(mea);
|
|
}
|
|
else if (OutsideMouseClick != null) OutsideMouseClick(null, null);
|
|
//
|
|
return false;
|
|
}
|
|
private bool MouseHook_RightButtonDown(MouseHook.MSLLHOOKSTRUCT mouseStruct)
|
|
{
|
|
return MouseHook_LeftButtonDown(mouseStruct);
|
|
}
|
|
private bool MouseHook_MiddleButtonDown(MouseHook.MSLLHOOKSTRUCT mouseStruct)
|
|
{
|
|
return MouseHook_LeftButtonDown(mouseStruct);
|
|
}
|
|
}
|
|
} |