/*
* ObserverCameraPanel.java 09 mars 2012
*
* Sweet Home 3D, Copyright (c) 2012 Emmanuel PUYBARET / eTeks <info@eteks.com>
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.eteks.sweethome3d.swing;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.KeyStroke;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.eteks.sweethome3d.model.UserPreferences;
import com.eteks.sweethome3d.tools.OperatingSystem;
import com.eteks.sweethome3d.viewcontroller.DialogView;
import com.eteks.sweethome3d.viewcontroller.ObserverCameraController;
import com.eteks.sweethome3d.viewcontroller.View;
/**
* Observer camera editing panel.
* @author Emmanuel Puybaret
*/
public class ObserverCameraPanel extends JPanel implements DialogView {
private final ObserverCameraController controller;
private JLabel xLabel;
private JSpinner xSpinner;
private JLabel yLabel;
private JSpinner ySpinner;
private JLabel elevationLabel;
private JSpinner elevationSpinner;
private JLabel yawLabel;
private JSpinner yawSpinner;
private JLabel pitchLabel;
private JSpinner pitchSpinner;
private JLabel fieldOfViewLabel;
private JSpinner fieldOfViewSpinner;
private JCheckBox adjustObserverCameraElevationCheckBox;
private String dialogTitle;
/**
* Creates a panel that displays observer camera attributes data according to the units
* set in <code>preferences</code>.
* @param preferences user preferences
* @param controller the controller of this panel
*/
public ObserverCameraPanel(UserPreferences preferences,
ObserverCameraController controller) {
super(new GridBagLayout());
this.controller = controller;
createComponents(preferences, controller);
setMnemonics(preferences);
layoutComponents(preferences);
}
/**
* Creates and initializes components and spinners model.
*/
private void createComponents(UserPreferences preferences,
final ObserverCameraController controller) {
// Get unit name matching current unit
String unitName = preferences.getLengthUnit().getName();
// Create X label and its spinner bound to X controller property
this.xLabel = new JLabel(SwingTools.getLocalizedLabelText(preferences,
HomeFurniturePanel.class, "xLabel.text", unitName));
final float maximumLength = 5E5f;
final NullableSpinner.NullableSpinnerLengthModel xSpinnerModel =
new NullableSpinner.NullableSpinnerLengthModel(preferences, -maximumLength, maximumLength);
this.xSpinner = new NullableSpinner(xSpinnerModel);
xSpinnerModel.setLength(controller.getX());
final PropertyChangeListener xChangeListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
xSpinnerModel.setLength((Float)ev.getNewValue());
}
};
controller.addPropertyChangeListener(ObserverCameraController.Property.X, xChangeListener);
xSpinnerModel.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent ev) {
controller.setX(xSpinnerModel.getLength());
}
});
// Create Y label and its spinner bound to Y controller property
this.yLabel = new JLabel(SwingTools.getLocalizedLabelText(preferences,
HomeFurniturePanel.class, "yLabel.text", unitName));
final NullableSpinner.NullableSpinnerLengthModel ySpinnerModel =
new NullableSpinner.NullableSpinnerLengthModel(preferences, -maximumLength, maximumLength);
this.ySpinner = new NullableSpinner(ySpinnerModel);
ySpinnerModel.setLength(controller.getY());
final PropertyChangeListener yChangeListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
ySpinnerModel.setLength((Float)ev.getNewValue());
}
};
controller.addPropertyChangeListener(ObserverCameraController.Property.Y, yChangeListener);
ySpinnerModel.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent ev) {
controller.setY(ySpinnerModel.getLength());
}
});
// Create camera elevation label and spinner bound to ELEVATION controller property
this.elevationLabel = new JLabel(String.format(SwingTools.getLocalizedLabelText(preferences,
ObserverCameraPanel.class, "elevationLabel.text"), unitName));
float maximumElevation = preferences.getLengthUnit().getMaximumElevation();
final NullableSpinner.NullableSpinnerLengthModel elevationSpinnerModel =
new NullableSpinner.NullableSpinnerLengthModel(preferences, controller.getMinimumElevation(), maximumElevation);
this.elevationSpinner = new AutoCommitSpinner(elevationSpinnerModel);
elevationSpinnerModel.setLength((float)Math.round(controller.getElevation() * 100) / 100);
elevationSpinnerModel.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent ev) {
controller.setElevation(elevationSpinnerModel.getLength());
}
});
PropertyChangeListener elevationChangeListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
elevationSpinnerModel.setLength((float)Math.round(controller.getElevation() * 100) / 100);
}
};
controller.addPropertyChangeListener(ObserverCameraController.Property.ELEVATION, elevationChangeListener);
// Create yaw label and spinner bound to YAW_IN_DEGREES controller property
this.yawLabel = new JLabel(SwingTools.getLocalizedLabelText(preferences,
ObserverCameraPanel.class, "yawLabel.text"));
final SpinnerNumberModel yawSpinnerModel = new SpinnerNumberModel(0, -10000, 10000, 5);
this.yawSpinner = new AutoCommitSpinner(yawSpinnerModel);
yawSpinnerModel.setValue(controller.getYawInDegrees());
yawSpinnerModel.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent ev) {
controller.setYawInDegrees(((Number)yawSpinnerModel.getValue()).intValue());
}
});
controller.addPropertyChangeListener(ObserverCameraController.Property.YAW_IN_DEGREES,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
yawSpinnerModel.setValue(controller.getYawInDegrees());
}
});
// Create pitch label and spinner bound to SPIN_IN_DEGREES controller property
this.pitchLabel = new JLabel(SwingTools.getLocalizedLabelText(preferences,
ObserverCameraPanel.class, "pitchLabel.text"));
final SpinnerNumberModel pitchSpinnerModel = new SpinnerNumberModel(0, -60, 90, 5);
this.pitchSpinner = new AutoCommitSpinner(pitchSpinnerModel);
pitchSpinnerModel.setValue(controller.getPitchInDegrees());
pitchSpinnerModel.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent ev) {
controller.setPitchInDegrees(((Number)pitchSpinnerModel.getValue()).intValue());
}
});
controller.addPropertyChangeListener(ObserverCameraController.Property.PITCH_IN_DEGREES,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
pitchSpinnerModel.setValue(controller.getPitchInDegrees());
}
});
// Create field of view label and spinner bound to FIELD_OF_VIEW_IN_DEGREES controller property
this.fieldOfViewLabel = new JLabel(SwingTools.getLocalizedLabelText(preferences,
ObserverCameraPanel.class, "fieldOfViewLabel.text"));
final SpinnerNumberModel fieldOfViewSpinnerModel = new SpinnerNumberModel(10, 10, 120, 1);
this.fieldOfViewSpinner = new AutoCommitSpinner(fieldOfViewSpinnerModel);
fieldOfViewSpinnerModel.setValue(controller.getFieldOfViewInDegrees());
fieldOfViewSpinnerModel.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent ev) {
controller.setFieldOfViewInDegrees(((Number)fieldOfViewSpinnerModel.getValue()).intValue());
}
});
controller.addPropertyChangeListener(ObserverCameraController.Property.FIELD_OF_VIEW_IN_DEGREES,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
fieldOfViewSpinnerModel.setValue(controller.getFieldOfViewInDegrees());
}
});
this.adjustObserverCameraElevationCheckBox = new JCheckBox(SwingTools.getLocalizedLabelText(preferences,
ObserverCameraPanel.class, "adjustObserverCameraElevationCheckBox.text"), controller.isElevationAdjusted());
this.adjustObserverCameraElevationCheckBox.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent ev) {
controller.setElevationAdjusted(adjustObserverCameraElevationCheckBox.isSelected());
}
});
controller.addPropertyChangeListener(ObserverCameraController.Property.OBSERVER_CAMERA_ELEVATION_ADJUSTED,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
adjustObserverCameraElevationCheckBox.setSelected(controller.isElevationAdjusted());
}
});
controller.addPropertyChangeListener(ObserverCameraController.Property.MINIMUM_ELEVATION,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
if (elevationSpinnerModel.getLength() != null) {
elevationSpinnerModel.setLength(Math.max(elevationSpinnerModel.getLength(), controller.getMinimumElevation()));
}
elevationSpinnerModel.setMinimum(controller.getMinimumElevation());
}
});
this.dialogTitle = preferences.getLocalizedString(
ObserverCameraPanel.class, "observerCamera.title");
}
/**
* Sets components mnemonics and label / component associations.
*/
private void setMnemonics(UserPreferences preferences) {
if (!OperatingSystem.isMacOSX()) {
this.xLabel.setDisplayedMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString(
ObserverCameraPanel.class, "xLabel.mnemonic")).getKeyCode());
this.xLabel.setLabelFor(this.xSpinner);
this.yLabel.setDisplayedMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString(
ObserverCameraPanel.class, "yLabel.mnemonic")).getKeyCode());
this.yLabel.setLabelFor(this.ySpinner);
this.elevationLabel.setDisplayedMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString(
ObserverCameraPanel.class, "elevationLabel.mnemonic")).getKeyCode());
this.elevationLabel.setLabelFor(this.elevationSpinner);
this.fieldOfViewLabel.setDisplayedMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString(
ObserverCameraPanel.class, "fieldOfViewLabel.mnemonic")).getKeyCode());
this.fieldOfViewLabel.setLabelFor(this.fieldOfViewLabel);
this.yawLabel.setDisplayedMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString(
ObserverCameraPanel.class, "yawLabel.mnemonic")).getKeyCode());
this.yawLabel.setLabelFor(this.yawLabel);
this.pitchLabel.setDisplayedMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString(
ObserverCameraPanel.class, "pitchLabel.mnemonic")).getKeyCode());
this.pitchLabel.setLabelFor(this.pitchLabel);
this.adjustObserverCameraElevationCheckBox.setMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString(
ObserverCameraPanel.class, "adjustObserverCameraElevationCheckBox.mnemonic")).getKeyCode());
}
}
/**
* Layouts panel components in panel with their labels.
*/
private void layoutComponents(UserPreferences preferences) {
int labelAlignment = OperatingSystem.isMacOSX()
? GridBagConstraints.LINE_END
: GridBagConstraints.LINE_START;
JPanel locationPanel = SwingTools.createTitledPanel(preferences.getLocalizedString(
ObserverCameraPanel.class, "locationPanel.title"));
// First row
Insets labelInsets = new Insets(0, 0, 5, 5);
locationPanel.add(this.xLabel, new GridBagConstraints(
0, 0, 1, 1, 0, 0, labelAlignment,
GridBagConstraints.NONE, labelInsets, 0, 0));
Insets componentInsets = new Insets(0, 0, 5, 0);
locationPanel.add(this.xSpinner, new GridBagConstraints(
1, 0, 1, 1, 0, 0, GridBagConstraints.LINE_START,
GridBagConstraints.HORIZONTAL, componentInsets, -15, 0));
// Second row
locationPanel.add(this.yLabel, new GridBagConstraints(
0, 1, 1, 1, 0, 0, labelAlignment,
GridBagConstraints.NONE, labelInsets, 0, 0));
locationPanel.add(this.ySpinner, new GridBagConstraints(
1, 1, 1, 1, 0, 0, GridBagConstraints.LINE_START,
GridBagConstraints.HORIZONTAL, componentInsets, -15, 0));
// Third row
locationPanel.add(this.elevationLabel, new GridBagConstraints(
0, 2, 1, 1, 0, 0, labelAlignment,
GridBagConstraints.NONE, labelInsets, 0, 0));
locationPanel.add(this.elevationSpinner, new GridBagConstraints(
1, 2, 1, 1, 0, 0, GridBagConstraints.LINE_START,
GridBagConstraints.HORIZONTAL, componentInsets, 0, 0));
Insets rowInsets;
if (OperatingSystem.isMacOSXLeopardOrSuperior()) {
// User smaller insets for Mac OS X 10.5
rowInsets = new Insets(0, 0, 0, 0);
} else {
rowInsets = new Insets(0, 0, 5, 0);
}
add(locationPanel, new GridBagConstraints(
0, 0, 1, 1, 0, 0, GridBagConstraints.LINE_START,
GridBagConstraints.HORIZONTAL, rowInsets, 0, 0));
JPanel anglesPanel = SwingTools.createTitledPanel(preferences.getLocalizedString(
ObserverCameraPanel.class, "anglesPanel.title"));
anglesPanel.add(this.yawLabel, new GridBagConstraints(
0, 0, 1, 1, 0, 0, labelAlignment,
GridBagConstraints.NONE, labelInsets, 0, 0));
anglesPanel.add(this.yawSpinner, new GridBagConstraints(
1, 0, 1, 1, 0, 0, GridBagConstraints.LINE_START,
GridBagConstraints.HORIZONTAL, componentInsets, -10, 0));
anglesPanel.add(this.pitchLabel, new GridBagConstraints(
0, 1, 1, 1, 0, 0, labelAlignment,
GridBagConstraints.NONE, labelInsets, 0, 0));
anglesPanel.add(this.pitchSpinner, new GridBagConstraints(
1, 1, 1, 1, 0, 0, GridBagConstraints.LINE_START,
GridBagConstraints.HORIZONTAL, componentInsets, 0, 0));
anglesPanel.add(this.fieldOfViewLabel, new GridBagConstraints(
0, 2, 1, 1, 0, 0, labelAlignment,
GridBagConstraints.NONE, labelInsets, 0, 0));
anglesPanel.add(this.fieldOfViewSpinner, new GridBagConstraints(
1, 2, 1, 1, 0, 0, GridBagConstraints.LINE_START,
GridBagConstraints.HORIZONTAL, componentInsets, 0, 0));
add(anglesPanel, new GridBagConstraints(
1, 0, 1, 1, 0, 0, GridBagConstraints.LINE_START,
GridBagConstraints.HORIZONTAL, rowInsets, 0, 0));
if (controller.isObserverCameraElevationAdjustedEditable()) {
add(this.adjustObserverCameraElevationCheckBox, new GridBagConstraints(
0, 1, 2, 1, 0, 0, GridBagConstraints.LINE_START,
GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
}
}
/**
* Displays this panel in a modal dialog box.
*/
public void displayView(View parentView) {
JFormattedTextField elevationSpinnerTextField =
((JSpinner.DefaultEditor)this.elevationSpinner.getEditor()).getTextField();
if (SwingTools.showConfirmDialog((JComponent)parentView, this, this.dialogTitle,
elevationSpinnerTextField) == JOptionPane.OK_OPTION
&& this.controller != null) {
this.controller.modifyObserverCamera();
}
}
}