KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > java > swing > plaf > windows > AnimationController


1 /*
2  * @(#)AnimationController.java 1.1 06/08/17
3  *
4  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package com.sun.java.swing.plaf.windows;
9
10 import java.security.AccessController JavaDoc;
11 import sun.security.action.GetBooleanAction;
12
13 import java.util.*;
14 import java.beans.PropertyChangeListener JavaDoc;
15 import java.beans.PropertyChangeEvent JavaDoc;
16 import java.awt.*;
17 import java.awt.event.*;
18 import javax.swing.*;
19
20
21
22 import sun.swing.UIClientPropertyKey;
23 import com.sun.java.swing.plaf.windows.TMSchema.State;
24 import static com.sun.java.swing.plaf.windows.TMSchema.State.*;
25 import com.sun.java.swing.plaf.windows.TMSchema.Part;
26 import com.sun.java.swing.plaf.windows.TMSchema.Prop;
27 import com.sun.java.swing.plaf.windows.XPStyle.Skin;
28
29 import sun.awt.AppContext;
30
31 /**
32  * A class to help mimic Vista theme animations. The only kind of
33  * animation it handles for now is 'transition' animation (this seems
34  * to be the only animation which Vista theme can do). This is when
35  * one picture fadein over another one in some period of time.
36  * According to
37  * https://connect.microsoft.com/feedback/ViewFeedback.aspx?FeedbackID=86852&SiteID=4
38  * The animations are all linear.
39  *
40  * This class has a number of responsibilities.
41  * <ul>
42  * <li> It trigger rapaint for the UI components involved in the animation
43  * <li> It tracks the animation state for every UI component involved in the
44  * animation and paints {@code Skin} in new {@code State} over the
45  * {@code Skin} in last {@code State} using
46  * {@code AlphaComposite.SrcOver.derive(alpha)} where {code alpha}
47  * depends on the state of animation
48  * </ul>
49  *
50  * @version 1.1 08/17/06
51  * @author Igor Kushnirskiy
52  */

53 class AnimationController implements ActionListener, PropertyChangeListener JavaDoc {
54
55     private final static boolean VISTA_ANIMATION_DISABLED =
56         AccessController.doPrivileged(new GetBooleanAction("swing.disablevistaanimation"));
57         
58
59     private final static Object JavaDoc ANIMATION_CONTROLLER_KEY =
60         new StringBuilder JavaDoc("ANIMATION_CONTROLLER_KEY");
61     
62     private final Map<JComponent, Map<Part, AnimationState>> animationStateMap =
63             new WeakHashMap<JComponent, Map<Part, AnimationState>>();
64
65     //this timer is used to cause repaint on animated components
66
//30 repaints per second should give smooth animation affect
67
private final javax.swing.Timer JavaDoc timer =
68         new javax.swing.Timer JavaDoc(1000/30, this);
69     
70     private static synchronized AnimationController getAnimationController() {
71         AppContext appContext = AppContext.getAppContext();
72         Object JavaDoc obj = appContext.get(ANIMATION_CONTROLLER_KEY);
73         if (obj == null) {
74             obj = new AnimationController();
75             appContext.put(ANIMATION_CONTROLLER_KEY, obj);
76         }
77         return (AnimationController) obj;
78     }
79
80     private AnimationController() {
81         timer.setRepeats(true);
82         timer.setCoalesce(true);
83         //we need to dispose the controller on l&f change
84
UIManager.addPropertyChangeListener(this);
85     }
86     
87     private static void triggerAnimation(JComponent c,
88                            Part part, State newState) {
89         if (c instanceof javax.swing.JTabbedPane JavaDoc
90             || part == Part.TP_BUTTON) {
91             //idk: we can not handle tabs animation because
92
//the same (component,part) is used to handle all the tabs
93
//and we can not track the states
94
//Vista theme might have transition duration for toolbar buttons
95
//but native application does not seem to animate them
96
return;
97         }
98         AnimationController controller =
99             AnimationController.getAnimationController();
100         State oldState = controller.getState(c, part);
101         if (oldState != newState) {
102             controller.putState(c, part, newState);
103             if (newState == State.DEFAULTED) {
104                 // it seems for DEFAULTED button state Vista does animation from
105
// HOT
106
oldState = State.HOT;
107             }
108             if (oldState != null) {
109                 long duration;
110                 if (newState == State.DEFAULTED) {
111                     //Only button might have DEFAULTED state
112
//idk: do not know how to get the value from Vista
113
//one second seems plausible value
114
duration = 1000;
115                 } else {
116                      duration = XPStyle.getXP().getThemeTransitionDuration(
117                            c, part,
118                            normalizeState(oldState),
119                            normalizeState(newState),
120                            Prop.TRANSITIONDURATIONS);
121                 }
122                 controller.startAnimation(c, part, oldState, newState, duration);
123             }
124         }
125     }
126
127     // for scrollbar up, down, left and right button pictures are
128
// defined by states. It seems that theme has duration defined
129
// only for up button states thus we doing this translation here.
130
private static State normalizeState(State state) {
131         State rv;
132         switch (state) {
133         case DOWNPRESSED:
134             /* falls through */
135         case LEFTPRESSED:
136             /* falls through */
137         case RIGHTPRESSED:
138             rv = UPPRESSED;
139             break;
140
141         case DOWNDISABLED:
142             /* falls through */
143         case LEFTDISABLED:
144             /* falls through */
145         case RIGHTDISABLED:
146             rv = UPDISABLED;
147             break;
148             
149         case DOWNHOT:
150             /* falls through */
151         case LEFTHOT:
152             /* falls through */
153         case RIGHTHOT:
154             rv = UPHOT;
155             break;
156             
157         case DOWNNORMAL:
158             /* falls through */
159         case LEFTNORMAL:
160             /* falls through */
161         case RIGHTNORMAL:
162             rv = UPNORMAL;
163             break;
164
165         default :
166             rv = state;
167             break;
168         }
169         return rv;
170     }
171
172     private synchronized State getState(JComponent component, Part part) {
173         State rv = null;
174         Object JavaDoc tmpObject =
175             component.getClientProperty(PartUIClientPropertyKey.getKey(part));
176         if (tmpObject instanceof State) {
177             rv = (State) tmpObject;
178         }
179         return rv;
180     }
181
182     private synchronized void putState(JComponent component, Part part,
183                                        State state) {
184         component.putClientProperty(PartUIClientPropertyKey.getKey(part),
185                                     state);
186     }
187
188     private synchronized void startAnimation(JComponent component,
189                                      Part part,
190                                      State startState,
191                                      State endState,
192                                      long millis) {
193         boolean isForwardAndReverse = false;
194         if (endState == State.DEFAULTED) {
195             isForwardAndReverse = true;
196         }
197         Map<Part, AnimationState> map = animationStateMap.get(component);
198         if (millis <= 0) {
199             if (map != null) {
200                 map.remove(part);
201                 if (map.size() == 0) {
202                     animationStateMap.remove(component);
203                 }
204             }
205             return;
206         }
207         if (map == null) {
208             map = new EnumMap<Part, AnimationState>(Part.class);
209             animationStateMap.put(component, map);
210         }
211         map.put(part,
212                 new AnimationState(startState, millis, isForwardAndReverse));
213         if (! timer.isRunning()) {
214             timer.start();
215         }
216     }
217
218     static void paintSkin(JComponent component, Skin skin,
219                       Graphics g, int dx, int dy, int dw, int dh, State state) {
220         if (VISTA_ANIMATION_DISABLED) {
221             skin.paintSkinRaw(g, dx, dy, dw, dh, state);
222             return;
223         }
224         triggerAnimation(component, skin.part, state);
225         AnimationController controller = getAnimationController();
226         synchronized (controller) {
227             AnimationState animationState = null;
228             Map<Part, AnimationState> map =
229                 controller.animationStateMap.get(component);
230             if (map != null) {
231                 animationState = map.get(skin.part);
232             }
233             if (animationState != null) {
234                 animationState.paintSkin(skin, g, dx, dy, dw, dh, state);
235             } else {
236                 skin.paintSkinRaw(g, dx, dy, dw, dh, state);
237             }
238         }
239     }
240
241     public synchronized void propertyChange(PropertyChangeEvent JavaDoc e) {
242         if ("lookAndFeel" == e.getPropertyName()
243             && ! (e.getNewValue() instanceof WindowsLookAndFeel) ) {
244             dispose();
245         }
246     }
247
248     public synchronized void actionPerformed(ActionEvent e) {
249         java.util.List JavaDoc<JComponent> componentsToRemove = null;
250         java.util.List JavaDoc<Part> partsToRemove = null;
251         for (JComponent component : animationStateMap.keySet()) {
252             component.repaint();
253             if (partsToRemove != null) {
254                 partsToRemove.clear();
255             }
256             Map<Part, AnimationState> map = animationStateMap.get(component);
257             if (! component.isShowing()
258                   || map == null
259                   || map.size() == 0) {
260                 if (componentsToRemove == null) {
261                     componentsToRemove = new ArrayList<JComponent>();
262                 }
263                 componentsToRemove.add(component);
264                 continue;
265             }
266             for (Part part : map.keySet()) {
267                 if (map.get(part).isDone()) {
268                     if (partsToRemove == null) {
269                         partsToRemove = new ArrayList<Part>();
270                     }
271                     partsToRemove.add(part);
272                 }
273             }
274             if (partsToRemove != null) {
275                 if (partsToRemove.size() == map.size()) {
276                     //animation is done for the component
277
if (componentsToRemove == null) {
278                         componentsToRemove = new ArrayList<JComponent>();
279                     }
280                     componentsToRemove.add(component);
281                 } else {
282                     for (Part part : partsToRemove) {
283                         map.remove(part);
284                     }
285                 }
286             }
287         }
288         if (componentsToRemove != null) {
289             for (JComponent component : componentsToRemove) {
290                 animationStateMap.remove(component);
291             }
292         }
293         if (animationStateMap.size() == 0) {
294             timer.stop();
295         }
296     }
297
298     private synchronized void dispose() {
299         timer.stop();
300         UIManager.removePropertyChangeListener(this);
301         synchronized (AnimationController.class) {
302             AppContext.getAppContext()
303                 .put(ANIMATION_CONTROLLER_KEY, null);
304         }
305     }
306
307     private static class AnimationState {
308         private final State startState;
309
310         //animation duration in nanoseconds
311
private final long duration;
312
313         //animatin start time in nanoseconds
314
private long startTime;
315
316         //direction the alpha value is changing
317
//forward - from 0 to 1
318
//!forward - from 1 to 0
319
private boolean isForward = true;
320
321         //if isForwardAndReverse the animation continually goes
322
//forward and reverse. alpha value is changing from 0 to 1 then
323
//from 1 to 0 and so forth
324
private boolean isForwardAndReverse;
325
326         private float progress;
327
328         AnimationState(final State startState,
329                        final long milliseconds,
330                        boolean isForwardAndReverse) {
331             assert startState != null && milliseconds > 0;
332             assert SwingUtilities.isEventDispatchThread();
333
334             this.startState = startState;
335             this.duration = milliseconds * 1000000;
336             this.startTime = System.nanoTime();
337             this.isForwardAndReverse = isForwardAndReverse;
338             progress = 0f;
339         }
340         private void updateProgress() {
341             assert SwingUtilities.isEventDispatchThread();
342
343             if (isDone()) {
344                 return;
345             }
346             long currentTime = System.nanoTime();
347
348             progress = ((float) (currentTime - startTime))
349                 / duration;
350             progress = Math.max(progress, 0); //in case time was reset
351
if (progress >= 1) {
352                 progress = 1;
353                 if (isForwardAndReverse) {
354                     startTime = currentTime;
355                     progress = 0;
356                     isForward = ! isForward;
357                 }
358             }
359         }
360         void paintSkin(Skin skin, Graphics _g,
361                        int dx, int dy, int dw, int dh, State state) {
362             assert SwingUtilities.isEventDispatchThread();
363             
364             updateProgress();
365             if (! isDone()) {
366                 Graphics2D g = (Graphics2D) _g.create();
367                 skin.paintSkinRaw(g, dx, dy, dw, dh, startState);
368                 float alpha;
369                 if (isForward) {
370                     alpha = progress;
371                 } else {
372                     alpha = 1 - progress;
373                 }
374                 g.setComposite(AlphaComposite.SrcOver.derive(alpha));
375                 skin.paintSkinRaw(g, dx, dy, dw, dh, state);
376                 g.dispose();
377             } else {
378                 skin.paintSkinRaw(_g, dx, dy, dw, dh, state);
379             }
380         }
381         boolean isDone() {
382             assert SwingUtilities.isEventDispatchThread();
383
384             return progress >= 1;
385         }
386     }
387     
388     private static class PartUIClientPropertyKey
389           implements UIClientPropertyKey {
390
391         private static final Map<Part, PartUIClientPropertyKey> map =
392             new EnumMap<Part, PartUIClientPropertyKey>(Part.class);
393
394         static synchronized PartUIClientPropertyKey getKey(Part part) {
395             PartUIClientPropertyKey rv = map.get(part);
396             if (rv == null) {
397                 rv = new PartUIClientPropertyKey(part);
398                 map.put(part, rv);
399             }
400             return rv;
401         }
402
403         private final Part part;
404         private PartUIClientPropertyKey(Part part) {
405             this.part = part;
406         }
407         public String JavaDoc toString() {
408             return part.toString();
409         }
410     }
411 }
412
Popular Tags