//
//
//
//
// $Revision$
//
using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using ICSharpCode.TextEditor.Document;
using ICSharpCode.TextEditor.Util;
namespace ICSharpCode.TextEditor
{
public class TextAreaClipboardHandler
{
TextArea textArea;
public bool EnableCut {
get {
return textArea.EnableCutOrPaste; //textArea.SelectionManager.HasSomethingSelected;
}
}
public bool EnableCopy {
get {
return true; //textArea.SelectionManager.HasSomethingSelected;
}
}
public delegate bool ClipboardContainsTextDelegate();
///
/// Is called when CachedClipboardContainsText should be updated.
/// If this property is null (the default value), the text editor uses
/// System.Windows.Forms.Clipboard.ContainsText.
///
///
/// This property is useful if you want to prevent the default Clipboard.ContainsText
/// behaviour that waits for the clipboard to be available - the clipboard might
/// never become available if it is owned by a process that is paused by the debugger.
///
public static ClipboardContainsTextDelegate GetClipboardContainsText;
public bool EnablePaste {
get {
if (!textArea.EnableCutOrPaste)
return false;
ClipboardContainsTextDelegate d = GetClipboardContainsText;
if (d != null) {
return d();
} else {
try {
return Clipboard.ContainsText();
} catch (ExternalException) {
return false;
}
}
}
}
public bool EnableDelete {
get {
return textArea.SelectionManager.HasSomethingSelected && !textArea.SelectionManager.SelectionIsReadonly;
}
}
public bool EnableSelectAll {
get {
return true;
}
}
public TextAreaClipboardHandler(TextArea textArea)
{
this.textArea = textArea;
textArea.SelectionManager.SelectionChanged += new EventHandler(DocumentSelectionChanged);
}
void DocumentSelectionChanged(object sender, EventArgs e)
{
// ((DefaultWorkbench)WorkbenchSingleton.Workbench).UpdateToolbars();
}
const string LineSelectedType = "MSDEVLineSelect"; // This is the type VS 2003 and 2005 use for flagging a whole line copy
bool CopyTextToClipboard(string stringToCopy, bool asLine)
{
if (stringToCopy.Length > 0) {
DataObject dataObject = new DataObject();
dataObject.SetData(DataFormats.UnicodeText, true, stringToCopy);
if (asLine) {
MemoryStream lineSelected = new MemoryStream(1);
lineSelected.WriteByte(1);
dataObject.SetData(LineSelectedType, false, lineSelected);
}
// Default has no highlighting, therefore we don't need RTF output
if (textArea.Document.HighlightingStrategy.Name != "Default") {
dataObject.SetData(DataFormats.Rtf, RtfWriter.GenerateRtf(textArea));
}
OnCopyText(new CopyTextEventArgs(stringToCopy));
SafeSetClipboard(dataObject);
return true;
} else {
return false;
}
}
// Code duplication: TextAreaClipboardHandler.cs also has SafeSetClipboard
[ThreadStatic] static int SafeSetClipboardDataVersion;
static void SafeSetClipboard(object dataObject)
{
// Work around ExternalException bug. (SD2-426)
// Best reproducable inside Virtual PC.
int version = unchecked(++SafeSetClipboardDataVersion);
try {
Clipboard.SetDataObject(dataObject, true);
} catch (ExternalException) {
Timer timer = new Timer();
timer.Interval = 100;
timer.Tick += delegate {
timer.Stop();
timer.Dispose();
if (SafeSetClipboardDataVersion == version) {
try {
Clipboard.SetDataObject(dataObject, true, 10, 50);
} catch (ExternalException) { }
}
};
timer.Start();
}
}
bool CopyTextToClipboard(string stringToCopy)
{
return CopyTextToClipboard(stringToCopy, false);
}
public void Cut(object sender, EventArgs e)
{
if (textArea.SelectionManager.HasSomethingSelected) {
if (CopyTextToClipboard(textArea.SelectionManager.SelectedText)) {
if (textArea.SelectionManager.SelectionIsReadonly)
return;
// Remove text
textArea.BeginUpdate();
textArea.Caret.Position = textArea.SelectionManager.SelectionCollection[0].StartPosition;
textArea.SelectionManager.RemoveSelectedText();
textArea.EndUpdate();
}
} else if (textArea.Document.TextEditorProperties.CutCopyWholeLine) {
// No text was selected, select and cut the entire line
int curLineNr = textArea.Document.GetLineNumberForOffset(textArea.Caret.Offset);
LineSegment lineWhereCaretIs = textArea.Document.GetLineSegment(curLineNr);
string caretLineText = textArea.Document.GetText(lineWhereCaretIs.Offset, lineWhereCaretIs.TotalLength);
textArea.SelectionManager.SetSelection(textArea.Document.OffsetToPosition(lineWhereCaretIs.Offset), textArea.Document.OffsetToPosition(lineWhereCaretIs.Offset + lineWhereCaretIs.TotalLength));
if (CopyTextToClipboard(caretLineText, true)) {
if (textArea.SelectionManager.SelectionIsReadonly)
return;
// remove line
textArea.BeginUpdate();
textArea.Caret.Position = textArea.Document.OffsetToPosition(lineWhereCaretIs.Offset);
textArea.SelectionManager.RemoveSelectedText();
textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToEnd, new TextLocation(0, curLineNr)));
textArea.EndUpdate();
}
}
}
public void Copy(object sender, EventArgs e)
{
if (!CopyTextToClipboard(textArea.SelectionManager.SelectedText) && textArea.Document.TextEditorProperties.CutCopyWholeLine) {
// No text was selected, select the entire line, copy it, and then deselect
int curLineNr = textArea.Document.GetLineNumberForOffset(textArea.Caret.Offset);
LineSegment lineWhereCaretIs = textArea.Document.GetLineSegment(curLineNr);
string caretLineText = textArea.Document.GetText(lineWhereCaretIs.Offset, lineWhereCaretIs.TotalLength);
CopyTextToClipboard(caretLineText, true);
}
}
public void Paste(object sender, EventArgs e)
{
if (!textArea.EnableCutOrPaste) {
return;
}
// Clipboard.GetDataObject may throw an exception...
for (int i = 0;; i++) {
try {
IDataObject data = Clipboard.GetDataObject();
if (data == null)
return;
bool fullLine = data.GetDataPresent(LineSelectedType);
if (data.GetDataPresent(DataFormats.UnicodeText)) {
string text = (string)data.GetData(DataFormats.UnicodeText);
// we got NullReferenceExceptions here, apparently the clipboard can contain null strings
if (!string.IsNullOrEmpty(text)) {
textArea.Document.UndoStack.StartUndoGroup();
try {
if (textArea.SelectionManager.HasSomethingSelected) {
textArea.Caret.Position = textArea.SelectionManager.SelectionCollection[0].StartPosition;
textArea.SelectionManager.RemoveSelectedText();
}
if (fullLine) {
int col = textArea.Caret.Column;
textArea.Caret.Column = 0;
if (!textArea.IsReadOnly(textArea.Caret.Offset))
textArea.InsertString(text);
textArea.Caret.Column = col;
} else {
// textArea.EnableCutOrPaste already checked readonly for this case
textArea.InsertString(text);
}
} finally {
textArea.Document.UndoStack.EndUndoGroup();
}
}
}
return;
} catch (ExternalException) {
// GetDataObject does not provide RetryTimes parameter
if (i > 5) throw;
}
}
}
public void Delete(object sender, EventArgs e)
{
new ICSharpCode.TextEditor.Actions.Delete().Execute(textArea);
}
public void SelectAll(object sender, EventArgs e)
{
new ICSharpCode.TextEditor.Actions.SelectWholeDocument().Execute(textArea);
}
protected virtual void OnCopyText(CopyTextEventArgs e)
{
if (CopyText != null) {
CopyText(this, e);
}
}
public event CopyTextEventHandler CopyText;
}
public delegate void CopyTextEventHandler(object sender, CopyTextEventArgs e);
public class CopyTextEventArgs : EventArgs
{
string text;
public string Text {
get {
return text;
}
}
public CopyTextEventArgs(string text)
{
this.text = text;
}
}
}