Background Animation
/* * Copyright (c) 2007, Romain Guy * 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. * * Redistributions 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 the TimingFramework project nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import java.awt.AlphaComposite; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Composite; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridBagLayout; import java.awt.HeadlessException; import java.awt.Image; import java.awt.Insets; import java.awt.LayoutManager; import java.awt.LayoutManager2; import java.awt.Paint; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.imageio.ImageIO; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSeparator; import javax.swing.JSlider; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /** * @author Romain Guy */ public class BackgroundAnimation_2008 extends JFrame { private AvatarChooser chooser; private CurvesPanel curves; public BackgroundAnimation_2008() throws HeadlessException { super("Stack Layout"); buildContentPane(); // buildDebugControls(); startAnimation(); setSize(640, 400); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } private void startAnimation() { Timer timer = new Timer(50, new ActionListener() { public void actionPerformed(ActionEvent e) { curves.animate(); curves.repaint(); } }); timer.start(); } private void buildDebugControls() { JPanel pane = new JPanel(new BorderLayout()); pane.setBackground(Color.WHITE); pane.add(new JSeparator(), BorderLayout.NORTH); final GraphPanel grapher = new GraphPanel(); JPanel graphPane = new JPanel(new FlowLayout(FlowLayout.CENTER)); graphPane.setOpaque(false); graphPane.add(grapher); JPanel buttonsPane = new JPanel(new FlowLayout(FlowLayout.CENTER)); buttonsPane.setOpaque(false); JSlider spacing = new JSlider(JSlider.HORIZONTAL, 0, 100, 40); spacing.setBackground(Color.WHITE); spacing.setPreferredSize(new Dimension(95, spacing.getPreferredSize().height)); spacing.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { JSlider slider = (JSlider) e.getSource(); double spacing = slider.getValue() / 100.0; chooser.setSpacing(spacing); grapher.spacing = spacing; grapher.repaint(); } }); JSlider sigma = new JSlider(JSlider.HORIZONTAL, 0, 100, 50); sigma.setBackground(Color.WHITE); sigma.setPreferredSize(new Dimension(95, sigma.getPreferredSize().height)); sigma.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { JSlider slider = (JSlider) e.getSource(); double sigma = slider.getValue() / 100.0; chooser.setSigma(sigma); grapher.sigma = sigma; grapher.repaint(); } }); JSlider position = new JSlider(JSlider.HORIZONTAL, -100, 100, 0); position.setBackground(Color.WHITE); position.setPreferredSize(new Dimension(95, position.getPreferredSize().height)); position.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { JSlider slider = (JSlider) e.getSource(); double position = slider.getValue() / 100.0; chooser.setPosition(position); grapher.position = position; grapher.repaint(); } }); JSlider amount = new JSlider(JSlider.HORIZONTAL, 1, 15, 5); amount.setBackground(Color.WHITE); amount.setPreferredSize(new Dimension(95, position.getPreferredSize().height)); amount.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { JSlider slider = (JSlider) e.getSource(); int amount = slider.getValue(); chooser.setAmount(amount); grapher.amount = amount; grapher.repaint(); } }); buttonsPane.add(new JLabel("Spacing: ")); buttonsPane.add(spacing); buttonsPane.add(new JLabel("Sigma: ")); buttonsPane.add(sigma); buttonsPane.add(new JLabel("Position: ")); buttonsPane.add(position); buttonsPane.add(new JLabel("Amount: ")); buttonsPane.add(amount); pane.add(buttonsPane, BorderLayout.NORTH); pane.add(graphPane, BorderLayout.CENTER); add(pane, BorderLayout.SOUTH); } private void buildContentPane() { JPanel pane = new JPanel(); pane.setLayout(new StackLayout()); GradientPanel gradient = new GradientPanel(); chooser = new AvatarChooser(); curves = new CurvesPanel(); pane.add(gradient, StackLayout.TOP); pane.add(chooser, StackLayout.TOP); pane.add(curves, StackLayout.TOP); add(pane); } private class GraphPanel extends JComponent { private double spacing = 0.4; private double position = 0.0; private double sigma = 0.5; private int amount = 5; @Override public Dimension getPreferredSize() { return new Dimension(200, 60); } @Override public boolean isOpaque() { return false; } @Override protected void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(Color.BLACK); g2.drawLine(0, 50, 100, 50); g2.drawLine(50, 0, 50, 60); g2.setColor(Color.BLUE); double lastY = 50.0; for (int x = 0; x < 100; x++) { double y = chooser.computeModifier((50.0 - x) / 25.0) * 45.0 + 10; if (x == 0) { lastY = y; } g2.drawLine(x - 1, 60 - (int) lastY, x, 60 - (int) y); lastY = y; } g2.setColor(Color.RED); for (int i = 0; i < amount; i++) { double offset = ((amount / 2) - i) * spacing; double x = (100.0 - 5.0) / 2.0; x += 25.0 * (position + offset); if (x > 100) { continue; } double y = 60.0 - (chooser.computeModifier(position + offset) * 45.0 + 10); g2.fill(new Rectangle2D.Double(x, y - 1, 5.0, 5.0)); } g2.setColor(Color.GREEN.darker()); g2.drawLine(25, 0, 25, 60); g2.drawLine(75, 0, 75, 60); g2.setColor(Color.DARK_GRAY); g2.drawString("Sigma: " + sigma, 110.0f, 16.0f); g2.drawString("Spacing: " + spacing, 110.0f, 30.0f); g2.drawString("Position: " + position, 110.0f, 44.0f); } } public static void main(String[] args) { try { UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); } catch (ClassNotFoundException ex) { ex.printStackTrace(); } catch (IllegalAccessException ex) { ex.printStackTrace(); } catch (InstantiationException ex) { ex.printStackTrace(); } catch (UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } SwingUtilities.invokeLater(new Runnable() { public void run() { BackgroundAnimation_2008 tester = new BackgroundAnimation_2008(); tester.setVisible(true); } }); } } /* * Copyright (c) 2007, Romain Guy 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. * Redistributions 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 the * TimingFramework project nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ class AvatarChooser extends JPanel { private static final double ANIM_SCROLL_DELAY = 450; private List<Image> avatars = null; private boolean loadingDone = false; private Thread picturesFinder = null; private Timer scrollerTimer = null; private Timer faderTimer = null; private Timer passwordTimer; private float veilAlphaLevel = 0.0f; private float alphaLevel = 0.0f; private float textAlphaLevel = 0.0f; private int avatarIndex; private double avatarPosition = 0.0; private double avatarSpacing = 0.4; private int avatarAmount = 5; private double sigma; private double rho; private double exp_multiplier; private double exp_member; private boolean damaged = true; private DrawableAvatar[] drawableAvatars; private String textAvatar; private FocusGrabber focusGrabber; private AvatarScroller avatarScroller; private CursorChanger cursorChanger; private MouseWheelScroller wheelScroller; private KeyScroller keyScroller; public AvatarChooser() { GridBagLayout layout = new GridBagLayout(); setLayout(layout); findAvatars(); setSigma(0.5); addComponentListener(new DamageManager()); initInputListeners(); addInputListeners(); } public void setAmount(int amount) { if (amount > avatars.size()) { throw new IllegalArgumentException("Too many avatars"); } this.avatarAmount = amount; repaint(); } // XXX package access for debugging purpose only void setPosition(double position) { this.avatarPosition = position; this.damaged = true; repaint(); } public void setSigma(double sigma) { this.sigma = sigma; this.rho = 1.0; computeEquationParts(); this.rho = computeModifierUnprotected(0.0); computeEquationParts(); this.damaged = true; repaint(); } public void setSpacing(double avatarSpacing) { if (avatarSpacing < 0.0 || avatarSpacing > 1.0) { throw new IllegalArgumentException("Spacing must be < 1.0 and > 0.0"); } this.avatarSpacing = avatarSpacing; this.damaged = true; repaint(); } @Override public Dimension getPreferredSize() { return new Dimension(128 * 3, 128 * 2); } @Override public Dimension getMaximumSize() { return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); } @Override public boolean isOpaque() { return false; } @Override public boolean isFocusable() { return true; } @Override protected void paintChildren(Graphics g) { Graphics2D g2 = (Graphics2D) g; Composite oldComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, veilAlphaLevel)); super.paintChildren(g); g2.setComposite(oldComposite); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if (!loadingDone && faderTimer == null) { return; } Insets insets = getInsets(); int x = insets.left; int y = insets.top; int width = getWidth() - insets.left - insets.right; int height = getHeight() - insets.top - insets.bottom; Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Composite oldComposite = g2.getComposite(); if (damaged) { drawableAvatars = sortAvatarsByDepth(x, y, width, height); damaged = false; } drawAvatars(g2, drawableAvatars); if (drawableAvatars.length > 0) { drawAvatarName(g2); } g2.setComposite(oldComposite); } private void drawAvatars(Graphics2D g2, DrawableAvatar[] drawableAvatars) { for (DrawableAvatar avatar : drawableAvatars) { AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) avatar .getAlpha()); g2.setComposite(composite); g2.drawImage(avatars.get(avatar.getIndex()), (int) avatar.getX(), (int) avatar.getY(), avatar .getWidth(), avatar.getHeight(), null); } } private DrawableAvatar[] sortAvatarsByDepth(int x, int y, int width, int height) { List<DrawableAvatar> drawables = new LinkedList<DrawableAvatar>(); for (int i = 0; i < avatars.size(); i++) { promoteAvatarToDrawable(drawables, x, y, width, height, i - avatarIndex); } DrawableAvatar[] drawableAvatars = new DrawableAvatar[drawables.size()]; drawableAvatars = drawables.toArray(drawableAvatars); Arrays.sort(drawableAvatars); return drawableAvatars; } private void drawAvatarName(Graphics2D g2) { Composite composite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, textAlphaLevel)); double bulletWidth = 150.0; double bulletHeight = 30.0; double x = (getWidth() - bulletWidth) / 2.0; double y = (getHeight() - 164) / 2.0 - bulletHeight * 1.4; drawAvatarBullet(g2, x, y, bulletWidth, bulletHeight); drawAvatarText(g2, y, bulletHeight); g2.setComposite(composite); } private void drawAvatarText(Graphics2D g2, double y, double bulletHeight) { FontRenderContext context = g2.getFontRenderContext(); Font font = new Font("Dialog", Font.PLAIN, 18); TextLayout layout = new TextLayout(textAvatar, font, context); Rectangle2D bounds = layout.getBounds(); float text_x = (float) ((getWidth() - bounds.getWidth()) / 2.0); float text_y = (float) (y + (bulletHeight - layout.getAscent() - layout.getDescent()) / 2.0) + layout.getAscent() - layout.getLeading(); g2.setColor(Color.BLACK); layout.draw(g2, text_x, text_y + 1); g2.setColor(Color.WHITE); layout.draw(g2, text_x, text_y); } private void drawAvatarBullet(Graphics2D g2, double x, double y, double bulletWidth, double bulletHeight) { RoundRectangle2D bullet = new RoundRectangle2D.Double(0.0, 0.0, bulletWidth, bulletHeight, bulletHeight, bulletHeight); Ellipse2D curve = new Ellipse2D.Double(-20.0, bulletHeight / 2.0, bulletWidth + 40.0, bulletHeight); g2.translate(x, y); g2.translate(-1, -2); g2.setColor(new Color(0, 0, 0, 170)); g2.fill(new RoundRectangle2D.Double(0.0, 0.0, bulletWidth + 2, bulletHeight + 4, bulletHeight + 4, bulletHeight + 4)); g2.translate(1, 2); Color startColor = new Color(10, 0, 40); Color endColor = new Color(175, 165, 225); Paint paint = g2.getPaint(); g2.setPaint(new GradientPaint(0.0f, 0.0f, startColor, 0.0f, (float) bulletHeight, endColor)); g2.fill(bullet); startColor = new Color(5, 0, 50); endColor = new Color(105, 100, 155); g2.setPaint(new GradientPaint(0.0f, 0.0f, startColor, 0.0f, (float) bulletHeight, endColor)); Area area = new Area(bullet); area.intersect(new Area(curve)); g2.fill(area); g2.setPaint(paint); g2.translate(-x, -y); } private void promoteAvatarToDrawable(List<DrawableAvatar> drawables, int x, int y, int width, int height, int offset) { double spacing = offset * avatarSpacing; double avatarPosition = this.avatarPosition + spacing; if (avatarIndex + offset < 0 || avatarIndex + offset >= avatars.size()) { return; } Image avatar = avatars.get(avatarIndex + offset); int avatarWidth = avatar.getWidth(null); int avatarHeight = avatar.getHeight(null); double result = computeModifier(avatarPosition); int newWidth = (int) (avatarWidth * result); if (newWidth == 0) { return; } int newHeight = (int) (avatarHeight * result); if (newHeight == 0) { return; } double avatar_x = x + (width - newWidth) / 2.0; double avatar_y = y + (height - newHeight / 2.0) / 2.0; double semiWidth = width / 2.0; avatar_x += avatarPosition * semiWidth; if (avatar_x >= width || avatar_x < -newWidth) { return; } drawables.add(new DrawableAvatar(avatarIndex + offset, avatar_x, avatar_y, newWidth, newHeight, avatarPosition, result)); } private void startFader() { faderTimer = new Timer(35, new FaderAction()); faderTimer.start(); } private void computeEquationParts() { exp_multiplier = Math.sqrt(2.0 * Math.PI) / sigma / rho; exp_member = 4.0 * sigma * sigma; } // XXX package access for debug purpose only double computeModifier(double x) { double result = computeModifierUnprotected(x); if (result > 1.0) { result = 1.0; } else if (result < -1.0) { result = -1.0; } return result; } private double computeModifierUnprotected(double x) { return exp_multiplier * Math.exp((-x * x) / exp_member); } private void addInputListeners() { addMouseListener(focusGrabber); addMouseListener(avatarScroller); addMouseMotionListener(cursorChanger); addMouseWheelListener(wheelScroller); addKeyListener(keyScroller); } private void initInputListeners() { // input listeners are all stateless // hence they can be instantiated once focusGrabber = new FocusGrabber(); avatarScroller = new AvatarScroller(); cursorChanger = new CursorChanger(); wheelScroller = new MouseWheelScroller(); keyScroller = new KeyScroller(); } private void removeInputListeners() { removeMouseListener(focusGrabber); removeMouseListener(avatarScroller); removeMouseMotionListener(cursorChanger); removeMouseWheelListener(wheelScroller); removeKeyListener(keyScroller); } private void findAvatars() { avatars = new ArrayList<Image>(); picturesFinder = new Thread(new PicturesFinderThread()); picturesFinder.start(); } private void setAvatarIndex(int index) { avatarIndex = index; textAvatar = "Photo " + index; } private void scrollBy(int increment) { if (loadingDone) { setAvatarIndex(avatarIndex + increment); if (avatarIndex < 0) { setAvatarIndex(0); } else if (avatarIndex >= avatars.size()) { setAvatarIndex(avatars.size() - 1); } damaged = true; repaint(); } } private void scrollAndAnimateBy(int increment) { if (loadingDone && (scrollerTimer == null || !scrollerTimer.isRunning())) { int index = avatarIndex + increment; if (index < 0) { index = 0; } else if (index >= avatars.size()) { index = avatars.size() - 1; } DrawableAvatar drawable = null; for (DrawableAvatar avatar : drawableAvatars) { if (avatar.index == index) { drawable = avatar; break; } } if (drawable != null) { scrollAndAnimate(drawable); } } } private void scrollAndAnimate(DrawableAvatar avatar) { if (loadingDone) { scrollerTimer = new Timer(10, new AutoScroller(avatar)); scrollerTimer.start(); } } private BufferedImage createReflectedPicture(BufferedImage avatar) { int avatarWidth = avatar.getWidth(); int avatarHeight = avatar.getHeight(); BufferedImage alphaMask = createGradientMask(avatarWidth, avatarHeight); return createReflectedPicture(avatar, alphaMask); } private BufferedImage createReflectedPicture(BufferedImage avatar, BufferedImage alphaMask) { int avatarWidth = avatar.getWidth(); int avatarHeight = avatar.getHeight(); BufferedImage buffer = createReflection(avatar, avatarWidth, avatarHeight); applyAlphaMask(buffer, alphaMask, avatarWidth, avatarHeight); return buffer; } private void applyAlphaMask(BufferedImage buffer, BufferedImage alphaMask, int avatarWidth, int avatarHeight) { Graphics2D g2 = buffer.createGraphics(); g2.setComposite(AlphaComposite.DstOut); g2.drawImage(alphaMask, null, 0, avatarHeight); g2.dispose(); } private BufferedImage createReflection(BufferedImage avatar, int avatarWidth, int avatarHeight) { BufferedImage buffer = new BufferedImage(avatarWidth, avatarHeight << 1, BufferedImage.TYPE_INT_ARGB); Graphics2D g = buffer.createGraphics(); g.drawImage(avatar, null, null); g.translate(0, avatarHeight << 1); AffineTransform reflectTransform = AffineTransform.getScaleInstance(1.0, -1.0); g.drawImage(avatar, reflectTransform, null); g.translate(0, -(avatarHeight << 1)); g.dispose(); return buffer; } private BufferedImage createGradientMask(int avatarWidth, int avatarHeight) { BufferedImage gradient = new BufferedImage(avatarWidth, avatarHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D g = gradient.createGraphics(); GradientPaint painter = new GradientPaint(0.0f, 0.0f, new Color(1.0f, 1.0f, 1.0f, 0.5f), 0.0f, avatarHeight / 2.0f, new Color(1.0f, 1.0f, 1.0f, 1.0f)); g.setPaint(painter); g.fill(new Rectangle2D.Double(0, 0, avatarWidth, avatarHeight)); g.dispose(); return gradient; } private DrawableAvatar getHitAvatar(int x, int y) { for (DrawableAvatar avatar : drawableAvatars) { Rectangle hit = new Rectangle((int) avatar.getX(), (int) avatar.getY(), avatar.getWidth(), avatar.getHeight() / 2); if (hit.contains(x, y)) { return avatar; } } return null; } private class PicturesFinderThread implements Runnable { public void run() { UnixGlobFileFilter allPngs = new UnixGlobFileFilter("*.jpg"); try { FileTreeWalker walker = new FileTreeWalker(new File("."), allPngs); PictureLoader loader = new PictureLoader(); walker.walk(loader); List<File> files = loader.getFilesList(); for (int i = 0; i < files.size(); i++) { BufferedImage image = ImageIO.read(files.get(i)); avatars.add(createReflectedPicture(image)); if (i == (files.size() / 2) + avatarAmount / 2) { setAvatarIndex(i - avatarAmount / 2); startFader(); } } } catch (IOException e) { } loadingDone = true; } private class PictureLoader implements FileTreeWalk { private List<File> filesList; private PictureLoader() { filesList = new ArrayList<File>(); } private List<File> getFilesList() { Collections.sort(filesList); return filesList; } public void walk(File path) { filesList.add(path); } } } private class FaderAction implements ActionListener { private long start = 0; private FaderAction() { alphaLevel = 0.0f; textAlphaLevel = 0.0f; } public void actionPerformed(ActionEvent e) { if (start == 0) { start = System.currentTimeMillis(); } alphaLevel = (System.currentTimeMillis() - start) / 500.0f; textAlphaLevel = alphaLevel; if (alphaLevel > 1.0f) { alphaLevel = 1.0f; textAlphaLevel = 1.0f; faderTimer.stop(); } repaint(); } } private class DrawableAvatar implements Comparable { private int index; private double x; private double y; private int width; private int height; private double zOrder; private double position; private DrawableAvatar(int index, double x, double y, int width, int height, double position, double zOrder) { this.index = index; this.x = x; this.y = y; this.width = width; this.height = height; this.position = position; this.zOrder = zOrder; } public int compareTo(Object o) { double zOrder2 = ((DrawableAvatar) o).zOrder; if (zOrder < zOrder2) { return -1; } else if (zOrder > zOrder2) { return 1; } return 0; } public double getPosition() { return position; } public double getAlpha() { return zOrder * alphaLevel; } public int getHeight() { return height; } public int getIndex() { return index; } public int getWidth() { return width; } public double getX() { return x; } public double getY() { return y; } } private class MouseWheelScroller implements MouseWheelListener { public void mouseWheelMoved(MouseWheelEvent e) { int increment = e.getWheelRotation(); scrollAndAnimateBy(increment); } } private class KeyScroller extends KeyAdapter { @Override public void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); switch (keyCode) { case KeyEvent.VK_LEFT: case KeyEvent.VK_UP: scrollAndAnimateBy(-1); break; case KeyEvent.VK_RIGHT: case KeyEvent.VK_DOWN: scrollAndAnimateBy(1); break; case KeyEvent.VK_END: scrollBy(avatars.size() - avatarIndex - 1); break; case KeyEvent.VK_HOME: scrollBy(-avatarIndex - 1); break; case KeyEvent.VK_PAGE_UP: scrollAndAnimateBy(-avatarAmount / 2); break; case KeyEvent.VK_PAGE_DOWN: scrollAndAnimateBy(avatarAmount / 2); break; } } } private class FocusGrabber extends MouseAdapter { @Override public void mouseClicked(MouseEvent e) { requestFocus(); } } private class AvatarScroller extends MouseAdapter { @Override public void mouseClicked(MouseEvent e) { if ((faderTimer != null && faderTimer.isRunning()) || (scrollerTimer != null && scrollerTimer.isRunning()) || drawableAvatars == null) { return; } if (e.getButton() == MouseEvent.BUTTON1) { DrawableAvatar avatar = getHitAvatar(e.getX(), e.getY()); if (avatar != null && avatar.getIndex() != avatarIndex) { scrollAndAnimate(avatar); } } } } private class DamageManager extends ComponentAdapter { @Override public void componentResized(ComponentEvent e) { damaged = true; } } private class AutoScroller implements ActionListener { private double position; private int index; private long start; private AutoScroller(DrawableAvatar avatar) { this.index = avatar.getIndex(); this.position = avatar.getPosition(); this.start = System.currentTimeMillis(); } public void actionPerformed(ActionEvent e) { long elapsed = System.currentTimeMillis() - start; if (elapsed < ANIM_SCROLL_DELAY / 2.0) { textAlphaLevel = (float) (1.0 - 2.0 * (elapsed / ANIM_SCROLL_DELAY)); } else { textAlphaLevel = (float) (((elapsed / ANIM_SCROLL_DELAY) - 0.5) * 2.0); if (textAlphaLevel > 1.0f) { textAlphaLevel = 1.0f; } } if (textAlphaLevel < 0.1f) { textAlphaLevel = 0.1f; textAvatar = "LoginName" + index; } double newPosition = (elapsed / ANIM_SCROLL_DELAY) * -position; if (elapsed >= ANIM_SCROLL_DELAY) { ((Timer) e.getSource()).stop(); setAvatarIndex(index); setPosition(0.0); return; } setPosition(newPosition); } } private class CursorChanger extends MouseMotionAdapter { @Override public void mouseMoved(MouseEvent e) { if ((scrollerTimer != null && scrollerTimer.isRunning()) || drawableAvatars == null) { return; } DrawableAvatar avatar = getHitAvatar(e.getX(), e.getY()); if (avatar != null) { getParent().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } else { getParent().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } } } } /* * Copyright (c) 2007, Romain Guy 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. * Redistributions 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 the * TimingFramework project nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ class CurvesPanel extends JPanel { protected RenderingHints hints; protected int counter = 0; protected Color start = new Color(255, 255, 255, 200); protected Color end = new Color(255, 255, 255, 0); public CurvesPanel() { this(new BorderLayout()); } public CurvesPanel(LayoutManager manager) { super(manager); hints = createRenderingHints(); } protected RenderingHints createRenderingHints() { RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); hints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); return hints; } public void animate() { counter++; } @Override public boolean isOpaque() { return false; } @Override protected void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; RenderingHints oldHints = g2.getRenderingHints(); g2.setRenderingHints(hints); float width = getWidth(); float height = getHeight(); g2.translate(0, -30); drawCurve(g2, 20.0f, -10.0f, 20.0f, -10.0f, width / 2.0f - 40.0f, 10.0f, 0.0f, -5.0f, width / 2.0f + 40, 1.0f, 0.0f, 5.0f, 50.0f, 5.0f, false); g2.translate(0, 30); g2.translate(0, height - 60); drawCurve(g2, 30.0f, -15.0f, 50.0f, 15.0f, width / 2.0f - 40.0f, 1.0f, 15.0f, -25.0f, width / 2.0f, 1.0f / 2.0f, 0.0f, 25.0f, 15.0f, 9.0f, false); g2.translate(0, -height + 60); drawCurve(g2, height - 35.0f, -5.0f, height - 50.0f, 10.0f, width / 2.0f - 40.0f, 1.0f, height - 35.0f, -25.0f, width / 2.0f, 1.0f / 2.0f, height - 20.0f, 25.0f, 25.0f, 7.0f, true); g2.setRenderingHints(oldHints); } protected void drawCurve(Graphics2D g2, float y1, float y1_offset, float y2, float y2_offset, float cx1, float cx1_offset, float cy1, float cy1_offset, float cx2, float cx2_offset, float cy2, float cy2_offset, float thickness, float speed, boolean invert) { float width = getWidth(); float offset = (float) Math.sin(counter / (speed * Math.PI)); float start_x = 0.0f; float start_y = offset * y1_offset + y1; float end_x = width; float end_y = offset * y2_offset + y2; float ctrl1_x = offset * cx1_offset + cx1; float ctrl1_y = offset * cy1_offset + cy1; float ctrl2_x = offset * cx2_offset + cx2; float ctrl2_y = offset * cy2_offset + cy2; GeneralPath thickCurve = new GeneralPath(); thickCurve.moveTo(start_x, start_y); thickCurve.curveTo(ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, end_x, end_y); thickCurve.lineTo(end_x, end_y + thickness); thickCurve.curveTo(ctrl2_x, ctrl2_y + thickness, ctrl1_x, ctrl1_y + thickness, start_x, start_y + thickness); thickCurve.lineTo(start_x, start_y); Rectangle bounds = thickCurve.getBounds(); if (!bounds.intersects(g2.getClipBounds())) { return; } GradientPaint painter = new GradientPaint(0, bounds.y, invert ? end : start, 0, bounds.y + bounds.height, invert ? start : end); Paint oldPainter = g2.getPaint(); g2.setPaint(painter); g2.fill(thickCurve); g2.setPaint(oldPainter); } } /* * Copyright (c) 2007, Romain Guy 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. * Redistributions 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 the * TimingFramework project nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ class GradientPanel extends JPanel { protected BufferedImage gradientImage; protected Color gradientStart = new Color(204, 249, 124); protected Color gradientEnd = new Color(174, 222, 94); public GradientPanel() { this(new BorderLayout()); } public GradientPanel(LayoutManager layout) { super(layout); addComponentListener(new GradientCacheManager()); } @Override protected void paintComponent(Graphics g) { createImageCache(); if (gradientImage != null) { g.drawImage(gradientImage, 0, 0, getWidth(), getHeight(), null); } } protected void createImageCache() { int width = 2; int height = getHeight(); if (width == 0 || height == 0) { return; } if (gradientImage == null || width != gradientImage.getWidth() || height != gradientImage.getHeight()) { gradientImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = gradientImage.createGraphics(); GradientPaint painter = new GradientPaint(0, 0, gradientEnd, 0, height / 2, gradientStart); g2.setPaint(painter); Rectangle2D rect = new Rectangle2D.Double(0, 0, width, height / 2.0); g2.fill(rect); painter = new GradientPaint(0, height / 2, gradientStart, 0, height, gradientEnd); g2.setPaint(painter); rect = new Rectangle2D.Double(0, (height / 2.0) - 1.0, width, height); g2.fill(rect); g2.dispose(); } } private void disposeImageCache() { synchronized (gradientImage) { gradientImage.flush(); gradientImage = null; } } private class GradientCacheManager implements ComponentListener { public void componentResized(ComponentEvent e) { } public void componentMoved(ComponentEvent e) { } public void componentShown(ComponentEvent e) { } public void componentHidden(ComponentEvent e) { disposeImageCache(); } } } /* * Copyright (c) 2007, Romain Guy 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. * Redistributions 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 the * TimingFramework project nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * * @author Romain Guy <romain.guy@mac.com> */ class StackLayout implements LayoutManager2 { public static final String BOTTOM = "bottom"; public static final String TOP = "top"; private List<Component> components = new LinkedList<Component>(); public void addLayoutComponent(Component comp, Object constraints) { synchronized (comp.getTreeLock()) { if (BOTTOM.equals(constraints)) { components.add(0, comp); } else if (TOP.equals(constraints)) { components.add(comp); } else { components.add(comp); } } } public void addLayoutComponent(String name, Component comp) { addLayoutComponent(comp, TOP); } public void removeLayoutComponent(Component comp) { synchronized (comp.getTreeLock()) { components.remove(comp); } } public float getLayoutAlignmentX(Container target) { return 0.5f; } public float getLayoutAlignmentY(Container target) { return 0.5f; } public void invalidateLayout(Container target) { } public Dimension preferredLayoutSize(Container parent) { synchronized (parent.getTreeLock()) { int width = 0; int height = 0; for (Component comp : components) { Dimension size = comp.getPreferredSize(); width = Math.max(size.width, width); height = Math.max(size.height, height); } Insets insets = parent.getInsets(); width += insets.left + insets.right; height += insets.top + insets.bottom; return new Dimension(width, height); } } public Dimension minimumLayoutSize(Container parent) { synchronized (parent.getTreeLock()) { int width = 0; int height = 0; for (Component comp : components) { Dimension size = comp.getMinimumSize(); width = Math.max(size.width, width); height = Math.max(size.height, height); } Insets insets = parent.getInsets(); width += insets.left + insets.right; height += insets.top + insets.bottom; return new Dimension(width, height); } } public Dimension maximumLayoutSize(Container target) { return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); } public void layoutContainer(Container parent) { synchronized (parent.getTreeLock()) { int width = parent.getWidth(); int height = parent.getHeight(); Rectangle bounds = new Rectangle(0, 0, width, height); int componentsCount = components.size(); for (int i = 0; i < componentsCount; i++) { Component comp = components.get(i); comp.setBounds(bounds); parent.setComponentZOrder(comp, componentsCount - i - 1); } } } } /* * Copyright (c) 2007, Romain Guy 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. * Redistributions 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 the * TimingFramework project nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ class FileTreeWalker { private File path; private static final FileFilter directoryFilter = new FileFilter() { public boolean accept(File pathname) { return pathname.isDirectory(); } }; private FileFilter filter; public FileTreeWalker(File path) throws IOException { this(path, new FileFilter() { public boolean accept(File pathname) { return pathname.isFile(); } }); } public FileTreeWalker(File path, FileFilter filter) throws IOException { if (path == null || !path.exists() || path.isFile()) { throw new IOException("Path " + path + " is not a valid directory."); } this.path = path; this.filter = filter; } public void walk(FileTreeWalk walk) { walkDirectory(walk, path); } private void walkDirectory(FileTreeWalk walk, File dir) { File[] files = dir.listFiles(filter); for (File file : files) { walk.walk(file); } File[] dirs = dir.listFiles(directoryFilter); for (File subDir : dirs) { walkDirectory(walk, subDir); } } } /* * Copyright (c) 2007, Romain Guy 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. * Redistributions 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 the * TimingFramework project nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ interface FileTreeWalk { public void walk(File path); } /* * Copyright (c) 2007, Romain Guy 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. * Redistributions 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 the * TimingFramework project nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * @author Romain Guy */ class UnixGlobFileFilter implements FileFilter { private Pattern pattern; public UnixGlobFileFilter(String filter) { pattern = Pattern.compile(globToRegex(filter)); } public boolean accept(File file) { String path = file.getName(); Matcher matcher = pattern.matcher(path); return matcher.matches(); } private String globToRegex(String glob) { char c = '\0'; boolean escape = false; boolean enclosed = false; StringBuffer buffer = new StringBuffer(glob.length()); for (int i = 0; i < glob.length(); i++) { c = glob.charAt(i); if (escape) { buffer.append('\\'); buffer.append(c); escape = false; continue; } switch (c) { case '*': buffer.append('.').append('*'); break; case '?': buffer.append('.'); break; case '\\': escape = true; break; case '.': buffer.append('\\').append('.'); break; case '{': buffer.append('('); enclosed = true; break; case '}': buffer.append(')'); enclosed = false; break; case ',': if (enclosed) buffer.append('|'); else buffer.append(','); break; default: buffer.append(c); } } return buffer.toString(); } }