source: trunk/src/org/apollo/gui/ExpandedTrackManager.java@ 1258

Last change on this file since 1258 was 1258, checked in by davidb, 5 years ago

Changed how DisplayController width, height and size are retrieved. Now does this top-level, rather than going through the AxisAlignmentBox. In doing so, can now control for when the window size has you yet been correctly mapped to the screen, and fall back to pre-defined MINIMUM defaults

File size: 16.6 KB
Line 
1package org.apollo.gui;
2
3import java.awt.Dimension;
4import java.awt.Graphics2D;
5import java.awt.GridBagConstraints;
6import java.awt.GridBagLayout;
7import java.awt.event.ActionEvent;
8import java.awt.event.ActionListener;
9import java.util.LinkedList;
10
11import javax.swing.JButton;
12import javax.swing.JLabel;
13import javax.swing.JPanel;
14
15import org.apollo.audio.SampledTrackModel;
16import org.apollo.audio.util.TrackMixSubject;
17import org.apollo.io.IconRepository;
18import org.apollo.mvc.AbstractSubject;
19import org.apollo.mvc.SubjectChangedEvent;
20import org.apollo.util.TrackModelHandler;
21import org.apollo.util.TrackModelLoadManager;
22import org.expeditee.core.bounds.AxisAlignedBoxBounds;
23import org.expeditee.gio.EcosystemManager;
24import org.expeditee.gio.InputManager.WindowEventListener;
25import org.expeditee.gio.InputManager.WindowEventType;
26import org.expeditee.gio.swing.SwingConversions;
27import org.expeditee.gio.swing.SwingMiscManager;
28import org.expeditee.gui.Browser;
29import org.expeditee.gui.DisplayController;
30import org.expeditee.gui.Popup;
31import org.expeditee.gui.PopupManager;
32import org.expeditee.gui.PopupManager.ExpandShrinkAnimator;
33
34/**
35 * All expanded tracks are contained here. Fires SubjectChangedEvent.EMPTY events whenever
36 * expansion selection changes.
37 *
38 * @author Brook Novak
39 *
40 */
41public class ExpandedTrackManager extends AbstractSubject implements TrackModelHandler {
42
43 private static final int MAX_EXPANDED_TRACK_SIZE = 4;
44
45 /** All epxanded tracks ... either in all in view or all selected ... to be viewed */
46 private LinkedList<ExpandedTrackPopup> expandedTracks = new LinkedList<ExpandedTrackPopup>(); // SHARED Model data
47
48 private MultiTrackExpansionPopup trackSelectionPopup = new MultiTrackExpansionPopup();
49
50 private static ExpandedTrackManager instance = new ExpandedTrackManager();
51
52 private ExpandedTrackManager()
53 {
54 // Keep view centered to browser size
55 EcosystemManager.getInputManager().addWindowEventListener(new WindowEventListener()
56 {
57 @Override
58 public void onWindowEvent(WindowEventType type)
59 {
60 if (type != WindowEventType.WINDOW_RESIZED) return;
61 if (PopupManager.getInstance().isShowing(trackSelectionPopup)) {
62 // User currently selecting..
63 trackSelectionPopup.setLocation((EcosystemManager.getGraphicsManager().getWindowSize().getWidth() - trackSelectionPopup.getFullBounds().getWidth()) / 2, 0);
64
65 } else { // expanded views might be showing
66
67 // Fails with maximising of window on some systems... swing
68 // bug!! really annoying
69 updateLayout();
70
71 }
72 }
73
74 });
75
76 // Register for (indirectly) handling track models
77 TrackModelLoadManager.getInstance().addTrackModelHandler(this);
78
79 PopupManager.getInstance().add(trackSelectionPopup);
80 }
81
82 /**
83 *
84 * @return The singleton instance.
85 */
86 public static ExpandedTrackManager getInstance() {
87 return instance;
88 }
89
90 private void fireTrackSelectionChanged() {
91 trackSelectionPopup.onTrackSelectionChanged();
92 fireSubjectChanged(SubjectChangedEvent.EMPTY);
93 }
94
95
96 /**
97 * Giver/receiver design pattern for only allowing this singleton
98 * to creat expanded views.
99 *
100 * @param requested
101 */
102 void receiveExpandedTrackPopup(ExpandedTrackPopup requested) {
103 synchronized(expandedTracks) {
104 assert (expandedTracks.size() < MAX_EXPANDED_TRACK_SIZE);
105 expandedTracks.add(requested);
106 }
107 // Notify observers
108 fireTrackSelectionChanged();
109 }
110
111 /**
112 * Whenever a ExpandedTrackPopup hides, it invokes this.
113 * @param xtp
114 */
115 void expandedTrackPopupHidden(ExpandedTrackPopup xtp) {
116 assert(xtp != null);
117 synchronized(expandedTracks) {
118 expandedTracks.remove(xtp);
119 }
120
121 // User might have closed one but kept more visible
122 updateLayout();
123
124 // Notify observers
125 fireTrackSelectionChanged();
126 }
127
128 /**
129 * Adds a SampledTrackModel ready for expanding.
130 *
131 * @param track
132 * The track to expand. Must not be null.
133 *
134 * @param trackSourceFrameName
135 * Where the expanded tracks track source was expanded from.
136 * Must not be null
137 *
138 * @param animationSource
139 * If null no animation will be used.
140 *
141 * @param mix
142 * The mix that the expanded view should use / manipulate. Must not be null.
143 *
144 * @return
145 * True if SampledTrackModel was added.
146 * False if not - because the selection amount is maxed out or
147 * the track is already selected.
148 *
149 * @throws NullPointerException
150 * If trackModel or trackMix or trackSourceFrameName or localFilename is null.
151 */
152 public boolean addTrackToSelection(SampledTrackModel track, String trackSourceFrameName, AxisAlignedBoxBounds animationSource, TrackMixSubject mix)
153 {
154 assert(track != null);
155 assert(Browser._theBrowser != null);
156 assert(mix != null);
157
158 synchronized(expandedTracks) {
159
160 if (expandedTracks.size() < MAX_EXPANDED_TRACK_SIZE && !isTrackInExpansionSelection(track)) {
161
162 ExpandedTrackPopup.giveToExpandedTrackManager(
163 track,
164 mix,
165 trackSourceFrameName,
166 0,
167 track.getFrameCount()); /** @see #receiveExpandedTrackPopup */
168
169 // Ensure that the trackSelectionPopup is not already showing / animated to show
170 if (!PopupManager.getInstance().isShowing(trackSelectionPopup)) {
171
172 trackSelectionPopup.setLocation((DisplayController.getFramePaintAreaWidth() - trackSelectionPopup.getFullBounds().getWidth()) / 2, 0);
173
174 // Creep from above
175 trackSelectionPopup.show();
176 }
177
178 // Animate a fake popup expanding into the trackSelectionPopup from the given source if not null
179 if (animationSource != null) {
180 ((ExpandShrinkAnimator) trackSelectionPopup.getAnimator()).setInitialBounds(animationSource);
181 }
182
183 return true;
184 }
185 }
186
187 return false;
188 }
189
190 /**
191 * Expands a given SampledTrackModel into view. Hides any other
192 * ExpandedTrackPopup currently in view.
193 *
194 * If the given track is arleady in view. It will be hidden and reshown.
195 *
196 * @param track
197 * Must not be null.
198 *
199 * @param mix
200 * The mix that the expanded view should use / manipulate. Must not be null.
201 *
202 * @param animationSource
203 * Must not be null.
204 *
205 * @param trackSourceFrameName
206 * Where the expanded tracks track source was expanded from.
207 * Must not be null
208 *
209 * @param frameStart
210 * the initial zoom - clamped
211 *
212 * @param frameLength
213 * the initial zoom - clamped
214 *
215 * @throws NullPointerException
216 * If trackModel or trackMix or trackSourceFrameName or localFilename is null.
217 *
218 */
219 public void expandSingleTrack(SampledTrackModel track, AxisAlignedBoxBounds animationSource, TrackMixSubject mix, String trackSourceFrameName, int frameStart, int frameLength)
220 {
221 assert(animationSource != null);
222 assert(track != null);
223
224 synchronized(expandedTracks) {
225 // Hide all expandedTracks that are currently showing
226 for (ExpandedTrackPopup expandedTrack : expandedTracks) {
227 expandedTrack.hide();
228 }
229 }
230
231 // Create a expanded tracks view
232 ExpandedTrackPopup.giveToExpandedTrackManager(
233 track,
234 mix,
235 trackSourceFrameName,
236 frameStart,
237 frameLength); /** @see #receiveExpandedTrackPopup */
238
239 // Show the expanded track from - expanding from the given animation source
240 expandTracks(animationSource);
241 }
242
243 /**
244 * Removes a SampledTrackModel from being expanded...
245 * @param track
246 */
247 public void removeTrackFromSelection(SampledTrackModel track) {
248
249 assert(track != null);
250
251 synchronized(expandedTracks) {
252
253 ExpandedTrackPopup expandedView = getExpandedTrackPopup(track);
254
255 // Does exist?
256 if (expandedView == null) return;
257
258 // Is already showing?
259 else if (PopupManager.getInstance().isShowing(expandedView)) return;
260
261 // Remove from selection
262 expandedTracks.remove(expandedView);
263
264 // Explicitly release memory since not called due to it never being in the PopupManager
265 expandedView.releaseMemory();
266
267 // Notify observers
268 fireTrackSelectionChanged();
269
270 // Hide the popup selection if nothing selected anymore
271 if (expandedTracks.isEmpty()) {
272 hideTrackSelectionPopup();
273 }
274 }
275 }
276
277 private void cancelSelection()
278 {
279 hideTrackSelectionPopup();
280
281 // Remove from selection
282 LinkedList<? extends ExpandedTrackPopup> old = (LinkedList<? extends ExpandedTrackPopup>) expandedTracks.clone();
283
284 synchronized(expandedTracks) {
285 expandedTracks.clear();
286 }
287
288 // Explicitly release memory since may have not of called due to it never being in the PopupManager
289 for (ExpandedTrackPopup xtp : old)
290 xtp.releaseMemory();
291
292 // Notify observers
293 fireTrackSelectionChanged();
294 }
295
296 /**
297 * @param track
298 * The track to search for.
299 *
300 * @return
301 * A tracks ExpandedTrackPopup if one exists.
302 */
303 private ExpandedTrackPopup getExpandedTrackPopup(SampledTrackModel track)
304 {
305 assert(track != null);
306
307 synchronized(expandedTracks) {
308 for (ExpandedTrackPopup xtp : expandedTracks) {
309 if (xtp.getObservedTrackModel() == track) return xtp;
310 }
311 }
312
313 return null;
314 }
315
316
317 /**
318 *
319 * @param track
320 * Must not be null.
321 *
322 * @return
323 * True if the track is expanded - or is selected to be expanded..
324 */
325 public boolean isTrackInExpansionSelection(SampledTrackModel track)
326 {
327 assert(track != null);
328 return getExpandedTrackPopup(track) != null;
329 }
330
331 /**
332 * @return
333 * True if at least 1 expanded track is showing - or is selected to show.
334 */
335 public boolean doesExpandedTrackExist()
336 {
337 synchronized(expandedTracks) {
338 return expandedTracks.size() > 0;
339 }
340 }
341
342 public boolean isAnyExpandedTrackVisible()
343 {
344 for (ExpandedTrackPopup xtp : expandedTracks) {
345 if (PopupManager.getInstance().isShowing(xtp)) return true;
346 }
347
348 return false;
349 }
350
351 /**
352 * Shows all popups (if not already visible) to view and lays them out.
353 *
354 */
355 public void showSelection()
356 {
357 expandTracks(null);
358 }
359
360 /**
361 * Returns immediately if there are no tracks selected. Only expands if the popup is not already expanded.
362 *
363 * @param directAnimSource
364 * Set to null to animate expanded popups from the trackSelectionPopup.
365 * Otherwise specify where to animate from. (Intention for latter case: a single expanded view from
366 * a widget).
367 */
368 private void expandTracks(AxisAlignedBoxBounds directAnimSource)
369 {
370 synchronized(expandedTracks) {
371
372 if (expandedTracks.isEmpty()) return;
373
374 // Animate the popups from the trackSelectionPopup if there is not directAnimSource
375 if (directAnimSource == null) {
376 directAnimSource = trackSelectionPopup.getBounds();
377 }
378
379 // Update the expanded view layouts
380 updateLayout();
381
382 // Animate the popup(s)
383 for (ExpandedTrackPopup xtp : expandedTracks) {
384
385 // Note: returns immediately if already showing
386 ((ExpandShrinkAnimator) xtp.getAnimator()).setInitialBounds(directAnimSource);
387 xtp.show();
388
389 }
390 }
391
392 // Hide the trackSelectionPopup
393 hideTrackSelectionPopup();
394
395 }
396
397 private void hideTrackSelectionPopup()
398 {
399 trackSelectionPopup.hide();
400 }
401
402 /**
403 * Sets the bounds for all ExpandedTrackPopups, whether they are showing or not, to the
404 * current state of the window.
405 */
406 private void updateLayout()
407 {
408 synchronized(expandedTracks) {
409
410 if (expandedTracks.isEmpty()) return;
411
412 assert (expandedTracks.size() > 0);
413 assert(Browser._theBrowser != null);
414
415 int cpWidth = DisplayController.getFramePaintAreaWidth();
416 int cpHeight = DisplayController.getFramePaintAreaHeight();
417 if (!DisplayController.isAudienceMode()) { // if message bay is showing - the remove the height
418
419 cpHeight -= DisplayController.getMessageBayPaintArea().getHeight();
420
421 }
422
423 if (cpHeight < 0) cpHeight = 0;
424
425 // Calc widths
426 final int EXPANDED_HORO_MARGINS = 100;
427 int widths = cpWidth - (2 * EXPANDED_HORO_MARGINS);
428 if (widths < 150) widths = 150;
429
430 // Calc heights (per track view)
431 final int DESIRED_EXPANDED_HEIGHT = 340;
432 final int EXPANDED_VERT_MARGINS = 20;
433 int heights;
434 if ((expandedTracks.size() * DESIRED_EXPANDED_HEIGHT) <= (cpHeight - (2 * EXPANDED_VERT_MARGINS))) {
435 heights = DESIRED_EXPANDED_HEIGHT;
436 } else {
437 heights = (cpHeight - (2 * EXPANDED_VERT_MARGINS)) / expandedTracks.size();
438 if (heights < 100) heights = 100;
439 }
440
441 // Center the popup(s)
442 int y = ((cpHeight - (1 * EXPANDED_VERT_MARGINS)) - (expandedTracks.size() * heights)) / 2;
443 if (y < 0 || y > cpHeight) y = EXPANDED_VERT_MARGINS;
444
445 int x = (cpWidth - widths) / 2;
446 if (x < 0) x = 0;
447
448 // Update bounds
449 for (ExpandedTrackPopup xtp : expandedTracks) {
450
451 xtp.setBounds(x, y, widths, heights);
452
453 y += heights;
454
455 }
456 }
457
458 }
459
460 /**
461 * Global Track model re-use .... can be called from any thread
462 */
463 public SampledTrackModel getSharedSampledTrackModel(String localfilename)
464 {
465 synchronized(expandedTracks) {
466 for (ExpandedTrackPopup xtp : expandedTracks) {
467 if (xtp.getObservedTrackModel().getLocalFilename().equals(localfilename))
468 return xtp.getObservedTrackModel();
469 }
470 }
471
472 return null;
473 }
474
475
476 /**
477 * A little popup that shows whenever there are tracks selected for expanding
478 * @author bjn8
479 *
480 */
481 private class MultiTrackExpansionPopup extends Popup implements ActionListener
482 {
483 private JPanel panel;
484 private JButton expandButton;
485 private JButton cancelButton;
486 private JLabel selected1;
487 private JLabel selected2;
488 private JLabel selectedMore;
489
490 //private final Color BACK_COLOR = SampledTrackGraphViewPort.ZOOM_BACKING_COLOR_NORMAL;
491
492 MultiTrackExpansionPopup()
493 {
494 super(new ExpandShrinkAnimator());
495
496 panel = new JPanel(new GridBagLayout());
497 panel.setSize(260, 80);
498
499 expandButton = new JButton();
500 SwingMiscManager.setJButtonIcon(expandButton, IconRepository.getIcon("expand.png"));
501 expandButton.addActionListener(this);
502 expandButton.setPreferredSize(new Dimension(40, 40));
503 expandButton.setToolTipText("Expand selection");
504
505 cancelButton = new JButton();
506 SwingMiscManager.setJButtonIcon(cancelButton, IconRepository.getIcon("close.png"));
507 cancelButton.addActionListener(this);
508 cancelButton.setPreferredSize(new Dimension(40, 40));
509 cancelButton.setToolTipText("Cancel selection");
510
511 selected1 = new JLabel();
512 selected1.setHorizontalTextPosition(JLabel.LEFT);
513 selected1.setVerticalTextPosition(JLabel.CENTER);
514
515 selected2 = new JLabel();
516 selected2.setHorizontalTextPosition(JLabel.LEFT);
517 selected2.setVerticalTextPosition(JLabel.CENTER);
518
519 selectedMore = new JLabel();
520 selectedMore.setHorizontalTextPosition(JLabel.CENTER);
521 selectedMore.setVerticalTextPosition(JLabel.CENTER);
522
523 GridBagConstraints c = new GridBagConstraints();
524 c.gridx = 0;
525 c.gridy = 0;
526 c.fill = GridBagConstraints.CENTER;
527 panel.add(expandButton, c);
528
529 c = new GridBagConstraints();
530 c.gridx = 1;
531 c.gridy = 0;
532 c.fill = GridBagConstraints.CENTER;
533 panel.add(cancelButton, c);
534
535 c = new GridBagConstraints();
536 c.gridx = 0;
537 c.gridy = 1;
538 c.gridwidth = 2;
539 c.fill = GridBagConstraints.CENTER;
540 panel.add(selected1, c);
541
542 c = new GridBagConstraints();
543 c.gridx = 0;
544 c.gridy = 2;
545 c.gridwidth = 2;
546 c.fill = GridBagConstraints.CENTER;
547 panel.add(selected2, c);
548
549 c = new GridBagConstraints();
550 c.gridx = 0;
551 c.gridy = 1;
552 c.gridwidth = 2;
553 c.gridheight = 2;
554 c.fill = GridBagConstraints.CENTER;
555 panel.add(selectedMore, c);
556 }
557
558 public void setLocation(int x, int y)
559 {
560 panel.setLocation(x, y);
561 }
562
563 public void onTrackSelectionChanged()
564 {
565 synchronized(expandedTracks) {
566
567 if (expandedTracks.isEmpty()) return;
568
569 selected1.setVisible(false);
570 selected2.setVisible(false);
571 selectedMore.setVisible(false);
572
573 if (expandedTracks.size() <= 2) {
574 for (int i = 0; i < expandedTracks.size(); i++) {
575
576 ExpandedTrackPopup expp = expandedTracks.get(i);
577 JLabel label = (i == 0) ? selected1 : selected2;
578
579 String trackName = expp.getObservedTrackModel().getName();
580
581 if (trackName == null || trackName.length() == 0) trackName = "Unnamed";
582 else if (trackName.length() > 16) trackName = trackName.substring(0, 14) + "...";
583
584 label.setText(trackName);
585 label.setVisible(true);
586
587 }
588
589 } else {
590 selectedMore.setText(expandedTracks.size() + " tracks selected");
591 selectedMore.setVisible(true);
592 }
593 }
594 }
595
596 public void actionPerformed(ActionEvent e)
597 {
598 if (e.getSource() == expandButton) {
599 showSelection();
600 } else if (e.getSource() == cancelButton) {
601 cancelSelection();
602 }
603 }
604
605 @Override
606 protected void paintInternal()
607 {
608 Graphics2D g = SwingMiscManager.getIfUsingSwingGraphicsManager().getCurrentSurface();
609
610 panel.paint(g);
611 }
612
613 @Override
614 public AxisAlignedBoxBounds getFullBounds()
615 {
616 return SwingConversions.fromSwingRectangle(panel.getBounds());
617 }
618
619 @Override
620 public void onHide()
621 {
622 }
623
624 @Override
625 public void onShow()
626 {
627 }
628 }
629}
Note: See TracBrowser for help on using the repository browser.