org.eclipse.swt.widgets.TableColumn.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.swt.widgets.TableColumn.java

Source

/*******************************************************************************
 * Copyright (c) 2002, 2015 Innoopract Informationssysteme GmbH 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:
 *    Innoopract Informationssysteme GmbH - initial API and implementation
 *    EclipseSource - ongoing development
 ******************************************************************************/
package org.eclipse.swt.widgets;

import static org.eclipse.swt.internal.widgets.MarkupUtil.isToolTipMarkupEnabledFor;
import static org.eclipse.swt.internal.widgets.MarkupValidator.isValidationDisabledFor;

import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.internal.lifecycle.WidgetLCA;
import org.eclipse.rap.rwt.internal.textsize.TextSizeUtil;
import org.eclipse.rap.rwt.theme.BoxDimensions;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.internal.SerializableCompatibility;
import org.eclipse.swt.internal.widgets.IColumnAdapter;
import org.eclipse.swt.internal.widgets.MarkupValidator;
import org.eclipse.swt.internal.widgets.tablecolumnkit.TableColumnLCA;

/**
 * Instances of this class represent a column in a table widget.
 * <p><dl>
 * <dt><b>Styles:</b></dt>
 * <dd>LEFT, RIGHT, CENTER</dd>
 * <dt><b>Events:</b></dt>
 * <dd> Move, Resize, Selection</dd>
 * </dl>
 * </p><p>
 * Note: Only one of the styles LEFT, RIGHT and CENTER may be specified.
 * </p><p>
 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
 * </p>
 *
 * @since 1.0
 */
public class TableColumn extends Item {

    // TODO [rh] Fow now: keep in sync with settings in appearance theme, should
    //      be moved to theme adapter or similar
    private static final int SORT_INDICATOR_WIDTH = 10;
    private static final int SPACING = 2;

    private final Table parent;
    private final IColumnAdapter columnAdapter;
    private int width;
    private String toolTipText;
    private boolean resizable;
    private boolean moveable;
    private boolean packed;

    /**
     * Constructs a new instance of this class given its parent
     * (which must be a <code>Table</code>) and a style value
     * describing its behavior and appearance. The item is added
     * to the end of the items maintained by its parent.
     * <p>
     * The style value is either one of the style constants defined in
     * class <code>SWT</code> which is applicable to instances of this
     * class, or must be built by <em>bitwise OR</em>'ing together
     * (that is, using the <code>int</code> "|" operator) two or more
     * of those <code>SWT</code> style constants. The class description
     * lists the style constants that are applicable to the class.
     * Style bits are also inherited from superclasses.
     * </p>
     *
     * @param parent a composite control which will be the parent of the new instance (cannot be null)
     * @param style the style of control to construct
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
     * </ul>
     * @exception SWTException <ul>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
     *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
     * </ul>
     *
     * @see SWT#LEFT
     * @see SWT#RIGHT
     * @see SWT#CENTER
     * @see Widget#checkSubclass
     * @see Widget#getStyle
     */
    public TableColumn(Table parent, int style) {
        this(parent, checkStyle(style), checkNull(parent).getColumnCount());
    }

    /**
     * Constructs a new instance of this class given its parent
     * (which must be a <code>Table</code>), a style value
     * describing its behavior and appearance, and the index
     * at which to place it in the items maintained by its parent.
     * <p>
     * The style value is either one of the style constants defined in
     * class <code>SWT</code> which is applicable to instances of this
     * class, or must be built by <em>bitwise OR</em>'ing together
     * (that is, using the <code>int</code> "|" operator) two or more
     * of those <code>SWT</code> style constants. The class description
     * lists the style constants that are applicable to the class.
     * Style bits are also inherited from superclasses.
     * </p>
     *
     * @param parent a composite control which will be the parent of the new instance (cannot be null)
     * @param style the style of control to construct
     * @param index the zero-relative index to store the receiver in its parent
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
     *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
     * </ul>
     * @exception SWTException <ul>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
     *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
     * </ul>
     *
     * @see SWT#LEFT
     * @see SWT#RIGHT
     * @see SWT#CENTER
     * @see Widget#checkSubclass
     * @see Widget#getStyle
     */
    public TableColumn(Table parent, int style, int index) {
        super(parent, checkStyle(style));
        this.parent = parent;
        resizable = true;
        columnAdapter = new ColumnAdapter();
        this.parent.createColumn(this, index);
    }

    /**
     * Returns the receiver's parent, which must be a <code>Table</code>.
     *
     * @return the receiver's parent
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public Table getParent() {
        checkWidget();
        return parent;
    }

    /**
     * Gets the width of the receiver.
     *
     * @return the width
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public int getWidth() {
        checkWidget();
        return width;
    }

    /**
     * Sets the receiver's tool tip text to the argument, which
     * may be null indicating that no tool tip text should be shown.
     *
     * @param toolTipText the new tool tip text (or null)
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public void setToolTipText(String toolTipText) {
        checkWidget();
        if (toolTipText != null && isToolTipMarkupEnabledFor(this) && !isValidationDisabledFor(this)) {
            MarkupValidator.getInstance().validate(toolTipText);
        }
        this.toolTipText = toolTipText;
    }

    /**
     * Returns the receiver's tool tip text, or null if it has
     * not been set.
     *
     * @return the receiver's tool tip text
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public String getToolTipText() {
        checkWidget();
        return toolTipText;
    }

    /**
     * Controls how text and images will be displayed in the receiver.
     * The argument should be one of <code>LEFT</code>, <code>RIGHT</code>
     * or <code>CENTER</code>.
     *
     * @param alignment the new alignment
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public void setAlignment(int alignment) {
        checkWidget();
        if ((alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER)) != 0) {
            style &= ~(SWT.LEFT | SWT.RIGHT | SWT.CENTER);
            style |= alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER);
        }
    }

    /**
     * Returns a value which describes the position of the
     * text or image in the receiver. The value will be one of
     * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>.
     *
     * @return the alignment
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public int getAlignment() {
        checkWidget();
        int result = SWT.LEFT;
        if ((style & SWT.CENTER) != 0) {
            result = SWT.CENTER;
        }
        if ((style & SWT.RIGHT) != 0) {
            result = SWT.RIGHT;
        }
        return result;
    }

    /**
     * Sets the width of the receiver.
     *
     * @param width the new width
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public void setWidth(int width) {
        checkWidget();
        if (width >= 0) {
            this.width = width;
            parent.updateScrollBars();
            notifyListeners(SWT.Resize, new Event());
            processNextColumnsMoveEvent();
            packed = false;
        }
    }

    /**
     * Causes the receiver to be resized to its preferred size.
     * For a composite, this involves computing the preferred size
     * from its layout, if there is one.
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public void pack() {
        checkWidget();
        int width = getPreferredWidth();
        if (width != getWidth()) {
            setWidth(width);
        }
        packed = true;
    }

    /**
     * Sets the moveable attribute.  A column that is
     * moveable can be reordered by the user by dragging
     * the header. A column that is not moveable cannot be
     * dragged by the user but may be reordered
     * by the programmer.
     *
     * @param moveable the moveable attribute
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @see Table#setColumnOrder(int[])
     * @see Table#getColumnOrder()
     * @see TableColumn#getMoveable()
     * @see SWT#Move
     */
    public void setMoveable(boolean moveable) {
        checkWidget();
        this.moveable = moveable;
    }

    /**
     * Gets the moveable attribute. A column that is
     * not moveable cannot be reordered by the user
     * by dragging the header but may be reordered
     * by the programmer.
     *
     * @return the moveable attribute
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @see Table#getColumnOrder()
     * @see Table#setColumnOrder(int[])
     * @see TableColumn#setMoveable(boolean)
     * @see SWT#Move
     */
    public boolean getMoveable() {
        checkWidget();
        return moveable;
    }

    /**
     * Sets the resizable attribute.  A column that is
     * resizable can be resized by the user dragging the
     * edge of the header.  A column that is not resizable
     * cannot be dragged by the user but may be resized
     * by the programmer.
     *
     * @param resizable the resize attribute
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public void setResizable(boolean resizable) {
        checkWidget();
        this.resizable = resizable;
    }

    /**
     * Gets the resizable attribute. A column that is
     * not resizable cannot be dragged by the user but
     * may be resized by the programmer.
     *
     * @return the resizable attribute
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public boolean getResizable() {
        checkWidget();
        return resizable;
    }

    ///////////////////////////////////////
    // Listener registration/deregistration

    /**
     * Adds the listener to the collection of listeners who will
     * be notified when the control is moved or resized, by sending
     * it one of the messages defined in the <code>ControlListener</code>
     * interface.
     *
     * @param listener the listener which should be notified
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
     * </ul>
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @see ControlListener
     * @see #removeControlListener
     */
    public void addControlListener(ControlListener listener) {
        checkWidget();
        if (listener == null) {
            error(SWT.ERROR_NULL_ARGUMENT);
        }
        TypedListener typedListener = new TypedListener(listener);
        addListener(SWT.Move, typedListener);
        addListener(SWT.Resize, typedListener);
    }

    /**
     * Removes the listener from the collection of listeners who will
     * be notified when the control is moved or resized.
     *
     * @param listener the listener which should no longer be notified
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
     * </ul>
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @see ControlListener
     * @see #addControlListener
     */
    public void removeControlListener(ControlListener listener) {
        checkWidget();
        if (listener == null) {
            error(SWT.ERROR_NULL_ARGUMENT);
        }
        removeListener(SWT.Move, listener);
        removeListener(SWT.Resize, listener);
    }

    /**
     * Adds the listener to the collection of listeners who will
     * be notified when the control is selected, by sending
     * it one of the messages defined in the <code>SelectionListener</code>
     * interface.
     * <p>
     * <code>widgetSelected</code> is called when the column header is selected.
     * <code>widgetDefaultSelected</code> is not called.
     * </p>
     *
     * @param listener the listener which should be notified
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
     * </ul>
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @see SelectionListener
     * @see #removeSelectionListener
     * @see SelectionEvent
     */
    public void addSelectionListener(SelectionListener listener) {
        checkWidget();
        if (listener == null) {
            SWT.error(SWT.ERROR_NULL_ARGUMENT);
        }
        TypedListener typedListener = new TypedListener(listener);
        addListener(SWT.Selection, typedListener);
        addListener(SWT.DefaultSelection, typedListener);
    }

    /**
     * Removes the listener from the collection of listeners who will
     * be notified when the control is selected.
     *
     * @param listener the listener which should no longer be notified
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
     * </ul>
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @see SelectionListener
     * @see #addSelectionListener
     */
    public void removeSelectionListener(SelectionListener listener) {
        checkWidget();
        if (listener == null) {
            SWT.error(SWT.ERROR_NULL_ARGUMENT);
        }
        removeListener(SWT.Selection, listener);
        removeListener(SWT.DefaultSelection, listener);
    }

    @Override
    public void setData(String key, Object value) {
        if (!RWT.TOOLTIP_MARKUP_ENABLED.equals(key) || !isToolTipMarkupEnabledFor(this)) {
            super.setData(key, value);
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getAdapter(Class<T> adapter) {
        if (adapter == IColumnAdapter.class) {
            return (T) columnAdapter;
        }
        if (adapter == WidgetLCA.class) {
            return (T) TableColumnLCA.INSTANCE;
        }
        return super.getAdapter(adapter);
    }

    ////////////////////
    // Widget dimensions

    final int getPreferredWidth() {
        // Compute width from the column itself
        int result = 0;
        Font font = parent.getHeaderFont();
        if (text.length() > 0) {
            if (text.indexOf('\n') != -1) {
                result = TextSizeUtil.textExtent(font, text, 0).x;
            } else {
                result = TextSizeUtil.stringExtent(font, text).x;
            }
        }
        Image image = getImage();
        if (image != null) {
            result += image.getBounds().width + SPACING;
        }
        if (parent.getSortColumn() == this && parent.getSortDirection() != SWT.NONE) {
            result += SORT_INDICATOR_WIDTH + SPACING;
        }
        BoxDimensions headerPadding = parent.getThemeAdapter().getHeaderPadding(parent);
        result += headerPadding.left + headerPadding.right;
        // Mimic Windows behaviour that forces first item to resolve
        if (parent.getItemCount() > 0 && parent.getCachedItems().length == 0) {
            parent.checkData(parent.getItem(0), 0);
        }
        // Extend computed width if there are wider items
        int columnIndex = parent.indexOf(this);
        int itemsPreferredWidth = parent.getItemsPreferredWidth(columnIndex);
        // Add 1px for the right column border
        return Math.max(result, itemsPreferredWidth) + 1;
    }

    ////////////////////////////
    // Widget and Item overrides

    @Override
    void releaseParent() {
        super.releaseParent();
        parent.destroyColumn(this);
    }

    @Override
    String getNameText() {
        return getText();
    }

    //////////////
    // Left offset

    final int getLeft() {
        int result = 0;
        TableColumn[] columns = parent.getColumns();
        int[] columnOrder = parent.getColumnOrder();
        int orderedIndex = -1;
        for (int i = 0; orderedIndex == -1 && i < columnOrder.length; i++) {
            if (columnOrder[i] == parent.indexOf(this)) {
                orderedIndex = i;
            }
        }
        for (int i = 0; i < orderedIndex; i++) {
            result += columns[columnOrder[i]].getWidth();
        }
        return result;
    }

    //////////////////
    // Helping methods

    private static int checkStyle(int style) {
        return checkBits(style, SWT.LEFT, SWT.CENTER, SWT.RIGHT, 0, 0, 0);
    }

    private static Table checkNull(Table table) {
        if (table == null) {
            SWT.error(SWT.ERROR_NULL_ARGUMENT);
        }
        return table;
    }

    private void processNextColumnsMoveEvent() {
        int[] columnsOrder = parent.getColumnOrder();
        boolean found = false;
        for (int i = 0; i < columnsOrder.length; i++) {
            TableColumn column = parent.getColumn(columnsOrder[i]);
            if (column == this) {
                found = true;
            } else if (found) {
                column.notifyListeners(SWT.Move, new Event());
            }
        }
    }

    ////////////////
    // Inner classes

    private final class ColumnAdapter implements IColumnAdapter, SerializableCompatibility {

        @Override
        public boolean isPacked() {
            return packed;
        }

        @Override
        public void clearPacked() {
            packed = false;
        }

    }
}