### Eclipse Workspace Patch 1.0 #P org.eclipse.jface Index: src/org/eclipse/jface/viewers/StyledViewerLabel.java =================================================================== RCS file: src/org/eclipse/jface/viewers/StyledViewerLabel.java diff -N src/org/eclipse/jface/viewers/StyledViewerLabel.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/jface/viewers/StyledViewerLabel.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2007 Michael Krkoska 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: + * Michael Krkoska - initial API and implementation + ******************************************************************************/ + +package org.eclipse.jface.viewers; + +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.graphics.Image; + +/** + * A viewer label for usage with {@link StyledCellLabelProvider}. + * + * @since 3.4 + * @author Michael Krkoska + */ +public class StyledViewerLabel extends ViewerLabel { + + StyleRange[] styleRanges; + + /** + * @param initialText + * @param initialImage + */ + public StyledViewerLabel(String initialText, Image initialImage) { + super(initialText, initialImage); + } + + /** + * @return Returns the styleRanges for this label. + */ + public StyleRange[] getStyleRanges() { + return styleRanges; + } + + /** + * @param styleRanges + * The styleRanges for this label. If null, no styles are + * applied. + */ + public void setStyleRanges(StyleRange[] styleRanges) { + this.styleRanges = styleRanges; + } +} Index: src/org/eclipse/jface/viewers/StyledCellLabelProvider.java =================================================================== RCS file: src/org/eclipse/jface/viewers/StyledCellLabelProvider.java diff -N src/org/eclipse/jface/viewers/StyledCellLabelProvider.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/jface/viewers/StyledCellLabelProvider.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,711 @@ +/******************************************************************************* + * Copyright (c) 2007 Michael Krkoska 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: + * Michael Krkoska - initial API and implementation + * Tom Schindl - initial API + *******************************************************************************/ +package org.eclipse.jface.viewers; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.GlyphMetrics; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.graphics.TextLayout; +import org.eclipse.swt.graphics.TextStyle; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Item; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableItem; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; +import org.eclipse.swt.widgets.Widget; + +/** + * A CellLabelProvider which supports styled labels by using owner draw. + *

+ * For providing the label's styles, create a subclass and overwrite + * {@link StyledCellLabelProvider#getStyledViewerLabel(Object)} . The default + * values for colors and fonts, which are applied on all parts of the label + * where no style is specified, can be provided by implementing + * {@link IFontProvider} or {@link IColorProvider} in the subclass. + *

+ *

+ * To specify the position of the image provided by the label provider, include + * the IMAGE_PLACEHOLDER char in your label. If this char is not included, the + * label is positioned at the beginning. + *

+ *

+ * To set up styled labels for your viewer's control, you must call + * {@link OwnerDrawLabelProvider#setUpOwnerDraw(ColumnViewer)}. + *

+ *

+ * For multi line labels usage of StyledMultiLineProvider is recommended. + *

+ * + * @since 3.4 + * @author Michael Krkoska + * @author Tom Schindl + */ +public class StyledCellLabelProvider extends OwnerDrawLabelProvider implements + ILabelProvider { + + /** + * This is the char which can be used as placeholder for an image in the + * label. + */ + public static final char IMAGE_PLACEHOLDER = '\uFFFC'; + + /** + * Style constant for centering the viewer's items vertically if their + * heights are less than the maximum vertical height. When using + * {@link StyledCellLabelProvider#EMULATE_NATIVE_POSITIONING} this style is + * ignored. + */ + public static final int CENTER_ITEMS_VERTICALLY = 1; + /** + * Style constant for centering the text of a viewer's item vertically if + * its height is less than the height of the item's image. + */ + public static final int CENTER_TEXT_VERTICALLY = 2; + /** + * Style constant for indicating that the styled colors are to be applied + * even it the viewer's item is selected. Default is not to apply colors. + */ + public static final int APPLY_COLORS_ON_SELECTION = 4; + /** + * Style constant for indicating that the positioning of image and text + * should emulate native behaviour as closely as possible. Since the height + * of the cells is not changed from native size, this style does not work + * with multi-line labels. + */ + public static final int EMULATE_NATIVE_POSITIONING = 8; + + private static final boolean isCarbon = SWT.getPlatform().equals("carbon"); //$NON-NLS-1$ + private static final boolean isGTK = SWT.getPlatform().equals("gtk"); //$NON-NLS-1$ + + boolean centerItemsVertically = false; + boolean centerTextVertically = false; + boolean applyColorsOnSelection = false; + boolean emulateNativePositioning = false; + + ColumnViewer viewer; + private IFontProvider fontProvider = null; + private IColorProvider colorProvider = null; + + private Map styledFonts = new HashMap(); + + private int cacheKeyIndex = -1; + private Object cacheKeyElement = null; + private StyledViewerLabel cachedViewerLabel = null; + private TextLayout cachedTextLayout = null; + private boolean paintFollowsMeasureImmediately = true; + + /** + * Creates a new StyledCellLabelProvider. + * + * @param viewer + * @param style + * the style bits + * @see StyledCellLabelProvider#CENTER_ITEMS_VERTICALLY + * @see StyledCellLabelProvider#CENTER_TEXT_VERTICALLY + * @see StyledCellLabelProvider#APPLY_COLORS_ON_SELECTION + */ + public StyledCellLabelProvider(ColumnViewer viewer, int style) { + this.viewer = viewer; + if ((CENTER_ITEMS_VERTICALLY & style) != 0) { + centerItemsVertically = true; + } + if ((CENTER_TEXT_VERTICALLY & style) != 0) { + centerTextVertically = true; + } + if ((APPLY_COLORS_ON_SELECTION & style) != 0) { + applyColorsOnSelection = true; + } + if ((EMULATE_NATIVE_POSITIONING & style) != 0) { + emulateNativePositioning = true; + } + if (this instanceof IFontProvider) { + fontProvider = (IFontProvider) this; + } + if (this instanceof IColorProvider) { + colorProvider = (IColorProvider) this; + } + } + + public void dispose() { + super.dispose(); + for (Iterator iterator = styledFonts.values().iterator(); iterator + .hasNext();) { + Font f = (Font) iterator.next(); + f.dispose(); + } + styledFonts.clear(); + if (cachedTextLayout != null && !cachedTextLayout.isDisposed()) + cachedTextLayout.dispose(); + } + + public Image getImage(Object element) { + return null; + } + + public String getText(Object element) { + return ""; //$NON-NLS-1$ + } + + public void update(ViewerCell cell) { + if (emulateNativePositioning) { + Object element = cell.getElement(); + cell.setText(getText(element)); + Image image = getImage(element); + cell.setImage(image); + cell.setBackground(getDefaultBackground(element)); + cell.setForeground(getDefaultForeground(element)); + cell.setFont(getDefaultFont(element)); + } else if (isCarbon) + // workaround for Bug 151460 + cell.setText(""); //$NON-NLS-1$ + + super.update(cell); + } + + /** + * Creates a TextLayout instance for either measuring or painting the item, + * using the StyleRanges contained in the {@link StyledViewerLabel}. + * + * Adds an image at the first occurence of + * {@link StyledCellLabelProvider#IMAGE_PLACEHOLDER} or at the beginning of + * the label. + * + * @param event + * the measure or paint event for which a TextLayout is needed + * @param viewerLabel + * the viewer label containing the text, image and styles for the + * TextLayout + * @return a TextLayout instance which must be disposed after use. + */ + protected TextLayout createTextLayout(Event event, + StyledViewerLabel viewerLabel) { + String label = viewerLabel.getText(); + Image image = viewerLabel.getImage(); + Font defaultFont = viewerLabel.getFont(); + if (defaultFont == null) + defaultFont = viewer.getControl().getFont(); + Color defaultBackground = viewerLabel.getBackground(); + boolean applyColors = (applyColorsOnSelection || (event.detail & SWT.SELECTED) == 0); + + Display display = event.display; + TextLayout layout = new TextLayout(display); + + int maxFontHeight = 0; + layout.setFont(defaultFont); + maxFontHeight = getFontHeight(defaultFont); + + int rangeOffset = 0, imageOffset = 0; + GlyphMetrics imageMetrics = null; + TextStyle imageStyle = null; + if (image != null) { + int index = label.indexOf(IMAGE_PLACEHOLDER); + String labelWithImage = label; + if (index < 0) { + labelWithImage = IMAGE_PLACEHOLDER + " " + label; //$NON-NLS-1$ + rangeOffset = 2; + } + if (emulateNativePositioning && labelWithImage.length() > 0 + && labelWithImage.charAt(0) == IMAGE_PLACEHOLDER) { + layout.setText(label); + rangeOffset = 0; + } else { + layout.setText(labelWithImage); + imageOffset = Math.max(0, index); + Rectangle bounds = image.getBounds(); + imageStyle = new TextStyle(null, null, null); + imageMetrics = new GlyphMetrics(bounds.height, 0, bounds.width); + imageStyle.metrics = imageMetrics; + } + } else { + layout.setText(label); + } + + StyleRange[] styleRanges = viewerLabel.getStyleRanges(); + if (styleRanges != null) { + for (int i = 0; i < styleRanges.length; i++) { + StyleRange styleRange = styleRanges[i]; + checkFont(display, styleRange); + if (!applyColors) { + styleRange = stripColors(styleRange); + } + layout.setStyle(styleRange, styleRange.start + rangeOffset, + styleRange.start + rangeOffset + styleRange.length - 1); + if (styleRange.font != null) { + maxFontHeight = Math.max(maxFontHeight, + getFontHeight(styleRange.font)); + } + } + } + + if (imageStyle != null) { + layout.setStyle(imageStyle, imageOffset, imageOffset); + } + + if (centerTextVertically && imageMetrics != null) { + int lineCount = layout.getLineCount(); + if (lineCount == 1 && imageMetrics.ascent > maxFontHeight) { + // vertically center the text if there is an image bigger than + // the text. + int height = imageMetrics.ascent; + int space = (height - maxFontHeight); + imageMetrics.ascent = (maxFontHeight) + Math.round(space / 2f); + // TODO why does it look better (at least on win xp) with this + // added pixel? + layout.setDescent(height - imageMetrics.ascent + 1); + } + + } + + if (defaultBackground != null && applyColors) { + int[] ranges = layout.getRanges(); + int oldEnd = -1; + TextStyle[] styles = layout.getStyles(); + for (int i = 0, length = ranges.length; i < length; i += 2) { + int start = ranges[i]; + int end = ranges[i + 1]; + if (start > oldEnd + 1) { + TextStyle style = new TextStyle(null, null, + defaultBackground); + layout.setStyle(style, oldEnd + 1, start - 1); + } + + TextStyle style = styles[i >> 1]; + if (style.background == null) { + style.background = defaultBackground; + layout.setStyle(style, start, end); + } + + oldEnd = end; + } + if (layout.getText().length() > oldEnd + 1) { + TextStyle style = new TextStyle(null, null, defaultBackground); + layout.setStyle(style, oldEnd + 1, + layout.getText().length() - 1); + } + } + + return layout; + } + + /** + * Handle the erase event. The default implementation does nothing to ensure + * keep native selection highlighting working. + * + * @param event + * the erase event + * @param element + * the model object + * @see SWT#EraseItem + */ + protected void erase(Event event, Object element) { + event.detail &= ~SWT.FOREGROUND; + } + + /** + * Returns a StyledViewerLabel instance containing the text, image and style + * information to use for displaying element. + * + * The default implementation sets the default font and colors if this + * StyledCellLabelProvider implements {@link IFontProvider} or + * {@link IColorProvider}. These are applied on the label if they are not + * overwritten by styles. + * + * This implementation does not set any styles. To achieve this, you should + * subclass, call super.getStyledViewerLabel(Object) and set your styles in + * the instance. + * + * @param element + * the element to create a viewer label for + * @return the styled viewer label containing the information how to display + * element + */ + protected StyledViewerLabel getStyledViewerLabel(Object element) { + StyledViewerLabel viewerLabel = new StyledViewerLabel(null, null); + viewerLabel.setImage(getImage(element)); + viewerLabel.setText(getText(element)); + viewerLabel.setFont(getDefaultFont(element)); + viewerLabel.setBackground(getDefaultBackground(element)); + viewerLabel.setForeground(getDefaultForeground(element)); + return viewerLabel; + } + + protected void measure(Event event, Object element) { + int height = isCarbon ? getCurrentHeight(event.widget) : 0; + + StyledViewerLabel viewerLabel = getViewerLabelFromCache(event, element); + if (viewerLabel == null) + viewerLabel = getStyledViewerLabel(element); + + measure(event, element, viewerLabel); + + if (isCarbon && event.height > height) { + /* + * on Carbon the first cell in a Table is not updated properly, if + * the size changes. + */ + viewer.getControl().redraw(); + } + } + + /** + * Handle the MeasureItem event. + * + * @param event + * The MeasureItem event + * @param element + * The element of the viewer's item of this MeasureItem event + * @param viewerLabel + * The ViewerLabel of the viewer's item of this MeasureItem event + * @see SWT#MeasureItem + */ + protected void measure(Event event, Object element, + StyledViewerLabel viewerLabel) { + TextLayout textLayout = createTextLayout(event, viewerLabel); + Rectangle bounds = textLayout.getBounds(); + + if (emulateNativePositioning) { + // don't change native height + bounds.height = event.height; + + // do change width to keep pack() working + int x = event.x; + if (isGTK || isCarbon) // see Bug 129978 + x = getBounds(event).x; + bounds.width += Math.max(0, getTextBounds(event).x - x); + } + + event.width = bounds.width; + event.height = bounds.height; + + putIntoCache(event, element, viewerLabel, textLayout); + } + + protected void paint(Event event, Object element) { + checkEventOrder(event, element); + StyledViewerLabel viewerLabel = getViewerLabelFromCache(event, element); + if (viewerLabel == null) + viewerLabel = getStyledViewerLabel(element); + paint(event, element, viewerLabel); + } + + /** + * Handle the PaintItem event. + * + * @param event + * The PaintItem event + * @param element + * The element of the viewer's item of this PaintItem event + * @param viewerLabel + * The ViewerLabel of the viewer's item of this PaintItem event + * @see SWT#PaintItem + */ + protected void paint(Event event, Object element, + StyledViewerLabel viewerLabel) { + + String label = viewerLabel.getText(); + Image image = viewerLabel.getImage(); + Color defaultBackground = viewerLabel.getBackground(); + + TextLayout textLayout = getLayoutFromCache(event, element); + if (textLayout == null + || (!applyColorsOnSelection && (event.detail & SWT.SELECTED) != 0)) + /* + * if painting selection and not applying colors, don't use cache, + * since at cache time we didn't know about SWT.SELECTED. This + * detail is not set in MeasureItem. + */ + textLayout = createTextLayout(event, viewerLabel); + + boolean applyColors = (applyColorsOnSelection || (event.detail & SWT.SELECTED) == 0); + GC gc = event.gc; + Color oldForeground = gc.getForeground(), oldBackground = gc + .getBackground(); + Color foreground = viewerLabel.getForeground(); + if (foreground != null && applyColors) { + gc.setForeground(foreground); + } + + if (defaultBackground != null && applyColors) { + gc.setBackground(defaultBackground); + } + + int heightOffset = 0; + if (centerItemsVertically && !emulateNativePositioning) { + int height = event.height; + if (isCarbon) + height = getCurrentHeight(event.widget); + heightOffset = (height - textLayout.getBounds().height) / 2; + heightOffset = Math.max(0, heightOffset); + } + + int widthOffset = 0; + int imageOffset = Math.max(0, label.indexOf(IMAGE_PLACEHOLDER)); + if (emulateNativePositioning + && (!isCarbon || (image != null && imageOffset == 0))) { + // on carbon indent the label only if there is a leading image + Rectangle textBounds = getTextBounds(event); + widthOffset += Math.max(0, textBounds.x - event.x); + heightOffset += Math.max(0, textBounds.y - event.y); + } + + textLayout.draw(gc, event.x + widthOffset, event.y + heightOffset); + + if (image != null) { + if (emulateNativePositioning && imageOffset == 0) { + Rectangle imageBounds = getImageBounds(event); + Rectangle bounds = image.getBounds(); + if (isCarbon) { + /* + * on carbon image bounds seem to behave differently, + * especially when the images in a viewer are of different + * sizes + */ + int x = imageBounds.x + + Math.max(0, + (imageBounds.width - bounds.width) / 2); + int y = imageBounds.y + + Math.max(0, + (imageBounds.height - bounds.height) / 2); + gc.drawImage(image, x, y); + } else { + gc.drawImage(image, 0, 0, bounds.width, bounds.height, + imageBounds.x, imageBounds.y, imageBounds.width, + imageBounds.height); + } + } else { + int lineIndex = textLayout.getLineIndex(imageOffset); + FontMetrics lineMetrics = textLayout.getLineMetrics(lineIndex); + Point point = textLayout.getLocation(imageOffset, false); + GlyphMetrics glyphMetrics = textLayout.getStyle(imageOffset).metrics; + int y = event.y + point.y + lineMetrics.getAscent() + - glyphMetrics.ascent + heightOffset; + /* + * Following line is because of a strange incorrect measurement + * of 3 pixels under win32 in some cases. + */ + y = Math.max(event.y, y); + gc.drawImage(image, event.x + point.x + widthOffset, y); + } + } + textLayout.dispose(); + + gc.setForeground(oldForeground); + gc.setBackground(oldBackground); + } + + int getFontHeight(Font font) { + int h = 0; + FontData[] fontData = font.getFontData(); + for (int i = 0; i < fontData.length; i++) { + h = Math.max(h, fontData[i].getHeight()); + } + return h; + } + + private void checkEventOrder(Event event, Object element) { + // checks if the event order of this SWT implementation is cachable + if (!paintFollowsMeasureImmediately || cacheKeyElement != element + || cacheKeyIndex != event.index) + paintFollowsMeasureImmediately = false; + } + + private void checkFont(Device device, StyleRange styleRange) { + int fontStyle = styleRange.fontStyle; + if (fontStyle != SWT.NORMAL) { + Font font = styleRange.font; + if (font == null) { + font = JFaceResources.getDefaultFont(); + } + boolean wrongStyle = false; + FontData[] fontData = font.getFontData(); + for (int i = 0, length = fontData.length; i < length && !wrongStyle; i++) { + if (fontData[i].getStyle() != fontStyle) { + wrongStyle = true; + } + } + if (wrongStyle) { + fontData = getFontData(font, fontStyle); + // TODO using fontData[0] as cache key might be wrong on motif + font = (Font) styledFonts.get(fontData[0]); + if (font == null) { + font = new Font(device, fontData); + styledFonts.put(fontData[0], font); + } + } + styleRange.font = font; + } + } + + private Rectangle getBounds(Event event) { + Item item = (Item) event.item; + Rectangle bounds = null; + if (item instanceof TreeItem) { + TreeItem treeItem = (TreeItem) item; + bounds = treeItem.getBounds(event.index); + } else if (item instanceof TableItem) { + TableItem tableItem = (TableItem) item; + bounds = tableItem.getBounds(event.index); + } + return bounds; + } + + private int getCurrentHeight(Widget widget) { + if (widget instanceof Table) + return ((Table) widget).getItemHeight(); + if (widget instanceof Tree) + return ((Tree) widget).getItemHeight(); + return -1; + } + + /** + * Provides a default background color for the given element, which is used + * for the part of the label where no background color is specified in the + * StyleRanges provided by the StyledViewerLabel. + * + * @param element + * the element + * @return the background color for the element, or null to + * use the default background color + */ + private Color getDefaultBackground(Object element) { + if (colorProvider != null) { + return colorProvider.getBackground(element); + } + return null; + } + + /** + * Provides a default font for the given element, which is used for the part + * of the label where no font is specified in the StyleRanges provided by + * the StyledViewerLabel. + * + * @param element + * the element + * @return the font for the element, or null to use the + * default font + */ + private Font getDefaultFont(Object element) { + if (fontProvider != null) { + return fontProvider.getFont(element); + } + return null; + } + + /** + * Provides a default foreground color for the given element, which is used + * for the part of the label where no foreground color is specified in the + * StyleRanges provided by the StyledViewerLabel. + * + * @param element + * the element + * @return the foreground color for the element, or null to + * use the default foreground color + */ + private Color getDefaultForeground(Object element) { + if (colorProvider != null) { + return colorProvider.getForeground(element); + } + return null; + } + + private FontData[] getFontData(Font font, int style) { + FontData[] fontDatas = font.getFontData(); + for (int i = 0; i < fontDatas.length; i++) { + fontDatas[i].setStyle(style); + } + return fontDatas; + } + + private Rectangle getImageBounds(Event event) { + Item item = (Item) event.item; + Rectangle imageBounds = null; + if (item instanceof TreeItem) { + TreeItem treeItem = (TreeItem) item; + imageBounds = treeItem.getImageBounds(event.index); + } else if (item instanceof TableItem) { + TableItem tableItem = (TableItem) item; + imageBounds = tableItem.getImageBounds(event.index); + } + return imageBounds; + } + + private TextLayout getLayoutFromCache(Event event, Object element) { + if (paintFollowsMeasureImmediately && cacheKeyElement == element + && cacheKeyIndex == event.index && cachedTextLayout != null + && !cachedTextLayout.isDisposed()) + return cachedTextLayout; + return null; + } + + private Rectangle getTextBounds(Event event) { + Item item = (Item) event.item; + Rectangle textBounds = null; + if (item instanceof TreeItem) { + TreeItem treeItem = (TreeItem) item; + textBounds = treeItem.getTextBounds(event.index); + } else if (item instanceof TableItem) { + TableItem tableItem = (TableItem) item; + textBounds = tableItem.getTextBounds(event.index); + } + return textBounds; + } + + private StyledViewerLabel getViewerLabelFromCache(Event event, + Object element) { + if (paintFollowsMeasureImmediately && cacheKeyElement == element + && cacheKeyIndex == event.index && cachedViewerLabel != null) + return cachedViewerLabel; + return null; + } + + private void putIntoCache(Event event, Object element, + StyledViewerLabel viewerLabel, TextLayout textLayout) { + cacheKeyElement = element; + cacheKeyIndex = event.index; + if (cachedTextLayout != null && !cachedTextLayout.isDisposed()) + cachedTextLayout.dispose(); + cachedTextLayout = textLayout; + cachedViewerLabel = viewerLabel; + } + + private StyleRange stripColors(StyleRange style) { + if (style.background == null && style.foreground == null) + return style; + StyleRange clone = new StyleRange(style.start, style.length, null, null); + clone.font = style.font; + clone.fontStyle = style.fontStyle; + clone.rise = style.rise; + clone.strikeout = style.strikeout; + if (style.metrics != null) { + clone.metrics = new GlyphMetrics(style.metrics.ascent, + style.metrics.descent, style.metrics.width); + } + return clone; + } + +} Index: src/org/eclipse/jface/viewers/WrappedStyledCellLabelProvider.java =================================================================== RCS file: src/org/eclipse/jface/viewers/WrappedStyledCellLabelProvider.java diff -N src/org/eclipse/jface/viewers/WrappedStyledCellLabelProvider.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/jface/viewers/WrappedStyledCellLabelProvider.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2007 Michael Krkoska 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: + * Michael Krkoska - initial API and implementation + *******************************************************************************/ + +package org.eclipse.jface.viewers; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Image; + +/** + * A wrapper for using {@link StyledCellLabelProvider} with classic label + * providers implementing any of ILabelProvider, IFontProvider, IColorProvider + * or the corresponding table variants. + * + * The provided color and font information is used as default style for the + * item. + * + * @since 3.4 + * @author Michael Krkoska + */ +public class WrappedStyledCellLabelProvider extends StyledCellLabelProvider + implements IColorProvider, IFontProvider { + + private ILabelProvider labelProvider; + private IFontProvider fontProvider = null; + private IColorProvider colorProvider = null; + private ITableLabelProvider tableLabelProvider = null; + private ITableFontProvider tableFontProvider = null; + private ITableColorProvider tableColorProvider = null; + private int columnIndex; + + /** + * Creates a WrappedStyledCellLabelProvider for usage with a ViewerColumn. + * + * @param viewer + * the viewer for this CellLabelProvider + * @param style + * the style bits for this StyledCellLabelProvider + * @param labelProvider + * the label provider implementing ILabelProvider and optionally + * IFontProvider or IColorProvider + */ + public WrappedStyledCellLabelProvider(ColumnViewer viewer, int style, + IBaseLabelProvider labelProvider) { + this(viewer, style, labelProvider, 0); + } + + /** + * Creates a WrappedStyledCellLabelProvider for usage with a ViewerColumn. + * + * @param viewer + * the viewer for this CellLabelProvider + * @param style + * the style bits for this StyledCellLabelProvider + * @param labelProvider + * the label provider implementing ILabelProvider and optionally + * IFontProvider or IColorProvider + * @param columnIndex + * the column index to pass to a wrapped ITable*Provider + */ + public WrappedStyledCellLabelProvider(ColumnViewer viewer, int style, + IBaseLabelProvider labelProvider, int columnIndex) { + super(viewer, style); + this.columnIndex = columnIndex; + setLabelProvider(labelProvider); + } + + public Color getBackground(Object element) { + if (tableColorProvider != null) { + return tableColorProvider.getBackground(element, columnIndex); + } + if (colorProvider != null && columnIndex == 0) { + return colorProvider.getBackground(element); + } + return null; + } + + public Font getFont(Object element) { + if (tableFontProvider != null) { + return tableFontProvider.getFont(element, columnIndex); + } + if (fontProvider != null && columnIndex == 0) { + return fontProvider.getFont(element); + } + return null; + } + + public Color getForeground(Object element) { + if (tableColorProvider != null) { + return tableColorProvider.getForeground(element, columnIndex); + } + if (colorProvider != null && columnIndex == 0) { + return colorProvider.getForeground(element); + } + return null; + } + + public Image getImage(Object element) { + if (tableLabelProvider != null) { + return tableLabelProvider.getColumnImage(element, columnIndex); + } + if (columnIndex == 0) { + return labelProvider.getImage(element); + } + return null; + } + + public String getText(Object element) { + if (tableLabelProvider != null) { + return tableLabelProvider.getColumnText(element, columnIndex); + } + if (columnIndex == 0) { + return labelProvider.getText(element); + } + return ""; //$NON-NLS-1$ + } + + private void setLabelProvider(IBaseLabelProvider labelProvider) { + Assert.isTrue(labelProvider instanceof ITableLabelProvider + || labelProvider instanceof ILabelProvider); + if (labelProvider instanceof ILabelProvider) { + this.labelProvider = (ILabelProvider) labelProvider; + } + if (labelProvider instanceof IFontProvider) { + fontProvider = (IFontProvider) labelProvider; + } + if (labelProvider instanceof IColorProvider) { + colorProvider = (IColorProvider) labelProvider; + } + if (labelProvider instanceof ITableLabelProvider) { + tableLabelProvider = (ITableLabelProvider) labelProvider; + } + if (labelProvider instanceof ITableFontProvider) { + tableFontProvider = (ITableFontProvider) labelProvider; + } + if (labelProvider instanceof ITableColorProvider) { + tableColorProvider = (ITableColorProvider) labelProvider; + } + } + +} #P org.eclipse.jface.snippets Index: META-INF/MANIFEST.MF =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jface.snippets/META-INF/MANIFEST.MF,v retrieving revision 1.2 diff -u -r1.2 MANIFEST.MF --- META-INF/MANIFEST.MF 29 May 2007 11:43:12 -0000 1.2 +++ META-INF/MANIFEST.MF 15 Jul 2007 18:09:37 -0000 @@ -6,7 +6,8 @@ Bundle-Vendor: Eclipse.org Require-Bundle: org.eclipse.jface, org.eclipse.jface.databinding, - org.eclipse.core.runtime + org.eclipse.core.runtime, + org.eclipse.jface.text Export-Package: org.eclipse.jface.snippets.dialogs, org.eclipse.jface.snippets.layout, org.eclipse.jface.snippets.viewers, Index: Eclipse JFace Snippets/org/eclipse/jface/snippets/viewers/Snippet043StyledCellLabelProvider.java =================================================================== RCS file: Eclipse JFace Snippets/org/eclipse/jface/snippets/viewers/Snippet043StyledCellLabelProvider.java diff -N Eclipse JFace Snippets/org/eclipse/jface/snippets/viewers/Snippet043StyledCellLabelProvider.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Eclipse JFace Snippets/org/eclipse/jface/snippets/viewers/Snippet043StyledCellLabelProvider.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,274 @@ +/******************************************************************************* + * Copyright (c) 2007 Michael Krkoska 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: + * Michael Krkoska - initial API and implementation + *******************************************************************************/ + +package org.eclipse.jface.snippets.viewers; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.jface.viewers.ArrayContentProvider; +import org.eclipse.jface.viewers.ColumnPixelData; +import org.eclipse.jface.viewers.ColumnViewer; +import org.eclipse.jface.viewers.IColorProvider; +import org.eclipse.jface.viewers.IFontProvider; +import org.eclipse.jface.viewers.OwnerDrawLabelProvider; +import org.eclipse.jface.viewers.StyledCellLabelProvider; +import org.eclipse.jface.viewers.StyledViewerLabel; +import org.eclipse.jface.viewers.TableLayout; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** + * A TableViewer using a StyledCellLabelProvider to show some of the + * possibilities: + *
    + *
  • placing the image provided by the label provider anywhere in the label
  • + *
  • multi-line labels (currently only by placing line breaks in the label)
  • + *
  • multi font labels
  • + *
  • multi colored labels
  • + *
+ * + * @author Michael Krkoska + */ +public class Snippet043StyledCellLabelProvider { + + private static final String[] TEXT = new String[] { // + // + "TextStyle defines a set of styles that can be\n" + // + "applied to a range of text. The hashCode() method\n" + // + "in this class uses the values of the public fields to",// + "compute the hash value.\uFFFC When storing instances\n" + // + "of the class in hashed collections do not modify\n" + // + "these fields after the object has been inserted.",// + "Application code does not need to explicitly\n" + // + "release the resources managed and thus no\n" + // + "dispose() method is provided.\uFFFC", }; + + private static final Pattern METHODS = Pattern.compile("\\w+\\(\\)"); + private static final int SHELL_WIDTH = 640; + private static final Display DISPLAY = Display.getDefault(); + private static int IMAGE_SIZE = 16; + private static final Image IMAGE1 = new Image(DISPLAY, DISPLAY + .getSystemImage(SWT.ICON_WARNING).getImageData().scaledTo( + IMAGE_SIZE, IMAGE_SIZE)); + private static final Image IMAGE2 = new Image(DISPLAY, DISPLAY + .getSystemImage(SWT.ICON_ERROR).getImageData().scaledTo(IMAGE_SIZE, + IMAGE_SIZE)); + + public static void main(String[] args) { + + Shell shell = new Shell(DISPLAY, SWT.CLOSE); + shell.setSize(SHELL_WIDTH, 300); + shell.setLayout(new GridLayout(2, false)); + + Snippet043StyledCellLabelProvider example = new Snippet043StyledCellLabelProvider(); + example.createPartControl(shell); + + shell.open(); + + while (!shell.isDisposed()) { + if (!DISPLAY.readAndDispatch()) { + DISPLAY.sleep(); + } + } + DISPLAY.dispose(); + } + + private TableViewer viewer; + + private Text text; + + public Snippet043StyledCellLabelProvider() { + int defaultSize = JFaceResources.getDefaultFontDescriptor() + .getFontData()[0].getHeight(); + JFaceResources.getFontRegistry().put( + "Courier", + new FontData[] { new FontData("Courier New", defaultSize, + SWT.NORMAL) }); + FontData[] fontData = JFaceResources.getFontRegistry() + .defaultFontDescriptor().getFontData(); + fontData[0].setHeight(fontData[0].getHeight() + 2); + JFaceResources.getFontRegistry().put("BiggerDefault", fontData); + } + + public void createPartControl(Composite parent) { + Label label = new Label(parent, SWT.NONE); + label.setText("type something here:"); + + text = new Text(parent, SWT.NONE); + text.setText("hash"); + text.setSelection(0, 4); + text.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + text.addListener(SWT.Modify, new Listener() { + + public void handleEvent(Event event) { + viewer.refresh(); + } + }); + + // SWT.FULL_SELECTION is needed on win32 as long as Bug 168807 exists + viewer = new TableViewer(parent, SWT.FULL_SELECTION); + + GridData data = new GridData(GridData.GRAB_HORIZONTAL + | GridData.GRAB_VERTICAL | GridData.FILL_BOTH); + data.horizontalSpan = 2; + viewer.getControl().setLayoutData(data); + + viewer.setContentProvider(new ArrayContentProvider()); + createColumns(); + + OwnerDrawLabelProvider.setUpOwnerDraw(viewer); + viewer.setInput(TEXT); + } + + private void createColumns() { + TableLayout layout = new TableLayout(); + viewer.getTable().setLayout(layout); + viewer.getTable().setHeaderVisible(true); + viewer.getTable().setLinesVisible(true); + + int width = SHELL_WIDTH - 2 * 5 + - viewer.getTable().getShell().computeTrim(0, 0, 0, 0).width; + int width1 = width / 2; + int width2 = width - width1; + + TableViewerColumn tc = new TableViewerColumn(viewer, SWT.NONE); + tc.getColumn().setText("col1"); + layout.addColumnData(new ColumnPixelData(width1)); + tc.setLabelProvider(new MyStyleRangeLabelProviderCol1(viewer, + StyledCellLabelProvider.CENTER_ITEMS_VERTICALLY)); + + tc = new TableViewerColumn(viewer, SWT.NONE); + tc.getColumn().setText("col2"); + layout.addColumnData(new ColumnPixelData(width2)); + tc.setLabelProvider(new MyStyleRangeLabelProviderCol2(viewer, + StyledCellLabelProvider.CENTER_ITEMS_VERTICALLY)); + } + + private StyleRange[] getStyleRanges(Object element, String label) { + String searchText = text.getText(); + TextPresentation tp = new TextPresentation(); + if (searchText.length() > 0) { + Color yellow = text.getDisplay().getSystemColor(SWT.COLOR_YELLOW); + int index = -1; + while ((index = label.indexOf(searchText, index + 1)) >= 0) { + StyleRange styleRange = new StyleRange(index, searchText + .length(), null, yellow); + tp.mergeStyleRange(styleRange); + } + } + + Font courier = JFaceResources.getFont("Courier"); + Matcher matcher = METHODS.matcher(label); + while (matcher.find()) { + StyleRange styleRange = new StyleRange(matcher.start(), matcher + .end() + - matcher.start(), null, null); + styleRange.font = courier; + tp.mergeStyleRange(styleRange); + } + + List ranges = new ArrayList(); + for (Iterator iterator = tp.getAllStyleRangeIterator(); iterator + .hasNext();) { + ranges.add(iterator.next()); + } + return (StyleRange[]) ranges.toArray(new StyleRange[ranges.size()]); + } + + private class MyStyleRangeLabelProviderCol1 extends StyledCellLabelProvider + implements IColorProvider, IFontProvider { + + public MyStyleRangeLabelProviderCol1(ColumnViewer viewer, int style) { + super(viewer, style); + } + + public Color getBackground(Object element) { + return null; + } + + public Font getFont(Object element) { + if (element == TEXT[0]) { + return JFaceResources.getFont("BiggerDefault"); + } + return null; + } + + public Color getForeground(Object element) { + if (element == TEXT[1]) { + return DISPLAY.getSystemColor(SWT.COLOR_DARK_RED); + } + return null; + } + + public Image getImage(Object element) { + return IMAGE1; + } + + public String getText(Object element) { + return element.toString(); + } + + protected StyledViewerLabel getStyledViewerLabel(Object element) { + StyledViewerLabel viewerLabel = super.getStyledViewerLabel(element); + viewerLabel.setStyleRanges(getStyleRanges(element, viewerLabel + .getText())); + return viewerLabel; + } + } + + private class MyStyleRangeLabelProviderCol2 extends + MyStyleRangeLabelProviderCol1 { + + public MyStyleRangeLabelProviderCol2(ColumnViewer viewer, int style) { + super(viewer, style); + } + + public Font getFont(Object element) { + if (element == TEXT[1]) { + return JFaceResources.getFont("BiggerDefault"); + } + return null; + } + + public Color getForeground(Object element) { + if (element == TEXT[2]) { + return DISPLAY.getSystemColor(SWT.COLOR_DARK_RED); + } + return null; + } + + public Image getImage(Object element) { + return IMAGE2; + } + } + +} Index: Eclipse JFace Snippets/org/eclipse/jface/snippets/viewers/Snippet042WrappedStyledCellLabelProvider.java =================================================================== RCS file: Eclipse JFace Snippets/org/eclipse/jface/snippets/viewers/Snippet042WrappedStyledCellLabelProvider.java diff -N Eclipse JFace Snippets/org/eclipse/jface/snippets/viewers/Snippet042WrappedStyledCellLabelProvider.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Eclipse JFace Snippets/org/eclipse/jface/snippets/viewers/Snippet042WrappedStyledCellLabelProvider.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (c) 2007 Michael Krkoska 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: + * Michael Krkoska - initial API and implementation + *******************************************************************************/ +package org.eclipse.jface.snippets.viewers; + +import java.util.regex.Pattern; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.viewers.ArrayContentProvider; +import org.eclipse.jface.viewers.ColumnPixelData; +import org.eclipse.jface.viewers.ColumnViewer; +import org.eclipse.jface.viewers.IBaseLabelProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.OwnerDrawLabelProvider; +import org.eclipse.jface.viewers.StyledCellLabelProvider; +import org.eclipse.jface.viewers.StyledViewerLabel; +import org.eclipse.jface.viewers.TableLayout; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.WrappedStyledCellLabelProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** + * A simple TableViewer using a StyledCellLabelProvider to color a part of the + * label. To show how to re-use existing label providers, a + * WrappedStyledCellLabelProvider is used instead of the preferable + * StyledCellLabelProviders. + * + * @author Michael Krkoska + */ +public class Snippet042WrappedStyledCellLabelProvider { + + static final Display DISPLAY = Display.getDefault(); + static final Color GREEN = DISPLAY.getSystemColor(SWT.COLOR_DARK_GREEN); + static final Image IMAGE_BIG = DISPLAY.getSystemImage(SWT.ICON_WARNING); + static final Image IMAGE = new Image(DISPLAY, IMAGE_BIG.getImageData() + .scaledTo(16, 16)); + static final Pattern BRACES = Pattern.compile("\\(.*\\)"); + + public static void main(String[] args) { + + Shell shell = new Shell(DISPLAY, SWT.CLOSE); + shell.setSize(300, 300); + shell.setLayout(new GridLayout()); + + Snippet042WrappedStyledCellLabelProvider example = new Snippet042WrappedStyledCellLabelProvider(); + example.createPartControl(shell); + + shell.open(); + + while (!shell.isDisposed()) { + if (!DISPLAY.readAndDispatch()) { + DISPLAY.sleep(); + } + } + DISPLAY.dispose(); + } + + private TableViewer viewer; + private StyleRange[] styles; + + public void createPartControl(Composite parent) { + // SWT.FULL_SELECTION is needed on win32 as long as Bug 168807 exists + viewer = new TableViewer(parent, SWT.FULL_SELECTION); + + GridData data = new GridData(GridData.GRAB_HORIZONTAL + | GridData.GRAB_VERTICAL | GridData.FILL_BOTH); + viewer.getControl().setLayoutData(data); + + viewer.setContentProvider(new ArrayContentProvider()); + createColumns(); + + OwnerDrawLabelProvider.setUpOwnerDraw(viewer); + + String[] s = new String[10]; + String ip = " " + StyledCellLabelProvider.IMAGE_PLACEHOLDER; + for (int i = 0; i < s.length; i++) { + s[i] = "row " + i + (i % 3 == 1 ? ip : "") + " (green)" + + (i % 3 == 2 ? ip : ""); + } + viewer.setInput(s); + } + + private void createColumns() { + TableLayout layout = new TableLayout(); + viewer.getTable().setLayout(layout); + viewer.getTable().setHeaderVisible(true); + viewer.getTable().setLinesVisible(true); + + int width = 300 - 2 * 5 - viewer.getTable().getShell().computeTrim(0, + 0, 0, 0).width; + + IBaseLabelProvider labelProvider = new OldLabelProvider(); + + TableViewerColumn tc = new TableViewerColumn(viewer, SWT.NONE); + tc.getColumn().setText("col1"); + layout.addColumnData(new ColumnPixelData(width)); + tc.setLabelProvider(new MyWrappedStyledCellLabelProvider(viewer, + StyledCellLabelProvider.CENTER_ITEMS_VERTICALLY + | StyledCellLabelProvider.CENTER_TEXT_VERTICALLY + | StyledCellLabelProvider.EMULATE_NATIVE_POSITIONING, + labelProvider)); + //viewer.setLabelProvider(labelProvider); + } + + private final class OldLabelProvider extends LabelProvider { + + public Font getFont(Object element) { + return JFaceResources.getFont("Font"); + } + + public Image getImage(Object element) { +// if (element.toString().startsWith("row 3")) +// return IMAGE_BIG; + if (!element.toString().startsWith("row 0")) { + return IMAGE; + } + return null; + } + + public String getText(Object element) { + return element == null ? "" : element.toString(); //$NON-NLS-1$ + } + } + + private final class MyWrappedStyledCellLabelProvider extends + WrappedStyledCellLabelProvider { + + public MyWrappedStyledCellLabelProvider(ColumnViewer viewer, int style, + IBaseLabelProvider labelProvider) { + super(viewer, style, labelProvider); + } + + protected StyledViewerLabel getStyledViewerLabel(Object element) { + StyledViewerLabel viewerLabel = super.getStyledViewerLabel(element); + viewerLabel.setStyleRanges(getStyleRanges(element, viewerLabel + .getText())); + return viewerLabel; + } + + public StyleRange[] getStyleRanges(Object element, String label) { + if (styles == null) { + styles = new StyleRange[2]; + styles[0] = new StyleRange(6, 9, GREEN, null); + styles[1] = new StyleRange(0, 5, null, null); + styles[1].fontStyle = SWT.BOLD; + } + return styles; + } + + } + +}