blob: df83a8c25441b5001a97193979b366150baaf249 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Michael Krkoska - initial API and implementation (bug 188333)
*******************************************************************************/
package org.eclipse.jface.viewers;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.window.Window;
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.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.TextLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.TreeItem;
/**
* A {@link SimpleStyledCellLabelProvider} supports styled labels by using owner
* draw by preserving native viewer behavior:
* <ul>
* <li>similar image and label positioning</li>
* <li>native drawing of focus and selection</li>
* </ul>
*
*
* <p>
* For providing the label's styles, create a subclass and overwrite
* {@link SimpleStyledCellLabelProvider#getLabelPresentationInfo(Object)} to
* return all information needed to render a element.
* </p>
* <p>
* The {@link SimpleStyledCellLabelProvider} will ignore all font settings on
* {@link StyleRange}. Different fonts would make labels wider, and the native
* selection drawing could not be reused.
* </p>
*
* <p><strong>NOTE:</strong> This API is experimental and may be deleted or
* changed before 3.4 is released.</p>
*
*
* @deprecated Will be removed before 3.4 M6. Use {@link StyledCellLabelProvider} instead.
*
* @since 3.4
*/
public abstract class SimpleStyledCellLabelProvider extends
OwnerDrawLabelProvider {
/**
* Holds all information used to render a styled element.
*/
public static class LabelPresentationInfo {
private final String text;
private final Image image;
private final StyleRange[] ranges;
private final Font defaultFont;
private final Color defaultForegroundColor;
private final Color defaultBackgroundColor;
/**
* Creates a {@link SimpleStyledCellLabelProvider.LabelPresentationInfo}.
*
* @param text
* the text of the current element
* @param ranges
* the styled ranges for the element
* @param image
* the image for the element or <code>null</code>
* @param defaultFont
* the default font for the element or <code>null</code>
* @param defaultForegroundColor
* the default foreground color for the element or
* <code>null</code>
* @param defaultBackgroundColor
* the default background color for the element or
* <code>null</code>
*/
public LabelPresentationInfo(String text, StyleRange[] ranges,
Image image, Font defaultFont, Color defaultForegroundColor,
Color defaultBackgroundColor) {
Assert.isNotNull(text);
Assert.isNotNull(ranges);
this.text = text;
this.ranges = ranges;
this.image = image;
this.defaultFont = defaultFont;
this.defaultForegroundColor = defaultForegroundColor;
this.defaultBackgroundColor = defaultBackgroundColor;
}
/**
* Provides the text of the current element.
*
* @return returns the text.
*/
public String getText() {
return this.text;
}
/**
* Provides the styled ranges that can be applied to the text provided
* by {@link #getText()}. The {@link SimpleStyledCellLabelProvider}
* will ignore all font settings.
*
* @return the styled ranges for the element
*/
public StyleRange[] getStyleRanges() {
return this.ranges;
}
/**
* Provides the image of the current element.
*
* @return returns the image.
*/
public Image getImage() {
return this.image;
}
/**
* Provides a default background color of the current element, which is
* used for the part of the label where no background color is specified
* in the StyleRanges provided by {@link #getStyleRanges}.
*
* @return the background color for the element, or <code>null</code>
* to use the default background color
*/
public Color getDefaultBackground() {
return this.defaultBackgroundColor;
}
/**
* Provides a default font of the current element.
*
* @return the font for the element, or <code>null</code> to use the
* default font
*/
public Font getDefaultFont() {
return this.defaultFont;
}
/**
* Provides a default foreground color of the current element, which is
* used for the part of the label where no foreground color is specified
* in the StyleRanges provided by {@link #getStyleRanges}.
*
* @return the foreground color for the element, or <code>null</code>
* to use the default foreground color
*/
public Color getDefaultForeground() {
return this.defaultForegroundColor;
}
}
private static final String KEY_TEXT_LAYOUT = "styled_label_key_"; //$NON-NLS-1$
/**
* 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 COLORS_ON_SELECTION = 1 << 0;
/**
* Style constant for indicating to draw the focus if requested by the owner
* draw event. Default is to draw the focus.
*/
public static final int NO_FOCUS = 1 << 1;
/**
* Private constant to indicate if owner draw is enabled for the
* label provider's column.
*/
private static final int OWNER_DRAW_ENABLED = 1 << 4;
private int style;
private TextLayout cachedTextLayout; // reused text layout for
// 'cachedLabelInfo'
private LabelPresentationInfo cachedLabelInfo;
private boolean cachedWasWithColors;
private ColumnViewer viewer;
private ViewerColumn column;
/**
* Creates a new StyledCellLabelProvider. By default, owner draw is enabled, focus is drawn and no
* colors are painted on selected elements.
*/
public SimpleStyledCellLabelProvider() {
this(0);
}
/**
* Creates a new StyledCellLabelProvider. By default, owner draw is enabled.
*
* @param style
* the style bits
* @see SimpleStyledCellLabelProvider#COLORS_ON_SELECTION
* @see SimpleStyledCellLabelProvider#NO_FOCUS
*/
public SimpleStyledCellLabelProvider(int style) {
this.style = style & (COLORS_ON_SELECTION | NO_FOCUS)
| OWNER_DRAW_ENABLED;
}
/**
* Returns <code>true</code> is the owner draw rendering is enabled for this label provider.
* By default owner draw rendering is enabled. If owner draw rendering is disabled, rending is
* done by the viewer and no styled ranges (see {@link LabelPresentationInfo#getStyleRanges()})
* are drawn.
*
* @return <code>true</code> is the rendering of styles is enabled.
*/
public boolean isOwnerDrawEnabled() {
return (this.style & OWNER_DRAW_ENABLED) != 0;
}
/**
* Specifies whether owner draw rendering is enabled for this label
* provider. By default owner draw rendering is enabled. If owner draw
* rendering is disabled, rendering is done by the viewer and no styled
* ranges (see {@link LabelPresentationInfo#getStyleRanges()}) are drawn.
* It is the caller's responsibility to also call
* {@link StructuredViewer#refresh()} or similar methods to update the
* underlying widget.
*
* @param enabled
* specifies if owner draw rendering is enabled
*/
public void setOwnerDrawEnabled(boolean enabled) {
boolean isEnabled= isOwnerDrawEnabled();
if (isEnabled != enabled) {
if (enabled) {
this.style |= OWNER_DRAW_ENABLED;
} else {
this.style &= ~OWNER_DRAW_ENABLED;
}
if (this.viewer != null) {
setOwnerDrawEnabled(this.viewer, this.column, enabled);
}
}
}
/**
* Returns the viewer on which this label provider is installed on or <code>null</code> if the
* label provider is not installed.
*
* @return the viewer on which this label provider is installed on or <code>null</code> if the
* label provider is not installed.
*/
protected final ColumnViewer getViewer() {
return this.viewer;
}
/**
* Returns the column on which this label provider is installed on or <code>null</code> if the
* label provider is not installed.
*
* @return the column on which this label provider is installed on or <code>null</code> if the
* label provider is not installed.
*/
protected final ViewerColumn getColumn() {
return this.column;
}
/**
* Returns a {@link LabelPresentationInfo} instance containing the text,
* image and style information to use for displaying element.
*
* @param element
* the element to create a presentation info for
* @return the presentation info
*/
protected abstract LabelPresentationInfo getLabelPresentationInfo(
Object element);
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.OwnerDrawLabelProvider#initialize(org.eclipse.jface.viewers.ColumnViewer, org.eclipse.jface.viewers.ViewerColumn)
*/
public void initialize(ColumnViewer viewer, ViewerColumn column) {
Assert.isTrue(this.viewer == null && this.column == null, "Label provider instance already in use"); //$NON-NLS-1$
this.viewer= viewer;
this.column= column;
super.initialize(viewer, column, isOwnerDrawEnabled());
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.BaseLabelProvider#dispose()
*/
public void dispose() {
if (this.cachedTextLayout != null) {
cachedTextLayout.dispose();
cachedTextLayout = null;
}
cachedLabelInfo = null;
this.viewer= null;
this.column= null;
super.dispose();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.OwnerDrawLabelProvider#update(org.eclipse.jface.viewers.ViewerCell)
*/
public void update(ViewerCell cell) {
LabelPresentationInfo info = getLabelPresentationInfo(cell.getElement());
cell.setImage(info.getImage());
cell.setText(info.getText());
cell.setFont(info.getDefaultFont());
cell.setBackground(info.getDefaultBackground());
cell.setForeground(info.getDefaultForeground());
if (isOwnerDrawEnabled()) {
// store info in the item to avoid recomputation
cell.getItem().setData(KEY_TEXT_LAYOUT + cell.getColumnIndex(), info);
} else {
// make sure the info is cleared to avoid leaks
cell.getItem().setData(KEY_TEXT_LAYOUT + cell.getColumnIndex(), null);
}
super.update(cell); // calls 'repaint' to trigger the paint listener
}
private TextLayout getSharedTextLayout(Display display) {
if (cachedTextLayout == null) {
cachedTextLayout = new TextLayout(display);
cachedTextLayout.setOrientation(Window.getDefaultOrientation());
}
return cachedTextLayout;
}
private boolean useColors(Event event) {
return (event.detail & SWT.SELECTED) == 0
|| (this.style & COLORS_ON_SELECTION) != 0;
}
private boolean drawFocus(Event event) {
return (event.detail & SWT.FOCUSED) != 0
&& (this.style & NO_FOCUS) == 0;
}
private LabelPresentationInfo getInfo(Event event) {
return (LabelPresentationInfo) event.item.getData(KEY_TEXT_LAYOUT + event.index);
}
/**
* Returns a {@link TextLayout} instance for the given
* {@link LabelPresentationInfo}. The text layout instance is managed by
* the label provider. Caller of the method must not dispose the text
* layout.
*
* @param diplay
* the current display
* @param labelPresentation
* the viewerLabel the label info
*
* @param applyColors
* if set, create colors in the result
* @param element
* the model element
* @param item
* the item
* @param index
* the index
* @return a TextLayout instance
*/
private TextLayout getTextLayoutForInfo(Display display,
LabelPresentationInfo labelPresentation, boolean applyColors, Item item, int index) {
// can use cache?
if (cachedLabelInfo == labelPresentation
&& applyColors == cachedWasWithColors) {
return cachedTextLayout; // use cached layout
}
TextLayout sharedLayout = getSharedTextLayout(display);
applyInfoToLayout(sharedLayout, labelPresentation, applyColors, item, index);
cachedLabelInfo = labelPresentation;
cachedWasWithColors = applyColors;
return sharedLayout;
}
/**
* Fills the given text layout with the styles, text and font of the label
* info.
*
* @param layout
* the text layout to fill
* @param labelInfo
* the viewer label
* @param applyColors
* if set, colors will be used
* @param item
* the item
* @param index
* the index
*/
private void applyInfoToLayout(TextLayout layout,
LabelPresentationInfo labelInfo, boolean applyColors, Item item, int index) {
layout.setText(""); // make sure no previous ranges are kept //$NON-NLS-1$
layout.setText(labelInfo.getText());
Font font = labelInfo.getDefaultFont();
if (font == null) {
if (item instanceof TableItem) {
font = ((TableItem)item).getFont(index);
} else if (item instanceof TreeItem) {
font = ((TreeItem)item).getFont(index);
}
}
layout.setFont(font); // set also if null to clear previous usages
StyleRange[] styleRanges = labelInfo.getStyleRanges();
for (int i = 0; i < styleRanges.length; i++) {
StyleRange curr = styleRanges[i];
// if no colors apply or font is set, create a clone and clear the
// colors and font
if (curr.font != null || !applyColors
&& (curr.foreground != null || curr.background != null)) {
curr = (StyleRange) curr.clone();
curr.font = null; // ignore font settings until bug 168807 is resolved
if (!applyColors) {
curr.foreground = null;
curr.background = null;
}
}
layout.setStyle(curr, curr.start, curr.start + curr.length - 1);
}
}
/**
* 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) {
// use native erase
LabelPresentationInfo labelInfo = getInfo(event);
if (labelInfo != null) {
// info has been set by 'update': announce that we paint ourselves
event.detail &= ~SWT.FOREGROUND;
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.OwnerDrawLabelProvider#measure(org.eclipse.swt.widgets.Event,
* java.lang.Object)
*/
protected void measure(Event event, Object element) {
// use native measuring
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.OwnerDrawLabelProvider#paint(org.eclipse.swt.widgets.Event,
* java.lang.Object)
*/
protected void paint(Event event, Object element) {
LabelPresentationInfo labelInfo = getInfo(event);
if (labelInfo == null) {
return; // no info cached: skip this entry, use native painting
}
boolean applyColors = useColors(event);
GC gc = event.gc;
Color oldForeground = gc.getForeground(); // remember colors to
// restore the GC later
Color oldBackground = gc.getBackground();
if (applyColors) {
Color foreground = labelInfo.getDefaultForeground();
if (foreground != null) {
gc.setForeground(foreground);
}
Color background = labelInfo.getDefaultBackground();
if (background != null) {
gc.setBackground(background);
}
}
Image image = labelInfo.getImage();
if (image != null) {
Rectangle imageBounds = getImageBounds(event);
Rectangle bounds = image.getBounds();
// center the image in the given space
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);
}
TextLayout textLayout = getTextLayoutForInfo(event.display, labelInfo,
applyColors, (Item)event.item, event.index);
Rectangle layoutBounds = textLayout.getBounds();
Rectangle textBounds = getTextBounds(event);
int x = textBounds.x;
int y = textBounds.y
+ Math.max(0, (textBounds.height - layoutBounds.height) / 2);
textLayout.draw(gc, x, y);
if (drawFocus(event)) {
Rectangle focusBounds = getBounds(event);
gc.drawFocus(focusBounds.x, focusBounds.y, focusBounds.width,
focusBounds.height);
}
gc.setForeground(oldForeground);
gc.setBackground(oldBackground);
}
private Rectangle getBounds(Event event) {
Item item = (Item) event.item;
if (item instanceof TreeItem) {
return ((TreeItem) item).getBounds();
} else if (item instanceof TableItem) {
return ((TableItem) item).getBounds();
}
return null;
}
private Rectangle getImageBounds(Event event) {
Item item = (Item) event.item;
if (item instanceof TreeItem) {
return ((TreeItem) item).getImageBounds(event.index);
} else if (item instanceof TableItem) {
return ((TableItem) item).getImageBounds(event.index);
}
return null;
}
private Rectangle getTextBounds(Event event) {
Item item = (Item) event.item;
if (item instanceof TreeItem) {
return ((TreeItem) item).getTextBounds(event.index);
} else if (item instanceof TableItem) {
return ((TableItem) item).getTextBounds(event.index);
}
return null;
}
}