Files
mRemoteNG/ObjectListView/Implementation/DropSink.cs
2025-02-04 16:05:27 +01:00

1398 lines
53 KiB
C#

///*
// * 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 <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 { return listView; }
// set { this.listView = value; }
// }
// private ObjectListView listView;
// /// <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) {
// this.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() {
// this.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() {
// 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
// /// <summary>
// /// Get or set the locations where a drop is allowed to occur (OR-ed together)
// /// </summary>
// public DropTargetLocation AcceptableLocations {
// get { return this.acceptableLocations; }
// set { this.acceptableLocations = value; }
// }
// private DropTargetLocation acceptableLocations;
// /// <summary>
// /// Gets or sets whether this sink allows model objects to be dragged from other lists
// /// </summary>
// public bool AcceptExternal {
// get { return this.acceptExternal; }
// set { this.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 this.autoScroll; }
// set { this.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 { return this.billboard; }
// set { this.billboard = value; }
// }
// private BillboardOverlay billboard;
// /// <summary>
// /// Get or set whether a drop can occur between items of the list
// /// </summary>
// public bool CanDropBetween {
// get { return (this.AcceptableLocations & DropTargetLocation.BetweenItems) == DropTargetLocation.BetweenItems; }
// set {
// if (value)
// this.AcceptableLocations |= DropTargetLocation.BetweenItems;
// else
// this.AcceptableLocations &= ~DropTargetLocation.BetweenItems;
// }
// }
// /// <summary>
// /// Get or set whether a drop can occur on the listview itself
// /// </summary>
// public bool CanDropOnBackground {
// get { return (this.AcceptableLocations & DropTargetLocation.Background) == DropTargetLocation.Background; }
// set {
// if (value)
// this.AcceptableLocations |= DropTargetLocation.Background;
// else
// this.AcceptableLocations &= ~DropTargetLocation.Background;
// }
// }
// /// <summary>
// /// Get or set whether a drop can occur on items in the list
// /// </summary>
// public bool CanDropOnItem {
// get { return (this.AcceptableLocations & DropTargetLocation.Item) == DropTargetLocation.Item; }
// set {
// if (value)
// this.AcceptableLocations |= DropTargetLocation.Item;
// else
// this.AcceptableLocations &= ~DropTargetLocation.Item;
// }
// }
// /// <summary>
// /// Get or set whether a drop can occur on a subitem in the list
// /// </summary>
// public bool CanDropOnSubItem {
// get { return (this.AcceptableLocations & DropTargetLocation.SubItem) == DropTargetLocation.SubItem; }
// set {
// if (value)
// this.AcceptableLocations |= DropTargetLocation.SubItem;
// else
// this.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 (this.dropTargetIndex != value) {
// this.dropTargetIndex = value;
// this.ListView.Invalidate();
// }
// }
// }
// private int dropTargetIndex = -1;
// /// <summary>
// /// Get the item that is the target of the drop
// /// </summary>
// public OLVListItem DropTargetItem {
// get {
// return this.ListView.GetItem(this.DropTargetIndex);
// }
// }
// /// <summary>
// /// Get or set the location of the target of the drop
// /// </summary>
// public DropTargetLocation DropTargetLocation {
// get { return dropTargetLocation; }
// set {
// if (this.dropTargetLocation != value) {
// this.dropTargetLocation = value;
// this.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 {
// if (this.dropTargetSubItemIndex != value) {
// this.dropTargetSubItemIndex = value;
// this.ListView.Invalidate();
// }
// }
// }
// private int dropTargetSubItemIndex = -1;
// /// <summary>
// /// Get or set the color that will be used to provide drop feedback
// /// </summary>
// public Color FeedbackColor {
// get { return this.feedbackColor; }
// set { this.feedbackColor = value; }
// }
// private Color feedbackColor;
// /// <summary>
// /// Get whether the alt key was down during this drop event
// /// </summary>
// public bool IsAltDown {
// get { return (this.KeyState & 32) == 32; }
// }
// /// <summary>
// /// Get whether any modifier key was down during this drop event
// /// </summary>
// public bool IsAnyModifierDown {
// get { return (this.KeyState & (4 + 8 + 32)) != 0; }
// }
// /// <summary>
// /// Get whether the control key was down during this drop event
// /// </summary>
// public bool IsControlDown {
// get { return (this.KeyState & 8) == 8; }
// }
// /// <summary>
// /// Get whether the left mouse button was down during this drop event
// /// </summary>
// public bool IsLeftMouseButtonDown {
// get { return (this.KeyState & 1) == 1; }
// }
// /// <summary>
// /// Get whether the right mouse button was down during this drop event
// /// </summary>
// public bool IsMiddleMouseButtonDown {
// get { return (this.KeyState & 16) == 16; }
// }
// /// <summary>
// /// Get whether the right mouse button was down during this drop event
// /// </summary>
// public bool IsRightMouseButtonDown {
// get { return (this.KeyState & 2) == 2; }
// }
// /// <summary>
// /// Get whether the shift key was down during this drop event
// /// </summary>
// public bool IsShiftDown {
// get { return (this.KeyState & 4) == 4; }
// }
// /// <summary>
// /// Get or set the state of the keys during this drop event
// /// </summary>
// public int KeyState {
// get { return this.keyState; }
// set { this.keyState = value; }
// }
// private int keyState;
// #endregion
// #region Events
// /// <summary>
// /// Triggered when the sink needs to know if a drop can occur.
// /// </summary>
// /// <remarks>
// /// Handlers should set Effect to indicate what is possible.
// /// Handlers can change any of the DropTarget* setttings to change
// /// the target of the drop.
// /// </remarks>
// public event EventHandler<OlvDropEventArgs> CanDrop;
// /// <summary>
// /// Triggered when the drop is made.
// /// </summary>
// public event EventHandler<OlvDropEventArgs> Dropped;
// /// <summary>
// /// Triggered when the sink needs to know if a drop can occur
// /// AND the source is an ObjectListView
// /// </summary>
// /// <remarks>
// /// Handlers should set Effect to indicate what is possible.
// /// Handlers can change any of the DropTarget* setttings to change
// /// the target of the drop.
// /// </remarks>
// public event EventHandler<ModelDropEventArgs> ModelCanDrop;
// /// <summary>
// /// Triggered when the drop is made.
// /// AND the source is an ObjectListView
// /// </summary>
// public event EventHandler<ModelDropEventArgs> ModelDropped;
// #endregion
// #region DropSink Interface
// /// <summary>
// /// Cleanup the drop sink when the mouse has left the control or
// /// the drag has finished.
// /// </summary>
// protected override void Cleanup() {
// this.DropTargetLocation = DropTargetLocation.None;
// this.ListView.FullRowSelect = this.originalFullRowSelect;
// this.Billboard.Text = null;
// }
// /// <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 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);
// }
// }
// /// <summary>
// /// The user has released the drop over this control
// /// </summary>
// /// <param name="args"></param>
// public override void Drop(DragEventArgs args) {
// this.TriggerDroppedEvent(args);
// this.timer.Stop();
// this.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 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);
// }
// /// <summary>
// /// The drag is moving over this control.
// /// </summary>
// /// <param name="args"></param>
// 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
// /// <summary>
// /// Trigger the Dropped events
// /// </summary>
// /// <param name="args"></param>
// 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);
// }
// /// <summary>
// /// Trigger CanDrop
// /// </summary>
// /// <param name="args"></param>
// protected virtual void OnCanDrop(OlvDropEventArgs args) {
// if (this.CanDrop != null)
// this.CanDrop(this, args);
// }
// /// <summary>
// /// Trigger Dropped
// /// </summary>
// /// <param name="args"></param>
// protected virtual void OnDropped(OlvDropEventArgs args) {
// if (this.Dropped != null)
// this.Dropped(this, args);
// }
// /// <summary>
// /// Trigger ModelCanDrop
// /// </summary>
// /// <param name="args"></param>
// 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);
// }
// /// <summary>
// /// Trigger ModelDropped
// /// </summary>
// /// <param name="args"></param>
// 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();
// }
// /// <summary>
// /// Handle the timer tick event, which is sent when the listview should
// /// scroll
// /// </summary>
// 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);
// }
// }
// /// <summary>
// /// When the mouse is at the given point, what should the target of the drop be?
// /// </summary>
// /// <remarks>This method should update the DropTarget* members of the given arg block</remarks>
// /// <param name="args"></param>
// /// <param name="pt">The mouse point, in client co-ordinates</param>
// 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;
// }
// /// <summary>
// /// What sort of action is possible when the mouse is at the given point?
// /// </summary>
// /// <remarks>
// /// </remarks>
// /// <param name="args"></param>
// /// <param name="pt"></param>
// /// <returns></returns>
// 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;
// }
// /// <summary>
// /// Based solely on the state of the modifier keys, what drop operation should
// /// be used?
// /// </summary>
// /// <returns>The drop operation that matches the state of the keys</returns>
// public DragDropEffects CalculateStandardDropActionFromKeys() {
// if (this.IsControlDown) {
// if (this.IsShiftDown)
// return DragDropEffects.Link;
// else
// return DragDropEffects.Copy;
// } else {
// return DragDropEffects.Move;
// }
// }
// /// <summary>
// /// Should the listview be made to scroll when the mouse is at the given point?
// /// </summary>
// /// <param name="pt"></param>
// 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();
// }
// }
// }
// /// <summary>
// /// Update the state of our sink to reflect the information that
// /// may have been written into the drop event args.
// /// </summary>
// /// <param name="args"></param>
// 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
// /// <summary>
// /// Draw the feedback that shows that the background is the target
// /// </summary>
// /// <param name="g"></param>
// /// <param name="bounds"></param>
// 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);
// }
// }
// }
// /// <summary>
// /// Draw the feedback that shows that an item (or a subitem) is the target
// /// </summary>
// /// <param name="g"></param>
// /// <param name="bounds"></param>
// /// <remarks>
// /// DropTargetItem and DropTargetSubItemIndex tells what is the target
// /// </remarks>
// 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);
// }
// }
// }
// /// <summary>
// /// Draw the feedback that shows the drop will occur before target
// /// </summary>
// /// <param name="g"></param>
// /// <param name="bounds"></param>
// 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);
// }
// /// <summary>
// /// Draw the feedback that shows the drop will occur after target
// /// </summary>
// /// <param name="g"></param>
// /// <param name="bounds"></param>
// 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);
// }
// /// <summary>
// /// Return a GraphicPath that is round corner rectangle.
// /// </summary>
// /// <param name="rect"></param>
// /// <param name="diameter"></param>
// /// <returns></returns>
// 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;
// }
// /// <summary>
// /// Calculate the target rectangle when the given item (and possible subitem)
// /// is the target of the drop.
// /// </summary>
// /// <param name="item"></param>
// /// <param name="subItem"></param>
// /// <returns></returns>
// 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;
// }
// /// <summary>
// /// Draw a "between items" line at the given co-ordinates
// /// </summary>
// /// <param name="g"></param>
// /// <param name="x1"></param>
// /// <param name="y1"></param>
// /// <param name="x2"></param>
// /// <param name="y2"></param>
// 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;
// }
// /// <summary>
// /// This drop sink allows items within the same list to be rearranged,
// /// as well as allowing items to be dropped from other lists.
// /// </summary>
// /// <remarks>
// /// <para>
// /// 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.
// /// </para>
// /// <para>
// /// This class does not work with grouping.
// /// </para>
// /// <para>
// /// 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.
// /// </para>
// /// <para>
// /// 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.
// /// </para>
// /// </remarks>
// public class RearrangingDropSink : SimpleDropSink
// {
// /// <summary>
// /// Create a RearrangingDropSink
// /// </summary>
// public RearrangingDropSink() {
// this.CanDropBetween = true;
// this.CanDropOnBackground = true;
// this.CanDropOnItem = false;
// }
// /// <summary>
// /// Create a RearrangingDropSink
// /// </summary>
// /// <param name="acceptDropsFromOtherLists"></param>
// public RearrangingDropSink(bool acceptDropsFromOtherLists)
// : this() {
// this.AcceptExternal = acceptDropsFromOtherLists;
// }
// /// <summary>
// /// Trigger OnModelCanDrop
// /// </summary>
// /// <param name="args"></param>
// 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;
// }
// }
// /// <summary>
// /// Trigger OnModelDropped
// /// </summary>
// /// <param name="args"></param>
// protected override void OnModelDropped(ModelDropEventArgs args) {
// base.OnModelDropped(args);
// if (!args.Handled)
// this.RearrangeModels(args);
// }
// /// <summary>
// /// Do the work of processing the dropped items
// /// </summary>
// /// <param name="args"></param>
// 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);
// }
// }
// }
// /// <summary>
// /// 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.
// /// </summary>
// public class OlvDropEventArgs : EventArgs
// {
// /// <summary>
// /// Create a OlvDropEventArgs
// /// </summary>
// public OlvDropEventArgs() {
// }
// #region Data Properties
// /// <summary>
// /// Get the data object that is being dragged
// /// </summary>
// public object DataObject {
// get { return this.dataObject; }
// internal set { this.dataObject = value; }
// }
// private object dataObject;
// /// <summary>
// /// Get the drop sink that originated this event
// /// </summary>
// public SimpleDropSink DropSink {
// get { return this.dropSink; }
// internal set { this.dropSink = value; }
// }
// private SimpleDropSink dropSink;
// /// <summary>
// /// Get or set the index of the item that is the target of the drop
// /// </summary>
// public int DropTargetIndex {
// get { return dropTargetIndex; }
// set { this.dropTargetIndex = value; }
// }
// private int dropTargetIndex = -1;
// /// <summary>
// /// Get or set the location of the target of the drop
// /// </summary>
// public DropTargetLocation DropTargetLocation {
// get { return dropTargetLocation; }
// set { this.dropTargetLocation = value; }
// }
// 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 { this.dropTargetSubItemIndex = value; }
// }
// private int dropTargetSubItemIndex = -1;
// /// <summary>
// /// Get the item that is the target of the drop
// /// </summary>
// public OLVListItem DropTargetItem {
// get {
// return this.ListView.GetItem(this.DropTargetIndex);
// }
// set {
// if (value == null)
// this.DropTargetIndex = -1;
// else
// this.DropTargetIndex = value.Index;
// }
// }
// /// <summary>
// /// Get or set the drag effect that should be used for this operation
// /// </summary>
// public DragDropEffects Effect {
// get { return this.effect; }
// set { this.effect = value; }
// }
// private DragDropEffects effect;
// /// <summary>
// /// Get or set if this event was handled. No further processing will be done for a handled event.
// /// </summary>
// public bool Handled {
// get { return this.handled; }
// set { this.handled = value; }
// }
// private bool handled;
// /// <summary>
// /// Get or set the feedback message for this operation
// /// </summary>
// /// <remarks>
// /// If this is not null, it will be displayed as a feedback message
// /// during the drag.
// /// </remarks>
// public string InfoMessage {
// get { return this.infoMessage; }
// set { this.infoMessage = value; }
// }
// private string infoMessage;
// /// <summary>
// /// Get the ObjectListView that is being dropped on
// /// </summary>
// public ObjectListView ListView {
// get { return this.listView; }
// internal set { this.listView = value; }
// }
// private ObjectListView listView;
// /// <summary>
// /// Get the location of the mouse (in target ListView co-ords)
// /// </summary>
// public Point MouseLocation {
// get { return this.mouseLocation; }
// internal set { this.mouseLocation = value; }
// }
// private Point mouseLocation;
// /// <summary>
// /// Get the drop action indicated solely by the state of the modifier keys
// /// </summary>
// public DragDropEffects StandardDropActionFromKeys {
// get {
// return this.DropSink.CalculateStandardDropActionFromKeys();
// }
// }
// #endregion
// }
// /// <summary>
// /// These events are triggered when the drag source is an ObjectListView.
// /// </summary>
// public class ModelDropEventArgs : OlvDropEventArgs
// {
// /// <summary>
// /// Create a ModelDropEventArgs
// /// </summary>
// public ModelDropEventArgs()
// {
// }
// /// <summary>
// /// Gets the model objects that are being dragged.
// /// </summary>
// 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();
// /// <summary>
// /// Gets the ObjectListView that is the source of the dragged objects.
// /// </summary>
// public ObjectListView SourceListView {
// get { return this.sourceListView; }
// internal set { this.sourceListView = value; }
// }
// private ObjectListView sourceListView;
// /// <summary>
// /// Get the model object that is being dropped upon.
// /// </summary>
// /// <remarks>This is only value for TargetLocation == Item</remarks>
// public object TargetModel {
// get { return this.targetModel; }
// internal set { this.targetModel = value; }
// }
// private object targetModel;
// /// <summary>
// /// Refresh all the objects involved in the operation
// /// </summary>
// 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);
// }
// }
// }
//}