Index: /CKEditor/trunk/_source/core/command.js
===================================================================
--- /CKEditor/trunk/_source/core/command.js	(revision 3084)
+++ /CKEditor/trunk/_source/core/command.js	(revision 3085)
@@ -8,7 +8,7 @@
 	this.state = ( 'state' in commandDefinition ) ? commandDefinition.state : CKEDITOR.TRISTATE_OFF;
 
-	this.exec = function()
+	this.exec = function( data )
 	{
-		commandDefinition.exec.call( this, editor );
+		return commandDefinition.exec.call( this, editor, data );
 	};
 
Index: /CKEditor/trunk/_source/core/config.js
===================================================================
--- /CKEditor/trunk/_source/core/config.js	(revision 3084)
+++ /CKEditor/trunk/_source/core/config.js	(revision 3085)
@@ -148,5 +148,5 @@
 	 */
 
-	plugins : 'basicstyles,blockquote,button,elementspath,horizontalrule,htmldataprocessor,image,indent,justify,keystrokes,link,list,newpage,pagebreak,pastefromword,preview,print,removeformat,smiley,sourcearea,table,specialchar,tab,toolbar,wysiwygarea',
+	plugins : 'basicstyles,blockquote,button,clipboard,elementspath,horizontalrule,htmldataprocessor,image,indent,justify,keystrokes,link,list,newpage,pagebreak,pastefromword,pastetext,preview,print,removeformat,smiley,sourcearea,table,specialchar,tab,toolbar,wysiwygarea',
 
 	/**
Index: /CKEditor/trunk/_source/core/editor.js
===================================================================
--- /CKEditor/trunk/_source/core/editor.js	(revision 3084)
+++ /CKEditor/trunk/_source/core/editor.js	(revision 3085)
@@ -388,5 +388,5 @@
 			var command = this.getCommand( commandName );
 			if ( command && command.state != CKEDITOR.TRISTATE_DISABLED )
-				return command.exec( this, data );
+				return command.exec( data );
 
 			// throw 'Unknown command name "' + commandName + '"';
Index: /CKEditor/trunk/_source/lang/en.js
===================================================================
--- /CKEditor/trunk/_source/lang/en.js	(revision 3084)
+++ /CKEditor/trunk/_source/lang/en.js	(revision 3085)
@@ -32,4 +32,7 @@
 	newPage			: 'New Page',
 	preview			: 'Preview',
+	cut				: 'Cut',
+	copy			: 'Copy',
+	paste			: 'Paste',
 	print			: 'Print',
 	underline		: 'Underline',
@@ -393,4 +396,13 @@
 	blockquote : 'Blockquote',
 
+	clipboard :
+	{
+		title		: 'Paste',
+		cutError	: 'Your browser security settings don\'t permit the editor to automatically execute cutting operations. Please use the keyboard for that (Ctrl+X).',
+		copyError	: 'Your browser security settings don\'t permit the editor to automatically execute copying operations. Please use the keyboard for that (Ctrl+C).',
+		pasteMsg	: 'Please paste inside the following box using the keyboard (Ctrl+V) and hit OK',
+		securityMsg	: 'Because of your browser security settings, the editor is not able to access your clipboard data directly. You are required to paste it again in this window.'
+	},
+
 	pastefromword :
 	{
@@ -400,4 +412,10 @@
 		ignoreFontFace : 'Ignore Font Face definitions',
 		removeStyle : 'Remove Styles definitions'
+	},
+
+	pasteText :
+	{
+		button : 'Paste as plain text',
+		title : 'Paste as Plain Text'
 	}
 };
Index: /CKEditor/trunk/_source/plugins/clipboard/dialogs/paste.js
===================================================================
--- /CKEditor/trunk/_source/plugins/clipboard/dialogs/paste.js	(revision 3085)
+++ /CKEditor/trunk/_source/plugins/clipboard/dialogs/paste.js	(revision 3085)
@@ -0,0 +1,124 @@
+/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.dialog.add( 'paste', function( editor )
+{
+	var isCustomDomain = CKEDITOR.env.ie && document.domain != window.location.hostname;
+
+	var iframeId = 'cke_' + CKEDITOR.tools.getNextNumber();
+
+	// For document.domain compatibility (#123) we must do all the magic in
+	// the URL for IE.
+	var src =
+		isCustomDomain ?
+			'javascript:void((function(){' +
+				'document.open();' +
+				'document.domain=\'' + document.domain + '\';' +
+				'document.write( window.parent.CKEDITOR._htmlToLoad );' +
+				'document.close();' +
+				'document.body.contentEditable = true;' +
+				'delete window.parent.CKEDITOR._htmlToLoad;' +
+				'window.focus();' +
+			'})())'
+		:
+			'javascript:void(0)';
+
+	var iframeHtml =
+		'<iframe' +
+			' src="' + src + '"' +
+			' id="' + iframeId + '"' +
+			' style="width:346px;background-color:white;height:130px;border:1px solid black"' +
+			' frameborder="0"' +
+			' allowtransparency="1">' +
+		'</iframe>';
+
+	var htmlToLoad =
+		'<!doctype html>' +
+		'<script type="text/javascript">' +
+			'window.onload = function()' +
+			'{' +
+				( CKEDITOR.env.ie ?
+					'document.body.contentEditable = "true";' :
+					'document.designMode = "on";' ) +
+				'window.focus();' +
+			'};' +
+			// Avoid errors if the pasted content has any script that
+			// fails. (#389)
+			'window.onerror = function()' +
+			'{' +
+				'return true;' +
+			'};' +
+		'</script><body style="margin:3px"></body>';
+
+	return {
+		title : editor.lang.clipboard.title,
+
+		minWidth : 400,
+		minHeight : 330,
+
+		onShow : function()
+		{
+			if ( isCustomDomain )
+				CKEDITOR._htmlToLoad = htmlToLoad;
+			else
+			{
+				var iframe = CKEDITOR.document.getById( iframeId );
+
+				var $doc = iframe.$.contentWindow.document;
+				$doc.open();
+				$doc.write( htmlToLoad );
+				$doc.close();
+
+				iframe.$.contentWindow.focus();
+			}
+		},
+
+		onOk : function()
+		{
+			var iframe = CKEDITOR.document.getById( iframeId );
+
+			var body = new CKEDITOR.dom.element( 
+				iframe.$.contentDocument ?
+					oBody = iframe.$.contentDocument.body :
+					oBody = iframe.$.contentWindow.document.body ) ;
+					
+			var html = body.getHtml();
+
+			// Fix relative anchor URLs (IE automatically adds the current page URL).
+			var re = new RegExp( window.location + "#", 'g' );
+			html = html.replace( re, '#');
+
+			this.restoreSelection();
+			this.clearSavedSelection();
+
+			this.getParentEditor().insertHtml( html );
+		},
+
+		contents : [
+			{
+				id : 'general',
+				label : editor.lang.common.generalTab,
+				elements : [
+					{
+						type : 'html',
+						id : 'securityMsg',
+						html : '<div style="white-space:normal;width:340px;">' + editor.lang.clipboard.securityMsg + '</div>'
+					},
+					{
+						type : 'html',
+						id : 'pasteMsg',
+						html : '<div style="white-space:normal;width:340px;">'+editor.lang.clipboard.pasteMsg +'</div>'
+					},
+					{
+						type : 'html',
+						id : 'content',
+						style : 'width:340px;height:130px',
+						html : '<div>' + iframeHtml + '</div>'
+					}
+				]
+			}
+		]
+	};
+});
Index: /CKEditor/trunk/_source/plugins/clipboard/plugin.js
===================================================================
--- /CKEditor/trunk/_source/plugins/clipboard/plugin.js	(revision 3085)
+++ /CKEditor/trunk/_source/plugins/clipboard/plugin.js	(revision 3085)
@@ -0,0 +1,157 @@
+/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @file Clipboard support
+ */
+
+(function()
+{
+	// Tries to execute any of the paste, cut or copy commands in IE. Returns a
+	// boolean indicating that the operation succeeded.
+	var execIECommand = function( editor, command )
+	{
+		var doc = editor.document,
+			body = doc.getBody();
+
+		var	enabled = false;
+		var onExec = function()
+		{
+			enabled = true;
+		};
+		
+		// The following seems to be the only reliable way to detect that
+		// clipboard commands are enabled in IE. It will fire the
+		// onpaste/oncut/oncopy events only if the security settings allowed
+		// the command to execute.
+		body.on( command, onExec );
+
+		doc.$.execCommand( command );
+
+		body.removeListener( command, onExec );
+		
+		return enabled;
+	};
+
+	// Attempts to execute the Cut and Copy operations.
+	var tryToCutCopy =
+		CKEDITOR.env.ie ?
+			function( editor, type )
+			{
+				return execIECommand( editor, type );
+			}
+		:		// !IE.
+			function( editor, type )
+			{
+				try
+				{
+					// Other browsers throw an error if the command is disabled.
+					return editor.document.$.execCommand( type );
+				}
+				catch( e )
+				{
+					return false;
+				}
+			};
+
+	// A class that represents one of the cut or copy commands.
+	var cutCopyCmd = function( type )
+	{
+		this.type = type;
+	};
+
+	cutCopyCmd.prototype =
+	{
+		exec : function( editor, data )
+		{
+			var success = tryToCutCopy( editor, this.type );
+
+			if ( !success )
+				alert( editor.lang.clipboard[ this.type + 'Error' ] );		// Show cutError or copyError.
+
+			return success;
+		}
+	};
+
+	// Paste command.
+	var pasteCmd =
+		CKEDITOR.env.ie ?
+			{
+				exec : function( editor, data )
+				{
+					// Prevent IE from pasting at the begining of the document.
+					editor.focus();
+
+					if ( !editor.fire( 'beforePaste' ) 
+						&& !execIECommand( editor, 'paste' ) )
+					{
+							editor.openDialog( 'paste' );
+					}
+				}
+			}
+		:
+			{
+				exec : function( editor )
+				{
+					try
+					{
+						if ( !editor.fire( 'beforePaste' )
+							&& !editor.document.$.execCommand( 'Paste', false, null ) )
+						{
+							throw 0;
+						}
+					}
+					catch ( e )
+					{
+						// Open the paste dialog.
+						editor.openDialog( 'paste' );
+					}
+				}
+			};
+
+	// Listens for some clipboard related keystrokes, so they get customized.
+	var onKey = function( event )
+	{
+		switch ( event.data.keyCode )
+		{
+			// Paste
+			case CKEDITOR.CTRL + 86 :		// CTRL+V
+			case CKEDITOR.SHIFT + 45 :		// SHIFT+INS
+				if ( this.fire( 'beforePaste' ) )
+					event.cancel();
+				return;
+
+			// Cut
+			case CKEDITOR.CTRL + 88 :		// CTRL+X
+			case CKEDITOR.SHIFT + 46 :		// SHIFT+DEL
+				// Save Undo snapshot.
+		}
+	};
+
+	// Register the plugin.
+	CKEDITOR.plugins.add( 'clipboard',
+		{
+			init : function( editor )
+			{
+				var addButtonCommand = function ( buttonName, commandName, command )
+				{
+					editor.addCommand( commandName, command );
+					editor.ui.addButton( buttonName,
+						{
+							label : editor.lang[ commandName ],
+							command : commandName
+						});
+				}
+
+				addButtonCommand( 'Cut', 'cut', new cutCopyCmd( 'cut' ) );
+				addButtonCommand( 'Copy', 'copy', new cutCopyCmd( 'copy' ) );
+				addButtonCommand( 'Paste', 'paste', pasteCmd );
+
+				CKEDITOR.dialog.add( 'paste', CKEDITOR.getUrl( this.path + 'dialogs/paste.js' ) );
+
+				editor.on( 'key', onKey, editor );
+			}
+		});
+})();
Index: /CKEditor/trunk/_source/plugins/keystrokes/plugin.js
===================================================================
--- /CKEditor/trunk/_source/plugins/keystrokes/plugin.js	(revision 3084)
+++ /CKEditor/trunk/_source/plugins/keystrokes/plugin.js	(revision 3085)
@@ -76,4 +76,6 @@
 (function()
 {
+	var cancel;
+
 	var onKeyDown = function( event )
 	{
@@ -84,10 +86,13 @@
 		var command = this.keystrokes[ keyCombination ];
 
-		var cancel = !this._.editor.fire( 'key', { keyCode : keyCombination } );
+		cancel = ( this._.editor.fire( 'key', { keyCode : keyCombination } ) === true );
 
 		if ( !cancel )
 		{
 			if ( command )
-				cancel = ( this._.editor.execCommand( command ) !== false );
+			{
+				var data = { from : 'keystrokeHandler' };
+				cancel = ( this._.editor.execCommand( command, data ) !== false );
+			}
 
 			if ( !cancel )
@@ -99,4 +104,13 @@
 
 		return !cancel;
+	};
+
+	var onKeyPress = function( event )
+	{
+		if ( cancel )
+		{
+			cancel = false;
+			event.data.preventDefault( true );
+		}
 	};
 
@@ -112,5 +126,12 @@
 		attach : function( domObject )
 		{
+			// For most browsers, it is enough to listen to the keydown event
+			// only.
 			domObject.on( 'keydown', onKeyDown, this );
+
+			// Some browsers instead, don't cancel key events in the keydown, but in the
+			// keypress. So we must do a longer trip in those cases.
+			if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) )
+				domObject.on( 'keypress', onKeyPress, this );
 		}
 	};
@@ -143,8 +164,4 @@
 	[ CKEDITOR.ALT + 122 /*F11*/, 'elementsPathFocus' ],
 
-	[ CKEDITOR.CTRL + 86 /*V*/, 'paste' ],
-	[ CKEDITOR.SHIFT + 45 /*INS*/, 'paste' ],
-	[ CKEDITOR.CTRL + 88 /*X*/, 'cut' ],
-	[ CKEDITOR.SHIFT + 46 /*DEL*/, 'cut' ],
 	[ CKEDITOR.CTRL + 90 /*Z*/, 'undo' ],
 	[ CKEDITOR.CTRL + 89 /*Y*/, 'redo' ],
Index: /CKEditor/trunk/_source/plugins/pastetext/dialogs/pastetext.js
===================================================================
--- /CKEditor/trunk/_source/plugins/pastetext/dialogs/pastetext.js	(revision 3085)
+++ /CKEditor/trunk/_source/plugins/pastetext/dialogs/pastetext.js	(revision 3085)
@@ -0,0 +1,65 @@
+/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+	CKEDITOR.dialog.add( 'pastetext', function( editor )
+		{
+			var textareaId = 'cke_' + CKEDITOR.tools.getNextNumber();
+
+			return {
+				title : editor.lang.pasteText.title,
+
+				minWidth : 400,
+				minHeight : 330,
+				
+				onShow : function()
+				{
+					// Reset the textarea value.
+					CKEDITOR.document.getById( textareaId ).setValue( '' );
+				},
+
+				onOk : function()
+				{
+					// Get the textarea value.
+					var text = CKEDITOR.document.getById( textareaId ).getValue();
+
+					// Restore the editing area selection.
+					this.restoreSelection();
+					this.clearSavedSelection();
+
+					// Inserts the text.
+					this.getParentEditor().insertText( text );
+				},
+
+				contents :
+				[
+					{
+						label : editor.lang.common.generalTab,
+						elements :
+						[
+							{
+								type : 'html',
+								id : 'pasteMsg',
+								html : '<div style="white-space:normal;width:340px;">' + editor.lang.clipboard.pasteMsg + '</div>'
+							},
+							{
+								type : 'html',
+								id : 'content',
+								style : 'width:340px;height:170px',
+								html :
+									'<textarea id="' + textareaId + '" style="' +
+										'width:346px;' +
+										'height:170px;' +
+										'border:1px solid black;' +
+										'background-color:white">' +
+									'</textarea>'
+							}
+						]
+					}
+				]
+			}
+		});
+})();
Index: /CKEditor/trunk/_source/plugins/pastetext/plugin.js
===================================================================
--- /CKEditor/trunk/_source/plugins/pastetext/plugin.js	(revision 3085)
+++ /CKEditor/trunk/_source/plugins/pastetext/plugin.js	(revision 3085)
@@ -0,0 +1,132 @@
+/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @file Paste as plain text plugin
+ */
+
+(function()
+{
+	// The pastetext command definition.
+	var pasteTextCmd =
+	{
+		exec : function( editor )
+		{
+			// We use getClipboardData just to test if the clipboard access has
+			// been granted by the user.
+			if ( CKEDITOR.getClipboardData() === false || !window.clipboardData )
+			{
+				editor.openDialog( 'pastetext' );
+				return;
+			}
+
+			text = clipboardData.getData( 'Text' );
+
+			editor.insertText( text );
+		}
+	};
+
+	// Register the plugin.
+	CKEDITOR.plugins.add( 'pastetext',
+	{
+		init : function( editor, pluginPath )
+		{
+			var commandName = 'pastetext',
+				command = editor.addCommand( commandName, pasteTextCmd );
+
+			editor.ui.addButton( 'PasteText',
+				{
+					label : editor.lang.pasteText.button,
+					command : commandName
+				});
+
+			CKEDITOR.dialog.add( commandName, CKEDITOR.getUrl( this.path + 'dialogs/pastetext.js' ) );
+
+			if ( editor.config.forcePasteAsPlainText )
+			{
+				editor.on( 'beforePaste', function( event )
+					{
+						setTimeout( function() { command.exec() }, 0 );
+						event.cancel();
+					},
+					null, null, 20 );
+			}
+		},
+		requires : [ 'clipboard' ]
+	});
+
+	var clipboardDiv;
+
+	CKEDITOR.getClipboardData = function()
+	{
+		if ( !CKEDITOR.env.ie )
+			return false;
+
+		var doc = CKEDITOR.document,
+			body = doc.getBody();
+
+		if ( !clipboardDiv )
+		{
+			clipboardDiv = doc.createElement( 'div',
+				{
+					attributes :
+						{
+							id: 'cke_hiddenDiv'
+						},
+					styles :
+						{
+							position : 'absolute',
+							visibility : 'hidden',
+							overflow : 'hidden',
+							width : '1px',
+							height : '1px'
+						}
+				});
+
+			clipboardDiv.setHtml( '' );
+
+			clipboardDiv.appendTo( body );
+		}
+
+		// The "enabled" flag is used to check whether the paste operation has
+		// been completed (the onpaste event has been fired).
+		var	enabled = false;
+		var setEnabled = function()
+		{
+			enabled = true;
+		};
+
+		body.on( 'paste', setEnabled );
+
+		// Create a text range and move it inside the div.
+		var textRange = body.$.createTextRange();
+		textRange.moveToElementText( clipboardDiv.$ );
+
+		// The execCommand in will fire the "onpaste", only if the
+		// security settings are enabled.
+		textRange.execCommand( 'Paste' );
+
+		// Get the DIV html and reset it.
+		var html = clipboardDiv.getHtml();
+		clipboardDiv.setHtml( '' );
+
+		body.removeListener( 'paste', setEnabled );
+
+		// Return the HTML or false if not enabled.
+		return enabled && html;
+	};	
+})();
+
+CKEDITOR.editor.prototype.insertText = function( text )
+{
+	text = CKEDITOR.tools.htmlEncode( text );
+
+	// TODO: Replace the following with fill line break processing (see V2).
+	text = text.replace( /(?:\r\n)|\n|\r/g, '<br>' );
+
+	this.insertHtml( text );
+};
+
+CKEDITOR.config.forcePasteAsPlainText = false;
Index: /CKEditor/trunk/_source/plugins/toolbar/plugin.js
===================================================================
--- /CKEditor/trunk/_source/plugins/toolbar/plugin.js	(revision 3084)
+++ /CKEditor/trunk/_source/plugins/toolbar/plugin.js	(revision 3085)
@@ -209,5 +209,5 @@
 		'Source', '-',
 		'NewPage', 'Preview', 'Print', '-',
-		'PasteFromWord', '-',
+		'Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-',
 		'Bold', 'Italic', 'Underline', 'Strike', '-',
 		'NumberedList', 'BulletedList', '-',
Index: /CKEditor/trunk/_source/skins/default/toolbar.css
===================================================================
--- /CKEditor/trunk/_source/skins/default/toolbar.css	(revision 3084)
+++ /CKEditor/trunk/_source/skins/default/toolbar.css	(revision 3085)
@@ -157,4 +157,24 @@
 }
 
+.cke_skin_default a.cke_button_cut .cke_icon
+{
+	background-position: 0 -96px;
+}
+
+.cke_skin_default a.cke_button_copy .cke_icon
+{
+	background-position: 0 -112px;
+}
+
+.cke_skin_default a.cke_button_paste .cke_icon
+{
+	background-position: 0 -128px;
+}
+
+.cke_skin_default a.cke_button_pastetext .cke_icon
+{
+	background-position: 0 -144px;
+}
+
 .cke_skin_default a.cke_button_find .cke_icon
 {
