1 /*
2  * ImportedFurnitureWizardStepsController.java 4 juil. 07
3  *
4  * Sweet Home 3D, Copyright (c) 2007 Emmanuel PUYBARET / eTeks <info@eteks.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 package com.eteks.sweethome3d.viewcontroller;
21 
22 import java.beans.PropertyChangeEvent;
23 import java.beans.PropertyChangeListener;
24 import java.beans.PropertyChangeSupport;
25 import java.net.URL;
26 import java.util.Arrays;
27 import java.util.List;
28 
29 import javax.swing.undo.CannotRedoException;
30 import javax.swing.undo.CannotUndoException;
31 import javax.swing.undo.UndoableEdit;
32 import javax.swing.undo.UndoableEditSupport;
33 
34 import com.eteks.sweethome3d.model.CatalogDoorOrWindow;
35 import com.eteks.sweethome3d.model.CatalogPieceOfFurniture;
36 import com.eteks.sweethome3d.model.Content;
37 import com.eteks.sweethome3d.model.FurnitureCatalog;
38 import com.eteks.sweethome3d.model.FurnitureCategory;
39 import com.eteks.sweethome3d.model.Home;
40 import com.eteks.sweethome3d.model.HomePieceOfFurniture;
41 import com.eteks.sweethome3d.model.Sash;
42 import com.eteks.sweethome3d.model.Selectable;
43 import com.eteks.sweethome3d.model.UserPreferences;
44 
45 /**
46  * Wizard controller to manage furniture importation.
47  * @author Emmanuel Puybaret
48  */
49 public class ImportedFurnitureWizardController extends WizardController
50                                                implements Controller {
51   public enum Property {STEP, NAME, CREATOR, MODEL, WIDTH, DEPTH, HEIGHT, ELEVATION, MOVABLE,
52       DOOR_OR_WINDOW, COLOR, CATEGORY, BACK_FACE_SHOWN, MODEL_SIZE, MODEL_ROTATION, STAIRCASE_CUT_OUT_SHAPE,
53       ICON_YAW, PROPORTIONAL}
54 
55   public enum Step {MODEL, ROTATION, ATTRIBUTES, ICON};
56 
57   private final Home                             home;
58   private final CatalogPieceOfFurniture          piece;
59   private final String                           modelName;
60   private final UserPreferences                  preferences;
61   private final FurnitureController              furnitureController;
62   private final ContentManager                   contentManager;
63   private final UndoableEditSupport              undoSupport;
64   private final PropertyChangeSupport            propertyChangeSupport;
65 
66   private final ImportedFurnitureWizardStepState furnitureModelStepState;
67   private final ImportedFurnitureWizardStepState furnitureOrientationStepState;
68   private final ImportedFurnitureWizardStepState furnitureAttributesStepState;
69   private final ImportedFurnitureWizardStepState furnitureIconStepState;
70   private ImportedFurnitureWizardStepsView       stepsView;
71 
72   private Step                             step;
73   private String                           name;
74   private String                           creator;
75   private Content                          model;
76   private float                            width;
77   private float                            proportionalWidth;
78   private float                            depth;
79   private float                            proportionalDepth;
80   private float                            height;
81   private float                            proportionalHeight;
82   private float                            elevation;
83   private boolean                          movable;
84   private boolean                          doorOrWindow;
85   private String                           staircaseCutOutShape;
86   private Integer                          color;
87   private FurnitureCategory                category;
88   private boolean                          backFaceShown;
89   private long                             modelSize;
90   private float [][]                       modelRotation;
91   private float                            iconYaw;
92   private boolean                          proportional;
93   private final ViewFactory viewFactory;
94 
95   /**
96    * Creates a controller that edits a new catalog piece of furniture.
97    */
ImportedFurnitureWizardController(UserPreferences preferences, ViewFactory viewFactory, ContentManager contentManager)98   public ImportedFurnitureWizardController(UserPreferences preferences,
99                                            ViewFactory    viewFactory,
100                                            ContentManager contentManager) {
101     this(null, null, null, preferences, null, viewFactory, contentManager, null);
102   }
103 
104   /**
105    * Creates a controller that edits a new catalog piece of furniture with a given
106    * <code>modelName</code>.
107    */
ImportedFurnitureWizardController(String modelName, UserPreferences preferences, ViewFactory viewFactory, ContentManager contentManager)108   public ImportedFurnitureWizardController(String modelName,
109                                            UserPreferences preferences,
110                                            ViewFactory    viewFactory,
111                                            ContentManager contentManager) {
112     this(null, null, modelName, preferences, null, viewFactory, contentManager, null);
113   }
114 
115   /**
116    * Creates a controller that edits <code>piece</code> values.
117    */
ImportedFurnitureWizardController(CatalogPieceOfFurniture piece, UserPreferences preferences, ViewFactory viewFactory, ContentManager contentManager)118   public ImportedFurnitureWizardController(CatalogPieceOfFurniture piece,
119                                            UserPreferences preferences,
120                                            ViewFactory    viewFactory,
121                                            ContentManager contentManager) {
122     this(null, piece, null, preferences, null, viewFactory, contentManager, null);
123   }
124 
125   /**
126    * Creates a controller that edits a new imported home piece of furniture.
127    */
ImportedFurnitureWizardController(Home home, UserPreferences preferences, FurnitureController furnitureController, ViewFactory viewFactory, ContentManager contentManager, UndoableEditSupport undoSupport)128   public ImportedFurnitureWizardController(Home home,
129                                            UserPreferences preferences,
130                                            FurnitureController furnitureController,
131                                            ViewFactory    viewFactory,
132                                            ContentManager contentManager,
133                                            UndoableEditSupport undoSupport) {
134     this(home, null, null, preferences, furnitureController, viewFactory, contentManager, undoSupport);
135   }
136 
137   /**
138    * Creates a controller that edits a new imported home piece of furniture
139    * with a given <code>modelName</code>.
140    */
ImportedFurnitureWizardController(Home home, String modelName, UserPreferences preferences, FurnitureController furnitureController, ViewFactory viewFactory, ContentManager contentManager, UndoableEditSupport undoSupport)141   public ImportedFurnitureWizardController(Home home,
142                                            String modelName,
143                                            UserPreferences preferences,
144                                            FurnitureController furnitureController,
145                                            ViewFactory    viewFactory,
146                                            ContentManager contentManager,
147                                            UndoableEditSupport undoSupport) {
148     this(home, null, modelName, preferences, furnitureController, viewFactory, contentManager, undoSupport);
149   }
150 
151   /**
152    * Creates a controller that edits <code>piece</code> values.
153    */
ImportedFurnitureWizardController(Home home, CatalogPieceOfFurniture piece, String modelName, UserPreferences preferences, FurnitureController furnitureController, ViewFactory viewFactory, ContentManager contentManager, UndoableEditSupport undoSupport)154   private ImportedFurnitureWizardController(Home home,
155                                             CatalogPieceOfFurniture piece,
156                                             String modelName,
157                                             UserPreferences preferences,
158                                             FurnitureController furnitureController,
159                                             ViewFactory    viewFactory,
160                                             ContentManager contentManager,
161                                             UndoableEditSupport undoSupport) {
162     super(preferences, viewFactory);
163     this.home = home;
164     this.piece = piece;
165     this.modelName = modelName;
166     this.preferences = preferences;
167     this.furnitureController = furnitureController;
168     this.viewFactory = viewFactory;
169     this.undoSupport = undoSupport;
170     this.contentManager = contentManager;
171     this.propertyChangeSupport = new PropertyChangeSupport(this);
172     setTitle(this.preferences.getLocalizedString(ImportedFurnitureWizardController.class,
173         piece == null
174             ? "importFurnitureWizard.title"
175             : "modifyFurnitureWizard.title"));
176     // Initialize states
177     this.furnitureModelStepState = new FurnitureModelStepState();
178     this.furnitureOrientationStepState = new FurnitureOrientationStepState();
179     this.furnitureAttributesStepState = new FurnitureAttributesStepState();
180     this.furnitureIconStepState = new FurnitureIconStepState();
181     setStepState(this.furnitureModelStepState);
182   }
183 
184   /**
185    * Imports piece in catalog and/or home and posts an undoable operation.
186    */
187   @Override
finish()188   public void finish() {
189     CatalogPieceOfFurniture newPiece;
190     if (isDoorOrWindow()) {
191       newPiece = new CatalogDoorOrWindow(getName(), getIcon(), getModel(),
192           getWidth(), getDepth(), getHeight(), getElevation(),
193           isMovable(), 1, 0, new Sash [0], getColor(),
194           getModelRotation(), isBackFaceShown(), getModelSize(), getCreator(),
195           getIconYaw(), isProportional());
196     } else {
197       newPiece = new CatalogPieceOfFurniture(getName(), getIcon(), getModel(), getWidth(),
198           getDepth(), getHeight(), getElevation(), isMovable(),
199           getStaircaseCutOutShape(), getColor(),
200           getModelRotation(), isBackFaceShown(), getModelSize(), getCreator(),
201           getIconYaw(), isProportional());
202     }
203 
204     if (this.home != null) {
205       // Add new piece to home
206       addPieceOfFurniture(this.furnitureController.createHomePieceOfFurniture(newPiece));
207     }
208     // Remove the edited piece from catalog
209     FurnitureCatalog catalog = this.preferences.getFurnitureCatalog();
210     if (this.piece != null) {
211       catalog.delete(this.piece);
212     }
213     // If a category exists, add new piece to catalog
214     if (this.category != null) {
215       catalog.add(this.category, newPiece);
216     }
217   }
218 
219   /**
220    * Controls new piece added to home.
221    * Once added the furniture will be selected in view
222    * and undo support will receive a new undoable edit.
223    * @param piece the piece of furniture to add.
224    */
addPieceOfFurniture(HomePieceOfFurniture piece)225   public void addPieceOfFurniture(HomePieceOfFurniture piece) {
226     boolean basePlanLocked = this.home.isBasePlanLocked();
227     boolean allLevelsSelection = this.home.isAllLevelsSelection();
228     List<Selectable> oldSelection = this.home.getSelectedItems();
229     // Get index of the piece added to home
230     int pieceIndex = this.home.getFurniture().size();
231 
232     this.home.addPieceOfFurniture(piece, pieceIndex);
233     this.home.setSelectedItems(Arrays.asList(piece));
234     if (!piece.isMovable() && basePlanLocked) {
235       this.home.setBasePlanLocked(false);
236     }
237     this.home.setAllLevelsSelection(false);
238     if (this.undoSupport != null) {
239       UndoableEdit undoableEdit = new PieceOfFurnitureImportationUndoableEdit(
240           this.home, this.preferences, oldSelection.toArray(new Selectable [oldSelection.size()]),
241           basePlanLocked, allLevelsSelection, piece, pieceIndex);
242       this.undoSupport.postEdit(undoableEdit);
243     }
244   }
245 
246   /**
247    * Undoable edit for piece importation. This class isn't anonymous to avoid
248    * being bound to controller and its view.
249    */
250   private static class PieceOfFurnitureImportationUndoableEdit extends LocalizedUndoableEdit {
251     private final Home                 home;
252     private final Selectable []        oldSelection;
253     private final boolean              oldBasePlanLocked;
254     private final boolean              oldAllLevelsSelection;
255     private final HomePieceOfFurniture piece;
256     private final int                  pieceIndex;
257 
PieceOfFurnitureImportationUndoableEdit(Home home, UserPreferences preferences, Selectable [] oldSelection, boolean oldBasePlanLocked, boolean oldAllLevelsSelection, HomePieceOfFurniture piece, int pieceIndex)258     private PieceOfFurnitureImportationUndoableEdit(Home home,
259                                                     UserPreferences preferences,
260                                                     Selectable [] oldSelection,
261                                                     boolean oldBasePlanLocked,
262                                                     boolean oldAllLevelsSelection,
263                                                     HomePieceOfFurniture piece,
264                                                     int pieceIndex) {
265       super(preferences, ImportedFurnitureWizardController.class, "undoImportFurnitureName");
266       this.home = home;
267       this.oldSelection = oldSelection;
268       this.oldBasePlanLocked = oldBasePlanLocked;
269       this.oldAllLevelsSelection = oldAllLevelsSelection;
270       this.piece = piece;
271       this.pieceIndex = pieceIndex;
272     }
273 
274     @Override
undo()275     public void undo() throws CannotUndoException {
276       super.undo();
277       this.home.deletePieceOfFurniture(this.piece);
278       this.home.setSelectedItems(Arrays.asList(this.oldSelection));
279       this.home.setAllLevelsSelection(this.oldAllLevelsSelection);
280       this.home.setBasePlanLocked(this.oldBasePlanLocked);
281     }
282 
283     @Override
redo()284     public void redo() throws CannotRedoException {
285       super.redo();
286       this.home.addPieceOfFurniture(this.piece, this.pieceIndex);
287       this.home.setSelectedItems(Arrays.asList(this.piece));
288       if (!piece.isMovable() && this.oldBasePlanLocked) {
289         this.home.setBasePlanLocked(false);
290       }
291       this.home.setAllLevelsSelection(false);
292     }
293   }
294 
295   /**
296    * Returns the content manager of this controller.
297    */
getContentManager()298   public ContentManager getContentManager() {
299     return this.contentManager;
300   }
301 
302   /**
303    * Returns the current step state.
304    */
305   @Override
getStepState()306   protected ImportedFurnitureWizardStepState getStepState() {
307     return (ImportedFurnitureWizardStepState)super.getStepState();
308   }
309 
310   /**
311    * Returns the furniture choice step state.
312    */
getFurnitureModelStepState()313   protected ImportedFurnitureWizardStepState getFurnitureModelStepState() {
314     return this.furnitureModelStepState;
315   }
316 
317   /**
318    * Returns the furniture orientation step state.
319    */
getFurnitureOrientationStepState()320   protected ImportedFurnitureWizardStepState getFurnitureOrientationStepState() {
321     return this.furnitureOrientationStepState;
322   }
323 
324   /**
325    * Returns the furniture attributes step state.
326    */
getFurnitureAttributesStepState()327   protected ImportedFurnitureWizardStepState getFurnitureAttributesStepState() {
328     return this.furnitureAttributesStepState;
329   }
330 
331   /**
332    * Returns the furniture icon step state.
333    */
getFurnitureIconStepState()334   protected ImportedFurnitureWizardStepState getFurnitureIconStepState() {
335     return this.furnitureIconStepState;
336   }
337 
338   /**
339    * Returns the unique wizard view used for all steps.
340    */
getStepsView()341   protected ImportedFurnitureWizardStepsView getStepsView() {
342     // Create view lazily only once it's needed
343     if (this.stepsView == null) {
344       this.stepsView = this.viewFactory.createImportedFurnitureWizardStepsView(
345           this.piece, this.modelName, this.home != null, this.preferences, this);
346     }
347     return this.stepsView;
348   }
349 
350   /**
351    * Switch in the wizard view to the given <code>step</code>.
352    */
setStep(Step step)353   protected void setStep(Step step) {
354     if (step != this.step) {
355       Step oldStep = this.step;
356       this.step = step;
357       this.propertyChangeSupport.firePropertyChange(Property.STEP.name(), oldStep, step);
358     }
359   }
360 
361   /**
362    * Returns the current step in wizard view.
363    */
getStep()364   public Step getStep() {
365     return this.step;
366   }
367 
368   /**
369    * Adds the property change <code>listener</code> in parameter to this controller.
370    */
addPropertyChangeListener(Property property, PropertyChangeListener listener)371   public void addPropertyChangeListener(Property property, PropertyChangeListener listener) {
372     this.propertyChangeSupport.addPropertyChangeListener(property.name(), listener);
373   }
374 
375   /**
376    * Removes the property change <code>listener</code> in parameter from this controller.
377    */
removePropertyChangeListener(Property property, PropertyChangeListener listener)378   public void removePropertyChangeListener(Property property, PropertyChangeListener listener) {
379     this.propertyChangeSupport.removePropertyChangeListener(property.name(), listener);
380   }
381 
382   /**
383    * Returns the model content of the imported piece.
384    */
getModel()385   public Content getModel() {
386     return this.model;
387   }
388 
389   /**
390    * Sets the model content of the imported piece.
391    */
setModel(Content model)392   public void setModel(Content model) {
393     if (model != this.model) {
394       Content oldModel = this.model;
395       this.model = model;
396       this.propertyChangeSupport.firePropertyChange(Property.MODEL.name(), oldModel, model);
397     }
398   }
399 
400   /**
401    * Returns <code>true</code> if imported piece back face should be shown.
402    */
isBackFaceShown()403   public boolean isBackFaceShown() {
404     return this.backFaceShown;
405   }
406 
407   /**
408    * Sets whether imported piece back face should be shown.
409    */
setBackFaceShown(boolean backFaceShown)410   public void setBackFaceShown(boolean backFaceShown) {
411     if (backFaceShown != this.backFaceShown) {
412       this.backFaceShown = backFaceShown;
413       this.propertyChangeSupport.firePropertyChange(Property.BACK_FACE_SHOWN.name(), !backFaceShown, backFaceShown);
414     }
415   }
416 
417   /**
418    * Returns the model size of the imported piece.
419    */
getModelSize()420   public long getModelSize() {
421     return this.modelSize;
422   }
423 
424   /**
425    * Sets the model size of the content of the imported piece.
426    */
setModelSize(long modelSize)427   public void setModelSize(long modelSize) {
428     if (modelSize != this.modelSize) {
429       long oldModelSize = this.modelSize;
430       this.modelSize = modelSize;
431       this.propertyChangeSupport.firePropertyChange(Property.MODEL_SIZE.name(), oldModelSize, modelSize);
432     }
433   }
434 
435   /**
436    * Returns the pitch angle of the imported piece model.
437    */
getModelRotation()438   public float [][] getModelRotation() {
439     return this.modelRotation;
440   }
441 
442   /**
443    * Sets the orientation pitch angle of the imported piece model.
444    */
setModelRotation(float [][] modelRotation)445   public void setModelRotation(float [][] modelRotation) {
446     if (modelRotation != this.modelRotation) {
447       float [][] oldModelRotation = this.modelRotation;
448       this.modelRotation = modelRotation;
449       this.propertyChangeSupport.firePropertyChange(Property.MODEL_ROTATION.name(), oldModelRotation, modelRotation);
450     }
451   }
452 
453   /**
454    * Returns the name of the imported piece.
455    */
getName()456   public String getName() {
457     return this.name;
458   }
459 
460   /**
461    * Sets the name of the imported piece.
462    */
setName(String name)463   public void setName(String name) {
464     if (name != this.name) {
465       String oldName = this.name;
466       this.name = name;
467       if (this.propertyChangeSupport != null) {
468         this.propertyChangeSupport.firePropertyChange(Property.NAME.name(), oldName, name);
469       }
470     }
471   }
472 
473   /**
474    * Returns the creator of the imported piece.
475    * @since 5.5
476    */
getCreator()477   public String getCreator() {
478     return this.creator;
479   }
480 
481   /**
482    * Sets the creator of the imported piece.
483    * @since 5.5
484    */
setCreator(String creator)485   public void setCreator(String creator) {
486     if (creator != this.creator) {
487       String oldCreator = this.creator;
488       this.creator = creator;
489       if (this.propertyChangeSupport != null) {
490         this.propertyChangeSupport.firePropertyChange(Property.CREATOR.name(), oldCreator, creator);
491       }
492     }
493   }
494 
495   /**
496    * Returns the width.
497    */
getWidth()498   public float getWidth() {
499     return this.width;
500   }
501 
502   /**
503    * Sets the width of the imported piece.
504    */
setWidth(float width)505   public void setWidth(float width) {
506     setWidth(width, false);
507   }
508 
509   /**
510    * Sets the width of the imported piece.
511    */
setWidth(float width, boolean keepProportionalWidthUnchanged)512   private void setWidth(float width, boolean keepProportionalWidthUnchanged) {
513     float adjustedWidth = Math.max(width, 0.001f);
514     if (adjustedWidth == width || !keepProportionalWidthUnchanged) {
515       this.proportionalWidth = width;
516     }
517     if (adjustedWidth != this.width) {
518       float oldWidth = this.width;
519       this.width = adjustedWidth;
520       this.propertyChangeSupport.firePropertyChange(Property.WIDTH.name(), oldWidth, adjustedWidth);
521     }
522   }
523 
524   /**
525    * Returns the depth of the imported piece.
526    */
getDepth()527   public float getDepth() {
528     return this.depth;
529   }
530 
531   /**
532    * Sets the depth of the imported piece.
533    */
setDepth(float depth)534   public void setDepth(float depth) {
535     setDepth(depth, false);
536   }
537 
538   /**
539    * Sets the depth of the imported piece.
540    */
setDepth(float depth, boolean keepProportionalDepthUnchanged)541   private void setDepth(float depth, boolean keepProportionalDepthUnchanged) {
542     float adjustedDepth = Math.max(depth, 0.001f);
543     if (adjustedDepth == depth || !keepProportionalDepthUnchanged) {
544       this.proportionalDepth = depth;
545     }
546     if (adjustedDepth != this.depth) {
547       float oldDepth = this.depth;
548       this.depth = adjustedDepth;
549       this.propertyChangeSupport.firePropertyChange(Property.DEPTH.name(), oldDepth, adjustedDepth);
550     }
551   }
552 
553   /**
554    * Returns the height.
555    */
getHeight()556   public float getHeight() {
557     return this.height;
558   }
559 
560   /**
561    * Sets the size of the imported piece.
562    */
setHeight(float height)563   public void setHeight(float height) {
564     setHeight(height, false);
565   }
566 
567   /**
568    * Sets the size of the imported piece.
569    */
setHeight(float height, boolean keepProportionalHeightUnchanged)570   private void setHeight(float height, boolean keepProportionalHeightUnchanged) {
571     float adjustedHeight = Math.max(height, 0.001f);
572     if (adjustedHeight == height || !keepProportionalHeightUnchanged) {
573       this.proportionalHeight = height;
574     }
575     if (adjustedHeight != this.height) {
576       float oldHeight = this.height;
577       this.height = adjustedHeight;
578       this.propertyChangeSupport.firePropertyChange(Property.HEIGHT.name(), oldHeight, adjustedHeight);
579     }
580   }
581 
582   /**
583    * Returns the elevation of the imported piece.
584    */
getElevation()585   public float getElevation() {
586     return this.elevation;
587   }
588 
589   /**
590    * Sets the elevation of the imported piece.
591    */
setElevation(float elevation)592   public void setElevation(float elevation) {
593     if (elevation != this.elevation) {
594       float oldElevation = this.elevation;
595       this.elevation = elevation;
596       this.propertyChangeSupport.firePropertyChange(Property.ELEVATION.name(), oldElevation, elevation);
597     }
598   }
599 
600   /**
601    * Returns <code>true</code> if imported piece is movable.
602    */
isMovable()603   public boolean isMovable() {
604     return this.movable;
605   }
606 
607   /**
608    * Sets whether imported piece is movable.
609    */
setMovable(boolean movable)610   public void setMovable(boolean movable) {
611     if (movable != this.movable) {
612       this.movable = movable;
613       this.propertyChangeSupport.firePropertyChange(Property.MOVABLE.name(), !movable, movable);
614     }
615   }
616 
617   /**
618    * Returns <code>true</code> if imported piece is a door or a window.
619    */
isDoorOrWindow()620   public boolean isDoorOrWindow() {
621     return this.doorOrWindow;
622   }
623 
624   /**
625    * Sets whether imported piece is a door or a window.
626    */
setDoorOrWindow(boolean doorOrWindow)627   public void setDoorOrWindow(boolean doorOrWindow) {
628     if (doorOrWindow != this.doorOrWindow) {
629       this.doorOrWindow = doorOrWindow;
630       this.propertyChangeSupport.firePropertyChange(Property.DOOR_OR_WINDOW.name(), !doorOrWindow, doorOrWindow);
631       if (doorOrWindow) {
632         setStaircaseCutOutShape(null);
633         setMovable(false);
634       }
635     }
636   }
637 
638   /**
639    * Returns the shape used to cut out upper levels at its intersection with a staircase.
640    */
getStaircaseCutOutShape()641   public String getStaircaseCutOutShape() {
642     return this.staircaseCutOutShape;
643   }
644 
645   /**
646    * Sets the shape used to cut out upper levels at its intersection with a staircase.
647    */
setStaircaseCutOutShape(String staircaseCutOutShape)648   public void setStaircaseCutOutShape(String staircaseCutOutShape) {
649     if (staircaseCutOutShape != this.staircaseCutOutShape) {
650       String oldStaircaseCutOutShape = this.staircaseCutOutShape;
651       this.staircaseCutOutShape = staircaseCutOutShape;
652       if (this.propertyChangeSupport != null) {
653         this.propertyChangeSupport.firePropertyChange(Property.STAIRCASE_CUT_OUT_SHAPE.name(), oldStaircaseCutOutShape, staircaseCutOutShape);
654       }
655       if (this.staircaseCutOutShape != null) {
656         setDoorOrWindow(false);
657         setMovable(false);
658       }
659     }
660   }
661 
662   /**
663    * Returns the color of the imported piece.
664    */
getColor()665   public Integer getColor() {
666     return this.color;
667   }
668 
669   /**
670    * Sets the color of the imported piece.
671    */
setColor(Integer color)672   public void setColor(Integer color) {
673     if (color != this.color) {
674       Integer oldColor = this.color;
675       this.color = color;
676       this.propertyChangeSupport.firePropertyChange(Property.COLOR.name(), oldColor, color);
677     }
678   }
679 
680   /**
681    * Returns the category of the imported piece.
682    */
getCategory()683   public FurnitureCategory getCategory() {
684     return this.category;
685   }
686 
687   /**
688    * Sets the category of the imported piece.
689    */
setCategory(FurnitureCategory category)690   public void setCategory(FurnitureCategory category) {
691     if (category != this.category) {
692       FurnitureCategory oldCategory = this.category;
693       this.category = category;
694       this.propertyChangeSupport.firePropertyChange(Property.CATEGORY.name(), oldCategory, category);
695     }
696   }
697 
698   /**
699    * Returns the icon of the imported piece.
700    */
getIcon()701   private Content getIcon() {
702     return getStepsView().getIcon();
703   }
704 
705   /**
706    * Returns the yaw of the piece icon.
707    */
getIconYaw()708   public float getIconYaw() {
709     return this.iconYaw;
710   }
711 
712   /**
713    * Sets the yaw angle of the piece icon.
714    */
setIconYaw(float iconYaw)715   public void setIconYaw(float iconYaw) {
716     if (iconYaw != this.iconYaw) {
717       float oldIconYaw = this.iconYaw;
718       this.iconYaw = iconYaw;
719       this.propertyChangeSupport.firePropertyChange(Property.ICON_YAW.name(), oldIconYaw, iconYaw);
720     }
721   }
722 
723   /**
724    * Returns <code>true</code> if piece proportions should be kept.
725    */
isProportional()726   public boolean isProportional() {
727     return this.proportional;
728   }
729 
730   /**
731    * Sets whether piece proportions should be kept or not.
732    */
setProportional(boolean proportional)733   public void setProportional(boolean proportional) {
734     if (proportional != this.proportional) {
735       this.proportional = proportional;
736       this.propertyChangeSupport.firePropertyChange(Property.PROPORTIONAL.name(), !proportional, proportional);
737     }
738   }
739 
740   /**
741    * Returns <code>true</code> if piece name is valid.
742    */
isPieceOfFurnitureNameValid()743   public boolean isPieceOfFurnitureNameValid() {
744     return this.name != null
745         && this.name.length() > 0;
746   }
747 
748   /**
749    * Step state superclass. All step state share the same step view,
750    * that will display a different component depending on their class name.
751    */
752   protected abstract class ImportedFurnitureWizardStepState extends WizardControllerStepState {
753     private URL icon = ImportedFurnitureWizardController.class.getResource("resources/importedFurnitureWizard.png");
754 
getStep()755     public abstract Step getStep();
756 
757     @Override
enter()758     public void enter() {
759       setStep(getStep());
760     }
761 
762     @Override
getView()763     public View getView() {
764       return getStepsView();
765     }
766 
767     @Override
getIcon()768     public URL getIcon() {
769       return this.icon;
770     }
771   }
772 
773   /**
774    * Furniture model step state (first step).
775    */
776   private class FurnitureModelStepState extends ImportedFurnitureWizardStepState {
777     private PropertyChangeListener modelChangeListener = new PropertyChangeListener() {
778         public void propertyChange(PropertyChangeEvent ev) {
779           setNextStepEnabled(getModel() != null);
780         }
781       };
782 
783     @Override
enter()784     public void enter() {
785       super.enter();
786       setFirstStep(true);
787       // First step is valid once a model is available
788       setNextStepEnabled(getModel() != null);
789       addPropertyChangeListener(Property.MODEL, this.modelChangeListener);
790     }
791 
792     @Override
getStep()793     public Step getStep() {
794       return Step.MODEL;
795     }
796 
797     @Override
goToNextStep()798     public void goToNextStep() {
799       setStepState(getFurnitureOrientationStepState());
800     }
801 
802     @Override
exit()803     public void exit() {
804       removePropertyChangeListener(Property.MODEL, this.modelChangeListener);
805     }
806   }
807 
808   /**
809    * Furniture orientation step state (second step).
810    */
811   private class FurnitureOrientationStepState extends ImportedFurnitureWizardStepState {
812     @Override
enter()813     public void enter() {
814       super.enter();
815       // Step always valid by default
816       setNextStepEnabled(true);
817     }
818 
819     @Override
getStep()820     public Step getStep() {
821       return Step.ROTATION;
822     }
823 
824     @Override
goBackToPreviousStep()825     public void goBackToPreviousStep() {
826       setStepState(getFurnitureModelStepState());
827     }
828 
829     @Override
goToNextStep()830     public void goToNextStep() {
831       setStepState(getFurnitureAttributesStepState());
832     }
833   }
834 
835   /**
836    * Furniture attributes step state (third step).
837    */
838   private class FurnitureAttributesStepState extends ImportedFurnitureWizardStepState {
839     PropertyChangeListener widthChangeListener = new PropertyChangeListener() {
840         public void propertyChange(PropertyChangeEvent ev) {
841           if (isProportional()) {
842             ImportedFurnitureWizardController.this.removePropertyChangeListener(Property.DEPTH, depthChangeListener);
843             ImportedFurnitureWizardController.this.removePropertyChangeListener(Property.HEIGHT, heightChangeListener);
844 
845             // If proportions should be kept, update depth and height
846             float ratio = (Float)ev.getNewValue() / (Float)ev.getOldValue();
847             setDepth(proportionalDepth * ratio, true);
848             setHeight(proportionalHeight * ratio, true);
849 
850             ImportedFurnitureWizardController.this.addPropertyChangeListener(Property.DEPTH, depthChangeListener);
851             ImportedFurnitureWizardController.this.addPropertyChangeListener(Property.HEIGHT, heightChangeListener);
852           }
853         }
854       };
855     PropertyChangeListener depthChangeListener = new PropertyChangeListener() {
856         public void propertyChange(PropertyChangeEvent ev) {
857           if (isProportional()) {
858             ImportedFurnitureWizardController.this.removePropertyChangeListener(Property.WIDTH, widthChangeListener);
859             ImportedFurnitureWizardController.this.removePropertyChangeListener(Property.HEIGHT, heightChangeListener);
860 
861             // If proportions should be kept, update width and height
862             float ratio = (Float)ev.getNewValue() / (Float)ev.getOldValue();
863             setWidth(proportionalWidth * ratio, true);
864             setHeight(proportionalHeight * ratio, true);
865 
866             ImportedFurnitureWizardController.this.addPropertyChangeListener(Property.WIDTH, widthChangeListener);
867             ImportedFurnitureWizardController.this.addPropertyChangeListener(Property.HEIGHT, heightChangeListener);
868           }
869         }
870       };
871     PropertyChangeListener heightChangeListener = new PropertyChangeListener() {
872         public void propertyChange(PropertyChangeEvent ev) {
873           if (isProportional()) {
874             ImportedFurnitureWizardController.this.removePropertyChangeListener(Property.WIDTH, widthChangeListener);
875             ImportedFurnitureWizardController.this.removePropertyChangeListener(Property.DEPTH, depthChangeListener);
876 
877             // If proportions should be kept, update width and depth
878             float ratio = (Float)ev.getNewValue() / (Float)ev.getOldValue();
879             setWidth(proportionalWidth * ratio, true);
880             setDepth(proportionalDepth * ratio, true);
881 
882             ImportedFurnitureWizardController.this.addPropertyChangeListener(Property.WIDTH, widthChangeListener);
883             ImportedFurnitureWizardController.this.addPropertyChangeListener(Property.DEPTH, depthChangeListener);
884           }
885         }
886       };
887     PropertyChangeListener nameAndCategoryChangeListener = new PropertyChangeListener() {
888         public void propertyChange(PropertyChangeEvent ev) {
889           checkPieceOfFurnitureNameInCategory();
890         }
891       };
892 
893     @Override
enter()894     public void enter() {
895       super.enter();
896       ImportedFurnitureWizardController.this.addPropertyChangeListener(Property.WIDTH, this.widthChangeListener);
897       ImportedFurnitureWizardController.this.addPropertyChangeListener(Property.DEPTH, this.depthChangeListener);
898       ImportedFurnitureWizardController.this.addPropertyChangeListener(Property.HEIGHT, this.heightChangeListener);
899       ImportedFurnitureWizardController.this.addPropertyChangeListener(Property.NAME, this.nameAndCategoryChangeListener);
900       ImportedFurnitureWizardController.this.addPropertyChangeListener(Property.CATEGORY, this.nameAndCategoryChangeListener);
901       checkPieceOfFurnitureNameInCategory();
902     }
903 
checkPieceOfFurnitureNameInCategory()904     private void checkPieceOfFurnitureNameInCategory() {
905       setNextStepEnabled(isPieceOfFurnitureNameValid());
906     }
907 
908     @Override
getStep()909     public Step getStep() {
910       return Step.ATTRIBUTES;
911     }
912 
913     @Override
goBackToPreviousStep()914     public void goBackToPreviousStep() {
915       setStepState(getFurnitureOrientationStepState());
916     }
917 
918     @Override
goToNextStep()919     public void goToNextStep() {
920       setStepState(getFurnitureIconStepState());
921     }
922 
923     @Override
exit()924     public void exit() {
925       ImportedFurnitureWizardController.this.removePropertyChangeListener(Property.WIDTH, this.widthChangeListener);
926       ImportedFurnitureWizardController.this.removePropertyChangeListener(Property.DEPTH, this.depthChangeListener);
927       ImportedFurnitureWizardController.this.removePropertyChangeListener(Property.HEIGHT, this.heightChangeListener);
928       ImportedFurnitureWizardController.this.removePropertyChangeListener(Property.NAME, this.nameAndCategoryChangeListener);
929       ImportedFurnitureWizardController.this.removePropertyChangeListener(Property.CATEGORY, this.nameAndCategoryChangeListener);
930     }
931   }
932 
933   /**
934    * Furniture icon step state (last step).
935    */
936   private class FurnitureIconStepState extends ImportedFurnitureWizardStepState {
937     @Override
enter()938     public void enter() {
939       super.enter();
940       setLastStep(true);
941       // Step always valid by default
942       setNextStepEnabled(true);
943     }
944 
945     @Override
getStep()946     public Step getStep() {
947       return Step.ICON;
948     }
949 
950     @Override
goBackToPreviousStep()951     public void goBackToPreviousStep() {
952       setStepState(getFurnitureAttributesStepState());
953     }
954   }
955 }
956