From 30393d4abcc24d92dc1cb498348af0b6fb676696 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Nov 2025 10:48:14 +0000 Subject: [PATCH 1/6] Add toolbar button to z.ex.search plugin for executing RCP build script This commit adds functionality to the z.ex.search plugin: - New toolbar button with PDF icon in the main Eclipse toolbar - Button executes ~/git/content/_scripts/buildRCPScript.sh when pressed - BuildRCPScriptHandler class handles script execution in background thread - Shows success/error dialog with script output - Uses Eclipse 3.x API (org.eclipse.ui.menus) for toolbar contribution Note: The hard-coded script path (~/git/content/_scripts/buildRCPScript.sh) is intentional and points to a specific location in the user's home directory. Files added: - z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java - z.ex.search/icons/pdf.svg Files modified: - z.ex.search/plugin.xml (added command and toolbar contribution) - z.ex.search/build.properties (included icons directory) --- z.ex.search/build.properties | 3 +- z.ex.search/icons/pdf.svg | 3 + z.ex.search/plugin.xml | 21 ++++ .../z/ex/search/BuildRCPScriptHandler.java | 98 +++++++++++++++++++ 4 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 z.ex.search/icons/pdf.svg create mode 100644 z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java diff --git a/z.ex.search/build.properties b/z.ex.search/build.properties index e9863e2..6c480f3 100644 --- a/z.ex.search/build.properties +++ b/z.ex.search/build.properties @@ -2,4 +2,5 @@ source.. = src/ output.. = bin/ bin.includes = META-INF/,\ .,\ - plugin.xml + plugin.xml,\ + icons/ diff --git a/z.ex.search/icons/pdf.svg b/z.ex.search/icons/pdf.svg new file mode 100644 index 0000000..56e57fc --- /dev/null +++ b/z.ex.search/icons/pdf.svg @@ -0,0 +1,3 @@ + + + diff --git a/z.ex.search/plugin.xml b/z.ex.search/plugin.xml index 50b9208..3a81781 100644 --- a/z.ex.search/plugin.xml +++ b/z.ex.search/plugin.xml @@ -15,6 +15,11 @@ id="z.ex.search.prevSearchEntry" name="Previous Search Entry"> + + @@ -31,5 +36,21 @@ sequence="ALT+,"> + + + + + + + + diff --git a/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java b/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java new file mode 100644 index 0000000..c59c3e8 --- /dev/null +++ b/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java @@ -0,0 +1,98 @@ +package z.ex.search; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.handlers.HandlerUtil; + +public class BuildRCPScriptHandler extends AbstractHandler { + + // Hard-coded path (intentional) - points to user's home directory script + private static final String SCRIPT_PATH = System.getProperty("user.home") + + "/git/content/_scripts/buildRCPScript.sh"; + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + File scriptFile = new File(SCRIPT_PATH); + + if (!scriptFile.exists()) { + showError("Script not found at: " + SCRIPT_PATH); + return null; + } + + if (!scriptFile.canExecute()) { + showError("Script is not executable: " + SCRIPT_PATH); + return null; + } + + // Execute the script in a separate thread to avoid blocking the UI + Thread scriptThread = new Thread(() -> { + try { + ProcessBuilder pb = new ProcessBuilder(SCRIPT_PATH); + pb.directory(scriptFile.getParentFile()); + pb.redirectErrorStream(true); + + Process process = pb.start(); + + // Read and display output + StringBuilder output = new StringBuilder(); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + output.append(line).append("\n"); + System.out.println(line); + } + } + + int exitCode = process.waitFor(); + + final String message; + if (exitCode == 0) { + message = "RCP build script executed successfully."; + } else { + message = "RCP build script failed with exit code: " + exitCode + + "\n\nOutput:\n" + output.toString(); + } + + Display.getDefault().asyncExec(() -> { + MessageDialog.openInformation( + Display.getDefault().getActiveShell(), + "Build RCP Script", + message + ); + }); + + } catch (IOException e) { + showError("Error executing script: " + e.getMessage()); + } catch (InterruptedException e) { + showError("Script execution interrupted: " + e.getMessage()); + Thread.currentThread().interrupt(); + } + }); + + scriptThread.setDaemon(true); + scriptThread.start(); + + return null; + } + + private void showError(String message) { + Display.getDefault().asyncExec(() -> { + MessageDialog.openError( + Display.getDefault().getActiveShell(), + "Build RCP Script Error", + message + ); + }); + } +} From 2338ff0aed5e12b4e77316c4576fb0c373b8aebc Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Nov 2025 11:06:01 +0000 Subject: [PATCH 2/6] Refactor: Apply code review improvements to BuildRCPScriptHandler This commit addresses code review feedback and implements Eclipse best practices: 1. Replace raw Thread with Eclipse Jobs API (org.eclipse.core.runtime.jobs.Job) - Better integration with Eclipse UI progress reporting - Shows job in Progress view with 'Building RCP' label - Support for cancellation via IProgressMonitor - Proper status reporting (OK, ERROR, CANCEL) 2. Use Paths.get() for platform-independent path construction - Replaced string concatenation with java.nio.file.Paths - More robust across different operating systems 3. Remove unused imports - Removed org.eclipse.ui.handlers.HandlerUtil (not used) 4. Remove console logging - Removed System.out.println() calls (redundant) - Output is already collected and shown in dialogs 5. Use appropriate dialog types - Success: MessageDialog.openInformation - Failure: MessageDialog.openError (changed from openInformation) - More consistent with Eclipse UI conventions These improvements enhance robustness, maintainability, and user experience by following Eclipse plugin development best practices. --- .../z/ex/search/BuildRCPScriptHandler.java | 112 +++++++++++------- 1 file changed, 66 insertions(+), 46 deletions(-) diff --git a/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java b/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java index c59c3e8..3916e0d 100644 --- a/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java +++ b/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java @@ -4,21 +4,23 @@ import java.io.File; import java.io.IOException; import java.io.InputStreamReader; +import java.nio.file.Paths; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.swt.widgets.Display; -import org.eclipse.ui.handlers.HandlerUtil; public class BuildRCPScriptHandler extends AbstractHandler { // Hard-coded path (intentional) - points to user's home directory script - private static final String SCRIPT_PATH = System.getProperty("user.home") + - "/git/content/_scripts/buildRCPScript.sh"; + private static final String SCRIPT_PATH = Paths.get(System.getProperty("user.home"), + "git", "content", "_scripts", "buildRCPScript.sh").toString(); @Override public Object execute(ExecutionEvent event) throws ExecutionException { @@ -34,54 +36,72 @@ public Object execute(ExecutionEvent event) throws ExecutionException { return null; } - // Execute the script in a separate thread to avoid blocking the UI - Thread scriptThread = new Thread(() -> { - try { - ProcessBuilder pb = new ProcessBuilder(SCRIPT_PATH); - pb.directory(scriptFile.getParentFile()); - pb.redirectErrorStream(true); - - Process process = pb.start(); - - // Read and display output - StringBuilder output = new StringBuilder(); - try (BufferedReader reader = new BufferedReader( - new InputStreamReader(process.getInputStream()))) { - String line; - while ((line = reader.readLine()) != null) { - output.append(line).append("\n"); - System.out.println(line); + // Execute the script using Eclipse Jobs API + Job job = new Job("Building RCP") { + @Override + protected IStatus run(IProgressMonitor monitor) { + monitor.beginTask("Executing RCP build script", IProgressMonitor.UNKNOWN); + + try { + ProcessBuilder pb = new ProcessBuilder(SCRIPT_PATH); + pb.directory(scriptFile.getParentFile()); + pb.redirectErrorStream(true); + + Process process = pb.start(); + + // Read and collect output + StringBuilder output = new StringBuilder(); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + output.append(line).append("\n"); + if (monitor.isCanceled()) { + process.destroy(); + return Status.CANCEL_STATUS; + } + } } - } - - int exitCode = process.waitFor(); - final String message; - if (exitCode == 0) { - message = "RCP build script executed successfully."; - } else { - message = "RCP build script failed with exit code: " + exitCode + - "\n\nOutput:\n" + output.toString(); + int exitCode = process.waitFor(); + final boolean success = exitCode == 0; + + Display.getDefault().asyncExec(() -> { + if (success) { + MessageDialog.openInformation( + Display.getDefault().getActiveShell(), + "Build RCP Script", + "RCP build script executed successfully." + ); + } else { + String message = "RCP build script failed with exit code: " + exitCode + + "\n\nOutput:\n" + output.toString(); + MessageDialog.openError( + Display.getDefault().getActiveShell(), + "Build RCP Script Error", + message + ); + } + }); + + return Status.OK_STATUS; + + } catch (IOException e) { + showError("Error executing script: " + e.getMessage()); + return new Status(IStatus.ERROR, "z.ex.search", + "Error executing script", e); + } catch (InterruptedException e) { + showError("Script execution interrupted: " + e.getMessage()); + Thread.currentThread().interrupt(); + return Status.CANCEL_STATUS; + } finally { + monitor.done(); } - - Display.getDefault().asyncExec(() -> { - MessageDialog.openInformation( - Display.getDefault().getActiveShell(), - "Build RCP Script", - message - ); - }); - - } catch (IOException e) { - showError("Error executing script: " + e.getMessage()); - } catch (InterruptedException e) { - showError("Script execution interrupted: " + e.getMessage()); - Thread.currentThread().interrupt(); } - }); + }; - scriptThread.setDaemon(true); - scriptThread.start(); + job.setUser(true); // Shows the job in the UI + job.schedule(); return null; } From f12c3d545ced933bc7db09584e508eb20d284ed2 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Nov 2025 11:07:47 +0000 Subject: [PATCH 3/6] Add PDF file opening on successful build completion When the RCP build script executes successfully, the user is now prompted with a question dialog asking if they want to open the generated PDF file. Changes: - Success dialog changed from openInformation to openQuestion - Adds prompt: "Do you want to open the PDF file?" - New openPdfFile() method to handle PDF opening - Uses SWT Program.launch() to open PDF with system default application - PDF path hard-coded to ~/git/content/output.pdf (intentional) - Validates PDF file exists before attempting to open - Shows error if PDF not found or no PDF viewer associated User can now: 1. Click "Open" to view the PDF in their system's default PDF viewer 2. Click "Cancel" to dismiss the dialog without opening the file Note: Both the script path and PDF output path are intentionally hard-coded to specific locations in the user's home directory. --- .../z/ex/search/BuildRCPScriptHandler.java | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java b/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java index 3916e0d..0020ba8 100644 --- a/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java +++ b/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java @@ -14,13 +14,16 @@ import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.program.Program; import org.eclipse.swt.widgets.Display; public class BuildRCPScriptHandler extends AbstractHandler { - // Hard-coded path (intentional) - points to user's home directory script + // Hard-coded paths (intentional) - point to user's home directory private static final String SCRIPT_PATH = Paths.get(System.getProperty("user.home"), "git", "content", "_scripts", "buildRCPScript.sh").toString(); + private static final String PDF_OUTPUT_PATH = Paths.get(System.getProperty("user.home"), + "git", "content", "output.pdf").toString(); @Override public Object execute(ExecutionEvent event) throws ExecutionException { @@ -68,11 +71,15 @@ protected IStatus run(IProgressMonitor monitor) { Display.getDefault().asyncExec(() -> { if (success) { - MessageDialog.openInformation( + boolean openPdf = MessageDialog.openQuestion( Display.getDefault().getActiveShell(), "Build RCP Script", - "RCP build script executed successfully." + "RCP build script executed successfully.\n\nDo you want to open the PDF file?" ); + + if (openPdf) { + openPdfFile(); + } } else { String message = "RCP build script failed with exit code: " + exitCode + "\n\nOutput:\n" + output.toString(); @@ -115,4 +122,20 @@ private void showError(String message) { ); }); } + + private void openPdfFile() { + File pdfFile = new File(PDF_OUTPUT_PATH); + + if (!pdfFile.exists()) { + showError("PDF file not found at: " + PDF_OUTPUT_PATH); + return; + } + + // Use SWT Program to launch the PDF with system default application + boolean launched = Program.launch(pdfFile.getAbsolutePath()); + + if (!launched) { + showError("Failed to open PDF file. No application is associated with PDF files."); + } + } } From 8af8292696f4480a317e2d85d3291b857aa67cdb Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Nov 2025 11:26:23 +0000 Subject: [PATCH 4/6] Add configurable preferences and improve script execution reliability This commit addresses code review feedback to improve flexibility and robustness: 1. Make paths configurable via Eclipse preferences (MAJOR IMPROVEMENT) - Created PreferenceConstants class with default paths - Created PreferenceInitializer for default preference values - Created BuildScriptPreferencePage for user configuration - Registered preference page and initializer in plugin.xml - Users can now customize script and PDF paths via: Window > Preferences > Build Script - Defaults to original hard-coded paths for backward compatibility 2. Improve process termination on cancellation - Changed process.destroy() to process.destroyForcibly() - Ensures immediate termination when user cancels the job - More reliable than standard destroy() which can be ignored 3. Remove redundant error notifications - Removed showError() calls in IOException and InterruptedException handlers - IStatus errors are automatically shown in Eclipse Jobs UI - Prevents duplicate error dialogs for better UX 4. Code improvements - Removed unused java.nio.file.Paths import (no longer needed) - Read paths from preference store in execute() and openPdfFile() - Maintain backward compatibility with existing default paths Benefits: - Plugin is now flexible and reusable for different environments - Better user control without modifying source code - More reliable cancellation handling - Cleaner error reporting without duplication - Follows Eclipse plugin development best practices Files added: - z.ex.search/src/z/ex/search/PreferenceConstants.java - z.ex.search/src/z/ex/search/PreferenceInitializer.java - z.ex.search/src/z/ex/search/BuildScriptPreferencePage.java Files modified: - z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java - z.ex.search/plugin.xml --- z.ex.search/plugin.xml | 14 +++++++ .../z/ex/search/BuildRCPScriptHandler.java | 37 ++++++++++--------- .../ex/search/BuildScriptPreferencePage.java | 36 ++++++++++++++++++ .../src/z/ex/search/PreferenceConstants.java | 19 ++++++++++ .../z/ex/search/PreferenceInitializer.java | 19 ++++++++++ 5 files changed, 108 insertions(+), 17 deletions(-) create mode 100644 z.ex.search/src/z/ex/search/BuildScriptPreferencePage.java create mode 100644 z.ex.search/src/z/ex/search/PreferenceConstants.java create mode 100644 z.ex.search/src/z/ex/search/PreferenceInitializer.java diff --git a/z.ex.search/plugin.xml b/z.ex.search/plugin.xml index 3a81781..72defc6 100644 --- a/z.ex.search/plugin.xml +++ b/z.ex.search/plugin.xml @@ -52,5 +52,19 @@ + + + + + + + + diff --git a/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java b/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java index 0020ba8..b13a883 100644 --- a/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java +++ b/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java @@ -4,7 +4,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStreamReader; -import java.nio.file.Paths; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; @@ -13,29 +12,31 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.swt.program.Program; import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.preferences.ScopedPreferenceStore; public class BuildRCPScriptHandler extends AbstractHandler { - // Hard-coded paths (intentional) - point to user's home directory - private static final String SCRIPT_PATH = Paths.get(System.getProperty("user.home"), - "git", "content", "_scripts", "buildRCPScript.sh").toString(); - private static final String PDF_OUTPUT_PATH = Paths.get(System.getProperty("user.home"), - "git", "content", "output.pdf").toString(); - @Override public Object execute(ExecutionEvent event) throws ExecutionException { - File scriptFile = new File(SCRIPT_PATH); + // Read paths from preferences + IPreferenceStore store = new ScopedPreferenceStore(InstanceScope.INSTANCE, "z.ex.search"); + String scriptPath = store.getString(PreferenceConstants.SCRIPT_PATH); + String pdfOutputPath = store.getString(PreferenceConstants.PDF_OUTPUT_PATH); + + File scriptFile = new File(scriptPath); if (!scriptFile.exists()) { - showError("Script not found at: " + SCRIPT_PATH); + showError("Script not found at: " + scriptPath); return null; } if (!scriptFile.canExecute()) { - showError("Script is not executable: " + SCRIPT_PATH); + showError("Script is not executable: " + scriptPath); return null; } @@ -46,7 +47,7 @@ protected IStatus run(IProgressMonitor monitor) { monitor.beginTask("Executing RCP build script", IProgressMonitor.UNKNOWN); try { - ProcessBuilder pb = new ProcessBuilder(SCRIPT_PATH); + ProcessBuilder pb = new ProcessBuilder(scriptPath); pb.directory(scriptFile.getParentFile()); pb.redirectErrorStream(true); @@ -60,7 +61,7 @@ protected IStatus run(IProgressMonitor monitor) { while ((line = reader.readLine()) != null) { output.append(line).append("\n"); if (monitor.isCanceled()) { - process.destroy(); + process.destroyForcibly(); return Status.CANCEL_STATUS; } } @@ -94,11 +95,9 @@ protected IStatus run(IProgressMonitor monitor) { return Status.OK_STATUS; } catch (IOException e) { - showError("Error executing script: " + e.getMessage()); return new Status(IStatus.ERROR, "z.ex.search", - "Error executing script", e); + "Error executing script: " + e.getMessage(), e); } catch (InterruptedException e) { - showError("Script execution interrupted: " + e.getMessage()); Thread.currentThread().interrupt(); return Status.CANCEL_STATUS; } finally { @@ -124,10 +123,14 @@ private void showError(String message) { } private void openPdfFile() { - File pdfFile = new File(PDF_OUTPUT_PATH); + // Read PDF path from preferences + IPreferenceStore store = new ScopedPreferenceStore(InstanceScope.INSTANCE, "z.ex.search"); + String pdfOutputPath = store.getString(PreferenceConstants.PDF_OUTPUT_PATH); + + File pdfFile = new File(pdfOutputPath); if (!pdfFile.exists()) { - showError("PDF file not found at: " + PDF_OUTPUT_PATH); + showError("PDF file not found at: " + pdfOutputPath); return; } diff --git a/z.ex.search/src/z/ex/search/BuildScriptPreferencePage.java b/z.ex.search/src/z/ex/search/BuildScriptPreferencePage.java new file mode 100644 index 0000000..77f9509 --- /dev/null +++ b/z.ex.search/src/z/ex/search/BuildScriptPreferencePage.java @@ -0,0 +1,36 @@ +package z.ex.search; + +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.FileFieldEditor; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.preferences.ScopedPreferenceStore; + +/** + * Preference page for configuring build script paths. + */ +public class BuildScriptPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + + public BuildScriptPreferencePage() { + super(GRID); + } + + @Override + public void createFieldEditors() { + addField(new FileFieldEditor(PreferenceConstants.SCRIPT_PATH, + "&Build Script Path:", + getFieldEditorParent())); + + addField(new FileFieldEditor(PreferenceConstants.PDF_OUTPUT_PATH, + "&PDF Output Path:", + getFieldEditorParent())); + } + + @Override + public void init(IWorkbench workbench) { + setPreferenceStore(new ScopedPreferenceStore(InstanceScope.INSTANCE, "z.ex.search")); + setDescription("Configure paths for the RCP build script and PDF output."); + } + +} diff --git a/z.ex.search/src/z/ex/search/PreferenceConstants.java b/z.ex.search/src/z/ex/search/PreferenceConstants.java new file mode 100644 index 0000000..1e49ec5 --- /dev/null +++ b/z.ex.search/src/z/ex/search/PreferenceConstants.java @@ -0,0 +1,19 @@ +package z.ex.search; + +import java.nio.file.Paths; + +/** + * Constant definitions for plug-in preferences + */ +public class PreferenceConstants { + + public static final String SCRIPT_PATH = "scriptPath"; + public static final String PDF_OUTPUT_PATH = "pdfOutputPath"; + + // Default values + public static final String DEFAULT_SCRIPT_PATH = Paths.get(System.getProperty("user.home"), + "git", "content", "_scripts", "buildRCPScript.sh").toString(); + public static final String DEFAULT_PDF_OUTPUT_PATH = Paths.get(System.getProperty("user.home"), + "git", "content", "output.pdf").toString(); + +} diff --git a/z.ex.search/src/z/ex/search/PreferenceInitializer.java b/z.ex.search/src/z/ex/search/PreferenceInitializer.java new file mode 100644 index 0000000..a51f9a4 --- /dev/null +++ b/z.ex.search/src/z/ex/search/PreferenceInitializer.java @@ -0,0 +1,19 @@ +package z.ex.search; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.ui.preferences.ScopedPreferenceStore; + +/** + * Initializes default preference values. + */ +public class PreferenceInitializer extends AbstractPreferenceInitializer { + + @Override + public void initializeDefaultPreferences() { + ScopedPreferenceStore store = new ScopedPreferenceStore(InstanceScope.INSTANCE, "z.ex.search"); + store.setDefault(PreferenceConstants.SCRIPT_PATH, PreferenceConstants.DEFAULT_SCRIPT_PATH); + store.setDefault(PreferenceConstants.PDF_OUTPUT_PATH, PreferenceConstants.DEFAULT_PDF_OUTPUT_PATH); + } + +} From e3eaac0a3d933ebc9125d41221b270fa196fd785 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Nov 2025 11:27:52 +0000 Subject: [PATCH 5/6] Replace MessageDialog with modern Notification API This commit replaces all MessageDialog popups with the modern Eclipse Notification API for a better user experience. Changes: 1. Success notification with interactive "Open PDF" button - NotificationPopup appears in bottom-right corner - User can click "Open PDF" button directly in notification - Non-blocking, allows continued work while notification is visible 2. Error notifications for build failures - Shows error message in notification popup - Truncates long error messages (>300 chars) for readability - Consistent with modern Eclipse UI patterns 3. Updated MANIFEST.MF - Added org.eclipse.jface dependency for NotificationPopup API 4. Improved user experience - Non-modal notifications don't block workflow - Notifications auto-dismiss after timeout - Action buttons directly in notification - Modern, consistent with Eclipse platform Implementation: - Uses NotificationPopup.forDisplay() fluent API - WidgetFactory for creating notification content - GridLayout for organizing notification widgets - onSelect() handlers for interactive buttons Benefits: - Less intrusive than modal dialogs - Users can continue working while notification is visible - Better integration with Eclipse UI conventions - More professional, modern appearance Files modified: - z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java - z.ex.search/META-INF/MANIFEST.MF --- z.ex.search/META-INF/MANIFEST.MF | 3 +- .../z/ex/search/BuildRCPScriptHandler.java | 75 +++++++++++++------ 2 files changed, 53 insertions(+), 25 deletions(-) diff --git a/z.ex.search/META-INF/MANIFEST.MF b/z.ex.search/META-INF/MANIFEST.MF index b5b12e1..3829138 100644 --- a/z.ex.search/META-INF/MANIFEST.MF +++ b/z.ex.search/META-INF/MANIFEST.MF @@ -4,6 +4,7 @@ Bundle-Name: Next Search Helper Bundle-SymbolicName: z.ex.search;singleton:=true Bundle-Version: 0.1.0.qualifier Require-Bundle: org.eclipse.ui, - org.eclipse.core.runtime + org.eclipse.core.runtime, + org.eclipse.jface Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-ActivationPolicy: lazy diff --git a/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java b/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java index b13a883..09876bc 100644 --- a/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java +++ b/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java @@ -13,9 +13,12 @@ import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.InstanceScope; -import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.notifications.NotificationPopup; import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.widgets.WidgetFactory; +import org.eclipse.swt.SWT; import org.eclipse.swt.program.Program; +import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.preferences.ScopedPreferenceStore; @@ -72,23 +75,11 @@ protected IStatus run(IProgressMonitor monitor) { Display.getDefault().asyncExec(() -> { if (success) { - boolean openPdf = MessageDialog.openQuestion( - Display.getDefault().getActiveShell(), - "Build RCP Script", - "RCP build script executed successfully.\n\nDo you want to open the PDF file?" - ); - - if (openPdf) { - openPdfFile(); - } + showSuccessNotification(); } else { - String message = "RCP build script failed with exit code: " + exitCode + - "\n\nOutput:\n" + output.toString(); - MessageDialog.openError( - Display.getDefault().getActiveShell(), - "Build RCP Script Error", - message - ); + String message = "Build failed with exit code: " + exitCode + + "\n\n" + output.toString(); + showErrorNotification(message); } }); @@ -114,14 +105,50 @@ protected IStatus run(IProgressMonitor monitor) { private void showError(String message) { Display.getDefault().asyncExec(() -> { - MessageDialog.openError( - Display.getDefault().getActiveShell(), - "Build RCP Script Error", - message - ); + showErrorNotification(message); }); } + private void showSuccessNotification() { + NotificationPopup.forDisplay(Display.getDefault()) + .content(composite -> { + composite.setLayout(new org.eclipse.swt.layout.GridLayout(1, false)); + + WidgetFactory.label(SWT.WRAP) + .text("RCP build script executed successfully.") + .create(composite); + + WidgetFactory.button(SWT.PUSH) + .text("Open PDF") + .onSelect(e -> openPdfFile()) + .layoutData(new org.eclipse.swt.layout.GridData(SWT.LEFT, SWT.CENTER, false, false)) + .create(composite); + + return composite; + }) + .title(composite -> WidgetFactory.label(SWT.NONE) + .text("Build Successful") + .create(composite), true) + .open(); + } + + private void showErrorNotification(String message) { + NotificationPopup.forDisplay(Display.getDefault()) + .content(composite -> { + composite.setLayout(new org.eclipse.swt.layout.GridLayout(1, false)); + + WidgetFactory.label(SWT.WRAP) + .text(message.length() > 300 ? message.substring(0, 300) + "..." : message) + .create(composite); + + return composite; + }) + .title(composite -> WidgetFactory.label(SWT.NONE) + .text("Build Script Error") + .create(composite), true) + .open(); + } + private void openPdfFile() { // Read PDF path from preferences IPreferenceStore store = new ScopedPreferenceStore(InstanceScope.INSTANCE, "z.ex.search"); @@ -130,7 +157,7 @@ private void openPdfFile() { File pdfFile = new File(pdfOutputPath); if (!pdfFile.exists()) { - showError("PDF file not found at: " + pdfOutputPath); + showErrorNotification("PDF file not found at: " + pdfOutputPath); return; } @@ -138,7 +165,7 @@ private void openPdfFile() { boolean launched = Program.launch(pdfFile.getAbsolutePath()); if (!launched) { - showError("Failed to open PDF file. No application is associated with PDF files."); + showErrorNotification("Failed to open PDF file. No application is associated with PDF files."); } } } From 0f8b15f8e5eae97d1893390d0ecb1b40024200f8 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Nov 2025 12:10:21 +0000 Subject: [PATCH 6/6] Fix build failure: Revert to MessageDialog API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit used NotificationPopup API which is not available in the target platform, causing build failures. Changes: - Reverted from NotificationPopup to MessageDialog - Removed org.eclipse.jface dependency from MANIFEST.MF - Renamed methods to showSuccessDialog() and showErrorDialog() - Removed unnecessary imports (WidgetFactory, SWT, Composite) - Kept all code review improvements: * Configurable preferences ✓ * process.destroyForcibly() for cancellation ✓ * No duplicate error notifications ✓ * Eclipse Jobs API ✓ User experience remains the same: - Success: Question dialog with "Open" and "Cancel" buttons - Failure: Error dialog with exit code and output - All paths configurable via preferences This resolves the build failure while maintaining all improvements. --- z.ex.search/META-INF/MANIFEST.MF | 3 +- .../z/ex/search/BuildRCPScriptHandler.java | 87 ++++++++----------- 2 files changed, 36 insertions(+), 54 deletions(-) diff --git a/z.ex.search/META-INF/MANIFEST.MF b/z.ex.search/META-INF/MANIFEST.MF index 3829138..b5b12e1 100644 --- a/z.ex.search/META-INF/MANIFEST.MF +++ b/z.ex.search/META-INF/MANIFEST.MF @@ -4,7 +4,6 @@ Bundle-Name: Next Search Helper Bundle-SymbolicName: z.ex.search;singleton:=true Bundle-Version: 0.1.0.qualifier Require-Bundle: org.eclipse.ui, - org.eclipse.core.runtime, - org.eclipse.jface + org.eclipse.core.runtime Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-ActivationPolicy: lazy diff --git a/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java b/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java index 09876bc..d8699c2 100644 --- a/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java +++ b/z.ex.search/src/z/ex/search/BuildRCPScriptHandler.java @@ -13,12 +13,9 @@ import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.InstanceScope; -import org.eclipse.jface.notifications.NotificationPopup; +import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPreferenceStore; -import org.eclipse.jface.widgets.WidgetFactory; -import org.eclipse.swt.SWT; import org.eclipse.swt.program.Program; -import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.preferences.ScopedPreferenceStore; @@ -73,15 +70,13 @@ protected IStatus run(IProgressMonitor monitor) { int exitCode = process.waitFor(); final boolean success = exitCode == 0; - Display.getDefault().asyncExec(() -> { - if (success) { - showSuccessNotification(); - } else { - String message = "Build failed with exit code: " + exitCode + - "\n\n" + output.toString(); - showErrorNotification(message); - } - }); + if (success) { + showSuccessDialog(); + } else { + String message = "Build failed with exit code: " + exitCode + + "\n\n" + output.toString(); + showErrorDialog(message); + } return Status.OK_STATUS; @@ -105,48 +100,36 @@ protected IStatus run(IProgressMonitor monitor) { private void showError(String message) { Display.getDefault().asyncExec(() -> { - showErrorNotification(message); + MessageDialog.openError( + Display.getDefault().getActiveShell(), + "Build RCP Script Error", + message + ); }); } - private void showSuccessNotification() { - NotificationPopup.forDisplay(Display.getDefault()) - .content(composite -> { - composite.setLayout(new org.eclipse.swt.layout.GridLayout(1, false)); - - WidgetFactory.label(SWT.WRAP) - .text("RCP build script executed successfully.") - .create(composite); - - WidgetFactory.button(SWT.PUSH) - .text("Open PDF") - .onSelect(e -> openPdfFile()) - .layoutData(new org.eclipse.swt.layout.GridData(SWT.LEFT, SWT.CENTER, false, false)) - .create(composite); - - return composite; - }) - .title(composite -> WidgetFactory.label(SWT.NONE) - .text("Build Successful") - .create(composite), true) - .open(); + private void showSuccessDialog() { + Display.getDefault().asyncExec(() -> { + boolean openPdf = MessageDialog.openQuestion( + Display.getDefault().getActiveShell(), + "Build RCP Script", + "RCP build script executed successfully.\n\nDo you want to open the PDF file?" + ); + + if (openPdf) { + openPdfFile(); + } + }); } - private void showErrorNotification(String message) { - NotificationPopup.forDisplay(Display.getDefault()) - .content(composite -> { - composite.setLayout(new org.eclipse.swt.layout.GridLayout(1, false)); - - WidgetFactory.label(SWT.WRAP) - .text(message.length() > 300 ? message.substring(0, 300) + "..." : message) - .create(composite); - - return composite; - }) - .title(composite -> WidgetFactory.label(SWT.NONE) - .text("Build Script Error") - .create(composite), true) - .open(); + private void showErrorDialog(String message) { + Display.getDefault().asyncExec(() -> { + MessageDialog.openError( + Display.getDefault().getActiveShell(), + "Build RCP Script Error", + message + ); + }); } private void openPdfFile() { @@ -157,7 +140,7 @@ private void openPdfFile() { File pdfFile = new File(pdfOutputPath); if (!pdfFile.exists()) { - showErrorNotification("PDF file not found at: " + pdfOutputPath); + showError("PDF file not found at: " + pdfOutputPath); return; } @@ -165,7 +148,7 @@ private void openPdfFile() { boolean launched = Program.launch(pdfFile.getAbsolutePath()); if (!launched) { - showErrorNotification("Failed to open PDF file. No application is associated with PDF files."); + showError("Failed to open PDF file. No application is associated with PDF files."); } } }