context: /*
   * DropSink.cs - Add drop sink ability to an ObjectListView
   *
   * Author: Phillip Piper
   * Date: 2009-03-17 5:15 PM
   *
   * Change log:
   * 2011-04-20   JPP  - Rewrote how ModelDropEventArgs.RefreshObjects() works on TreeListViews
   * v2.4.1
   * 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 <http://www.gnu.org/licenses/>.
   *
   * 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
  {
      /// <summary>
      /// Objects that implement this interface can acts as the receiver for drop
      /// operation for an ObjectListView.
      /// </summary>
      public interface IDropSink
      {
          /// <summary>
          /// Gets or sets the ObjectListView that is the drop sink
          /// </summary>
          ObjectListView ListView { get; set; }
  
          /// <summary>
          /// Draw any feedback that is appropriate to the current drop state.
          /// </summary>
          /// <remarks>
          /// 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. 
          /// </remarks>
          /// <param name="g">A Graphic for drawing</param>
          /// <param name="bounds">The contents bounds of the ListView (not including any header)</param>
          void DrawFeedback(Graphics g, Rectangle bounds);
  
          /// <summary>
          /// The user has released the drop over this control
          /// </summary>
          /// <remarks>
          /// Implementators should set args.Effect to the appropriate DragDropEffects. This value is returned
          /// to the originator of the drag.
          /// </remarks>
          /// <param name="args"></param>
          void Drop(DragEventArgs args);
  
          /// <summary>
          /// A drag has entered this control.
          /// </summary>
          /// <remarks>Implementators should set args.Effect to the appropriate DragDropEffects.</remarks>
          /// <param name="args"></param>
          void Enter(DragEventArgs args);
  
          /// <summary>
          /// Change the cursor to reflect the current drag operation.
          /// </summary>
          /// <param name="args"></param>
          void GiveFeedback(GiveFeedbackEventArgs args);
  
          /// <summary>
          /// The drag has left the bounds of this control
          /// </summary>
          void Leave();
  
          /// <summary>
          /// The drag is moving over this control.
          /// </summary>
          /// <remarks>This is where any drop target should be calculated.
          /// Implementators should set args.Effect to the appropriate DragDropEffects.
          /// </remarks>
          /// <param name="args"></param>
          void Over(DragEventArgs args);
  
          /// <summary>
          /// Should the drag be allowed to continue?
          /// </summary>
          /// <param name="args"></param>
          void QueryContinue(QueryContinueDragEventArgs args);
      }
  
      /// <summary>
      /// This is a do-nothing implementation of IDropSink that is a useful
      /// base class for more sophisticated implementations.
      /// </summary>
      public class AbstractDropSink : IDropSink
      {
          #region IDropSink Members
  
          /// <summary>
          /// Gets or sets the ObjectListView that is the drop sink
          /// </summary>
          public virtual ObjectListView ListView { get; set; }
  
          /// <summary>
          /// Draw any feedback that is appropriate to the current drop state.
          /// </summary>
          /// <remarks>
          /// 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. 
          /// </remarks>
          /// <param name="g">A Graphic for drawing</param>
          /// <param name="bounds">The contents bounds of the ListView (not including any header)</param>
          public virtual void DrawFeedback(Graphics g, Rectangle bounds)
          {
          }
  
          /// <summary>
          /// The user has released the drop over this control
          /// </summary>
          /// <remarks>
          /// Implementators should set args.Effect to the appropriate DragDropEffects. This value is returned
          /// to the originator of the drag.
          /// </remarks>
          /// <param name="args"></param>
          public virtual void Drop(DragEventArgs args)
          {
              Cleanup();
          }
  
          /// <summary>
          /// A drag has entered this control.
          /// </summary>
          /// <remarks>Implementators should set args.Effect to the appropriate DragDropEffects.</remarks>
          /// <param name="args"></param>
          public virtual void Enter(DragEventArgs args)
          {
          }
  
          /// <summary>
          /// The drag has left the bounds of this control
          /// </summary>
          public virtual void Leave()
          {
              Cleanup();
          }
  
          /// <summary>
          /// The drag is moving over this control.
          /// </summary>
          /// <remarks>This is where any drop target should be calculated.
          /// Implementators should set args.Effect to the appropriate DragDropEffects.
          /// </remarks>
          /// <param name="args"></param>
          public virtual void Over(DragEventArgs args)
          {
          }
  
          /// <summary>
          /// Change the cursor to reflect the current drag operation.
          /// </summary>
          /// <remarks>You only need to override this if you want non-standard cursors.
          /// The standard cursors are supplied automatically.</remarks>
          /// <param name="args"></param>
          public virtual void GiveFeedback(GiveFeedbackEventArgs args)
          {
              args.UseDefaultCursors = true;
          }
  
          /// <summary>
          /// Should the drag be allowed to continue?
          /// </summary>
          /// <remarks>
          /// 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. 
          /// </remarks>
          /// <param name="args"></param>
          public virtual void QueryContinue(QueryContinueDragEventArgs args)
          {
          }
  
          #endregion
  
          #region Commands
  
          /// <summary>
          /// This is called when the mouse leaves the drop region and after the
          /// drop has completed.
          /// </summary>
          protected virtual void Cleanup()
          {
          }
  
          #endregion
      }
  
      /// <summary>
      /// The enum indicates which target has been found for a drop operation
      /// </summary>
      [Flags]
      public enum DropTargetLocation
      {
          /// <summary>
          /// No applicable target has been found
          /// </summary>
          None = 0,
  
          /// <summary>
          /// The list itself is the target of the drop
          /// </summary>
          Background = 0x01,
  
          /// <summary>
          /// An item is the target
          /// </summary>
          Item = 0x02,
  
          /// <summary>
          /// 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
          /// </summary>
          BetweenItems = 0x04,
  
          /// <summary>
          /// Above an item is the target
          /// </summary>
          AboveItem = 0x08,
  
          /// <summary>
          /// Below an item is the target
          /// </summary>
          BelowItem = 0x10,
  
          /// <summary>
          /// A subitem is the target of the drop
          /// </summary>
          SubItem = 0x20,
  
          /// <summary>
          /// On the right of an item is the target (not currently used)
          /// </summary>
          RightOfItem = 0x40,
  
          /// <summary>
          /// On the left of an item is the target (not currently used)
          /// </summary>
          LeftOfItem = 0x80
      }
  
      /// <summary>
      /// This class represents a simple implementation of a drop sink.
      /// </summary>
      /// <remarks>
      /// Actually, it's far from simple and can do quite a lot in its own right.
      /// </remarks>
      public class SimpleDropSink : AbstractDropSink
      {
          #region Life and death
  
          /// <summary>
          /// Make a new drop sink
          /// </summary>
          public SimpleDropSink()
          {
              timer = new Timer();
              timer.Interval = 250;
              timer.Tick += timer_Tick;
  
              CanDropOnItem = true;
              //this.CanDropOnSubItem = true;
              //this.CanDropOnBackground = true;
              //this.CanDropBetween = true;
  
              FeedbackColor = Color.FromArgb(180, Color.MediumBlue);
              Billboard = new BillboardOverlay();
          }
  
          #endregion
  
          #region Public properties
  
          /// <summary>
          /// Get or set the locations where a drop is allowed to occur (OR-ed together)
          /// </summary>
          public DropTargetLocation AcceptableLocations { get; set; }
  
          /// <summary>
          /// Gets or sets whether this sink allows model objects to be dragged from other lists
          /// </summary>
          public bool AcceptExternal
          {
              get { return acceptExternal; }
              set { acceptExternal = value; }
          }
  
          private bool acceptExternal = true;
  
          /// <summary>
          /// Gets or sets whether the ObjectListView should scroll when the user drags
          /// something near to the top or bottom rows.
          /// </summary>
          public bool AutoScroll
          {
              get { return autoScroll; }
              set { autoScroll = value; }
          }
  
          private bool autoScroll = true;
  
          /// <summary>
          /// Gets the billboard overlay that will be used to display feedback
          /// messages during a drag operation. 
          /// </summary>
          /// <remarks>Set this to null to stop the feedback.</remarks>
          public BillboardOverlay Billboard { get; set; }
  
          /// <summary>
          /// Get or set whether a drop can occur between items of the list
          /// </summary>
          public bool CanDropBetween
          {
              get { return (AcceptableLocations & DropTargetLocation.BetweenItems) == DropTargetLocation.BetweenItems; }
              set
              {
                  if (value)
                      AcceptableLocations |= DropTargetLocation.BetweenItems;
                  else
                      AcceptableLocations &= ~DropTargetLocation.BetweenItems;
              }
          }
  
          /// <summary>
          /// Get or set whether a drop can occur on the listview itself
          /// </summary>
          public bool CanDropOnBackground
          {
              get { return (AcceptableLocations & DropTargetLocation.Background) == DropTargetLocation.Background; }
              set
              {
                  if (value)
                      AcceptableLocations |= DropTargetLocation.Background;
                  else
                      AcceptableLocations &= ~DropTargetLocation.Background;
              }
          }
  
          /// <summary>
          /// Get or set whether a drop can occur on items in the list
          /// </summary>
          public bool CanDropOnItem
          {
              get { return (AcceptableLocations & DropTargetLocation.Item) == DropTargetLocation.Item; }
              set
              {
                  if (value)
                      AcceptableLocations |= DropTargetLocation.Item;
                  else
                      AcceptableLocations &= ~DropTargetLocation.Item;
              }
          }
  
          /// <summary>
          /// Get or set whether a drop can occur on a subitem in the list
          /// </summary>
          public bool CanDropOnSubItem
          {
              get { return (AcceptableLocations & DropTargetLocation.SubItem) == DropTargetLocation.SubItem; }
              set
              {
                  if (value)
                      AcceptableLocations |= DropTargetLocation.SubItem;
                  else
                      AcceptableLocations &= ~DropTargetLocation.SubItem;
              }
          }
  
          /// <summary>
          /// Get or set the index of the item that is the target of the drop
          /// </summary>
          public int DropTargetIndex
          {
              get { return dropTargetIndex; }
              set
              {
                  if (dropTargetIndex != value)
                  {
                      dropTargetIndex = value;
                      ListView.Invalidate();
                  }
              }
          }
  
          private int dropTargetIndex = -1;
  
          /// <summary>
          /// Get the item that is the target of the drop
          /// </summary>
          public OLVListItem DropTargetItem
          {
              get { return ListView.GetItem(DropTargetIndex); }
          }
  
          /// <summary>
          /// Get or set the location of the target of the drop
          /// </summary>
          public DropTargetLocation DropTargetLocation
          {
              get { return dropTargetLocation; }
              set
              {
                  if (dropTargetLocation != value)
                  {
                      dropTargetLocation = value;
                      ListView.Invalidate();
                  }
              }
          }
  
          private DropTargetLocation dropTargetLocation;
  
          /// <summary>
          /// Get or set the index of the subitem that is the target of the drop
          /// </summary>
          public int DropTargetSubItemIndex
          {
              get { return dropTargetSubItemIndex; }
              set
              {
  
gt:                 if (dropTargetSubItemIndex != value)