Background Animation : Animation « Advanced Graphics « Java






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();
  }
}


 

 








Related examples in the same category

1.Animated Graphics
2.Simple animation techniqueSimple animation technique
3.Custom move interaction, Switching interactions, Update world extent geometry and Scroll handlingCustom move interaction, Switching interactions, Update world extent geometry and Scroll handling
4.Scroll HandlingScroll Handling
5.Curve AnimationCurve Animation
6.ClockClock
7.Moving Button
8.Moving Button Container
9.Smooth Moves