/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.diagram.ui.util;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.SWTGraphics;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.LayerConstants;
import org.eclipse.gef.editparts.LayerManager;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.teiid.designer.diagram.ui.DiagramUiConstants;
import org.teiid.designer.diagram.ui.DiagramUiPlugin;
import org.teiid.designer.diagram.ui.editor.DiagramEditor;
import org.teiid.designer.diagram.ui.model.DiagramModelNode;
import org.teiid.designer.diagram.ui.part.DiagramEditPart;
import org.teiid.designer.ui.IDiagramTypeEditPart;
/**
* @since 8.0
*/
public class SaveDiagramHelper implements DiagramUiConstants {
private static final String FILE_DIALOG_TITLE = "SaveDiagramDialog.title"; //$NON-NLS-1$
private static final long MAX_SAVEABLE_DIAGRAM_SIZE = 9000L * 9000L;
private static final int COLOR_DEPTH = 16; // 16 is plenty for our modest pallette of colors
/**
* @since 5.0
*/
public SaveDiagramHelper() {
super();
}
public void saveDiagramToFile( final DiagramEditor editor ) {
// System.out.println("\n\n[SaveDiagramHelper.saveDiagramToFile] TOP" );
if (editor != null) {
// testImageCreateSizes();
ImageLoader imageLoader = null;
GraphicalViewer viewer = editor.getDiagramViewer();
List lstEditParts = viewer.getSelectedEditParts();
if (lstEditParts.size() > 0 && selectionIsValid(lstEditParts)) {
imageLoader = createImageLoaderForSelection(editor, lstEditParts);
} else {
imageLoader = createImageLoaderForFullDiagram(editor);
}
if (imageLoader == null) {
return;
}
// One more time, free all the memory and handles you can...
System.gc();
saveThisImageFile(imageLoader);
}
}
private boolean checkDiagramSize( Rectangle rect ) {
// check for excessively large diagram regions
long lRectangleSize = rect.width * rect.height;
// System.out.println("[SaveDiagramHelper.checkDiagramSize] About to test rectangle, size is: " + lRectangleSize );
if (lRectangleSize > MAX_SAVEABLE_DIAGRAM_SIZE) {
// System.out.println("[SaveDiagramHelper.checkDiagramSize] Rectangle TOO LARGE TO SAVE: " + lRectangleSize );
String sMessage = Util.getString("SaveDiagramWarningDialog1.message", MAX_SAVEABLE_DIAGRAM_SIZE) //$NON-NLS-1$
+ Util.getString("SaveDiagramWarningDialog2.message", lRectangleSize); //$NON-NLS-1$
MessageDialog.openWarning(getShell(), Util.getString("SaveDiagramWarningDialog.title"), sMessage); //$NON-NLS-1$
return false;
}
return true;
}
protected Shell getShell() {
return DiagramUiPlugin.getDefault().getCurrentWorkbenchWindow().getShell();
}
private boolean selectionIsValid( List lstEditParts ) {
/*
* By default, the diagram edit part itself is selected when the diagram is first presented.
* A selection is only meaningful for our purpose if it is a classifier.
*/
if (lstEditParts.size() == 1) {
Object oEditPart = lstEditParts.iterator().next();
if (oEditPart instanceof IDiagramTypeEditPart) {
return false;
}
}
return true;
}
private void saveThisImageFile( ImageLoader imageLoader ) {
/*
I tried all of the following:
SWT.IMAGE_BMP;
SWT.IMAGE_GIF;
SWT.IMAGE_JPEG;
SWT.IMAGE_PNG;
GIF and PNG threw exceptions, so they are not supported.
bmp and jpg are supported, so I added bmp to the extensions filter.
*/
// create coordinated arrays for extension literals and save constants
String[] aryDisplayExtensions = new String[] {"*.jpg", "*.bmp"}; //$NON-NLS-1$ //$NON-NLS-2$
String[] aryExactExtensions = new String[] {".jpg", ".bmp"}; //$NON-NLS-1$ //$NON-NLS-2$
int[] aryConstants = new int[] {SWT.IMAGE_JPEG, SWT.IMAGE_BMP};
// Display file dialog for user to choose libraries
Shell shell = DiagramUiPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell();
final FileDialog dlg = new FileDialog(shell, SWT.SAVE | SWT.SINGLE);
dlg.setText(DiagramUiConstants.Util.getString(FILE_DIALOG_TITLE));
dlg.setFilterExtensions(aryDisplayExtensions);
// present the dialog
final String file = dlg.open();
if (file != null) {
String finalFileName = file;
int iSelectedExtension = -1;
for (int i = 0; i < aryExactExtensions.length; i++) {
if (file.endsWith(aryExactExtensions[i])) {
iSelectedExtension = i;
break;
}
}
// default to jpg
int iExtensionType = SWT.IMAGE_JPEG;
if (iSelectedExtension != -1) {
iExtensionType = aryConstants[iSelectedExtension];
}
// do the save
final String sFileName = finalFileName;
final int iSelectedExtensionType = iExtensionType;
final ImageLoader finalImageLoader = imageLoader;
final String sProgressMessage = Util.getString("SaveDiagramDialog.progressMessage"); //$NON-NLS-1$
if (finalImageLoader != null) {
// final String sProgressMessage = Util.getString( "SaveDiagramDialog.progressMessage" );
final IRunnableWithProgress op = new IRunnableWithProgress() {
@Override
public void run( final IProgressMonitor theMonitor ) {
theMonitor.beginTask(sProgressMessage, 100);
theMonitor.worked(50);
// first free all the memory and handles you can...
System.gc();
// System.out.println("[SaveDiagramHelper.saveDiagramToFile] About to save...imageLoader.logicalScreenWidth / imageLoader.logicalScreenHeight "
// + finalImageLoader.logicalScreenWidth + " / " + finalImageLoader.logicalScreenHeight );
finalImageLoader.save(sFileName, iSelectedExtensionType);
theMonitor.done();
}
};
try {
new ProgressMonitorDialog(Display.getCurrent().getActiveShell()).run(false, true, op);
} catch (InterruptedException e) {
} catch (InvocationTargetException e) {
DiagramUiConstants.Util.log(e.getTargetException());
} catch (Exception e) {
DiagramUiConstants.Util.log(e.getMessage());
}
}
}
}
private ImageLoader createImageLoaderForSelection( DiagramEditor editor,
List lstEditParts ) {
ImageLoader imageLoader = null;
GraphicalViewer viewer = editor.getDiagramViewer();
Rectangle rectSelection = createRectangleForSelectedEditParts(lstEditParts);
// System.out.println("[SaveDiagramHelper.createImageLoaderForSelection] Selection Rectangle: " + rectSelection );
LayerManager lm = (LayerManager)viewer.getEditPartRegistry().get(LayerManager.ID);
IFigure f = lm.getLayer(LayerConstants.PRINTABLE_LAYERS);
Rectangle rectFullDiagram = f.getBounds();
Display display = Display.getDefault();
// === 1. Create full sized startingImage
Image startingImage = null;
ImageData scaledIData = null;
// first free all the memory and handles you can...
System.gc();
// Fresh idea: Let's try making the dim of startImage JUST LARGE ENOUGH
// to start at 0, 0 and include the selection:
int iStartDiagramWidth = rectSelection.x + rectSelection.width;
int iStartDiagramHeight = rectSelection.y + rectSelection.height;
Rectangle rectSmallestRect = new Rectangle(0, 0, iStartDiagramWidth, iStartDiagramHeight);
// System.out.println("[SaveDiagramHelper.createImageLoaderForSelection] rectSmallestRect: (used to scaledTo) " +
// rectSmallestRect );
// check for oversize diagram and bail out
boolean bContinue = checkDiagramSize(rectSmallestRect);
if (!bContinue) {
return imageLoader;
}
try {
// the 'scaleTo' call is the workaround for Windows 16M image size limit problem
startingImage = new Image(display, 10, 10);
ImageData imageData = new ImageData(10, 10, COLOR_DEPTH, new PaletteData(0xFF, 0xFF00, 0xFF0000));
scaledIData = imageData.scaledTo(iStartDiagramWidth, iStartDiagramHeight);
startingImage = new Image(display, scaledIData);
} catch (Exception e) {
DiagramUiConstants.Util.log(e.getMessage());
}
// System.out.println("[SveDiagramHelper.createImageLoaderForSelection] startingImage.getImageData().depth = " +
// startingImage.getImageData().depth );
// System.out.println("[SveDiagramHelper.createImageLoaderForSelection] startingImage.getImageData().width = " +
// startingImage.getImageData().width );
// System.out.println("[SveDiagramHelper.createImageLoaderForSelection] startingImage.getImageData().height = " +
// startingImage.getImageData().height );
//
// long lFullSize = startingImage.getImageData().depth
// * startingImage.getImageData().width
// * startingImage.getImageData().height;
// System.out.println("[SveDiagramHelper.createImageLoaderForSelection] iFullSize = " + lFullSize );
// === 2. do copyArea to copy the selected portion of the workImage
// === to (0, 0) of the startingImage
GC gcStarting = new GC(startingImage);
SWTGraphics graphics = new SWTGraphics(gcStarting);
// System.out.println("[SaveDiagramHelper.createImageLoaderForSelection] About to call f.paint(graphics)" );
f.paint(graphics);
// defeat any existing clipping by setting clipping on this GC to the full size of the diagram
gcStarting.setClipping(rectFullDiagram.x, rectFullDiagram.y, rectFullDiagram.width, rectFullDiagram.height);
// now copy the selection rectangle to the upper left corner of the image
gcStarting.copyArea(rectSelection.x, rectSelection.y, rectSelection.width, rectSelection.height, 0, 0);
// create an empty image that is the size of just the part we want
Image finalImage = new Image(display, rectSelection.width, rectSelection.height);
// Use drawImage to copy just what we want into the finalImage
GC gcFinal = new GC(finalImage);
gcFinal.drawImage(startingImage,
0,
0,
rectSelection.width,
rectSelection.height,
0,
0,
rectSelection.width,
rectSelection.height);
// === 3. create imageLoader from the finalImage
imageLoader = new ImageLoader();
imageLoader.logicalScreenWidth = rectSelection.width;
imageLoader.logicalScreenHeight = rectSelection.height;
imageLoader.data = new ImageData[] {finalImage.getImageData()};
// clean up
gcStarting.dispose();
gcFinal.dispose();
graphics.dispose();
startingImage.dispose();
finalImage.dispose();
// System.out.println("[SaveDiagramHelper.createImageLoaderForSelection] About to save...imageLoader.logicalScreenWidth / imageLoader.logicalScreenHeight "
// + imageLoader.logicalScreenWidth + " / " + imageLoader.logicalScreenHeight );
return imageLoader;
}
private Rectangle createRectangleForSelectedEditParts( List lstEditParts ) {
/*
* Calculate the minimum rectangle that contains the selected edit parts
* 1. Walk the edit parts seeking:
* a) The smallest X
* b) The largest (x + its width)
* c) The smallest Y
* d) The largest (y + its height)
*/
int iSmallestX = -1;
int iLargestXPlusWidth = 0;
int iSmallestY = -1;
int iLargestYPlusHeight = 0;
int iX = 0;
int iWidth = 0;
int iXPlusWidth = 0;
int iY = 0;
int iHeight = 0;
int iYPlusHeight = 0;
Iterator it = lstEditParts.iterator();
while (it.hasNext()) {
EditPart ep = (EditPart)it.next();
if (ep instanceof DiagramEditPart) {
DiagramEditPart dep = (DiagramEditPart)ep;
DiagramModelNode dmNode = (DiagramModelNode)dep.getModel();
iX = dmNode.getX();
iWidth = dmNode.getWidth();
iXPlusWidth = iX + iWidth;
iY = dmNode.getY();
iHeight = dmNode.getHeight();
iYPlusHeight = iY + iHeight;
// if new values are better, swap them in
if (iSmallestX == -1 || iX < iSmallestX) {
iSmallestX = iX;
}
if (iXPlusWidth > iLargestXPlusWidth) {
iLargestXPlusWidth = iXPlusWidth;
}
if (iSmallestY == -1 || iY < iSmallestY) {
iSmallestY = iY;
}
if (iYPlusHeight > iLargestYPlusHeight) {
iLargestYPlusHeight = iYPlusHeight;
}
}
}
int iFullWidth = iLargestXPlusWidth - iSmallestX;
int iFullHeight = iLargestYPlusHeight - iSmallestY;
// create the rectangle that contains the selected edit parts
Rectangle rect = new Rectangle(iSmallestX, iSmallestY, iFullWidth, iFullHeight);
// System.out.println("[SaveDiagramHelper.createRectangleForSelectedEditParts] Rectangle: " + rect );
return rect;
}
private ImageLoader createImageLoaderForFullDiagram( DiagramEditor editor ) {
ImageLoader imageLoader = null;
Image finalImage = null;
GraphicalViewer viewer = editor.getDiagramViewer();
LayerManager lm = (LayerManager)viewer.getEditPartRegistry().get(LayerManager.ID);
IFigure f = lm.getLayer(LayerConstants.PRINTABLE_LAYERS);
Rectangle rectFullDiagram = f.getBounds();
// check for oversize diagram and bail out
boolean bContinue = checkDiagramSize(rectFullDiagram);
if (!bContinue) {
return imageLoader;
}
Display display = Display.getDefault();
// first free all the memory and handles you can...
System.gc();
// the 'scaleTo' call is the workaround for Windows 16M image size limit problem
ImageData imageData = new ImageData(10, 10, COLOR_DEPTH, new PaletteData(0xFF, 0xFF00, 0xFF0000));
ImageData scaledIData = imageData.scaledTo(rectFullDiagram.width, rectFullDiagram.height);
finalImage = new Image(display, scaledIData);
// System.out.println("[SveDiagramHelper.createImageLoaderForFullDiagram] startingImage.getImageData().depth = " +
// finalImage.getImageData().depth );
// System.out.println("[SveDiagramHelper.createImageLoaderForFullDiagram] startingImage.getImageData().width = " +
// finalImage.getImageData().width );
// System.out.println("[SveDiagramHelper.createImageLoaderForFullDiagram] startingImage.getImageData().height = " +
// finalImage.getImageData().height );
//
// long lFullSize = finalImage.getImageData().depth
// * finalImage.getImageData().width
// * finalImage.getImageData().height;
// System.out.println("[SveDiagramHelper.createImageLoaderForFullDiagram] iFullSize = " + lFullSize );
GC graphicContext = new GC(finalImage);
SWTGraphics graphics = new SWTGraphics(graphicContext);
// System.out.println("[SveDiagramHelper.createImageLoaderForFullDiagram]...About to call f.paint() " );
f.paint(graphics);
imageLoader = new ImageLoader();
imageLoader.data = new ImageData[] {finalImage.getImageData()};
imageLoader.logicalScreenHeight = rectFullDiagram.height;
imageLoader.logicalScreenWidth = rectFullDiagram.width;
// clean up
graphicContext.dispose();
graphics.dispose();
finalImage.dispose();
return imageLoader;
}
/*
* jh Case 4514 test
* This method can be used to find the largest Image that can be created before
* the OS returns 'NO HANDLE'.
*/
// public void testImageCreateSizes() {
//
// int TRIES = 100;
// Display display = Display.getDefault();
//
// Image startingImage = null;
// ImageData scaledIData = null;
// Image finalImage = null;
//
// int iStartWidth = 9000;
// int iStartHeight = 9000;
// int iWidth;
// int iHeight;
// int iFactor;
//
// for ( int i = 0; i < TRIES; i++ ) {
//
// if ( startingImage != null ) {
// startingImage.dispose();
// }
//
// if ( finalImage != null ) {
// finalImage.dispose();
// }
//
// startingImage = null;
// scaledIData = null;
// finalImage = null;
//
// System.gc();
//
// iFactor = (i + 1) * 10;
// iWidth = iStartWidth + iFactor;
// iHeight = iStartHeight + iFactor;
//
// System.out.println( "\n[SaveDiagramHelper.testImageCreateSizes] About to try... " );
// System.out.println( "\n[SaveDiagramHelper.testImageCreateSizes] iWidth: " + iWidth );
// System.out.println( "[SaveDiagramHelper.testImageCreateSizes] iHeight: " + iHeight );
//
// // the 'scaleTo' call is the workaround for Windows 16M image size limit problem
// startingImage = new Image( display, 10, 10 );
// scaledIData
// = startingImage.getImageData().scaledTo( iWidth, iHeight );
//
// finalImage = new Image( display, scaledIData );
// System.out.println( "[SaveDiagramHelper.testImageCreateSizes] SUCCESS! " );
// }
// }
}