// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.ohe.gui;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import org.openstreetmap.josm.plugins.ohe.OpeningTimeUtils;
public class TimeRect extends JPanel implements MouseListener, MouseMotionListener {
public static final int[] transformCursorTypes = new int[] {
Cursor.MOVE_CURSOR, Cursor.N_RESIZE_CURSOR,
Cursor.NE_RESIZE_CURSOR, Cursor.E_RESIZE_CURSOR,
Cursor.SE_RESIZE_CURSOR, Cursor.S_RESIZE_CURSOR,
Cursor.SW_RESIZE_CURSOR, Cursor.W_RESIZE_CURSOR,
Cursor.NW_RESIZE_CURSOR };
public static final int minuteResterize = 15;
public static final int verticalNonDrawedPixels = 5;
public static final boolean[][] transformDirections = new boolean[][] {
{true, true, true, true}, // Drag
{true, false, false, false}, // N
{true, true, false, false}, // NE
{false, true, false, false}, // E
{false, true, true, false}, // SE
{false, false, true, false}, // S
{false, false, true, true}, // SW
{false, false, false, true}, // W
{true, false, false, true}, // NW
};
public static final int roundCornerSize = 8;
private final int clickAreaSize = 16;
private OheEditor editor;
private int dayStart;
private int dayEnd;
private int minuteStart;
private int minuteEnd;
public TimeRect(OheEditor editor, int dayStart, int dayEnd, int minutesStart, int minutesEnd) {
OpeningTimeUtils.ensureValidDay(dayStart, "dayStart");
OpeningTimeUtils.ensureValidDay(dayEnd, "dayEnd");
OpeningTimeUtils.ensureValidMinute(minutesStart, "minutesStart");
OpeningTimeUtils.ensureValidMinute(minutesEnd, "minutesEnd");
this.editor = editor;
this.dayStart = dayStart;
this.dayEnd = dayEnd;
this.minuteStart = minutesStart;
this.minuteEnd = minutesEnd;
transformType = -1;
setOpaque(true);
addMouseListener(this);
addMouseMotionListener(this);
}
/**
* Returns the starting day, as an index between 0 and 6.
* @return the starting day index
*/
public int getDayStart() {
return dayStart;
}
/**
* Returns the ending day, as an index between 0 and 6.
* @return the ending day index
*/
public int getDayEnd() {
return dayEnd;
}
public int getMinuteStart() {
return minuteStart;
}
public int getMinuteEnd() {
return minuteEnd;
}
public void reposition() {
setBounds(editor.getPanelBoundsForTimeinterval(dayStart, dayEnd + 1, minuteStart, minuteEnd));
editor.contentPanel.repaint();
}
private boolean isZeroMinuteInterval() {
return minuteStart == minuteEnd;
}
private boolean isOpenEndInterval() {
return minuteEnd == 24 * 60 + 1;
}
private void updateTimeInterval(int newDayStart, int newDayEnd, int newMinuteStart, int newMinuteEnd) {
OpeningTimeUtils.ensureValidDay(newDayStart, "newDayStart");
OpeningTimeUtils.ensureValidDay(newDayEnd, "newDayEnd");
OpeningTimeUtils.ensureValidMinute(newMinuteStart, "newMinuteStart");
OpeningTimeUtils.ensureValidMinute(newMinuteEnd, "newMinuteEnd");
dayStart = newDayStart;
dayEnd = newDayEnd;
minuteStart = newMinuteStart;
minuteEnd = newMinuteEnd;
editor.dialog.updateValueField(editor.timeRects);
reposition();
}
@Override
public void paintComponent(Graphics g) {
drawTimeRect((Graphics2D) g, new Rectangle(0, 0, getWidth(),
getHeight()), isZeroMinuteInterval(), isOpenEndInterval());
}
public static void drawTimeRect(Graphics2D g2D, Rectangle bounds, boolean isZeroMinuteInterval, boolean isOpenEndInterval) {
Color innerColor = new Color(135, 135, 234);
if (isOpenEndInterval)
innerColor = new Color(234, 135, 135);
int tmpRoundCornerSize = TimeRect.roundCornerSize;
int verticalNonFilledBorder = 1;
if (isZeroMinuteInterval) {
innerColor = new Color(135, 234, 135);
tmpRoundCornerSize = 0;
verticalNonFilledBorder = verticalNonDrawedPixels;
}
g2D.setColor(innerColor);
g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
.6f));
g2D.fillRoundRect(bounds.x + 1, bounds.y + verticalNonFilledBorder,
bounds.width - 2, bounds.height - 1 - 2
* verticalNonFilledBorder, tmpRoundCornerSize,
tmpRoundCornerSize);
g2D.setColor(new Color(255, 0, 0));
g2D.setComposite(AlphaComposite
.getInstance(AlphaComposite.SRC_OVER, 1f));
g2D.drawRoundRect(bounds.x + 1, bounds.y + verticalNonFilledBorder,
bounds.width - 2, bounds.height - 1 - 2
* verticalNonFilledBorder, tmpRoundCornerSize,
tmpRoundCornerSize);
}
private int actualDayDrag;
private int actualMinuteDrag;
private int dragX;
private int dragY;
private int transformType;
// Calculate where the Component was clicked and returns the transformtype
private int getTransformType(MouseEvent evt) {
int tmpClickAreaWidth = Math.min(clickAreaSize, getWidth() / 3);
int tmpClickAreaHeight = Math.min(clickAreaSize, getHeight() / 3);
boolean isInNorthernTransformClickArea = evt.getY() < tmpClickAreaHeight;
boolean isInEasternTransformClickArea = evt.getX() > getWidth()
- tmpClickAreaWidth;
boolean isInSouthernTransformClickArea = evt.getY() > getHeight()
- tmpClickAreaHeight;
boolean isInWesternTransformClickArea = evt.getX() < tmpClickAreaWidth;
if (isZeroMinuteInterval()) {
isInNorthernTransformClickArea = false;
isInSouthernTransformClickArea = false;
}
int tType = 0;
for (int i = 1; i < transformDirections.length && tType == 0; i++) {
if (isInNorthernTransformClickArea == transformDirections[i][0]
&& isInEasternTransformClickArea == transformDirections[i][1]
&& isInSouthernTransformClickArea == transformDirections[i][2]
&& isInWesternTransformClickArea == transformDirections[i][3])
tType = i;
}
return tType;
}
public void showMenu(MouseEvent evt) {
JPopupMenu menu = new JPopupMenu();
final JCheckBoxMenuItem cbMenuItem = new JCheckBoxMenuItem(tr("open end"), isOpenEndInterval());
menu.add(cbMenuItem);
cbMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (cbMenuItem.isSelected())
updateTimeInterval(dayStart, dayEnd, minuteStart, 24 * 60 + 1);
else
updateTimeInterval(dayStart, dayEnd, minuteStart, 24 * 60);
}
});
menu.show(this, evt.getX(), evt.getY());
}
@Override
public void mouseClicked(MouseEvent evt) {
}
@Override
public void mouseEntered(MouseEvent evt) {
}
@Override
public void mouseExited(MouseEvent evt) {
if (transformType < 0) {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
editor.mousePositionChanged(0, 0, false);
}
@Override
public void mousePressed(MouseEvent evt) {
if (evt.isPopupTrigger()) {
showMenu(evt);
} else {
actualDayDrag = 0;
actualMinuteDrag = 0;
dragX = evt.getXOnScreen();
dragY = evt.getYOnScreen();
transformType = getTransformType(evt);
}
}
@Override
public void mouseReleased(MouseEvent evt) {
transformType = -1;
if (evt.isPopupTrigger()) {
showMenu(evt);
}
}
@Override
public void mouseDragged(MouseEvent evt) {
if (transformType >= 0) {
int xDiff = evt.getXOnScreen() - dragX;
int yDiff = evt.getYOnScreen() - dragY;
xDiff = (int) Math.round(xDiff / editor.getDayWidth())
- actualDayDrag;
yDiff = (int) Math.round(yDiff
/ (editor.getMinuteHeight() * minuteResterize))
* minuteResterize - actualMinuteDrag;
if (xDiff != 0) {
int newDayStart = dayStart;
int newDayEnd = dayEnd;
if (transformDirections[transformType][3])
newDayStart += xDiff;
if (transformDirections[transformType][1])
newDayEnd += xDiff;
if (newDayStart > newDayEnd) {
editor.removeTimeRect(this);
transformType = -1;
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
} else if (newDayStart >= 0 && newDayEnd <= 6) {
actualDayDrag += xDiff;
updateTimeInterval(newDayStart, newDayEnd, minuteStart, minuteEnd);
}
}
if (yDiff != 0 && transformType >= 0) {
int newMinutesStart = minuteStart;
int newMinutesEnd = minuteEnd;
if (transformDirections[transformType][0])
newMinutesStart = newMinutesStart + yDiff;
if (transformDirections[transformType][2] && !isOpenEndInterval())
newMinutesEnd = newMinutesEnd + yDiff;
if (newMinutesStart >= 0 && newMinutesStart <= 24*60+1
&& (newMinutesEnd <= 24 * 60 || isOpenEndInterval())) {
actualMinuteDrag += yDiff;
updateTimeInterval(dayStart, dayEnd, newMinutesStart, newMinutesEnd);
}
}
}
editor.mousePositionChanged(evt.getX() + getX(), evt.getY() + getY(), true);
}
@Override
public void mouseMoved(MouseEvent evt) {
if (transformType < 0) {
setCursor(new Cursor(transformCursorTypes[getTransformType(evt)]));
}
editor.mousePositionChanged(evt.getX() + getX(), evt.getY() + getY(), true);
}
}