001package jmri.jmrit.display;
002
003import java.awt.Point;
004import java.awt.Rectangle;
005import java.awt.event.MouseEvent;
006
007import javax.swing.JTextField;
008import javax.swing.SwingUtilities;
009
010/**
011 * A text field hosted by a positionable panel in an editor whose painting is
012 * zoomed with a Graphics transform. It keeps hit testing and mouse coordinates
013 * aligned with the scaled rendering.
014 */
015class PositionableJTextField extends JTextField {
016
017    @Override
018    public void repaint(long tm, int x, int y, int width, int height) {
019        PositionableJPanel panel = getPositionablePanel();
020        if (panel == null || panel.getParent() == null || panel.getEditor() == null
021                || panel.getEditor().getTargetPanel() == null) {
022            super.repaint(tm, x, y, width, height);
023            return;
024        }
025
026        Rectangle dirtyBounds = SwingUtilities.convertRectangle(this,
027                new Rectangle(x, y, width, height), panel.getEditor().getTargetPanel());
028        panel.getEditor().repaintTargetPanel(dirtyBounds);
029    }
030
031    private PositionableJPanel getPositionablePanel() {
032        var parent = getParent();
033        if (parent instanceof PositionableJPanel) {
034            return (PositionableJPanel) parent;
035        } else {
036            return null;
037        }
038    }
039
040    private Point getPanelOrigin() {
041        PositionableJPanel panel = getPositionablePanel();
042        if (panel == null || panel.getEditor() == null || panel.getEditor().getTargetPanel() == null
043                || getParent() == null) {
044            return null;
045        }
046        return SwingUtilities.convertPoint(this, 0, 0, panel.getEditor().getTargetPanel());
047    }
048
049    @Override
050    public boolean contains(int x, int y) {
051        PositionableJPanel panel = getPositionablePanel();
052        Point origin = getPanelOrigin();
053        if (panel == null || origin == null) {
054            return super.contains(x, y);
055        }
056        double scale = panel.getEditor().getPaintScale();
057        if (scale == 1.0) {
058            return super.contains(x, y);
059        }
060        double unscaledX = (x + origin.x) / scale - origin.x;
061        double unscaledY = (y + origin.y) / scale - origin.y;
062        return unscaledX >= 0 && unscaledX < getWidth()
063                && unscaledY >= 0 && unscaledY < getHeight();
064    }
065
066    @Override
067    protected void processMouseEvent(MouseEvent event) {
068        super.processMouseEvent(transformMouseEvent(event));
069    }
070
071    @Override
072    protected void processMouseMotionEvent(MouseEvent event) {
073        super.processMouseMotionEvent(transformMouseEvent(event));
074    }
075
076    private MouseEvent transformMouseEvent(MouseEvent event) {
077        PositionableJPanel panel = getPositionablePanel();
078        Point origin = getPanelOrigin();
079        if (panel == null || origin == null) {
080            return event;
081        }
082        double scale = panel.getEditor().getPaintScale();
083        if (scale == 1.0) {
084            return event;
085        }
086
087        int x = (int) Math.round((event.getX() + origin.x) / scale - origin.x);
088        int y = (int) Math.round((event.getY() + origin.y) / scale - origin.y);
089        return new MouseEvent(this, event.getID(), event.getWhen(), event.getModifiersEx(), x, y,
090                event.getXOnScreen(), event.getYOnScreen(), event.getClickCount(),
091                event.isPopupTrigger(), event.getButton());
092    }
093}