001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package icy.undo; 020 021import icy.resource.ResourceUtil; 022import icy.resource.icon.IcyIcon; 023import icy.util.StringUtil; 024 025import java.awt.Image; 026 027import javax.swing.UIManager; 028import javax.swing.undo.CannotRedoException; 029import javax.swing.undo.CannotUndoException; 030import javax.swing.undo.UndoableEdit; 031 032/** 033 * Abstract Icy {@link UndoableEdit} class. 034 * 035 * @author Stephane 036 */ 037public abstract class AbstractIcyUndoableEdit implements IcyUndoableEdit 038{ 039 protected static final IcyIcon DEFAULT_ICON = new IcyIcon(ResourceUtil.ICON_LIGHTING, 16); 040 041 /** 042 * Source of the UndoableEdit 043 */ 044 protected Object source; 045 046 /** 047 * Defaults to true; becomes false if this edit is undone, true 048 * again if it is redone. 049 */ 050 protected boolean hasBeenDone; 051 052 /** 053 * True if this edit has not received <code>die</code>; defaults 054 * to <code>true</code>. 055 */ 056 protected boolean alive; 057 058 /** 059 * Used to recognize the edit in the undo manager panel 060 */ 061 protected IcyIcon icon; 062 063 /** 064 * Representation name in the history panel 065 */ 066 protected String presentationName; 067 068 /** 069 * Mergeable property of this edit 070 */ 071 protected boolean mergeable; 072 073 /** 074 * Creates an <code>UndoableAction</code> which defaults <code>hasBeenDone</code> and 075 * <code>alive</code> to <code>true</code>. 076 */ 077 public AbstractIcyUndoableEdit(Object source, String name, Image icon) 078 { 079 super(); 080 081 // this.source = new WeakReference<Object>(source); 082 this.source = source; 083 hasBeenDone = true; 084 alive = true; 085 086 if (icon != null) 087 this.icon = new IcyIcon(icon, 16); 088 else 089 this.icon = DEFAULT_ICON; 090 presentationName = name; 091 // by default collapse operation is supported 092 mergeable = true; 093 } 094 095 /** 096 * Creates an <code>UndoableAction</code> which defaults <code>hasBeenDone</code> and 097 * <code>alive</code> to <code>true</code>. 098 */ 099 public AbstractIcyUndoableEdit(Object source, String name) 100 { 101 this(source, name, null); 102 } 103 104 /** 105 * Creates an <code>UndoableAction</code> which defaults <code>hasBeenDone</code> and 106 * <code>alive</code> to <code>true</code>. 107 */ 108 public AbstractIcyUndoableEdit(Object source, Image icon) 109 { 110 this(source, "", icon); 111 } 112 113 /** 114 * Creates an <code>UndoableAction</code> which defaults <code>hasBeenDone</code> and 115 * <code>alive</code> to <code>true</code>. 116 */ 117 public AbstractIcyUndoableEdit(Object source) 118 { 119 this(source, "", null); 120 } 121 122 @Override 123 public Object getSource() 124 { 125 return source; 126 } 127 128 @Override 129 public IcyIcon getIcon() 130 { 131 return icon; 132 } 133 134 /** 135 * Sets <code>alive</code> to false. Note that this is a one way operation; dead edits cannot be 136 * resurrected.<br> 137 * Sending <code>undo</code> or <code>redo</code> to a dead edit results in an exception being 138 * thrown. 139 * <p> 140 * Typically an edit is killed when it is consolidated by another edit's <code>addEdit</code> or 141 * <code>replaceEdit</code> method, or when it is dequeued from an <code>UndoManager</code>. 142 */ 143 @Override 144 public void die() 145 { 146 alive = false; 147 148 // remove source reference 149 source = null; 150 } 151 152 /** 153 * Throws <code>CannotUndoException</code> if <code>canUndo</code> returns <code>false</code>. 154 * Sets <code>hasBeenDone</code> to <code>false</code>. Subclasses should override to undo the 155 * operation represented by this edit. Override should begin with 156 * a call to super. 157 * 158 * @exception CannotUndoException 159 * if <code>canUndo</code> returns <code>false</code> 160 * @see #canUndo 161 */ 162 @Override 163 public void undo() throws CannotUndoException 164 { 165 if (!canUndo()) 166 throw new CannotUndoException(); 167 168 hasBeenDone = false; 169 } 170 171 @Override 172 public boolean canUndo() 173 { 174 return alive && hasBeenDone; 175 } 176 177 /** 178 * Throws <code>CannotRedoException</code> if <code>canRedo</code> returns false. Sets 179 * <code>hasBeenDone</code> to <code>true</code>. 180 * Subclasses should override to redo the operation represented by 181 * this edit. Override should begin with a call to super. 182 * 183 * @exception CannotRedoException 184 * if <code>canRedo</code> returns <code>false</code> 185 * @see #canRedo 186 */ 187 @Override 188 public void redo() throws CannotRedoException 189 { 190 if (!canRedo()) 191 throw new CannotRedoException(); 192 193 hasBeenDone = true; 194 } 195 196 @Override 197 public boolean canRedo() 198 { 199 return alive && !hasBeenDone; 200 } 201 202 /* 203 * This default implementation returns false. 204 */ 205 @Override 206 public boolean addEdit(UndoableEdit anEdit) 207 { 208 return false; 209 } 210 211 /* 212 * This default implementation returns false. 213 */ 214 @Override 215 public boolean replaceEdit(UndoableEdit anEdit) 216 { 217 return false; 218 } 219 220 /* 221 * This default implementation returns true. 222 */ 223 @Override 224 final public boolean isSignificant() 225 { 226 // should always returns true for easier UndoManager manipulation 227 return true; 228 } 229 230 @Override 231 public boolean isMergeable() 232 { 233 return mergeable; 234 } 235 236 public void setMergeable(boolean value) 237 { 238 mergeable = value; 239 } 240 241 /** 242 * This default implementation returns "". Used by <code>getUndoPresentationName</code> and 243 * <code>getRedoPresentationName</code> to 244 * construct the strings they return. Subclasses should override to 245 * return an appropriate description of the operation this edit 246 * represents. 247 * 248 * @return the empty string "" 249 * @see #getUndoPresentationName 250 * @see #getRedoPresentationName 251 */ 252 @Override 253 public String getPresentationName() 254 { 255 return presentationName; 256 } 257 258 /** 259 * Retrieves the value from the defaults table with key 260 * <code>AbstractUndoableEdit.undoText</code> and returns 261 * that value followed by a space, followed by <code>getPresentationName</code>. 262 * If <code>getPresentationName</code> returns "", 263 * then the defaults value is returned alone. 264 * 265 * @return the value from the defaults table with key <code>AbstractUndoableEdit.undoText</code> 266 * , followed 267 * by a space, followed by <code>getPresentationName</code> unless 268 * <code>getPresentationName</code> is "" in which 269 * case, the defaults value is returned alone. 270 * @see #getPresentationName 271 */ 272 @Override 273 public String getUndoPresentationName() 274 { 275 String name = getPresentationName(); 276 277 if (!StringUtil.isEmpty(name)) 278 name = UIManager.getString("AbstractUndoableEdit.undoText") + " " + name; 279 else 280 name = UIManager.getString("AbstractUndoableEdit.undoText"); 281 282 return name; 283 } 284 285 /** 286 * Retrieves the value from the defaults table with key 287 * <code>AbstractUndoableEdit.redoText</code> and returns 288 * that value followed by a space, followed by <code>getPresentationName</code>. 289 * If <code>getPresentationName</code> returns "", 290 * then the defaults value is returned alone. 291 * 292 * @return the value from the defaults table with key <code>AbstractUndoableEdit.redoText</code> 293 * , followed 294 * by a space, followed by <code>getPresentationName</code> unless 295 * <code>getPresentationName</code> is "" in which 296 * case, the defaults value is returned alone. 297 * @see #getPresentationName 298 */ 299 @Override 300 public String getRedoPresentationName() 301 { 302 String name = getPresentationName(); 303 304 if (!StringUtil.isEmpty(name)) 305 name = UIManager.getString("AbstractUndoableEdit.redoText") + " " + name; 306 else 307 name = UIManager.getString("AbstractUndoableEdit.redoText"); 308 309 return name; 310 } 311 312 /** 313 * Returns a string that displays and identifies this 314 * object's properties. 315 * 316 * @return a String representation of this object 317 */ 318 @Override 319 public String toString() 320 { 321 return super.toString() + " hasBeenDone: " + hasBeenDone + " alive: " + alive; 322 } 323 324 // /** 325 // * Update live state of the edit.<br> 326 // * For instance, an edit attached to a Plugin should probably die when the plugin is 327 // closed.<br> 328 // * Returns false if the edit should die, true otherwise. 329 // */ 330 // public abstract boolean updateLiveState(); 331 // { 332 // // no more reference on source --> die 333 // if (getSource() == null) 334 // die(); 335 // } 336}