### Eclipse Workspace Patch 1.0 #P org.eclipse.jface.snippets Index: Eclipse JFace Snippets/org/eclipse/jface/snippets/viewers/Snippet042StyledMultiLineLabelSupport.java =================================================================== RCS file: Eclipse JFace Snippets/org/eclipse/jface/snippets/viewers/Snippet042StyledMultiLineLabelSupport.java diff -N Eclipse JFace Snippets/org/eclipse/jface/snippets/viewers/Snippet042StyledMultiLineLabelSupport.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Eclipse JFace Snippets/org/eclipse/jface/snippets/viewers/Snippet042StyledMultiLineLabelSupport.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,257 @@ +/******************************************************************************* + * 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.StyledCellLabelProvider; +import org.eclipse.jface.viewers.StyledLabelSupport; +import org.eclipse.jface.viewers.StyledMultiLineLabelSupport; +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 StyledMultiLineLabelSupport to show some of the + * posibilities: + * + * + * @author Michael Krkoska + */ +public class Snippet042StyledMultiLineLabelSupport { + + 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)); + + Snippet042StyledMultiLineLabelSupport example = new Snippet042StyledMultiLineLabelSupport(); + example.createPartControl(shell); + + shell.open(); + + while ( !shell.isDisposed() ) { + if ( !DISPLAY.readAndDispatch() ) { + DISPLAY.sleep(); + } + } + DISPLAY.dispose(); + } + + private TableViewer viewer; + + private Text text; + + public Snippet042StyledMultiLineLabelSupport() { + 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(); + + StyledMultiLineLabelSupport.setUpMultiLineLabelSupport(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, new MyStyledFontSupport())); + + tc = new TableViewerColumn(viewer, SWT.NONE); + tc.getColumn().setText("col2"); + layout.addColumnData(new ColumnPixelData(width2)); + tc.setLabelProvider(new MyStyleRangeLabelProviderCol2(viewer, new MyStyledFontSupport())); + } + + private class MyStyledFontSupport extends StyledMultiLineLabelSupport { + + public MyStyledFontSupport() { + super(StyledLabelSupport.CENTER_ITEMS_VERTICALLY | StyledLabelSupport.CENTER_TEXT_VERTICALLY); + } + + public 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 final class MyStyleRangeLabelProviderCol1 extends StyledCellLabelProvider { + + private MyStyleRangeLabelProviderCol1( ColumnViewer viewer, StyledLabelSupport fontSupport ) { + super(viewer, fontSupport); + } + + 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(); + } + } + + private final class MyStyleRangeLabelProviderCol2 extends StyledCellLabelProvider { + + private MyStyleRangeLabelProviderCol2( ColumnViewer viewer, StyledLabelSupport fontSupport ) { + super(viewer, fontSupport); + } + + public Color getBackground( Object element ) { + return null; + } + + 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; + } + + public String getText( Object element ) { + return element.toString(); + } + } + +} Index: Eclipse JFace Snippets/org/eclipse/jface/snippets/viewers/Snippet041WrappedStyledCellLabelProvider.java =================================================================== RCS file: Eclipse JFace Snippets/org/eclipse/jface/snippets/viewers/Snippet041WrappedStyledCellLabelProvider.java diff -N Eclipse JFace Snippets/org/eclipse/jface/snippets/viewers/Snippet041WrappedStyledCellLabelProvider.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Eclipse JFace Snippets/org/eclipse/jface/snippets/viewers/Snippet041WrappedStyledCellLabelProvider.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,149 @@ +/******************************************************************************* + * 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.Matcher; +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.IBaseLabelProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.OwnerDrawLabelProvider; +import org.eclipse.jface.viewers.StyledLabelSupport; +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 StyledLabelSupport 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 Snippet041WrappedStyledCellLabelProvider { + + private static final Display DISPLAY = Display.getDefault(); + private static final Color GREEN = DISPLAY.getSystemColor(SWT.COLOR_DARK_GREEN); + private static final Image IMAGE_BIG = DISPLAY.getSystemImage(SWT.ICON_WARNING); + private static final Image IMAGE = new Image(DISPLAY, IMAGE_BIG.getImageData().scaledTo(16, 16)); + private 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()); + + Snippet041WrappedStyledCellLabelProvider example = new Snippet041WrappedStyledCellLabelProvider(); + example.createPartControl(shell); + + shell.open(); + + while ( !shell.isDisposed() ) { + if ( !DISPLAY.readAndDispatch() ) { + DISPLAY.sleep(); + } + } + DISPLAY.dispose(); + } + + private TableViewer viewer; + + 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]; + for ( int i = 0; i < s.length; i++ ) { + s[i] = "row " + i + " (green)"; + } + viewer.setInput(s); + } + + private void createColumns() { + TableLayout layout = new TableLayout(); + viewer.getTable().setLayout(layout); + viewer.getTable().setHeaderVisible(false); + viewer.getTable().setLinesVisible(true); + + int width = 300 - 2 * 5 - viewer.getTable().getShell().computeTrim(0, 0, 0, 0).width; + + IBaseLabelProvider labelProvider = new MyStyledLabelProvider(); + + TableViewerColumn tc = new TableViewerColumn(viewer, SWT.NONE); + tc.getColumn().setText("col1"); + layout.addColumnData(new ColumnPixelData(width)); + MyStyledLabelSupport support = new MyStyledLabelSupport(); + tc.setLabelProvider(new WrappedStyledCellLabelProvider(viewer, support, labelProvider)); + } + + private final class MyStyledLabelProvider extends LabelProvider { + + public Font getFont( Object element ) { + return JFaceResources.getFont("Font"); + } + + public Image getImage( Object element ) { +// if(element.toString().startsWith("row 1")) +// 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 MyStyledLabelSupport extends StyledLabelSupport { + + public MyStyledLabelSupport() { + super(StyledLabelSupport.CENTER_ITEMS_VERTICALLY | StyledLabelSupport.CENTER_TEXT_VERTICALLY); + } + + public StyleRange[] getStyleRanges( Object element, String label ) { + Matcher matcher = BRACES.matcher(label); + if ( matcher.find() ) { + StyleRange styleRange = new StyleRange(matcher.start(), matcher.end() - matcher.start(), null, null); + styleRange.foreground = GREEN; + return new StyleRange[] { styleRange }; + } + return null; + } + + } + +} #P org.eclipse.jface Index: src/org/eclipse/jface/viewers/StyledLabelSupport.java =================================================================== RCS file: src/org/eclipse/jface/viewers/StyledLabelSupport.java diff -N src/org/eclipse/jface/viewers/StyledLabelSupport.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/jface/viewers/StyledLabelSupport.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,339 @@ +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; + + +/** + * @since 3.4 + * + */ +public abstract class StyledLabelSupport { + + /** + * 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. + */ + 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; + + boolean centerItemsVertically = false; + boolean centerTextVertically = false; + + private Map styledFonts = new HashMap(); + + /** + * Creates a new StyledLabelSupport. + * + * @param style + * the style bits + * @see StyledLabelSupport#CENTER_ITEMS_VERTICALLY + * @see StyledLabelSupport#CENTER_TEXT_VERTICALLY + */ + public StyledLabelSupport( int style ) { + if ( (CENTER_ITEMS_VERTICALLY & style) != 0 ) { + centerItemsVertically = true; + } + if ( (CENTER_TEXT_VERTICALLY & style) != 0 ) { + centerTextVertically = true; + } + } + + /** + * Dispose any resources acquired during usage. The instance stays usable. + * + * More specifically: Styled fonts are created if needed and these have to be disposed again. + * + * This method is called automatically if a StyledCellLabelProvider's viewer is disposed. + */ + public void dispose() { + for ( Iterator iterator = styledFonts.values().iterator(); iterator.hasNext(); ) { + Font f = (Font)iterator.next(); + f.dispose(); + } + styledFonts.clear(); + } + + /** + * Returns an array of StyleRange instances which are to be applied to the + * label. + * + * @param element + * The element whose label is going to be displayed + * @param label + * The label for which to provide the StyleRanges + * @return An array of StyleRange instances, may be null if no styles are to + * be applied for this label. + */ + public abstract StyleRange[] getStyleRanges( Object element, String label ); + + /** + * Creates a TextLayout instance for either measuring or painting the item, + * using the StyleRanges supplied by + * {@link StyledLabelSupport#getStyleRanges(Object, String)}. + * + * Adds an image at the first occurence of + * {@link StyledLabelSupport#IMAGE_PLACEHOLDER} or at the beginning of the + * label. + * + * @param event + * the measure or paint event for which a TextLayout is needed + * @param label + * the label to display. + * @param image + * the image to display. May be null. + * @param defaultFont + * the default font for the label. If null the system default + * font is used. + * @param defaultBackground + * the default background color. May be null. + * @param element + * the model element used to obtain label and styles. + * @return a TextLayout instance which must be disposed after use. + */ + protected TextLayout createTextLayout( Event event, String label, Image image, Font defaultFont, Color defaultBackground, Object element ) { + + Display display = event.display; + TextLayout layout = new TextLayout(display); + + int maxFontHeight = 0; + if ( defaultFont != null ) { + layout.setFont(defaultFont); + maxFontHeight = getFontHeight(defaultFont); + } + else { + maxFontHeight = getFontHeight(display.getSystemFont()); + } + + 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; + } + 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 = getStyleRanges(element, label); + if ( styleRanges != null ) { + for ( int i = 0; i < styleRanges.length; i++ ) { + StyleRange styleRange = styleRanges[i]; + checkFont(display, 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 ) { + 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; + } + + /** + * @param event + * @param element + * @param viewerLabel + */ + protected void measure( Event event, Object element, ViewerLabel viewerLabel ) { + String label = viewerLabel.getText(); + Image image = viewerLabel.getImage(); + Font font = viewerLabel.getFont(); + Color defaultBackground = viewerLabel.getBackground(); + TextLayout textLayout = createTextLayout(event, label, image, font, defaultBackground, element); + Rectangle bounds = textLayout.getBounds(); + event.setBounds(new Rectangle(0, 0, bounds.width, bounds.height)); + textLayout.dispose(); + } + + /** + * @param event + * @param element + * @param viewerLabel + */ + protected void paint( Event event, Object element, ViewerLabel viewerLabel ) { + String label = viewerLabel.getText(); + Image image = viewerLabel.getImage(); + Font font = viewerLabel.getFont(); + Color defaultBackground = viewerLabel.getBackground(); + TextLayout textLayout = createTextLayout(event, label, image, font, defaultBackground, element); + GC gc = event.gc; + Color oldForeground = gc.getForeground(), oldBackground = gc.getBackground(); + Color foreground = viewerLabel.getForeground(); + if ( foreground != null ) { + gc.setForeground(foreground); + } + + if ( defaultBackground != null ) { + gc.setBackground(defaultBackground); + } + + int heightOffset = 0; + if ( centerItemsVertically ) { + heightOffset = (event.height - textLayout.getBounds().height) / 2; + heightOffset = Math.max(0, heightOffset); + } + + textLayout.draw(gc, event.x, event.y + heightOffset); + + if ( image != null ) { + int imageOffset = Math.max(0, label.indexOf(IMAGE_PLACEHOLDER)); + 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, 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, (int)fontData[i].height); + } + return h; + } + + 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 styledFont = (Font)styledFonts.get(fontData[0]); + if ( styledFont == null ) { + styledFont = new Font(device, fontData); + styledFonts.put(fontData[0], styledFont); + } + } + styleRange.font = font; + } + } + + 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; + } +} 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,92 @@ +package org.eclipse.jface.viewers; + +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Event; + + +/** + * @since 3.4 + */ +public class StyledCellLabelProvider extends OwnerDrawLabelProvider implements ILabelProvider, IFontProvider, IColorProvider { + + private ColumnViewer viewer; + + private StyledLabelSupport support; + + /** + * @param viewer + * @param support + */ + public StyledCellLabelProvider( ColumnViewer viewer, StyledLabelSupport support ) { + super(); + this.viewer = viewer; + this.support = support; + } + + public void dispose() { + super.dispose(); + support.dispose(); + } + + public Color getBackground( Object element ) { + return null; + } + + public Font getFont( Object element ) { + return null; + } + + public Color getForeground( Object element ) { + return null; + } + + public Image getImage( Object element ) { + return null; + } + + public String getText( Object element ) { + return ""; //$NON-NLS-1$ + } + + protected void measure( Event event, Object element ) { + int height = event.height; + int width = event.width; + + ViewerCell cell = viewer.getViewerRowFromItem(event.item).getCell(event.index); + + ViewerLabel viewerLabel = new ViewerLabel(cell.getText(), cell.getImage()); + viewerLabel.setImage(getImage(element)); + viewerLabel.setText(getText(element)); + viewerLabel.setFont(getFont(element)); + viewerLabel.setBackground(getBackground(element)); + viewerLabel.setForeground(getForeground(element)); + support.measure(event, element, viewerLabel); + + if ( event.height != height || event.width != width ) { + // On Carbon the first cell in a Table is not updated properly, if the size changes. + Rectangle rect = cell.getBounds(); + viewer.getControl().redraw(rect.x, rect.y, rect.width, rect.height, true); + } + } + + protected void paint( Event event, Object element ) { + ViewerCell cell = viewer.getViewerRowFromItem(event.item).getCell(event.index); + + ViewerLabel viewerLabel = new ViewerLabel(cell.getText(), cell.getImage()); + viewerLabel.setImage(getImage(element)); + viewerLabel.setText(getText(element)); + viewerLabel.setFont(getFont(element)); + viewerLabel.setBackground(getBackground(element)); + viewerLabel.setForeground(getForeground(element)); + + support.paint(event, element, viewerLabel); + } + + StyledLabelSupport getStyledLabelSupport() { + return support; + } + +} 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,136 @@ +/******************************************************************************* + * 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 StyledLabelSupport} 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 { + + 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 support the StyledLabelSupport for providing the item's style + * @param labelProvider the label provider implementing ILabelProvider and optionally IFontProvider or IColorProvider + */ + public WrappedStyledCellLabelProvider( ColumnViewer viewer, StyledLabelSupport support, IBaseLabelProvider labelProvider ) { + this(viewer, support, labelProvider, 0); + } + + /** + * Creates a WrappedStyledCellLabelProvider for usage with a ViewerColumn. + * + * @param viewer the viewer for this CellLabelProvider + * @param support the StyledLabelSupport for providing the item's style + * @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, StyledLabelSupport support, IBaseLabelProvider labelProvider, int columnIndex ) { + super(viewer, support); + 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; + } + } + +} Index: src/org/eclipse/jface/viewers/StyledMultiLineLabelSupport.java =================================================================== RCS file: src/org/eclipse/jface/viewers/StyledMultiLineLabelSupport.java diff -N src/org/eclipse/jface/viewers/StyledMultiLineLabelSupport.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/jface/viewers/StyledMultiLineLabelSupport.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,307 @@ +/******************************************************************************* + * 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 java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.GlyphMetrics; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.TextLayout; +import org.eclipse.swt.graphics.TextStyle; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.TableItem; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeColumn; +import org.eclipse.swt.widgets.TreeItem; + + +/** + * A StyledLabelSupport which supports multiple lines per label. + * + *

+ * To set up a StyledMultiLineLabelSupport for your viewer's control, you must call + * {@link StyledMultiLineLabelSupport#setUpMultiLineLabelSupport(ColumnViewer)}. + *

+ * @since 3.4 + * @author Michael Krkoska + */ +public abstract class StyledMultiLineLabelSupport extends StyledLabelSupport { + + /** + * Set up the owner draw callbacks for the viewer and enable multi line support for StyledCellLabelProviders. + * @param viewer + * the viewer the owner draw is set up + */ + public static void setUpMultiLineLabelSupport( final ColumnViewer viewer ) { + OwnerDrawLabelProvider.setUpOwnerDraw(viewer); + + Listener l = new Listener() { + + public void handleEvent( Event event ) { + ViewerColumn column; + int i = 0; + while ( (column = viewer.getViewerColumn(i++)) != null ) { + CellLabelProvider provider = column.getLabelProvider(); + + if ( provider instanceof StyledCellLabelProvider ) { + StyledLabelSupport styledLabelSupport = ((StyledCellLabelProvider)provider).getStyledLabelSupport(); + if ( styledLabelSupport instanceof StyledMultiLineLabelSupport ) { + ((StyledMultiLineLabelSupport)styledLabelSupport).columnsResized(event); + } + } + } + } + + }; + if ( viewer.getControl() instanceof Table ) { + Table table = (Table)viewer.getControl(); + TableColumn[] cols = table.getColumns(); + for ( int i = 0; i < cols.length; i++ ) { + cols[i].addListener(SWT.Resize, l); + } + } + else if ( viewer.getControl() instanceof Tree ) { + Tree tree = (Tree)viewer.getControl(); + TreeColumn[] cols = tree.getColumns(); + for ( int i = 0; i < cols.length; i++ ) { + cols[i].addListener(SWT.Resize, l); + } + } + } + + private boolean wrapLines = false; + private int measureHeightIndex = -1; + private int maxHeight = 0; + private int[] knownColumnWidths; + + /** + * Creates a new StyledMultiLineLabelSupport. + * + * @param style + * the style bits + * @see StyledLabelSupport#CENTER_ITEMS_VERTICALLY + * @see StyledLabelSupport#CENTER_TEXT_VERTICALLY + */ + /* @see SWT#WRAP */ + public StyledMultiLineLabelSupport( int style ) { + super(style); + if ( (SWT.WRAP & style) != 0 ) { + // TODO Wrapping is deactivated as long as either Bug 130024 or Bug 148039 are still existing + // wrapLines = true; + } + } + + protected TextLayout createTextLayout( Event event, String label, Image image, Font defaultFont, Color defaultBackground, Object element ) { + + TextLayout layout = super.createTextLayout(event, label, image, defaultFont, defaultBackground, element); + + if ( centerTextVertically && image != null ) { + int imageOffset = Math.max(0, label.indexOf(IMAGE_PLACEHOLDER)); + GlyphMetrics imageMetrics = layout.getStyle(imageOffset).metrics; + + if ( imageMetrics != null ) { + int lineCount = layout.getLineCount(); + if ( lineCount > 1 ) { + // do a different (unfortunately inferior) centering for multiline text + // than in the single line case + centerMultilineVertically(layout, defaultFont, imageMetrics, imageOffset); + } + } + } + + if ( wrapLines ) { + layout.setWidth(Math.max(getItemWidth(event), 16)); + } + + return layout; + } + + protected void measure( Event event, Object element, ViewerLabel viewerLabel ) { + super.measure(event, element, viewerLabel); + + if ( measureHeightIndex == event.index ) { + maxHeight = Math.max(event.getBounds().height, maxHeight); + } + } + + private void centerMultilineVertically( TextLayout layout, Font defaultFont, GlyphMetrics imageMetrics, int imageOffset ) { + + Font systemFont = layout.getDevice().getSystemFont(); + + int maxFontHeight = 0; + int lineIndex = layout.getLineIndex(imageOffset); + int[] lineOffsets = layout.getLineOffsets(); + int lineStart = lineOffsets[lineIndex]; + int lineEnd = lineOffsets[lineIndex + 1] - 1; + int oldEnd = -1; + int[] ranges = layout.getRanges(); + TextStyle[] styles = layout.getStyles(); + List lineStyles = new ArrayList(); + + 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, null); + layout.setStyle(style, Math.max(oldEnd + 1, lineStart), Math.min(start - 1, lineEnd)); + lineStyles.add(style); + Font font = getFirstNotNullFont(style.font, defaultFont, systemFont); + maxFontHeight = Math.max(maxFontHeight, getFontHeight(font)); + } + + if ( start > lineEnd || end < lineStart ) { + continue; + } + if ( start == imageOffset && end == imageOffset ) { + oldEnd = end; + continue; + } + + TextStyle style = styles[i >> 1]; + if ( end > lineEnd ) { + TextStyle clonedStyle = cloneStyle(style); + layout.setStyle(clonedStyle, Math.max(start, lineStart), Math.min(end, lineEnd)); + lineStyles.add(clonedStyle); + Font font = getFirstNotNullFont(style.font, defaultFont, systemFont); + maxFontHeight = Math.max(maxFontHeight, getFontHeight(font)); + } + else { + lineStyles.add(style); + } + + oldEnd = end; + } + if ( layout.getText().length() > oldEnd + 1 ) { + TextStyle style = new TextStyle(null, null, null); + layout.setStyle(style, Math.max(oldEnd + 1, lineStart), Math.min(layout.getText().length() - 1, lineEnd)); + lineStyles.add(style); + Font font = getFirstNotNullFont(style.font, defaultFont, systemFont); + maxFontHeight = Math.max(maxFontHeight, getFontHeight(font)); + } + + if ( imageMetrics.ascent > maxFontHeight ) { + int height = imageMetrics.ascent; + int space = (height - maxFontHeight); + int rise = Math.round(space / 2f); + for ( Iterator iterator = lineStyles.iterator(); iterator.hasNext(); ) { + TextStyle style = (TextStyle)iterator.next(); + style.rise = rise; + } + } + } + + private TextStyle cloneStyle( TextStyle style ) { + TextStyle clone = new TextStyle(style.font, style.foreground, style.background); + 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; + } + + private void columnsResized( Event event ) { + if ( !wrapLines ) { + return; + } + initKnownWidths(event); + if ( event.widget instanceof TableColumn ) { + TableColumn tc = (TableColumn)event.widget; + int newWidth = tc.getWidth(); + if ( newWidth == knownColumnWidths[event.index] ) { + return; + } + knownColumnWidths[event.index] = newWidth; + int index = tc.getParent().indexOf(tc); + measureHeightIndex = index; + maxHeight = 0; + tc.getParent().redraw(); + tc.getParent().update(); // trigger measure events for all visible items -> calculates maxHeight + if ( maxHeight >= tc.getParent().getItemHeight() ) { + return; + } + // TODO This is deactivated as long as either Bug 130024 or Bug 148039 are still existing + // tc.getParent().setItemHeight(maxHeight); + measureHeightIndex = -1; + maxHeight = 0; + tc.getParent().redraw(); + tc.getParent().update(); + } + else if ( event.widget instanceof TreeColumn ) { + TreeColumn tc = (TreeColumn)event.widget; + int newWidth = tc.getWidth(); + if ( newWidth == knownColumnWidths[event.index] ) { + return; + } + knownColumnWidths[event.index] = newWidth; + int index = tc.getParent().indexOf(tc); + measureHeightIndex = index; + maxHeight = 0; + tc.getParent().redraw(); + tc.getParent().update(); // trigger measure events for all visible items -> calculates maxHeight + if ( maxHeight >= tc.getParent().getItemHeight() ) { + return; + } + // TODO This is deactivated as long as either Bug 130024 or Bug 148039 are still existing + // tc.getParent().setItemHeight(maxHeight); + measureHeightIndex = -1; + maxHeight = 0; + tc.getParent().redraw(); + tc.getParent().update(); + } + } + + private Font getFirstNotNullFont( Font f1, Font f2, Font f3 ) { + if ( f1 != null ) { + return f1; + } + if ( f2 != null ) { + return f2; + } + return f3; + } + + private int getItemWidth( Event event ) { + if ( !wrapLines ) { + return -1; + } + if ( event.item instanceof TableItem ) { + return ((TableItem)event.item).getBounds(event.index).width; + } + if ( event.item instanceof TreeItem ) { + return ((TreeItem)event.item).getBounds(event.index).width; + } + return -1; + } + + private void initKnownWidths( Event event ) { + if ( knownColumnWidths == null ) { + if ( event.widget instanceof TableColumn ) { + TableColumn tc = (TableColumn)event.widget; + knownColumnWidths = new int[tc.getParent().getColumnCount()]; + } + else if ( event.widget instanceof TreeColumn ) { + TreeColumn tc = (TreeColumn)event.widget; + knownColumnWidths = new int[tc.getParent().getColumnCount()]; + } + } + } +}