// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.ohe.gui; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.ArrayList; import javax.swing.JPanel; import javax.swing.JScrollPane; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.plugins.ohe.ClockSystem; import org.openstreetmap.josm.plugins.ohe.OpeningTimeUtils; import org.openstreetmap.josm.plugins.ohe.parser.OpeningTimeCompiler; public class OheEditor extends JPanel implements MouseListener, MouseMotionListener { private final class OhePanel extends JPanel { @Override public void setSize(Dimension d) { super.setSize(d); repositionTimeRects(); } @Override public void paintComponent(Graphics g) { if (OheEditor.this.isEnabled()) { g.setColor(Color.WHITE); g.fillRect(0, 0, getWidth(), getHeight()); // draw the time from 12PM to 00AM in a different color if (dialog.getHourMode() == ClockSystem.TWELVE_HOURS) { g.setColor(new Color(255, 255, 218)); g.fillRect(0, getMinutePosition(12 * 60), getWidth(), getHeight() - getMinutePosition(12 * 60)); } // horizontal Lines for (int i = 0; i <= 24; ++i) { if (i % 3 == 0) { g.setColor(Color.BLACK); } else { g.setColor(Color.LIGHT_GRAY); } g.drawLine(0, getMinutePosition(i * 60), getWidth(), getMinutePosition(i * 60)); } // vertical Lines g.setColor(Color.BLACK); for (int i = 0; i <= 7; ++i) { g.drawLine(getDayPosition(i), 0, getDayPosition(i), getHeight()); } // if a new Rect is dragged draw it if (day0 >= 0) { Graphics2D g2D = (Graphics2D) g; int day2 = Math.min(day0, day1); int day3 = Math.max(day0, day1); int minute2 = Math.min(minute0, minute1); int minute3 = Math.max(minute0, minute1); Rectangle bounds = getPanelBoundsForTimeinterval(day2, day3 + 1, minute2, minute3); TimeRect.drawTimeRect(g2D, bounds, minute2 == minute3, false); } } else { g.setColor(Color.LIGHT_GRAY); g.fillRect(0, 0, getWidth(), getHeight()); } } } final OheDialogPanel dialog; private final JScrollPane scrollPane; final JPanel contentPanel; ArrayList<TimeRect> timeRects; private final int dayAxisHeight = 20; private final int timeAxisWidth = 45; public OheEditor(OheDialogPanel oheDialogPanel) { dialog = oheDialogPanel; // the MainPanel for showing the TimeRects contentPanel = new OhePanel(); contentPanel.addMouseListener(this); contentPanel.addMouseMotionListener(this); contentPanel.setLayout(null); contentPanel.setPreferredSize(new Dimension(180, 384)); initTimeRects(); scrollPane = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); scrollPane.setViewportView(contentPanel); // the upper Panel for showing Weekdays scrollPane.setColumnHeaderView(new JPanel() { @Override public Dimension getPreferredSize() { return new Dimension(contentPanel.getWidth(), dayAxisHeight); } @Override public void paintComponent(Graphics g) { g.setColor(Color.WHITE); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(Color.BLACK); for (int i = 0; i < 7; ++i) { if (i > 0) { g.drawLine(getDayPosition(i), 0, getDayPosition(i), getHeight()); } String text = OpeningTimeCompiler.WEEKDAYS[i]; g.drawString(text, (int) (getDayPosition(i + 0.5) - g.getFontMetrics().stringWidth(text) * 0.5), (int) (dayAxisHeight * 0.5 + g.getFontMetrics().getHeight() * 0.35)); } } }); // the left Panel for showing the hours scrollPane.setRowHeaderView(new JPanel() { @Override public Dimension getPreferredSize() { return new Dimension(timeAxisWidth, contentPanel.getHeight()); } @Override public void paintComponent(Graphics g) { g.setColor(Color.WHITE); g.fillRect(0, 0, getWidth(), getHeight()); // draw the time from 12PM to 00AM in a different color if (dialog.getHourMode() == ClockSystem.TWELVE_HOURS) { g.setColor(new Color(255, 255, 218)); g.fillRect(0, getMinutePosition(12 * 60), getWidth(), getHeight() - getMinutePosition(12 * 60)); } for (int i = 0; i <= 24; ++i) { if (i % 3 == 0) { g.setColor(Color.BLACK); if (i % 24 != 0) { String text = OpeningTimeUtils.timeString(i * 60, dialog.getHourMode(), false); g.drawString(text, (timeAxisWidth - g.getFontMetrics().stringWidth(text)) / 2, getMinutePosition(i * 60) + (int) (g.getFontMetrics().getHeight() * 0.35)); } } else { g.setColor(Color.LIGHT_GRAY); } g.drawLine(getWidth() - 4, getMinutePosition(i * 60), getWidth(), getMinutePosition(i * 60)); } g.setColor(Color.BLACK); String text = OpeningTimeUtils.timeString(0, dialog.getHourMode(), false); g.drawString(text, (timeAxisWidth - g.getFontMetrics().stringWidth(text)) / 2, getMinutePosition(0) + (int) (g.getFontMetrics().getHeight() * 1.0)); if (dialog.getHourMode() == ClockSystem.TWELVE_HOURS) { text = "AM"; g.drawString(text, (timeAxisWidth - g.getFontMetrics().stringWidth(text)) / 2, getMinutePosition(0) + (int) (g.getFontMetrics().getHeight() * 1.85)); text = "PM"; g.drawString(text, (timeAxisWidth - g.getFontMetrics().stringWidth(text)) / 2, getMinutePosition(12 * 60) + (int) (g.getFontMetrics().getHeight() * 1.2)); } } }); setLayout(new BorderLayout()); add(scrollPane, BorderLayout.CENTER); } /** * Updates all the TimeRects with new data. */ public void initTimeRects() { contentPanel.removeAll(); ArrayList<int[]> time; try { time = dialog.getTime(); } catch (Exception exc) { Main.warn(exc, "Disable opening hours editor:"); setEnabled(false); return; } setEnabled(true); timeRects = new ArrayList<>(); if (time != null) { for (int[] timeRectValues : time) { int day0 = timeRectValues[0]; int day1 = timeRectValues[1]; int minute0 = timeRectValues[2]; int minute1 = timeRectValues[3]; TimeRect timeRect = new TimeRect(OheEditor.this, day0, day1, minute0, minute1); timeRects.add(timeRect); contentPanel.add(timeRect); } } repositionTimeRects(); repaint(); } protected void repositionTimeRects() { if (timeRects != null) { for (TimeRect timeRect : timeRects) { timeRect.reposition(); } } } // returns the physical Borders of the TimeRect on the mainPanel public Rectangle getPanelBoundsForTimeinterval(int dayStart, int dayEnd, int minutesStart, int minutesEnd) { int x = getDayPosition(dayStart); int y = getMinutePosition(minutesStart); int width = getDayPosition(dayEnd) - getDayPosition(dayStart); int height = getMinutePosition(minutesEnd) - getMinutePosition(minutesStart); if (minutesStart == minutesEnd) return new Rectangle(x, y - 2 - TimeRect.verticalNonDrawedPixels, width, height + 5 + 2 * TimeRect.verticalNonDrawedPixels); return new Rectangle(x, y, width, height + 1); } public double getDayWidth() { return (contentPanel.getWidth() - 1) / 7.0; } public int getDayPosition(double d) { return (int) (d * getDayWidth()); } public double getMinuteHeight() { return (contentPanel.getHeight() - 1) / (24.0 * 60); } public int getMinutePosition(int minute) { return (int) (minute * getMinuteHeight()); } /** * Removes the given timerect from the panel and from the arraylist */ public void removeTimeRect(TimeRect timeRectToRemove) { timeRects.remove(timeRectToRemove); contentPanel.remove(timeRectToRemove); dialog.updateValueField(timeRects); repaint(); } // drawing a new Rect private int day0 = -1; private int minute0; private int day1; private int minute1; private int xDragStart; private int yDragStart; @Override public void mouseClicked(MouseEvent evt) { } @Override public void mouseEntered(MouseEvent evt) { if (!isEnabled()) { return; // allow no mouse actions when the editor is not enabled } mousePositionChanged(0, 0, true); } @Override public void mouseExited(MouseEvent evt) { if (!isEnabled()) { return; // allow no mouse actions when the editor is not enabled } mousePositionChanged(0, 0, false); } @Override public void mousePressed(MouseEvent evt) { if (!isEnabled()) { return; // allow no mouse actions when the editor is not enabled } day0 = (int) Math.floor(evt.getX() / getDayWidth()); minute0 = (int) Math.floor(evt.getY() / (getMinuteHeight() * TimeRect.minuteResterize)) * TimeRect.minuteResterize; day1 = day0; minute1 = minute0; xDragStart = evt.getX(); yDragStart = evt.getY(); } @Override public void mouseReleased(MouseEvent evt) { if (!isEnabled()) { return; // allow no mouse actions when the editor is not enabled } // mouse must be moved 5px before creating a rect if (xDragStart == -1 || Math.abs(evt.getX() - xDragStart) + Math.abs(evt.getY() - yDragStart) > 5) { int day2 = Math.min(day0, day1); int day3 = Math.max(day0, day1); int minute2 = Math.min(minute0, minute1); int minute3 = Math.max(minute0, minute1); TimeRect timeRect = new TimeRect(OheEditor.this, day2, day3, minute2, minute3); timeRects.add(timeRect); contentPanel.add(timeRect); timeRect.reposition(); dialog.updateValueField(timeRects); day0 = -1; repaint(); } } @Override public void mouseDragged(MouseEvent evt) { if (!isEnabled()) { return; // allow no mouse actions when the editor is not enabled } // mouse must be moved 5px before drawing a rect if (xDragStart == -1 || Math.abs(evt.getX() - xDragStart) + Math.abs(evt.getY() - yDragStart) > 5) { xDragStart = -1; day1 = (int) Math.floor(evt.getX() / getDayWidth()); minute1 = (int) Math.floor(evt.getY() / (getMinuteHeight() * TimeRect.minuteResterize)) * TimeRect.minuteResterize; // ensure that the new time is in a valid range day1 = Math.max(day1, 0); day1 = Math.min(day1, 6); minute1 = Math.max(minute1, 0); minute1 = Math.min(minute1, 24 * 60); repaint(); } mousePositionChanged(evt.getX(), evt.getY(), true); } @Override public void mouseMoved(MouseEvent evt) { if (!isEnabled()) { return; // allow no mouse actions when the editor is not enabled } mousePositionChanged(evt.getX(), evt.getY(), true); } public void mousePositionChanged(int x, int y, boolean mouseInside) { if (mouseInside) { int actualDay = (int) Math.floor(x / getDayWidth()); int minutes = (int) Math.floor(y / (getMinuteHeight() * TimeRect.minuteResterize)) * TimeRect.minuteResterize; actualDay = Math.max(0, Math.min(6, actualDay)); minutes = Math.max(0, Math.min(24 * 60, minutes)); dialog.setMousePositionText(OpeningTimeCompiler.WEEKDAYS[actualDay] + " " + OpeningTimeUtils.timeString(minutes, dialog.getHourMode(), true)); } else { dialog.setMousePositionText("-"); } } }