/* * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistribution in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * This software is provided "AS IS," without a warranty of any * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY * EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY * DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR * RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE OR * ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, * SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF * THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or * intended for use in the design, construction, operation or * maintenance of any nuclear facility. */ import java.io.*; import java.net.URL; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.border.*; import javax.imageio.*; /** * Simple demonstration of the BorderFill class. Create bordered * descendants of the main window by dragging out rectangular * regions with the mouse. Resize or move descendants by * dragging on an edge or corner with the left mouse button. * A right button menu allows one to change the background color * or change the stacking order. */ public class TestBorderFill extends JPanel { /* MW - "Margin Width". Mouse gestures tha * occur within the margin mean resize or drag. */ private final static int MW = 8; private final static int MW2 = 2 * MW; /* The following constants are used to encode the * eight "margin" areas that will support move or * resize operations. They are: * TL T TR * L R * BL B BR * The corners support resizing up to a minimum size * of MW2 x MW2, and the four edges support * moving. */ private final static int T = 0; /* Top */ private final static int L = 1; /* Left */ private final static int R = 2; /* Right */ private final static int B = 3; /* Bottom */ private final static int TL = 4; /* Top Left */ private final static int TR = 5; /* Top Right */ private final static int BL = 6; /* Bottom Left */ private final static int BR = 7; /* Bottom Right */ /** * Hand press/drag/release by interactively resizing * a child TestBorderFill instance. We also handle * showing the popup menu here. */ private class MouseHandler implements MouseListener, MouseMotionListener { private final JPopupMenu popup; private final Point startPoint = new Point(); private final Rectangle startBounds = new Rectangle(); private int edge = -1; private TestBorderFill drag = null; public MouseHandler(JPopupMenu popup) { this.popup = popup; } /** * Return the edge/corner under the mouse or -1. */ private int toEdge(MouseEvent e) { int x = e.getX(), y = e.getY(); int r = e.getComponent().getWidth() - MW; int b = e.getComponent().getHeight() - MW; if (y < MW) { if (x < MW) { return TL; } else if (x > r) { return TR; } else { return T; } } else if (y > b) { if (x < MW) { return BL; } else if (x > r) { return BR; } else { return B; } } else if (x < MW) { return L; } else if (x > r) { return R; } else { return -1; } } private boolean maybeShowPopup(MouseEvent e) { if (e.isPopupTrigger()) { popup.show(e.getComponent(), e.getX(), e.getY()); return true; } else { return false; } } /* Don't enabled resizing for the top level * TestBorderFill instance - just it's descendants. */ private boolean isResizeEnabled() { return getParent() instanceof TestBorderFill; } public void mousePressed(MouseEvent e) { if (maybeShowPopup(e)) { return; } startPoint.setLocation(e.getX(), e.getY()); startBounds.setBounds(getBounds()); if (isResizeEnabled()) { edge = toEdge(e); } } /** * Clip x,y,w,h to rectangle defined by the insets of the * drag panel's parent, if drag is non null, otherwise * this TestBorderFill's parent. Note that preventing the child * from overlapping the inset area where the parent border is * drawn is essential - Swing doesn't clip a containers children * to the insets area and will not correctly repaint the insets * area when a child that overlaps the border is painted. */ private void setClippedBounds(int x, int y, int w, int h) { Container c = (drag != null) ? drag : TestBorderFill.this; Container p = c.getParent(); Insets insets = p.getInsets(); int x1 = Math.max(x, insets.left); int y1 = Math.max(y, insets.top); int w1 = Math.min(w - (x1 - x), (p.getWidth() - insets.right) - x); int h1 = Math.min(h - (y1 - y), (p.getHeight() - insets.bottom) - y); if ((w1 > 0) && (h1 > 0)) { c.setVisible(true); c.setBounds(x1, y1, w1, h1); } } /** * Clip x,y so that this TestBorderFill panel doesn't * extend outside of the insets area of this.getParent(). */ private void setClippedOrigin(int x, int y) { Container p = getParent(); Insets insets = p.getInsets(); int x1 = Math.min(x, p.getWidth() - (getWidth() + insets.right)); int x2 = Math.max(x1, insets.left); int y1 = Math.min(y, p.getHeight() - (getHeight() + insets.bottom)); int y2 = Math.max(y1, insets.top); setBounds(x2, y2, getWidth(), getHeight()); } /** * If the mouse is being dragged over a T,L,R,B edge than move * this. If the mouse is being dragged over a TL,TR,BL,BR * corner then resize this. Otherwise sweep out a child * TestBorderFill panel. */ public void mouseDragged(MouseEvent e) { int button1Mask = InputEvent.BUTTON1_DOWN_MASK; if ((e.getModifiersEx() & button1Mask) != button1Mask) { return; } int x, y, w, h, r, b; int dx = e.getX() - startPoint.x; int dy = e.getY() - startPoint.y; switch(edge) { case T: case L: case R: case B: setClippedOrigin(getX() + dx, getY() + dy); break; case TL: r = getX() + getWidth(); b = getY() + getHeight(); x = Math.max(0, Math.min(r - MW2, getX() + dx)); y = Math.max(0, Math.min(b - MW2, getY() + dy)); w = Math.abs(r - x); h = Math.abs(b - y); setClippedBounds(x, y, w, h); break; case TR: b = getY() + getHeight(); y = Math.max(0, Math.min(b - MW2, getY() + dy)); h = Math.abs(b - y); w = Math.max(MW2, startBounds.width + dx); setClippedBounds(getX(), y, w, h); break; case BR: w = Math.max(MW2, startBounds.width + dx); h = Math.max(MW2, startBounds.height + dy); setClippedBounds(getX(), getY(), w, h); break; case BL: r = getX() + getWidth(); b = getY() + getHeight(); x = Math.max(0, Math.min(r - MW2, getX() + dx)); w = Math.abs(r - x); h = Math.max(MW2, startBounds.height + dy); setClippedBounds(x, getY(), w, h); break; default: x = Math.min(startPoint.x, e.getX()); y = Math.min(startPoint.y, e.getY()); w = Math.abs(dx); h = Math.abs(dy); if (drag == null) { drag = new TestBorderFill(getBorder(), getBackground()); drag.setVisible(false); add(drag, 0); } setClippedBounds(x, y, w, h); } } public void mouseReleased(MouseEvent e) { maybeShowPopup(e); if (drag != null) { drag = null; edge = -1; } } public void mouseClicked(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mouseMoved(MouseEvent e) { if (!isResizeEnabled()) { return; } int c = Cursor.DEFAULT_CURSOR; switch(toEdge(e)) { case T: case L: case R: case B: c = Cursor.MOVE_CURSOR; break; case TL: c = Cursor.NW_RESIZE_CURSOR; break; case TR: c = Cursor.NE_RESIZE_CURSOR; break; case BL: c = Cursor.SW_RESIZE_CURSOR; break; case BR: c = Cursor.SE_RESIZE_CURSOR; break; } if (getCursor().getType() != c) { setCursor(Cursor.getPredefinedCursor(c)); } } } private void toFront(ActionEvent e) { getParent().add(this, 0); getParent().repaint(); } private void toBack(ActionEvent e) { getParent().add(this); getParent().repaint(); } private void chooseColor(ActionEvent e) { final JColorChooser cc = new JColorChooser(getBackground()); ActionListener ok = new ActionListener() { public void actionPerformed(ActionEvent e) { setBackground(cc.getColor()); SwingUtilities.getWindowAncestor(cc).dispose(); } }; ActionListener cancel = new ActionListener() { public void actionPerformed(ActionEvent e) { SwingUtilities.getWindowAncestor(cc).dispose(); } }; JDialog dialog = JColorChooser.createDialog( /* parent */ this, /* title */ "Background Color", /* modal */ false, /* JCC */ cc, /* OK */ ok, /* Cancel */ cancel); dialog.show(); } /** * Overridden to return false which means that children may overlap. *

* This is a wee bit obscure. */ public boolean isOptimizedDrawingEnabled() { return false; } /** * The constructor sets the "opaque" property to false because our * border has rounded edges - the corner pixels are transparent. * By default components paint their entire background, including * the insets area - which would defeat our niclely rounded corners. * So we only paint within the insets area here. */ protected void paintComponent(Graphics g) { Insets insets = getInsets(); int w = getWidth() - (insets.left + insets.right); int h = getHeight() - (insets.top + insets.bottom); g.setColor(getBackground()); g.fillRect(insets.left, insets.top, w, h); } public TestBorderFill(Border border, Color background) { super(null); setBorder(border); setBackground(background); setOpaque(false); JPopupMenu popup = new JPopupMenu(); Action actions[] = { new AbstractAction("To Front") { public void actionPerformed(ActionEvent e) { toFront(e); } }, new AbstractAction("To Back") { public void actionPerformed(ActionEvent e) { toBack(e); } }, new AbstractAction("Color ...") { public void actionPerformed(ActionEvent e) { chooseColor(e); } } }; for(int i = 0; i < actions.length; i++) { popup.add(actions[i]); } MouseHandler mouseHandler = new MouseHandler(popup); addMouseListener(mouseHandler); addMouseMotionListener(mouseHandler); } public static void main(String[] args) throws Exception { JFrame f = new JFrame("Border Fill"); URL file0 = TestBorderFill.class.getResource("border0.png"); Border border0 = new BorderFill( ImageIO.read(file0), new Rectangle[] { /* 0 */ new Rectangle(32, 0, 10, 21), /* 1 */ new Rectangle(498, 0, 21, 21), /* 2 */ new Rectangle(498, 32, 21, 10), /* 3 */ new Rectangle(498, 202, 21, 21), /* 4 */ new Rectangle(32, 202, 10, 21), /* 5 */ new Rectangle(0, 202, 21, 21), /* 6 */ new Rectangle(0, 32, 21, 10), /* 7 */ new Rectangle(0, 0, 21, 21) }, new boolean[]{true, true, true, true} ); Color background0 = new Color(0xB29F7F); URL file1 = TestBorderFill.class.getResource("border1.png"); Border border1 = new BorderFill( ImageIO.read(file1), new Rectangle[] { /* 0 */ new Rectangle(23, 0, 16, 15), /* 1 */ new Rectangle(109, 0, 22, 15), /* 2 */ new Rectangle(121, 15, 10, 16), /* 3 */ new Rectangle(108, 82, 23, 14), /* 4 */ new Rectangle(24, 82, 16, 14), /* 5 */ new Rectangle(0, 82, 23, 14), /* 6 */ new Rectangle(0, 15, 9, 16), /* 7 */ new Rectangle(0, 0, 23, 15) }, new boolean[]{true, true, true, true} ); Color background1 = Color.white; TestBorderFill p = new TestBorderFill(border0, background0); p.setPreferredSize(new Dimension(640, 480)); f.getContentPane().add(p, BorderLayout.CENTER); WindowListener l = new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }; f.addWindowListener(l); f.pack(); f.setVisible(true); } }