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

Last change on this file since 1102 was 1102, checked in by davidb, 6 years ago

Reworking of the code-base to separate logic from graphics. This version of Expeditee now supports a JFX graphics as an alternative to SWING

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.getFramePaintArea().getWidth() - 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.getFramePaintArea().getWidth();
416 int cpHeight = DisplayController.getFramePaintArea().getHeight();
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.