From 91b30af47436f469e8902016e4255a12918e421f Mon Sep 17 00:00:00 2001 From: Svata Dedic Date: Wed, 23 Oct 2024 19:54:12 +0200 Subject: [PATCH] Support for UserQuestionException during reloads --- platform/openide.text/apichanges.xml | 14 +++++++ platform/openide.text/arch.xml | 9 +++++ platform/openide.text/manifest.mf | 3 +- .../openide/text/AskEditorQuestions.java | 35 +++++++++++++++++ .../org/openide/text/DocumentOpenClose.java | 3 ++ .../text/UserQuestionExceptionHandler.java | 39 +++++++++++++++++-- 6 files changed, 98 insertions(+), 5 deletions(-) diff --git a/platform/openide.text/apichanges.xml b/platform/openide.text/apichanges.xml index f6b91ab84d22..0a991abd767e 100644 --- a/platform/openide.text/apichanges.xml +++ b/platform/openide.text/apichanges.xml @@ -25,6 +25,20 @@ Text API + + + DocumentFilter can block a reload, UserQuestionExceptions can be branded to true or false. + + + + + + The implementation handles UserQuestionException during reload, which means a DocumentFilter + may block document reload. Support for automatic UserQuestionException handling was added as branding API + + Added EditorCookie.Observable.PROP_RELOADING and associated begin/end events diff --git a/platform/openide.text/arch.xml b/platform/openide.text/arch.xml index 931a6206e035..cfc5f5e7f598 100644 --- a/platform/openide.text/arch.xml +++ b/platform/openide.text/arch.xml @@ -437,6 +437,15 @@ in methods org/netbeans/modules/openide/text/Bundle.properties to yes or no in a branding file in your application. + + Controls handling of UserQuestionExceptions thrown during document I/O by + CloneableEditorSupport + Specific classes can be branded to result in + "yes" (reload without asking) or "no" (cancel the re/load operation). If unspecified or set to any other value, the + user will be asked the question (this is the default behaviour). +

+ The support is available from version 6.96 + diff --git a/platform/openide.text/manifest.mf b/platform/openide.text/manifest.mf index ccc8f1fd21f4..408e5e1efc96 100644 --- a/platform/openide.text/manifest.mf +++ b/platform/openide.text/manifest.mf @@ -2,5 +2,4 @@ Manifest-Version: 1.0 OpenIDE-Module: org.openide.text OpenIDE-Module-Localizing-Bundle: org/openide/text/Bundle.properties AutoUpdate-Essential-Module: true -OpenIDE-Module-Specification-Version: 6.95 - +OpenIDE-Module-Specification-Version: 6.96 diff --git a/platform/openide.text/src/org/netbeans/modules/openide/text/AskEditorQuestions.java b/platform/openide.text/src/org/netbeans/modules/openide/text/AskEditorQuestions.java index 49f2454d7257..e8ce073b4127 100644 --- a/platform/openide.text/src/org/netbeans/modules/openide/text/AskEditorQuestions.java +++ b/platform/openide.text/src/org/netbeans/modules/openide/text/AskEditorQuestions.java @@ -18,13 +18,48 @@ */ package org.netbeans.modules.openide.text; +import java.util.MissingResourceException; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; import org.openide.util.NbBundle; +import org.openide.util.UserQuestionException; public final class AskEditorQuestions { + public enum QuestionResult { + /** + * The implementation should ask the user. The default, if no + * value is present in the resource bundle. + */ + ASK_USER, + + /** + * Assume yes, do not ask the user, proceed immediately. + */ + YES, + + /** + * Assume no, do not ask the user, proceed immediately. + */ + NO, + } private AskEditorQuestions() { } + + public static QuestionResult askUserQuestion(UserQuestionException uqe) { + String key = "UserQuestionAnswer_" + uqe.getClass().getName(); + try { + String ask = NbBundle.getMessage(AskEditorQuestions.class, key); // NOI18N + if ("yes".equals(ask)) { + return QuestionResult.YES; + } + if ("no".equals(ask)) { + return QuestionResult.NO; + } + } catch (MissingResourceException ex) { + // expected + } + return QuestionResult.ASK_USER; + } public static boolean askReloadDocument(String localizedMessage) { String ask = NbBundle.getMessage(AskEditorQuestions.class, "ASK_OnReload"); // NOI18N diff --git a/platform/openide.text/src/org/openide/text/DocumentOpenClose.java b/platform/openide.text/src/org/openide/text/DocumentOpenClose.java index 5e043ebf997c..0c22cad48964 100644 --- a/platform/openide.text/src/org/openide/text/DocumentOpenClose.java +++ b/platform/openide.text/src/org/openide/text/DocumentOpenClose.java @@ -729,6 +729,9 @@ private void atomicLockedRun() { loadDoc.remove(0, loadDoc.getLength()); } } catch (BadLocationException ex) { + if (ex.getCause() instanceof IOException) { + throw (IOException)ex.getCause(); + } LOG.log(Level.INFO, null, ex); } } diff --git a/platform/openide.text/src/org/openide/text/UserQuestionExceptionHandler.java b/platform/openide.text/src/org/openide/text/UserQuestionExceptionHandler.java index 578c168887b5..74bdcc77cfbc 100644 --- a/platform/openide.text/src/org/openide/text/UserQuestionExceptionHandler.java +++ b/platform/openide.text/src/org/openide/text/UserQuestionExceptionHandler.java @@ -20,6 +20,7 @@ import java.io.IOException; import javax.swing.text.StyledDocument; +import org.netbeans.modules.openide.text.AskEditorQuestions; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; import org.openide.util.Exceptions; @@ -45,6 +46,29 @@ class UserQuestionExceptionHandler implements Runnable { } void runInEDT() { + AskEditorQuestions.QuestionResult shouldAsk = AskEditorQuestions.askUserQuestion(uqe); + // attempt to handle automatic responses synchronously: + if (AskEditorQuestions.QuestionResult.NO == shouldAsk) { + openRefused(); + return; + } else if (AskEditorQuestions.QuestionResult.YES == shouldAsk) { + try { + uqe.confirmed(); + uqe = null; + doc = openDocument(); + opened(doc); + return; + } catch (UserQuestionException ex) { + // bad luck, go for EDT access. + uqe = ex; + } catch (IOException ex1) { + handleIOException(ex1); + return; + } catch (RuntimeException ex) { + handleRuntimeException(ex); + return; + } + } Mutex.EVENT.readAccess(this); } @@ -60,9 +84,18 @@ boolean handleUserQuestionException() { handleStart(); try { while (true) { - NotifyDescriptor nd = new NotifyDescriptor.Confirmation(uqe.getLocalizedMessage(), NotifyDescriptor.YES_NO_OPTION); - nd.setOptions(new Object[]{NotifyDescriptor.YES_OPTION, NotifyDescriptor.NO_OPTION}); - Object res = DialogDisplayer.getDefault().notify(nd); + AskEditorQuestions.QuestionResult shouldAsk = AskEditorQuestions.askUserQuestion(uqe); + Object res; + if (AskEditorQuestions.QuestionResult.ASK_USER == shouldAsk) { + NotifyDescriptor nd = new NotifyDescriptor.Confirmation(uqe.getLocalizedMessage(), NotifyDescriptor.YES_NO_OPTION); + nd.setOptions(new Object[]{NotifyDescriptor.YES_OPTION, NotifyDescriptor.NO_OPTION}); + res = DialogDisplayer.getDefault().notify(nd); + } else if (AskEditorQuestions.QuestionResult.YES == shouldAsk) { + res = NotifyDescriptor.OK_OPTION; + } else { + res = NotifyDescriptor.NO_OPTION; + } + if (NotifyDescriptor.OK_OPTION.equals(res)) { try { uqe.confirmed();