| /******************************************************************************* |
| * Copyright (c) 2012, 2016 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 |
| * Dirk Fauth <dirk.fauth@googlemail.com> - made commandHandlers and eventHandlers |
| * visible to subclasses for testing |
| * Dirk Fauth <dirk.fauth@googlemail.com> - Bug 447145 |
| ******************************************************************************/ |
| package org.eclipse.nebula.widgets.nattable.layer; |
| |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedHashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.concurrent.locks.ReadWriteLock; |
| import java.util.concurrent.locks.ReentrantReadWriteLock; |
| |
| import org.eclipse.nebula.widgets.nattable.command.ILayerCommand; |
| import org.eclipse.nebula.widgets.nattable.command.ILayerCommandHandler; |
| import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes; |
| import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry; |
| import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry; |
| import org.eclipse.nebula.widgets.nattable.config.IConfiguration; |
| import org.eclipse.nebula.widgets.nattable.layer.cell.IConfigLabelAccumulator; |
| import org.eclipse.nebula.widgets.nattable.layer.cell.IConfigLabelProvider; |
| import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; |
| import org.eclipse.nebula.widgets.nattable.layer.cell.LayerCell; |
| import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent; |
| import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEventHandler; |
| import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter; |
| import org.eclipse.nebula.widgets.nattable.painter.layer.GridLineCellLayerPainter; |
| import org.eclipse.nebula.widgets.nattable.painter.layer.ILayerPainter; |
| import org.eclipse.nebula.widgets.nattable.persistence.IPersistable; |
| import org.eclipse.nebula.widgets.nattable.style.DisplayMode; |
| import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry; |
| import org.eclipse.nebula.widgets.nattable.util.IClientAreaProvider; |
| import org.eclipse.swt.graphics.Rectangle; |
| |
| /** |
| * Base layer implementation with common methods for managing listeners and |
| * caching, etc. |
| */ |
| public abstract class AbstractLayer implements ILayer { |
| |
| private String regionName; |
| protected ILayerPainter layerPainter; |
| private IClientAreaProvider clientAreaProvider = IClientAreaProvider.DEFAULT; |
| private IConfigLabelAccumulator configLabelAccumulator; |
| |
| protected final Map<Class<? extends ILayerCommand>, ILayerCommandHandler<? extends ILayerCommand>> commandHandlers = |
| new LinkedHashMap<Class<? extends ILayerCommand>, ILayerCommandHandler<? extends ILayerCommand>>(); |
| protected final Map<Class<? extends ILayerEvent>, ILayerEventHandler<? extends ILayerEvent>> eventHandlers = |
| new HashMap<Class<? extends ILayerEvent>, ILayerEventHandler<? extends ILayerEvent>>(); |
| |
| private final List<IPersistable> persistables = new LinkedList<IPersistable>(); |
| private final Set<ILayerListener> listeners = new LinkedHashSet<ILayerListener>(); |
| private final Collection<IConfiguration> configurations = new LinkedList<IConfiguration>(); |
| |
| private boolean configurationApplied = false; |
| |
| /** |
| * {@link ReadWriteLock} that is used to ensure that no concurrent |
| * modifications happen on event handling |
| * |
| * @since 1.4 |
| */ |
| protected ReadWriteLock eventHelperLock = new ReentrantReadWriteLock(); |
| |
| // Dispose |
| |
| @Override |
| public void dispose() {} |
| |
| // Regions |
| |
| @Override |
| public LabelStack getRegionLabelsByXY(int x, int y) { |
| LabelStack regionLabels = new LabelStack(); |
| if (this.regionName != null) { |
| regionLabels.addLabel(this.regionName); |
| } |
| return regionLabels; |
| } |
| |
| public String getRegionName() { |
| return this.regionName; |
| } |
| |
| public void setRegionName(String regionName) { |
| this.regionName = regionName; |
| } |
| |
| // Config lables |
| |
| @Override |
| public LabelStack getConfigLabelsByPosition(int columnPosition, int rowPosition) { |
| LabelStack configLabels = new LabelStack(); |
| if (this.configLabelAccumulator != null) { |
| this.configLabelAccumulator.accumulateConfigLabels(configLabels, columnPosition, rowPosition); |
| } |
| if (this.regionName != null) { |
| configLabels.addLabel(this.regionName); |
| } |
| return configLabels; |
| } |
| |
| public IConfigLabelAccumulator getConfigLabelAccumulator() { |
| return this.configLabelAccumulator; |
| } |
| |
| public void setConfigLabelAccumulator(IConfigLabelAccumulator cellLabelAccumulator) { |
| this.configLabelAccumulator = cellLabelAccumulator; |
| } |
| |
| // Persistence |
| |
| @Override |
| public void saveState(String prefix, Properties properties) { |
| for (IPersistable persistable : this.persistables) { |
| persistable.saveState(prefix, properties); |
| } |
| } |
| |
| @Override |
| public void loadState(String prefix, Properties properties) { |
| for (IPersistable persistable : this.persistables) { |
| persistable.loadState(prefix, properties); |
| } |
| } |
| |
| @Override |
| public void registerPersistable(IPersistable persistable) { |
| this.persistables.add(persistable); |
| } |
| |
| @Override |
| public void unregisterPersistable(IPersistable persistable) { |
| this.persistables.remove(persistable); |
| } |
| |
| // Configuration |
| |
| public void addConfiguration(IConfiguration configuration) { |
| this.configurations.add(configuration); |
| } |
| |
| public void clearConfiguration() { |
| this.configurations.clear(); |
| } |
| |
| @Override |
| public void configure(ConfigRegistry configRegistry, UiBindingRegistry uiBindingRegistry) { |
| if (!this.configurationApplied) { |
| for (IConfiguration configuration : this.configurations) { |
| configuration.configureLayer(this); |
| configuration.configureRegistry(configRegistry); |
| configuration.configureUiBindings(uiBindingRegistry); |
| } |
| |
| // Bug 447145 |
| // to avoid registering configuration values multiple times, we |
| // remember that this layer has already been configured |
| this.configurationApplied = true; |
| } |
| } |
| |
| // Commands |
| |
| @Override |
| @SuppressWarnings({ "unchecked", "rawtypes" }) |
| public boolean doCommand(ILayerCommand command) { |
| for (Class<? extends ILayerCommand> commandClass : this.commandHandlers.keySet()) { |
| if (commandClass.isInstance(command)) { |
| ILayerCommandHandler commandHandler = this.commandHandlers.get(commandClass); |
| if (commandHandler.doCommand(this, command)) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| // Command handlers |
| |
| /** |
| * Layers should use this method to register their command handlers and call |
| * it from their constructor. This allows easy overriding if required of |
| * command handlers |
| */ |
| protected void registerCommandHandlers() { |
| // No op |
| } |
| |
| @Override |
| public void registerCommandHandler(ILayerCommandHandler<?> commandHandler) { |
| this.commandHandlers.put(commandHandler.getCommandClass(), commandHandler); |
| } |
| |
| @Override |
| public void unregisterCommandHandler(Class<? extends ILayerCommand> commandClass) { |
| this.commandHandlers.remove(commandClass); |
| } |
| |
| // Events |
| |
| @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; |
| } |
| |
| /** |
| * Handle layer event notification. Convert it to your context and propagate |
| * <i>UP</i>. |
| * |
| * If you override this method you <strong>MUST NOT FORGET</strong> to raise |
| * the event up the layer stack by calling |
| * <code>super.fireLayerEvent(event)</code> - unless you plan to eat the |
| * event yourself. |
| **/ |
| @Override |
| @SuppressWarnings({ "unchecked", "rawtypes" }) |
| public void handleLayerEvent(ILayerEvent event) { |
| this.eventHelperLock.readLock().lock(); |
| try { |
| for (Class<? extends ILayerEvent> eventClass : this.eventHandlers.keySet()) { |
| if (eventClass.isInstance(event)) { |
| ILayerEventHandler eventHandler = this.eventHandlers.get(eventClass); |
| eventHandler.handleLayerEvent(event); |
| } |
| } |
| |
| // Pass on the event to our parent |
| if (event.convertToLocal(this)) { |
| fireLayerEvent(event); |
| } |
| } finally { |
| this.eventHelperLock.readLock().unlock(); |
| } |
| } |
| |
| public void registerEventHandler(ILayerEventHandler<?> eventHandler) { |
| this.eventHelperLock.writeLock().lock(); |
| try { |
| this.eventHandlers.put(eventHandler.getLayerEventClass(), eventHandler); |
| } finally { |
| this.eventHelperLock.writeLock().unlock(); |
| } |
| } |
| |
| public void unregisterEventHandler(ILayerEventHandler<?> eventHandler) { |
| this.eventHelperLock.writeLock().lock(); |
| try { |
| this.eventHandlers.remove(eventHandler.getLayerEventClass()); |
| } finally { |
| this.eventHelperLock.writeLock().unlock(); |
| } |
| } |
| |
| /** |
| * Pass the event to all the {@link ILayerListener} registered on this |
| * layer. A cloned copy is passed to each listener. |
| */ |
| @Override |
| public void fireLayerEvent(ILayerEvent event) { |
| if (this.listeners.size() > 0) { |
| Iterator<ILayerListener> it = this.listeners.iterator(); |
| boolean isLastListener = false; |
| do { |
| ILayerListener l = it.next(); |
| isLastListener = !it.hasNext(); // Lookahead |
| |
| // Fire cloned event to first n-1 listeners; fire original event |
| // to last listener |
| ILayerEvent eventToFire = isLastListener ? event : event.cloneEvent(); |
| l.handleLayerEvent(eventToFire); |
| } while (!isLastListener); |
| } |
| } |
| |
| /** |
| * @return {@link ILayerPainter}. Defaults to |
| * {@link GridLineCellLayerPainter} |
| */ |
| @Override |
| public ILayerPainter getLayerPainter() { |
| if (this.layerPainter == null) { |
| this.layerPainter = new GridLineCellLayerPainter(); |
| } |
| return this.layerPainter; |
| } |
| |
| public void setLayerPainter(ILayerPainter layerPainter) { |
| this.layerPainter = layerPainter; |
| } |
| |
| // Client area |
| |
| @Override |
| public IClientAreaProvider getClientAreaProvider() { |
| return this.clientAreaProvider; |
| } |
| |
| @Override |
| public void setClientAreaProvider(IClientAreaProvider clientAreaProvider) { |
| this.clientAreaProvider = clientAreaProvider; |
| } |
| |
| @Override |
| public String toString() { |
| return getClass().getSimpleName(); |
| } |
| |
| @Override |
| public ILayerCell getCellByPosition(int columnPosition, int rowPosition) { |
| if (columnPosition < 0 || columnPosition >= getColumnCount() |
| || rowPosition < 0 || rowPosition >= getRowCount()) { |
| return null; |
| } |
| |
| return new LayerCell(this, columnPosition, rowPosition); |
| } |
| |
| @Override |
| public Rectangle getBoundsByPosition(int columnPosition, int rowPosition) { |
| ILayerCell cell = getCellByPosition(columnPosition, rowPosition); |
| ILayer cellLayer = cell.getLayer(); |
| |
| int xOffset = -1; |
| int yOffset = -1; |
| int width = 0; |
| int height = 0; |
| { |
| int column = cell.getOriginColumnPosition(); |
| int end = column + cell.getColumnSpan(); |
| for (; column < end; column++) { |
| int columnOffset = cellLayer.getStartXOfColumnPosition(column); |
| if (column < cellLayer.getColumnCount()) { |
| xOffset = columnOffset; |
| break; |
| } |
| } |
| for (; column < end; column++) { |
| width += cellLayer.getColumnWidthByPosition(column); |
| } |
| } |
| { |
| int row = cell.getOriginRowPosition(); |
| int end = row + cell.getRowSpan(); |
| for (; row < end; row++) { |
| int rowOffset = cellLayer.getStartYOfRowPosition(row); |
| if (row < cellLayer.getRowCount()) { |
| yOffset = rowOffset; |
| break; |
| } |
| } |
| for (; row < end; row++) { |
| height += cellLayer.getRowHeightByPosition(row); |
| } |
| } |
| |
| return new Rectangle(xOffset, yOffset, width, height); |
| } |
| |
| @Override |
| public String getDisplayModeByPosition(int columnPosition, int rowPosition) { |
| return DisplayMode.NORMAL; |
| } |
| |
| @Override |
| public ICellPainter getCellPainter( |
| int columnPosition, int rowPosition, |
| ILayerCell cell, IConfigRegistry configRegistry) { |
| return configRegistry.getConfigAttribute( |
| CellConfigAttributes.CELL_PAINTER, |
| cell.getDisplayMode(), |
| cell.getConfigLabels().getLabels()); |
| } |
| |
| /** |
| * @return <code>true</code> if the layer has a dynamic size (e.g. viewport) |
| * or a fixed size. |
| * @since 1.4 |
| */ |
| public boolean isDynamicSizeLayer() { |
| return false; |
| } |
| |
| /** |
| * @return The collection of labels that are provided by this layer. |
| * @since 1.4 |
| */ |
| public Collection<String> getProvidedLabels() { |
| Collection<String> labels = null; |
| |
| if (getUnderlyingLayerByPosition(0, 0) instanceof AbstractLayer) { |
| labels = ((AbstractLayer) getUnderlyingLayerByPosition(0, 0)).getProvidedLabels(); |
| } else { |
| labels = new LinkedHashSet<String>(); |
| } |
| |
| // add the region |
| if (this.regionName != null) { |
| labels.add(this.regionName); |
| } |
| |
| // add the labels configured via IConfigLabelAccumulator |
| if (this.configLabelAccumulator != null |
| && this.configLabelAccumulator instanceof IConfigLabelProvider) { |
| labels.addAll(((IConfigLabelProvider) this.configLabelAccumulator).getProvidedLabels()); |
| } |
| |
| return labels; |
| } |
| } |