///* // * DropSink.cs - Add drop sink ability to an ObjectListView // * // * Author: Phillip Piper // * Date: 2009-03-17 5:15 PM // * // * Change log: // * 2010-08-24 JPP - Moved AcceptExternal property up to SimpleDragSource. // * v2.3 // * 2009-09-01 JPP - Correctly handle case where RefreshObjects() is called for // * objects that were children but are now roots. // * 2009-08-27 JPP - Added ModelDropEventArgs.RefreshObjects() to simplify updating after // * a drag-drop operation // * 2009-08-19 JPP - Changed to use OlvHitTest() // * v2.2.1 // * 2007-07-06 JPP - Added StandardDropActionFromKeys property to OlvDropEventArgs // * v2.2 // * 2009-05-17 JPP - Added a Handled flag to OlvDropEventArgs // * - Tweaked the appearance of the drop-on-background feedback // * 2009-04-15 JPP - Separated DragDrop.cs into DropSink.cs // * 2009-03-17 JPP - Initial version // * // * Copyright (C) 2009-2010 Phillip Piper // * // * This program is free software: you can redistribute it and/or modify // * it under the terms of the GNU General Public License as published by // * the Free Software Foundation, either version 3 of the License, or // * (at your option) any later version. // * // * This program is distributed in the hope that it will be useful, // * but WITHOUT ANY WARRANTY; without even the implied warranty of // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // * GNU General Public License for more details. // * // * You should have received a copy of the GNU General Public License // * along with this program. If not, see . // * // * If you wish to use this code in a closed source application, please contact phillip_piper@bigfoot.com. // */ //using System; //using System.Collections; //using System.Drawing; //using System.Drawing.Drawing2D; //using System.Windows.Forms; //namespace BrightIdeasSoftware //{ // /// // /// Objects that implement this interface can acts as the receiver for drop // /// operation for an ObjectListView. // /// // public interface IDropSink // { // /// // /// Gets or sets the ObjectListView that is the drop sink // /// // ObjectListView ListView { get; set; } // /// // /// Draw any feedback that is appropriate to the current drop state. // /// // /// // /// Any drawing is done over the top of the ListView. This operation should disturb // /// the Graphic as little as possible. Specifically, do not erase the area into which // /// you draw. // /// // /// A Graphic for drawing // /// The contents bounds of the ListView (not including any header) // void DrawFeedback(Graphics g, Rectangle bounds); // /// // /// The user has released the drop over this control // /// // /// // /// Implementators should set args.Effect to the appropriate DragDropEffects. This value is returned // /// to the originator of the drag. // /// // /// // void Drop(DragEventArgs args); // /// // /// A drag has entered this control. // /// // /// Implementators should set args.Effect to the appropriate DragDropEffects. // /// // void Enter(DragEventArgs args); // /// // /// Change the cursor to reflect the current drag operation. // /// // /// // void GiveFeedback(GiveFeedbackEventArgs args); // /// // /// The drag has left the bounds of this control // /// // void Leave(); // /// // /// The drag is moving over this control. // /// // /// This is where any drop target should be calculated. // /// Implementators should set args.Effect to the appropriate DragDropEffects. // /// // /// // void Over(DragEventArgs args); // /// // /// Should the drag be allowed to continue? // /// // /// // void QueryContinue(QueryContinueDragEventArgs args); // } // /// // /// This is a do-nothing implementation of IDropSink that is a useful // /// base class for more sophisticated implementations. // /// // public class AbstractDropSink : IDropSink // { // #region IDropSink Members // /// // /// Gets or sets the ObjectListView that is the drop sink // /// // public virtual ObjectListView ListView { // get { return listView; } // set { this.listView = value; } // } // private ObjectListView listView; // /// // /// Draw any feedback that is appropriate to the current drop state. // /// // /// // /// Any drawing is done over the top of the ListView. This operation should disturb // /// the Graphic as little as possible. Specifically, do not erase the area into which // /// you draw. // /// // /// A Graphic for drawing // /// The contents bounds of the ListView (not including any header) // public virtual void DrawFeedback(Graphics g, Rectangle bounds) { // } // /// // /// The user has released the drop over this control // /// // /// // /// Implementators should set args.Effect to the appropriate DragDropEffects. This value is returned // /// to the originator of the drag. // /// // /// // public virtual void Drop(DragEventArgs args) { // this.Cleanup(); // } // /// // /// A drag has entered this control. // /// // /// Implementators should set args.Effect to the appropriate DragDropEffects. // /// // public virtual void Enter(DragEventArgs args) { // } // /// // /// The drag has left the bounds of this control // /// // public virtual void Leave() { // this.Cleanup(); // } // /// // /// The drag is moving over this control. // /// // /// This is where any drop target should be calculated. // /// Implementators should set args.Effect to the appropriate DragDropEffects. // /// // /// // public virtual void Over(DragEventArgs args) { // } // /// // /// Change the cursor to reflect the current drag operation. // /// // /// You only need to override this if you want non-standard cursors. // /// The standard cursors are supplied automatically. // /// // public virtual void GiveFeedback(GiveFeedbackEventArgs args) { // args.UseDefaultCursors = true; // } // /// // /// Should the drag be allowed to continue? // /// // /// // /// You only need to override this if you want the user to be able // /// to end the drop in some non-standard way, e.g. dragging to a // /// certain point even without releasing the mouse, or going outside // /// the bounds of the application. // /// // /// // public virtual void QueryContinue(QueryContinueDragEventArgs args) { // } // #endregion // #region Commands // /// // /// This is called when the mouse leaves the drop region and after the // /// drop has completed. // /// // protected virtual void Cleanup() { // } // #endregion // } // /// // /// The enum indicates which target has been found for a drop operation // /// // [Flags] // public enum DropTargetLocation // { // /// // /// No applicable target has been found // /// // None = 0, // /// // /// The list itself is the target of the drop // /// // Background = 0x01, // /// // /// An item is the target // /// // Item = 0x02, // /// // /// Between two items (or above the top item or below the bottom item) // /// can be the target. This is not actually ever a target, only a value indicate // /// that it is valid to drop between items // /// // BetweenItems = 0x04, // /// // /// Above an item is the target // /// // AboveItem = 0x08, // /// // /// Below an item is the target // /// // BelowItem = 0x10, // /// // /// A subitem is the target of the drop // /// // SubItem = 0x20, // /// // /// On the right of an item is the target (not currently used) // /// // RightOfItem = 0x40, // /// // /// On the left of an item is the target (not currently used) // /// // LeftOfItem = 0x80 // } // /// // /// This class represents a simple implementation of a drop sink. // /// // /// // /// Actually, it's far from simple and can do quite a lot in its own right. // /// // public class SimpleDropSink : AbstractDropSink // { // #region Life and death // /// // /// Make a new drop sink // /// // public SimpleDropSink() { // this.timer = new Timer(); // this.timer.Interval = 250; // this.timer.Tick += new EventHandler(this.timer_Tick); // this.CanDropOnItem = true; // //this.CanDropOnSubItem = true; // //this.CanDropOnBackground = true; // //this.CanDropBetween = true; // this.FeedbackColor = Color.FromArgb(180, Color.MediumBlue); // this.billboard = new BillboardOverlay(); // } // #endregion // #region Public properties // /// // /// Get or set the locations where a drop is allowed to occur (OR-ed together) // /// // public DropTargetLocation AcceptableLocations { // get { return this.acceptableLocations; } // set { this.acceptableLocations = value; } // } // private DropTargetLocation acceptableLocations; // /// // /// Gets or sets whether this sink allows model objects to be dragged from other lists // /// // public bool AcceptExternal { // get { return this.acceptExternal; } // set { this.acceptExternal = value; } // } // private bool acceptExternal = true; // /// // /// Gets or sets whether the ObjectListView should scroll when the user drags // /// something near to the top or bottom rows. // /// // public bool AutoScroll { // get { return this.autoScroll; } // set { this.autoScroll = value; } // } // private bool autoScroll = true; // /// // /// Gets the billboard overlay that will be used to display feedback // /// messages during a drag operation. // /// // /// Set this to null to stop the feedback. // public BillboardOverlay Billboard { // get { return this.billboard; } // set { this.billboard = value; } // } // private BillboardOverlay billboard; // /// // /// Get or set whether a drop can occur between items of the list // /// // public bool CanDropBetween { // get { return (this.AcceptableLocations & DropTargetLocation.BetweenItems) == DropTargetLocation.BetweenItems; } // set { // if (value) // this.AcceptableLocations |= DropTargetLocation.BetweenItems; // else // this.AcceptableLocations &= ~DropTargetLocation.BetweenItems; // } // } // /// // /// Get or set whether a drop can occur on the listview itself // /// // public bool CanDropOnBackground { // get { return (this.AcceptableLocations & DropTargetLocation.Background) == DropTargetLocation.Background; } // set { // if (value) // this.AcceptableLocations |= DropTargetLocation.Background; // else // this.AcceptableLocations &= ~DropTargetLocation.Background; // } // } // /// // /// Get or set whether a drop can occur on items in the list // /// // public bool CanDropOnItem { // get { return (this.AcceptableLocations & DropTargetLocation.Item) == DropTargetLocation.Item; } // set { // if (value) // this.AcceptableLocations |= DropTargetLocation.Item; // else // this.AcceptableLocations &= ~DropTargetLocation.Item; // } // } // /// // /// Get or set whether a drop can occur on a subitem in the list // /// // public bool CanDropOnSubItem { // get { return (this.AcceptableLocations & DropTargetLocation.SubItem) == DropTargetLocation.SubItem; } // set { // if (value) // this.AcceptableLocations |= DropTargetLocation.SubItem; // else // this.AcceptableLocations &= ~DropTargetLocation.SubItem; // } // } // /// // /// Get or set the index of the item that is the target of the drop // /// // public int DropTargetIndex { // get { return dropTargetIndex; } // set { // if (this.dropTargetIndex != value) { // this.dropTargetIndex = value; // this.ListView.Invalidate(); // } // } // } // private int dropTargetIndex = -1; // /// // /// Get the item that is the target of the drop // /// // public OLVListItem DropTargetItem { // get { // return this.ListView.GetItem(this.DropTargetIndex); // } // } // /// // /// Get or set the location of the target of the drop // /// // public DropTargetLocation DropTargetLocation { // get { return dropTargetLocation; } // set { // if (this.dropTargetLocation != value) { // this.dropTargetLocation = value; // this.ListView.Invalidate(); // } // } // } // private DropTargetLocation dropTargetLocation; // /// // /// Get or set the index of the subitem that is the target of the drop // /// // public int DropTargetSubItemIndex { // get { return dropTargetSubItemIndex; } // set { // if (this.dropTargetSubItemIndex != value) { // this.dropTargetSubItemIndex = value; // this.ListView.Invalidate(); // } // } // } // private int dropTargetSubItemIndex = -1; // /// // /// Get or set the color that will be used to provide drop feedback // /// // public Color FeedbackColor { // get { return this.feedbackColor; } // set { this.feedbackColor = value; } // } // private Color feedbackColor; // /// // /// Get whether the alt key was down during this drop event // /// // public bool IsAltDown { // get { return (this.KeyState & 32) == 32; } // } // /// // /// Get whether any modifier key was down during this drop event // /// // public bool IsAnyModifierDown { // get { return (this.KeyState & (4 + 8 + 32)) != 0; } // } // /// // /// Get whether the control key was down during this drop event // /// // public bool IsControlDown { // get { return (this.KeyState & 8) == 8; } // } // /// // /// Get whether the left mouse button was down during this drop event // /// // public bool IsLeftMouseButtonDown { // get { return (this.KeyState & 1) == 1; } // } // /// // /// Get whether the right mouse button was down during this drop event // /// // public bool IsMiddleMouseButtonDown { // get { return (this.KeyState & 16) == 16; } // } // /// // /// Get whether the right mouse button was down during this drop event // /// // public bool IsRightMouseButtonDown { // get { return (this.KeyState & 2) == 2; } // } // /// // /// Get whether the shift key was down during this drop event // /// // public bool IsShiftDown { // get { return (this.KeyState & 4) == 4; } // } // /// // /// Get or set the state of the keys during this drop event // /// // public int KeyState { // get { return this.keyState; } // set { this.keyState = value; } // } // private int keyState; // #endregion // #region Events // /// // /// Triggered when the sink needs to know if a drop can occur. // /// // /// // /// Handlers should set Effect to indicate what is possible. // /// Handlers can change any of the DropTarget* setttings to change // /// the target of the drop. // /// // public event EventHandler CanDrop; // /// // /// Triggered when the drop is made. // /// // public event EventHandler Dropped; // /// // /// Triggered when the sink needs to know if a drop can occur // /// AND the source is an ObjectListView // /// // /// // /// Handlers should set Effect to indicate what is possible. // /// Handlers can change any of the DropTarget* setttings to change // /// the target of the drop. // /// // public event EventHandler ModelCanDrop; // /// // /// Triggered when the drop is made. // /// AND the source is an ObjectListView // /// // public event EventHandler ModelDropped; // #endregion // #region DropSink Interface // /// // /// Cleanup the drop sink when the mouse has left the control or // /// the drag has finished. // /// // protected override void Cleanup() { // this.DropTargetLocation = DropTargetLocation.None; // this.ListView.FullRowSelect = this.originalFullRowSelect; // this.Billboard.Text = null; // } // /// // /// Draw any feedback that is appropriate to the current drop state. // /// // /// // /// Any drawing is done over the top of the ListView. This operation should disturb // /// the Graphic as little as possible. Specifically, do not erase the area into which // /// you draw. // /// // /// A Graphic for drawing // /// The contents bounds of the ListView (not including any header) // public override void DrawFeedback(Graphics g, Rectangle bounds) { // g.SmoothingMode = ObjectListView.SmoothingMode; // switch (this.DropTargetLocation) { // case DropTargetLocation.Background: // this.DrawFeedbackBackgroundTarget(g, bounds); // break; // case DropTargetLocation.Item: // this.DrawFeedbackItemTarget(g, bounds); // break; // case DropTargetLocation.AboveItem: // this.DrawFeedbackAboveItemTarget(g, bounds); // break; // case DropTargetLocation.BelowItem: // this.DrawFeedbackBelowItemTarget(g, bounds); // break; // } // if (this.Billboard != null) { // this.Billboard.Draw(this.ListView, g, bounds); // } // } // /// // /// The user has released the drop over this control // /// // /// // public override void Drop(DragEventArgs args) { // this.TriggerDroppedEvent(args); // this.timer.Stop(); // this.Cleanup(); // } // /// // /// A drag has entered this control. // /// // /// Implementators should set args.Effect to the appropriate DragDropEffects. // /// // public override void Enter(DragEventArgs args) { // //System.Diagnostics.Debug.WriteLine("Enter"); // /* // * When FullRowSelect is true, we have two problems: // * 1) GetItemRect(ItemOnly) returns the whole row rather than just the icon/text, which messes // * up our calculation of the drop rectangle. // * 2) during the drag, the Timer events will not fire! This is the major problem, since without // * those events we can't autoscroll. // * // * The first problem we can solve through coding, but the second is more difficult. // * We avoid both problems by turning off FullRowSelect during the drop operation. // */ // this.originalFullRowSelect = this.ListView.FullRowSelect; // this.ListView.FullRowSelect = false; // // Setup our drop event args block // this.dropEventArgs = new ModelDropEventArgs(); // this.dropEventArgs.DropSink = this; // this.dropEventArgs.ListView = this.ListView; // this.dropEventArgs.DataObject = args.Data; // OLVDataObject olvData = args.Data as OLVDataObject; // if (olvData != null) { // this.dropEventArgs.SourceListView = olvData.ListView; // this.dropEventArgs.SourceModels = olvData.ModelObjects; // } // this.Over(args); // } // /// // /// The drag is moving over this control. // /// // /// // public override void Over(DragEventArgs args) { // //System.Diagnostics.Debug.WriteLine("Over"); // this.KeyState = args.KeyState; // Point pt = this.ListView.PointToClient(new Point(args.X, args.Y)); // args.Effect = this.CalculateDropAction(args, pt); // this.CheckScrolling(pt); // } // #endregion // #region Events // /// // /// Trigger the Dropped events // /// // /// // protected virtual void TriggerDroppedEvent(DragEventArgs args) { // this.dropEventArgs.Handled = false; // // If the source is an ObjectListView, trigger the ModelDropped event // if (this.dropEventArgs.SourceListView != null) // this.OnModelDropped(this.dropEventArgs); // if (!this.dropEventArgs.Handled) // this.OnDropped(this.dropEventArgs); // } // /// // /// Trigger CanDrop // /// // /// // protected virtual void OnCanDrop(OlvDropEventArgs args) { // if (this.CanDrop != null) // this.CanDrop(this, args); // } // /// // /// Trigger Dropped // /// // /// // protected virtual void OnDropped(OlvDropEventArgs args) { // if (this.Dropped != null) // this.Dropped(this, args); // } // /// // /// Trigger ModelCanDrop // /// // /// // protected virtual void OnModelCanDrop(ModelDropEventArgs args) { // // Don't allow drops from other list, if that's what's configured // if (!this.AcceptExternal && args.SourceListView != null && args.SourceListView != this.ListView) { // args.Effect = DragDropEffects.None; // args.DropTargetLocation = DropTargetLocation.None; // args.InfoMessage = "This list doesn't accept drops from other lists"; // return; // } // if (this.ModelCanDrop != null) // this.ModelCanDrop(this, args); // } // /// // /// Trigger ModelDropped // /// // /// // protected virtual void OnModelDropped(ModelDropEventArgs args) { // if (this.ModelDropped != null) // this.ModelDropped(this, args); // } // #endregion // #region Implementation // private void timer_Tick(object sender, EventArgs e) { // this.HandleTimerTick(); // } // /// // /// Handle the timer tick event, which is sent when the listview should // /// scroll // /// // protected virtual void HandleTimerTick() { // // If the mouse has been released, stop scrolling. // // This is only necessary if the mouse is released outside of the control. // // If the mouse is released inside the control, we would receive a Drop event. // if ((this.IsLeftMouseButtonDown && (Control.MouseButtons & MouseButtons.Left) != MouseButtons.Left) || // (this.IsMiddleMouseButtonDown && (Control.MouseButtons & MouseButtons.Middle) != MouseButtons.Middle) || // (this.IsRightMouseButtonDown && (Control.MouseButtons & MouseButtons.Right) != MouseButtons.Right)) { // this.timer.Stop(); // this.Cleanup(); // return; // } // // Auto scrolling will continune while the mouse is close to the ListView // const int GRACE_PERIMETER = 30; // Point pt = this.ListView.PointToClient(Cursor.Position); // Rectangle r2 = this.ListView.ClientRectangle; // r2.Inflate(GRACE_PERIMETER, GRACE_PERIMETER); // if (r2.Contains(pt)) { // this.ListView.LowLevelScroll(0, this.scrollAmount); // } // } // /// // /// When the mouse is at the given point, what should the target of the drop be? // /// // /// This method should update the DropTarget* members of the given arg block // /// // /// The mouse point, in client co-ordinates // protected virtual void CalculateDropTarget(OlvDropEventArgs args, Point pt) { // const int SMALL_VALUE = 3; // DropTargetLocation location = DropTargetLocation.None; // int targetIndex = -1; // int targetSubIndex = 0; // if (this.CanDropOnBackground) // location = DropTargetLocation.Background; // // Which item is the mouse over? // // If it is not over any item, it's over the background. // //ListViewHitTestInfo info = this.ListView.HitTest(pt.X, pt.Y); // OlvListViewHitTestInfo info = this.ListView.OlvHitTest(pt.X, pt.Y); // if (info.Item != null && this.CanDropOnItem) { // location = DropTargetLocation.Item; // targetIndex = info.Item.Index; // if (info.SubItem != null && this.CanDropOnSubItem) // targetSubIndex = info.Item.SubItems.IndexOf(info.SubItem); // } // // Check to see if the mouse is "between" rows. // // ("between" is somewhat loosely defined) // if (this.CanDropBetween && this.ListView.GetItemCount() > 0) { // // If the mouse is over an item, check to see if it is near the top or bottom // if (location == DropTargetLocation.Item) { // if (pt.Y - SMALL_VALUE <= info.Item.Bounds.Top) // location = DropTargetLocation.AboveItem; // if (pt.Y + SMALL_VALUE >= info.Item.Bounds.Bottom) // location = DropTargetLocation.BelowItem; // } else { // // Is there an item a little below the mouse? // // If so, we say the drop point is above that row // info = this.ListView.OlvHitTest(pt.X, pt.Y + SMALL_VALUE); // if (info.Item != null) { // targetIndex = info.Item.Index; // location = DropTargetLocation.AboveItem; // } else { // // Is there an item a little above the mouse? // info = this.ListView.OlvHitTest(pt.X, pt.Y - SMALL_VALUE); // if (info.Item != null) { // targetIndex = info.Item.Index; // location = DropTargetLocation.BelowItem; // } // } // } // } // args.DropTargetLocation = location; // args.DropTargetIndex = targetIndex; // args.DropTargetSubItemIndex = targetSubIndex; // } // /// // /// What sort of action is possible when the mouse is at the given point? // /// // /// // /// // /// // /// // /// // public virtual DragDropEffects CalculateDropAction(DragEventArgs args, Point pt) { // this.CalculateDropTarget(this.dropEventArgs, pt); // this.dropEventArgs.MouseLocation = pt; // this.dropEventArgs.InfoMessage = null; // this.dropEventArgs.Handled = false; // if (this.dropEventArgs.SourceListView != null) { // this.dropEventArgs.TargetModel = this.ListView.GetModelObject(this.dropEventArgs.DropTargetIndex); // this.OnModelCanDrop(this.dropEventArgs); // } // if (!this.dropEventArgs.Handled) // this.OnCanDrop(this.dropEventArgs); // this.UpdateAfterCanDropEvent(this.dropEventArgs); // return this.dropEventArgs.Effect; // } // /// // /// Based solely on the state of the modifier keys, what drop operation should // /// be used? // /// // /// The drop operation that matches the state of the keys // public DragDropEffects CalculateStandardDropActionFromKeys() { // if (this.IsControlDown) { // if (this.IsShiftDown) // return DragDropEffects.Link; // else // return DragDropEffects.Copy; // } else { // return DragDropEffects.Move; // } // } // /// // /// Should the listview be made to scroll when the mouse is at the given point? // /// // /// // protected virtual void CheckScrolling(Point pt) { // if (!this.AutoScroll) // return; // Rectangle r = this.ListView.ContentRectangle; // int rowHeight = this.ListView.RowHeightEffective; // int close = rowHeight; // // In Tile view, using the whole row height is too much // if (this.ListView.View == View.Tile) // close /= 2; // if (pt.Y <= (r.Top + close)) { // // Scroll faster if the mouse is closer to the top // this.timer.Interval = ((pt.Y <= (r.Top + close / 2)) ? 100 : 350); // this.timer.Start(); // this.scrollAmount = -rowHeight; // } else { // if (pt.Y >= (r.Bottom - close)) { // this.timer.Interval = ((pt.Y >= (r.Bottom - close / 2)) ? 100 : 350); // this.timer.Start(); // this.scrollAmount = rowHeight; // } else { // this.timer.Stop(); // } // } // } // /// // /// Update the state of our sink to reflect the information that // /// may have been written into the drop event args. // /// // /// // protected virtual void UpdateAfterCanDropEvent(OlvDropEventArgs args) { // this.DropTargetIndex = args.DropTargetIndex; // this.DropTargetLocation = args.DropTargetLocation; // this.DropTargetSubItemIndex = args.DropTargetSubItemIndex; // if (this.Billboard != null) { // Point pt = args.MouseLocation; // pt.Offset(5, 5); // if (this.Billboard.Text != this.dropEventArgs.InfoMessage || this.Billboard.Location != pt) { // this.Billboard.Text = this.dropEventArgs.InfoMessage; // this.Billboard.Location = pt; // this.ListView.Invalidate(); // } // } // } // #endregion // #region Rendering // /// // /// Draw the feedback that shows that the background is the target // /// // /// // /// // protected virtual void DrawFeedbackBackgroundTarget(Graphics g, Rectangle bounds) { // float penWidth = 12.0f; // Rectangle r = bounds; // r.Inflate((int)-penWidth / 2, (int)-penWidth / 2); // using (Pen p = new Pen(Color.FromArgb(128, this.FeedbackColor), penWidth)) { // using (GraphicsPath path = this.GetRoundedRect(r, 30.0f)) { // g.DrawPath(p, path); // } // } // } // /// // /// Draw the feedback that shows that an item (or a subitem) is the target // /// // /// // /// // /// // /// DropTargetItem and DropTargetSubItemIndex tells what is the target // /// // protected virtual void DrawFeedbackItemTarget(Graphics g, Rectangle bounds) { // if (this.DropTargetItem == null) // return; // Rectangle r = this.CalculateDropTargetRectangle(this.DropTargetItem, this.DropTargetSubItemIndex); // r.Inflate(1, 1); // float diameter = r.Height / 3; // using (GraphicsPath path = this.GetRoundedRect(r, diameter)) { // using (SolidBrush b = new SolidBrush(Color.FromArgb(48, this.FeedbackColor))) { // g.FillPath(b, path); // } // using (Pen p = new Pen(this.FeedbackColor, 3.0f)) { // g.DrawPath(p, path); // } // } // } // /// // /// Draw the feedback that shows the drop will occur before target // /// // /// // /// // protected virtual void DrawFeedbackAboveItemTarget(Graphics g, Rectangle bounds) { // if (this.DropTargetItem == null) // return; // Rectangle r = this.CalculateDropTargetRectangle(this.DropTargetItem, this.DropTargetSubItemIndex); // this.DrawBetweenLine(g, r.Left, r.Top, r.Right, r.Top); // } // /// // /// Draw the feedback that shows the drop will occur after target // /// // /// // /// // protected virtual void DrawFeedbackBelowItemTarget(Graphics g, Rectangle bounds) { // if (this.DropTargetItem == null) // return; // Rectangle r = this.CalculateDropTargetRectangle(this.DropTargetItem, this.DropTargetSubItemIndex); // this.DrawBetweenLine(g, r.Left, r.Bottom, r.Right, r.Bottom); // } // /// // /// Return a GraphicPath that is round corner rectangle. // /// // /// // /// // /// // protected GraphicsPath GetRoundedRect(Rectangle rect, float diameter) { // GraphicsPath path = new GraphicsPath(); // RectangleF arc = new RectangleF(rect.X, rect.Y, diameter, diameter); // path.AddArc(arc, 180, 90); // arc.X = rect.Right - diameter; // path.AddArc(arc, 270, 90); // arc.Y = rect.Bottom - diameter; // path.AddArc(arc, 0, 90); // arc.X = rect.Left; // path.AddArc(arc, 90, 90); // path.CloseFigure(); // return path; // } // /// // /// Calculate the target rectangle when the given item (and possible subitem) // /// is the target of the drop. // /// // /// // /// // /// // protected virtual Rectangle CalculateDropTargetRectangle(OLVListItem item, int subItem) { // if (subItem > 0) // return item.SubItems[subItem].Bounds; // Rectangle r = this.ListView.CalculateCellTextBounds(item, subItem); // // Allow for indent // if (item.IndentCount > 0) { // int indentWidth = this.ListView.SmallImageSize.Width; // r.X += (indentWidth * item.IndentCount); // r.Width -= (indentWidth * item.IndentCount); // } // return r; // } // /// // /// Draw a "between items" line at the given co-ordinates // /// // /// // /// // /// // /// // /// // protected virtual void DrawBetweenLine(Graphics g, int x1, int y1, int x2, int y2) { // using (Brush b = new SolidBrush(this.FeedbackColor)) { // int x = x1; // int y = y1; // using (GraphicsPath gp = new GraphicsPath()) { // gp.AddLine( // x, y + 5, // x, y - 5); // gp.AddBezier( // x, y - 6, // x + 3, y - 2, // x + 6, y - 1, // x + 11, y); // gp.AddBezier( // x + 11, y, // x + 6, y + 1, // x + 3, y + 2, // x, y + 6); // gp.CloseFigure(); // g.FillPath(b, gp); // } // x = x2; // y = y2; // using (GraphicsPath gp = new GraphicsPath()) { // gp.AddLine( // x, y + 6, // x, y - 6); // gp.AddBezier( // x, y - 7, // x - 3, y - 2, // x - 6, y - 1, // x - 11, y); // gp.AddBezier( // x - 11, y, // x - 6, y + 1, // x - 3, y + 2, // x, y + 7); // gp.CloseFigure(); // g.FillPath(b, gp); // } // } // using (Pen p = new Pen(this.FeedbackColor, 3.0f)) { // g.DrawLine(p, x1, y1, x2, y2); // } // } // #endregion // private Timer timer; // private int scrollAmount; // private bool originalFullRowSelect; // private ModelDropEventArgs dropEventArgs; // } // /// // /// This drop sink allows items within the same list to be rearranged, // /// as well as allowing items to be dropped from other lists. // /// // /// // /// // /// This class can only be used on plain ObjectListViews and FastObjectListViews. // /// The other flavours have no way to implement the insert operation that is required. // /// // /// // /// This class does not work with grouping. // /// // /// // /// This class works when the OLV is sorted, but it is up to the programmer // /// to decide what rearranging such lists "means". Example: if the control is sorting // /// students by academic grade, and the user drags a "Fail" grade student up amonst the "A+" // /// students, it is the responsibility of the programmer to makes the appropriate changes // /// to the model and redraw/rebuild the control so that the users action makes sense. // /// // /// // /// Users of this class should listen for the CanDrop event to decide // /// if models from another OLV can be moved to OLV under this sink. // /// // /// // public class RearrangingDropSink : SimpleDropSink // { // /// // /// Create a RearrangingDropSink // /// // public RearrangingDropSink() { // this.CanDropBetween = true; // this.CanDropOnBackground = true; // this.CanDropOnItem = false; // } // /// // /// Create a RearrangingDropSink // /// // /// // public RearrangingDropSink(bool acceptDropsFromOtherLists) // : this() { // this.AcceptExternal = acceptDropsFromOtherLists; // } // /// // /// Trigger OnModelCanDrop // /// // /// // protected override void OnModelCanDrop(ModelDropEventArgs args) { // base.OnModelCanDrop(args); // if (args.Handled) // return; // args.Effect = DragDropEffects.Move; // // Don't allow drops from other list, if that's what's configured // if (!this.AcceptExternal && args.SourceListView != this.ListView) { // args.Effect = DragDropEffects.None; // args.DropTargetLocation = DropTargetLocation.None; // args.InfoMessage = "This list doesn't accept drops from other lists"; // } // // If we are rearranging a list, don't allow drops on the background // if (args.DropTargetLocation == DropTargetLocation.Background && args.SourceListView == this.ListView) { // args.Effect = DragDropEffects.None; // args.DropTargetLocation = DropTargetLocation.None; // } // } // /// // /// Trigger OnModelDropped // /// // /// // protected override void OnModelDropped(ModelDropEventArgs args) { // base.OnModelDropped(args); // if (!args.Handled) // this.RearrangeModels(args); // } // /// // /// Do the work of processing the dropped items // /// // /// // public virtual void RearrangeModels(ModelDropEventArgs args) { // switch (args.DropTargetLocation) { // case DropTargetLocation.AboveItem: // this.ListView.MoveObjects(args.DropTargetIndex, args.SourceModels); // break; // case DropTargetLocation.BelowItem: // this.ListView.MoveObjects(args.DropTargetIndex + 1, args.SourceModels); // break; // case DropTargetLocation.Background: // this.ListView.AddObjects(args.SourceModels); // break; // default: // return; // } // if (args.SourceListView != this.ListView) { // args.SourceListView.RemoveObjects(args.SourceModels); // } // } // } // /// // /// When a drop sink needs to know if something can be dropped, or // /// to notify that a drop has occured, it uses an instance of this class. // /// // public class OlvDropEventArgs : EventArgs // { // /// // /// Create a OlvDropEventArgs // /// // public OlvDropEventArgs() { // } // #region Data Properties // /// // /// Get the data object that is being dragged // /// // public object DataObject { // get { return this.dataObject; } // internal set { this.dataObject = value; } // } // private object dataObject; // /// // /// Get the drop sink that originated this event // /// // public SimpleDropSink DropSink { // get { return this.dropSink; } // internal set { this.dropSink = value; } // } // private SimpleDropSink dropSink; // /// // /// Get or set the index of the item that is the target of the drop // /// // public int DropTargetIndex { // get { return dropTargetIndex; } // set { this.dropTargetIndex = value; } // } // private int dropTargetIndex = -1; // /// // /// Get or set the location of the target of the drop // /// // public DropTargetLocation DropTargetLocation { // get { return dropTargetLocation; } // set { this.dropTargetLocation = value; } // } // private DropTargetLocation dropTargetLocation; // /// // /// Get or set the index of the subitem that is the target of the drop // /// // public int DropTargetSubItemIndex { // get { return dropTargetSubItemIndex; } // set { this.dropTargetSubItemIndex = value; } // } // private int dropTargetSubItemIndex = -1; // /// // /// Get the item that is the target of the drop // /// // public OLVListItem DropTargetItem { // get { // return this.ListView.GetItem(this.DropTargetIndex); // } // set { // if (value == null) // this.DropTargetIndex = -1; // else // this.DropTargetIndex = value.Index; // } // } // /// // /// Get or set the drag effect that should be used for this operation // /// // public DragDropEffects Effect { // get { return this.effect; } // set { this.effect = value; } // } // private DragDropEffects effect; // /// // /// Get or set if this event was handled. No further processing will be done for a handled event. // /// // public bool Handled { // get { return this.handled; } // set { this.handled = value; } // } // private bool handled; // /// // /// Get or set the feedback message for this operation // /// // /// // /// If this is not null, it will be displayed as a feedback message // /// during the drag. // /// // public string InfoMessage { // get { return this.infoMessage; } // set { this.infoMessage = value; } // } // private string infoMessage; // /// // /// Get the ObjectListView that is being dropped on // /// // public ObjectListView ListView { // get { return this.listView; } // internal set { this.listView = value; } // } // private ObjectListView listView; // /// // /// Get the location of the mouse (in target ListView co-ords) // /// // public Point MouseLocation { // get { return this.mouseLocation; } // internal set { this.mouseLocation = value; } // } // private Point mouseLocation; // /// // /// Get the drop action indicated solely by the state of the modifier keys // /// // public DragDropEffects StandardDropActionFromKeys { // get { // return this.DropSink.CalculateStandardDropActionFromKeys(); // } // } // #endregion // } // /// // /// These events are triggered when the drag source is an ObjectListView. // /// // public class ModelDropEventArgs : OlvDropEventArgs // { // /// // /// Create a ModelDropEventArgs // /// // public ModelDropEventArgs() // { // } // /// // /// Gets the model objects that are being dragged. // /// // public IList SourceModels { // get { return this.dragModels; } // internal set { // this.dragModels = value; // TreeListView tlv = this.SourceListView as TreeListView; // if (tlv != null) { // foreach (object model in this.SourceModels) { // object parent = tlv.GetParent(model); // if (!toBeRefreshed.Contains(parent)) // toBeRefreshed.Add(parent); // } // } // } // } // private IList dragModels; // private ArrayList toBeRefreshed = new ArrayList(); // /// // /// Gets the ObjectListView that is the source of the dragged objects. // /// // public ObjectListView SourceListView { // get { return this.sourceListView; } // internal set { this.sourceListView = value; } // } // private ObjectListView sourceListView; // /// // /// Get the model object that is being dropped upon. // /// // /// This is only value for TargetLocation == Item // public object TargetModel { // get { return this.targetModel; } // internal set { this.targetModel = value; } // } // private object targetModel; // /// // /// Refresh all the objects involved in the operation // /// // public void RefreshObjects() { // TreeListView tlv = this.SourceListView as TreeListView; // if (tlv != null) { // foreach (object model in this.SourceModels) { // object parent = tlv.GetParent(model); // if (!toBeRefreshed.Contains(parent)) // toBeRefreshed.Add(parent); // } // } // toBeRefreshed.AddRange(this.SourceModels); // if (this.ListView == this.SourceListView) { // toBeRefreshed.Add(this.TargetModel); // this.ListView.RefreshObjects(toBeRefreshed); // } else { // this.SourceListView.RefreshObjects(toBeRefreshed); // this.ListView.RefreshObject(this.TargetModel); // } // } // } //}