/// <summary> /// Determines whether two <see cref="DragDropInfo"/> objects have the same settings. /// </summary> /// <param name="obj">The object to compare.</param> /// <returns><c>true</c> if the two objects have the same settings, otherwise <c>false</c>.</returns> public override bool Equals(object obj) { DragDropInfo other = obj as DragDropInfo; if (obj != null) { return(Location == other.Location && KeyStates == other.KeyStates && Data == other.Data && AllowedEffects == other.AllowedEffects && IsInternal == other.IsInternal && Source == other.Source && VirtualBufferPosition == other.VirtualBufferPosition); } else { return(false); } }
/// <summary> /// Determines whether the handler can accept data for a drag and drop operation. /// </summary> /// <param name="dragDropInfo"> /// Information about the drag and drop operation. /// </param> /// <returns><c>true</c> if the handler can accept data now, otherwise <c>false</c>.</returns> /// <remarks> /// <para>This method is used by the editor to check whether the drop handler can accept data /// after it has been designated to handle a drag and drop operation. For example, /// the drop handler may be able to handle data only if the view is not read-only. /// The implementation of this method would check the read-only status of the view.</para> /// <para>If one drop handler returns <c>false</c>, /// another drop handler might be used to handle the drop operation, even if /// the ordering of <see cref="IDropHandler"/> objects dictates otherwise.</para> /// </remarks> public virtual bool IsDropEnabled(DragDropInfo dragDropInfo) { if (dragDropInfo == null) { throw new ArgumentNullException(nameof(dragDropInfo)); } //ensure the source allows either move or copy operation if (!( (dragDropInfo.AllowedEffects & NSDragOperation.Copy) == NSDragOperation.Copy || (dragDropInfo.AllowedEffects & NSDragOperation.Move) == NSDragOperation.Move)) { return(false); } //only allow dropping when the view is not read-only return(!_cocoaTextView.Options.GetOptionValue <bool>(DefaultTextViewOptions.ViewProhibitUserInputId)); }
/// <summary> /// Determines what drag & drop effect should be displayed to the user based on the state of the operation. /// </summary> protected virtual DragDropPointerEffects GetDragDropEffect(DragDropInfo dragDropInfo) { if (dragDropInfo == null) { throw new ArgumentNullException(nameof(dragDropInfo)); } VirtualSnapshotPoint dropPoint = dragDropInfo.VirtualBufferPosition.TranslateTo(_cocoaTextView.TextSnapshot); //if an external drop is being performed on a read-only region, then disallow it if (_cocoaTextView.TextBuffer.IsReadOnly(dropPoint.Position)) { return(DragDropPointerEffects.None); } //determine mode based on user key pressings if (((dragDropInfo.AllowedEffects & NSDragOperation.Copy) == NSDragOperation.Copy) && ((dragDropInfo.KeyStates & NSEventModifierMask.AlternateKeyMask) == NSEventModifierMask.AlternateKeyMask)) { return(DragDropPointerEffects.Copy | DragDropPointerEffects.Track); } //if (((dragDropInfo.AllowedEffects & NSDragOperation.Move) == NSDragOperation.Move) && ((dragDropInfo.KeyStates & NSEventModifierMask.ShiftKey) == NSEventModifierMask.ShiftKey)) // return DragDropPointerEffects.Move | DragDropPointerEffects.Track; //if control flow gets here, then the user's key pressings must be ignored in order to continue the drag/drop operation because //the combination of the user's key pressings with the allowed drag/drop effects is invalid. //if move mode is allowed, set move effect if ((dragDropInfo.AllowedEffects & NSDragOperation.Move) == NSDragOperation.Move) { return(DragDropPointerEffects.Move | DragDropPointerEffects.Track); } //if copy mode is allowed, then indicate copy mode if ((dragDropInfo.AllowedEffects & NSDragOperation.Copy) == NSDragOperation.Copy) { return(DragDropPointerEffects.Copy | DragDropPointerEffects.Track); } return(DragDropPointerEffects.None); }
/// <summary> /// Indicates that the drag and drop operation is in progress. /// </summary> /// <param name="dragDropInfo"> /// Information about the drag and drop operation in progress. /// </param> /// <returns> /// A <see cref="DragDropPointerEffects"/> for the current operation. For example, this can be used to /// indicate a copy operation when the CTRL key is down. /// </returns> /// <remarks>This method is called continuously while the user is dragging the mouse over the text editor during /// a drag and drop operation. It can be used to /// draw additional information next to the mouse cursor as a preview of the text after the drop operation. /// </remarks> public virtual DragDropPointerEffects HandleDraggingOver(DragDropInfo dragDropInfo) { return(this.GetDragDropEffect(dragDropInfo)); }
/// <summary> /// This method selects the text at the end of the drop operation. /// </summary> /// <remarks> /// This method will only be called if the drop of data resulted in an <see cref="DragDropEffects"/> other than DragDropEffects.None. /// </remarks> /// <param name="insertionPoint">The position at which data was inserted.</param> /// <param name="dataLength">The length of the data inserted in the buffer.</param> /// <param name="virtualSpaceLength">The length of whitespace inserted in the buffer to fill the gap between the closest buffer position /// and the position at which data was dropped. This value will be non-zero only if data was dropped into virtual space.</param> /// <param name="dragDropInfo">The <see cref="DragDropInfo"/> class containing information about the drop.</param> /// <param name="reverse">True if the existing selection prior to the drop was reversed.</param> protected virtual void SelectText(SnapshotPoint insertionPoint, int dataLength, DragDropInfo dragDropInfo, bool reverse) { if (insertionPoint == null) { throw new ArgumentNullException(nameof(insertionPoint)); } if (dragDropInfo == null) { throw new ArgumentNullException(nameof(dragDropInfo)); } VirtualSnapshotPoint anchorPoint = new VirtualSnapshotPoint(insertionPoint); VirtualSnapshotPoint activePoint = new VirtualSnapshotPoint(insertionPoint.Add(dataLength)); if (dragDropInfo.IsInternal && reverse) { _editorOperations.SelectAndMoveCaret(activePoint, anchorPoint, TextSelectionMode.Stream); } else { _editorOperations.SelectAndMoveCaret(anchorPoint, activePoint, TextSelectionMode.Stream); } }
/// <summary> /// This method is called after the edits are made to the buffer to perform any necessary post edit actions. /// </summary> /// <param name="successfulEdit">If true, the edits performed on the buffer were successful, otherwise, the edits failed.</param> /// <param name="dragDropInfo">The <see cref="DragDropInfo"/> holding information about the currently ongoing drag/drop operation.</param> protected abstract void PerformPostEditActions(DragDropInfo dragDropInfo, bool successfulEdit);
/// <summary> /// This method is called before edits are made to the buffer to perform any necessary pre edit actions. /// </summary> /// <param name="dragDropInfo">The <see cref="DragDropInfo"/> holding information about the currently ongoing drag/drop operation.</param> protected abstract void PerformPreEditActions(DragDropInfo dragDropInfo);
/// <summary> /// This method extracts the text of an <see cref="DragDropInfo"/> object. /// </summary> protected abstract string ExtractText(DragDropInfo dragDropInfo);
/// <summary> /// Indicates that the drag and drop operation has completed, and that the final tasks, if any, should be performed now. /// </summary> /// <param name="dragDropInfo"> /// Information about the drag and drop operation in progress. /// </param> /// <returns> /// The drag and drop effects of this drop operation. For example, if the drop operation has moved data, /// DragDropPointerEffects.Move should be returned. /// </returns> /// <remarks>This method is called when the user drops the data onto the editor. /// This marks the end of a drag and drop operation. /// The <see cref="IDropHandler"/> is expected to perform the final tasks of the operation. /// </remarks> public virtual DragDropPointerEffects HandleDataDropped(DragDropInfo dragDropInfo) { if (dragDropInfo == null) { throw new ArgumentNullException(nameof(dragDropInfo)); } ITextSelection selection = _cocoaTextView.Selection; //keeps track of the result of this operation DragDropPointerEffects result = DragDropPointerEffects.None; //tracks the location at which the data was dropped VirtualSnapshotPoint dropLocation = dragDropInfo.VirtualBufferPosition; //convert the drag/drop data to text string dragDropText = this.ExtractText(dragDropInfo); bool isReversed = selection.IsReversed; bool copyRequested = (dragDropInfo.KeyStates & NSEventModifierMask.AlternateKeyMask) == NSEventModifierMask.AlternateKeyMask; bool copyAllowed = (dragDropInfo.AllowedEffects & NSDragOperation.Copy) == NSDragOperation.Copy; ITextSnapshot preEditSnapshot = _cocoaTextView.TextSnapshot; // track the point where the data will be inserted ITrackingPoint insertionPoint = preEditSnapshot.CreateTrackingPoint(dropLocation.Position, PointTrackingMode.Negative); // track the currently selected spans before any edits are performed on the buffer List <ITrackingSpan> selectionSpans = new List <ITrackingSpan>(); foreach (SnapshotSpan selectedSpan in selection.SelectedSpans) { selectionSpans.Add(preEditSnapshot.CreateTrackingSpan(selectedSpan, SpanTrackingMode.EdgeExclusive)); } // perform any necessary pre edit actions this.PerformPreEditActions(dragDropInfo); // clear selection before data operations if (!selection.IsEmpty) { selection.Clear(); } // a reference to the snapshot resulting from the edits bool successfulEdit = false; // if the data is being dropped in virtual space, calculate how many whitespace characters will be inserted // to fill the gap between the dropped point and the closest buffer position int virtualSpaceLength = 0; if (dragDropInfo.VirtualBufferPosition.IsInVirtualSpace) { virtualSpaceLength = _editorOperations.GetWhitespaceForVirtualSpace(dragDropInfo.VirtualBufferPosition).Length; } if (copyRequested && copyAllowed) { //copy the data by inserting it in the buffer successfulEdit = this.InsertText(dropLocation, dragDropText); if (successfulEdit) { result = DragDropPointerEffects.Copy; } } else { //the data needs to be moved if (dragDropInfo.IsInternal) { //delete the existing selection, and add the data to the new location successfulEdit = this.MoveText(dropLocation, selectionSpans, dragDropText); } else { //the drag is not from this text view, just insert the data at dropLocation successfulEdit = this.InsertText(dropLocation, dragDropText); } //set the pointer effect to move if the edit was successful since that implies that the data was moved successfully if (successfulEdit) { result = DragDropPointerEffects.Move; } } // finally select the newly inserted data if the operation was successful if (result != DragDropPointerEffects.None) { SnapshotPoint textInsertionPoint = insertionPoint.GetPoint(_cocoaTextView.TextSnapshot); // if the data was inserted in virtual space, offset the selection's anchor point by the whitespace that was inserted // in virtual space if (virtualSpaceLength != 0) { textInsertionPoint = textInsertionPoint.Add(virtualSpaceLength); } this.SelectText(textInsertionPoint, dragDropText.Length, dragDropInfo, isReversed); } // perform any post edit actions as necessary this.PerformPostEditActions(dragDropInfo, successfulEdit); return(result); }