001: package com.xoetrope.swing.animation;
002:
003: import com.xoetrope.carousel.build.BuildProperties;
004: import com.xoetrope.util.XTextDefaults;
005: import java.awt.AlphaComposite;
006: import java.awt.Color;
007: import java.awt.Dimension;
008: import java.awt.GradientPaint;
009: import java.awt.Graphics;
010: import java.awt.Graphics2D;
011: import java.awt.Paint;
012: import java.awt.Point;
013: import java.awt.RenderingHints;
014: import java.awt.image.BufferedImage;
015: import java.awt.print.PageFormat;
016: import java.awt.print.Printable;
017: import java.awt.print.PrinterException;
018: import java.util.regex.Matcher;
019: import java.util.regex.Pattern;
020: import javax.swing.JComponent;
021: import javax.swing.plaf.basic.BasicHTML;
022: import javax.swing.text.BadLocationException;
023: import javax.swing.text.Document;
024: import javax.swing.text.View;
025: import net.xoetrope.debug.DebugLogger;
026:
027: import net.xoetrope.xui.XAttributedComponent;
028: import net.xoetrope.xui.XProject;
029: import net.xoetrope.xui.XProjectManager;
030: import net.xoetrope.xui.XTextHolder;
031: import net.xoetrope.xui.helper.XTranslator;
032: import net.xoetrope.xui.helper.XuiUtilities;
033:
034: import org.jdesktop.animation.timing.Animator;
035: import org.jdesktop.animation.timing.Animator.RepeatBehavior;
036: import org.jdesktop.animation.timing.TimingTarget;
037:
038: /**
039: * All demos extend this JAnimationSurface Abstract class. From
040: * this class demos must implement the drawObjects method. This
041: * class handles animated demos, the demo must implement the
042: * JAnimationContext interface.
043: *
044: * <p> Copyright (c) Xoetrope Ltd., 2001-2006, This software is licensed under
045: * the GNU Public License (GPL), please see license.txt for more details. If
046: * you make commercial use of this software you must purchase a commercial
047: * license from Xoetrope.</p>
048: * <p> $Revision: 1.27 $</p>
049: */
050: public abstract class XAnimationSurface extends JComponent implements
051: Printable, TimingTarget, XAttributedComponent, XTextHolder {
052: protected Object antiAlias = RenderingHints.VALUE_ANTIALIAS_ON;
053: protected Object rendering = RenderingHints.VALUE_RENDER_QUALITY;
054: protected Paint texture;
055: protected AlphaComposite composite;
056: protected BufferedImage bimg;
057: protected int imageType;
058: protected Animator animator;
059: protected long sleepTime;
060:
061: protected long startTime;
062: protected XAnimationContext animationContext;
063: protected int oldW = 0, oldH = 0;
064: protected int oX, oY, oW, oH;
065:
066: protected String label;
067: protected boolean finished;
068: protected boolean autoStart;
069: protected float ascent = 0.0f;
070:
071: protected int position = 0;
072: protected int increment = 1;
073: protected int loopTime;
074: protected float timingFraction;
075:
076: /**
077: * The owner project and the context in which this object operates.
078: */
079: protected XProject currentProject = XProjectManager
080: .getCurrentProject();
081:
082: protected XAnimationSurface() {
083: setImageType(0);
084: setDoubleBuffered(false);
085: finished = false;
086: autoStart = true;
087: sleepTime = 100L;
088: loopTime = 10000;
089:
090: animator = new Animator(loopTime);
091: }
092:
093: /**
094: * Initialize the animation surface and prepare the starting point/setup
095: */
096: public void init() {
097: Point p = getLocation();
098: Dimension size = getSize();
099: oX = p.x;
100: oY = p.y;
101: oW = size.width;
102: oH = size.height;
103:
104: reset();
105: }
106:
107: public void reset() {
108: }
109:
110: /**
111: * This method will receive all of the timing events from an Animator
112: * during an animation. The fraction is the percent elapsed (0 to 1)
113: * of the current animation cycle.
114: * @param fraction the fraction of completion between the start and
115: * end of the current cycle. Note that on reversing cycles
116: * ({@link Animator.Direction#BACKWARD}) the fraction decreases
117: * from 1.0 to 0 on backwards-running cycles. Note also that animations
118: * with a duration of {@link Animator#INFINITE INFINITE} will call
119: * timingEvent with an undefined value for fraction, since there is
120: * no fraction that makes sense if the animation has no defined length.
121: * @see Animator.Direction
122: */
123: public void timingEvent(float fraction) {
124:
125: }
126:
127: /**
128: * Called when the Animator's animation begins. This provides a chance
129: * for targets to perform any setup required at animation start time.
130: */
131: public void begin() {
132: if (!animator.isRunning())
133: animator.start();
134: }
135:
136: /**
137: * Called when the Animator's animation ends
138: */
139: public void end() {
140: }
141:
142: /**
143: * Called when the Animator repeats the animation cycle
144: */
145: public void repeat() {
146: if (!animator.isRunning()) {
147: animator.setRepeatBehavior(RepeatBehavior.LOOP);
148: animator.setRepeatCount(-1);
149: }
150: }
151:
152: /**
153: * Are there more steps to come?
154: * @param at the animation thread
155: * @return true if the animation has completed
156: */
157: public boolean isFinished(Animator at) {
158: return !animator.isRunning();
159: }
160:
161: /**
162: * Should the object be animated in this step?
163: * @param at the animation thread
164: * @return true if the animation has started and is still animating
165: */
166: public boolean isAnimated(Animator at) {
167: return animator.isRunning();
168: }
169:
170: /**
171: * Has the animation begun
172: * @param at the animation thread
173: * @return true if the animation has started
174: */
175: public boolean isStarted() {
176: return animator.isRunning();
177: }
178:
179: /**
180: * Do the rendering of the animation object
181: * @param at the animation thread
182: */
183: public void render(Animator at) {
184: if (isShowing()) {
185: // Cause paint to be invoked on the EDT
186: // repaint();
187: Graphics g;
188: try {
189: g = getGraphics();
190: if (g != null) {
191: paintComponent(g);
192: g.dispose();
193: }
194: } catch (Exception e) {
195: if (BuildProperties.DEBUG)
196: DebugLogger
197: .logError("Animation graphics context error "
198: + e);
199: }
200: } else
201: finished = true;
202: }
203:
204: //=========================================================================
205: // Access Functions
206: //=========================================================================
207: /**
208: * Sets the update increment.
209: * @param incr the increment for animation steps in milliseconds
210: */
211: public void setIncrement(int incr) {
212: increment = incr;
213: }
214:
215: /**
216: * Get the update increment
217: * @return the animation step increment in milliseconds
218: */
219: public int getIncrement() {
220: return increment;
221: }
222:
223: /**
224: * Set the label/text of the component. Usage depends on the concrete component
225: * type
226: *
227: * @param newText the new text
228: */
229: public void setText(String newText) {
230: if (newText != null) {
231: if (newText.indexOf("html") >= 0)
232: putClientProperty("html", BasicHTML.createHTMLView(
233: this , newText));
234:
235: label = XuiUtilities.replace(newText,
236: XTextDefaults.CRLF_PAIR_ENCODING,
237: XTextDefaults.CRLF_PAIR);
238: }
239: }
240:
241: /**
242: * Get the text of the control
243: * @return the text
244: */
245: public String getText() {
246: View v = (View) getClientProperty("html");
247: if (v == null)
248: return label;
249: else {
250: try {
251: Document doc = v.getDocument();
252: return doc.getText(0, doc.getLength() - 1);
253: } catch (BadLocationException ble) {
254: return "<html></html>";
255: }
256: }
257: }
258:
259: //- JDK 1.5 methods-----------------------------------------------------------
260: /**
261: * Replaces each substring of this string that matches the literal target
262: * sequence with the specified literal replacement sequence. The
263: * replacement proceeds from the beginning of the string to the end, for
264: * example, replacing "aa" with "b" in the string "aaa" will result in
265: * "ba" rather than "ab".
266: * Pulled from JDK 1.5 for compatibility with 1.4
267: * @param source the source text/string
268: * @param target The sequence of char values to be replaced
269: * @param replacement The replacement sequence of char values
270: * @return The resulting string
271: */
272: public static String replace(String source, CharSequence target,
273: CharSequence replacement) {
274: return Pattern
275: .compile(target.toString(), 0x10/*Pattern.LITERAL*/)
276: .matcher(source).replaceAll(
277: XAnimationSurface.quoteReplacement(replacement
278: .toString()));
279: }
280:
281: /**
282: * Returns a literal replacement <code>String</code> for the specified
283: * <code>String</code>.
284: *
285: * This method produces a <code>String</code> that will work
286: * use as a literal replacement <code>s</code> in the
287: * <code>appendReplacement</code> method of the {@link Matcher} class.
288: * The <code>String</code> produced will match the sequence of characters
289: * in <code>s</code> treated as a literal sequence. Slashes ('\') and
290: * dollar signs ('$') will be given no special meaning.
291: *
292: * @param s The string to be literalized
293: * @return A literal string replacement
294: * @since 1.5
295: */
296: public static String quoteReplacement(String s) {
297: if ((s.indexOf('\\') == -1) && (s.indexOf('$') == -1))
298: return s;
299:
300: StringBuffer sb = new StringBuffer();
301: for (int i = 0; i < s.length(); i++) {
302: char c = s.charAt(i);
303: if (c == '\\') {
304: sb.append('\\');
305: sb.append('\\');
306: } else if (c == '$') {
307: sb.append('\\');
308: sb.append('$');
309: } else
310: sb.append(c);
311: }
312: return sb.toString();
313: }
314:
315: //- JDK 1.5 methods-----------------------------------------------------------
316:
317: /**
318: * Set the way in which the image is presented.
319: * @param imgType = 0 to create a buffered image of TYPE_INT_ARGB,<br>
320: * 1 = this class does not set up a buffered image or,<br>
321: * >2 = BufferedImage.<<type>> + 2 where type is the BufferedImage type constant
322: */
323: public void setImageType(int imgType) {
324: if (imgType == 0) {
325: // if ( this instanceof XAnimationContext ) {
326: // imageType = 2;
327: // }
328: // else
329: imageType = 1;
330: } else
331: imageType = imgType;
332: bimg = null;
333: }
334:
335: /**
336: * Get the iamge type used for buffering the animation
337: * @param the image type flag
338: */
339: public int getImageType() {
340: return imageType;
341: }
342:
343: /**
344: * Turn the value anti-aliasing on or off. By default the anti-aliasing is on
345: * @param aa true to turn the anti-aliasing on
346: */
347: public void setAntiAlias(boolean aa) {
348: antiAlias = aa ? RenderingHints.VALUE_ANTIALIAS_ON
349: : RenderingHints.VALUE_ANTIALIAS_OFF;
350: }
351:
352: /**
353: * Get the value anti-aliasing value
354: * @param aa true if anti-aliasing is on
355: */
356: public boolean getAntiAlias() {
357: return (antiAlias == RenderingHints.VALUE_ANTIALIAS_ON);
358: }
359:
360: /**
361: * Set the rendering hint for quality or speed
362: * @param rd true for quality, false for speed
363: */
364: public void setRendering(boolean rd) {
365: rendering = rd ? RenderingHints.VALUE_RENDER_QUALITY
366: : RenderingHints.VALUE_RENDER_SPEED;
367: }
368:
369: /**
370: * Get the rendering hint
371: * @return true if rendering is to favour quality over speed
372: */
373: public boolean getRendering() {
374: return rendering == RenderingHints.VALUE_RENDER_QUALITY;
375: }
376:
377: /**
378: * Set a texture for paints
379: * @param obj the Paint object, for example a GradientPaint
380: */
381: public void setTexture(Object obj) {
382: if (obj instanceof GradientPaint)
383: texture = new GradientPaint(0, 0, Color.white,
384: getSize().width * 2, 0, Color.green);
385: else
386: texture = (Paint) obj;
387: }
388:
389: /**
390: * Set the composite instance
391: * @param cp the composite e.g. AlphaComposite
392: */
393: public void setComposite(boolean cp) {
394: composite = cp ? AlphaComposite.getInstance(
395: AlphaComposite.SRC_OVER, 0.5f) : null;
396: }
397:
398: /**
399: * Get the composite setup state
400: * @return true if a compisite is used
401: */
402: public boolean getComposite() {
403: return composite != null;
404: }
405:
406: /**
407: * Set the animation loop time in milliseconds
408: * @param lt the animation time
409: */
410: public void setLoopTime(int lt) {
411: loopTime = lt;
412: if (!animator.isRunning())
413: animator.setDuration(loopTime);
414: }
415:
416: /**
417: * Get the animation loop time
418: * @return the loop time
419: */
420: public int getLoopTime() {
421: return loopTime;
422: }
423:
424: /**
425: * Create a buffer for the animation
426: * @param w the image width
427: * @param h the image height
428: * @param imgType the image type e.g. BufferedImage.TYPE_INT_ARGB
429: * @return the new image
430: */
431: public BufferedImage createBufferedImage(int w, int h, int imgType) {
432: BufferedImage bi = null;
433: if (imgType == 0)
434: bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB /*2*/);
435: else
436: bi = new BufferedImage(w, h, imgType);
437:
438: //bi = new BufferedImage( w, h, BufferedImage.TYPE_INT_ARGB /*2*/ );
439: return bi;
440: }
441:
442: /**
443: * Create a graphics context for an image buffer and prepare that buffer for use.
444: *
445: * @param width the width
446: * @param height the height
447: * @param bi the buffered image
448: * @param g the graphics context
449: * @return the Java2D graphics context for the buffer
450: */
451: public Graphics2D createGraphics2D(int width, int height,
452: BufferedImage bi, Graphics g) {
453: Graphics2D g2 = null;
454:
455: if (bi != null)
456: g2 = bi.createGraphics();
457: else
458: g2 = (Graphics2D) g;
459:
460: g2.setBackground(getBackground());
461: g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias);
462: g2.setRenderingHint(RenderingHints.KEY_RENDERING, rendering);
463:
464: // This gives problems with overlays.
465: //g2.clearRect( 0, 0, width, height );
466:
467: if (isOpaque()) {
468: if (texture != null) {
469: // set composite to opaque for texture fills
470: g2.setComposite(AlphaComposite.SrcOver);
471: g2.setPaint(texture);
472: g2.fillRect(0, 0, width, height);
473: }
474: }
475:
476: if (composite != null)
477: g2.setComposite(composite);
478:
479: return g2;
480: }
481:
482: /**
483: * All classes that extend JAnimationSurface must implement this routine...
484: * @param w the width
485: * @param h the height
486: * @param g2 the graphics context
487: */
488: public abstract void drawObjects(int w, int h, Graphics2D g2);
489:
490: /**
491: * Paint the component, buffering the image in the process if necessary
492: * @param g the graphics context
493: */
494: public void paintComponent(Graphics g) {
495: // super.paintComponent( g );
496:
497: Dimension d = getSize();
498:
499: if (bimg == null) {
500: if (imageType == 1) {
501: bimg = null;
502: } else if ((bimg == null) || (oldW != d.width)
503: || (oldH != d.height)) {
504: bimg = createBufferedImage(d.width, d.height,
505: imageType - 2);
506: if (this instanceof AnimationStep)
507: ((AnimationStep) this ).reset();
508: }
509: }
510: autoStart();
511:
512: oldW = d.width;
513: oldH = d.height;
514:
515: // if ( animator != null )
516: // animationContext.step( d.width, d.height );
517:
518: Graphics2D g2 = createGraphics2D(d.width, d.height, bimg, g);
519: drawObjects(d.width, d.height, g2);
520: g2.dispose();
521:
522: if (bimg != null) {
523: g.drawImage(bimg, 0, 0, null);
524: getToolkit().sync();
525: }
526: }
527:
528: /**
529: * Print this component
530: * @param g the graphics context
531: * @param pf the page format
532: * @param pi the page index
533: * @return Printable.PAGE_EXISTS on success or Printable.NO_SUCH_PAGE
534: * @throws java.awt.print.PrinterException Problems!
535: */
536: public int print(Graphics g, PageFormat pf, int pi)
537: throws PrinterException {
538: if (pi >= 1) {
539: return Printable.NO_SUCH_PAGE;
540: }
541:
542: Graphics2D g2d = (Graphics2D) g;
543: g2d.translate(pf.getImageableX(), pf.getImageableY());
544: g2d.translate(pf.getImageableWidth() / 2, pf
545: .getImageableHeight() / 2);
546:
547: Dimension d = getSize();
548:
549: double scale = Math.min(pf.getImageableWidth() / d.width, pf
550: .getImageableHeight()
551: / d.height);
552: if (scale < 1.0) {
553: g2d.scale(scale, scale);
554: }
555:
556: g2d.translate(-d.width / 2.0, -d.height / 2.0);
557:
558: if (bimg == null) {
559: Graphics2D g2 = createGraphics2D(d.width, d.height, null,
560: g2d);
561: drawObjects(d.width, d.height, g2);
562: g2.dispose();
563: } else {
564: g2d.drawImage(bimg, 0, 0, this );
565: }
566:
567: return Printable.PAGE_EXISTS;
568: }
569:
570: /**
571: * Set the animation thread.
572: * @param thread the animation thread
573: */
574: public void setAnimator(Animator thread) {
575: animator = thread;
576: }
577:
578: /**
579: * Get the animation thread.
580: * @return the animation thread
581: */
582: public Animator getAnimator() {
583: return animator;
584: }
585:
586: /**
587: * Begin the animation
588: */
589: public void start() {
590: init();
591: finished = false;
592: //animationContext = ( XAnimationContext )this;
593: if (animator == null)
594: animator = new Animator(loopTime);
595:
596: animator.addTarget(this );
597: animator.setResolution((int) sleepTime);
598:
599: try {
600: if (!animator.isRunning())
601: animator.start();
602: } catch (IllegalThreadStateException e) {
603: }
604: }
605:
606: /**
607: * End the animation
608: */
609: public synchronized void stop() {
610: finished = true;
611: animator = null;
612: }
613:
614: /**
615: * Flag the animation to automatically start on first display
616: * @param as true to automatically start, false to wait for a start signal
617: */
618: public void setAutoStart(boolean as) {
619: autoStart = as;
620: }
621:
622: /**
623: * Get the autostart parameter
624: * @return the autostart value
625: */
626: public boolean getAutoStart() {
627: return autoStart;
628: }
629:
630: /**
631: * Start an autostart animation if it hassn't started already
632: */
633: protected void autoStart() {
634: if (!autoStart || ((animator != null) && animator.isRunning()))
635: return;
636: start();
637: }
638:
639: /**
640: * Set the sleep time for the animation thread
641: * @param st defaults to 100 milliseconds
642: */
643: public void setSleepTime(long st) {
644: sleepTime = st;
645: if ((animator != null) && (!animator.isRunning()))
646: animator.setResolution((int) st);
647: }
648:
649: /**
650: * Get the sleep time in milliseconds
651: * @return the sleep time
652: */
653: public long getSleepTime() {
654: return sleepTime;
655: }
656:
657: /**
658: * Set one or more attributes of the component.
659: * @param attribName the name of the attribute
660: * <ul>
661: * <li>text - set the text</li>
662: * <li>content - set the text</li>
663: * <li>autostart - true to automatically start the animation</li>
664: * <li>increment - set the step size</li>
665: * <li>sleep - the sleep time in milliseconds</li>
666: * <li>loopTime - the length of the animation loop in milliseconds</li>
667: * </ul>
668: * @param attribValue the value of the attribute
669: * @return 0 for success, non zero otherwise
670: */
671: public int setAttribute(String attribName, Object attribValue) {
672: String attribNameLwr = attribName.toLowerCase();
673: String attribValueStr = (String) attribValue;
674: String attribValueLwr = attribValueStr.toLowerCase();
675: if (attribNameLwr.equals("content")
676: || attribNameLwr.equals("text")) {
677: XTranslator translator = currentProject.getTranslator();
678: if (translator != null)
679: setText(translator.translate(attribValueStr));
680: else
681: setText(attribValueStr);
682: } else if (attribNameLwr.equals("autostart"))
683: setAutoStart(attribValueLwr.equals("true"));
684: else if (attribNameLwr.equals("increment"))
685: setIncrement(Integer.parseInt(attribValueLwr));
686: else if (attribNameLwr.equals("antialias"))
687: setAntiAlias(attribValueLwr.equals("true"));
688: else if (attribNameLwr.equals("imagetype"))
689: setImageType(Integer.parseInt(attribValueLwr));
690: else if (attribNameLwr.equals("sleep"))
691: setSleepTime(Integer.parseInt(attribValueLwr));
692: else if (attribNameLwr.equals("looptime"))
693: loopTime = Integer.parseInt(attribValueLwr);
694: return 0;
695: }
696: }
|