Package org.eclipse.nebula.widgets.nattable

Source Code of org.eclipse.nebula.widgets.nattable.NatTable

/*******************************************************************************
* Copyright (c) 2012, 2013 Original authors 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:
*     Original authors and others - initial API and implementation
******************************************************************************/
package org.eclipse.nebula.widgets.nattable;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;

import org.eclipse.nebula.widgets.nattable.command.DisposeResourcesCommand;
import org.eclipse.nebula.widgets.nattable.command.ILayerCommand;
import org.eclipse.nebula.widgets.nattable.command.ILayerCommandHandler;
import org.eclipse.nebula.widgets.nattable.command.StructuralRefreshCommand;
import org.eclipse.nebula.widgets.nattable.command.VisualRefreshCommand;
import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.config.IConfiguration;
import org.eclipse.nebula.widgets.nattable.conflation.EventConflaterChain;
import org.eclipse.nebula.widgets.nattable.conflation.IEventConflater;
import org.eclipse.nebula.widgets.nattable.conflation.VisualChangeEventConflater;
import org.eclipse.nebula.widgets.nattable.coordinate.Range;
import org.eclipse.nebula.widgets.nattable.edit.ActiveCellEditorRegistry;
import org.eclipse.nebula.widgets.nattable.edit.CellEditorCreatedEvent;
import org.eclipse.nebula.widgets.nattable.edit.editor.ICellEditor;
import org.eclipse.nebula.widgets.nattable.grid.command.ClientAreaResizeCommand;
import org.eclipse.nebula.widgets.nattable.grid.command.InitializeGridCommand;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.ILayerListener;
import org.eclipse.nebula.widgets.nattable.layer.LabelStack;
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
import org.eclipse.nebula.widgets.nattable.layer.event.CellVisualUpdateEvent;
import org.eclipse.nebula.widgets.nattable.layer.event.ColumnVisualUpdateEvent;
import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;
import org.eclipse.nebula.widgets.nattable.layer.event.IVisualChangeEvent;
import org.eclipse.nebula.widgets.nattable.layer.event.RowVisualUpdateEvent;
import org.eclipse.nebula.widgets.nattable.layer.stack.DummyGridLayerStack;
import org.eclipse.nebula.widgets.nattable.painter.IOverlayPainter;
import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter;
import org.eclipse.nebula.widgets.nattable.painter.layer.ILayerPainter;
import org.eclipse.nebula.widgets.nattable.painter.layer.NatLayerPainter;
import org.eclipse.nebula.widgets.nattable.persistence.IPersistable;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.MoveDirectionEnum;
import org.eclipse.nebula.widgets.nattable.selection.event.CellSelectionEvent;
import org.eclipse.nebula.widgets.nattable.selection.event.ISelectionEvent;
import org.eclipse.nebula.widgets.nattable.style.theme.ThemeConfiguration;
import org.eclipse.nebula.widgets.nattable.style.theme.ThemeManager;
import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry;
import org.eclipse.nebula.widgets.nattable.ui.mode.ConfigurableModeEventHandler;
import org.eclipse.nebula.widgets.nattable.ui.mode.Mode;
import org.eclipse.nebula.widgets.nattable.ui.mode.ModeSupport;
import org.eclipse.nebula.widgets.nattable.util.GUIHelper;
import org.eclipse.nebula.widgets.nattable.util.IClientAreaProvider;
import org.eclipse.nebula.widgets.nattable.viewport.command.RecalculateScrollBarsCommand;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ScrollBar;

//this warning suppression is because of the ActiveCellEditorRegistry usage to ensure backwards compatibility
@SuppressWarnings("deprecation")
public class NatTable extends Canvas implements ILayer, PaintListener,
        IClientAreaProvider, ILayerListener, IPersistable {

    public static final int DEFAULT_STYLE_OPTIONS = SWT.NO_BACKGROUND
            | SWT.NO_REDRAW_RESIZE | SWT.DOUBLE_BUFFERED | SWT.V_SCROLL
            | SWT.H_SCROLL;

    /**
     * Key that is used for loading NatTable states. Is set to <code>true</code>
     * in case the initial painting is not finished yet. In this case there is
     * no need to call refresh commands on loading.
     */
    public static final String INITIAL_PAINT_COMPLETE_FLAG = "NatTable.initialPaintComplete"; //$NON-NLS-1$

    private UiBindingRegistry uiBindingRegistry;

    private ModeSupport modeSupport;

    private final EventConflaterChain conflaterChain = new EventConflaterChain();

    private final List<IOverlayPainter> overlayPainters = new ArrayList<IOverlayPainter>();

    private final List<IPersistable> persistables = new LinkedList<IPersistable>();

    private ILayer underlyingLayer;

    private IConfigRegistry configRegistry;

    protected final Collection<IConfiguration> configurations = new LinkedList<IConfiguration>();

    protected String id = GUIHelper.getSequenceNumber();

    private ILayerPainter layerPainter = new NatLayerPainter(this);

    private final boolean autoconfigure;

    /**
     * Listener that is added because of Bug 415459.<br/>
     * It is added to the parent composite and will close an active cell editor
     * in case the parent is resized. We need to listen to the parent composite
     * resize, because resizing a shell or a part in e4 does not cause loosing
     * the focus. Therefore the editor will stay open in such cases. As this
     * causes rendering issues when using percentage sizing, this listener
     * closes an editor on parent composite resize.<br/>
     * It is not registered as listener to NatTable itself, because this would
     * have impact when filtering or dynamic updates cause scrollbars to become
     * visible/invisible, which result in resizing of the NatTable.
     */
    private Listener closeEditorOnParentResize = new Listener() {
        @Override
        public void handleEvent(Event event) {
            // as resizing doesn't cause the current active editor to loose
            // focus
            // we are closing the current active editor manually
            if (!commitAndCloseActiveCellEditor()) {
                // if committing didn't work out we need to perform a hard close
                // otherwise the state of the table would be unstale
                getActiveCellEditor().close();
            }
        }
    };

    /**
     * This flag is used to deal with runtime issues on loading states while the
     * initial rendering is not finished yet.
     */
    private boolean initialPaintComplete = false;

    /**
     * The {@link ThemeManager} that is used to switch
     * {@link ThemeConfiguration}s at runtime.
     */
    private ThemeManager themeManager;

    /**
     * The active cell editor or {@code null} if there is no one.
     */
    private ICellEditor activeCellEditor;

    public NatTable(Composite parent) {
        this(parent, DEFAULT_STYLE_OPTIONS);
    }

    /**
     * @param parent
     *            widget for the table.
     * @param autoconfigure
     *            if set to False - No auto configuration is done - Default
     *            settings are <i>not</i> loaded. Configuration(s) have to be
     *            manually added by invoking addConfiguration(). At the minimum
     *            the {@link DefaultNatTableStyleConfiguration} must be added
     *            for the table to render.
     */
    public NatTable(Composite parent, boolean autoconfigure) {
        this(parent, DEFAULT_STYLE_OPTIONS, autoconfigure);
    }

    public NatTable(Composite parent, ILayer layer) {
        this(parent, DEFAULT_STYLE_OPTIONS, layer);
    }

    public NatTable(Composite parent, ILayer layer, boolean autoconfigure) {
        this(parent, DEFAULT_STYLE_OPTIONS, layer, autoconfigure);
    }

    public NatTable(Composite parent, final int style) {
        this(parent, style, new DummyGridLayerStack());
    }

    public NatTable(Composite parent, final int style, boolean autoconfigure) {
        this(parent, style, new DummyGridLayerStack(), autoconfigure);
    }

    public NatTable(final Composite parent, final int style, ILayer layer) {
        this(parent, style, layer, true);
    }

    public NatTable(final Composite parent, final int style,
            final ILayer layer, boolean autoconfigure) {
        super(parent, style);

        // Disable scroll bars by default; if a Viewport is available, it will
        // enable the scroll bars
        disableScrollBar(getHorizontalBar());
        disableScrollBar(getVerticalBar());

        initInternalListener();

        internalSetLayer(layer);

        this.autoconfigure = autoconfigure;
        if (autoconfigure) {
            this.configurations.add(new DefaultNatTableStyleConfiguration());
            configure();
        }

        this.conflaterChain.add(getVisualChangeEventConflater());
        this.conflaterChain.start();

        parent.addListener(SWT.Resize, this.closeEditorOnParentResize);

        addDisposeListener(new DisposeListener() {

            @Override
            public void widgetDisposed(DisposeEvent e) {
                doCommand(new DisposeResourcesCommand());
                NatTable.this.conflaterChain.stop();
                ActiveCellEditorRegistry.unregisterActiveCellEditor();
                layer.dispose();

                parent.removeListener(SWT.Resize, NatTable.this.closeEditorOnParentResize);
            }

        });
    }

    protected IEventConflater getVisualChangeEventConflater() {
        return new VisualChangeEventConflater(this);
    }

    private void disableScrollBar(ScrollBar scrollBar) {
        if (scrollBar != null) {
            scrollBar.setMinimum(0);
            scrollBar.setMaximum(1);
            scrollBar.setThumb(1);
            scrollBar.setEnabled(false);
        }
    }

    public ILayer getLayer() {
        return this.underlyingLayer;
    }

    public void setLayer(ILayer layer) {
        if (this.autoconfigure) {
            throw new IllegalStateException("May only set layer post construction if autoconfigure is turned off"); //$NON-NLS-1$
        }

        internalSetLayer(layer);
    }

    private void internalSetLayer(ILayer layer) {
        if (layer != null) {
            this.underlyingLayer = layer;
            this.underlyingLayer.setClientAreaProvider(new IClientAreaProvider() {

                @Override
                public Rectangle getClientArea() {
                    final Rectangle clientArea = new Rectangle(0, 0, 0, 0);
                    if (!isDisposed()) {
                        getDisplay().syncExec(new Runnable() {
                            @Override
                            public void run() {
                                Rectangle natClientArea = NatTable.this
                                        .getClientArea();
                                clientArea.x = natClientArea.x;
                                clientArea.y = natClientArea.y;
                                clientArea.width = natClientArea.width;
                                clientArea.height = natClientArea.height;
                            }
                        });
                    }
                    return clientArea;
                }

            });
            this.underlyingLayer.addLayerListener(this);
        }
    }

    /**
     * Adds a configuration to the table.
     * <p>
     * Configurations are processed when the {@link #configure()} method is
     * invoked. Each configuration object then has a chance to configure the
     * <ol>
     * <li>ILayer</li>
     * <li>ConfigRegistry</li>
     * <li>UiBindingRegistry</li>
     * </ol>
     */
    public void addConfiguration(IConfiguration configuration) {
        if (this.autoconfigure) {
            throw new IllegalStateException("May only add configurations post construction if autoconfigure is turned off"); //$NON-NLS-1$
        }

        this.configurations.add(configuration);
    }

    /**
     * @return {@link IConfigRegistry} used to hold the configuration bindings
     *         by Layer, DisplayMode and Config labels.
     */
    public IConfigRegistry getConfigRegistry() {
        if (this.configRegistry == null) {
            this.configRegistry = new ConfigRegistry();
            this.themeManager = new ThemeManager(this.configRegistry);
        }
        return this.configRegistry;
    }

    public void setConfigRegistry(IConfigRegistry configRegistry) {
        if (this.autoconfigure) {
            throw new IllegalStateException("May only set config registry post construction if autoconfigure is turned off"); //$NON-NLS-1$
        }

        this.configRegistry = configRegistry;
        this.themeManager = new ThemeManager(configRegistry);
    }

    /**
     * @return Registry holding all the UIBindings contributed by the underlying
     *         layers
     */
    public UiBindingRegistry getUiBindingRegistry() {
        if (this.uiBindingRegistry == null) {
            this.uiBindingRegistry = new UiBindingRegistry(this);
        }
        return this.uiBindingRegistry;
    }

    public void setUiBindingRegistry(UiBindingRegistry uiBindingRegistry) {
        if (this.autoconfigure) {
            throw new IllegalStateException("May only set UI binding registry post construction if autoconfigure is turned off"); //$NON-NLS-1$
        }

        this.uiBindingRegistry = uiBindingRegistry;
    }

    public String getID() {
        return this.id;
    }

    @Override
    protected void checkSubclass() {}

    protected void initInternalListener() {
        this.modeSupport = new ModeSupport(this);
        this.modeSupport.registerModeEventHandler(Mode.NORMAL_MODE,
                new ConfigurableModeEventHandler(this.modeSupport, this));
        this.modeSupport.switchMode(Mode.NORMAL_MODE);

        addPaintListener(this);

        addFocusListener(new FocusListener() {

            @Override
            public void focusLost(final FocusEvent arg0) {
                redraw();
            }

            @Override
            public void focusGained(final FocusEvent arg0) {
                redraw();
            }

        });

        addListener(SWT.Resize, new Listener() {
            @Override
            public void handleEvent(final Event e) {
                doCommand(new ClientAreaResizeCommand(NatTable.this));
                redraw();
            }
        });
    }

    @Override
    public boolean forceFocus() {
        return super.forceFocus();
    }

    // Painting ///////////////////////////////////////////////////////////////

    public List<IOverlayPainter> getOverlayPainters() {
        return this.overlayPainters;
    }

    public void addOverlayPainter(IOverlayPainter overlayPainter) {
        this.overlayPainters.add(overlayPainter);
    }

    public void removeOverlayPainter(IOverlayPainter overlayPainter) {
        this.overlayPainters.remove(overlayPainter);
    }

    @Override
    public void paintControl(final PaintEvent event) {
        paintNatTable(event);
        this.initialPaintComplete = true;
    }

    private void paintNatTable(final PaintEvent event) {
        getLayerPainter().paintLayer(this, event.gc, 0, 0,
                new Rectangle(event.x, event.y, event.width, event.height),
                getConfigRegistry());
    }

    @Override
    public ILayerPainter getLayerPainter() {
        return this.layerPainter;
    }

    public void setLayerPainter(ILayerPainter layerPainter) {
        this.layerPainter = layerPainter;
    }

    /**
     * Repaint only a specific column in the grid. This method is optimized so
     * that only the specific column is repainted and nothing else.
     *
     * @param columnPosition
     *            column of the grid to repaint
     */
    public void repaintColumn(int columnPosition) {
        int xOffset = getStartXOfColumnPosition(columnPosition);
        if (xOffset < 0) {
            return;
        }
        redraw(xOffset, 0, getColumnWidthByPosition(columnPosition),
                getHeight(), true);
    }

    /**
     * Repaint only a specific row in the grid. This method is optimized so that
     * only the specific row is repainted and nothing else.
     *
     * @param rowPosition
     *            row of the grid to repaint
     */
    public void repaintRow(int rowPosition) {
        int yOffset = getStartYOfRowPosition(rowPosition);
        if (yOffset < 0) {
            return;
        }
        redraw(0, yOffset, getWidth(), getRowHeightByPosition(rowPosition),
                true);
    }

    /**
     * Repaint only a specific cell in the grid. This method is optimized so
     * that only the specific cell is repainted and nothing else.
     *
     * @param columnPosition
     *            column position of the cell to repaint
     * @param rowPosition
     *            row position of the cell to repaint
     */
    public void repaintCell(int columnPosition, int rowPosition) {
        int xOffset = getStartXOfColumnPosition(columnPosition);
        int yOffset = getStartYOfRowPosition(rowPosition);

        redraw(xOffset, yOffset, getColumnWidthByPosition(columnPosition),
                getRowHeightByPosition(rowPosition), true);
    }

    /**
     * Repaint the area to the right of the last column in case there is more
     * space available than columns to paint.
     */
    public void repaintHorizontalLeftOver() {
        int leftOverSpace = getClientArea().width - getWidth();
        if (leftOverSpace > 0)
            redraw(getWidth(), 0, leftOverSpace, getHeight(), true);
    }

    /**
     * Repaint the area to the bottom of the last row in case there is more
     * space available than rows to paint.
     */
    public void repaintVerticalLeftOver() {
        int leftOverSpace = getClientArea().height - getHeight();
        if (leftOverSpace > 0)
            redraw(0, getHeight(), getClientArea().width, leftOverSpace, true);
    }

    public void updateResize() {
        updateResize(true);
    }

    /**
     * Update the table screen by re-calculating everything again. It should not
     * be called too frequently.
     *
     * @param redraw
     *            true to redraw the table
     */
    private void updateResize(final boolean redraw) {
        if (isDisposed()) {
            return;
        }
        doCommand(new RecalculateScrollBarsCommand());
        if (redraw) {
            redraw();
        }
    }

    /**
     * Refreshes the entire NatTable as every layer will be refreshed.
     */
    public void refresh() {
        doCommand(new StructuralRefreshCommand());
    }

    @Override
    public void configure(ConfigRegistry configRegistry,
            UiBindingRegistry uiBindingRegistry) {
        throw new UnsupportedOperationException("Cannot use this method to configure NatTable. Use no-argument configure() instead."); //$NON-NLS-1$
    }

    /**
     * Processes all the registered {@link IConfiguration} (s). All the
     * underlying layers are walked and given a chance to configure. Note: all
     * desired configuration tweaks must be done <i>before</i> this method is
     * invoked.
     */
    public void configure() {
        if (this.underlyingLayer == null) {
            throw new IllegalStateException("Layer must be set before configure is called"); //$NON-NLS-1$
        }

        if (this.underlyingLayer != null) {
            this.underlyingLayer.configure((ConfigRegistry) getConfigRegistry(),
                    getUiBindingRegistry());
        }

        for (IConfiguration configuration : this.configurations) {
            configuration.configureLayer(this);
            configuration.configureRegistry(getConfigRegistry());
            configuration.configureUiBindings(getUiBindingRegistry());
        }

        // Once everything is initialized and properly configured we will
        // now formally initialize the grid
        doCommand(new InitializeGridCommand(this));
    }

    // Events /////////////////////////////////////////////////////////////////

    @Override
    public void handleLayerEvent(ILayerEvent event) {
        for (ILayerListener layerListener : this.listeners) {
            layerListener.handleLayerEvent(event);
        }

        if (event instanceof CellVisualUpdateEvent) {
            CellVisualUpdateEvent update = (CellVisualUpdateEvent) event;
            repaintCell(update.getColumnPosition(), update.getRowPosition());
            return;
        }

        if (event instanceof ColumnVisualUpdateEvent) {
            ColumnVisualUpdateEvent update = (ColumnVisualUpdateEvent) event;
            // if more than one column has changed repaint the whole table
            Collection<Range> ranges = update.getColumnPositionRanges();
            if (ranges.size() == 1) {
                Range range = ranges.iterator().next();
                if (range.end - range.start == 1) {
                    repaintColumn(range.start);
                    return;
                }
            }
        }

        if (event instanceof RowVisualUpdateEvent) {
            RowVisualUpdateEvent update = (RowVisualUpdateEvent) event;
            // if more than one row has changed repaint the whole table
            Collection<Range> ranges = update.getRowPositionRanges();
            if (ranges.size() == 1) {
                Range range = ranges.iterator().next();
                if (range.end - range.start == 1) {
                    repaintRow(range.start);
                    return;
                }
            }
        }

        if (event instanceof ISelectionEvent) {
            if (event instanceof CellSelectionEvent) {
                Event e = new Event();
                e.widget = this;
                try {
                    notifyListeners(SWT.Selection, e);
                } catch (RuntimeException re) {
                    re.printStackTrace();
                }
            }

            // in case of selections we redraw immediately
            // this is because with Bug 440037 it was reported that
            // NatTable is too lazy in handling selections which
            // was caused by the EventConflaterChain that only performs
            // updates every 100ms to avoid flickering when handling too
            // many refresh operations in a short period
            redraw();
        } else if (event instanceof IVisualChangeEvent) {
            this.conflaterChain.addEvent(event);
        }

        if (event instanceof CellEditorCreatedEvent) {
            CellEditorCreatedEvent editorEvent = (CellEditorCreatedEvent) event;
            this.activeCellEditor = editorEvent.getEditor();
            Control editorControl = this.activeCellEditor.getEditorControl();
            if (editorControl != null && !editorControl.isDisposed()) {
                editorControl.addDisposeListener(new DisposeListener() {

                    @Override
                    public void widgetDisposed(DisposeEvent e) {
                        NatTable.this.activeCellEditor = null;
                        ActiveCellEditorRegistry.unregisterActiveCellEditor();
                    }
                });
            } else {
                this.activeCellEditor = null;
                ActiveCellEditorRegistry.unregisterActiveCellEditor();
            }
            ActiveCellEditorRegistry.registerActiveCellEditor(this.activeCellEditor);
        }
    }

    // ILayer /////////////////////////////////////////////////////////////////

    // Persistence

    /**
     * Save the state of the table to the properties object.
     * {@link ILayer#saveState(String, Properties)} is invoked on all the
     * underlying layers. This properties object will be populated with the
     * settings of all underlying layers and any {@link IPersistable} registered
     * with those layers.
     */
    @Override
    public void saveState(final String prefix, final Properties properties) {
        BusyIndicator.showWhile(null, new Runnable() {

            @Override
            public void run() {
                NatTable.this.underlyingLayer.saveState(prefix, properties);
            }
        });
    }

    /**
     * Restore the state of the underlying layers from the values in the
     * properties object.
     *
     * @see #saveState(String, Properties)
     */
    @Override
    public void loadState(final String prefix, final Properties properties) {
        BusyIndicator.showWhile(null, new Runnable() {

            @Override
            public void run() {
                // if the initial painting is not finished yet, tell this the
                // underlying
                // mechanisms so there will be no refresh events fired
                if (!NatTable.this.initialPaintComplete)
                    properties.setProperty(INITIAL_PAINT_COMPLETE_FLAG, "true"); //$NON-NLS-1$

                NatTable.this.underlyingLayer.loadState(prefix, properties);
            }
        });
    }

    /**
     * @see ILayer#registerPersistable(IPersistable)
     */
    @Override
    public void registerPersistable(IPersistable persistable) {
        this.persistables.add(persistable);
    }

    @Override
    public void unregisterPersistable(IPersistable persistable) {
        this.persistables.remove(persistable);
    }

    // Command

    @Override
    public boolean doCommand(ILayerCommand command) {
        return this.underlyingLayer.doCommand(command);
    }

    @Override
    public void registerCommandHandler(ILayerCommandHandler<?> commandHandler) {
        this.underlyingLayer.registerCommandHandler(commandHandler);
    }

    @Override
    public void unregisterCommandHandler(
            Class<? extends ILayerCommand> commandClass) {
        this.underlyingLayer.unregisterCommandHandler(commandClass);
    }

    // Events

    private final List<ILayerListener> listeners = new ArrayList<ILayerListener>();

    @Override
    public void fireLayerEvent(ILayerEvent event) {
        this.underlyingLayer.fireLayerEvent(event);
    }

    @Override
    public void addLayerListener(ILayerListener listener) {
        this.listeners.add(listener);
    }

    @Override
    public void removeLayerListener(ILayerListener listener) {
        this.listeners.remove(listener);
    }

    @Override
    public boolean hasLayerListener(
            Class<? extends ILayerListener> layerListenerClass) {
        for (ILayerListener listener : this.listeners) {
            if (listener.getClass().equals(layerListenerClass)) {
                return true;
            }
        }
        return false;
    }

    // Columns

    @Override
    public int getColumnCount() {
        return this.underlyingLayer.getColumnCount();
    }

    @Override
    public int getPreferredColumnCount() {
        return this.underlyingLayer.getPreferredColumnCount();
    }

    @Override
    public int getColumnIndexByPosition(int columnPosition) {
        return this.underlyingLayer.getColumnIndexByPosition(columnPosition);
    }

    @Override
    public int localToUnderlyingColumnPosition(int localColumnPosition) {
        return localColumnPosition;
    }

    @Override
    public int underlyingToLocalColumnPosition(ILayer sourceUnderlyingLayer,
            int underlyingColumnPosition) {
        if (sourceUnderlyingLayer != this.underlyingLayer) {
            return -1;
        }

        return underlyingColumnPosition;
    }

    @Override
    public Collection<Range> underlyingToLocalColumnPositions(
            ILayer sourceUnderlyingLayer,
            Collection<Range> underlyingColumnPositionRanges) {
        if (sourceUnderlyingLayer != this.underlyingLayer) {
            return null;
        }

        return underlyingColumnPositionRanges;
    }

    // Width

    @Override
    public int getWidth() {
        return this.underlyingLayer.getWidth();
    }

    @Override
    public int getPreferredWidth() {
        return this.underlyingLayer.getPreferredWidth();
    }

    @Override
    public int getColumnWidthByPosition(int columnPosition) {
        return this.underlyingLayer.getColumnWidthByPosition(columnPosition);
    }

    // Column resize

    @Override
    public boolean isColumnPositionResizable(int columnPosition) {
        return this.underlyingLayer.isColumnPositionResizable(columnPosition);
    }

    // X

    @Override
    public int getColumnPositionByX(int x) {
        return this.underlyingLayer.getColumnPositionByX(x);
    }

    @Override
    public int getStartXOfColumnPosition(int columnPosition) {
        return this.underlyingLayer.getStartXOfColumnPosition(columnPosition);
    }

    // Underlying

    @Override
    public Collection<ILayer> getUnderlyingLayersByColumnPosition(
            int columnPosition) {
        Collection<ILayer> underlyingLayers = new HashSet<ILayer>();
        underlyingLayers.add(this.underlyingLayer);
        return underlyingLayers;
    }

    // Rows

    @Override
    public int getRowCount() {
        return this.underlyingLayer.getRowCount();
    }

    @Override
    public int getPreferredRowCount() {
        return this.underlyingLayer.getPreferredRowCount();
    }

    @Override
    public int getRowIndexByPosition(int rowPosition) {
        return this.underlyingLayer.getRowIndexByPosition(rowPosition);
    }

    @Override
    public int localToUnderlyingRowPosition(int localRowPosition) {
        return localRowPosition;
    }

    @Override
    public int underlyingToLocalRowPosition(ILayer sourceUnderlyingLayer,
            int underlyingRowPosition) {
        if (sourceUnderlyingLayer != this.underlyingLayer) {
            return -1;
        }

        return underlyingRowPosition;
    }

    @Override
    public Collection<Range> underlyingToLocalRowPositions(
            ILayer sourceUnderlyingLayer,
            Collection<Range> underlyingRowPositionRanges) {
        if (sourceUnderlyingLayer != this.underlyingLayer) {
            return null;
        }

        return underlyingRowPositionRanges;
    }

    // Height

    @Override
    public int getHeight() {
        return this.underlyingLayer.getHeight();
    }

    @Override
    public int getPreferredHeight() {
        return this.underlyingLayer.getPreferredHeight();
    }

    @Override
    public int getRowHeightByPosition(int rowPosition) {
        return this.underlyingLayer.getRowHeightByPosition(rowPosition);
    }

    // Row resize

    @Override
    public boolean isRowPositionResizable(int rowPosition) {
        return this.underlyingLayer.isRowPositionResizable(rowPosition);
    }

    // Y

    @Override
    public int getRowPositionByY(int y) {
        return this.underlyingLayer.getRowPositionByY(y);
    }

    @Override
    public int getStartYOfRowPosition(int rowPosition) {
        return this.underlyingLayer.getStartYOfRowPosition(rowPosition);
    }

    // Underlying

    @Override
    public Collection<ILayer> getUnderlyingLayersByRowPosition(int rowPosition) {
        Collection<ILayer> underlyingLayers = new HashSet<ILayer>();
        underlyingLayers.add(this.underlyingLayer);
        return underlyingLayers;
    }

    // Cell features

    @Override
    public ILayerCell getCellByPosition(int columnPosition, int rowPosition) {
        return this.underlyingLayer.getCellByPosition(columnPosition, rowPosition);
    }

    @Override
    public Rectangle getBoundsByPosition(int columnPosition, int rowPosition) {
        return this.underlyingLayer.getBoundsByPosition(columnPosition, rowPosition);
    }

    @Override
    public String getDisplayModeByPosition(int columnPosition, int rowPosition) {
        return this.underlyingLayer.getDisplayModeByPosition(columnPosition,
                rowPosition);
    }

    @Override
    public LabelStack getConfigLabelsByPosition(int columnPosition,
            int rowPosition) {
        return this.underlyingLayer.getConfigLabelsByPosition(columnPosition,
                rowPosition);
    }

    @Override
    public Object getDataValueByPosition(int columnPosition, int rowPosition) {
        return this.underlyingLayer.getDataValueByPosition(columnPosition,
                rowPosition);
    }

    @Override
    public ICellPainter getCellPainter(int columnPosition, int rowPosition,
            ILayerCell cell, IConfigRegistry configRegistry) {
        return this.underlyingLayer.getCellPainter(columnPosition, rowPosition,
                cell, configRegistry);
    }

    // IRegionResolver

    @Override
    public LabelStack getRegionLabelsByXY(int x, int y) {
        return this.underlyingLayer.getRegionLabelsByXY(x, y);
    }

    @Override
    public ILayer getUnderlyingLayerByPosition(int columnPosition,
            int rowPosition) {
        return this.underlyingLayer;
    }

    @Override
    public IClientAreaProvider getClientAreaProvider() {
        return this;
    }

    @Override
    public void setClientAreaProvider(IClientAreaProvider clientAreaProvider) {
        this.underlyingLayer.setClientAreaProvider(clientAreaProvider);
    }

    // DND /////////////////////////////////////////////////////////////////

    /**
     * Adds support for dragging items out of this control via a user
     * drag-and-drop operation.
     *
     * @param operations
     *            a bitwise OR of the supported drag and drop operation types (
     *            <code>DROP_COPY</code>,<code>DROP_LINK</code>, and
     *            <code>DROP_MOVE</code>)
     * @param transferTypes
     *            the transfer types that are supported by the drag operation
     * @param listener
     *            the callback that will be invoked to set the drag data and to
     *            cleanup after the drag and drop operation finishes
     * @see org.eclipse.swt.dnd.DND
     */
    public void addDragSupport(final int operations,
            final Transfer[] transferTypes, final DragSourceListener listener) {
        final DragSource dragSource = new DragSource(this, operations);
        dragSource.setTransfer(transferTypes);

        DragSourceListener wrapper = new DragSourceListener() {

            @Override
            public void dragStart(DragSourceEvent event) {
                listener.dragStart(event);
            }

            @Override
            public void dragSetData(DragSourceEvent event) {
                listener.dragSetData(event);
            }

            @Override
            public void dragFinished(DragSourceEvent event) {
                listener.dragFinished(event);
                // ensure to stop any current active internal drag mode
                NatTable.this.modeSupport.switchMode(Mode.NORMAL_MODE);
            }
        };

        dragSource.addDragListener(wrapper);
    }

    /**
     * Adds support for dropping items into this control via a user
     * drag-and-drop operation.
     *
     * @param operations
     *            a bitwise OR of the supported drag and drop operation types (
     *            <code>DROP_COPY</code>,<code>DROP_LINK</code>, and
     *            <code>DROP_MOVE</code>)
     * @param transferTypes
     *            the transfer types that are supported by the drop operation
     * @param listener
     *            the callback that will be invoked after the drag and drop
     *            operation finishes
     * @see org.eclipse.swt.dnd.DND
     */
    public void addDropSupport(final int operations,
            final Transfer[] transferTypes, final DropTargetListener listener) {
        final DropTarget dropTarget = new DropTarget(this, operations);
        dropTarget.setTransfer(transferTypes);
        dropTarget.addDropListener(listener);
    }

    // Theme styling

    /**
     * Will unregister the style configurations that were applied before by
     * another {@link ThemeConfiguration} and register the style configurations
     * of the given {@link ThemeConfiguration}.
     *
     * @param themeConfiguration
     *            The ThemeConfiguration that contains the style configurations
     *            to apply.
     */
    public void setTheme(ThemeConfiguration themeConfiguration) {
        this.themeManager.applyTheme(themeConfiguration);
        doCommand(new VisualRefreshCommand());
    }

    // Editor
    /**
     * Returns the active cell editor that is currently open or {@code null} if
     * there is no editor active.
     *
     * @return the active editor or {@code null}
     */
    public ICellEditor getActiveCellEditor() {
        return this.activeCellEditor;
    }

    /**
     * Checks if there is an active cell editor registered. If there is one, it
     * is tried to commit the value that is currently entered there.
     *
     * @return <code>false</code> if there is an open editor that can not be
     *         committed because of conversion/validation errors,
     *         <code>true</code> if there is no active open editor or it could
     *         be closed after committing the value.
     */
    public boolean commitAndCloseActiveCellEditor() {
        if (this.activeCellEditor != null) {
            return this.activeCellEditor.commit(MoveDirectionEnum.NONE, true);
        }
        return true;
    }

}
TOP

Related Classes of org.eclipse.nebula.widgets.nattable.NatTable

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.