001: package snow.utils.gui;
002:
003: import java.awt.*;
004: import java.awt.event.*;
005: import java.awt.image.*;
006: import java.beans.*;
007: import javax.swing.*;
008: import javax.swing.plaf.*;
009: import javax.swing.border.*;
010: import javax.swing.plaf.metal.*;
011:
012: /**
013: * A panel, which [slowly] animates its background colors, but with only consuming neglectable cpu time.
014: *
015: * It has a constant height of labelfont/2 and can be displayed somewhere on the bottom of another panel.
016: *
017: * Note, that one must start/end the animation manually by by a call to activateAnimation().
018: * One can use a ComponentListener attached to a toplevel container for this purpose.
019: *
020: * One can use it for busy info messages. The animation
021: * attracts the users attention, so one knows what's going on.
022: */
023: public class AnimatedColorPanel extends JPanel {
024:
025: private ActivatedColorThread activatedColorThread = null;
026: // The thread, which runs in the selected state.
027:
028: //public AnimatedColorPanel thisPanel;
029: private boolean allIsReadyForSpecialUpdates;
030: private Paint paint; // used in activated state
031: private float gradientLength = 40;
032:
033: private long delayTime = 100;
034:
035: /**
036: * This one creates a panel with BoxLayout consisting
037: * of a vertical strut, which keeps the height on the
038: * label font size.
039: *
040: * delayTime in millisec is the time between animation updates.
041: * It should be bigger than 99 millisecs and controls the cpu load.
042: */
043: public AnimatedColorPanel(long delayTime) {
044: super ();
045: this .delayTime = delayTime;
046: if (this .delayTime < 44)
047: this .delayTime = 44; // be nice to other threads
048: int panelHeight = 2 + UIManager.getFont("Label.font").getSize() / 2;
049: BoxLayout boxLayout = new BoxLayout(this , BoxLayout.Y_AXIS);
050: this .setLayout(boxLayout);
051: this .add(Box.createVerticalStrut(panelHeight));
052: //this.setOpaque(false); // enables us to see the background paint
053: //this.thisPanel = this;
054: this .paint = this .getBackground(); // initially
055: EventQueue.invokeLater(new Runnable() {
056: public void run() {
057: allIsReadyForSpecialUpdates = true;
058: }
059: });
060: this .updateSpecialUI(); // one time for initializing
061: } // Constructor
062:
063: /**
064: * This one requires a LayoutManager and doesn't and
065: * the geometry must be handles by the caller.
066: *
067: * delayTime in millisec is the time between animation updates.
068: * It should be bigger than 99 millisecs and controls the cpu load.
069: *
070: */
071: public AnimatedColorPanel(LayoutManager layoutManager,
072: long delayTime) {
073: super ();
074: this .delayTime = delayTime;
075: if (this .delayTime < 44)
076: this .delayTime = 44; // be nice to other threads
077: this .setLayout(layoutManager);
078: //this.setOpaque(false); // enables us to see the background paint
079: // this.thisPanel = this;
080: EventQueue.invokeLater(new Runnable() {
081: public void run() {
082: allIsReadyForSpecialUpdates = true;
083: }
084: });
085: this .updateSpecialUI(); // one time for initializing
086: } // Constructor
087:
088: /**
089: * This must be called manually for starting/ending
090: * the animation.
091: * One can use a component listener (on a toplevel container)
092: * for this purpose.
093: */
094: public synchronized void activateAnimation(boolean doActivate) {
095: //if(true) return;
096:
097: if (doActivate) {
098: if (this .activatedColorThread == null) {
099: this .paint = this .getBackground(); // initially
100: this .gradientLength = this .getWidth() / 2f;
101: this .activatedColorThread = new ActivatedColorThread(
102: this );
103: activatedColorThread.setDaemon(true);
104: this .activatedColorThread.start();
105: }
106: } else {
107: if (this .activatedColorThread != null) {
108: this .activatedColorThread.doTerminate();
109: this .activatedColorThread = null;
110: this .paint = this .getBackground(); // initially
111: this .repaint();
112: }
113: }
114: }
115:
116: /**
117: * Just calls <code>paint(g)</code>. This method was overridden to
118: * prevent an unnecessary call to clear the background.
119: *
120: * @param g the Graphics context in which to paint
121: */
122: @Override
123: public void update(Graphics g) {
124: this .paint(g);
125: }
126:
127: /**
128: * Process additional update-work, after having called
129: * the parent method.
130: */
131: @Override
132: public void updateUI() {
133: super .updateUI();
134: // additional updates :
135: if (this .allIsReadyForSpecialUpdates) {
136: updateSpecialUI();
137: }
138: }
139:
140: public void updateSpecialUI() {
141: // Check if we have an efcn theme :
142: //this.setForeground( UIManager.getColor("Label.foreground") );
143: //this.setBackground( UIManager.getColor("Label.background") );
144: } // updateSpecialUI
145:
146: @Override
147: public void paintComponent(final Graphics g) {
148: super .paintComponent(g);
149: if (!this .isOpaque()) {
150: return;
151: }
152:
153: final Graphics2D graphics2D = (Graphics2D) g;
154: if (this .paint != null) {
155: Rectangle rec = this .getVisibleRect();
156: graphics2D.setPaint(this .paint);
157: graphics2D.fill(rec);
158: } else {
159: this .paint = this .getBackground(); // initially
160: }
161:
162: }
163:
164: public Color slightlyBrighter(final Color color) {
165: int r = color.getRed() + 32;
166: if (r > 255)
167: r = 255;
168: int g = color.getGreen() + 32;
169: if (g > 255)
170: g = 255;
171: int b = color.getBlue() + 32;
172: if (b > 255)
173: b = 255;
174: return new Color(r, g, b);
175: } // slightlyBrighter
176:
177: public Color slightlyDarker(final Color color) {
178: int r = color.getRed() - 32;
179: if (r < 0)
180: r = 0;
181: int g = color.getGreen() - 32;
182: if (g < 0)
183: g = 0;
184: int b = color.getBlue() - 32;
185: if (b < 0)
186: b = 0;
187: return new Color(r, g, b);
188: } // slightlyBrighter
189:
190: private void setPaint(final Paint newPaint) {
191: if (newPaint != null) {
192: this .paint = newPaint;
193: //this.setBackground(Color.red);
194: }
195: this .repaint();
196: }
197:
198: /**
199: * This thread performs the animated color change while the
200: * button is activated.
201: */
202: private class ActivatedColorThread extends Thread {
203: // private Color brightColor;
204: private Color darkColor;
205: private boolean terminateThread = false;
206: private JPanel parentPanel;
207:
208: public ActivatedColorThread(JPanel thePanel) {
209: this .parentPanel = thePanel;
210: this .setDaemon(true); // let it terminate, when the JVM shuts down
211: this .setName("Animated color panel");
212:
213: // Give other threads more attention:
214: this .setPriority(Thread.MIN_PRIORITY);
215: // this.brightColor = this.parentPanel.getBackground().brighter().brighter();
216: this .darkColor = this .parentPanel.getBackground().darker()
217: .darker();
218: }
219:
220: @Override
221: public void run() {
222: int maximumLocation = 0;
223: int maximumLocationStep = 5;
224:
225: int colorOffset = 0;
226: int colorOffsetStep = 2;
227:
228: // Take the 2:1 average of background and foreground color :
229: Color bg = getBackground();
230: Color fg = getForeground();
231: final int rBasis = (2 * bg.getRed() + fg.getRed()) / 3;
232: final int gBasis = (2 * bg.getGreen() + fg.getGreen()) / 3;
233: final int bBasis = (2 * bg.getBlue() + fg.getBlue()) / 3;
234: final Color basisColor = new Color(rBasis, gBasis, bBasis);
235:
236: while (!this .terminateThread) {
237: int shiftLength = 2 * this .parentPanel.getWidth();
238: // turn direction at borders :
239: if (maximumLocation > shiftLength) {
240: maximumLocationStep = -6;
241: }
242: if (maximumLocation < 6) {
243: maximumLocationStep = +6;
244: }
245: maximumLocation += maximumLocationStep;
246: // make the color change effect medium ( +- 60 ) :
247: if (colorOffset > 150) {
248: colorOffsetStep = -4;
249: }
250: if (colorOffset < -150) {
251: colorOffsetStep = +4;
252: }
253: colorOffset += colorOffsetStep;
254:
255: int c_Red = basisColor.getRed() + colorOffset;
256: if (c_Red > 255)
257: c_Red = 255;
258: if (c_Red < 0)
259: c_Red = 0;
260: int c_Green = basisColor.getGreen() - colorOffset;
261: if (c_Green > 255)
262: c_Green = 255;
263: if (c_Green < 0)
264: c_Green = 0;
265: int c_Blue = basisColor.getBlue() + colorOffset;
266: if (c_Blue > 255)
267: c_Blue = 255;
268: if (c_Blue < 0)
269: c_Blue = 0;
270:
271: final int cRed = c_Red;
272: final int cGreen = c_Green;
273: final int cBlue = c_Blue;
274: final int currentLocation = maximumLocation;
275:
276: // Don't paint, if the parentPanel is not showing or not visible :
277: if (this .parentPanel.isShowing()
278: && this .parentPanel.isVisible()) {
279: EventQueue.invokeLater(new Runnable() {
280: public void run() {
281: if (!terminateThread) {
282: synchronized (AnimatedColorPanel.this ) {
283: Color secondColor = new Color(cRed,
284: cGreen, cBlue);
285: Color firstColor = encreaseContrast(
286: getBackground(),
287: secondColor);
288: final GradientPaint gradientPaint = new GradientPaint(
289: 2f * currentLocation, 0f,
290: firstColor, 2f
291: * currentLocation
292: + gradientLength,
293: 0f, secondColor, true /* cyclic */);
294: if (gradientPaint != null) {
295: setPaint(gradientPaint);
296: }
297: }
298: }
299: }
300: });
301: }
302: try {
303: Thread.sleep(delayTime);
304: } catch (Exception wurscht) {
305: wurscht.printStackTrace();
306: }
307: } // thread while loop
308: } // run
309:
310: public void doTerminate() {
311: this .terminateThread = true;
312: }
313:
314: public Color encreaseContrast(Color basisColor, Color checkColor) {
315: int basisValue = basisColor.getRed()
316: + basisColor.getGreen() + basisColor.getBlue();
317: int checkValue = checkColor.getRed()
318: + checkColor.getGreen() + checkColor.getBlue();
319: int r = 0;
320: int g = 0;
321: int b = 0;
322: int offset = 64;
323: if (basisValue > checkValue) {
324: r = basisColor.getRed() + offset;
325: if (r > 255)
326: r = 255;
327: g = basisColor.getGreen() + offset;
328: if (g > 255)
329: g = 255;
330: b = basisColor.getBlue() + offset;
331: if (b > 255)
332: b = 255;
333: } else {
334: r = basisColor.getRed() - offset;
335: if (r < 0)
336: r = 0;
337: g = basisColor.getGreen() - offset;
338: if (g < 0)
339: g = 0;
340: b = basisColor.getBlue() - offset;
341: if (b < 0)
342: b = 0;
343: }
344: return new Color(r, g, b);
345: } // encreaseContrast
346:
347: } // class ActivatedColorThread
348:
349: }
|