/src/Core/ImageStrip.cs
C# | 1387 lines | 1097 code | 235 blank | 55 comment | 121 complexity | 3ff36a48e0243580cfbbd3ef60bb71f2 MD5 | raw file
- /////////////////////////////////////////////////////////////////////////////////
- // Paint.NET //
- // Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. //
- // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. //
- // See src/Resources/Files/License.txt for full licensing and attribution //
- // details. //
- // . //
- /////////////////////////////////////////////////////////////////////////////////
-
- using PaintDotNet.Base;
- using PaintDotNet.SystemLayer;
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Drawing;
- using System.Drawing.Drawing2D;
- using System.Windows.Forms;
- using System.Windows.Forms.VisualStyles;
-
- namespace PaintDotNet
- {
- /*
- * Coordinate spaces:
- *
- * - Client -- This is the same as the Windows client space.
- * - Screen -- This is the same as the Windows screen space.
- * - View -- This is a virtual coordinate in the viewable, scrollable area. Client->View is defined as Client.X+ScrollOffset. View->Client is View.X-ScrollOffset
- * - Item -- This is relative to an item. Item->View is Item.X+(itemIndex * ItemViewSize.Width)
- *
- * */
-
- public class ImageStrip
- : Control
- {
- public enum ItemPart
- {
- None,
- Image,
- CloseButton
- }
-
- public sealed class Item
- {
- private PushButtonState _imageRenderState;
- private Image _image;
-
- private bool _selected;
-
- private PushButtonState _checkRenderState;
- private CheckState _checkState;
-
- private PushButtonState _closeRenderState;
-
- private bool _dirty;
-
- private bool _lockedDirtyValue;
- private int _dirtyValueLockCount;
-
- private object _tag;
-
- public event EventHandler Changed;
- private void OnChanged()
- {
- if (Changed != null)
- {
- Changed(this, EventArgs.Empty);
- }
- }
-
- public Image Image
- {
- get
- {
- return _image;
- }
-
- set
- {
- _image = value;
- OnChanged();
- }
- }
-
- public PushButtonState ImageRenderState
- {
- get
- {
- return _imageRenderState;
- }
-
- set
- {
- if (_imageRenderState == value) return;
- _imageRenderState = value;
- OnChanged();
- }
- }
-
- public bool Selected
- {
- get
- {
- return _selected;
- }
-
- set
- {
- if (_selected == value) return;
- _selected = value;
- OnChanged();
- }
- }
-
- public bool Dirty
- {
- get {
- return _dirtyValueLockCount > 0 ? _lockedDirtyValue : _dirty;
- }
-
- set
- {
- if (_dirty == value) return;
- _dirty = value;
-
- if (_dirtyValueLockCount <= 0)
- {
- OnChanged();
- }
- }
- }
-
- public void LockDirtyValue(bool forceValue)
- {
- ++_dirtyValueLockCount;
-
- if (_dirtyValueLockCount == 1)
- {
- _lockedDirtyValue = forceValue;
- }
- }
-
- public void UnlockDirtyValue()
- {
- --_dirtyValueLockCount;
-
- if (_dirtyValueLockCount == 0)
- {
- OnChanged();
- }
- else if (_dirtyValueLockCount < 0)
- {
- throw new InvalidOperationException("Calls to UnlockDirtyValue() must be matched by a preceding call to LockDirtyValue()");
- }
- }
-
- public bool Checked
- {
- get
- {
- return (CheckState == CheckState.Checked);
- }
-
- set {
- CheckState = value ? CheckState.Checked : CheckState.Unchecked;
- }
- }
-
- public CheckState CheckState
- {
- get
- {
- return _checkState;
- }
-
- set
- {
- if (_checkState == value) return;
- _checkState = value;
- OnChanged();
- }
- }
-
- public PushButtonState CheckRenderState
- {
- get
- {
- return _checkRenderState;
- }
-
- set
- {
- if (_checkRenderState == value) return;
- _checkRenderState = value;
- OnChanged();
- }
- }
-
- public PushButtonState CloseRenderState
- {
- get
- {
- return _closeRenderState;
- }
-
- set
- {
- if (_closeRenderState == value) return;
- _closeRenderState = value;
- OnChanged();
- }
- }
-
- public void SetPartRenderState(ItemPart itemPart, PushButtonState renderState)
- {
- switch (itemPart)
- {
- case ItemPart.None:
- break;
-
- case ItemPart.CloseButton:
- CloseRenderState = renderState;
- break;
-
- case ItemPart.Image:
- ImageRenderState = renderState;
- break;
-
- default:
- throw new InvalidEnumArgumentException();
- }
- }
-
- public object Tag
- {
- get
- {
- return _tag;
- }
-
- set
- {
- _tag = value;
- OnChanged();
- }
- }
-
- public void Update()
- {
- OnChanged();
- }
-
- public Item()
- {
- }
-
- public Item(Image image)
- {
- _image = image;
- }
- }
-
- private bool _managedFocus;
- private bool _showScrollButtons;
-
- private int _scrollOffset;
- private bool _showCloseButtons;
- private const int CloseButtonLength = 13;
- private const int ImagePadding = 2;
- private const int CloseButtonPadding = 2;
-
- private int _mouseOverIndex = -1;
- private ItemPart _mouseOverItemPart = ItemPart.None;
- private bool _mouseOverApplyRendering;
-
- private int _mouseDownIndex = -1;
- private MouseButtons _mouseDownButton = MouseButtons.None;
- private ItemPart _mouseDownItemPart = ItemPart.None;
- private bool _mouseDownApplyRendering;
-
- private bool _drawShadow = true;
- private bool _drawDirtyOverlay = true;
-
- public bool DrawShadow
- {
- get
- {
- return _drawShadow;
- }
-
- set
- {
- if (_drawShadow == value) return;
- _drawShadow = value;
- Refresh();
- }
- }
-
- public bool DrawDirtyOverlay
- {
- get
- {
- return _drawDirtyOverlay;
- }
-
- set
- {
- if (_drawDirtyOverlay == value) return;
- _drawDirtyOverlay = value;
- Refresh();
- }
- }
-
- // This is done as an optimization: otherwise we're getting flooded with MouseMove events
- // and constantly refreshing our rendering. So CPU usage goes to heck.
- private Point _lastMouseMovePt = new Point(-32000, -32000);
-
- private readonly List<Item> _items = new List<Item>();
-
- protected ArrowButton LeftScrollButton { get; private set; }
-
- protected ArrowButton RightScrollButton { get; private set; }
-
- private void MouseStatesToItemStates()
- {
- UI.SuspendControlPainting(this);
-
- foreach (Item t in _items)
- {
- t.CheckRenderState = PushButtonState.Normal;
- t.CloseRenderState = PushButtonState.Normal;
- t.ImageRenderState = PushButtonState.Normal;
- t.Selected = false;
- }
-
- if (_mouseDownApplyRendering)
- {
- if (_mouseDownIndex < 0 || _mouseDownIndex >= _items.Count)
- {
- _mouseDownApplyRendering = false;
- }
- else
- {
- _items[_mouseDownIndex].SetPartRenderState(_mouseDownItemPart, PushButtonState.Pressed);
- _items[_mouseDownIndex].Selected = true;
- }
- }
- else if (_mouseOverApplyRendering)
- {
- if (_mouseOverIndex < 0 || _mouseOverIndex >= _items.Count)
- {
- _mouseOverApplyRendering = false;
- }
- else
- {
- _items[_mouseOverIndex].SetPartRenderState(_mouseOverItemPart, PushButtonState.Hot);
- _items[_mouseOverIndex].Selected = true;
- }
- }
-
- UI.ResumeControlPainting(this);
- Invalidate();
- }
-
- public ImageStrip()
- {
- SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
- SetStyle(ControlStyles.Selectable, false);
-
- DoubleBuffered = true;
- ResizeRedraw = true;
-
- InitializeComponent();
- }
-
- private void InitializeComponent()
- {
- LeftScrollButton = new ArrowButton();
- RightScrollButton = new ArrowButton();
- SuspendLayout();
- //
- // leftScrollButton
- //
- LeftScrollButton.Name = "LeftScrollButton";
- LeftScrollButton.ArrowDirection = ArrowDirection.Left;
- LeftScrollButton.ArrowOutlineWidth = 1.0f;
- LeftScrollButton.Click += LeftScrollButtonClick;
- LeftScrollButton.DrawWithGradient = true;
- //
- // rightScrollButton
- //
- RightScrollButton.Name = "RightScrollButton";
- RightScrollButton.ArrowDirection = ArrowDirection.Right;
- RightScrollButton.ArrowOutlineWidth = 1.0f;
- RightScrollButton.Click += RightScrollButtonClick;
- RightScrollButton.DrawWithGradient = true;
- //
- // ImageStrip
- //
- Name = "ImageStrip";
- TabStop = false;
- Controls.Add(LeftScrollButton);
- Controls.Add(RightScrollButton);
- ResumeLayout();
- PerformLayout();
- }
-
- public event EventHandler<EventArgs<ArrowDirection>> ScrollArrowClicked;
-
- protected void OnScrollArrowClicked(ArrowDirection arrowDirection)
- {
- if (ScrollArrowClicked != null)
- {
- ScrollArrowClicked(this, new EventArgs<ArrowDirection>(arrowDirection));
- }
- }
-
- private void LeftScrollButtonClick(object sender, EventArgs e)
- {
- Focus();
- OnScrollArrowClicked(ArrowDirection.Left);
- }
-
- private void RightScrollButtonClick(object sender, EventArgs e)
- {
- Focus();
- OnScrollArrowClicked(ArrowDirection.Right);
- }
-
- /// <summary>
- /// This event is raised when this control wishes to relinquish focus.
- /// </summary>
- public event EventHandler RelinquishFocus;
-
- private void OnRelinquishFocus()
- {
- if (RelinquishFocus != null)
- {
- RelinquishFocus(this, EventArgs.Empty);
- }
- }
-
- /// <summary>
- /// Gets or sets whether the control manages focus.
- /// </summary>
- /// <remarks>
- /// If this is true, the toolstrip will capture focus when the mouse enters its client area. It will then
- /// relinquish focus (via the RelinquishFocus event) when the mouse leaves. It will not capture or
- /// attempt to relinquish focus if MenuStripEx.IsAnyMenuActive returns true.
- /// </remarks>
- public bool ManagedFocus
- {
- get
- {
- return _managedFocus;
- }
-
- set
- {
- _managedFocus = value;
- }
- }
-
- public void AddItem(Item newItem)
- {
- if (_items.Contains(newItem))
- {
- throw new ArgumentException("newItem was already added to this control");
- }
-
- newItem.Changed += ItemChanged;
- _items.Add(newItem);
-
- PerformLayout();
- Invalidate();
- }
-
- public void RemoveItem(Item item)
- {
- if (!_items.Contains(item))
- {
- throw new ArgumentException("item was never added to this control");
- }
-
- item.Changed -= ItemChanged;
- _items.Remove(item);
-
- PerformLayout();
- Invalidate();
- }
-
- public void ClearItems()
- {
- SuspendLayout();
- UI.SuspendControlPainting(this);
-
- while (_items.Count > 0)
- {
- RemoveItem(_items[_items.Count - 1]);
- }
-
- UI.ResumeControlPainting(this);
- ResumeLayout(true);
-
- Invalidate();
- }
-
- private void ItemChanged(object sender, EventArgs e)
- {
- Invalidate();
- }
-
- /// <summary>
- /// Raised when an item is clicked on.
- /// </summary>
- /// <remarks>
- /// e.Data.First is a reference to the Item.
- /// e.Data.Second is the ItemPart.
- /// e.Data.Third is the MouseButtons that was used to click on the ItemPart.
- /// </remarks>
- public event EventHandler<EventArgs<Triple<Item, ItemPart, MouseButtons>>> ItemClicked;
-
- protected virtual void OnItemClicked(Item item, ItemPart itemPart, MouseButtons mouseButtons)
- {
- if (ItemClicked != null)
- {
- ItemClicked(this, new EventArgs<Triple<Item, ItemPart, MouseButtons>>(
- Triple.Create(item, itemPart, mouseButtons)));
- }
- }
-
- public void PerformItemClick(int itemIndex, ItemPart itemPart, MouseButtons mouseButtons)
- {
- PerformItemClick(_items[itemIndex], itemPart, mouseButtons);
- }
-
- public void PerformItemClick(Item item, ItemPart itemPart, MouseButtons mouseButtons)
- {
- OnItemClicked(item, itemPart, mouseButtons);
- }
-
- public Item[] Items
- {
- get
- {
- return _items.ToArray();
- }
- }
-
- public int ItemCount
- {
- get
- {
- return _items.Count;
- }
- }
-
- public bool ShowScrollButtons
- {
- get
- {
- return _showScrollButtons;
- }
-
- set
- {
- if (_showScrollButtons == value) return;
- _showScrollButtons = value;
- PerformLayout();
- Invalidate(true);
- }
- }
-
- public int MinScrollOffset
- {
- get
- {
- return 0;
- }
- }
-
- public int MaxScrollOffset
- {
- get
- {
- Size itemSize = ItemSize;
-
- int itemsLength = itemSize.Width * _items.Count;
- int viewLength = itemsLength - ClientSize.Width;
- int maxScrollOffset = Math.Max(0, viewLength);
- return maxScrollOffset;
- }
- }
-
- public int ScrollOffset
- {
- get
- {
- return _scrollOffset;
- }
-
- set
- {
- int clampedValue = Utility.Clamp(value, MinScrollOffset, MaxScrollOffset);
-
- if (_scrollOffset == clampedValue) return;
- _scrollOffset = clampedValue;
- OnScrollOffsetChanged();
- Invalidate(true);
- }
- }
-
- public event EventHandler ScrollOffsetChanged;
-
- private void OnScrollOffsetChanged()
- {
- PerformLayout();
-
- if (ScrollOffsetChanged != null)
- {
- ScrollOffsetChanged(this, EventArgs.Empty);
- }
- }
-
- /// <summary>
- /// Gets the viewable area, in View coordinate space.
- /// </summary>
- public Rectangle ViewRectangle
- {
- get
- {
- Size itemSize = ItemSize;
- return new Rectangle(0, 0, itemSize.Width * ItemCount, itemSize.Height);
- }
- }
-
- protected override void OnLayout(LayoutEventArgs levent)
- {
- int arrowWidth = UI.ScaleWidth(16);
-
- ScrollOffset = Utility.Clamp(_scrollOffset, MinScrollOffset, MaxScrollOffset);
-
- // Determine arrow visibility / position
- LeftScrollButton.Size = new Size(arrowWidth, ClientSize.Height);
- LeftScrollButton.Location = new Point(0, 0);
-
- RightScrollButton.Size = new Size(arrowWidth, ClientSize.Height);
- RightScrollButton.Location = new Point(ClientSize.Width - RightScrollButton.Width, 0);
-
- bool showEitherButton = _showScrollButtons && (ViewRectangle.Width > ClientRectangle.Width);
- bool showRightButton = (_scrollOffset < MaxScrollOffset) && showEitherButton;
- bool showLeftButton = (_scrollOffset > MinScrollOffset) && showEitherButton;
-
- RightScrollButton.Enabled = showRightButton;
- RightScrollButton.Visible = showRightButton;
- LeftScrollButton.Enabled = showLeftButton;
- LeftScrollButton.Visible = showLeftButton;
-
- base.OnLayout(levent);
- }
-
- public bool ShowCloseButtons
- {
- get
- {
- return _showCloseButtons;
- }
-
- set
- {
- if (_showCloseButtons == value) return;
- _showCloseButtons = value;
- PerformLayout();
- Invalidate();
- }
- }
-
- public int PreferredMinClientWidth
- {
- get
- {
- if (_items.Count == 0)
- {
- return 0;
- }
-
- int minWidth = ItemSize.Width;
-
- if (LeftScrollButton.Visible || RightScrollButton.Visible)
- {
- minWidth += LeftScrollButton.Width;
- minWidth += RightScrollButton.Width;
- }
-
- minWidth = Math.Min(minWidth, ViewRectangle.Width);
-
- return minWidth;
- }
- }
-
- public Size PreferredImageSize
- {
- get
- {
- Rectangle itemRect;
- Rectangle imageRect;
-
- MeasureItemPartRectangles(out itemRect, out imageRect);
- return new Size(imageRect.Width - ImagePadding * 2, imageRect.Height - ImagePadding * 2);
- }
- }
-
- public Size ItemSize
- {
- get
- {
- Rectangle itemRect;
- Rectangle imageRect;
-
- MeasureItemPartRectangles(out itemRect, out imageRect);
- return itemRect.Size;
- }
- }
-
- protected virtual void DrawItemBackground(Graphics g, Item item, Rectangle itemRect)
- {
- }
-
- private static void DrawItemHighlight(
- Graphics g,
- Item item,
- Rectangle itemRect,
- Rectangle highlightRect)
- {
- Color backFillColor;
- Color outlineColor;
-
- if (item.Checked)
- {
- backFillColor = Color.FromArgb(192, SystemColors.Highlight);
- outlineColor = backFillColor;
- }
- else if (item.Selected)
- {
- backFillColor = Color.FromArgb(64, SystemColors.HotTrack);
- outlineColor = Color.FromArgb(64, SystemColors.HotTrack);
- }
- else
- {
- backFillColor = Color.Transparent;
- outlineColor = Color.Transparent;
- }
-
- using (var backFillBrush = new SolidBrush(backFillColor))
- {
- g.FillRectangle(backFillBrush, highlightRect);
- }
-
- using (var outlinePen = new Pen(outlineColor))
- {
- g.DrawRectangle(outlinePen, highlightRect.X, highlightRect.Y, highlightRect.Width - 1, highlightRect.Height - 1);
- }
- }
-
- private static void DrawItemCloseButton(
- Graphics g,
- Item item,
- Rectangle itemRect,
- Rectangle closeButtonRect)
- {
- if (!item.Checked || !item.Selected) return;
- const string resourceNamePrefix = "Images.ImageStrip.CloseButton.";
- const string resourceNameSuffix = ".png";
- string resourceNameInfix = item.CloseRenderState.ToString();
-
- string resourceName = resourceNamePrefix + resourceNameInfix + resourceNameSuffix;
-
- ImageResource imageResource = PdnResources.GetImageResource(resourceName);
- Image image = imageResource.Reference;
-
- g.SmoothingMode = SmoothingMode.AntiAlias;
- g.InterpolationMode = InterpolationMode.HighQualityBicubic;
-
- g.DrawImage(image, closeButtonRect, new Rectangle(0, 0, image.Width, image.Width), GraphicsUnit.Pixel);
- }
-
- private static void DrawItemDirtyOverlay(
- Graphics g,
- Item item,
- Rectangle itemRect,
- Rectangle dirtyOverlayRect)
- {
- Color outerPenColor = Color.White;
- Color innerPenColor = Color.Orange;
-
- const int xInset = 2;
- int scaledXInset = UI.ScaleWidth(xInset);
-
- const float outerPenWidth = 4.0f;
- const float innerPenWidth = 2.0f;
-
- float scaledOuterPenWidth = UI.ScaleWidth(outerPenWidth);
- float scaledInnerPenWidth = UI.ScaleWidth(innerPenWidth);
-
- SmoothingMode oldSM = g.SmoothingMode;
- g.SmoothingMode = SmoothingMode.AntiAlias;
-
- int left = dirtyOverlayRect.Left + scaledXInset;
- int top = dirtyOverlayRect.Top + scaledXInset;
- int right = dirtyOverlayRect.Right - scaledXInset;
- int bottom = dirtyOverlayRect.Bottom - scaledXInset;
-
- float r = Math.Min((right - left) / 2.0f, (bottom - top) / 2.0f);
-
- var centerPt = new PointF((left + right) / 2.0f, (top + bottom) / 2.0f);
- const float twoPiOver5 = (float)(Math.PI * 0.4);
-
- var a = new PointF(centerPt.X + r * (float)Math.Sin(twoPiOver5), centerPt.Y - r * (float)Math.Cos(twoPiOver5));
- var b = new PointF(centerPt.X + r * (float)Math.Sin(2 * twoPiOver5), centerPt.Y - r * (float)Math.Cos(2 * twoPiOver5));
- var c = new PointF(centerPt.X + r * (float)Math.Sin(3 * twoPiOver5), centerPt.Y - r * (float)Math.Cos(3 * twoPiOver5));
- var d = new PointF(centerPt.X + r * (float)Math.Sin(4 * twoPiOver5), centerPt.Y - r * (float)Math.Cos(4 * twoPiOver5));
- var e = new PointF(centerPt.X + r * (float)Math.Sin(5 * twoPiOver5), centerPt.Y - r * (float)Math.Cos(5 * twoPiOver5));
-
- var lines =
- new[]
- {
- centerPt, a,
- centerPt, b,
- centerPt, c,
- centerPt, d,
- centerPt, e
- };
-
- using (var outerPen = new Pen(outerPenColor, scaledOuterPenWidth))
- {
- for (int i = 0; i < lines.Length; i += 2)
- {
- g.DrawLine(outerPen, lines[i], lines[i + 1]);
- }
- }
-
- using (var innerPen = new Pen(innerPenColor, scaledInnerPenWidth))
- {
- for (int i = 0; i < lines.Length; i += 2)
- {
- g.DrawLine(innerPen, lines[i], lines[i + 1]);
- }
- }
-
- g.SmoothingMode = oldSM;
- }
-
- private static void DrawItemImageShadow(
- Graphics g,
- Item item,
- Rectangle itemRect,
- Rectangle imageRect,
- Rectangle imageInsetRect)
- {
- Rectangle shadowRect = Rectangle.Inflate(imageInsetRect, 1, 1);
- Utility.DrawDropShadow1Px(g, shadowRect);
- }
-
- private static void DrawItemImage(
- Graphics g,
- Item item,
- Rectangle itemRect,
- Rectangle imageRect,
- Rectangle imageInsetRect)
- {
- // Draw the image
- if (item.Image != null)
- {
- g.DrawImage(
- item.Image,
- imageInsetRect,
- new Rectangle(0, 0, item.Image.Width, item.Image.Height),
- GraphicsUnit.Pixel);
- }
- }
-
- private void DrawItem(Graphics g, Item item, Point offset)
- {
- Rectangle itemRect;
- Rectangle imageRect;
- Rectangle imageInsetRect;
- Rectangle closeButtonRect;
- Rectangle dirtyOverlayRect;
-
- MeasureItemPartRectangles(
- item,
- out itemRect,
- out imageRect,
- out imageInsetRect,
- out closeButtonRect,
- out dirtyOverlayRect);
-
- itemRect.X += offset.X;
- itemRect.Y += offset.Y;
-
- imageRect.X += offset.X;
- imageRect.Y += offset.Y;
-
- imageInsetRect.X += offset.X;
- imageInsetRect.Y += offset.Y;
-
- closeButtonRect.X += offset.X;
- closeButtonRect.Y += offset.Y;
-
- dirtyOverlayRect.X += offset.X;
- dirtyOverlayRect.Y += offset.Y;
-
- DrawItemBackground(g, item, itemRect);
-
- Rectangle highlightRect = itemRect;
- DrawItemHighlight(g, item, itemRect, highlightRect);
-
- // Fill background and draw outline
- if (_drawShadow)
- {
- DrawItemImageShadow(g, item, itemRect, imageRect, imageInsetRect);
- }
-
- DrawItemImage(g, item, itemRect, imageRect, imageInsetRect);
-
- if (_showCloseButtons)
- {
- DrawItemCloseButton(g, item, itemRect, closeButtonRect);
- }
-
- if (_drawDirtyOverlay && item.Dirty)
- {
- DrawItemDirtyOverlay(g, item, itemRect, dirtyOverlayRect);
- }
- }
-
- public Point ClientPointToViewPoint(Point clientPt)
- {
- int viewX = clientPt.X + _scrollOffset;
- return new Point(viewX, clientPt.Y);
- }
-
- public Rectangle ClientRectangleToViewRectangle(Rectangle clientRect)
- {
- Point viewPt = ClientPointToViewPoint(clientRect.Location);
- return new Rectangle(viewPt, clientRect.Size);
- }
-
- public Point ViewPointToClientPoint(Point viewPt)
- {
- int clientX = viewPt.X - _scrollOffset;
- return new Point(clientX, viewPt.Y);
- }
-
- public Rectangle ViewRectangleToClientRectangle(Rectangle viewRect)
- {
- Point clientPt = ViewPointToClientPoint(viewRect.Location);
- return new Rectangle(clientPt, viewRect.Size);
- }
-
- private Point ViewPointToItemPoint(int itemIndex, Point viewPt)
- {
- Rectangle itemRect = ItemIndexToItemViewRectangle(itemIndex);
- var itemPt = new Point(viewPt.X - itemRect.X, viewPt.Y);
- return itemPt;
- }
-
- private Rectangle ItemIndexToItemViewRectangle(int itemIndex)
- {
- Size itemSize = ItemSize;
- return new Rectangle(itemSize.Width * itemIndex, itemSize.Height, itemSize.Width, itemSize.Height);
- }
-
- public int ViewPointToItemIndex(Point viewPt)
- {
- if (!ViewRectangle.Contains(viewPt))
- {
- return -1;
- }
-
- Size itemSize = ItemSize;
- int index = viewPt.X / itemSize.Width;
-
- return index;
- }
-
- private void MeasureItemPartRectangles(
- out Rectangle itemRect,
- out Rectangle imageRect)
- {
- itemRect = new Rectangle(
- 0,
- 0,
- ClientSize.Height,
- ClientSize.Height);
-
- imageRect = new Rectangle(
- itemRect.Left,
- itemRect.Top,
- itemRect.Width,
- itemRect.Width);
- }
-
- private void MeasureItemPartRectangles(
- Item item,
- out Rectangle itemRect,
- out Rectangle imageRect,
- out Rectangle imageInsetRect,
- out Rectangle closeButtonRect,
- out Rectangle dirtyOverlayRect)
- {
- MeasureItemPartRectangles(out itemRect, out imageRect);
-
- var imageInsetRectMax = new Rectangle(
- imageRect.Left + ImagePadding,
- imageRect.Top + ImagePadding,
- imageRect.Width - ImagePadding * 2,
- imageRect.Height - ImagePadding * 2);
-
- Size imageInsetSize = item.Image == null ? imageRect.Size : Utility.ComputeThumbnailSize(item.Image.Size, imageInsetRectMax.Width);
-
- int scaledCloseButtonLength = UI.ScaleWidth(CloseButtonLength);
- int scaledCloseButtonPadding = UI.ScaleWidth(CloseButtonPadding);
-
- imageInsetRect = new Rectangle(
- imageInsetRectMax.Left + (imageInsetRectMax.Width - imageInsetSize.Width) / 2,
- imageInsetRectMax.Bottom - imageInsetSize.Height,
- imageInsetSize.Width,
- imageInsetSize.Height);
-
- closeButtonRect = new Rectangle(
- imageInsetRectMax.Right - scaledCloseButtonLength - scaledCloseButtonPadding,
- imageInsetRectMax.Top + scaledCloseButtonPadding,
- scaledCloseButtonLength,
- scaledCloseButtonLength);
-
- dirtyOverlayRect = new Rectangle(
- imageInsetRectMax.Left + scaledCloseButtonPadding,
- imageInsetRectMax.Top + scaledCloseButtonPadding,
- scaledCloseButtonLength,
- scaledCloseButtonLength);
- }
-
- private ItemPart ItemPointToItemPart(Item item, Point pt)
- {
- Rectangle itemRect;
- Rectangle imageRect;
- Rectangle imageInsetRect;
- Rectangle closeButtonRect;
- Rectangle dirtyOverlayRect;
-
- MeasureItemPartRectangles(
- item,
- out itemRect,
- out imageRect,
- out imageInsetRect,
- out closeButtonRect,
- out dirtyOverlayRect);
-
- if (closeButtonRect.Contains(pt))
- {
- return ItemPart.CloseButton;
- }
-
- return imageRect.Contains(pt) ? ItemPart.Image : ItemPart.None;
- }
-
- private Rectangle ItemIndexToClientRect(int itemIndex)
- {
- Size itemSize = ItemSize;
-
- var clientRect = new Rectangle(
- itemSize.Width * itemIndex,
- 0,
- itemSize.Width,
- itemSize.Height);
-
- return clientRect;
- }
-
- private void CalculateVisibleScrollOffsets(
- int itemIndex,
- out int minOffset,
- out int maxOffset,
- out int minFullyShownOffset,
- out int maxFullyShownOffset)
- {
- Rectangle itemClientRect = ItemIndexToClientRect(itemIndex);
-
- minOffset = itemClientRect.Left + 1 - ClientSize.Width;
- maxOffset = itemClientRect.Right - 1;
- minFullyShownOffset = itemClientRect.Right - ClientSize.Width;
- maxFullyShownOffset = itemClientRect.Left;
-
- if (LeftScrollButton.Visible)
- {
- maxOffset -= LeftScrollButton.Width;
- maxFullyShownOffset -= LeftScrollButton.Width;
- }
-
- if (!RightScrollButton.Visible) return;
- minOffset += RightScrollButton.Width;
- minFullyShownOffset += RightScrollButton.Width;
- }
-
- public Rectangle ScrolledViewRect
- {
- get
- {
- return new Rectangle(_scrollOffset, 0, ClientSize.Width, ClientSize.Height);
- }
- }
-
- public bool IsItemVisible(int index)
- {
- Rectangle itemRect = ItemIndexToClientRect(index);
- Rectangle intersect = Rectangle.Intersect(itemRect, ScrolledViewRect);
- return (intersect.Width > 0 || intersect.Height > 0);
- }
-
- public bool IsItemFullyVisible(int index)
- {
- Rectangle itemRect = ItemIndexToClientRect(index);
- Rectangle svRect = ScrolledViewRect;
-
- if (LeftScrollButton.Visible)
- {
- svRect.X += LeftScrollButton.Width;
- svRect.Width -= LeftScrollButton.Width;
- }
-
- if (RightScrollButton.Visible)
- {
- svRect.Width -= RightScrollButton.Width;
- }
-
- Rectangle intersect = Rectangle.Intersect(itemRect, svRect);
- return (intersect == itemRect);
- }
-
- public void EnsureItemFullyVisible(Item item)
- {
- int index = _items.IndexOf(item);
- EnsureItemFullyVisible(index);
- }
-
- public void EnsureItemFullyVisible(int index)
- {
- if (IsItemFullyVisible(index))
- {
- return;
- }
-
- int minOffset;
- int maxOffset;
- int minFullyShownOffset;
- int maxFullyShownOffset;
-
- CalculateVisibleScrollOffsets(index, out minOffset, out maxOffset,
- out minFullyShownOffset, out maxFullyShownOffset);
-
- // Pick the offset that moves the image the fewest number of pixels
- int oldOffset = _scrollOffset;
- int dxMin = Math.Abs(oldOffset - minFullyShownOffset);
- int dxMax = Math.Abs(oldOffset - maxFullyShownOffset);
-
- ScrollOffset = dxMin <= dxMax ? minFullyShownOffset : maxFullyShownOffset;
- }
-
- protected override void OnPaint(PaintEventArgs e)
- {
- if (UI.IsControlPaintingEnabled(this))
- {
- Size itemSize = ItemSize;
- var firstItemRect = new Rectangle(-_scrollOffset, 0, itemSize.Width, itemSize.Height);
-
- for (int i = 0; i < _items.Count; ++i)
- {
- if (!IsItemVisible(i)) continue;
- var itemOffset = new Point(firstItemRect.X + itemSize.Width * i, firstItemRect.Y);
- DrawItem(e.Graphics, _items[i], itemOffset);
- }
- }
-
- base.OnPaint(e);
- }
-
- protected override void OnMouseDown(MouseEventArgs e)
- {
- if (_mouseDownButton == MouseButtons.None)
- {
- var clientPt = new Point(e.X, e.Y);
- Point viewPt = ClientPointToViewPoint(clientPt);
- int itemIndex = ViewPointToItemIndex(viewPt);
-
- if (itemIndex >= 0 && itemIndex < _items.Count)
- {
- Item item = _items[itemIndex];
- Point itemPt = ViewPointToItemPoint(itemIndex, viewPt);
- ItemPart itemPart = ItemPointToItemPart(item, itemPt);
-
- if (itemPart == ItemPart.Image)
- {
- OnItemClicked(item, itemPart, e.Button);
-
- _mouseDownApplyRendering = false;
- _mouseOverIndex = itemIndex;
- _mouseOverItemPart = itemPart;
- _mouseOverApplyRendering = true;
- }
- else
- {
- _mouseDownIndex = itemIndex;
- _mouseDownItemPart = itemPart;
- _mouseDownButton = e.Button;
- _mouseDownApplyRendering = true;
- _mouseOverApplyRendering = false;
- }
-
- MouseStatesToItemStates();
- Refresh();
- }
- }
-
- base.OnMouseDown(e);
- }
-
- protected override void OnMouseMove(MouseEventArgs e)
- {
- GetFocus();
-
- var clientPt = new Point(e.X, e.Y);
-
- if (clientPt != _lastMouseMovePt)
- {
- Point viewPt = ClientPointToViewPoint(clientPt);
- int itemIndex = ViewPointToItemIndex(viewPt);
-
- if (_mouseDownButton == MouseButtons.None)
- {
- if (itemIndex >= 0 && itemIndex < _items.Count)
- {
- Item item = _items[itemIndex];
- Point itemPt = ViewPointToItemPoint(itemIndex, viewPt);
- ItemPart itemPart = ItemPointToItemPart(item, itemPt);
-
- _mouseOverIndex = itemIndex;
- _mouseOverItemPart = itemPart;
- _mouseOverApplyRendering = true;
- }
- else
- {
- _mouseOverApplyRendering = false;
- }
- }
- else
- {
- _mouseOverApplyRendering = false;
-
- if (itemIndex != _mouseDownIndex)
- {
- _mouseDownApplyRendering = false;
- }
- else if (itemIndex < 0 || itemIndex >= _items.Count)
- {
- _mouseDownApplyRendering = false;
- }
- else
- {
- Item item = Items[itemIndex];
- Point itemPt = ViewPointToItemPoint(itemIndex, viewPt);
-
- ItemPart itemPart = ItemPointToItemPart(item, itemPt);
-
- if (itemPart != _mouseDownItemPart)
- {
- _mouseDownApplyRendering = false;
- }
- }
- }
-
- MouseStatesToItemStates();
- Refresh();
- }
-
- _lastMouseMovePt = clientPt;
- base.OnMouseMove(e);
- }
-
- protected override void OnMouseUp(MouseEventArgs e)
- {
- bool raisedClickEvent = false;
-
- if (_mouseDownButton == e.Button)
- {
- var clientPt = new Point(e.X, e.Y);
- Point viewPt = ClientPointToViewPoint(clientPt);
- int itemIndex = ViewPointToItemIndex(viewPt);
-
- if (itemIndex >= 0 && itemIndex < _items.Count)
- {
- Item item = _items[itemIndex];
- Point itemPt = ViewPointToItemPoint(itemIndex, viewPt);
- ItemPart itemPart = ItemPointToItemPart(item, itemPt);
-
- if (itemIndex == _mouseDownIndex && itemPart == _mouseDownItemPart)
- {
- if (itemPart == ItemPart.CloseButton && !item.Checked)
- {
- // Can only close 'checked' images, just like how tab switching+closing works in IE7
- itemPart = ItemPart.Image;
- }
-
- OnItemClicked(item, itemPart, _mouseDownButton);
- raisedClickEvent = true;
- }
-
- _mouseOverApplyRendering = true;
- _mouseOverItemPart = itemPart;
- _mouseOverIndex = itemIndex;
- }
-
- _mouseDownApplyRendering = false;
- _mouseDownButton = MouseButtons.None;
-
- MouseStatesToItemStates();
- Refresh();
- }
-
- if (raisedClickEvent)
- {
- ForceMouseMove();
- }
-
- base.OnMouseUp(e);
- }
-
- protected override void OnMouseWheel(MouseEventArgs e)
- {
- float count = (float)e.Delta / SystemInformation.MouseWheelScrollDelta;
- var pixels = (int)(count * ItemSize.Width);
- int newSO = ScrollOffset - pixels;
- ScrollOffset = newSO;
-
- ForceMouseMove();
-
- base.OnMouseWheel(e);
- }
-
- private void ForceMouseMove()
- {
- Point clientPt = PointToClient(MousePosition);
- _lastMouseMovePt = new Point(_lastMouseMovePt.X + 1, _lastMouseMovePt.Y + 1);
- var me = new MouseEventArgs(MouseButtons.None, 0, clientPt.X, clientPt.Y, 0);
- OnMouseMove(me);
- }
-
- protected override void OnMouseEnter(EventArgs e)
- {
- GetFocus();
- base.OnMouseEnter(e);
- }
-
- private void GetFocus()
- {
- if (_managedFocus && !MenuStripEx.IsAnyMenuActive && UI.IsOurAppActive)
- {
- Focus();
- }
- }
-
- protected override void OnMouseLeave(EventArgs e)
- {
- _mouseDownApplyRendering = false;
- _mouseOverApplyRendering = false;
-
- MouseStatesToItemStates();
- Refresh();
-
- if (_managedFocus && !MenuStripEx.IsAnyMenuActive && UI.IsOurAppActive)
- {
- OnRelinquishFocus();
- }
-
- base.OnMouseLeave(e);
- }
- }
- }