Line 0
Link Here
|
|
|
1 |
/* |
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
3 |
* |
4 |
* Copyright 2012 Oracle and/or its affiliates. All rights reserved. |
5 |
* |
6 |
* Oracle and Java are registered trademarks of Oracle and/or its affiliates. |
7 |
* Other names may be trademarks of their respective owners. |
8 |
* |
9 |
* The contents of this file are subject to the terms of either the GNU |
10 |
* General Public License Version 2 only ("GPL") or the Common |
11 |
* Development and Distribution License("CDDL") (collectively, the |
12 |
* "License"). You may not use this file except in compliance with the |
13 |
* License. You can obtain a copy of the License at |
14 |
* http://www.netbeans.org/cddl-gplv2.html |
15 |
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the |
16 |
* specific language governing permissions and limitations under the |
17 |
* License. When distributing the software, include this License Header |
18 |
* Notice in each file and include the License file at |
19 |
* nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this |
20 |
* particular file as subject to the "Classpath" exception as provided |
21 |
* by Oracle in the GPL Version 2 section of the License file that |
22 |
* accompanied this code. If applicable, add the following below the |
23 |
* License Header, with the fields enclosed by brackets [] replaced by |
24 |
* your own identifying information: |
25 |
* "Portions Copyrighted [year] [name of copyright owner]" |
26 |
* |
27 |
* If you wish your version of this file to be governed by only the CDDL |
28 |
* or only the GPL Version 2, indicate your decision by adding |
29 |
* "[Contributor] elects to include this software in this distribution |
30 |
* under the [CDDL or GPL Version 2] license." If you do not indicate a |
31 |
* single choice of license, a recipient has the option to distribute |
32 |
* your version of this file under either the CDDL, the GPL Version 2 or |
33 |
* to extend the choice of license to its licensees as provided above. |
34 |
* However, if you add GPL Version 2 code and therefore, elected the GPL |
35 |
* Version 2 license, then the option applies only if the new code is |
36 |
* made subject to such option by the copyright holder. |
37 |
* |
38 |
* Contributor(s): |
39 |
* |
40 |
* Portions Copyrighted 2012 Sun Microsystems, Inc. |
41 |
*/ |
42 |
package org.netbeans.swing.tabcontrol.plaf; |
43 |
|
44 |
import java.awt.Rectangle; |
45 |
import java.awt.event.ActionEvent; |
46 |
import java.awt.event.ActionListener; |
47 |
import java.util.Set; |
48 |
import javax.swing.Icon; |
49 |
import javax.swing.Timer; |
50 |
import javax.swing.event.ChangeEvent; |
51 |
import javax.swing.event.ChangeListener; |
52 |
import org.netbeans.swing.tabcontrol.TabDataModel; |
53 |
import org.netbeans.swing.tabcontrol.customtabs.Tabbed; |
54 |
import org.openide.util.Lookup; |
55 |
import org.openide.util.WeakSet; |
56 |
import org.openide.util.lookup.ServiceProvider; |
57 |
import org.openide.windows.TopComponent; |
58 |
|
59 |
/** |
60 |
* Support class to implement animation in tab headers to indicate some sort of |
61 |
* 'busy' state.<br |
62 |
* The default implementation tracks changes in registered tab containers |
63 |
* (<code>install(Tabbed, TabDataModel)</code>) and if any tab is marked as 'busy' |
64 |
* (<code>TopComponent.makeBusy(boolean)</code>) it forces repeated repaints of |
65 |
* that tab to allow animation effects. The UI of the tab container then can use |
66 |
* an icon this class provides (<code>getBusyIcon(boolean)</code>)to indicate |
67 |
* the busy state. The default implementation of this class ensures that the |
68 |
* icon is properly animated in each repaint. |
69 |
* |
70 |
* @see TopComponent#makeBusy(boolean) |
71 |
* |
72 |
* @author S. Aubrecht |
73 |
*/ |
74 |
public abstract class BusyTabsSupport { |
75 |
|
76 |
private Timer animationTimer; |
77 |
|
78 |
private final ChangeListener modelListener = new ChangeListener() { |
79 |
|
80 |
@Override |
81 |
public void stateChanged( ChangeEvent e ) { |
82 |
checkBusyTabs(); |
83 |
} |
84 |
}; |
85 |
|
86 |
private final Set<Tabbed> containers = new WeakSet<Tabbed>(10); |
87 |
private final Set<Tabbed> busyContainers = new WeakSet<Tabbed>(10); |
88 |
|
89 |
/** |
90 |
* @return The default implementation registered in global Lookup. |
91 |
* @see DefaultBusyTabsSupport |
92 |
*/ |
93 |
public static BusyTabsSupport getDefault() { |
94 |
return Lookup.getDefault().lookup( BusyTabsSupport.class ); |
95 |
} |
96 |
|
97 |
/** |
98 |
* Track changes in given tab container to have busy tab headers repainted. |
99 |
* @param tabContainer Tab container. This method should be typically called |
100 |
* from container's <code>addNotify()</code>. |
101 |
* @param tabModel Container data model. |
102 |
*/ |
103 |
public final void install( Tabbed tabContainer, TabDataModel tabModel ) { |
104 |
if( containers.contains( tabContainer ) ) |
105 |
return; |
106 |
tabModel.addChangeListener( modelListener ); |
107 |
containers.add( tabContainer ); |
108 |
checkBusyTabs(); |
109 |
} |
110 |
|
111 |
/** |
112 |
* Unregister the given tab container. This method should be typically called |
113 |
* from container's <code>removeNotify()</code>. |
114 |
* |
115 |
* @param tabContainer |
116 |
* @param tabModel |
117 |
*/ |
118 |
public final void uninstall( Tabbed tabContainer, TabDataModel tabModel ) { |
119 |
if( busyContainers.remove( tabContainer ) ) |
120 |
repaintAll( tabContainer ); |
121 |
tabModel.removeChangeListener( modelListener ); |
122 |
containers.remove( tabContainer ); |
123 |
checkBusyTabs(); |
124 |
} |
125 |
|
126 |
/** |
127 |
* An icon that can be shown in busy tab's header to indicate some lengthy |
128 |
* process is being run in it. The default implementation ensures the icon |
129 |
* is properly animated. |
130 |
* @param isTabSelected True to get icon for a selected tab, false to get |
131 |
* icon for regular tab state. |
132 |
* @return Busy-like icon. |
133 |
*/ |
134 |
public abstract Icon getBusyIcon( boolean isTabSelected ); |
135 |
|
136 |
/** |
137 |
* Notification that busy state has changed for a given tab. |
138 |
* @param tabContainer Tab container. |
139 |
* @param tabIndex Index of the tab. |
140 |
* @param busy |
141 |
*/ |
142 |
public final void makeTabBusy( Tabbed tabContainer, int tabIndex, boolean busy ) { |
143 |
if( !busy ) { |
144 |
Rectangle tabRect = tabContainer.getTabBounds( tabIndex ); |
145 |
tabContainer.getComponent().repaint( tabRect.x, tabRect.y, tabRect.width, tabRect.height ); |
146 |
} |
147 |
checkBusyTabs(); |
148 |
} |
149 |
|
150 |
/** |
151 |
* |
152 |
* @return The time in milliseconds between repaints of busy tabs. Value of |
153 |
* zero means there are no repeated repaints. |
154 |
*/ |
155 |
protected abstract int getRepaintTimerIntervalMillis(); |
156 |
|
157 |
/** |
158 |
* Invoked between each repaint events. The default implementation animates |
159 |
* the busy icon in this method. |
160 |
*/ |
161 |
protected abstract void tick(); |
162 |
|
163 |
private void startAnimationTimer() { |
164 |
if( null != animationTimer ) { |
165 |
return; |
166 |
} |
167 |
int interval = getRepaintTimerIntervalMillis(); |
168 |
if( interval <= 0 ) |
169 |
return; |
170 |
animationTimer = new Timer( interval, new ActionListener() { |
171 |
@Override |
172 |
public void actionPerformed( ActionEvent e ) { |
173 |
checkBusyTabs(); |
174 |
repaintBusyTabs(); |
175 |
tick(); |
176 |
} |
177 |
}); |
178 |
animationTimer.setRepeats( true ); |
179 |
animationTimer.start(); |
180 |
} |
181 |
|
182 |
private void stopAnimationTimer() { |
183 |
if( null == animationTimer ) { |
184 |
return; |
185 |
} |
186 |
animationTimer.stop(); |
187 |
animationTimer = null; |
188 |
repaintBusyTabs(); |
189 |
} |
190 |
|
191 |
private void checkBusyTabs() { |
192 |
busyContainers.clear(); |
193 |
|
194 |
for( Tabbed tc : containers ) { |
195 |
if( hasBusyTabs( tc ) ) |
196 |
busyContainers.add( tc ); |
197 |
} |
198 |
|
199 |
if( busyContainers.isEmpty() ) { |
200 |
stopAnimationTimer(); |
201 |
} else { |
202 |
startAnimationTimer(); |
203 |
} |
204 |
} |
205 |
|
206 |
private void repaintBusyTabs() { |
207 |
for( Tabbed tc : busyContainers ) { |
208 |
repaintBusy( tc ); |
209 |
} |
210 |
} |
211 |
|
212 |
private void repaintBusy( Tabbed tabbed ) { |
213 |
for( int i=0; i<tabbed.getTabCount(); i++ ) { |
214 |
TopComponent tc = tabbed.getTopComponentAt( i ); |
215 |
if( tabbed.isBusy( tc ) ) { |
216 |
Rectangle rect = tabbed.getTabBounds( i ); |
217 |
tabbed.getComponent().repaint( rect.x, rect.y, rect.width, rect.height ); |
218 |
} |
219 |
} |
220 |
} |
221 |
|
222 |
private void repaintAll( Tabbed tc ) { |
223 |
Rectangle rect = tc.getTabsArea(); |
224 |
tc.getComponent().repaint( rect.x, rect.y, rect.width, rect.height ); |
225 |
} |
226 |
|
227 |
private boolean hasBusyTabs( Tabbed tabbedContainer ) { |
228 |
boolean res = false; |
229 |
for( int tabIndex = 0; tabIndex<tabbedContainer.getTabCount(); tabIndex++ ) { |
230 |
if( tabbedContainer.isBusy( tabbedContainer.getTopComponentAt( tabIndex ) ) ) { |
231 |
res = true; |
232 |
break; |
233 |
} |
234 |
} |
235 |
return res; |
236 |
} |
237 |
|
238 |
/** |
239 |
* The default implementation of busy tabs. |
240 |
* @see BusyIcon |
241 |
*/ |
242 |
@ServiceProvider(service=BusyTabsSupport.class) |
243 |
public static final class DefaultBusyTabsSupport extends BusyTabsSupport { |
244 |
|
245 |
private BusyIcon busyIconSelected; |
246 |
private BusyIcon busyIconDefault; |
247 |
|
248 |
@Override |
249 |
public Icon getBusyIcon( boolean isTabSelected ) { |
250 |
if( isTabSelected ) { |
251 |
if( null == busyIconSelected ) { |
252 |
busyIconSelected = BusyIcon.create( isTabSelected ); |
253 |
} |
254 |
return busyIconSelected; |
255 |
} else { |
256 |
if( null == busyIconDefault ) { |
257 |
busyIconDefault = BusyIcon.create( isTabSelected ); |
258 |
} |
259 |
return busyIconDefault; |
260 |
} |
261 |
} |
262 |
|
263 |
@Override |
264 |
protected void tick() { |
265 |
if( null != busyIconSelected ) |
266 |
busyIconSelected.tick(); |
267 |
|
268 |
if( null != busyIconDefault ) |
269 |
busyIconDefault.tick(); |
270 |
} |
271 |
|
272 |
@Override |
273 |
protected int getRepaintTimerIntervalMillis() { |
274 |
return 150; |
275 |
} |
276 |
} |
277 |
} |