1 /* 2 * UserPreferences.java 15 mai 2006 3 * 4 * Sweet Home 3D, Copyright (c) 2006 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.model; 21 22 import java.beans.PropertyChangeListener; 23 import java.beans.PropertyChangeSupport; 24 import java.io.IOException; 25 import java.io.InputStream; 26 import java.math.BigDecimal; 27 import java.security.AccessControlException; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.Collections; 31 import java.util.Enumeration; 32 import java.util.HashMap; 33 import java.util.Iterator; 34 import java.util.LinkedHashMap; 35 import java.util.List; 36 import java.util.Locale; 37 import java.util.Map; 38 import java.util.MissingResourceException; 39 import java.util.Properties; 40 import java.util.PropertyPermission; 41 import java.util.PropertyResourceBundle; 42 import java.util.ResourceBundle; 43 44 /** 45 * User preferences. 46 * @author Emmanuel Puybaret 47 */ 48 public abstract class UserPreferences { 49 /** 50 * The properties of user preferences that may change. <code>PropertyChangeListener</code>s added 51 * to user preferences will be notified under a property name equal to the string value of one these properties. 52 */ 53 public enum Property {LANGUAGE, SUPPORTED_LANGUAGES, UNIT, CURRENCY, VALUE_ADDED_TAX_ENABLED, DEFAULT_VALUE_ADDED_TAX_PERCENTAGE, 54 MAGNETISM_ENABLED, RULERS_VISIBLE, GRID_VISIBLE, DEFAULT_FONT_NAME, 55 FURNITURE_VIEWED_FROM_TOP, FURNITURE_MODEL_ICON_SIZE, ROOM_FLOOR_COLORED_OR_TEXTURED, WALL_PATTERN, NEW_WALL_PATTERN, 56 NEW_WALL_THICKNESS, NEW_WALL_HEIGHT, NEW_WALL_SIDEBOARD_THICKNESS, NEW_WALL_SIDEBOARD_HEIGHT, NEW_ROOM_FLOOR_COLOR, NEW_FLOOR_THICKNESS, 57 RECENT_HOMES, IGNORED_ACTION_TIP, FURNITURE_CATALOG_VIEWED_IN_TREE, NAVIGATION_PANEL_VISIBLE, 58 AERIAL_VIEW_CENTERED_ON_SELECTION_ENABLED, OBSERVER_CAMERA_SELECTED_AT_CHANGE, CHECK_UPDATES_ENABLED, 59 UPDATES_MINIMUM_DATE, AUTO_SAVE_DELAY_FOR_RECOVERY, AUTO_COMPLETION_STRINGS, RECENT_COLORS, RECENT_TEXTURES, HOME_EXAMPLES} 60 61 public static final String FURNITURE_LIBRARY_TYPE = "Furniture library"; 62 public static final String TEXTURES_LIBRARY_TYPE = "Textures library"; 63 public static final String LANGUAGE_LIBRARY_TYPE = "Language library"; 64 65 private static final String [] DEFAULT_SUPPORTED_LANGUAGES; 66 private static final List<ClassLoader> DEFAULT_CLASS_LOADER = 67 Arrays.asList(new ClassLoader [] {UserPreferences.class.getClassLoader()}); 68 69 private static final TextStyle DEFAULT_TEXT_STYLE = new TextStyle(18f); 70 private static final TextStyle DEFAULT_ROOM_TEXT_STYLE = new TextStyle(24f); 71 72 static { 73 Properties supportedLanguagesProperties = new Properties(); 74 String [] defaultSupportedLanguages; 75 try { 76 // As of version 4.1 where Trusted-Library manifest attribute was added to applet jars, 77 // UserPreferences.properties was renamed as SupportedLanguages.properties 78 // because strangely UserPreferences.properties resource couldn't be found in applet environment 79 InputStream in = UserPreferences.class.getResourceAsStream("SupportedLanguages.properties"); 80 supportedLanguagesProperties.load(in); in.close()81 in.close(); 82 // Get property value of supportedLanguages 83 defaultSupportedLanguages = supportedLanguagesProperties.getProperty("supportedLanguages", "en").split("\\s"); 84 } catch (IOException ex) { 85 defaultSupportedLanguages = new String [] {"en"}; 86 } 87 DEFAULT_SUPPORTED_LANGUAGES = defaultSupportedLanguages; 88 } 89 90 private final PropertyChangeSupport propertyChangeSupport; 91 private final Map<Class<?>, ResourceBundle> classResourceBundles; 92 private final Map<String, ResourceBundle> resourceBundles; 93 94 private FurnitureCatalog furnitureCatalog; 95 private TexturesCatalog texturesCatalog; 96 private PatternsCatalog patternsCatalog; 97 private final String defaultCountry; 98 private String [] supportedLanguages; 99 private String language; 100 private String currency; 101 private boolean valueAddedTaxEnabled; 102 private BigDecimal defaultValueAddedTaxPercentage; 103 private LengthUnit unit; 104 private boolean furnitureCatalogViewedInTree = true; 105 private boolean aerialViewCenteredOnSelectionEnabled; 106 private boolean observerCameraSelectedAtChange = true; 107 private boolean navigationPanelVisible = true; 108 private boolean magnetismEnabled = true; 109 private boolean rulersVisible = true; 110 private boolean gridVisible = true; 111 private String defaultFontName; 112 private boolean drawingModeEnabled; 113 private boolean furnitureViewedFromTop; 114 private int furnitureModelIconSize = 128; 115 private boolean roomFloorColoredOrTextured; 116 private TextureImage wallPattern; 117 private TextureImage newWallPattern; 118 private float newWallThickness; 119 private float newWallHeight; 120 private float newWallBaseboardThickness; 121 private float newWallBaseboardHeight; 122 private Integer newRoomFloorColor; 123 private float newFloorThickness; 124 private List<String> recentHomes; 125 private boolean checkUpdatesEnabled; 126 private Long updatesMinimumDate; 127 private int autoSaveDelayForRecovery; 128 private Map<String, List<String>> autoCompletionStrings; 129 private List<Integer> recentColors; 130 private List<TextureImage> recentTextures; 131 private List<HomeDescriptor> homeExamples; 132 133 /** 134 * Creates user preferences.<br> 135 * Caution: during creation, the default locale will be updated if it doesn't belong to the supported ones. 136 */ UserPreferences()137 public UserPreferences() { 138 this.propertyChangeSupport = new PropertyChangeSupport(this); 139 this.classResourceBundles = new HashMap<Class<?>, ResourceBundle>(); 140 this.resourceBundles = new HashMap<String, ResourceBundle>(); 141 this.autoCompletionStrings = new LinkedHashMap<String, List<String>>(); 142 this.recentHomes = Collections.emptyList(); 143 this.recentColors = Collections.emptyList(); 144 this.recentTextures = Collections.emptyList(); 145 this.homeExamples = Collections.emptyList(); 146 147 try { 148 this.drawingModeEnabled = Boolean.getBoolean("com.eteks.sweethome3d.j3d.drawingModeEnabled"); 149 } catch (SecurityException ex) { 150 } 151 152 this.supportedLanguages = DEFAULT_SUPPORTED_LANGUAGES; 153 this.defaultCountry = Locale.getDefault().getCountry(); 154 String defaultLanguage = Locale.getDefault().getLanguage(); 155 // Find closest language among supported languages in Sweet Home 3D 156 // For example, use simplified Chinese even for Chinese users (zh_?) not from China (zh_CN) 157 // unless their exact locale is supported as in Taiwan (zh_TW) 158 for (String supportedLanguage : this.supportedLanguages) { 159 if (supportedLanguage.equals(defaultLanguage + "_" + this.defaultCountry)) { 160 this.language = supportedLanguage; 161 break; // Found the exact supported language 162 } else if (this.language == null 163 && supportedLanguage.startsWith(defaultLanguage)) { 164 this.language = supportedLanguage; // Found a supported language 165 } 166 } 167 // If no language was found, let's use English by default 168 if (this.language == null) { 169 this.language = Locale.ENGLISH.getLanguage(); 170 } 171 updateDefaultLocale(); 172 } 173 174 /** 175 * Updates default locale from preferences language. 176 */ updateDefaultLocale()177 private void updateDefaultLocale() { 178 try { 179 int underscoreIndex = this.language.indexOf("_"); 180 if (underscoreIndex != -1) { 181 Locale.setDefault(new Locale(this.language.substring(0, underscoreIndex), 182 this.language.substring(underscoreIndex + 1))); 183 } else { 184 Locale.setDefault(new Locale(this.language, this.defaultCountry)); 185 } 186 } catch (AccessControlException ex) { 187 // Let's keep default language even if it's not supported 188 this.language = Locale.getDefault().getLanguage(); 189 } 190 } 191 192 /** 193 * Writes user preferences. 194 * @throws RecorderException if user preferences couldn'y be saved. 195 */ write()196 public abstract void write() throws RecorderException; 197 198 /** 199 * Adds the property change <code>listener</code> in parameter to these preferences. 200 * <br>Caution: a user preferences instance generally exists during all the application ; 201 * therefore you should take care of not bounding permanently listeners to this 202 * object (for example, do not create anonymous listeners on user preferences 203 * in classes depending on an edited home). 204 * @since 6.4 205 */ addPropertyChangeListener(PropertyChangeListener listener)206 public void addPropertyChangeListener(PropertyChangeListener listener) { 207 this.propertyChangeSupport.addPropertyChangeListener(listener); 208 } 209 210 /** 211 * Removes the property change <code>listener</code> in parameter from these preferences. 212 * @since 6.4 213 */ removePropertyChangeListener(PropertyChangeListener listener)214 public void removePropertyChangeListener(PropertyChangeListener listener) { 215 this.propertyChangeSupport.removePropertyChangeListener(listener); 216 } 217 218 /** 219 * Adds the <code>listener</code> in parameter to these preferences to listen 220 * to the changes of the given <code>property</code>. 221 * <br>Caution: a user preferences instance generally exists during all the application ; 222 * therefore you should take care of not bounding permanently listeners to this 223 * object (for example, do not create anonymous listeners on user preferences 224 * in classes depending on an edited home). 225 */ addPropertyChangeListener(Property property, PropertyChangeListener listener)226 public void addPropertyChangeListener(Property property, 227 PropertyChangeListener listener) { 228 this.propertyChangeSupport.addPropertyChangeListener(property.name(), listener); 229 } 230 231 /** 232 * Removes the <code>listener</code> in parameter from these preferences. 233 */ removePropertyChangeListener(Property property, PropertyChangeListener listener)234 public void removePropertyChangeListener(Property property, 235 PropertyChangeListener listener) { 236 this.propertyChangeSupport.removePropertyChangeListener(property.name(), listener); 237 } 238 239 /** 240 * Returns the furniture catalog. 241 */ getFurnitureCatalog()242 public FurnitureCatalog getFurnitureCatalog() { 243 return this.furnitureCatalog; 244 } 245 246 /** 247 * Sets furniture catalog. 248 */ setFurnitureCatalog(FurnitureCatalog catalog)249 protected void setFurnitureCatalog(FurnitureCatalog catalog) { 250 this.furnitureCatalog = catalog; 251 } 252 253 /** 254 * Returns the textures catalog. 255 */ getTexturesCatalog()256 public TexturesCatalog getTexturesCatalog() { 257 return this.texturesCatalog; 258 } 259 260 /** 261 * Sets textures catalog. 262 */ setTexturesCatalog(TexturesCatalog catalog)263 protected void setTexturesCatalog(TexturesCatalog catalog) { 264 this.texturesCatalog = catalog; 265 } 266 267 /** 268 * Returns the patterns catalog available to fill plan areas. 269 */ getPatternsCatalog()270 public PatternsCatalog getPatternsCatalog() { 271 return this.patternsCatalog; 272 } 273 274 /** 275 * Sets the patterns available to fill plan areas. 276 */ setPatternsCatalog(PatternsCatalog catalog)277 protected void setPatternsCatalog(PatternsCatalog catalog) { 278 this.patternsCatalog = catalog; 279 } 280 281 /** 282 * Returns the length unit currently in use. 283 */ getLengthUnit()284 public LengthUnit getLengthUnit() { 285 return this.unit; 286 } 287 288 /** 289 * Changes the unit currently in use, and notifies listeners of this change. 290 * @param unit one of the values of Unit. 291 */ setUnit(LengthUnit unit)292 public void setUnit(LengthUnit unit) { 293 if (this.unit != unit) { 294 LengthUnit oldUnit = this.unit; 295 this.unit = unit; 296 this.propertyChangeSupport.firePropertyChange(Property.UNIT.name(), oldUnit, unit); 297 } 298 } 299 300 /** 301 * Returns the preferred language to display information, noted with an ISO 639 code 302 * that may be followed by an underscore and an ISO 3166 code. 303 */ getLanguage()304 public String getLanguage() { 305 return this.language; 306 } 307 308 /** 309 * If {@linkplain #isLanguageEditable() language can be changed}, sets the preferred language to display information, 310 * changes current default locale accordingly and notifies listeners of this change. 311 * @param language an ISO 639 code that may be followed by an underscore and an ISO 3166 code 312 * (for example fr, de, it, en_US, zh_CN). 313 */ setLanguage(String language)314 public void setLanguage(String language) { 315 if (!language.equals(this.language) 316 && isLanguageEditable()) { 317 String oldLanguage = this.language; 318 this.language = language; 319 updateDefaultLocale(); 320 this.classResourceBundles.clear(); 321 this.resourceBundles.clear(); 322 this.propertyChangeSupport.firePropertyChange(Property.LANGUAGE.name(), 323 oldLanguage, language); 324 } 325 } 326 327 /** 328 * Returns <code>true</code> if the language in preferences can be set. 329 * @return <code>true</code> except if <code>user.language</code> System property isn't writable. 330 * @since 3.4 331 */ isLanguageEditable()332 public boolean isLanguageEditable() { 333 try { 334 SecurityManager securityManager = System.getSecurityManager(); 335 if (securityManager != null) { 336 securityManager.checkPermission(new PropertyPermission("user.language", "write")); 337 } 338 return true; 339 } catch (AccessControlException ex) { 340 return false; 341 } 342 } 343 344 /** 345 * Returns the array of default available languages in Sweet Home 3D. 346 */ getDefaultSupportedLanguages()347 public String [] getDefaultSupportedLanguages() { 348 return DEFAULT_SUPPORTED_LANGUAGES.clone(); 349 } 350 351 /** 352 * Returns the array of available languages in Sweet Home 3D including languages in libraries. 353 * @since 3.4 354 */ getSupportedLanguages()355 public String [] getSupportedLanguages() { 356 return this.supportedLanguages.clone(); 357 } 358 359 /** 360 * Sets the available languages in Sweet Home 3D. 361 * @since 3.4 362 */ setSupportedLanguages(String [] supportedLanguages)363 protected void setSupportedLanguages(String [] supportedLanguages) { 364 if (!Arrays.deepEquals(this.supportedLanguages, supportedLanguages)) { 365 String [] oldSupportedLanguages = this.supportedLanguages; 366 this.supportedLanguages = supportedLanguages.clone(); 367 this.propertyChangeSupport.firePropertyChange(Property.SUPPORTED_LANGUAGES.name(), 368 oldSupportedLanguages, supportedLanguages); 369 } 370 } 371 372 /** 373 * Returns the string matching <code>resourceKey</code> in current language in the 374 * context of <code>resourceClass</code>. 375 * If <code>resourceParameters</code> isn't empty the string is considered 376 * as a format string, and the returned string will be formatted with these parameters. 377 * This implementation searches first the key in a properties file named as 378 * <code>resourceClass</code>, then if this file doesn't exist, it searches 379 * the key prefixed by <code>resourceClass</code> name and a dot in a package.properties file 380 * in the folder matching the package of <code>resourceClass</code>. 381 * @throws IllegalArgumentException if no string for the given key can be found 382 */ getLocalizedString(Class<?> resourceClass, String resourceKey, Object ... resourceParameters)383 public String getLocalizedString(Class<?> resourceClass, 384 String resourceKey, 385 Object ... resourceParameters) { 386 ResourceBundle classResourceBundle = this.classResourceBundles.get(resourceClass); 387 if (classResourceBundle == null) { 388 try { 389 classResourceBundle = getResourceBundle(resourceClass.getName()); 390 this.classResourceBundles.put(resourceClass, classResourceBundle); 391 } catch (IOException ex) { 392 try { 393 String className = resourceClass.getName(); 394 int lastIndex = className.lastIndexOf("."); 395 String resourceFamily; 396 if (lastIndex != -1) { 397 resourceFamily = className.substring(0, lastIndex) + ".package"; 398 } else { 399 resourceFamily = "package"; 400 } 401 classResourceBundle = new PrefixedResourceBundle(getResourceBundle(resourceFamily), 402 resourceClass.getSimpleName() + "."); 403 this.classResourceBundles.put(resourceClass, classResourceBundle); 404 } catch (IOException ex2) { 405 throw new IllegalArgumentException( 406 "Can't find resource bundle for " + resourceClass, ex); 407 } 408 } 409 } 410 411 return getLocalizedString(classResourceBundle, resourceKey, resourceParameters); 412 } 413 414 /** 415 * Returns the string matching <code>resourceKey</code> in current language 416 * for the given resource family. 417 * <code>resourceFamily</code> should match the absolute path of a .properties resource family, 418 * shouldn't start by a slash and may contain dots '.' or slash '/' as folder separators. 419 * If <code>resourceParameters</code> isn't empty the string is considered 420 * as a format string, and the returned string will be formatted with these parameters. 421 * This implementation searches the key in a properties file named as 422 * <code>resourceFamily</code>. 423 * @throws IllegalArgumentException if no string for the given key can be found 424 * @since 2.3 425 */ getLocalizedString(String resourceFamily, String resourceKey, Object ... resourceParameters)426 public String getLocalizedString(String resourceFamily, 427 String resourceKey, 428 Object ... resourceParameters) { 429 try { 430 ResourceBundle resourceBundle = getResourceBundle(resourceFamily); 431 return getLocalizedString(resourceBundle, resourceKey, resourceParameters); 432 } catch (IOException ex) { 433 throw new IllegalArgumentException( 434 "Can't find resource bundle for " + resourceFamily, ex); 435 } 436 } 437 438 /** 439 * Returns a new resource bundle for the given <code>familyName</code> 440 * that matches current default locale. The search will be done 441 * only among .properties files. 442 * @throws IOException if no .properties file was found 443 */ getResourceBundle(String resourceFamily)444 private ResourceBundle getResourceBundle(String resourceFamily) throws IOException { 445 resourceFamily = resourceFamily.replace('.', '/'); 446 ResourceBundle resourceBundle = this.resourceBundles.get(resourceFamily); 447 if (resourceBundle != null) { 448 return resourceBundle; 449 } 450 Locale defaultLocale = Locale.getDefault(); 451 String language = defaultLocale.getLanguage(); 452 String country = defaultLocale.getCountry(); 453 String [] suffixes = {".properties", 454 "_" + language + ".properties", 455 "_" + language + "_" + country + ".properties"}; 456 for (String suffix : suffixes) { 457 for (ClassLoader classLoader : getResourceClassLoaders()) { 458 InputStream in = classLoader.getResourceAsStream(resourceFamily + suffix); 459 if (in != null) { 460 final ResourceBundle parentResourceBundle = resourceBundle; 461 try { 462 resourceBundle = new PropertyResourceBundle(in) { 463 { 464 setParent(parentResourceBundle); 465 } 466 }; 467 break; 468 } catch (IllegalArgumentException ex) { 469 // May happen if the file contains some wrongly encoded characters 470 ex.printStackTrace(); 471 } finally { 472 in.close(); 473 } 474 } 475 } 476 } 477 if (resourceBundle == null) { 478 throw new IOException("No available resource bundle for " + resourceFamily); 479 } 480 this.resourceBundles.put(resourceFamily, resourceBundle); 481 return resourceBundle; 482 } 483 484 /** 485 * Returns the string matching <code>resourceKey</code> for the given resource bundle. 486 */ getLocalizedString(ResourceBundle resourceBundle, String resourceKey, Object... resourceParameters)487 private String getLocalizedString(ResourceBundle resourceBundle, 488 String resourceKey, 489 Object... resourceParameters) { 490 try { 491 String localizedString = resourceBundle.getString(resourceKey); 492 if (resourceParameters.length > 0) { 493 localizedString = String.format(localizedString, resourceParameters); 494 } 495 return localizedString; 496 } catch (MissingResourceException ex) { 497 throw new IllegalArgumentException("Unknown key " + resourceKey); 498 } 499 } 500 501 /** 502 * Returns the keys of the localized property strings of the given resource family. 503 * <code>resourceFamily</code> should match the absolute path of a .properties resource family, 504 * shouldn't start by a slash and may contain dots '.' or slash '/' as folder separators. 505 * @since 5.7 506 */ getLocalizedStringKeys(String resourceFamily)507 public Iterator<String> getLocalizedStringKeys(String resourceFamily) { 508 try { 509 final Enumeration<String> keys = getResourceBundle(resourceFamily).getKeys(); 510 return new Iterator<String>() { 511 public boolean hasNext() { 512 return keys.hasMoreElements(); 513 } 514 515 public String next() { 516 return keys.nextElement(); 517 } 518 519 public void remove() { 520 throw new UnsupportedOperationException("Enumeration not modifiable"); 521 } 522 }; 523 } catch (IOException ex) { 524 return Collections.<String>emptyList().iterator(); 525 } 526 } 527 528 /** 529 * Returns the class loaders through which localized strings returned by 530 * {@link #getLocalizedString(Class, String, Object...) getLocalizedString} might be loaded. 531 * @since 2.3 532 */ 533 public List<ClassLoader> getResourceClassLoaders() { 534 return DEFAULT_CLASS_LOADER; 535 } 536 537 /** 538 * Returns the currency in use, noted with ISO 4217 code, or <code>null</code> 539 * if prices aren't used in application. 540 */ 541 public String getCurrency() { 542 return this.currency; 543 } 544 545 /** 546 * Sets the currency in use. 547 */ 548 public void setCurrency(String currency) { 549 if (currency != this.currency 550 && (currency == null || !currency.equals(this.currency))) { 551 String oldCurrency = this.currency; 552 this.currency = currency; 553 this.propertyChangeSupport.firePropertyChange(Property.CURRENCY.name(), oldCurrency, currency); 554 555 } 556 } 557 558 /** 559 * Returns <code>true</code> if Value Added Tax should be taken in account in prices. 560 * @since 6.0 561 */ 562 public boolean isValueAddedTaxEnabled() { 563 return this.valueAddedTaxEnabled; 564 } 565 566 /** 567 * Sets whether Value Added Tax should be taken in account in prices. 568 * @param valueAddedTaxEnabled if <code>true</code> VAT will be added to prices. 569 * @since 6.0 570 */ 571 public void setValueAddedTaxEnabled(boolean valueAddedTaxEnabled) { 572 if (this.valueAddedTaxEnabled != valueAddedTaxEnabled) { 573 this.valueAddedTaxEnabled = valueAddedTaxEnabled; 574 this.propertyChangeSupport.firePropertyChange(Property.VALUE_ADDED_TAX_ENABLED.name(), 575 !valueAddedTaxEnabled, valueAddedTaxEnabled); 576 } 577 } 578 579 /** 580 * Returns the Value Added Tax percentage applied to prices by default, or <code>null</code> 581 * if VAT isn't taken into account in the application. 582 * @since 6.0 583 */ 584 public BigDecimal getDefaultValueAddedTaxPercentage() { 585 return this.defaultValueAddedTaxPercentage; 586 } 587 588 /** 589 * Sets the Value Added Tax percentage applied to prices by default. 590 * @param valueAddedTaxPercentage the default VAT percentage 591 * @since 6.0 592 */ 593 public void setDefaultValueAddedTaxPercentage(BigDecimal valueAddedTaxPercentage) { 594 if (valueAddedTaxPercentage != this.defaultValueAddedTaxPercentage 595 && (valueAddedTaxPercentage == null || !valueAddedTaxPercentage.equals(this.defaultValueAddedTaxPercentage))) { 596 BigDecimal oldValueAddedTaxPercentage = this.defaultValueAddedTaxPercentage; 597 this.defaultValueAddedTaxPercentage = valueAddedTaxPercentage; 598 this.propertyChangeSupport.firePropertyChange(Property.DEFAULT_VALUE_ADDED_TAX_PERCENTAGE.name(), oldValueAddedTaxPercentage, valueAddedTaxPercentage); 599 600 } 601 } 602 603 /** 604 * Returns <code>true</code> if the furniture catalog should be viewed in a tree. 605 * @since 2.3 606 */ 607 public boolean isFurnitureCatalogViewedInTree() { 608 return this.furnitureCatalogViewedInTree; 609 } 610 611 /** 612 * Sets whether the furniture catalog should be viewed in a tree or a different way. 613 * @since 2.3 614 */ 615 public void setFurnitureCatalogViewedInTree(boolean furnitureCatalogViewedInTree) { 616 if (this.furnitureCatalogViewedInTree != furnitureCatalogViewedInTree) { 617 this.furnitureCatalogViewedInTree = furnitureCatalogViewedInTree; 618 this.propertyChangeSupport.firePropertyChange(Property.FURNITURE_CATALOG_VIEWED_IN_TREE.name(), 619 !furnitureCatalogViewedInTree, furnitureCatalogViewedInTree); 620 } 621 } 622 623 /** 624 * Returns <code>true</code> if the navigation panel should be displayed. 625 * @since 2.3 626 */ 627 public boolean isNavigationPanelVisible() { 628 return this.navigationPanelVisible; 629 } 630 631 /** 632 * Sets whether the navigation panel should be displayed or not. 633 * @since 2.3 634 */ 635 public void setNavigationPanelVisible(boolean navigationPanelVisible) { 636 if (this.navigationPanelVisible != navigationPanelVisible) { 637 this.navigationPanelVisible = navigationPanelVisible; 638 this.propertyChangeSupport.firePropertyChange(Property.NAVIGATION_PANEL_VISIBLE.name(), 639 !navigationPanelVisible, navigationPanelVisible); 640 } 641 } 642 643 /** 644 * Sets whether aerial view should be centered on selection or not. 645 * @since 4.0 646 */ 647 public void setAerialViewCenteredOnSelectionEnabled(boolean aerialViewCenteredOnSelectionEnabled) { 648 if (aerialViewCenteredOnSelectionEnabled != this.aerialViewCenteredOnSelectionEnabled) { 649 this.aerialViewCenteredOnSelectionEnabled = aerialViewCenteredOnSelectionEnabled; 650 this.propertyChangeSupport.firePropertyChange(Property.AERIAL_VIEW_CENTERED_ON_SELECTION_ENABLED.name(), 651 !aerialViewCenteredOnSelectionEnabled, aerialViewCenteredOnSelectionEnabled); 652 } 653 } 654 655 /** 656 * Returns whether aerial view should be centered on selection or not. 657 * @since 4.0 658 */ 659 public boolean isAerialViewCenteredOnSelectionEnabled() { 660 return this.aerialViewCenteredOnSelectionEnabled; 661 } 662 663 /** 664 * Sets whether the observer camera should be selected at each change. 665 * @since 5.5 666 */ 667 public void setObserverCameraSelectedAtChange(boolean observerCameraSelectedAtChange) { 668 if (observerCameraSelectedAtChange != this.observerCameraSelectedAtChange) { 669 this.observerCameraSelectedAtChange = observerCameraSelectedAtChange; 670 this.propertyChangeSupport.firePropertyChange(Property.OBSERVER_CAMERA_SELECTED_AT_CHANGE.name(), 671 !observerCameraSelectedAtChange, observerCameraSelectedAtChange); 672 } 673 } 674 675 /** 676 * Returns whether the observer camera should be selected at each change. 677 * @since 5.5 678 */ 679 public boolean isObserverCameraSelectedAtChange() { 680 return this.observerCameraSelectedAtChange; 681 } 682 683 /** 684 * Returns <code>true</code> if magnetism is enabled. 685 * @return <code>true</code> by default. 686 */ 687 public boolean isMagnetismEnabled() { 688 return this.magnetismEnabled; 689 } 690 691 /** 692 * Sets whether magnetism is enabled or not, and notifies 693 * listeners of this change. 694 * @param magnetismEnabled <code>true</code> if magnetism is enabled, 695 * <code>false</code> otherwise. 696 */ 697 public void setMagnetismEnabled(boolean magnetismEnabled) { 698 if (this.magnetismEnabled != magnetismEnabled) { 699 this.magnetismEnabled = magnetismEnabled; 700 this.propertyChangeSupport.firePropertyChange(Property.MAGNETISM_ENABLED.name(), 701 !magnetismEnabled, magnetismEnabled); 702 } 703 } 704 705 /** 706 * Returns <code>true</code> if rulers are visible. 707 * @return <code>true</code> by default. 708 */ 709 public boolean isRulersVisible() { 710 return this.rulersVisible; 711 } 712 713 /** 714 * Sets whether rulers are visible or not, and notifies 715 * listeners of this change. 716 * @param rulersVisible <code>true</code> if rulers are visible, 717 * <code>false</code> otherwise. 718 */ 719 public void setRulersVisible(boolean rulersVisible) { 720 if (this.rulersVisible != rulersVisible) { 721 this.rulersVisible = rulersVisible; 722 this.propertyChangeSupport.firePropertyChange(Property.RULERS_VISIBLE.name(), 723 !rulersVisible, rulersVisible); 724 } 725 } 726 727 /** 728 * Returns <code>true</code> if plan grid visible. 729 * @return <code>true</code> by default. 730 */ 731 public boolean isGridVisible() { 732 return this.gridVisible; 733 } 734 735 /** 736 * Sets whether plan grid is visible or not, and notifies 737 * listeners of this change. 738 * @param gridVisible <code>true</code> if grid is visible, 739 * <code>false</code> otherwise. 740 */ 741 public void setGridVisible(boolean gridVisible) { 742 if (this.gridVisible != gridVisible) { 743 this.gridVisible = gridVisible; 744 this.propertyChangeSupport.firePropertyChange(Property.GRID_VISIBLE.name(), 745 !gridVisible, gridVisible); 746 } 747 } 748 749 /** 750 * Returns <code>true</code> is {@link HomeEnvironment#getDrawingMode() drawing mode} 751 * should be taken into account. 752 * @since 6.0 753 */ 754 public boolean isDrawingModeEnabled() { 755 return this.drawingModeEnabled; 756 } 757 758 /** 759 * Returns the name of the font that should be used by default or <code>null</code> 760 * if the default font should be the default one in the application. 761 * @since 5.0 762 */ 763 public String getDefaultFontName() { 764 return this.defaultFontName; 765 } 766 767 /** 768 * Sets the name of the font that should be used by default. 769 * @since 5.0 770 */ 771 public void setDefaultFontName(String defaultFontName) { 772 if (defaultFontName != this.defaultFontName 773 && (defaultFontName == null || !defaultFontName.equals(this.defaultFontName))) { 774 String oldName = this.defaultFontName; 775 this.defaultFontName = defaultFontName; 776 this.propertyChangeSupport.firePropertyChange(Property.DEFAULT_FONT_NAME.name(), oldName, defaultFontName); 777 } 778 } 779 780 /** 781 * Returns <code>true</code> if furniture should be viewed from its top in plan. 782 * @since 2.0 783 */ 784 public boolean isFurnitureViewedFromTop() { 785 return this.furnitureViewedFromTop; 786 } 787 788 /** 789 * Sets how furniture icon should be displayed in plan, and notifies 790 * listeners of this change. 791 * @param furnitureViewedFromTop if <code>true</code> the furniture 792 * should be viewed from its top. 793 * @since 2.0 794 */ 795 public void setFurnitureViewedFromTop(boolean furnitureViewedFromTop) { 796 if (this.furnitureViewedFromTop != furnitureViewedFromTop) { 797 this.furnitureViewedFromTop = furnitureViewedFromTop; 798 this.propertyChangeSupport.firePropertyChange(Property.FURNITURE_VIEWED_FROM_TOP.name(), 799 !furnitureViewedFromTop, furnitureViewedFromTop); 800 } 801 } 802 803 /** 804 * Returns the size used to generate icons of furniture viewed from top. 805 * @since 5.5 806 */ 807 public int getFurnitureModelIconSize() { 808 return this.furnitureModelIconSize; 809 } 810 811 /** 812 * Sets the name of the font that should be used by default. 813 * @since 5.5 814 */ 815 public void setFurnitureModelIconSize(int furnitureModelIconSize) { 816 if (furnitureModelIconSize != this.furnitureModelIconSize) { 817 int oldSize = this.furnitureModelIconSize; 818 this.furnitureModelIconSize = furnitureModelIconSize; 819 this.propertyChangeSupport.firePropertyChange(Property.FURNITURE_MODEL_ICON_SIZE.name(), oldSize, furnitureModelIconSize); 820 } 821 } 822 823 /** 824 * Returns <code>true</code> if room floors should be rendered with color or texture 825 * in plan. 826 * @return <code>false</code> by default. 827 * @since 2.0 828 */ 829 public boolean isRoomFloorColoredOrTextured() { 830 return this.roomFloorColoredOrTextured; 831 } 832 833 /** 834 * Sets whether room floors should be rendered with color or texture, 835 * and notifies listeners of this change. 836 * @param roomFloorColoredOrTextured <code>true</code> if floor color 837 * or texture is used, <code>false</code> otherwise. 838 * @since 2.0 839 */ 840 public void setFloorColoredOrTextured(boolean roomFloorColoredOrTextured) { 841 if (this.roomFloorColoredOrTextured != roomFloorColoredOrTextured) { 842 this.roomFloorColoredOrTextured = roomFloorColoredOrTextured; 843 this.propertyChangeSupport.firePropertyChange(Property.ROOM_FLOOR_COLORED_OR_TEXTURED.name(), 844 !roomFloorColoredOrTextured, roomFloorColoredOrTextured); 845 } 846 } 847 848 /** 849 * Returns the wall pattern in plan used by default. 850 * @since 2.0 851 */ 852 public TextureImage getWallPattern() { 853 return this.wallPattern; 854 } 855 856 /** 857 * Sets how walls should be displayed in plan by default, and notifies 858 * listeners of this change. 859 * @since 2.0 860 */ 861 public void setWallPattern(TextureImage wallPattern) { 862 if (this.wallPattern != wallPattern) { 863 TextureImage oldWallPattern = this.wallPattern; 864 this.wallPattern = wallPattern; 865 this.propertyChangeSupport.firePropertyChange(Property.WALL_PATTERN.name(), 866 oldWallPattern, wallPattern); 867 } 868 } 869 870 /** 871 * Returns the pattern used for new walls in plan or <code>null</code> if it's not set. 872 * @since 4.0 873 */ 874 public TextureImage getNewWallPattern() { 875 return this.newWallPattern; 876 } 877 878 /** 879 * Sets how new walls should be displayed in plan, and notifies 880 * listeners of this change. 881 * @since 4.0 882 */ 883 public void setNewWallPattern(TextureImage newWallPattern) { 884 if (this.newWallPattern != newWallPattern) { 885 TextureImage oldWallPattern = this.newWallPattern; 886 this.newWallPattern = newWallPattern; 887 this.propertyChangeSupport.firePropertyChange(Property.NEW_WALL_PATTERN.name(), 888 oldWallPattern, newWallPattern); 889 } 890 } 891 892 /** 893 * Returns default thickness of new walls in home. 894 */ 895 public float getNewWallThickness() { 896 return this.newWallThickness; 897 } 898 899 /** 900 * Sets default thickness of new walls in home, and notifies 901 * listeners of this change. 902 */ 903 public void setNewWallThickness(float newWallThickness) { 904 if (this.newWallThickness != newWallThickness) { 905 float oldDefaultThickness = this.newWallThickness; 906 this.newWallThickness = newWallThickness; 907 this.propertyChangeSupport.firePropertyChange(Property.NEW_WALL_THICKNESS.name(), 908 oldDefaultThickness, newWallThickness); 909 } 910 } 911 912 /** 913 * Returns default wall height of new home walls. 914 */ 915 public float getNewWallHeight() { 916 return this.newWallHeight; 917 } 918 919 /** 920 * Sets default wall height of new walls, and notifies 921 * listeners of this change. 922 */ 923 public void setNewWallHeight(float newWallHeight) { 924 if (this.newWallHeight != newWallHeight) { 925 float oldWallHeight = this.newWallHeight; 926 this.newWallHeight = newWallHeight; 927 this.propertyChangeSupport.firePropertyChange(Property.NEW_WALL_HEIGHT.name(), 928 oldWallHeight, newWallHeight); 929 } 930 } 931 932 /** 933 * Returns default baseboard thickness of new walls in home. 934 * @since 5.0 935 */ 936 public float getNewWallBaseboardThickness() { 937 return this.newWallBaseboardThickness; 938 } 939 940 /** 941 * Sets default baseboard thickness of new walls in home, and notifies 942 * listeners of this change. 943 * @since 5.0 944 */ 945 public void setNewWallBaseboardThickness(float newWallBaseboardThickness) { 946 if (this.newWallBaseboardThickness != newWallBaseboardThickness) { 947 float oldThickness = this.newWallBaseboardThickness; 948 this.newWallBaseboardThickness = newWallBaseboardThickness; 949 this.propertyChangeSupport.firePropertyChange(Property.NEW_WALL_SIDEBOARD_THICKNESS.name(), 950 oldThickness, newWallBaseboardThickness); 951 } 952 } 953 954 /** 955 * Returns default baseboard height of new home walls. 956 * @since 5.0 957 */ 958 public float getNewWallBaseboardHeight() { 959 return this.newWallBaseboardHeight; 960 } 961 962 /** 963 * Sets default baseboard height of new walls, and notifies 964 * listeners of this change. 965 * @since 5.0 966 */ 967 public void setNewWallBaseboardHeight(float newWallBaseboardHeight) { 968 if (this.newWallBaseboardHeight != newWallBaseboardHeight) { 969 float oldHeight = this.newWallBaseboardHeight; 970 this.newWallBaseboardHeight = newWallBaseboardHeight; 971 this.propertyChangeSupport.firePropertyChange(Property.NEW_WALL_SIDEBOARD_HEIGHT.name(), 972 oldHeight, newWallBaseboardHeight); 973 } 974 } 975 976 /** 977 * Returns the default color of new rooms in home. 978 * @since 6.4 979 */ 980 public Integer getNewRoomFloorColor() { 981 return this.newRoomFloorColor; 982 } 983 984 /** 985 * Sets the default color of new rooms in home, and notifies 986 * listeners of this change. 987 * @since 6.4 988 */ 989 public void setNewRoomFloorColor(Integer newRoomFloorColor) { 990 if (this.newRoomFloorColor != newRoomFloorColor) { 991 Integer oldRoomFloorColor = this.newRoomFloorColor; 992 this.newRoomFloorColor = newRoomFloorColor; 993 this.propertyChangeSupport.firePropertyChange(Property.NEW_ROOM_FLOOR_COLOR.name(), 994 oldRoomFloorColor, newRoomFloorColor); 995 } 996 } 997 998 /** 999 * Returns default thickness of the floor of new levels in home. 1000 * @since 3.4 1001 */ 1002 public float getNewFloorThickness() { 1003 return this.newFloorThickness; 1004 } 1005 1006 /** 1007 * Sets default thickness of the floor of new levels in home, and notifies 1008 * listeners of this change. 1009 * @since 3.4 1010 */ 1011 public void setNewFloorThickness(float newFloorThickness) { 1012 if (this.newFloorThickness != newFloorThickness) { 1013 float oldFloorThickness = this.newFloorThickness; 1014 this.newFloorThickness = newFloorThickness; 1015 this.propertyChangeSupport.firePropertyChange(Property.NEW_FLOOR_THICKNESS.name(), 1016 oldFloorThickness, newFloorThickness); 1017 } 1018 } 1019 1020 /** 1021 * Returns <code>true</code> if updates should be checked. 1022 * @since 4.0 1023 */ 1024 public boolean isCheckUpdatesEnabled() { 1025 return this.checkUpdatesEnabled; 1026 } 1027 1028 /** 1029 * Sets whether updates should be checked or not. 1030 * @since 4.0 1031 */ 1032 public void setCheckUpdatesEnabled(boolean updatesChecked) { 1033 if (updatesChecked != this.checkUpdatesEnabled) { 1034 this.checkUpdatesEnabled = updatesChecked; 1035 this.propertyChangeSupport.firePropertyChange(Property.CHECK_UPDATES_ENABLED.name(), 1036 !updatesChecked, updatesChecked); 1037 } 1038 } 1039 1040 /** 1041 * Returns the minimum date of updates that may interest user. 1042 * @return the date expressed in millis second since the epoch or <code>null</code> if not defined. 1043 * @since 4.0 1044 */ 1045 public Long getUpdatesMinimumDate() { 1046 return this.updatesMinimumDate; 1047 } 1048 1049 /** 1050 * Sets the minimum date of updates that may interest user, and notifies 1051 * listeners of this change. 1052 * @since 4.0 1053 */ 1054 public void setUpdatesMinimumDate(Long updatesMinimumDate) { 1055 if (this.updatesMinimumDate != updatesMinimumDate 1056 && (updatesMinimumDate == null || !updatesMinimumDate.equals(this.updatesMinimumDate))) { 1057 Long oldUpdatesMinimumDate = this.updatesMinimumDate; 1058 this.updatesMinimumDate = updatesMinimumDate; 1059 this.propertyChangeSupport.firePropertyChange(Property.UPDATES_MINIMUM_DATE.name(), 1060 oldUpdatesMinimumDate, updatesMinimumDate); 1061 } 1062 } 1063 1064 /** 1065 * Returns the delay between two automatic save operations of homes for recovery purpose. 1066 * @return a delay in milliseconds or 0 to disable auto save. 1067 * @since 3.0 1068 */ 1069 public int getAutoSaveDelayForRecovery() { 1070 return this.autoSaveDelayForRecovery; 1071 } 1072 1073 /** 1074 * Sets the delay between two automatic save operations of homes for recovery purpose. 1075 * @since 3.0 1076 */ 1077 public void setAutoSaveDelayForRecovery(int autoSaveDelayForRecovery) { 1078 if (this.autoSaveDelayForRecovery != autoSaveDelayForRecovery) { 1079 float oldAutoSaveDelayForRecovery = this.autoSaveDelayForRecovery; 1080 this.autoSaveDelayForRecovery = autoSaveDelayForRecovery; 1081 this.propertyChangeSupport.firePropertyChange(Property.AUTO_SAVE_DELAY_FOR_RECOVERY.name(), 1082 oldAutoSaveDelayForRecovery, autoSaveDelayForRecovery); 1083 } 1084 } 1085 1086 /** 1087 * Returns an unmodifiable list of the recent homes. 1088 */ 1089 public List<String> getRecentHomes() { 1090 return Collections.unmodifiableList(this.recentHomes); 1091 } 1092 1093 /** 1094 * Sets the recent homes list and notifies listeners of this change. 1095 */ 1096 public void setRecentHomes(List<String> recentHomes) { 1097 if (!recentHomes.equals(this.recentHomes)) { 1098 List<String> oldRecentHomes = this.recentHomes; 1099 this.recentHomes = new ArrayList<String>(recentHomes); 1100 this.propertyChangeSupport.firePropertyChange(Property.RECENT_HOMES.name(), 1101 oldRecentHomes, getRecentHomes()); 1102 } 1103 } 1104 1105 /** 1106 * Returns the maximum count of homes that should be proposed to the user. 1107 */ 1108 public int getRecentHomesMaxCount() { 1109 return 10; 1110 } 1111 1112 /** 1113 * Returns the maximum count of stored cameras in homes that should be proposed to the user. 1114 * @since 4.5 1115 */ 1116 public int getStoredCamerasMaxCount() { 1117 return 50; 1118 } 1119 1120 /** 1121 * Sets which action tip should be ignored. 1122 * <br>This method should be overridden to store the ignore information. 1123 * By default it just notifies listeners of this change. 1124 */ 1125 public void setActionTipIgnored(String actionKey) { 1126 this.propertyChangeSupport.firePropertyChange(Property.IGNORED_ACTION_TIP.name(), null, actionKey); 1127 } 1128 1129 /** 1130 * Returns whether an action tip should be ignored or not. 1131 * <br>This method should be overridden to return the display information 1132 * stored in {@link #setActionTipIgnored(String) setActionTipIgnored}. 1133 * By default it returns <code>true</code>. 1134 */ 1135 public boolean isActionTipIgnored(String actionKey) { 1136 return true; 1137 } 1138 1139 /** 1140 * Resets the ignore flag of action tips. 1141 * <br>This method should be overridden to clear all the display flags. 1142 * By default it just notifies listeners of this change. 1143 */ 1144 public void resetIgnoredActionTips() { 1145 this.propertyChangeSupport.firePropertyChange(Property.IGNORED_ACTION_TIP.name(), null, null); 1146 } 1147 1148 /** 1149 * Returns the default text style of a class of selectable item. 1150 */ 1151 public TextStyle getDefaultTextStyle(Class<? extends Selectable> selectableClass) { 1152 if (Room.class.isAssignableFrom(selectableClass)) { 1153 return DEFAULT_ROOM_TEXT_STYLE; 1154 } else { 1155 return DEFAULT_TEXT_STYLE; 1156 } 1157 } 1158 1159 /** 1160 * Returns the strings that may be used for the auto completion of the given <code>property</code>. 1161 * @since 3.4 1162 */ 1163 public List<String> getAutoCompletionStrings(String property) { 1164 List<String> propertyAutoCompletionStrings = this.autoCompletionStrings.get(property); 1165 if (propertyAutoCompletionStrings != null) { 1166 return Collections.unmodifiableList(propertyAutoCompletionStrings); 1167 } else { 1168 return Collections.emptyList(); 1169 } 1170 } 1171 1172 /** 1173 * Adds the given string to the list of the strings used in auto completion of a <code>property</code> 1174 * and notifies listeners of this change. 1175 * @since 3.4 1176 */ 1177 public void addAutoCompletionString(String property, String autoCompletionString) { 1178 if (autoCompletionString != null 1179 && autoCompletionString.length() > 0) { 1180 List<String> propertyAutoCompletionStrings = this.autoCompletionStrings.get(property); 1181 if (propertyAutoCompletionStrings == null) { 1182 propertyAutoCompletionStrings = new ArrayList<String>(); 1183 } else if (!propertyAutoCompletionStrings.contains(autoCompletionString)) { 1184 propertyAutoCompletionStrings = new ArrayList<String>(propertyAutoCompletionStrings); 1185 } else { 1186 return; 1187 } 1188 propertyAutoCompletionStrings.add(0, autoCompletionString); 1189 setAutoCompletionStrings(property, propertyAutoCompletionStrings); 1190 } 1191 } 1192 1193 /** 1194 * Sets the auto completion strings list of the given <code>property</code> and notifies listeners of this change. 1195 * @since 3.4 1196 */ 1197 public void setAutoCompletionStrings(String property, List<String> autoCompletionStrings) { 1198 List<String> propertyAutoCompletionStrings = this.autoCompletionStrings.get(property); 1199 if (!autoCompletionStrings.equals(propertyAutoCompletionStrings)) { 1200 this.autoCompletionStrings.put(property, new ArrayList<String>(autoCompletionStrings)); 1201 this.propertyChangeSupport.firePropertyChange(Property.AUTO_COMPLETION_STRINGS.name(), 1202 null, property); 1203 } 1204 } 1205 1206 /** 1207 * Returns the list of properties with auto completion strings. 1208 * @since 3.4 1209 */ 1210 public List<String> getAutoCompletedProperties() { 1211 if (this.autoCompletionStrings != null) { 1212 return Arrays.asList(this.autoCompletionStrings.keySet().toArray(new String [this.autoCompletionStrings.size()])); 1213 } else { 1214 return Collections.emptyList(); 1215 } 1216 } 1217 1218 /** 1219 * Returns an unmodifiable list of the recent colors. 1220 * @since 4.0 1221 */ 1222 public List<Integer> getRecentColors() { 1223 return Collections.unmodifiableList(this.recentColors); 1224 } 1225 1226 /** 1227 * Sets the recent colors list and notifies listeners of this change. 1228 * @since 4.0 1229 */ 1230 public void setRecentColors(List<Integer> recentColors) { 1231 if (!recentColors.equals(this.recentColors)) { 1232 List<Integer> oldRecentColors = this.recentColors; 1233 this.recentColors = new ArrayList<Integer>(recentColors); 1234 this.propertyChangeSupport.firePropertyChange(Property.RECENT_COLORS.name(), 1235 oldRecentColors, getRecentColors()); 1236 } 1237 } 1238 1239 /** 1240 * Returns an unmodifiable list of the recent textures. 1241 * @since 4.4 1242 */ 1243 public List<TextureImage> getRecentTextures() { 1244 return Collections.unmodifiableList(this.recentTextures); 1245 } 1246 1247 /** 1248 * Sets the recent colors list and notifies listeners of this change. 1249 * @since 4.4 1250 */ 1251 public void setRecentTextures(List<TextureImage> recentTextures) { 1252 if (!recentTextures.equals(this.recentTextures)) { 1253 List<TextureImage> oldRecentTextures = this.recentTextures; 1254 this.recentTextures = new ArrayList<TextureImage>(recentTextures); 1255 this.propertyChangeSupport.firePropertyChange(Property.RECENT_TEXTURES.name(), 1256 oldRecentTextures, getRecentTextures()); 1257 } 1258 } 1259 1260 /** 1261 * Sets the home examples available for the user. 1262 * @since 5.5 1263 */ 1264 protected void setHomeExamples(List<HomeDescriptor> homeExamples) { 1265 if (!homeExamples.equals(this.homeExamples)) { 1266 List<HomeDescriptor> oldExamples = this.homeExamples; 1267 this.homeExamples = new ArrayList<HomeDescriptor>(homeExamples); 1268 this.propertyChangeSupport.firePropertyChange(Property.HOME_EXAMPLES.name(), 1269 oldExamples, getHomeExamples()); 1270 } 1271 } 1272 1273 /** 1274 * Returns the home examples available for the user. 1275 * @since 5.5 1276 */ 1277 public List<HomeDescriptor> getHomeExamples() { 1278 return Collections.unmodifiableList(this.homeExamples); 1279 } 1280 1281 /** 1282 * Adds the language library to make the languages it contains available to supported languages. 1283 * @param languageLibraryLocation the location where the library can be found. 1284 * @since 2.3 1285 */ 1286 public abstract void addLanguageLibrary(String languageLibraryLocation) throws RecorderException; 1287 1288 /** 1289 * Returns <code>true</code> if the language library at the given location exists. 1290 * @param languageLibraryLocation the name of the resource to check 1291 * @since 2.3 1292 */ 1293 public abstract boolean languageLibraryExists(String languageLibraryLocation) throws RecorderException; 1294 1295 /** 1296 * Adds <code>furnitureLibraryName</code> to furniture catalog 1297 * to make the furniture it contains available. 1298 * @param furnitureLibraryLocation the location where the library can be found. 1299 */ 1300 public abstract void addFurnitureLibrary(String furnitureLibraryLocation) throws RecorderException; 1301 1302 /** 1303 * Returns <code>true</code> if the furniture library at the given location exists. 1304 * @param furnitureLibraryLocation the name of the resource to check 1305 */ 1306 public abstract boolean furnitureLibraryExists(String furnitureLibraryLocation) throws RecorderException; 1307 1308 /** 1309 * Adds the textures library at the given location to textures catalog 1310 * to make the textures it contains available. 1311 * @param texturesLibraryLocation the location where the library can be found. 1312 * @since 2.3 1313 */ 1314 public abstract void addTexturesLibrary(String texturesLibraryLocation) throws RecorderException; 1315 1316 /** 1317 * Returns <code>true</code> if the textures library at the given location exists. 1318 * @param texturesLibraryLocation the name of the resource to check 1319 * @since 2.3 1320 */ 1321 public abstract boolean texturesLibraryExists(String texturesLibraryLocation) throws RecorderException; 1322 1323 /** 1324 * Returns the libraries available in user preferences. 1325 * @since 4.0 1326 */ 1327 public abstract List<Library> getLibraries(); 1328 1329 /** 1330 * A resource bundle with a prefix added to resource key. 1331 */ 1332 private static class PrefixedResourceBundle extends ResourceBundle { 1333 private ResourceBundle resourceBundle; 1334 private String keyPrefix; 1335 1336 public PrefixedResourceBundle(ResourceBundle resourceBundle, 1337 String keyPrefix) { 1338 this.resourceBundle = resourceBundle; 1339 this.keyPrefix = keyPrefix; 1340 } 1341 1342 @Override 1343 public Locale getLocale() { 1344 return this.resourceBundle.getLocale(); 1345 } 1346 1347 @Override 1348 protected Object handleGetObject(String key) { 1349 key = this.keyPrefix + key; 1350 return this.resourceBundle.getObject(key); 1351 } 1352 1353 @Override 1354 public Enumeration<String> getKeys() { 1355 return this.resourceBundle.getKeys(); 1356 } 1357 } 1358 } 1359