replaced <code></code> with more concise {@code}
[idea/community.git] / platform / lang-impl / src / com / intellij / application / options / codeStyle / arrangement / animation / ArrangementAnimationPanel.java
1 /*
2  * Copyright 2000-2012 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.application.options.codeStyle.arrangement.animation;
17
18 import com.intellij.application.options.codeStyle.arrangement.ArrangementConstants;
19 import com.intellij.ide.ui.UISettings;
20 import com.intellij.util.ui.GridBag;
21 import com.intellij.util.ui.UIUtil;
22 import org.jetbrains.annotations.NotNull;
23 import org.jetbrains.annotations.Nullable;
24
25 import javax.swing.*;
26 import java.awt.*;
27 import java.awt.image.BufferedImage;
28
29 /**
30  * @author Denis Zhdanov
31  * @since 11/2/12 8:50 PM
32  */
33 public class ArrangementAnimationPanel extends JPanel {
34
35   @NotNull private final JComponent myContent;
36   private int myAnimationIterationStep;
37
38   @Nullable private BufferedImage myImage;
39   @Nullable private BufferedImage myCurrentImage;
40   @Nullable private Listener      myListener;
41
42   private final boolean myExpand;
43   private final boolean myHorizontal;
44   private boolean myDelayedAnimation;
45   private boolean myAnimated;
46
47   public ArrangementAnimationPanel(@NotNull JComponent content, boolean expand, boolean horizontal) {
48     super(new GridBagLayout());
49     myContent = content;
50     myExpand = expand;
51     myHorizontal = horizontal;
52     add(content, new GridBag().fillCell().weightx(1).weighty(1));
53     setOpaque(false);
54     setBackground(UIUtil.getListBackground());
55     doLayout();
56   }
57
58   public void startAnimation() {
59     myDelayedAnimation = true;
60   }
61
62   private void prepareForAnimation() {
63     Dimension size = myContent.getPreferredSize();
64     Rectangle bounds = myContent.getBounds();
65     applyDoubledBuffered(myContent, false);
66     //myContent.setDoubleBuffered(false);
67     myContent.setBounds(0, 0, size.width, size.height);
68     myContent.validate();
69     //noinspection UndesirableClassUsage
70     myImage = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB);
71     final Graphics2D graphics = myImage.createGraphics();
72     UISettings.setupAntialiasing(graphics);
73     graphics.setClip(0, 0, size.width, size.height);
74     graphics.setColor(UIUtil.getListBackground());
75     graphics.fillRect(0, 0, size.width, size.height);
76     myContent.paint(graphics);
77     graphics.dispose();
78     int expectedDurationMillis = 500;
79     myAnimationIterationStep = Math.max(
80       (myHorizontal ? size.width : size.height) / (expectedDurationMillis / ArrangementConstants.ANIMATION_STEPS_TIME_GAP_MILLIS), 1);
81
82     myContent.setBounds(bounds);
83     applyDoubledBuffered(myContent, true);
84
85     if (myExpand) {
86       if (myHorizontal) {
87         myCurrentImage = myImage.getSubimage(0, 0, myAnimationIterationStep, myImage.getHeight());
88       }
89       else {
90         myCurrentImage = myImage.getSubimage(0, 0, myImage.getWidth(), myAnimationIterationStep);
91       }
92     }
93     else {
94       if (myHorizontal) {
95         myCurrentImage = myImage.getSubimage(
96           0, 0, myImage.getWidth() - myAnimationIterationStep, myImage.getHeight()
97         );
98       }
99       else {
100         myCurrentImage = myImage.getSubimage(
101           0, 0, myImage.getWidth(), myImage.getHeight() - myAnimationIterationStep
102         );
103       }
104     }
105     invalidate();
106   }
107   
108   private static void applyDoubledBuffered(JComponent component, boolean doubleBuffered) {
109     component.setDoubleBuffered(doubleBuffered);
110     for (int i = 0; i < component.getComponentCount(); i++) {
111       applyDoubledBuffered((JComponent)component.getComponent(i), doubleBuffered);
112     }
113   }
114
115   /**
116    * Asks current panel to switch to the next drawing iteration
117    * 
118    * @return    {@code true} if there are more iterations
119    */
120   public boolean nextIteration() {
121     int widthToUse = getImageWidthToUse();
122     int heightToUse = getImageHeightToUse();
123     if (widthToUse <= 0 || heightToUse <= 0) {
124       myImage = null;
125       myCurrentImage = null;
126       myAnimated = true;
127       return false;
128     }
129
130     myCurrentImage = myImage.getSubimage(0, 0, widthToUse, heightToUse);
131
132     invalidate();
133     return true;
134   }
135
136   @Override
137   public void paint(Graphics g) {
138     if (myDelayedAnimation) {
139       prepareForAnimation();
140       myDelayedAnimation = false;
141     }
142
143     if (myCurrentImage == null) {
144       super.paint(g);
145       return;
146     }
147
148     g.drawImage(myCurrentImage, 0, 0, myCurrentImage.getWidth(), myCurrentImage.getHeight(), null);
149     if (myListener != null) {
150       myListener.onPaint();
151     }
152   }
153
154   @Override
155   public Dimension getMinimumSize() {
156     return getPreferredSize();
157   }
158
159   @Override
160   public Dimension getMaximumSize() {
161     return getPreferredSize();
162   }
163
164   @Override
165   public Dimension getPreferredSize() {
166     if (myAnimated) {
167       return myContent.getPreferredSize();
168     }
169
170     if (myCurrentImage == null) {
171       Dimension size = myContent.getPreferredSize();
172       int width = (myHorizontal && myExpand) ? myAnimationIterationStep : size.width;
173       int height = (!myHorizontal && myExpand) ? myAnimationIterationStep : size.height;
174       return new Dimension(width, height);
175     }
176     return new Dimension(myCurrentImage.getWidth(), myCurrentImage.getHeight());
177   }
178
179   private int getImageWidthToUse() {
180     assert myCurrentImage != null;
181     if (!myHorizontal) {
182       return myCurrentImage.getWidth();
183     }
184     
185     int sign = myExpand ? 1 : -1;
186     int result = myCurrentImage.getWidth() + sign * myAnimationIterationStep;
187
188     if (result <= 0 || result > myImage.getWidth()) {
189       return -1;
190     }
191     return result;
192   }
193
194   private int getImageHeightToUse() {
195     assert myCurrentImage != null;
196     if (myHorizontal) {
197       return myCurrentImage.getHeight();
198     }
199
200     int sign = myExpand ? 1 : -1;
201     int result = myCurrentImage.getHeight() + sign * myAnimationIterationStep;
202
203     if (result <= 0 || result > myImage.getHeight()) {
204       return -1;
205     }
206     return result;
207   }
208   
209   public void setListener(@Nullable Listener listener) {
210     myListener = listener;
211   }
212
213   @Override
214   public String toString() {
215     return "animation panel for " + myContent.toString();
216   }
217
218   public interface Listener {
219     void onPaint();
220   }
221 }