Java tutorial
/* * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, * Santa Clara, California 95054, U.S.A. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; import java.util.Map; import java.util.HashMap; /** * Container which can transform its children, for example:<br> * <pre> * JButton button = new JButton("Hello"); * JXTransformer t = new JXTransformer(button); * t.rotate(Math.PI/2);</pre> * * <strong>Note:</strong> * This component was designed to transform simple components * like JButton, JLabel etc. * * @author Alexander Potochkin * * https://swinghelper.dev.java.net/ * http://weblogs.java.net/blog/alexfromsun/ */ public class JXTransformer extends JPanel { private Component glassPane = new MagicGlassPane(); private Component view; private Rectangle visibleRect; private Map<?,?> renderingHints; private AffineTransform at; public JXTransformer() { this(null); } public JXTransformer(JComponent view) { this(view, new AffineTransform()); } public JXTransformer(JComponent view, AffineTransform at) { super(null); setTransform(at); super.addImpl(glassPane, null, 0); setView(view); Handler handler = new Handler(); addHierarchyBoundsListener(handler); addComponentListener(handler); } public Component getView() { return view; } public void setView(Component view) { if (getView() != null) { super.remove(getView()); } if (view != null) { super.addImpl(view, null, 1); } this.view = view; doLayout(); revalidate(); repaint(); } public Map<?,?> getRenderingHints() { if (renderingHints == null) { return null; } return new HashMap<Object,Object>(renderingHints); } public void setRenderingHints(Map<?,?> renderingHints) { if (renderingHints == null) { this.renderingHints = null; } else { this.renderingHints = new HashMap<Object,Object>(renderingHints); } repaint(); } protected void addImpl(Component comp, Object constraints, int index) { setView(comp); } public void remove(int index) { Component c = getComponent(index); if (c == view) { view = null; super.remove(index); } else if (c == glassPane) { throw new IllegalArgumentException("GlassPane can't be removed"); } else { throw new AssertionError("Unknown component with index " + index); } } public void removeAll() { remove(view); } //This is important public boolean isOptimizedDrawingEnabled() { return false; } public void setLayout(LayoutManager mgr) { if (mgr != null) { throw new IllegalArgumentException("Only null layout is supported"); } super.setLayout(mgr); } public void doLayout() { if (view != null) { view.setSize(view.getPreferredSize()); visibleRect = getVisibleRect(); view.setLocation(visibleRect.x, visibleRect.y); } glassPane.setLocation(0, 0); glassPane.setSize(getWidth(), getHeight()); } public Dimension getPreferredSize() { if (isPreferredSizeSet()) { return super.getPreferredSize(); } Dimension size = getTransformedSize().getSize(); Insets insets = getInsets(); size.width += insets.left + insets.right; size.height += insets.top + insets.bottom; return size; } private Rectangle getTransformedSize() { if (view != null) { Dimension viewSize = view.getSize(); Rectangle viewRect = new Rectangle(viewSize); return at.createTransformedShape(viewRect).getBounds(); } return new Rectangle(super.getPreferredSize()); } public void paint(Graphics g) { //repaint the whole transformer in case the view component was repainted Rectangle clipBounds = g.getClipBounds(); if (clipBounds != null && !clipBounds.equals(visibleRect)) { repaint(); } //clear the background g.setColor(getBackground()); g.fillRect(0, 0, getWidth(), getHeight()); if (view != null && at.getDeterminant() != 0) { Graphics2D g2 = (Graphics2D) g.create(); Insets insets = getInsets(); Rectangle bounds = getBounds(); //don't forget about insets bounds.x += insets.left; bounds.y += insets.top; bounds.width -= insets.left + insets.right; bounds.height -= insets.top + insets.bottom; double centerX1 = bounds.getCenterX(); double centerY1 = bounds.getCenterY(); Rectangle tb = getTransformedSize(); double centerX2 = tb.getCenterX(); double centerY2 = tb.getCenterY(); //set antialiasing by default g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); if (renderingHints != null) { g2.addRenderingHints(renderingHints); } //translate it to the center of the view component again double tx = centerX1 - centerX2 - getX(); double ty = centerY1 - centerY2 - getY(); g2.translate((int) tx, (int) ty); g2.transform(at); view.paint(g2); g2.dispose(); } //paint the border paintBorder(g); } private class MagicGlassPane extends JPanel { private Component mouseEnteredComponent; private Component mouseDraggedComponent; private Component mouseCurrentComponent; public MagicGlassPane() { super(null); setOpaque(false); enableEvents(AWTEvent.MOUSE_EVENT_MASK); enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK); enableEvents(AWTEvent.MOUSE_WHEEL_EVENT_MASK); ToolTipManager.sharedInstance().registerComponent(this); } private MouseEvent transformMouseEvent(MouseEvent event) { if (event == null) { throw new IllegalArgumentException("MouseEvent is null"); } MouseEvent newEvent; if (event instanceof MouseWheelEvent) { MouseWheelEvent mouseWheelEvent = (MouseWheelEvent) event; newEvent = new MouseWheelEvent(mouseWheelEvent.getComponent(), mouseWheelEvent.getID(), mouseWheelEvent.getWhen(), mouseWheelEvent.getModifiers(), mouseWheelEvent.getX(), mouseWheelEvent.getY(), mouseWheelEvent.getClickCount(), mouseWheelEvent.isPopupTrigger(), mouseWheelEvent.getScrollType(), mouseWheelEvent.getScrollAmount(), mouseWheelEvent.getWheelRotation()); } else { newEvent = new MouseEvent(event.getComponent(), event.getID(), event.getWhen(), event.getModifiers(), event.getX(), event.getY(), event.getClickCount(), event.isPopupTrigger(), event.getButton()); } if (view != null && at.getDeterminant() != 0) { Rectangle viewBounds = getTransformedSize(); Insets insets = JXTransformer.this.getInsets(); int xgap = (getWidth() - (viewBounds.width + insets.left + insets.right)) / 2; int ygap = (getHeight() - (viewBounds.height + insets.top + insets.bottom)) / 2; double x = newEvent.getX() + viewBounds.getX() - insets.left; double y = newEvent.getY() + viewBounds.getY() - insets.top; Point2D p = new Point2D.Double(x - xgap, y - ygap); Point2D tp; try { tp = at.inverseTransform(p, null); } catch (NoninvertibleTransformException ex) { //can't happen, we check it before throw new AssertionError("NoninvertibleTransformException"); } //Use transformed coordinates to get the current component mouseCurrentComponent = SwingUtilities.getDeepestComponentAt(view, (int) tp.getX(), (int) tp.getY()); if (mouseCurrentComponent == null) { mouseCurrentComponent = JXTransformer.this; } Component tempComponent = mouseCurrentComponent; if (mouseDraggedComponent != null) { tempComponent = mouseDraggedComponent; } Point point = SwingUtilities.convertPoint(view, (int) tp.getX(), (int) tp.getY(), tempComponent); newEvent.setSource(tempComponent); newEvent.translatePoint(point.x - event.getX(), point.y - event.getY()); } return newEvent; } protected void processMouseEvent(MouseEvent e) { MouseEvent transformedEvent = transformMouseEvent(e); switch (e.getID()) { case MouseEvent.MOUSE_ENTERED: if (mouseDraggedComponent == null || mouseCurrentComponent == mouseDraggedComponent) { dispatchMouseEvent(transformedEvent); } break; case MouseEvent.MOUSE_EXITED: if (mouseEnteredComponent != null) { dispatchMouseEvent(createEnterExitEvent(mouseEnteredComponent, MouseEvent.MOUSE_EXITED, e)); mouseEnteredComponent = null; } break; case MouseEvent.MOUSE_RELEASED: if (mouseDraggedComponent != null && e.getButton() == MouseEvent.BUTTON1) { transformedEvent.setSource(mouseDraggedComponent); mouseDraggedComponent = null; } dispatchMouseEvent(transformedEvent); break; default: dispatchMouseEvent(transformedEvent); } super.processMouseEvent(e); } private void dispatchMouseEvent(MouseEvent event) { MouseListener[] mouseListeners = event.getComponent().getMouseListeners(); for (MouseListener listener : mouseListeners) { //skip all ToolTipManager's related listeners if (!listener.getClass().getName().startsWith("javax.swing.ToolTipManager")) { switch (event.getID()) { case MouseEvent.MOUSE_PRESSED: listener.mousePressed(event); break; case MouseEvent.MOUSE_RELEASED: listener.mouseReleased(event); break; case MouseEvent.MOUSE_CLICKED: listener.mouseClicked(event); break; case MouseEvent.MOUSE_EXITED: listener.mouseExited(event); break; case MouseEvent.MOUSE_ENTERED: listener.mouseEntered(event); break; default: throw new AssertionError(); } } } } protected void processMouseMotionEvent(MouseEvent e) { MouseEvent transformedEvent = transformMouseEvent(e); if (mouseEnteredComponent == null) { mouseEnteredComponent = mouseCurrentComponent; } switch (e.getID()) { case MouseEvent.MOUSE_MOVED: if (mouseCurrentComponent != mouseEnteredComponent) { dispatchMouseEvent(createEnterExitEvent(mouseEnteredComponent, MouseEvent.MOUSE_EXITED, e)); dispatchMouseEvent(createEnterExitEvent(mouseCurrentComponent, MouseEvent.MOUSE_ENTERED, e)); } break; case MouseEvent.MOUSE_DRAGGED: if (mouseDraggedComponent == null) { mouseDraggedComponent = mouseEnteredComponent; } if (mouseEnteredComponent == mouseDraggedComponent && mouseCurrentComponent != mouseDraggedComponent) { dispatchMouseEvent(createEnterExitEvent(mouseDraggedComponent, MouseEvent.MOUSE_EXITED, e)); } else if (mouseEnteredComponent != mouseDraggedComponent && mouseCurrentComponent == mouseDraggedComponent) { dispatchMouseEvent(createEnterExitEvent(mouseDraggedComponent, MouseEvent.MOUSE_ENTERED, e)); } if (mouseDraggedComponent != null) { transformedEvent.setSource(mouseDraggedComponent); } break; } mouseEnteredComponent = mouseCurrentComponent; //dispatch MouseMotionEvent MouseMotionListener[] mouseMotionListeners = transformedEvent.getComponent().getMouseMotionListeners(); for (MouseMotionListener listener : mouseMotionListeners) { //skip all ToolTipManager's related listeners if (!listener.getClass().getName().startsWith("javax.swing.ToolTipManager")) { switch (transformedEvent.getID()) { case MouseEvent.MOUSE_MOVED: listener.mouseMoved(transformedEvent); break; case MouseEvent.MOUSE_DRAGGED: listener.mouseDragged(transformedEvent); break; default: throw new AssertionError(); } } } super.processMouseMotionEvent(e); } protected void processMouseWheelEvent(MouseWheelEvent e) { MouseWheelEvent transformedEvent = (MouseWheelEvent) transformMouseEvent(e); MouseWheelListener[] mouseWheelListeners = transformedEvent.getComponent().getMouseWheelListeners(); for (MouseWheelListener listener : mouseWheelListeners) { listener.mouseWheelMoved(transformedEvent); } super.processMouseWheelEvent(e); } public String getToolTipText(MouseEvent event) { if (mouseEnteredComponent instanceof JComponent) { return ((JComponent) mouseEnteredComponent).getToolTipText(); } return null; } private MouseEvent createEnterExitEvent(Component c, int eventId, MouseEvent mouseEvent) { return new MouseEvent(c, eventId, mouseEvent.getWhen(), 0, mouseEvent.getX(), mouseEvent.getY(), 0, false, MouseEvent.NOBUTTON); } public String toString() { return "GlassPane"; } } /** * This class helps view component to be in the visible area; * this is important when transformer is inside JScrollPane */ private class Handler extends ComponentAdapter implements HierarchyBoundsListener { public void componentMoved(ComponentEvent e) { update(); } public void ancestorMoved(HierarchyEvent e) { update(); } public void ancestorResized(HierarchyEvent e) { update(); } private void update() { if (!getVisibleRect().equals(visibleRect)) { revalidate(); } } } /** * Never returns null */ public AffineTransform getTransform() { return new AffineTransform(at); } public void setTransform(AffineTransform at) { if (at == null) { throw new IllegalArgumentException("AffineTransform is null"); } this.at = new AffineTransform(at); revalidate(); repaint(); } public void rotate(double theta) { AffineTransform transform = getTransform(); transform.rotate(theta); setTransform(transform); } public void scale(double sx, double sy) { AffineTransform transform = getTransform(); transform.scale(sx, sy); setTransform(transform); } public void shear(double sx, double sy) { AffineTransform transform = getTransform(); transform.shear(sx, sy); setTransform(transform); } } ///////////// /* * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, * Santa Clara, California 95054, U.S.A. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ import javax.swing.*; import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.*; import java.awt.event.*; import java.awt.geom.AffineTransform; import java.util.ArrayList; import java.util.List; import java.util.Vector; /* * @author Alexander Potochkin * * https://swinghelper.dev.java.net/ * http://weblogs.java.net/blog/alexfromsun/ */ class TransformerDemo extends JFrame implements ChangeListener { private List<JXTransformer> transformers = new ArrayList<JXTransformer>(); private JSlider rotationSlider; private JSlider scalingSlider; private JSlider shearingSlider; public TransformerDemo() { super("Transformer demo"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JMenuBar bar = new JMenuBar(); JMenu lafMenu = new JMenu("LaF"); JMenuItem winLaf = new JMenuItem("Windows LaF"); lafMenu.add(winLaf); winLaf.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setLaf("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); } }); JMenuItem motifLaf = new JMenuItem("Motif LaF"); lafMenu.add(motifLaf); motifLaf.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setLaf("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); } }); bar.add(lafMenu); JMenuItem metalLaf = new JMenuItem("Metal LaF"); lafMenu.add(metalLaf); metalLaf.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setLaf("javax.swing.plaf.metal.MetalLookAndFeel"); } }); JMenu settingsMenu = new JMenu("Settings"); settingsMenu.setMnemonic(KeyEvent.VK_S); JMenuItem item = new JMenuItem("Reset sliders", KeyEvent.VK_R); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.ALT_MASK)); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { rotationSlider.setValue(0); scalingSlider.setValue(100); shearingSlider.setValue(0); } }); settingsMenu.add(item); bar.add(settingsMenu); setJMenuBar(bar); JPanel panel = new JPanel(new BorderLayout()); panel.add(createDemoPanel()); panel.add(createStressTestPanel(), BorderLayout.EAST); add(new JScrollPane(panel)); add(new JScrollPane(createToolPanel()), BorderLayout.SOUTH); pack(); } private void setLaf(String laf) { try { UIManager.setLookAndFeel(laf); SwingUtilities.updateComponentTreeUI(this); } catch (Exception e) { e.printStackTrace(); } for (JXTransformer t : transformers) { t.revalidate(); t.repaint(); } } private JPanel createStressTestPanel() { JPanel panel = new JPanel(); TitledBorder titledBorder = BorderFactory.createTitledBorder("Stress test (with tooltips)"); titledBorder.setTitleJustification(TitledBorder.CENTER); panel.setBorder(titledBorder); JButton lowerButton = new JButton("Button"); lowerButton.setLayout(new FlowLayout()); lowerButton.setToolTipText("Lower button"); JButton middleButton = new JButton(); middleButton.setToolTipText("Middle button"); middleButton.setLayout(new FlowLayout()); lowerButton.add(middleButton); JButton upperButton = new JButton("Upper button"); upperButton.setToolTipText("Upper button"); middleButton.add(upperButton); panel.add(createTransformer(lowerButton)); return panel; } private JPanel createDemoPanel() { JPanel buttonPanel = new JPanel(new GridLayout(3, 2)); TitledBorder titledBorder = BorderFactory.createTitledBorder("Try three sliders below !"); Font titleFont = titledBorder.getTitleFont(); titledBorder.setTitleFont(titleFont.deriveFont(titleFont.getSize2D() + 10)); titledBorder.setTitleJustification(TitledBorder.CENTER); buttonPanel.setBorder(titledBorder); JButton b = new JButton("JButton"); b.setPreferredSize(new Dimension(100, 50)); buttonPanel.add(createTransformer(b)); Vector<String> v = new Vector<String>(); v.add("One"); v.add("Two"); v.add("Three"); JList list = new JList(v); buttonPanel.add(createTransformer(list)); buttonPanel.add(createTransformer(new JCheckBox("JCheckBox"))); JSlider slider = new JSlider(0, 100); slider.setLabelTable(slider.createStandardLabels(25, 0)); slider.setPaintLabels(true); slider.setPaintTicks(true); slider.setMajorTickSpacing(10); buttonPanel.add(createTransformer(slider)); buttonPanel.add(createTransformer(new JRadioButton("JRadioButton"))); final JLabel label = new JLabel("JLabel"); label.addMouseListener(new MouseAdapter() { public void mouseEntered(MouseEvent e) { Font font = label.getFont(); label.setFont(font.deriveFont(font.getSize2D() + 10)); } public void mouseExited(MouseEvent e) { Font font = label.getFont(); label.setFont(font.deriveFont(font.getSize2D() - 10)); } }); buttonPanel.add(createTransformer(label)); return buttonPanel; } private JXTransformer createTransformer(JComponent c) { JXTransformer t = new JXTransformer(c); transformers.add(t); return t; } private JPanel createToolPanel() { JPanel panel = new JPanel(new GridLayout(1, 0)); rotationSlider = new JSlider(-180, 180, 0); rotationSlider.setLabelTable(rotationSlider.createStandardLabels(90, -180)); rotationSlider.setPaintLabels(true); rotationSlider.setPaintTicks(true); rotationSlider.setMajorTickSpacing(45); rotationSlider.addChangeListener(this); rotationSlider.setBorder(BorderFactory.createTitledBorder("Rotate")); panel.add(rotationSlider); shearingSlider = new JSlider(-10, 10, 0); shearingSlider.setLabelTable(shearingSlider.createStandardLabels(5, -10)); shearingSlider.setPaintLabels(true); shearingSlider.setPaintTicks(true); shearingSlider.setMajorTickSpacing(2); shearingSlider.addChangeListener(this); shearingSlider.setBorder(BorderFactory.createTitledBorder("Shear")); panel.add(shearingSlider); scalingSlider = new JSlider(50, 150, 100); scalingSlider.setLabelTable(scalingSlider.createStandardLabels(25, 50)); scalingSlider.setPaintLabels(true); scalingSlider.setPaintTicks(true); scalingSlider.setMajorTickSpacing(10); scalingSlider.addChangeListener(this); scalingSlider.setBorder(BorderFactory.createTitledBorder("Scale")); panel.add(scalingSlider); return panel; } public void stateChanged(ChangeEvent e) { AffineTransform at = new AffineTransform(); at.rotate(rotationSlider.getValue() * Math.PI / 180); double scale = scalingSlider.getValue() / 100.0; at.scale(scale, scale); double shear = shearingSlider.getValue() / 10.0; at.shear(shear, 0); for (JXTransformer t : transformers) { t.setTransform(at); } } public static void main(String[] args) { TransformerDemo demo = new TransformerDemo(); demo.setSize(800, 600); demo.setVisible(true); } }