/**
* FyLLGen - A Java based tool for collecting and distributing family data
*
* Copyright (C) 2007-2011 Christian Packenius
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.chris_soft.fyllgen.widget.dialog;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseWheelListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.events.ShellListener;
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.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import de.chris_soft.fyllgen.GUI;
import de.chris_soft.fyllgen.Statics;
import de.chris_soft.fyllgen.utilities.SwtConsts;
import de.chris_soft.fyllgen.utilities.SwtUtilities;
/**
* Zurechtschneiden eines Images, damit es als Portrait eingebunden werden kann.
* @author Christian Packenius, 20110905.
*/
public class ImageCutDialog extends Dialog implements SelectionListener, PaintListener, ShellListener,
MouseWheelListener, MouseListener, MouseMoveListener {
/**
* Das eigentliche Fenster-Objekt.
*/
private Shell shell;
/**
* Button, um eine angew�hlte Person zu verwenden.
*/
private Button buttonUse;
/**
* Canvas, in dem eventuell ein Image angezeigt wird.
*/
private Canvas canvasImage;
/**
* Zu beschneidendes Image.
*/
private File image2view;
/**
* Original-Image.
*/
private Image image;
/**
* Name des beschnittenen Bildes.
*/
public String newImageName = null;
/**
* Gr��e des Canvas mit dem Image - wird beim �ffnen gesetzt.
*/
private Rectangle canvasSize;
/**
* Faktor, mit dem die Imagegr��e vor der Anzeige multipliziert wird.
*/
private double imageFactor;
/**
* X-Koordinate f�r die Darstellung.
*/
private int imageX;
/**
* Y-Koordinate f�r die Darstellung.
*/
private int imageY;
/**
* Gr��e des Images.
*/
private Rectangle imageBounds;
/**
* Verschiedene Koordinaten.
*/
private int mouseX = -1, mouseY = -1, imageOldX, imageOldY;
/**
* Verschiedene Rechtecksangaben.
*/
private Rectangle portraitBounds, portraitBounds1, portraitBounds2;
/**
* Gr��e des vergr��erten/verkleinerten Images.
*/
private int resizedWidth, resizedHeight;
/**
* Breite der erzeugten Portraitfotos.
*/
private int portraitWidth = 97;
/**
* H�he der erzeugten Portraitfotos.
*/
private int portraitHeight = 128;
/**
* Angabe, ob das Portrait in Graustufen umgewandelt werden soll.
*/
private boolean intoGray = true;
/**
* Konstruktor. Erzeugt das Fenster, ohne es anzuzeigen.
* @param parent Parent-Shell, �ber der dieser Dialog angezeigt wird.
* @param imageFile Image-Datei, die zu editieren ist.
* @throws IOException Probleme beim Laden des Images.
*/
public ImageCutDialog(Shell parent, File imageFile) throws IOException {
super(parent, 0);
image2view = imageFile;
FileInputStream in = new FileInputStream(image2view);
image = new Image(SwtConsts.display, new ImageData(in));
in.close();
shell = new Shell(parent, SWT.CLOSE | SWT.TITLE | SWT.APPLICATION_MODAL);
shell.setImage(GUI.instance.shellIcon);
shell.addShellListener(this);
shell.setText("Portrait festlegen");
shell.setSize(700, 500);
shell.addMouseWheelListener(this);
// Noch mittig ins vorhandene Fenster setzen.
SwtUtilities.centerInParent(shell, parent);
// Shell hat innen einen Rahmen.
FormLayout shellLayout = new FormLayout();
shellLayout.marginBottom = shellLayout.marginLeft = shellLayout.marginRight = shellLayout.marginTop = 5;
shell.setLayout(shellLayout);
// Innen ein Composite.
Composite inner = new Composite(shell, 0);
FormLayout innerLayout = new FormLayout();
inner.setLayout(innerLayout);
FormData fd = new FormData();
fd.top = new FormAttachment(0, 0);
fd.left = new FormAttachment(0, 0);
fd.right = new FormAttachment(100, 0);
fd.bottom = new FormAttachment(100, 0);
inner.setLayoutData(fd);
// Button zum Verwenden der in der Liste angeklickten Person erzeugen.
buttonUse = new Button(inner, SWT.PUSH);
buttonUse.setText("�bernehmen");
fd = new FormData();
fd.left = new FormAttachment(0, 0);
fd.right = new FormAttachment(100, 0);
fd.bottom = new FormAttachment(100, 0);
buttonUse.setLayoutData(fd);
buttonUse.addSelectionListener(this);
buttonUse.setEnabled(true);
buttonUse.addMouseWheelListener(this);
// Darstellung eines Images.
canvasImage = new Canvas(inner, 0);
fd = new FormData();
fd.top = new FormAttachment(0, 0);
fd.right = new FormAttachment(100, 0);
fd.bottom = new FormAttachment(buttonUse, -5, SWT.TOP);
fd.left = new FormAttachment(0, 0);
canvasImage.setLayoutData(fd);
canvasImage.addPaintListener(this);
canvasImage.addMouseWheelListener(this);
canvasImage.addMouseListener(this);
canvasImage.addMouseMoveListener(this);
}
/**
* Shell sichtbar machen. Vorher aber alle Personen in die Liste einf�gen.
*/
public void open() {
portraitBounds2 = portraitBounds1 = portraitBounds = imageBounds = canvasSize = new Rectangle(0, 0, 2, 2);
shell.open();
canvasSize = canvasImage.getBounds();
portraitBounds = new Rectangle(canvasSize.width / 2 - portraitWidth / 2,
canvasSize.height / 2 - portraitHeight / 2, portraitWidth, portraitHeight);
portraitBounds1 = new Rectangle(portraitBounds.x - 1, portraitBounds.y - 1, portraitBounds.width + 2,
portraitBounds.height + 2);
portraitBounds2 = new Rectangle(portraitBounds1.x - 1, portraitBounds1.y - 1, portraitBounds1.width + 2,
portraitBounds1.height + 2);
initImageSize();
canvasImage.redraw();
// Ereignis-Liste abarbeiten.
while (!shell.isDisposed()) {
if (!SwtConsts.display.readAndDispatch()) {
SwtConsts.display.sleep();
}
}
}
private void initImageSize() {
imageBounds = image.getBounds();
imageFactor = 1;// Math.max(fWidth, fHeight);
imageX = (canvasSize.width - imageBounds.width) / 2;
imageY = (canvasSize.height - imageBounds.height) / 2;
resizedWidth = imageBounds.width;
resizedHeight = imageBounds.height;
}
/**
* @see org.eclipse.swt.events.ShellListener#shellActivated(org.eclipse.swt.events.ShellEvent)
*/
public void shellActivated(ShellEvent e) {
// Ignorieren.
}
/**
* @see org.eclipse.swt.events.ShellListener#shellClosed(org.eclipse.swt.events.ShellEvent)
*/
public void shellClosed(ShellEvent e) {
shell.removeShellListener(this);
image.dispose();
}
/**
* @see org.eclipse.swt.events.ShellListener#shellDeactivated(org.eclipse.swt.events.ShellEvent)
*/
public void shellDeactivated(ShellEvent e) {
// Ignorieren.
}
/**
* @see org.eclipse.swt.events.ShellListener#shellDeiconified(org.eclipse.swt.events.ShellEvent)
*/
public void shellDeiconified(ShellEvent e) {
// Ignorieren.
}
/**
* @see org.eclipse.swt.events.ShellListener#shellIconified(org.eclipse.swt.events.ShellEvent)
*/
public void shellIconified(ShellEvent e) {
// Ignorieren.
}
/**
* Ein Button wurde gedr�ckt.
* @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetDefaultSelected(SelectionEvent e) {
testButtonsPressed(e);
}
/**
* Ein Button wurde gedr�ckt.
* @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetSelected(SelectionEvent e) {
testButtonsPressed(e);
}
/**
* Pr�ft, ob einer der Buttons gedr�ckt wurde.
*/
private void testButtonsPressed(SelectionEvent e) {
if (e.widget == buttonUse) {
Image helperImage = new Image(SwtConsts.display, portraitWidth, portraitHeight);
GC gc = new GC(helperImage);
gc.drawImage(image, 0, 0, imageBounds.width, imageBounds.height, imageX - portraitBounds.x, imageY
- portraitBounds.y, resizedWidth, resizedHeight);
gc.dispose();
Image grayImage = null;
if (intoGray) {
grayImage = new Image(SwtConsts.display, helperImage, SWT.IMAGE_GRAY);
helperImage.dispose();
}
else {
grayImage = helperImage;
}
ImageLoader io = new ImageLoader();
io.data = new ImageData[] { grayImage.getImageData() };
newImageName = Statics.getUniqueID() + ".png";
new File("images").mkdir();
io.save("images/" + newImageName, SWT.IMAGE_PNG);
grayImage.dispose();
shell.dispose();
}
}
/**
* Das Bild soll neu gezeichnet werden.
* @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
*/
public void paintControl(PaintEvent e) {
// Das eigentliche Bild vergr��ert/verkleinert und verschoben darstellen.
e.gc.drawImage(image, 0, 0, imageBounds.width, imageBounds.height, imageX, imageY, resizedWidth, resizedHeight);
// Schwarz/wei�-Rahmen f�r das Grau-Portrait.
e.gc.setForeground(SwtConsts.COLOR_WHITE);
e.gc.drawRectangle(portraitBounds1);
e.gc.setForeground(SwtConsts.COLOR_BLACK);
e.gc.drawRectangle(portraitBounds2);
// Den Mittelteil als Portrait grau darstellen.
Image helperImage = new Image(SwtConsts.display, portraitWidth, portraitHeight);
GC gc = new GC(helperImage);
gc.drawImage(image, 0, 0, imageBounds.width, imageBounds.height, imageX - portraitBounds.x, imageY
- portraitBounds.y, resizedWidth, resizedHeight);
gc.dispose();
Image grayImage = null;
if (intoGray) {
grayImage = new Image(SwtConsts.display, helperImage, SWT.IMAGE_GRAY);
helperImage.dispose();
}
else {
grayImage = helperImage;
}
e.gc.drawImage(grayImage, portraitBounds.x, portraitBounds.y);
grayImage.dispose();
}
/**
* Scrollen des Mausrades f�hrt zu einer Vergr��erung/Verkleinerung des
* Bildes. Der Zoom wird gleichzeitig derart verschoben, dass das Zentrum des
* Bildes bestehen bleibt.
* @see org.eclipse.swt.events.MouseWheelListener#mouseScrolled(org.eclipse.swt.events.MouseEvent)
*/
public void mouseScrolled(MouseEvent e) {
double percentX = ((double) canvasSize.width / 2 - imageX) / resizedWidth;
double percentY = ((double) canvasSize.height / 2 - imageY) / resizedHeight;
if (e.count > 0) {
imageFactor *= 1.05;
}
else if (e.count < 0) {
imageFactor /= 1.05;
}
resizedWidth = (int) (imageBounds.width * imageFactor + .5);
resizedHeight = (int) (imageBounds.height * imageFactor + .5);
imageX = (int) ((double) canvasSize.width / 2 - percentX * resizedWidth);
imageY = (int) ((double) canvasSize.height / 2 - percentY * resizedHeight);
canvasImage.redraw();
}
/**
* Doppelklick bleibt ungenutzt.
* @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
*/
public void mouseDoubleClick(MouseEvent e) {
// Methodenaufruf wird ignoriert.
}
/**
* Das Bild soll verschoben werden. Dies wird hier initiiert.
* @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
*/
public void mouseDown(MouseEvent e) {
mouseX = e.x;
mouseY = e.y;
imageOldX = imageX;
imageOldY = imageY;
}
/**
* Das Bild wurde zu Ende verschoben.
* @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
*/
public void mouseUp(MouseEvent e) {
mouseX = -1;
mouseY = -1;
}
/**
* Wenn die Maus bei gedr�ckter Maustaste bewegt wird, verschiebt sich das
* Bild.
* @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
*/
public void mouseMove(MouseEvent e) {
if (mouseX >= 0) {
imageX = imageOldX + e.x - mouseX;
imageY = imageOldY + e.y - mouseY;
canvasImage.redraw();
}
}
/**
* Dieser Dialog kann auch als eigenst�ndige Applikation gestartet werden.
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
Display display = Display.getDefault();
Shell shell = new Shell(display);
// Properties-File einlesen.
Properties props = new Properties();
props.setProperty("portraitX", "97");
props.setProperty("portraitY", "128");
int width = 97;
int height = 128;
boolean intoGray = true;
try {
FileInputStream in = new FileInputStream("size.properties");
props.load(in);
in.close();
width = Integer.parseInt(props.getProperty("portraitX"));
height = Integer.parseInt(props.getProperty("portraitY"));
intoGray = props.getProperty("intoGray").equals("true");
}
catch (Exception e) {
// Ignorieren.
}
// Alle Bilder im aktuellen Verzeichnis finden.
for (File file : new File(".").listFiles()) {
String filename = file.getName();
if (filename.toLowerCase().endsWith(".jpg") || filename.toLowerCase().endsWith(".png")) {
ImageCutDialog icd = null;
do {
icd = new ImageCutDialog(shell, file);
icd.portraitWidth = width;
icd.portraitHeight = height;
icd.intoGray = intoGray;
icd.newImageName = null;
icd.open();
} while (icd.newImageName != null);
}
}
shell.dispose();
display.dispose();
}
}