1 /*
2  * VideoController.java 15 fev 2010
3  *
4  * Sweet Home 3D, Copyright (c) 2010 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.lang.ref.WeakReference;
26 import java.util.List;
27 
28 import com.eteks.sweethome3d.model.AspectRatio;
29 import com.eteks.sweethome3d.model.Camera;
30 import com.eteks.sweethome3d.model.Home;
31 import com.eteks.sweethome3d.model.HomeEnvironment;
32 import com.eteks.sweethome3d.model.UserPreferences;
33 
34 /**
35  * The controller of the video creation view.
36  * @author Emmanuel Puybaret
37  */
38 public class VideoController implements Controller {
39   /**
40    * The properties that may be edited by the view associated to this controller.
41    */
42   public enum Property {ASPECT_RATIO, FRAME_RATE, WIDTH, HEIGHT, QUALITY, SPEED, CAMERA_PATH, TIME, CEILING_LIGHT_COLOR}
43 
44   private final Home                  home;
45   private final UserPreferences       preferences;
46   private final ViewFactory           viewFactory;
47   private final ContentManager        contentManager;
48   private final PropertyChangeSupport propertyChangeSupport;
49   private DialogView                  videoView;
50 
51   private AspectRatio                 aspectRatio;
52   private int                         frameRate;
53   private int                         width;
54   private int                         height;
55   private int                         quality;
56   private float                       speed;
57   private List<Camera>                cameraPath;
58   private long                        time;
59   private int                         ceilingLightColor;
60 
VideoController(Home home, UserPreferences preferences, ViewFactory viewFactory, ContentManager contentManager)61   public VideoController(Home home,
62                          UserPreferences preferences,
63                          ViewFactory viewFactory,
64                          ContentManager contentManager) {
65     this.home = home;
66     this.preferences = preferences;
67     this.viewFactory = viewFactory;
68     this.contentManager = contentManager;
69     this.propertyChangeSupport = new PropertyChangeSupport(this);
70 
71     updateProperties();
72     home.getEnvironment().addPropertyChangeListener(HomeEnvironment.Property.CEILING_LIGHT_COLOR, new HomeEnvironmentChangeListener(this));
73   }
74 
75   /**
76    * Home environment listener that updates properties. This listener is bound to this controller
77    * with a weak reference to avoid strong link between home and this controller.
78    */
79   private static class HomeEnvironmentChangeListener implements PropertyChangeListener {
80     private WeakReference<VideoController> videoController;
81 
HomeEnvironmentChangeListener(VideoController videoController)82     public HomeEnvironmentChangeListener(VideoController videoController) {
83       this.videoController = new WeakReference<VideoController>(videoController);
84     }
85 
propertyChange(PropertyChangeEvent ev)86     public void propertyChange(PropertyChangeEvent ev) {
87       // If controller was garbage collected, remove this listener from home
88       final VideoController controller = this.videoController.get();
89       if (controller == null) {
90         ((HomeEnvironment)ev.getSource()).removePropertyChangeListener(HomeEnvironment.Property.CEILING_LIGHT_COLOR, this);
91       } else {
92         controller.updateProperties();
93       }
94     }
95   }
96 
97   /**
98    * Returns the view associated with this controller.
99    */
getView()100   public DialogView getView() {
101     // Create view lazily only once it's needed
102     if (this.videoView == null) {
103       this.videoView = this.viewFactory.createVideoView(this.home, this.preferences, this);
104     }
105     return this.videoView;
106   }
107 
108   /**
109    * Displays the view controlled by this controller.
110    */
displayView(View parentView)111   public void displayView(View parentView) {
112     getView().displayView(parentView);
113   }
114 
115   /**
116    * Returns the content manager of this controller.
117    */
getContentManager()118   public ContentManager getContentManager() {
119     return this.contentManager;
120   }
121 
122   /**
123    * Adds the property change <code>listener</code> in parameter to this controller.
124    */
addPropertyChangeListener(Property property, PropertyChangeListener listener)125   public void addPropertyChangeListener(Property property, PropertyChangeListener listener) {
126     this.propertyChangeSupport.addPropertyChangeListener(property.name(), listener);
127   }
128 
129   /**
130    * Removes the property change <code>listener</code> in parameter from this controller.
131    */
removePropertyChangeListener(Property property, PropertyChangeListener listener)132   public void removePropertyChangeListener(Property property, PropertyChangeListener listener) {
133     this.propertyChangeSupport.removePropertyChangeListener(property.name(), listener);
134   }
135 
136   /**
137    * Updates edited properties from the video creation preferences.
138    */
updateProperties()139   protected void updateProperties() {
140     HomeEnvironment homeEnvironment = this.home.getEnvironment();
141     setFrameRate(homeEnvironment.getVideoFrameRate());
142     setWidth(homeEnvironment.getVideoWidth(), false);
143     setHeight(homeEnvironment.getVideoHeight(), false);
144     setAspectRatio(homeEnvironment.getVideoAspectRatio());
145     setQuality(homeEnvironment.getVideoQuality());
146     setSpeed(homeEnvironment.getVideoSpeed());
147     List<Camera> videoCameraPath = homeEnvironment.getVideoCameraPath();
148     setCameraPath(videoCameraPath);
149     setTime(videoCameraPath.isEmpty()
150         ? this.home.getCamera().getTime()
151         : videoCameraPath.get(0).getTime());
152     setCeilingLightColor(homeEnvironment.getCeillingLightColor());
153   }
154 
155   /**
156    * Sets the aspect ratio of the video.
157    */
setAspectRatio(AspectRatio aspectRatio)158   public void setAspectRatio(AspectRatio aspectRatio) {
159     if (this.aspectRatio != aspectRatio) {
160       AspectRatio oldAspectRatio = this.aspectRatio;
161       this.aspectRatio = aspectRatio;
162       this.propertyChangeSupport.firePropertyChange(Property.ASPECT_RATIO.name(), oldAspectRatio, aspectRatio);
163       this.home.getEnvironment().setVideoAspectRatio(this.aspectRatio);
164       setHeight(Math.round(this.width / this.aspectRatio.getValue()), false);
165     }
166   }
167 
168   /**
169    * Returns the aspect ratio of the video.
170    */
getAspectRatio()171   public AspectRatio getAspectRatio() {
172     return this.aspectRatio;
173   }
174 
175   /**
176    * Sets the frame rate of the video.
177    */
setFrameRate(int frameRate)178   public void setFrameRate(int frameRate) {
179     if (this.frameRate != frameRate) {
180       int oldFrameRate = this.frameRate;
181       this.frameRate = frameRate;
182       this.propertyChangeSupport.firePropertyChange(Property.QUALITY.name(), oldFrameRate, frameRate);
183       this.home.getEnvironment().setVideoFrameRate(this.frameRate);
184     }
185   }
186 
187   /**
188    * Returns the frame rate of the video.
189    */
getFrameRate()190   public int getFrameRate() {
191     return this.frameRate;
192   }
193 
194   /**
195    * Sets the width of the video.
196    */
setWidth(int width)197   public void setWidth(int width) {
198     setWidth(width, true);
199   }
200 
setWidth(int width, boolean updateHeight)201   private void setWidth(int width, boolean updateHeight) {
202     if (this.width != width) {
203       int oldWidth = this.width;
204       this.width = width;
205       this.propertyChangeSupport.firePropertyChange(Property.WIDTH.name(), oldWidth, width);
206       if (updateHeight) {
207         setHeight(Math.round(width / this.aspectRatio.getValue()), false);
208       }
209       this.home.getEnvironment().setVideoWidth(this.width);
210     }
211   }
212 
213   /**
214    * Returns the width of the video.
215    */
getWidth()216   public int getWidth() {
217     return this.width;
218   }
219 
220   /**
221    * Sets the height of the video.
222    */
setHeight(int height)223   public void setHeight(int height) {
224     setHeight(height, true);
225   }
226 
setHeight(int height, boolean updateWidth)227   private void setHeight(int height, boolean updateWidth) {
228     if (this.height != height) {
229       int oldHeight = this.height;
230       this.height = height;
231       this.propertyChangeSupport.firePropertyChange(Property.HEIGHT.name(), oldHeight, height);
232       if (updateWidth) {
233         setWidth(Math.round(height * this.aspectRatio.getValue()), false);
234       }
235     }
236   }
237 
238   /**
239    * Returns the height of the video.
240    */
getHeight()241   public int getHeight() {
242     return this.height;
243   }
244 
245   /**
246    * Sets the rendering quality of the video.
247    */
setQuality(int quality)248   public void setQuality(int quality) {
249     if (this.quality != quality) {
250       int oldQuality = this.quality;
251       this.quality = Math.min(quality, getQualityLevelCount() - 1);
252       this.propertyChangeSupport.firePropertyChange(Property.QUALITY.name(), oldQuality, quality);
253       this.home.getEnvironment().setVideoQuality(this.quality);
254     }
255   }
256 
257   /**
258    * Returns the rendering quality of the video.
259    */
getQuality()260   public int getQuality() {
261     return this.quality;
262   }
263 
264   /**
265    * Sets the preferred speed of movements in the video in m/s.
266    * @since 6.0
267    */
setSpeed(float speed)268   public void setSpeed(float speed) {
269     if (this.speed != speed) {
270       float oldSpeed = this.speed;
271       this.speed = speed;
272       this.propertyChangeSupport.firePropertyChange(Property.SPEED.name(), oldSpeed, speed);
273       this.home.getEnvironment().setVideoSpeed(this.speed);
274     }
275   }
276 
277   /**
278    * Returns the preferred speed of movements in the video in m/s.
279    * @since 6.0
280    */
getSpeed()281   public float getSpeed() {
282     return this.speed;
283   }
284 
285   /**
286    * Returns the maximum value for quality.
287    */
getQualityLevelCount()288   public int getQualityLevelCount() {
289     return 4;
290   }
291 
292   /**
293    * Returns the camera path of the video.
294    */
getCameraPath()295   public List<Camera> getCameraPath() {
296     return this.cameraPath;
297   }
298 
299   /**
300    * Sets the camera locations of the video.
301    */
setCameraPath(List<Camera> cameraPath)302   public void setCameraPath(List<Camera> cameraPath) {
303     if (this.cameraPath != cameraPath) {
304       List<Camera> oldCameraPath = this.cameraPath;
305       this.cameraPath = cameraPath;
306       this.propertyChangeSupport.firePropertyChange(Property.CAMERA_PATH.name(), oldCameraPath, cameraPath);
307       this.home.getEnvironment().setVideoCameraPath(this.cameraPath);
308     }
309   }
310 
311   /**
312    * Sets the edited time in UTC time zone.
313    */
setTime(long time)314   public void setTime(long time) {
315     if (this.time != time) {
316       long oldTime = this.time;
317       this.time = time;
318       this.propertyChangeSupport.firePropertyChange(Property.TIME.name(), oldTime, time);
319       this.home.getCamera().setTime(time);
320     }
321   }
322 
323   /**
324    * Returns the edited time in UTC time zone.
325    */
getTime()326   public long getTime() {
327     return this.time;
328   }
329 
330   /**
331    * Sets the edited ceiling light color.
332    */
setCeilingLightColor(int ceilingLightColor)333   public void setCeilingLightColor(int ceilingLightColor) {
334     if (this.ceilingLightColor != ceilingLightColor) {
335       int oldCeilingLightColor = this.ceilingLightColor;
336       this.ceilingLightColor = ceilingLightColor;
337       this.propertyChangeSupport.firePropertyChange(Property.CEILING_LIGHT_COLOR.name(), oldCeilingLightColor, ceilingLightColor);
338       this.home.getEnvironment().setCeillingLightColor(ceilingLightColor);
339     }
340   }
341 
342   /**
343    * Returns the edited ceiling light color.
344    */
getCeilingLightColor()345   public int getCeilingLightColor() {
346     return this.ceilingLightColor;
347   }
348 
349   /**
350    * Controls the change of value of a visual property in home.
351    * @deprecated {@link #setVisualProperty(String, Object) setVisualProperty} should be replaced by a call to
352    * {@link #setHomeProperty(String, String) setHomeProperty} to ensure the property can be easily saved and read.
353    */
setVisualProperty(String propertyName, Object propertyValue)354   public void setVisualProperty(String propertyName,
355                                 Object propertyValue) {
356     this.home.setVisualProperty(propertyName, propertyValue);
357   }
358 
359   /**
360    * Controls the change of value of a property in home.
361    * @since 5.2
362    */
setHomeProperty(String propertyName, String propertyValue)363   public void setHomeProperty(String propertyName,
364                                 String propertyValue) {
365     this.home.setProperty(propertyName, propertyValue);
366   }
367 }
368