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,264 @@
+/*******************************************************************************
+ * 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.OwnerDrawLabelProvider;
+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 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 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();
+
+ 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, 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|SWT.WRAP);
+ }
+
+ 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: 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,296 @@
+/*******************************************************************************
+ * 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.Control;
+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;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * A StyledLabelSupport which supports multiple lines per label.
+ *
+ * @since 3.4
+ * @author Michael Krkoska
+ */
+public abstract class StyledMultiLineLabelSupport extends StyledLabelSupport {
+
+ private static final String KEY_HEIGHTWATCHER = "org.eclipse.jface.viewers.StyledMultiLineLabelSupport.Heightwatcher"; //$NON-NLS-1$
+ private static final String KEY_COLUMNRESIZER = "org.eclipse.jface.viewers.StyledMultiLineLabelSupport.ColumnResizer"; //$NON-NLS-1$
+
+ private HeightWatcher heightWatcher = new HeightWatcher();
+ private ColumnResizer columnResizer = new ColumnResizer();
+ private boolean wrapLines = false;
+ private boolean shrinkPending = false;
+
+ /**
+ * 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);
+
+ addColumnResizer(event.widget);
+
+ HeightWatcher heightWatcher = (HeightWatcher) event.widget.getData(KEY_HEIGHTWATCHER);
+ if (heightWatcher == null) {
+ heightWatcher = this.heightWatcher;
+ event.widget.setData(KEY_HEIGHTWATCHER, heightWatcher);
+ event.widget.getDisplay().asyncExec(heightWatcher);
+ heightWatcher.widget = event.widget;
+ }
+ heightWatcher.maxHeight = Math.max(event.getBounds().height, heightWatcher.maxHeight);
+ }
+
+ private void addColumnResizer(Widget w) {
+ if (w instanceof Table) {
+ Table table = (Table) w;
+ TableColumn[] cols = table.getColumns();
+ for (int i = 0; i < cols.length; i++) {
+ if (cols[i].getData(KEY_COLUMNRESIZER) != columnResizer) {
+ cols[i].setData(KEY_COLUMNRESIZER, columnResizer);
+ cols[i].addListener(SWT.Resize, columnResizer);
+ }
+ }
+ } else if (w instanceof Tree) {
+ Tree tree = (Tree) w;
+ TreeColumn[] cols = tree.getColumns();
+ for (int i = 0; i < cols.length; i++) {
+ if (cols[i].getData(KEY_COLUMNRESIZER) != columnResizer) {
+ cols[i].setData(KEY_COLUMNRESIZER, columnResizer);
+ cols[i].addListener(SWT.Resize, columnResizer);
+ }
+ }
+ }
+ }
+
+ 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 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;
+ }
+
+ /**
+ * Try to shrink the item height of the viewer's control. Only performed if the SWT.WRAP style is set.
+ *
+ * @param control
+ * The control of the viewer, either Table or Tree.
+ */
+ protected void shrinkHeight(final Control control) {
+ if (!wrapLines || shrinkPending)
+ return;
+ shrinkPending = true;
+ control.getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ /* This is executed in an asyncExec because table init doesn't work on Vista
+ with multicolumned tables otherwise */
+ heightWatcher.shrink = true;
+ heightWatcher.maxHeight = 0;
+ control.redraw();
+ shrinkPending = false;
+ }
+ });
+ }
+
+ private class ColumnResizer implements Listener {
+
+ public void handleEvent(Event event) {
+ Control c = null;
+ if (event.widget instanceof TableColumn)
+ c = ((TableColumn) event.widget).getParent();
+ else if (event.widget instanceof TreeColumn)
+ c = ((TreeColumn) event.widget).getParent();
+ shrinkHeight(c);
+ }
+ }
+
+ private class HeightWatcher implements Runnable {
+
+ Widget widget;
+ int maxHeight = 0;
+ boolean expand = SWT.getPlatform().equals("carbon"); //$NON-NLS-1$
+ boolean shrink = false;
+
+ public void run() {
+ if (!wrapLines || widget.isDisposed())
+ return;
+ if (widget instanceof Table) {
+ Table t = (Table) widget;
+ int currentHeight = t.getItemHeight();
+ if ((currentHeight > maxHeight && shrink) || (currentHeight < maxHeight && expand)) {
+ // TODO Wrapping is deactivated as long as either Bug 130024 or Bug 148039 are still existing
+ // t.setItemHeight(maxHeight);
+ }
+ } else if (widget instanceof Tree) {
+ Tree t = (Tree) widget;
+ int currentHeight = t.getItemHeight();
+ if ((currentHeight > maxHeight && shrink) || (currentHeight < maxHeight && expand)) {
+ // TODO Wrapping is deactivated as long as either Bug 130024 or Bug 148039 are still existing
+ // t.setItemHeight(maxHeight);
+ }
+ }
+ widget.setData(KEY_HEIGHTWATCHER, null);
+ maxHeight = 0;
+ shrink = false;
+ }
+ }
+}