/* * BackgroundImageWizardController.java 8 juin 07 * * Sweet Home 3D, Copyright (c) 2007 Emmanuel PUYBARET / eTeks <info@eteks.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.eteks.sweethome3d.viewcontroller; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.net.URL; import javax.swing.undo.AbstractUndoableEdit; import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; import javax.swing.undo.UndoableEdit; import javax.swing.undo.UndoableEditSupport; import com.eteks.sweethome3d.model.BackgroundImage; import com.eteks.sweethome3d.model.Content; import com.eteks.sweethome3d.model.Home; import com.eteks.sweethome3d.model.Level; import com.eteks.sweethome3d.model.UserPreferences; /** * Wizard controller for background image in plan. * @author Emmanuel Puybaret */ public class BackgroundImageWizardController extends WizardController implements Controller { public enum Property {STEP, IMAGE, SCALE_DISTANCE, SCALE_DISTANCE_POINTS, X_ORIGIN, Y_ORIGIN} public enum Step {CHOICE, SCALE, ORIGIN}; private final Home home; private final UserPreferences preferences; private final ViewFactory viewFactory; private final ContentManager contentManager; private final UndoableEditSupport undoSupport; private final PropertyChangeSupport propertyChangeSupport; private final BackgroundImageWizardStepState imageChoiceStepState; private final BackgroundImageWizardStepState imageScaleStepState; private final BackgroundImageWizardStepState imageOriginStepState; private View stepsView; private Step step; private Content image; private Float scaleDistance; private float scaleDistanceXStart; private float scaleDistanceYStart; private float scaleDistanceXEnd; private float scaleDistanceYEnd; private float xOrigin; private float yOrigin; public BackgroundImageWizardController(Home home, UserPreferences preferences, ViewFactory viewFactory, ContentManager contentManager, UndoableEditSupport undoSupport) { super(preferences, viewFactory); this.home = home; this.preferences = preferences; this.viewFactory = viewFactory; this.contentManager = contentManager; this.undoSupport = undoSupport; this.propertyChangeSupport = new PropertyChangeSupport(this); setTitle(preferences.getLocalizedString(BackgroundImageWizardController.class, "wizard.title")); setResizable(true); // Initialize states this.imageChoiceStepState = new ImageChoiceStepState(); this.imageScaleStepState = new ImageScaleStepState(); this.imageOriginStepState = new ImageOriginStepState(); setStepState(this.imageChoiceStepState); } /** * Changes background image in model and posts an undoable operation. */ @Override public void finish() { Level selectedLevel = this.home.getSelectedLevel(); BackgroundImage oldImage = selectedLevel != null ? selectedLevel.getBackgroundImage() : this.home.getBackgroundImage(); float [][] scaleDistancePoints = getScaleDistancePoints(); BackgroundImage image = new BackgroundImage(getImage(), getScaleDistance(), scaleDistancePoints [0][0], scaleDistancePoints [0][1], scaleDistancePoints [1][0], scaleDistancePoints [1][1], getXOrigin(), getYOrigin()); if (selectedLevel != null) { selectedLevel.setBackgroundImage(image); } else { this.home.setBackgroundImage(image); } boolean modification = oldImage == null; UndoableEdit undoableEdit = new BackgroundImageUndoableEdit(this.home, selectedLevel, this.preferences, modification, oldImage, image); this.undoSupport.postEdit(undoableEdit); } /** * Undoable edit for background image. This class isn't anonymous to avoid * being bound to controller and its view. */ private static class BackgroundImageUndoableEdit extends AbstractUndoableEdit { private final Home home; private final Level level; private final UserPreferences preferences; private final boolean modification; private final BackgroundImage oldImage; private final BackgroundImage image; private BackgroundImageUndoableEdit(Home home, Level level, UserPreferences preferences, boolean modification, BackgroundImage oldImage, BackgroundImage image) { this.home = home; this.level = level; this.preferences = preferences; this.modification = modification; this.oldImage = oldImage; this.image = image; } @Override public void undo() throws CannotUndoException { super.undo(); this.home.setSelectedLevel(this.level); if (this.level != null) { this.level.setBackgroundImage(this.oldImage); } else { this.home.setBackgroundImage(this.oldImage); } } @Override public void redo() throws CannotRedoException { super.redo(); this.home.setSelectedLevel(this.level); if (this.level != null) { this.level.setBackgroundImage(this.image); } else { this.home.setBackgroundImage(this.image); } } @Override public String getPresentationName() { return this.preferences.getLocalizedString(BackgroundImageWizardController.class, this.modification ? "undoImportBackgroundImageName" : "undoModifyBackgroundImageName"); } } /** * Returns the content manager of this controller. */ public ContentManager getContentManager() { return this.contentManager; } /** * Returns the current step state. */ @Override protected BackgroundImageWizardStepState getStepState() { return (BackgroundImageWizardStepState)super.getStepState(); } /** * Returns the image choice step state. */ protected BackgroundImageWizardStepState getImageChoiceStepState() { return this.imageChoiceStepState; } /** * Returns the image origin step state. */ protected BackgroundImageWizardStepState getImageOriginStepState() { return this.imageOriginStepState; } /** * Returns the image scale step state. */ protected BackgroundImageWizardStepState getImageScaleStepState() { return this.imageScaleStepState; } /** * Returns the unique wizard view used for all steps. */ protected View getStepsView() { // Create view lazily only once it's needed if (this.stepsView == null) { BackgroundImage image = this.home.getSelectedLevel() != null ? this.home.getSelectedLevel().getBackgroundImage() : this.home.getBackgroundImage(); this.stepsView = this.viewFactory.createBackgroundImageWizardStepsView( image, this.preferences, this); } return this.stepsView; } /** * Switch in the wizard view to the given <code>step</code>. */ protected void setStep(Step step) { if (step != this.step) { Step oldStep = this.step; this.step = step; this.propertyChangeSupport.firePropertyChange(Property.STEP.name(), oldStep, step); } } /** * Returns the current step in wizard view. */ public Step getStep() { return this.step; } /** * Adds the property change <code>listener</code> in parameter to this controller. */ public void addPropertyChangeListener(Property property, PropertyChangeListener listener) { this.propertyChangeSupport.addPropertyChangeListener(property.name(), listener); } /** * Removes the property change <code>listener</code> in parameter from this controller. */ public void removePropertyChangeListener(Property property, PropertyChangeListener listener) { this.propertyChangeSupport.removePropertyChangeListener(property.name(), listener); } /** * Sets the image content of the background image. */ public void setImage(Content image) { if (image != this.image) { Content oldImage = this.image; this.image = image; this.propertyChangeSupport.firePropertyChange(Property.IMAGE.name(), oldImage, image); } } /** * Returns the image content of the background image. */ public Content getImage() { return this.image; } /** * Sets the scale distance of the background image. */ public void setScaleDistance(Float scaleDistance) { if (scaleDistance != this.scaleDistance) { Float oldScaleDistance = this.scaleDistance; this.scaleDistance = scaleDistance; this.propertyChangeSupport.firePropertyChange( Property.SCALE_DISTANCE.name(), oldScaleDistance, scaleDistance); } } /** * Returns the scale distance of the background image. */ public Float getScaleDistance() { return this.scaleDistance; } /** * Sets the coordinates of the scale distance points of the background image. */ public void setScaleDistancePoints(float scaleDistanceXStart, float scaleDistanceYStart, float scaleDistanceXEnd, float scaleDistanceYEnd) { if (scaleDistanceXStart != this.scaleDistanceXStart || scaleDistanceYStart != this.scaleDistanceYStart || scaleDistanceXEnd != this.scaleDistanceXEnd || scaleDistanceYEnd != this.scaleDistanceYEnd) { float [][] oldDistancePoints = new float [][] {{this.scaleDistanceXStart, this.scaleDistanceYStart}, {this.scaleDistanceXEnd, this.scaleDistanceYEnd}}; this.scaleDistanceXStart = scaleDistanceXStart; this.scaleDistanceYStart = scaleDistanceYStart; this.scaleDistanceXEnd = scaleDistanceXEnd; this.scaleDistanceYEnd = scaleDistanceYEnd; this.propertyChangeSupport.firePropertyChange( Property.SCALE_DISTANCE.name(), oldDistancePoints, new float [][] {{scaleDistanceXStart, scaleDistanceYStart}, {scaleDistanceXEnd, scaleDistanceYEnd}}); } } /** * Returns the coordinates of the scale distance points of the background image. */ public float [][] getScaleDistancePoints() { return new float [][] {{this.scaleDistanceXStart, this.scaleDistanceYStart}, {this.scaleDistanceXEnd, this.scaleDistanceYEnd}}; } /** * Sets the origin of the background image. */ public void setOrigin(float xOrigin, float yOrigin) { if (xOrigin != this.xOrigin) { Float oldXOrigin = this.xOrigin; this.xOrigin = xOrigin; this.propertyChangeSupport.firePropertyChange( Property.X_ORIGIN.name(), oldXOrigin, xOrigin); } if (yOrigin != this.yOrigin) { Float oldYOrigin = this.yOrigin; this.yOrigin = yOrigin; this.propertyChangeSupport.firePropertyChange( Property.Y_ORIGIN.name(), oldYOrigin, yOrigin); } } /** * Returns the abcissa of the origin of the background image. */ public float getXOrigin() { return this.xOrigin; } /** * Returns the ordinate of the origin of the background image. */ public float getYOrigin() { return this.yOrigin; } /** * Step state superclass. All step state share the same step view, * that will display a different component depending on their class name. */ protected abstract class BackgroundImageWizardStepState extends WizardControllerStepState { private URL icon = BackgroundImageWizardController.class.getResource("resources/backgroundImageWizard.png"); public abstract Step getStep(); @Override public void enter() { setStep(getStep()); } @Override public View getView() { return getStepsView(); } @Override public URL getIcon() { return this.icon; } } /** * Image choice step state (first step). */ private class ImageChoiceStepState extends BackgroundImageWizardStepState { public ImageChoiceStepState() { BackgroundImageWizardController.this.addPropertyChangeListener(Property.IMAGE, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { setNextStepEnabled(getImage() != null); } }); } @Override public void enter() { super.enter(); setFirstStep(true); setNextStepEnabled(getImage() != null); } @Override public Step getStep() { return Step.CHOICE; } @Override public void goToNextStep() { setStepState(getImageScaleStepState()); } } /** * Image scale step state (second step). */ private class ImageScaleStepState extends BackgroundImageWizardStepState { public ImageScaleStepState() { BackgroundImageWizardController.this.addPropertyChangeListener(Property.SCALE_DISTANCE, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { setNextStepEnabled(getScaleDistance() != null); } }); } @Override public void enter() { super.enter(); setNextStepEnabled(getScaleDistance() != null); } @Override public Step getStep() { return Step.SCALE; } @Override public void goBackToPreviousStep() { setStepState(getImageChoiceStepState()); } @Override public void goToNextStep() { setStepState(getImageOriginStepState()); } } /** * Image origin step state (last step). */ private class ImageOriginStepState extends BackgroundImageWizardStepState { @Override public void enter() { super.enter(); setLastStep(true); // Last step is always valid by default setNextStepEnabled(true); } @Override public Step getStep() { return Step.ORIGIN; } @Override public void goBackToPreviousStep() { setStepState(getImageScaleStepState()); } } }