package at.laborg.briss.gui;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import at.laborg.briss.model.CropFinder;
import at.laborg.briss.model.PageCluster;
import at.laborg.briss.BrissGUI;
@SuppressWarnings("serial")
private static DrawableCropRect curCrop;
private static Point lastDragPoint;
private static Point cropStartPoint;
private static Point popUpMenuPoint;
private static Point relativeHotCornerGrabDistance;
private static ActionState actionState = ActionState.NOTHING;
private final static int SELECT_BORDER_WIDTH = 1;
private final static Font BASE_FONT = new Font(null, Font.PLAIN, 10);
private final static Composite SMOOTH_NORMAL = AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, .2f);
private final static Composite SMOOTH_SELECT = AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, .5f);
private final static Composite XOR_COMPOSITE = AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, .8f);
private final static float[] DASH_PATTERN = { 25f, 25f };
private final static BasicStroke SELECTED_STROKE = new BasicStroke(
SELECT_BORDER_WIDTH, BasicStroke.CAP_SQUARE,
BasicStroke.JOIN_BEVEL, 1.0f, DASH_PATTERN, 0f);
private final PageCluster cluster;
private final List<DrawableCropRect> crops = new ArrayList<DrawableCropRect>();
private final BufferedImage img;
private enum ActionState {
NOTHING, DRAWING_NEW_CROP, RESIZING_HOTCORNER_UL, RESIZING_HOTCORNER_LR, MOVE_CROP
}
private final BrissGUI briss;
public MergedPanel(PageCluster cluster, BrissGUI briss) {
super();
this.briss = briss;
this.cluster = cluster;
this.img = cluster.getImageData().getPreviewImage();
Float[] autoRatios = CropFinder.getAutoCropFloats(img);
cluster.addRatios(autoRatios);
setPreferredSize(new Dimension(img.getWidth(), img.getHeight()));
setSize(new Dimension(img.getWidth(), img.getHeight()));
if (cluster.getImageData().isRenderable()) {
MergedPanelMouseAdapter mouseAdapter = new MergedPanelMouseAdapter();
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
}
addRatiosAsCrops(cluster.getRatiosList());
setToolTipText(createInfoString(cluster));
addKeyListener(new MergedPanelKeyAdapter());
setFocusable(true);
repaint();
}
for (Float[] ratios : cluster.getRatiosList()) {
DrawableCropRect rect = new DrawableCropRect();
rect.x = (int) (img.getWidth() * ratios[0]);
rect.y = (int) (img.getHeight() * ratios[3]);
rect.width = (int) (img.getWidth() * (1 - (ratios[0] + ratios[2])));
rect.height = (int) (img.getHeight() * (1 - (ratios[1] + ratios[3])));
crops.add(rect);
}
}
StringBuilder sb = new StringBuilder();
sb.append("<html>");
sb.append(cluster.isEvenPage() ? "Even " : "Odd ").append("page<br>");
sb.append(cluster.getAllPages().size() + " pages: ");
int pagecounter = 0;
for (Integer pageNumber : cluster.getAllPages()) {
sb.append(pageNumber + " ");
if (pagecounter++ > 10) {
pagecounter = 0;
sb.append("<br>");
}
}
sb.append("</html>");
return sb.toString();
}
@Override
public void paint(Graphics g) {
update(g);
}
@Override
public void update(Graphics g) {
if (!isEnabled())
return;
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(img, null, 0, 0);
int cropCnt = 0;
for (DrawableCropRect crop : crops) {
drawNormalCropRectangle(g2, cropCnt, crop);
if (crop.isSelected()) {
drawSelectionOverlay(g2, crop);
}
cropCnt++;
}
g2.dispose();
}
DrawableCropRect crop) {
g2.setComposite(SMOOTH_NORMAL);
g2.setColor(Color.BLUE);
g2.fill(crop);
g2.setColor(Color.BLACK);
g2.setFont(scaleFont(String.valueOf(cropCnt + 1), crop));
g2.drawString(String.valueOf(cropCnt + 1), crop.x, crop.y + crop.height);
int cD = DrawableCropRect.CORNER_DIMENSION;
g2.fillRect(crop.x, crop.y, cD, cD);
g2.fillRect(crop.x + crop.width - cD - 1,
crop.y + crop.height - cD - 1, cD, cD);
}
g2.setComposite(XOR_COMPOSITE);
g2.setColor(Color.BLACK);
g2.setStroke(SELECTED_STROKE);
g2.drawRect(crop.x + SELECT_BORDER_WIDTH / 2, crop.y
+ SELECT_BORDER_WIDTH / 2, crop.width - SELECT_BORDER_WIDTH,
crop.height - SELECT_BORDER_WIDTH);
int w = Math.round(25.4f * crop.width / 72f);
int h = Math.round(25.4f * crop.height / 72f);
String size = Integer.toString(w) + "x" + Integer.toString(h);
g2.setFont(scaleFont(size, crop));
g2.setColor(Color.YELLOW);
g2.setComposite(SMOOTH_SELECT);
g2.drawString(size, crop.x + SELECT_BORDER_WIDTH, crop.y
+ crop.height - SELECT_BORDER_WIDTH);
}
for (DrawableCropRect crop : crops) {
if (crop.contains(p)) {
crop.setSelected(!crop.isSelected());
break;
}
}
repaint();
return;
}
int max = -1;
for (DrawableCropRect crop : crops) {
if (crop.isSelected()) {
if (crop.width > max) {
max = crop.width;
}
}
}
return max;
}
int max = -1;
for (DrawableCropRect crop : crops) {
if (crop.isSelected()) {
if (crop.height > max) {
max = crop.height;
}
}
}
return max;
}
int min = Integer.MAX_VALUE;
for (DrawableCropRect crop : crops) {
if (crop.isSelected()) {
if (crop.x < min) {
min = crop.x;
}
}
}
return min;
}
int min = Integer.MAX_VALUE;
for (DrawableCropRect crop : crops) {
if (crop.isSelected()) {
if (crop.y < min) {
min = crop.y;
}
}
}
return min;
}
int maxW = -1;
int maxH = -1;
for (DrawableCropRect crop : crops) {
if (crop.width > maxW) {
maxW = crop.width;
}
if (crop.height > maxH) {
maxH = crop.height;
}
}
return new Dimension(maxW, maxH);
}
for (DrawableCropRect crop : crops) {
if (crop.isSelected()) {
int diffToMax = width - crop.width;
crop.grow(diffToMax / 2, 0);
}
}
updateClusterRatios(crops);
repaint();
}
for (DrawableCropRect crop : crops) {
if (crop.isSelected()) {
int diffToMax = height - crop.height;
crop.grow(0, diffToMax / 2);
}
}
updateClusterRatios(crops);
repaint();
}
for (DrawableCropRect crop : crops) {
if (crop.isSelected()) {
int diffToMaxW = width - crop.width;
int diffToMaxH = height - crop.height;
crop.grow(diffToMaxW / 2, diffToMaxH / 2);
}
}
updateClusterRatios(crops);
repaint();
}
for (DrawableCropRect crop : crops) {
if (crop.isSelected()) {
if (((width < 0) && (crop.width <= -width)) ||
((height < 0) && (crop.height <= -height)))
return;
crop.setSize(crop.width + width, crop.height + height);
}
}
updateClusterRatios(crops);
repaint();
}
for (DrawableCropRect crop : crops) {
crop.setSize(width, height);
}
updateClusterRatios(crops);
repaint();
}
for (DrawableCropRect crop : crops) {
if (crop.isSelected()) {
int newX = crop.x + x;
int newY = crop.y + y;
crop.setLocation(newX, newY);
}
}
repaint();
}
for (DrawableCropRect crop : crops) {
if (crop.isSelected()) {
crop.setLocation(x, y);
}
}
repaint();
}
for (DrawableCropRect crop : crops)
crop.setSelected(select);
repaint();
}
cluster.clearRatios();
for (Rectangle crop : tmpCrops) {
cluster.addRatios(getCutRatiosForPdf(crop, img.getWidth(),
img.getHeight()));
}
}
int imgHeight) {
int x1, x2, y1, y2;
x1 = crop.x;
x2 = x1 + crop.width;
y1 = crop.y;
y2 = y1 + crop.height;
if (x1 < 0) {
x1 = 0;
}
if (x2 > imgWidth) {
x2 = imgWidth;
}
if (y1 < 0) {
y1 = 0;
}
if (y2 > imgHeight) {
y2 = imgHeight;
}
Float[] ratios = new Float[4];
ratios[0] = (float) x1 / imgWidth;
ratios[1] = (float) (imgHeight - y2) / imgHeight;
ratios[2] = 1 - ((float) x2 / imgWidth);
ratios[3] = 1 - ((float) (imgHeight - y1) / imgHeight);
return ratios;
}
private Font
scaleFont(String text, Rectangle rect) {
int size = BASE_FONT.getSize();
int width = this.getFontMetrics(BASE_FONT).stringWidth(text);
int height = this.getFontMetrics(BASE_FONT).getHeight();
if (width == 0 || height == 0)
return BASE_FONT;
float scaleFactorWidth = rect.width / width;
float scaleFactorHeight = rect.height / height;
float scaledWidth = (scaleFactorWidth * size);
float scaledHeight = (scaleFactorHeight * size);
return BASE_FONT
.deriveFont((scaleFactorHeight > scaleFactorWidth) ? scaledWidth
: scaledHeight);
}
ClipBoard.getInstance().clear();
for (DrawableCropRect crop : crops) {
if (crop.isSelected()) {
ClipBoard.getInstance().addCrop(crop);
}
}
updateClusterRatios(crops);
repaint();
}
for (DrawableCropRect crop : ClipBoard.getInstance().getCrops()) {
if (!crops.contains(crop)) {
DrawableCropRect newCrop = new DrawableCropRect(crop);
crops.add(newCrop);
}
}
ClipBoard.getInstance().clear();
updateClusterRatios(crops);
repaint();
}
for (DrawableCropRect crop : crops) {
if (crop.contains(p)) {
briss.alignSelRects(crop.x, crop.y, crop.width, crop.height);
break;
}
}
}
List<DrawableCropRect> removeList = new ArrayList<DrawableCropRect>();
for (DrawableCropRect crop : crops) {
if (crop.isSelected()) {
removeList.add(crop);
}
}
crops.removeAll(removeList);
updateClusterRatios(crops);
repaint();
}
for (Rectangle crop : crops) {
if (crop.x < 0) {
crop.width -= -crop.x;
crop.x = 0;
}
if (crop.y < 0) {
crop.height -= -crop.y;
crop.y = 0;
}
if (crop.x + crop.width > getWidth()) {
crop.width = getWidth() - crop.x;
}
if (crop.y + crop.height > getHeight()) {
crop.height = getHeight() - crop.y;
}
}
}
List<Rectangle> cropsToTrash = new ArrayList<Rectangle>();
for (Rectangle crop : crops) {
if (crop.getWidth() < 2 * DrawableCropRect.CORNER_DIMENSION
|| crop.getHeight() < 2 * DrawableCropRect.CORNER_DIMENSION) {
cropsToTrash.add(crop);
}
}
crops.removeAll(cropsToTrash);
}
@Override
switch (e.getKeyCode()) {
case KeyEvent.VK_C:
if (e.getModifiers() == InputEvent.CTRL_MASK) {
copyToClipBoard();
}
break;
case KeyEvent.VK_V:
if (e.getModifiers() == InputEvent.CTRL_MASK) {
pasteFromClipBoard();
}
break;
case KeyEvent.VK_DELETE:
deleteAllSelected();
break;
case KeyEvent.VK_LEFT:
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_UP:
case KeyEvent.VK_DOWN:
int x = 0;
int y = 0;
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
x = -1;
break;
case KeyEvent.VK_RIGHT:
x = 1;
break;
case KeyEvent.VK_UP:
y = -1;
break;
case KeyEvent.VK_DOWN:
y = 1;
break;
}
if ((e.getModifiers() & InputEvent.SHIFT_MASK) != 0) {
x *= 10;
y *= 10;
}
if ((e.getModifiers() & InputEvent.CTRL_MASK) != 0) {
briss.resizeSelRects(x, y);
}
else {
briss.moveSelectedRects(x, y);
}
break;
default:
}
}
}
ActionListener {
@Override
if (MergedPanel.this.contains(e.getPoint())) {
MergedPanel.this.requestFocusInWindow();
}
}
@Override
if (PopUpMenuForCropRectangles.DELETE.equals(e.getActionCommand())) {
for (Rectangle crop : crops) {
if (crop.contains(popUpMenuPoint)) {
crops.remove(crop);
break;
}
}
cluster.clearRatios();
repaint();
} else if (PopUpMenuForCropRectangles.SELECT_DESELECT.equals(e
.getActionCommand())) {
changeSelectRectangle(popUpMenuPoint);
} else if (PopUpMenuForCropRectangles.COPY.equals(e
.getActionCommand())) {
copyToClipBoard();
} else if (PopUpMenuForCropRectangles.PASTE.equals(e
.getActionCommand())) {
pasteFromClipBoard();
} else if (PopUpMenuForCropRectangles.ALIGN_SELECTED.equals(e
.getActionCommand())) {
alignSelected(popUpMenuPoint);
}
}
@Override
Point curPoint = mE.getPoint();
switch (actionState) {
case DRAWING_NEW_CROP:
if (cropStartPoint == null) {
cropStartPoint = curPoint;
}
curCrop.x = (curPoint.x < cropStartPoint.x) ? curPoint.x
: cropStartPoint.x;
curCrop.width = Math.abs(curPoint.x - cropStartPoint.x);
curCrop.y = (curPoint.y < cropStartPoint.y) ? curPoint.y
: cropStartPoint.y;
curCrop.height = Math.abs(curPoint.y - cropStartPoint.y);
break;
case MOVE_CROP:
if (lastDragPoint == null) {
lastDragPoint = curPoint;
}
if (mE.isShiftDown()) {
briss.moveSelectedRects(curPoint.x - lastDragPoint.x,
curPoint.y - lastDragPoint.y);
}
else {
curCrop.translate(curPoint.x - lastDragPoint.x,
curPoint.y - lastDragPoint.y);
}
lastDragPoint = curPoint;
break;
case RESIZING_HOTCORNER_LR:
if (lastDragPoint == null) {
lastDragPoint = curPoint;
}
if (mE.isShiftDown()) {
briss.resizeSelRects(curPoint.x - lastDragPoint.x,
curPoint.y - lastDragPoint.y);
}
else {
curPoint.translate(relativeHotCornerGrabDistance.x,
relativeHotCornerGrabDistance.y);
curCrop.setNewHotCornerLR(curPoint);
}
lastDragPoint = curPoint;
break;
case RESIZING_HOTCORNER_UL:
if (lastDragPoint == null) {
lastDragPoint = curPoint;
}
if (mE.isShiftDown()) {
briss.resizeSelRects(lastDragPoint.x - curPoint.x,
lastDragPoint.y - curPoint.y);
briss.moveSelectedRects(curPoint.x - lastDragPoint.x,
curPoint.y - lastDragPoint.y);
}
else {
curPoint.translate(relativeHotCornerGrabDistance.x,
relativeHotCornerGrabDistance.y);
curCrop.setNewHotCornerUL(curPoint);
}
lastDragPoint = curPoint;
break;
}
repaint();
}
@Override
Point p = mE.getPoint();
if (mE.isPopupTrigger()) {
showPopUpMenu(mE);
}
if (mE.isControlDown()) {
changeSelectRectangle(p);
return;
}
if (SwingUtilities.isLeftMouseButton(mE)) {
for (DrawableCropRect crop : crops) {
if (crop.containsInHotCornerUL(p)) {
actionState = ActionState.RESIZING_HOTCORNER_UL;
relativeHotCornerGrabDistance = new Point(crop.x - p.x,
crop.y - p.y);
curCrop = crop;
return;
}
}
for (DrawableCropRect crop : crops) {
if (crop.containsInHotCornerLR(p)) {
actionState = ActionState.RESIZING_HOTCORNER_LR;
relativeHotCornerGrabDistance = new Point(crop.x
+ crop.width - p.x, crop.y + crop.height - p.y);
curCrop = crop;
return;
}
}
for (DrawableCropRect crop : crops) {
if (crop.contains(p)) {
actionState = ActionState.MOVE_CROP;
curCrop = crop;
return;
}
}
actionState = ActionState.DRAWING_NEW_CROP;
if (curCrop == null) {
curCrop = new DrawableCropRect();
crops.add(curCrop);
cropStartPoint = p;
}
}
}
@Override
if (mE.isPopupTrigger()) {
showPopUpMenu(mE);
}
clipCropsToVisibleArea();
removeToSmallCrops();
updateClusterRatios(crops);
actionState = ActionState.NOTHING;
cropStartPoint = null;
lastDragPoint = null;
curCrop = null;
repaint();
}
private void (MouseEvent e) {
popUpMenuPoint = e.getPoint();
new PopUpMenuForCropRectangles().show(e.getComponent(), e.getX(),
e.getY());
}
private class extends JPopupMenu {
public static final String DELETE = "Delete rectangle";
public static final String SELECT_DESELECT = "Select/Deselect rectangle";
public static final String COPY = "Copy Selected rectangles";
public static final String PASTE = "Paste rectangles";
public static final String ALIGN_SELECTED = "Align selected rectangles";
public () {
boolean isContainedInRectangle = false;
for (DrawableCropRect crop : crops) {
if (crop.contains(popUpMenuPoint)) {
isContainedInRectangle = true;
}
}
if (isContainedInRectangle) {
JMenuItem deleteItem = new JMenuItem(DELETE);
deleteItem.addActionListener(MergedPanelMouseAdapter.this);
add(deleteItem);
JMenuItem selectDeselectItem = new JMenuItem(
SELECT_DESELECT);
selectDeselectItem
.addActionListener(MergedPanelMouseAdapter.this);
add(selectDeselectItem);
}
boolean copyPossible = false;
for (DrawableCropRect crop : crops) {
if (crop.isSelected()) {
copyPossible = true;
}
}
JMenuItem copyItem = new JMenuItem(COPY);
copyItem.addActionListener(MergedPanelMouseAdapter.this);
copyItem.setEnabled(copyPossible);
add(copyItem);
JMenuItem pasteItem = new JMenuItem(PASTE);
pasteItem.addActionListener(MergedPanelMouseAdapter.this);
pasteItem.setEnabled(ClipBoard.getInstance()
.getAmountOfCropsInClipBoard() > 0);
add(pasteItem);
JMenuItem alignItem = new JMenuItem(ALIGN_SELECTED);
alignItem.addActionListener(MergedPanelMouseAdapter.this);
alignItem.setEnabled(true);
add(alignItem);
}
}
}
}