/**************************************************************************************************
* Copyright (c) 2010 Fabian Steeg. 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
* <p/>
* Contributors: Fabian Steeg - initial API and implementation
*************************************************************************************************/
package de.uni_koeln.ub.drc.ui.views;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
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.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Scale;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.ISelectionService;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.part.ViewPart;
import de.uni_koeln.ub.drc.data.Box;
import de.uni_koeln.ub.drc.data.Index;
import de.uni_koeln.ub.drc.data.Page;
import de.uni_koeln.ub.drc.data.Word;
import de.uni_koeln.ub.drc.ui.DrcUiActivator;
import de.uni_koeln.ub.drc.ui.Messages;
/**
* View containing the scanned page used to check the original word while
* editing.
*
* @author Fabian Steeg (fsteeg), Mihail Atanassov (matana)
*/
public final class CheckView extends ViewPart {
/**
* The class / CheckView ID
*/
public static final String ID = CheckView.class.getName().toLowerCase();
private Composite parent;
private Canvas imageCanvas;
private boolean imageLoaded = false;
private ScrolledComposite scrolledComposite;
private ImageData imageData;
private Label suggestions;
private Job job;
private Button check;
private Text word;
private List<Button> suggestionButtons = new ArrayList<Button>();
private Composite bottom;
private Page page;
private Composite zoomBottom;
private Scale scale;
private float scaleFactor = 1;
private Image cachedImage;
private int originalHeight;
private int originalWidth;
@Override
public void setFocus() {
}
/**
* @param parent
* The parent composite for this part
*/
@Override
public void createPartControl(Composite parent) {
this.parent = parent;
scale();
scrolledComposite = new ScrolledComposite(parent, SWT.V_SCROLL
| SWT.H_SCROLL | SWT.BORDER);
imageCanvas = new Canvas(scrolledComposite, SWT.BORDER | SWT.CENTER);
scrolledComposite.setContent(imageCanvas);
scrolledComposite.setExpandVertical(true);
scrolledComposite.setExpandHorizontal(true);
addSuggestions();
attachSelectionListener();
imageCanvas.addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent e) {
if (word != null && !word.isDisposed()) {
markPosition(word, e.gc, wordFromWidget(word));
}
}
});
GridLayoutFactory.fillDefaults().generateLayout(parent);
}
private void attachSelectionListener() {
ISelectionService selectionService = (ISelectionService) getSite()
.getService(ISelectionService.class);
selectionService.addSelectionListener(new ISelectionListener() {
@Override
public void selectionChanged(IWorkbenchPart part,
ISelection selection) {
IStructuredSelection structuredSelection = (IStructuredSelection) selection;
if (structuredSelection.getFirstElement() instanceof Page) {
List<Page> pages = Arrays.asList((Page) structuredSelection
.getFirstElement());
setSelection(pages);
}
}
});
}
/**
* @param pages
* The selected pages
*/
public void setSelection(final List<Page> pages) {
if (pages != null && pages.size() > 0
&& (page == null || !page.equals(pages.get(0)))) {
page = pages.get(0);
try {
updateImage(page);
} catch (MalformedURLException e) {
handle(e);
} catch (IOException e) {
handle(e);
}
} else {
return;
}
}
/**
* @param text
* The active text widget
*/
public void setSelection(final Text text) {
Word word = null;
if (imageLoaded && text != null && !text.isDisposed()
&& (word = wordFromWidget(text)) != null) {
this.word = text;
imageCanvas.redraw();
scrollTo(word);
if (job != null) {
/*
* If a word is selected while we had a Job running for the
* previous word, cancel that:
*/
job.cancel();
}
if (!check.getSelection()) {
suggestions.setText(Messages.get().EditSuggestionsDisabled);
disposeButtons();
} else if (word.isLocked()) {
suggestions
.setText(Messages.get().NoEditSuggestionsWordIsLocked);
} else {
findEditSuggestions(word, text);
job.setPriority(Job.DECORATE);
job.schedule();
}
} else if (text == null) {
suggestions.setText(Messages.get().NoWordSelected);
}
}
private void scrollTo(Word word) {
Point p = newOrigin(word.position(), originalHeight, originalWidth,
imageCanvas.getBackgroundImage());
scrolledComposite.setOrigin(p);
}
private void scale() {
zoomBottom = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout(4, false);
zoomBottom.setLayout(layout);
Label zoomLabel = new Label(zoomBottom, SWT.NONE);
zoomLabel.setText(Messages.get().Zoom);
Label downScaleLabel = new Label(zoomBottom, SWT.NONE);
downScaleLabel.setText(Messages.get().Minus);
scale = new Scale(zoomBottom, SWT.NONE);
Label upScaleLabel = new Label(zoomBottom, SWT.NONE);
upScaleLabel.setText(Messages.get().Plus);
Rectangle clientArea = zoomBottom.getClientArea();
scale.setBounds(clientArea.x, clientArea.y, 200, 64);
scale.setOrientation(SWT.LEFT_TO_RIGHT);
scale.setMinimum(30);
scale.setMaximum(100);
scale.setSelection(100);
scale.setToolTipText(Messages.get().ZoomToolTip);
addListenerToScaleWidget();
}
private void addListenerToScaleWidget() {
scale.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
scaleFactor = scale.getSelection() / 100F;
scale.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
try {
updateImage(page);
} catch (IOException e) {
e.printStackTrace();
}
imageCanvas.redraw();
if (word != null && !word.isDisposed()) {
scrollTo(wordFromWidget(word));
}
}
});
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
}
private void addSuggestions() {
bottom = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout(7, false);
bottom.setLayout(layout);
check = new Button(bottom, SWT.CHECK);
check.setToolTipText(Messages.get().SuggestCorrections);
check.setSelection(false);
check.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
setSelection(word);
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
suggestions = new Label(bottom, SWT.WRAP);
suggestions.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
}
private void displaySuggestionButtons(final Word word, final Text text) {
bottom.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
String words = word.suggestions().mkString(" "); //$NON-NLS-1$
for (String string : words.split(" ")) { //$NON-NLS-1$
final Button b = new Button(bottom, SWT.FLAT);
b.setLayoutData(new GridData(SWT.NONE));
b.setText(string);
b.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
if (text != null) {
text.setText(b.getText());
text.setSelection(text.getCaretPosition());
text.setFocus();
}
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
suggestionButtons.add(b);
}
bottom.pack();
bottom.redraw();
}
});
}
private void disposeButtons() {
if (!suggestionButtons.isEmpty()) {
for (Button b : suggestionButtons) {
b.dispose();
}
}
suggestionButtons = new ArrayList<Button>();
}
private void drawBoxArea(final Rectangle rect, final GC gc) {
gc.setAlpha(50);
gc.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_GREEN));
gc.fillRectangle(rect);
}
private void drawBoxBorder(final Rectangle rect, final GC gc) {
gc.setAlpha(200);
gc.setLineWidth(2);
gc.setForeground(parent.getDisplay().getSystemColor(
SWT.COLOR_DARK_GREEN));
gc.drawRectangle(rect);
}
private void findEditSuggestions(final Word word, final Text text) {
disposeButtons();
suggestions.setText(Messages.get().FindingEditSuggestions);
job = new Job(Messages.get().EditSuggestionsSearchJob) {
@Override
protected IStatus run(final IProgressMonitor monitor) {
final boolean complete = word.prepSuggestions();
suggestions.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
if (!complete) {
suggestions.setText(Messages.get().FindingEditSuggestions);
} else {
String info = (word.suggestions().size() == 0) ? Messages
.get().NoReasonableEditSuggestionsFound
: String.format(
Messages.get().SuggestionsFor
+ " %s (" + Messages.get().Originally //$NON-NLS-1$
+ " '%s'):", word.history().top() //$NON-NLS-1$
.form(), word.original());
if (!bottom.isDisposed())
displaySuggestionButtons(word, text);
if (!suggestions.isDisposed())
suggestions.setText(info);
}
}
});
return Status.OK_STATUS;
}
@Override
protected void canceling() {
word.cancelled_$eq(true);
suggestions.setText(Messages.get().FindingEditSuggestions);
};
};
}
private void handle(Exception e) {
MessageDialog.openError(parent.getShell(),
Messages.get().CouldNotLoadScan,
Messages.get().CouldNotLoadImageForCurrentPage);
e.printStackTrace();
}
private Image loadImage(final Page page) throws IOException {
final InputStream in = getInputStream(page);
imageData = new ImageData(in);
Image newImage = new Image(parent.getDisplay(), imageData);
return newImage;
}
private void markPosition(final Text text, GC gc, Word word) {
if (imageCanvas.getBackgroundImage() != null) {
Box box = word.position();
Rectangle rect = getScaledRect(box);
drawBoxArea(rect, gc);
drawBoxBorder(rect, gc);
scrolledComposite.setMinSize(imageCanvas.getBackgroundImage()
.getBounds().width, imageCanvas.getBackgroundImage()
.getBounds().height);
scrolledComposite.layout(true, true);
Point p = newOrigin(box, originalHeight, originalWidth,
imageCanvas.getBackgroundImage());
scrolledComposite.setOrigin(p);
}
}
private void cacheImage(final Image img) {
if (cachedImage != null)
cachedImage.dispose();
cachedImage = new Image(parent.getDisplay(), img, SWT.IMAGE_COPY);
if (scaleFactor < 1) {
cachedImage = scaleImage(cachedImage);
}
imageCanvas.setBackgroundImage(cachedImage);
originalHeight = cachedImage.getBounds().height;
originalWidth = cachedImage.getBounds().width;
}
private Point newOrigin(Box box, int oldHeigt, int oldWidth, Image newImage) {
int height = newImage.getBounds().height;
int width = newImage.getBounds().width;
int x = ((oldWidth - width) / 2) + (int) (box.x() * scaleFactor);
int y = ((oldHeigt - height) / 2) + (int) (box.y() * scaleFactor);
return new Point(x - 25, y - 25);
}
private Image scaleImage(final Image image) {
Rectangle rect = image.getBounds();
ImageData data = image.getImageData().scaledTo(
(int) (rect.width * scaleFactor),
(int) (rect.height * scaleFactor));
image.dispose();
return new Image(parent.getDisplay(), data);
}
private Rectangle getScaledRect(Box box) {
int startX = (int) ((scaleFactor * box.x()) - (15 * scaleFactor));
int startY = (int) ((scaleFactor * box.y()) - (6 * scaleFactor));
int boxWidth = (int) ((scaleFactor * box.width()) + (25 * scaleFactor));
int boxHeight = (int) ((scaleFactor * box.height()) + (18 * scaleFactor));
return new Rectangle(startX, startY, boxWidth, boxHeight);
}
private void updateImage(final Page page) throws IOException {
if (imageCanvas != null && imageCanvas.getBackgroundImage() != null
&& !imageCanvas.getBackgroundImage().isDisposed())
imageCanvas.getBackgroundImage().dispose();
cacheImage(loadImage(page));
Image displayedImage = new Image(parent.getDisplay(),
cachedImage.getImageData());
imageData = displayedImage.getImageData();
if (imageCanvas != null)
imageCanvas.setBackgroundImage(displayedImage);
imageLoaded = true;
scrolledComposite.setMinSize(displayedImage.getBounds().width,
displayedImage.getBounds().height);
scrolledComposite.layout(true, true);
}
private InputStream getInputStream(final Page page) {
InputStream in = new BufferedInputStream(new ByteArrayInputStream(
Index.loadImageFor(DrcUiActivator.getDefault().currentUser()
.collection(), DrcUiActivator.getDefault().db(), page)));
return in;
}
private Word wordFromWidget(Text text) {
return (Word) text.getData(Word.class.toString());
}
}