001 /* 002 * EditorState 003 * 004 * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.apps; 009 010 import java.io.IOException; 011 import java.util.Locale; 012 import java.util.Vector; 013 import net.sourceforge.jiu.data.PixelImage; 014 import net.sourceforge.jiu.ops.Operation; 015 import net.sourceforge.jiu.ops.ProgressListener; 016 017 /** 018 * Represents the state of the editor, including image(s), modified flag, 019 * current file name and directories and more. 020 * This class must not know GUI-specific information like Frame or JFrame objects. 021 * These GUI classes (more precisely, the JIU classes that extend them) will have to 022 * know EditorState and update according to the information they retrieve from an 023 * EditorState object associated with them. 024 * EditorState is a pure data container. 025 * @author Marco Schmidt 026 */ 027 public class EditorState implements MenuIndexConstants 028 { 029 /** 030 * The default number of undo steps possible. 031 */ 032 public static final int DEFAULT_MAX_UNDO_IMAGES = 2; 033 034 /** 035 * The default number of redo steps possible. 036 */ 037 public static final int DEFAULT_MAX_REDO_IMAGES = DEFAULT_MAX_UNDO_IMAGES; 038 039 /** 040 * All allowed zoom levels, as percentage values in ascending order. 041 */ 042 public static final int[] ZOOM_LEVELS = {5, 7, 10, 15, 20, 30, 50, 70, 100, 150, 200, 300, 500, 700, 1000, 2000, 3000, 5000}; 043 044 /** 045 * The index into the {@link #ZOOM_LEVELS} array that holds the original size zoom level (100 percent). 046 * So, ZOOM_LEVELS[ORIGINAL_SIZE_ZOOM_INDEX] must be equal to 100. 047 */ 048 public static final int ORIGINAL_SIZE_ZOOM_INDEX = 8; 049 050 /** 051 * Integer constant for <em>nearest neighbor interpolation</em>. 052 * A fast but ugly method. 053 */ 054 public static final int INTERPOLATION_NEAREST_NEIGHBOR = 0; 055 056 /** 057 * Integer constant for <em>bilinear neighbor interpolation</em>. 058 * A slow but nice method. 059 */ 060 public static final int INTERPOLATION_BILINEAR = 1; 061 062 /** 063 * Integer constant for <em>bicubic interpolation</em>. 064 * A very slow method, but with the nicest output of the three supported interpolation types. 065 */ 066 public static final int INTERPOLATION_BICUBIC = 2; 067 068 /** 069 * The default interpolation type, one of the three INTERPOLATION_xyz constants. 070 */ 071 public static final int DEFAULT_INTERPOLATION = INTERPOLATION_NEAREST_NEIGHBOR; 072 private String currentDirectory; 073 private String fileName; 074 private PixelImage currentImage; 075 private int interpolation; 076 private Locale locale; 077 private int maxRedoImages; 078 private int maxUndoImages; 079 private boolean modified; 080 private Vector progressListeners; 081 private Vector redoImages; 082 private Vector redoModified; 083 private String startupImageName; 084 private Strings strings; 085 private Vector undoImages; 086 private Vector undoModified; 087 private int zoomIndex = ORIGINAL_SIZE_ZOOM_INDEX; 088 private double zoomFactorX; 089 private double zoomFactorY; 090 private boolean zoomToFit; 091 092 /** 093 * Create new EditorState object and initialize its private fields 094 * to default values. 095 */ 096 public EditorState() 097 { 098 locale = Locale.getDefault(); 099 setStrings(null); 100 progressListeners = new Vector(); 101 maxRedoImages = DEFAULT_MAX_REDO_IMAGES; 102 maxUndoImages = DEFAULT_MAX_UNDO_IMAGES; 103 redoImages = new Vector(maxRedoImages); 104 redoModified = new Vector(maxRedoImages); 105 undoImages = new Vector(maxUndoImages); 106 undoModified = new Vector(maxUndoImages); 107 zoomFactorX = 1.0; 108 zoomFactorY = 1.0; 109 zoomToFit = false; 110 } 111 112 private void addImageToRedo(PixelImage image, boolean modifiedState) 113 { 114 if (maxRedoImages < 1) 115 { 116 return; 117 } 118 if (redoImages.size() == maxRedoImages) 119 { 120 redoImages.setElementAt(null, 0); 121 redoImages.removeElementAt(0); 122 redoModified.removeElementAt(0); 123 } 124 redoImages.addElement(image); 125 redoModified.addElement(new Boolean(modifiedState)); 126 } 127 128 private void addImageToUndo(PixelImage image, boolean modifiedState) 129 { 130 if (maxUndoImages < 1) 131 { 132 return; 133 } 134 if (undoImages.size() == maxUndoImages) 135 { 136 undoImages.setElementAt(null, 0); 137 undoImages.removeElementAt(0); 138 undoModified.removeElementAt(0); 139 } 140 undoImages.addElement(image); 141 undoModified.addElement(new Boolean(modifiedState)); 142 } 143 144 /** 145 * Adds the argument progress listener to the internal list of progress 146 * listeners to be notified by progress updates. 147 * @param pl object implementing ProgressListener to be added 148 */ 149 public void addProgressListener(ProgressListener pl) 150 { 151 progressListeners.addElement(pl); 152 } 153 154 /** 155 * Returns if a redo operation is possible right now. 156 */ 157 public boolean canRedo() 158 { 159 return (redoImages.size() > 0); 160 } 161 162 /** 163 * Returns if an undo operation is possible right now. 164 */ 165 public boolean canUndo() 166 { 167 return (undoImages.size() > 0); 168 } 169 170 public void clearRedo() 171 { 172 int index = 0; 173 while (index < redoImages.size()) 174 { 175 redoImages.setElementAt(null, index++); 176 } 177 redoImages.setSize(0); 178 redoModified.setSize(0); 179 } 180 181 public void clearUndo() 182 { 183 int index = 0; 184 while (index < undoImages.size()) 185 { 186 undoImages.setElementAt(null, index++); 187 } 188 undoImages.setSize(0); 189 undoModified.setSize(0); 190 } 191 192 public void ensureStringsAvailable() 193 { 194 if (getStrings() == null) 195 { 196 setStrings(Strings.DEFAULT_LANGUAGE_ISO_639_CODE); 197 } 198 } 199 200 /** 201 * Returns the current directory. 202 * This directory will be used when file dialogs are opened. 203 */ 204 public String getCurrentDirectory() 205 { 206 return currentDirectory; 207 } 208 209 /** 210 * Returns the name of the file from which the current image was loaded. 211 */ 212 public String getFileName() 213 { 214 return fileName; 215 } 216 217 /** 218 * Returns the image object currently loaded. 219 */ 220 public PixelImage getImage() 221 { 222 return currentImage; 223 } 224 225 /** 226 * Returns the current interpolation type, one of the INTERPOLATION_xyz constants. 227 */ 228 public int getInterpolation() 229 { 230 return interpolation; 231 } 232 233 /** 234 * Returns the Locale object currently used. 235 */ 236 public Locale getLocale() 237 { 238 return locale; 239 } 240 241 /** 242 * Returns the current modified state (true if image was modified and not saved 243 * after modification, false otherwise). 244 */ 245 public boolean getModified() 246 { 247 return modified; 248 } 249 250 /** 251 * Returns the internal list of progress listeners. 252 */ 253 public Vector getProgressListeners() 254 { 255 return progressListeners; 256 } 257 258 public String getStartupImageName() 259 { 260 return startupImageName; 261 } 262 263 /** 264 * Returns the Strings object currently in use. 265 */ 266 public Strings getStrings() 267 { 268 return strings; 269 } 270 271 /** 272 * Returns the current zoom factor in horizontal direction. 273 * The value 1.0 means that the image is displayed at its 274 * original size. 275 * Anything smaller means that the image is scaled down, 276 * anything larger means that the image is scaled up. 277 * The value must not be smaller than or equal to 0.0. 278 * @return zoom factor in horizontal direction 279 * @see #getZoomFactorY 280 */ 281 public double getZoomFactorX() 282 { 283 return zoomFactorX; 284 } 285 286 /** 287 * Returns the current zoom factor in vertical direction. 288 * The value 1.0 means that the image is displayed at its 289 * original size. 290 * Anything smaller means that the image is scaled down, 291 * anything larger means that the image is scaled up. 292 * The value must not be smaller than or equal to 0.0. 293 * @return zoom factor in vertical direction 294 * @see #getZoomFactorX 295 */ 296 public double getZoomFactorY() 297 { 298 return zoomFactorY; 299 } 300 301 /** 302 * Returns if image display is currently set to "zoom to fit" 303 * Zoom to fit means that the image is always zoomed to fit exactly into the window. 304 */ 305 public boolean getZoomToFit() 306 { 307 return zoomToFit; 308 } 309 310 /** 311 * Returns if this state encapsulates an image object. 312 */ 313 public boolean hasImage() 314 { 315 return (currentImage != null); 316 } 317 318 /** 319 * Adds all ProgressListener objects from the internal list of listeners to 320 * the argument operation. 321 */ 322 public void installProgressListeners(Operation op) 323 { 324 if (op == null) 325 { 326 return; 327 } 328 // cannot use Iterator because it's 1.2+ 329 int index = 0; 330 while (index < progressListeners.size()) 331 { 332 ProgressListener pl = (ProgressListener)progressListeners.elementAt(index++); 333 op.addProgressListener(pl); 334 } 335 } 336 337 /** 338 * Returns if the image is displayed at maximum zoom level. 339 */ 340 public boolean isMaximumZoom() 341 { 342 return zoomIndex == ZOOM_LEVELS.length - 1; 343 } 344 345 /** 346 * Returns if the image is displayed at minimum zoom level. 347 */ 348 public boolean isMinimumZoom() 349 { 350 return zoomIndex == 0; 351 } 352 353 /** 354 * Returns if the current zoom level is set to original size 355 * (each image pixel is displayed as one pixel). 356 */ 357 public boolean isZoomOriginalSize() 358 { 359 return zoomIndex == ORIGINAL_SIZE_ZOOM_INDEX; 360 } 361 362 /** 363 * Perform a redo operation, restore the state before the last undo operation. 364 * Before that is done, save the current state for an undo. 365 */ 366 public void redo() 367 { 368 if (redoImages.size() < 1) 369 { 370 return; 371 } 372 addImageToUndo(currentImage, modified); 373 int redoIndex = redoImages.size() - 1; 374 currentImage = (PixelImage)redoImages.elementAt(redoIndex); 375 redoImages.setElementAt(null, redoIndex); 376 redoImages.setSize(redoIndex); 377 modified = ((Boolean)redoModified.elementAt(redoIndex)).booleanValue(); 378 redoModified.setSize(redoIndex); 379 } 380 381 public void resetZoomFactors() 382 { 383 setZoomFactors(1.0, 1.0); 384 } 385 386 /** 387 * Sets a new current directory. 388 * @param newCurrentDirectory the directory to be used as current directory from now on 389 */ 390 public void setCurrentDirectory(String newCurrentDirectory) 391 { 392 currentDirectory = newCurrentDirectory; 393 } 394 395 /** 396 * Sets a new file name. 397 * This is used mostly after a new image was loaded from a file or 398 * if the current image is closed (then a null value would be given to this method). 399 * @param newFileName new name of the current file 400 */ 401 public void setFileName(String newFileName) 402 { 403 fileName = newFileName; 404 } 405 406 /** 407 * Sets image and modified state to argument values. 408 * @param image new current image 409 * @param newModifiedState new state of modified flag 410 */ 411 public void setImage(PixelImage image, boolean newModifiedState) 412 { 413 if (hasImage()) 414 { 415 addImageToUndo(currentImage, modified); 416 } 417 currentImage = image; 418 modified = newModifiedState; 419 clearRedo(); 420 } 421 422 public void setStartupImageName(String name) 423 { 424 startupImageName = name; 425 } 426 427 /** 428 * Sets a new interpolation type to be used for display. 429 * @param newInterpolation an int for the interpolation type, must be one of the INTERPOLATION_xyz constants 430 */ 431 public void setInterpolation(int newInterpolation) 432 { 433 if (newInterpolation == INTERPOLATION_NEAREST_NEIGHBOR || 434 newInterpolation == INTERPOLATION_BILINEAR || 435 newInterpolation == INTERPOLATION_BICUBIC) 436 { 437 interpolation = newInterpolation; 438 } 439 } 440 441 /** 442 * Defines a new Locale to be used. 443 * @param newLocale Locale object used from now on 444 * @see #setStrings 445 */ 446 public void setLocale(Locale newLocale) 447 { 448 locale = newLocale; 449 } 450 451 /*public void setModified(boolean modifiedState) 452 { 453 modified = modifiedState; 454 }*/ 455 456 /** 457 * Set new Strings resource. 458 * @param iso639Code language of the new Strings resource 459 */ 460 public void setStrings(String iso639Code) 461 { 462 Strings newStrings = null; 463 try 464 { 465 StringLoader loader; 466 if (iso639Code == null) 467 { 468 loader = new StringLoader(); 469 } 470 else 471 { 472 loader = new StringLoader(iso639Code); 473 } 474 newStrings = loader.load(); 475 } 476 catch (IOException ioe) 477 { 478 } 479 if (newStrings != null) 480 { 481 strings = newStrings; 482 } 483 } 484 485 /** 486 * Sets the zoom factors to the argument values. 487 */ 488 public void setZoomFactors(double zoomX, double zoomY) 489 { 490 zoomFactorX = zoomX; 491 zoomFactorY = zoomY; 492 } 493 494 /** 495 * Perform an undo step - the previous state will be set, the 496 * current state will be saved for a redo operation 497 * @see #redo 498 */ 499 public void undo() 500 { 501 if (undoImages.size() < 1) 502 { 503 return; 504 } 505 addImageToRedo(currentImage, modified); 506 int undoIndex = undoImages.size() - 1; 507 currentImage = (PixelImage)undoImages.elementAt(undoIndex); 508 undoImages.setElementAt(null, undoIndex); 509 undoImages.setSize(undoIndex); 510 modified = ((Boolean)undoModified.elementAt(undoIndex)).booleanValue(); 511 undoModified.setSize(undoIndex); 512 } 513 514 /** 515 * Increase the zoom level by one. 516 * @see #zoomOut 517 * @see #zoomSetOriginalSize 518 */ 519 public void zoomIn() 520 { 521 if (zoomIndex + 1 == ZOOM_LEVELS.length) 522 { 523 return; 524 } 525 zoomIndex++; 526 zoomFactorX = 1.0 * ZOOM_LEVELS[zoomIndex] / 100; 527 zoomFactorY = zoomFactorX; 528 } 529 530 /** 531 * Decrease the zoom level by one. 532 * @see #zoomIn 533 * @see #zoomSetOriginalSize 534 */ 535 public void zoomOut() 536 { 537 if (zoomIndex == 0) 538 { 539 return; 540 } 541 zoomIndex--; 542 zoomFactorX = 1.0 * ZOOM_LEVELS[zoomIndex] / 100; 543 zoomFactorY = zoomFactorX; 544 } 545 546 /** 547 * Set the zoom level to 100 percent (1:1). 548 * Each image pixel will be displayed as one pixel 549 * @see #zoomIn 550 * @see #zoomOut 551 */ 552 public void zoomSetOriginalSize() 553 { 554 zoomIndex = ORIGINAL_SIZE_ZOOM_INDEX; 555 zoomFactorX = 1.0; 556 zoomFactorY = 1.0; 557 } 558 }