1 /* 2 * UserPreferences.js 3 * 4 * Sweet Home 3D, Copyright (c) 2024 Space Mushrooms <[email protected]> 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 21 // Requires core.js 22 // LengthUnit.js 23 // URLContent.js 24 25 /** 26 * User preferences. 27 * @constructor 28 * @author Emmanuel Puybaret 29 */ 30 function UserPreferences() { 31 this.propertyChangeSupport = new PropertyChangeSupport(this); 32 33 this.initSupportedLanguages(UserPreferences.DEFAULT_SUPPORTED_LANGUAGES); 34 35 this.resourceBundles = []; 36 this.furnitureCatalogResourceBundles = []; 37 this.texturesCatalogResourceBundles = []; 38 39 /** @type {FurnitureCatalog} */ 40 this.furnitureCatalog = null; 41 /** @type {TexturesCatalog} */ 42 this.texturesCatalog = null; 43 /** @type {PatternsCatalog} */ 44 this.patternsCatalog = null; 45 this.currency = null; 46 this.valueAddedTaxEnabled = false 47 this.defaultValueAddedTaxPercentage = null; 48 /** @type {LengthUnit} */ 49 this.unit = null; 50 this.furnitureCatalogViewedInTree = false; 51 this.navigationPanelVisible = true; 52 this.editingIn3DViewEnabled = false; 53 this.aerialViewCenteredOnSelectionEnabled = false; 54 this.observerCameraSelectedAtChange = true; 55 this.magnetismEnabled = true; 56 this.rulersVisible = true; 57 this.gridVisible = true; 58 this.defaultFontName = null; 59 this.furnitureViewedFromTop = true; 60 this.furnitureModelIconSize = 128; 61 this.roomFloorColoredOrTextured = true; 62 /** @type {TextureImage} */ 63 this.wallPattern = null; 64 /** @type {TextureImage} */ 65 this.newWallPattern = null; 66 this.newWallThickness = 7.5; 67 this.newWallHeight = 250; 68 this.newWallBaseboardThickness = 1; 69 this.newWallBaseboardHeight = 7; 70 this.newRoomFloorColor = null; 71 this.newFloorThickness = 12; 72 this.autoSaveDelayForRecovery = 0; 73 this.recentHomes = []; 74 this.autoCompletionStrings = {}; 75 this.recentColors = []; 76 this.recentTextures = []; 77 this.homeExamples = []; 78 79 this.ignoredActionTips = {}; 80 } 81 82 UserPreferences.DEFAULT_SUPPORTED_LANGUAGES = ["bg", "cs", "de", "el", "en", "es", "fr", "it", "ja", "hu", "nl", "pl", "pt", "pt_BR", "ru", "sv", "vi", "zh_CN", "zh_TW"]; 83 84 UserPreferences.DEFAULT_TEXT_STYLE = new TextStyle(18); 85 UserPreferences.DEFAULT_ROOM_TEXT_STYLE = new TextStyle(24); 86 87 /** 88 * Initializes the supportedLanguage property (and potentially the language property if it has to change) 89 * @private 90 */ 91 UserPreferences.prototype.initSupportedLanguages = function(supportedLanguages) { 92 this.supportedLanguages = supportedLanguages; 93 // We also initialize the language except if already set and within the supported languages 94 if (!this.language || this.supportedLanguages.indexOf(this.language) === -1) { 95 var defaultLocale = Locale.getDefault(); 96 if (defaultLocale === null) { 97 defaultLocale = "en"; 98 } 99 this.defaultCountry = ""; 100 var defaultLanguage = defaultLocale; 101 if (defaultLocale.indexOf("_") > 0) { 102 this.defaultCountry = defaultLocale.substring(defaultLocale.indexOf("_") + 1, defaultLocale.length); 103 defaultLanguage = this.language 104 ? this.language.substring(0, this.language.indexOf("_")) 105 : defaultLocale.substring(0, defaultLocale.indexOf("_")); 106 } 107 // Find closest language among supported languages in Sweet Home 3D 108 // For example, use simplified Chinese even for Chinese users (zh_?) not from China (zh_CN) 109 // unless their exact locale is supported as in Taiwan (zh_TW) 110 for (var i = 0; i < this.supportedLanguages.length; i++) { 111 var supportedLanguage = this.supportedLanguages[i]; 112 if (this.defaultCountry != "" && supportedLanguage == defaultLanguage + "_" + this.defaultCountry 113 || this.defaultCountry == "" && supportedLanguage == defaultLanguage) { 114 this.language = supportedLanguage; 115 break; // Found the exact supported language 116 } else if (this.language === undefined 117 && supportedLanguage.indexOf(defaultLanguage) === 0) { 118 this.language = supportedLanguage; // Found a supported language 119 } 120 } 121 // If no language was found, let's use English by default 122 if (this.language === undefined) { 123 this.language = "en"; 124 } 125 this.updateDefaultLocale(); 126 } 127 } 128 129 /** 130 * Updates default locale from preferences language. 131 * @private 132 */ 133 UserPreferences.prototype.updateDefaultLocale = function() { 134 if (this.language.indexOf("_") !== -1 135 || this.defaultCountry == "") { 136 Locale.setDefault(this.language); 137 } else { 138 Locale.setDefault(this.language + "_" + this.defaultCountry); 139 } 140 } 141 142 /** 143 * Writes user preferences. 144 */ 145 UserPreferences.prototype.write = function() { 146 // Does nothing 147 } 148 149 /** 150 * Adds the property change <code>listener</code> in parameter to these preferences. 151 * @since 6.4 152 */ 153 UserPreferences.prototype.addPropertyChangeListener = function(listener) { 154 this.propertyChangeSupport.addPropertyChangeListener(listener); 155 } 156 157 /** 158 * Removes the property change <code>listener</code> in parameter from these preferences. 159 * @since 6.4 160 */ 161 UserPreferences.prototype.removePropertyChangeListener = function(listener) { 162 this.propertyChangeSupport.removePropertyChangeListener(listener); 163 } 164 165 /** 166 * Adds the <code>listener</code> in parameter to these preferences to listen 167 * to the changes of the given <code>property</code>. 168 * The listener is a function that will receive in parameter an event of {@link PropertyChangeEvent} class. 169 */ 170 UserPreferences.prototype.addPropertyChangeListener = function(property, listener) { 171 this.propertyChangeSupport.addPropertyChangeListener(property, listener); 172 } 173 174 /** 175 * Removes the <code>listener</code> in parameter from these preferences. 176 */ 177 UserPreferences.prototype.removePropertyChangeListener = function(property, listener) { 178 this.propertyChangeSupport.removePropertyChangeListener(property, listener); 179 } 180 181 /** 182 * Returns the furniture catalog. 183 * @ignore 184 */ 185 UserPreferences.prototype.getFurnitureCatalog = function() { 186 return this.furnitureCatalog; 187 } 188 189 /** 190 * Sets furniture catalog. 191 * @ignore 192 */ 193 UserPreferences.prototype.setFurnitureCatalog = function(catalog) { 194 this.furnitureCatalog = catalog; 195 } 196 197 /** 198 * Returns the textures catalog. 199 * @ignore 200 */ 201 UserPreferences.prototype.getTexturesCatalog = function() { 202 return this.texturesCatalog; 203 } 204 205 /** 206 * Sets textures catalog. 207 * @ignore 208 */ 209 UserPreferences.prototype.setTexturesCatalog = function(catalog) { 210 this.texturesCatalog = catalog; 211 } 212 213 /** 214 * Returns the patterns catalog available to fill plan areas. 215 */ 216 UserPreferences.prototype.getPatternsCatalog = function() { 217 return this.patternsCatalog; 218 } 219 220 /** 221 * Sets the patterns available to fill plan areas. 222 * @ignore 223 */ 224 UserPreferences.prototype.setPatternsCatalog = function(catalog) { 225 this.patternsCatalog = catalog; 226 } 227 228 /** 229 * Returns the length unit currently in use. 230 * @return {LengthUnit} 231 */ 232 UserPreferences.prototype.getLengthUnit = function() { 233 return this.unit; 234 } 235 236 /** 237 * Changes the unit currently in use, and notifies listeners of this change. 238 * @param unit one of the values of Unit. 239 */ 240 UserPreferences.prototype.setUnit = function(unit) { 241 if (this.unit !== unit) { 242 var oldUnit = this.unit; 243 this.unit = unit; 244 this.propertyChangeSupport.firePropertyChange("UNIT", oldUnit, unit); 245 } 246 } 247 248 /** 249 * Returns the preferred language to display information, noted with an ISO 639 code 250 * that may be followed by an underscore and an ISO 3166 code. 251 */ 252 UserPreferences.prototype.getLanguage = function() { 253 return this.language; 254 } 255 256 /** 257 * If language can be changed, sets the preferred language to display information, 258 * changes current default locale accordingly and notifies listeners of this change. 259 * @param language an ISO 639 code that may be followed by an underscore and an ISO 3166 code 260 * (for example fr, de, it, en_US, zh_CN). 261 */ 262 UserPreferences.prototype.setLanguage = function(language) { 263 if (language != this.language && this.isLanguageEditable()) { 264 var oldLanguage = this.language; 265 this.language = language; 266 // Make it accessible to other localized parts (e.g. LengthUnit) 267 this.updateDefaultLocale(); 268 this.resourceBundles = []; 269 this.furnitureCatalogResourceBundles = []; 270 this.texturesCatalogResourceBundles = []; 271 this.propertyChangeSupport.firePropertyChange("LANGUAGE", oldLanguage, language); 272 } 273 } 274 275 /** 276 * Returns <code>true</code> if the language in preferences can be set. 277 * @return <code>true</code> except if <code>user.language</code> System property isn't writable. 278 * @ignore 279 */ 280 UserPreferences.prototype.isLanguageEditable = function() { 281 return true; 282 } 283 284 /** 285 * Returns the array of default available languages in Sweet Home 3D. 286 * @return an array of languages_countries ISO representations 287 */ 288 UserPreferences.prototype.getDefaultSupportedLanguages = function() { 289 return UserPreferences.DEFAULT_SUPPORTED_LANGUAGES.slice(0); 290 } 291 292 /** 293 * Returns the array of available languages in Sweet Home 3D including languages in libraries. 294 */ 295 UserPreferences.prototype.getSupportedLanguages = function() { 296 return this.supportedLanguages.slice(0); 297 } 298 299 /** 300 * Returns the array of available languages in Sweet Home 3D. 301 */ 302 UserPreferences.prototype.setSupportedLanguages = function(supportedLanguages) { 303 if (this.supportedLanguages != supportedLanguages) { 304 var oldSupportedLanguages = this.supportedLanguages; 305 var oldLanguage = this.language; 306 this.initSupportedLanguages(supportedLanguages.slice(0)); 307 this.propertyChangeSupport.firePropertyChange("SUPPORTED_LANGUAGES", oldSupportedLanguages, supportedLanguages); 308 if (oldLanguage != this.language) { 309 this.propertyChangeSupport.firePropertyChange("LANGUAGE", oldLanguage, language); 310 } 311 } 312 } 313 314 /** 315 * Returns the string matching <code>resourceKey</code> in current language in the 316 * context of <code>resourceClass</code> or for a resource family if <code>resourceClass</code> 317 * is a string. 318 * If <code>resourceParameters</code> isn't empty the string is considered 319 * as a format string, and the returned string will be formatted with these parameters. 320 * This implementation searches first the key in a properties file named as 321 * <code>resourceClass</code>, then if this file doesn't exist, it searches 322 * the key prefixed by <code>resourceClass</code> name and a dot in a package.properties file 323 * in the folder matching the package of <code>resourceClass</code>. 324 * @throws IllegalArgumentException if no string for the given key can be found 325 */ 326 UserPreferences.prototype.getLocalizedString = function(resourceClass, resourceKey, resourceParameters) { 327 this.getResourceBundles(resourceClass); 328 if (resourceClass == "DefaultFurnitureCatalog") { 329 return CoreTools.getStringFromKey.apply(null, [this.furnitureCatalogResourceBundles, resourceKey].concat(Array.prototype.slice.call(arguments, 2))); 330 } else if (resourceClass == "DefaultTexturesCatalog") { 331 return CoreTools.getStringFromKey.apply(null, [this.texturesCatalogResourceBundles, resourceKey].concat(Array.prototype.slice.call(arguments, 2))); 332 } else { 333 // JSweet-generated code interop: if resourceClass is a constructor, it may contain the Java class full name in __class 334 if (resourceClass.__class) { 335 var resourceClassArray = resourceClass.__class.split('.'); 336 resourceClass = resourceClassArray[resourceClassArray.length - 1]; 337 } 338 var key = resourceClass + "." + resourceKey; 339 return CoreTools.getStringFromKey.apply(null, [this.resourceBundles, key].concat(Array.prototype.slice.call(arguments, 2))); 340 } 341 } 342 343 /** 344 * Returns the keys of the localized property strings of the given resource family. 345 * @throws IllegalArgumentException if the given resourceFamily is not supported 346 */ 347 UserPreferences.prototype.getLocalizedStringKeys = function(resourceFamily) { 348 if (resourceClass == "DefaultFurnitureCatalog") { 349 var keys = {}; 350 for (var i = 0; i < resourceBundles.length; i++) { 351 if (resourceBundles[i] != null) { 352 CoreTools.merge(keys, resourceBundles[i]); 353 } 354 } 355 return Object.getOwnPropertyNames(keys); 356 } else { 357 throw new IllegalArgumentException("unsupported family"); 358 } 359 } 360 361 /** 362 * Returns the resource bundle for the given resource family. 363 */ 364 UserPreferences.prototype.getResourceBundles = function(resourceClass) { 365 if (resourceClass == "DefaultFurnitureCatalog") { 366 if (this.furnitureCatalogResourceBundles.length == 0) { 367 this.furnitureCatalogResourceBundles = CoreTools.loadResourceBundles("resources/DefaultFurnitureCatalog", Locale.getDefault()); 368 } 369 return this.furnitureCatalogResourceBundles; 370 } else if (resourceClass == "DefaultTexturesCatalog") { 371 if (this.texturesCatalogResourceBundles.length == 0) { 372 this.texturesCatalogResourceBundles = CoreTools.loadResourceBundles("resources/DefaultTexturesCatalog", Locale.getDefault()); 373 } 374 return this.texturesCatalogResourceBundles; 375 } else { 376 if (this.resourceBundles.length == 0) { 377 this.resourceBundles = CoreTools.loadResourceBundles("resources/localization", Locale.getDefault()); 378 } 379 return this.resourceBundles; 380 } 381 } 382 383 /** 384 * Returns the default currency in use, noted with ISO 4217 code, or <code>null</code> 385 * if prices aren't used in application. 386 * @ignore 387 */ 388 UserPreferences.prototype.getCurrency = function() { 389 return this.currency; 390 } 391 392 /** 393 * Sets the default currency in use. 394 * @ignore 395 */ 396 UserPreferences.prototype.setCurrency = function(currency) { 397 if (currency != this.currency) { 398 var oldCurrency = this.currency; 399 this.currency = currency; 400 this.propertyChangeSupport.firePropertyChange("CURRENCY", oldCurrency, currency); 401 } 402 } 403 404 /** 405 * Returns <code>true</code> if Value Added Tax should be taken in account in prices. 406 * @since 6.0 407 * @ignore 408 */ 409 UserPreferences.prototype.isValueAddedTaxEnabled = function() { 410 return this.valueAddedTaxEnabled; 411 } 412 413 /** 414 * Sets whether Value Added Tax should be taken in account in prices. 415 * @param valueAddedTaxEnabled if <code>true</code> VAT will be added to prices. 416 * @since 6.0 417 * @ignore 418 */ 419 UserPreferences.prototype.setValueAddedTaxEnabled = function(valueAddedTaxEnabled) { 420 if (this.valueAddedTaxEnabled !== valueAddedTaxEnabled) { 421 this.valueAddedTaxEnabled = valueAddedTaxEnabled; 422 this.propertyChangeSupport.firePropertyChange("VALUE_ADDED_TAX_ENABLED", 423 !valueAddedTaxEnabled, valueAddedTaxEnabled); 424 } 425 } 426 427 /** 428 * Returns the Value Added Tax percentage applied to prices by default, or <code>null</code> 429 * if VAT isn't taken into account in the application. 430 * @since 6.0 431 * @ignore 432 */ 433 UserPreferences.prototype.getDefaultValueAddedTaxPercentage = function() { 434 return this.defaultValueAddedTaxPercentage; 435 } 436 437 /** 438 * Sets the Value Added Tax percentage applied to prices by default. 439 * @param {Big} valueAddedTaxPercentage the default VAT percentage 440 * @since 6.0 441 * @ignore 442 */ 443 UserPreferences.prototype.setDefaultValueAddedTaxPercentage = function(valueAddedTaxPercentage) { 444 if (valueAddedTaxPercentage !== this.defaultValueAddedTaxPercentage 445 && (valueAddedTaxPercentage == null || this.defaultValueAddedTaxPercentage == null || !valueAddedTaxPercentage.eq(this.defaultValueAddedTaxPercentage))) { 446 var oldValueAddedTaxPercentage = this.defaultValueAddedTaxPercentage; 447 this.defaultValueAddedTaxPercentage = valueAddedTaxPercentage; 448 this.propertyChangeSupport.firePropertyChange("DEFAULT_VALUE_ADDED_TAX_PERCENTAGE", oldValueAddedTaxPercentage, valueAddedTaxPercentage); 449 450 } 451 } 452 453 /** 454 * Returns <code>true</code> if the furniture catalog should be viewed in a tree. 455 * @return {boolean} 456 * @ignore 457 */ 458 UserPreferences.prototype.isFurnitureCatalogViewedInTree = function() { 459 return this.furnitureCatalogViewedInTree; 460 } 461 462 /** 463 * Sets whether the furniture catalog should be viewed in a tree or a different way. 464 * @param {boolean} 465 * @ignore 466 */ 467 UserPreferences.prototype.setFurnitureCatalogViewedInTree = function(furnitureCatalogViewedInTree) { 468 if (this.furnitureCatalogViewedInTree !== furnitureCatalogViewedInTree) { 469 this.furnitureCatalogViewedInTree = furnitureCatalogViewedInTree; 470 this.propertyChangeSupport.firePropertyChange("FURNITURE_CATALOG_VIEWED_IN_TREE", 471 !furnitureCatalogViewedInTree, furnitureCatalogViewedInTree); 472 } 473 } 474 475 /** 476 * Returns <code>true</code> if the navigation panel should be displayed. 477 * @return {boolean} 478 */ 479 UserPreferences.prototype.isNavigationPanelVisible = function() { 480 return this.navigationPanelVisible; 481 } 482 483 /** 484 * Sets whether the navigation panel should be displayed or not. 485 * @param {boolean} navigationPanelVisible 486 */ 487 UserPreferences.prototype.setNavigationPanelVisible = function(navigationPanelVisible) { 488 if (this.navigationPanelVisible !== navigationPanelVisible) { 489 this.navigationPanelVisible = navigationPanelVisible; 490 this.propertyChangeSupport.firePropertyChange("NAVIGATION_PANEL_VISIBLE", 491 !navigationPanelVisible, navigationPanelVisible); 492 } 493 } 494 495 /** 496 * Returns whether interactive editing in 3D view is enabled or not. 497 * @return {boolean} 498 * @since 7.2 499 */ 500 UserPreferences.prototype.isEditingIn3DViewEnabled = function() { 501 return this.editingIn3DViewEnabled; 502 } 503 504 /** 505 * Sets whether interactive editing in 3D view is enabled or not. 506 * @param {boolean} editingIn3DViewEnabled 507 * @since 7.2 508 */ 509 UserPreferences.prototype.setEditingIn3DViewEnabled = function(editingIn3DViewEnabled) { 510 if (editingIn3DViewEnabled != this.editingIn3DViewEnabled) { 511 this.editingIn3DViewEnabled = editingIn3DViewEnabled; 512 this.propertyChangeSupport.firePropertyChange("EDITING_IN_3D_VIEW_ENABLED", 513 !editingIn3DViewEnabled, editingIn3DViewEnabled); 514 } 515 } 516 517 /** 518 * Returns whether observer camera should be centered on selection or not. 519 * @return {boolean} 520 */ 521 UserPreferences.prototype.isAerialViewCenteredOnSelectionEnabled = function() { 522 return this.aerialViewCenteredOnSelectionEnabled; 523 } 524 525 /** 526 * Sets whether aerial view should be centered on selection or not. 527 * @param {boolean} aerialViewCenteredOnSelectionEnabled 528 */ 529 UserPreferences.prototype.setAerialViewCenteredOnSelectionEnabled = function(aerialViewCenteredOnSelectionEnabled) { 530 if (aerialViewCenteredOnSelectionEnabled !== this.aerialViewCenteredOnSelectionEnabled) { 531 this.aerialViewCenteredOnSelectionEnabled = aerialViewCenteredOnSelectionEnabled; 532 this.propertyChangeSupport.firePropertyChange("AERIAL_VIEW_CENTERED_ON_SELECTION_ENABLED", 533 !aerialViewCenteredOnSelectionEnabled, aerialViewCenteredOnSelectionEnabled); 534 } 535 } 536 537 /** 538 * Returns whether the observer camera should be selected at each change. 539 * @return {boolean} 540 * @since 5.5 541 */ 542 UserPreferences.prototype.isObserverCameraSelectedAtChange = function() { 543 return this.observerCameraSelectedAtChange; 544 } 545 546 /** 547 * Sets whether the observer camera should be selected at each change. 548 * @param {boolean} observerCameraSelectedAtChange 549 * @since 5.5 550 */ 551 UserPreferences.prototype.setObserverCameraSelectedAtChange = function(observerCameraSelectedAtChange) { 552 if (observerCameraSelectedAtChange !== this.observerCameraSelectedAtChange) { 553 this.observerCameraSelectedAtChange = observerCameraSelectedAtChange; 554 this.propertyChangeSupport.firePropertyChange("OBSERVER_CAMERA_SELECTED_AT_CHANGE", 555 !observerCameraSelectedAtChange, observerCameraSelectedAtChange); 556 } 557 } 558 559 /** 560 * Returns <code>true</code> if magnetism is enabled. 561 * @return {boolean} <code>true</code> by default. 562 */ 563 UserPreferences.prototype.isMagnetismEnabled = function() { 564 return this.magnetismEnabled; 565 } 566 567 /** 568 * Sets whether magnetism is enabled or not, and notifies 569 * listeners of this change. 570 * @param {boolean} magnetismEnabled <code>true</code> if magnetism is enabled, 571 * <code>false</code> otherwise. 572 */ 573 UserPreferences.prototype.setMagnetismEnabled = function(magnetismEnabled) { 574 if (this.magnetismEnabled !== magnetismEnabled) { 575 this.magnetismEnabled = magnetismEnabled; 576 this.propertyChangeSupport.firePropertyChange("MAGNETISM_ENABLED", 577 !magnetismEnabled, magnetismEnabled); 578 } 579 } 580 581 /** 582 * Returns <code>true</code> if rulers are visible. 583 * @return {boolean} <code>true</code> by default. 584 * @ignore 585 */ 586 UserPreferences.prototype.isRulersVisible = function() { 587 return this.rulersVisible; 588 } 589 590 /** 591 * Sets whether rulers are visible or not, and notifies 592 * listeners of this change. 593 * @param {boolean} rulersVisible <code>true</code> if rulers are visible, 594 * <code>false</code> otherwise. 595 * @ignore 596 */ 597 UserPreferences.prototype.setRulersVisible = function(rulersVisible) { 598 if (this.rulersVisible !== rulersVisible) { 599 this.rulersVisible = rulersVisible; 600 this.propertyChangeSupport.firePropertyChange("RULERS_VISIBLE", 601 !rulersVisible, rulersVisible); 602 } 603 } 604 605 /** 606 * Returns <code>true</code> if plan grid visible. 607 * @return {boolean} <code>true</code> by default. 608 */ 609 UserPreferences.prototype.isGridVisible = function() { 610 return this.gridVisible; 611 } 612 613 /** 614 * Sets whether plan grid is visible or not, and notifies 615 * listeners of this change. 616 * @param {boolean} gridVisible <code>true</code> if grid is visible, 617 * <code>false</code> otherwise. 618 */ 619 UserPreferences.prototype.setGridVisible = function(gridVisible) { 620 if (this.gridVisible !== gridVisible) { 621 this.gridVisible = gridVisible; 622 this.propertyChangeSupport.firePropertyChange("GRID_VISIBLE", 623 !gridVisible, gridVisible); 624 } 625 } 626 627 /** 628 * Returns the name of the font that should be used by default or <code>null</code> 629 * if the default font should be the default one in the application. 630 * @return {string} 631 */ 632 UserPreferences.prototype.getDefaultFontName = function() { 633 return this.defaultFontName; 634 } 635 636 /** 637 * Sets the name of the font that should be used by default. 638 * @param {string} defaultFontName 639 */ 640 UserPreferences.prototype.setDefaultFontName = function(defaultFontName) { 641 if (defaultFontName != this.defaultFontName) { 642 var oldName = this.defaultFontName; 643 this.defaultFontName = defaultFontName; 644 this.propertyChangeSupport.firePropertyChange("DEFAULT_FONT_NAME", oldName, defaultFontName); 645 } 646 } 647 648 /** 649 * Returns <code>true</code> if furniture should be viewed from its top in plan. 650 * @return {boolean} 651 */ 652 UserPreferences.prototype.isFurnitureViewedFromTop = function() { 653 return this.furnitureViewedFromTop; 654 } 655 656 /** 657 * Sets how furniture icon should be displayed in plan, and notifies 658 * listeners of this change. 659 * @param {boolean} furnitureViewedFromTop if <code>true</code> the furniture 660 * should be viewed from its top. 661 */ 662 UserPreferences.prototype.setFurnitureViewedFromTop = function(furnitureViewedFromTop) { 663 if (this.furnitureViewedFromTop !== furnitureViewedFromTop) { 664 this.furnitureViewedFromTop = furnitureViewedFromTop; 665 this.propertyChangeSupport.firePropertyChange("FURNITURE_VIEWED_FROM_TOP", 666 !furnitureViewedFromTop, furnitureViewedFromTop); 667 } 668 } 669 670 /** 671 * Returns the size used to generate icons of furniture viewed from top. 672 * @since 5.5 673 */ 674 UserPreferences.prototype.getFurnitureModelIconSize = function() { 675 return this.furnitureModelIconSize; 676 } 677 678 /** 679 * Sets the name of the font that should be used by default. 680 * @since 5.5 681 */ 682 UserPreferences.prototype.setFurnitureModelIconSize = function(furnitureModelIconSize) { 683 if (furnitureModelIconSize !== this.furnitureModelIconSize) { 684 var oldSize = this.furnitureModelIconSize; 685 this.furnitureModelIconSize = furnitureModelIconSize; 686 this.propertyChangeSupport.firePropertyChange("FURNITURE_MODEL_ICON_SIZE", oldSize, furnitureModelIconSize); 687 } 688 } 689 690 /** 691 * Returns <code>true</code> if room floors should be rendered with color or texture in plan. 692 * @return <code>false</code> by default. 693 */ 694 UserPreferences.prototype.isRoomFloorColoredOrTextured = function() { 695 return this.roomFloorColoredOrTextured; 696 } 697 698 /** 699 * Sets whether room floors should be rendered with color or texture, 700 * and notifies listeners of this change. 701 * @param roomFloorColoredOrTextured <code>true</code> if floor color 702 * or texture is used, <code>false</code> otherwise. 703 */ 704 UserPreferences.prototype.setFloorColoredOrTextured = function(roomFloorColoredOrTextured) { 705 if (this.roomFloorColoredOrTextured !== roomFloorColoredOrTextured) { 706 this.roomFloorColoredOrTextured = roomFloorColoredOrTextured; 707 this.propertyChangeSupport.firePropertyChange("ROOM_FLOOR_COLORED_OR_TEXTURED", 708 !roomFloorColoredOrTextured, roomFloorColoredOrTextured); 709 } 710 } 711 712 /** 713 * Returns the wall pattern in plan used by default. 714 * @return {TextureImage} 715 * @ignore 716 */ 717 UserPreferences.prototype.getWallPattern = function() { 718 return this.wallPattern; 719 } 720 721 /** 722 * Sets how walls should be displayed in plan by default, and notifies 723 * listeners of this change. 724 * @ignore 725 */ 726 UserPreferences.prototype.setWallPattern = function(wallPattern) { 727 if (this.wallPattern !== wallPattern) { 728 var oldWallPattern = this.wallPattern; 729 this.wallPattern = wallPattern; 730 this.propertyChangeSupport.firePropertyChange("WALL_PATTERN", 731 oldWallPattern, wallPattern); 732 } 733 } 734 735 /** 736 * Returns the pattern used for new walls in plan or <code>null</code> if it's not set. 737 * @return {TextureImage} 738 */ 739 UserPreferences.prototype.getNewWallPattern = function() { 740 return this.newWallPattern; 741 } 742 743 /** 744 * Sets how new walls should be displayed in plan, and notifies 745 * listeners of this change. 746 */ 747 UserPreferences.prototype.setNewWallPattern = function(newWallPattern) { 748 if (this.newWallPattern !== newWallPattern) { 749 var oldWallPattern = this.newWallPattern; 750 this.newWallPattern = newWallPattern; 751 this.propertyChangeSupport.firePropertyChange("NEW_WALL_PATTERN", 752 oldWallPattern, newWallPattern); 753 } 754 } 755 756 /** 757 * Returns default thickness of new walls in home. 758 */ 759 UserPreferences.prototype.getNewWallThickness = function() { 760 return this.newWallThickness; 761 } 762 763 /** 764 * Sets default thickness of new walls in home, and notifies 765 * listeners of this change. 766 */ 767 UserPreferences.prototype.setNewWallThickness = function(newWallThickness) { 768 if (this.newWallThickness !== newWallThickness) { 769 var oldDefaultThickness = this.newWallThickness; 770 this.newWallThickness = newWallThickness; 771 this.propertyChangeSupport.firePropertyChange("NEW_WALL_THICKNESS", 772 oldDefaultThickness, newWallThickness); 773 } 774 } 775 776 /** 777 * Returns default wall height of new home walls. 778 */ 779 UserPreferences.prototype.getNewWallHeight = function() { 780 return this.newWallHeight; 781 } 782 783 /** 784 * Sets default wall height of new walls, and notifies 785 * listeners of this change. 786 */ 787 UserPreferences.prototype.setNewWallHeight = function(newWallHeight) { 788 if (this.newWallHeight !== newWallHeight) { 789 var oldWallHeight = this.newWallHeight; 790 this.newWallHeight = newWallHeight; 791 this.propertyChangeSupport.firePropertyChange("NEW_WALL_HEIGHT", 792 oldWallHeight, newWallHeight); 793 } 794 } 795 796 /** 797 * Returns default baseboard thickness of new walls in home. 798 */ 799 UserPreferences.prototype.getNewWallBaseboardThickness = function() { 800 return this.newWallBaseboardThickness; 801 } 802 803 /** 804 * Sets default baseboard thickness of new walls in home, and notifies 805 * listeners of this change. 806 */ 807 UserPreferences.prototype.setNewWallBaseboardThickness = function(newWallBaseboardThickness) { 808 if (this.newWallBaseboardThickness !== newWallBaseboardThickness) { 809 var oldThickness = this.newWallBaseboardThickness; 810 this.newWallBaseboardThickness = newWallBaseboardThickness; 811 this.propertyChangeSupport.firePropertyChange("NEW_WALL_SIDEBOARD_THICKNESS", 812 oldThickness, newWallBaseboardThickness); 813 } 814 } 815 816 /** 817 * Returns default baseboard height of new home walls. 818 */ 819 UserPreferences.prototype.getNewWallBaseboardHeight = function() { 820 return this.newWallBaseboardHeight; 821 } 822 823 /** 824 * Sets default baseboard height of new walls, and notifies 825 * listeners of this change. 826 */ 827 UserPreferences.prototype.setNewWallBaseboardHeight = function(newWallBaseboardHeight) { 828 if (this.newWallBaseboardHeight !== newWallBaseboardHeight) { 829 var oldHeight = this.newWallBaseboardHeight; 830 this.newWallBaseboardHeight = newWallBaseboardHeight; 831 this.propertyChangeSupport.firePropertyChange("NEW_WALL_SIDEBOARD_HEIGHT", 832 oldHeight, newWallBaseboardHeight); 833 } 834 } 835 836 /** 837 * Returns the default color of new rooms in home. 838 * @since 6.4 839 */ 840 UserPreferences.prototype.getNewRoomFloorColor = function() { 841 return this.newRoomFloorColor; 842 } 843 844 /** 845 * Sets the default color of new rooms in home, and notifies 846 * listeners of this change. 847 * @since 6.4 848 */ 849 UserPreferences.prototype.setNewRoomFloorColor = function(newRoomFloorColor) { 850 if (this.newRoomFloorColor !== newRoomFloorColor) { 851 var oldRoomFloorColor = this.newRoomFloorColor; 852 this.newRoomFloorColor = newRoomFloorColor; 853 this.propertyChangeSupport.firePropertyChange("NEW_ROOM_FLOOR_COLOR", 854 oldRoomFloorColor, newRoomFloorColor); 855 } 856 } 857 858 /** 859 * Returns default thickness of the floor of new levels in home. 860 */ 861 UserPreferences.prototype.getNewFloorThickness = function() { 862 return this.newFloorThickness; 863 } 864 865 /** 866 * Sets default thickness of the floor of new levels in home, and notifies 867 * listeners of this change. 868 */ 869 UserPreferences.prototype.setNewFloorThickness = function(newFloorThickness) { 870 if (this.newFloorThickness !== newFloorThickness) { 871 var oldFloorThickness = this.newFloorThickness; 872 this.newFloorThickness = newFloorThickness; 873 this.propertyChangeSupport.firePropertyChange("NEW_FLOOR_THICKNESS", 874 oldFloorThickness, newFloorThickness); 875 } 876 } 877 878 /** 879 * Returns the delay between two automatic save operations of homes for recovery purpose. 880 * @return a delay in milliseconds or 0 to disable auto save. 881 * @ignore 882 */ 883 UserPreferences.prototype.getAutoSaveDelayForRecovery = function() { 884 return this.autoSaveDelayForRecovery; 885 } 886 887 /** 888 * Sets the delay between two automatic save operations of homes for recovery purpose. 889 * @ignore 890 */ 891 UserPreferences.prototype.setAutoSaveDelayForRecovery = function(autoSaveDelayForRecovery) { 892 if (this.autoSaveDelayForRecovery !== autoSaveDelayForRecovery) { 893 var oldAutoSaveDelayForRecovery = this.autoSaveDelayForRecovery; 894 this.autoSaveDelayForRecovery = autoSaveDelayForRecovery; 895 this.propertyChangeSupport.firePropertyChange("AUTO_SAVE_DELAY_FOR_RECOVERY", 896 oldAutoSaveDelayForRecovery, autoSaveDelayForRecovery); 897 } 898 } 899 900 /** 901 * Returns an unmodifiable list of the recent homes. 902 * @ignore 903 */ 904 UserPreferences.prototype.getRecentHomes = function() { 905 return this.recentHomes.slice(0); 906 } 907 908 /** 909 * Sets the recent homes list and notifies listeners of this change. 910 * @ignore 911 */ 912 UserPreferences.prototype.setRecentHomes = function(recentHomes) { 913 if (recentHomes != this.recentHomes) { 914 var oldRecentHomes = this.recentHomes; 915 this.recentHomes = recentHomes.slice(0); 916 this.propertyChangeSupport.firePropertyChange("RECENT_HOMES", 917 oldRecentHomes, this.getRecentHomes()); 918 } 919 } 920 921 /** 922 * Returns the maximum count of homes that should be proposed to the user. 923 * @ignore 924 */ 925 UserPreferences.prototype.getRecentHomesMaxCount = function() { 926 return 10; 927 } 928 929 /** 930 * Returns the maximum count of stored cameras in homes that should be proposed to the user. 931 * @ignore 932 */ 933 UserPreferences.prototype.getStoredCamerasMaxCount = function() { 934 return 50; 935 } 936 937 /** 938 * Sets which action tip should be ignored. 939 * <br>This method should be overridden to store the ignore information. 940 * By default it just notifies listeners of this change. 941 * @ignore 942 */ 943 UserPreferences.prototype.setActionTipIgnored = function(actionKey) { 944 this.propertyChangeSupport.firePropertyChange("IGNORED_ACTION_TIP", null, actionKey); 945 } 946 947 /** 948 * Returns whether an action tip should be ignored or not. 949 * <br>This method should be overridden to return the display information 950 * stored in setActionTipIgnored. 951 * By default it returns <code>true</code>. 952 * @ignore 953 */ 954 UserPreferences.prototype.isActionTipIgnored = function(actionKey) { 955 return true; 956 } 957 958 /** 959 * Resets the ignore flag of action tips. 960 * <br>This method should be overridden to clear all the display flags. 961 * By default it just notifies listeners of this change. 962 * @ignore 963 */ 964 UserPreferences.prototype.resetIgnoredActionTips = function() { 965 this.propertyChangeSupport.firePropertyChange("IGNORED_ACTION_TIP", null, null); 966 } 967 968 /** 969 * Returns the default text style of a class of selectable item. 970 * @ignore 971 */ 972 UserPreferences.prototype.getDefaultTextStyle = function(selectableClass) { 973 if (selectableClass.name == "Room") { 974 return UserPreferences.DEFAULT_ROOM_TEXT_STYLE; 975 } else { 976 return UserPreferences.DEFAULT_TEXT_STYLE; 977 } 978 } 979 980 /** 981 * Returns the strings that may be used for the auto completion of the given <code>property</code>. 982 * @ignore 983 */ 984 UserPreferences.prototype.getAutoCompletionStrings = function(property) { 985 var propertyAutoCompletionStrings = this.autoCompletionStrings.get(property); 986 if (propertyAutoCompletionStrings !== undefined) { 987 return propertyAutoCompletionStrings.slice(0); 988 } else { 989 return []; 990 } 991 } 992 993 /** 994 * Adds the given string to the list of the strings used in auto completion of a <code>property</code> 995 * and notifies listeners of this change. 996 * @ignore 997 */ 998 UserPreferences.prototype.addAutoCompletionString = function(property, autoCompletionString) { 999 if (autoCompletionString !== null 1000 && autoCompletionString.length > 0) { 1001 var propertyAutoCompletionStrings = this.autoCompletionStrings [property]; 1002 if (propertyAutoCompletionStrings === undefined) { 1003 propertyAutoCompletionStrings = []; 1004 } else if (propertyAutoCompletionStrings.indexOf(autoCompletionString) < 0) { 1005 propertyAutoCompletionStrings = propertyAutoCompletionStrings.slice(0); 1006 } else { 1007 return; 1008 } 1009 propertyAutoCompletionStrings.splice(0, 0, autoCompletionString); 1010 this.setAutoCompletionStrings(property, propertyAutoCompletionStrings); 1011 } 1012 } 1013 1014 /** 1015 * Sets the auto completion strings list of the given <code>property</code> and notifies listeners of this change. 1016 * @ignore 1017 */ 1018 UserPreferences.prototype.setAutoCompletionStrings = function(property, autoCompletionStrings) { 1019 var propertyAutoCompletionStrings = this.autoCompletionStrings [property]; 1020 if (autoCompletionStrings != propertyAutoCompletionStrings) { 1021 this.autoCompletionStrings [property] = autoCompletionStrings.slice(0); 1022 this.propertyChangeSupport.firePropertyChange("AUTO_COMPLETION_STRINGS", 1023 null, property); 1024 } 1025 } 1026 1027 /** 1028 * Returns the list of properties with auto completion strings. 1029 * @ignore 1030 */ 1031 UserPreferences.prototype.getAutoCompletedProperties = function() { 1032 if (this.autoCompletionStrings !== null) { 1033 return Object.keys(this.autoCompletionStrings); 1034 } else { 1035 return []; 1036 } 1037 } 1038 1039 /** 1040 * Returns the list of the recent colors. 1041 * @ignore 1042 */ 1043 UserPreferences.prototype.getRecentColors = function() { 1044 return this.recentColors; 1045 } 1046 1047 /** 1048 * Sets the recent colors list and notifies listeners of this change. 1049 * @ignore 1050 */ 1051 UserPreferences.prototype.setRecentColors = function(recentColors) { 1052 if (recentColors != this.recentColors) { 1053 var oldRecentColors = this.recentColors; 1054 this.recentColors = recentColors.slice(0); 1055 this.propertyChangeSupport.firePropertyChange("RECENT_COLORS", 1056 oldRecentColors, this.getRecentColors()); 1057 } 1058 } 1059 1060 /** 1061 * Returns the list of the recent textures. 1062 * @ignore 1063 */ 1064 UserPreferences.prototype.getRecentTextures = function() { 1065 return this.recentTextures; 1066 } 1067 1068 /** 1069 * Sets the recent colors list and notifies listeners of this change. 1070 * @ignore 1071 */ 1072 UserPreferences.prototype.setRecentTextures = function(recentTextures) { 1073 if (recentTextures != this.recentTextures) { 1074 var oldRecentTextures = this.recentTextures; 1075 this.recentTextures = recentTextures.slice(0); 1076 this.propertyChangeSupport.firePropertyChange("RECENT_TEXTURES", 1077 oldRecentTextures, this.getRecentTextures()); 1078 } 1079 } 1080 1081 /** 1082 * Sets the home examples available for the user. 1083 * @param {HomeDescriptor[]} homeExamples an array of examples 1084 * @since 5.5 1085 * @ignore 1086 */ 1087 UserPreferences.prototype.setHomeExamples = function(homeExamples) { 1088 if (homeExamples != this.homeExamples) { 1089 var oldExamples = this.homeExamples; 1090 this.homeExamples = homeExamples.slice(0); 1091 this.propertyChangeSupport.firePropertyChange("HOME_EXAMPLES", 1092 oldExamples, this.getHomeExamples()); 1093 } 1094 } 1095 1096 /** 1097 * Returns the home examples available for the user. 1098 * @return {HomeDescriptor[]} an array of examples. 1099 * @since 5.5 1100 * @ignore 1101 */ 1102 UserPreferences.prototype.getHomeExamples = function() { 1103 return this.homeExamples; 1104 } 1105 1106 /** 1107 * @return {boolean} <code>true</code> if updates should be checked. 1108 * @ignore 1109 */ 1110 UserPreferences.prototype.isCheckUpdatesEnabled = function() { 1111 // Empty implementation because it is used by the controller but useless for the Web version 1112 } 1113 1114 /** 1115 * Sets whether updates should be checked or not. 1116 * @param {boolean} updatesChecked 1117 * @since 4.0 1118 */ 1119 UserPreferences.prototype.setCheckUpdatesEnabled = function(updatesChecked) { 1120 // Empty implementation because it is used by the controller but useless for the Web version 1121 } 1122 1123 /** 1124 * Returns <code>true</code> if large imported images should be resized without requesting user. 1125 * @ignore 1126 */ 1127 UserPreferences.prototype.isImportedImageResizedWithoutPrompting = function() { 1128 return true; 1129 } 1130 1131 1132 /** 1133 * Default user preferences. 1134 * @param {string[]|boolean} [furnitureCatalogUrls] 1135 * @param {string} [furnitureResourcesUrlBase] 1136 * @param {string[]} [texturesCatalogUrls] 1137 * @param {string} [texturesResourcesUrlBase] 1138 * @constructor 1139 * @extends UserPreferences 1140 * @author Emmanuel Puybaret 1141 */ 1142 function DefaultUserPreferences(furnitureCatalogUrls, furnitureResourcesUrlBase, 1143 texturesCatalogUrls, texturesResourcesUrlBase) { 1144 UserPreferences.call(this); 1145 1146 var readCatalogs; 1147 var userLanguage; 1148 if (furnitureCatalogUrls !== undefined 1149 && (typeof furnitureCatalogUrls === "boolean")) { 1150 readCatalogs = furnitureCatalogUrls; 1151 userLanguage = furnitureResourcesUrlBase; 1152 this.furnitureCatalogUrls = undefined; 1153 this.furnitureResourcesUrlBase = undefined; 1154 this.texturesCatalogUrls = undefined; 1155 this.texturesResourcesUrlBase = undefined; 1156 } else { 1157 readCatalogs = true; 1158 userLanguage = Locale.getDefault(); 1159 this.furnitureCatalogUrls = furnitureCatalogUrls; 1160 this.furnitureResourcesUrlBase = furnitureResourcesUrlBase; 1161 this.texturesCatalogUrls = texturesCatalogUrls; 1162 this.texturesResourcesUrlBase = texturesResourcesUrlBase; 1163 } 1164 1165 // Build default patterns catalog 1166 var patterns = []; 1167 patterns.push(new DefaultPatternTexture("foreground")); 1168 patterns.push(new DefaultPatternTexture("reversedHatchUp")); 1169 patterns.push(new DefaultPatternTexture("reversedHatchDown")); 1170 patterns.push(new DefaultPatternTexture("reversedCrossHatch")); 1171 patterns.push(new DefaultPatternTexture("background")); 1172 patterns.push(new DefaultPatternTexture("hatchUp")); 1173 patterns.push(new DefaultPatternTexture("hatchDown")); 1174 patterns.push(new DefaultPatternTexture("crossHatch")); 1175 var patternsCatalog = new PatternsCatalog(patterns); 1176 this.setPatternsCatalog(patternsCatalog); 1177 this.setFurnitureCatalog(readCatalogs && (typeof DefaultFurnitureCatalog === "function") 1178 ? (Array.isArray(furnitureCatalogUrls) 1179 ? new DefaultFurnitureCatalog(furnitureCatalogUrls, furnitureResourcesUrlBase) 1180 : new DefaultFurnitureCatalog(this)) 1181 : new FurnitureCatalog()); 1182 this.setTexturesCatalog(readCatalogs && (typeof DefaultTexturesCatalog === "function") 1183 ? (Array.isArray(texturesCatalogUrls) 1184 ? new DefaultTexturesCatalog(texturesCatalogUrls, texturesResourcesUrlBase) 1185 : new DefaultTexturesCatalog(this)) 1186 : new TexturesCatalog()); 1187 1188 if (userLanguage == "en_US") { 1189 this.setUnit(LengthUnit.INCH); 1190 this.setNewWallThickness(7.62); 1191 this.setNewWallHeight(243.84); 1192 this.setNewWallBaseboardThickness(0.9525); 1193 this.setNewWallBaseboardHeight(6.35); 1194 } else { 1195 this.setUnit(LengthUnit.CENTIMETER); 1196 this.setNewWallThickness(7.5); 1197 this.setNewWallHeight(250); 1198 this.setNewWallBaseboardThickness(1); 1199 this.setNewWallBaseboardHeight(7); 1200 } 1201 1202 this.setNewFloorThickness(12); 1203 this.setNavigationPanelVisible(false); 1204 this.setWallPattern(patternsCatalog.getPattern("hatchUp")); 1205 this.setNewWallPattern(this.getWallPattern()); 1206 this.setAerialViewCenteredOnSelectionEnabled(true); 1207 this.setAutoSaveDelayForRecovery(60000); 1208 } 1209 DefaultUserPreferences.prototype = Object.create(UserPreferences.prototype); 1210 DefaultUserPreferences.prototype.constructor = DefaultUserPreferences; 1211 1212 /** 1213 * Writes user preferences. 1214 */ 1215 DefaultUserPreferences.prototype.write = function() { 1216 UserPreferences.prototype.write.call(this); 1217 } 1218 1219 1220 /** 1221 * Creates a pattern built from resources. 1222 * @param {string} name 1223 * @constructor 1224 * @ignore 1225 * @author Emmanuel Puybaret 1226 */ 1227 function DefaultPatternTexture(name) { 1228 this.name = name; 1229 this.image = new URLContent(ZIPTools.getScriptFolder() + "resources/patterns/" + this.name + ".png"); 1230 } 1231 1232 DefaultPatternTexture["__class"] = "com.eteks.sweethome3d.io.DefaultPatternTexture"; 1233 DefaultPatternTexture["__interfaces"] = ["com.eteks.sweethome3d.model.TextureImage"]; 1234 DefaultPatternTexture['__transients'] = ["image"]; 1235 1236 /** 1237 * Returns the name of this texture. 1238 * @return {string} 1239 */ 1240 DefaultPatternTexture.prototype.getName = function () { 1241 return this.name; 1242 } 1243 1244 /** 1245 * Returns the creator of this texture. 1246 * @return {string} 1247 */ 1248 DefaultPatternTexture.prototype.getCreator = function () { 1249 return null; 1250 } 1251 1252 /** 1253 * Returns the content of the image used for this texture. 1254 * @return {Object} 1255 */ 1256 DefaultPatternTexture.prototype.getImage = function () { 1257 return this.image; 1258 } 1259 1260 /** 1261 * Returns the width of the image in centimeters. 1262 * @return {number} 1263 */ 1264 DefaultPatternTexture.prototype.getWidth = function () { 1265 return 10; 1266 } 1267 1268 /** 1269 * Returns the height of the image in centimeters. 1270 * @return {number} 1271 */ 1272 DefaultPatternTexture.prototype.getHeight = function () { 1273 return 10; 1274 } 1275 1276 /** 1277 * Returns <code>true</code> if the object in parameter is equal to this texture. 1278 * @param {Object} obj 1279 * @return {boolean} 1280 */ 1281 DefaultPatternTexture.prototype.equals = function (obj) { 1282 if (obj === this) { 1283 return true; 1284 } else if (obj instanceof DefaultPatternTexture) { 1285 var pattern = obj; 1286 return pattern.name == this.name; 1287 } else { 1288 return false; 1289 } 1290 } 1291 1292 1293 /** 1294 * User's preferences, synchronized with a backend. 1295 * @param {{furnitureCatalogURLs: string[], 1296 * furnitureResourcesURLBase: string, 1297 * texturesCatalogURLs: string[], 1298 * texturesResourcesURLBase: string, 1299 * writePreferencesURL: string, 1300 * readPreferencesURL: string, 1301 * writeResourceURL: string, 1302 * readResourceURL: string, 1303 * writePreferencesResourceURL: string, 1304 * readPreferencesResourceURL: string, 1305 * defaultUserLanguage: string, 1306 * writingObserver: {writeStarted: Function, 1307 * writeSucceeded: Function, 1308 * writeFailed: Function, 1309 * connectionFound: Function, 1310 * connectionLost: Function} 1311 * }} [configuration] preferences configuration. 1312 * If configuration.writePreferencesResourceURL / configuration.readPreferencesResourceURL is missing, 1313 * configuration.writeResourceURL / configuration.readResourceURL will be used. 1314 * @constructor 1315 * @extends UserPreferences 1316 * @author Louis Grignon 1317 * @author Emmanuel Puybaret 1318 */ 1319 function RecordedUserPreferences(configuration) { 1320 UserPreferences.call(this); 1321 1322 if (configuration !== undefined) { 1323 this.furnitureCatalogUrls = configuration.furnitureCatalogURLs; 1324 this.furnitureResourcesUrlBase = configuration.furnitureResourcesURLBase; 1325 this.texturesCatalogUrls = configuration.texturesCatalogURLs; 1326 this.texturesResourcesUrlBase = configuration.texturesResourcesURLBase; 1327 this.writePreferencesUrl = configuration.writePreferencesURL; 1328 this.readPreferencesUrl = configuration.readPreferencesURL; 1329 this.writeResourceUrl = configuration.writePreferencesResourceURL !== undefined 1330 ? configuration.writePreferencesResourceURL : configuration.writeResourceURL; 1331 this.readResourceUrl = configuration.readPreferencesResourceURL !== undefined 1332 ? configuration.readPreferencesResourceURL : configuration.readResourceURL; 1333 this.writingObserver = configuration.writingObserver; 1334 } 1335 1336 var userLanguage; 1337 if (configuration !== undefined && configuration.defaultUserLanguage !== undefined) { 1338 userLanguage = configuration.defaultUserLanguage; 1339 } else { 1340 userLanguage = this.getLanguage(); 1341 } 1342 1343 this.uploadingBlobs = {}; 1344 this.properties = {}; 1345 this.setFurnitureCatalog(new FurnitureCatalog()); 1346 this.setTexturesCatalog(new TexturesCatalog()); 1347 if (this.readPreferencesUrl) { 1348 this.updatePreferencesFromProperties(this.properties, userLanguage, false); 1349 this.readPreferences(this.properties, userLanguage); 1350 } else { 1351 // Initialize properties from default preferences 1352 this.updatePreferencesFromProperties(this.properties, userLanguage, true); 1353 this.addListeners(); 1354 } 1355 1356 } 1357 RecordedUserPreferences.prototype = Object.create(UserPreferences.prototype); 1358 RecordedUserPreferences.prototype.constructor = RecordedUserPreferences; 1359 1360 RecordedUserPreferences.LANGUAGE = "language"; 1361 RecordedUserPreferences.UNIT = "unit"; 1362 RecordedUserPreferences.CURRENCY = "currency"; 1363 RecordedUserPreferences.VALUE_ADDED_TAX_ENABLED = "valueAddedTaxEnabled"; 1364 RecordedUserPreferences.DEFAULT_VALUE_ADDED_TAX_PERCENTAGE = "defaultValueAddedTaxPercentage"; 1365 RecordedUserPreferences.FURNITURE_CATALOG_VIEWED_IN_TREE = "furnitureCatalogViewedInTree"; 1366 RecordedUserPreferences.NAVIGATION_PANEL_VISIBLE = "navigationPanelVisible"; 1367 RecordedUserPreferences.EDITING_IN_3D_VIEW_ENABLED = "editingIn3DViewEnabled"; 1368 RecordedUserPreferences.AERIAL_VIEW_CENTERED_ON_SELECTION_ENABLED = "aerialViewCenteredOnSelectionEnabled"; 1369 RecordedUserPreferences.OBSERVER_CAMERA_SELECTED_AT_CHANGE = "observerCameraSelectedAtChange"; 1370 RecordedUserPreferences.MAGNETISM_ENABLED = "magnetismEnabled"; 1371 RecordedUserPreferences.RULERS_VISIBLE = "rulersVisible"; 1372 RecordedUserPreferences.GRID_VISIBLE = "gridVisible"; 1373 RecordedUserPreferences.DEFAULT_FONT_NAME = "defaultFontName"; 1374 RecordedUserPreferences.FURNITURE_VIEWED_FROM_TOP = "furnitureViewedFromTop"; 1375 RecordedUserPreferences.FURNITURE_MODEL_ICON_SIZE = "furnitureModelIconSize"; 1376 RecordedUserPreferences.ROOM_FLOOR_COLORED_OR_TEXTURED = "roomFloorColoredOrTextured"; 1377 RecordedUserPreferences.WALL_PATTERN = "wallPattern"; 1378 RecordedUserPreferences.NEW_WALL_PATTERN = "newWallPattern"; 1379 RecordedUserPreferences.NEW_WALL_THICKNESS = "newWallThickness"; 1380 RecordedUserPreferences.NEW_WALL_HEIGHT = "newHomeWallHeight"; 1381 RecordedUserPreferences.NEW_WALL_BASEBOARD_THICKNESS = "newWallBaseboardThickness"; 1382 RecordedUserPreferences.NEW_WALL_BASEBOARD_HEIGHT = "newWallBaseboardHeight"; 1383 RecordedUserPreferences.NEW_FLOOR_THICKNESS = "newFloorThickness"; 1384 RecordedUserPreferences.AUTO_SAVE_DELAY_FOR_RECOVERY = "autoSaveDelayForRecovery"; 1385 RecordedUserPreferences.RECENT_HOMES = "recentHomes#"; 1386 RecordedUserPreferences.IGNORED_ACTION_TIP = "ignoredActionTip#"; 1387 RecordedUserPreferences.TEXTURE_NAME = "textureName#"; 1388 RecordedUserPreferences.TEXTURE_CREATOR = "textureCreator#"; 1389 RecordedUserPreferences.TEXTURE_CATEGORY = "textureCategory#"; 1390 RecordedUserPreferences.TEXTURE_IMAGE = "textureImage#"; 1391 RecordedUserPreferences.TEXTURE_WIDTH = "textureWidth#"; 1392 RecordedUserPreferences.TEXTURE_HEIGHT = "textureHeight#"; 1393 1394 /** 1395 * Returns value of property in properties map, and return defaultValue if value is null or undefined. 1396 * @param {string, string} properties 1397 * @param {string} propertyKey 1398 * @param {any} [defaultValue] 1399 * @return {any} property's value 1400 * @private 1401 */ 1402 RecordedUserPreferences.prototype.getProperty = function(properties, propertyKey, defaultValue) { 1403 if (properties[propertyKey] === undefined || properties[propertyKey] === null) { 1404 return defaultValue; 1405 } 1406 return properties[propertyKey]; 1407 } 1408 1409 /** 1410 * Returns preferences internal properties. 1411 * @return {string, string} 1412 * @private 1413 */ 1414 RecordedUserPreferences.prototype.getProperties = function() { 1415 return this.properties; 1416 } 1417 1418 /** 1419 * Sets value of a property in properties map. 1420 * @param {string, string} properties 1421 * @param {string} propertyKey 1422 * @param {any} propertyValue 1423 * @private 1424 */ 1425 RecordedUserPreferences.prototype.setProperty = function(properties, propertyKey, propertyValue) { 1426 properties[propertyKey] = propertyValue; 1427 } 1428 1429 /** 1430 * Removes the given property in properties map. 1431 * @param {string, string} properties 1432 * @param {string} propertyKey 1433 * @private 1434 */ 1435 RecordedUserPreferences.prototype.removeProperty = function(properties, propertyKey) { 1436 delete properties[propertyKey]; 1437 } 1438 1439 /** 1440 * Updates saved preferences from the given properties. 1441 * @param {string, string} properties 1442 * @param {string} defaultUserLanguage 1443 * @param {boolean} updateCatalogs 1444 * @private 1445 */ 1446 RecordedUserPreferences.prototype.updatePreferencesFromProperties = function(properties, defaultUserLanguage, updateCatalogs) { 1447 this.setLanguage(this.getProperty(properties, RecordedUserPreferences.LANGUAGE, defaultUserLanguage)); 1448 1449 // Read default furniture and textures catalog 1450 if (updateCatalogs) { 1451 this.updateDefaultCatalogs(); 1452 } 1453 this.readModifiableTexturesCatalog(properties); 1454 1455 var defaultPreferences = new DefaultUserPreferences(false, defaultUserLanguage); 1456 defaultPreferences.setLanguage(this.getLanguage()); 1457 1458 // Fill default patterns catalog 1459 var patternsCatalog = defaultPreferences.getPatternsCatalog(); 1460 this.setPatternsCatalog(patternsCatalog); 1461 1462 // Read other preferences 1463 var unit = LengthUnit[this.getProperty(properties, RecordedUserPreferences.UNIT)]; 1464 if (!unit) { 1465 unit = defaultPreferences.getLengthUnit(); 1466 } 1467 this.setUnit(unit); 1468 1469 this.setCurrency(this.getProperty(properties, RecordedUserPreferences.CURRENCY, defaultPreferences.getCurrency())); 1470 this.setValueAddedTaxEnabled( 1471 this.getProperty(properties, RecordedUserPreferences.VALUE_ADDED_TAX_ENABLED, 1472 '' + defaultPreferences.isValueAddedTaxEnabled()) == 'true'); 1473 var percentage = this.getProperty(properties, RecordedUserPreferences.DEFAULT_VALUE_ADDED_TAX_PERCENTAGE); 1474 var valueAddedTaxPercentage = defaultPreferences.getDefaultValueAddedTaxPercentage(); 1475 if (percentage !== null) { 1476 try { 1477 valueAddedTaxPercentage = new Big(percentage); 1478 } catch (ex) { 1479 } 1480 } 1481 this.setDefaultValueAddedTaxPercentage(valueAddedTaxPercentage); 1482 this.setFurnitureCatalogViewedInTree( 1483 this.getProperty(properties, RecordedUserPreferences.FURNITURE_CATALOG_VIEWED_IN_TREE, 1484 '' + defaultPreferences.isFurnitureCatalogViewedInTree()) == 'true'); 1485 this.setNavigationPanelVisible( 1486 this.getProperty(properties, RecordedUserPreferences.NAVIGATION_PANEL_VISIBLE, 1487 '' + defaultPreferences.isNavigationPanelVisible()) == 'true'); 1488 this.setEditingIn3DViewEnabled( 1489 this.getProperty(properties, RecordedUserPreferences.EDITING_IN_3D_VIEW_ENABLED, 1490 '' + defaultPreferences.isEditingIn3DViewEnabled()) == 'true'); 1491 this.setAerialViewCenteredOnSelectionEnabled( 1492 this.getProperty(properties, RecordedUserPreferences.AERIAL_VIEW_CENTERED_ON_SELECTION_ENABLED, 1493 '' + defaultPreferences.isAerialViewCenteredOnSelectionEnabled()) == 'true'); 1494 this.setObserverCameraSelectedAtChange( 1495 this.getProperty(properties, RecordedUserPreferences.OBSERVER_CAMERA_SELECTED_AT_CHANGE, 1496 '' + defaultPreferences.isObserverCameraSelectedAtChange()) == 'true'); 1497 this.setMagnetismEnabled( 1498 this.getProperty(properties, RecordedUserPreferences.MAGNETISM_ENABLED, 'true') == 'true'); 1499 this.setRulersVisible( 1500 this.getProperty(properties, RecordedUserPreferences.RULERS_VISIBLE, '' + defaultPreferences.isMagnetismEnabled()) == 'true'); 1501 this.setGridVisible( 1502 this.getProperty(properties, RecordedUserPreferences.GRID_VISIBLE, '' + defaultPreferences.isGridVisible()) == 'true'); 1503 this.setDefaultFontName(this.getProperty(properties, RecordedUserPreferences.DEFAULT_FONT_NAME, defaultPreferences.getDefaultFontName())); 1504 this.setFurnitureViewedFromTop( 1505 this.getProperty(properties, RecordedUserPreferences.FURNITURE_VIEWED_FROM_TOP, 1506 '' + defaultPreferences.isFurnitureViewedFromTop()) == 'true'); 1507 this.setFurnitureModelIconSize(parseInt(this.getProperty(properties, RecordedUserPreferences.FURNITURE_MODEL_ICON_SIZE, 1508 '' + defaultPreferences.getFurnitureModelIconSize()))); 1509 this.setFloorColoredOrTextured( 1510 this.getProperty(properties, RecordedUserPreferences.ROOM_FLOOR_COLORED_OR_TEXTURED, 1511 '' + defaultPreferences.isRoomFloorColoredOrTextured()) == 'true'); 1512 1513 try { 1514 this.setWallPattern(patternsCatalog.getPattern(this.getProperty(properties, RecordedUserPreferences.WALL_PATTERN, 1515 defaultPreferences.getWallPattern().getName()))); 1516 } catch (ex) { 1517 // Ensure wall pattern always exists even if new patterns are added in future versions 1518 this.setWallPattern(defaultPreferences.getWallPattern()); 1519 } 1520 1521 try { 1522 if (defaultPreferences.getNewWallPattern() != null) { 1523 this.setNewWallPattern(patternsCatalog.getPattern(this.getProperty(properties, RecordedUserPreferences.NEW_WALL_PATTERN, 1524 defaultPreferences.getNewWallPattern().getName()))); 1525 } 1526 } catch (ex) { 1527 // Keep new wall pattern unchanged 1528 } 1529 1530 this.setNewWallThickness(parseFloat(this.getProperty(properties, RecordedUserPreferences.NEW_WALL_THICKNESS, 1531 '' + defaultPreferences.getNewWallThickness()))); 1532 this.setNewWallHeight(parseFloat(this.getProperty(properties, RecordedUserPreferences.NEW_WALL_HEIGHT, 1533 '' + defaultPreferences.getNewWallHeight()))); 1534 this.setNewWallBaseboardThickness(defaultPreferences.getNewWallBaseboardThickness()); 1535 this.setNewWallBaseboardHeight(defaultPreferences.getNewWallBaseboardHeight()); 1536 this.setNewWallBaseboardThickness(parseFloat(this.getProperty(properties, RecordedUserPreferences.NEW_WALL_BASEBOARD_THICKNESS, 1537 '' + defaultPreferences.getNewWallBaseboardThickness()))); 1538 this.setNewWallBaseboardHeight(parseFloat(this.getProperty(properties, RecordedUserPreferences.NEW_WALL_BASEBOARD_HEIGHT, 1539 '' + defaultPreferences.getNewWallBaseboardHeight()))); 1540 this.setNewFloorThickness(parseFloat(this.getProperty(properties, RecordedUserPreferences.NEW_FLOOR_THICKNESS, 1541 '' + defaultPreferences.getNewFloorThickness()))); 1542 this.setAutoSaveDelayForRecovery(parseInt(this.getProperty(properties, RecordedUserPreferences.AUTO_SAVE_DELAY_FOR_RECOVERY, 1543 '' + defaultPreferences.getAutoSaveDelayForRecovery()))); 1544 // Read recent homes list 1545 var recentHomes = []; 1546 for (var i = 1; i <= this.getRecentHomesMaxCount(); i++) { 1547 var recentHome = this.getProperty(properties, RecordedUserPreferences.RECENT_HOMES + i); 1548 if (recentHome != null) { 1549 recentHomes.push(recentHome); 1550 } 1551 } 1552 this.setRecentHomes(recentHomes); 1553 1554 // Read ignored action tips 1555 for (var i = 1; ; i++) { 1556 var ignoredActionTip = this.getProperty(properties, RecordedUserPreferences.IGNORED_ACTION_TIP + i, ""); 1557 if (ignoredActionTip.length == 0) { 1558 break; 1559 } else { 1560 this.ignoredActionTips[ignoredActionTip] = true; 1561 } 1562 } 1563 } 1564 1565 /** 1566 * Adds listeners to update catalogs and follow properties to save. 1567 * @private 1568 */ 1569 RecordedUserPreferences.prototype.addListeners = function() { 1570 var preferences = this; 1571 this.addPropertyChangeListener("LANGUAGE", function() { 1572 preferences.updateDefaultCatalogs(); 1573 }); 1574 1575 // Add a listener to track written properties and ignore the other ones during a call to write 1576 var savedPropertyListener = function() { 1577 preferences.writtenPropertiesUpdated = true; 1578 }; 1579 var writtenProperties = ["LANGUAGE", "UNIT", "CURRENCY", "VALUE_ADDED_TAX_ENABLED", "DEFAULT_VALUE_ADDED_TAX_PERCENTAGE", 1580 "FURNITURE_CATALOG_VIEWED_IN_TREE", "NAVIGATION_PANEL_VISIBLE", "EDITING_IN_3D_VIEW_ENABLED", "AERIAL_VIEW_CENTERED_ON_SELECTION_ENABLED", 1581 "OBSERVER_CAMERA_SELECTED_AT_CHANGE", "MAGNETISM_ENABLED", "GRID_VISIBLE", "DEFAULT_FONT_NAME", "FURNITURE_VIEWED_FROM_TOP", 1582 "FURNITURE_MODEL_ICON_SIZE", "ROOM_FLOOR_COLORED_OR_TEXTURED", "NEW_WALL_PATTERN", "NEW_WALL_THICKNESS", 1583 "NEW_WALL_HEIGHT", "NEW_WALL_BASEBOARD_THICKNESS", "NEW_WALL_BASEBOARD_HEIGHT", "NEW_FLOOR_THICKNESS"]; 1584 for (var i = 0; i < writtenProperties.length; i++) { 1585 var writtenProperty = writtenProperties[i]; 1586 this.addPropertyChangeListener(writtenProperty, savedPropertyListener); 1587 } 1588 this.getTexturesCatalog().addTexturesListener(savedPropertyListener); 1589 } 1590 1591 /** 1592 * Read preferences properties from backend. 1593 * @param {string, string} properties 1594 * @private 1595 */ 1596 RecordedUserPreferences.prototype.readPreferences = function(properties, defaultUserLanguage) { 1597 try { 1598 var preferences = this; 1599 var updateJsonPreferences = function(jsonPreferences) { 1600 if (jsonPreferences != null) { 1601 var preferencesData = JSON.parse(jsonPreferences); 1602 for (var i in preferencesData) { 1603 properties [i] = preferencesData [i]; 1604 } 1605 } 1606 preferences.updatePreferencesFromProperties(properties, defaultUserLanguage, true); 1607 preferences.addListeners(); 1608 }; 1609 1610 if (this.readPreferencesUrl.indexOf(LocalStorageURLContent.LOCAL_STORAGE_PREFIX) === 0) { 1611 var key = this.readPreferencesUrl.substring(LocalStorageURLContent.LOCAL_STORAGE_PREFIX.length); 1612 updateJsonPreferences(localStorage.getItem(key)); 1613 } else if (this.readPreferencesUrl.indexOf(IndexedDBURLContent.INDEXED_DB_PREFIX) === 0) { 1614 new IndexedDBURLContent(this.readPreferencesUrl).getBlob({ 1615 blobReady: function(blob) { 1616 var reader = new FileReader(); 1617 // Use onload rather that addEventListener for Cordova support 1618 reader.onload = function() { 1619 updateJsonPreferences(reader.result); 1620 }; 1621 reader.readAsText(blob); 1622 }, 1623 blobError : function(status, error) { 1624 preferences.updateDefaultCatalogs(); 1625 preferences.addListeners(); 1626 if (status != -1) { 1627 console.log("Can't read preferences from indexedDB " + status + " " + error); 1628 } 1629 } 1630 }); 1631 } else { 1632 var request = new XMLHttpRequest(); 1633 var querySeparator = this.readPreferencesUrl.indexOf('?') != -1 ? '&' : '?'; 1634 request.open("GET", this.readPreferencesUrl + querySeparator + "requestId=" + UUID.randomUUID(), true); 1635 request.addEventListener("load", function() { 1636 if (request.readyState === XMLHttpRequest.DONE 1637 && request.status === 200) { 1638 updateJsonPreferences(request.responseText); 1639 } else { 1640 preferences.updateDefaultCatalogs(); 1641 preferences.addListeners(); 1642 } 1643 }); 1644 var errorListener = function(ev) { 1645 console.log("Can't read preferences from server"); 1646 preferences.updateDefaultCatalogs(); 1647 preferences.addListeners(); 1648 }; 1649 request.addEventListener("error", errorListener); 1650 request.addEventListener("timeout", errorListener); 1651 request.timeout = 10000; 1652 request.send(); 1653 } 1654 } catch (ex) { 1655 console.log(ex); 1656 preferences.updateDefaultCatalogs(); 1657 preferences.addListeners(); 1658 } 1659 } 1660 1661 /** 1662 * @private 1663 */ 1664 RecordedUserPreferences.prototype.updateDefaultCatalogs = function() { 1665 // Delete default pieces of current furniture catalog 1666 var furnitureCatalog = this.getFurnitureCatalog(); 1667 for (var i = furnitureCatalog.getCategories().length - 1; i >= 0; i--) { 1668 var category = furnitureCatalog.getCategory(i); 1669 for (var j = category.getFurniture().length - 1; j >= 0; j--) { 1670 var piece = category.getPieceOfFurniture(j); 1671 if (!piece.isModifiable()) { 1672 furnitureCatalog['delete'](piece); // Can't call delete method directly because delete is a JS reserved word 1673 } 1674 } 1675 } 1676 1677 // Add default pieces 1678 var resourceFurnitureCatalog = typeof DefaultFurnitureCatalog === "function" 1679 ? (Array.isArray(this.furnitureCatalogUrls) 1680 ? this.readFurnitureCatalogFromResource(this.furnitureCatalogUrls, this.furnitureResourcesUrlBase) 1681 : new DefaultFurnitureCatalog(this)) 1682 : new FurnitureCatalog(); 1683 for (var i = 0; i < resourceFurnitureCatalog.getCategories().length; i++) { 1684 var category = resourceFurnitureCatalog.getCategory(i); 1685 for (var j = 0; j < category.getFurniture().length; j++) { 1686 var piece = category.getPieceOfFurniture(j); 1687 furnitureCatalog.add(category, piece); 1688 } 1689 } 1690 1691 // Delete default textures of current textures catalog 1692 var texturesCatalog = this.getTexturesCatalog(); 1693 for (var i = texturesCatalog.getCategories().length - 1; i >= 0; i--) { 1694 var category = texturesCatalog.getCategory(i); 1695 for (var j = category.getTextures().length - 1; j >= 0; j--) { 1696 var texture = category.getTexture(j); 1697 if (!texture.isModifiable()) { 1698 texturesCatalog['delete'](texture); 1699 } 1700 } 1701 } 1702 1703 // Add default textures 1704 var resourceTexturesCatalog = typeof DefaultTexturesCatalog === "function" 1705 ? (Array.isArray(this.texturesCatalogUrls) 1706 ? this.readTexturesCatalogFromResource(this.texturesCatalogUrls, this.texturesResourcesUrlBase) 1707 : new DefaultTexturesCatalog(this)) 1708 : new TexturesCatalog(); 1709 1710 for (var i = 0; i < resourceTexturesCatalog.getCategories().length; i++) { 1711 var category = resourceTexturesCatalog.getCategory(i); 1712 for (var j = 0; j < category.getTextures().length; j++) { 1713 var texture = category.getTexture(j); 1714 texturesCatalog.add(category, texture); 1715 } 1716 } 1717 } 1718 1719 /** 1720 * Returns the furniture catalog contained from the given resources. 1721 * @param {Array} furnitureCatalogUrls 1722 * @param {String} furnitureResourcesUrlBase 1723 * @protected 1724 */ 1725 RecordedUserPreferences.prototype.readFurnitureCatalogFromResource = function(furnitureCatalogUrls, furnitureResourcesUrlBase) { 1726 return new DefaultFurnitureCatalog(furnitureCatalogUrls, furnitureResourcesUrlBase); 1727 } 1728 1729 /** 1730 * Returns the textures catalog contained from the the given resources. 1731 * @param {Array} texturesCatalogUrls 1732 * @param {String} texturesResourcesUrlBase 1733 * @protected 1734 */ 1735 RecordedUserPreferences.prototype.readTexturesCatalogFromResource = function(texturesCatalogUrls, texturesResourcesUrlBase) { 1736 return new DefaultTexturesCatalog(texturesCatalogUrls, texturesResourcesUrlBase); 1737 } 1738 1739 /** 1740 * Reads modifiable textures catalog from preferences. 1741 * @param {string, string} properties 1742 * @private 1743 */ 1744 RecordedUserPreferences.prototype.readModifiableTexturesCatalog = function(properties) { 1745 var texture; 1746 for (var i = 1; (texture = this.readModifiableTexture(properties, i)) != null; i++) { 1747 if (texture.getImage().getURL() != "") { 1748 var textureCategory = this.readModifiableTextureCategory(properties, i); 1749 this.getTexturesCatalog().add(textureCategory, texture); 1750 } 1751 } 1752 } 1753 1754 /** 1755 * Returns the modifiable texture read from <code>properties</code> at the given <code>index</code>. 1756 * @param {string, string} properties 1757 * @param {number} index the index of the read texture 1758 * @return the read texture or <code>null</code> if the texture at the given index doesn't exist. 1759 * @protected 1760 */ 1761 RecordedUserPreferences.prototype.readModifiableTexture = function(properties, index) { 1762 var name = this.getProperty(properties, RecordedUserPreferences.TEXTURE_NAME + index, null); 1763 if (name == null) { 1764 // Return null if key textureName# doesn't exist 1765 return null; 1766 } 1767 var image = URLContent.fromURL(this.getProperty(properties, RecordedUserPreferences.TEXTURE_IMAGE + index, "")); 1768 var width = parseFloat(this.getProperty(properties, RecordedUserPreferences.TEXTURE_WIDTH + index, "0.1")); 1769 var height = parseFloat(this.getProperty(properties, RecordedUserPreferences.TEXTURE_HEIGHT + index, "0.1")); 1770 var creator = this.getProperty(properties, RecordedUserPreferences.TEXTURE_CREATOR + index, null); 1771 return new CatalogTexture(null, name, image, width, height, creator, true); 1772 } 1773 1774 /** 1775 * Returns the category of a texture at the given <code>index</code> 1776 * read from <code>properties</code>. 1777 * @param {string, string} properties 1778 * @param {number} index the index of the read texture 1779 * @protected 1780 */ 1781 RecordedUserPreferences.prototype.readModifiableTextureCategory = function(properties, index) { 1782 var category = this.getProperty(properties, RecordedUserPreferences.TEXTURE_CATEGORY + index, ""); 1783 return new TexturesCategory(category); 1784 } 1785 1786 /** 1787 * Writes user preferences to properties, and sends to the <code>writePreferencesUrl</code> (if 1788 * given at the creation) a JSON content describing preferences. 1789 */ 1790 RecordedUserPreferences.prototype.write = function() { 1791 UserPreferences.prototype.write.call(this); 1792 1793 // Write actually preferences only if written properties were updated 1794 if (this.writtenPropertiesUpdated) { 1795 var properties = this.getProperties(); 1796 this.writeModifiableTexturesCatalog(properties); 1797 1798 // Write other preferences 1799 this.setProperty(properties, RecordedUserPreferences.LANGUAGE, this.getLanguage()); 1800 this.setProperty(properties, RecordedUserPreferences.UNIT, this.getLengthUnit().name()); 1801 var currency = this.getCurrency(); 1802 if (currency === null) { 1803 this.removeProperty(properties, RecordedUserPreferences.CURRENCY); 1804 } else { 1805 this.setProperty(properties, RecordedUserPreferences.CURRENCY, currency); 1806 } 1807 this.setProperty(properties, RecordedUserPreferences.VALUE_ADDED_TAX_ENABLED, '' + this.isValueAddedTaxEnabled()); 1808 var valueAddedTaxPercentage = this.getDefaultValueAddedTaxPercentage(); 1809 if (valueAddedTaxPercentage === null) { 1810 this.removeProperty(properties, RecordedUserPreferences.DEFAULT_VALUE_ADDED_TAX_PERCENTAGE); 1811 } else { 1812 this.setProperty(properties, RecordedUserPreferences.DEFAULT_VALUE_ADDED_TAX_PERCENTAGE, valueAddedTaxPercentage.toString()); 1813 } 1814 this.setProperty(properties, RecordedUserPreferences.FURNITURE_CATALOG_VIEWED_IN_TREE, '' + this.isFurnitureCatalogViewedInTree()); 1815 this.setProperty(properties, RecordedUserPreferences.NAVIGATION_PANEL_VISIBLE, '' + this.isNavigationPanelVisible()); 1816 this.setProperty(properties, RecordedUserPreferences.EDITING_IN_3D_VIEW_ENABLED, '' + this.isEditingIn3DViewEnabled()); 1817 this.setProperty(properties, RecordedUserPreferences.AERIAL_VIEW_CENTERED_ON_SELECTION_ENABLED, '' + this.isAerialViewCenteredOnSelectionEnabled()); 1818 this.setProperty(properties, RecordedUserPreferences.OBSERVER_CAMERA_SELECTED_AT_CHANGE, '' + this.isObserverCameraSelectedAtChange()); 1819 this.setProperty(properties, RecordedUserPreferences.MAGNETISM_ENABLED, '' + this.isMagnetismEnabled()); 1820 this.setProperty(properties, RecordedUserPreferences.RULERS_VISIBLE, '' + this.isRulersVisible()); 1821 this.setProperty(properties, RecordedUserPreferences.GRID_VISIBLE, '' + this.isGridVisible()); 1822 var defaultFontName = this.getDefaultFontName(); 1823 if (defaultFontName == null) { 1824 this.removeProperty(properties, RecordedUserPreferences.DEFAULT_FONT_NAME); 1825 } else { 1826 this.setProperty(properties, RecordedUserPreferences.DEFAULT_FONT_NAME, defaultFontName); 1827 } 1828 this.setProperty(properties, RecordedUserPreferences.FURNITURE_VIEWED_FROM_TOP, '' + this.isFurnitureViewedFromTop()); 1829 this.setProperty(properties, RecordedUserPreferences.FURNITURE_MODEL_ICON_SIZE, '' + this.getFurnitureModelIconSize()); 1830 this.setProperty(properties, RecordedUserPreferences.ROOM_FLOOR_COLORED_OR_TEXTURED, '' + this.isRoomFloorColoredOrTextured()); 1831 this.setProperty(properties, RecordedUserPreferences.WALL_PATTERN, this.getWallPattern().getName()); 1832 var newWallPattern = this.getNewWallPattern(); 1833 if (newWallPattern != null) { 1834 this.setProperty(properties, RecordedUserPreferences.NEW_WALL_PATTERN, newWallPattern.getName()); 1835 } 1836 this.setProperty(properties, RecordedUserPreferences.NEW_WALL_THICKNESS, '' + this.getNewWallThickness()); 1837 this.setProperty(properties, RecordedUserPreferences.NEW_WALL_HEIGHT, '' + this.getNewWallHeight()); 1838 this.setProperty(properties, RecordedUserPreferences.NEW_WALL_BASEBOARD_THICKNESS, '' + this.getNewWallBaseboardThickness()); 1839 this.setProperty(properties, RecordedUserPreferences.NEW_WALL_BASEBOARD_HEIGHT, '' + this.getNewWallBaseboardHeight()); 1840 this.setProperty(properties, RecordedUserPreferences.NEW_FLOOR_THICKNESS, '' + this.getNewFloorThickness()); 1841 this.setProperty(properties, RecordedUserPreferences.AUTO_SAVE_DELAY_FOR_RECOVERY, '' + this.getAutoSaveDelayForRecovery()); 1842 // Write recent homes list 1843 var recentHomes = this.getRecentHomes(); 1844 for (var i = 0; i < recentHomes.length && i < this.getRecentHomesMaxCount(); i++) { 1845 this.setProperty(properties, RecordedUserPreferences.RECENT_HOMES + (i + 1), recentHomes[i]); 1846 } 1847 // Write ignored action tips 1848 var ignoredActionTipsKeys = Object.keys(this.ignoredActionTips); 1849 for (var i = 0; i < ignoredActionTipsKeys.length; i++) { 1850 var key = ignoredActionTipsKeys[i]; 1851 if (this.ignoredActionTips[key]) { 1852 this.setProperty(properties, RecordedUserPreferences.IGNORED_ACTION_TIP + (i + 1), key); 1853 } 1854 } 1855 1856 if (Object.keys(this.uploadingBlobs).length > 0) { 1857 var preferences = this; 1858 // Wait blobs uploading end before trying to write preferences referencing them 1859 setTimeout(function() { 1860 preferences.write(); 1861 }, 1000); 1862 } else { 1863 this.writtenPropertiesUpdated = false; 1864 this.writePreferences(properties); 1865 } 1866 } 1867 } 1868 1869 /** 1870 * Sends user preferences stored in properties to backend. 1871 * @param {string, string} properties 1872 * @private 1873 */ 1874 RecordedUserPreferences.prototype.writePreferences = function(properties) { 1875 if (this.writePreferencesUrl) { 1876 var preferences = this; 1877 if (this.writingPreferences) { 1878 // Avoid writing preferences twice at the same time 1879 setTimeout(function() { 1880 preferences.writePreferences(properties); 1881 }, 100); 1882 } else { 1883 this.writingPreferences = true; 1884 var jsonPreferences = JSON.stringify(properties); 1885 var successHandler = function() { 1886 if (preferences.writingObserver !== undefined 1887 && preferences.writingObserver.writeSucceeded) { 1888 preferences.writingObserver.writeSucceeded(properties); 1889 } 1890 setTimeout(function() { 1891 delete preferences.writingPreferences; 1892 }, 500); 1893 }; 1894 var errorHandler = function(status, error) { 1895 if (preferences.writingObserver !== undefined 1896 && preferences.writingObserver.writeFailed) { 1897 preferences.writingObserver.writeFailed(properties, status, error); 1898 } 1899 setTimeout(function() { 1900 delete preferences.writingPreferences; 1901 // Retry 1902 preferences.writePreferences(properties); 1903 }, 10000); 1904 }; 1905 1906 if (this.writePreferencesUrl.indexOf(LocalStorageURLContent.LOCAL_STORAGE_PREFIX) === 0) { 1907 try { 1908 var key = this.writePreferencesUrl.substring(LocalStorageURLContent.LOCAL_STORAGE_PREFIX.length); 1909 localStorage.setItem(key, jsonPreferences); 1910 successHandler(); 1911 delete preferences.writingPreferences; 1912 } catch (ex) { 1913 errorHandler(ex, ex.message); 1914 } 1915 } else if (this.writePreferencesUrl.indexOf(IndexedDBURLContent.INDEXED_DB_PREFIX) === 0) { 1916 var preferencesContent = new BlobURLContent(new Blob([jsonPreferences], { type: 'application/json' })); 1917 preferencesContent.writeBlob(this.writePreferencesUrl, "", { 1918 blobSaved: function(content, name) { 1919 URL.revokeObjectURL(preferencesContent.getURL()); 1920 successHandler(); 1921 }, 1922 blobError : function(status, error) { 1923 URL.revokeObjectURL(preferencesContent.getURL()); 1924 errorHandler(status, error); 1925 } 1926 }); 1927 } else { 1928 var request = new XMLHttpRequest(); 1929 var querySeparator = this.writePreferencesUrl.indexOf('?') != -1 ? '&' : '?'; 1930 request.open("POST", this.writePreferencesUrl + querySeparator + "updateId=" + UUID.randomUUID(), true); 1931 request.addEventListener('load', function (ev) { 1932 if (request.readyState === XMLHttpRequest.DONE) { 1933 if (request.status === 200) { 1934 successHandler(); 1935 } else { 1936 errorHandler(request.status, request.responseText); 1937 } 1938 } 1939 }); 1940 var errorListener = function(ev) { 1941 errorHandler(0, "Can't post " + preferences.writePreferencesUrl); 1942 }; 1943 request.addEventListener("error", errorListener); 1944 request.addEventListener("timeout", errorListener); 1945 request.send(jsonPreferences); 1946 } 1947 } 1948 } 1949 } 1950 1951 /** 1952 * Sets which action tip should be ignored. 1953 * @ignore 1954 */ 1955 RecordedUserPreferences.prototype.setActionTipIgnored = function(actionKey) { 1956 this.ignoredActionTips[actionKey] = true; 1957 UserPreferences.prototype.setActionTipIgnored.call(this, actionKey); 1958 } 1959 1960 /** 1961 * Returns whether an action tip should be ignored or not. 1962 * @ignore 1963 */ 1964 RecordedUserPreferences.prototype.isActionTipIgnored = function(actionKey) { 1965 var ignoredActionTip = this.ignoredActionTips[actionKey]; 1966 return ignoredActionTip === true; 1967 } 1968 1969 /** 1970 * Resets the display flag of action tips. 1971 * @ignore 1972 */ 1973 RecordedUserPreferences.prototype.resetIgnoredActionTips = function() { 1974 var keys = Object.keys(this.ignoredActionTips); 1975 for (var i = 0; i < keys.length; i++) { 1976 this.ignoredActionTips[keys[i]] = false; 1977 } 1978 UserPreferences.prototype.resetIgnoredActionTips.call(this); 1979 } 1980 1981 /** 1982 * Throws an exception because these user preferences can't manage language libraries. 1983 * @ignore 1984 */ 1985 RecordedUserPreferences.prototype.addLanguageLibrary = function(location) { 1986 throw new UnsupportedOperationException(); 1987 } 1988 1989 /** 1990 * Throws an exception because these user preferences can't manage additional language libraries. 1991 * @ignore 1992 */ 1993 RecordedUserPreferences.prototype.languageLibraryExists = function(location) { 1994 throw new UnsupportedOperationException(); 1995 } 1996 1997 /** 1998 * Returns <code>true</code> if the furniture library at the given <code>location</code> exists. 1999 * @ignore 2000 */ 2001 RecordedUserPreferences.prototype.furnitureLibraryExists = function(location) { 2002 throw new UnsupportedOperationException(); 2003 } 2004 2005 /** 2006 * Throws an exception because these user preferences can't manage additional furniture libraries. 2007 * @ignore 2008 */ 2009 RecordedUserPreferences.prototype.addFurnitureLibrary = function(location) { 2010 throw new UnsupportedOperationException(); 2011 } 2012 2013 /** 2014 * Returns <code>true</code> if the textures library at the given <code>location</code> exists. 2015 * @ignore 2016 */ 2017 RecordedUserPreferences.prototype.texturesLibraryExists = function(location) { 2018 throw new UnsupportedOperationException(); 2019 } 2020 2021 /** 2022 * Throws an exception because these user preferences can't manage additional textures libraries. 2023 * @ignore 2024 */ 2025 RecordedUserPreferences.prototype.addTexturesLibrary = function(location) { 2026 throw new UnsupportedOperationException(); 2027 } 2028 2029 /** 2030 * Throws an exception because these user preferences don't manage additional libraries. 2031 * @ignore 2032 */ 2033 RecordedUserPreferences.prototype.getLibraries = function() { 2034 throw new UnsupportedOperationException(); 2035 } 2036 2037 /** 2038 * Save modifiable textures to catalog.json and upload new resources. 2039 * @param {string, string} properties 2040 * @private 2041 */ 2042 RecordedUserPreferences.prototype.writeModifiableTexturesCatalog = function(properties) { 2043 if (this.writeResourceUrl && this.readResourceUrl) { 2044 var index = 1; 2045 var texturesCatalog = this.getTexturesCatalog(); 2046 var preferences = this; 2047 for (var i = 0; i < texturesCatalog.getCategoriesCount(); i++) { 2048 var textureCategory = texturesCatalog.getCategory(i); 2049 for (var j = 0; j < textureCategory.getTexturesCount(); j++) { 2050 var catalogTexture = textureCategory.getTexture(j); 2051 var textureImage = catalogTexture.getImage(); 2052 if (catalogTexture.isModifiable() 2053 && textureImage instanceof URLContent) { 2054 this.setProperty(properties, RecordedUserPreferences.TEXTURE_NAME + index, catalogTexture.getName()); 2055 this.setProperty(properties, RecordedUserPreferences.TEXTURE_CATEGORY + index, textureCategory.getName()); 2056 this.setProperty(properties, RecordedUserPreferences.TEXTURE_WIDTH + index, catalogTexture.getWidth()); 2057 this.setProperty(properties, RecordedUserPreferences.TEXTURE_HEIGHT + index, catalogTexture.getHeight()); 2058 if (catalogTexture.getCreator() != null) { 2059 this.setProperty(properties, RecordedUserPreferences.TEXTURE_CREATOR + index, catalogTexture.getCreator()); 2060 } else { 2061 this.removeProperty(properties, RecordedUserPreferences.TEXTURE_CREATOR + index); 2062 } 2063 2064 if (textureImage instanceof LocalURLContent 2065 && (!(textureImage instanceof LocalStorageURLContent) 2066 || this.writeResourceUrl.indexOf(LocalStorageURLContent.LOCAL_STORAGE_PREFIX) < 0) 2067 && (!(textureImage instanceof IndexedDBURLContent) 2068 || this.writeResourceUrl.indexOf(IndexedDBURLContent.INDEXED_DB_PREFIX) < 0)) { 2069 if (!this.isSavedContentInResourceScope(textureImage)) { 2070 var textureImageFileName = this.uploadingBlobs[textureImage.getURL()]; 2071 if (textureImageFileName === undefined) { 2072 textureImage.getBlob({ 2073 textureImage: textureImage, 2074 blobReady: function(blob) { 2075 textureImageFileName = UUID.randomUUID(); 2076 preferences.uploadingBlobs[this.textureImage.getURL()] = textureImageFileName; 2077 var imageExtension = blob.type == "image/png" ? ".png" : ".jpg"; 2078 var loadListener = function(textureImage, fileName, textureIndex) { 2079 if (!preferences.isSavedContentInResourceScope(textureImage)) { 2080 var savedContent = URLContent.fromURL( 2081 CoreTools.format(preferences.readResourceUrl.replace(/(%[^s])/g, "%$1"), encodeURIComponent(fileName))); 2082 textureImage.setSavedContent(savedContent); 2083 } 2084 delete preferences.uploadingBlobs[textureImage.getURL()]; 2085 preferences.setProperty(properties, RecordedUserPreferences.TEXTURE_IMAGE + textureIndex, textureImage.getSavedContent().getURL()); 2086 }; 2087 preferences.writeResource(textureImage, textureImageFileName + imageExtension, index, loadListener); 2088 }, 2089 blobError: function(status, error) { 2090 contentsObserver.resourcesError(status, error); 2091 } 2092 }); 2093 } 2094 } else { 2095 // Always update uploading blobs map because blob may have been saved elsewhere 2096 delete preferences.uploadingBlobs[textureImage.getURL()]; 2097 this.setProperty(properties, RecordedUserPreferences.TEXTURE_IMAGE + index, textureImage.getSavedContent().getURL()); 2098 } 2099 } else if (textureImage instanceof URLContent) { 2100 this.setProperty(properties, RecordedUserPreferences.TEXTURE_IMAGE + index, textureImage.getURL()); 2101 } 2102 index++; 2103 } 2104 } 2105 } 2106 2107 // Remove obsolete keys 2108 for ( ; this.getProperty(properties, RecordedUserPreferences.TEXTURE_NAME + index, null) != null; index++) { 2109 this.removeProperty(properties, RecordedUserPreferences.TEXTURE_NAME + index); 2110 this.removeProperty(properties, RecordedUserPreferences.TEXTURE_IMAGE + index); 2111 this.removeProperty(properties, RecordedUserPreferences.TEXTURE_CATEGORY + index); 2112 this.removeProperty(properties, RecordedUserPreferences.TEXTURE_WIDTH + index); 2113 this.removeProperty(properties, RecordedUserPreferences.TEXTURE_HEIGHT + index); 2114 this.removeProperty(properties, RecordedUserPreferences.TEXTURE_CREATOR + index); 2115 } 2116 } 2117 } 2118 2119 /** 2120 * Returns <code>true</code> if the saved content of the given content exists 2121 * and depends on the scope where the resources managed by preferences are saved. 2122 * @param {URLContent} urlContent content 2123 * @private 2124 */ 2125 RecordedUserPreferences.prototype.isSavedContentInResourceScope = function(urlContent) { 2126 var savedContent = urlContent.getSavedContent(); 2127 return savedContent !== null 2128 && (!(savedContent instanceof IndexedDBURLContent) 2129 || this.writeResourceUrl.indexOf(IndexedDBURLContent.INDEXED_DB_PREFIX) < 0 2130 || savedContent.getURL().indexOf(this.writeResourceUrl.substring(0, this.writeResourceUrl.indexOf('?'))) === 0); 2131 } 2132 2133 /** 2134 * @param {BlobURLContent} urlContent blob content 2135 * @param {string} path unique file name of the written resource 2136 * @param {number} index 2137 * @param {function()} loadListener called when content is uploaded 2138 * @private 2139 */ 2140 RecordedUserPreferences.prototype.writeResource = function(urlContent, path, index, loadListener) { 2141 var preferences = this; 2142 urlContent.writeBlob(this.writeResourceUrl, path, { 2143 blobSaved: function(content, name) { 2144 if (preferences.writingObserver !== undefined 2145 && preferences.writingObserver.writeSucceeded) { 2146 preferences.writingObserver.writeSucceeded(content.getBlob()); 2147 } 2148 loadListener(content, path, index); 2149 }, 2150 blobError : function(status, error) { 2151 if (preferences.writingObserver !== undefined 2152 && preferences.writingObserver.writeFailed) { 2153 preferences.writingObserver.writeFailed(urlContent.getBlob(), status, error); 2154 } 2155 // In case of error, wait 10s before attempting a new upload 2156 setTimeout(function() { 2157 // Check it wasn't saved elsewhere 2158 if (urlContent.getSavedContent() === null) { 2159 preferences.writeResource(urlContent, path, index, loadListener); 2160 } else { 2161 loadListener(urlContent, index); 2162 } 2163 }, 10000); 2164 } 2165 }); 2166 } 2167