Coverage Report - org.jtheque.core.managers.view.impl.components.InfiniteWaitFigure
 
Classes in this File Line Coverage Branch Coverage Complexity
InfiniteWaitFigure
0 %
0/107
0 %
0/26
1.938
InfiniteWaitFigure$1
0 %
0/3
N/A
1.938
InfiniteWaitFigure$InactiveKeyListener
0 %
0/1
N/A
1.938
InfiniteWaitFigure$InactiveMouseMotionListener
0 %
0/1
N/A
1.938
InfiniteWaitFigure$SimpleGlassPaneAncestorListener
0 %
0/7
0 %
0/2
1.938
 
 1  
 package org.jtheque.core.managers.view.impl.components;
 2  
 
 3  
 /*
 4  
  * This file is part of JTheque.
 5  
  *
 6  
  * JTheque is free software: you can redistribute it and/or modify
 7  
  * it under the terms of the GNU General Public License as published by
 8  
  * the Free Software Foundation, either version 3 of the License.
 9  
  *
 10  
  * JTheque is distributed in the hope that it will be useful,
 11  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13  
  * GNU General Public License for more details.
 14  
  *
 15  
  * You should have received a copy of the GNU General Public License
 16  
  * along with JTheque.  If not, see <http://www.gnu.org/licenses/>.
 17  
  */
 18  
 
 19  
 import org.jtheque.core.managers.Managers;
 20  
 import org.jtheque.core.managers.log.ILoggingManager;
 21  
 
 22  
 import javax.swing.JComponent;
 23  
 import javax.swing.RootPaneContainer;
 24  
 import javax.swing.SwingUtilities;
 25  
 import javax.swing.Timer;
 26  
 import javax.swing.event.AncestorEvent;
 27  
 import javax.swing.event.AncestorListener;
 28  
 import java.awt.AWTException;
 29  
 import java.awt.Color;
 30  
 import java.awt.Graphics;
 31  
 import java.awt.Graphics2D;
 32  
 import java.awt.Rectangle;
 33  
 import java.awt.RenderingHints;
 34  
 import java.awt.Robot;
 35  
 import java.awt.Window;
 36  
 import java.awt.event.ActionEvent;
 37  
 import java.awt.event.ActionListener;
 38  
 import java.awt.event.ComponentAdapter;
 39  
 import java.awt.event.ComponentEvent;
 40  
 import java.awt.event.ComponentListener;
 41  
 import java.awt.event.KeyAdapter;
 42  
 import java.awt.event.KeyListener;
 43  
 import java.awt.event.MouseMotionAdapter;
 44  
 import java.awt.event.MouseMotionListener;
 45  
 import java.awt.geom.AffineTransform;
 46  
 import java.awt.geom.Area;
 47  
 import java.awt.geom.Ellipse2D;
 48  
 import java.awt.geom.Point2D;
 49  
 import java.awt.geom.Rectangle2D;
 50  
 import java.awt.image.BufferedImage;
 51  
 
 52  
 /**
 53  
  * A wait figure with an infinite animation.
 54  
  *
 55  
  * @author Baptiste Wicht
 56  
  */
 57  0
 public final class InfiniteWaitFigure implements WaitFigure, ActionListener {
 58  
     private static final int CLOCK_TICK_WIDTH = 12;
 59  
     private static final int NUMBER_OF_BARS = 12;
 60  
     private static final int SPEED = 1000 / 8;
 61  
     private static final double SCALE = 1.5d;
 62  
 
 63  
     private int iterate;
 64  
 
 65  
     private JComponent glassPane;
 66  
 
 67  
     private BufferedImage imageBuf;
 68  
     private Area[] bars;
 69  
     private Rectangle barsBounds;
 70  
     private Rectangle barsScreenBounds;
 71  
     private AffineTransform centerAndScaleTransform;
 72  
     private Timer timer;
 73  
     private Color[] colors;
 74  
     private int colorOffset;
 75  
     
 76  0
     private final KeyListener keyListener = new InactiveKeyListener();
 77  0
     private final MouseMotionListener mouseMotionListener = new InactiveMouseMotionListener();
 78  
 
 79  
     /**
 80  
      * A key listener who do nothing. 
 81  
      * 
 82  
      * @author Baptiste Wicht
 83  
      */
 84  0
     private static final class InactiveKeyListener extends KeyAdapter{}
 85  
 
 86  
     /**
 87  
      * A mouse motion listener who do nothing. 
 88  
      * 
 89  
      * @author Baptiste Wicht
 90  
      */
 91  0
     private static final class InactiveMouseMotionListener extends MouseMotionAdapter{}
 92  
     
 93  0
     private final ComponentListener componentAdapter = new ComponentAdapter() {
 94  
         @Override
 95  
         public void componentResized(ComponentEvent e) {
 96  0
             resize();
 97  0
         }
 98  
     };
 99  
 
 100  
     @Override
 101  
     public void actionPerformed(ActionEvent event) {
 102  0
         if (event.getSource() == timer) {
 103  0
             update();
 104  
         }
 105  0
     }
 106  
 
 107  
     @Override
 108  
     public void init() {
 109  0
         timer = new Timer(SPEED, this);
 110  
 
 111  0
         colors = new Color[NUMBER_OF_BARS * 2];
 112  
 
 113  0
         bars = buildTicker(NUMBER_OF_BARS);
 114  
 
 115  0
         barsBounds = new Rectangle();
 116  
 
 117  0
         int i = 0;
 118  0
         for (Area bar : bars) {
 119  0
             barsBounds = barsBounds.union(bar.getBounds());
 120  
 
 121  0
             int channel = 224 - 128 / (i + 1);
 122  0
             colors[i] = new Color(channel, channel, channel);
 123  0
             colors[NUMBER_OF_BARS + i] = colors[i];
 124  
 
 125  0
             ++i;
 126  
         }
 127  0
     }
 128  
 
 129  
     @Override
 130  
     public void paint(Graphics graphics) {
 131  0
         Rectangle clip = glassPane.getBounds();
 132  
 
 133  0
         if (imageBuf == null) {
 134  0
             Color bg = ((RootPaneContainer) SwingUtilities.getWindowAncestor(glassPane)).
 135  
                     getContentPane().getBackground();
 136  0
             graphics.setColor(new Color(bg.getRed(), bg.getGreen(), bg.getBlue(), 180));
 137  0
             graphics.fillRect(clip.x, clip.y, clip.width, clip.height);
 138  
         }
 139  
 
 140  0
         Graphics2D g2 = (Graphics2D) graphics.create();
 141  0
         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
 142  0
         g2.transform(centerAndScaleTransform);
 143  
 
 144  0
         int i = 0;
 145  0
         for (Area bar : bars) {
 146  0
             ++i;
 147  0
             g2.setColor(colors[i + colorOffset]);
 148  0
             g2.fill(bar);
 149  
         }
 150  0
     }
 151  
 
 152  
     /**
 153  
      * This method will be called when the frame will be resized.
 154  
      */
 155  
     void resize() {
 156  0
         glassPane.setOpaque(false);
 157  0
         imageBuf = null;
 158  0
         iterate = 3;
 159  0
     }
 160  
 
 161  
     @Override
 162  
     public void start() {
 163  0
         glassPane.setOpaque(false);
 164  
 
 165  0
         Window window = SwingUtilities.getWindowAncestor(glassPane);
 166  
 
 167  0
         if (window == null) {
 168  0
             glassPane.addAncestorListener(new SimpleGlassPaneAncestorListener());
 169  
         } else {
 170  0
             window.addComponentListener(componentAdapter);
 171  
         }
 172  
 
 173  0
         iterate = 3;
 174  
 
 175  0
         glassPane.addMouseMotionListener(mouseMotionListener);
 176  0
         glassPane.addKeyListener(keyListener);
 177  
 
 178  0
         timer.start();
 179  0
     }
 180  
 
 181  
     @Override
 182  
     public void stop() {
 183  0
         timer.stop();
 184  
 
 185  0
         imageBuf = null;
 186  
 
 187  0
         glassPane.removeMouseMotionListener(mouseMotionListener);
 188  0
         glassPane.removeKeyListener(keyListener);
 189  
 
 190  0
         Window oWindow = SwingUtilities.getWindowAncestor(glassPane);
 191  0
         if (oWindow != null) {
 192  0
             oWindow.removeComponentListener(componentAdapter);
 193  
         }
 194  0
     }
 195  
 
 196  
     /**
 197  
      * Update the figure. This method will be called each turn of the timer.
 198  
      */
 199  
     void update() {
 200  0
         if (colorOffset == NUMBER_OF_BARS - 1) {
 201  0
             colorOffset = 0;
 202  
         } else {
 203  0
             ++colorOffset;
 204  
         }
 205  
 
 206  0
         if (barsScreenBounds == null) {
 207  0
             glassPane.repaint();
 208  
         } else {
 209  0
             glassPane.repaint(barsScreenBounds);
 210  
         }
 211  
 
 212  0
         if (imageBuf == null) {
 213  0
             if (iterate < 0) {
 214  
                 try {
 215  0
                     makeSnapshot();
 216  0
                     glassPane.setOpaque(true);
 217  0
                 } catch (AWTException e1) {
 218  0
                     Managers.getManager(ILoggingManager.class).getLogger(getClass()).error(e1);
 219  0
                 }
 220  
             } else {
 221  0
                 --iterate;
 222  
             }
 223  
         }
 224  0
     }
 225  
 
 226  
     /**
 227  
      * Make a snapshot.
 228  
      *
 229  
      * @throws AWTException When an error occurs during the snapshot.
 230  
      */
 231  
     private void makeSnapshot() throws AWTException {
 232  0
         Rectangle oRectangle = new Rectangle(glassPane.getBounds());
 233  
 
 234  0
         imageBuf = new Robot().createScreenCapture(oRectangle);
 235  0
     }
 236  
 
 237  
     /**
 238  
      * Build the tickers.
 239  
      *
 240  
      * @param barCount The number of bars
 241  
      * @return All the tickers
 242  
      */
 243  
     private Area[] buildTicker(int barCount) {
 244  0
         Area[] ticker = new Area[barCount];
 245  0
         Point2D center = new Point2D.Double(0, 0);
 246  0
         double fixedAngle = 2 * Math.PI / barCount;
 247  
 
 248  0
         for (double i = 0.0; i < barCount; i++) {
 249  0
             Area primitive = buildPrimitive();
 250  
 
 251  0
             AffineTransform toCenter = AffineTransform.getTranslateInstance(center.getX(), center.getY());
 252  0
             AffineTransform toBorder = AffineTransform.getTranslateInstance(35.0, -6.0);
 253  0
             AffineTransform toCircle = AffineTransform.getRotateInstance(-i * fixedAngle, center.getX(), center.getY());
 254  
 
 255  0
             AffineTransform toWheel = new AffineTransform();
 256  0
             toWheel.concatenate(toCenter);
 257  0
             toWheel.concatenate(toBorder);
 258  
 
 259  0
             primitive.transform(toWheel);
 260  0
             primitive.transform(toCircle);
 261  
 
 262  0
             ticker[(int) i] = primitive;
 263  
         }
 264  
 
 265  0
         return ticker;
 266  
     }
 267  
 
 268  
     /**
 269  
      * Build a primitive.
 270  
      *
 271  
      * @return A primitive
 272  
      */
 273  
     private Area buildPrimitive() {
 274  
         int length;
 275  
 
 276  0
         length = glassPane.getWidth() < glassPane.getHeight() ? glassPane.getWidth() / 10 : glassPane.getHeight() / 10;
 277  
 
 278  0
         Rectangle2D.Double body = new Rectangle2D.Double(6, 0, length, CLOCK_TICK_WIDTH);
 279  0
         Ellipse2D.Double head = new Ellipse2D.Double(0, 0, CLOCK_TICK_WIDTH, CLOCK_TICK_WIDTH);
 280  0
         Ellipse2D.Double tail = new Ellipse2D.Double(length, 0, CLOCK_TICK_WIDTH, CLOCK_TICK_WIDTH);
 281  
 
 282  0
         Area tick = new Area(body);
 283  
 
 284  0
         tick.add(new Area(head));
 285  0
         tick.add(new Area(tail));
 286  
 
 287  0
         return tick;
 288  
     }
 289  
 
 290  
     @Override
 291  
     public void setBounds(int width, int height) {
 292  0
         centerAndScaleTransform = new AffineTransform();
 293  0
         centerAndScaleTransform.translate(width / 2d, height / 2d);
 294  
 
 295  0
         centerAndScaleTransform.scale(SCALE, SCALE);
 296  
 
 297  0
         if (barsBounds != null) {
 298  0
             Area oBounds = new Area(barsBounds);
 299  0
             oBounds.transform(centerAndScaleTransform);
 300  0
             barsScreenBounds = oBounds.getBounds();
 301  
         }
 302  0
     }
 303  
 
 304  
     @Override
 305  
     public void setGlassPane(JComponent glassPane) {
 306  0
         this.glassPane = glassPane;
 307  0
     }
 308  
 
 309  
     /**
 310  
      * An ancestor listener for the glass pane.
 311  
      *
 312  
      * @author Baptiste Wicht
 313  
      */
 314  0
     private final class SimpleGlassPaneAncestorListener implements AncestorListener {
 315  
         @Override
 316  
         public void ancestorAdded(AncestorEvent event) {
 317  0
             Window w = SwingUtilities.getWindowAncestor(glassPane);
 318  0
             if (w != null) {
 319  0
                 w.addComponentListener(componentAdapter);
 320  
             }
 321  0
         }
 322  
 
 323  
         @Override
 324  
         public void ancestorRemoved(AncestorEvent event) {
 325  
             //Nothing to do
 326  0
         }
 327  
 
 328  
         @Override
 329  
         public void ancestorMoved(AncestorEvent event) {
 330  
             //Nothing to do
 331  0
         }
 332  
     }
 333  
 }