/*******************************************************************************
* Copyright (c) 2012 OpenLegacy Inc.
* 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:
* OpenLegacy Inc. - initial API and implementation
*******************************************************************************/
package org.openlegacy.ide.eclipse.components;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.openlegacy.ide.eclipse.Messages;
import org.openlegacy.terminal.TerminalSnapshot;
import org.openlegacy.terminal.render.DefaultTerminalSnapshotImageRenderer;
import org.openlegacy.terminal.render.TerminalSnapshotImageRenderer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
public class SnapshotComposite extends Composite {
private enum RectangleDrawType {
FILL,
DRAW
}
private class RectangleDrawAction {
protected RectangleDrawType type;
protected Rectangle rectangle;
protected Color background;
protected Color foregraund;
public RectangleDrawAction(RectangleDrawType type, Rectangle rectangle, Color background, Color foregraund) {
this.type = type;
this.rectangle = rectangle;
this.background = background;
this.foregraund = foregraund;
}
}
private TerminalSnapshot terminalSnapshot;
private TerminalSnapshot terminalSnapshotCopy;
private int cursorRow = 1;
private int cursorCol = 1;
private int maxRowCount = 0;
private int maxColCount = 0;
private Image defaultImage = null;
private Label cursorLabel = null;
private Canvas canvas;
private boolean isScalable = false;
private double scale = 1.0d;
private RectangleDrawAction lastRectangleDrawAction = null;
private boolean showingEnlarged = false;
public SnapshotComposite(Composite parent) {
super(parent, SWT.NONE);
initialize();
}
public SnapshotComposite(Composite parent, TerminalSnapshot terminalSnapshot) {
super(parent, SWT.NONE);
this.terminalSnapshot = terminalSnapshot;
this.terminalSnapshotCopy = terminalSnapshot;
this.generateDefaultImage();
initialize();
}
private void initialize() {
GridData gd = new GridData(GridData.FILL_HORIZONTAL, GridData.FILL_VERTICAL, true, true);
gd.widthHint = 850;
gd.heightHint = 450;
this.setLayoutData(gd);
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 1;
this.setLayout(gridLayout);
gd = new GridData(GridData.FILL_HORIZONTAL, GridData.FILL_VERTICAL, true, true);
gd.widthHint = 825;
gd.heightHint = 400;
this.canvas = new Canvas(this, SWT.NONE);
this.canvas.setBackground(new Color(Display.getCurrent(), new RGB(0x00, 0x00, 0x00)));
this.canvas.setLayoutData(gd);
this.canvas.addPaintListener(this.getTerminalPaintListener());
DefaultTerminalSnapshotImageRenderer renderer = new DefaultTerminalSnapshotImageRenderer();
this.maxRowCount = renderer.getMaxImageRow() - 2;
this.maxColCount = renderer.getMaxImageColumn() - renderer.getLeftColumnsOffset();
this.canvas.addFocusListener(this.getFocusListener());
this.canvas.addListener(SWT.MouseDown, this.getMouseDownListener());
this.canvas.addKeyListener(this.getKeyListener());
GridData labelGd = new GridData();
labelGd.verticalAlignment = GridData.BEGINNING;
this.cursorLabel = new Label(this, SWT.NONE);
this.cursorLabel.setLayoutData(labelGd);
this.displayCursorPosition();
this.canvas.addPaintListener(this.getRectanglePaintListener());
this.canvas.addPaintListener(this.getCursorPaintListener());
}
public void setSnapshot(TerminalSnapshot terminalSnapshot) {
this.terminalSnapshot = terminalSnapshot;
if ((terminalSnapshot != null) && (!terminalSnapshot.equals(this.terminalSnapshotCopy))) {
this.terminalSnapshotCopy = terminalSnapshot;
this.generateDefaultImage();
}
if (terminalSnapshot != null) {
this.lastRectangleDrawAction = null;
}
this.canvas.redraw();
}
public Canvas getCanvas() {
return this.canvas;
}
public void setDrawingRectangle(Rectangle rectangle) {
if (rectangle == null) {
this.lastRectangleDrawAction = null;
if ((this.terminalSnapshot == null) || (!this.terminalSnapshot.equals(this.terminalSnapshotCopy))) {
this.setSnapshot(this.terminalSnapshotCopy);
}
return;
}
this.lastRectangleDrawAction = new RectangleDrawAction(RectangleDrawType.DRAW, rectangle, null,
getDisplay().getSystemColor(SWT.COLOR_YELLOW));
this.setSnapshot(null);
}
public void setIsScalable(boolean isScalable) {
this.isScalable = isScalable;
}
public void initDoubleClickEnlargeListener() {
this.canvas.addListener(SWT.MouseDoubleClick, this.getEnlargeListener());
}
private void generateDefaultImage() {
if (SnapshotComposite.this.terminalSnapshot == null) {
return;
}
TerminalSnapshotImageRenderer renderer = new DefaultTerminalSnapshotImageRenderer();
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
renderer.render(SnapshotComposite.this.terminalSnapshot, baos);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
SnapshotComposite.this.defaultImage = new Image(getShell().getDisplay(), bais);
}
private Listener getMouseDownListener() {
return new Listener() {
public void handleEvent(Event e) {
SnapshotComposite.this.displayCursorPosition();
SnapshotComposite.this.calcCursorRectangle();
SnapshotComposite.this.setSnapshot(null);
}
};
}
private FocusListener getFocusListener() {
return new FocusListener() {
public void focusLost(FocusEvent e) {
if (!SnapshotComposite.this.showingEnlarged) {
SnapshotComposite.this.lastRectangleDrawAction = null;
}
}
public void focusGained(FocusEvent e) {}
};
}
private KeyListener getKeyListener() {
return new KeyListener() {
public void keyReleased(KeyEvent e) {
return;
}
public void keyPressed(KeyEvent e) {
switch (e.keyCode) {
case SWT.ARROW_UP:
if (SnapshotComposite.this.cursorRow > 1) {
SnapshotComposite.this.cursorRow--;
}
break;
case SWT.ARROW_DOWN:
if (SnapshotComposite.this.cursorRow < SnapshotComposite.this.maxRowCount) {
SnapshotComposite.this.cursorRow++;
}
break;
case SWT.ARROW_LEFT:
if (SnapshotComposite.this.cursorCol > 1) {
SnapshotComposite.this.cursorCol--;
}
break;
case SWT.ARROW_RIGHT:
if (SnapshotComposite.this.cursorCol < SnapshotComposite.this.maxColCount) {
SnapshotComposite.this.cursorCol++;
}
break;
case SWT.HOME:
if (SnapshotComposite.this.cursorCol == 1) {
return;
}
SnapshotComposite.this.cursorCol = 1;
break;
case SWT.END:
if (SnapshotComposite.this.cursorCol == SnapshotComposite.this.maxColCount) {
return;
}
SnapshotComposite.this.cursorCol = SnapshotComposite.this.maxColCount;
break;
default:
return;
}
SnapshotComposite.this.calcCursorRectangle();
SnapshotComposite.this.displayCursorPosition();
SnapshotComposite.this.setSnapshot(null);
}
};
}
private void displayCursorPosition() {
if (this.cursorLabel != null) {
this.cursorLabel.setText(Messages.label_col_row + ": " + this.cursorRow + " " + Messages.label_col_column + ": "
+ this.cursorCol);
this.cursorLabel.pack(true);
}
}
private void calcCursorRectangle() {
DefaultTerminalSnapshotImageRenderer renderer = new DefaultTerminalSnapshotImageRenderer();
int x = renderer.toWidth(SnapshotComposite.this.cursorCol - 1 + renderer.getLeftColumnsOffset());
int y = renderer.toHeight(SnapshotComposite.this.cursorRow) + renderer.getTopPixelsOffset();
int width = renderer.toWidth(1);
int height = 3;
this.lastRectangleDrawAction = new RectangleDrawAction(RectangleDrawType.FILL, new Rectangle(x, y, width, height),
getDisplay().getSystemColor(SWT.COLOR_WHITE), null);
}
private PaintListener getCursorPaintListener() {
return new PaintListener() {
public void paintControl(PaintEvent e) {
if ((SnapshotComposite.this.lastRectangleDrawAction == null)
|| (SnapshotComposite.this.lastRectangleDrawAction.type.equals(RectangleDrawType.DRAW))) {
return;
}
if (SnapshotComposite.this.defaultImage != null) {
drawImage(SnapshotComposite.this.defaultImage, e.gc);
}
e.gc.setBackground(SnapshotComposite.this.lastRectangleDrawAction.background);
fillRectangle(SnapshotComposite.this.lastRectangleDrawAction.rectangle, e.gc);
}
};
}
private PaintListener getTerminalPaintListener() {
return new PaintListener() {
public void paintControl(PaintEvent e) {
drawImage(SnapshotComposite.this.defaultImage, e.gc);
// SnapshotComposite.this.defaultImage = image;
}
};
}
private PaintListener getRectanglePaintListener() {
return new PaintListener() {
public void paintControl(PaintEvent e) {
if ((SnapshotComposite.this.lastRectangleDrawAction == null)
|| (SnapshotComposite.this.lastRectangleDrawAction.type.equals(RectangleDrawType.FILL))) {
return;
}
if (SnapshotComposite.this.defaultImage != null) {
drawImage(SnapshotComposite.this.defaultImage, e.gc);
}
e.gc.setForeground(SnapshotComposite.this.lastRectangleDrawAction.foregraund);
drawRectangle(SnapshotComposite.this.lastRectangleDrawAction.rectangle, e.gc);
}
};
}
private void drawImage(Image image, GC gc) {
if (image == null) {
return;
}
Rectangle imageBounds = image.getBounds();
Rectangle drawingBounds = getDrawingBounds(image.getBounds(), true);
gc.drawImage(image, 0, 0, imageBounds.width, imageBounds.height, drawingBounds.x, drawingBounds.y, drawingBounds.width,
drawingBounds.height);
}
private void drawRectangle(Rectangle rectangle, GC gc) {
if (rectangle == null) {
return;
}
Rectangle drawingBounds = getDrawingBounds(rectangle, false);
gc.drawRectangle(drawingBounds);
}
private void fillRectangle(Rectangle rectangle, GC gc) {
if (rectangle == null) {
return;
}
Rectangle drawingBounds = getDrawingBounds(rectangle, false);
gc.fillRectangle(drawingBounds);
}
private Rectangle getDrawingBounds(Rectangle rect, boolean calcScale) {
if (!isScalable) {
return rect;
}
Rectangle canvasBounds = this.canvas.getBounds();
if (calcScale) {
double hScale = (double)canvasBounds.width / rect.width;
double vScale = (double)canvasBounds.height / rect.height;
scale = Math.min(1.0d, Math.min(hScale, vScale));
}
int width = (int)(rect.width * scale);
int height = (int)(rect.height * scale);
int x = (int)(rect.x * scale);
int y = (int)(rect.y * scale);
return new Rectangle(x, y, width, height);
}
private Listener getEnlargeListener() {
return new Listener() {
public void handleEvent(Event e) {
if (!isVisible()) {
return;
}
SnapshotComposite.this.showEnlargedImage();
}
};
}
private void showEnlargedImage() {
if (this.defaultImage == null) {
return;
}
this.showingEnlarged = true;
final Rectangle imageBounds = this.defaultImage.getBounds();
Rectangle canvasBounds = this.canvas.getBounds();
// Don't bother if the image is smaller than the canvas.
if (imageBounds.width < canvasBounds.width & imageBounds.height < canvasBounds.height) {
return;
}
Rectangle displayBounds = getDisplay().getBounds();
int x = (canvasBounds.width - imageBounds.width) / 2;
int y = (canvasBounds.height - imageBounds.height) / 2;
Point where = toDisplay(new Point(x, y));
// calculate new bounds
x = Math.max(0, Math.min(where.x, displayBounds.width - imageBounds.width));
y = Math.max(0, Math.min(where.y, displayBounds.height - imageBounds.height));
final Shell shell = new Shell(getShell(), SWT.NO_TRIM);
shell.setBounds(x - 1, y - 1, imageBounds.width + 2, imageBounds.height + 2);
shell.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
if (isVisible()) {
e.gc.drawImage(SnapshotComposite.this.defaultImage, 1, 1);
e.gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_BLACK));
e.gc.drawRectangle(0, 0, imageBounds.width + 1, imageBounds.height + 1);
if (SnapshotComposite.this.lastRectangleDrawAction != null) {
switch (SnapshotComposite.this.lastRectangleDrawAction.type) {
case DRAW:
e.gc.setForeground(SnapshotComposite.this.lastRectangleDrawAction.foregraund);
e.gc.drawRectangle(SnapshotComposite.this.lastRectangleDrawAction.rectangle);
break;
case FILL:
e.gc.setBackground(SnapshotComposite.this.lastRectangleDrawAction.background);
e.gc.fillRectangle(SnapshotComposite.this.lastRectangleDrawAction.rectangle);
break;
}
}
}
}
});
// minimize image on mouse double clicked
shell.addListener(SWT.MouseDoubleClick, getShellMouseListener(shell));
// minimize image when mouse leaves image bounds
shell.addListener(SWT.MouseExit, getShellMouseListener(shell));
shell.open();
}
private Listener getShellMouseListener(final Shell shell) {
return new Listener() {
public void handleEvent(Event e) {
if (shell.isVisible()) {
shell.close();
shell.dispose();
SnapshotComposite.this.showingEnlarged = false;
SnapshotComposite.this.canvas.redraw();
}
}
};
}
}