X-Git-Url: http://gitweb.fperrin.net/?p=GpsPrune.git;a=blobdiff_plain;f=src%2Ftim%2Fprune%2Fcorrelate%2FCorrelator.java;fp=src%2Ftim%2Fprune%2Fcorrelate%2FCorrelator.java;h=0aaafc36577628a1165b5867f539555b2c06abcd;hp=0000000000000000000000000000000000000000;hb=ce6f2161b8596f7018d6a76bff79bc9e571f35fd;hpb=2d8cb72e84d5cc1089ce77baf1e34ea3ea2f8465 diff --git a/src/tim/prune/correlate/Correlator.java b/src/tim/prune/correlate/Correlator.java new file mode 100644 index 0000000..0aaafc3 --- /dev/null +++ b/src/tim/prune/correlate/Correlator.java @@ -0,0 +1,699 @@ +package tim.prune.correlate; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Iterator; +import java.util.TimeZone; +import java.util.TreeSet; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JTextField; + +import tim.prune.App; +import tim.prune.GenericFunction; +import tim.prune.I18nManager; +import tim.prune.config.TimezoneHelper; +import tim.prune.data.DataPoint; +import tim.prune.data.Distance; +import tim.prune.data.Field; +import tim.prune.data.MediaObject; +import tim.prune.data.MediaList; +import tim.prune.data.TimeDifference; +import tim.prune.data.Timestamp; +import tim.prune.data.Track; +import tim.prune.data.Unit; +import tim.prune.data.UnitSetLibrary; +import tim.prune.tips.TipManager; + +/** + * Abstract superclass of the two correlator functions + */ +public abstract class Correlator extends GenericFunction +{ + protected JDialog _dialog; + private CardStack _cards = null; + private JTable _selectionTable = null; + protected JTable _previewTable = null; + private boolean _previewEnabled = false; // flag required to enable preview function on final panel + private boolean[] _cardEnabled = null; // flag for each card + private TimeZone _timezone = null; + private JTextField _offsetHourBox = null, _offsetMinBox = null, _offsetSecBox = null; + private JRadioButton _mediaLaterOption = null, _pointLaterOption = null; + private JRadioButton _timeLimitRadio = null, _distLimitRadio = null; + private JTextField _limitMinBox = null, _limitSecBox = null; + private JTextField _limitDistBox = null; + private JComboBox _distUnitsDropdown = null; + private JButton _nextButton = null, _backButton = null; + protected JButton _okButton = null; + + + /** + * Constructor + * @param inApp App object to report actions to + */ + public Correlator(App inApp) { + super(inApp); + } + + /** + * @return type key eg photo, audio + */ + protected abstract String getMediaTypeKey(); + + /** + * @return media list + */ + protected abstract MediaList getMediaList(); + + /** + * Begin the function by initialising and showing the dialog + */ + public void begin() + { + // Check whether track has timestamps, exit if not + if (!_app.getTrackInfo().getTrack().hasData(Field.TIMESTAMP)) + { + JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.correlate.notimestamps"), + I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE); + return; + } + // Show warning if no uncorrelated audios + if (!getMediaList().hasUncorrelatedMedia()) + { + Object[] buttonTexts = {I18nManager.getText("button.continue"), I18nManager.getText("button.cancel")}; + if (JOptionPane.showOptionDialog(_parentFrame, + I18nManager.getText("dialog.correlate.nouncorrelated" + getMediaTypeKey() + "s"), + I18nManager.getText(getNameKey()), JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]) + == JOptionPane.NO_OPTION) + { + return; + } + } + // Create dialog if necessary + if (_dialog == null) + { + _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true); + _dialog.setLocationRelativeTo(_parentFrame); + _dialog.getContentPane().add(makeDialogContents()); + _dialog.pack(); + } + _okButton.setEnabled(false); + // Init timezone to the currently selected one + _timezone = TimezoneHelper.getSelectedTimezone(); + // Go to first available card + int card = 0; + _cardEnabled = null; + while (!isCardEnabled(card)) { + card++; + } + _cards.showCard(card); + showCard(0); // does set up and next/prev enabling + if (!isCardEnabled(1)) { + _app.showTip(TipManager.Tip_ManuallyCorrelateOne); + } + _dialog.setVisible(true); + } + + /** + * Make contents of correlate dialog + * @return JPanel containing gui elements + */ + private JPanel makeDialogContents() + { + JPanel mainPanel = new JPanel(); + mainPanel.setLayout(new BorderLayout()); + + // Card panel in the middle + _cards = new CardStack(); + + // First panel (not required by photo correlator) + JPanel card1 = makeFirstPanel(); + if (card1 == null) {card1 = new JPanel();} + _cards.addCard(card1); + + // Second panel for selection of linked media + _cards.addCard(makeSecondPanel()); + + // Third panel for options and preview + _cards.addCard(makeThirdPanel()); + mainPanel.add(_cards, BorderLayout.CENTER); + + // Button panel at the bottom + JPanel buttonPanel = new JPanel(); + _backButton = new JButton(I18nManager.getText("button.back")); + _backButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + showCard(-1); + } + }); + _backButton.setEnabled(false); + buttonPanel.add(_backButton); + _nextButton = new JButton(I18nManager.getText("button.next")); + _nextButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + showCard(1); + } + }); + buttonPanel.add(_nextButton); + _okButton = new JButton(I18nManager.getText("button.ok")); + _okButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + finishCorrelation(); + _dialog.dispose(); + } + }); + _okButton.setEnabled(false); + buttonPanel.add(_okButton); + JButton cancelButton = new JButton(I18nManager.getText("button.cancel")); + cancelButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + _dialog.dispose(); + } + }); + buttonPanel.add(cancelButton); + mainPanel.add(buttonPanel, BorderLayout.SOUTH); + return mainPanel; + } + + /** + * Construct a table model for the photo / audio selection table + * @return table model + */ + protected MediaSelectionTableModel makeSelectionTableModel() + { + MediaList mediaList = getMediaList(); + MediaSelectionTableModel model = new MediaSelectionTableModel( + "dialog.correlate.select." + getMediaTypeKey() + "name", + "dialog.correlate.select." + getMediaTypeKey() + "later"); + int numMedia = mediaList.getNumMedia(); + for (int i=0; i set = new TreeSet(); + // loop through rows of table adding to list + int numRows = inModel.getRowCount(); + int i; + for (i=0; i iterator = set.iterator(); + for (i=0; i<(numRows+1)/2; i++) + { + pair = iterator.next(); + } + return pair.getIndex(); + } + + + /** + * Disable the ok button + */ + public void disableOkButton() + { + if (_okButton != null) { + _okButton.setEnabled(false); + } + } + + /** + * @return gui components for first panel, or null if empty + */ + protected JPanel makeFirstPanel() { + return null; + } + + /** + * Make the second panel for the selection screen + * @return JPanel object containing gui elements + */ + private JPanel makeSecondPanel() + { + JPanel card = new JPanel(); + card.setLayout(new BorderLayout(10, 10)); + JLabel introLabel = new JLabel(I18nManager.getText( + "dialog.correlate." + getMediaTypeKey() + "select.intro")); + introLabel.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6)); + card.add(introLabel, BorderLayout.NORTH); + // table doesn't have model yet - that will be attached later + _selectionTable = new JTable(); + JScrollPane photoScrollPane = new JScrollPane(_selectionTable); + photoScrollPane.setPreferredSize(new Dimension(400, 100)); + card.add(photoScrollPane, BorderLayout.CENTER); + return card; + } + + + /** + * Make contents of third panel including options and preview + * @return JPanel containing gui elements + */ + private JPanel makeThirdPanel() + { + OptionsChangedListener optionsChangedListener = new OptionsChangedListener(this); + // Second panel for options + JPanel card2 = new JPanel(); + card2.setLayout(new BorderLayout()); + JPanel card2Top = new JPanel(); + card2Top.setLayout(new BoxLayout(card2Top, BoxLayout.Y_AXIS)); + JLabel introLabel = new JLabel(I18nManager.getText("dialog.correlate.options.intro")); + introLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 5, 6)); + card2Top.add(introLabel); + // time offset section + JPanel offsetPanel = new JPanel(); + offsetPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("dialog.correlate.options.offsetpanel"))); + offsetPanel.setLayout(new BoxLayout(offsetPanel, BoxLayout.Y_AXIS)); + JPanel offsetPanelTop = new JPanel(); + offsetPanelTop.setLayout(new FlowLayout()); + offsetPanelTop.setBorder(null); + offsetPanelTop.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset") + ": ")); + _offsetHourBox = new JTextField(3); + _offsetHourBox.addKeyListener(optionsChangedListener); + offsetPanelTop.add(_offsetHourBox); + offsetPanelTop.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.hours"))); + _offsetMinBox = new JTextField(3); + _offsetMinBox.addKeyListener(optionsChangedListener); + offsetPanelTop.add(_offsetMinBox); + offsetPanelTop.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.minutes"))); + _offsetSecBox = new JTextField(3); + _offsetSecBox.addKeyListener(optionsChangedListener); + offsetPanelTop.add(_offsetSecBox); + offsetPanelTop.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.seconds"))); + offsetPanel.add(offsetPanelTop); + + // radio buttons for photo / point later + JPanel offsetPanelBot = new JPanel(); + offsetPanelBot.setLayout(new FlowLayout()); + offsetPanelBot.setBorder(null); + _mediaLaterOption = new JRadioButton(I18nManager.getText("dialog.correlate.options." + getMediaTypeKey() + "later")); + _pointLaterOption = new JRadioButton(I18nManager.getText("dialog.correlate.options.pointlater" + getMediaTypeKey())); + _mediaLaterOption.addItemListener(optionsChangedListener); + _pointLaterOption.addItemListener(optionsChangedListener); + ButtonGroup laterGroup = new ButtonGroup(); + laterGroup.add(_mediaLaterOption); + laterGroup.add(_pointLaterOption); + offsetPanelBot.add(_mediaLaterOption); + offsetPanelBot.add(_pointLaterOption); + offsetPanel.add(offsetPanelBot); + offsetPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + card2Top.add(offsetPanel); + + // listener for radio buttons + ActionListener radioListener = new ActionListener() { + public void actionPerformed(ActionEvent e) { + enableEditBoxes(); + } + }; + // time limits section + JPanel limitsPanel = new JPanel(); + limitsPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("dialog.correlate.options.limitspanel"))); + limitsPanel.setLayout(new BoxLayout(limitsPanel, BoxLayout.Y_AXIS)); + JPanel timeLimitPanel = new JPanel(); + timeLimitPanel.setLayout(new FlowLayout()); + JRadioButton noTimeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.notimelimit")); + noTimeLimitRadio.addItemListener(optionsChangedListener); + noTimeLimitRadio.addActionListener(radioListener); + timeLimitPanel.add(noTimeLimitRadio); + _timeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.timelimit") + ": "); + _timeLimitRadio.addItemListener(optionsChangedListener); + _timeLimitRadio.addActionListener(radioListener); + timeLimitPanel.add(_timeLimitRadio); + groupRadioButtons(noTimeLimitRadio, _timeLimitRadio); + _limitMinBox = new JTextField(3); + _limitMinBox.addKeyListener(optionsChangedListener); + timeLimitPanel.add(_limitMinBox); + timeLimitPanel.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.minutes"))); + _limitSecBox = new JTextField(3); + _limitSecBox.addKeyListener(optionsChangedListener); + timeLimitPanel.add(_limitSecBox); + timeLimitPanel.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.seconds"))); + limitsPanel.add(timeLimitPanel); + // distance limits + JPanel distLimitPanel = new JPanel(); + distLimitPanel.setLayout(new FlowLayout()); + JRadioButton noDistLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.nodistancelimit")); + noDistLimitRadio.addItemListener(optionsChangedListener); + noDistLimitRadio.addActionListener(radioListener); + distLimitPanel.add(noDistLimitRadio); + _distLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.distancelimit") + ": "); + _distLimitRadio.addItemListener(optionsChangedListener); + _distLimitRadio.addActionListener(radioListener); + distLimitPanel.add(_distLimitRadio); + groupRadioButtons(noDistLimitRadio, _distLimitRadio); + _limitDistBox = new JTextField(4); + _limitDistBox.addKeyListener(optionsChangedListener); + distLimitPanel.add(_limitDistBox); + String[] distUnitsOptions = {I18nManager.getText("units.kilometres"), I18nManager.getText("units.metres"), + I18nManager.getText("units.miles")}; + _distUnitsDropdown = new JComboBox(distUnitsOptions); + _distUnitsDropdown.addItemListener(optionsChangedListener); + distLimitPanel.add(_distUnitsDropdown); + limitsPanel.add(distLimitPanel); + limitsPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + card2Top.add(limitsPanel); + + // preview button + JButton previewButton = new JButton(I18nManager.getText("button.preview")); + previewButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + createPreview(true); + } + }); + card2Top.add(previewButton); + card2.add(card2Top, BorderLayout.NORTH); + // preview + _previewTable = new JTable(new MediaPreviewTableModel("dialog.correlate.select." + getMediaTypeKey() + "name")); + JScrollPane previewScrollPane = new JScrollPane(_previewTable); + previewScrollPane.setPreferredSize(new Dimension(300, 100)); + card2.add(previewScrollPane, BorderLayout.CENTER); + return card2; + } + + + /** + * Go to the next or previous card in the stack + * @param increment 1 for next, -1 for previous card + */ + private void showCard(int increment) + { + int currCard = _cards.getCurrentCardIndex(); + int next = currCard + increment; + if (!isCardEnabled(next)) { + next += increment; + } + setupCard(next); + _backButton.setEnabled(next > 0 && (isCardEnabled(next-1) || isCardEnabled(next-2))); + _nextButton.setEnabled(next < (_cards.getNumCards()-1)); + _cards.showCard(next); + } + + /** + * @param inCardNum index of card + * @return true if specified card is enabled + */ + private boolean isCardEnabled(int inCardNum) + { + if (_cardEnabled == null) { + _cardEnabled = getCardEnabledFlags(); + } + return (inCardNum >= 0 && inCardNum < _cardEnabled.length && _cardEnabled[inCardNum]); + } + + /** + * @return array of boolean flags denoting availability of cards + */ + protected boolean[] getCardEnabledFlags() + { + // by default first is off and third is always on; second depends on selection table + return new boolean[] {false, makeSelectionTableModel().getRowCount() > 0, true}; + } + + /** + * Set up the specified card + * @param inCardNum index of card + */ + protected void setupCard(int inCardNum) + { + _previewEnabled = false; + if (inCardNum == 1) + { + // set up photo selection card + MediaSelectionTableModel model = makeSelectionTableModel(); + _selectionTable.setModel(model); + for (int i=0; i