/******************************************************************************* * Copyright (c) 2007, 2014 compeople AG 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: * compeople AG - initial API and implementation *******************************************************************************/ package org.eclipse.riena.internal.ui.ridgets.swt; import java.util.HashMap; import java.util.Map; import org.osgi.service.log.LogService; import org.eclipse.core.databinding.observable.map.IObservableMap; import org.eclipse.core.runtime.Assert; import org.eclipse.equinox.log.Logger; import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider; import org.eclipse.jface.viewers.ITableColorProvider; import org.eclipse.jface.viewers.ITableFontProvider; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CLabel; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Display; import org.eclipse.riena.core.Log4r; import org.eclipse.riena.internal.ui.swt.utils.RcpUtilities; import org.eclipse.riena.ui.ridgets.IColumnFormatter; import org.eclipse.riena.ui.ridgets.ITableFormatter; import org.eclipse.riena.ui.ridgets.swt.TableFormatter; import org.eclipse.riena.ui.swt.utils.SwtUtilities; /** * Label provider that formats the columns of a {@link TableRidget}. {@link IColumnFormatter}s can be used to modify the text, image, foreground color, * background color or font of a particular column. * <p> * The appropriate image for a column is computed in the following fashion: * <ul> * <li>if a column has a formatter, use the image from the formatter, if not null</li> * <li>if the column has a boolean or Boolean value, use the default image for boolean values (i.e. checked / unchecked box)</li> * <li>otherwise no image is shown</li> * </ul> * * @see TableRidget */ public class TableRidgetLabelProvider extends ObservableMapLabelProvider implements ITableColorProvider, ITableFontProvider { private final static Logger LOGGER = Log4r.getLogger(TableRidgetLabelProvider.class); private final static ITableFormatter DEFAULT_TABLE_FORMATTER = new TableFormatter(); private static Image checkedImage; private static Image uncheckedImage; private final int numColumns; private IColumnFormatter[] formatters; private ITableFormatter tableFormatter; private Map<Object, Image> imageMap; private boolean checkBoxInFirstColumn; /** * Create a new instance * * @param viewer * a non-null {@link TreeViewer} instance * @param attributeMap * a non-null {@link IObservableMap} instance * @param formatters * an array of objects that implement {@link IColumnFormatter}. The array must have the same number of entries as attributeMap, however * individual entries can be null. * @throws RuntimeException * if attributeMap and labelProviders have not the same number of entries */ public TableRidgetLabelProvider(final IObservableMap[] attributeMap, final IColumnFormatter[] formatters) { this(attributeMap, formatters, attributeMap.length); } protected TableRidgetLabelProvider(final IObservableMap[] attributeMap, final IColumnFormatter[] formatters, final int numColumns) { super(attributeMap); Assert.isLegal(numColumns == formatters.length, String.format("expected %d formatters, got %d", numColumns, //$NON-NLS-1$ formatters.length)); this.numColumns = numColumns; this.formatters = new IColumnFormatter[formatters.length]; System.arraycopy(formatters, 0, this.formatters, 0, this.formatters.length); imageMap = new HashMap<Object, Image>(); checkBoxInFirstColumn = false; } @Override public Image getImage(final Object element) { return getColumnImage(element, 0); } @Override public Image getColumnImage(final Object element, final int columnIndex) { Object formatterImage = null; final IColumnFormatter formatter = getFormatter(columnIndex); if (formatter != null) { formatterImage = formatter.getImage(element); } else { formatterImage = getTableFormatter().getImage(element, getColumnValue(element, columnIndex), columnIndex); } Image result = null; if (formatterImage instanceof Image) { result = (Image) formatterImage; } else if (formatterImage instanceof ImageData) { final ImageData formatterImageData = (ImageData) formatterImage; final Display display = RcpUtilities.getDisplay(); if (display != null) { final Image oldImage = imageMap.get(element); if (!SwtUtilities.isDisposed(oldImage)) { oldImage.dispose(); } result = new Image(display, formatterImageData); imageMap.put(element, result); } } if (result == null && columnIndex < attributeMaps.length) { final Object value = attributeMaps[columnIndex].get(element); if (value instanceof Boolean) { if ((columnIndex == 0) && isCheckBoxInFirstColumn()) { return null; } result = getCheckBoxImage(((Boolean) value).booleanValue()); } } return result; } private Image getCheckBoxImage(final boolean checked) { if (checked) { if (checkedImage == null) { checkedImage = getCheckBoxImage(SharedImages.IMG_CHECKED); } return checkedImage; } else { if (uncheckedImage == null) { uncheckedImage = getCheckBoxImage(SharedImages.IMG_UNCHECKED); } return uncheckedImage; } } private Image getCheckBoxImage(final String key) { Image image = Activator.getSharedImage(key); if ((image != null) && (SwtUtilities.isDpiScalingEnabled())) { int width = image.getBounds().width; int height = image.getBounds().height; width = SwtUtilities.convertXToDpi(width); height = SwtUtilities.convertYToDpi(height); image = resize(image, width, height); } return image; } private Image resize(final Image image, final int width, final int height) { final ImageData scaledImageData = image.getImageData().scaledTo(width, height); final Image scaled = new Image(RcpUtilities.getDisplay(), scaledImageData); return scaled; } // private Image resize(final Image image, final int width, final int height) { // // // TODO transparency (http://www.eclipse.org/articles/Article-SWT-images/graphics-resources.html#Transparency) // final Image scaled = new Image(RcpUtilities.getDisplay(), width, height); // final GC gc = new GC(scaled); // gc.setAntialias(SWT.ON); // gc.setInterpolation(SWT.HIGH); // gc.drawImage(image, 0, 0, image.getBounds().width, image.getBounds().height, 0, 0, width, height); // gc.dispose(); // image.dispose(); // // return scaled; // // } @Override public void dispose() { super.dispose(); disposeImages(); imageMap = null; } /** * Disposes all the images that this label provider has created. */ public void disposeImages() { for (final Map.Entry<Object, Image> entry : imageMap.entrySet()) { final Image image = entry.getValue(); if (!SwtUtilities.isDisposed(image)) { image.dispose(); } } imageMap.clear(); } /** * Disposes the image of the given element if the image was create from this label provider. * * @param element * an element that was deleted */ public void disposeImageOfElement(final Object element) { final Image image = imageMap.get(element); if (image == null) { LOGGER.log(LogService.LOG_WARNING, "No image found for element: " + element.toString()); //$NON-NLS-1$ return; } image.dispose(); imageMap.remove(element); } @Override public String getColumnText(final Object element, final int columnIndex) { String result = null; final IColumnFormatter formatter = getFormatter(columnIndex); if (formatter != null) { result = formatter.getText(element); } else { result = getTableFormatter().getText(element, getColumnValue(element, columnIndex), columnIndex); } if (result == null) { result = super.getColumnText(element, columnIndex); } final Object value = getColumnValue(element, columnIndex); if (value instanceof Boolean) { if ((columnIndex == 0) && isCheckBoxInFirstColumn()) { return null; } } return result; } public Color getForeground(final Object element, final int columnIndex) { final IColumnFormatter formatter = getFormatter(columnIndex); Object color; if (formatter != null) { color = formatter.getForeground(element); } else { color = getTableFormatter().getForeground(element, getColumnValue(element, columnIndex), columnIndex); } if (color instanceof Color) { return (Color) color; } return null; } public Color getBackground(final Object element, final int columnIndex) { final IColumnFormatter formatter = getFormatter(columnIndex); Object color; if (formatter != null) { color = formatter.getBackground(element); } else { color = getTableFormatter().getBackground(element, getColumnValue(element, columnIndex), columnIndex); } if (color instanceof Color) { return (Color) color; } return null; } public int getHorizontalAlignment(final Object element, final int columnIndex) { final IColumnFormatter formatter = getFormatter(columnIndex); int alignment; if (formatter != null) { alignment = formatter.getHorizontalAlignment(element); } else { alignment = getTableFormatter().getHorizontalAlignment(element, getColumnValue(element, columnIndex), columnIndex); } return alignment; } public Font getFont(final Object element, final int columnIndex) { final IColumnFormatter formatter = getFormatter(columnIndex); Object font; if (formatter != null) { font = formatter.getFont(element); } else { font = getTableFormatter().getFont(element, getColumnValue(element, columnIndex), columnIndex); } if (font instanceof Font) { return (Font) font; } return null; } /** * Returns the value of the given element at the specified column index. * * @param element * @param columnIndex * column index * @return value or {@code null} if column index is not correct */ public Object getColumnValue(final Object element, final int columnIndex) { if (columnIndex < attributeMaps.length) { return attributeMaps[columnIndex].get(element); } return null; } /** * Get the text displayed in the tool tip for object. * * <p> * <b>If {@link #getToolTipText(Object)} and {@link #getToolTipImage(Object)} both return <code>null</code> the control is set back to standard behavior</b> * </p> * * @param element * the element for which the tool tip is shown * @param columnIndex * column index for which the tool tip is shown * @return the {@link String} or <code>null</code> if there is not text to display */ public String getToolTipText(final Object element, final int columnIndex) { final IColumnFormatter formatter = getFormatter(columnIndex); if (formatter != null) { return formatter.getToolTip(element); } else { return getTableFormatter().getToolTip(element, getColumnValue(element, columnIndex), columnIndex); } } /** * Get the image displayed in the tool tip for object. * * <p> * <b>If {@link #getToolTipText(Object)} and {@link #getToolTipImage(Object)} both return <code>null</code> the control is set back to standard behavior</b> * </p> * * @param object * the element for which the tool tip is shown * @param columnIndex * column index for which the tool tip is shown * @return {@link Image} or <code>null</code> if there is not image. */ public Image getToolTipImage(final Object object, final int columnIndex) { final IColumnFormatter formatter = getFormatter(columnIndex); Object image; if (formatter != null) { image = formatter.getToolTipImage(object); } else { image = getTableFormatter().getToolTipImage(object, getColumnValue(object, columnIndex), columnIndex); } if (image instanceof Image) { return (Image) image; } return null; } /** * Return the background color used for the tool tip * * @param object * the {@link Object} for which the tool tip is shown * @param columnIndex * column index for which the tool tip is shown * * @return the {@link Color} used or <code>null</code> if you want to use the default color {@link SWT#COLOR_INFO_BACKGROUND} * @see SWT#COLOR_INFO_BACKGROUND */ public Color getToolTipBackgroundColor(final Object object, final int columnIndex) { final IColumnFormatter formatter = getFormatter(columnIndex); Object color; if (formatter != null) { color = formatter.getToolTipBackgroundColor(object); } else { color = getTableFormatter().getToolTipBackgroundColor(object, getColumnValue(object, columnIndex), columnIndex); } if (color instanceof Color) { return (Color) color; } return null; } /** * The foreground color used to display the the text in the tool tip * * @param object * the {@link Object} for which the tool tip is shown * @param columnIndex * column index for which the tool tip is shown * @return the {@link Color} used or <code>null</code> if you want to use the default color {@link SWT#COLOR_INFO_FOREGROUND} * @see SWT#COLOR_INFO_FOREGROUND */ public Color getToolTipForegroundColor(final Object object, final int columnIndex) { final IColumnFormatter formatter = getFormatter(columnIndex); Object color; if (formatter != null) { color = formatter.getToolTipForegroundColor(object); } else { color = getTableFormatter().getToolTipForegroundColor(object, getColumnValue(object, columnIndex), columnIndex); } if (color instanceof Color) { return (Color) color; } return null; } /** * Get the {@link Font} used to display the tool tip * * @param object * the element for which the tool tip is shown * @param columnIndex * column index for which the tool tip is shown * @return {@link Font} or <code>null</code> if the default font is to be used. */ public Font getToolTipFont(final Object object, final int columnIndex) { final IColumnFormatter formatter = getFormatter(columnIndex); Object font; if (formatter != null) { font = formatter.getToolTipFont(object); } else { font = getTableFormatter().getToolTipFont(object, getColumnValue(object, columnIndex), columnIndex); } if (font instanceof Font) { return (Font) font; } return null; } /** * Return the amount of pixels in x and y direction you want the tool tip to pop up from the mouse pointer. The default shift is 10px right and 0px below * your mouse cursor. Be aware of the fact that you should at least position the tool tip 1px right to your mouse cursor else click events may not get * propagated properly. * * @param object * the element for which the tool tip is shown * @param columnIndex * column index for which the tool tip is shown * @return {@link Point} to shift of the tool tip or <code>null</code> if the default shift should be used. */ public Point getToolTipShift(final Object object, final int columnIndex) { final IColumnFormatter formatter = getFormatter(columnIndex); Object point; if (formatter != null) { point = formatter.getToolTipShift(object); } else { point = getTableFormatter().getToolTipShift(object, getColumnValue(object, columnIndex), columnIndex); } if (point instanceof Point) { return (Point) point; } return null; } /** * The time in milliseconds the tool tip is shown for. * * @param object * the {@link Object} for which the tool tip is shown * @param columnIndex * column index for which the tool tip is shown * @return time in milliseconds the tool tip is shown for */ public int getToolTipTimeDisplayed(final Object object, final int columnIndex) { final IColumnFormatter formatter = getFormatter(columnIndex); if (formatter != null) { return formatter.getToolTipTimeDisplayed(object); } else { return getTableFormatter().getToolTipTimeDisplayed(object, getColumnValue(object, columnIndex), columnIndex); } } /** * The time in milliseconds until the tool tip is displayed. * * @param object * the {@link Object} for which the tool tip is shown * @param columnIndex * column index for which the tool tip is shown * @return time in milliseconds until the tool tip is displayed */ public int getToolTipDisplayDelayTime(final Object object, final int columnIndex) { final IColumnFormatter formatter = getFormatter(columnIndex); if (formatter != null) { return formatter.getToolTipDisplayDelayTime(object); } else { return getTableFormatter().getToolTipDisplayDelayTime(object, getColumnValue(object, columnIndex), columnIndex); } } /** * The {@link SWT} style used to create the {@link CLabel} (see there for supported styles). By default {@link SWT#SHADOW_NONE} is used. * * @param object * the element for which the tool tip is shown * @param columnIndex * column index for which the tool tip is shown * @return the style used to create the label * @see CLabel */ public int getToolTipStyle(final Object object, final int columnIndex) { final IColumnFormatter formatter = getFormatter(columnIndex); if (formatter != null) { return formatter.getToolTipStyle(object); } else { return getTableFormatter().getToolTipStyle(object, getColumnValue(object, columnIndex), columnIndex); } } // protected methods //////////////////// /** * @return a formatter that was set or {@code null} if formatter of the column wasn't set */ protected IColumnFormatter getFormatter(final int columnIndex) { return columnIndex < formatters.length ? formatters[columnIndex] : null; } /** * @return a formatter that was set or a default formatter (never return {@code null}) */ protected ITableFormatter getTableFormatter() { return tableFormatter != null ? tableFormatter : DEFAULT_TABLE_FORMATTER; } // helping methods ////////////////// public int getColumnCount() { return this.formatters.length; } public void setFormatters(final IColumnFormatter[] formatters) { Assert.isLegal(numColumns == formatters.length, String.format("expected %d formatters, got %d", numColumns, //$NON-NLS-1$ formatters.length)); this.formatters = new IColumnFormatter[formatters.length]; System.arraycopy(formatters, 0, this.formatters, 0, this.formatters.length); } public void setTableFormatter(final ITableFormatter formatter) { tableFormatter = formatter; } public boolean isCheckBoxInFirstColumn() { return checkBoxInFirstColumn; } public void setCheckBoxInFirstColumn(final boolean checkBoxInFirstColumn) { this.checkBoxInFirstColumn = checkBoxInFirstColumn; } }