1 /* 2 * ModelMaterialsComponent.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 CoreTools.js 22 // Requires toolkit.js 23 24 /** 25 * Component giving access to materials editor. When the user clicks 26 * on this button a dialog appears to let him choose materials. 27 * @param {UserPreferences} preferences user preferences 28 * @param {ModelMaterialsController} controller modelMaterials choice controller 29 * @constructor 30 * @author Louis Grignon 31 * @author Emmanuel Puybaret 32 */ 33 function ModelMaterialsComponent(preferences, controller) { 34 JSComponent.call(this, preferences, document.createElement("span"), true); 35 36 this.controller = controller; 37 38 this.getHTMLElement().innerHTML = '<button class="model-materials-button">' + this.getLocalizedLabelText('ModelMaterialsComponent', "modifyButton.text") + '</button>'; 39 this.button = this.findElement(".model-materials-button"); 40 this.button.disabled = true; 41 42 var component = this; 43 this.registerEventListener(this.button, "click", function(ev) { 44 component.openModelMaterialsSelectorDialog(); 45 }); 46 } 47 ModelMaterialsComponent.prototype = Object.create(JSComponent.prototype); 48 ModelMaterialsComponent.prototype.constructor = ModelMaterialsComponent; 49 50 /** 51 * Enables or disables this component. 52 * @param {boolean} enabled 53 */ 54 ModelMaterialsComponent.prototype.setEnabled = function(enabled) { 55 this.button.disabled = !enabled; 56 } 57 58 /** 59 * @private 60 */ 61 ModelMaterialsComponent.prototype.openModelMaterialsSelectorDialog = function() { 62 var dialog = new JSModelMaterialsSelectorDialog(this.getUserPreferences(), this.controller); 63 dialog.displayView(); 64 } 65 66 ModelMaterialsComponent.prototype.dispose = function() { 67 JSComponent.prototype.dispose.call(this); 68 } 69 70 /** 71 * The modelMaterials selector dialog class. 72 * @param {UserPreferences} preferences the current user preferences 73 * @param {ModelMaterialsController} controller modelMaterials choice controller 74 * @extends JSDialog 75 * @constructor 76 * @private 77 */ 78 function JSModelMaterialsSelectorDialog(preferences, controller) { 79 this.controller = controller; 80 81 var html = 82 '<div data-name="preview-panel">' + 83 ' <span>@{ModelMaterialsComponent.previewLabel.text}</span><br/>' + 84 ' <canvas id="model-preview-canvas"></canvas>' + 85 '</div>' + 86 '<div data-name="edit-panel">' + 87 ' <div>' + 88 ' <span>@{ModelMaterialsComponent.materialsLabel.text}</span>' + 89 ' <div data-name="materials-list">' + 90 ' </div>' + 91 ' </div>' + 92 ' <div class="color-texture-shininess-panel">' + 93 ' <span>@{ModelMaterialsComponent.colorAndTextureLabel.text}</span><br/>' + 94 ' <div class="label-input-grid">' + 95 ' <label class="whole-line"><input type="radio" name="color-and-texture-checkbox" value="DEFAULT">@{ModelMaterialsComponent.defaultColorAndTextureRadioButton.text}</label>' + 96 ' <label class="whole-line"><input type="radio" name="color-and-texture-checkbox" value="INVISIBLE">@{ModelMaterialsComponent.invisibleRadioButton.text}</label>' + 97 ' <label><input type="radio" name="color-and-texture-checkbox" value="COLOR">@{ModelMaterialsComponent.colorRadioButton.text}</label>' + 98 ' <span data-name="color-button"></span>' + 99 ' <label><input type="radio" name="color-and-texture-checkbox" value="TEXTURE">@{ModelMaterialsComponent.textureRadioButton.text}</label>' + 100 ' <span data-name="texture-component"></span>' + 101 ' </div>' + 102 ' <br />' + 103 ' <span>@{ModelMaterialsComponent.shininessLabel.text}</span><br/>' + 104 ' <input type="range" name="shininess-slider" min="0" max="128" list="model-materials-shininess-list" /> ' + 105 ' <datalist id="model-materials-shininess-list"></datalist> ' + 106 ' <div class="slider-labels"><div>@{ModelMaterialsComponent.mattLabel.text}</div><div>@{ModelMaterialsComponent.shinyLabel.text}</div></div>' + 107 ' </div>' + 108 '</div>'; 109 110 JSDialog.call(this, preferences, "@{HomeFurnitureController.modelMaterialsTitle}", html, 111 { 112 size: "medium", 113 applier: function(dialog) { 114 controller.setMaterials(dialog.materialsList.getMaterials()); 115 }, 116 disposer: function(dialog) { 117 dialog.selectedMaterialBlinker.stop(); 118 dialog.colorButton.dispose(); 119 dialog.textureComponent.dispose(); 120 } 121 }); 122 123 this.getHTMLElement().classList.add("model-materials-chooser-dialog"); 124 125 this.initMaterialsList(); 126 this.initPreviewPanel(); 127 this.initColorAndTexturePanel(); 128 this.initShininessPanel(); 129 130 var dialog = this; 131 if (this.materialsList.size() > 0) { 132 this.materialsList.addSelectionInterval(0, 0); 133 } else { 134 // Add a listener that will select first row as soon as the list contains some data 135 var selectFirstMaterialOnContentAvailable = function() { 136 if (dialog.materialsList.size() > 0) { 137 dialog.materialsList.addSelectionInterval(0, 0); 138 dialog.materialsList.removeListDataListener(selectFirstMaterialOnContentAvailable); 139 } 140 }; 141 this.materialsList.addListDataListener(selectFirstMaterialOnContentAvailable); 142 } 143 144 this.enableComponents(); 145 146 var selectedMaterialBlinker = { 147 delay: 500, 148 animationTask: null, 149 start: function() { 150 var materialsList = dialog.materialsList; 151 var toggleBlinkingState = function() { 152 if (materialsList.size() > 1) { 153 var materials = materialsList.getMaterials(); 154 if (selectedMaterialBlinker.delay !== 100) { 155 selectedMaterialBlinker.delay = 100; 156 if (materials == null) { 157 materials = dialog.newArray(materialsList.size()); 158 } else { 159 materials = materials.slice(0); 160 } 161 var selectedIndices = materialsList.getSelectedIndices(); 162 for (var i = 0; i < selectedIndices.length; i++) { 163 var index = selectedIndices[i]; 164 var defaultMaterial = materialsList.getDefaultMaterialAt(index); 165 var selectedMaterial = materials [index] != null 166 ? materials [index] 167 : defaultMaterial; 168 var blinkColor = 0xFF2244FF; 169 if (selectedMaterial.getTexture() == null) { 170 var selectedColor = selectedMaterial.getColor(); 171 if (selectedColor == null) { 172 selectedColor = defaultMaterial.getColor(); 173 if (selectedColor == null) { 174 selectedColor = 0xFF000000; 175 } 176 } 177 selectedColor &= 0x00FFFFFF; 178 var red = (selectedColor >> 16) & 0xFF; 179 var green = (selectedColor >> 8) & 0xFF; 180 var blue = selectedColor & 0xFF; 181 if (Math.max(red, Math.max(green, blue)) > 0x77) { 182 // Display a darker color for a bright color 183 blinkColor = 0xFF000000 | (red / 2 << 16) | (green / 2 << 8) | (blue / 2); 184 } else if ((red + green + blue) / 3 > 0x0F) { 185 // Display a brighter color for a dark color 186 blinkColor = 0xFF000000 187 | (Math.min(red * 2, 0xFF) << 16) 188 | (Math.min(green * 2, 0xFF) << 8) 189 | Math.min(blue * 2, 0xFF); 190 } 191 } 192 materials [index] = new HomeMaterial( 193 selectedMaterial.getName(), blinkColor, null, selectedMaterial.getShininess()); 194 } 195 } else { 196 selectedMaterialBlinker.delay = 1000; 197 } 198 dialog.previewComponent.setModelMaterials(materials); 199 } 200 201 selectedMaterialBlinker.animationTask = setTimeout(toggleBlinkingState, 202 selectedMaterialBlinker.delay); 203 }; 204 205 selectedMaterialBlinker.animationTask = setTimeout(toggleBlinkingState, selectedMaterialBlinker.delay); 206 }, 207 stop: function() { 208 clearTimeout(selectedMaterialBlinker.animationTask); 209 }, 210 restart: function() { 211 selectedMaterialBlinker.stop(); 212 selectedMaterialBlinker.delay = 101; 213 selectedMaterialBlinker.start(); 214 } 215 }; 216 217 selectedMaterialBlinker.start(); 218 this.materialsList.addListSelectionListener(function(ev) { 219 selectedMaterialBlinker.restart(); 220 }); 221 this.selectedMaterialBlinker = selectedMaterialBlinker; 222 } 223 JSModelMaterialsSelectorDialog.prototype = Object.create(JSDialog.prototype); 224 JSModelMaterialsSelectorDialog.prototype.constructor = JSModelMaterialsSelectorDialog; 225 226 /** 227 * Returns an array of the given size initialized with <code>null</code>. 228 * @param {number} size 229 * @return {Array} 230 * @private 231 */ 232 JSModelMaterialsSelectorDialog.prototype.newArray = function(size) { 233 var array = new Array(size); 234 for (var i = 0; i < array.length; i++) { 235 array [i] = null; 236 } 237 return array; 238 } 239 240 /** 241 * @private 242 */ 243 JSModelMaterialsSelectorDialog.prototype.initMaterialsList = function() { 244 var dialog = this; 245 var controller = this.controller; 246 var materialsListElement = this.getElement("materials-list"); 247 248 var defaultMaterials = []; 249 var materials = controller.getMaterials(); 250 if (materials != null) { 251 materials = materials.slice(0); 252 } 253 ModelManager.getInstance().loadModel(controller.getModel(), true, 254 { 255 modelUpdated : function(modelRoot) { 256 defaultMaterials = ModelManager.getInstance().getMaterials( 257 modelRoot, (controller.getModelFlags() & PieceOfFurniture.HIDE_EDGE_COLOR_MATERIAL) != 0, controller.getModelCreator()); 258 if (materials != null) { 259 // Keep only materials that are defined in default materials set 260 // (the list can be different if the model loader interprets differently a 3D model file 261 // or if materials come from a paste style action) 262 var updatedMaterials = dialog.newArray(defaultMaterials.length); 263 var foundInDefaultMaterials = false; 264 for (var i = 0; i < defaultMaterials.length; i++) { 265 var materialName = defaultMaterials [i].getName(); 266 for (var j = 0; j < materials.length; j++) { 267 if (materials [j] != null && materials [j].getName() == materialName) { 268 updatedMaterials [i] = materials [j]; 269 foundInDefaultMaterials = true; 270 break; 271 } 272 } 273 } 274 if (foundInDefaultMaterials) { 275 materials = updatedMaterials; 276 } else { 277 materials = null; 278 } 279 } 280 } 281 }); 282 283 var materialsList = 284 this.materialsList = { 285 element: materialsListElement, 286 materials: materials, 287 defaultMaterials: defaultMaterials, 288 dataListeners: [], 289 selectionListeners: [], 290 /** 291 * @return {number} 292 */ 293 size: function() { 294 if (materialsList.defaultMaterials != null) { 295 return materialsList.defaultMaterials.length; 296 } else { 297 return 0; 298 } 299 }, 300 /** 301 * @return {HomeMaterial[]} 302 */ 303 getMaterials: function() { 304 return materialsList.materials; 305 }, 306 /** 307 * @param {number} index 308 * @return {HomeMaterial} 309 */ 310 getDefaultMaterialAt: function(index) { 311 return materialsList.defaultMaterials[index]; 312 }, 313 /** 314 * @param {number} index 315 * @return {HomeMaterial} 316 */ 317 getElementAt: function(index) { 318 var material; 319 if (materialsList.materials != null 320 && materialsList.materials [index] != null 321 && materialsList.materials [index].getName() != null 322 && materialsList.materials [index].getName() == materialsList.defaultMaterials [index].getName()) { 323 material = materialsList.materials [index]; 324 } else { 325 material = new HomeMaterial(materialsList.defaultMaterials [index].getName(), null, null, null); 326 } 327 return material; 328 }, 329 /** 330 * @param {HomeMaterial} material 331 * @param {number} index 332 */ 333 setMaterialAt: function(material, index) { 334 if (material.getColor() == null 335 && material.getTexture() == null 336 && material.getShininess() == null) { 337 if (materialsList.materials != null) { 338 materialsList.materials [index] = null; 339 var containsOnlyNull = true; 340 for (var i = 0; i < materialsList.materials.length; i++) { 341 if (materialsList.materials[i] != null) { 342 containsOnlyNull = false; 343 break; 344 } 345 } 346 if (containsOnlyNull) { 347 materialsList.materials = null; 348 } 349 } 350 } else { 351 if (materialsList.materials == null 352 || materialsList.materials.length != materialsList.defaultMaterials.length) { 353 materialsList.materials = dialog.newArray(materialsList.defaultMaterials.length); 354 } 355 materialsList.materials [index] = material; 356 } 357 358 materialsList.fireContentsChanged(); 359 }, 360 fireContentsChanged: function() { 361 for (var i = 0; i < materialsList.dataListeners.length; i++) { 362 materialsList.dataListeners[i](); 363 } 364 materialsList.repaint(); 365 }, 366 /** 367 * @param {HomeMaterial} material 368 * @param {HomeMaterial} defaultMaterial 369 * @param {boolean} [selected] default false 370 */ 371 createListItem: function(material, defaultMaterial, selected) { 372 var materialTexture = material.getTexture(); 373 var materialColor = material.getColor(); 374 if (materialTexture == null && materialColor == null) { 375 materialTexture = defaultMaterial.getTexture(); 376 if (materialTexture == null) { 377 materialColor = defaultMaterial.getColor(); 378 } 379 } 380 381 var itemBackground = document.createElement("div"); 382 itemBackground.classList.add("icon"); 383 if (materialTexture != null && materialTexture.getImage() != null) { 384 // Display material texture image with an icon 385 TextureManager.getInstance().loadTexture(materialTexture.getImage(), 386 { 387 textureUpdated: function(image) { 388 itemBackground.style.backgroundImage = "url('" + image.src + "')"; 389 }, 390 textureError: function(error) { 391 itemBackground.style.backgroundImage = "none"; 392 } 393 }); 394 } else if (materialColor != null 395 && (materialColor & 0xFF000000) != 0) { 396 itemBackground.style.backgroundColor = ColorTools.integerToHexadecimalString(materialColor); 397 } else { 398 // Empty icon 399 } 400 401 var item = document.createElement("div"); 402 item.textContent = material.getName(); 403 if (selected) { 404 item.classList.add("selected"); 405 } 406 407 item.appendChild(itemBackground); 408 return item; 409 }, 410 repaint: function() { 411 var defaultMaterials = materialsList.defaultMaterials; 412 var materials = materialsList.materials; 413 var selectedIndices = materialsList.getSelectedIndices(); 414 415 materialsList.element.innerHTML = ""; 416 if (defaultMaterials != null) { 417 // Generate content 418 materialsList.element.innerHTML = ""; 419 for (var i = 0; i < defaultMaterials.length; i++) { 420 var material = materialsList.getElementAt(i); 421 var defaultMaterial = materialsList.getDefaultMaterialAt(i); 422 var selected = selectedIndices.indexOf(i) > -1; 423 materialsList.element.appendChild(materialsList.createListItem(material, defaultMaterial, selected)); 424 } 425 426 // 1) Single click 427 dialog.registerEventListener(materialsList.element.children, "click", function(ev) { 428 var item = this; 429 var multiSelection = OperatingSystem.isMacOSX() ? ev.metaKey : ev.ctrlKey; 430 if (multiSelection) { 431 if (item.classList.contains("selected")) { 432 item.classList.remove("selected"); 433 materialsList.fireSelectionChanged(); 434 } else { 435 materialsList.selectItem(item); 436 } 437 } else { 438 for (var i = 0; i < materialsList.element.children.length; i++) { 439 var otherItem = materialsList.element.children[i]; 440 if (otherItem != item) { 441 otherItem.classList.remove("selected"); 442 } 443 } 444 materialsList.selectItem(item); 445 } 446 }); 447 448 // 2) Double click 449 dialog.registerEventListener(materialsList.element.children, "dblclick", function(ev) { 450 materialsList.selectItem(this); 451 if (dialog.colorButton.getColor() != null) { 452 dialog.colorButton.findElement("button").click(); 453 } else if (controller.getTextureController().getTexture() != null 454 && dialog.textureComponent != null) { 455 dialog.textureComponent.findElement("button").click(); 456 } 457 }); 458 } 459 }, 460 /** 461 * Triggered whenever this list's content changes (materials count or data). This is not about selection, 462 * please also see addListSelectionListener 463 * @param {function()} listener 464 */ 465 addListDataListener: function(listener) { 466 materialsList.dataListeners.push(listener); 467 }, 468 /** 469 * @param {function()} listener 470 */ 471 removeListDataListener: function(listener) { 472 materialsList.dataListeners.splice(materialsList.dataListeners.index(listener), 1); 473 }, 474 /** 475 * add listener on selection event (when one or more material are selected, or deselected) 476 * @param {function()} listener 477 */ 478 addListSelectionListener: function(listener) { 479 materialsList.selectionListeners.push(listener); 480 }, 481 /** 482 * @param {number} index1 483 * @param {number} index2 484 */ 485 addSelectionInterval: function(index1, index2) { 486 for (var i = index1; i <= index2; i++) { 487 materialsList.element.children[i].classList.add("selected"); 488 } 489 materialsList.fireSelectionChanged(); 490 }, 491 /** 492 * @param {number} index1 493 * @param {number} index2 494 */ 495 removeSelectionInterval: function(index1, index2) { 496 for (var i = index1; i <= index2; i++) { 497 materialsList.element.children[i].classList.remove("selected"); 498 } 499 materialsList.fireSelectionChanged(); 500 }, 501 clearSelection: function() { 502 materialsList.removeSelectionInterval(0, materialsList.size() - 1); 503 }, 504 selectItem: function(item) { 505 item.classList.add("selected"); 506 materialsList.fireSelectionChanged(); 507 }, 508 fireSelectionChanged: function() { 509 for (var i = 0; i < materialsList.selectionListeners.length; i++) { 510 materialsList.selectionListeners[i](); 511 } 512 }, 513 /** 514 * @return {boolean} 515 */ 516 isSelectionEmpty: function() { 517 return materialsList.element.querySelector(".selected") != null; 518 }, 519 /** 520 * @return {number[]} 521 */ 522 getSelectedIndices: function() { 523 var selectedIndices = []; 524 var items = materialsList.element.children; 525 for (var i = 0; i < items.length; i++) { 526 if (items[i].classList.contains("selected")) { 527 selectedIndices.push(i); 528 } 529 } 530 return selectedIndices; 531 }, 532 }; 533 534 this.materialsList.repaint(); 535 536 // List selection change handler 537 this.materialsList.addListSelectionListener(function() { 538 var selectedIndices = dialog.materialsList.getSelectedIndices(); 539 if (selectedIndices.length > 0) { 540 var material = dialog.materialsList.getElementAt(selectedIndices[0]); 541 var texture = material.getTexture(); 542 var color = material.getColor(); 543 var shininess = material.getShininess(); 544 var defaultMaterial = dialog.materialsList.getDefaultMaterialAt(selectedIndices[0]); 545 if (color == null && texture == null) { 546 dialog.defaultColorAndTextureRadioButton.checked = true; 547 // Display default color or texture in buttons 548 texture = defaultMaterial.getTexture(); 549 if (texture != null) { 550 dialog.colorButton.setColor(null); 551 controller.getTextureController().setTexture(texture); 552 } else { 553 color = defaultMaterial.getColor(); 554 if (color != null) { 555 controller.getTextureController().setTexture(null); 556 dialog.colorButton.setColor(color); 557 } 558 } 559 } else if (texture != null) { 560 dialog.textureRadioButton.checked = true; 561 dialog.colorButton.setColor(null); 562 controller.getTextureController().setTexture(texture); 563 } else if ((color & 0xFF000000) == 0) { 564 dialog.invisibleRadioButton.checked = true; 565 // Display default color or texture in buttons 566 texture = defaultMaterial.getTexture(); 567 if (texture != null) { 568 dialog.colorButton.setColor(null); 569 controller.getTextureController().setTexture(texture); 570 } else { 571 color = defaultMaterial.getColor(); 572 if (color != null) { 573 controller.getTextureController().setTexture(null); 574 dialog.colorButton.setColor(color); 575 } 576 } 577 } else { 578 dialog.colorRadioButton.checked = true; 579 controller.getTextureController().setTexture(null); 580 dialog.colorButton.setColor(color); 581 } 582 583 if (shininess != null) { 584 dialog.shininessSlider.value = shininess * 128; 585 } else { 586 dialog.shininessSlider.value = defaultMaterial.getShininess() != null 587 ? defaultMaterial.getShininess() * 128 588 : 0; 589 } 590 } 591 dialog.enableComponents(); 592 }); 593 } 594 595 /** 596 * @private 597 */ 598 JSModelMaterialsSelectorDialog.prototype.initPreviewPanel = function() { 599 var controller = this.controller; 600 var dialog = this; 601 602 var previewPanel = this.getElement("preview-panel"); 603 var previewCanvas = this.findElement("#model-preview-canvas"); 604 605 previewCanvas.width = 250; 606 previewCanvas.height = 250; 607 608 var previewComponent = 609 dialog.previewComponent = new ModelPreviewComponent(previewCanvas, true); 610 ModelManager.getInstance().loadModel(controller.getModel(), false, 611 { 612 modelUpdated : function(modelRoot) { 613 var materialsList = dialog.materialsList; 614 previewComponent.setModel( 615 controller.getModel(), controller.getModelFlags(), controller.getModelRotation(), 616 controller.getModelWidth(), controller.getModelDepth(), controller.getModelHeight()); 617 previewComponent.setModelMaterials(materialsList.getMaterials()); 618 previewComponent.setModelTransformations(controller.getModelTransformations()); 619 materialsList.addListDataListener(function() { 620 previewComponent.setModelMaterials(materialsList.getMaterials()); 621 }); 622 }, 623 modelError: function() { 624 previewPanel.style.visibility = "hidden"; 625 } 626 }); 627 628 var mousePressed = function(ev) { 629 var pickedMaterial = dialog.previewComponent.getPickedMaterial(); 630 if (pickedMaterial != null) { 631 for (var i = 0, n = dialog.materialsList.size(); i < n; i++) { 632 var material = dialog.materialsList.getDefaultMaterialAt(i); 633 if (material.getName() !== null 634 && material.getName() == pickedMaterial.getName()) { 635 var multiSelection = OperatingSystem.isMacOSX() ? ev.metaKey : ev.ctrlKey; 636 if (multiSelection) { 637 var selectedIndices = dialog.materialsList.getSelectedIndices(); 638 if (selectedIndices.indexOf(i) >= 0) { 639 dialog.materialsList.removeSelectionInterval(i, i); 640 } else { 641 dialog.materialsList.addSelectionInterval(i, i); 642 } 643 } else { 644 dialog.materialsList.clearSelection(); 645 dialog.materialsList.addSelectionInterval(i, i); 646 } 647 } 648 } 649 } 650 }; 651 if (OperatingSystem.isInternetExplorerOrLegacyEdge() 652 && window.PointerEvent) { 653 // Multi touch support for IE and Edge 654 dialog.previewComponent.getHTMLElement().addEventListener("pointerdown", mousePressed); 655 dialog.previewComponent.getHTMLElement().addEventListener("mousedown", mousePressed); 656 } else { 657 dialog.previewComponent.getHTMLElement().addEventListener("touchstart", mousePressed); 658 dialog.previewComponent.getHTMLElement().addEventListener("mousedown", mousePressed); 659 } 660 } 661 662 663 /** 664 * @private 665 */ 666 JSModelMaterialsSelectorDialog.prototype.initColorAndTexturePanel = function() { 667 var dialog = this; 668 var controller = this.controller; 669 670 this.defaultColorAndTextureRadioButton = this.findElement("[name='color-and-texture-checkbox'][value='DEFAULT']"); 671 this.invisibleRadioButton = this.findElement("[name='color-and-texture-checkbox'][value='INVISIBLE']"); 672 this.colorRadioButton = this.findElement("[name='color-and-texture-checkbox'][value='COLOR']"); 673 this.textureRadioButton = this.findElement("[name='color-and-texture-checkbox'][value='TEXTURE']"); 674 675 /** 676 * @param {function(HomeMaterial, index): HomeMaterial} materialCreator 677 */ 678 var modifySelectedMaterials = function(materialCreator) { 679 var selectedIndices = dialog.materialsList.getSelectedIndices(); 680 for (var i = 0; i < selectedIndices.length; i++) { 681 var index = selectedIndices[i]; 682 var material = dialog.materialsList.getElementAt(index); 683 dialog.materialsList.setMaterialAt( 684 materialCreator(material, index), 685 index); 686 } 687 }; 688 689 // Listen to click events on radio buttons rather than change events 690 // to avoid firing events when checked state is set internaly 691 this.registerEventListener(this.defaultColorAndTextureRadioButton, "click", function(ev) { 692 if (!this.disabled && this.checked) { 693 modifySelectedMaterials(function(material) { 694 return new HomeMaterial(material.getName(), null, null, material.getShininess()); 695 }); 696 } 697 }); 698 699 this.registerEventListener(this.invisibleRadioButton, "click", function(ev) { 700 if (!this.disabled && this.checked) { 701 modifySelectedMaterials(function(material) { 702 return new HomeMaterial(material.getName(), 0, null, material.getShininess()); 703 }); 704 } 705 }); 706 707 var colorChoiceChangeListener = function() { 708 if (!dialog.colorRadioButton.disabled && dialog.colorRadioButton.checked) { 709 modifySelectedMaterials(function(material, index) { 710 var defaultMaterial = dialog.materialsList.getDefaultMaterialAt(index); 711 var color = defaultMaterial.getColor() != dialog.colorButton.getColor() || defaultMaterial.getTexture() != null 712 ? dialog.colorButton.getColor() 713 : null; 714 return new HomeMaterial(material.getName(), color, null, material.getShininess()); 715 }); 716 } 717 }; 718 this.colorButton = new ColorButton(this.getUserPreferences(), 719 { 720 colorChanged: function(selectedColor) { 721 if (selectedColor != null) { 722 dialog.colorRadioButton.checked = true; 723 colorChoiceChangeListener(); 724 } 725 } 726 }); 727 this.colorButton.setColorDialogTitle(this.getUserPreferences().getLocalizedString( 728 "ModelMaterialsComponent", "colorDialog.title")); 729 this.registerEventListener(this.colorRadioButton, "click", function(ev) { 730 if (dialog.colorRadioButton.checked) { 731 colorChoiceChangeListener(); 732 } 733 }); 734 this.attachChildComponent("color-button", this.colorButton); 735 736 var textureChoiceChangeListener = function() { 737 if (!dialog.textureRadioButton.disabled && dialog.textureRadioButton.checked) { 738 modifySelectedMaterials(function(material, index) { 739 var defaultTexture = dialog.materialsList.getDefaultMaterialAt(index).getTexture(); 740 var texture = defaultTexture != controller.getTextureController().getTexture() 741 ? controller.getTextureController().getTexture() 742 : null; 743 return new HomeMaterial(material.getName(), null, texture, material.getShininess()); 744 }); 745 } 746 }; 747 this.textureComponent = controller.getTextureController().getView(); 748 this.textureComponent.textureChanged = function(texture) { 749 if (texture != null) { 750 dialog.textureRadioButton.checked = true; 751 textureChoiceChangeListener(); 752 } 753 }; 754 this.registerEventListener(this.textureRadioButton, "click", function(ev) { 755 if (dialog.textureRadioButton.checked) { 756 textureChoiceChangeListener(); 757 } 758 }); 759 this.registerPropertyChangeListener(controller.getTextureController(), "TEXTURE", function(ev) { 760 if (ev.getNewValue() != null) { 761 if (!dialog.textureRadioButton.checked) { 762 dialog.textureRadioButton.checked = true; 763 } 764 textureChoiceChangeListener(); 765 } 766 }); 767 this.attachChildComponent("texture-component", this.textureComponent); 768 } 769 770 /** 771 * @private 772 */ 773 JSModelMaterialsSelectorDialog.prototype.initShininessPanel = function() { 774 var dialog = this; 775 this.shininessSlider = this.getElement("shininess-slider"); 776 777 var shininessList = this.findElement("#model-materials-shininess-list"); 778 for (var i = 0; i <= 128; i+= 16) { 779 var option = document.createElement("option"); 780 option.value = i; 781 shininessList.appendChild(option); 782 } 783 784 this.registerEventListener(this.shininessSlider, "input", function(ev) { 785 var shininess = this.value; 786 var selectedIndices = dialog.materialsList.getSelectedIndices(); 787 for (var i = 0; i < selectedIndices.length; i++) { 788 var index = selectedIndices[i]; 789 var material = dialog.materialsList.getElementAt(index); 790 dialog.materialsList.setMaterialAt( 791 new HomeMaterial(material.getName(), material.getColor(), material.getTexture(), shininess / 128), 792 index); 793 } 794 }); 795 }; 796 797 /** 798 * @private 799 */ 800 JSModelMaterialsSelectorDialog.prototype.enableComponents = function() { 801 var selectionEmpty = !this.materialsList.isSelectionEmpty(); 802 this.defaultColorAndTextureRadioButton.disabled = selectionEmpty; 803 this.invisibleRadioButton.disabled = selectionEmpty; 804 this.textureRadioButton.disabled = selectionEmpty; 805 this.textureComponent.setEnabled(!selectionEmpty); 806 this.colorRadioButton.disabled = selectionEmpty; 807 this.colorButton.setEnabled(!selectionEmpty); 808 this.shininessSlider.disabled = selectionEmpty; 809 } 810 811