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 visibleItems; private MouseHook mouseHook; private bool inside; /// /// Duration (ms) of tooltip showing /// public int ToolTipDuration { get; set; } /// /// Occurs when user selected item for inserting into text /// public event EventHandler ItemSelected; /// /// Occurs when current hovered item is changing /// public event EventHandler ItemHovered; /// /// Occurs when mouse click is outside the controls area /// public event EventHandler OutsideMouseClick; /// /// Colors /// 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 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 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); } } }