0001: /*
0002: * Jacareto Copyright (c) 2002-2005
0003: * Applied Computer Science Research Group, Darmstadt University of
0004: * Technology, Institute of Mathematics & Computer Science,
0005: * Ludwigsburg University of Education, and Computer Based
0006: * Learning Research Group, Aachen University. All rights reserved.
0007: *
0008: * Jacareto is free software; you can redistribute it and/or
0009: * modify it under the terms of the GNU General Public
0010: * License as published by the Free Software Foundation; either
0011: * version 2 of the License, or (at your option) any later version.
0012: *
0013: * Jacareto is distributed in the hope that it will be useful,
0014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0016: * General Public License for more details.
0017: *
0018: * You should have received a copy of the GNU General Public
0019: * License along with Jacareto; if not, write to the Free
0020: * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
0021: *
0022: */
0023:
0024: package jacareto.cleverphl.gui;
0025:
0026: import com.jgoodies.forms.builder.PanelBuilder;
0027: import com.jgoodies.forms.layout.CellConstraints;
0028: import com.jgoodies.forms.layout.FormLayout;
0029:
0030: import jacareto.cleverphl.CleverPHL;
0031: import jacareto.cleverphl.session.Session;
0032: import jacareto.cleverphl.session.SessionEvent;
0033: import jacareto.cleverphl.session.SessionListEvent;
0034: import jacareto.cleverphl.session.SessionListListener;
0035: import jacareto.cleverphl.session.SessionListener;
0036: import jacareto.record.AudioClipRecordable;
0037: import jacareto.record.MediaClipRecordable;
0038: import jacareto.record.Recordable;
0039: import jacareto.record.VideoClipRecordable;
0040: import jacareto.replay.MediaClipReplayer;
0041: import jacareto.replay.Replay;
0042: import jacareto.replay.ReplayMode;
0043: import jacareto.replay.Replayer;
0044: import jacareto.replay.event.ReplayEvent;
0045: import jacareto.replay.event.ReplayListener;
0046: import jacareto.replay.exception.ComponentNotFoundException;
0047: import jacareto.toolkit.ResourceLoader;
0048: import jacareto.track.TrackModel;
0049: import jacareto.track.TrackModelEvent;
0050: import jacareto.track.TrackModelListener;
0051: import jacareto.track.block.Block;
0052: import jacareto.track.block.BlockType;
0053: import jacareto.track.sync.SyncMode;
0054: import jacareto.trackimpl.DataModelConnector;
0055: import jacareto.trackimpl.DefaultTrackModel;
0056: import jacareto.trackimpl.SelectionModelConnector;
0057: import jacareto.trackimpl.TrackViewPanel;
0058: import jacareto.trackimpl.blockimpl.DefaultBlock;
0059: import jacareto.trackimpl.blockimpl.DefaultBlockFactory;
0060: import jacareto.trackimpl.syncimpl.TrackSynchronizer;
0061:
0062: import org.apache.commons.lang.Validate;
0063:
0064: import java.awt.event.ActionEvent;
0065: import java.awt.event.ActionListener;
0066: import java.awt.event.ItemEvent;
0067: import java.awt.event.ItemListener;
0068: import java.awt.event.KeyEvent;
0069: import java.awt.event.WindowAdapter;
0070: import java.awt.event.WindowEvent;
0071:
0072: import java.beans.PropertyChangeEvent;
0073: import java.beans.PropertyChangeListener;
0074:
0075: import java.text.DecimalFormat;
0076:
0077: import java.util.Hashtable;
0078:
0079: import javax.swing.AbstractAction;
0080: import javax.swing.BorderFactory;
0081: import javax.swing.ButtonGroup;
0082: import javax.swing.ImageIcon;
0083: import javax.swing.JButton;
0084: import javax.swing.JCheckBox;
0085: import javax.swing.JCheckBoxMenuItem;
0086: import javax.swing.JComponent;
0087: import javax.swing.JFormattedTextField;
0088: import javax.swing.JFrame;
0089: import javax.swing.JLabel;
0090: import javax.swing.JPanel;
0091: import javax.swing.JRadioButton;
0092: import javax.swing.JSlider;
0093: import javax.swing.JSpinner;
0094: import javax.swing.KeyStroke;
0095: import javax.swing.SpinnerNumberModel;
0096: import javax.swing.border.EmptyBorder;
0097: import javax.swing.event.ChangeEvent;
0098: import javax.swing.event.ChangeListener;
0099: import javax.swing.text.NumberFormatter;
0100:
0101: /**
0102: * <p>
0103: * The TrackEditorFrame provides a graphical overview over the record structure from the
0104: * perspective of the three medias:
0105: * </p>
0106: *
0107: * <ul>
0108: * <li>
0109: * Audio
0110: * </li>
0111: * <li>
0112: * Video
0113: * </li>
0114: * <li>
0115: * Application interaction
0116: * </li>
0117: * </ul>
0118: *
0119: *
0120: * @author Oliver Specht
0121: */
0122: public class TrackEditorFrame extends CleverPHLFrame implements
0123: ReplayListener, TrackModelListener, ActionListener,
0124: ChangeListener, ItemListener, SessionListListener,
0125: SessionListener {
0126: /** The label for the current timeLabel position text field */
0127: private JLabel timeLabel = new JLabel("Time in ms: ");
0128:
0129: /** The text field to show the current timeLabel position in ms */
0130: private SliderValueTextField timeTextField = new SliderValueTextField();
0131:
0132: /** The button group for the different sync calculations */
0133: private ButtonGroup buttonGroup;
0134:
0135: /** The button to switch to greedy calculation */
0136: private JRadioButton greedySyncButton = new JRadioButton();
0137:
0138: /** The button to switch to relative calculation */
0139: private JRadioButton relativeSyncButton = new JRadioButton();
0140:
0141: /** The spinner to set the maximum buffer size */
0142: private JSpinner maxBuffSpinner;
0143:
0144: /** The spinner to set the sync rate size */
0145: private JSpinner syncRateSpinner;
0146:
0147: /** The checkbox to set the synchronization on/off */
0148: private JCheckBox syncSwitch;
0149:
0150: /** The fast forward button. */
0151: private JButton ffwdButton;
0152:
0153: /** The images */
0154: private ImageIcon arrowTime;
0155: private ImageIcon clock;
0156: private ImageIcon syncLeft;
0157: private ImageIcon syncRight;
0158:
0159: /** The view panel */
0160: private TrackViewPanel viewPanel;
0161:
0162: /** The TrackSynchronizer */
0163: private TrackSynchronizer trackSynchronizer;
0164:
0165: /** The slider */
0166: private Slider slider;
0167:
0168: /** Connector to connect the TreeSelectionModel with the TrackSelectionModel */
0169: SelectionModelConnector selectionConnector;
0170:
0171: /** Connector to connect the three data models (Record, Structure and TrackModel) */
0172: DataModelConnector dataConnector;
0173:
0174: /** The TrackModel of this frame */
0175: TrackModel trackModel;
0176:
0177: /**
0178: * Default constructor.
0179: *
0180: * @param cleverPHL {@link CleverPHL}
0181: */
0182: public TrackEditorFrame(CleverPHL cleverPHL) {
0183: super (cleverPHL, "CleverPHL.TrackEditorFrame",
0184: "CleverPHL.Menu.ViewTrackEditor");
0185: cleverPHL.getSessionList().addSessionListListener(this );
0186: init();
0187: buildContent();
0188:
0189: WindowListenerAdapter adapter = new WindowListenerAdapter(this );
0190: addWindowListener(adapter);
0191: }
0192:
0193: /**
0194: * Reinitializes the TrackEditorFrame.
0195: */
0196: public void reInit() {
0197: //System.out.println ("TrackEditorFrame.reInit() - Old TrackModel: " + trackModel);
0198: initTitle();
0199: this .trackModel = DefaultTrackModel.create(this .cleverPHL
0200: .getSessionList().getActual().getRecord());
0201: this .trackModel.addTrackModelListener(this );
0202:
0203: // System.out.println("TrackEditorFrame.reInit() - New TrackModel: " + trackModel);
0204: // set the TrackModel in the TrackViewPanel
0205: this .viewPanel.setTrackModel(this .trackModel);
0206: slider.reInit(this .trackModel.getEndTime());
0207:
0208: // init the TrackSynchronizer with the main Controller and TrackModel
0209: this .trackSynchronizer = new TrackSynchronizer(this .cleverPHL
0210: .getSessionList().getActual().getReplay(),
0211: this .trackModel);
0212: this .trackSynchronizer.setMainController(this .cleverPHL
0213: .getSessionList().getActual().getReplay());
0214:
0215: // connect the two selection models
0216: selectionConnector = SelectionModelConnector
0217: .create(this .cleverPHL.getSessionList().getActual()
0218: .getStructureTree());
0219: selectionConnector.getTrackSelectionModel()
0220: .addTrackSelectionListener(this .viewPanel);
0221:
0222: // connect the two data models
0223: // System.out.println("TrackEditorFrame: Connecting DataModels");
0224: if (dataConnector == null) {
0225: dataConnector = DataModelConnector.create(this .trackModel,
0226: this .cleverPHL.getSessionList().getActual());
0227: } else {
0228: // System.out.println("TrackEditorFrame: Reinit DataModelConnector");
0229: dataConnector.reInit(this .cleverPHL.getSessionList()
0230: .getActual(), this .trackModel);
0231: }
0232:
0233: dataConnector.setEnvironment(this .cleverPHL.getEnvironment());
0234:
0235: slider.reInit(this .viewPanel.getTrackModel().getEndTime());
0236:
0237: //timeTextField.setValue (new Integer(0));
0238: this .syncSwitch.setEnabled(true);
0239: this .validate();
0240: }
0241:
0242: private void initTitle() {
0243: if (this .cleverPHL.getSessionList().getActual() == null) {
0244: this .setTitle(getCleverPHL().getLanguage().getString(
0245: "CleverPHL.TrackEditorFrame.Title")
0246: + " - "
0247: + this .cleverPHL.getEnvironment().getLanguage()
0248: .getString("Session.DefaultName"));
0249: } else {
0250: this .setTitle(getCleverPHL().getLanguage().getString(
0251: "CleverPHL.TrackEditorFrame.Title")
0252: + " - "
0253: + this .cleverPHL.getSessionList().getActual()
0254: .getName());
0255: }
0256: }
0257:
0258: private void init() {
0259: this .setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
0260: initTitle();
0261: this .viewPanel = new TrackViewPanel(getCleverPHL()
0262: .getEnvironment(), this );
0263:
0264: slider = new Slider(0);
0265: slider.addChangeListener(timeTextField);
0266:
0267: ffwdButton = new JButton("ffwd");
0268: ffwdButton.addActionListener(new ActionListener() {
0269: public void actionPerformed(ActionEvent event) {
0270: Session session = getCleverPHL().getSessionList()
0271: .getActual();
0272: Replay replay = session.getReplay();
0273: long newTime = slider.getTimeValue();
0274:
0275: if (newTime > replay.getMediaTime()) {
0276: session.fastForwardToTime(newTime);
0277: }
0278: }
0279: });
0280:
0281: timeLabel.setBorder(BorderFactory.createLoweredBevelBorder());
0282: timeTextField.setEditable(true);
0283: timeTextField.addPropertyChangeListener(slider);
0284: timeTextField.setValue(new Integer(0));
0285:
0286: // Add ActionListeners to RadioButtons
0287: this .greedySyncButton.setActionCommand(String
0288: .valueOf(SyncMode.GREEDY));
0289: this .relativeSyncButton.setActionCommand(String
0290: .valueOf(SyncMode.RELATIVE));
0291: this .greedySyncButton.addActionListener(this );
0292: this .relativeSyncButton.addActionListener(this );
0293: this .relativeSyncButton.setSelected(true);
0294:
0295: // Create Spinners with number model
0296: SpinnerNumberModel maxBuffModel = new SpinnerNumberModel(200,
0297: 0, 2000, 1);
0298: this .maxBuffSpinner = new JSpinner(maxBuffModel);
0299: this .maxBuffSpinner.addChangeListener(this );
0300:
0301: SpinnerNumberModel syncRateModel = new SpinnerNumberModel(200,
0302: 15, 2000, 1);
0303: this .syncRateSpinner = new JSpinner(syncRateModel);
0304: this .syncRateSpinner.addChangeListener(this );
0305:
0306: this .syncSwitch = new JCheckBox();
0307: this .syncSwitch.addItemListener(this );
0308: }
0309:
0310: private void buildContent() {
0311: this .loadIcons();
0312:
0313: FormLayout layout = new FormLayout("13px,p:g,13px",
0314: "f:p:g,3dlu,p,3dlu,p,10dlu,p,3dlu,p");
0315: PanelBuilder builder = new PanelBuilder(layout);
0316: builder.setBorder(new EmptyBorder(17, 5, 17, 5));
0317:
0318: CellConstraints cc = new CellConstraints();
0319: builder.add(viewPanel, cc.xy(2, 1));
0320: builder.add(slider, cc.xyw(1, 3, 3));
0321:
0322: //slider.setFocusable (false);
0323: builder.add(buildTrackerPanel(), cc.xy(2, 5,
0324: CellConstraints.LEFT, CellConstraints.TOP));
0325:
0326: builder.addSeparator(getCleverPHL().getLanguage().getString(
0327: "CleverPHL.TrackEditorFrame.Synchronization")
0328: + " ", cc.xy(2, 7));
0329:
0330: builder.add(buildSyncPanel(), cc.xy(2, 9,
0331: CellConstraints.CENTER, CellConstraints.TOP));
0332:
0333: this .setSyncPanelEnabled(false);
0334: this .syncSwitch.setEnabled(false);
0335:
0336: this .setContentPane(builder.getPanel());
0337: this .validate();
0338: }
0339:
0340: /**
0341: * <p>
0342: * Sets the audio player to the given enabled status.
0343: * </p>
0344: *
0345: * @param enabled boolean true if the audio player should be enabled
0346: */
0347: public void setAudioEnabled(boolean enabled) {
0348: MediaClipReplayer replayer = ((MediaClipReplayer) this .trackSynchronizer
0349: .getSyncModel().getController(BlockType.AUDIO));
0350:
0351: if (replayer != null) {
0352: replayer.setAudioEnabled(enabled);
0353: } else {
0354: MediaClipReplayer controller = (MediaClipReplayer) this .cleverPHL
0355: .getSessionList().getActual().getReplay()
0356: .getFirstResponsibleForElement(
0357: new AudioClipRecordable(cleverPHL
0358: .getEnvironment()));
0359:
0360: this .trackSynchronizer.getSyncModel().setController(
0361: controller, BlockType.AUDIO);
0362: }
0363: }
0364:
0365: /**
0366: * <p>
0367: * Sets the video player to the given enabled status.
0368: * </p>
0369: *
0370: * @param enabled boolean true if the video player should be enabled
0371: */
0372: public void setVideoEnabled(boolean enabled) {
0373: MediaClipReplayer replayer = (MediaClipReplayer) this .trackSynchronizer
0374: .getSyncModel().getController(BlockType.VIDEO);
0375:
0376: if (replayer != null) {
0377: replayer.setVideoEnabled(enabled);
0378: } else {
0379: MediaClipReplayer controller = (MediaClipReplayer) this .cleverPHL
0380: .getSessionList().getActual().getReplay()
0381: .getFirstResponsibleForElement(
0382: new VideoClipRecordable(cleverPHL
0383: .getEnvironment()));
0384:
0385: this .trackSynchronizer.getSyncModel().setController(
0386: controller, BlockType.VIDEO);
0387: }
0388: }
0389:
0390: /**
0391: * Stops the audio replay
0392: */
0393: public void stopAudio() {
0394: MediaClipReplayer replayer = (MediaClipReplayer) this .trackSynchronizer
0395: .getSyncModel().getController(BlockType.AUDIO);
0396:
0397: if (replayer != null) {
0398: replayer.stopAudio();
0399: }
0400:
0401: Block currentBlock = this .trackSynchronizer.getSyncModel()
0402: .getCurrentBlock(BlockType.AUDIO);
0403:
0404: if (currentBlock != null) {
0405: this .trackSynchronizer.getSyncModel().clearCurrentBlock(
0406: currentBlock);
0407: }
0408: }
0409:
0410: /**
0411: * Stops the video replay
0412: */
0413: public void stopVideo() {
0414: MediaClipReplayer replayer = (MediaClipReplayer) this .trackSynchronizer
0415: .getSyncModel().getController(BlockType.AUDIO);
0416:
0417: if (replayer != null) {
0418: replayer.stopVideo();
0419: }
0420:
0421: Block currentBlock = this .trackSynchronizer.getSyncModel()
0422: .getCurrentBlock(BlockType.VIDEO);
0423:
0424: if (currentBlock != null) {
0425: this .trackSynchronizer.getSyncModel().clearCurrentBlock(
0426: currentBlock);
0427: }
0428: }
0429:
0430: /**
0431: * Switches the enabling of the sync panel
0432: *
0433: * @param enable boolean
0434: */
0435: private void setSyncPanelEnabled(boolean enable) {
0436: this .maxBuffSpinner.setEnabled(enable);
0437: this .syncRateSpinner.setEnabled(enable);
0438: this .greedySyncButton.setEnabled(enable);
0439: this .relativeSyncButton.setEnabled(enable);
0440: }
0441:
0442: private JComponent buildTrackerPanel() {
0443: FormLayout layout = new FormLayout(
0444: "100px,r:p,3dlu,100px,3dlu,p,3dlu,p", "p");
0445: PanelBuilder builder = new PanelBuilder(layout);
0446:
0447: CellConstraints cc = new CellConstraints();
0448:
0449: builder.add(new JLabel(this .arrowTime), cc.xy(1, 1));
0450: builder.add(new JLabel(getCleverPHL().getLanguage().getString(
0451: "General.Time")
0452: + ":", this .clock, JLabel.LEFT), cc.xy(2, 1));
0453: builder.add(this .timeTextField, cc.xy(4, 1));
0454: builder.add(new JLabel(getCleverPHL().getLanguage().getString(
0455: "General.Abbreviations.Msec")), cc.xy(6, 1));
0456: builder.add(ffwdButton, cc.xy(8, 1));
0457:
0458: return builder.getPanel();
0459: }
0460:
0461: private JComponent buildSyncPanel() {
0462: FormLayout layout = new FormLayout(
0463: "p,3dlu,p,46px,p,3dlu,p,3dlu,p,46px,p,46px,p,3dlu,p",
0464: "p,3dlu,p");
0465: PanelBuilder builder = new PanelBuilder(layout);
0466:
0467: // build the button group for the sync mode
0468: buttonGroup = new ButtonGroup();
0469: buttonGroup.add(greedySyncButton);
0470: buttonGroup.add(relativeSyncButton);
0471:
0472: CellConstraints cc = new CellConstraints();
0473:
0474: builder.add(new JLabel(getCleverPHL().getLanguage().getString(
0475: "CleverPHL.TrackEditorFrame.OnOff")
0476: + ":"), cc.xywh(1, 1, 1, 3, CellConstraints.LEFT,
0477: CellConstraints.CENTER));
0478: builder.add(this .syncSwitch, cc.xywh(3, 1, 1, 3));
0479:
0480: builder.add(new JLabel(this .syncLeft), cc.xywh(4, 1, 1, 3,
0481: CellConstraints.LEFT, CellConstraints.CENTER));
0482:
0483: builder.add(new JLabel(getCleverPHL().getLanguage().getString(
0484: "CleverPHL.TrackEditorFrame.MaximumBuffersize")
0485: + ":"), cc.xy(5, 1));
0486: builder.add(this .maxBuffSpinner, cc.xy(7, 1));
0487: builder.add(new JLabel(getCleverPHL().getLanguage().getString(
0488: "General.Abbreviations.Msec")), cc.xy(9, 1));
0489: builder.add(new JLabel(getCleverPHL().getLanguage().getString(
0490: "CleverPHL.TrackEditorFrame.SynchronizationRate")
0491: + ":"), cc.xy(5, 3));
0492: builder.add(this .syncRateSpinner, cc.xy(7, 3));
0493: builder.add(new JLabel(getCleverPHL().getLanguage().getString(
0494: "General.Abbreviations.Msec")), cc.xy(9, 3));
0495:
0496: builder.add(new JLabel(this .syncRight), cc.xywh(10, 1, 1, 3,
0497: CellConstraints.CENTER, CellConstraints.CENTER));
0498: builder
0499: .addLabel(
0500: " "
0501: + getCleverPHL()
0502: .getLanguage()
0503: .getString(
0504: "CleverPHL.TrackEditorFrame.Calculation")
0505: + " ", cc.xywh(11, 1, 1, 3,
0506: CellConstraints.LEFT,
0507: CellConstraints.CENTER));
0508: builder.add(new JLabel(this .syncLeft), cc.xywh(12, 1, 1, 3,
0509: CellConstraints.LEFT, CellConstraints.CENTER));
0510:
0511: greedySyncButton.setText(getCleverPHL().getLanguage()
0512: .getString("CleverPHL.TrackEditorFrame.Greedy"));
0513: builder.add(this .greedySyncButton, cc.xy(13, 1));
0514: relativeSyncButton.setText(getCleverPHL().getLanguage()
0515: .getString("CleverPHL.TrackEditorFrame.Relative"));
0516: builder.add(this .relativeSyncButton, cc.xy(13, 3));
0517:
0518: JPanel panel = builder.getPanel();
0519:
0520: return panel;
0521: }
0522:
0523: /**
0524: * Sets the slider to the given position in ms
0525: *
0526: * @param ms long value in ms to set the slider to.
0527: */
0528: public void setSliderTime(long ms) {
0529: this .slider.setTimeValue(ms);
0530: }
0531:
0532: /**
0533: * Returns the value the slider is currently set on.
0534: *
0535: * @return the slider value (time in ms)
0536: */
0537: public long getSliderTime() {
0538: return this .slider.getTimeValue();
0539: }
0540:
0541: /**
0542: * Returns the currently selected StructureElement in the {@link jacareto.struct.StructureTree}
0543: * as a {@link Block}
0544: *
0545: * @return {@link Block}
0546: */
0547: public Block getStructureTreeSelection() {
0548: return this .selectionConnector.getSelectedTreeBlock();
0549: }
0550:
0551: /**
0552: * <p>
0553: * Called when the {@link jacareto.replay.Replay} instance replay status has changed.
0554: * </p>
0555: *
0556: * @param event {@link ReplayEvent}
0557: */
0558: public void replayStateChanged(ReplayEvent event) {
0559: // System.out.println("TrackEditorFrame.replayStateChanged(): " + event.getID());
0560: if (event.getID() == ReplayEvent.STARTED) {
0561: ffwdButton.setEnabled(false);
0562: this .trackSynchronizer.start();
0563: } else if (event.getID() == ReplayEvent.STOPPED) {
0564: this .trackSynchronizer.stop();
0565: ffwdButton.setEnabled(true);
0566: } else if (event.getID() == ReplayEvent.PAUSED) {
0567: ffwdButton.setEnabled(true);
0568: } else if (event.getID() == ReplayEvent.CONTINUED) {
0569: ffwdButton.setEnabled(false);
0570: } else if (event.getID() == ReplayEvent.RESTARTED) {
0571: reInit();
0572: setSliderTime(0);
0573: } else if (event.getID() == ReplayEvent.REPLAYED_ELEMENT_CHANGED) {
0574: if (event.getReplayedElement() instanceof MediaClipRecordable) {
0575: MediaClipReplayer replayer = (MediaClipReplayer) this .cleverPHL
0576: .getSessionList().getActual().getReplay()
0577: .getFirstResponsibleForElement(
0578: event.getReplayedElement());
0579:
0580: replayer.addControllerListener(this .trackSynchronizer);
0581:
0582: if (this .trackSynchronizer.getSyncModel()
0583: .getController(BlockType.AUDIO) == null) {
0584: this .trackSynchronizer.getSyncModel()
0585: .setController(replayer, BlockType.AUDIO);
0586: this .trackSynchronizer.setSyncStartTime();
0587: }
0588:
0589: // Only synchronize in REALTIME mode
0590: if (!(this .cleverPHL.getSessionList().getActual()
0591: .getReplay().getMode().getTimeMode() == ReplayMode.REALTIME)) {
0592: this .trackSynchronizer.setSynchronize(false);
0593: }
0594: }
0595:
0596: Block currentBlock = ((DefaultTrackModel) this .trackModel)
0597: .getBlock(event.getReplayedElement());
0598: this .trackSynchronizer.getSyncModel().setCurrentBlock(
0599: currentBlock);
0600: this
0601: .setSliderTime(this .viewPanel
0602: .getTrackModel()
0603: .getStartTime(
0604: DefaultBlockFactory
0605: .getInstance()
0606: .createCustomBlock(
0607: event
0608: .getReplayedElement())));
0609: }
0610: }
0611:
0612: public TrackSynchronizer getTrackSynchronizer() {
0613: return this .trackSynchronizer;
0614: }
0615:
0616: /**
0617: * Replays the given {@link Block}
0618: *
0619: * @param block {@link Block}
0620: */
0621: public void playBlock(Block block) {
0622: Recordable recordable = ((DefaultBlock) block).getRecordable();
0623:
0624: Block currentBlock = this .trackSynchronizer.getSyncModel()
0625: .getCurrentBlock(block.getType());
0626: Recordable currentRecordable = null;
0627:
0628: if (currentBlock != null) {
0629: currentRecordable = ((DefaultBlock) currentBlock)
0630: .getRecordable();
0631: }
0632:
0633: if ((currentRecordable == null)
0634: || (!currentRecordable.equals(recordable))) {
0635: Replayer replayer = this .cleverPHL.getSessionList()
0636: .getActual().getReplay()
0637: .getFirstResponsibleForElement(recordable);
0638:
0639: if (replayer != null) {
0640: this .trackSynchronizer.getSyncModel().setController(
0641: (MediaClipReplayer) replayer, BlockType.AUDIO);
0642: this .trackSynchronizer.getSyncModel().setCurrentBlock(
0643: block);
0644:
0645: try {
0646: replayer.replay(recordable);
0647: } catch (ComponentNotFoundException e) {
0648: e.printStackTrace();
0649: }
0650: }
0651: }
0652: }
0653:
0654: public void trackModelChanged(TrackModelEvent event) {
0655: // System.out.println ("TrackEditorFrame.trackModelChagned() public");
0656: Validate.notNull(this .trackModel);
0657: this .viewPanel.setTrackModel(this .trackModel);
0658: this .trackSynchronizer.setTrackModel(this .trackModel);
0659: slider.reInit(this .trackModel.getEndTime());
0660: }
0661:
0662: private void trackModelChanged() {
0663: // System.out.println ("TrackEditorFrame.trackModelChagned() private");
0664: Validate.notNull(this .trackModel);
0665: this .viewPanel.setTrackModel(this .trackModel);
0666: this .trackSynchronizer.setTrackModel(this .trackModel);
0667: this .trackSynchronizer.initBufferMap();
0668: slider.reInit(this .trackModel.getEndTime());
0669: }
0670:
0671: public void blockTimeChanged(TrackModelEvent event) {
0672: Validate.notNull(event);
0673: trackModelChanged();
0674: }
0675:
0676: public void blockAlignmentChanged(TrackModelEvent event) {
0677: Validate.notNull(event);
0678: trackModelChanged();
0679: }
0680:
0681: public void blockRemoved(TrackModelEvent event) {
0682: Validate.notNull(event);
0683: trackModelChanged();
0684: }
0685:
0686: public void blockAdded(TrackModelEvent event) {
0687: Validate.notNull(event);
0688: trackModelChanged();
0689: }
0690:
0691: /**
0692: * <p>
0693: * Sets the sync mode in the track synchronizer} to {@link SyncMode#GREEDY} or {@link
0694: * SyncMode#RELATIVE}.
0695: * </p>
0696: *
0697: * <p>
0698: * Called by RadioButtons in the button group
0699: * </p>
0700: *
0701: * @param event DOCUMENT ME!
0702: */
0703: public void actionPerformed(ActionEvent event) {
0704: this .trackSynchronizer.getSyncMode().setBufferMode(
0705: Integer.parseInt(event.getActionCommand()));
0706: }
0707:
0708: /**
0709: * <p>
0710: * Sets the SyncRate and the maximum buffer size to the values of the spinners (sync rate
0711: * spinner and max buff spinner)
0712: * </p>
0713: *
0714: * @param event {@link ChangeEvent}
0715: */
0716: public void stateChanged(ChangeEvent event) {
0717: if (event.getSource() == this .syncRateSpinner) {
0718: this .trackSynchronizer.getSyncMode().setMaxBuffer(
0719: ((Integer) this .maxBuffSpinner.getModel()
0720: .getValue()).intValue());
0721: } else if (event.getSource() == this .maxBuffSpinner) {
0722: this .trackSynchronizer.getSyncMode().setMaxBuffer(
0723: ((Integer) this .maxBuffSpinner.getModel()
0724: .getValue()).intValue());
0725: }
0726: }
0727:
0728: /**
0729: * <p>
0730: * Switches the synchronization on and off.
0731: * </p>
0732: *
0733: * <p>
0734: * Called by synch switch
0735: * </p>
0736: *
0737: * @param event DOCUMENT ME!
0738: */
0739: public void itemStateChanged(ItemEvent event) {
0740: this .setSyncPanelEnabled(this .syncSwitch.isSelected());
0741: this .trackSynchronizer.setSynchronize(this .syncSwitch
0742: .isSelected());
0743: }
0744:
0745: /**
0746: * <p>
0747: * Called when the SessionList has changed.
0748: * </p>
0749: *
0750: * @param event {@link SessionListEvent}
0751: */
0752: public void sessionListChanged(SessionListEvent event) {
0753: // System.out.println("TrackEditorFrame.SessionListChanged(): " + event.getID());
0754: if (event.getID() == SessionListEvent.NEW_ACTUAL_SESSION) {
0755: this .cleverPHL.getSessionList().getActual().getReplay()
0756: .addReplayListener(this );
0757: this .cleverPHL.getSessionList().getActual()
0758: .addSessionListener(this );
0759: reInit();
0760: } else if (event.getID() == SessionListEvent.SESSION_ADDED) {
0761: ;
0762: } else if (event.getID() == SessionListEvent.SESSION_REMOVED) {
0763: this .syncSwitch.setEnabled(false);
0764: }
0765: }
0766:
0767: /**
0768: * <p>
0769: * Called when the state of the session has changed.
0770: * </p>
0771: *
0772: * @param event {@link SessionEvent}
0773: */
0774: public void sessionStateChanged(SessionEvent event) {
0775: // System.out.println("TrackEditorFrame.sessionStateChanged(): " + event.getID());
0776: reInit();
0777: }
0778:
0779: /**
0780: * <p>
0781: * Loads the icons shown on the SyncPanel.
0782: * </p>
0783: */
0784: private void loadIcons() {
0785: String dir = this .cleverPHL.getEnvironment().getFiles().getDir(
0786: "SYNC_DIR").toString();
0787: this .arrowTime = ResourceLoader.getImageIcon(dir + "/"
0788: + "arrow_time.png");
0789: this .clock = ResourceLoader.getImageIcon(dir + "/"
0790: + "clock.png");
0791: this .syncLeft = ResourceLoader.getImageIcon(dir + "/"
0792: + "sync_left.png");
0793: this .syncRight = ResourceLoader.getImageIcon(dir + "/"
0794: + "sync_right.png");
0795: }
0796:
0797: public void close() {
0798: ((JCheckBoxMenuItem) cleverPHL.getMainFrame()
0799: .getCleverPHLMenuBar().getMenuItem(
0800: "CleverPHL.Menu.ViewTrackEditor"))
0801: .setSelected(false);
0802: this .setVisible(false);
0803: }
0804:
0805: /**
0806: * <p>
0807: * Class to provide a {@link JSlider} which maps the time values (long) to the range of a
0808: * {@link JSlider} (int)
0809: * </p>
0810: *
0811: * @author Oliver Specht
0812: * @version $revision$
0813: */
0814: private class Slider extends JSlider implements
0815: PropertyChangeListener {
0816: //~ Instance fields ------------------------------------------------------------------------
0817:
0818: private Hashtable labelTable = new Hashtable();
0819: private final int NUMBER_TICKS = 10;
0820: private final int MAXIMUM_RANGE = Integer.MAX_VALUE;
0821:
0822: //private final int MAXIMUM_RANGE = Integer.MAX_VALUE;
0823: private double ratio;
0824: private long maxTimeValue = 0;
0825: private long actualTimeValue;
0826: private int actualSliderValue;
0827:
0828: //~ Constructors ---------------------------------------------------------------------------
0829:
0830: /**
0831: * Creates a new Slider.
0832: *
0833: * @param maxTimeValue long the maximum slider value in ms
0834: */
0835: public Slider(long maxTimeValue) {
0836: super (0, Integer.MAX_VALUE);
0837: this .setFocusable(false);
0838: this .setPaintTicks(true);
0839: this .setPaintLabels(true);
0840: this .setBorder(new EmptyBorder(0, 0, 0, 0));
0841: this .setSnapToTicks(false);
0842: this .labelTable.put(new Integer(0), new JLabel("00:00"));
0843:
0844: if (maxTimeValue >= 0) {
0845: this .maxTimeValue = maxTimeValue;
0846: }
0847: }
0848:
0849: //~ Methods --------------------------------------------------------------------------------
0850:
0851: /**
0852: * Reinitializes the slider with the given maximum value.
0853: *
0854: * @param maxTimeValue the maximum value for the slider in ms
0855: */
0856: public void reInit(long maxTimeValue) {
0857: this .maxTimeValue = maxTimeValue;
0858:
0859: int step = MAXIMUM_RANGE / NUMBER_TICKS;
0860: long value = maxTimeValue / NUMBER_TICKS;
0861:
0862: for (int i = 1; i <= NUMBER_TICKS; i++) {
0863: this .labelTable.put(new Integer(i * step), new JLabel(
0864: millisecondsToString(i * value)));
0865: }
0866:
0867: this .setLabelTable(this .labelTable);
0868:
0869: // Should not happen
0870: if (actualTimeValue > maxTimeValue) {
0871: setTimeValue(0);
0872: }
0873: }
0874:
0875: /**
0876: * <p>
0877: * Converts time in milliseconds to a {@link String} in the format HH:mm:ss.SSS.
0878: * </p>
0879: *
0880: * @param time the time in milliseconds.
0881: *
0882: * @return {@link String} representing the time in the format HH:mm:ss.SSS.
0883: */
0884: private String millisecondsToString(long time) {
0885: // int milliseconds = (int) (time % 1000);
0886: int seconds = (int) ((time / 1000) % 60);
0887:
0888: int minutes = (int) ((time / 60000) % 60);
0889:
0890: // int hours = (int) ((time / 3600000) % 24);
0891: // String millisecondsStr = ((milliseconds < 10) ? "00" : ((milliseconds < 100) ? "0" : "")) +
0892: // milliseconds;
0893: String secondsStr = ((seconds < 10) ? "0" : "") + seconds;
0894:
0895: String minutesStr = ((minutes < 10) ? "0" : "") + minutes;
0896:
0897: // String hoursStr = ((hours < 10) ? "0" : "") + hours;
0898: // return new String(hoursStr+":"+minutesStr+":"+secondsStr+"."+millisecondsStr);
0899: return new String(minutesStr + ":" + secondsStr);
0900: }
0901:
0902: /**
0903: * Called when a new value is entered in the {@link JFormattedTextField} of the slider.
0904: *
0905: * @param event {@link PropertyChangeEvent}
0906: */
0907: public void propertyChange(PropertyChangeEvent event) {
0908: if ("value".equals(event.getPropertyName())) {
0909: Number value = (Number) event.getNewValue();
0910:
0911: if (value != null) {
0912: removeChangeListener(timeTextField);
0913: this .setTimeValue(value.longValue());
0914: addChangeListener(timeTextField);
0915: }
0916: }
0917: }
0918:
0919: /**
0920: * <p>
0921: * Calling the {@link JSlider#setValue(int)} method of JSlider with correct values.
0922: * </p>
0923: *
0924: * <p>
0925: * Overwritten to get correct mapping of times.
0926: * </p>
0927: *
0928: * @param value long the value to be set
0929: */
0930: public void setTimeValue(long value) {
0931: Validate.isTrue(value >= -1, "Given Value is < -1: "
0932: + value);
0933:
0934: if (this .maxTimeValue > 0) {
0935: if (value > this .maxTimeValue) {
0936: value = this .maxTimeValue;
0937: }
0938:
0939: ratio = MAXIMUM_RANGE / ((double) this .maxTimeValue);
0940: } else {
0941: ratio = 0;
0942: }
0943:
0944: int newValue = (int) (ratio * value);
0945:
0946: super .setValue(newValue);
0947: actualTimeValue = value;
0948: actualSliderValue = getValue();
0949: }
0950:
0951: public long getTimeValue() {
0952: return actualTimeValue;
0953: }
0954:
0955: public void setValue(int value) {
0956: ratio = ((double) this .maxTimeValue) / MAXIMUM_RANGE;
0957:
0958: long newTimeValue = (long) (value * ratio);
0959:
0960: if (value > actualSliderValue) {
0961: setTimeValue(newTimeValue);
0962: } else {
0963: setTimeValue(actualTimeValue);
0964: }
0965: }
0966:
0967: /**
0968: * Called when the slider has been moved
0969: */
0970:
0971: /*public void stateChanged (ChangeEvent event) {
0972: if (! getValueIsAdjusting ()) {
0973: removeChangeListener (this);
0974: ratio = ((double) this.maxTimeValue) / MAXIMUM_RANGE;
0975: setTimeValue ((long) (getValue () * ratio));
0976: System.out.println ("actualValue: " + actualSliderValue);
0977: System.out.println ("getValue: " + getValue());
0978: addChangeListener (this);
0979: }
0980: }*/
0981: /*public void mousePressed (MouseEvent event) {}
0982: public void mouseReleased (MouseEvent event) {
0983: ((Component) event.getSource()).dispatchEvent (new MouseEvent ((Component) event.getSource(), MouseEvent.MOUSE_PRESSED, event.getWhen(), event.getModifiers(), event.getX(), event.getY(), 1, event.isPopupTrigger()));
0984: }
0985: public void mouseExited (MouseEvent event) {}
0986: public void mouseEntered (MouseEvent event) {}
0987: public void mouseClicked (MouseEvent event) {}*/
0988: /*public void mouseMoved (MouseEvent event) {}
0989: public void mouseDragged (MouseEvent event) {
0990: setTimeValue (actualTimeValue);
0991: } */
0992: }
0993:
0994: /**
0995: * {@link JFormattedTextField} which shows the currently selected slider value.
0996: *
0997: * @author Oliver Specht
0998: * @version $revision$
0999: */
1000: private class SliderValueTextField extends JFormattedTextField
1001: implements ChangeListener {
1002: //~ Instance fields ------------------------------------------------------------------------
1003:
1004: /** Default Integer formatter */
1005:
1006: // NumberFormatter formatter = new NumberFormatter(NumberFormat.getIntegerInstance ());
1007: DecimalFormat decimalFormat = new DecimalFormat(
1008: "###,###,### ms");
1009: NumberFormatter formatter = new NumberFormatter(decimalFormat);
1010:
1011: //~ Constructors ---------------------------------------------------------------------------
1012:
1013: /**
1014: * Creates a new SliderValueTextField
1015: */
1016: public SliderValueTextField() {
1017: super ();
1018: this .setFormatter(formatter);
1019: this .setColumns(8);
1020:
1021: // react on ENTER key
1022: this .getInputMap().put(
1023: KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
1024: "check");
1025: this .getActionMap().put("check", new TextFieldAction(this ));
1026: }
1027:
1028: //~ Methods --------------------------------------------------------------------------------
1029:
1030: /**
1031: * Called when the slider has been moved
1032: *
1033: * @param event {@link ChangeEvent}
1034: */
1035: public void stateChanged(ChangeEvent event) {
1036: if (event.getSource() instanceof Slider) {
1037: if (!slider.getValueIsAdjusting()) {
1038: long value = slider.getTimeValue();
1039: this .removePropertyChangeListener(slider);
1040:
1041: //this.setValue (new Long(value));
1042: this .setText(String.valueOf(value));
1043: this .addPropertyChangeListener(slider);
1044: }
1045:
1046: /*Slider slider = (Slider) event.getSource ();
1047: long value = slider.getTimeValue ();
1048: this.removePropertyChangeListener (slider);
1049: //this.setValue (new Long(value));
1050: this.setText (String.valueOf (value));
1051: this.addPropertyChangeListener (slider); */
1052:
1053: // check if slider has been moved by user
1054: /*if (! slider.getValueIsAdjusting ()) {
1055: this.removePropertyChangeListener (slider);
1056: this.setValue (new Long(value));
1057: this.addPropertyChangeListener (slider);
1058: } else {
1059: System.out.println ("Slider Change");
1060: this.setText (String.valueOf (value));
1061: } */
1062: }
1063: }
1064:
1065: //~ Inner Classes --------------------------------------------------------------------------
1066:
1067: /**
1068: * Action to be performed when user has entered text and pressed ENTER. Is only used
1069: * especially for {@link JFormattedTextField} of the slider}
1070: *
1071: * @author Oliver Specht
1072: * @version $revision$
1073: */
1074: private class TextFieldAction extends AbstractAction {
1075: //~ Instance fields --------------------------------------------------------------------
1076:
1077: SliderValueTextField textField;
1078:
1079: //~ Constructors -----------------------------------------------------------------------
1080:
1081: /**
1082: * Creates a new TextFieldAction which sets the slider to the correct value if number
1083: * is entered.
1084: *
1085: * @param textField SliderValueTextField this action is performed on
1086: */
1087: public TextFieldAction(SliderValueTextField textField) {
1088: this .textField = textField;
1089: }
1090:
1091: //~ Methods ----------------------------------------------------------------------------
1092:
1093: /**
1094: * Performed when the user has entered a text and pressed ENTER.
1095: *
1096: * @param event {@link ActionEvent}
1097: */
1098: public void actionPerformed(ActionEvent event) {
1099: if (!textField.isEditValid()) { //The text is invalid.
1100: textField.selectAll();
1101: } else {
1102: try { //The text is valid,
1103: textField.commitEdit(); //so use it.
1104: } catch (java.text.ParseException exception) {
1105: exception.printStackTrace();
1106: }
1107: }
1108: }
1109: }
1110: }
1111:
1112: private class WindowListenerAdapter extends WindowAdapter {
1113: //~ Instance fields ------------------------------------------------------------------------
1114:
1115: TrackEditorFrame frame;
1116:
1117: //~ Constructors ---------------------------------------------------------------------------
1118:
1119: public WindowListenerAdapter(TrackEditorFrame frame) {
1120: Validate.notNull(frame);
1121:
1122: this .frame = frame;
1123: }
1124:
1125: //~ Methods --------------------------------------------------------------------------------
1126:
1127: public void windowClosing(WindowEvent event) {
1128: super.windowClosing(event);
1129: frame.close();
1130: }
1131: }
1132: }
|