/* * ImportedFurnitureWizardStepsPanel.java 4 juil. 07 * * Sweet Home 3D, Copyright (c) 2007 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.CardLayout; import java.awt.Color; import java.awt.Component; import java.awt.ComponentOrientation; import java.awt.Cursor; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.GraphicsConfiguration; import java.awt.GraphicsEnvironment; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.Insets; import java.awt.MouseInfo; import java.awt.Point; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.GraphicsConfigTemplate3D; import javax.media.j3d.Transform3D; import javax.media.j3d.View; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.ComboBoxEditor; import javax.swing.DefaultListCellRenderer; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JSpinner; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JToolTip; import javax.swing.JWindow; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.TransferHandler; import javax.swing.UIManager; import javax.swing.border.EtchedBorder; import javax.swing.event.AncestorEvent; import javax.swing.event.AncestorListener; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.vecmath.Matrix3f; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import com.eteks.sweethome3d.j3d.Component3DManager; import com.eteks.sweethome3d.j3d.ModelManager; import com.eteks.sweethome3d.j3d.OBJWriter; import com.eteks.sweethome3d.model.CatalogPieceOfFurniture; import com.eteks.sweethome3d.model.Content; import com.eteks.sweethome3d.model.FurnitureCategory; import com.eteks.sweethome3d.model.RecorderException; import com.eteks.sweethome3d.model.UserPreferences; import com.eteks.sweethome3d.tools.OperatingSystem; import com.eteks.sweethome3d.tools.TemporaryURLContent; import com.eteks.sweethome3d.tools.URLContent; import com.eteks.sweethome3d.viewcontroller.ContentManager; import com.eteks.sweethome3d.viewcontroller.ImportedFurnitureWizardController; import com.eteks.sweethome3d.viewcontroller.ImportedFurnitureWizardStepsView; /** * Wizard panel for furniture import. * @author Emmanuel Puybaret */ public class ImportedFurnitureWizardStepsPanel extends JPanel implements ImportedFurnitureWizardStepsView { private final ImportedFurnitureWizardController controller; private CardLayout cardLayout; private JLabel modelChoiceOrChangeLabel; private JButton modelChoiceOrChangeButton; private JButton findModelsButton; private JLabel modelChoiceErrorLabel; private ModelPreviewComponent modelPreviewComponent; private JLabel orientationLabel; private JButton defaultOrientationButton; private JButton turnLeftButton; private JButton turnRightButton; private JButton turnUpButton; private JButton turnDownButton; private int horizontalAngle; private int verticalAngle; private JToolTip orientationToolTip; private JWindow orientationToolTipWindow; private RotationPreviewComponent rotationPreviewComponent; private JLabel backFaceShownLabel; private JCheckBox backFaceShownCheckBox; private JLabel attributesLabel; private JLabel nameLabel; private JTextField nameTextField; private JCheckBox addToCatalogCheckBox; private JLabel categoryLabel; private JComboBox categoryComboBox; private JLabel widthLabel; private JSpinner widthSpinner; private JLabel depthLabel; private JSpinner depthSpinner; private JLabel heightLabel; private JSpinner heightSpinner; private JCheckBox keepProportionsCheckBox; private JLabel elevationLabel; private JSpinner elevationSpinner; private AttributesPreviewComponent attributesPreviewComponent; private JCheckBox movableCheckBox; private JCheckBox doorOrWindowCheckBox; private JCheckBox staircaseCheckBox; private JLabel colorLabel; private ColorButton colorButton; private JButton clearColorButton; private JLabel iconLabel; private IconPreviewComponent iconPreviewComponent; private Cursor defaultCursor; private Executor modelLoader; /** * Creates a view for furniture import. */ public ImportedFurnitureWizardStepsPanel(CatalogPieceOfFurniture piece, String modelName, boolean importHomePiece, UserPreferences preferences, final ImportedFurnitureWizardController controller) { this.controller = controller; // Create a model loader for each wizard in case model loading hangs this.modelLoader = Executors.newSingleThreadExecutor(); createComponents(importHomePiece, preferences, controller); setMnemonics(preferences); layoutComponents(); updateController(piece, preferences); if (modelName != null) { updateController(modelName, preferences, controller.getContentManager(), importHomePiece ? null : preferences.getFurnitureCatalog().getCategories().get(0), true); } controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.STEP, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { updateStep(controller); } }); } /** * Creates components displayed by this panel. */ private void createComponents(final boolean importHomePiece, final UserPreferences preferences, final ImportedFurnitureWizardController controller) { // Get unit name matching current unit String unitName = preferences.getLengthUnit().getName(); // Model panel components this.modelChoiceOrChangeLabel = new JLabel(); this.modelChoiceOrChangeButton = new JButton(); final FurnitureCategory defaultModelCategory = (importHomePiece || preferences.getFurnitureCatalog().getCategories().size() == 0) ? null : preferences.getFurnitureCatalog().getCategories().get(0); this.modelChoiceOrChangeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ev) { String modelName = showModelChoiceDialog(preferences, controller.getContentManager()); if (modelName != null) { updateController(modelName, preferences, controller.getContentManager(), defaultModelCategory, false); } } }); try { this.findModelsButton = new JButton(SwingTools.getLocalizedLabelText(preferences, ImportedFurnitureWizardStepsPanel.class, "findModelsButton.text")); final String findModelsUrl = preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "findModelsButton.url"); this.findModelsButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ev) { boolean documentShown = false; try { // Display Find models page in browser documentShown = SwingTools.showDocumentInBrowser(new URL(findModelsUrl)); } catch (MalformedURLException ex) { // Document isn't shown } if (!documentShown) { // If the document wasn't shown, display a message // with a copiable URL in a message box JTextArea findModelsMessageTextArea = new JTextArea(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "findModelsMessage.text")); String findModelsTitle = preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "findModelsMessage.title"); findModelsMessageTextArea.setEditable(false); findModelsMessageTextArea.setOpaque(false); JOptionPane.showMessageDialog(SwingUtilities.getRootPane(ImportedFurnitureWizardStepsPanel.this), findModelsMessageTextArea, findModelsTitle, JOptionPane.INFORMATION_MESSAGE); } } }); } catch (IllegalArgumentException ex) { // Don't create findModelsButton if its text or url isn't defined } this.modelChoiceErrorLabel = new JLabel(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "modelChoiceErrorLabel.text")); // Make modelChoiceErrorLabel visible only if an error occurred during model content loading this.modelChoiceErrorLabel.setVisible(false); this.modelPreviewComponent = new ModelPreviewComponent(true); // Add a transfer handler to model preview component to let user drag and drop a file in component this.modelPreviewComponent.setTransferHandler(new TransferHandler() { @Override public boolean canImport(JComponent comp, DataFlavor [] flavors) { return Arrays.asList(flavors).contains(DataFlavor.javaFileListFlavor); } @Override public boolean importData(JComponent comp, Transferable transferedFiles) { boolean success = true; try { List<File> files = (List<File>)transferedFiles.getTransferData(DataFlavor.javaFileListFlavor); final String modelName = files.get(0).getAbsolutePath(); EventQueue.invokeLater(new Runnable() { public void run() { updateController(modelName, preferences, controller.getContentManager(), defaultModelCategory, false); } }); } catch (UnsupportedFlavorException ex) { success = false; } catch (IOException ex) { success = false; } if (!success) { EventQueue.invokeLater(new Runnable() { public void run() { JOptionPane.showMessageDialog(SwingUtilities.getRootPane(ImportedFurnitureWizardStepsPanel.this), preferences.getLocalizedString(ImportedFurnitureWizardStepsPanel.class, "modelChoiceError")); } }); } return success; } }); this.modelPreviewComponent.setBorder(SwingTools.getDropableComponentBorder()); // Orientation panel components this.orientationLabel = new JLabel(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "orientationLabel.text")); this.defaultOrientationButton = new JButton(new ResourceAction(preferences, ImportedFurnitureWizardStepsPanel.class, "DEFAULT_ORIENTATION", true) { @Override public void actionPerformed(ActionEvent ev) { updateModelRotation(new Transform3D()); horizontalAngle = 0; verticalAngle = 0; } }); final String angleTooltipFormat = preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "angleTooltipFeedback"); this.orientationToolTip = new JToolTip(); this.turnLeftButton = new AutoRepeatButton(new ResourceAction(preferences, ImportedFurnitureWizardStepsPanel.class, "TURN_LEFT", true) { @Override public void actionPerformed(ActionEvent ev) { Transform3D oldTransform = getModelRotationTransform(); Transform3D leftRotation = new Transform3D(); int deltaAngle = (ev.getModifiers() & ActionEvent.SHIFT_MASK) == 0 ? -90 : -1; leftRotation.rotY(Math.toRadians(deltaAngle)); leftRotation.mul(oldTransform); updateModelRotation(leftRotation); horizontalAngle = (horizontalAngle + deltaAngle) % 360; orientationToolTip.setTipText(String.format(angleTooltipFormat, horizontalAngle)); verticalAngle = 0; } }); this.turnRightButton = new AutoRepeatButton(new ResourceAction(preferences, ImportedFurnitureWizardStepsPanel.class, "TURN_RIGHT", true) { @Override public void actionPerformed(ActionEvent ev) { Transform3D oldTransform = getModelRotationTransform(); Transform3D rightRotation = new Transform3D(); int deltaAngle = (ev.getModifiers() & ActionEvent.SHIFT_MASK) == 0 ? 90 : 1; rightRotation.rotY(Math.toRadians(deltaAngle)); rightRotation.mul(oldTransform); updateModelRotation(rightRotation); horizontalAngle = (horizontalAngle + deltaAngle) % 360; orientationToolTip.setTipText(String.format(angleTooltipFormat, horizontalAngle)); verticalAngle = 0; } }); this.turnUpButton = new AutoRepeatButton(new ResourceAction(preferences, ImportedFurnitureWizardStepsPanel.class, "TURN_UP", true) { @Override public void actionPerformed(ActionEvent ev) { Transform3D oldTransform = getModelRotationTransform(); Transform3D upRotation = new Transform3D(); int deltaAngle = (ev.getModifiers() & ActionEvent.SHIFT_MASK) == 0 ? -90 : -1; upRotation.rotX(Math.toRadians(deltaAngle)); upRotation.mul(oldTransform); updateModelRotation(upRotation); verticalAngle = (verticalAngle + deltaAngle) % 360; orientationToolTip.setTipText(String.format(angleTooltipFormat, verticalAngle)); horizontalAngle = 0; } }); this.turnDownButton = new AutoRepeatButton(new ResourceAction(preferences, ImportedFurnitureWizardStepsPanel.class, "TURN_DOWN", true) { @Override public void actionPerformed(ActionEvent ev) { Transform3D oldTransform = getModelRotationTransform(); Transform3D downRotation = new Transform3D(); int deltaAngle = (ev.getModifiers() & ActionEvent.SHIFT_MASK) == 0 ? 90 : 1; downRotation.rotX(Math.toRadians(deltaAngle)); downRotation.mul(oldTransform); updateModelRotation(downRotation); verticalAngle = (verticalAngle + deltaAngle) % 360; orientationToolTip.setTipText(String.format(angleTooltipFormat, verticalAngle)); horizontalAngle = 0; } }); this.backFaceShownLabel = new JLabel(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "backFaceShownLabel.text")); this.backFaceShownCheckBox = new JCheckBox(SwingTools.getLocalizedLabelText(preferences, ImportedFurnitureWizardStepsPanel.class, "backFaceShownCheckBox.text")); this.backFaceShownCheckBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent ev) { controller.setBackFaceShown(backFaceShownCheckBox.isSelected()); } }); controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.BACK_FACE_SHOWN, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { // If back face shown changes update back face shown check box backFaceShownCheckBox.setSelected(controller.isBackFaceShown()); } }); this.rotationPreviewComponent = new RotationPreviewComponent(preferences, controller); // Attributes panel components this.attributesLabel = new JLabel(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "attributesLabel.text")); this.nameLabel = new JLabel(SwingTools.getLocalizedLabelText(preferences, ImportedFurnitureWizardStepsPanel.class, "nameLabel.text")); this.nameTextField = new JTextField(10); if (!OperatingSystem.isMacOSXLeopardOrSuperior()) { SwingTools.addAutoSelectionOnFocusGain(this.nameTextField); } DocumentListener nameListener = new DocumentListener() { public void changedUpdate(DocumentEvent ev) { nameTextField.getDocument().removeDocumentListener(this); controller.setName(nameTextField.getText().trim()); nameTextField.getDocument().addDocumentListener(this); } public void insertUpdate(DocumentEvent ev) { changedUpdate(ev); } public void removeUpdate(DocumentEvent ev) { changedUpdate(ev); } }; this.nameTextField.getDocument().addDocumentListener(nameListener); controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.NAME, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { // If name changes update name text field if (!nameTextField.getText().trim().equals(controller.getName())) { nameTextField.setText(controller.getName()); } } }); this.addToCatalogCheckBox = new JCheckBox(SwingTools.getLocalizedLabelText(preferences, ImportedFurnitureWizardStepsPanel.class, "addToCatalogCheckBox.text")); // Propose the add to catalog option only for home furniture import this.addToCatalogCheckBox.setVisible(importHomePiece); this.addToCatalogCheckBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent ev) { if (addToCatalogCheckBox.isSelected()) { categoryComboBox.setEnabled(true); controller.setCategory((FurnitureCategory)categoryComboBox.getSelectedItem()); } else { categoryComboBox.setEnabled(false); controller.setCategory(null); } } }); this.categoryLabel = new JLabel(SwingTools.getLocalizedLabelText(preferences, ImportedFurnitureWizardStepsPanel.class, "categoryLabel.text")); this.categoryComboBox = new JComboBox(preferences.getFurnitureCatalog().getCategories().toArray()); // The piece category isn't enabled by default for home furniture import this.categoryComboBox.setEnabled(!importHomePiece); this.categoryComboBox.setEditable(true); final ComboBoxEditor defaultEditor = this.categoryComboBox.getEditor(); // Change editor to edit category name this.categoryComboBox.setEditor(new ComboBoxEditor() { public Object getItem() { String name = (String)defaultEditor.getItem(); name = name.trim(); // If category is empty, replace it by the last selected item if (name.length() == 0) { setItem(categoryComboBox.getSelectedItem()); } FurnitureCategory category = new FurnitureCategory(name); // Search an existing category List<FurnitureCategory> categories = preferences.getFurnitureCatalog().getCategories(); int categoryIndex = Collections.binarySearch(categories, category); if (categoryIndex >= 0) { return categories.get(categoryIndex); } // If no existing category was found, return a new one return category; } public void setItem(Object value) { if (value != null) { FurnitureCategory category = (FurnitureCategory)value; defaultEditor.setItem(category.getName()); } } public void addActionListener(ActionListener l) { defaultEditor.addActionListener(l); } public Component getEditorComponent() { return defaultEditor.getEditorComponent(); } public void removeActionListener(ActionListener l) { defaultEditor.removeActionListener(l); } public void selectAll() { defaultEditor.selectAll(); } }); this.categoryComboBox.setRenderer(new DefaultListCellRenderer() { public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { FurnitureCategory category = (FurnitureCategory)value; return super.getListCellRendererComponent(list, category.getName(), index, isSelected, cellHasFocus); } }); this.categoryComboBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent ev) { controller.setCategory((FurnitureCategory)ev.getItem()); } }); controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.CATEGORY, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { // If category changes update category combo box FurnitureCategory category = controller.getCategory(); if (category != null) { categoryComboBox.setSelectedItem(category); } } }); if (this.categoryComboBox.getItemCount() > 0) { this.categoryComboBox.setSelectedIndex(0); } this.widthLabel = new JLabel(SwingTools.getLocalizedLabelText(preferences, ImportedFurnitureWizardStepsPanel.class, "widthLabel.text", unitName)); final float minimumLength = preferences.getLengthUnit().getMinimumLength(); final float maximumLength = preferences.getLengthUnit().getMaximumLength(); final NullableSpinner.NullableSpinnerLengthModel widthSpinnerModel = new NullableSpinner.NullableSpinnerLengthModel(preferences, Math.min(controller.getWidth(), minimumLength), maximumLength); this.widthSpinner = new NullableSpinner(widthSpinnerModel); widthSpinnerModel.addChangeListener(new ChangeListener () { public void stateChanged(ChangeEvent ev) { widthSpinnerModel.removeChangeListener(this); // If width spinner value changes update controller controller.setWidth(widthSpinnerModel.getLength()); widthSpinnerModel.addChangeListener(this); } }); controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.WIDTH, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { // If width changes update width spinner widthSpinnerModel.setLength(controller.getWidth()); widthSpinnerModel.setMinimum(Math.min(controller.getWidth(), minimumLength)); } }); this.depthLabel = new JLabel(SwingTools.getLocalizedLabelText(preferences, ImportedFurnitureWizardStepsPanel.class, "depthLabel.text", unitName)); final NullableSpinner.NullableSpinnerLengthModel depthSpinnerModel = new NullableSpinner.NullableSpinnerLengthModel(preferences, Math.min(controller.getDepth(), minimumLength), maximumLength); this.depthSpinner = new NullableSpinner(depthSpinnerModel); depthSpinnerModel.addChangeListener(new ChangeListener () { public void stateChanged(ChangeEvent ev) { depthSpinnerModel.removeChangeListener(this); // If depth spinner value changes update controller controller.setDepth(depthSpinnerModel.getLength()); depthSpinnerModel.addChangeListener(this); } }); controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.DEPTH, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { // If depth changes update depth spinner depthSpinnerModel.setLength(controller.getDepth()); depthSpinnerModel.setMinimum(Math.min(controller.getDepth(), minimumLength)); } }); this.heightLabel = new JLabel(SwingTools.getLocalizedLabelText(preferences, ImportedFurnitureWizardStepsPanel.class, "heightLabel.text", unitName)); final NullableSpinner.NullableSpinnerLengthModel heightSpinnerModel = new NullableSpinner.NullableSpinnerLengthModel(preferences, Math.min(controller.getHeight(), minimumLength), maximumLength); this.heightSpinner = new NullableSpinner(heightSpinnerModel); heightSpinnerModel.addChangeListener(new ChangeListener () { public void stateChanged(ChangeEvent ev) { heightSpinnerModel.removeChangeListener(this); // If width spinner value changes update controller controller.setHeight(heightSpinnerModel.getLength()); heightSpinnerModel.addChangeListener(this); } }); controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.HEIGHT, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { // If height changes update height spinner heightSpinnerModel.setLength(controller.getHeight()); heightSpinnerModel.setMinimum(Math.min(controller.getHeight(), minimumLength)); } }); this.keepProportionsCheckBox = new JCheckBox(SwingTools.getLocalizedLabelText(preferences, ImportedFurnitureWizardStepsPanel.class, "keepProportionsCheckBox.text")); this.keepProportionsCheckBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent ev) { controller.setProportional(keepProportionsCheckBox.isSelected()); } }); controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.PROPORTIONAL, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { // If proportional property changes update keep proportions check box keepProportionsCheckBox.setSelected(controller.isProportional()); } }); this.elevationLabel = new JLabel(SwingTools.getLocalizedLabelText(preferences, ImportedFurnitureWizardStepsPanel.class, "elevationLabel.text", unitName)); final NullableSpinner.NullableSpinnerLengthModel elevationSpinnerModel = new NullableSpinner.NullableSpinnerLengthModel(preferences, 0f, preferences.getLengthUnit().getMaximumElevation()); this.elevationSpinner = new NullableSpinner(elevationSpinnerModel); elevationSpinnerModel.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent ev) { widthSpinnerModel.removeChangeListener(this); controller.setElevation(elevationSpinnerModel.getLength()); widthSpinnerModel.addChangeListener(this); } }); controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.ELEVATION, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { // If elevation changes update elevation spinner elevationSpinnerModel.setLength(controller.getElevation()); } }); this.movableCheckBox = new JCheckBox(SwingTools.getLocalizedLabelText(preferences, ImportedFurnitureWizardStepsPanel.class, "movableCheckBox.text")); this.movableCheckBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent ev) { controller.setMovable(movableCheckBox.isSelected()); } }); controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.MOVABLE, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { // If movable changes update movable check box movableCheckBox.setSelected(controller.isMovable()); } }); this.doorOrWindowCheckBox = new JCheckBox(SwingTools.getLocalizedLabelText(preferences, ImportedFurnitureWizardStepsPanel.class, "doorOrWindowCheckBox.text")); this.doorOrWindowCheckBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent ev) { controller.setDoorOrWindow(doorOrWindowCheckBox.isSelected()); } }); controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.DOOR_OR_WINDOW, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { // If door or window changes update door or window check box boolean doorOrWindow = controller.isDoorOrWindow(); doorOrWindowCheckBox.setSelected(doorOrWindow); movableCheckBox.setEnabled(!doorOrWindow && controller.getStaircaseCutOutShape() == null); } }); this.staircaseCheckBox = new JCheckBox(SwingTools.getLocalizedLabelText(preferences, ImportedFurnitureWizardStepsPanel.class, "staircaseCheckBox.text")); this.staircaseCheckBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent ev) { controller.setStaircaseCutOutShape(staircaseCheckBox.isSelected() ? "M0,0 v1 h1 v-1 z" : null); } }); controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.STAIRCASE_CUT_OUT_SHAPE, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { // If staircase cut out shape changes update its check box String staircaseCutOutShape = controller.getStaircaseCutOutShape(); staircaseCheckBox.setSelected(staircaseCutOutShape != null); movableCheckBox.setEnabled(!controller.isDoorOrWindow() && staircaseCutOutShape == null); } }); this.colorLabel = new JLabel( String.format(SwingTools.getLocalizedLabelText(preferences, ImportedFurnitureWizardStepsPanel.class, "colorLabel.text"), unitName)); this.colorButton = new ColorButton(preferences); this.colorButton.setColorDialogTitle(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "colorDialog.title")); this.colorButton.addPropertyChangeListener(ColorButton.COLOR_PROPERTY, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { controller.setColor(colorButton.getColor()); } }); this.clearColorButton = new JButton(SwingTools.getLocalizedLabelText(preferences, ImportedFurnitureWizardStepsPanel.class, "clearColorButton.text")); this.clearColorButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ev) { controller.setColor(null); } }); this.clearColorButton.setEnabled(false); controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.COLOR, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { // If color changes update color buttons colorButton.setColor(controller.getColor()); clearColorButton.setEnabled(controller.getColor() != null); } }); this.attributesPreviewComponent = new AttributesPreviewComponent(controller); // Icon panel components this.iconLabel = new JLabel(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "iconLabel.text")); this.iconPreviewComponent = new IconPreviewComponent(this.controller); } /** * A button that repeats its action when kept pressed. */ private class AutoRepeatButton extends JButton { private boolean shiftPressed; public AutoRepeatButton(final Action action) { super(action); // Create a timer that will repeat action each 40 ms when SHIFT is pressed final Timer timer = new Timer(40, new ActionListener() { public void actionPerformed(final ActionEvent ev) { action.actionPerformed( new ActionEvent(this, ActionEvent.ACTION_PERFORMED, null, ev.getWhen(), ActionEvent.SHIFT_MASK)); showOrientationToolTip(); } }); timer.setInitialDelay(250); // Update timer when button is armed addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent ev) { EventQueue.invokeLater(new Runnable() { public void run() { if (shiftPressed) { if (getModel().isArmed() && !timer.isRunning()) { timer.restart(); } else if (!getModel().isArmed() && timer.isRunning()) { timer.stop(); } } } }); } }); addMouseListener(new MouseAdapter() { @Override public void mousePressed(final MouseEvent ev) { shiftPressed = ev.isShiftDown(); } @Override public void mouseClicked(final MouseEvent ev) { showOrientationToolTip(); } @Override public void mouseReleased(MouseEvent ev) { new Timer(500, new ActionListener() { public void actionPerformed(final ActionEvent ev) { deleteOrientationToolTip(); ((Timer)ev.getSource()).stop(); } }).start(); } }); } } /** * Shows the orientation tool tip. */ private void showOrientationToolTip() { if (this.orientationToolTipWindow == null) { // Show tool tip in a window (we don't use a Swing Popup because // we require the tool tip window to resize itself depending on the content) this.orientationToolTipWindow = new JWindow(SwingUtilities.getWindowAncestor(this)); this.orientationToolTipWindow.setFocusableWindowState(false); this.orientationToolTipWindow.add(this.orientationToolTip); } else { this.orientationToolTip.revalidate(); } Point point = MouseInfo.getPointerInfo().getLocation(); // Add to point the half of cursor size Dimension cursorSize = getToolkit().getBestCursorSize(16, 16); if (cursorSize.width != 0) { point.x += cursorSize.width + 2; point.y += cursorSize.height + 2; } else { // If custom cursor isn't supported let's consider // default cursor size is 16 pixels wide point.x += 18; point.y += 18; } this.orientationToolTipWindow.setLocation(point); this.orientationToolTipWindow.pack(); this.orientationToolTipWindow.setVisible(true); this.orientationToolTip.paintImmediately(this.orientationToolTip.getBounds()); } /** * Deletes tool tip text window from screen. */ private void deleteOrientationToolTip() { if (this.orientationToolTipWindow != null) { this.orientationToolTipWindow.setVisible(false); } } /** * Sets components mnemonics and label / component associations. */ private void setMnemonics(UserPreferences preferences) { if (!OperatingSystem.isMacOSX()) { if (this.findModelsButton != null) { this.findModelsButton.setMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "findModelsButton.mnemonic")).getKeyCode()); } this.backFaceShownCheckBox.setMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "backFaceShownCheckBox.mnemonic")).getKeyCode()); this.nameLabel.setDisplayedMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "nameLabel.mnemonic")).getKeyCode()); this.nameLabel.setLabelFor(this.nameTextField); this.categoryLabel.setDisplayedMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "categoryLabel.mnemonic")).getKeyCode()); this.categoryLabel.setLabelFor(this.categoryComboBox); this.addToCatalogCheckBox.setMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "addToCatalogCheckBox.mnemonic")).getKeyCode());; this.widthLabel.setDisplayedMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "widthLabel.mnemonic")).getKeyCode()); this.widthLabel.setLabelFor(this.widthSpinner); this.depthLabel.setDisplayedMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "depthLabel.mnemonic")).getKeyCode()); this.depthLabel.setLabelFor(this.depthSpinner); this.heightLabel.setDisplayedMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "heightLabel.mnemonic")).getKeyCode()); this.heightLabel.setLabelFor(this.heightSpinner); this.keepProportionsCheckBox.setMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "keepProportionsCheckBox.mnemonic")).getKeyCode()); this.elevationLabel.setDisplayedMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "elevationLabel.mnemonic")).getKeyCode()); this.elevationLabel.setLabelFor(this.elevationSpinner); this.movableCheckBox.setMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "movableCheckBox.mnemonic")).getKeyCode());; this.doorOrWindowCheckBox.setMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "doorOrWindowCheckBox.mnemonic")).getKeyCode());; this.staircaseCheckBox.setMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "staircaseCheckBox.mnemonic")).getKeyCode());; this.colorLabel.setDisplayedMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "colorLabel.mnemonic")).getKeyCode()); this.colorLabel.setLabelFor(this.colorButton); this.clearColorButton.setMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "clearColorButton.mnemonic")).getKeyCode()); } } /** * Layouts components in 4 panels added to this panel as cards. */ private void layoutComponents() { this.cardLayout = new CardLayout(); setLayout(this.cardLayout); JPanel modelPanel = new JPanel(new GridBagLayout()); modelPanel.add(this.modelChoiceOrChangeLabel, new GridBagConstraints( 0, 0, 2, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(5, 0, 5, 0), 0, 0)); if (this.findModelsButton != null) { modelPanel.add(this.modelChoiceOrChangeButton, new GridBagConstraints( 0, 1, 1, 1, 1, 0, GridBagConstraints.LINE_END, GridBagConstraints.NONE, new Insets(0, 0, 0, 10), 0, 0)); modelPanel.add(this.findModelsButton, new GridBagConstraints( 1, 1, 1, 1, 1, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); } else { modelPanel.add(this.modelChoiceOrChangeButton, new GridBagConstraints( 0, 1, 2, 1, 1, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); } modelPanel.add(this.modelChoiceErrorLabel, new GridBagConstraints( 0, 2, 2, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(5, 0, 0, 0), 0, 0)); modelPanel.add(this.modelPreviewComponent, new GridBagConstraints( 0, 3, 2, 1, 0, 1, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5, 0, 5, 0), 0, 0)); JPanel orientationPanel = new JPanel(new GridBagLayout()); orientationPanel.add(this.orientationLabel, new GridBagConstraints( 0, 0, 2, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(5, 0, 5, 0), 0, 0)); orientationPanel.add(this.rotationPreviewComponent, new GridBagConstraints( 0, 1, 1, 1, 1, 1, GridBagConstraints.LINE_END, GridBagConstraints.NONE, new Insets(0, 0, 5, 15), 0, 0)); JPanel rotationButtonsPanel = new JPanel(new GridBagLayout()) { @Override public void applyComponentOrientation(ComponentOrientation orientation) { // Ignore panel orientation to ensure left button is always at left of panel } }; rotationButtonsPanel.add(this.turnUpButton, new GridBagConstraints( 1, 0, 1, 1, 0, 0, GridBagConstraints.SOUTH, GridBagConstraints.NONE, new Insets(0, 0, 5, 0), 0, 0)); rotationButtonsPanel.add(this.turnLeftButton, new GridBagConstraints( 0, 1, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0)); rotationButtonsPanel.add(this.defaultOrientationButton, new GridBagConstraints( 1, 1, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 5, 0), 0, 0)); rotationButtonsPanel.add(this.turnRightButton, new GridBagConstraints( 2, 1, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 5, 0), 0, 0)); rotationButtonsPanel.add(this.turnDownButton, new GridBagConstraints( 1, 2, 1, 1, 0, 0, GridBagConstraints.NORTH, GridBagConstraints.NONE, new Insets(0, 0, 5, 0), 0, 0)); orientationPanel.add(rotationButtonsPanel, new GridBagConstraints( 1, 1, 1, 1, 1, 1, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 5, 0), 0, 0)); orientationPanel.add(this.backFaceShownLabel, new GridBagConstraints( 0, 4, 2, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(5, 0, 5, 0), 0, 0)); orientationPanel.add(this.backFaceShownCheckBox, new GridBagConstraints( 0, 5, 2, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 5, 0), 0, 0)); JPanel attributesPanel = new JPanel(new GridBagLayout()); attributesPanel.add(this.attributesLabel, new GridBagConstraints( 0, 0, 3, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(5, 0, 5, 0), 0, 0)); attributesPanel.add(this.attributesPreviewComponent, new GridBagConstraints( 0, 1, 1, 13, 1, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0)); attributesPanel.add(this.nameLabel, new GridBagConstraints( 1, 1, 1, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 10, 5), 0, 0)); attributesPanel.add(this.nameTextField, new GridBagConstraints( 2, 1, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 10, 0), 0, 0)); attributesPanel.add(this.addToCatalogCheckBox, new GridBagConstraints( 1, 2, 2, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 10, 0), 0, 0)); attributesPanel.add(this.categoryLabel, new GridBagConstraints( 1, 3, 1, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 10, 5), 0, 0)); attributesPanel.add(this.categoryComboBox, new GridBagConstraints( 2, 3, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 10, 0), 0, 0)); attributesPanel.add(this.widthLabel, new GridBagConstraints( 1, 4, 1, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 10, 5), 0, 0)); attributesPanel.add(this.widthSpinner, new GridBagConstraints( 2, 4, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 10, 0), 0, 0)); attributesPanel.add(this.depthLabel, new GridBagConstraints( 1, 5, 1, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 10, 5), 0, 0)); attributesPanel.add(this.depthSpinner, new GridBagConstraints( 2, 5, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 10, 0), 0, 0)); attributesPanel.add(this.heightLabel, new GridBagConstraints( 1, 6, 1, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 10, 5), 0, 0)); attributesPanel.add(this.heightSpinner, new GridBagConstraints( 2, 6, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 10, 0), 0, 0)); attributesPanel.add(this.keepProportionsCheckBox, new GridBagConstraints( 1, 7, 2, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 15, 0), 0, 0)); attributesPanel.add(this.elevationLabel, new GridBagConstraints( 1, 8, 1, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 10, 5), 0, 0)); attributesPanel.add(this.elevationSpinner, new GridBagConstraints( 2, 8, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 10, 0), 0, 0)); attributesPanel.add(this.movableCheckBox, new GridBagConstraints( 1, 9, 2, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 10, 0), 0, 0)); attributesPanel.add(this.doorOrWindowCheckBox, new GridBagConstraints( 1, 10, 2, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 10, 0), 0, 0)); attributesPanel.add(this.staircaseCheckBox, new GridBagConstraints( 1, 11, 2, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 10, 0), 0, 0)); attributesPanel.add(this.colorLabel, new GridBagConstraints( 1, 12, 1, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0)); attributesPanel.add(this.colorButton, new GridBagConstraints( 2, 12, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 5, 0), 0, 0)); attributesPanel.add(this.clearColorButton, new GridBagConstraints( 2, 13, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 5, 0), 0, 0)); // Add a dummy label to force components to be at top of panel attributesPanel.add(new JLabel(), new GridBagConstraints( 1, 14, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); JPanel iconPanel = new JPanel(new GridBagLayout()); iconPanel.add(this.iconLabel, new GridBagConstraints( 0, 0, 1, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(5, 0, 5, 0), 0, 0)); iconPanel.add(this.iconPreviewComponent, new GridBagConstraints( 0, 1, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5, 0, 5, 0), 0, 0)); add(modelPanel, ImportedFurnitureWizardController.Step.MODEL.name()); add(orientationPanel, ImportedFurnitureWizardController.Step.ROTATION.name()); add(attributesPanel, ImportedFurnitureWizardController.Step.ATTRIBUTES.name()); add(iconPanel, ImportedFurnitureWizardController.Step.ICON.name()); } /** * Switches to the component card matching current step. */ private void updateStep(ImportedFurnitureWizardController controller) { ImportedFurnitureWizardController.Step step = controller.getStep(); this.cardLayout.show(this, step.name()); switch (step) { case MODEL: this.modelChoiceOrChangeButton.requestFocusInWindow(); break; case ATTRIBUTES: this.nameTextField.requestFocusInWindow(); break; } } /** * Returns the transformation matching current model rotation. */ private Transform3D getModelRotationTransform() { float [][] modelRotation = this.controller.getModelRotation(); Matrix3f modelRotationMatrix = new Matrix3f(modelRotation [0][0], modelRotation [0][1], modelRotation [0][2], modelRotation [1][0], modelRotation [1][1], modelRotation [1][2], modelRotation [2][0], modelRotation [2][1], modelRotation [2][2]); Transform3D transform = new Transform3D(); transform.setRotation(modelRotationMatrix); return transform; } /** * Updates model rotation from the values of <code>transform</code>. */ private void updateModelRotation(Transform3D transform) { Matrix3f modelRotationMatrix = new Matrix3f(); transform.getRotationScale(modelRotationMatrix); this.controller.setModelRotation(new float [][] {{modelRotationMatrix.m00, modelRotationMatrix.m01, modelRotationMatrix.m02}, {modelRotationMatrix.m10, modelRotationMatrix.m11, modelRotationMatrix.m12}, {modelRotationMatrix.m20, modelRotationMatrix.m21, modelRotationMatrix.m22}}); } /** * Updates controller initial values from <code>piece</code>. */ private void updateController(final CatalogPieceOfFurniture piece, final UserPreferences preferences) { updatePreviewComponentsModel(null); if (piece == null) { setModelChoiceTexts(preferences); } else { setModelChangeTexts(preferences); setReadingState(); // Load piece model asynchronously ModelManager.getInstance().loadModel(piece.getModel(), new ModelManager.ModelObserver() { public void modelUpdated(BranchGroup modelRoot) { updatePreviewComponentsModel(piece.getModel()); setDefaultState(); controller.setModel(piece.getModel()); controller.setModelRotation(piece.getModelRotation()); controller.setBackFaceShown(piece.isBackFaceShown()); controller.setName(piece.getName()); controller.setCategory(piece.getCategory()); controller.setWidth(piece.getWidth()); controller.setDepth(piece.getDepth()); controller.setHeight(piece.getHeight()); controller.setMovable(piece.isMovable()); controller.setDoorOrWindow(piece.isDoorOrWindow()); controller.setElevation(piece.getElevation()); controller.setColor(piece.getColor()); controller.setIconYaw(piece.getIconYaw()); controller.setProportional(piece.isProportional()); } public void modelError(Exception ex) { controller.setModel(null); setModelChoiceTexts(preferences); modelChoiceErrorLabel.setVisible(true); if (isShowing()) { SwingUtilities.getWindowAncestor(modelChoiceErrorLabel).pack(); } setDefaultState(); } }); } } /** * Reads model from <code>modelName</code> and updates controller values. */ private void updateController(final String modelName, final UserPreferences preferences, final ContentManager contentManager, final FurnitureCategory defaultCategory, final boolean ignoreException) { // Cancel current model this.controller.setModel(null); updatePreviewComponentsModel(null); setReadingState(); // Read model in modelLoader executor this.modelLoader.execute(new Runnable() { public void run() { Content modelContent = null; try { modelContent = contentManager.getContent(modelName); } catch (RecorderException ex) { setDefaultStateAndShowModelChoiceError(modelName, preferences, !ignoreException); } try { BranchGroup model = ModelManager.getInstance().loadModel(modelContent); final Vector3f modelSize = ModelManager.getInstance().getSize(model); // Copy model to a temporary OBJ content with materials and textures final Content copiedContent = copyToTemporaryOBJContent(model, modelName); EventQueue.invokeLater(new Runnable() { public void run() { // Load copied content using cache to make it accessible by preview components ModelManager.getInstance().loadModel(copiedContent, new ModelManager.ModelObserver() { public void modelUpdated(BranchGroup modelRoot) { setDefaultStateAndInitializeReadModel(copiedContent, modelName, defaultCategory, modelSize, preferences, contentManager); } public void modelError(Exception ex) { setDefaultStateAndShowModelChoiceError(modelName, preferences, !ignoreException); } }); } }); return; } catch (IllegalArgumentException ex) { // Thrown by getSize if model is empty } catch (IOException ex) { // Try with zipped content } try { // Copy model content to a temporary content modelContent = TemporaryURLContent.copyToTemporaryURLContent(modelContent); } catch (IOException ex) { setDefaultStateAndShowModelChoiceError(modelName, preferences, !ignoreException); return; } // If content couldn't be loaded, try to load model as a zipped file ZipInputStream zipIn = null; try { URLContent urlContent = (URLContent)modelContent; // Open zipped stream zipIn = new ZipInputStream(urlContent.openStream()); // Parse entries to see if one is readable for (ZipEntry entry; (entry = zipIn.getNextEntry()) != null; ) { String entryName = entry.getName(); // Ignore directory entries and entries starting by a dot if (!entryName.endsWith("/")) { int slashIndex = entryName.lastIndexOf('/'); String entryFileName = entryName.substring(++slashIndex); if (!entryFileName.startsWith(".")) { URL entryUrl = new URL("jar:" + urlContent.getURL() + "!/" + URLEncoder.encode(entryName, "UTF-8").replace("+", "%20").replace("%2F", "/")); final Content entryContent = new TemporaryURLContent(entryUrl); final AtomicBoolean loaded = new AtomicBoolean(); final CountDownLatch latch = new CountDownLatch(1); EventQueue.invokeAndWait(new Runnable() { public void run() { // Load content using cache to make it accessible by preview components ModelManager.getInstance().loadModel(entryContent, new ModelManager.ModelObserver() { public void modelUpdated(BranchGroup modelRoot) { try { final Vector3f modelSize = ModelManager.getInstance().getSize(modelRoot); setDefaultStateAndInitializeReadModel(entryContent, modelName, defaultCategory, modelSize, preferences, contentManager); loaded.set(true); } catch (IllegalArgumentException ex) { // Thrown by getSize if model is empty } latch.countDown(); } public void modelError(Exception ex) { latch.countDown(); } }); } }); latch.await(); if (loaded.get()) { return; } } } } } catch (IOException ex) { setDefaultStateAndShowModelChoiceError(modelName, preferences, !ignoreException); return; } catch (InterruptedException ex) { setDefaultState(); return; } catch (InvocationTargetException ex) { // Show next message } finally { try { if (zipIn != null) { zipIn.close(); } } catch (IOException ex) { // Ignore close exception } } // Found no readable model EventQueue.invokeLater(new Runnable() { public void run() { if (isShowing()) { setDefaultState(); setModelChoiceTexts(preferences); JOptionPane.showMessageDialog(SwingUtilities.getRootPane(ImportedFurnitureWizardStepsPanel.this), preferences.getLocalizedString(ImportedFurnitureWizardStepsPanel.class, "modelChoiceFormatError")); } } }); } }); } /** * Restores default state and initializes read model. */ private void setDefaultStateAndInitializeReadModel(final Content modelContent, final String modelName, final FurnitureCategory defaultCategory, final Vector3f modelSize, final UserPreferences preferences, final ContentManager contentManager) { setDefaultState(); updatePreviewComponentsModel(modelContent); controller.setModel(modelContent); setModelChangeTexts(preferences); modelChoiceErrorLabel.setVisible(false); controller.setModelRotation(new float [][] {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}); controller.setBackFaceShown(false); controller.setName(contentManager.getPresentationName( modelName, ContentManager.ContentType.MODEL)); controller.setCategory(defaultCategory); // Initialize size with default values controller.setWidth(modelSize.x); controller.setDepth(modelSize.z); controller.setHeight(modelSize.y); controller.setMovable(true); controller.setDoorOrWindow(false); controller.setColor(null); controller.setIconYaw((float)Math.PI / 8); controller.setProportional(true); } /** * Restores default state and shows an error message about the chosen model. */ private void setDefaultStateAndShowModelChoiceError(final String modelName, final UserPreferences preferences, boolean showError) { setDefaultState(); if (showError) { EventQueue.invokeLater(new Runnable() { public void run() { JOptionPane.showMessageDialog(SwingUtilities.getRootPane(ImportedFurnitureWizardStepsPanel.this), preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "modelChoiceError", modelName)); } }); } } /** * Returns a copy of a given <code>model</code> as a zip content at OBJ format. * Caution : this method must be thread safe because it can be called from model loader executor. */ private Content copyToTemporaryOBJContent(BranchGroup model, String modelName) throws IOException { try { setReadingState(); String objFile = new File(modelName).getName(); if (!objFile.toLowerCase().endsWith(".obj")) { objFile += ".obj"; } // Ensure the file contains only letters, figures, underscores, dots, hyphens or spaces if (objFile.matches(".*[^a-zA-Z0-9_\\.\\-\\ ].*")) { objFile = "model.obj"; } File tempZipFile = OperatingSystem.createTemporaryFile("import", ".zip"); OBJWriter.writeNodeInZIPFile(model, tempZipFile, 0, objFile, "3D model import " + modelName); return new TemporaryURLContent(new URL("jar:" + tempZipFile.toURI().toURL() + "!/" + URLEncoder.encode(objFile, "UTF-8").replace("+", "%20"))); } finally { setDefaultState(); } } /** * Sets the cursor to wait cursor and disables model choice button. */ private void setReadingState() { this.modelChoiceOrChangeButton.setEnabled(false); Component rootPane = SwingUtilities.getRoot(ImportedFurnitureWizardStepsPanel.this); if (rootPane != null) { if (this.defaultCursor == null) { this.defaultCursor = rootPane.getCursor(); } rootPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); } else { addAncestorListener(new AncestorListener() { public void ancestorAdded(AncestorEvent event) { removeAncestorListener(this); if (!modelChoiceOrChangeButton.isEnabled()) { setReadingState(); } } public void ancestorRemoved(AncestorEvent event) { } public void ancestorMoved(AncestorEvent event) { } }); } } /** * Sets the default cursor and enables model choice button. */ private void setDefaultState() { if (EventQueue.isDispatchThread()) { this.modelChoiceOrChangeButton.setEnabled(true); Component rootPane = SwingUtilities.getRoot(ImportedFurnitureWizardStepsPanel.this); if (rootPane != null) { rootPane.setCursor(this.defaultCursor); } } else { EventQueue.invokeLater(new Runnable() { public void run() { setDefaultState(); } }); } } /** * Updates the model displayed by preview components. */ private void updatePreviewComponentsModel(final Content model) { modelPreviewComponent.setModel(model); rotationPreviewComponent.setModel(model); attributesPreviewComponent.setModel(model); iconPreviewComponent.setModel(model); } /** * Sets the texts of label and button of model panel with change texts. */ private void setModelChangeTexts(UserPreferences preferences) { this.modelChoiceOrChangeLabel.setText(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "modelChangeLabel.text")); this.modelChoiceOrChangeButton.setText(SwingTools.getLocalizedLabelText(preferences, ImportedFurnitureWizardStepsPanel.class, "modelChangeButton.text")); if (!OperatingSystem.isMacOSX()) { this.modelChoiceOrChangeButton.setMnemonic( KeyStroke.getKeyStroke(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "modelChangeButton.mnemonic")).getKeyCode()); } } /** * Sets the texts of label and button of model panel with choice texts. */ private void setModelChoiceTexts(UserPreferences preferences) { this.modelChoiceOrChangeLabel.setText(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "modelChoiceLabel.text")); this.modelChoiceOrChangeButton.setText(SwingTools.getLocalizedLabelText(preferences, ImportedFurnitureWizardStepsPanel.class, "modelChoiceButton.text")); if (!OperatingSystem.isMacOSX()) { this.modelChoiceOrChangeButton.setMnemonic( KeyStroke.getKeyStroke(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "modelChoiceButton.mnemonic")).getKeyCode()); } } /** * Returns the model name chosen for a file chooser dialog. */ private String showModelChoiceDialog(UserPreferences preferences, ContentManager contentManager) { return contentManager.showOpenDialog(this, preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "modelChoiceDialog.title"), ContentManager.ContentType.MODEL); } /** * Returns the icon content of the chosen piece. * Icon is created once on demand of view's controller, because it demands either * icon panel being displayed, or an offscreen 3D buffer that costs too much to create at each yaw change. */ public Content getIcon() { try { return this.iconPreviewComponent.getIcon(400); } catch (IOException ex) { try { return new URLContent(new URL("file:/dummySweetHome3DContent")); } catch (MalformedURLException ex1) { return null; } } } /** * Preview component for model changes. */ private static abstract class AbstractModelPreviewComponent extends ModelPreviewComponent { private BranchGroup modelNode; public AbstractModelPreviewComponent(boolean pitchAndScaleChangeSupported) { super(pitchAndScaleChangeSupported); } /** * Adds listeners to <code>controller</code> to update the rotation of the piece model * displayed by this component. */ protected void addRotationListener(final ImportedFurnitureWizardController controller) { controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.BACK_FACE_SHOWN, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { setBackFaceShown(controller.isBackFaceShown()); } }); controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.MODEL, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { modelNode = null; } }); PropertyChangeListener rotationChangeListener = new PropertyChangeListener () { public void propertyChange(final PropertyChangeEvent ev) { setModelRotation(controller.getModelRotation()); if (ev.getOldValue() != null && getModel() != null) { // Update size when a new rotation is provided if (modelNode == null) { ModelManager.getInstance().loadModel(getModel(), new ModelManager.ModelObserver() { public void modelUpdated(BranchGroup modelRoot) { modelNode = modelRoot; updateSize(controller, (float [][])ev.getOldValue(), (float [][])ev.getNewValue()); } public void modelError(Exception ex) { } }); } else { updateSize(controller, (float [][])ev.getOldValue(), (float [][])ev.getNewValue()); } } } private void updateSize(final ImportedFurnitureWizardController controller, float [][] oldModelRotation, float [][] newModelRotation) { try { Transform3D normalization = ModelManager.getInstance().getNormalizedTransform(modelNode, oldModelRotation, 1f); Transform3D scaleTransform = new Transform3D(); scaleTransform.setScale(new Vector3d(controller.getWidth(), controller.getHeight(), controller.getDepth())); scaleTransform.mul(normalization); // Compute rotation before old model rotation Matrix3f oldModelRotationMatrix = new Matrix3f(oldModelRotation [0][0], oldModelRotation [0][1], oldModelRotation [0][2], oldModelRotation [1][0], oldModelRotation [1][1], oldModelRotation [1][2], oldModelRotation [2][0], oldModelRotation [2][1], oldModelRotation [2][2]); oldModelRotationMatrix.invert(); Transform3D backRotationTransform = new Transform3D(); backRotationTransform.setRotation(oldModelRotationMatrix); backRotationTransform.mul(scaleTransform); // Compute size after new model rotation Matrix3f newModelRotationMatrix = new Matrix3f(newModelRotation [0][0], newModelRotation [0][1], newModelRotation [0][2], newModelRotation [1][0], newModelRotation [1][1], newModelRotation [1][2], newModelRotation [2][0], newModelRotation [2][1], newModelRotation [2][2]); Transform3D transform = new Transform3D(); transform.setRotation(newModelRotationMatrix); transform.mul(backRotationTransform); Vector3f newSize = ModelManager.getInstance().getSize(modelNode, transform); controller.setWidth(newSize.x); controller.setHeight(newSize.y); controller.setDepth(newSize.z); } catch (IllegalArgumentException ex) { // Model is empty } } }; controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.MODEL_ROTATION, rotationChangeListener); } /** * Adds listeners to <code>controller</code> to update the rotation and the size of the piece model * displayed by this component. */ protected void addSizeListeners(final ImportedFurnitureWizardController controller) { controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.BACK_FACE_SHOWN, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { setBackFaceShown(controller.isBackFaceShown()); } }); PropertyChangeListener sizeChangeListener = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { setModelRotationAndSize(controller.getModelRotation(), controller.getWidth(), controller.getDepth(), controller.getHeight()); } }; controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.MODEL_ROTATION, sizeChangeListener); controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.WIDTH, sizeChangeListener); controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.DEPTH, sizeChangeListener); controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.HEIGHT, sizeChangeListener); } /** * Adds listener to <code>controller</code> to update the color of the piece * displayed by this component. */ protected void addColorListener(final ImportedFurnitureWizardController controller) { PropertyChangeListener colorChangeListener = new PropertyChangeListener () { public void propertyChange(PropertyChangeEvent ev) { setModelColor(controller.getColor()); } }; controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.COLOR, colorChangeListener); } /** * Adds listener to <code>controller</code> to update the yaw of the piece icon * displayed by this component. */ protected void addIconYawListener(final ImportedFurnitureWizardController controller) { PropertyChangeListener iconYawChangeListener = new PropertyChangeListener () { public void propertyChange(PropertyChangeEvent ev) { setViewYaw(controller.getIconYaw()); } }; controller.addPropertyChangeListener(ImportedFurnitureWizardController.Property.ICON_YAW, iconYawChangeListener); } } /** * Preview component for model orientation. */ private static class RotationPreviewComponent extends AbstractModelPreviewComponent { private JLabel frontViewLabel; private JPanel frontViewPanel; private Component frontViewComponent3D; private JLabel sideViewLabel; private JPanel sideViewPanel; private Component sideViewComponent3D; private JLabel topViewLabel; private JPanel topViewPanel; private Component topViewComponent3D; private JLabel perspectiveViewLabel; public RotationPreviewComponent(UserPreferences preferences, final ImportedFurnitureWizardController controller) { super(true); addRotationListener(controller); createComponents(preferences); layoutComponents(); GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment(); if (graphicsEnvironment.getScreenDevices().length == 1) { // If only one screen device is available, create components 3D immediately, // otherwise create it once the screen device of the parent is known createOtherViewsComponent3D(graphicsEnvironment.getDefaultScreenDevice().getDefaultConfiguration()); } } /** * Creates components displayed by this panel. */ private void createComponents(UserPreferences preferences) { this.frontViewLabel = new JLabel(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "frontViewLabel.text")); this.frontViewPanel = new JPanel(new GridLayout()); this.sideViewLabel = new JLabel(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "sideViewLabel.text")); this.sideViewPanel = new JPanel(new GridLayout()); this.topViewLabel = new JLabel(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "topViewLabel.text")); this.topViewPanel = new JPanel(new GridLayout()); this.perspectiveViewLabel = new JLabel(preferences.getLocalizedString( ImportedFurnitureWizardStepsPanel.class, "perspectiveViewLabel.text")); setBorder(null); // Add a hierarchy listener to link canvases to universe once this component is made visible addAncestorListener(); } /** * Creates the 3D components for front, side and top view. */ private void createOtherViewsComponent3D(GraphicsConfiguration configuration) { frontViewComponent3D = getComponent3D(configuration); Color backgroundColor = new Color(0xE5E5E5); frontViewComponent3D.setBackground(backgroundColor); frontViewPanel.add(frontViewComponent3D); sideViewComponent3D = getComponent3D(configuration); sideViewComponent3D.setBackground(backgroundColor); sideViewPanel.add(sideViewComponent3D); topViewComponent3D = getComponent3D(configuration); topViewComponent3D.setBackground(backgroundColor); topViewPanel.add(topViewComponent3D); } /** * Returns a component 3D depending on offscreen view being required or not. */ private Component getComponent3D(GraphicsConfiguration configuration) { if (Boolean.valueOf(System.getProperty("com.eteks.sweethome3d.j3d.useOffScreen3DView", "false"))) { GraphicsConfigTemplate3D gc = new GraphicsConfigTemplate3D(); gc.setSceneAntialiasing(GraphicsConfigTemplate3D.PREFERRED); try { // Instantiate JCanvas3D inner class by reflection to be able to run under Java 3D 1.3 return (Component)Class.forName("com.sun.j3d.exp.swing.JCanvas3D"). getConstructor(GraphicsConfigTemplate3D.class).newInstance(gc); } catch (ClassNotFoundException ex) { throw new UnsupportedOperationException("Java 3D 1.5 required to display an offscreen 3D view"); } catch (Exception ex) { UnsupportedOperationException ex2 = new UnsupportedOperationException(); ex2.initCause(ex); throw ex2; } } else { return Component3DManager.getInstance().getOnscreenCanvas3D(configuration, null); } } @Override public Dimension getPreferredSize() { return new Dimension(200, 204 + this.frontViewLabel.getPreferredSize().height * 2); } /** * Adds an ancestor listener to this component to manage canvases attachment to universe. */ private void addAncestorListener() { addAncestorListener(new AncestorListener() { public void ancestorAdded(AncestorEvent ev) { if (frontViewComponent3D == null) { createOtherViewsComponent3D(ev.getAncestor().getGraphicsConfiguration()); } EventQueue.invokeLater(new Runnable() { public void run() { // Attach the 3 other canvases to super class universe with their own view once main view is attached createView(0, 0, 1, View.PARALLEL_PROJECTION).addCanvas3D(getCanvas3D(frontViewComponent3D)); createView(Locale.getDefault().equals(Locale.US) ? -(float)Math.PI / 2 : (float)Math.PI / 2, 0, 1, View.PARALLEL_PROJECTION).addCanvas3D(getCanvas3D(sideViewComponent3D)); createView(0, -(float)Math.PI / 2, 1, View.PARALLEL_PROJECTION).addCanvas3D(getCanvas3D(topViewComponent3D)); revalidate(); repaint(); } }); } public void ancestorRemoved(AncestorEvent ev) { // Detach the 3 canvases from their view getCanvas3D(frontViewComponent3D).getView().removeCanvas3D(getCanvas3D(frontViewComponent3D)); getCanvas3D(sideViewComponent3D).getView().removeCanvas3D(getCanvas3D(sideViewComponent3D)); getCanvas3D(topViewComponent3D).getView().removeCanvas3D(getCanvas3D(topViewComponent3D)); // Super class did the remaining of clean up } public void ancestorMoved(AncestorEvent ev) { } }); } /** * Returns the <code>Canvas3D</code> instance associated to the given component. */ private Canvas3D getCanvas3D(Component component3D) { if (component3D instanceof Canvas3D) { return (Canvas3D)component3D; } else { try { // Call JCanvas3D#getOffscreenCanvas3D by reflection to be able to run under Java 3D 1.3 return (Canvas3D)Class.forName("com.sun.j3d.exp.swing.JCanvas3D").getMethod("getOffscreenCanvas3D").invoke(component3D); } catch (Exception ex) { UnsupportedOperationException ex2 = new UnsupportedOperationException(); ex2.initCause(ex); throw ex2; } } } /** * Returns a bordered panel that includes <code>component3D</code>. */ private JPanel getComponent3DBorderedPanel(Component component3D) { JPanel panel = new JPanel(new GridLayout(1, 1)); panel.add(component3D); component3D.setFocusable(false); panel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); return panel; } /** * Layouts components. */ private void layoutComponents() { // Remove default component 3D to put it in another place JComponent defaultComponent3D = getComponent3D(); remove(defaultComponent3D); setLayout(new GridBagLayout()); // Place the 4 3D components differently depending on US or other country if (Locale.getDefault().equals(Locale.US)) { // Default projection view at top left add(this.perspectiveViewLabel, new GridBagConstraints( 0, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 2, 5), 0, 0)); add(getComponent3DBorderedPanel(defaultComponent3D), new GridBagConstraints( 0, 1, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 5, 5), 0, 0)); // Top view at top right add(this.topViewLabel, new GridBagConstraints( 1, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 2, 0), 0, 0)); add(getComponent3DBorderedPanel(this.topViewPanel), new GridBagConstraints( 1, 1, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 5, 0), 0, 0)); // Left view at bottom left add(this.sideViewLabel, new GridBagConstraints( 0, 2, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 2, 5), 0, 0)); add(getComponent3DBorderedPanel(this.sideViewPanel), new GridBagConstraints( 0, 3, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 5), 0, 0)); // Front view at bottom right add(this.frontViewLabel, new GridBagConstraints( 1, 2, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 2, 0), 0, 0)); add(getComponent3DBorderedPanel(this.frontViewPanel), new GridBagConstraints( 1, 3, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); } else { // Right view at top left add(this.sideViewLabel, new GridBagConstraints( 0, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 2, 5), 0, 0)); add(getComponent3DBorderedPanel(this.sideViewPanel), new GridBagConstraints( 0, 1, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 5, 5), 0, 0)); // Front view at top right add(this.frontViewLabel, new GridBagConstraints( 1, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 2, 0), 0, 0)); add(getComponent3DBorderedPanel(this.frontViewPanel), new GridBagConstraints( 1, 1, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 5, 0), 0, 0)); // Default projection view at bottom left add(this.perspectiveViewLabel, new GridBagConstraints( 0, 2, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 2, 5), 0, 0)); add(getComponent3DBorderedPanel(defaultComponent3D), new GridBagConstraints( 0, 3, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 5), 0, 0)); // Top view at bottom right add(this.topViewLabel, new GridBagConstraints( 1, 2, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 2, 0), 0, 0)); add(getComponent3DBorderedPanel(this.topViewPanel), new GridBagConstraints( 1, 3, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); } } } /** * Preview component for model attributes. */ private static class AttributesPreviewComponent extends AbstractModelPreviewComponent { public AttributesPreviewComponent(ImportedFurnitureWizardController controller) { super(true); addSizeListeners(controller); addColorListener(controller); } } /** * Preview component for model icon. */ private static class IconPreviewComponent extends AbstractModelPreviewComponent { private ImportedFurnitureWizardController controller; public IconPreviewComponent(ImportedFurnitureWizardController controller) { super(false); this.controller = controller; addSizeListeners(controller); addColorListener(controller); addIconYawListener(controller); setBackground(UIManager.getColor("window")); } @Override public Dimension getPreferredSize() { Insets insets = getInsets(); return new Dimension(128 + insets.left + insets.right, 128 + insets.top + insets.bottom); } /** * Sets the <code>yaw</code> angle used by view platform transform. */ protected void setViewYaw(float viewYaw) { super.setViewYaw(viewYaw); this.controller.setIconYaw(viewYaw); } } }