Index: /CKEditor/branches/prototype/_source/core/dom/event.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/dom/event.js	(revision 2389)
+++ /CKEditor/branches/prototype/_source/core/dom/event.js	(revision 2390)
@@ -40,2 +40,63 @@
 	this.$ = domEvent;
 };
+
+CKEDITOR.dom.event.prototype =
+{
+	/**
+	 * Gets the key code associated to the event.
+	 * @returns {Number} The key code.
+	 * @example
+	 * alert( event.getKey() );  "65" is "a" has been pressed
+	 */
+	getKey : function()
+	{
+		return this.$.keyCode || this.$.which;
+	},
+
+	/**
+	 * Gets a number represeting the combination of the keys pressed during the
+	 * event. It is the sum with the current key code and the {@link CKEDITOR.CTRL},
+	 * {@link CKEDITOR.SHIFT} and {@link CKEDITOR.ALT} constants.
+	 * @returns {Number} The number representing the keys combination.
+	 * @example
+	 * alert( event.getKeystroke() == 65 );                                   // "a" key
+	 * alert( event.getKeystroke() == CKEDITOR.CTRL + 65 );                   // CTRL + "a" key
+	 * alert( event.getKeystroke() == CKEDITOR.CTRL + CKEDITOR.SHIFT + 65 );  // CTRL + SHIFT + "a" key
+	 */
+	getKeystroke : function()
+	{
+		var keystroke = this.getKey();
+
+		if ( this.$.ctrlKey || this.$.metaKey )
+			keystroke += CKEDITOR.CTRL;
+
+		if ( this.$.shiftKey )
+			keystroke += CKEDITOR.SHIFT;
+
+		if ( this.$.altKey )
+			keystroke += CKEDITOR.ALT;
+
+		return keystroke;
+	}
+};
+
+/**
+ * CTRL key (1000).
+ * @constant
+ * @example
+ */
+CKEDITOR.CTRL = 1000;
+
+/**
+ * SHIFT key (2000).
+ * @constant
+ * @example
+ */
+CKEDITOR.SHIFT = 2000;
+
+/**
+ * ALT key (4000).
+ * @constant
+ * @example
+ */
+CKEDITOR.ALT = 4000;
Index: /CKEditor/branches/prototype/_source/lang/ar.js
===================================================================
--- /CKEditor/branches/prototype/_source/lang/ar.js	(revision 2389)
+++ /CKEditor/branches/prototype/_source/lang/ar.js	(revision 2390)
@@ -27,4 +27,9 @@
 	underline	: 'تسطير',
 	bold		: 'غامق',
-	italic		: 'مائل'
+	italic		: 'مائل',
+
+	elementsPath :
+	{
+		eleTitle : '%1 element'
+	}
 };
Index: /CKEditor/branches/prototype/_source/lang/en.js
===================================================================
--- /CKEditor/branches/prototype/_source/lang/en.js	(revision 2389)
+++ /CKEditor/branches/prototype/_source/lang/en.js	(revision 2390)
@@ -48,4 +48,9 @@
 	underline	: 'Underline',
 	bold		: 'Bold',
-	italic		: 'Italic'
+	italic		: 'Italic',
+
+	elementsPath :
+	{
+		eleTitle : '%1 element'
+	}
 };
Index: /CKEditor/branches/prototype/_source/lang/it.js
===================================================================
--- /CKEditor/branches/prototype/_source/lang/it.js	(revision 2389)
+++ /CKEditor/branches/prototype/_source/lang/it.js	(revision 2390)
@@ -27,4 +27,9 @@
 	underline	: 'Sottolineato',
 	bold		: 'Grassetto',
-	italic		: 'Corsivo'
+	italic		: 'Corsivo',
+
+	elementsPath :
+	{
+		eleTitle : '%1 element'
+	}
 };
Index: /CKEditor/branches/prototype/_source/lang/pt-br.js
===================================================================
--- /CKEditor/branches/prototype/_source/lang/pt-br.js	(revision 2389)
+++ /CKEditor/branches/prototype/_source/lang/pt-br.js	(revision 2390)
@@ -27,4 +27,9 @@
 	underline	: 'Sublinhado',
 	bold		: 'Negrito',
-	italic		: 'Itálico'
+	italic		: 'Itálico',
+
+	elementsPath :
+	{
+		eleTitle : '%1 element'
+	}
 };
Index: /CKEditor/branches/prototype/_source/plugins/button/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/button/plugin.js	(revision 2389)
+++ /CKEditor/branches/prototype/_source/plugins/button/plugin.js	(revision 2390)
@@ -111,12 +111,50 @@
 	render : function( editor, output )
 	{
+		var env = CKEDITOR.env.opera;
+
+		var id = 'cke_' + CKEDITOR.tools.getNextNumber();
+
+		var instance =
+		{
+			id : id,
+			button : this,
+			editor : editor,
+			focus : function()
+			{
+				var element = CKEDITOR.document.getById( this.id );
+				element.focus();
+			},
+			execute : function()
+			{
+				this.button.click( editor );
+			}
+		};
+
+		var index = CKEDITOR.ui.button._.instances.push( instance ) - 1;
+
 		output.push(
-			'<a id="cke_', CKEDITOR.tools.getNextNumber(),
-				'" class="cke_button ', this.className, '" href="do:', this.label,
-				'" title="', this.title,
-				'" onclick="return CKEDITOR.ui.button._.click(\'', editor.name, '\',\'', this.name, '\', this.id);">' +
+			'<a id="', id, '"' +
+				' class="cke_button ', this.className, '" href="do:', this.label, '"' +
+				' title="', this.title, '"' +
+				' tabindex="-1"' +
+				' hidefocus="true"' );
+
+		// Some browsers don't cancel key events in the keydown but in the
+		// keypress.
+		// TODO: Check if really needed for Gecko+Mac.
+		if ( env.opera || ( env.gecko && env.mac ) )
+		{
+			output.push(
+				' onkeypress="return false;"' );
+		}
+
+		output.push(
+				' onkeydown="return CKEDITOR.ui.button._.keydown(', index, ', event);"' +
+				' onclick="return CKEDITOR.ui.button._.click(', index, ', event);">' +
 					'<span class="cke_icon"></span>' +
 					'<span class="cke_label">', this.label, '</span>' +
 			'</a>' );
+
+		return instance;
 	}
 };
@@ -128,12 +166,21 @@
 CKEDITOR.ui.button._ =
 {
-	click : function( instanceName, uiItemName, elementId )
-	{
-		var editor = CKEDITOR.instances[ instanceName ];
-		editor.focus();
-
-		var uiItem = editor.ui.get( uiItemName ).click( editor, elementId );
-
+	instances : [],
+
+	click : function( index )
+	{
+		CKEDITOR.ui.button._.instances[ index ].execute();
 		return false;
+	},
+
+	keydown : function( index, ev )
+	{
+		var instance = CKEDITOR.ui.button._.instances[ index ];
+
+		if ( instance.onkey )
+		{
+			ev = new CKEDITOR.dom.event( ev );
+			return ( instance.onkey( instance, ev.getKeystroke() ) !== false );
+		}
 	}
 };
Index: /CKEditor/branches/prototype/_source/plugins/elementspath/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/elementspath/plugin.js	(revision 2389)
+++ /CKEditor/branches/prototype/_source/plugins/elementspath/plugin.js	(revision 2390)
@@ -25,53 +25,91 @@
  */
 
-CKEDITOR.plugins.add( 'elementspath',
+(function()
 {
-	requires : [ 'selection' ],
+	var commands =
+	{
+		toolbarFocus :
+		{
+			exec : function( editor )
+			{
+				var idBase = editor._.elementsPath.idBase;
+				var element = CKEDITOR.document.getById( idBase + '0' );
 
-	init : function( editor, pluginPath )
+				if ( element )
+					element.focus();
+			}
+		}
+	};
+
+	CKEDITOR.plugins.add( 'elementspath',
 	{
-		var spaceId = 'cke_path_' + editor.name;
-		var spaceElement;
-		var getSpaceElement = function()
+		requires : [ 'selection' ],
+
+		init : function( editor, pluginPath )
 		{
-			if ( !spaceElement )
-				spaceElement = CKEDITOR.document.getById( spaceId );
-			return spaceElement;
-		};
+			var spaceId = 'cke_path_' + editor.name;
+			var spaceElement;
+			var getSpaceElement = function()
+			{
+				if ( !spaceElement )
+					spaceElement = CKEDITOR.document.getById( spaceId );
+				return spaceElement;
+			};
 
-		editor.on( 'themeSpace', function( event )
-			{
-				if ( event.data.space == 'bottom' )
-					event.data.html += '<div id="' + spaceId + '" class="cke_path"><br></div>';
-			});
+			var idBase = 'cke_elementspath_' + CKEDITOR.tools.getNextNumber() + '_';
 
-		editor.on( 'selectionChange', function()
-			{
-				var element = this.getSelection().getStartElement(),
-					html = [],
-					elementsList = this._.elementsPathList = [];
+			editor._.elementsPath = { idBase : idBase };
 
-				while ( element )
+			editor.on( 'themeSpace', function( event )
 				{
-					var index = elementsList.push( element ) - 1,
-						name = element.getName();
+					if ( event.data.space == 'bottom' )
+						event.data.html += '<div id="' + spaceId + '" class="cke_path"><br></div>';
+				});
 
-					html.unshift( '<a href="element:', name, '" onclick="return CKEDITOR._.elementsPathClick( \'', this.name, '\',', index, ');">', name, '</a>' );
+			editor.on( 'selectionChange', function()
+				{
+					var element = this.getSelection().getStartElement(),
+						html = [],
+						elementsList = this._.elementsPath.list = [];
 
-					if ( name == 'body' )
-						break;
+					while ( element )
+					{
+						var index = elementsList.push( element ) - 1,
+							name = element.getName();
 
-					element = element.getParent();
-				}
+						html.unshift(
+							'<a' +
+								' id="', idBase, index, '"' +
+								' href="element:', name, '"' +
+								' tabindex="-1"' +
+								' title="', editor.lang.elementsPath.eleTitle.replace( /%1/, name ), '"' +
+								' onkeydown="return CKEDITOR._.elementsPath.keydown(\'', this.name, '\',', index, ', event);"' +
 
-				getSpaceElement().setHtml( html.join('') );
-			});
+								// Some browsers don't cancel key events in the keydown but in the
+								// keypress. (Opera / Gecko+Mac)
+								' onkeypress="return false;"',
 
-		editor.on( 'contentDomUnload', function()
-			{
-				getSpaceElement().setHtml( '<br>' );
-			});
-	}
-});
+								' onclick="return CKEDITOR._.elementsPath.click(\'', this.name, '\',', index, ');">',
+									name,
+							'</a>' );
+
+						if ( name == 'body' )
+							break;
+
+						element = element.getParent();
+					}
+
+					getSpaceElement().setHtml( html.join('') );
+				});
+
+			editor.on( 'contentDomUnload', function()
+				{
+					getSpaceElement().setHtml( '<br>' );
+				});
+
+			editor.addCommand( 'elementsPathFocus', commands.toolbarFocus );
+		}
+	});
+})();
 
 /**
@@ -79,12 +117,58 @@
  * @private
  */
-CKEDITOR._.elementsPathClick = function( instanceName, elementIndex )
+CKEDITOR._.elementsPath =
 {
-	var editor = CKEDITOR.instances[ instanceName ];
-	editor.focus();
+	click : function( instanceName, elementIndex )
+	{
+		var editor = CKEDITOR.instances[ instanceName ];
+		editor.focus();
 
-	var element = editor._.elementsPathList[ elementIndex ];
-	editor.getSelection().selectElement( element );
+		var element = editor._.elementsPath.list[ elementIndex ];
+		editor.getSelection().selectElement( element );
 
-	return false;
+		return false;
+	},
+
+	keydown : function( instanceName, elementIndex, ev )
+	{
+		var instance = CKEDITOR.ui.button._.instances[ elementIndex ];
+		var editor = CKEDITOR.instances[ instanceName ];
+		var idBase = editor._.elementsPath.idBase;
+
+		var element;
+
+		ev = new CKEDITOR.dom.event( ev );
+
+		switch ( ev.getKeystroke() )
+		{
+			case 37 :					// LEFT-ARROW
+			case 9 :					// TAB
+				element = CKEDITOR.document.getById( idBase + ( elementIndex + 1 ) );
+				if ( !element )
+					element = CKEDITOR.document.getById( idBase + '0' );
+				element.focus();
+				return false;
+
+			case 39 :					// RIGHT-ARROW
+			case CKEDITOR.SHIFT + 9 :	// SHIFT + TAB
+				element = CKEDITOR.document.getById( idBase + ( elementIndex - 1 ) );
+				if ( !element )
+					element = CKEDITOR.document.getById( idBase + ( editor._.elementsPath.list.length - 1 ) );
+				element.focus();
+				return false;
+
+			case 27 :					// ESC
+				editor.focus();
+				return false;
+
+			case 13 :					// ENTER	// Opera
+			case 32 :					// SPACE
+				this.click( instanceName, elementIndex );
+				return false;
+
+			//default :
+			//	alert( ev.getKeystroke() );
+		}
+		return true;
+	}
 };
Index: /CKEditor/branches/prototype/_source/plugins/toolbar/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/toolbar/plugin.js	(revision 2389)
+++ /CKEditor/branches/prototype/_source/plugins/toolbar/plugin.js	(revision 2390)
@@ -25,46 +25,160 @@
  */
 
-CKEDITOR.plugins.add( 'toolbar',
+(function()
 {
-	init : function( editor, pluginPath )
-	{
-		editor.on( 'themeSpace', function( event )
+	var toolbox = function()
+	{
+		this.toolbars = [];
+	};
+
+	toolbox.prototype.focus = function()
+	{
+		for ( var t = 0, toolbar ; toolbar = this.toolbars[ t++ ] ; )
+		{
+			for ( var i = 0, item ; item = toolbar.items[ i++ ] ; )
 			{
-				if ( event.data.space == editor.config.toolbarLocation )
+				if ( item.focus )
 				{
-					var output = [ '<div class="cke_toolbox">' ];
-
-					var toolbar = editor.config.toolbar;
-
-					for ( var r = 0 ; r < toolbar.length ; r++ )
+					item.focus();
+					return;
+				}
+			}
+		}
+	};
+
+	var commands =
+	{
+		toolbarFocus :
+		{
+			exec : function( editor )
+			{
+				if ( editor.toolbox )
+					editor.toolbox.focus();
+			}
+		}
+	};
+
+	CKEDITOR.plugins.add( 'toolbar',
+	{
+		init : function( editor, pluginPath )
+		{
+			var itemKeystroke = function( item, keystroke )
+			{
+				switch ( keystroke )
+				{
+					case 39 :					// RIGHT-ARROW
+					case 9 :					// TAB
+						// Look for the next item in the toolbar.
+						while ( ( item = item.next || ( item.toolbar.next && item.toolbar.next.items[ 0 ] ) ) && !item.focus )
+						{ /*jsl:pass*/ }
+
+						// If available, just focus it, otherwise focus the
+						// first one.
+						if ( item )
+							item.focus();
+						else
+							editor.toolbox.focus();
+
+						return false;
+
+					case 37 :					// LEFT-ARROW
+					case CKEDITOR.SHIFT + 9 :	// SHIFT + TAB
+						// Look for the previous item in the toolbar.
+						while ( ( item = item.previous || ( item.toolbar.previous && item.toolbar.previous.items[ item.toolbar.previous.items.length - 1 ] ) ) && !item.focus )
+						{ /*jsl:pass*/ }
+
+						// If available, just focus it, otherwise focus the
+						// last one.
+						if ( item )
+							item.focus();
+						else
+						{
+							var lastToolbarItems = editor.toolbox.toolbars[ editor.toolbox.toolbars.length - 1 ].items;
+							lastToolbarItems[ lastToolbarItems.length - 1 ].focus();
+						}
+
+						return false;
+
+					case 27 :					// ESC
+						editor.focus();
+						return false;
+
+					case 32 :					// SPACE
+						item.execute();
+						return false;
+				}
+				return true;
+			};
+
+			editor.on( 'themeSpace', function( event )
+				{
+					if ( event.data.space == editor.config.toolbarLocation )
 					{
-						var row = toolbar[ r ];
-
-						output.push( '<div class="cke_toolbar">' );
-
-						for ( var i = 0 ; i < row.length ; i++ )
+						editor.toolbox = new toolbox();
+
+						var output = [ '<div class="cke_toolbox">' ];
+
+						var toolbars = editor.toolbox.toolbars,
+							toolbar = editor.config.toolbar;
+
+						for ( var r = 0 ; r < toolbar.length ; r++ )
 						{
-							var item,
-								itemName = row[ i ];
-
-							if ( itemName == '-' )
-								item = CKEDITOR.ui.separator;
-							else
-								item = editor.ui.get( row[ i ] );
-
-							if ( item )
-								item.render( editor, output );
+							var row = toolbar[ r ],
+								toolbarId = 'cke_' + CKEDITOR.tools.getNextNumber(),
+								toolbarObj = { id : toolbarId, items : [] };
+
+							output.push( '<div id="', toolbarId, '" class="cke_toolbar">' );
+
+							// Add the toolbar to the "editor.toolbox.toolbars"
+							// array.
+							var index = toolbars.push( toolbarObj ) - 1;
+
+							// Create the next/previous reference.
+							if ( index > 0 )
+							{
+								toolbarObj.previous = toolbars[ index - 1 ];
+								toolbarObj.previous.next = toolbarObj;
+							}
+
+							// Create all items defined for this toolbar.
+							for ( var i = 0 ; i < row.length ; i++ )
+							{
+								var item,
+									itemName = row[ i ];
+
+								if ( itemName == '-' )
+									item = CKEDITOR.ui.separator;
+								else
+									item = editor.ui.get( itemName );
+
+								if ( item )
+								{
+									var itemObj = item.render( editor, output );
+									index = toolbarObj.items.push( itemObj ) - 1;
+
+									if ( index > 0 )
+									{
+										itemObj.previous = toolbarObj.items[ index - 1 ];
+										itemObj.previous.next = itemObj;
+									}
+
+									itemObj.toolbar = toolbarObj;
+									itemObj.onkey = itemKeystroke;
+								}
+							}
+
+							output.push( '</div>' );
 						}
 
 						output.push( '</div>' );
+
+						event.data.html += output.join( '' );
 					}
-
-					output.push( '</div>' );
-
-					event.data.html += output.join( '' );
-				}
-			});
-	}
-});
+				});
+
+			editor.addCommand( 'toolbarFocus', commands.toolbarFocus );
+		}
+	});
+})();
 
 /**
@@ -78,4 +192,5 @@
 	{
 		output.push( '<span class="cke_separator"></span>' );
+		return {};
 	}
 };
Index: /CKEditor/branches/prototype/_source/skins/default/elementspath.css
===================================================================
--- /CKEditor/branches/prototype/_source/skins/default/elementspath.css	(revision 2389)
+++ /CKEditor/branches/prototype/_source/skins/default/elementspath.css	(revision 2390)
@@ -35,5 +35,7 @@
 }
 
-.cke_skin_default .cke_path a:hover
+.cke_skin_default .cke_path a:hover,
+.cke_skin_default .cke_path a:focus,
+.cke_skin_default .cke_path a:active	/* IE */
 {
 	border: solid 1px #316ac5;
@@ -43,3 +45,4 @@
 	padding-left: 4px;
 	padding-right: 4px;
+	outline: none;
 }
Index: /CKEditor/branches/prototype/_source/skins/default/toolbar.css
===================================================================
--- /CKEditor/branches/prototype/_source/skins/default/toolbar.css	(revision 2389)
+++ /CKEditor/branches/prototype/_source/skins/default/toolbar.css	(revision 2390)
@@ -60,5 +60,7 @@
 }
 
-.cke_skin_default a:hover.cke_button
+.cke_skin_default a:hover.cke_button,
+.cke_skin_default a:focus.cke_button,
+.cke_skin_default a:active.cke_button	/* IE */
 {
 	border: solid 1px #316ac5;
@@ -73,4 +75,5 @@
 	float: left;
 	height: 18px;
+	outline: none;
 }
 
