source: trunk/src/org/apollo/widgets/FramePlayer.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: 23.2 KB
Line 
1package org.apollo.widgets;
2
3import java.awt.Dimension;
4import java.awt.GridBagConstraints;
5import java.awt.GridBagLayout;
6import java.awt.Insets;
7import java.awt.event.ActionEvent;
8import java.awt.event.ActionListener;
9import java.io.BufferedReader;
10import java.io.File;
11import java.io.FileReader;
12import java.io.FileWriter;
13import java.io.IOException;
14import java.util.Deque;
15import java.util.HashSet;
16import java.util.LinkedList;
17import java.util.List;
18import java.util.Set;
19
20import javax.swing.ComboBoxModel;
21import javax.swing.DefaultComboBoxModel;
22import javax.swing.JButton;
23import javax.swing.JComboBox;
24import javax.swing.JPanel;
25import javax.swing.JSlider;
26import javax.swing.JToggleButton;
27import javax.swing.event.ChangeEvent;
28import javax.swing.event.ChangeListener;
29
30import org.apollo.audio.ApolloSubjectChangedEvent;
31import org.apollo.audio.SampledAudioManager;
32import org.apollo.audio.util.MultiTrackPlaybackController;
33import org.apollo.audio.util.SoundDesk;
34import org.apollo.audio.util.TrackMixSubject;
35import org.apollo.audio.util.MultiTrackPlaybackController.MultitrackLoadListener;
36import org.apollo.gui.FrameLayoutDaemon;
37import org.apollo.io.AudioPathManager;
38import org.apollo.io.IconRepository;
39import org.apollo.items.FramePlaybackLauncher;
40import org.apollo.mvc.Observer;
41import org.apollo.mvc.Subject;
42import org.apollo.mvc.SubjectChangedEvent;
43import org.apollo.util.ApolloSystemLog;
44import org.apollo.util.AudioMath;
45import org.expeditee.core.Colour;
46import org.expeditee.core.Image;
47import org.expeditee.gio.gesture.StandardGestureActions;
48import org.expeditee.gio.swing.SwingMiscManager;
49import org.expeditee.gui.DisplayController;
50import org.expeditee.gui.DisplayObserver;
51import org.expeditee.gui.FrameIO;
52import org.expeditee.gui.MessageBay;
53import org.expeditee.items.ItemParentStateChangedEvent;
54import org.expeditee.items.Text;
55import org.expeditee.items.widgets.SwingWidget;
56
57/**
58 * This a last minute hack ... should be revised
59 *
60 * @author Brook Novak
61 *
62 */
63public class FramePlayer extends SwingWidget
64 implements Observer, MultitrackLoadListener, ActionListener, DisplayObserver {
65
66 private static final int BUTTON_SIZE = 40;
67
68 private static TrackMixSubject masterMix = null;
69 private static String currentPlayingFrame = null;
70
71 private JButton playPauseButton;
72 private JButton stopButton;
73 private JButton rewindButton;
74 private JButton playLauncherButton;
75 private JComboBox<String> frameSelection;
76 private JToggleButton muteButton;
77 private JSlider volumeSlider;
78
79 private int state = READY;
80 private String abortMessage = null;
81
82 private boolean isUpdatingGUI = false;
83
84 private static final String CURRENT_FRAME_SPECIFIER = "Current Frame";
85
86 public static String FRAME_PLAYERMASTER_CHANNEL_ID = "#$frameplayer#master$";
87
88 private static final Colour LOADING_BORDER_COLOR = Colour.FromRGB255(22, 205, 5);
89 //private static final Color FAILED_MESSAGE_COLOR = Color.RED;
90 //private static final Font MESSAGE_FONT = TrackWidgetCommons.FREESPACE_TRACKNAME_FONT;
91
92 /** States - mutex in this widget but not really .. for example since can be loading tracks and the graph at the same time... */
93 private static final int READY = 1; // Waiting for user interaction
94 private static final int PLAYBACK_LOADING = 2; // Loading tracks from file/cache to play/resume
95 private static final int PLAYING = 3; // Playing audio.
96
97 private static final int TYPED_FRAME_CAPACITY = 10;
98 private static final int FRAME_HISTORY_CAPACITY = 5;
99
100 private static final String TYPED_FRAMED_HISTORY_FILE = ".typedframes";
101
102 private static Deque<String> typedFrameNames = new LinkedList<String>();
103 private Set<String> frameComboModelData = new HashSet<String>();
104
105 static {
106 masterMix = SoundDesk.getInstance().getOrCreateMix(FRAME_PLAYERMASTER_CHANNEL_ID);
107
108 // Read typed frames
109 File f = new File(AudioPathManager.AUDIO_HOME_DIRECTORY + TYPED_FRAMED_HISTORY_FILE);
110 if (f.exists()) {
111 BufferedReader in = null;
112 String line = null;
113 try {
114
115 // Open the vbase for reading
116 in = new BufferedReader(new FileReader(f));
117
118 // Read the sbase file and check all names
119 while ((line = in.readLine()) != null && typedFrameNames.size() < TYPED_FRAME_CAPACITY) {
120 line = line.trim();
121 if (!line.isEmpty() && !typedFrameNames.contains(line))
122 typedFrameNames.add(line);
123 }
124
125 } catch (Exception e) {
126 e.printStackTrace();
127 // Clean up
128 } finally {
129 if (in != null) {
130 try {
131 in.close();
132 } catch (IOException e) {
133 e.printStackTrace();
134 }
135 }
136 }
137 }
138 }
139
140 /**
141 * Saves typed frames to file
142 */
143 public static void saveTypedFrames() {
144
145 FileWriter out = null;
146
147 try {
148 // Open the vbase for appending
149 out = new FileWriter(AudioPathManager.AUDIO_HOME_DIRECTORY + TYPED_FRAMED_HISTORY_FILE, false);
150 for (String str : typedFrameNames) {
151 out.write(str + "\n");
152 }
153 } catch (IOException e) {
154 e.printStackTrace();
155
156 // Clean up
157 } finally {
158 if (out != null) {
159 try {
160 out.close();
161 } catch (IOException e) {
162 e.printStackTrace();
163 }
164 }
165 }
166 }
167
168
169 /**
170 * Constructor called by Expeditee.
171 *
172 * @param source
173 * @param args
174 */
175 public FramePlayer(Text source, String[] args) {
176 super(source, new JPanel(new GridBagLayout()),
177 BUTTON_SIZE * 12, BUTTON_SIZE * 12,
178 BUTTON_SIZE, BUTTON_SIZE);
179
180 playPauseButton = new JButton();
181 playPauseButton.addActionListener(this);
182 SwingMiscManager.setJButtonIcon(playPauseButton, IconRepository.getIcon("play.png"));
183 playPauseButton.setPreferredSize(new Dimension(BUTTON_SIZE, BUTTON_SIZE));
184 playPauseButton.setToolTipText("Play selection / Pause");
185
186 stopButton = new JButton();
187 stopButton.setEnabled(false);
188 stopButton.addActionListener(this);
189 SwingMiscManager.setJButtonIcon(stopButton, IconRepository.getIcon("stop.png"));
190 stopButton.setPreferredSize(new Dimension(BUTTON_SIZE, BUTTON_SIZE));
191 stopButton.setToolTipText("Stop playback");
192
193 rewindButton = new JButton();
194 rewindButton.addActionListener(this);
195 SwingMiscManager.setJButtonIcon(rewindButton, IconRepository.getIcon("rewind.png"));
196 rewindButton.setPreferredSize(new Dimension(BUTTON_SIZE, BUTTON_SIZE));
197 rewindButton.setToolTipText("Rewind to start");
198
199 // Icon changes
200 muteButton = new JToggleButton();
201 SwingMiscManager.setJButtonIcon(muteButton, IconRepository.getIcon("volmute.png"));
202 muteButton.setPreferredSize(new Dimension(BUTTON_SIZE, BUTTON_SIZE));
203 muteButton.setToolTipText("Toggle mute");
204 muteButton.addChangeListener(new ChangeListener() {
205 public void stateChanged(ChangeEvent e) {
206 if (!FramePlayer.this.isUpdatingGUI) {
207 muteChanged();
208 }
209 }
210 });
211
212 playLauncherButton = new JButton();
213 playLauncherButton.addActionListener(this);
214 SwingMiscManager.setJButtonIcon(playLauncherButton, IconRepository.getIcon("frameplay.png"));
215 playLauncherButton.setPreferredSize(new Dimension(BUTTON_SIZE, BUTTON_SIZE));
216 playLauncherButton.setToolTipText("Play from a specific position");
217
218 final int VOLUME_SPACING = 6;
219 volumeSlider = new JSlider(JSlider.HORIZONTAL);
220 volumeSlider.setMinimum(0);
221 volumeSlider.setMaximum(100);
222 volumeSlider.addChangeListener(new ChangeListener() {
223 public void stateChanged(ChangeEvent e) {
224 if (!FramePlayer.this.isUpdatingGUI) {
225 volumeChanged();
226 }
227 // Update the icons
228 updateButtonGUI();
229 }
230 });
231 volumeSlider.setPreferredSize(new Dimension((3 * BUTTON_SIZE) - (2 * VOLUME_SPACING), BUTTON_SIZE));
232
233 frameSelection = new JComboBox<String>();
234 frameSelection.setEditable(true);
235 frameSelection.setPreferredSize(new Dimension(4 * BUTTON_SIZE, BUTTON_SIZE));
236
237 GridBagConstraints c = new GridBagConstraints();
238 c.gridx = 0;
239 c.gridy = 0;
240 c.fill = GridBagConstraints.BOTH;
241 _swingComponent.add(playPauseButton, c);
242
243 c = new GridBagConstraints();
244 c.gridx = 1;
245 c.gridy = 0;
246 c.fill = GridBagConstraints.BOTH;
247 _swingComponent.add(playLauncherButton, c);
248
249 c = new GridBagConstraints();
250 c.gridx = 2;
251 c.gridy = 0;
252 c.fill = GridBagConstraints.BOTH;
253 _swingComponent.add(stopButton, c);
254
255 c = new GridBagConstraints();
256 c.gridx = 3;
257 c.gridy = 0;
258 c.fill = GridBagConstraints.BOTH;
259 _swingComponent.add(rewindButton, c);
260
261 c = new GridBagConstraints();
262 c.gridx = 4;
263 c.gridy = 0;
264 c.fill = GridBagConstraints.BOTH;
265 _swingComponent.add(muteButton, c);
266
267 c = new GridBagConstraints();
268 c.gridx = 5;
269 c.gridy = 0;
270 c.fill = GridBagConstraints.BOTH;
271 c.insets = new Insets(0,VOLUME_SPACING,0,VOLUME_SPACING);
272 _swingComponent.add(volumeSlider, c);
273
274 c = new GridBagConstraints();
275 c.gridx = 6;
276 c.gridy = 0;
277 c.fill = GridBagConstraints.BOTH;
278 _swingComponent.add(frameSelection, c);
279
280 updateButtonGUI();
281 updateBorderColor();
282 updateFrameSelection();
283
284 setWidgetEdgeThickness(TrackWidgetCommons.STOPPED_TRACK_EDGE_THICKNESS);
285
286 }
287
288
289 /**
290 * Sets the GUI and all the popups to reflect the current state.
291 * Must be on the swing thread.
292 *
293 * @param newState
294 * The new state. The same state then it is ignored, unless
295 * its LOADING_TRACK_GRAPH - in that case the graph is reloaded.
296 */
297 private void setState(int newState) {
298 if (this.state == newState) return; // no need to process request.
299
300 switch(newState) {
301 case READY:
302
303 rewindButton.setEnabled(true);
304 stopButton.setEnabled(false);
305 playPauseButton.setEnabled(true);
306 SwingMiscManager.setJButtonIcon(playPauseButton, IconRepository.getIcon("play.png"));
307 playLauncherButton.setEnabled(true);
308
309 setWidgetEdgeThickness(TrackWidgetCommons.STOPPED_TRACK_EDGE_THICKNESS);
310
311 break;
312
313 case PLAYBACK_LOADING:
314 playLauncherButton.setEnabled(false);
315 stopButton.setEnabled(false);
316 rewindButton.setEnabled(false);
317 playPauseButton.setEnabled(false);
318 break;
319
320 case PLAYING:
321 stopButton.setEnabled(true);
322 rewindButton.setEnabled(false);
323 playLauncherButton.setEnabled(true);
324 playPauseButton.setEnabled(true);
325 SwingMiscManager.setJButtonIcon(playPauseButton, IconRepository.getIcon("pause.png"));
326
327 setWidgetEdgeThickness(TrackWidgetCommons.PLAYING_TRACK_EDGE_THICKNESS);
328
329 break;
330
331 }
332
333 state = newState;
334
335 updateBorderColor();
336
337 invalidateSelf();
338 DisplayController.requestRefresh(true);
339 }
340
341 /**
342 * {@inheritDoc}
343 */
344 @Override
345 protected void onParentStateChanged(int eventType) {
346 super.onParentStateChanged(eventType);
347
348
349 //Frame currentFrame = DisplayIO.getCurrentFrame();
350 // String currentFrameName = (currentFrame != null) ? currentFrame.getName() : null;
351
352 switch (eventType) {
353
354 case ItemParentStateChangedEvent.EVENT_TYPE_ADDED:
355 case ItemParentStateChangedEvent.EVENT_TYPE_ADDED_VIA_OVERLAY:
356 case ItemParentStateChangedEvent.EVENT_TYPE_SHOWN:
357 case ItemParentStateChangedEvent.EVENT_TYPE_SHOWN_VIA_OVERLAY:
358
359 // Setup observers
360 SoundDesk.getInstance().addObserver(this); // for solo
361 MultiTrackPlaybackController.getInstance().addObserver(this); // the core!
362 masterMix.addObserver(this);
363 DisplayController.addDisplayObserver(this);
364
365 // Evaluate the state of this add set the state accordingly
366 if (currentPlayingFrame != null
367 && MultiTrackPlaybackController.getInstance().isLoading(
368 currentPlayingFrame,
369 masterMix.getChannelID())) {
370
371 // Ensure that am receiving notifiactions:
372 List<String> loaded = MultiTrackPlaybackController.getInstance().attachLoadListener(this);
373 assert(loaded != null);
374
375 setState(PLAYBACK_LOADING);
376
377 } else if (currentPlayingFrame != null
378 && MultiTrackPlaybackController.getInstance().isPlaying(
379 currentPlayingFrame,
380 masterMix.getChannelID())) {
381
382 setState(PLAYING);
383
384 } else {
385 setState(READY);
386 }
387
388 updateFrameSelection();
389
390 break;
391
392 case ItemParentStateChangedEvent.EVENT_TYPE_REMOVED:
393
394 // Cancel loading of audio
395 if (state == PLAYBACK_LOADING) {
396 assert(currentPlayingFrame != null);
397
398 MultiTrackPlaybackController.getInstance().cancelLoad(
399 currentPlayingFrame,
400 masterMix.getChannelID());
401 }
402
403 case ItemParentStateChangedEvent.EVENT_TYPE_REMOVED_VIA_OVERLAY: // TODO revise - and in sampled track widget
404 case ItemParentStateChangedEvent.EVENT_TYPE_HIDDEN:
405
406 // Remove observers
407 DisplayController.removeDisplayObserver(this);
408 SoundDesk.getInstance().removeObserver(this);
409 MultiTrackPlaybackController.getInstance().removeObserver(this);
410 masterMix.removeObserver(this);
411
412 setState(READY);
413 break;
414
415 }
416 }
417
418
419
420 /**
421 * {@inheritDoc}
422 */
423 public void multiplaybackLoadStatusUpdate(int id, Object state) {
424
425 // NOTE: Could switch to an unloaded state
426 if (this.state != PLAYBACK_LOADING) return;
427
428 switch(id) {
429 case MultitrackLoadListener.LOAD_CANCELLED:
430 setState(READY);
431 break;
432 case MultitrackLoadListener.LOAD_COMPLETE:
433 break;
434 case MultitrackLoadListener.LOAD_FAILED_BAD_GRAPH:
435 abortMessage = "Graph contains loops";
436 ((Exception)state).printStackTrace();
437 break;
438 case MultitrackLoadListener.LOAD_FAILED_GENERIC:
439 abortMessage = "Unexpected error";
440 ((Exception)state).printStackTrace();
441 break;
442 case MultitrackLoadListener.LOAD_FAILED_PLAYBACK:
443 abortMessage = "Unable to aquire sound device";
444 break;
445 case MultitrackLoadListener.NOTHING_TO_PLAY:
446 abortMessage = "Nothing to play"; // could be due to user slecting empty space
447 break;
448 case MultitrackLoadListener.TRACK_LOAD_FAILED_IO:
449 // This is special... the loader does not abort... and it tries to load more.
450 ((Exception)state).printStackTrace();
451 break;
452 case MultitrackLoadListener.TRACK_LOADED:
453 break;
454
455 }
456
457 if (abortMessage != null) {
458 ApolloSystemLog.println("Aborted playback - " + abortMessage);
459 setState(READY);
460 }
461
462 }
463
464 /**
465 * {@inheritDoc}
466 */
467 public Subject getObservedSubject() {
468 return null;
469 }
470
471 /**
472 * {@inheritDoc}
473 */
474 public void modelChanged(Subject source, SubjectChangedEvent event) {
475
476 // Synch GUI with track state
477 switch (event.getID()) {
478
479 case ApolloSubjectChangedEvent.PLAYBACK_STARTED:
480
481 if (currentPlayingFrame != null &&
482 MultiTrackPlaybackController.getInstance().isCurrentPlaybackSubject(
483 currentPlayingFrame, masterMix.getChannelID())) {
484 setState(PLAYING);
485 }
486
487 break;
488
489 case ApolloSubjectChangedEvent.PLAYBACK_STOPPED:
490
491 if (state == PLAYING) {
492 //assert(currentPlayingFrame != null);
493
494 // Transition into new state
495 setState(READY);
496
497 }
498 break;
499
500 case ApolloSubjectChangedEvent.MULTIPLAYBACK_LOADING:
501
502 // Adjust state accordingly
503 if (currentPlayingFrame != null &&
504 MultiTrackPlaybackController.getInstance().isCurrentPlaybackSubject(
505 currentPlayingFrame, masterMix.getChannelID()) && state != PLAYBACK_LOADING) {
506 MultiTrackPlaybackController.getInstance().attachLoadListener(this);
507 setState(PLAYBACK_LOADING);
508 }
509 break;
510
511 case ApolloSubjectChangedEvent.VOLUME: // From obseved track mix
512 updateVolume();
513 break;
514
515 case ApolloSubjectChangedEvent.MUTE: // From obseved track mix
516 updateMute();
517 updateBorderColor();
518 break;
519
520 }
521
522 }
523
524 /**
525 * {@inheritDoc}
526 */
527 public void setObservedSubject(Subject parent) {
528 }
529
530 /**
531 * {@inheritDoc}
532 */
533 @Override
534 protected String[] getArgs() {
535 return null;
536 }
537
538 /**
539 * @return
540 * The selected frame name. Null if not valid.
541 */
542 private String getSelectedFrameName() {
543
544 if ((frameSelection.getSelectedItem() != null && frameSelection.getSelectedItem().equals(CURRENT_FRAME_SPECIFIER))
545 || frameSelection.getSelectedItem() == null)
546 return DisplayController.getCurrentFrame().getName();
547
548 String frameSpecifier = (String)frameSelection.getSelectedItem();
549
550 if (frameSpecifier.isEmpty()) {
551 return DisplayController.getCurrentFrame().getName();
552 }
553
554 frameSpecifier = frameSpecifier.trim();
555 int metaIndex = frameSpecifier.indexOf('<');
556 if (metaIndex > 0) {
557 frameSpecifier = frameSpecifier.substring(0, metaIndex).trim();
558 }
559
560 if (!FrameIO.isValidFrameName(frameSpecifier)) return null;
561
562 return frameSpecifier;
563 }
564
565 /**
566 * {@inheritDoc}
567 */
568 public void actionPerformed(ActionEvent e) {
569
570 if (!(state == PLAYING || state == READY)) return; // safety
571
572 String selectedFrameName = getSelectedFrameName();
573
574 if (e.getSource() == playPauseButton) {
575
576 if (state == READY) { // comence playback
577
578 if (selectedFrameName == null) {
579
580 // Bad frame specifier
581 MessageBay.displayMessage("Cannot play frame \"" +
582 frameSelection.getSelectedItem() +
583 "\" - bad name");
584
585 } else {
586
587 int startFrame = -1, endFrame = -1;
588
589 // Remember typed frames
590 if (frameSelection.getSelectedItem() != null && !frameSelection.getSelectedItem().equals(CURRENT_FRAME_SPECIFIER)) {
591
592 String typedFrame = (String)frameSelection.getSelectedItem();
593
594 if (!frameComboModelData.contains(typedFrame.toLowerCase())) {
595
596 typedFrameNames.addFirst(selectedFrameName);
597 if (typedFrameNames.size() >= TYPED_FRAME_CAPACITY)
598 typedFrameNames.removeLast();
599 updateFrameSelection();
600
601 }
602
603 }
604
605 // Resume playback?
606 if (currentPlayingFrame != null &&
607 MultiTrackPlaybackController.getInstance().isMarkedAsPaused(
608 currentPlayingFrame, masterMix.getChannelID())) {
609
610 long runningTime = FrameLayoutDaemon.inferCurrentTotalMSTime();
611 long inferredTotalFrameLength = -1;
612 if (runningTime > 0) {
613 inferredTotalFrameLength = AudioMath.millisecondsToFrames(
614 runningTime,
615 SampledAudioManager.getInstance().getDefaultPlaybackFormat());
616 }
617
618
619 startFrame = MultiTrackPlaybackController.getInstance().getLastSuspendedFrame();
620
621 if (inferredTotalFrameLength > 0 &&
622 startFrame >= 0 && startFrame < inferredTotalFrameLength) {
623 // Work around for playing to end of frame: give biggest value
624 // since it is eventually clamped:
625 endFrame = Integer.MAX_VALUE;
626 }
627 }
628
629 // Play from beginning of selection to end of selection
630 if (startFrame < 0) {
631 startFrame = 0;
632 endFrame = Integer.MAX_VALUE; // see notes about for workaround hack
633 }
634
635 if (startFrame < endFrame) {
636
637 playFrame(this,
638 selectedFrameName,
639 false, // TODO: Set appropriatly
640 startFrame,
641 endFrame);
642
643 setState(PLAYBACK_LOADING);
644
645 }
646 }
647
648 } else { // pause
649
650 MultiTrackPlaybackController.getInstance().setPauseMark(true);
651 MultiTrackPlaybackController.getInstance().stopPlayback();
652
653 }
654
655
656 } else if (e.getSource() == stopButton) {
657 assert(currentPlayingFrame != null);
658
659 if (MultiTrackPlaybackController.getInstance().isCurrentPlaybackSubject(
660 currentPlayingFrame, masterMix.getChannelID())) {
661 MultiTrackPlaybackController.getInstance().setPauseMark(false);
662 MultiTrackPlaybackController.getInstance().stopPlayback();
663 }
664
665
666 } else if (e.getSource() == rewindButton) {
667 assert(state != PLAYING);
668 MultiTrackPlaybackController.getInstance().setPauseMark(false);
669
670 } else if (e.getSource() == playLauncherButton) {
671
672 String target = getSelectedFrameName();
673 if (target == DisplayController.getCurrentFrame().getName()) target = null;
674
675 // Create the launcher
676 FramePlaybackLauncher launcher = new FramePlaybackLauncher(target);
677 launcher.setPosition(DisplayController.getMousePosition());
678
679 // Pick it up
680 StandardGestureActions.pickup(launcher);
681 }
682
683 }
684
685
686 private void updateBorderColor() {
687
688 // Get border color currently used
689 Colour oldC = getSource().getBorderColor();
690 Colour newC = null;
691
692 if (this.state == PLAYBACK_LOADING) {
693
694 newC = LOADING_BORDER_COLOR;
695
696 } else {
697
698 newC = TrackWidgetCommons.getBorderColor(
699 SoundDesk.getInstance().isSolo(masterMix.getChannelID()),
700 masterMix.isMuted());
701
702 }
703
704 // Update the color
705 if (!newC.equals(oldC)) {
706 setWidgetEdgeColor(newC);
707 }
708 }
709
710 /**
711 * Sets the mute icon to represent the current volume value in the slider.
712 * Note: this is not the icon if mute is on.
713 */
714 private void updateButtonGUI() {
715
716 Image newIcon = null;
717 if (volumeSlider.getValue() <= 25)
718 newIcon = IconRepository.getIcon("vol25.png");
719 else if (volumeSlider.getValue() <= 50)
720 newIcon = IconRepository.getIcon("vol50.png");
721 else if (volumeSlider.getValue() <= 75)
722 newIcon = IconRepository.getIcon("vol75.png");
723 else // maxing
724 newIcon = IconRepository.getIcon("vol100.png");
725
726 SwingMiscManager.setJButtonIcon(muteButton, newIcon);
727 }
728
729
730 public void volumeChanged() {
731 masterMix.setVolume(((float)volumeSlider.getValue()) / 100.0f);
732 }
733
734 public void muteChanged() {
735 masterMix.setMuted(muteButton.isSelected());
736 }
737
738
739
740 /**
741 * Updates the volume GUI for all views
742 */
743 public void updateVolume() {
744 int volume = (int)(100 * masterMix.getVolume());
745
746 if (volumeSlider.getValue() == volume) return;
747
748 isUpdatingGUI = true;
749
750 volumeSlider.setValue(volume);
751
752 isUpdatingGUI = false;
753 }
754
755
756 /**
757 * Updates the mute button GUI for all views.
758 */
759 public void updateMute() {
760 if (muteButton.isSelected() == masterMix.isMuted()) return;
761
762 isUpdatingGUI = true;
763
764 muteButton.setSelected(masterMix.isMuted());
765
766 isUpdatingGUI = false;
767 }
768
769 /**
770 * Updates the combo box on frame changes
771 */
772 public void frameChanged() {
773 updateFrameSelection();
774 }
775
776 private void updateFrameSelection() {
777
778
779 frameComboModelData.clear();
780 List<String> orderedModelData = new LinkedList<String>();
781
782 orderedModelData.add(CURRENT_FRAME_SPECIFIER);
783
784 // Place last visited frames
785 List<String> history = DisplayController.getUnmodifiableVisitedList();
786
787 for (int i = history.size() - 1; i >= 0; i--) {
788 String str = history.get(i);
789
790 if ((history.size() - i) >= FRAME_HISTORY_CAPACITY) break;
791
792 String frameName = str;
793 for (int j = 0; j <= (history.size() - i); j++) frameName += "<";
794
795 orderedModelData.add(frameName);
796 }
797
798 // Append last typed frames
799 for (String str : typedFrameNames) {
800 if (!history.contains(str))
801 orderedModelData.add(str);
802 }
803
804 ComboBoxModel<String> model = new DefaultComboBoxModel<String>((String[]) orderedModelData.toArray());
805
806 // Remove helper tags from model data
807
808 frameComboModelData = new HashSet<String>();
809 for (String str : orderedModelData) {
810 int index = str.indexOf('<');
811 assert(index != 0);
812 if (index > 1) {
813 frameComboModelData.add(str.substring(0, index).trim().toLowerCase());
814 } else {
815 frameComboModelData.add(str.trim().toLowerCase());
816 }
817 }
818
819 Object prevSelected = frameSelection.getSelectedItem();
820
821 frameSelection.setModel(model);
822
823 if (prevSelected == null)
824 frameSelection.setSelectedIndex(0);
825 else
826 frameSelection.setSelectedItem(prevSelected);
827
828
829
830 }
831
832 /**
833 * Plays a frame with the FramePlayer mix.
834 *
835 * @see MultiTrackPlaybackController#playFrame(MultitrackLoadListener, String, String, boolean, int, int, int)
836 *
837 */
838 public static void playFrame(
839 MultitrackLoadListener loadListener,
840 String rootFrameName,
841 boolean resume,
842 int startFrame,
843 int endFrame) {
844
845 if (loadListener == null) throw new NullPointerException("loadListener");
846 if (rootFrameName == null) throw new NullPointerException("rootFrameName");
847
848
849 currentPlayingFrame = rootFrameName;
850
851 MultiTrackPlaybackController.getInstance().playFrame(
852 loadListener,
853 rootFrameName,
854 masterMix.getChannelID(),
855 resume,
856 startFrame,
857 endFrame);
858 }
859
860}
Note: See TracBrowser for help on using the repository browser.