view dwtx/jface/viewers/StyledCellLabelProvider.d @ 70:46a6e0e6ccd4

Merge with d-fied sources of 3.4M7
author Frank Benoit <benoit@tionex.de>
date Thu, 22 May 2008 01:36:46 +0200
parents
children 4878bef4a38e
line wrap: on
line source

/*******************************************************************************
 * 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)
 * Port to the D programming language:
 *     Frank Benoit <benoit@tionex.de>
 *******************************************************************************/
module dwtx.jface.viewers.StyledCellLabelProvider;


import dwt.DWT;
import dwt.custom.StyleRange;
import dwt.graphics.Color;
import dwt.graphics.GC;
import dwt.graphics.Image;
import dwt.graphics.Rectangle;
import dwt.graphics.TextLayout;
import dwt.widgets.Display;
import dwt.widgets.Event;
import dwtx.core.runtime.Assert;

/**
 * A {@link StyledCellLabelProvider} supports styled labels by using owner
 * draw.
 * Besides the styles in labels, the label provider preserves 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 StyledCellLabelProvider#update(ViewerCell)} to
 * return set all information needed to render a element. Use
 * {@link ViewerCell#setStyleRanges(StyleRange[])} to set style ranges
 * on the label.
 * </p>
 * <p>
 * The current version of the {@link StyledCellLabelProvider} 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>
 * 
 * @since 3.4
 */
public abstract class StyledCellLabelProvider extends OwnerDrawLabelProvider {

    /**
     * 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;

    // reused text layout
    private TextLayout cachedTextLayout;
    
    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 StyledCellLabelProvider() {
        this(0);
    }

    /**
     * Creates a new StyledCellLabelProvider. By default, owner draw is enabled.
     * 
     * @param style
     *            the style bits
     * @see StyledCellLabelProvider#COLORS_ON_SELECTION
     * @see StyledCellLabelProvider#NO_FOCUS
     */
    public StyledCellLabelProvider(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 ViewerCell#getStyleRanges()})
     * are drawn.
     * 
     * @return <code>true</code> is the rendering of styles is enabled.
     */
    public bool isOwnerDrawEnabled() {
        return (this.style & OWNER_DRAW_ENABLED) !is 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 ViewerCell#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(bool enabled) {
        bool isEnabled= isOwnerDrawEnabled();
        if (isEnabled !is enabled) {
            if (enabled) {
                this.style |= OWNER_DRAW_ENABLED;
            } else {
                this.style &= ~OWNER_DRAW_ENABLED;
            }
            if (this.viewer !is 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;
    }
        
    /* (non-Javadoc)
     * @see dwtx.jface.viewers.OwnerDrawLabelProvider#initialize(dwtx.jface.viewers.ColumnViewer, dwtx.jface.viewers.ViewerColumn)
     */
    public void initialize(ColumnViewer viewer, ViewerColumn column) {
        Assert.isTrue(this.viewer is null && this.column is null, "Label provider instance already in use"); //$NON-NLS-1$
        
        this.viewer= viewer;
        this.column= column;
        super.initialize(viewer, column, isOwnerDrawEnabled());
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see dwtx.jface.viewers.BaseLabelProvider#dispose()
     */
    public void dispose() {
        if (this.cachedTextLayout !is null) {
            cachedTextLayout.dispose();
            cachedTextLayout = null;
        }
    
        this.viewer= null;
        this.column= null;
        
        super.dispose();
    }

    /*
     * (non-Javadoc)
     * 
     * @see dwtx.jface.viewers.OwnerDrawLabelProvider#update(dwtx.jface.viewers.ViewerCell)
     */
    public void update(ViewerCell cell) {
        // clients must override and configure the cell and call super
        super.update(cell); // calls 'repaint' to trigger the paint listener
    }

    private TextLayout getSharedTextLayout(Display display) {
        if (cachedTextLayout is null) {
            int orientation = viewer.getControl().getStyle() & (DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT);
            cachedTextLayout = new TextLayout(display);
            cachedTextLayout.setOrientation(orientation);
        } else {
            cachedTextLayout.setText(""); // make sure no previous ranges are cleared //$NON-NLS-1$
        }
        return cachedTextLayout;
    }

    private bool useColors(Event event) {
        return (event.detail & DWT.SELECTED) is 0
                || (this.style & COLORS_ON_SELECTION) !is 0;
    }

    private bool drawFocus(Event event) {
        return (event.detail & DWT.FOCUSED) !is 0
                && (this.style & NO_FOCUS) is 0;
    }

    /**
     * Returns a {@link TextLayout} instance for the given cell
     * configured with the style ranges. 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 applyColors
     *            if set, create colors in the result
     * @param cell
     *            the viewer cell
     * @return a TextLayout instance
     */
    private TextLayout getTextLayoutForInfo(Display display, ViewerCell cell, bool applyColors) {
        TextLayout layout = getSharedTextLayout(display);
        
        layout.setText(cell.getText());
        layout.setFont(cell.getFont()); // set also if null to clear previous usages
        
        StyleRange[] styleRanges = cell.getStyleRanges();
        if (styleRanges !is null) { // user didn't fill styled ranges
            for (int i = 0; i < styleRanges.length; i++) {
                StyleRange curr = prepareStyleRange(styleRanges[i], applyColors);
                layout.setStyle(curr, curr.start, curr.start + curr.length - 1);
            }
        }

        return layout;
    }
    
    /**
     * Prepares the given style range before it is applied to the label. This method makes sure that
     * no colors are drawn when the element is selected.
     * The current version of the {@link StyledCellLabelProvider} will also ignore all font settings on the
     * style range. Clients can override.
     * 
     * @param styleRange
     *               the style range to prepare. the style range element must not be modified
     * @param applyColors
     *               specifies if colors should be applied.
     * @return
     *               returns the style range to use on the label
     */
    protected StyleRange prepareStyleRange(StyleRange styleRange, bool applyColors) {
        // if no colors apply or font is set, create a clone and clear the
        // colors and font
        if (styleRange.font !is null || !applyColors
                && (styleRange.foreground !is null || styleRange.background !is null)) {
            styleRange = (StyleRange) styleRange.clone();
            styleRange.font = null; // ignore font settings until bug 168807 is resolved
            if (!applyColors) {
                styleRange.foreground = null;
                styleRange.background = null;
            }
        }
        return styleRange;
    }

    private ViewerCell getViewerCell(Event event, Object element) {
        ViewerRow row= viewer.getViewerRowFromItem(event.item);
        return new ViewerCell(row, event.index, element);
    }
    
    /**
     * 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 DWT#EraseItem
     */
    protected void erase(Event event, Object element) {
        // use native erase
        if (isOwnerDrawEnabled()) {
            // info has been set by 'update': announce that we paint ourselves
            event.detail &= ~DWT.FOREGROUND;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see dwtx.jface.viewers.OwnerDrawLabelProvider#measure(dwt.widgets.Event,
     *      java.lang.Object)
     */
    protected void measure(Event event, Object element) {
        // use native measuring
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see dwtx.jface.viewers.OwnerDrawLabelProvider#paint(dwt.widgets.Event,
     *      java.lang.Object)
     */
    protected void paint(Event event, Object element) {
        if (!isOwnerDrawEnabled())
            return;
        
        ViewerCell cell= getViewerCell(event, element);

        bool applyColors = useColors(event);
        GC gc = event.gc;
        // remember colors to restore the GC later
        Color oldForeground = gc.getForeground();
        Color oldBackground = gc.getBackground();
        
        if (applyColors) {
            Color foreground= cell.getForeground();
            if (foreground !is null) {
                gc.setForeground(foreground);
            }
            
            Color background= cell.getBackground();
            if (background !is null) {
                gc.setBackground(background);
            }
        }

        Image image = cell.getImage();
        if (image !is null) {
            Rectangle imageBounds = cell.getImageBounds();
            if (imageBounds !is null) {
                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, cell, applyColors);

        Rectangle textBounds = cell.getTextBounds();
        if (textBounds !is null) {
            Rectangle layoutBounds = textLayout.getBounds();
    
            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 = cell.getViewerRow().getBounds();
            gc.drawFocus(focusBounds.x, focusBounds.y, focusBounds.width,
                    focusBounds.height);
        }
        
        if (applyColors) {
            gc.setForeground(oldForeground);
            gc.setBackground(oldBackground);
        }
    }

}