});
// Create tool bar to play recorded animation in 3D view
this.videoToolBar = new JToolBar();
this.videoToolBar.setFloatable(false);
final ActionMap actionMap = getActionMap();
this.videoToolBar.add(actionMap.get(ActionType.DELETE_CAMERA_PATH));
this.videoToolBar.addSeparator();
this.videoToolBar.add(actionMap.get(ActionType.SKIP_BACKWARD));
this.videoToolBar.add(actionMap.get(ActionType.SEEK_BACKWARD));
this.videoToolBar.add(actionMap.get(ActionType.RECORD));
this.playbackPauseButton = new JButton(actionMap.get(ActionType.PLAYBACK));
this.videoToolBar.add(this.playbackPauseButton);
this.videoToolBar.add(actionMap.get(ActionType.SEEK_FORWARD));
this.videoToolBar.add(actionMap.get(ActionType.SKIP_FORWARD));
this.videoToolBar.addSeparator();
this.videoToolBar.add(actionMap.get(ActionType.DELETE_LAST_RECORD));
for (int i = 0; i < videoToolBar.getComponentCount(); i++) {
Component component = this.videoToolBar.getComponent(i);
if (component instanceof JButton) {
JButton button = (JButton)component;
button.setBorderPainted(true);
button.setFocusable(true);
}
}
this.tipLabel = new JLabel();
Font toolTipFont = UIManager.getFont("ToolTip.font");
this.tipLabel.setFont(toolTipFont);
this.progressLabel = new JLabel();
this.progressLabel.setFont(toolTipFont);
this.progressLabel.setHorizontalAlignment(JLabel.CENTER);
this.progressBar = new JProgressBar();
this.progressBar.setIndeterminate(true);
this.progressBar.getModel().addChangeListener(new ChangeListener() {
private long timeAfterFirstImage;
public void stateChanged(ChangeEvent ev) {
int progressValue = progressBar.getValue();
progressBar.setIndeterminate(progressValue <= progressBar.getMinimum() + 1);
if (progressValue == progressBar.getMinimum()
|| progressValue == progressBar.getMaximum()) {
progressLabel.setText("");
if (progressValue == progressBar.getMinimum()) {
int framesCount = progressBar.getMaximum() - progressBar.getMinimum();
String progressLabelFormat = preferences.getLocalizedString(VideoPanel.class, "progressStartLabel.format");
progressLabel.setText(String.format(progressLabelFormat, framesCount,
formatDuration(framesCount * 1000 / controller.getFrameRate())));
}
} else if (progressValue == progressBar.getMinimum() + 1) {
this.timeAfterFirstImage = System.currentTimeMillis();
} else {
// Update progress label once the second image is generated
// (the first one can take more time because of initialization process)
String progressLabelFormat = preferences.getLocalizedString(VideoPanel.class, "progressLabel.format");
long estimatedRemainingTime = (System.currentTimeMillis() - this.timeAfterFirstImage)
/ (progressValue - 1 - progressBar.getMinimum())
* (progressBar.getMaximum() - progressValue - 1);
String estimatedRemainingTimeText = formatDuration(estimatedRemainingTime);
progressLabel.setText(String.format(progressLabelFormat,
progressValue, progressBar.getMaximum(), estimatedRemainingTimeText));
}
}
/**
* Returns a localized string of <code>duration</code> in millis.
*/
private String formatDuration(long duration) {
long durationInSeconds = duration / 1000;
if (duration - durationInSeconds * 1000 >= 500) {
durationInSeconds++;
}
String estimatedRemainingTimeText;
if (durationInSeconds < 60) {
estimatedRemainingTimeText = String.format(preferences.getLocalizedString(
VideoPanel.class, "seconds.format"), durationInSeconds);
} else if (durationInSeconds < 3600) {
estimatedRemainingTimeText = String.format(preferences.getLocalizedString(
VideoPanel.class, "minutesSeconds.format"), durationInSeconds / 60, durationInSeconds % 60);
} else {
long hours = durationInSeconds / 3600;
long minutes = (durationInSeconds % 3600) / 60;
estimatedRemainingTimeText = String.format(preferences.getLocalizedString(
VideoPanel.class, "hoursMinutes.format"), hours, minutes);
}
return estimatedRemainingTimeText;
}
});
// Create video format label and combo box bound to WIDTH, HEIGHT, ASPECT_RATIO and FRAME_RATE controller properties
this.videoFormatLabel = new JLabel();
this.videoFormatComboBox = new JComboBox(VIDEO_FORMATS);
this.videoFormatComboBox.setMaximumRowCount(VIDEO_FORMATS.length);
this.videoFormatComboBox.setRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
VideoFormat videoFormat = (VideoFormat)value;
String aspectRatio;
switch (getAspectRatio(videoFormat)) {
case RATIO_4_3 :
aspectRatio = "4/3";
break;
case RATIO_16_9 :
default :
aspectRatio = "16/9";
break;
}
Dimension videoSize = videoFormat.getSize();
String displayedValue = String.format(videoFormatComboBoxFormat, videoSize.width, videoSize.height,
aspectRatio, (int)videoFormat.getFrameRate());
return super.getListCellRendererComponent(list, displayedValue, index, isSelected,
cellHasFocus);
}
});
this.videoFormatComboBox.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent ev) {
controller.setWidth(((VideoFormat)videoFormatComboBox.getSelectedItem()).getSize().width);
controller.setAspectRatio(getAspectRatio((VideoFormat)videoFormatComboBox.getSelectedItem()));
controller.setFrameRate((int)((VideoFormat)videoFormatComboBox.getSelectedItem()).getFrameRate());
}
});
PropertyChangeListener propertyChangeListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
videoFormatComboBox.setSelectedItem(controller.getAspectRatio());
}
};
controller.addPropertyChangeListener(VideoController.Property.WIDTH, propertyChangeListener);
controller.addPropertyChangeListener(VideoController.Property.HEIGHT, propertyChangeListener);
controller.addPropertyChangeListener(VideoController.Property.ASPECT_RATIO, propertyChangeListener);
controller.addPropertyChangeListener(VideoController.Property.FRAME_RATE, propertyChangeListener);
// Quality label and slider bound to QUALITY controller property
this.qualityLabel = new JLabel();
this.qualitySlider = new JSlider(1, controller.getQualityLevelCount()) {
@Override
public String getToolTipText(MouseEvent ev) {
float valueUnderMouse = getSliderValueAt(this, ev.getX(), preferences);
float valueToTick = valueUnderMouse - (float)Math.floor(valueUnderMouse);
if (valueToTick < 0.25f || valueToTick > 0.75f) {
// Display a tooltip that explains the different quality levels
return "<html><table><tr valign='middle'>"
+ "<td><img border='1' src='"
+ new ResourceURLContent(PhotoPanel.class, "resources/quality" + Math.round(valueUnderMouse - qualitySlider.getMinimum()) + ".jpg").getURL() + "'></td>"
+ "<td>" + preferences.getLocalizedString(VideoPanel.class, "quality" + Math.round(valueUnderMouse - qualitySlider.getMinimum()) + "DescriptionLabel.text") + "</td>"
+ "</tr></table>";
} else {
return null;
}
}
};
// Add a listener that displays also the tool tip when user clicks on the slider
this.qualitySlider.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(final MouseEvent ev) {
EventQueue.invokeLater(new Runnable() {
public void run() {
float valueUnderMouse = getSliderValueAt(qualitySlider, ev.getX(), preferences);
if (qualitySlider.getValue() == Math.round(valueUnderMouse)) {
ToolTipManager toolTipManager = ToolTipManager.sharedInstance();
int initialDelay = toolTipManager.getInitialDelay();
toolTipManager.setInitialDelay(Math.min(initialDelay, 150));
toolTipManager.mouseMoved(ev);
toolTipManager.setInitialDelay(initialDelay);
}
}
});
}
});
this.qualitySlider.setPaintLabels(true);
this.qualitySlider.setPaintTicks(true);
this.qualitySlider.setMajorTickSpacing(1);
this.qualitySlider.setSnapToTicks(true);
final boolean offScreenImageSupported = Component3DManager.getInstance().isOffScreenImageSupported();
this.qualitySlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent ev) {
if (!offScreenImageSupported) {
// Can't support 2 first quality levels if offscreen image isn't supported
qualitySlider.setValue(Math.max(qualitySlider.getMinimum() + 2, qualitySlider.getValue()));
}
controller.setQuality(qualitySlider.getValue() - qualitySlider.getMinimum());
}
});
controller.addPropertyChangeListener(VideoController.Property.QUALITY,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
qualitySlider.setValue(qualitySlider.getMinimum() + controller.getQuality());
updateAdvancedComponents();
}
});
this.qualitySlider.setValue(this.qualitySlider.getMinimum() + controller.getQuality());
this.advancedComponentsSeparator = new JSeparator();
// Create date and time labels and spinners bound to TIME controller property
Date time = new Date(Camera.convertTimeToTimeZone(controller.getTime(), TimeZone.getDefault().getID()));
this.dateLabel = new JLabel();
final SpinnerDateModel dateSpinnerModel = new SpinnerDateModel();
dateSpinnerModel.setValue(time);
this.dateSpinner = new JSpinner(dateSpinnerModel);
String datePattern = ((SimpleDateFormat)DateFormat.getDateInstance(DateFormat.SHORT)).toPattern();
if (datePattern.indexOf("yyyy") == -1) {
datePattern = datePattern.replace("yy", "yyyy");
}
JSpinner.DateEditor dateEditor = new JSpinner.DateEditor(this.dateSpinner, datePattern);
this.dateSpinner.setEditor(dateEditor);
SwingTools.addAutoSelectionOnFocusGain(dateEditor.getTextField());
this.timeLabel = new JLabel();
final SpinnerDateModel timeSpinnerModel = new SpinnerDateModel();
timeSpinnerModel.setValue(time);
this.timeSpinner = new JSpinner(timeSpinnerModel);
// From http://en.wikipedia.org/wiki/12-hour_clock#Use_by_country
String [] twelveHoursCountries = {
"AU", // Australia
"BD", // Bangladesh
"CA", // Canada (excluding Quebec, in French)
"CO", // Colombia
"EG", // Egypt
"HN", // Honduras
"JO", // Jordan
"MX", // Mexico
"MY", // Malaysia
"NI", // Nicaragua
"NZ", // New Zealand
"PH", // Philippines
"PK", // Pakistan
"SA", // Saudi Arabia
"SV", // El Salvador
"US", // United States
"VE"}; // Venezuela
SimpleDateFormat timeInstance;
if ("en".equals(Locale.getDefault().getLanguage())) {
if (Arrays.binarySearch(twelveHoursCountries, Locale.getDefault().getCountry()) >= 0) {
timeInstance = (SimpleDateFormat)DateFormat.getTimeInstance(DateFormat.SHORT, Locale.US); // 12 hours notation
} else {
timeInstance = (SimpleDateFormat)DateFormat.getTimeInstance(DateFormat.SHORT, Locale.UK); // 24 hours notation
}
} else {
timeInstance = (SimpleDateFormat)DateFormat.getTimeInstance(DateFormat.SHORT);
}
JSpinner.DateEditor timeEditor = new JSpinner.DateEditor(this.timeSpinner, timeInstance.toPattern());
this.timeSpinner.setEditor(timeEditor);
SwingTools.addAutoSelectionOnFocusGain(timeEditor.getTextField());
final PropertyChangeListener timeChangeListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
Date date = new Date(Camera.convertTimeToTimeZone(controller.getTime(), TimeZone.getDefault().getID()));
dateSpinnerModel.setValue(date);
timeSpinnerModel.setValue(date);
}
};
controller.addPropertyChangeListener(VideoController.Property.TIME, timeChangeListener);
final ChangeListener dateTimeChangeListener = new ChangeListener() {
public void stateChanged(ChangeEvent ev) {
controller.removePropertyChangeListener(VideoController.Property.TIME, timeChangeListener);
// Merge date and time
GregorianCalendar dateCalendar = new GregorianCalendar();
dateCalendar.setTime((Date)dateSpinnerModel.getValue());
GregorianCalendar timeCalendar = new GregorianCalendar();
timeCalendar.setTime((Date)timeSpinnerModel.getValue());
Calendar utcCalendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
utcCalendar.set(GregorianCalendar.YEAR, dateCalendar.get(GregorianCalendar.YEAR));
utcCalendar.set(GregorianCalendar.MONTH, dateCalendar.get(GregorianCalendar.MONTH));
utcCalendar.set(GregorianCalendar.DAY_OF_MONTH, dateCalendar.get(GregorianCalendar.DAY_OF_MONTH));
utcCalendar.set(GregorianCalendar.HOUR_OF_DAY, timeCalendar.get(GregorianCalendar.HOUR_OF_DAY));
utcCalendar.set(GregorianCalendar.MINUTE, timeCalendar.get(GregorianCalendar.MINUTE));
utcCalendar.set(GregorianCalendar.SECOND, timeCalendar.get(GregorianCalendar.SECOND));
controller.setTime(utcCalendar.getTimeInMillis());
controller.addPropertyChangeListener(VideoController.Property.TIME, timeChangeListener);
}
};
dateSpinnerModel.addChangeListener(dateTimeChangeListener);
timeSpinnerModel.addChangeListener(dateTimeChangeListener);
this.dayNightLabel = new JLabel();
final ImageIcon dayIcon = new ImageIcon(PhotoPanel.class.getResource("resources/day.png"));
final ImageIcon nightIcon = new ImageIcon(PhotoPanel.class.getResource("resources/night.png"));
PropertyChangeListener dayNightListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
if (home.getCompass().getSunElevation(
Camera.convertTimeToTimeZone(controller.getTime(), home.getCompass().getTimeZone())) > 0) {
dayNightLabel.setIcon(dayIcon);
} else {
dayNightLabel.setIcon(nightIcon);
}
}
};
controller.addPropertyChangeListener(VideoController.Property.TIME, dayNightListener);
home.getCompass().addPropertyChangeListener(dayNightListener);
dayNightListener.propertyChange(null);
this.ceilingLightEnabledCheckBox = new JCheckBox();
this.ceilingLightEnabledCheckBox.setSelected(controller.getCeilingLightColor() > 0);
controller.addPropertyChangeListener(VideoController.Property.CEILING_LIGHT_COLOR,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
ceilingLightEnabledCheckBox.setSelected(controller.getCeilingLightColor() > 0);
}
});
this.ceilingLightEnabledCheckBox.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent ev) {
controller.setCeilingLightColor(ceilingLightEnabledCheckBox.isSelected() ? 0xD0D0D0 : 0);
}
});
this.createButton = new JButton(actionMap.get(ActionType.START_VIDEO_CREATION));
this.saveButton = new JButton(actionMap.get(ActionType.SAVE_VIDEO));
this.closeButton = new JButton(actionMap.get(ActionType.CLOSE));
setComponentTexts(preferences);
updatePlaybackTimer();
this.videoFormatComboBox.setSelectedItem(new VideoFormat(VideoFormat.JPEG,