diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/DocumentWindowController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/DocumentWindowController.java index 696dfbbc3..cb2060420 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/DocumentWindowController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/DocumentWindowController.java @@ -69,6 +69,7 @@ import com.oracle.javafx.scenebuilder.kit.editor.selection.AbstractSelectionGroup; import com.oracle.javafx.scenebuilder.kit.editor.selection.ObjectSelectionGroup; import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument; +import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument.FXOMDocumentSwitch; import com.oracle.javafx.scenebuilder.kit.fxom.FXOMNodes; import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject; import com.oracle.javafx.scenebuilder.kit.library.Library; @@ -98,6 +99,7 @@ import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import javafx.beans.InvalidationListener; import javafx.beans.binding.Bindings; @@ -125,6 +127,7 @@ import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.stage.FileChooser; +import javafx.stage.Stage; import javafx.stage.FileChooser.ExtensionFilter; import javafx.stage.Window; import javafx.stage.WindowEvent; @@ -409,7 +412,11 @@ public SplitController getDocumentSplitController() { public void loadFromFile(File fxmlFile) throws IOException { final URL fxmlURL = fxmlFile.toURI().toURL(); final String fxmlText = FXOMDocument.readContentFromURL(fxmlURL); - editorController.setFxmlTextAndLocation(fxmlText, fxmlURL, false); + + FXOMDocumentSwitch[] options = FXOMDocumentSwitch.PRESERVE_UNRESOLVED_IMPORTS + .fromToggle(getPreferencesRecordGlobal().isPreserveUnresolvedImports()); + + editorController.setFxmlTextAndLocation(fxmlText, fxmlURL, false, options); updateLoadFileTime(); updateStageTitle(); // No-op if fxml has not been loaded yet updateFromDocumentPreferences(true); @@ -2362,6 +2369,37 @@ private PreferencesRecordGlobal getPreferencesRecordGlobal() { } return recordGlobal; } + + public void showMissingTypesNotificationIfNeeded(File fxmlFile) { + FXOMDocument doc = getEditorController().getFxomDocument(); + if (doc.hasUnresolvableTypes()) { + notifyUserAboutUnresolvableImports(fxmlFile, doc.getUnresolvableTypes(), getStage()); + } + } + + private void notifyUserAboutUnresolvableImports(File fxmlFile, List missingTypes, Stage owner) { + LOGGER.log(Level.WARNING, "Detected unresolved types in FXML: {0}: {1}", + new Object[] {fxmlFile, missingTypes.stream().collect(Collectors.joining(","))}); + + final ErrorDialog errorDialog = new ErrorDialog(owner); + String first10 = missingTypes.stream() + .limit(10) + .collect(Collectors.joining(";"+System.lineSeparator())); + + errorDialog.setMessage(I18N.getString("alert.open.failure.unresolved.imports", + Integer.toString(missingTypes.size()))); + errorDialog.setDetails(I18N.getString("alert.open.failure.unresolved.imports.details", first10)); + errorDialog.setDetailsTitle(I18N.getString("alert.open.failure.unresolved.imports", + Integer.toString(missingTypes.size()))); + + String allMissing = missingTypes.stream() + .collect(Collectors.joining(";"+System.lineSeparator())); + + errorDialog.setDebugInfo(I18N.getString("alert.open.failure.unresolved.imports.advice", + allMissing, missingTypes.size())); + errorDialog.setTitle(I18N.getString("alert.title.open") + ": " + fxmlFile.getName()); + errorDialog.showAndWait(); + } } ///** diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java index 99033c5f7..7c4115e8c 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java @@ -531,7 +531,8 @@ public void handleOpenFilesAction(List files, Runnable onSuccess) { EditorController.updateNextInitialDirectory(fileObjs.get(0)); Consumer> onError = errors -> showFileOpenErrors(errors, - () -> WelcomeDialogWindowController.getInstance().getStage()); + () -> WelcomeDialogWindowController.getInstance() + .getStage()); // Fix for #45 if (userLibrary.isFirstExplorationCompleted()) { @@ -715,10 +716,14 @@ public DocumentWindowController getFrontDocumentWindow() { } private void performOpenFiles(List fxmlFiles) { - performOpenFiles(fxmlFiles, r -> showFileOpenErrors(r, getOwnerWindow()), () -> { /* no action here */ } ); + performOpenFiles(fxmlFiles, + r -> showFileOpenErrors(r, getOwnerWindow()), + () -> { /* no action here */ } ); } - private void performOpenFiles(List fxmlFiles, Consumer> onError, Runnable onSuccess) { + private void performOpenFiles(List fxmlFiles, + Consumer> onError, + Runnable onSuccess) { assert fxmlFiles != null; assert fxmlFiles.isEmpty() == false; @@ -740,6 +745,7 @@ private void performOpenFiles(List fxmlFiles, Consumer fxmlFiles, Consumer recentItems = new ArrayList<>(); private LocalDate showUpdateDialogDate = null; @@ -400,6 +402,14 @@ public void setWildcardImports(boolean wildcardImports) { this.wildcardImports = wildcardImports; } + public boolean isPreserveUnresolvedImports() { + return this.preserveUnresolvedImports; + } + + public void setPreserveUnresolvedImports(boolean preserveUnresolvedImports) { + this.preserveUnresolvedImports = preserveUnresolvedImports; + } + public boolean isAlternateTextInputControlPaste() { return alternatePasteBehavior; } @@ -501,6 +511,9 @@ public void readFromJavaPreferences() { // Wildcard imports setWildcardImports(applicationRootPreferences.getBoolean(WILDCARD_IMPORT, DEFAULT_WILDCARD_IMPORTS)); + // Unresolvable Imports + setPreserveUnresolvedImports(applicationRootPreferences.getBoolean(PRESERVE_UNRESOLVED_IMPORTS, DEFAULT_PRESERVE_UNRESOLVED_IMPORTS)); + // Alternate paste behavior for Text Input Controls setAlternateTextInputControlPaste(applicationRootPreferences.getBoolean(ALTERNATE_TEXT_INPUT_PASTE, DEFAULT_ALTERNATE_TEXT_INPUT_PASTE)); } @@ -578,6 +591,9 @@ public void writeToJavaPreferences(String key) { case ALTERNATE_TEXT_INPUT_PASTE: applicationRootPreferences.putBoolean(ALTERNATE_TEXT_INPUT_PASTE,isAlternateTextInputControlPaste()); break; + case PRESERVE_UNRESOLVED_IMPORTS: + applicationRootPreferences.putBoolean(PRESERVE_UNRESOLVED_IMPORTS, isPreserveUnresolvedImports()); + break; default: super.writeToJavaPreferences(key); break; diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesWindowController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesWindowController.java index 055f17f7c..fc7fdef17 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesWindowController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesWindowController.java @@ -55,6 +55,7 @@ import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesController.TOOL_THEME; import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesController.WILDCARD_IMPORT; import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesController.ALTERNATE_TEXT_INPUT_PASTE; +import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesController.PRESERVE_UNRESOLVED_IMPORTS; import static com.oracle.javafx.scenebuilder.kit.preferences.PreferencesRecordGlobalBase.DEFAULT_ALIGNMENT_GUIDES_COLOR; import static com.oracle.javafx.scenebuilder.kit.preferences.PreferencesRecordGlobalBase.DEFAULT_BACKGROUND_IMAGE; @@ -143,6 +144,8 @@ public class PreferencesWindowController extends AbstractFxmlWindowController { @FXML private CheckBox wildcardImports; @FXML + private CheckBox preserveUnresolvedImports; + @FXML private CheckBox alternatePasteBehavior; @FXML private Label alternatePasteBehaviorLabel; @@ -265,6 +268,10 @@ protected void controllerDidLoadFxml() { // Wildcard Imports wildcardImports.setSelected(recordGlobal.isWildcardImports()); wildcardImports.selectedProperty().addListener(new WildcardImportListener()); + + // Unresolvable Imports + preserveUnresolvedImports.setSelected(recordGlobal.isPreserveUnresolvedImports()); + preserveUnresolvedImports.selectedProperty().addListener(new PreserveUnresolvedImportListener()); if (EditorPlatform.IS_MAC) { // Alternate paste behavior for Text Input Controls @@ -594,6 +601,17 @@ public void changed(ObservableValue observable, Boolean oldVa } } + private static class PreserveUnresolvedImportListener implements ChangeListener { + + @Override + public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { + final PreferencesController preferencesController = PreferencesController.getSingleton(); + final PreferencesRecordGlobal recordGlobal = preferencesController.getRecordGlobal(); + recordGlobal.setPreserveUnresolvedImports(newValue); + recordGlobal.writeToJavaPreferences(PRESERVE_UNRESOLVED_IMPORTS); + } + } + private static class AlternatePasteListener implements ChangeListener { @Override diff --git a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp.properties b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp.properties index b3f4b97ac..e9436a5b5 100644 --- a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp.properties +++ b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp.properties @@ -250,6 +250,7 @@ prefs.cssanalyzer.columns.defaults.last = "Defaults" Column Last prefs.recent.items = Recent items : prefs.animate.accordion = Animate Accordion : prefs.wildcard.import = Use Wildcard Imports : +prefs.preserve.unresolved.import = Load FXML with unresolved imports : prefs.tic.paste.alternate.behavior = Alternative paste behavior for text input : prefs.tic.paste.alternate.behavior.tooltip = MacOS only: Enables workaround for pasting into text input controls.\nDisable when pasting text is not working as expected. prefs.reset.default = Reset to Builtin Default Values @@ -431,12 +432,15 @@ alert.save.noextension.savewith = Save with '.fxml' alert.save.noextension.savewithout = Save without '.fxml' alert.open.failure.charset.not.found = The given charset could not be set. alert.open.failure.charset.not.found.details = It may be due to the encoding in your document. +alert.open.failure.unresolved.imports = Failed to resolve {0} FXML import(s): +alert.open.failure.unresolved.imports.details = Use Show Details to obtain a full list of unresolveable imports:\n\n{0}\n\nScene Builder will open the FXML for editing but the custom content will not be rendered correctly.\n\nUnresolved UI elements will be removed from FXML when the file is saved.\n\nPlease add the missing components to your user library within the JAR/FXML Manager. If individual class files are used, make sure, those are organized in a directory structure matching the package structure.\n\n +alert.open.failure.unresolved.imports.advice = Please add the missing components to your user library within the JAR/FXML Manager.\nIf individual class files are used, make sure, those are organized in a directory structure matching the package structure.\n\n{0}\n\nA total of {1} unresolvable import(s) was found.\n alert.welcome.file.not.found.question = One or more project files were not found. alert.welcome.file.not.found.message = If those are located on a removable or network drive, please make sure the drive is connected.\n\n alert.welcome.file.not.found.title = Project file(s) not found alert.welcome.file.not.found.okay = Remove From List alert.welcome.file.not.found.no = OK - + # ----------------------------------------------------------------------------- # Log Messages # ----------------------------------------------------------------------------- diff --git a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/preferences/Preferences.fxml b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/preferences/Preferences.fxml index 23af4ca44..39ebcc2db 100644 --- a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/preferences/Preferences.fxml +++ b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/preferences/Preferences.fxml @@ -1,7 +1,7 @@