| /******************************************************************************* |
| * 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()); |
| } |
| } |