Bug 385795 - [TabbedProperties] Need support for changing color of a
tab-label and displaying dynamic images on a tab in Tabbed Properties
View
Bug 386417 - A tab label in Tabbed Properties View is cut-off towards
right-side

Support dynamically changing images or colors of a tab label.
diff --git a/bundles/org.eclipse.ui.views.properties.tabbed/META-INF/MANIFEST.MF b/bundles/org.eclipse.ui.views.properties.tabbed/META-INF/MANIFEST.MF
index f00c5f0..282da85 100644
--- a/bundles/org.eclipse.ui.views.properties.tabbed/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.ui.views.properties.tabbed/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %Plugin.name
 Bundle-SymbolicName: org.eclipse.ui.views.properties.tabbed;singleton:=true
-Bundle-Version: 3.5.300.qualifier
+Bundle-Version: 3.6.0.qualifier
 Bundle-Activator: org.eclipse.ui.internal.views.properties.tabbed.TabbedPropertyViewPlugin
 Bundle-Vendor: %Plugin.providerName
 Bundle-Localization: plugin
diff --git a/bundles/org.eclipse.ui.views.properties.tabbed/src/org/eclipse/ui/internal/views/properties/tabbed/view/TabbedPropertyList.java b/bundles/org.eclipse.ui.views.properties.tabbed/src/org/eclipse/ui/internal/views/properties/tabbed/view/TabbedPropertyList.java
index 468f1d5..c157d8a 100755
--- a/bundles/org.eclipse.ui.views.properties.tabbed/src/org/eclipse/ui/internal/views/properties/tabbed/view/TabbedPropertyList.java
+++ b/bundles/org.eclipse.ui.views.properties.tabbed/src/org/eclipse/ui/internal/views/properties/tabbed/view/TabbedPropertyList.java
@@ -8,9 +8,12 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *     Mariot Chauvin <mariot.chauvin@obeo.fr> - bug 259553
+ *     Amit Joglekar <joglekar@us.ibm.com> - Support for dynamic images (bug 385795)
  *******************************************************************************/
 package org.eclipse.ui.internal.views.properties.tabbed.view;
 
+import java.util.Map;
+
 import org.eclipse.jface.resource.JFaceResources;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.accessibility.ACC;
@@ -34,6 +37,7 @@
 import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.graphics.FontMetrics;
 import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.graphics.RGB;
 import org.eclipse.swt.graphics.Rectangle;
@@ -69,6 +73,17 @@
 
 	private ListElement[] elements;
 
+	/**
+	 * This map specifies the number of dynamic images for a tab. It has a
+	 * ITabItem as key and number of dynamic images for the tab as value. It is
+	 * set using the setDynamicImageCount() method. It is used to calculate the
+	 * width of the widest tab by setting aside enough space for displaying the
+	 * dynamic images. Individual dynamic images are displayed/removed from a
+	 * tab by using the showDynamicImage() and hideDynamicImage() methods on the
+	 * tab's ListElement object.
+	 */
+	private Map tabToDynamicImageCountMap;
+
 	private int selectedElementIndex = NONE;
 
 	private int topVisibleIndex = NONE;
@@ -126,6 +141,10 @@
 
 		private boolean hover;
 
+		private Image[] dynamicImages;
+
+		private Color textColor = widgetForeground;
+
 		/**
 		 * Constructor for ListElement.
 		 * 
@@ -187,6 +206,32 @@
 		}
 
 		/**
+		 * Constructor for ListElement.
+		 * 
+		 * @param parent
+		 *            the parent Composite.
+		 * @param tab
+		 *            the tab item for the element.
+		 * @param dynamicImageCount
+		 *            number of dynamic images for this element
+		 * @param index
+		 *            the index in the list.
+		 */
+		public ListElement(Composite parent, final ITabItem tab,
+				int dynamicImageCount, int index) {
+			this(parent, tab, index);
+			/*
+			 * Dynamic images are not displayed initially, set all of them to
+			 * null. Clients should call showDynamicImage() method to display a
+			 * dynamic image.
+			 */
+			this.dynamicImages = new Image[dynamicImageCount];
+			for (int i = 0; i < dynamicImageCount; i++) {
+				this.dynamicImages[i] = null;
+			}
+		}
+
+		/**
 		 * Set selected value for this element.
 		 * 
 		 * @param selected
@@ -198,6 +243,64 @@
 		}
 
 		/**
+		 * Show the dynamic image at specified index in dynamicImages array. The
+		 * image width should not be more than 16 pixels. The caller is
+		 * responsible for loading the image appropriately and managing it's
+		 * resources.
+		 * 
+		 * @param index
+		 * @param image
+		 */
+		public void showDynamicImage(int index, Image image) {
+			if (index >= 0 && index < dynamicImages.length) {
+				if (dynamicImages[index] != image) {
+					dynamicImages[index] = image;
+					redraw();
+				}
+			}
+		}
+
+		/**
+		 * Hide the dynamic image at specified index in dynamicImages array. The
+		 * caller is responsible for managing image resources and disposing it
+		 * appropriately.
+		 * 
+		 * @param index
+		 */
+		public void hideDynamicImage(int index) {
+			if (index >= 0 && index < dynamicImages.length) {
+				if (dynamicImages[index] != null) {
+					dynamicImages[index] = null;
+					redraw();
+				}
+			}
+		}
+
+		/**
+		 * Sets color to be used for drawing tab label text. The caller is
+		 * responsible for managing the color's resources and disposing it
+		 * appropriately after setDefaultTextColor() is later invoked.
+		 * 
+		 * @param textColor
+		 */
+		public void setTextColor(Color textColor) {
+			if (textColor != null && !this.textColor.equals(textColor)) {
+				this.textColor = textColor;
+				redraw();
+			}
+		}
+
+		/**
+		 * Sets default color for tab label text
+		 */
+		public void setDefaultTextColor() {
+			if (!this.textColor.equals(widgetForeground)) {
+				this.textColor = widgetForeground;
+				redraw();
+			}
+		}
+
+		/**
 		 * Paint the element.
 		 * 
 		 * @param e
@@ -242,6 +345,9 @@
 						bounds.height + 1);
 			}
 
+			/*
+			 * Add INDENT pixels to the left as a margin.
+			 */
 			int textIndent = INDENT;
 			FontMetrics fm = e.gc.getFontMetrics();
 			int height = fm.getHeight();
@@ -256,7 +362,7 @@
 					textIndent = textIndent - 3;
 				}
 				e.gc.drawImage(tab.getImage(), textIndent, textMiddle - 1);
-				textIndent = textIndent + 16 + 5;
+				textIndent = textIndent + 16 + 4;
 			} else if (tab.isIndented()) {
 				textIndent = textIndent + INDENT;
 			}
@@ -276,6 +382,33 @@
 					+ point.x, bounds.height - 4);
 			}
 
+			/* Draw dynamic images, if any */
+			boolean hasDynamicImage = false;
+			for (int i = 0; i < dynamicImages.length; i++) {
+				Image dynamicImage = dynamicImages[i];
+				if (dynamicImage != null && !dynamicImage.isDisposed()) {
+					hasDynamicImage = true;
+					break;
+				}
+			}
+			if (hasDynamicImage) {
+				int drawPosition = textIndent
+						+ e.gc.textExtent(tab.getText()).x + 4;
+				boolean addSpace = false;
+				for (int i = 0; i < dynamicImages.length; i++) {
+					Image dynamicImage = dynamicImages[i];
+					if (dynamicImage != null && !dynamicImage.isDisposed()) {
+						if (addSpace) {
+							drawPosition = drawPosition + 3;
+						}
+						e.gc.drawImage(dynamicImage, drawPosition,
+								textMiddle - 1);
+						drawPosition = drawPosition + 16;
+						addSpace = true;
+					}
+				}
+			}
+
 			/* draw the bottom line on the tab for selected and default */
 			if (!hover) {
 				e.gc.setForeground(listBackground);
@@ -549,6 +682,15 @@
 	}
 
 	/**
+	 * Returns the number of elements in this list viewer.
+	 * 
+	 * @return number of elements
+	 */
+	public int getNumberOfElements() {
+		return elements.length;
+	}
+
+	/**
 	 * Returns the element with the given index from this list viewer. Returns
 	 * <code>null</code> if the index is out of range.
 	 * 
@@ -575,6 +717,14 @@
 	}
 
 	/**
+	 * @return zero-relative index of the widest item, or -1 if this list is
+	 *         empty.
+	 */
+	public int getWidestLabelIndex() {
+		return widestLabelIndex;
+	}
+
+	/**
 	 * Removes all elements from this list.
 	 */
 	public void removeAll() {
@@ -591,6 +741,20 @@
 	}
 
 	/**
+	 * Sets a map containing an ITabItem as key and number of dynamic images as
+	 * value. It is used to calculate the width of the widest tab by setting
+	 * aside enough space (16 pixels per image) for displaying the dynamic
+	 * images. Individual dynamic images are displayed/removed from a tab by
+	 * using the showDynamicImage() and hideDynamicImage() methods on the tab's
+	 * ListElement object.
+	 * 
+	 * @param tabToDynamicImageCountMap
+	 */
+	public void setDynamicImageCount(Map tabToDynamicImageCountMap) {
+		this.tabToDynamicImageCountMap = tabToDynamicImageCountMap;
+	}
+
+	/**
 	 * Sets the new list elements.
 	 * 
 	 * @param children
@@ -605,18 +769,20 @@
 		} else {
 			widestLabelIndex = 0;
 			for (int i = 0; i < children.length; i++) {
-				elements[i] = new ListElement(this, (ITabItem) children[i], i);
+				int dynamicImageCount = 0;
+				if (tabToDynamicImageCountMap != null
+						&& tabToDynamicImageCountMap.containsKey(children[i])) {
+					dynamicImageCount = ((Integer) tabToDynamicImageCountMap
+							.get(children[i])).intValue();
+				}
+				elements[i] = new ListElement(this, (ITabItem) children[i],
+						dynamicImageCount, i);
 				elements[i].setVisible(false);
 				elements[i].setLayoutData(null);
 
 				if (i != widestLabelIndex) {
-					String label = ((ITabItem) children[i]).getText();
-					int width = getTextDimension(label).x;
-					if (((ITabItem) children[i]).isIndented()) {
-						width = width + INDENT;
-					}
-					if (width > getTextDimension(((ITabItem) children[widestLabelIndex])
-							.getText()).x) {
+					int width = getTabWidth((ITabItem) children[i]);
+					if (width > getTabWidth((ITabItem) children[widestLabelIndex])) {
 						widestLabelIndex = i;
 					}
 				}
@@ -626,6 +792,40 @@
 		computeTopAndBottomTab();
 	}
 
+	private int getTabWidth(ITabItem tabItem) {
+		int width = getTextDimension(tabItem.getText()).x;
+		/*
+		 * To anticipate for the icon placement we should always keep the
+		 * space available after the label. So when the active tab includes
+		 * an icon the width of the tab doesn't change.
+		 */
+		if (tabItem.getImage() != null) {
+			width = width + 16 + 4;
+		}
+		if (tabItem.isIndented()) {
+			width = width + INDENT;
+		}
+		if (tabToDynamicImageCountMap != null) {
+			int dynamicImageCount = 0;
+			if (tabToDynamicImageCountMap.containsKey(tabItem)) {
+				dynamicImageCount = ((Integer) tabToDynamicImageCountMap
+						.get(tabItem)).intValue();
+			}
+			if (dynamicImageCount > 0) {
+				/*
+				 * Keep some space between tab's text and first dynamic image
+				 */
+				width = width + 4;
+				width = width + (dynamicImageCount * 16);
+				/*
+				 * Keep some space between consecutive dynamic images
+				 */
+				width = width + ((dynamicImageCount - 1) * 3);
+			}
+		}
+		return width;
+	}
+
 	/**
 	 * Selects one of the elements in the list.
 	 * 
@@ -684,21 +884,12 @@
 			String properties_not_available = TabbedPropertyMessages.TabbedPropertyList_properties_not_available;
 			result.x = getTextDimension(properties_not_available).x + INDENT;
 		} else {
-			ITabItem widestTab = elements[widestLabelIndex].getTabItem();
-			int width = getTextDimension(widestTab.getText()).x + INDENT;
 			/*
-			 * To anticipate for the icon placement we should always keep the
-			 * space available after the label. So when the active tab includes
-			 * an icon the width of the tab doesn't change.
+			 * Add INDENT pixels to the left of the longest tab as a margin.
 			 */
-			if (widestTab.getImage() != null) {
-				width = width + 16 + 4;
-			}
-			if (widestTab.isIndented()) {
-				width = width + 10;
-			}
+			int width = getTabWidth(elements[widestLabelIndex].getTabItem()) + INDENT;
 			/*
-			 * Add 10 pixels to the right of the longest string as a margin.
+			 * Add 10 pixels to the right of the longest tab as a margin.
 			 */
 			result.x = width + 10;
 		}
diff --git a/bundles/org.eclipse.ui.views.properties.tabbed/src/org/eclipse/ui/views/properties/tabbed/TabbedPropertySheetPage.java b/bundles/org.eclipse.ui.views.properties.tabbed/src/org/eclipse/ui/views/properties/tabbed/TabbedPropertySheetPage.java
index c3b14caac2..6881820 100644
--- a/bundles/org.eclipse.ui.views.properties.tabbed/src/org/eclipse/ui/views/properties/tabbed/TabbedPropertySheetPage.java
+++ b/bundles/org.eclipse.ui.views.properties.tabbed/src/org/eclipse/ui/views/properties/tabbed/TabbedPropertySheetPage.java
@@ -443,8 +443,9 @@
 	 * Dispose the contributor with the provided contributor id. This happens on
 	 * part close as well as when contributors switch between the workbench
 	 * part and contributor from a selection.
+	 * @since 3.6
 	 */
-	private void disposeContributor() {
+	protected void disposeContributor() {
 		/**
 		 * If the current tab is about to be disposed we have to call
 		 * aboutToBeHidden
@@ -606,7 +607,18 @@
 		}
 	}
 
-	private void disposeTabs(Collection tabs) {
+	/**
+	 * Disposes the TabContents objects passed to this method. If the
+	 * 'currentTab' is going to be disposed, then the caller should call
+	 * aboutToBeHidden() on the currentTab and set it to null before calling
+	 * this method. Also, the caller needs to ensure that descriptorToTab map
+	 * entries corresponding to the disposed TabContents objects are also
+	 * removed.
+	 * 
+	 * @param tabs
+	 * @since 3.6
+	 */
+	protected void disposeTabs(Collection tabs) {
 		for (Iterator iter = tabs.iterator(); iter.hasNext();) {
 			TabContents tab = (TabContents) iter.next();
 			Composite composite = (Composite) tabToComposite.remove(tab);
@@ -1052,4 +1064,57 @@
     	}
 		return registry.getLabelProvider().getImage(selection);
     }
+
+	/**
+	 * Returns the TabContents object corresponding to the given tab-descriptor.
+	 * 
+	 * @param tabDescriptor
+	 *            tab-descriptor whose TabContents object is to be returned
+	 * @return TabContents object corresponding to the given tab-descriptor key
+	 *         in descriptorToTab map, or null if the key does not exist in the
+	 *         map
+	 * @since 3.6
+	 */
+	protected TabContents getTabContents(ITabDescriptor tabDescriptor) {
+		TabContents tabContents = null;
+		if (this.descriptorToTab.containsKey(tabDescriptor)) {
+			tabContents = (TabContents) this.descriptorToTab.get(tabDescriptor);
+		}
+		return tabContents;
+	}
+
+	/**
+	 * Get the current selection-contributor if any
+	 * 
+	 * @return The selection-contributor, or null.
+	 * @since 3.6
+	 */
+	protected ITabbedPropertySheetPageContributor getSelectionContributor() {
+		return this.selectionContributor;
+	}
+
+	/**
+	 * Get the currently active contributor id. It may not match the contributor
+	 * id from the workbench part that created this instance because if all the
+	 * elements in a structured selection implement
+	 * ITabbedPropertySheetPageContributor and they all return the same unique
+	 * contributor ID, then tabs and sections associated with that contributor
+	 * ID are used by the tabbed property view for that selection.
+	 * 
+	 * @return contributor id
+	 * @since 3.6
+	 */
+	protected String getCurrentContributorId() {
+		return this.currentContributorId;
+	}
+
+	/**
+	 * Get the current selection
+	 * 
+	 * @return selection
+	 * @since 3.6
+	 */
+	protected ISelection getCurrentSelection() {
+		return this.currentSelection;
+	}
 }
\ No newline at end of file
diff --git a/tests/org.eclipse.ui.tests.views.properties.tabbed/icons/error_tsk.gif b/tests/org.eclipse.ui.tests.views.properties.tabbed/icons/error_tsk.gif
new file mode 100644
index 0000000..9b048d6
--- /dev/null
+++ b/tests/org.eclipse.ui.tests.views.properties.tabbed/icons/error_tsk.gif
Binary files differ
diff --git a/tests/org.eclipse.ui.tests.views.properties.tabbed/icons/info_tsk.gif b/tests/org.eclipse.ui.tests.views.properties.tabbed/icons/info_tsk.gif
new file mode 100644
index 0000000..2da001e
--- /dev/null
+++ b/tests/org.eclipse.ui.tests.views.properties.tabbed/icons/info_tsk.gif
Binary files differ
diff --git a/tests/org.eclipse.ui.tests.views.properties.tabbed/icons/warn_tsk.gif b/tests/org.eclipse.ui.tests.views.properties.tabbed/icons/warn_tsk.gif
new file mode 100644
index 0000000..14009e9
--- /dev/null
+++ b/tests/org.eclipse.ui.tests.views.properties.tabbed/icons/warn_tsk.gif
Binary files differ
diff --git a/tests/org.eclipse.ui.tests.views.properties.tabbed/plugin.properties b/tests/org.eclipse.ui.tests.views.properties.tabbed/plugin.properties
index fe51e2a..585d6e8 100644
--- a/tests/org.eclipse.ui.tests.views.properties.tabbed/plugin.properties
+++ b/tests/org.eclipse.ui.tests.views.properties.tabbed/plugin.properties
@@ -13,6 +13,7 @@
 
 Views.category.name = Tabbed Properties View
 Views.example.name = Tabbed Properties Tests View
+Views.decoration.name = Tabbed Properties Decoration Tests View
 Views.dynamic.name = Tabbed Properties Dynamic Tests View
 Views.text.name = Tabbed Properties Text Tests View
 Views.override.name = Tabbed Properties Override Tests View
diff --git a/tests/org.eclipse.ui.tests.views.properties.tabbed/plugin.xml b/tests/org.eclipse.ui.tests.views.properties.tabbed/plugin.xml
index 5e6a97e..bda4408 100644
--- a/tests/org.eclipse.ui.tests.views.properties.tabbed/plugin.xml
+++ b/tests/org.eclipse.ui.tests.views.properties.tabbed/plugin.xml
@@ -35,6 +35,13 @@
             class="org.eclipse.ui.tests.views.properties.tabbed.override.OverrideTestsView"
             id="org.eclipse.ui.tests.views.properties.tabbed.override.OverrideTestsView">
       </view>
+      <view
+            category="org.eclipse.ui.views.properties.tabbed"
+            class="org.eclipse.ui.tests.views.properties.tabbed.decorations.views.DecorationTestsView"
+            icon="icons/sample.gif"
+            id="org.eclipse.ui.tests.views.properties.tabbed.decorations.views.DecorationTestsView"
+            name="%Views.decoration.name">
+      </view>
    </extension>
    <extension
          point="org.eclipse.ui.views.properties.tabbed.propertyContributor">
@@ -74,16 +81,19 @@
                afterTab="propertyTab4"
                category="first"
                id="propertyTab5"
+               image="icons/info_tsk.gif"
                label="Information"/>
          <propertyTab
                afterTab="propertyTab5"
                category="first"
                id="propertyTab6"
+               image="icons/warn_tsk.gif"
                label="Warning"/>
          <propertyTab
                afterTab="propertyTab6"
                category="first"
                id="propertyTab7"
+               image="icons/error_tsk.gif"
                label="Error"/>
          <propertyTab
                category="second"
diff --git a/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/AllTests.java b/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/AllTests.java
index f796d69..c9ae90d 100644
--- a/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/AllTests.java
+++ b/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/AllTests.java
@@ -28,6 +28,7 @@
         suite.addTestSuite(TabbedPropertySheetPageDynamicTest.class);
         suite.addTestSuite(TabbedPropertySheetPageTextTest.class);
         suite.addTestSuite(TabbedPropertySheetPageOverrideTest.class);
+        suite.addTestSuite(TabbedPropertySheetPageDecorationsTest.class);
         return suite;
     }
 
diff --git a/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/TabbedPropertySheetPageDecorationsTest.java b/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/TabbedPropertySheetPageDecorationsTest.java
new file mode 100644
index 0000000..7bbf2b8
--- /dev/null
+++ b/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/TabbedPropertySheetPageDecorationsTest.java
@@ -0,0 +1,231 @@
+/*******************************************************************************
+ * Copyright (c) 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.ui.tests.views.properties.tabbed;
+
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreeNode;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.internal.views.properties.tabbed.view.TabbedPropertyComposite;
+import org.eclipse.ui.tests.views.properties.tabbed.decorations.TabbedPropertySheetPageWithDecorations;
+import org.eclipse.ui.tests.views.properties.tabbed.decorations.views.DecorationTestsView;
+import org.eclipse.ui.tests.views.properties.tabbed.views.TestsPerspective;
+import org.eclipse.ui.tests.views.properties.tabbed.views.TestsViewContentProvider;
+import org.eclipse.ui.views.properties.tabbed.ITabDescriptor;
+
+import junit.framework.TestCase;
+
+public class TabbedPropertySheetPageDecorationsTest extends TestCase {
+
+    private DecorationTestsView decorationTestsView;
+
+    private TreeNode[] treeNodes;
+
+    protected void setUp()
+        throws Exception {
+        super.setUp();
+
+        /**
+         * Close the existing perspectives.
+         */
+        IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench()
+            .getActiveWorkbenchWindow();
+        assertNotNull(workbenchWindow);
+        IWorkbenchPage workbenchPage = workbenchWindow.getActivePage();
+        assertNotNull(workbenchPage);
+        workbenchPage.closeAllPerspectives(false, false);
+
+        /**
+         * Open the tests perspective.
+         */
+        PlatformUI.getWorkbench().showPerspective(
+            TestsPerspective.TESTS_PERSPECTIVE_ID, workbenchWindow);
+
+        /**
+         * Select the Decoration Tests view.
+         */
+        IViewPart view = workbenchPage.showView(DecorationTestsView.DECORATION_TESTS_VIEW_ID);
+        assertNotNull(view);
+        assertTrue(view instanceof DecorationTestsView);
+        decorationTestsView = (DecorationTestsView) view;
+
+        /**
+         * get the list of tree nodes from the view.
+         */
+        IContentProvider contentProvider = decorationTestsView.getViewer()
+            .getContentProvider();
+        assertTrue(contentProvider instanceof TestsViewContentProvider);
+        TestsViewContentProvider viewContentProvider = (TestsViewContentProvider) contentProvider;
+        treeNodes = viewContentProvider.getInvisibleRoot().getChildren();
+        assertEquals(treeNodes.length, 8);
+    }
+
+    protected void tearDown()
+        throws Exception {
+        super.tearDown();
+
+        /**
+		 * Bug 175070: Make sure the views have finished painting.
+         */
+        while (Display.getCurrent().readAndDispatch()) {
+            //
+        }
+
+        /**
+         * Deselect everything in the Tests view.
+         */
+        setSelection(new TreeNode[] {} );
+    }
+
+    /**
+     * Set the selection in the view to cause the properties view to change.
+     * 
+     * @param selectedNodes
+     *            nodes to select in the view.
+     */
+    private void setSelection(TreeNode[] selectedNodes) {
+        StructuredSelection selection = new StructuredSelection(selectedNodes);
+        decorationTestsView.getViewer().setSelection(selection, true);
+    }
+
+    /**
+     * When Information node is selected, the Information tab is widest if decorations are not used.
+     */
+    public void test_widestLabelIndex1_WithoutDecorations() {
+    	((TabbedPropertySheetPageWithDecorations)decorationTestsView.getTabbedPropertySheetPage()).useDecorations(false);
+        /**
+         * select Information node
+         */
+        setSelection(new TreeNode[] {treeNodes[0]});
+        ITabDescriptor[] tabDescriptors = decorationTestsView.getTabbedPropertySheetPage().getActiveTabs();
+
+        /**
+         * First tab is Name
+         */
+        assertEquals("Name", tabDescriptors[0].getLabel());//$NON-NLS-1$
+        /**
+         * Second tab is Information
+         */
+        assertEquals("Information", tabDescriptors[1].getLabel());//$NON-NLS-1$
+        /**
+         * Third tab is Message
+         */
+        assertEquals("Message", tabDescriptors[2].getLabel());//$NON-NLS-1$
+        /**
+         * No fourth tab
+         */
+        assertEquals(3, tabDescriptors.length);
+
+        /**
+         * Information tab is widest
+         */
+        assertEquals(1, ((TabbedPropertyComposite) decorationTestsView.getTabbedPropertySheetPage().getControl()).getList().getWidestLabelIndex());
+    }
+
+    /**
+     * When Information node is selected, the Name tab is widest if decorations are used.
+     */
+    public void test_widestLabelIndex1_WithDecorations() {
+    	((TabbedPropertySheetPageWithDecorations)decorationTestsView.getTabbedPropertySheetPage()).useDecorations(true);
+        /**
+         * select Information node
+         */
+        setSelection(new TreeNode[] {treeNodes[0]});
+        ITabDescriptor[] tabDescriptors = decorationTestsView.getTabbedPropertySheetPage().getActiveTabs();
+
+        /**
+         * First tab is Name
+         */
+        assertEquals("Name", tabDescriptors[0].getLabel());//$NON-NLS-1$
+        /**
+         * Second tab is Information
+         */
+        assertEquals("Information", tabDescriptors[1].getLabel());//$NON-NLS-1$
+        /**
+         * Third tab is Message
+         */
+        assertEquals("Message", tabDescriptors[2].getLabel());//$NON-NLS-1$
+        /**
+         * No fourth tab
+         */
+        assertEquals(3, tabDescriptors.length);
+
+        /**
+         * Name tab is widest
+         */
+        assertEquals(0, ((TabbedPropertyComposite) decorationTestsView.getTabbedPropertySheetPage().getControl()).getList().getWidestLabelIndex());
+    }
+
+    /**
+     * When Two Information nodes are selected, the Information tab is widest if decorations are not used.
+     */
+    public void test_widestLabelIndex2_WithoutDecorations() {
+    	((TabbedPropertySheetPageWithDecorations)decorationTestsView.getTabbedPropertySheetPage()).useDecorations(false);
+        /**
+         * select nodes
+         */
+        setSelection(new TreeNode[] {treeNodes[0], treeNodes[1]});
+        ITabDescriptor[] tabDescriptors = decorationTestsView.getTabbedPropertySheetPage().getActiveTabs();
+
+        /**
+         * First tab is Information
+         */
+        assertEquals("Information", tabDescriptors[0].getLabel());//$NON-NLS-1$
+        /**
+         * Second tab is Message
+         */
+        assertEquals("Message", tabDescriptors[1].getLabel());//$NON-NLS-1$
+        /**
+         * No other tab
+         */
+        assertEquals(2, tabDescriptors.length);
+
+        /**
+         * Information tab is widest
+         */
+        assertEquals(0, ((TabbedPropertyComposite) decorationTestsView.getTabbedPropertySheetPage().getControl()).getList().getWidestLabelIndex());
+    }
+
+    /**
+     * When Two Information nodes are selected, the Message tab is widest if decorations are used.
+     */
+    public void test_widestLabelIndex2_WithDecorations() {
+    	((TabbedPropertySheetPageWithDecorations)decorationTestsView.getTabbedPropertySheetPage()).useDecorations(true);
+        /**
+         * select nodes
+         */
+        setSelection(new TreeNode[] {treeNodes[0], treeNodes[1]});
+        ITabDescriptor[] tabDescriptors = decorationTestsView.getTabbedPropertySheetPage().getActiveTabs();
+
+        /**
+         * First tab is Information
+         */
+        assertEquals("Information", tabDescriptors[0].getLabel());//$NON-NLS-1$
+        /**
+         * Second tab is Message
+         */
+        assertEquals("Message", tabDescriptors[1].getLabel());//$NON-NLS-1$
+        /**
+         * No other tab
+         */
+        assertEquals(2, tabDescriptors.length);
+
+        /**
+         * Message tab is widest
+         */
+        assertEquals(1, ((TabbedPropertyComposite) decorationTestsView.getTabbedPropertySheetPage().getControl()).getList().getWidestLabelIndex());
+    }
+}
diff --git a/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/TabbedPropertySheetPageTest.java b/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/TabbedPropertySheetPageTest.java
index 56b8b8e..3f5f2d2 100644
--- a/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/TabbedPropertySheetPageTest.java
+++ b/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/TabbedPropertySheetPageTest.java
@@ -20,6 +20,7 @@
 import org.eclipse.ui.IWorkbenchPage;
 import org.eclipse.ui.IWorkbenchWindow;
 import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.internal.views.properties.tabbed.view.TabbedPropertyComposite;
 import org.eclipse.ui.tests.views.properties.tabbed.sections.InformationTwoSection;
 import org.eclipse.ui.tests.views.properties.tabbed.sections.NameSection;
 import org.eclipse.ui.tests.views.properties.tabbed.views.TestsPerspective;
@@ -201,6 +202,105 @@
     }
 
     /**
+     * When Information node is selected, the Information tab is widest.
+     */
+    public void test_widestLabelIndex1() {
+        /**
+         * select Information node
+         */
+        setSelection(new TreeNode[] {treeNodes[0]});
+        ITabDescriptor[] tabDescriptors = testsView.getTabbedPropertySheetPage().getActiveTabs();
+
+        /**
+         * First tab is Name
+         */
+        assertEquals("Name", tabDescriptors[0].getLabel());//$NON-NLS-1$
+        /**
+         * Second tab is Information
+         */
+        assertEquals("Information", tabDescriptors[1].getLabel());//$NON-NLS-1$
+        /**
+         * Third tab is Message
+         */
+        assertEquals("Message", tabDescriptors[2].getLabel());//$NON-NLS-1$
+        /**
+         * No fourth tab
+         */
+        assertEquals(3, tabDescriptors.length);
+
+        /**
+         * Information tab is widest
+         */
+        assertEquals(1, ((TabbedPropertyComposite) testsView.getTabbedPropertySheetPage().getControl()).getList().getWidestLabelIndex());
+    }
+
+    /**
+     * When Error node is selected, the Message tab is widest.
+     */
+    public void test_widestLabelIndex2() {
+        /**
+         * select Error node
+         */
+        setSelection(new TreeNode[] {treeNodes[2]});
+        ITabDescriptor[] tabDescriptors = testsView.getTabbedPropertySheetPage().getActiveTabs();
+
+        /**
+         * First tab is Name
+         */
+        assertEquals("Name", tabDescriptors[0].getLabel());//$NON-NLS-1$
+        /**
+         * Second tab is Error
+         */
+        assertEquals("Error", tabDescriptors[1].getLabel());//$NON-NLS-1$
+        /**
+         * Third tab is Message
+         */
+        assertEquals("Message", tabDescriptors[2].getLabel());//$NON-NLS-1$
+        /**
+         * No fourth tab
+         */
+        assertEquals(3, tabDescriptors.length);
+
+        /**
+         * Message tab is widest
+         */
+        assertEquals(2, ((TabbedPropertyComposite) testsView.getTabbedPropertySheetPage().getControl()).getList().getWidestLabelIndex());
+    }
+
+    /**
+     * When Warning node is selected, the Warning tab is widest.
+     */
+    public void test_widestLabelIndex3() {
+        /**
+         * select Warning node
+         */
+        setSelection(new TreeNode[] {treeNodes[3]});
+        ITabDescriptor[] tabDescriptors = testsView.getTabbedPropertySheetPage().getActiveTabs();
+
+        /**
+         * First tab is Name
+         */
+        assertEquals("Name", tabDescriptors[0].getLabel());//$NON-NLS-1$
+        /**
+         * Second tab is Warning
+         */
+        assertEquals("Warning", tabDescriptors[1].getLabel());//$NON-NLS-1$
+        /**
+         * Third tab is Message
+         */
+        assertEquals("Message", tabDescriptors[2].getLabel());//$NON-NLS-1$
+        /**
+         * No fourth tab
+         */
+        assertEquals(3, tabDescriptors.length);
+
+        /**
+         * Warning tab is widest
+         */
+        assertEquals(1, ((TabbedPropertyComposite) testsView.getTabbedPropertySheetPage().getControl()).getList().getWidestLabelIndex());
+    }
+
+    /**
      * When File, Folder and Project Nodes are selected, only the Resource tab
      * displays. Tests input attribute.
      */
@@ -230,6 +330,10 @@
         assertNull(tabContents);
         ITabDescriptor[] TabDescriptors = testsView.getTabbedPropertySheetPage().getActiveTabs();
         assertEquals(0, TabDescriptors.length);
+        /**
+         * widestLabelIndex should be -1
+         */
+        assertEquals(-1, ((TabbedPropertyComposite) testsView.getTabbedPropertySheetPage().getControl()).getList().getWidestLabelIndex());
     }
 
 }
diff --git a/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/decorations/TabbedPropertySheetPageWithDecorations.java b/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/decorations/TabbedPropertySheetPageWithDecorations.java
new file mode 100644
index 0000000..a848db0
--- /dev/null
+++ b/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/decorations/TabbedPropertySheetPageWithDecorations.java
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * Copyright (c) 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.ui.tests.views.properties.tabbed.decorations;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.internal.views.properties.tabbed.view.TabbedPropertyComposite;
+import org.eclipse.ui.internal.views.properties.tabbed.view.TabbedPropertyList;
+import org.eclipse.ui.views.properties.tabbed.ITabDescriptor;
+import org.eclipse.ui.views.properties.tabbed.ITabItem;
+import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor;
+import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
+
+public class TabbedPropertySheetPageWithDecorations extends
+		TabbedPropertySheetPage {
+
+	private boolean useDecorations;
+
+	private Image image;
+
+	private Color color = Display.getCurrent().getSystemColor(SWT.COLOR_RED);
+
+	public TabbedPropertySheetPageWithDecorations(
+			ITabbedPropertySheetPageContributor tabbedPropertySheetPageContributor) {
+		super(tabbedPropertySheetPageContributor);
+	}
+
+	public void selectionChanged(IWorkbenchPart part, ISelection selection) {
+		if (selection.equals(getCurrentSelection())) {
+			return;
+		}
+		super.selectionChanged(part, selection);
+		if (useDecorations) {
+			/*
+			 * Call ListElement's showDynamicImage(), hideDynamicImage(),
+			 * setTextColor() and setDefaultTextColor() methods to make sure
+			 * that they don't throw any exceptions.
+			 */
+			TabbedPropertyList tabbedPropertyList = ((TabbedPropertyComposite) this
+					.getControl()).getList();
+			for (int i = 0; i < tabbedPropertyList.getNumberOfElements(); i++) {
+				TabbedPropertyList.ListElement tabListElement = (TabbedPropertyList.ListElement) tabbedPropertyList
+						.getElementAt(i);
+				if (tabListElement != null) {
+					ITabItem tab = tabListElement.getTabItem();
+					if (tab.getText().equals("Name")) {
+						/*
+						 * The Name tab can have 5 images. Check boundary
+						 * conditions to make sure that the code does not throw
+						 * IndexOutOfBoundsException.
+						 */
+						tabListElement.showDynamicImage(-1, image);
+						tabListElement.hideDynamicImage(-1);
+
+						tabListElement.showDynamicImage(0, image);
+						tabListElement.hideDynamicImage(0);
+
+						tabListElement.showDynamicImage(2, image);
+						tabListElement.hideDynamicImage(2);
+
+						tabListElement.showDynamicImage(4, image);
+						tabListElement.hideDynamicImage(4);
+
+						tabListElement.showDynamicImage(5, image);
+						tabListElement.hideDynamicImage(5);
+
+						tabListElement.showDynamicImage(7, image);
+						tabListElement.hideDynamicImage(7);
+
+						/*
+						 * Set and reset the tab-label's color. Make sure that
+						 * the code does not throw NullPointerException.
+						 */
+						tabListElement.setTextColor(null);
+						tabListElement.setTextColor(color);
+						tabListElement.setDefaultTextColor();
+					} else if (tab.getText().equals("Message")) {
+						/*
+						 * The Name tab can have 3 images. Check boundary
+						 * conditions to make sure that the code does not throw
+						 * IndexOutOfBoundsException.
+						 */
+						tabListElement.showDynamicImage(-1, image);
+						tabListElement.hideDynamicImage(-1);
+
+						tabListElement.showDynamicImage(0, image);
+						tabListElement.hideDynamicImage(0);
+
+						tabListElement.showDynamicImage(1, image);
+						tabListElement.hideDynamicImage(1);
+
+						tabListElement.showDynamicImage(2, image);
+						tabListElement.hideDynamicImage(2);
+
+						tabListElement.showDynamicImage(3, image);
+						tabListElement.hideDynamicImage(3);
+
+						tabListElement.showDynamicImage(7, image);
+						tabListElement.hideDynamicImage(7);
+					}
+				}
+			}
+		}
+	}
+
+	protected void updateTabs(ITabDescriptor[] descriptors) {
+		super.updateTabs(descriptors);
+		if (useDecorations) {
+			// Set the number of decoration-images in the TabbedPropertyList
+			TabbedPropertyList tabbedPropertyList = ((TabbedPropertyComposite) this
+					.getControl()).getList();
+			Map tabToImageDecorationsMap = getImageDecorationsForTabs(descriptors);
+			tabbedPropertyList.setDynamicImageCount(tabToImageDecorationsMap);
+		}
+	}
+
+	private Map getImageDecorationsForTabs(ITabItem[] tabItems) {
+		Map tabToImageDecorationsMap = new HashMap();
+		for (int i = 0; i < tabItems.length; i++) {
+			if (tabItems[i].getText().equals("Name")) {
+				tabToImageDecorationsMap.put(tabItems[i], new Integer(5));
+			} else if (tabItems[i].getText().equals("Message")) {
+				tabToImageDecorationsMap.put(tabItems[i], new Integer(3));
+			} else {
+				tabToImageDecorationsMap.put(tabItems[i], new Integer(0));
+			}
+		}
+		return tabToImageDecorationsMap;
+	}
+
+	public void useDecorations(boolean value) {
+		this.useDecorations = value;
+	}
+}
diff --git a/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/decorations/views/DecorationTestsView.java b/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/decorations/views/DecorationTestsView.java
new file mode 100644
index 0000000..c254034
--- /dev/null
+++ b/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/decorations/views/DecorationTestsView.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.ui.tests.views.properties.tabbed.decorations.views;
+
+import org.eclipse.ui.tests.views.properties.tabbed.decorations.TabbedPropertySheetPageWithDecorations;
+import org.eclipse.ui.tests.views.properties.tabbed.views.TestsView;
+import org.eclipse.ui.views.properties.IPropertySheetPage;
+
+public class DecorationTestsView extends TestsView {
+
+	public static final String DECORATION_TESTS_VIEW_ID = "org.eclipse.ui.tests.views.properties.tabbed.decorations.views.DecorationTestsView"; //$NON-NLS-1$
+
+    public Object getAdapter(Class adapter) {
+        if (adapter == IPropertySheetPage.class) {
+            if (tabbedPropertySheetPage == null) {
+                tabbedPropertySheetPage = new TabbedPropertySheetPageWithDecorations(this);
+            }
+            return tabbedPropertySheetPage;
+        }
+        return super.getAdapter(adapter);
+    }
+
+}
diff --git a/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/views/TestsView.java b/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/views/TestsView.java
index 8099080..57f7fe1 100644
--- a/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/views/TestsView.java
+++ b/tests/org.eclipse.ui.tests.views.properties.tabbed/src/org/eclipse/ui/tests/views/properties/tabbed/views/TestsView.java
@@ -28,7 +28,7 @@
 
     private TreeViewer viewer;
 
-    private TabbedPropertySheetPage tabbedPropertySheetPage;
+    protected TabbedPropertySheetPage tabbedPropertySheetPage;
 
     public static final String TESTS_VIEW_ID = "org.eclipse.ui.tests.views.properties.tabbed.views.TestsView"; //$NON-NLS-1$