source: trunk/src/org/expeditee/gui/PopupManager.java@ 1561

Last change on this file since 1561 was 1561, checked in by davidb, 3 years ago

A set of changes that spans three things: beat detection, time stretching; and a debug class motivated by the need to look at a canvas redraw issue most notable when a waveform widget is playing

File size: 8.3 KB
Line 
1/**
2 * PopupManager.java
3 * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package org.expeditee.gui;
20
21import java.util.LinkedList;
22import java.util.List;
23
24import org.expeditee.core.Point;
25import org.expeditee.core.bounds.AxisAlignedBoxBounds;
26import org.expeditee.gio.EcosystemManager;
27import org.expeditee.gio.EcosystemManager.Ecosystem;
28
29/**
30 * A centralised container for all custom pop-ups in expeditee.
31 *
32 * @author Brook Novak
33 */
34public final class PopupManager implements DisplayObserver
35{
36 /** Singleton */
37 private static PopupManager _instance = null;
38
39 /**
40 * @return The singleton instance.
41 */
42 public static PopupManager getInstance()
43 {
44 // Only valid for Swing ecosystems
45 if (EcosystemManager.getType() != Ecosystem.Swing) {
46 return null;
47 }
48
49 // Construct the instance
50 if (_instance == null) {
51 _instance = new PopupManager();
52 }
53
54 return _instance;
55 }
56
57 /** Singleton */
58 private PopupManager()
59 {
60 }
61
62 private LinkedList<Popup> _popups = new LinkedList<Popup>();
63
64 /** Time its takes for a maximise/minimise to animate, in milliseconds. */
65 //private final int ANIMATION_DURATION = 180;
66 /** in milliseconds. TODO: Expand. cts16 */
67 //private final int ANIMATION_RATE = 30;
68
69 public void add(Popup p)
70 {
71 if (p == null) {
72 return;
73 }
74
75 synchronized(_popups) {
76 if (!_popups.contains(p)) {
77 _popups.add(p);
78 }
79 }
80 }
81
82 public void remove(Popup p)
83 {
84 if (p == null) {
85 return;
86 }
87
88 synchronized(_popups) {
89 if (_popups.contains(p)) {
90 _popups.remove(p);
91 }
92 }
93 }
94
95 public List<Popup> getPopups()
96 {
97 synchronized(_popups) {
98 return new LinkedList<Popup>(_popups);
99 }
100 }
101
102 /**
103 * Determines whether a given point is over a pop-up.
104 *
105 * @param p
106 *
107 * @return True if p is over a pop-up
108 *
109 * @throws NullPointerException
110 * If p is null
111 */
112 public boolean isPointOverPopup(Point p)
113 {
114 if (p == null) {
115 throw new NullPointerException("p");
116 }
117
118 synchronized(_popups) {
119 for (Popup popup : _popups) {
120 if (popup.getBounds().contains(p) && popup.isShowing()) {
121 return true;
122 }
123 }
124 }
125
126 return false;
127 }
128
129 /**
130 * Use this instead of isVisible to determine if the popup is showing or not,
131 * since it considers animation.
132 *
133 * @return
134 * True if this popup is showing. <b>IMPORTANT:</b> This includes
135 * if the popup is not yet visible, but in an animation sequence for showing...
136 *
137 * @throws NullPointerException
138 * If p is null
139 */
140 public boolean isShowing(Popup p)
141 {
142 if (p == null) {
143 throw new NullPointerException("p");
144 }
145
146 synchronized(_popups) {
147 return _popups.contains(p) && p.isShowing();
148 }
149 }
150
151 /**
152 * @return
153 * True if a pop-up is showing. False otherwise.
154 */
155 public boolean isAnyPopupsShowing()
156 {
157 synchronized(_popups) {
158 return !_popups.isEmpty();
159 }
160 }
161
162 public void hideAutoHidePopups()
163 {
164 synchronized(_popups) {
165 for (Popup p : _popups) {
166 if (p.doesAutoHide()) {
167 p.hide();
168 }
169 }
170 }
171 }
172
173 /**
174 * @return
175 * True if the mouse click event for going back a frame should be consumed
176 * due to a pop-up requesting this event to be consumed currently showing.
177 */
178 public boolean shouldConsumeBackClick()
179 {
180 synchronized(_popups) {
181 for (Popup p : _popups) {
182 if (p.shouldConsumeBackClick()) {
183 return true;
184 }
185 }
186 }
187
188 return false;
189 }
190
191 @Override
192 public void frameChanged()
193 {
194 }
195
196 /**
197 * Paints current popup animations to the expeditee browser content pane.
198 */
199 public void paint()
200 {
201 /*
202 if (Browser.DEBUG) {
203 System.out.println("**** PopupManger::paint() -- num popups in arraylist: " + _popups.size());
204 }
205 */
206
207 //Bryce: popups temporarily disabled due to them not working and interfering in things.
208 //David: block of code now reintroduced: still some issue but working through them.
209
210 synchronized (_popups) {
211 for (Popup popup : _popups) {
212 popup.update();
213 popup.paint();
214 }
215 }
216
217 }
218
219 /**
220 * Provides animations for a popup when hiding or when showing.
221 *
222 * Note that {@link PopupAnimator#paint(Graphics)} and {@link PopupAnimator#update(float)}
223 * will always be invoked at seperate times - so do not have to worry about thread-safety.
224 *
225 * These should only be used once... one per popup at a time.
226 *
227 * @author Brook Novak
228 */
229 public static abstract class PopupAnimator
230 {
231 /** True if animating to show the popup, false if to hide. */
232 private boolean _showing = true;
233
234 /** Whether the animation is currently paused. */
235 private boolean _paused = true;
236
237 private long _startTime;
238 private long _duration;
239 private long _animationCurrentTime;
240
241 public PopupAnimator()
242 {
243 _animationCurrentTime = _startTime = System.currentTimeMillis();
244 _duration = getDurationMillis();
245 }
246
247 /**
248 * @return False if the animation has finished, true if further updates are required.
249 */
250 public boolean update()
251 {
252 long currentTime = System.currentTimeMillis();
253
254 if (currentTime > _startTime + _duration) {
255 currentTime = _startTime + _duration;
256 }
257
258 // If paused, make sure elapsed time stays constant
259 if (_paused) {
260 _startTime += currentTime - _animationCurrentTime;
261 }
262
263 if (Browser.DEBUG) {
264 System.out.println("*** PopupManager::upate() updating animationCurrentTime: " + currentTime);
265 }
266 _animationCurrentTime = currentTime;
267
268 return !isFinished();
269 }
270
271 public void hide()
272 {
273 if (!_showing) {
274 return;
275 }
276
277 _showing = false;
278 complementAnimationProgress();
279 }
280
281 public void show()
282 {
283 if (_showing) {
284 return;
285 }
286
287 _showing = true;
288 complementAnimationProgress();
289 }
290
291 public boolean isShowing()
292 {
293 return _showing;
294 }
295
296 public void start()
297 {
298 _animationCurrentTime = _startTime = System.currentTimeMillis();
299 unpause();
300 }
301
302 public void pause()
303 {
304 pause(true);
305 }
306
307 public void unpause()
308 {
309 pause(false);
310 }
311
312 public void pause(boolean pause)
313 {
314 _paused = pause;
315 }
316
317 private void complementAnimationProgress()
318 {
319 _startTime = _animationCurrentTime - ((_startTime + _duration) - _animationCurrentTime);
320 }
321
322 public float getProgress()
323 {
324 return (_animationCurrentTime - _startTime) / ((float) _duration);
325 }
326
327 public boolean isFinished()
328 {
329 return _animationCurrentTime != _startTime + _duration;
330 }
331
332 public abstract long getDurationMillis();
333
334 public abstract AxisAlignedBoxBounds getAnimatedBounds(AxisAlignedBoxBounds fullBounds);
335
336 }
337
338 public static class ExpandShrinkAnimator extends PopupAnimator
339 {
340 public static final long DURATION_MS = 2000;
341
342 public AxisAlignedBoxBounds _initialBounds = null;
343
344 public ExpandShrinkAnimator()
345 {
346 }
347
348 public ExpandShrinkAnimator(AxisAlignedBoxBounds initialBounds)
349 {
350 setInitialBounds(initialBounds);
351 }
352
353 public void setInitialBounds(AxisAlignedBoxBounds initialBounds)
354 {
355 _initialBounds = initialBounds;
356 }
357
358 @Override
359 public long getDurationMillis() { return DURATION_MS; }
360
361 @Override
362 public AxisAlignedBoxBounds getAnimatedBounds(AxisAlignedBoxBounds fullBounds)
363 {
364 if (_initialBounds == null) {
365 setInitialBounds(fullBounds.clone());
366 _initialBounds.getTopLeft().setY(_initialBounds.getTopLeft().getY() + _initialBounds.getSize().height);
367 //System.out.println("**** PopupManager::getAnimateBounds() forcing height to be fullBoundsHeight: ");
368 _initialBounds.getSize().height = 0;
369 //_initialBounds.getSize().height = fullBounds.getHeight(); // used to be 0
370 }
371
372 float percentShown = getProgress();
373 if (!isShowing()) {
374 percentShown = 1 - percentShown;
375 }
376 if (Browser.DEBUG) {
377 System.out.println("Percentage = " + percentShown + " " + fullBounds);
378 }
379 return AxisAlignedBoxBounds.lerp(_initialBounds, fullBounds, percentShown);
380 }
381 }
382}
Note: See TracBrowser for help on using the repository browser.