blob: 4bd968f000e95abd5932c3543eb7584a5c5f1119 [file] [log] [blame]
/*******************************************************************************
* 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.copy.command;
import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler;
import org.eclipse.nebula.widgets.nattable.coordinate.IValueIterator;
import org.eclipse.nebula.widgets.nattable.coordinate.RangeList;
import org.eclipse.nebula.widgets.nattable.copy.serializing.CopyDataToClipboardSerializer;
import org.eclipse.nebula.widgets.nattable.copy.serializing.CopyFormattedTextToClipboardSerializer;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
import org.eclipse.nebula.widgets.nattable.serializing.ISerializer;
/**
* Handler class for copying selected data within the {@link SelectionLayer} to the clipboard.
* This handler is registered by default with the {@link SelectionLayer}, without references
* to the header regions. You can override the copy data behaviour by registering an instance
* of this handler to a layer above the {@link SelectionLayer}. This way the registered custom
* instance will consume a {@link CopyDataToClipboardCommand} and the registered default handler
* won't be called.
*/
public class CopyDataCommandHandler extends AbstractLayerCommandHandler<CopyDataToClipboardCommand> {
/**
* The SelectionLayer needed to retrieve the selected data to copy to the clipboard.
*/
private final SelectionLayer selectionLayer;
/**
* The column header layer of the grid, needed to also copy the column header data.
*/
private final ILayer columnHeaderDataLayer;
/**
* The row header layer of the grid, needed to also copy the row header data.
*/
private final ILayer rowHeaderDataLayer;
/**
* Flag to specify which serializer should be used for copying the data.
* <code>false</code> will use the CopyDataToClipboardSerializer which simply calls
* <code>toString()</code> to serialize the data to copy, <code>true</code> will
* use the CopyFormattedTextToClipboardSerializer which will use the configured
* IDisplayConverter to get the String representation of the value to copy.
*/
private boolean copyFormattedText;
/**
* Creates an instance that only checks the {@link SelectionLayer} for data to add to the
* clipboard.
* @param selectionLayer The {@link SelectionLayer} within the NatTable. Can not be <code>null</code>.
*/
public CopyDataCommandHandler(SelectionLayer selectionLayer) {
this(selectionLayer, null, null);
}
/**
* Creates an instance that checks the {@link SelectionLayer} and the header layers if they are given.
* @param selectionLayer The {@link SelectionLayer} within the NatTable. Can not be <code>null</code>.
* @param columnHeaderDataLayer The column header data layer within the NatTable grid. Can be <code>null</code>.
* @param rowHeaderDataLayer The row header data layer within the NatTable grid. Can be <code>null</code>.
*/
public CopyDataCommandHandler(SelectionLayer selectionLayer, ILayer columnHeaderDataLayer, ILayer rowHeaderDataLayer) {
assert selectionLayer != null : "The SelectionLayer can not be null on creating a CopyDataCommandHandler"; //$NON-NLS-1$
this.selectionLayer = selectionLayer;
this.columnHeaderDataLayer = columnHeaderDataLayer;
this.rowHeaderDataLayer = rowHeaderDataLayer;
}
/**
* Specify which serializer to use for copying.
* @param copyFormattedText <code>false</code> will use the CopyDataToClipboardSerializer
* which simply calls <code>toString()</code> to serialize the data to copy,
* <code>true</code> will use the CopyFormattedTextToClipboardSerializer which
* will use the configured IDisplayConverter to get the String representation of
* the value to copy
*/
public void setCopyFormattedText(boolean copyFormattedText) {
this.copyFormattedText = copyFormattedText;
}
@Override
public boolean doCommand(CopyDataToClipboardCommand command) {
ISerializer serializer = copyFormattedText ?
new CopyFormattedTextToClipboardSerializer(assembleCopiedDataStructure(), command) :
new CopyDataToClipboardSerializer(assembleCopiedDataStructure(), command);
serializer.serialize();
return true;
}
@Override
public Class<CopyDataToClipboardCommand> getCommandClass() {
return CopyDataToClipboardCommand.class;
}
/**
* Collects and assembles the selected data that should be copied to the clipboard.
*
* Creates the two dimensional array whose dimensions are calculated based on the selection
* within the {@link SelectionLayer} and the configured column and row headers.
*
* @return A two dimensional array containing the selected cells to copy to the clipboard.
* The first level of this array represent the row positions of the cells, while the
* second level contains the cells itself based on the column position.
*/
protected ILayerCell[][] assembleCopiedDataStructure() {
final RangeList selectedRowPositions = selectionLayer.getSelectedRowPositions();
final RangeList selectedColumnPositions = selectionLayer.getSelectedColumnPositions();
final int rowOffset = (columnHeaderDataLayer != null) ? columnHeaderDataLayer.getRowCount() : 0;
final int columnOffset = (rowHeaderDataLayer != null) ? rowHeaderDataLayer.getColumnCount() : 0;
final ILayerCell[][] cells = new ILayerCell[selectedRowPositions.values().size() + rowOffset][];
int cellsIdx = 0;
while (cellsIdx < rowOffset) {
cells[cellsIdx++] = assembleColumnHeader(selectedColumnPositions, columnOffset, cellsIdx);
}
for (final IValueIterator rowIter = selectedRowPositions.values().iterator(); rowIter.hasNext(); ) {
final int rowPosition = rowIter.nextValue();
cells[cellsIdx++] = assembleBody(selectedColumnPositions, columnOffset, rowPosition);
}
return cells;
}
/**
* Collects and assembles the column header information for the specified column header row.
*
* If there is no column header configured an empty array with the matching dimensions will be returned.
*
* @param selectedColumnPositions The column positions of which the information should be collected
* @param columnOffset The column offset of table body
* @param headerRowPosition The row position in the column header of which the information should be collected
* @return An array containing the column header information
*/
protected ILayerCell[] assembleColumnHeader(final RangeList selectedColumnPositions, final int columnOffset,
final int headerRowPosition) {
final ILayerCell[] headerCells = new ILayerCell[selectedColumnPositions.values().size() + columnOffset];
int headerIdx = columnOffset;
if (columnHeaderDataLayer != null) {
for (final IValueIterator columnIter = selectedColumnPositions.values().iterator(); columnIter.hasNext(); headerIdx++) {
final int columnPosition = columnIter.nextValue();
headerCells[headerIdx] = columnHeaderDataLayer.getCellByPosition(columnPosition, headerRowPosition);
}
}
return headerCells;
}
/**
* Collects and assembles the selected data per row position that should be copied to the clipboard.
* If there is a row header layer configured for this handler, the row header cells of the selected
* row position are also added to the resulting array.
*
* @param selectedColumnPositions The column positions of which the information should be collected
* @param columnOffset The column offset of table body
* @param currentRowPosition The row position of which the selected cells should be collected
* @return An array containing the specified cells
*/
protected ILayerCell[] assembleBody(final RangeList selectedColumnPositions, final int columnOffset,
final int currentRowPosition) {
final ILayerCell[] bodyCells = new ILayerCell[selectedColumnPositions.values().size() + columnOffset];
int bodyIdx = 0;
if (rowHeaderDataLayer != null) {
for (; bodyIdx < columnOffset; bodyIdx++) {
bodyCells[bodyIdx] = rowHeaderDataLayer.getCellByPosition(bodyIdx, currentRowPosition);
}
}
for (final IValueIterator columnIter = selectedColumnPositions.values().iterator(); columnIter.hasNext(); bodyIdx++) {
final int columnPosition = columnIter.nextValue();
if (selectionLayer.isCellPositionSelected(columnPosition, currentRowPosition)) {
bodyCells[bodyIdx] = selectionLayer.getCellByPosition(columnPosition, currentRowPosition);
}
}
return bodyCells;
}
}