Java tutorial
/** * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.datavyu.views; import java.awt.Color; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; import java.util.SimpleTimeZone; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JPanel; import javax.swing.SwingConstants; import javax.swing.WindowConstants; import javax.swing.filechooser.FileFilter; import net.miginfocom.swing.MigLayout; import org.apache.commons.lang.NotImplementedException; import org.jdesktop.application.Action; import org.jdesktop.application.Application; import org.jdesktop.application.ResourceMap; import org.datavyu.Datavyu; import org.datavyu.Datavyu.Platform; import org.datavyu.controllers.CreateNewCellC; import org.datavyu.controllers.SetNewCellStopTimeC; import org.datavyu.controllers.SetSelectedCellStartTimeC; import org.datavyu.controllers.SetSelectedCellStopTimeC; import org.datavyu.controllers.component.MixerController; import org.datavyu.controllers.id.IDController; import org.datavyu.event.component.CarriageEvent; import org.datavyu.event.component.TimescaleEvent; import org.datavyu.event.component.TracksControllerEvent; import org.datavyu.event.component.TracksControllerListener; import org.datavyu.models.PlaybackModel; import org.datavyu.models.component.MixerConstants; import org.datavyu.models.component.RegionState; import org.datavyu.models.component.TimescaleConstants; import org.datavyu.models.component.ViewportStateImpl; import org.datavyu.models.component.ViewportState; import org.datavyu.models.id.Identifier; import org.datavyu.plugins.PluginManager; import org.datavyu.util.ClockTimer; import org.datavyu.util.ClockTimer.ClockListener; import org.datavyu.util.FloatUtils; import org.datavyu.views.component.TrackPainter; import com.usermetrix.jclient.Logger; import com.usermetrix.jclient.UserMetrix; import java.awt.event.WindowListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.ActionMap; import javax.swing.JDialog; import javax.swing.JOptionPane; import org.datavyu.plugins.DataViewer; import org.datavyu.plugins.Plugin; import org.datavyu.views.DataController; /** * Quicktime video controller. */ public final class DataControllerV extends DatavyuDialog implements ClockListener, TracksControllerListener, DataController, PropertyChangeListener { private static final double LOW_RATE = 5D; /** One second in milliseconds. */ private static final long ONE_SECOND = 1000L; /** Rate of playback for rewinding. */ private static final float REWIND_RATE = -32F; /** Rate of normal playback. */ private static final float PLAY_RATE = 1F; /** Rate of playback for fast forwarding. */ private static final float FFORWARD_RATE = 32F; /** Sequence of allowable shuttle rates. */ private static final float[] SHUTTLE_RATES; /** * The threshold to use while synchronising viewers (augmented by rate). */ private static final long SYNC_THRESH = 200; /** * How often to synchronise the viewers with the master clock. */ private static final long SYNC_PULSE = 500; // Initialize SHUTTLE_RATES static { SHUTTLE_RATES = new float[] { -32F, -16F, -8F, -4F, -2F, -1F, -1 / 2F, -1 / 4F, -1 / 8F, -1 / 16F, -1 / 32F, 0F, 1 / 32F, 1 / 16F, 1 / 8F, 1 / 4F, 1 / 2F, 1F, 2F, 4F, 8F, 16F, 32F }; } /** The jump multiplier for shift-jogging. */ private static final int SHIFTJOG = 5; /** The jump multiplier for ctrl-shift-jogging. */ private static final int CTRLSHIFTJOG = 10; /** Format for representing time. */ private static final DateFormat CLOCK_FORMAT; private static final DateFormat CLOCK_FORMAT_HTML; // initialize standard date format for clock display. static { CLOCK_FORMAT = new SimpleDateFormat("HH:mm:ss:SSS"); CLOCK_FORMAT.setTimeZone(new SimpleTimeZone(0, "NO_ZONE")); Color hoursColor = TimescaleConstants.HOURS_COLOR; Color minutesColor = TimescaleConstants.MINUTES_COLOR; Color secondsColor = TimescaleConstants.SECONDS_COLOR; Color millisecondsColor = TimescaleConstants.MILLISECONDS_COLOR; CLOCK_FORMAT_HTML = new SimpleDateFormat("'<html>" + "<font color=\"" + toRGBString(hoursColor) + "\">'HH'</font>':" + "'<font color=\"" + toRGBString(minutesColor) + "\">'mm'</font>':" + "'<font color=\"" + toRGBString(secondsColor) + "\">'ss'</font>':" + "'<font color=\"" + toRGBString(millisecondsColor) + "\">'SSS'</font>" + "</html>'"); CLOCK_FORMAT_HTML.setTimeZone(new SimpleTimeZone(0, "NO_ZONE")); } // ------------------------------------------------------------------------- // [static] // /** The logger for this class. */ private static Logger LOGGER = UserMetrix.getLogger(DataControllerV.class); /** Determines whether or not Shift is being held. */ private boolean shiftMask = false; /** Determines whether or not Control is being held. */ private boolean ctrlMask = false; // ------------------------------------------------------------------------- // // /** The list of viewers associated with this controller. */ private Set<DataViewer> viewers; /** Clock timer. */ private ClockTimer clock = new ClockTimer(); /** Is the tracks panel currently shown? */ private boolean tracksPanelEnabled = true; /** The controller for manipulating tracks. */ private MixerController mixerController; /** */ private javax.swing.JButton createNewCell; /** */ private javax.swing.JButton createNewCellSettingOffset; /** */ private javax.swing.JButton findButton; /** */ private javax.swing.JTextField findOffsetField; /** */ private javax.swing.JTextField findTextField; /** */ private javax.swing.JButton forwardButton; /** */ private javax.swing.JButton goBackButton; /** */ private javax.swing.JTextField goBackTextField; /** */ private javax.swing.JPanel gridButtonPanel; /** */ private javax.swing.JLabel jLabel1; /** */ private javax.swing.JLabel jLabel2; /** */ private javax.swing.JButton jogBackButton; /** */ private javax.swing.JButton jogForwardButton; /** */ private javax.swing.JLabel lblSpeed; /** */ private javax.swing.JButton addDataButton; /** */ private javax.swing.JButton pauseButton; /** */ private javax.swing.JButton playButton; /** */ private javax.swing.JButton rewindButton; /** */ private javax.swing.JButton osxsetCellOffsetButton; /** */ private javax.swing.JButton setCellOffsetButton; /** */ private javax.swing.JButton setCellOnsetButton; /** */ private javax.swing.JButton pointCellButton; /** */ private javax.swing.JButton showTracksButton; /** */ private javax.swing.JButton shuttleBackButton; /** */ private javax.swing.JButton shuttleForwardButton; /** */ private javax.swing.JButton stopButton; /** */ private javax.swing.JLabel timestampLabel; /** */ private javax.swing.JPanel tracksPanel; /** Model containing playback information. */ private PlaybackModel playbackModel; // ------------------------------------------------------------------------- // [initialization] // /** * Constructor. Creates a new DataControllerV. * * @param parent * The parent of this form. * @param modal * Should the dialog be modal or not? */ public DataControllerV(final java.awt.Frame parent, final boolean modal) { super(parent, modal); clock.registerListener(this); setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); if (Datavyu.getPlatform() == Platform.MAC) { initComponentsMac(); } else { initComponents(); } setResizable(false); setName(this.getClass().getSimpleName()); viewers = new LinkedHashSet<DataViewer>(); playbackModel = new PlaybackModel(); playbackModel.setPauseRate(0); playbackModel.setLastSync(0); playbackModel.setMaxDuration(ViewportStateImpl.MINIMUM_MAX_END); final int defaultEndTime = (int) MixerConstants.DEFAULT_DURATION; playbackModel.setWindowPlayStart(0); playbackModel.setWindowPlayEnd(defaultEndTime); mixerController = new MixerController(); tracksPanel.add(mixerController.getTracksPanel(), "growx"); mixerController.addTracksControllerListener(this); mixerController.getMixerModel().getViewportModel().addPropertyChangeListener(this); mixerController.getMixerModel().getRegionModel().addPropertyChangeListener(this); mixerController.getMixerModel().getNeedleModel().addPropertyChangeListener(this); tracksPanelEnabled = true; showTracksPanel(tracksPanelEnabled); updateCurrentTimeLabel(); } public static String formatTime(final long time) { return CLOCK_FORMAT.format(new Date(time)); } private static String toRGBString(final Color color) { return String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue()); } /** * Handles opening a data source. * * @param jd * The file chooser used to open the data source. */ private void openVideo(final PluginChooser chooser) { Plugin plugin = chooser.getSelectedPlugin(); File f = chooser.getSelectedFile(); if (plugin != null) { try { DataViewer dataViewer = plugin.getNewDataViewer(Datavyu.getApplication().getMainFrame(), false); dataViewer.setIdentifier(IDController.generateIdentifier()); dataViewer.setDataFeed(f); dataViewer.seekTo(clock.getTime()); dataViewer.setDatastore(Datavyu.getProjectController().getDB()); addDataViewer(plugin.getTypeIcon(), dataViewer, f, dataViewer.getTrackPainter()); mixerController.bindTrackActions(dataViewer.getIdentifier(), dataViewer.getCustomActions()); dataViewer.addViewerStateListener(mixerController.getTracksEditorController() .getViewerStateListener(dataViewer.getIdentifier())); } catch (Throwable t) { LOGGER.error(t); JOptionPane.showMessageDialog(null, "Could not open data source: " + t.getMessage()); t.printStackTrace(); } } } /** * Tells the Data Controller if shift is being held or not. * * @param shift * True for shift held; false otherwise. */ public void setShiftMask(final boolean shift) { shiftMask = shift; } /** * Tells the Data Controller if ctrl is being held or not. * * @param ctrl * True for ctrl held; false otherwise. */ public void setCtrlMask(final boolean ctrl) { ctrlMask = ctrl; } // ------------------------------------------------------------------------- // [interface] org.datavyu.util.ClockTimer.Listener // /** * @param time * Current clock time in milliseconds. */ public void clockStart(final long time) { resetSync(); long playTime = time; final long windowPlayStart = playbackModel.getWindowPlayStart(); if (playTime < windowPlayStart) { playTime = windowPlayStart; clockStep(playTime); } float currentRate = clock.getRate(); clock.stop(); setCurrentTime(playTime); clock.setTime(playTime); clock.setRate(currentRate); clock.start(); } /** * Reset the sync. */ private void resetSync() { playbackModel.setLastSync(0); } /** * @param time * Current clock time in milliseconds. */ public void clockTick(final long time) { try { setCurrentTime(time); // We are playing back at a rate which is too fast and probably // won't allow us to stream all the information at the file. We fake // playback by doing a bunch of seekTo's. if (playbackModel.isFakePlayback()) { for (DataViewer v : viewers) { if ((time > v.getOffset()) && isWithinPlayRange(time, v)) { v.seekTo(time - v.getOffset()); } } // DataViewer is responsible for playing video. } else { // Synchronise viewers only if we have exceeded our pulse time. if ((time - playbackModel.getLastSync()) > (SYNC_PULSE * clock.getRate())) { long thresh = (long) (SYNC_THRESH * Math.abs(clock.getRate())); playbackModel.setLastSync(time); for (DataViewer v : viewers) { /* * Use offsets to determine if the video file should * start playing. */ if (!v.isPlaying() && isWithinPlayRange(time, v)) { v.seekTo(time - v.getOffset()); v.play(); } // BugzID:1797 - Viewers who are "playing" outside their // timeframe should be asked to stop. if (v.isPlaying() && !isWithinPlayRange(time, v)) { v.stop(); } // For plugins with low data rate, use frame rate // to determine threshold. if ((0 < v.getFrameRate()) && (v.getFrameRate() <= LOW_RATE)) { thresh = (long) (ONE_SECOND / v.getFrameRate() / clock.getRate()); } /* * Only synchronise the data viewers if we have a * noticable drift. */ if (v.isPlaying() && (Math.abs(v.getCurrentTime() - (time - v.getOffset())) > thresh)) { v.seekTo(time - v.getOffset()); } } } } // BugzID:466 - Prevent rewind wrapping the clock past the start // point of the view window. final long windowPlayStart = playbackModel.getWindowPlayStart(); if (time < windowPlayStart) { setCurrentTime(windowPlayStart); clock.stop(); clock.setTime(windowPlayStart); clockStop(windowPlayStart); } // BugzID:756 - don't play video once past the max duration. final long windowPlayEnd = playbackModel.getWindowPlayEnd(); if ((time >= windowPlayEnd) && (clock.getRate() >= 0)) { setCurrentTime(windowPlayEnd); clock.stop(); clock.setTime(windowPlayEnd); clockStop(windowPlayEnd); return; } } catch (Exception e) { LOGGER.error("Unable to Sync viewers", e); } } /** * Determines whether a DataViewer has data for the desired time. * * @param time * The time we wish to play at or seek to. * @param view * The DataViewer to check. * @return True if data exists at this time, and false otherwise. */ private boolean isWithinPlayRange(final long time, final DataViewer view) { return (time >= view.getOffset()) && (time < (view.getOffset() + view.getDuration())); } /** * @param time * Current clock time in milliseconds. */ public void clockStop(final long time) { clock.stop(); resetSync(); setCurrentTime(time); for (DataViewer viewer : viewers) { viewer.stop(); if (isWithinPlayRange(time, viewer)) { viewer.seekTo(time - viewer.getOffset()); } } } /** * @param rate * Current (updated) clock rate. */ public void clockRate(final float rate) { resetSync(); lblSpeed.setText(FloatUtils.doubleToFractionStr(new Double(rate))); long time = getCurrentTime(); // If rate is faster than two times - we need to fake playback to give // the illusion of 'smooth'. We do this by stopping the dataviewer and // doing many seekTo's to grab individual frames. if (Math.abs(rate) > 2.0 || rate < 0) { playbackModel.setFakePlayback(true); for (DataViewer viewer : viewers) { viewer.stop(); if (isWithinPlayRange(time, viewer)) { viewer.setPlaybackSpeed(rate); } } // Rate is less than two times - use the data viewer internal code // to draw every frame. } else { playbackModel.setFakePlayback(false); for (DataViewer viewer : viewers) { viewer.setPlaybackSpeed(rate); if (!clock.isStopped()) { viewer.play(); } } } } /** * @param time * Current clock time in milliseconds. */ public void clockStep(final long time) { resetSync(); setCurrentTime(time); for (DataViewer viewer : viewers) { if (isWithinPlayRange(time, viewer)) { viewer.seekTo(time - viewer.getOffset()); } } } /** * @return the mixer controller. */ public MixerController getMixerController() { return mixerController; } /** * Set time location for data streams. * * @param milliseconds * The millisecond time. */ public void setCurrentTime(final long milliseconds) { resetSync(); updateCurrentTimeLabel(); mixerController.getMixerModel().getNeedleModel().setCurrentTime(milliseconds); } private void updateCurrentTimeLabel() { timestampLabel.setText(tracksPanelEnabled ? CLOCK_FORMAT_HTML.format(getCurrentTime()) : CLOCK_FORMAT_HTML.format(getCurrentTime())); } /** * Get the current master clock time for the controller. * * @return Time in milliseconds. */ public long getCurrentTime() { return clock.getTime(); } /** Recalculates the maximum viewer duration. */ public void updateMaxViewerDuration() { long maxDuration = ViewportStateImpl.MINIMUM_MAX_END; Iterator<DataViewer> it = viewers.iterator(); while (it.hasNext()) { DataViewer dv = it.next(); if ((dv.getDuration() + dv.getOffset()) > maxDuration) { maxDuration = dv.getDuration() + dv.getOffset(); } } mixerController.getMixerModel().getViewportModel().setViewportMaxEnd(maxDuration, true); if (viewers.isEmpty()) { mixerController.getNeedleController().resetNeedlePosition(); mixerController.getMixerModel().getRegionModel().resetPlaybackRegion(); } } /** * Remove the specified viewer from the controller. * * @param viewer * The viewer to shutdown. * @return True if the controller contained this viewer. */ public boolean shutdown(final DataViewer viewer) { // Was the viewer removed. boolean removed = viewers.remove(viewer); if (removed) { viewer.clearDataFeed(); // BugzID:2000 viewer.removeViewerStateListener( mixerController.getTracksEditorController().getViewerStateListener(viewer.getIdentifier())); // Recalculate the maximum playback duration. updateMaxViewerDuration(); // Remove the data viewer from the tracks panel. mixerController.deregisterTrack(viewer.getIdentifier()); // Data viewer removed, mark project as changed. Datavyu.getProjectController().projectChanged(); } return removed; } /** * Remove the specified viewer from the controller. * * @param id * The identifier of the viewer to shutdown. */ public void shutdown(final Identifier id) { DataViewer viewer = null; for (DataViewer v : viewers) { if (v.getIdentifier().equals(id)) { viewer = v; break; } } if ((viewer == null) || !shouldRemove()) { return; } viewers.remove(viewer); viewer.stop(); viewer.clearDataFeed(); JDialog viewDialog = viewer.getParentJDialog(); if (viewDialog != null) { viewDialog.dispose(); } // BugzID:2000 viewer.removeViewerStateListener( mixerController.getTracksEditorController().getViewerStateListener(viewer.getIdentifier())); // Recalculate the maximum playback duration. updateMaxViewerDuration(); // Remove the data viewer from the tracks panel. mixerController.deregisterTrack(viewer.getIdentifier()); // Data viewer removed, mark project as changed. Datavyu.getProjectController().projectChanged(); } /** * Binds a window event listener to a data viewer. * * @param id The identifier of the viewer to bind to. */ public void bindWindowListenerToDataViewer(final Identifier id, final WindowListener wl) { DataViewer viewer = null; for (DataViewer v : viewers) { if (v.getIdentifier().equals(id)) { viewer = v; break; } } if (viewer != null) { viewer.getParentJDialog().addWindowListener(wl); } } /** * Binds a window event listener to a data viewer. * * @param id The identifier of the viewer to bind to. */ public void setDataViewerVisibility(final Identifier id, final boolean visible) { DataViewer viewer = null; for (DataViewer v : viewers) { if (v.getIdentifier().equals(id)) { viewer = v; break; } } if (viewer != null) { viewer.setDataViewerVisible(visible); } } /** * Presents a confirmation dialog when removing a plugin from the project. * @return True if the plugin should be removed, false otherwise. */ private boolean shouldRemove() { ResourceMap rMap = Application.getInstance(Datavyu.class).getContext().getResourceMap(Datavyu.class); String cancel = "Cancel"; String ok = "OK"; String[] options = new String[2]; if (Datavyu.getPlatform() == Platform.MAC) { options[0] = cancel; options[1] = ok; } else { options[0] = ok; options[1] = cancel; } int selection = JOptionPane.showOptionDialog(this, rMap.getString("ClosePluginDialog.message"), rMap.getString("ClosePluginDialog.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, cancel); // Button behaviour is platform dependent. return (Datavyu.getPlatform() == Platform.MAC) ? (selection == 1) : (selection == 0); } /** * Helper method for Building a button for the data controller - sets the * icon, selected icon, action map and name. * * @param rMap The resource map that holds the icons for this button. * @param aMap The action map holding the action that this button invokes. * @param name The prefix to use when looking for actions and buttons. * @param modifier The modifier (if any) to apply to the prefix. Maybe null. * * @return A configured button. */ private JButton buildButton(final ResourceMap rMap, final ActionMap aMap, final String name, final String modifier) { JButton result = new JButton(); result.setAction(aMap.get(name + "Action")); if (modifier == null) { result.setIcon(rMap.getIcon(name + "Button.icon")); result.setPressedIcon(rMap.getIcon(name + "SelectedButton.icon")); } else { result.setIcon(rMap.getIcon(modifier + name + "Button.icon")); result.setPressedIcon(rMap.getIcon(modifier + name + "SelectedButton.icon")); } result.setFocusPainted(false); result.setName(name + "Button"); return result; } /** * Initialize the view for Macs. */ private void initComponentsMac() { gridButtonPanel = new javax.swing.JPanel(); goBackTextField = new javax.swing.JTextField(); findTextField = new javax.swing.JTextField(); addDataButton = new javax.swing.JButton(); timestampLabel = new javax.swing.JLabel(); lblSpeed = new javax.swing.JLabel(); jLabel1 = new javax.swing.JLabel(); jLabel2 = new javax.swing.JLabel(); findOffsetField = new javax.swing.JTextField(); showTracksButton = new javax.swing.JButton(); tracksPanel = new javax.swing.JPanel(new MigLayout("fill")); final int fontSize = 11; org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application .getInstance(org.datavyu.Datavyu.class).getContext().getResourceMap(DataControllerV.class); setTitle(resourceMap.getString("title")); addWindowListener(new java.awt.event.WindowAdapter() { @Override public void windowClosing(final java.awt.event.WindowEvent evt) { formWindowClosing(evt); } }); gridButtonPanel.setBackground(Color.WHITE); gridButtonPanel.setLayout(new MigLayout("wrap 5, ins 15 2 15 2")); // Add data button addDataButton.setText(resourceMap.getString("addDataButton.text")); addDataButton.setFont(new Font("Tahoma", Font.PLAIN, fontSize)); addDataButton.setFocusPainted(false); addDataButton.setName("addDataButton"); addDataButton.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { openVideoButtonActionPerformed(evt); } }); gridButtonPanel.add(addDataButton, "span 2, w 90!, h 25!"); // Timestamp panel JPanel timestampPanel = new JPanel(new MigLayout("", "push[][][]0![]push")); timestampPanel.setOpaque(false); // Timestamp label timestampLabel.setFont(new Font("Tahoma", Font.BOLD, fontSize)); timestampLabel.setHorizontalAlignment(SwingConstants.CENTER); timestampLabel.setText("00:00:00:000"); timestampLabel.setHorizontalTextPosition(SwingConstants.CENTER); timestampLabel.setName("timestampLabel"); timestampPanel.add(timestampLabel); jLabel1.setText("@"); timestampPanel.add(jLabel1); lblSpeed.setFont(new Font("Tahoma", Font.BOLD, fontSize)); lblSpeed.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 2)); lblSpeed.setName("lblSpeed"); lblSpeed.setText("0"); timestampPanel.add(lblSpeed); jLabel2.setFont(new Font("Tahoma", Font.BOLD, fontSize)); jLabel2.setText("x"); timestampPanel.add(jLabel2); // Set cell onset button javax.swing.ActionMap actionMap = org.jdesktop.application.Application .getInstance(org.datavyu.Datavyu.class).getContext().getActionMap(DataControllerV.class, this); gridButtonPanel.add(timestampPanel, "span 3, pushx, growx"); // placeholder setCellOnsetButton = buildButton(resourceMap, actionMap, "setCellOnset", null); gridButtonPanel.add(setCellOnsetButton, "w 45!, h 45!"); // Sync button pointCellButton = buildButton(resourceMap, actionMap, "pointCell", null); gridButtonPanel.add(pointCellButton, "w 45!, h 45!"); // Set cell onset button. osxsetCellOffsetButton = buildButton(resourceMap, actionMap, "setCellOffset", "osx"); gridButtonPanel.add(osxsetCellOffsetButton, "w 45!, h 45!"); // Instant cell button. JButton placeholder2 = new JButton(); placeholder2.setEnabled(false); placeholder2.setFocusPainted(false); gridButtonPanel.add(placeholder2, "w 45!, h 45!"); // Sync video button JButton placeholder3 = new JButton(); placeholder3.setEnabled(false); placeholder3.setFocusPainted(false); gridButtonPanel.add(placeholder3, "w 80!, h 45!"); // Rewind video button rewindButton = buildButton(resourceMap, actionMap, "rewind", null); gridButtonPanel.add(rewindButton, "w 45!, h 45!"); // Play video button playButton = buildButton(resourceMap, actionMap, "play", null); playButton.setRequestFocusEnabled(false); gridButtonPanel.add(playButton, "w 45!, h 45!"); // Fast forward button forwardButton = buildButton(resourceMap, actionMap, "forward", null); gridButtonPanel.add(forwardButton, "w 45!, h 45!"); // Go back button goBackButton = buildButton(resourceMap, actionMap, "goBack", null); gridButtonPanel.add(goBackButton, "w 45!, h 45!"); // Go back text field goBackTextField.setHorizontalAlignment(SwingConstants.CENTER); goBackTextField.setText("00:00:05:000"); goBackTextField.setName("goBackTextField"); gridButtonPanel.add(goBackTextField, "w 80!, h 45!"); // Shuttle back button shuttleBackButton = buildButton(resourceMap, actionMap, "shuttleBack", null); gridButtonPanel.add(shuttleBackButton, "w 45!, h 45!"); // Stop button stopButton = buildButton(resourceMap, actionMap, "stop", null); gridButtonPanel.add(stopButton, "w 45!, h 45!"); // Shuttle forward button shuttleForwardButton = buildButton(resourceMap, actionMap, "shuttleForward", null); gridButtonPanel.add(shuttleForwardButton, "w 45!, h 45!"); // Find button findButton = buildButton(resourceMap, actionMap, "find", null); gridButtonPanel.add(findButton, "w 45!, h 45!"); // Find text field findTextField.setHorizontalAlignment(SwingConstants.CENTER); findTextField.setText("00:00:00:000"); findTextField.setName("findOnsetLabel"); gridButtonPanel.add(findTextField, "w 80!, h 45!"); // Jog back button jogBackButton = buildButton(resourceMap, actionMap, "jogBack", null); gridButtonPanel.add(jogBackButton, "w 45!, h 45!"); // Pause button pauseButton = buildButton(resourceMap, actionMap, "pause", null); gridButtonPanel.add(pauseButton, "w 45!, h 45!"); // Jog forward button jogForwardButton = buildButton(resourceMap, actionMap, "jogForward", null); gridButtonPanel.add(jogForwardButton, "w 45!, h 45!"); // Create new cell button createNewCell = buildButton(resourceMap, actionMap, "createNewCell", null); createNewCell.setAlignmentY(0.0F); createNewCell.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); gridButtonPanel.add(createNewCell, "span 1 2, w 45!, h 92!"); // Find offset field findOffsetField.setHorizontalAlignment(SwingConstants.CENTER); findOffsetField.setText("00:00:00:000"); findOffsetField.setToolTipText(resourceMap.getString("findOffsetField.toolTipText")); findOffsetField.setEnabled(false); findOffsetField.setName("findOffsetLabel"); gridButtonPanel.add(findOffsetField, "w 80!, h 45!"); // Create new cell setting offset button createNewCellSettingOffset = buildButton(resourceMap, actionMap, "createNewCellAndSetOnset", null); gridButtonPanel.add(createNewCellSettingOffset, "span 2, w 92!, h 45!"); // Set cell offset button setCellOffsetButton = buildButton(resourceMap, actionMap, "setCellOffset", null); gridButtonPanel.add(setCellOffsetButton, "w 45!, h 45!"); // Show tracks button showTracksButton.setIcon(resourceMap.getIcon("showTracksButton.hide.icon")); showTracksButton.setName("showTracksButton"); showTracksButton.getAccessibleContext().setAccessibleName("Show Tracks"); showTracksButton.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { showTracksButtonActionPerformed(evt); } }); gridButtonPanel.add(showTracksButton, "w 80!, h 45!"); getContentPane().setLayout(new MigLayout("hidemode 3, fillx", "[growprio 0]0[]", "")); getContentPane().add(gridButtonPanel, ""); getContentPane().setBackground(Color.WHITE); tracksPanel.setBackground(Color.WHITE); tracksPanel.setVisible(false); getContentPane().add(tracksPanel, "growx"); pack(); } /** * Initialize the view for OS other than Macs. */ private void initComponents() { gridButtonPanel = new javax.swing.JPanel(); goBackTextField = new javax.swing.JTextField(); findTextField = new javax.swing.JTextField(); addDataButton = new javax.swing.JButton(); timestampLabel = new javax.swing.JLabel(); lblSpeed = new javax.swing.JLabel(); createNewCell = new javax.swing.JButton(); jLabel1 = new javax.swing.JLabel(); jLabel2 = new javax.swing.JLabel(); findOffsetField = new javax.swing.JTextField(); showTracksButton = new javax.swing.JButton(); tracksPanel = new javax.swing.JPanel(new MigLayout("fill")); final int fontSize = 11; org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application .getInstance(org.datavyu.Datavyu.class).getContext().getResourceMap(DataControllerV.class); setTitle(resourceMap.getString("title")); addWindowListener(new java.awt.event.WindowAdapter() { @Override public void windowClosing(final java.awt.event.WindowEvent evt) { formWindowClosing(evt); } }); gridButtonPanel.setBackground(Color.WHITE); gridButtonPanel.setLayout(new MigLayout("wrap 5")); // Add data button addDataButton.setText(resourceMap.getString("addDataButton.text")); addDataButton.setFont(new Font("Tahoma", Font.PLAIN, fontSize)); addDataButton.setFocusPainted(false); addDataButton.setName("addDataButton"); addDataButton.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { openVideoButtonActionPerformed(evt); } }); gridButtonPanel.add(addDataButton, "span 2, w 90!, h 25!"); // Timestamp panel JPanel timestampPanel = new JPanel(new MigLayout("", "push[][][]0![]push")); timestampPanel.setOpaque(false); // Timestamp label timestampLabel.setFont(new Font("Tahoma", Font.BOLD, fontSize)); timestampLabel.setHorizontalAlignment(SwingConstants.CENTER); timestampLabel.setText("00:00:00:000"); timestampLabel.setHorizontalTextPosition(SwingConstants.CENTER); timestampLabel.setName("timestampLabel"); timestampPanel.add(timestampLabel); jLabel1.setText("@"); timestampPanel.add(jLabel1); lblSpeed.setFont(new Font("Tahoma", Font.BOLD, fontSize)); lblSpeed.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 2)); lblSpeed.setName("lblSpeed"); lblSpeed.setText("0"); timestampPanel.add(lblSpeed); jLabel2.setFont(new Font("Tahoma", Font.BOLD, fontSize)); jLabel2.setText("x"); timestampPanel.add(jLabel2); javax.swing.ActionMap actionMap = org.jdesktop.application.Application .getInstance(org.datavyu.Datavyu.class).getContext().getActionMap(DataControllerV.class, this); gridButtonPanel.add(timestampPanel, "span 3, pushx, growx"); JButton placeholder = new JButton(); placeholder.setEnabled(false); placeholder.setFocusPainted(false); gridButtonPanel.add(placeholder, "w 45!, h 45!"); // Set cell onset button setCellOnsetButton = buildButton(resourceMap, actionMap, "setCellOnset", "win"); gridButtonPanel.add(setCellOnsetButton, "w 45!, h 45!"); // Set new cell offset button pointCellButton = buildButton(resourceMap, actionMap, "pointCell", "win"); gridButtonPanel.add(pointCellButton, "w 45!, h 45!"); // Go back button goBackButton = buildButton(resourceMap, actionMap, "goBack", null); gridButtonPanel.add(goBackButton, "w 45!, h 45!"); // Sync video button JButton placeholder2 = new JButton(); placeholder2.setEnabled(false); placeholder2.setFocusPainted(false); gridButtonPanel.add(placeholder2, "w 80!, h 45!"); // Rewind video button rewindButton = buildButton(resourceMap, actionMap, "rewind", null); gridButtonPanel.add(rewindButton, "w 45!, h 45!"); // Play video button playButton = buildButton(resourceMap, actionMap, "play", null); playButton.setRequestFocusEnabled(false); gridButtonPanel.add(playButton, "w 45!, h 45!"); // Fast forward button forwardButton = buildButton(resourceMap, actionMap, "forward", null); gridButtonPanel.add(forwardButton, "w 45!, h 45!"); // Find button findButton = buildButton(resourceMap, actionMap, "find", "win"); gridButtonPanel.add(findButton, "span 1 2, w 45!, h 95!"); // Go back text field goBackTextField.setHorizontalAlignment(SwingConstants.CENTER); goBackTextField.setText("00:00:05:000"); goBackTextField.setName("goBackTextField"); gridButtonPanel.add(goBackTextField, "w 80!, h 45!"); // Shuttle back button shuttleBackButton = buildButton(resourceMap, actionMap, "shuttleBack", null); gridButtonPanel.add(shuttleBackButton, "w 45!, h 45!"); // Stop button stopButton = buildButton(resourceMap, actionMap, "stop", null); gridButtonPanel.add(stopButton, "w 45!, h 45!"); // Shuttle forward button shuttleForwardButton = buildButton(resourceMap, actionMap, "shuttleForward", null); gridButtonPanel.add(shuttleForwardButton, "w 45!, h 45!"); // Find text field findTextField.setHorizontalAlignment(SwingConstants.CENTER); findTextField.setText("00:00:00:000"); findTextField.setName("findOnsetLabel"); gridButtonPanel.add(findTextField, "w 80!, h 45!"); // Jog back button jogBackButton = buildButton(resourceMap, actionMap, "jogBack", null); gridButtonPanel.add(jogBackButton, "w 45!, h 45!"); // Pause button pauseButton = buildButton(resourceMap, actionMap, "pause", null); gridButtonPanel.add(pauseButton, "w 45!, h 45!"); // Jog forward button jogForwardButton = buildButton(resourceMap, actionMap, "jogForward", null); gridButtonPanel.add(jogForwardButton, "w 45!, h 45!"); // Create new cell button createNewCell = buildButton(resourceMap, actionMap, "createNewCell", null); createNewCell.setAlignmentY(0.0F); createNewCell.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); gridButtonPanel.add(createNewCell, "span 1 2, w 45!, h 95!"); // Find offset field findOffsetField.setHorizontalAlignment(SwingConstants.CENTER); findOffsetField.setText("00:00:00:000"); findOffsetField.setToolTipText(resourceMap.getString("findOffsetField.toolTipText")); findOffsetField.setEnabled(false); findOffsetField.setName("findOffsetLabel"); gridButtonPanel.add(findOffsetField, "w 80!, h 45!"); // Create new cell setting offset button createNewCellSettingOffset = buildButton(resourceMap, actionMap, "createNewCellAndSetOnset", null); gridButtonPanel.add(createNewCellSettingOffset, "span 2, w 95!, h 45!"); // Set cell offset button setCellOffsetButton = buildButton(resourceMap, actionMap, "setCellOffset", null); gridButtonPanel.add(setCellOffsetButton, "w 45!, h 45!"); // Show tracks button showTracksButton.setIcon(resourceMap.getIcon("showTracksButton.hide.icon")); showTracksButton.setName("showTracksButton"); showTracksButton.getAccessibleContext().setAccessibleName("Show Tracks"); showTracksButton.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { showTracksButtonActionPerformed(evt); } }); gridButtonPanel.add(showTracksButton, "w 80!, h 45!"); getContentPane().setLayout(new MigLayout("ins 0, hidemode 3, fillx", "[growprio 0]0[]", "")); getContentPane().add(gridButtonPanel, ""); getContentPane().setBackground(Color.WHITE); tracksPanel.setBackground(Color.WHITE); tracksPanel.setVisible(false); getContentPane().add(tracksPanel, "growx"); pack(); } /** * Action to invoke when the user closes the data controller. * * @param evt * The event that triggered this action. */ private void formWindowClosing(final java.awt.event.WindowEvent evt) { setVisible(false); } /** * Action to invoke when the user clicks on the open button. * * @param evt * The event that triggered this action. */ private void openVideoButtonActionPerformed(final java.awt.event.ActionEvent evt) { LOGGER.event("Add data"); PluginChooser chooser = null; // TODO finish this switch (Datavyu.getPlatform()) { case WINDOWS: chooser = new WindowsJFC(); break; case MAC: chooser = new MacOSJFC(); break; case LINUX: chooser = new LinuxJFC(); break; default: throw new NotImplementedException("Plugin chooser unimplemented."); } PluginManager pm = PluginManager.getInstance(); chooser.addPlugin(pm.getPlugins()); for (FileFilter ff : pm.getFileFilters()) { chooser.addChoosableFileFilter(ff); } if (JFileChooser.APPROVE_OPTION == chooser.showOpenDialog(this)) { openVideo(chooser); } } /** * Action to invoke when the user clicks the show tracks button. * * @param evt * The event that triggered this action. */ private void showTracksButtonActionPerformed(final java.awt.event.ActionEvent evt) { assert (evt.getSource() instanceof JButton); JButton button = (JButton) evt.getSource(); ResourceMap resourceMap = Application.getInstance(org.datavyu.Datavyu.class).getContext() .getResourceMap(DataControllerV.class); if (tracksPanelEnabled) { LOGGER.event("Show tracks"); // Panel is being displayed, hide it button.setIcon(resourceMap.getIcon("showTracksButton.show.icon")); } else { LOGGER.event("Hide tracks"); // Panel is hidden, show it button.setIcon(resourceMap.getIcon("showTracksButton.hide.icon")); } tracksPanelEnabled = !tracksPanelEnabled; showTracksPanel(tracksPanelEnabled); updateCurrentTimeLabel(); } /** * Adds a data viewer to this data controller. * * @param icon * The icon associated with the data viewer. * @param viewer * The new viewer that we are adding to the data controller. * @param f * The parent file that the viewer represents. */ private void addDataViewer(final ImageIcon icon, final DataViewer viewer, final File f, final TrackPainter trackPainter) { assert viewer.getIdentifier() != null; addViewer(viewer, 0); // Add the file to the tracks information panel addTrack(viewer.getIdentifier(), icon, f.getAbsolutePath(), f.getName(), viewer.getDuration(), viewer.getOffset(), trackPainter); Datavyu.getProjectController().projectChanged(); } /** * Returns set of dataviewers. * * @return set of dataviewers. */ public Set<DataViewer> getDataViewers() { return viewers; } /** * Adds a track to the tracks panel. * * @param icon * Icon associated with the track * @param mediaPath * Absolute file path to the media file. * @param name * The name of the track to add. * @param duration * The duration of the data feed in milliseconds. * @param offset * The time offset of the data feed in milliseconds. * @param trackPainter * Track painter to use. */ public void addTrack(final Identifier id, final ImageIcon icon, final String mediaPath, final String name, final long duration, final long offset, final TrackPainter trackPainter) { mixerController.addNewTrack(id, icon, mediaPath, name, duration, offset, trackPainter); } /** * Add a viewer to the data controller with the given offset. * * @param viewer * The data viewer to add. * @param offset * The offset value in milliseconds. */ public void addViewer(final DataViewer viewer, final long offset) { // Add the QTDataViewer to the list of viewers we are controlling. viewers.add(viewer); viewer.setParentController(this); viewer.setOffset(offset); boolean visible = viewer.getParentJDialog().isVisible(); Datavyu.getApplication().show(viewer.getParentJDialog()); if (!visible) { viewer.getParentJDialog().setVisible(false); } // adjust the overall frame rate. float fps = viewer.getFrameRate(); if (fps > playbackModel.getCurrentFPS()) { playbackModel.setCurrentFPS(fps); } // Update track viewer. long maxDuration = playbackModel.getMaxDuration(); if ((viewer.getOffset() + viewer.getDuration()) > maxDuration) { maxDuration = viewer.getOffset() + viewer.getDuration(); } // BugzID:2114 - If this is the first viewer we are adding, always reset // max duration. if (viewers.size() == 1) { maxDuration = viewer.getOffset() + viewer.getDuration(); } mixerController.getMixerModel().getViewportModel().setViewportMaxEnd(maxDuration, true); } /** * Action to invoke when the user clicks the set cell onset button. */ @Action public void setCellOnsetAction() { LOGGER.event("Set cell onset"); new SetSelectedCellStartTimeC(getCurrentTime()); setFindTime(getCurrentTime()); } /** * Action to invoke when the user clicks on the set cell offest button. */ @Action public void setCellOffsetAction() { LOGGER.event("Set cell offset"); new SetSelectedCellStopTimeC(getCurrentTime()); setFindOffsetField(getCurrentTime()); } /** * @param show * true to show the tracks layout, false otherwise. */ public void showTracksPanel(final boolean show) { tracksPanel.setVisible(show); tracksPanel.repaint(); pack(); validate(); } /** * Handler for a TracksControllerEvent. * * @param e * event */ public void tracksControllerChanged(final TracksControllerEvent e) { switch (e.getTracksEvent()) { case CARRIAGE_EVENT: handleCarriageEvent((CarriageEvent) e.getEventObject()); break; case TIMESCALE_EVENT: handleTimescaleEvent((TimescaleEvent) e.getEventObject()); break; default: break; } } /** * Handles a TimescaleEvent. * * @param e * The timescale event that triggered this action. */ private void handleTimescaleEvent(final TimescaleEvent e) { final boolean wasClockRunning = !clock.isStopped(); final boolean togglePlaybackMode = e.getTogglePlaybackMode(); if (!wasClockRunning && togglePlaybackMode) { playAt(PLAY_RATE); clockStart(e.getTime()); } else { gotoTime(e.getTime()); } } private void gotoTime(final long time) { long newTime = time; if (newTime < playbackModel.getWindowPlayStart()) { newTime = playbackModel.getWindowPlayStart(); } if (newTime > playbackModel.getWindowPlayEnd()) { newTime = playbackModel.getWindowPlayEnd(); } clockStop(newTime); setCurrentTime(newTime); clock.setTime(newTime); } /** * Handles a CarriageEvent (when the carriage moves due to user * interaction). * * @param e * The carriage event that triggered this action. */ private void handleCarriageEvent(final CarriageEvent e) { switch (e.getEventType()) { case OFFSET_CHANGE: handleCarriageOffsetChangeEvent(e); break; case CARRIAGE_LOCK: case BOOKMARK_CHANGED: case BOOKMARK_SAVE: Datavyu.getProjectController().projectChanged(); break; default: throw new IllegalArgumentException("Unknown carriage event."); } } /** * @param e */ private void handleCarriageOffsetChangeEvent(final CarriageEvent e) { // Look through our data viewers and update the offset for (DataViewer viewer : viewers) { /* * Found our data viewer, update the DV offset and the settings in * the project file. */ if (viewer.getIdentifier().equals(e.getTrackId())) { viewer.setOffset(e.getOffset()); } } Datavyu.getProjectController().projectChanged(); // Recalculate the maximum playback duration. long maxDuration = ViewportStateImpl.MINIMUM_MAX_END; for (DataViewer viewer : viewers) { if ((viewer.getDuration() + viewer.getOffset()) > maxDuration) { maxDuration = viewer.getDuration() + viewer.getOffset(); } } mixerController.getMixerModel().getViewportModel().setViewportMaxEnd(maxDuration, false); } private void handleNeedleChanged(final PropertyChangeEvent e) { if (clock.isStopped()) { final long newTime = mixerController.getMixerModel().getNeedleModel().getCurrentTime(); clock.setTime(newTime); clockStep(newTime); } updateCurrentTimeLabel(); } private void handleRegionChanged(final PropertyChangeEvent e) { final RegionState region = mixerController.getMixerModel().getRegionModel().getRegion(); playbackModel.setWindowPlayStart(region.getRegionStart()); playbackModel.setWindowPlayEnd(region.getRegionEnd()); } private void handleViewportChanged(final PropertyChangeEvent e) { final ViewportState viewport = mixerController.getMixerModel().getViewportModel().getViewport(); playbackModel.setMaxDuration(viewport.getMaxEnd()); } // ------------------------------------------------------------------------- // Simulated clicks (for numpad calls) // /** Simulates play button clicked. */ public void pressPlay() { playButton.doClick(); } /** Simulates forward button clicked. */ public void pressForward() { forwardButton.doClick(); } /** Simulates rewind button clicked. */ public void pressRewind() { rewindButton.doClick(); } /** Simulates pause button clicked. */ public void pressPause() { pauseButton.doClick(); } /** Simulates stop button clicked. */ public void pressStop() { stopButton.doClick(); } /** Simulates shuttle forward button clicked. */ public void pressShuttleForward() { shuttleForwardButton.doClick(); } /** Simulates shuttle back button clicked. */ public void pressShuttleBack() { shuttleBackButton.doClick(); } /** Simulates find button clicked. */ public void pressFind() { findButton.doClick(); } /** Simulates set cell onset button clicked. */ public void pressSetCellOnset() { setCellOnsetButton.doClick(); } /** Simulates set cell offset button clicked. */ public void pressSetCellOffset() { setCellOffsetButton.doClick(); } /** Simulates set cell offset button clicked. */ public void pressSetCellOffsetOSX() { osxsetCellOffsetButton.doClick(); } /** Simulates set new cell onset button clicked. */ public void pressPointCell() { pointCellButton.doClick(); } /** Simulates go back button clicked. */ public void pressGoBack() { goBackButton.doClick(); } /** Simulates create new cell button clicked. */ public void pressCreateNewCell() { createNewCell.doClick(); } /** Simulates create new cell setting offset button clicked. */ public void pressCreateNewCellSettingOffset() { createNewCellSettingOffset.doClick(); } // ------------------------------------------------------------------------ // Playback actions // /** * Action to invoke when the user clicks on the play button. */ @Action public void playAction() { LOGGER.event("Play"); System.out.println("Play button..."); System.out.println(System.currentTimeMillis()); // BugzID:464 - When stopped at the end of the region of interest. // pressing play jumps the stream back to the start of the video before // starting to play again. if ((getCurrentTime() >= playbackModel.getWindowPlayEnd()) && clock.isStopped()) { jumpTo(playbackModel.getWindowPlayStart()); } playAt(PLAY_RATE); } /** * Action to invoke when the user clicks on the fast forward button. */ @Action public void forwardAction() { LOGGER.event("Fast forward"); playAt(FFORWARD_RATE); } /** * Action to invoke when the user clicks on the rewind button. */ @Action public void rewindAction() { LOGGER.event("Rewind"); playAt(REWIND_RATE); } /** * Action to invoke when the user clicks on the pause button. */ @Action public void pauseAction() { LOGGER.event("Pause"); System.out.println("Pause button..."); System.out.println(System.currentTimeMillis()); // Resume from pause at playback rate prior to pause. if (clock.isStopped()) { shuttleAt(playbackModel.getPauseRate()); // Pause views - store current playback rate. } else { playbackModel.setPauseRate(clock.getRate()); clock.stop(); lblSpeed.setText( "[" + FloatUtils.doubleToFractionStr(Double.valueOf(playbackModel.getPauseRate())) + "]"); } } /** * Action to invoke when the user clicks on the stop button. */ @Action public void stopAction() { LOGGER.event("Stop event"); System.out.println("Stop button"); System.out.println(System.currentTimeMillis()); clock.stop(); playbackModel.setPauseRate(0); } /** * Action to invoke when the user clicks on the shuttle forward button. * * @todo proper behavior for reversing shuttle direction? */ @Action public void shuttleForwardAction() { LOGGER.event("Shuttle forward"); shuttle(1); } /** * Action to invoke when the user clicks on the shuttle back button. */ @Action public void shuttleBackAction() { LOGGER.event("Shuttle back"); shuttle(-1); } /** * Searches the shuttle rates array for the given rate, and returns the * index. * * @param pRate * The rate to search for. * @return The index of the rate, or -100 if not found. */ private int findShuttleIndex(final float pRate) { for (int i = 0; i < SHUTTLE_RATES.length; i++) { if (SHUTTLE_RATES[i] == pRate) { return i; } } return -100; } /** * Populates the find time in the controller. * * @param milliseconds * The time to use when populating the find field. */ public void setFindTime(final long milliseconds) { findTextField.setText(CLOCK_FORMAT.format(milliseconds)); } /** * Populates the find offset time in the controller. * * @param milliseconds * The time to use when populating the find field. */ public void setFindOffsetField(final long milliseconds) { findOffsetField.setText(CLOCK_FORMAT.format(milliseconds)); } /** * Action to invoke when the user clicks on the find button. */ @Action public void findAction() { LOGGER.event("Find"); if (shiftMask) { findOffsetAction(); } else { try { jumpTo(CLOCK_FORMAT.parse(findTextField.getText()).getTime()); } catch (ParseException e) { LOGGER.error("unable to find within video", e); } } } /** * Action to invoke when the user holds shift down. */ public void findOffsetAction() { try { jumpTo(CLOCK_FORMAT.parse(findOffsetField.getText()).getTime()); } catch (ParseException e) { LOGGER.error("unable to find within video", e); } } public void clearRegionOfInterestAction() { mixerController.clearRegionOfInterest(); } /** * Sets the playback region of interest to lie from the find time to offset * time. */ public void setRegionOfInterestAction() { try { final long findTextTime = CLOCK_FORMAT.parse(findTextField.getText()).getTime(); final long findOffsetTime = CLOCK_FORMAT.parse(findOffsetField.getText()).getTime(); final long newWindowPlayStart = findTextTime; final long newWindowPlayEnd = (findOffsetTime > newWindowPlayStart) ? findOffsetTime : newWindowPlayStart; mixerController.getMixerModel().getRegionModel().setPlaybackRegion(newWindowPlayStart, newWindowPlayEnd); mixerController.getMixerModel().getNeedleModel().setCurrentTime(newWindowPlayStart); } catch (ParseException e) { LOGGER.error("Unable to set playback region of interest", e); } } /** * Action to invoke when the user clicks on the go back button. */ @Action public void goBackAction() { try { LOGGER.event("Go back"); long j = -CLOCK_FORMAT.parse(goBackTextField.getText()).getTime(); jump(j); // BugzID:721 - After going back - start playing again. playAt(PLAY_RATE); } catch (ParseException e) { LOGGER.error("unable to find within video", e); } } /** * Action to invoke when the user clicks on the jog backwards button. */ @Action public void jogBackAction() { LOGGER.event("Jog back"); int mul = 1; if (shiftMask) { mul = SHIFTJOG; } if (ctrlMask) { mul = CTRLSHIFTJOG; } long stepSize = ((-ONE_SECOND) / (long) playbackModel.getCurrentFPS()); long nextTime = (long) (mul * stepSize); /* BugzID:1544 - Preserve precision - force jog to frame markers. */ nextTime = nextTime - (clock.getTime() % stepSize); /* BugzID:1361 - Disallow jog to skip past the region boundaries. */ if ((clock.getTime() + nextTime) > playbackModel.getWindowPlayStart()) { jump(nextTime); } else { jumpTo(playbackModel.getWindowPlayStart()); } } /** * Action to invoke when the user clicks on the jog forwards button. */ @Action public void jogForwardAction() { LOGGER.event("Jog forward"); int mul = 1; if (shiftMask) { mul = SHIFTJOG; } if (ctrlMask) { mul = CTRLSHIFTJOG; } long stepSize = ((ONE_SECOND) / (long) playbackModel.getCurrentFPS()); long nextTime = (long) (mul * stepSize); /* BugzID:1544 - Preserve precision - force jog to frame markers. */ long mod = (clock.getTime() % stepSize); if (mod != 0) { nextTime = nextTime + stepSize - mod; } /* BugzID:1361 - Disallow jog to skip past the region boundaries. */ if ((clock.getTime() + nextTime) < playbackModel.getWindowPlayEnd()) { jump(nextTime); } else { jumpTo(playbackModel.getWindowPlayEnd()); } } // ------------------------------------------------------------------------ // [private] play back action helper functions // /** * @param rate * Rate of play. */ private void playAt(final float rate) { playbackModel.setPauseRate(0); shuttleAt(rate); } /** * @param direction * The required direction of the shuttle. */ private void shuttle(final int shuttlejump) { float currentRate = clock.getRate(); if (currentRate == 0) { currentRate = playbackModel.getPauseRate(); } try { shuttleAt(SHUTTLE_RATES[findShuttleIndex(currentRate) + shuttlejump]); } catch (java.lang.ArrayIndexOutOfBoundsException e) { System.out.println("Error finding shuttle index given current rate: " + currentRate); } } /** * @param rate * Rate of shuttle. */ private void shuttleAt(final float rate) { clock.setRate(rate); clock.start(); } /** * @param step * Milliseconds to jump. */ private void jump(final long step) { clock.stop(); playbackModel.setPauseRate(0); clock.stepTime(step); } /** * @param time * Absolute time to jump to. */ private void jumpTo(final long time) { clock.stop(); clock.setTime(time); } // ------------------------------------------------------------------------- // // /** * Action to invoke when the user clicks on the create new cell button. */ @Action public void createNewCellAction() { LOGGER.event("New cell"); CreateNewCellC controller = new CreateNewCellC(); controller.createDefaultCell(); } /** * Action to invoke when the user clicks on the new cell button. */ @Action public void createNewCellAndSetOnsetAction() { LOGGER.event("New cell set onset"); new CreateNewCellC(getCurrentTime()); } /** * Action to invoke when the user clicks on the new cell offset button. */ @Action public void pointCellAction() { LOGGER.event("Set new cell offset"); long time = getCurrentTime(); new CreateNewCellC(time); new SetNewCellStopTimeC(time); setFindOffsetField(time); } /** * Action to invoke when the user clicks on the sync video button. */ @Action public void syncVideoAction() { } @Override public void propertyChange(final PropertyChangeEvent e) { if (e.getSource() == mixerController.getNeedleController().getNeedleModel()) { handleNeedleChanged(e); } else if (e.getSource() == mixerController.getMixerModel().getViewportModel()) { handleViewportChanged(e); } else if (e.getSource() == mixerController.getRegionController().getModel()) { handleRegionChanged(e); } } }