映射鼠标事件坐标

Mapping mouse event coordinates

我已经实现了一个 JLayer<JPanel> 组件,它绘制了一个缩放的 Graphics 自身,因此他所有的下降组件也将被缩放。 此 JLayer 作为 ContentPane 应用于 JFrame 组件。

主要问题是所有缩放实际上只适用于图形,组件的实际大小和位置保持不变。这意味着所有的鼠标事件都发生在用户看到的错误位置。

我对它进行了调整,在框架的顶部定义了一个 GlassPane JComponent,它有自己的 MouseInputAdapter,它将 MouseEvent 重新发送到使用 SwingUtilities.getDeepestComponentAt() 的底层组件。这是通过创建一个新的 MouseEvent 并根据缩放值映射鼠标坐标来实现的。 (修改How to use RootPanes教程获得)

这种方法显然不能令人满意,因为很多事件根本无法触发(例如 MOUSE_ENTERED 所有下降组件触发的事件)。 另一方面,使用 LayerUI.paint() 覆盖意味着我必须拥有重新映射所有鼠标坐标的东西。

  1. 有没有办法在不破坏内部 MouseEvent 处理的情况下映射鼠标坐标?

  2. 有没有另一种方法可以放大组件的位图,同时修改实际位置和大小?我试过这个但是调用内部组件的 setSize() 似乎以某种方式第二次调用 LayerUi.paint() 所以我得到的只是一些与实际小部件位置断开连接的更大的图形

这是我到目前为止所做的,感谢 MadProgrammer 和 PBar 扩展,在 java 8.

中使用 JLayer

TransformUI 本身包括 PBar 的 org.pbjar.jxlayer.plaf.ext.MouseEventUIorg.pbjar.jxlayer.plaf.ext.TransformUI:

package jzoom.transform;

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.util.HashSet;
import java.util.Set;

import javax.swing.JComponent;
import javax.swing.JLayer;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.plaf.LayerUI;

/**
 * 
 * This UI apply an {@link AffineTransform} to all the visible objects, and apply the inversion
 *  of the same transform to all the mouse event's coordinates
 * 
 * @author Andrea.Maracci based on the pbjar JXLayer extension
 *
 */
public class TransformUI extends LayerUI<JComponent> {
    private static final long serialVersionUID = 1L;

    private Component lastEnteredTarget, lastPressedTarget;

    private final Set<JComponent> originalDoubleBuffered = new HashSet<JComponent>();
    private AffineTransform transform = new AffineTransform();
    private JLayer<JComponent> installedLayer;
    private boolean dispatchingMode = false;

    /**
     * Process the mouse events and map the mouse coordinates inverting the internal affine transformation.
     * 
     * @param
     * event the event to be dispatched
     * layer the layer this LayerUI is set to
     * 
     */
    @Override
    public void eventDispatched(AWTEvent event, final JLayer<? extends JComponent> layer) {
        if (event instanceof MouseEvent) {
            MouseEvent mouseEvent = (MouseEvent) event;
            /*
             * The if discriminate between generated and original event.
             * Removing it cause a stack overflow caused by the event being redispatched to this class.
             */
            if (!dispatchingMode) {
                // Process an original mouse event
                dispatchingMode = true;
                try {
                    redispatchMouseEvent(mouseEvent, layer);
                } finally {
                    dispatchingMode = false;
                }
            } else {
                /*
                 * Process generated mouse events
                 * Added a check, because on mouse entered or exited, the cursor
                 * may be set to specific dragging cursors.
                 */
                if (MouseEvent.MOUSE_ENTERED == mouseEvent.getID() || MouseEvent.MOUSE_EXITED == mouseEvent.getID()) {
                    layer.getGlassPane().setCursor(null);
                } else {
                    Component component = mouseEvent.getComponent();
                    layer.getGlassPane().setCursor(component.getCursor());
                }
            }
        } else {
            super.eventDispatched(event, layer);
        }
        layer.repaint();
    }

    /**
     * Set the affine transformation applied to the graphics
     * @param transform the transformation
     */
    public void setTransform(AffineTransform transform) {
        if (transform != null) {
            this.transform = transform;
        }
    }

    /**
     * Return the affine transformation applied to the graphics
     * @return the transformation
     */
    public AffineTransform getTransform() {
        return transform;
    }

    /**
     * Paint the specified component {@code c} applying the transformation on it's graphic
     * 
     * @param
     * g - the Graphics context in which to paint
     * c - the component being painted
     */
    @Override
    public void paint(Graphics g, JComponent c) {
        if (g instanceof Graphics2D) {
            Graphics2D g2 = (Graphics2D) g.create();
            JLayer<? extends JComponent> l = (JLayer<? extends JComponent>) c;
            g2.transform(transform);
            paintLayer(g2, l);
            g2.dispose();
        }
    }

    /**
     * Paint the view decorated by the JLayer {@code layer} and the JLayer itself
     * 
     * @param g2
     * @param layer the layer this LayerUI is set to
     */
    private final void paintLayer(Graphics2D g2, JLayer<? extends JComponent> layer) {
        JComponent view = layer.getView();
        if (view != null) {
            if (view.getX() < 0 || view.getY() < 0) {
                setToNoDoubleBuffering(view);
                g2.translate(view.getX(), view.getY());
                view.paint(g2);
                for (JComponent jComp : originalDoubleBuffered) {
                    jComp.setDoubleBuffered(true);
                }
                originalDoubleBuffered.clear();
                return;
            }
        }
        layer.paint(g2);
    }

    /**
     * Disable the double buffering for the {@code component} and for all of it's children
     * 
     * @param component
     */
    private void setToNoDoubleBuffering(Component component) {
        if (component instanceof JComponent) {
            JComponent jComp = (JComponent) component;
            if (jComp.isDoubleBuffered()) {
                originalDoubleBuffered.add(jComp);
                jComp.setDoubleBuffered(false);
            }
        }
        if (component instanceof Container) {
            Container container = (Container) component;
            for (int index = 0; index < container.getComponentCount(); index++) {
                setToNoDoubleBuffering(container.getComponent(index));
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void uninstallUI(JComponent component) {
        if (!(component instanceof JLayer<?>)) {
            throw new IllegalArgumentException(
                    this.getClass().getName() + " invalid class, must be a JLayer component");
        }
        JLayer<JComponent> jlayer = (JLayer<JComponent>) component;
        jlayer.setLayerEventMask(0);
        super.uninstallUI(component);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void installUI(JComponent component) throws IllegalStateException {
        super.installUI(component);
        if (installedLayer != null) {
            throw new IllegalStateException(this.getClass().getName() + " cannot be shared between multiple layers");
        }
        if (!(component instanceof JLayer<?>)) {
            throw new IllegalArgumentException(
                    this.getClass().getName() + " invalid class, must be a JLayer component");
        }
        // component.getClass().getDeclaringClass();
        installedLayer = (JLayer<JComponent>) component;
        installedLayer.setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK
                | AWTEvent.MOUSE_WHEEL_EVENT_MASK | AWTEvent.KEY_EVENT_MASK | AWTEvent.FOCUS_EVENT_MASK);
    }

    /**
     * Process the mouse events and map the mouse coordinates inverting the internal affine transformation.
     * It consume the original event, calculates the mapped mouse coordinates and find the real target of the mouse event.
     * It than create a new event with the correct informations in it and redispatch it to the target event
     * 
     * @param originalEvent the event to be dispatched
     * @param layer the layer this LayerUI is set to
     */
    private void redispatchMouseEvent(MouseEvent originalEvent, JLayer<? extends JComponent> layer) {
        if (layer.getView() != null) {
            if (originalEvent.getComponent() != layer.getGlassPane()) {
                originalEvent.consume();
            }
            MouseEvent newEvent = null;

            Point realPoint = calculateTargetPoint(layer, originalEvent);
            Component realTarget = getTarget(layer, realPoint);

            // Component realTarget =
            // SwingUtilities.getDeepestComponentAt(layer.getView(),
            // realPoint.x, realPoint.y);

            if (realTarget != null) {
                //System.out.println(realTarget.getClass().getName());
                realTarget = getListeningComponent(originalEvent, realTarget);
            }

            switch (originalEvent.getID()) {
            case MouseEvent.MOUSE_PRESSED:
                newEvent = transformMouseEvent(layer, originalEvent, realTarget, realPoint);
                if (newEvent != null) {
                    lastPressedTarget = newEvent.getComponent();
                }
                break;
            case MouseEvent.MOUSE_RELEASED:
                newEvent = transformMouseEvent(layer, originalEvent, lastPressedTarget, realPoint);
                lastPressedTarget = null;
                break;
            case MouseEvent.MOUSE_CLICKED:
                newEvent = transformMouseEvent(layer, originalEvent, realTarget, realPoint);
                lastPressedTarget = null;
                break;
            case MouseEvent.MOUSE_MOVED:
                newEvent = transformMouseEvent(layer, originalEvent, realTarget, realPoint);
                generateEnterExitEvents(layer, originalEvent, realTarget, realPoint);
                break;
            case MouseEvent.MOUSE_ENTERED:
                generateEnterExitEvents(layer, originalEvent, realTarget, realPoint);
                break;
            case MouseEvent.MOUSE_EXITED:
                generateEnterExitEvents(layer, originalEvent, realTarget, realPoint);
                break;
            case MouseEvent.MOUSE_DRAGGED:
                newEvent = transformMouseEvent(layer, originalEvent, lastPressedTarget, realPoint);
                generateEnterExitEvents(layer, originalEvent, realTarget, realPoint);
                break;
            case (MouseEvent.MOUSE_WHEEL):
                // redispatchMouseWheelEvent((MouseWheelEvent) originalEvent,
                // realTarget, realPoint);
                newEvent = transformMouseWheelEvent(layer, (MouseWheelEvent) originalEvent, realTarget, realPoint);
                break;/**/
            }
            dispatchMouseEvent(newEvent);
        }
    }

    /**
     * Apply the inverse transformation to {@code point}
     * 
     * @param layer the layer this LayerUI is set to
     * @param point the starting point
     * @return the transformed point
     */
    private Point transformPoint(JLayer<? extends JComponent> layer, Point point) {
        if (transform != null) {
            try {
                transform.inverseTransform(point, point);
            } catch (NoninvertibleTransformException e) {
                e.printStackTrace();
            }
        }
        return point;
    }

    /**
     * Find the deepest component in the AWT hierarchy
     * 
     * @param layer  the layer to which this UI is installed
     * @param targetPoint the point in layer's coordinates
     * @return the component in the specified point
     */
    private Component getTarget(JLayer<? extends JComponent> layer, Point targetPoint) {
        Component view = layer.getView();
        if (view == null) {
            return null;
        } else {
            Point viewPoint = SwingUtilities.convertPoint(layer, targetPoint, view);
            return SwingUtilities.getDeepestComponentAt(view, viewPoint.x, viewPoint.y);
        }
    }

    /**
     * Convert the {@code mouseEvent}'s coordinates to the {@code layer}'s space
     * @param layer the layer this LayerUI is set to
     * @param mouseEvent the original mouse event
     * @return the {@code mouseEvent}'s point transformed to the {@code layer}'s coordinate space
     */
    private Point calculateTargetPoint(JLayer<? extends JComponent> layer,
            MouseEvent mouseEvent) {
        Point point = mouseEvent.getPoint();
        //SwingUtilities.convertPointToScreen(point, mouseEvent.getComponent());
        //SwingUtilities.convertPointFromScreen(point, layer);
        point = SwingUtilities.convertPoint(mouseEvent.getComponent(), point, layer);
        return transformPoint(layer, point);

        }

    private MouseEvent transformMouseEvent(JLayer<? extends JComponent> layer, MouseEvent mouseEvent, Component target, Point realPoint) {
        return transformMouseEvent( layer, mouseEvent, target, realPoint, mouseEvent.getID());
    }

    /**
     * Create the new event to being dispatched
     */
    private MouseEvent transformMouseEvent(JLayer<? extends JComponent> layer, MouseEvent mouseEvent, Component target, Point targetPoint, int id) {
        if (target == null) {
            return null;
        } else {
            Point newPoint = SwingUtilities.convertPoint(layer, targetPoint, target);
            return new MouseEvent(target, //
                    id, //
                    mouseEvent.getWhen(), //
                    mouseEvent.getModifiers(), //
                    newPoint.x, //
                    newPoint.y, //
                    mouseEvent.getClickCount(), //
                    mouseEvent.isPopupTrigger(), //
                    mouseEvent.getButton());
        }
    }

    /**
     * Create the new mouse wheel event to being dispached
     */
    private MouseWheelEvent transformMouseWheelEvent( JLayer<? extends JComponent> layer, MouseWheelEvent mouseWheelEvent, Component target,
            Point targetPoint) {
        if (target == null) {
            return null;
        } else {
            Point newPoint = SwingUtilities.convertPoint(layer, targetPoint, target);
            return new MouseWheelEvent(target, //
                    mouseWheelEvent.getID(), //
                    mouseWheelEvent.getWhen(), //
                    mouseWheelEvent.getModifiers(), //
                    newPoint.x, //
                    newPoint.y, //
                    mouseWheelEvent.getClickCount(), //
                    mouseWheelEvent.isPopupTrigger(), //
                    mouseWheelEvent.getScrollType(), //
                    mouseWheelEvent.getScrollAmount(), //
                    mouseWheelEvent.getWheelRotation() //
            );
        }
    }

    /**
     * dispatch the {@code mouseEvent}
     * @param mouseEvent the event to be dispatched
     */
    private void dispatchMouseEvent(MouseEvent mouseEvent) {
        if (mouseEvent != null) {
            Component target = mouseEvent.getComponent();
            target.dispatchEvent(mouseEvent);
        }
    }

    /**
     * Get the listening component associated to the {@code component}'s {@code event}
     */
    private Component getListeningComponent(MouseEvent event, Component component) {
        switch (event.getID()) {
        case (MouseEvent.MOUSE_CLICKED):
        case (MouseEvent.MOUSE_ENTERED):
        case (MouseEvent.MOUSE_EXITED):
        case (MouseEvent.MOUSE_PRESSED):
        case (MouseEvent.MOUSE_RELEASED):
            return getMouseListeningComponent(component);
        case (MouseEvent.MOUSE_DRAGGED):
        case (MouseEvent.MOUSE_MOVED):
            return getMouseMotionListeningComponent(component);
        case (MouseEvent.MOUSE_WHEEL):
            return getMouseWheelListeningComponent(component);
        }
        return null;
    }

    /**
     * Cycles through the {@code component}'s parents to find the {@link Component} with associated {@link MouseListener}
     */
    private Component getMouseListeningComponent(Component component) {
        if (component.getMouseListeners().length > 0) {
            return component;
        } else {
            Container parent = component.getParent();
            if (parent != null) {
                return getMouseListeningComponent(parent);
            } else {
                return null;
            }
        }
    }

    /**
     * Cycles through the {@code component}'s parents to find the {@link Component} with associated {@link MouseMotionListener}
     */
    private Component getMouseMotionListeningComponent(Component component) {
        /*
         * Mouse motion events may result in MOUSE_ENTERED and MOUSE_EXITED.
         * 
         * Therefore, components with MouseListeners registered should be
         * returned as well.
         */
        if (component.getMouseMotionListeners().length > 0 || component.getMouseListeners().length > 0) {
            return component;
        } else {
            Container parent = component.getParent();
            if (parent != null) {
                return getMouseMotionListeningComponent(parent);
            } else {
                return null;
            }
        }
    }

    /**
     * Cycles through the {@code component}'s parents to find the {@link Component} with associated {@link MouseWheelListener}
     */
    private Component getMouseWheelListeningComponent(Component component) {
        if (component.getMouseWheelListeners().length > 0) {
            return component;
        } else {
            Container parent = component.getParent();
            if (parent != null) {
                return getMouseWheelListeningComponent(parent);
            } else {
                return null;
            }
        }
    }

    /**
     * Generate a {@code MOUSE_ENTERED} and {@code MOUSE_EXITED} event when the target component is changed
     */
    private void generateEnterExitEvents( JLayer<? extends JComponent> layer,MouseEvent originalEvent, Component newTarget, Point realPoint) {
        if (lastEnteredTarget != newTarget) {
            dispatchMouseEvent(
                    transformMouseEvent(layer, originalEvent, lastEnteredTarget, realPoint, MouseEvent.MOUSE_EXITED));
            lastEnteredTarget = newTarget;
            //System.out.println("Last " + lastEnteredTarget.getClass().getName());
            dispatchMouseEvent(
                    transformMouseEvent(layer, originalEvent, lastEnteredTarget, realPoint, MouseEvent.MOUSE_ENTERED));
        }
    }
}

ZoomPanel 是一个 JPanel,其中包含一个 JLayer 和一个 TransformUIJLayer 包含一个 JPanel 和一个 SpringLayout,其中使用比例因子更新约束以正确布局组件(JXLayer 有它自己的 LayoutManager 但在 JLayer 中你不能设置它)

package jzoom.transform;

import ...

public class ZoomPanel extends JPanel {

    private static final long serialVersionUID = 1L;

    private AffineTransform transform;
    private TransformUI layerUI;
    private JLayer<JComponent> layer;

    private SpringLayout layout;
    private JPanel springPanel;

    private Container view = null;

    public ZoomPanel() {
        this(null);
    }

    public ZoomPanel(Container view) {

        setLayout(new BorderLayout());

        this.view = view;
        transform = new AffineTransform();

        layout = new SpringLayout();
        springPanel = new JPanel(layout);
        if (view != null) {
            updateConstraints();
            springPanel.add(view);
        }

        layerUI = new TransformUI();
        layerUI.setTransform(transform);
        layer = new JLayer<JComponent>(springPanel, layerUI);

        super.add(layer);

    }

    private void updateConstraints() {
        Spring width = layout.getConstraint(SpringLayout.WIDTH, springPanel);
        Spring height = layout.getConstraint(SpringLayout.HEIGHT, springPanel);

        SpringLayout.Constraints constraints = layout.getConstraints(view);
        constraints.setX(Spring.constant(0));
        constraints.setY(Spring.constant(0));
        constraints.setWidth(Spring.scale(width, (float) (1 / transform.getScaleX())));
        constraints.setHeight(Spring.scale(height, (float) (1 / transform.getScaleX())));
    }

    public void setView(Container view) {
        if (this.view != null) {
            throw new IllegalStateException(
                    this.getClass().getName() + " cannot be shared between multiple containers");
        }

        if (view != null) {
            this.view = view;
            updateConstraints();
            springPanel.add(view);
        } else {
            throw new IllegalArgumentException("Can't set a null view");
        }
    }

    public double getScale() {
        return transform.getScaleX();
    }

    public void zoomIn() {
        setScale(transform.getScaleX() + 0.1);
    }

    public void zoomOut() {
        setScale(transform.getScaleX() - 0.1);
    }

    public void setScale(double scale) {
        if (!(scale < 1)) {
            transform.setToIdentity();
            transform.scale(scale, scale);
            updateConstraints();
            springPanel.updateUI();
        }
    }

    protected Component addToView(Component comp, Object constraints, int index) {
        if (view != null) {
            view.add(comp, constraints, index);
            return comp;
        }

        if (comp instanceof Container) {
            setView((Container) comp);
            return view;
        }

        throw new IllegalStateException("You need to add or set a Container view before adding Components");
    }

    @Override
    public Component add(Component comp) {
        // TODO Auto-generated method stub
        return addToView(comp, null, this.getComponentCount());
    }

    @Override
    public Component add(Component comp, int index) {
        // TODO Auto-generated method stub
        return addToView(comp, null, index);
    }

    @Override
    public void add(Component comp, Object constraints) {
        // TODO Auto-generated method stub
        addToView(comp, constraints, this.getComponentCount());
    }

    @Override
    public void add(Component comp, Object constraints, int index) {
        // TODO Auto-generated method stub
        addToView(comp, constraints, index);
    }

    private void inspectView(Container view) {
        PrintStream ps = null;
        try {
            ps = new PrintStream("C:\Users\andrea.maracci\Documents\sicraReflectionTemp.txt");
            inspectView(view, 0, ps);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (ps != null) {
                ps.close();
            }
        }
    }

    private static void inspectView(Component component, Integer level, PrintStream ps) {
        for (Integer i = 0; i < level; i++) {
            ps.print("\t");
        }
        ps.print(level + ")");
        ps.println("Inspecting " + component.getClass().getName());

        int accessibleCount = 0;
        if (component.getAccessibleContext() != null) {
            accessibleCount = component.getAccessibleContext().getAccessibleChildrenCount();
            if (accessibleCount > 0) {
                ps.println("*********************************************ACCESSIBLE CONTEXT*********************************************");
                for (int i = 0; i < accessibleCount; i++) {
                    ps.println(i + ") " + component.getAccessibleContext().getAccessibleChild(i).getClass().getName());
                }
                ps.println("************************************************************************************************************");
            }
        }

        if (component instanceof JComponent) {
            JComponent jComponent = ((JComponent)component);

            if (jComponent.getComponentCount() > 0) {
                Component[] children = jComponent.getComponents();
                for (Component child : children) {
                    inspectView(child, ++level, ps);
                }
            }
        }
        --level;
    }
}

这是一个可怕的测试程序

package jzoom.test;

import ...

public class TestFinal {
    public static void main(String[] args) {
        new TestFinal();
    }

    public TestFinal() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (UnsupportedLookAndFeelException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setMinimumSize(new Dimension(400,500));
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JLayer<JComponent> layer;
        private TransformUI layerUI;
        private JPanel content;
        private AffineTransform transform = new AffineTransform();
        private ZoomPanel zoomPanel;

        public TestPane() {

            content = new JPanel(new GridBagLayout());
            // content = new JPanel(new XYLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridy = 0;
            gbc.weighty = 0;
            gbc.weightx = 0;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            JLabel label = new JLabel("Hello");
            JTextField field = new JTextField("World", 20);

            content.add(label, gbc);
            gbc.weightx = 1;
            content.add(field, gbc);
            // content.add(label, new XYConstraints(50, 20, 50, 22));
            // content.add(field, new XYConstraints(100, 20, 200, 22));

            gbc.gridy++;
            gbc.gridwidth = 2;

            final JSlider slider = new JSlider(100, 200);
            slider.setValue(100);
            slider.addChangeListener(new ChangeListener() {

                @Override
                public void stateChanged(ChangeEvent e) {
                    int value = slider.getValue();
                    double scale = value / 100d;
                    zoomPanel.setScale(scale);
                }
            });
            content.add(slider, gbc);
            // content.add(slider, new XYConstraints(75, 50, 200, 50));

            gbc.gridy++;
            gbc.gridwidth = 2;
            gbc.weighty = 1;
            gbc.fill = GridBagConstraints.BOTH;
            JTextArea textArea = new JTextArea();
            textArea.setEditable(true);
            textArea.setText(
                    "pollofritto\npalma\npalmipedone\ncaccoletta\namammata\na\nasd\nasdgfag\nasdafa\nasdfasf\nadsfasdf\nadfadsf\nadsfdasf\nasdfdas\npollofritto\npalma\npalmipedone\ncaccoletta\namammata\na\nasd\nasdgfag\nasdafa\nasdfasf\nadsfasdf\nadfadsf\nadsfdasf\nasdfdas");
            // textArea.setPreferredSize(new Dimensions());
            JScrollPane scrollPane = new JScrollPane(textArea);
            scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
            scrollPane.setPreferredSize(new Dimension(200, 75));
            content.add(scrollPane, gbc);

            gbc.gridy++;
            gbc.gridwidth = 2;
            gbc.weighty = 0;
            gbc.weightx = 1;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            String[] petStrings = { "Bird", "Cat", "Dog", "Rabbit", "Pig" };
            JComboBox petList = new JComboBox(petStrings);
            content.add(petList, gbc);

            JButton zoomIn = new JButton("Zoom In");
            // zoomIn.addMouseListener(new ZoomMouseListener());
            zoomIn.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    // System.out.println("FIRST");
                    // layerUI.zoomIn();

                    //double zoom = transform.getScaleX();
                    //transform.setToIdentity();
                    //transform.scale(zoom + 0.1, zoom + 0.1);
                    zoomPanel.zoomIn();

                    // jLayer.repaint();
                }
            });

            JButton zoomOut = new JButton("Zoom Out");
            zoomOut.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    zoomPanel.zoomOut();

                    //double zoom = transform.getScaleX();
                    //transform.setToIdentity();
                    //transform.scale(zoom - 0.1, zoom - 0.1);
                    // jLayer.repaint();
                }
            });
            gbc.gridy++;
            gbc.gridx = 0;
            gbc.gridwidth = 0;
            gbc.anchor = GridBagConstraints.LINE_END;

            // content.add(zoomOut, new XYConstraints(50, 120, 100, 25));
            // content.add(zoomIn, new XYConstraints(170, 120, 100, 25));

            JPanel button = new JPanel();
            button.setLayout(new BoxLayout(button, BoxLayout.LINE_AXIS));
            button.add(zoomOut);
            button.add(zoomIn);
            gbc.fill = GridBagConstraints.NONE;
            content.add(button, gbc);

            setLayout(new BorderLayout());
            setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

            zoomPanel = new ZoomPanel();
            zoomPanel.setView(content);
            add(zoomPanel);
        }
    }
}