1 /*
2  * RoomController.java 20 nov. 2008
3  *
4  * Sweet Home 3D, Copyright (c) 2008 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.awt.geom.Area;
23 import java.awt.geom.GeneralPath;
24 import java.awt.geom.Line2D;
25 import java.awt.geom.PathIterator;
26 import java.awt.geom.Point2D;
27 import java.beans.PropertyChangeEvent;
28 import java.beans.PropertyChangeListener;
29 import java.beans.PropertyChangeSupport;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35 
36 import javax.swing.undo.CannotRedoException;
37 import javax.swing.undo.CannotUndoException;
38 import javax.swing.undo.UndoableEditSupport;
39 
40 import com.eteks.sweethome3d.model.Baseboard;
41 import com.eteks.sweethome3d.model.Home;
42 import com.eteks.sweethome3d.model.HomeTexture;
43 import com.eteks.sweethome3d.model.Level;
44 import com.eteks.sweethome3d.model.Room;
45 import com.eteks.sweethome3d.model.Selectable;
46 import com.eteks.sweethome3d.model.UserPreferences;
47 import com.eteks.sweethome3d.model.Wall;
48 
49 /**
50  * A MVC controller for room view.
51  * @author Emmanuel Puybaret
52  */
53 public class RoomController implements Controller {
54   /**
55    * The properties that may be edited by the view associated to this controller.
56    */
57   public enum Property {NAME, AREA_VISIBLE, FLOOR_VISIBLE, FLOOR_COLOR, FLOOR_PAINT, FLOOR_SHININESS,
58       CEILING_VISIBLE, CEILING_COLOR, CEILING_PAINT, CEILING_SHININESS,
59       SPLIT_SURROUNDING_WALLS, WALL_SIDES_COLOR, WALL_SIDES_PAINT, WALL_SIDES_SHININESS, WALL_SIDES_BASEBOARD}
60 
61   /**
62    * The possible values for {@linkplain #getFloorPaint() room paint type}.
63    */
64   public enum RoomPaint {DEFAULT, COLORED, TEXTURED}
65 
66   private final Home                  home;
67   private final UserPreferences       preferences;
68   private final ViewFactory           viewFactory;
69   private final ContentManager        contentManager;
70   private final UndoableEditSupport   undoSupport;
71   private TextureChoiceController     floorTextureController;
72   private TextureChoiceController     ceilingTextureController;
73   private TextureChoiceController     wallSidesTextureController;
74   private BaseboardChoiceController   wallSidesBaseboardController;
75   private final PropertyChangeSupport propertyChangeSupport;
76   private DialogView                  roomView;
77 
78   private String    name;
79   private Boolean   areaVisible;
80   private Boolean   floorVisible;
81   private Integer   floorColor;
82   private RoomPaint floorPaint;
83   private Float     floorShininess;
84   private Boolean   ceilingVisible;
85   private Integer   ceilingColor;
86   private RoomPaint ceilingPaint;
87   private Float     ceilingShininess;
88   private boolean   wallSidesEditable;
89   private boolean   splitSurroundingWalls;
90   private boolean   splitSurroundingWallsNeeded;
91   private Integer   wallSidesColor;
92   private RoomPaint wallSidesPaint;
93   private Float     wallSidesShininess;
94 
95   /**
96    * Creates the controller of room view with undo support.
97    */
RoomController(final Home home, UserPreferences preferences, ViewFactory viewFactory, ContentManager contentManager, UndoableEditSupport undoSupport)98   public RoomController(final Home home,
99                         UserPreferences preferences,
100                         ViewFactory viewFactory,
101                         ContentManager contentManager,
102                         UndoableEditSupport undoSupport) {
103     this.home = home;
104     this.preferences = preferences;
105     this.viewFactory = viewFactory;
106     this.contentManager = contentManager;
107     this.undoSupport = undoSupport;
108     this.propertyChangeSupport = new PropertyChangeSupport(this);
109 
110     updateProperties();
111   }
112 
113   /**
114    * Returns the texture controller of the room floor.
115    */
getFloorTextureController()116   public TextureChoiceController getFloorTextureController() {
117     // Create sub controller lazily only once it's needed
118     if (this.floorTextureController == null) {
119       this.floorTextureController = new TextureChoiceController(
120           this.preferences.getLocalizedString(RoomController.class, "floorTextureTitle"),
121           this.preferences, this.viewFactory, this.contentManager);
122       this.floorTextureController.addPropertyChangeListener(TextureChoiceController.Property.TEXTURE,
123           new PropertyChangeListener() {
124             public void propertyChange(PropertyChangeEvent ev) {
125               setFloorPaint(RoomPaint.TEXTURED);
126             }
127           });
128     }
129     return this.floorTextureController;
130   }
131 
132   /**
133    * Returns the texture controller of the room ceiling.
134    */
getCeilingTextureController()135   public TextureChoiceController getCeilingTextureController() {
136     // Create sub controller lazily only once it's needed
137     if (this.ceilingTextureController == null) {
138       this.ceilingTextureController = new TextureChoiceController(
139           this.preferences.getLocalizedString(RoomController.class, "ceilingTextureTitle"),
140           this.preferences, this.viewFactory, this.contentManager);
141       this.ceilingTextureController.addPropertyChangeListener(TextureChoiceController.Property.TEXTURE,
142           new PropertyChangeListener() {
143             public void propertyChange(PropertyChangeEvent ev) {
144               setCeilingPaint(RoomPaint.TEXTURED);
145             }
146           });
147     }
148     return this.ceilingTextureController;
149   }
150 
151   /**
152    * Returns the texture controller of the room wall sides.
153    */
getWallSidesTextureController()154   public TextureChoiceController getWallSidesTextureController() {
155     // Create sub controller lazily only once it's needed
156     if (this.wallSidesTextureController == null) {
157       this.wallSidesTextureController = new TextureChoiceController(
158           this.preferences.getLocalizedString(RoomController.class, "wallSidesTextureTitle"),
159           this.preferences, this.viewFactory, this.contentManager);
160       this.wallSidesTextureController.addPropertyChangeListener(TextureChoiceController.Property.TEXTURE,
161           new PropertyChangeListener() {
162             public void propertyChange(PropertyChangeEvent ev) {
163               setWallSidesPaint(RoomPaint.TEXTURED);
164             }
165           });
166     }
167     return this.wallSidesTextureController;
168   }
169 
170   /**
171    * Returns the controller of the wall sides baseboard.
172    * @since 5.0
173    */
getWallSidesBaseboardController()174   public BaseboardChoiceController getWallSidesBaseboardController() {
175     // Create sub controller lazily only once it's needed
176     if (this.wallSidesBaseboardController == null) {
177       this.wallSidesBaseboardController = new BaseboardChoiceController(
178           this.preferences, this.viewFactory, this.contentManager);
179     }
180     return this.wallSidesBaseboardController;
181   }
182 
183   /**
184    * Returns the view associated with this controller.
185    */
getView()186   public DialogView getView() {
187     // Create view lazily only once it's needed
188     if (this.roomView == null) {
189       this.roomView = this.viewFactory.createRoomView(this.preferences, this);
190     }
191     return this.roomView;
192   }
193 
194   /**
195    * Displays the view controlled by this controller.
196    */
displayView(View parentView)197   public void displayView(View parentView) {
198     getView().displayView(parentView);
199   }
200 
201   /**
202    * Adds the property change <code>listener</code> in parameter to this controller.
203    */
addPropertyChangeListener(Property property, PropertyChangeListener listener)204   public void addPropertyChangeListener(Property property, PropertyChangeListener listener) {
205     this.propertyChangeSupport.addPropertyChangeListener(property.name(), listener);
206   }
207 
208   /**
209    * Removes the property change <code>listener</code> in parameter from this controller.
210    */
removePropertyChangeListener(Property property, PropertyChangeListener listener)211   public void removePropertyChangeListener(Property property, PropertyChangeListener listener) {
212     this.propertyChangeSupport.removePropertyChangeListener(property.name(), listener);
213   }
214 
215   /**
216    * Returns <code>true</code> if the given <code>property</code> is editable.
217    * Depending on whether a property is editable or not, the view associated to this controller
218    * may render it differently.
219    * The implementation of this method always returns <code>true</code> except for <code>WALL</code> properties.
220    */
isPropertyEditable(Property property)221   public boolean isPropertyEditable(Property property) {
222     switch (property) {
223       case SPLIT_SURROUNDING_WALLS :
224       case WALL_SIDES_COLOR :
225       case WALL_SIDES_PAINT :
226       case WALL_SIDES_SHININESS :
227       case WALL_SIDES_BASEBOARD :
228         return this.wallSidesEditable;
229       default :
230         return true;
231     }
232   }
233 
234   /**
235    * Updates edited properties from selected rooms in the home edited by this controller.
236    */
updateProperties()237   protected void updateProperties() {
238     List<Room> selectedRooms = Home.getRoomsSubList(this.home.getSelectedItems());
239     if (selectedRooms.isEmpty()) {
240       setAreaVisible(null); // Nothing to edit
241       setFloorColor(null);
242       getFloorTextureController().setTexture(null);
243       setFloorPaint(null);
244       setFloorShininess(null);
245       setCeilingColor(null);
246       getCeilingTextureController().setTexture(null);
247       setCeilingPaint(null);
248       setCeilingShininess(null);
249     } else {
250       // Search the common properties among selected rooms
251       Room firstRoom = selectedRooms.get(0);
252 
253       String name = firstRoom.getName();
254       if (name != null) {
255         for (int i = 1; i < selectedRooms.size(); i++) {
256           if (!name.equals(selectedRooms.get(i).getName())) {
257             name = null;
258             break;
259           }
260         }
261       }
262       setName(name);
263 
264       // Search the common areaVisible value among rooms
265       Boolean areaVisible = firstRoom.isAreaVisible();
266       for (int i = 1; i < selectedRooms.size(); i++) {
267         if (areaVisible != selectedRooms.get(i).isAreaVisible()) {
268           areaVisible = null;
269           break;
270         }
271       }
272       setAreaVisible(areaVisible);
273 
274       // Search the common floorVisible value among rooms
275       Boolean floorVisible = firstRoom.isFloorVisible();
276       for (int i = 1; i < selectedRooms.size(); i++) {
277         if (floorVisible != selectedRooms.get(i).isFloorVisible()) {
278           floorVisible = null;
279           break;
280         }
281       }
282       setFloorVisible(floorVisible);
283 
284       // Search the common floor color among rooms
285       Integer floorColor = firstRoom.getFloorColor();
286       if (floorColor != null) {
287         for (int i = 1; i < selectedRooms.size(); i++) {
288           if (!floorColor.equals(selectedRooms.get(i).getFloorColor())) {
289             floorColor = null;
290             break;
291           }
292         }
293       }
294       setFloorColor(floorColor);
295 
296       // Search the common floor texture among rooms
297       HomeTexture floorTexture = firstRoom.getFloorTexture();
298       if (floorTexture != null) {
299         for (int i = 1; i < selectedRooms.size(); i++) {
300           if (!floorTexture.equals(selectedRooms.get(i).getFloorTexture())) {
301             floorTexture = null;
302             break;
303           }
304         }
305       }
306       getFloorTextureController().setTexture(floorTexture);
307 
308       boolean defaultColorsAndTextures = true;
309       for (int i = 0; i < selectedRooms.size(); i++) {
310         Room room = selectedRooms.get(i);
311         if (room.getFloorColor() != null
312             || room.getFloorTexture() != null) {
313           defaultColorsAndTextures = false;
314           break;
315         }
316       }
317 
318       if (floorColor != null) {
319         setFloorPaint(RoomPaint.COLORED);
320       } else if (floorTexture != null) {
321         setFloorPaint(RoomPaint.TEXTURED);
322       } else if (defaultColorsAndTextures) {
323         setFloorPaint(RoomPaint.DEFAULT);
324       } else {
325         setFloorPaint(null);
326       }
327 
328       // Search the common floor shininess among rooms
329       Float floorShininess = firstRoom.getFloorShininess();
330       for (int i = 1; i < selectedRooms.size(); i++) {
331         if (!floorShininess.equals(selectedRooms.get(i).getFloorShininess())) {
332           floorShininess = null;
333           break;
334         }
335       }
336       setFloorShininess(floorShininess);
337 
338       // Search the common ceilingVisible value among rooms
339       Boolean ceilingVisible = firstRoom.isCeilingVisible();
340       for (int i = 1; i < selectedRooms.size(); i++) {
341         if (ceilingVisible != selectedRooms.get(i).isCeilingVisible()) {
342           ceilingVisible = null;
343           break;
344         }
345       }
346       setCeilingVisible(ceilingVisible);
347 
348       // Search the common ceiling color among rooms
349       Integer ceilingColor = firstRoom.getCeilingColor();
350       if (ceilingColor != null) {
351         for (int i = 1; i < selectedRooms.size(); i++) {
352           if (!ceilingColor.equals(selectedRooms.get(i).getCeilingColor())) {
353             ceilingColor = null;
354             break;
355           }
356         }
357       }
358       setCeilingColor(ceilingColor);
359 
360       // Search the common ceiling texture among rooms
361       HomeTexture ceilingTexture = firstRoom.getCeilingTexture();
362       if (ceilingTexture != null) {
363         for (int i = 1; i < selectedRooms.size(); i++) {
364           if (!ceilingTexture.equals(selectedRooms.get(i).getCeilingTexture())) {
365             ceilingTexture = null;
366             break;
367           }
368         }
369       }
370       getCeilingTextureController().setTexture(ceilingTexture);
371 
372       defaultColorsAndTextures = true;
373       for (int i = 0; i < selectedRooms.size(); i++) {
374         Room room = selectedRooms.get(i);
375         if (room.getCeilingColor() != null
376             || room.getCeilingTexture() != null) {
377           defaultColorsAndTextures = false;
378           break;
379         }
380       }
381 
382       if (ceilingColor != null) {
383         setCeilingPaint(RoomPaint.COLORED);
384       } else if (ceilingTexture != null) {
385         setCeilingPaint(RoomPaint.TEXTURED);
386       } else if (defaultColorsAndTextures) {
387         setCeilingPaint(RoomPaint.DEFAULT);
388       } else {
389         setCeilingPaint(null);
390       }
391 
392       // Search the common ceiling shininess among rooms
393       Float ceilingShininess = firstRoom.getCeilingShininess();
394       for (int i = 1; i < selectedRooms.size(); i++) {
395         if (!ceilingShininess.equals(selectedRooms.get(i).getCeilingShininess())) {
396           ceilingShininess = null;
397           break;
398         }
399       }
400       setCeilingShininess(ceilingShininess);
401     }
402 
403     List<WallSide> wallSides = getRoomsWallSides(selectedRooms, null);
404     if (wallSides.isEmpty()) {
405       this.wallSidesEditable =
406       this.splitSurroundingWallsNeeded  =
407       this.splitSurroundingWalls = false;
408       setWallSidesColor(null);
409       setWallSidesPaint(null);
410       setWallSidesShininess(null);
411       getWallSidesBaseboardController().setVisible(null);
412       getWallSidesBaseboardController().setThickness(null);
413       getWallSidesBaseboardController().setHeight(null);
414       getWallSidesBaseboardController().setColor(null);
415       getWallSidesBaseboardController().getTextureController().setTexture(null);
416       getWallSidesBaseboardController().setPaint(null);
417     } else {
418       this.wallSidesEditable = true;
419       this.splitSurroundingWallsNeeded = splitWalls(wallSides, null, null, null);
420       this.splitSurroundingWalls = false;
421       WallSide firstWallSide = wallSides.get(0);
422 
423       // Search the common wall color among wall sides
424       Integer wallSidesColor = firstWallSide.getSide() == WallSide.LEFT_SIDE
425           ? firstWallSide.getWall().getLeftSideColor()
426           : firstWallSide.getWall().getRightSideColor();
427       if (wallSidesColor != null) {
428         for (int i = 1; i < wallSides.size(); i++) {
429           WallSide wallSide = wallSides.get(i);
430           if (!wallSidesColor.equals(wallSide.getSide() == WallSide.LEFT_SIDE
431                   ? wallSide.getWall().getLeftSideColor()
432                   : wallSide.getWall().getRightSideColor())) {
433             wallSidesColor = null;
434             break;
435           }
436         }
437       }
438       setWallSidesColor(wallSidesColor);
439 
440       // Search the common wall texture among wall sides
441       HomeTexture wallSidesTexture = firstWallSide.getSide() == WallSide.LEFT_SIDE
442           ? firstWallSide.getWall().getLeftSideTexture()
443           : firstWallSide.getWall().getRightSideTexture();
444       if (wallSidesTexture != null) {
445         for (int i = 1; i < wallSides.size(); i++) {
446           WallSide wallSide = wallSides.get(i);
447           if (!wallSidesTexture.equals(wallSide.getSide() == WallSide.LEFT_SIDE
448                   ? wallSide.getWall().getLeftSideTexture()
449                   : wallSide.getWall().getRightSideTexture())) {
450             wallSidesTexture = null;
451             break;
452           }
453         }
454       }
455       getWallSidesTextureController().setTexture(wallSidesTexture);
456 
457       boolean defaultColorsAndTextures = true;
458       for (int i = 0; i < wallSides.size(); i++) {
459         WallSide wallSide = wallSides.get(i);
460         if ((wallSide.getSide() == WallSide.LEFT_SIDE
461                 ? wallSide.getWall().getLeftSideColor()
462                 : wallSide.getWall().getRightSideColor()) != null
463             || (wallSide.getSide() == WallSide.LEFT_SIDE
464                     ? wallSide.getWall().getLeftSideTexture()
465                     : wallSide.getWall().getRightSideTexture()) != null) {
466           defaultColorsAndTextures = false;
467           break;
468         }
469       }
470 
471       if (wallSidesColor != null) {
472         setWallSidesPaint(RoomPaint.COLORED);
473       } else if (wallSidesTexture != null) {
474         setWallSidesPaint(RoomPaint.TEXTURED);
475       } else if (defaultColorsAndTextures) {
476         setWallSidesPaint(RoomPaint.DEFAULT);
477       } else {
478         setWallSidesPaint(null);
479       }
480 
481       // Search the common floor shininess among rooms
482       Float wallSidesShininess = firstWallSide.getSide() == WallSide.LEFT_SIDE
483           ? firstWallSide.getWall().getLeftSideShininess()
484           : firstWallSide.getWall().getRightSideShininess();
485       if (wallSidesShininess != null) {
486         for (int i = 1; i < wallSides.size(); i++) {
487           WallSide wallSide = wallSides.get(i);
488           if (!wallSidesShininess.equals(wallSide.getSide() == WallSide.LEFT_SIDE
489                   ? wallSide.getWall().getLeftSideShininess()
490                   : wallSide.getWall().getRightSideShininess())) {
491             wallSidesShininess = null;
492             break;
493           }
494         }
495       }
496       setWallSidesShininess(wallSidesShininess);
497 
498       Baseboard firstWallSideBaseboard = firstWallSide.getSide() == WallSide.LEFT_SIDE
499           ? firstWallSide.getWall().getLeftSideBaseboard()
500           : firstWallSide.getWall().getRightSideBaseboard();
501       Boolean wallSidesBaseboardVisible = firstWallSideBaseboard != null;
502       for (int i = 1; i < wallSides.size(); i++) {
503         WallSide wallSide = wallSides.get(i);
504         if (wallSidesBaseboardVisible
505             != (wallSide.getSide() == WallSide.LEFT_SIDE
506                   ? wallSide.getWall().getLeftSideBaseboard() != null
507                   : wallSide.getWall().getRightSideBaseboard() != null)) {
508           wallSidesBaseboardVisible = null;
509           break;
510         }
511       }
512       getWallSidesBaseboardController().setVisible(wallSidesBaseboardVisible);
513 
514       // Search the common baseboard thickness  among walls
515       Float wallSidesBaseboardThickness = firstWallSideBaseboard != null
516           ? firstWallSideBaseboard.getThickness()
517           : this.preferences.getNewWallBaseboardThickness();
518       for (int i = 1; i < wallSides.size(); i++) {
519         WallSide wallSide = wallSides.get(i);
520         Baseboard baseboard = wallSide.getSide() == WallSide.LEFT_SIDE
521             ? wallSide.getWall().getLeftSideBaseboard()
522             : wallSide.getWall().getRightSideBaseboard();
523         if (!wallSidesBaseboardThickness.equals(baseboard != null
524                 ? baseboard.getThickness()
525                 : this.preferences.getNewWallBaseboardThickness())) {
526           wallSidesBaseboardThickness = null;
527           break;
528         }
529       }
530       getWallSidesBaseboardController().setThickness(wallSidesBaseboardThickness);
531 
532       // Search the common baseboard height among walls
533       Float wallSidesBaseboardHeight = firstWallSideBaseboard != null
534           ? firstWallSideBaseboard.getHeight()
535           : this.preferences.getNewWallBaseboardHeight();
536       for (int i = 1; i < wallSides.size(); i++) {
537         WallSide wallSide = wallSides.get(i);
538         Baseboard baseboard = wallSide.getSide() == WallSide.LEFT_SIDE
539             ? wallSide.getWall().getLeftSideBaseboard()
540             : wallSide.getWall().getRightSideBaseboard();
541         if (!wallSidesBaseboardHeight.equals(baseboard != null
542                 ? baseboard.getHeight()
543                 : this.preferences.getNewWallBaseboardHeight())) {
544           wallSidesBaseboardHeight = null;
545           break;
546         }
547       }
548       getWallSidesBaseboardController().setHeight(wallSidesBaseboardHeight);
549 
550       // Search the maximum height among walls
551       float maxBaseboardHeight = firstWallSide.getWall().isTrapezoidal()
552           ? Math.max(firstWallSide.getWall().getHeight(), firstWallSide.getWall().getHeightAtEnd())
553           : firstWallSide.getWall().getHeight();
554       for (int i = 1; i < wallSides.size(); i++) {
555         Wall wall = wallSides.get(i).getWall();
556         maxBaseboardHeight = Math.max(maxBaseboardHeight,
557             wall.isTrapezoidal()
558                 ? Math.max(wall.getHeight(), wall.getHeightAtEnd())
559                 : wall.getHeight());
560       }
561       getWallSidesBaseboardController().setMaxHeight(maxBaseboardHeight);
562 
563       // Search the common baseboard color among walls
564       Integer wallSidesBaseboardColor = firstWallSideBaseboard != null
565           ? firstWallSideBaseboard.getColor()
566           : null;
567       if (wallSidesBaseboardColor != null) {
568         for (int i = 1; i < wallSides.size(); i++) {
569           WallSide wallSide = wallSides.get(i);
570           Baseboard baseboard = wallSide.getSide() == WallSide.LEFT_SIDE
571               ? wallSide.getWall().getLeftSideBaseboard()
572               : wallSide.getWall().getRightSideBaseboard();
573           if (baseboard == null
574               || !wallSidesBaseboardColor.equals(baseboard.getColor())) {
575             wallSidesBaseboardColor = null;
576             break;
577           }
578         }
579       }
580       getWallSidesBaseboardController().setColor(wallSidesBaseboardColor);
581 
582       // Search the common right baseboard texture among walls
583       HomeTexture wallSidesBaseboardTexture = firstWallSideBaseboard != null
584           ? firstWallSideBaseboard.getTexture()
585           : null;
586       if (wallSidesBaseboardTexture != null) {
587         for (int i = 1; i < wallSides.size(); i++) {
588           WallSide wallSide = wallSides.get(i);
589           Baseboard baseboard = wallSide.getSide() == WallSide.LEFT_SIDE
590               ? wallSide.getWall().getLeftSideBaseboard()
591               : wallSide.getWall().getRightSideBaseboard();
592           if (baseboard == null
593               || !wallSidesBaseboardTexture.equals(baseboard.getTexture())) {
594             wallSidesBaseboardTexture = null;
595             break;
596           }
597         }
598       }
599       getWallSidesBaseboardController().getTextureController().setTexture(wallSidesBaseboardTexture);
600 
601       defaultColorsAndTextures = true;
602       for (int i = 0; i < wallSides.size(); i++) {
603         WallSide wallSide = wallSides.get(i);
604         Baseboard baseboard = wallSide.getSide() == WallSide.LEFT_SIDE
605             ? wallSide.getWall().getLeftSideBaseboard()
606             : wallSide.getWall().getRightSideBaseboard();
607         if (baseboard != null
608             && (baseboard.getColor() != null
609                 || baseboard.getTexture() != null)) {
610           defaultColorsAndTextures = false;
611           break;
612         }
613       }
614 
615       if (wallSidesBaseboardColor != null) {
616         getWallSidesBaseboardController().setPaint(BaseboardChoiceController.BaseboardPaint.COLORED);
617       } else if (wallSidesBaseboardTexture != null) {
618         getWallSidesBaseboardController().setPaint(BaseboardChoiceController.BaseboardPaint.TEXTURED);
619       } else if (defaultColorsAndTextures) {
620         getWallSidesBaseboardController().setPaint(BaseboardChoiceController.BaseboardPaint.DEFAULT);
621       } else {
622         getWallSidesBaseboardController().setPaint(null);
623       }
624     }
625   }
626 
627   /**
628    * Returns the wall sides close to each room of <code>rooms</code>.
629    */
getRoomsWallSides(List<Room> rooms, List<WallSide> defaultWallSides)630   private List<WallSide> getRoomsWallSides(List<Room> rooms, List<WallSide> defaultWallSides) {
631     List<WallSide> wallSides = new ArrayList<WallSide>();
632     for (Room room : rooms) {
633       Area roomArea = new Area(getPath(room.getPoints(), true));
634       if (defaultWallSides != null) {
635         for (WallSide wallSide : defaultWallSides) {
636           if (isRoomItersectingWallSide(wallSide.getWall().getPoints(), wallSide.getSide(), roomArea)) {
637             wallSides.add(wallSide);
638           }
639         }
640       } else {
641         for (Wall wall : this.home.getWalls()) {
642           if ((wall.getLevel() == null || wall.getLevel().isViewable())
643               && wall.isAtLevel(this.home.getSelectedLevel())) {
644             float [][] wallPoints = wall.getPoints();
645             if (isRoomItersectingWallSide(wallPoints, WallSide.LEFT_SIDE, roomArea)) {
646               wallSides.add(new WallSide(wall, WallSide.LEFT_SIDE));
647             }
648             if (isRoomItersectingWallSide(wallPoints, WallSide.RIGHT_SIDE, roomArea)) {
649               wallSides.add(new WallSide(wall, WallSide.RIGHT_SIDE));
650             }
651           }
652         }
653       }
654     }
655     return wallSides;
656   }
657 
658   /**
659    * Returns <code>true</code> if the wall points on the given <code>wallSide</code>
660    * intersects room area.
661    */
isRoomItersectingWallSide(float [][] wallPoints, int wallSide, Area roomArea)662   private boolean isRoomItersectingWallSide(float [][] wallPoints, int wallSide, Area roomArea) {
663     Area wallSideTestArea = getWallSideArea(wallPoints, wallSide);
664     float wallSideTestAreaSurface = getSurface(wallSideTestArea);
665     wallSideTestArea.intersect(roomArea);
666     if (!wallSideTestArea.isEmpty()) {
667       float wallSideIntersectionSurface = getSurface(wallSideTestArea);
668       // Take into account only walls that shares a minimum surface with the room
669       if (wallSideIntersectionSurface > wallSideTestAreaSurface * 0.02f) {
670         return true;
671       }
672     }
673     return false;
674   }
675 
676   /**
677    * Returns the area of the side of the given <code>wall</code>.
678    */
getWallSideArea(float [][] wallPoints, int wallSide)679   private Area getWallSideArea(float [][] wallPoints, int wallSide) {
680     final float thickness = 2f;
681     // Build an area of the given thickness using walls instances
682     if (wallPoints.length == 4) {
683       if (wallSide == WallSide.LEFT_SIDE) {
684         return new Area(getPath(new Wall(
685             wallPoints [0][0], wallPoints [0][1], wallPoints [1][0], wallPoints [1][1], thickness, 0).getPoints(), true));
686       } else {
687         return new Area(getPath(new Wall(
688             wallPoints [2][0], wallPoints [2][1], wallPoints [3][0], wallPoints [3][1], thickness, 0).getPoints(), true));
689       }
690     } else {
691       float [][] wallSidePoints = new float [wallPoints.length / 2][];
692       System.arraycopy(wallPoints, wallSide == WallSide.LEFT_SIDE ? 0 : wallSidePoints.length,
693           wallSidePoints, 0, wallSidePoints.length);
694       Wall [] wallSideWalls = new Wall [wallSidePoints.length - 1];
695       for (int i = 0; i < wallSideWalls.length; i++) {
696         wallSideWalls [i] = new Wall(wallSidePoints [i][0], wallSidePoints [i][1], wallSidePoints [i + 1][0], wallSidePoints [i + 1][1], thickness, 0);
697         if (i > 0) {
698           wallSideWalls [i].setWallAtStart(wallSideWalls [i - 1]);
699           wallSideWalls [i - 1].setWallAtEnd(wallSideWalls [i]);
700         }
701       }
702       wallSidePoints = new float [wallPoints.length][];
703       float [][] wallSideWallPoints = null;
704       for (int i = 0; i < wallSideWalls.length; i++) {
705         wallSideWallPoints = wallSideWalls [i].getPoints();
706         wallSidePoints [i] = wallSideWallPoints [0];
707         wallSidePoints [wallSidePoints.length - i - 1] = wallSideWallPoints [3];
708       }
709       wallSidePoints [wallSidePoints.length / 2 - 1] = wallSideWallPoints [1];
710       wallSidePoints [wallSidePoints.length / 2] = wallSideWallPoints [2];
711       return new Area(getPath(wallSidePoints, true));
712     }
713   }
714 
715   /**
716    * Returns the shape matching the coordinates in <code>points</code> array.
717    */
getPath(float [][] points, boolean closedPath)718   private GeneralPath getPath(float [][] points, boolean closedPath) {
719     GeneralPath path = new GeneralPath();
720     path.moveTo(points [0][0], points [0][1]);
721     for (int i = 1; i < points.length; i++) {
722       path.lineTo(points [i][0], points [i][1]);
723     }
724     if (closedPath) {
725       path.closePath();
726     }
727     return path;
728   }
729 
730   /**
731    * Returns the surface of the given <code>area</code>.
732    */
getSurface(Area area)733   private float getSurface(Area area) {
734     // Add the surface of the different polygons of this room
735     float surface = 0;
736     List<float []> currentPathPoints = new ArrayList<float[]>();
737     for (PathIterator it = area.getPathIterator(null); !it.isDone(); ) {
738       float [] roomPoint = new float[2];
739       switch (it.currentSegment(roomPoint)) {
740         case PathIterator.SEG_MOVETO :
741           currentPathPoints.add(roomPoint);
742           break;
743         case PathIterator.SEG_LINETO :
744           currentPathPoints.add(roomPoint);
745           break;
746         case PathIterator.SEG_CLOSE :
747           float [][] pathPoints =
748               currentPathPoints.toArray(new float [currentPathPoints.size()][]);
749           surface += Math.abs(getSignedSurface(pathPoints));
750           currentPathPoints.clear();
751           break;
752       }
753       it.next();
754     }
755     return surface;
756   }
757 
getSignedSurface(float areaPoints [][])758   private float getSignedSurface(float areaPoints [][]) {
759     // From "Area of a General Polygon" algorithm described in
760     // http://www.davidchandler.com/AreaOfAGeneralPolygon.pdf
761     float area = 0;
762     for (int i = 1; i < areaPoints.length; i++) {
763       area += areaPoints [i][0] * areaPoints [i - 1][1];
764       area -= areaPoints [i][1] * areaPoints [i - 1][0];
765     }
766     area += areaPoints [0][0] * areaPoints [areaPoints.length - 1][1];
767     area -= areaPoints [0][1] * areaPoints [areaPoints.length - 1][0];
768     return area / 2;
769   }
770 
771   /**
772    * Sets the edited name.
773    */
setName(String name)774   public void setName(String name) {
775     if (name != this.name) {
776       String oldName = this.name;
777       this.name = name;
778       this.propertyChangeSupport.firePropertyChange(Property.NAME.name(), oldName, name);
779     }
780   }
781 
782   /**
783    * Returns the edited name.
784    */
getName()785   public String getName() {
786     return this.name;
787   }
788 
789   /**
790    * Sets whether room area is visible or not.
791    */
setAreaVisible(Boolean areaVisible)792   public void setAreaVisible(Boolean areaVisible) {
793     if (areaVisible != this.areaVisible) {
794       Boolean oldAreaVisible = this.areaVisible;
795       this.areaVisible = areaVisible;
796       this.propertyChangeSupport.firePropertyChange(Property.AREA_VISIBLE.name(), oldAreaVisible, areaVisible);
797     }
798   }
799 
800   /**
801    * Returns whether room area is visible or not.
802    */
getAreaVisible()803   public Boolean getAreaVisible() {
804     return this.areaVisible;
805   }
806 
807   /**
808    * Sets whether room floor is visible or not.
809    */
setFloorVisible(Boolean floorVisible)810   public void setFloorVisible(Boolean floorVisible) {
811     if (floorVisible != this.floorVisible) {
812       Boolean oldFloorVisible = this.floorVisible;
813       this.floorVisible = floorVisible;
814       this.propertyChangeSupport.firePropertyChange(Property.FLOOR_VISIBLE.name(), oldFloorVisible, floorVisible);
815     }
816   }
817 
818   /**
819    * Returns whether room floor is visible or not.
820    */
getFloorVisible()821   public Boolean getFloorVisible() {
822     return this.floorVisible;
823   }
824 
825   /**
826    * Sets the edited color of the floor.
827    */
setFloorColor(Integer floorColor)828   public void setFloorColor(Integer floorColor) {
829     if (floorColor != this.floorColor) {
830       Integer oldFloorColor = this.floorColor;
831       this.floorColor = floorColor;
832       this.propertyChangeSupport.firePropertyChange(Property.FLOOR_COLOR.name(), oldFloorColor, floorColor);
833 
834       setFloorPaint(RoomPaint.COLORED);
835     }
836   }
837 
838   /**
839    * Returns the edited color of the floor.
840    */
getFloorColor()841   public Integer getFloorColor() {
842     return this.floorColor;
843   }
844 
845   /**
846    * Sets whether the floor is colored, textured or unknown painted.
847    */
setFloorPaint(RoomPaint floorPaint)848   public void setFloorPaint(RoomPaint floorPaint) {
849     if (floorPaint != this.floorPaint) {
850       RoomPaint oldFloorPaint = this.floorPaint;
851       this.floorPaint = floorPaint;
852       this.propertyChangeSupport.firePropertyChange(Property.FLOOR_PAINT.name(), oldFloorPaint, floorPaint);
853     }
854   }
855 
856   /**
857    * Returns whether the floor is colored, textured or unknown painted.
858    */
getFloorPaint()859   public RoomPaint getFloorPaint() {
860     return this.floorPaint;
861   }
862 
863   /**
864    * Sets the edited shininess of the floor.
865    */
setFloorShininess(Float floorShininess)866   public void setFloorShininess(Float floorShininess) {
867     if (floorShininess != this.floorShininess) {
868       Float oldFloorShininess = this.floorShininess;
869       this.floorShininess = floorShininess;
870       this.propertyChangeSupport.firePropertyChange(Property.FLOOR_SHININESS.name(), oldFloorShininess, floorShininess);
871     }
872   }
873 
874   /**
875    * Returns the edited shininess of the floor.
876    */
getFloorShininess()877   public Float getFloorShininess() {
878     return this.floorShininess;
879   }
880 
881   /**
882    * Sets whether room ceiling is visible or not.
883    */
setCeilingVisible(Boolean ceilingCeilingVisible)884   public void setCeilingVisible(Boolean ceilingCeilingVisible) {
885     if (ceilingCeilingVisible != this.ceilingVisible) {
886       Boolean oldCeilingVisible = this.ceilingVisible;
887       this.ceilingVisible = ceilingCeilingVisible;
888       this.propertyChangeSupport.firePropertyChange(Property.CEILING_VISIBLE.name(), oldCeilingVisible, ceilingCeilingVisible);
889     }
890   }
891 
892   /**
893    * Returns whether room ceiling is ceilingCeilingVisible or not.
894    */
getCeilingVisible()895   public Boolean getCeilingVisible() {
896     return this.ceilingVisible;
897   }
898 
899   /**
900    * Sets the edited color of the ceiling.
901    */
setCeilingColor(Integer ceilingColor)902   public void setCeilingColor(Integer ceilingColor) {
903     if (ceilingColor != this.ceilingColor) {
904       Integer oldCeilingColor = this.ceilingColor;
905       this.ceilingColor = ceilingColor;
906       this.propertyChangeSupport.firePropertyChange(Property.CEILING_COLOR.name(), oldCeilingColor, ceilingColor);
907 
908       setCeilingPaint(RoomPaint.COLORED);
909     }
910   }
911 
912   /**
913    * Returns the edited color of the ceiling.
914    */
getCeilingColor()915   public Integer getCeilingColor() {
916     return this.ceilingColor;
917   }
918 
919   /**
920    * Sets whether the ceiling is colored, textured or unknown painted.
921    */
setCeilingPaint(RoomPaint ceilingPaint)922   public void setCeilingPaint(RoomPaint ceilingPaint) {
923     if (ceilingPaint != this.ceilingPaint) {
924       RoomPaint oldCeilingPaint = this.ceilingPaint;
925       this.ceilingPaint = ceilingPaint;
926       this.propertyChangeSupport.firePropertyChange(Property.CEILING_PAINT.name(), oldCeilingPaint, ceilingPaint);
927     }
928   }
929 
930   /**
931    * Returns whether the ceiling is colored, textured or unknown painted.
932    */
getCeilingPaint()933   public RoomPaint getCeilingPaint() {
934     return this.ceilingPaint;
935   }
936 
937   /**
938    * Sets the edited shininess of the ceiling.
939    */
setCeilingShininess(Float ceilingShininess)940   public void setCeilingShininess(Float ceilingShininess) {
941     if (ceilingShininess != this.ceilingShininess) {
942       Float oldCeilingShininess = this.ceilingShininess;
943       this.ceilingShininess = ceilingShininess;
944       this.propertyChangeSupport.firePropertyChange(Property.CEILING_SHININESS.name(), oldCeilingShininess, ceilingShininess);
945     }
946   }
947 
948   /**
949    * Returns the edited shininess of the ceiling.
950    */
getCeilingShininess()951   public Float getCeilingShininess() {
952     return this.ceilingShininess;
953   }
954 
955   /**
956    * Returns <code>true</code> if walls around the edited rooms should be split.
957    * @since 4.0
958    */
isSplitSurroundingWalls()959   public boolean isSplitSurroundingWalls() {
960     return this.splitSurroundingWalls;
961   }
962 
963   /**
964    * Sets whether walls around the edited rooms should be split or not.
965    * @since 4.0
966    */
setSplitSurroundingWalls(boolean splitSurroundingWalls)967   public void setSplitSurroundingWalls(boolean splitSurroundingWalls) {
968     if (splitSurroundingWalls != this.splitSurroundingWalls) {
969       this.splitSurroundingWalls = splitSurroundingWalls;
970       this.propertyChangeSupport.firePropertyChange(Property.SPLIT_SURROUNDING_WALLS.name(), !splitSurroundingWalls, splitSurroundingWalls);
971     }
972   }
973 
974   /**
975    * Returns <code>true</code> if walls around the edited rooms need to be split
976    * to avoid changing the color of wall sides that belong to neighborhood rooms.
977    * @since 4.0
978    */
isSplitSurroundingWallsNeeded()979   public boolean isSplitSurroundingWallsNeeded() {
980     return this.splitSurroundingWallsNeeded;
981   }
982 
983   /**
984    * Sets the edited color of the wall sides.
985    * @since 4.0
986    */
setWallSidesColor(Integer wallSidesColor)987   public void setWallSidesColor(Integer wallSidesColor) {
988     if (wallSidesColor != this.wallSidesColor) {
989       Integer oldWallSidesColor = this.wallSidesColor;
990       this.wallSidesColor = wallSidesColor;
991       this.propertyChangeSupport.firePropertyChange(Property.WALL_SIDES_COLOR.name(), oldWallSidesColor, wallSidesColor);
992 
993       setWallSidesPaint(RoomPaint.COLORED);
994     }
995   }
996 
997   /**
998    * Returns the edited color of the wall sides.
999    * @since 4.0
1000    */
getWallSidesColor()1001   public Integer getWallSidesColor() {
1002     return this.wallSidesColor;
1003   }
1004 
1005   /**
1006    * Sets whether the wall sides are colored, textured or unknown painted.
1007    * @since 4.0
1008    */
setWallSidesPaint(RoomPaint wallSidesPaint)1009   public void setWallSidesPaint(RoomPaint wallSidesPaint) {
1010     if (wallSidesPaint != this.wallSidesPaint) {
1011       RoomPaint oldWallSidesPaint = this.wallSidesPaint;
1012       this.wallSidesPaint = wallSidesPaint;
1013       this.propertyChangeSupport.firePropertyChange(Property.WALL_SIDES_PAINT.name(), oldWallSidesPaint, wallSidesPaint);
1014     }
1015   }
1016 
1017   /**
1018    * Returns whether the wall sides are colored, textured or unknown painted.
1019    * @since 4.0
1020    */
getWallSidesPaint()1021   public RoomPaint getWallSidesPaint() {
1022     return this.wallSidesPaint;
1023   }
1024 
1025   /**
1026    * Sets the edited shininess of the wall sides.
1027    * @since 4.0
1028    */
setWallSidesShininess(Float wallSidesShininess)1029   public void setWallSidesShininess(Float wallSidesShininess) {
1030     if (wallSidesShininess != this.wallSidesShininess) {
1031       Float oldWallSidesShininess = this.wallSidesShininess;
1032       this.wallSidesShininess = wallSidesShininess;
1033       this.propertyChangeSupport.firePropertyChange(Property.WALL_SIDES_SHININESS.name(), oldWallSidesShininess, wallSidesShininess);
1034     }
1035   }
1036 
1037   /**
1038    * Returns the edited shininess of the wall sides.
1039    * @since 4.0
1040    */
getWallSidesShininess()1041   public Float getWallSidesShininess() {
1042     return this.wallSidesShininess;
1043   }
1044 
1045   /**
1046    * Controls the modification of selected rooms in edited home.
1047    */
modifyRooms()1048   public void modifyRooms() {
1049     List<Selectable> oldSelection = this.home.getSelectedItems();
1050     List<Room> selectedRooms = Home.getRoomsSubList(oldSelection);
1051     if (!selectedRooms.isEmpty()) {
1052       String name = getName();
1053       Boolean areaVisible = getAreaVisible();
1054       Boolean floorVisible = getFloorVisible();
1055       RoomPaint floorPaint = getFloorPaint();
1056       Integer floorColor = floorPaint == RoomPaint.COLORED
1057           ? getFloorColor() : null;
1058       HomeTexture floorTexture = floorPaint == RoomPaint.TEXTURED
1059           ? getFloorTextureController().getTexture() : null;
1060       Float floorShininess = getFloorShininess();
1061       Boolean ceilingVisible = getCeilingVisible();
1062       RoomPaint ceilingPaint = getCeilingPaint();
1063       Integer ceilingColor = ceilingPaint == RoomPaint.COLORED
1064           ? getCeilingColor() : null;
1065       HomeTexture ceilingTexture = ceilingPaint == RoomPaint.TEXTURED
1066           ? getCeilingTextureController().getTexture() : null;
1067       Float ceilingShininess = getCeilingShininess();
1068       RoomPaint wallSidesPaint = getWallSidesPaint();
1069       Integer wallSidesColor = wallSidesPaint == RoomPaint.COLORED
1070           ? getWallSidesColor() : null;
1071       HomeTexture wallSidesTexture = wallSidesPaint == RoomPaint.TEXTURED
1072           ? getWallSidesTextureController().getTexture() : null;
1073       Float wallSidesShininess = getWallSidesShininess();
1074       Boolean wallSidesBaseboardVisible = getWallSidesBaseboardController().getVisible();
1075       Float wallSidesBaseboardThickness = getWallSidesBaseboardController().getThickness();
1076       Float wallSidesBaseboardHeight = getWallSidesBaseboardController().getHeight();
1077       BaseboardChoiceController.BaseboardPaint wallSidesBaseboardPaint = getWallSidesBaseboardController().getPaint();
1078       Integer wallSidesBaseboardColor = wallSidesBaseboardPaint == BaseboardChoiceController.BaseboardPaint.COLORED
1079           ? getWallSidesBaseboardController().getColor() : null;
1080       HomeTexture wallSidesBaseboardTexture = wallSidesBaseboardPaint == BaseboardChoiceController.BaseboardPaint.TEXTURED
1081           ? getWallSidesBaseboardController().getTextureController().getTexture()
1082           : null;
1083       List<WallSide> selectedRoomsWallSides = getRoomsWallSides(selectedRooms, null);
1084 
1085       // Create an array of modified rooms with their current properties values
1086       ModifiedRoom [] modifiedRooms = new ModifiedRoom [selectedRooms.size()];
1087       for (int i = 0; i < modifiedRooms.length; i++) {
1088         modifiedRooms [i] = new ModifiedRoom(selectedRooms.get(i));
1089       }
1090 
1091       // Apply modification
1092       List<ModifiedWall> deletedWalls = new ArrayList<ModifiedWall>();
1093       List<ModifiedWall> addedWalls = new ArrayList<ModifiedWall>();
1094       List<Selectable> newSelection = new ArrayList<Selectable>(oldSelection);
1095       if (this.splitSurroundingWalls) {
1096         if (splitWalls(selectedRoomsWallSides, deletedWalls, addedWalls, newSelection)) {
1097           this.home.setSelectedItems(newSelection);
1098           // Update wall sides
1099           selectedRoomsWallSides = getRoomsWallSides(selectedRooms, selectedRoomsWallSides);
1100         }
1101       }
1102 
1103       // Create an array of modified wall sides with their current properties values
1104       ModifiedWallSide [] modifiedWallSides = new ModifiedWallSide [selectedRoomsWallSides.size()];
1105       for (int i = 0; i < modifiedWallSides.length; i++) {
1106         modifiedWallSides [i] = new ModifiedWallSide(selectedRoomsWallSides.get(i));
1107       }
1108       doModifyRoomsAndWallSides(home, modifiedRooms, name, areaVisible,
1109           floorVisible, floorPaint, floorColor, floorTexture, floorShininess,
1110           ceilingVisible, ceilingPaint, ceilingColor, ceilingTexture, ceilingShininess,
1111           modifiedWallSides, this.preferences.getNewWallBaseboardThickness(), this.preferences.getNewWallBaseboardHeight(),
1112           wallSidesPaint, wallSidesColor, wallSidesTexture, wallSidesShininess,
1113           wallSidesBaseboardVisible, wallSidesBaseboardThickness, wallSidesBaseboardHeight,
1114           wallSidesBaseboardPaint, wallSidesBaseboardColor, wallSidesBaseboardTexture, null, null);
1115       if (this.undoSupport != null) {
1116         this.undoSupport.postEdit(new RoomsAndWallSidesModificationUndoableEdit(this.home, this.preferences,
1117             oldSelection.toArray(new Selectable [oldSelection.size()]), newSelection.toArray(new Selectable [newSelection.size()]),
1118             modifiedRooms, name, areaVisible,
1119             floorVisible, floorPaint, floorColor, floorTexture, floorShininess,
1120             ceilingVisible, ceilingPaint, ceilingColor, ceilingTexture, ceilingShininess,
1121             modifiedWallSides, this.preferences.getNewWallBaseboardThickness(), this.preferences.getNewWallBaseboardHeight(),
1122             wallSidesPaint, wallSidesColor, wallSidesTexture, wallSidesShininess,
1123             wallSidesBaseboardVisible, wallSidesBaseboardThickness, wallSidesBaseboardHeight,
1124             wallSidesBaseboardPaint, wallSidesBaseboardColor, wallSidesBaseboardTexture,
1125             deletedWalls.toArray(new ModifiedWall [deletedWalls.size()]),
1126             addedWalls.toArray(new ModifiedWall [addedWalls.size()])));
1127       }
1128       if (name != null) {
1129         this.preferences.addAutoCompletionString("RoomName", name);
1130       }
1131     }
1132   }
1133 
1134   /**
1135    * Splits walls that overfill on other rooms if needed and returns <code>false</code> if the operation wasn't needed.
1136    */
splitWalls(List<WallSide> wallSides, List<ModifiedWall> deletedWalls, List<ModifiedWall> addedWalls, List<Selectable> selectedItems)1137   private boolean splitWalls(List<WallSide> wallSides,
1138                              List<ModifiedWall> deletedWalls,
1139                              List<ModifiedWall> addedWalls,
1140                              List<Selectable> selectedItems) {
1141     Map<Wall, ModifiedWall> existingWalls = null;
1142     List<Wall> newWalls = new ArrayList<Wall>();
1143     WallSide splitWallSide;
1144     do {
1145       splitWallSide = null;
1146       Wall firstWall = null;
1147       Wall secondWall = null;
1148       ModifiedWall deletedWall = null;
1149       for (int i = 0; i < wallSides.size() && splitWallSide == null; i++) {
1150         WallSide wallSide = wallSides.get(i);
1151         Wall wall = wallSide.getWall();
1152         Float arcExtent = wall.getArcExtent();
1153         if (arcExtent == null || arcExtent.floatValue() == 0) { // Ignore round walls
1154           Area wallArea = new Area(getPath(wall.getPoints(), true));
1155           for (WallSide intersectedWallSide : wallSides) {
1156             Wall intersectedWall = intersectedWallSide.getWall();
1157             if (wall != intersectedWall) {
1158               Area intersectedWallArea = new Area(getPath(intersectedWall.getPoints(), true));
1159               intersectedWallArea.intersect(wallArea);
1160               if (!intersectedWallArea.isEmpty()
1161                   && intersectedWallArea.isSingular()) {
1162                 float [] intersection = computeIntersection(
1163                     wall.getXStart(), wall.getYStart(), wall.getXEnd(), wall.getYEnd(),
1164                     intersectedWall.getXStart(), intersectedWall.getYStart(), intersectedWall.getXEnd(), intersectedWall.getYEnd());
1165                 if (intersection != null) {
1166                   // Duplicate new walls to copy their characteristics
1167                   firstWall = (Wall)wall.duplicate();
1168                   secondWall = (Wall)wall.duplicate();
1169                   // Store wall level to add new walls at the same level as the split wall
1170                   firstWall.setLevel(wall.getLevel());
1171                   secondWall.setLevel(wall.getLevel());
1172 
1173                   // Change split walls end and start point
1174                   firstWall.setXEnd(intersection [0]);
1175                   firstWall.setYEnd(intersection [1]);
1176                   secondWall.setXStart(intersection [0]);
1177                   secondWall.setYStart(intersection [1]);
1178 
1179                   if (firstWall.getLength() > intersectedWall.getThickness() / 2
1180                       && secondWall.getLength() > intersectedWall.getThickness() / 2) {
1181                     // If method is called for test purpose
1182                     if (deletedWalls == null) {
1183                       return true;
1184                     }
1185 
1186                     if (existingWalls == null) {
1187                       // Store all walls at start and end in case there would be more than one change on a wall
1188                       existingWalls = new HashMap<Wall, ModifiedWall>(wallSides.size());
1189                       for (WallSide side : wallSides) {
1190                         if (!existingWalls.containsKey(side.getWall())) {
1191                           existingWalls.put(side.getWall(), new ModifiedWall(side.getWall()));
1192                         }
1193                       }
1194                     }
1195 
1196                     deletedWall = existingWalls.get(wall);
1197                     Wall wallAtStart = wall.getWallAtStart();
1198                     if (wallAtStart != null) {
1199                       firstWall.setWallAtStart(wallAtStart);
1200                       if (wallAtStart.getWallAtEnd() == wall) {
1201                         wallAtStart.setWallAtEnd(firstWall);
1202                       } else {
1203                         wallAtStart.setWallAtStart(firstWall);
1204                       }
1205                     }
1206 
1207                     Wall wallAtEnd = wall.getWallAtEnd();
1208                     if (wallAtEnd != null) {
1209                       secondWall.setWallAtEnd(wallAtEnd);
1210                       if (wallAtEnd.getWallAtEnd() == wall) {
1211                         wallAtEnd.setWallAtEnd(secondWall);
1212                       } else {
1213                         wallAtEnd.setWallAtStart(secondWall);
1214                       }
1215                     }
1216 
1217                     firstWall.setWallAtEnd(secondWall);
1218                     secondWall.setWallAtStart(firstWall);
1219 
1220                     if (wall.getHeightAtEnd() != null) {
1221                       Float heightAtIntersecion = wall.getHeight()
1222                           + (wall.getHeightAtEnd() - wall.getHeight())
1223                             * (float)Point2D.distance(wall.getXStart(), wall.getYStart(), intersection [0], intersection [1])
1224                             / wall.getLength();
1225                       firstWall.setHeightAtEnd(heightAtIntersecion);
1226                       secondWall.setHeight(heightAtIntersecion);
1227                     }
1228 
1229                     splitWallSide = wallSide;
1230                     break;
1231                   }
1232                 }
1233               }
1234             }
1235           }
1236         }
1237       }
1238 
1239       if (splitWallSide != null) {
1240         newWalls.add(firstWall);
1241         newWalls.add(secondWall);
1242         Wall splitWall = splitWallSide.getWall();
1243         if (this.home.getWalls().contains(splitWall)) {
1244           deletedWalls.add(deletedWall);
1245         } else {
1246           // Remove from newWalls in case it was a wall split twice
1247           for (int i = newWalls.size() - 1; i >= 0; i--) {
1248             if (newWalls.get(i) == splitWall) {
1249               newWalls.remove(i);
1250               break;
1251             }
1252           }
1253         }
1254         // Update selected items
1255         if (selectedItems.remove(splitWall)) {
1256           selectedItems.add(firstWall);
1257           selectedItems.add(secondWall);
1258         }
1259 
1260         wallSides.remove(splitWallSide);
1261         wallSides.add(new WallSide(firstWall, splitWallSide.getSide()));
1262         wallSides.add(new WallSide(secondWall, splitWallSide.getSide()));
1263         // Update any wall side that reference the same wall
1264         List<WallSide> sameWallSides = new ArrayList<WallSide>();
1265         for (int i = wallSides.size() - 1;  i >= 0; i--) {
1266           WallSide wallSide = wallSides.get(i);
1267           if (wallSide.getWall() == splitWall) {
1268             wallSides.remove(i);
1269             sameWallSides.add(new WallSide(firstWall, wallSide.getSide()));
1270             sameWallSides.add(new WallSide(secondWall, wallSide.getSide()));
1271           }
1272         }
1273         wallSides.addAll(sameWallSides);
1274       }
1275     } while (splitWallSide != null);
1276 
1277     // If method is called for test purpose
1278     if (deletedWalls == null) {
1279       return false;
1280     } else {
1281       for (Wall newWall : newWalls) {
1282         ModifiedWall addedWall = new ModifiedWall(newWall);
1283         addedWalls.add(addedWall);
1284         this.home.addWall(newWall);
1285         newWall.setLevel(addedWall.getLevel());
1286       }
1287       for (ModifiedWall deletedWall : deletedWalls) {
1288         this.home.deleteWall(deletedWall.getWall());
1289       }
1290       return !deletedWalls.isEmpty();
1291     }
1292   }
1293 
1294   /**
1295    * Returns the intersection between a line segment and a second line.
1296    */
computeIntersection(float xPoint1, float yPoint1, float xPoint2, float yPoint2, float xPoint3, float yPoint3, float xPoint4, float yPoint4)1297   private float [] computeIntersection(float xPoint1, float yPoint1, float xPoint2, float yPoint2,
1298                                        float xPoint3, float yPoint3, float xPoint4, float yPoint4) {
1299     float [] point = PlanController.computeIntersection(xPoint1, yPoint1, xPoint2, yPoint2,
1300         xPoint3, yPoint3, xPoint4, yPoint4);
1301     if (Line2D.ptSegDistSq(xPoint1, yPoint1, xPoint2, yPoint2, point [0], point [1]) < 1E-7
1302         && (Math.abs(xPoint1 - point [0]) > 1E-4
1303             || Math.abs(yPoint1 - point [1]) > 1E-4)
1304         && (Math.abs(xPoint2 - point [0]) > 1E-4
1305             || Math.abs(yPoint2 - point [1]) > 1E-4)) {
1306       return point;
1307     } else {
1308       return null;
1309     }
1310   }
1311 
1312   /**
1313    * Undoable edit for rooms modification. This class isn't anonymous to avoid
1314    * being bound to controller and its view.
1315    */
1316   private static class RoomsAndWallSidesModificationUndoableEdit extends LocalizedUndoableEdit {
1317     private final Home                home;
1318     private final Selectable []       oldSelection;
1319     private final Selectable []       newSelection;
1320     private final ModifiedRoom []     modifiedRooms;
1321     private final String              name;
1322     private final Boolean             areaVisible;
1323     private final Boolean             floorVisible;
1324     private final RoomPaint           floorPaint;
1325     private final Integer             floorColor;
1326     private final HomeTexture         floorTexture;
1327     private final Float               floorShininess;
1328     private final Boolean             ceilingVisible;
1329     private final RoomPaint           ceilingPaint;
1330     private final Integer             ceilingColor;
1331     private final HomeTexture         ceilingTexture;
1332     private final Float               ceilingShininess;
1333     private final ModifiedWallSide [] modifiedWallSides;
1334     private final float               newWallBaseboardHeight;
1335     private final float               newWallBaseboardThickness;
1336     private final RoomPaint           wallSidesPaint;
1337     private final Integer             wallSidesColor;
1338     private final HomeTexture         wallSidesTexture;
1339     private final Float               wallSidesShininess;
1340     private final Boolean             wallSidesBaseboardVisible;
1341     private final Float               wallSidesBaseboardThickness;
1342     private final Float               wallSidesBaseboardHeight;
1343     private final BaseboardChoiceController.BaseboardPaint wallSidesBaseboardPaint;
1344     private final Integer             wallSidesBaseboardColor;
1345     private final HomeTexture         wallSidesBaseboardTexture;
1346     private final ModifiedWall []     deletedWalls;
1347     private final ModifiedWall []     addedWalls;
1348 
RoomsAndWallSidesModificationUndoableEdit(Home home, UserPreferences preferences, Selectable [] oldSelection, Selectable [] newSelection, ModifiedRoom [] modifiedRooms, String name, Boolean areaVisible, Boolean floorVisible, RoomPaint floorPaint, Integer floorColor, HomeTexture floorTexture, Float floorShininess, Boolean ceilingVisible, RoomPaint ceilingPaint, Integer ceilingColor, HomeTexture ceilingTexture, Float ceilingShininess, ModifiedWallSide [] modifiedWallSides, float newWallBaseboardThickness, float newWallBaseboardHeight, RoomPaint wallSidesPaint, Integer wallSidesColor, HomeTexture wallSidesTexture, Float wallSidesShininess, Boolean wallSidesBaseboardVisible, Float wallSidesBaseboardThickness, Float wallSidesBaseboardHeight, BaseboardChoiceController.BaseboardPaint wallSidesBaseboardPaint, Integer wallSidesBaseboardColor, HomeTexture wallSidesBaseboardTexture, ModifiedWall [] deletedWalls, ModifiedWall [] addedWalls)1349     private RoomsAndWallSidesModificationUndoableEdit(Home home,
1350                                           UserPreferences preferences,
1351                                           Selectable [] oldSelection,
1352                                           Selectable [] newSelection,
1353                                           ModifiedRoom [] modifiedRooms,
1354                                           String name,
1355                                           Boolean areaVisible,
1356                                           Boolean floorVisible,
1357                                           RoomPaint floorPaint,
1358                                           Integer floorColor,
1359                                           HomeTexture floorTexture,
1360                                           Float floorShininess,
1361                                           Boolean ceilingVisible,
1362                                           RoomPaint ceilingPaint,
1363                                           Integer ceilingColor,
1364                                           HomeTexture ceilingTexture,
1365                                           Float ceilingShininess,
1366                                           ModifiedWallSide [] modifiedWallSides,
1367                                           float newWallBaseboardThickness,
1368                                           float newWallBaseboardHeight,
1369                                           RoomPaint wallSidesPaint,
1370                                           Integer wallSidesColor,
1371                                           HomeTexture wallSidesTexture,
1372                                           Float wallSidesShininess,
1373                                           Boolean wallSidesBaseboardVisible,
1374                                           Float wallSidesBaseboardThickness,
1375                                           Float wallSidesBaseboardHeight,
1376                                           BaseboardChoiceController.BaseboardPaint wallSidesBaseboardPaint,
1377                                           Integer wallSidesBaseboardColor,
1378                                           HomeTexture wallSidesBaseboardTexture,
1379                                           ModifiedWall [] deletedWalls,
1380                                           ModifiedWall [] addedWalls) {
1381       super(preferences, RoomController.class, "undoModifyRoomsName");
1382       this.home = home;
1383       this.oldSelection = oldSelection;
1384       this.newSelection = newSelection;
1385       this.modifiedRooms = modifiedRooms;
1386       this.name = name;
1387       this.areaVisible = areaVisible;
1388       this.floorVisible = floorVisible;
1389       this.floorPaint = floorPaint;
1390       this.floorColor = floorColor;
1391       this.floorTexture = floorTexture;
1392       this.floorShininess = floorShininess;
1393       this.ceilingVisible = ceilingVisible;
1394       this.ceilingPaint = ceilingPaint;
1395       this.ceilingColor = ceilingColor;
1396       this.ceilingTexture = ceilingTexture;
1397       this.ceilingShininess = ceilingShininess;
1398       this.modifiedWallSides = modifiedWallSides;
1399       this.newWallBaseboardThickness = newWallBaseboardThickness;
1400       this.newWallBaseboardHeight = newWallBaseboardHeight;
1401       this.wallSidesPaint = wallSidesPaint;
1402       this.wallSidesColor = wallSidesColor;
1403       this.wallSidesTexture = wallSidesTexture;
1404       this.wallSidesShininess = wallSidesShininess;
1405       this.wallSidesBaseboardVisible = wallSidesBaseboardVisible;
1406       this.wallSidesBaseboardThickness = wallSidesBaseboardThickness;
1407       this.wallSidesBaseboardHeight = wallSidesBaseboardHeight;
1408       this.wallSidesBaseboardPaint = wallSidesBaseboardPaint;
1409       this.wallSidesBaseboardColor = wallSidesBaseboardColor;
1410       this.wallSidesBaseboardTexture = wallSidesBaseboardTexture;
1411       this.deletedWalls = deletedWalls;
1412       this.addedWalls = addedWalls;
1413     }
1414 
1415     @Override
undo()1416     public void undo() throws CannotUndoException {
1417       super.undo();
1418       undoModifyRoomsAndWallSides(this.home, this.modifiedRooms, this.modifiedWallSides, this.deletedWalls, this.addedWalls);
1419       this.home.setSelectedItems(Arrays.asList(this.oldSelection));
1420     }
1421 
1422     @Override
redo()1423     public void redo() throws CannotRedoException {
1424       super.redo();
1425       doModifyRoomsAndWallSides(this.home,
1426           this.modifiedRooms, this.name, this.areaVisible,
1427           this.floorVisible, this.floorPaint, this.floorColor, this.floorTexture, this.floorShininess,
1428           this.ceilingVisible, this.ceilingPaint, this.ceilingColor, this.ceilingTexture, this.ceilingShininess,
1429           this.modifiedWallSides, newWallBaseboardThickness, this.newWallBaseboardHeight,
1430           this.wallSidesPaint, this.wallSidesColor, this.wallSidesTexture, this.wallSidesShininess,
1431           this.wallSidesBaseboardVisible, this.wallSidesBaseboardThickness, this.wallSidesBaseboardHeight,
1432           this.wallSidesBaseboardPaint, this.wallSidesBaseboardColor, this.wallSidesBaseboardTexture,
1433           this.deletedWalls, this.addedWalls);
1434       this.home.setSelectedItems(Arrays.asList(this.newSelection));
1435     }
1436   }
1437 
1438   /**
1439    * Modifies rooms and walls properties with the values in parameter.
1440    */
doModifyRoomsAndWallSides(Home home, ModifiedRoom [] modifiedRooms, String name, Boolean areaVisible, Boolean floorVisible, RoomPaint floorPaint, Integer floorColor, HomeTexture floorTexture, Float floorShininess, Boolean ceilingVisible, RoomPaint ceilingPaint, Integer ceilingColor, HomeTexture ceilingTexture, Float ceilingShininess, ModifiedWallSide [] modifiedWallSides, float newWallBaseboardThickness, float newWallBaseboardHeight, RoomPaint wallSidesPaint, Integer wallSidesColor, HomeTexture wallSidesTexture, Float wallSidesShininess, Boolean wallSidesBaseboardVisible, Float wallSidesBaseboardThickness, Float wallSidesBaseboardHeight, BaseboardChoiceController.BaseboardPaint wallSidesBaseboardPaint, Integer wallSidesBaseboardColor, HomeTexture wallSidesBaseboardTexture, ModifiedWall [] deletedWalls, ModifiedWall [] addedWalls)1441   private static void doModifyRoomsAndWallSides(Home home, ModifiedRoom [] modifiedRooms,
1442                                                 String name, Boolean areaVisible,
1443                                                 Boolean floorVisible, RoomPaint floorPaint, Integer floorColor, HomeTexture floorTexture, Float floorShininess,
1444                                                 Boolean ceilingVisible, RoomPaint ceilingPaint, Integer ceilingColor, HomeTexture ceilingTexture, Float ceilingShininess,
1445                                                 ModifiedWallSide [] modifiedWallSides,
1446                                                 float newWallBaseboardThickness, float newWallBaseboardHeight,
1447                                                 RoomPaint wallSidesPaint, Integer wallSidesColor, HomeTexture wallSidesTexture, Float wallSidesShininess,
1448                                                 Boolean wallSidesBaseboardVisible, Float wallSidesBaseboardThickness, Float wallSidesBaseboardHeight,
1449                                                 BaseboardChoiceController.BaseboardPaint wallSidesBaseboardPaint, Integer wallSidesBaseboardColor, HomeTexture wallSidesBaseboardTexture,
1450                                                 ModifiedWall [] deletedWalls,
1451                                                 ModifiedWall [] addedWalls) {
1452     if (deletedWalls != null) {
1453       for (ModifiedWall newWall : addedWalls) {
1454         newWall.resetJoinedWalls();
1455         home.addWall(newWall.getWall());
1456         newWall.getWall().setLevel(newWall.getLevel());
1457       }
1458       for (ModifiedWall deletedWall : deletedWalls) {
1459         home.deleteWall(deletedWall.getWall());
1460       }
1461     }
1462     for (ModifiedRoom modifiedRoom : modifiedRooms) {
1463       Room room = modifiedRoom.getRoom();
1464       if (name != null) {
1465         room.setName(name);
1466       }
1467       if (areaVisible != null) {
1468         room.setAreaVisible(areaVisible);
1469       }
1470       if (floorVisible != null) {
1471         room.setFloorVisible(floorVisible);
1472       }
1473       if (floorPaint != null) {
1474         switch (floorPaint) {
1475           case DEFAULT :
1476             room.setFloorColor(null);
1477             room.setFloorTexture(null);
1478             break;
1479           case COLORED :
1480             room.setFloorColor(floorColor);
1481             room.setFloorTexture(null);
1482             break;
1483           case TEXTURED :
1484             room.setFloorColor(null);
1485             room.setFloorTexture(floorTexture);
1486             break;
1487         }
1488       }
1489       if (floorShininess != null) {
1490         room.setFloorShininess(floorShininess);
1491       }
1492       if (ceilingVisible != null) {
1493         room.setCeilingVisible(ceilingVisible);
1494       }
1495       if (ceilingPaint != null) {
1496         switch (ceilingPaint) {
1497           case DEFAULT :
1498             room.setCeilingColor(null);
1499             room.setCeilingTexture(null);
1500             break;
1501           case COLORED :
1502             room.setCeilingColor(ceilingColor);
1503             room.setCeilingTexture(null);
1504             break;
1505           case TEXTURED :
1506             room.setCeilingColor(null);
1507             room.setCeilingTexture(ceilingTexture);
1508             break;
1509         }
1510       }
1511       if (ceilingShininess != null) {
1512         room.setCeilingShininess(ceilingShininess);
1513       }
1514     }
1515     for (ModifiedWallSide modifiedWallSide : modifiedWallSides) {
1516       WallSide wallSide = modifiedWallSide.getWallSide();
1517       Wall wall = wallSide.getWall();
1518       if (wallSide.getSide() == WallSide.LEFT_SIDE) {
1519         if (wallSidesPaint != null) {
1520           switch (wallSidesPaint) {
1521             case DEFAULT :
1522               wall.setLeftSideColor(null);
1523               wall.setLeftSideTexture(null);
1524               break;
1525             case COLORED :
1526               wall.setLeftSideColor(wallSidesColor);
1527               wall.setLeftSideTexture(null);
1528               break;
1529             case TEXTURED :
1530               wall.setLeftSideColor(null);
1531               wall.setLeftSideTexture(wallSidesTexture);
1532               break;
1533           }
1534         }
1535         if (wallSidesShininess != null) {
1536           wall.setLeftSideShininess(wallSidesShininess);
1537         }
1538       } else {
1539         if (wallSidesPaint != null) {
1540           switch (wallSidesPaint) {
1541             case DEFAULT :
1542               wall.setRightSideColor(null);
1543               wall.setRightSideTexture(null);
1544               break;
1545             case COLORED :
1546               wall.setRightSideColor(wallSidesColor);
1547               wall.setRightSideTexture(null);
1548               break;
1549             case TEXTURED :
1550               wall.setRightSideColor(null);
1551               wall.setRightSideTexture(wallSidesTexture);
1552               break;
1553           }
1554         }
1555         if (wallSidesShininess != null) {
1556           wall.setRightSideShininess(wallSidesShininess);
1557         }
1558       }
1559 
1560       if (wallSidesBaseboardVisible == Boolean.FALSE) {
1561         if (wallSide.getSide() == WallSide.LEFT_SIDE) {
1562           wall.setLeftSideBaseboard(null);
1563         } else {
1564           wall.setRightSideBaseboard(null);
1565         }
1566       } else {
1567         Baseboard baseboard = wallSide.getSide() == WallSide.LEFT_SIDE
1568             ? wall.getLeftSideBaseboard()
1569             : wall.getRightSideBaseboard();
1570         if (wallSidesBaseboardVisible == Boolean.TRUE
1571             || baseboard != null) {
1572           float baseboardThickness = baseboard != null
1573               ? baseboard.getThickness()
1574               : newWallBaseboardThickness;
1575           float baseboardHeight = baseboard != null
1576               ? baseboard.getHeight()
1577               : newWallBaseboardHeight;
1578           Integer baseboardColor = baseboard != null
1579               ? baseboard.getColor()
1580               : null;
1581           HomeTexture baseboardTexture = baseboard != null
1582               ? baseboard.getTexture()
1583               : null;
1584           if (wallSidesBaseboardPaint != null) {
1585             switch (wallSidesBaseboardPaint) {
1586               case DEFAULT :
1587                 baseboardColor = null;
1588                 baseboardTexture = null;
1589                 break;
1590               case COLORED :
1591                 if (wallSidesBaseboardColor != null) {
1592                   baseboardColor = wallSidesBaseboardColor;
1593                 }
1594                 baseboardTexture = null;
1595                 break;
1596               case TEXTURED :
1597                 baseboardColor = null;
1598                 if (wallSidesBaseboardTexture != null) {
1599                   baseboardTexture = wallSidesBaseboardTexture;
1600                 }
1601                 break;
1602             }
1603           }
1604           baseboard = Baseboard.getInstance(
1605               wallSidesBaseboardThickness != null
1606                   ? wallSidesBaseboardThickness
1607                   : baseboardThickness,
1608               wallSidesBaseboardHeight != null
1609                   ? wallSidesBaseboardHeight
1610                   : baseboardHeight,
1611               baseboardColor, baseboardTexture);
1612           if (wallSide.getSide() == WallSide.LEFT_SIDE) {
1613             wall.setLeftSideBaseboard(baseboard);
1614           } else {
1615             wall.setRightSideBaseboard(baseboard);
1616           }
1617         }
1618       }
1619     }
1620   }
1621 
1622   /**
1623    * Restores room properties from the values stored in <code>modifiedRooms</code> and <code>modifiedWallSides</code>.
1624    */
undoModifyRoomsAndWallSides(Home home, ModifiedRoom [] modifiedRooms, ModifiedWallSide [] modifiedWallSides, ModifiedWall [] deletedWalls, ModifiedWall [] addedWalls)1625   private static void undoModifyRoomsAndWallSides(Home home,
1626                                                   ModifiedRoom [] modifiedRooms,
1627                                                   ModifiedWallSide [] modifiedWallSides,
1628                                                   ModifiedWall [] deletedWalls,
1629                                                   ModifiedWall [] addedWalls) {
1630     for (ModifiedRoom modifiedRoom : modifiedRooms) {
1631       modifiedRoom.reset();
1632     }
1633     for (ModifiedWallSide modifiedWallSide : modifiedWallSides) {
1634       modifiedWallSide.reset();
1635     }
1636     for (ModifiedWall newWall : addedWalls) {
1637       home.deleteWall(newWall.getWall());
1638     }
1639     for (ModifiedWall deletedWall : deletedWalls) {
1640       deletedWall.resetJoinedWalls();
1641       home.addWall(deletedWall.getWall());
1642       deletedWall.getWall().setLevel(deletedWall.getLevel());
1643     }
1644   }
1645 
1646   /**
1647    * Stores the current properties values of a modified room.
1648    */
1649   private static final class ModifiedRoom {
1650     private final Room        room;
1651     private final String      name;
1652     private final boolean     areaVisible;
1653     private final boolean     floorVisible;
1654     private final Integer     floorColor;
1655     private final HomeTexture floorTexture;
1656     private final float       floorShininess;
1657     private final boolean     ceilingVisible;
1658     private final Integer     ceilingColor;
1659     private final HomeTexture ceilingTexture;
1660     private final float       ceilingShininess;
1661 
ModifiedRoom(Room room)1662     public ModifiedRoom(Room room) {
1663       this.room = room;
1664       this.name = room.getName();
1665       this.areaVisible = room.isAreaVisible();
1666       this.floorVisible = room.isFloorVisible();
1667       this.floorColor = room.getFloorColor();
1668       this.floorTexture = room.getFloorTexture();
1669       this.floorShininess = room.getFloorShininess();
1670       this.ceilingVisible = room.isCeilingVisible();
1671       this.ceilingColor = room.getCeilingColor();
1672       this.ceilingTexture = room.getCeilingTexture();
1673       this.ceilingShininess = room.getCeilingShininess();
1674     }
1675 
getRoom()1676     public Room getRoom() {
1677       return this.room;
1678     }
1679 
reset()1680     public void reset() {
1681       this.room.setName(this.name);
1682       this.room.setAreaVisible(this.areaVisible);
1683       this.room.setFloorVisible(this.floorVisible);
1684       this.room.setFloorColor(this.floorColor);
1685       this.room.setFloorTexture(this.floorTexture);
1686       this.room.setFloorShininess(this.floorShininess);
1687       this.room.setCeilingVisible(this.ceilingVisible);
1688       this.room.setCeilingColor(this.ceilingColor);
1689       this.room.setCeilingTexture(this.ceilingTexture);
1690       this.room.setCeilingShininess(this.ceilingShininess);
1691     }
1692   }
1693 
1694   /**
1695    * A wall side.
1696    */
1697   private static class WallSide {
1698     public static final int LEFT_SIDE = 0;
1699     public static final int RIGHT_SIDE = 1;
1700 
1701     private Wall          wall;
1702     private int           side;
1703     private final Wall    wallAtStart;
1704     private final Wall    wallAtEnd;
1705     private final boolean joinedAtEndOfWallAtStart;
1706     private final boolean joinedAtStartOfWallAtEnd;
1707 
WallSide(Wall wall, int side)1708     public WallSide(Wall wall, int side) {
1709       this.wall = wall;
1710       this.side = side;
1711       this.wallAtStart = wall.getWallAtStart();
1712       this.joinedAtEndOfWallAtStart =
1713           this.wallAtStart != null
1714           && this.wallAtStart.getWallAtEnd() == wall;
1715       this.wallAtEnd = wall.getWallAtEnd();
1716       this.joinedAtStartOfWallAtEnd =
1717           this.wallAtEnd != null
1718           && wallAtEnd.getWallAtStart() == wall;
1719     }
1720 
getWall()1721     public Wall getWall() {
1722       return this.wall;
1723     }
1724 
getSide()1725     public int getSide() {
1726       return this.side;
1727     }
1728 
getWallAtStart()1729     public Wall getWallAtStart() {
1730       return this.wallAtStart;
1731     }
1732 
getWallAtEnd()1733     public Wall getWallAtEnd() {
1734       return this.wallAtEnd;
1735     }
1736 
isJoinedAtEndOfWallAtStart()1737     public boolean isJoinedAtEndOfWallAtStart() {
1738       return this.joinedAtEndOfWallAtStart;
1739     }
1740 
isJoinedAtStartOfWallAtEnd()1741     public boolean isJoinedAtStartOfWallAtEnd() {
1742       return this.joinedAtStartOfWallAtEnd;
1743     }
1744   }
1745 
1746   /**
1747    * A modified wall.
1748    */
1749   private static class ModifiedWall {
1750     private Wall          wall;
1751     private final Level   level;
1752     private final Wall    wallAtStart;
1753     private final Wall    wallAtEnd;
1754     private final boolean joinedAtEndOfWallAtStart;
1755     private final boolean joinedAtStartOfWallAtEnd;
1756 
ModifiedWall(Wall wall)1757     public ModifiedWall(Wall wall) {
1758       this.wall = wall;
1759       this.level = wall.getLevel();
1760       this.wallAtStart = wall.getWallAtStart();
1761       this.joinedAtEndOfWallAtStart =
1762           this.wallAtStart != null
1763           && this.wallAtStart.getWallAtEnd() == wall;
1764       this.wallAtEnd = wall.getWallAtEnd();
1765       this.joinedAtStartOfWallAtEnd =
1766           this.wallAtEnd != null
1767           && wallAtEnd.getWallAtStart() == wall;
1768     }
1769 
getWall()1770     public Wall getWall() {
1771       return this.wall;
1772     }
1773 
getLevel()1774     public Level getLevel() {
1775       return this.level;
1776     }
1777 
resetJoinedWalls()1778     public void resetJoinedWalls() {
1779       if (this.wallAtStart != null) {
1780         this.wall.setWallAtStart(this.wallAtStart);
1781         if (this.joinedAtEndOfWallAtStart) {
1782           this.wallAtStart.setWallAtEnd(this.wall);
1783         } else {
1784           this.wallAtStart.setWallAtStart(this.wall);
1785         }
1786       }
1787       if (this.wallAtEnd != null) {
1788         this.wall.setWallAtEnd(wallAtEnd);
1789         if (this.joinedAtStartOfWallAtEnd) {
1790           this.wallAtEnd.setWallAtStart(this.wall);
1791         } else {
1792           this.wallAtEnd.setWallAtEnd(this.wall);
1793         }
1794       }
1795     }
1796   }
1797 
1798   /**
1799    * Stores the current properties values of a modified wall side.
1800    */
1801   private static final class ModifiedWallSide {
1802     private final WallSide    wallSide;
1803     private final Integer     wallColor;
1804     private final HomeTexture wallTexture;
1805     private final Float       wallShininess;
1806     private final Baseboard   wallBaseboard;
1807 
ModifiedWallSide(WallSide wallSide)1808     public ModifiedWallSide(WallSide wallSide) {
1809       this.wallSide = wallSide;
1810       Wall wall = wallSide.getWall();
1811       if (wallSide.getSide() == WallSide.LEFT_SIDE) {
1812         this.wallColor = wall.getLeftSideColor();
1813         this.wallTexture = wall.getLeftSideTexture();
1814         this.wallShininess = wall.getLeftSideShininess();
1815         this.wallBaseboard = wall.getLeftSideBaseboard();
1816       } else {
1817         this.wallColor = wall.getRightSideColor();
1818         this.wallTexture = wall.getRightSideTexture();
1819         this.wallShininess = wall.getRightSideShininess();
1820         this.wallBaseboard = wall.getRightSideBaseboard();
1821       }
1822     }
1823 
getWallSide()1824     public WallSide getWallSide() {
1825       return this.wallSide;
1826     }
1827 
reset()1828     public void reset() {
1829       Wall wall = this.wallSide.getWall();
1830       if (this.wallSide.getSide() == WallSide.LEFT_SIDE) {
1831         wall.setLeftSideColor(this.wallColor);
1832         wall.setLeftSideTexture(this.wallTexture);
1833         wall.setLeftSideShininess(this.wallShininess);
1834         wall.setLeftSideBaseboard(this.wallBaseboard);
1835       } else {
1836         wall.setRightSideColor(this.wallColor);
1837         wall.setRightSideTexture(this.wallTexture);
1838         wall.setRightSideShininess(this.wallShininess);
1839         wall.setRightSideBaseboard(this.wallBaseboard);
1840       }
1841       Wall wallAtStart = wallSide.getWallAtStart();
1842       if (wallAtStart != null) {
1843         wall.setWallAtStart(wallAtStart);
1844         if (wallSide.isJoinedAtEndOfWallAtStart()) {
1845           wallAtStart.setWallAtEnd(wall);
1846         } else {
1847           wallAtStart.setWallAtStart(wall);
1848         }
1849       }
1850       Wall wallAtEnd = wallSide.getWallAtEnd();
1851       if (wallAtEnd != null) {
1852         wall.setWallAtEnd(wallAtEnd);
1853         if (wallSide.isJoinedAtStartOfWallAtEnd()) {
1854           wallAtEnd.setWallAtStart(wall);
1855         } else {
1856           wallAtEnd.setWallAtEnd(wall);
1857         }
1858       }
1859     }
1860   }
1861 }
1862