using System;
using System.Drawing;
using System.Windows.Forms;
namespace Oli.Controls
{
///
/// Horizontal line, which indicates the current drop position.
///
/// In order to be able to delete the visual cue properly, we need to draw in inverted mode. Since
/// Microsoft has not included this option into the BCL, we need to fall back on the Win32 API.
public class VisualCue
{
///
/// Indicates that no visual cue has been drawn.
///
public const int NoVisualCue = -1;
private ListBox _listBox;
private int _index = NoVisualCue;
///
/// Initializes a new instance of the VisualCue class.
///
/// ListBox or control derived from ListBox which owns the visual cue.
public VisualCue(ListBox listBox)
{
_listBox = listBox;
}
///
/// Gets the index of the item before which the visual cue has been drawn or NoVisualCue if no visual cue has been drawn.
///
public int Index
{
get { return _index; }
}
///
/// Clears the visual cue, if some visual cue is visible.
///
public void Clear()
{
if (_index != NoVisualCue) {
Draw(_index); // Draws in inverted mode and thus deletes the visual cue;
_index = NoVisualCue;
}
}
///
/// Draws a visual cue before (above) the list item indicated by itemIndex. If itemIndex is greater than
/// the index of the last item of the list, then the visual cue is drawn after (below) the last list item.
///
/// Remembers the passed index for later use by the Clear method and for the Index property.
/// Index of a list item.
public void Draw(int itemIndex)
{
Rectangle rect;
Point l1p1, l1p2, l2p1, l2p2;
if (_listBox.Sorted) { // Draw vertical cue if the list is sorted, since items could just be dropped anywhere.
rect = _listBox.ClientRectangle;
rect = _listBox.RectangleToScreen(rect); // We need screen coordinates.
l1p1 = new Point(rect.Left, rect.Top);
l1p2 = new Point(rect.Left, rect.Bottom);
l2p1 = new Point(rect.Left + 1, rect.Top);
l2p2 = new Point(rect.Left + 1, rect.Bottom);
} else { // Draw horizontal line.
// Figure out where to draw the cue.
if (_listBox.Items.Count == 0) { // No current item available. Get the client rectangle instead.
rect = _listBox.ClientRectangle;
} else if (itemIndex < _listBox.Items.Count) { // Somewhere in the list, this is our current item.
rect = _listBox.GetItemRectangle(itemIndex);
} else { // Somewhere after the last item. Let's draw the visual cue just after the last item in the list.
rect = _listBox.GetItemRectangle(_listBox.Items.Count - 1); // Take the last items rectangle...
rect.Y += rect.Height; // ... and move it down by the item height.
}
rect.Y -= 1; // Center the 2-pixel line around the calculated position.
if (rect.Y < _listBox.ClientRectangle.Y) { // Make sure we draw inside of the client rectangle.
rect.Y = _listBox.ClientRectangle.Y;
}
rect = _listBox.RectangleToScreen(rect); // We need screen coordinates.
l1p1 = new Point(rect.Left, rect.Top);
l1p2 = new Point(rect.Right, rect.Top);
l2p1 = new Point(rect.Left, rect.Top + 1);
l2p2 = new Point(rect.Right, rect.Top + 1);
}
IntPtr hdc = Win32.GetDC(IntPtr.Zero); // Get device context.
Win32.SetROP2(hdc, Win32.R2_NOT); // Switch to inverted mode.
Win32.MoveToEx(hdc, l1p1.X, l1p1.Y, IntPtr.Zero);
Win32.LineTo(hdc, l1p2.X, l1p2.Y);
Win32.MoveToEx(hdc, l2p1.X, l2p1.Y, IntPtr.Zero);
Win32.LineTo(hdc, l2p2.X, l2p2.Y);
Win32.ReleaseDC(IntPtr.Zero, hdc); // Release device context.
_index = itemIndex;
}
}
}