source: trunk/src/org/apollo/widgets/FramePlayer.java@ 1434

Last change on this file since 1434 was 1434, checked in by bln4, 5 years ago

Implementation of ProfileManager. Refactor + additional content for how new profiles are created. The refactoring split out the creation of the default profile from user profiles. Refactoring revealed a long term bug that was causing user profiles to generate with incorrect information. The additional content fixed this bug by introducing the ${USER.NAME} variable, so that the default profile frameset can specify resource locations located in the users resource directory.

org.expeditee.auth.AuthenticatorBrowser
org.expeditee.auth.account.Create
org.expeditee.gui.Browser
org.expeditee.gui.management.ProfileManager
org.expeditee.setting.DirectoryListSetting
org.expeditee.setting.ListSetting
org.expeditee.settings.UserSettings

Implementation of ResourceManager as a core location to get resources from the file system. Also the additional variable ${CURRENT_FRAMESET} to represent the current frameset, so that images can be stored in the directory of the current frameset. This increases portability of framesets.

org.expeditee.gui.FrameIO
org.expeditee.gui.management.ResourceManager
org.expeditee.gui.management.ResourceUtil
Audio:

#NB: Audio used to only operate on a single directory. This has been updated to work in a same way as images. That is: when you ask for a specific resouce, it looks to the user settings to find a sequence of directories to look at in order until it manages to find the desired resource.


There is still need however for a single(ish) source of truth for the .banks and .mastermix file. Therefore these files are now always located in resource-<username>\audio.
org.apollo.agents.MelodySearch
org.apollo.audio.structure.AudioStructureModel
org.apollo.audio.util.MultiTrackPlaybackController
org.apollo.audio.util.SoundDesk
org.apollo.gui.FrameLayoutDaemon
org.apollo.io.AudioPathManager
org.apollo.util.AudioPurger
org.apollo.widgets.FramePlayer
org.apollo.widgets.SampledTrack

Images:

org.expeditee.items.ItemUtils

Frames:

org.expeditee.gui.FrameIO

Fixed a error in the FramePlayer class caused by an incorrect use of toArray().

org.apollo.widgets.FramePlayer


Added several short cut keys to allow for the Play/Pause (Ctrl + P), mute (Ctrl + M) and volume up/down (Ctrl + +/-) when hovering over SampledTrack widgets.

org.apollo.widgets.SampledTrack


Changed the way that Authenticate.login parses the new users profile to be more consistance with other similar places in code.

org.expeditee.auth.account.Authenticate


Encapsulated _body, _surrogateItemsBody and _primaryItemsBody in Frame class. Also changed getBody function to take a boolean flag as to if it should respect the current surrogate mode. If it should then it makes sure that labels have not changed since last time getBody was called.

org.expeditee.gui.Frame

File size: 23.3 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_USERNAME_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_USERNAME_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(new String[] {}));
805 //ComboBoxModel model = new DefaultComboBoxModel(orderedModelData.toArray());
806
807 // Remove helper tags from model data
808
809 frameComboModelData = new HashSet<String>();
810 for (String str : orderedModelData) {
811 int index = str.indexOf('<');
812 assert(index != 0);
813 if (index > 1) {
814 frameComboModelData.add(str.substring(0, index).trim().toLowerCase());
815 } else {
816 frameComboModelData.add(str.trim().toLowerCase());
817 }
818 }
819
820 Object prevSelected = frameSelection.getSelectedItem();
821
822 frameSelection.setModel(model);
823
824 if (prevSelected == null)
825 frameSelection.setSelectedIndex(0);
826 else
827 frameSelection.setSelectedItem(prevSelected);
828
829
830
831 }
832
833 /**
834 * Plays a frame with the FramePlayer mix.
835 *
836 * @see MultiTrackPlaybackController#playFrame(MultitrackLoadListener, String, String, boolean, int, int, int)
837 *
838 */
839 public static void playFrame(
840 MultitrackLoadListener loadListener,
841 String rootFrameName,
842 boolean resume,
843 int startFrame,
844 int endFrame) {
845
846 if (loadListener == null) throw new NullPointerException("loadListener");
847 if (rootFrameName == null) throw new NullPointerException("rootFrameName");
848
849
850 currentPlayingFrame = rootFrameName;
851
852 MultiTrackPlaybackController.getInstance().playFrame(
853 loadListener,
854 rootFrameName,
855 masterMix.getChannelID(),
856 resume,
857 startFrame,
858 endFrame);
859 }
860
861}
Note: See TracBrowser for help on using the repository browser.