Index: /CKEditor/branches/prototype/_source/core/ckeditor.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/ckeditor.js	(revision 2735)
+++ /CKEditor/branches/prototype/_source/core/ckeditor.js	(revision 2736)
@@ -88,2 +88,25 @@
 // Load the bootstrap script.
 CKEDITOR.loader.load( 'core/_bootstrap' );		// @Packager.RemoveLine
+
+// Tri-state constants.
+
+/**
+ * Used to indicate the ON or ACTIVE state.
+ * @constant
+ * @example
+ */
+CKEDITOR.TRISTATE_ON = 1;
+
+/**
+ * Used to indicate the OFF or NON ACTIVE state.
+ * @constant
+ * @example
+ */
+CKEDITOR.TRISTATE_OFF = 2;
+
+/**
+ * Used to indicate DISABLED state.
+ * @constant
+ * @example
+ */
+CKEDITOR.TRISTATE_DISABLED = 0;
Index: /CKEditor/branches/prototype/_source/core/command.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/command.js	(revision 2736)
+++ /CKEditor/branches/prototype/_source/core/command.js	(revision 2736)
@@ -0,0 +1,37 @@
+﻿/*
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ */
+
+CKEDITOR.command = function( editor, commandDefinition )
+{
+	this.state = CKEDITOR.TRISTATE_OFF;
+
+	this.exec = function()
+	{
+		commandDefinition.exec.call( this, editor );
+	};
+	
+	CKEDITOR.tools.extend( this, commandDefinition );
+
+	// Call the CKEDITOR.event constructor to initialize this instance.
+	CKEDITOR.event.call( this );
+};
+
+CKEDITOR.event.implementOn( CKEDITOR.command.prototype );
Index: /CKEditor/branches/prototype/_source/core/dom/elementpath.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/dom/elementpath.js	(revision 2735)
+++ /CKEditor/branches/prototype/_source/core/dom/elementpath.js	(revision 2736)
@@ -92,2 +92,29 @@
 	};
 })();
+
+CKEDITOR.dom.elementPath.prototype =
+{
+	/**
+	 * Compares this element path with another one.
+	 * @param {CKEDITOR.dom.elementPath} otherPath The elementPath object to be
+	 * compared with this one.
+	 * @returns {Boolean} "true" if the paths are equal, containing the same
+	 * number of elements and the same elements in the same order.
+	 */
+	compare : function( otherPath )
+	{
+		var thisElements = this.elements;
+		var otherElements = otherPath && otherPath.elements;
+		
+		if ( !otherElements || thisElements.length != otherElements.length )
+			return false;
+		
+		for ( var i = 0 ; i < thisElements.length ; i++ )
+		{
+			if ( !thisElements[ i ].equals( otherElements[ i ] ) )
+				return false;
+		} 
+		
+		return true;
+	}
+};
Index: /CKEditor/branches/prototype/_source/core/dom/range.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/dom/range.js	(revision 2735)
+++ /CKEditor/branches/prototype/_source/core/dom/range.js	(revision 2736)
@@ -426,4 +426,35 @@
 			return null;
 		},
+		
+		/**
+		 * Transforms the startContainer and endContainer properties from text
+		 * nodes to element nodes, whenever possible. This is actually possible
+		 * if either of the boundary containers point to a text node, and its
+		 * offset is set to zero, or after the last char in the node.
+		 */
+		optimize : function()
+		{
+			var container = this.startContainer;
+			var offset = this.startOffset;
+			
+			if ( container.type != CKEDITOR.NODE_ELEMENT )
+			{
+				if ( !offset )
+					this.setStartBefore( container );
+				else if ( offset >= container.getLength() )
+					this.setStartAfter( container );
+			}
+
+			container = this.endContainer;
+			offset = this.endOffset;
+
+			if ( container.type != CKEDITOR.NODE_ELEMENT )
+			{
+				if ( !offset )
+					this.setEndBefore( container );
+				else if ( offset >= container.getLength() )
+					this.setEndAfter( container );
+			}
+		},
 
 		trim : function( ignoreStart, ignoreEnd )
Index: /CKEditor/branches/prototype/_source/core/editor.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/editor.js	(revision 2735)
+++ /CKEditor/branches/prototype/_source/core/editor.js	(revision 2736)
@@ -327,5 +327,5 @@
 		addCommand : function( commandName, commandDefinition )
 		{
-			this._.commands[ commandName ] = commandDefinition;
+			this._.commands[ commandName ] = new CKEDITOR.command( this, commandDefinition );
 		},
 
@@ -361,5 +361,5 @@
 		execCommand : function( commandName, data )
 		{
-			var command = this._.commands[ commandName ];
+			var command = this.getCommand( commandName );
 			if ( command )
 				return command.exec( this, data );
@@ -367,4 +367,19 @@
 			// throw 'Unknown command name "' + commandName + '"';
 			return false;
+		},
+		
+		/**
+		 * Gets one of the registered commands. Note that, after registering a
+		 * command definition with addCommand, it is transformed internally
+		 * into an instance of {@link CKEDITOR.command}, which will be then
+		 * returned by this function.
+		 * @param {String} commandName The name of the command to be returned.
+		 * This is the same used to register the command with addCommand.
+		 * @returns {CKEDITOR.command} The command object identified by the
+		 * provided name.
+		 */
+		getCommand : function( commandName )
+		{
+			return this._.commands[ commandName ];
 		},
 
Index: /CKEditor/branches/prototype/_source/core/event.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/event.js	(revision 2735)
+++ /CKEditor/branches/prototype/_source/core/event.js	(revision 2736)
@@ -120,5 +120,5 @@
 			 * someObject.on( 'someEvent', function() { ... }, null, null, 1 );    // 1st called
 			 */
-			on  : function( eventName, listenerFunction, scopeObj, listenerData, priority )
+			on : function( eventName, listenerFunction, scopeObj, listenerData, priority )
 			{
 				// Get the event entry (create it if needed).
Index: /CKEditor/branches/prototype/_source/core/loader.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/loader.js	(revision 2735)
+++ /CKEditor/branches/prototype/_source/core/loader.js	(revision 2736)
@@ -45,4 +45,5 @@
 			'core/ckeditor_base'	: [],
 			'core/ckeditor_basic'	: [ 'core/editor_basic', 'core/env', 'core/event' ],
+			'core/command'			: [],
 			'core/config'			: [ 'core/ckeditor_base' ],
 			'core/dom'				: [],
@@ -59,5 +60,5 @@
 			'core/dom/window'		: [ 'core/dom/domobject' ],
 			'core/dtd'				: [ 'core/tools' ],
-			'core/editor'			: [ 'core/config', 'core/editor_basic', 'core/focusmanager', 'core/lang', 'core/plugins', 'core/skins', 'core/themes', 'core/tools', 'core/ui' ],
+			'core/editor'			: [ 'core/command', 'core/config', 'core/editor_basic', 'core/focusmanager', 'core/lang', 'core/plugins', 'core/skins', 'core/themes', 'core/tools', 'core/ui' ],
 			'core/editor_basic'		: [ 'core/event' ],
 			'core/env'				: [],
Index: /CKEditor/branches/prototype/_source/plugins/basicstyles/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/basicstyles/plugin.js	(revision 2735)
+++ /CKEditor/branches/prototype/_source/plugins/basicstyles/plugin.js	(revision 2736)
@@ -30,5 +30,14 @@
 		var addButtonCommand = function( buttonName, buttonLabel, commandName, styleDefiniton )
 		{
-			editor.addCommand( commandName, new CKEDITOR.styleCommand( styleDefiniton ) );
+			var style = new CKEDITOR.style( styleDefiniton );
+			
+			editor.attachStyleStateChange( style, function( state )
+				{
+					var command = editor.getCommand( commandName );
+					command.state = state;
+					command.fire( 'state' );
+				});
+		
+			editor.addCommand( commandName, new CKEDITOR.styleCommand( style ) );
 
 			editor.ui.addButton( buttonName,
Index: /CKEditor/branches/prototype/_source/plugins/button/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/button/plugin.js	(revision 2735)
+++ /CKEditor/branches/prototype/_source/plugins/button/plugin.js	(revision 2736)
@@ -84,4 +84,6 @@
 			editor.execCommand( definition.command );
 		};
+
+	this._ = {};
 };
 
@@ -113,5 +115,5 @@
 		var env = CKEDITOR.env;
 
-		var id = 'cke_' + CKEDITOR.tools.getNextNumber();
+		var id = this._.id = 'cke_' + CKEDITOR.tools.getNextNumber();
 
 		var instance =
@@ -122,5 +124,5 @@
 			focus : function()
 			{
-				var element = CKEDITOR.document.getById( this.id );
+				var element = CKEDITOR.document.getById( id );
 				element.focus();
 			},
@@ -130,4 +132,21 @@
 			}
 		};
+		
+		// Get the command name.
+		var command = this.command;
+		
+		if ( command )
+		{
+			// Get the command instance.
+			command = editor.getCommand( command );
+			
+			if ( command )
+			{
+				command.on( 'state', function()
+					{
+						this.setState( command.state );
+					}, this);
+			}
+		}
 
 		var index = CKEDITOR.ui.button._.instances.push( instance ) - 1;
@@ -165,4 +184,25 @@
 
 		return instance;
+	},
+	
+	setState : function( state )
+	{
+		var element = CKEDITOR.document.getById( this._.id );
+		
+		switch ( state )
+		{
+			case CKEDITOR.TRISTATE_ON :
+				element.addClass( 'cke_on' );
+				element.removeClass( 'cke_disabled' );
+				break;
+			case CKEDITOR.TRISTATE_DISABLED :
+				element.addClass( 'cke_disabled' );
+				element.removeClass( 'cke_on' );
+				break;
+			default :
+				element.removeClass( 'cke_on' );
+				element.removeClass( 'cke_disabled' );
+				break;
+		}
 	}
 };
Index: /CKEditor/branches/prototype/_source/plugins/editingblock/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/editingblock/plugin.js	(revision 2735)
+++ /CKEditor/branches/prototype/_source/plugins/editingblock/plugin.js	(revision 2736)
@@ -178,4 +178,5 @@
 
 		this.mode = mode;
+		this.fire( 'mode' );
 	};
 
Index: /CKEditor/branches/prototype/_source/plugins/selection/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/selection/plugin.js	(revision 2735)
+++ /CKEditor/branches/prototype/_source/plugins/selection/plugin.js	(revision 2736)
@@ -27,5 +27,4 @@
 	// the current node and check it on successive requests. If there is any
 	// change on the tree, then the selectionChange event gets fired.
-	var checkSelectionPreviousPath;
 	var checkSelectionChange = function()
 	{
@@ -35,28 +34,13 @@
 		if ( !sel )
 			return;
-
-		sel.normalize();
-
-		// Get the element at the start of the selection.
-		var node = sel.getStartElement(),
-			changed,
-			currentPath = [],
-			counter = 0;
-
-		// Loops through the parent tree of the main node.
-		while( node )
-		{
-			// Look for changes in the parent node tree.
-			if ( !changed && ( !checkSelectionPreviousPath || !node.equals( checkSelectionPreviousPath[ counter++ ] ) ) )
-				changed = true;
-
-			currentPath.push( node );
-			node = node.getParent();
+			
+		var firstElement = sel.getStartElement();
+		var currentPath = new CKEDITOR.dom.elementPath( firstElement );
+		
+		if ( !currentPath.compare( this._.selectionPreviousPath ) )
+		{
+			this._.selectionPreviousPath = currentPath;
+			this.fire( 'selectionChange', { selection : sel, path : currentPath, element : firstElement } );
 		}
-
-		checkSelectionPreviousPath = currentPath;
-
-		if ( changed )
-			this.fire( 'selectionChange', { selection : sel } );
 	};
 
@@ -460,4 +444,33 @@
 
 				case CKEDITOR.SELECTION_TEXT :
+				
+					var range = this.getRanges()[0];
+					
+					if ( range )
+					{
+						if ( !range.collapsed )
+						{
+							range.optimize();
+
+							node = range.startContainer;
+							
+							if ( node.type != CKEDITOR.NODE_ELEMENT )
+								return node.getParent();
+							
+							node = node.getChild( range.startOffset );
+							
+							if ( !node || node.type != CKEDITOR.NODE_ELEMENT )
+								return range.startContainer;
+							
+							var child = node.getFirst();
+							while (  child && child.type == CKEDITOR.NODE_ELEMENT )
+							{
+								node = child;
+								child = child.getFirst();
+							}
+							
+							return node;
+						}
+					}
 
 					if ( CKEDITOR.env.ie )
@@ -515,12 +528,4 @@
 		},
 
-		normalize : function()
-		{
-			var ranges = this.getRanges();
-
-			for ( var i = 0 ; i < ranges.length ; i++ )
-				ranges[ i ].enlarge( CKEDITOR.ENLARGE_ELEMENT );
-		},
-
 		reset : function()
 		{
Index: /CKEditor/branches/prototype/_source/plugins/sourcearea/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/sourcearea/plugin.js	(revision 2735)
+++ /CKEditor/branches/prototype/_source/plugins/sourcearea/plugin.js	(revision 2736)
@@ -103,4 +103,11 @@
 				command : 'source'
 			});
+
+		editor.on( 'mode', function()
+			{
+				var command = editor.getCommand( 'source' );
+				command.state = ( editor.mode == 'source' ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );
+				command.fire( 'state' );
+			});
 	}
 });
Index: /CKEditor/branches/prototype/_source/plugins/styles/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/styles/plugin.js	(revision 2735)
+++ /CKEditor/branches/prototype/_source/plugins/styles/plugin.js	(revision 2736)
@@ -20,5 +20,71 @@
  */
 
-CKEDITOR.plugins.add( 'styles' );
+CKEDITOR.plugins.add( 'styles',
+{
+	requires : [ 'selection' ]
+});
+
+/**
+ * Registers a function to be called whenever a style changes its state in the
+ * editing area. The current state is passed to the function. The possible
+ * states are {@link CKEDITOR.TRISTATE_ON} and {@link CKEDITOR.TRISTATE_OFF}.
+ * @param {CKEDITOR.style} The style to be watched.
+ * @param {Function} The function to be called when the style state changes.
+ * @example
+ * // Create a style object for the <b> element.
+ * var style = new CKEDITOR.style( { element : 'b' } );
+ * var editor = CKEDITOR.instances.editor1;
+ * editor.attachStyleStateChange( style, function( state )
+ *     {
+ *         if ( state == CKEDITOR.TRISTATE_ON )
+ *             alert( 'The current state for the B element is ON' );
+ *         else
+ *             alert( 'The current state for the B element is OFF' );
+ *     });
+ */
+CKEDITOR.editor.prototype.attachStyleStateChange = function( style, callback )
+{
+	// Try to get the list of attached callbacks.
+	var styleStateChangeCallbacks = this._.styleStateChangeCallbacks;
+
+	// If it doesn't exist, it means this is the first call. So, let's create
+	// all the structure to manage the style checks and the callback calls.
+	if ( !styleStateChangeCallbacks )
+	{
+		// Create the callbacks array.
+		styleStateChangeCallbacks = this._.styleStateChangeCallbacks = [];
+
+		// Attach to the selectionChange event, so we can check the styles at
+		// that point.
+		this.on( 'selectionChange', function( ev )
+			{
+				// Loop throw all registered callbacks.
+				for ( var i = 0 ; i < styleStateChangeCallbacks.length ; i++ )
+				{
+					var callback = styleStateChangeCallbacks[ i ];
+					
+					// Check the current state for the style defined for that
+					// callback.
+					var currentState = callback.style.checkActive( ev.data.path ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF;
+					
+					// If the state changed since the last check.
+					if ( callback.state !== currentState )
+					{
+						// Call the callback function, passing the current
+						// state to it.
+						callback.fn.call( this, currentState );
+						
+						// Save the current state, so it can be compared next
+						// time.
+						callback.state !== currentState;
+					}
+				}
+			});
+	}
+
+	// Save the callback info, so it can be checked on the next occurence of
+	// selectionChange.
+	styleStateChangeCallbacks.push( { style : style, fn : callback } );
+};
 
 CKEDITOR.STYLE_BLOCK = 1;
@@ -73,4 +139,62 @@
 							applyBlockStyle
 						: null ).call( this, range );
+		},
+
+		/**
+		 * Get the style state inside an element path. Returns "true" if the
+		 * element is active in the path.
+		 */
+		checkActive : function( elementPath )
+		{
+			switch ( this.type )
+			{
+				case CKEDITOR.STYLE_BLOCK :
+					return this.checkElementRemovable( elementPath.block || elementPath.blockLimit, true );
+
+				case CKEDITOR.STYLE_INLINE :
+
+					var elements = elementPath.elements;
+
+					for ( var i = 0, element ; i < elements.length ; i++ )
+					{
+						element = elements[i];
+
+						if ( element == elementPath.block || element == elementPath.blockLimit )
+							continue;
+
+						if ( this.checkElementRemovable( element, true ) )
+							return true;
+					}
+			}
+			return false;
+		},
+
+		// Checks if an element, or any of its attributes, is removable by the
+		// current style definition.
+		checkElementRemovable : function( element, fullMatch )
+		{
+			if ( !element || element.getName() != this.element )
+				return false ;
+
+			var def = this._.definition;
+			var attribs = def.attributes;
+			var styles = def.styles;
+			
+			// If no attributes are defined in the element.
+			if ( !fullMatch && !element.hasAttributes() )
+				return true ;
+
+			for ( var attName in attribs )
+			{
+				if ( element.getAttribute( attName ) == attribs[ attName ] )
+				{
+					if ( !fullMatch )
+						return true;
+				}
+				else if ( fullMatch )
+					return false;
+			}
+			
+			return true;
 		},
 
@@ -481,7 +605,7 @@
 })();
 
-CKEDITOR.styleCommand = function( styleDefinition )
+CKEDITOR.styleCommand = function( style )
 {
-	this.style = new CKEDITOR.style( styleDefinition );
+	this.style = style;
 };
 
Index: /CKEditor/branches/prototype/_source/skins/default/toolbar.css
===================================================================
--- /CKEditor/branches/prototype/_source/skins/default/toolbar.css	(revision 2735)
+++ /CKEditor/branches/prototype/_source/skins/default/toolbar.css	(revision 2736)
@@ -68,4 +68,15 @@
 }
 
+.cke_skin_default a.cke_button.cke_on
+{
+	background-color: #a3d7ff;
+	border: solid 1px #316ac5;
+	filter: alpha(opacity=100); /* IE */
+	opacity: 1; /* Safari, Opera and Mozilla */
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
+}
+
 .cke_skin_default a:hover.cke_button,
 .cke_skin_default a:focus.cke_button,
@@ -84,4 +95,7 @@
 	height: 18px;
 	outline: none;
+	-moz-border-radius: 3px;
+	-webkit-border-radius: 3px;
+	border-radius: 3px;
 }
 
