blob: d223796545e16303fd36e57625e54eb5aeeb5422 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 Dirk Fauth.
* 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:
* Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation
******************************************************************************/
package org.eclipse.nebula.widgets.nattable.resize;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.ILayerListener;
import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;
import org.eclipse.nebula.widgets.nattable.print.command.PrintEntireGridCommand;
import org.eclipse.nebula.widgets.nattable.print.command.TurnViewportOffCommand;
import org.eclipse.nebula.widgets.nattable.print.command.TurnViewportOnCommand;
import org.eclipse.nebula.widgets.nattable.resize.event.ColumnResizeEvent;
import org.eclipse.nebula.widgets.nattable.resize.event.RowResizeEvent;
import org.eclipse.nebula.widgets.nattable.util.IClientAreaProvider;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
/**
* Helper class that renders a {@link ILayer} in-memory to trigger auto-resizing
* of rows and columns in case content painters are configured to calculate the
* necessary dimensions.
* <p>
* Note that this operation is expensive in terms of memory consumption and
* processing time. Be careful when using this helper for huge tables.
* </p>
*
* @since 1.4
*/
public class AutoResizeHelper {
/**
* The {@link ILayer} that should be used for in-memory rendering to trigger
* auto-resizing.
*/
protected final ILayer layer;
/**
* The {@link IConfigRegistry} needed for rendering.
*/
protected final IConfigRegistry configRegistry;
/**
* The total area needed to render the whole layer at once.
*/
protected Rectangle totalArea;
/**
* The original {@link IClientAreaProvider} needed to restore the original
* state after processing.
*/
protected IClientAreaProvider originalClientAreaProvider;
/**
* Flag to indicate that an automatic resize was triggered on rendering.
*/
protected boolean resizedOnPrinting = true;
/**
* {@link ILayerListener} that is added to the {@link ILayer} to get
* informed about {@link RowResizeEvent} and {@link ColumnResizeEvent} to
* know if an automatic resize was triggered on rendering.
*/
protected ILayerListener resizeListener = new ILayerListener() {
@Override
public void handleLayerEvent(ILayerEvent event) {
if (!AutoResizeHelper.this.resizedOnPrinting &&
(event instanceof RowResizeEvent || event instanceof ColumnResizeEvent)) {
AutoResizeHelper.this.resizedOnPrinting = true;
}
}
};
/**
* The {@link IClientAreaProvider} that is used for rendering the whole
* layer in-memory.
*/
protected IClientAreaProvider clientAreaProvider = new IClientAreaProvider() {
@Override
public Rectangle getClientArea() {
return AutoResizeHelper.this.totalArea;
}
};
/**
*
* @param layer
* The {@link ILayer} that should be used for in-memory rendering
* to trigger auto-resizing.
* @param configRegistry
* The {@link IConfigRegistry} needed for rendering.
*/
private AutoResizeHelper(ILayer layer, IConfigRegistry configRegistry) {
this.layer = layer;
this.configRegistry = configRegistry;
this.originalClientAreaProvider = layer.getClientAreaProvider();
calculateTotalArea();
}
/**
* Executes in-memory rendering of the given {@link ILayer} to trigger
* content based auto-resizing.
*
* @param layer
* The {@link ILayer} that should be used for in-memory rendering
* to trigger auto-resizing.
* @param configRegistry
* The {@link IConfigRegistry} needed for rendering.
*/
public static void autoResize(ILayer layer, IConfigRegistry configRegistry) {
AutoResizeHelper helper = new AutoResizeHelper(layer, configRegistry);
helper.init();
try {
// as long as resize events were triggered on rendering, we paint
// in-memory again to ensure everything was at least rendered once
// and resized correctly
while (helper.resizedOnPrinting) {
helper.resizedOnPrinting = false;
helper.calculateTotalArea();
helper.paintInMemory();
}
} finally {
helper.restore();
}
}
/**
* Paints the layer on a temporary image GC. If painters are configured for
* automatic size calculation, this painting will trigger the resize events.
*/
protected void paintInMemory() {
Image tmpImage = new Image(Display.getDefault(), this.totalArea.width, this.totalArea.height);
GC tempGC = new GC(tmpImage);
try {
// render the layer on the temporary GC
paintLayer(tempGC, this.totalArea);
} finally {
// ensure the temporary created resources are disposed after
// processing
tempGC.dispose();
tmpImage.dispose();
}
}
/**
* Print the part of the layer that matches the given print bounds.
*
* @param gc
* The print GC to render the layer to.
* @param printBounds
* The bounds of the print page.
*/
protected void paintLayer(GC gc, Rectangle printBounds) {
this.layer.getLayerPainter().paintLayer(
this.layer, gc, 0, 0, printBounds, this.configRegistry);
}
/**
* Calculate the total area needed to render the whole layer.
*/
protected void calculateTotalArea() {
this.totalArea = new Rectangle(0, 0, this.layer.getWidth(), this.layer.getHeight());
}
/**
* Prepare the layer for complete in-memory rendering.
*/
protected void init() {
this.layer.addLayerListener(this.resizeListener);
this.layer.setClientAreaProvider(this.clientAreaProvider);
this.layer.doCommand(new TurnViewportOffCommand());
this.layer.doCommand(new PrintEntireGridCommand());
}
/**
* Restore the original state of the layer before in-memory rendering
* preparations.
*/
protected void restore() {
this.layer.removeLayerListener(this.resizeListener);
this.layer.setClientAreaProvider(this.originalClientAreaProvider);
this.layer.doCommand(new TurnViewportOnCommand());
}
}