1 /* 2 * Copyright (c) 2014, Oracle and/or its affiliates. 3 * All rights reserved. Use is subject to license terms. 4 * 5 * This file is available and licensed under the following license: 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the distribution. 16 * - Neither the name of Oracle Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 package com.oracle.javafx.scenebuilder.app; 34 35 import com.oracle.javafx.scenebuilder.app.i18n.I18N; 36 import com.oracle.javafx.scenebuilder.kit.editor.EditorController; 37 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument; 38 import com.oracle.javafx.scenebuilder.kit.util.FileWatcher; 39 40 import java.io.File; 41 import java.io.IOException; 42 import java.net.URISyntaxException; 43 import java.nio.file.Path; 44 import java.util.ArrayList; 45 import java.util.List; 46 47 import javafx.beans.value.ChangeListener; 48 import javafx.collections.ObservableList; 49 50 /** 51 * 52 */ 53 public class DocumentWatchingController implements FileWatcher.Delegate { 54 55 private final DocumentWindowController documentWindowController; 56 private final EditorController editorController; 57 private final ResourceController resourceController; 58 private final SceneStyleSheetMenuController sceneStyleSheetMenuController; 59 private final FileWatcher fileWatcher 60 = new FileWatcher(2000 /* ms */, this, DocumentWindowController.class.getSimpleName()); 61 62 63 public DocumentWatchingController(DocumentWindowController documentWindowController) { 64 this.documentWindowController = documentWindowController; 65 this.editorController = documentWindowController.getEditorController(); 66 this.resourceController = documentWindowController.getResourceController(); 67 this.sceneStyleSheetMenuController = documentWindowController.getSceneStyleSheetMenuController(); 68 69 this.editorController.sceneStyleSheetProperty().addListener( 70 (ChangeListener<ObservableList<File>>) (ov, t, 71 t1) -> update()); 72 } 73 74 public void start() { 75 fileWatcher.start(); 76 } 77 78 79 public void stop() { 80 fileWatcher.stop(); 81 } 82 83 84 public void update() { 85 /* 86 * The file watcher associated to this document window controller watches: 87 * 1) the file holding the FXML document (if any) 88 * 2) the resource file set in the Preview menu (if any) 89 * 3) the style sheets files set in the Preview menu (if any) 90 */ 91 92 final List<Path> targets = new ArrayList<>(); 93 94 // 1) 95 final FXOMDocument fxomDocument = editorController.getFxomDocument(); 96 if ((fxomDocument != null) && (fxomDocument.getLocation() != null)) { 97 try { 98 final File fxmlFile = new File(fxomDocument.getLocation().toURI()); 99 targets.add(fxmlFile.toPath()); 100 } catch(URISyntaxException x) { 101 throw new IllegalStateException("Bug", x); //NOI18N 102 } 103 } 104 105 // 2) 106 if (resourceController.getResourceFile() != null) { 107 targets.add(resourceController.getResourceFile().toPath()); 108 } 109 110 // 3) 111 if (editorController.getSceneStyleSheets() != null) { 112 for (File sceneStyleSheet : editorController.getSceneStyleSheets()) { 113 targets.add(sceneStyleSheet.toPath()); 114 } 115 } 116 117 fileWatcher.setTargets(targets); 118 } 119 120 public void removeDocumentTarget() { 121 final FXOMDocument fxomDocument = editorController.getFxomDocument(); 122 assert fxomDocument != null; 123 assert fxomDocument.getLocation() != null; 124 125 try { 126 final File fxmlFile = new File(fxomDocument.getLocation().toURI()); 127 assert fileWatcher.getTargets().contains(fxmlFile.toPath()); 128 fileWatcher.removeTarget(fxmlFile.toPath()); 129 } catch(URISyntaxException x) { 130 throw new IllegalStateException("Bug", x); //NOI18N 131 } 132 } 133 134 /* 135 * FileWatcher.Delegate 136 */ 137 138 @Override 139 public void fileWatcherDidWatchTargetCreation(Path target) { 140 // Ignored 141 } 142 143 @Override 144 public void fileWatcherDidWatchTargetDeletion(Path target) { 145 if (isPathMatchingResourceLocation(target)) { 146 // Resource file has disappeared 147 resourceController.performRemoveResource(); 148 // Call above has invoked 149 // - FXOMDocument.refreshSceneGraph() 150 // - DocumentWatchingController.update() 151 editorController.getMessageLog().logInfoMessage("log.info.file.deleted", 152 I18N.getBundle(), target.getFileName()); 153 } else if (isPathMatchingSceneStyleSheet(target)) { 154 sceneStyleSheetMenuController.performRemoveSceneStyleSheet(target.toFile()); 155 // Call above has invoked 156 // - FXOMDocument.reapplyCSS() 157 // - DocumentWatchingController.update() 158 editorController.getMessageLog().logInfoMessage("log.info.file.deleted", 159 I18N.getBundle(), target.getFileName()); 160 } 161 /* 162 * Else it's the document file which has disappeared : 163 * We ignore this event : file will be recreated when user runs 164 * the save command. 165 */ 166 } 167 168 @Override 169 public void fileWatcherDidWatchTargetModification(Path target) { 170 if (isPathMatchingResourceLocation(target)) { 171 // Resource file has been modified -> refresh the scene graph 172 resourceController.performReloadResource(); 173 // Call above has invoked FXOMDocument.refreshSceneGraph() 174 editorController.getMessageLog().logInfoMessage("log.info.reload", 175 I18N.getBundle(), target.getFileName()); 176 177 } else if (isPathMatchingDocumentLocation(target)) { 178 if (documentWindowController.isDocumentDirty() == false) { 179 // Try to reload the fxml text on disk 180 try { 181 documentWindowController.reload(); 182 editorController.getMessageLog().logInfoMessage("log.info.reload", 183 I18N.getBundle(), target.getFileName()); 184 } catch(IOException x) { 185 // Here we silently ignore the failure : 186 // loadFromFile() has failed but left the document unchanged. 187 } 188 } 189 } else if (isPathMatchingSceneStyleSheet(target)) { 190 final FXOMDocument fxomDocument = editorController.getFxomDocument(); 191 if (fxomDocument != null) { 192 fxomDocument.reapplyCSS(target); 193 editorController.getMessageLog().logInfoMessage("log.info.reload", 194 I18N.getBundle(), target.getFileName()); 195 } 196 } 197 } 198 199 200 /* 201 * Private 202 */ 203 204 private boolean isPathMatchingDocumentLocation(Path p) { 205 final boolean result; 206 207 final FXOMDocument fxomDocument = editorController.getFxomDocument(); 208 if ((fxomDocument != null) && (fxomDocument.getLocation() != null)) { 209 try { 210 final File fxmlFile = new File(fxomDocument.getLocation().toURI()); 211 result = p.equals(fxmlFile.toPath()); 212 } catch(URISyntaxException x) { 213 throw new IllegalStateException("Bug", x); //NOI18N 214 } 215 } else { 216 result = false; 217 } 218 219 return result; 220 } 221 222 private boolean isPathMatchingResourceLocation(Path p) { 223 final boolean result; 224 225 if (resourceController.getResourceFile() != null) { 226 result = p.equals(resourceController.getResourceFile().toPath()); 227 } else { 228 result = false; 229 } 230 231 return result; 232 } 233 234 235 private boolean isPathMatchingSceneStyleSheet(Path p) { 236 final boolean result; 237 238 if (editorController.getSceneStyleSheets() != null) { 239 result = editorController.getSceneStyleSheets().contains(p.toFile()); 240 } else { 241 result = false; 242 } 243 244 return result; 245 } 246 247 }