alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*******************************************************************************
 * 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.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>native drawing of focus and selection
 * </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>NOTE: 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
		 * @param defaultFont
		 *            the default font for the element or <code>null
		 * @param defaultForegroundColor
		 *            the default foreground color for the element or
		 *            <code>null
		 * @param defaultBackgroundColor
		 *            the default background color for the element or
		 *            <code>null
		 */
		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
		 *         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 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
		 *         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 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 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 if the
	 * label provider is not installed.
	 * 
	 * @return the viewer on which this label provider is installed on or <code>null 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 if the
	 * label provider is not installed.
	 * 
	 * @return the column on which this label provider is installed on or <code>null 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) {
			int orientation = viewer.getControl().getStyle() & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT);
			cachedTextLayout = new TextLayout(display);
			cachedTextLayout.setOrientation(orientation);
		}
		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;
	}

}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.