diff --git a/bundles/org.eclipse.core.commands/META-INF/MANIFEST.MF b/bundles/org.eclipse.core.commands/META-INF/MANIFEST.MF index 164eadec0f0..29990cb8a33 100644 --- a/bundles/org.eclipse.core.commands/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.core.commands/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.core.commands -Bundle-Version: 3.12.100.qualifier +Bundle-Version: 3.12.200.qualifier Bundle-Vendor: %providerName Bundle-Localization: plugin Export-Package: org.eclipse.core.commands, diff --git a/bundles/org.eclipse.core.commands/src/org/eclipse/core/internal/commands/util/Util.java b/bundles/org.eclipse.core.commands/src/org/eclipse/core/internal/commands/util/Util.java index 42c748b5f06..52710ca8258 100644 --- a/bundles/org.eclipse.core.commands/src/org/eclipse/core/internal/commands/util/Util.java +++ b/bundles/org.eclipse.core.commands/src/org/eclipse/core/internal/commands/util/Util.java @@ -255,14 +255,16 @@ public static final String getHelpContextId(Command command) { String contextId = null; if (method != null) { - boolean accessible = method.isAccessible(); - method.setAccessible(true); - try { - contextId = (String) method.invoke(command); - } catch (Exception e) { - // do nothing + boolean accessible = method.canAccess(command); + if (method.trySetAccessible()) { + try { + contextId = (String) method.invoke(command); + } catch (Exception ignored) { + // do nothing + } finally { + method.setAccessible(accessible); + } } - method.setAccessible(accessible); } return contextId; } diff --git a/bundles/org.eclipse.e4.ui.css.core/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.ui.css.core/META-INF/MANIFEST.MF index 4ad9f1f4c3e..164a33caaa9 100644 --- a/bundles/org.eclipse.e4.ui.css.core/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.e4.ui.css.core/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-SymbolicName: org.eclipse.e4.ui.css.core;singleton:=true Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-Localization: plugin -Bundle-Version: 0.14.400.qualifier +Bundle-Version: 0.14.500.qualifier Export-Package: org.eclipse.e4.ui.css.core;x-internal:=true, org.eclipse.e4.ui.css.core.css2;x-friends:="org.eclipse.e4.ui.css.swt.theme,org.eclipse.e4.ui.css.swt,org.eclipse.e4.ui.css.jface", org.eclipse.e4.ui.css.core.dom;x-friends:="org.eclipse.e4.ui.css.swt,org.eclipse.ui.views.properties.tabbed,org.eclipse.ui.forms", diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/SACParserFactoryImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/SACParserFactoryImpl.java index d1302e969b1..5ff046bbbb4 100644 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/SACParserFactoryImpl.java +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/SACParserFactoryImpl.java @@ -13,6 +13,7 @@ *******************************************************************************/ package org.eclipse.e4.ui.css.core.impl.sac; +import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; import org.eclipse.e4.ui.css.core.SACConstants; @@ -49,7 +50,11 @@ public Parser makeParser(String name) throws ClassNotFoundException, IllegalAcce String classNameParser = parsers.get(name); if (classNameParser != null) { Class classParser = super.getClass().getClassLoader().loadClass(classNameParser); - return (Parser) classParser.newInstance(); + try { + return (Parser) classParser.getDeclaredConstructor().newInstance(); + } catch (InvocationTargetException | NoSuchMethodException e) { + throw (InstantiationException) new InstantiationException(classNameParser).initCause(e); + } } throw new IllegalAccessException("SAC parser with name=" + name + " was not registered into SAC parser factory."); diff --git a/bundles/org.eclipse.e4.ui.progress/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.ui.progress/META-INF/MANIFEST.MF index 2641558056c..ee8ae932c12 100644 --- a/bundles/org.eclipse.e4.ui.progress/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.e4.ui.progress/META-INF/MANIFEST.MF @@ -1,7 +1,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-SymbolicName: org.eclipse.e4.ui.progress;singleton:=true -Bundle-Version: 0.4.400.qualifier +Bundle-Version: 0.4.600.qualifier Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/bundles/org.eclipse.e4.ui.progress/src/org/eclipse/e4/ui/progress/internal/ProgressServiceImpl.java b/bundles/org.eclipse.e4.ui.progress/src/org/eclipse/e4/ui/progress/internal/ProgressServiceImpl.java index 12b874492a9..08a0d651c18 100644 --- a/bundles/org.eclipse.e4.ui.progress/src/org/eclipse/e4/ui/progress/internal/ProgressServiceImpl.java +++ b/bundles/org.eclipse.e4.ui.progress/src/org/eclipse/e4/ui/progress/internal/ProgressServiceImpl.java @@ -58,7 +58,6 @@ public class ProgressServiceImpl implements IProgressService { ProgressManager progressManager; @Inject - @Optional FinishedJobs finishedJobs; @Inject diff --git a/bundles/org.eclipse.e4.ui.workbench.renderers.swt/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.ui.workbench.renderers.swt/META-INF/MANIFEST.MF index defa89e1d9e..68da40138bf 100644 --- a/bundles/org.eclipse.e4.ui.workbench.renderers.swt/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.e4.ui.workbench.renderers.swt/META-INF/MANIFEST.MF @@ -1,7 +1,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-SymbolicName: org.eclipse.e4.ui.workbench.renderers.swt;singleton:=true -Bundle-Version: 0.16.400.qualifier +Bundle-Version: 0.16.500.qualifier Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/CTabRendering.java b/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/CTabRendering.java index f562024aaff..d280e10502d 100644 --- a/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/CTabRendering.java +++ b/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/CTabRendering.java @@ -1254,14 +1254,15 @@ public ReflectionSupport(T instance) { protected Object getFieldValue(Field field) { Object value = null; if (field != null) { - boolean accessible = field.isAccessible(); - try { - field.setAccessible(true); - value = field.get(instance); - } catch (Exception exc) { - // do nothing - } finally { - field.setAccessible(accessible); + boolean accessible = field.canAccess(instance); + if (field.trySetAccessible()) { + try { + value = field.get(instance); + } catch (Exception ignored) { + // do nothing + } finally { + field.setAccessible(accessible); + } } } return value; diff --git a/bundles/org.eclipse.jface/.settings/.api_filters b/bundles/org.eclipse.jface/.settings/.api_filters deleted file mode 100644 index 675a88b29bd..00000000000 --- a/bundles/org.eclipse.jface/.settings/.api_filters +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/bundles/org.eclipse.jface/META-INF/MANIFEST.MF b/bundles/org.eclipse.jface/META-INF/MANIFEST.MF index 17def932ecf..83e8c9b404d 100644 --- a/bundles/org.eclipse.jface/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.jface/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.jface;singleton:=true -Bundle-Version: 3.34.0.qualifier +Bundle-Version: 3.34.100.qualifier Bundle-Vendor: %providerName Bundle-Localization: plugin Export-Package: org.eclipse.jface, diff --git a/bundles/org.eclipse.ui.editors/META-INF/MANIFEST.MF b/bundles/org.eclipse.ui.editors/META-INF/MANIFEST.MF index 7a34055bd76..a3e278d607e 100644 --- a/bundles/org.eclipse.ui.editors/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.ui.editors/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.ui.editors; singleton:=true -Bundle-Version: 3.17.300.qualifier +Bundle-Version: 3.18.0.qualifier Bundle-Activator: org.eclipse.ui.internal.editors.text.EditorsPlugin Bundle-ActivationPolicy: lazy Bundle-Vendor: %providerName diff --git a/bundles/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorDefaultsPreferencePage.java b/bundles/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorDefaultsPreferencePage.java index 95009b7b315..5fea8e14203 100644 --- a/bundles/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorDefaultsPreferencePage.java +++ b/bundles/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorDefaultsPreferencePage.java @@ -428,6 +428,8 @@ private OverlayPreferenceStore createDialogOverlayStore() { ArrayList overlayKeys= new ArrayList<>(); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SHOW_LEADING_SPACES)); + overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_USE_FIND_REPLACE_OVERLAY)); + overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_FIND_REPLACE_OVERLAY_AT_BOTTOM)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SHOW_ENCLOSED_SPACES)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SHOW_TRAILING_SPACES)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SHOW_LEADING_IDEOGRAPHIC_SPACES)); @@ -729,6 +731,8 @@ private OverlayPreferenceStore createOverlayStore() { overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, AbstractTextEditor.PREFERENCE_COLOR_FIND_SCOPE)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE_COLOR)); + overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_USE_FIND_REPLACE_OVERLAY)); + overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_FIND_REPLACE_OVERLAY_AT_BOTTOM)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.INT, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH)); @@ -859,6 +863,15 @@ public void widgetSelected(SelectionEvent e) { IntegerDomain lineSpaceDomain= new IntegerDomain(0, 1000); addTextField(appearanceComposite, lineSpacing, lineSpaceDomain, 15, 0); + label= TextEditorMessages.TextEditorPreferencePage_useFindReplaceOverlay; + Preference useFindReplaceOverlay= new Preference(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_USE_FIND_REPLACE_OVERLAY, label, null); + final Button useOverlay= addCheckBox(appearanceComposite, useFindReplaceOverlay, new BooleanDomain(), 0); + + label= TextEditorMessages.TextEditorPreferencePage_showFindReplaceOverlayAtBottom; + Preference findReplaceOverlayAtBottom= new Preference(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_FIND_REPLACE_OVERLAY_AT_BOTTOM, label, null); + final Button overlayAtBottom= addCheckBox(appearanceComposite, findReplaceOverlayAtBottom, new BooleanDomain(), 0); + createDependency(useOverlay, useFindReplaceOverlay, new Control[] { overlayAtBottom }); + label= TextEditorMessages.TextEditorPreferencePage_enableWordWrap; Preference enableWordWrap= new Preference(AbstractTextEditor.PREFERENCE_WORD_WRAP_ENABLED, label, null); addCheckBox(appearanceComposite, enableWordWrap, new BooleanDomain(), 0); diff --git a/bundles/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.java b/bundles/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.java index 5858610848b..2872aea091f 100644 --- a/bundles/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.java +++ b/bundles/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.java @@ -35,6 +35,8 @@ private TextEditorMessages() { public static String LinkedModeConfigurationBlock_DASHED_BOX; public static String TextEditorPreferencePage_displayedTabWidth; public static String TextEditorPreferencePage_lineSpacing; + public static String TextEditorPreferencePage_useFindReplaceOverlay; + public static String TextEditorPreferencePage_showFindReplaceOverlayAtBottom; public static String TextEditorPreferencePage_enableWordWrap; public static String TextEditorPreferencePage_convertTabsToSpaces; public static String TextEditorPreferencePage_undoHistorySize; diff --git a/bundles/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.properties b/bundles/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.properties index c2d724c3f8c..cf6d2aac96a 100644 --- a/bundles/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.properties +++ b/bundles/org.eclipse.ui.editors/src/org/eclipse/ui/internal/editors/text/TextEditorMessages.properties @@ -17,6 +17,8 @@ EditorsPlugin_internal_error=Internal Error TextEditorPreferencePage_displayedTabWidth=Displayed &tab width: TextEditorPreferencePage_lineSpacing=Line &spacing (extra % of font height): +TextEditorPreferencePage_useFindReplaceOverlay=Use find/replace overla&y +TextEditorPreferencePage_showFindReplaceOverlayAtBottom=&Display find/replace overlay at bottom of editor TextEditorPreferencePage_enableWordWrap=&Enable word wrap when opening an editor TextEditorPreferencePage_convertTabsToSpaces=&Insert spaces for tabs TextEditorPreferencePage_undoHistorySize=&Undo history size: diff --git a/bundles/org.eclipse.ui.editors/src/org/eclipse/ui/texteditor/AbstractDecoratedTextEditorPreferenceConstants.java b/bundles/org.eclipse.ui.editors/src/org/eclipse/ui/texteditor/AbstractDecoratedTextEditorPreferenceConstants.java index 0bb4b3347c8..20cb709f516 100644 --- a/bundles/org.eclipse.ui.editors/src/org/eclipse/ui/texteditor/AbstractDecoratedTextEditorPreferenceConstants.java +++ b/bundles/org.eclipse.ui.editors/src/org/eclipse/ui/texteditor/AbstractDecoratedTextEditorPreferenceConstants.java @@ -220,6 +220,30 @@ private AbstractDecoratedTextEditorPreferenceConstants() { *

*/ public final static String EDITOR_LINE_NUMBER_RULER= "lineNumberRuler"; //$NON-NLS-1$ + + /** + * A named preference that controls whether the find/replace overlay is used in place of the + * dialog. + * + *

+ * The preference value is of type Boolean + *

+ * + * @since 3.18 + */ + public final static String EDITOR_USE_FIND_REPLACE_OVERLAY= "useFindReplaceOverlay"; //$NON-NLS-1$ + + /** + * A named preference that controls whether the editor overlay to access find and replace + * functionality should be aligned to the bottom of the editor page instead of to the top. + * + *

+ * The preference value is of type Boolean + *

+ * + * @since 3.18 + */ + public final static String EDITOR_FIND_REPLACE_OVERLAY_AT_BOTTOM= "findReplaceOverlayAtBottom"; //$NON-NLS-1$ /** * A named preference that controls if the caret offset is shown in the status line. @@ -723,6 +747,8 @@ private AbstractDecoratedTextEditorPreferenceConstants() { * @param store the preference store to be initialized */ public static void initializeDefaultValues(IPreferenceStore store) { + store.setDefault(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_USE_FIND_REPLACE_OVERLAY, true); + store.setDefault(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_FIND_REPLACE_OVERLAY_AT_BOTTOM, false); store.setDefault(AbstractDecoratedTextEditorPreferenceConstants.USE_ANNOTATIONS_PREFERENCE_PAGE, false); store.setDefault(AbstractDecoratedTextEditorPreferenceConstants.USE_QUICK_DIFF_PREFERENCE_PAGE, false); diff --git a/bundles/org.eclipse.ui.ide.application/META-INF/MANIFEST.MF b/bundles/org.eclipse.ui.ide.application/META-INF/MANIFEST.MF index 5a898885d6b..cdcac93fad7 100644 --- a/bundles/org.eclipse.ui.ide.application/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.ui.ide.application/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Plugin.name Bundle-SymbolicName: org.eclipse.ui.ide.application;singleton:=true -Bundle-Version: 1.5.400.qualifier +Bundle-Version: 1.5.500.qualifier Bundle-Vendor: %Plugin.providerName Bundle-Localization: plugin Require-Bundle: org.eclipse.ui.ide;bundle-version="[3.21.0,4.0.0)", diff --git a/bundles/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/dialogs/UriSchemeHandlerPreferencePage.java b/bundles/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/dialogs/UriSchemeHandlerPreferencePage.java index a277e2cd764..819a0fa68cc 100644 --- a/bundles/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/dialogs/UriSchemeHandlerPreferencePage.java +++ b/bundles/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/dialogs/UriSchemeHandlerPreferencePage.java @@ -11,7 +11,7 @@ package org.eclipse.ui.internal.ide.application.dialogs; import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UriHandlerPreferencePage_Confirm_Handle; -import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UriHandlerPreferencePage_Warning_OtherApp; +import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UriHandlerPreferencePage_Warning_NotPossible; import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UriHandlerPreferencePage_Warning_OtherApp_Confirmation; import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UriHandlerPreferencePage_Warning_OtherApp_Confirmation_Description; import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UriHandlerPreferencePage_Warning_OtherApp_Description; @@ -27,6 +27,7 @@ import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_LauncherCannotBeDetermined; import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_LoadingText; import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_Page_Description; +import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_RegistrationUnsupported; import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_UnsupportedOperatingSystem; import java.util.ArrayList; @@ -140,6 +141,9 @@ protected Control createContents(Composite parent) { setDataOnTableViewer(Collections.emptyList()); } else { + if (!operatingSystemRegistration.supportsRegistration()) { + setErrorMessage(UrlHandlerPreferencePage_RegistrationUnsupported); + } setDataOnTableViewer(getLoadingSchemeInformationList()); startRegistrationReadingJob(); } @@ -294,6 +298,12 @@ private void setSchemeDetails(UiSchemeInformation schemeInfo) { private void handleCheckbox(CheckStateChangedEvent event) { UiSchemeInformation schemeInformation = (UiSchemeInformation) event.getElement(); + if (!operatingSystemRegistration.supportsRegistration()) { + messageDialogWrapper.openWarning(getShell(), UriHandlerPreferencePage_Warning_NotPossible, + UrlHandlerPreferencePage_RegistrationUnsupported); + tableViewer.setChecked(schemeInformation, false); + return; + } if (event.getChecked() && schemeInformation.information.schemeIsHandledByOther()) { if (operatingSystemRegistration.canOverwriteOtherApplicationsRegistration()) { boolean answer = messageDialogWrapper.openQuestion(getShell(), @@ -310,7 +320,7 @@ private void handleCheckbox(CheckStateChangedEvent event) { schemeInformation.checked = false; tableViewer.setChecked(schemeInformation, schemeInformation.checked); - messageDialogWrapper.openWarning(getShell(), UriHandlerPreferencePage_Warning_OtherApp, + messageDialogWrapper.openWarning(getShell(), UriHandlerPreferencePage_Warning_NotPossible, NLS.bind(UriHandlerPreferencePage_Warning_OtherApp_Description, schemeInformation.information.getHandlerInstanceLocation(), schemeInformation.information.getName())); diff --git a/bundles/org.eclipse.ui.ide/META-INF/MANIFEST.MF b/bundles/org.eclipse.ui.ide/META-INF/MANIFEST.MF index b2d5cbb9302..64b62e924a6 100644 --- a/bundles/org.eclipse.ui.ide/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.ui.ide/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Plugin.name Bundle-SymbolicName: org.eclipse.ui.ide; singleton:=true -Bundle-Version: 3.22.200.qualifier +Bundle-Version: 3.22.300.qualifier Bundle-Activator: org.eclipse.ui.internal.ide.IDEWorkbenchPlugin Bundle-ActivationPolicy: lazy Bundle-Vendor: %Plugin.providerName diff --git a/bundles/org.eclipse.ui.ide/extensions/org/eclipse/ui/actions/RefreshAction.java b/bundles/org.eclipse.ui.ide/extensions/org/eclipse/ui/actions/RefreshAction.java index 9211d4aebfd..94707e8c36d 100644 --- a/bundles/org.eclipse.ui.ide/extensions/org/eclipse/ui/actions/RefreshAction.java +++ b/bundles/org.eclipse.ui.ide/extensions/org/eclipse/ui/actions/RefreshAction.java @@ -16,11 +16,13 @@ package org.eclipse.ui.actions; import java.lang.reflect.InvocationTargetException; +import java.net.URI; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.core.filesystem.IFileInfo; +import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceRuleFactory; @@ -113,11 +115,13 @@ void checkLocationDeleted(IProject project) throws CoreException { if (!project.exists()) { return; } - IFileInfo location = IDEResourceInfoUtils.getFileInfo(project.getLocationURI()); + URI locationURI = project.getLocationURI(); + IFileInfo location = IDEResourceInfoUtils.getFileInfo(locationURI); if (!location.exists()) { + String displayedProjectPath = toDisplayPath(locationURI); String message = NLS.bind( IDEWorkbenchMessages.RefreshAction_locationDeletedMessage, - project.getName(), location.toString()); + project.getName(), displayedProjectPath); final MessageDialog dialog = new MessageDialog(getShell(), IDEWorkbenchMessages.RefreshAction_dialogTitle, // dialog @@ -143,6 +147,11 @@ protected int getShellStyle() { } } + private static String toDisplayPath(URI locationURI) { + IFileStore fileStore = IDEResourceInfoUtils.getFileStore(locationURI); + return fileStore != null ? fileStore.toString() : locationURI.toString(); + } + @Override protected String getOperationMessage() { return IDEWorkbenchMessages.RefreshAction_progressMessage; diff --git a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java index 557b04e50a2..a211b1e91e7 100644 --- a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java +++ b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java @@ -525,7 +525,7 @@ public class IDEWorkbenchMessages extends NLS { public static String WorkbenchPreference_unsupportedEncoding; public static String WorkbenchPreference_encoding_encodingMessage; - public static String UriHandlerPreferencePage_Warning_OtherApp; + public static String UriHandlerPreferencePage_Warning_NotPossible; public static String UriHandlerPreferencePage_Warning_OtherApp_Description; public static String UriHandlerPreferencePage_Warning_OtherApp_Confirmation; @@ -545,6 +545,7 @@ public class IDEWorkbenchMessages extends NLS { public static String UrlHandlerPreferencePage_Error_Reading_Scheme; public static String UrlHandlerPreferencePage_Error_Writing_Scheme; public static String UrlHandlerPreferencePage_UnsupportedOperatingSystem; + public static String UrlHandlerPreferencePage_RegistrationUnsupported; public static String UrlHandlerPreferencePage_LauncherCannotBeDetermined; // ---workspace --- diff --git a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/dialogs/ResourceInfoPage.java b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/dialogs/ResourceInfoPage.java index c9d1dd0adf3..2991c6d5100 100644 --- a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/dialogs/ResourceInfoPage.java +++ b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/dialogs/ResourceInfoPage.java @@ -316,6 +316,7 @@ public void widgetSelected(SelectionEvent e) { Label locationTitle = new Label(basicInfoComposite, SWT.LEFT); locationTitle.setText(LOCATION_TITLE); gd = new GridData(); + gd.verticalAlignment = SWT.TOP; locationTitle.setLayoutData(gd); locationValue = new Text(basicInfoComposite, SWT.WRAP @@ -325,6 +326,8 @@ public void widgetSelected(SelectionEvent e) { locationValue.setText(locationStr); gd = new GridData(); gd.horizontalAlignment = GridData.FILL; + gd.widthHint = convertWidthInCharsToPixels(MAX_VALUE_WIDTH); + gd.grabExcessHorizontalSpace = true; locationValue.setLayoutData(gd); locationValue.setBackground(locationValue.getDisplay() .getSystemColor(SWT.COLOR_WIDGET_BACKGROUND)); @@ -360,6 +363,7 @@ public void widgetSelected(SelectionEvent e) { }); Button pathCopy = new Button(basicInfoComposite, SWT.PUSH); + pathCopy.setLayoutData(gd); pathCopy.setImage(PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_TOOL_COPY)); pathCopy.setToolTipText(PATH_BUTTON_TOOLTIP); pathCopy.addSelectionListener(new SelectionAdapter() { diff --git a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties index c7ba18ed8bd..74ca6f833a9 100644 --- a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties +++ b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties @@ -327,8 +327,8 @@ RefreshAction_toolTip = Refresh RefreshAction_progressMessage = Refreshing... RefreshAction_problemTitle = Refresh Problems RefreshAction_problemMessage = Problems occurred refreshing the selected resources. -RefreshAction_locationDeletedMessage = The location for project ''{0}'' ({1}) has been deleted.\nDelete ''{0}'' from the workspace? -RefreshAction_dialogTitle = Project location has been deleted +RefreshAction_locationDeletedMessage = Directory ''{1}'' has been deleted.\nDelete also project ''{0}'' from the workspace? +RefreshAction_dialogTitle = Project has been deleted on file system SelectWorkingSetAction_text= Select &Working Set... @@ -505,7 +505,7 @@ WorkbenchPreference_unsupportedEncoding = The selected encoding is not supported WorkbenchPreference_encoding_encodingMessage = Byte Order Mark is {0} # --- Link Handler Preference Page --- -UriHandlerPreferencePage_Warning_OtherApp=Action not possible +UriHandlerPreferencePage_Warning_NotPossible=Action not possible UriHandlerPreferencePage_Warning_OtherApp_Description=Another application ({0}) handles the "{1}" scheme.\n\nRemove the "{1}" scheme in the other application first. UriHandlerPreferencePage_Warning_OtherApp_Confirmation=Confirm Handler Change UriHandlerPreferencePage_Warning_OtherApp_Confirmation_Description=The other application ({0}) will no longer handle the "{1}" scheme.\n\nShould this application handle the "{1}" scheme? @@ -522,6 +522,7 @@ UrlHandlerPreferencePage_Column_Handler_Text_Other_Application=Other application UrlHandlerPreferencePage_Error_Reading_Scheme=Error while reading scheme information from operating system UrlHandlerPreferencePage_Error_Writing_Scheme=Error while writing scheme information to operating system UrlHandlerPreferencePage_UnsupportedOperatingSystem=Operating system {0} is not supported. +UrlHandlerPreferencePage_RegistrationUnsupported=Link Handlers cannot be registered as the application is signed. UrlHandlerPreferencePage_LauncherCannotBeDetermined=Eclipse launcher path cannot be determined. # ---Workspace diff --git a/bundles/org.eclipse.ui.workbench.texteditor/META-INF/MANIFEST.MF b/bundles/org.eclipse.ui.workbench.texteditor/META-INF/MANIFEST.MF index f974d911961..557cfe4b1a4 100644 --- a/bundles/org.eclipse.ui.workbench.texteditor/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.ui.workbench.texteditor/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.ui.workbench.texteditor; singleton:=true -Bundle-Version: 3.17.400.qualifier +Bundle-Version: 3.17.500.qualifier Bundle-Activator: org.eclipse.ui.internal.texteditor.TextEditorPlugin Bundle-ActivationPolicy: lazy Bundle-Vendor: %providerName @@ -31,6 +31,7 @@ Require-Bundle: org.eclipse.core.expressions;bundle-version="[3.4.100,4.0.0)", org.eclipse.jface.text;bundle-version="[3.19.0,4.0.0)", org.eclipse.swt;bundle-version="[3.107.0,4.0.0)", - org.eclipse.ui;bundle-version="[3.5.0,4.0.0)" + org.eclipse.ui;bundle-version="[3.5.0,4.0.0)", + org.eclipse.jface.notifications Bundle-RequiredExecutionEnvironment: JavaSE-17 Automatic-Module-Name: org.eclipse.ui.workbench.texteditor diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/case_sensitive.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/case_sensitive.png new file mode 100644 index 00000000000..c2ac8a79a85 Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/case_sensitive.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/case_sensitive@2x.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/case_sensitive@2x.png new file mode 100644 index 00000000000..272bc485ada Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/case_sensitive@2x.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/regex.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/regex.png new file mode 100644 index 00000000000..43b37ca9b54 Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/regex.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/regex@2x.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/regex@2x.png new file mode 100644 index 00000000000..53a95b12a98 Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/regex@2x.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/replace-all@2x.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/replace-all@2x.png new file mode 100644 index 00000000000..0d6a96fcd8a Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/replace-all@2x.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/replace.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/replace.png new file mode 100644 index 00000000000..59d140ab7d0 Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/replace.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/replace@2x.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/replace@2x.png new file mode 100644 index 00000000000..76652066a0f Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/replace@2x.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/replace_all.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/replace_all.png new file mode 100644 index 00000000000..2511faa1f87 Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/replace_all.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_all.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_all.png new file mode 100644 index 00000000000..141fdcf3cc4 Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_all.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_all@2x.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_all@2x.png new file mode 100644 index 00000000000..fd59741c3df Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_all@2x.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_in_area.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_in_area.png new file mode 100644 index 00000000000..e820c06d5cb Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_in_area.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_in_area@2x.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_in_area@2x.png new file mode 100644 index 00000000000..c8c207eeff4 Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_in_area@2x.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_next@2x.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_next@2x.png new file mode 100644 index 00000000000..d46b3a06265 Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_next@2x.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_prev@2x.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_prev@2x.png new file mode 100644 index 00000000000..8e26ea4b4d6 Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/search_prev@2x.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/select_next.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/select_next.png new file mode 100644 index 00000000000..9fcc646d92b Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/select_next.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/select_prev.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/select_prev.png new file mode 100644 index 00000000000..f2e6a039c5d Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/select_prev.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/whole_word.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/whole_word.png new file mode 100644 index 00000000000..54afac2abbe Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/whole_word.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/whole_word@2x.png b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/whole_word@2x.png new file mode 100644 index 00000000000..7b66f352083 Binary files /dev/null and b/bundles/org.eclipse.ui.workbench.texteditor/icons/full/obj16/whole_word@2x.png differ diff --git a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/FindReplaceMessages.java b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/FindReplaceMessages.java index 72f1fe46c4f..1e30529ea75 100644 --- a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/FindReplaceMessages.java +++ b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/FindReplaceMessages.java @@ -60,5 +60,18 @@ private FindReplaceMessages() { public static String FindReplace_SelectAllButton_label; public static String FindReplace_CloseButton_label; - + public static String FindReplaceOverlay_downSearchButton_toolTip; + public static String FindReplaceOverlay_upSearchButton_toolTip; + public static String FindReplaceOverlay_searchAllButton_toolTip; + public static String FindReplaceOverlay_searchInSelectionButton_toolTip; + public static String FindReplaceOverlay_regexSearchButton_toolTip; + public static String FindReplaceOverlay_caseSensitiveButton_toolTip; + public static String FindReplaceOverlay_wholeWordsButton_toolTip; + public static String FindReplaceOverlay_replaceButton_toolTip; + public static String FindReplaceOverlay_replaceAllButton_toolTip; + public static String FindReplaceOverlay_searchBar_message; + public static String FindReplaceOverlay_replaceBar_message; + public static String FindReplaceOverlay_replaceToggle_toolTip; + public static String FindReplaceOverlayFirstTimePopup_FindReplaceOverlayFirstTimePopup_message; + public static String FindReplaceOverlayFirstTimePopup_FindReplaceOverlayFirstTimePopup_title; } diff --git a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/FindReplaceMessages.properties b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/FindReplaceMessages.properties index efd2971b780..da945bbceb3 100644 --- a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/FindReplaceMessages.properties +++ b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/FindReplaceMessages.properties @@ -43,4 +43,20 @@ FindReplace_ReplaceFindButton_label=Replace/Fin&d FindReplace_ReplaceSelectionButton_label=&Replace FindReplace_ReplaceAllButton_label=Replace &All FindReplace_SelectAllButton_label=&Select All -FindReplace_CloseButton_label=Close \ No newline at end of file +FindReplace_CloseButton_label=Close + +# Messages for the "new" Find-Replace-Overlay +FindReplaceOverlay_upSearchButton_toolTip=Search backward (Shift + Enter) +FindReplaceOverlay_downSearchButton_toolTip=Search forward (Enter) +FindReplaceOverlay_searchAllButton_toolTip=Search all (Ctrl + Enter) +FindReplaceOverlay_searchInSelectionButton_toolTip=Only search in selected area (Ctrl + Shift + A) +FindReplaceOverlay_regexSearchButton_toolTip=Match regular expression pattern (Ctrl + Shift + P) +FindReplaceOverlay_caseSensitiveButton_toolTip=Match case (Ctrl + Shift + C) +FindReplaceOverlay_wholeWordsButton_toolTip=Match whole word (Ctrl + Shift + W) +FindReplaceOverlay_replaceButton_toolTip=Replace (Enter) +FindReplaceOverlay_replaceAllButton_toolTip=Replace all (Ctrl + Enter) +FindReplaceOverlay_searchBar_message=Find +FindReplaceOverlay_replaceBar_message=Replace +FindReplaceOverlay_replaceToggle_toolTip=Toggle input for replace (Ctrl + R) +FindReplaceOverlayFirstTimePopup_FindReplaceOverlayFirstTimePopup_message=Find and replace can now be done using an overlay embedded inside the editor. If you prefer the dialog, you can disable the overlay in the preferences or disable it now. +FindReplaceOverlayFirstTimePopup_FindReplaceOverlayFirstTimePopup_title=New Find/Replace Overlay diff --git a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceAction.java b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceAction.java index 5839928d3c8..e253c0902b4 100644 --- a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceAction.java +++ b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceAction.java @@ -21,6 +21,10 @@ import org.eclipse.swt.widgets.Shell; import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; +import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jface.dialogs.IPageChangedListener; import org.eclipse.jface.dialogs.PageChangedEvent; @@ -54,6 +58,39 @@ */ public class FindReplaceAction extends ResourceAction implements IUpdate { + private static final String INSTANCE_SCOPE_NODE_NAME = "org.eclipse.ui.editors"; //$NON-NLS-1$ + + private static final String USE_FIND_REPLACE_OVERLAY = "useFindReplaceOverlay"; //$NON-NLS-1$ + + private static final String FIND_REPLACE_OVERLAY_AT_BOTTOM = "findReplaceOverlayAtBottom"; //$NON-NLS-1$ + + private boolean shouldUseOverlay() { + IEclipsePreferences preferences = InstanceScope.INSTANCE.getNode(INSTANCE_SCOPE_NODE_NAME); + boolean overlayPreference = preferences.getBoolean(USE_FIND_REPLACE_OVERLAY, true); + return overlayPreference && fWorkbenchPart instanceof StatusTextEditor; + } + + private static boolean shouldPositionOverlayOnTop() { + IEclipsePreferences preferences = InstanceScope.INSTANCE.getNode(INSTANCE_SCOPE_NODE_NAME); + return !preferences.getBoolean(FIND_REPLACE_OVERLAY_AT_BOTTOM, false); + } + + private IPreferenceChangeListener overlayDialogPreferenceListener = new IPreferenceChangeListener() { + + @Override + public void preferenceChange(PreferenceChangeEvent event) { + if (overlay == null) { + return; + } + if (event.getKey().equals(USE_FIND_REPLACE_OVERLAY)) { + overlay.close(); + } else if (event.getKey().equals(FIND_REPLACE_OVERLAY_AT_BOTTOM)) { + overlay.setPositionToTop(shouldPositionOverlayOnTop()); + } + } + + }; + /** * Represents the "global" find/replace dialog. It tracks the active * part and retargets the find/replace dialog accordingly. The find/replace @@ -219,7 +256,6 @@ public void checkShell(Shell shell) { } - /** * Listener for disabling the dialog on shell close. *

@@ -246,6 +282,8 @@ public void checkShell(Shell shell) { */ private Shell fShell; + private FindReplaceOverlay overlay; + /** * Creates a new find/replace action for the given workbench part. *

@@ -264,6 +302,8 @@ public FindReplaceAction(ResourceBundle bundle, String prefix, IWorkbenchPart wo Assert.isLegal(workbenchPart != null); fWorkbenchPart= workbenchPart; update(); + + hookDialogPreferenceListener(); } /** @@ -291,6 +331,8 @@ public FindReplaceAction(ResourceBundle bundle, String prefix, Shell shell, IFin fTarget= target; fShell= shell; update(); + + hookDialogPreferenceListener(); } /** @@ -312,13 +354,29 @@ public FindReplaceAction(ResourceBundle bundle, String prefix, IWorkbenchWindow super(bundle, prefix); fWorkbenchWindow= workbenchWindow; update(); + + hookDialogPreferenceListener(); + } + + private void hookDialogPreferenceListener() { + IEclipsePreferences preferences = InstanceScope.INSTANCE.getNode(INSTANCE_SCOPE_NODE_NAME); + preferences.addPreferenceChangeListener(overlayDialogPreferenceListener); } @Override public void run() { - if (fTarget == null) + if (fTarget == null) { return; + } + + if (shouldUseOverlay()) { + showOverlayInEditor(); + } else { + showDialog(); + } + } + private void showDialog() { final FindReplaceDialog dialog; final boolean isEditable; @@ -352,6 +410,24 @@ public void run() { dialog.open(); } + private void showOverlayInEditor() { + if (overlay == null) { + Shell shellToUse = null; + + if (fShell == null) { + shellToUse = fWorkbenchPart.getSite().getShell(); + } else { + shellToUse = fShell; + } + overlay = new FindReplaceOverlay(shellToUse, fWorkbenchPart, fTarget); + + FindReplaceOverlayFirstTimePopup.displayPopupIfNotAlreadyShown(shellToUse); + } + + overlay.setPositionToTop(shouldPositionOverlayOnTop()); + overlay.open(); + } + @Override public void update() { diff --git a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceDialog.java b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceDialog.java index 0e1321a5078..dd9466e0f19 100644 --- a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceDialog.java +++ b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceDialog.java @@ -143,7 +143,7 @@ public void modifyText(ModifyEvent e) { } findReplaceLogic.performIncrementalSearch(getFindString()); - evaluateFindReplaceStatus(false); + evaluateFindReplaceStatus(); updateButtonState(!findReplaceLogic.isActive(SearchOptions.INCREMENTAL)); } @@ -1298,16 +1298,10 @@ private void activateInFindReplaceLogicIf(SearchOptions option, boolean shouldAc } } - private void evaluateFindReplaceStatus() { - evaluateFindReplaceStatus(true); - } - /** * Evaluate the status of the FindReplaceLogic object. - * - * @param allowBeep Whether the evaluation should beep on some codes. */ - private void evaluateFindReplaceStatus(boolean allowBeep) { + private void evaluateFindReplaceStatus() { IFindReplaceStatus status = findReplaceLogic.getStatus(); String dialogMessage = status.accept(new FindReplaceLogicMessageGenerator()); @@ -1317,23 +1311,6 @@ private void evaluateFindReplaceStatus(boolean allowBeep) { } else { fStatusLabel.setForeground(JFaceColors.getErrorText(fStatusLabel.getDisplay())); } - - if (!status.wasSuccessful()) { - tryToBeep(allowBeep); - } - } - - /** - * Tries beeping using the default beep. Will beep if the shell is currently - * usable. - * - * @param allowBeep Whether or not beeps should be allowed. Suppresses all beeps - * if false. - */ - private void tryToBeep(boolean allowBeep) { - if (okToUse(getShell()) && allowBeep) { - getShell().getDisplay().beep(); - } } private String getCurrentSelection() { diff --git a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceOverlay.java b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceOverlay.java new file mode 100644 index 00000000000..5f465d90b56 --- /dev/null +++ b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceOverlay.java @@ -0,0 +1,866 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ +package org.eclipse.ui.texteditor; + +import org.osgi.framework.FrameworkUtil; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGBA; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Scrollable; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.ToolBar; +import org.eclipse.swt.widgets.ToolItem; +import org.eclipse.swt.widgets.Widget; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.jface.resource.JFaceColors; +import org.eclipse.jface.window.Window; + +import org.eclipse.jface.text.IFindReplaceTarget; +import org.eclipse.jface.text.IFindReplaceTargetExtension; + +import org.eclipse.ui.IPartListener; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.internal.findandreplace.FindReplaceLogic; +import org.eclipse.ui.internal.findandreplace.FindReplaceMessages; +import org.eclipse.ui.internal.findandreplace.SearchOptions; +import org.eclipse.ui.internal.findandreplace.status.IFindReplaceStatus; + +/** + * @since 3.17 + */ +class FindReplaceOverlay extends Dialog { + + private static final String REPLACE_BAR_OPEN_DIALOG_SETTING = "replaceBarOpen"; //$NON-NLS-1$ + private static final double WORST_CASE_RATIO_EDITOR_TO_OVERLAY = 0.95; + private static final double BIG_WIDTH_RATIO_EDITOR_TO_OVERLAY = 0.7; + private static final String MINIMAL_WIDTH_TEXT = "THIS TEXT "; //$NON-NLS-1$ + private static final String COMPROMISE_WIDTH_TEXT = "THIS TEXT HAS A REASONABLE"; //$NON-NLS-1$ + private static final String IDEAL_WIDTH_TEXT = "THIS TEXT HAS A REASONABLE LENGTH FOR SEARCHING"; //$NON-NLS-1$ + private FindReplaceLogic findReplaceLogic; + private IWorkbenchPart targetPart; + private boolean overlayOpen; + private boolean replaceBarOpen; + + private Composite container; + private Button replaceToggle; + + private Composite contentGroup; + + private Composite searchContainer; + private Composite searchBarContainer; + private Text searchBar; + private ToolBar searchTools; + + private ToolItem searchInSelectionButton; + private ToolItem wholeWordSearchButton; + private ToolItem caseSensitiveSearchButton; + private ToolItem regexSearchButton; + private ToolItem searchUpButton; + private ToolItem searchDownButton; + private ToolItem searchAllButton; + + private Composite replaceContainer; + private Composite replaceBarContainer; + private Text replaceBar; + private ToolBar replaceTools; + private ToolItem replaceButton; + private ToolItem replaceAllButton; + + private Color backgroundToUse; + private Color normalTextForegroundColor; + private boolean positionAtTop = true; + + public FindReplaceOverlay(Shell parent, IWorkbenchPart part, IFindReplaceTarget target) { + super(parent); + createFindReplaceLogic(target); + + setShellStyle(SWT.MODELESS); + setBlockOnOpen(false); + targetPart = part; + + } + + @Override + protected boolean isResizable() { + return false; + } + + private void createFindReplaceLogic(IFindReplaceTarget target) { + findReplaceLogic = new FindReplaceLogic(); + boolean isTargetEditable = false; + if (target != null) { + isTargetEditable = target.isEditable(); + } + findReplaceLogic.updateTarget(target, isTargetEditable); + findReplaceLogic.activate(SearchOptions.INCREMENTAL); + findReplaceLogic.activate(SearchOptions.GLOBAL); + findReplaceLogic.activate(SearchOptions.WRAP); + findReplaceLogic.activate(SearchOptions.FORWARD); + } + + private void performReplaceAll() { + BusyIndicator.showWhile(getShell() != null ? getShell().getDisplay() : Display.getCurrent(), + () -> findReplaceLogic.performReplaceAll(getFindString(), getReplaceString())); + } + + private void performSelectAll() { + BusyIndicator.showWhile(getShell() != null ? getShell().getDisplay() : Display.getCurrent(), + () -> findReplaceLogic.performSelectAll(getFindString())); + } + + private KeyListener shortcuts = KeyListener.keyPressedAdapter(e -> { + e.doit = false; + if ((e.stateMask & SWT.CTRL) != 0 && (e.keyCode == 'F' || e.keyCode == 'f')) { + close(); + } else if ((e.stateMask & SWT.CTRL) != 0 && (e.keyCode == 'R' || e.keyCode == 'r')) { + if (findReplaceLogic.getTarget().isEditable()) { + toggleReplace(); + } + } else if ((e.stateMask & SWT.CTRL) != 0 && (e.keyCode == 'W' || e.keyCode == 'w')) { + toggleToolItem(wholeWordSearchButton); + } else if ((e.stateMask & SWT.CTRL) != 0 && (e.keyCode == 'P' || e.keyCode == 'p')) { + toggleToolItem(regexSearchButton); + } else if ((e.stateMask & SWT.CTRL) != 0 && (e.keyCode == 'A' || e.keyCode == 'a')) { + toggleToolItem(searchInSelectionButton); + } else if ((e.stateMask & SWT.CTRL) != 0 && (e.keyCode == 'C' || e.keyCode == 'c')) { + toggleToolItem(caseSensitiveSearchButton); + } else if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) { + performEnterAction(e); + } else if (e.keyCode == SWT.ESC) { + close(); + } else { + e.doit = true; + } + }); + + private void performEnterAction(KeyEvent e) { + boolean isShiftPressed = (e.stateMask & SWT.SHIFT) != 0; + boolean isCtrlPressed = (e.stateMask & SWT.CTRL) != 0; + if (okayToUse(replaceBar) && replaceBar.isFocusControl()) { + if (isCtrlPressed) { + performReplaceAllOnEnter(); + } else { + performReplaceOnEnter(); + } + } else { + if (isCtrlPressed) { + performSearchAltOnEnter(); + } else { + performSearchOnEnter(isShiftPressed); + } + } + } + + private void performReplaceAllOnEnter() { + performReplaceAll(); + evaluateFindReplaceStatus(); + } + + private void performReplaceOnEnter() { + performSingleReplace(); + evaluateFindReplaceStatus(); + } + + private void performSearchAltOnEnter() { + performSelectAll(); + evaluateFindReplaceStatus(); + } + + private void performSearchOnEnter(boolean isShiftPressed) { + performSearch(!isShiftPressed); + evaluateFindReplaceStatus(); + } + + private void toggleToolItem(ToolItem toolItem) { + toolItem.setSelection(!toolItem.getSelection()); + toolItem.notifyListeners(SWT.Selection, null); + } + + private ControlListener shellMovementListener = new ControlListener() { + @Override + public void controlMoved(ControlEvent e) { + positionToPart(); + } + + @Override + public void controlResized(ControlEvent e) { + positionToPart(); + } + }; + + private FocusListener overlayFocusListener = FocusListener.focusLostAdapter(e -> { + findReplaceLogic.activate(SearchOptions.GLOBAL); + searchInSelectionButton.setSelection(false); + }); + + private PaintListener widgetMovementListener = __ -> positionToPart(); + + private IPartListener partListener = new IPartListener() { + @Override + public void partActivated(IWorkbenchPart part) { + if (getShell() != null) { + getShell().setVisible(isPartCurrentlyDisplayedInPartSash()); + } + } + + @Override + public void partDeactivated(IWorkbenchPart part) { + // Do nothing + } + + @Override + public void partBroughtToTop(IWorkbenchPart part) { + if (getShell() != null) { + getShell().setVisible(isPartCurrentlyDisplayedInPartSash()); + } + } + + @Override + public void partClosed(IWorkbenchPart part) { + close(); + } + + @Override + public void partOpened(IWorkbenchPart part) { + // Do nothing + } + }; + + private boolean isPartCurrentlyDisplayedInPartSash() { + IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); + + // Check if the targetPart is currently displayed on the active page + boolean isPartDisplayed = false; + + if (activePage != null) { + IWorkbenchPart activePart = activePage.getActivePart(); + if (activePart != null && activePart == targetPart) { + isPartDisplayed = true; + } + } + + return isPartDisplayed; + } + + /** + * Returns the dialog settings object used to share state between several + * find/replace overlays. + * + * @return the dialog settings to be used + */ + private static IDialogSettings getDialogSettings() { + IDialogSettings settings = PlatformUI + .getDialogSettingsProvider(FrameworkUtil.getBundle(FindReplaceOverlay.class)).getDialogSettings(); + return settings; + } + + @Override + public boolean close() { + if (!overlayOpen) { + return true; + } + storeOverlaySettings(); + + findReplaceLogic.activate(SearchOptions.GLOBAL); + overlayOpen = false; + replaceBarOpen = false; + unbindListeners(); + container.dispose(); + return super.close(); + } + + @Override + public int open() { + int returnCode = Window.OK; + if (!overlayOpen) { + returnCode = super.open(); + bindListeners(); + restoreOverlaySettings(); + } + overlayOpen = true; + applyOverlayColors(backgroundToUse, true); + initFindStringFromSelection(); + + getShell().layout(); + positionToPart(); + + searchBar.forceFocus(); + return returnCode; + } + + private void storeOverlaySettings() { + getDialogSettings().put(REPLACE_BAR_OPEN_DIALOG_SETTING, replaceBarOpen); + } + + private void restoreOverlaySettings() { + Boolean shouldOpenReplaceBar = getDialogSettings().getBoolean(REPLACE_BAR_OPEN_DIALOG_SETTING); + if (shouldOpenReplaceBar && replaceToggle != null) { + toggleReplace(); + } + } + + private void applyOverlayColors(Color color, boolean tryToColorReplaceBar) { + searchTools.setBackground(color); + searchInSelectionButton.setBackground(color); + wholeWordSearchButton.setBackground(color); + regexSearchButton.setBackground(color); + caseSensitiveSearchButton.setBackground(color); + searchAllButton.setBackground(color); + searchUpButton.setBackground(color); + searchDownButton.setBackground(color); + + searchBarContainer.setBackground(color); + searchBar.setBackground(color); + searchContainer.setBackground(color); + + if (replaceBarOpen && tryToColorReplaceBar) { + replaceContainer.setBackground(color); + replaceBar.setBackground(color); + replaceBarContainer.setBackground(color); + replaceAllButton.setBackground(color); + replaceButton.setBackground(color); + } + } + + private void unbindListeners() { + getShell().removeFocusListener(overlayFocusListener); + if (targetPart != null && targetPart instanceof StatusTextEditor textEditor) { + Control targetWidget = textEditor.getSourceViewer().getTextWidget(); + if (targetWidget != null) { + targetWidget.getShell().removeControlListener(shellMovementListener); + targetWidget.removePaintListener(widgetMovementListener); + targetPart.getSite().getPage().removePartListener(partListener); + } + } + } + + private void bindListeners() { + getShell().addFocusListener(overlayFocusListener); + if (targetPart instanceof StatusTextEditor textEditor) { + Control targetWidget = textEditor.getSourceViewer().getTextWidget(); + + targetWidget.getShell().addControlListener(shellMovementListener); + targetWidget.addPaintListener(widgetMovementListener); + targetPart.getSite().getPage().addPartListener(partListener); + } + } + + @Override + public Control createContents(Composite parent) { + backgroundToUse = new Color(getShell().getDisplay(), new RGBA(0, 0, 0, 0)); + Control ret = createDialog(parent); + + getShell().layout(); + positionToPart(); + return ret; + } + + private Control createDialog(final Composite parent) { + createMainContainer(parent); + + retrieveBackgroundColor(); + + createFindContainer(); + createSearchBar(); + createSearchTools(); + + container.layout(); + + applyDialogFont(container); + return container; + } + + /** + * HACK: In order to not introduce a hard-coded color, we need to retrieve the + * color of the "SWT.SEARCH"-Text. Since that search-bar has a border, we don't + * want to have it in our own form. Instead, we create such a bar at start-up, + * grab it's color and then immediately dispose of that bar. + */ + private void retrieveBackgroundColor() { + Text textBarForRetrievingTheRightColor = new Text(container, SWT.SINGLE | SWT.SEARCH); + container.layout(); + backgroundToUse = textBarForRetrievingTheRightColor.getBackground(); + normalTextForegroundColor = textBarForRetrievingTheRightColor.getForeground(); + textBarForRetrievingTheRightColor.dispose(); + } + + private void createSearchTools() { + searchTools = new ToolBar(searchContainer, SWT.HORIZONTAL); + GridDataFactory.fillDefaults().grab(false, true).align(GridData.CENTER, GridData.END).applyTo(searchTools); + + createWholeWordsButton(); + createCaseSensitiveButton(); + createRegexSearchButton(); + createAreaSearchButton(); + + @SuppressWarnings("unused") + ToolItem separator = new ToolItem(searchTools, SWT.SEPARATOR); + + searchUpButton = new ToolItem(searchTools, SWT.PUSH); + searchUpButton.setImage(FindReplaceOverlayImages.get(FindReplaceOverlayImages.OBJ_FIND_PREV)); + searchUpButton.setToolTipText(FindReplaceMessages.FindReplaceOverlay_upSearchButton_toolTip); + searchUpButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + performSearch(false); + evaluateFindReplaceStatus(); + })); + searchDownButton = new ToolItem(searchTools, SWT.PUSH); + searchDownButton.setSelection(true); // by default, search down + searchDownButton.setImage(FindReplaceOverlayImages.get(FindReplaceOverlayImages.OBJ_FIND_NEXT)); + searchDownButton.setToolTipText(FindReplaceMessages.FindReplaceOverlay_downSearchButton_toolTip); + searchDownButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + performSearch(true); + evaluateFindReplaceStatus(); + })); + searchAllButton = new ToolItem(searchTools, SWT.PUSH); + searchAllButton.setImage(FindReplaceOverlayImages.get(FindReplaceOverlayImages.OBJ_SEARCH_ALL)); + searchAllButton.setToolTipText(FindReplaceMessages.FindReplaceOverlay_searchAllButton_toolTip); + searchAllButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + performSelectAll(); + evaluateFindReplaceStatus(); + })); + } + + private void createAreaSearchButton() { + searchInSelectionButton = new ToolItem(searchTools, SWT.CHECK); + searchInSelectionButton.setImage(FindReplaceOverlayImages.get(FindReplaceOverlayImages.OBJ_SEARCH_IN_AREA)); + searchInSelectionButton.setToolTipText(FindReplaceMessages.FindReplaceOverlay_searchInSelectionButton_toolTip); + searchInSelectionButton.setSelection(findReplaceLogic.isActive(SearchOptions.WHOLE_WORD)); + searchInSelectionButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + activateInFindReplacerIf(SearchOptions.GLOBAL, !searchInSelectionButton.getSelection()); + updateIncrementalSearch(); + })); + } + + private void createRegexSearchButton() { + regexSearchButton = new ToolItem(searchTools, SWT.CHECK); + regexSearchButton.setImage(FindReplaceOverlayImages.get(FindReplaceOverlayImages.OBJ_FIND_REGEX)); + regexSearchButton.setToolTipText(FindReplaceMessages.FindReplaceOverlay_regexSearchButton_toolTip); + regexSearchButton.setSelection(findReplaceLogic.isActive(SearchOptions.REGEX)); + regexSearchButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + activateInFindReplacerIf(SearchOptions.REGEX, ((ToolItem) e.widget).getSelection()); + wholeWordSearchButton.setEnabled(!findReplaceLogic.isActive(SearchOptions.REGEX)); + updateIncrementalSearch(); + })); + } + + private void createCaseSensitiveButton() { + caseSensitiveSearchButton = new ToolItem(searchTools, SWT.CHECK); + caseSensitiveSearchButton.setImage(FindReplaceOverlayImages.get(FindReplaceOverlayImages.OBJ_CASE_SENSITIVE)); + caseSensitiveSearchButton.setToolTipText(FindReplaceMessages.FindReplaceOverlay_caseSensitiveButton_toolTip); + caseSensitiveSearchButton.setSelection(findReplaceLogic.isActive(SearchOptions.CASE_SENSITIVE)); + caseSensitiveSearchButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + activateInFindReplacerIf(SearchOptions.CASE_SENSITIVE, caseSensitiveSearchButton.getSelection()); + updateIncrementalSearch(); + })); + } + + private void createWholeWordsButton() { + wholeWordSearchButton = new ToolItem(searchTools, SWT.CHECK); + wholeWordSearchButton.setImage(FindReplaceOverlayImages.get(FindReplaceOverlayImages.OBJ_WHOLE_WORD)); + wholeWordSearchButton.setToolTipText(FindReplaceMessages.FindReplaceOverlay_wholeWordsButton_toolTip); + wholeWordSearchButton.setSelection(findReplaceLogic.isActive(SearchOptions.WHOLE_WORD)); + wholeWordSearchButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + activateInFindReplacerIf(SearchOptions.WHOLE_WORD, wholeWordSearchButton.getSelection()); + updateIncrementalSearch(); + })); + } + + private void createReplaceTools() { + Color warningColor = JFaceColors.getErrorText(getShell().getDisplay()); + + replaceTools = new ToolBar(replaceContainer, SWT.HORIZONTAL); + GridDataFactory.fillDefaults().grab(false, true).align(GridData.CENTER, GridData.END).applyTo(replaceTools); + replaceButton = new ToolItem(replaceTools, SWT.PUSH); + replaceButton.setImage(FindReplaceOverlayImages.get(FindReplaceOverlayImages.OBJ_REPLACE)); + replaceButton.setToolTipText(FindReplaceMessages.FindReplaceOverlay_replaceButton_toolTip); + replaceButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + if (getFindString().isEmpty()) { + showUserFeedback(warningColor, true); + return; + } + performSingleReplace(); + evaluateFindReplaceStatus(); + })); + replaceAllButton = new ToolItem(replaceTools, SWT.PUSH); + replaceAllButton.setImage(FindReplaceOverlayImages.get(FindReplaceOverlayImages.OBJ_REPLACE_ALL)); + replaceAllButton.setToolTipText(FindReplaceMessages.FindReplaceOverlay_replaceAllButton_toolTip); + replaceAllButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + if (getFindString().isEmpty()) { + showUserFeedback(warningColor, true); + return; + } + performReplaceAll(); + evaluateFindReplaceStatus(); + })); + } + + private void createSearchBar() { + searchBar = new Text(searchBarContainer, SWT.SINGLE); + GridDataFactory.fillDefaults().grab(true, false).align(GridData.FILL, GridData.END).applyTo(searchBar); + searchBar.forceFocus(); + searchBar.selectAll(); + searchBar.addModifyListener(e -> { + wholeWordSearchButton.setEnabled(findReplaceLogic.isWholeWordSearchAvailable(getFindString())); + + showUserFeedback(normalTextForegroundColor, true); + // don't perform incremental search if we are already on the word. + if (!getFindString().equals(findReplaceLogic.getTarget().getSelectionText())) { + updateIncrementalSearch(); + } + }); + searchBar.addFocusListener(new FocusListener() { + + @Override + public void focusGained(FocusEvent e) { + // we want to update the base-location of where we start incremental search + // to the currently selected position in the target + // when coming back into the dialog + findReplaceLogic.deactivate(SearchOptions.INCREMENTAL); + findReplaceLogic.activate(SearchOptions.INCREMENTAL); + } + + @Override + public void focusLost(FocusEvent e) { + showUserFeedback(normalTextForegroundColor, false); + } + + }); + searchBar.addKeyListener(shortcuts); + searchBar.setMessage(FindReplaceMessages.FindReplaceOverlay_searchBar_message); + } + + private void updateIncrementalSearch() { + // clear the current incrementally searched selection to avoid having an old + // selection left when incrementally searching for an invalid string + if (findReplaceLogic.getTarget() instanceof IFindReplaceTargetExtension targetExtension) { + targetExtension.setSelection(targetExtension.getLineSelection().x, 0); + } + findReplaceLogic.performIncrementalSearch(getFindString()); + evaluateFindReplaceStatus(); + } + + private void createReplaceBar() { + replaceBar = new Text(replaceBarContainer, SWT.SINGLE); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.END).applyTo(replaceBar); + replaceBar.setMessage(FindReplaceMessages.FindReplaceOverlay_replaceBar_message); + replaceBar.addFocusListener(FocusListener.focusLostAdapter(e -> { + replaceBar.setForeground(normalTextForegroundColor); + searchBar.setForeground(normalTextForegroundColor); + })); + replaceBar.addKeyListener(shortcuts); + } + + private void createFindContainer() { + searchContainer = new Composite(contentGroup, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, true).align(GridData.FILL, GridData.FILL).applyTo(searchContainer); + GridLayoutFactory.fillDefaults().numColumns(2).extendedMargins(4, 4, 2, 8).equalWidth(false) + .applyTo(searchContainer); + searchContainer.setBackground(getShell().getDisplay().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW)); + searchBarContainer = new Composite(searchContainer, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, true).align(GridData.FILL, GridData.END).applyTo(searchBarContainer); + GridLayoutFactory.fillDefaults().numColumns(1).applyTo(searchBarContainer); + } + + private void createReplaceContainer() { + replaceContainer = new Composite(contentGroup, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, true).align(GridData.FILL, GridData.FILL).applyTo(replaceContainer); + GridLayoutFactory.fillDefaults().margins(0, 1).numColumns(2).extendedMargins(4, 4, 2, 8).equalWidth(false) + .applyTo(replaceContainer); + replaceContainer.setBackground(getShell().getDisplay().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW)); + replaceBarContainer = new Composite(replaceContainer, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, true).align(GridData.FILL, GridData.END).applyTo(replaceBarContainer); + GridLayoutFactory.fillDefaults().numColumns(1).equalWidth(false).applyTo(replaceBarContainer); + } + + private void createMainContainer(final Composite parent) { + container = new Composite(parent, SWT.NONE); + GridLayoutFactory.fillDefaults().numColumns(2).equalWidth(false).margins(2, 2).spacing(2, 0).applyTo(container); + GridDataFactory.fillDefaults().grab(true, true).align(GridData.FILL, GridData.FILL).applyTo(container); + + if (findReplaceLogic.getTarget().isEditable()) { + createReplaceToggle(); + } + + contentGroup = new Composite(container, SWT.NULL); + GridLayoutFactory.fillDefaults().numColumns(1).equalWidth(false).spacing(2, 3).applyTo(contentGroup); + GridDataFactory.fillDefaults().grab(true, true).align(GridData.FILL, GridData.FILL).applyTo(contentGroup); + } + + private void createReplaceToggle() { + replaceToggle = new Button(container, SWT.PUSH); + GridDataFactory.fillDefaults().grab(false, true).align(GridData.BEGINNING, GridData.FILL) + .applyTo(replaceToggle); + replaceToggle.setToolTipText(FindReplaceMessages.FindReplaceOverlay_replaceToggle_toolTip); + replaceToggle.setText("⯈"); //$NON-NLS-1$ + replaceToggle.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> toggleReplace())); + } + + private void toggleReplace() { + if (!replaceBarOpen) { + createReplaceDialog(); + replaceToggle.setText("⯅"); //$NON-NLS-1$ + } else { + hideReplace(); + replaceToggle.setText("⯈"); //$NON-NLS-1$ + } + replaceToggle.setSelection(false); // We don't want the button to look "locked in", so don't + // use it's selectionState + } + + private void hideReplace() { + if (!replaceBarOpen) { + return; + } + searchBar.forceFocus(); + replaceBarOpen = false; + replaceContainer.dispose(); + positionToPart(); + } + + private void createReplaceDialog() { + if (replaceBarOpen) { + return; + } + replaceBarOpen = true; + createReplaceContainer(); + createReplaceBar(); + createReplaceTools(); + positionToPart(); + applyOverlayColors(backgroundToUse, true); + replaceBar.forceFocus(); + } + + private void enableSearchTools(boolean enable) { + ((GridData) searchTools.getLayoutData()).exclude = !enable; + searchTools.setVisible(enable); + + if (enable) { + ((GridLayout) searchTools.getParent().getLayout()).numColumns = 2; + } else { + ((GridLayout) searchTools.getParent().getLayout()).numColumns = 1; + } + } + + private void enableReplaceToggle(boolean enable) { + if (!okayToUse(replaceToggle)) { + return; + } + ((GridData) replaceToggle.getLayoutData()).exclude = !enable; + replaceToggle.setVisible(enable); + } + + private void enableReplaceTools(boolean enable) { + if (!okayToUse(replaceTools)) { + return; + } + ((GridData) replaceTools.getLayoutData()).exclude = !enable; + replaceTools.setVisible(enable); + + if (enable) { + ((GridLayout) replaceTools.getParent().getLayout()).numColumns = 2; + } else { + ((GridLayout) replaceTools.getParent().getLayout()).numColumns = 1; + } + } + + private int getIdealDialogWidth(Rectangle targetBounds) { + int replaceToggleWidth = 0; + if (okayToUse(replaceToggle)) { + replaceToggleWidth = replaceToggle.getBounds().width; + } + int toolBarWidth = searchTools.getSize().x; + GC gc = new GC(searchBar); + gc.setFont(searchBar.getFont()); + int idealWidth = gc.stringExtent(IDEAL_WIDTH_TEXT).x; // $NON-NLS-1$ + int idealCompromiseWidth = gc.stringExtent(COMPROMISE_WIDTH_TEXT).x; // $NON-NLS-1$ + int worstCompromiseWidth = gc.stringExtent(MINIMAL_WIDTH_TEXT).x; // $NON-NLS-1$ + gc.dispose(); + + int newWidth = idealWidth + toolBarWidth + replaceToggleWidth; + if (newWidth > targetBounds.width * BIG_WIDTH_RATIO_EDITOR_TO_OVERLAY) { + newWidth = (int) (targetBounds.width * BIG_WIDTH_RATIO_EDITOR_TO_OVERLAY); + enableSearchTools(true); + enableReplaceTools(true); + enableReplaceToggle(true); + } + if (newWidth < idealCompromiseWidth + toolBarWidth) { + enableSearchTools(false); + enableReplaceTools(false); + enableReplaceToggle(true); + } + if (newWidth < worstCompromiseWidth + toolBarWidth) { + newWidth = (int) (targetBounds.width * WORST_CASE_RATIO_EDITOR_TO_OVERLAY); + enableReplaceToggle(false); + enableSearchTools(false); + enableReplaceTools(false); + } + return newWidth; + } + + private Point getNewPosition(Widget targetTextWidget, Point targetOrigin, Rectangle targetBounds, + Point expectedSize) { + Point verticalScrollBarSize = ((Scrollable) targetTextWidget).getVerticalBar().getSize(); + Point horizontalScrollBarSize = ((Scrollable) targetTextWidget).getHorizontalBar().getSize(); + + int newX = targetOrigin.x + targetBounds.width - expectedSize.x - verticalScrollBarSize.x + - ((StyledText) targetTextWidget).getRightMargin(); + int newY = targetOrigin.y; + if (!positionAtTop) { + newY += targetBounds.height - expectedSize.y - horizontalScrollBarSize.y; + } + return new Point(newX, newY); + } + + /** + * When making the text-bar 100% small and then regrowing it, we want the text + * to start at the first character again. + */ + private void repositionTextSelection() { + if (okayToUse(searchBar) && !searchBar.isFocusControl()) { + searchBar.setSelection(0, 0); + } + if (okayToUse(replaceBar) && !replaceBar.isFocusControl()) { + replaceBar.setSelection(0, 0); + } + } + + private void positionToPart() { + getShell().requestLayout(); + if (!(targetPart instanceof StatusTextEditor)) { + return; + } + + StatusTextEditor textEditor = (StatusTextEditor) targetPart; + Control targetWidget = textEditor.getSourceViewer().getTextWidget(); + if (!okayToUse(targetWidget)) { + this.close(); + return; + } + + Point targetOrigin = targetWidget.toDisplay(0, 0); + Rectangle targetBounds = targetWidget.getBounds(); + + int newWidth = getIdealDialogWidth(targetBounds); + int newHeight = container.computeSize(SWT.DEFAULT, SWT.DEFAULT).y; + + Point newPosition = getNewPosition(targetWidget, targetOrigin, targetBounds, new Point(newWidth, newHeight)); + + getShell().setSize(new Point(newWidth, newHeight)); + getShell().setLocation(newPosition); + getShell().layout(true); + + repositionTextSelection(); + } + + private String getFindString() { + return searchBar.getText(); + } + + private String getReplaceString() { + if (!okayToUse(replaceBar)) { + return ""; //$NON-NLS-1$ + } + return replaceBar.getText(); + + } + + private void performSingleReplace() { + findReplaceLogic.performReplaceAndFind(getFindString(), getReplaceString()); + } + + private void performSearch(boolean forward) { + boolean oldForwardSearchSetting = findReplaceLogic.isActive(SearchOptions.FORWARD); + activateInFindReplacerIf(SearchOptions.FORWARD, forward); + findReplaceLogic.deactivate(SearchOptions.INCREMENTAL); + findReplaceLogic.performSearch(getFindString()); + activateInFindReplacerIf(SearchOptions.FORWARD, oldForwardSearchSetting); + findReplaceLogic.activate(SearchOptions.INCREMENTAL); + } + + private void initFindStringFromSelection() { + String initText = findReplaceLogic.getTarget().getSelectionText(); + if (initText.isEmpty()) { + return; + } + if (initText.contains(System.lineSeparator())) { // $NON-NLS-1$ + findReplaceLogic.deactivate(SearchOptions.GLOBAL); + searchInSelectionButton.setSelection(true); + } else { + searchBar.setText(initText); + searchBar.setSelection(0, initText.length()); + } + } + + private void evaluateFindReplaceStatus() { + Color warningColor = JFaceColors.getErrorText(getShell().getDisplay()); + IFindReplaceStatus status = findReplaceLogic.getStatus(); + + if (!status.wasSuccessful()) { + boolean colorReplaceBar = okayToUse(replaceBar) && replaceBar.isFocusControl(); + showUserFeedback(warningColor, colorReplaceBar); + } else { + showUserFeedback(normalTextForegroundColor, false); + } + } + + private void showUserFeedback(Color feedbackColor, boolean colorReplaceBar) { + searchBar.setForeground(feedbackColor); + if (colorReplaceBar && okayToUse(replaceBar)) { + replaceBar.setForeground(feedbackColor); + } + } + + private void activateInFindReplacerIf(SearchOptions option, boolean shouldActivate) { + if (shouldActivate) { + findReplaceLogic.activate(option); + } else { + findReplaceLogic.deactivate(option); + } + } + + private static boolean okayToUse(Widget widget) { + return widget != null && !widget.isDisposed(); + } + + public void setPositionToTop(boolean shouldPositionOverlayOnTop) { + positionAtTop = shouldPositionOverlayOnTop; + } +} \ No newline at end of file diff --git a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceOverlayFirstTimePopup.java b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceOverlayFirstTimePopup.java new file mode 100644 index 00000000000..e274a7db02f --- /dev/null +++ b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceOverlayFirstTimePopup.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ +package org.eclipse.ui.texteditor; + +import java.time.Duration; +import java.util.Objects; + +import org.osgi.framework.FrameworkUtil; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.InstanceScope; + +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.notifications.NotificationPopup; + +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.internal.findandreplace.FindReplaceMessages; + +/** + * Utility class to display a popup the first time the FindReplaceOverlay is + * shown, informing the user about the new functionality. This class will track + * whether the popup was already shown and will only show the Overlay on the + * first time the popup was shown. + */ +class FindReplaceOverlayFirstTimePopup { + + private FindReplaceOverlayFirstTimePopup() { + } + + private static final String PREFERENCE_NODE_NAME = "org.eclipse.ui.editors"; //$NON-NLS-1$ + private static final String SETTING_POPUP_WAS_SHOWN_BEFORE = "hasShownOverlayPopupBefore"; //$NON-NLS-1$ + /** + * How long to wait until the pop up should vanish in Ms. + */ + private static final Duration POPUP_VANISH_TIME = Duration.ofSeconds(6); + private static final String USE_FIND_REPLACE_OVERLAY = "useFindReplaceOverlay"; //$NON-NLS-1$ + + /** + * Returns the dialog settings object used to remember whether the popup was + * already shown or not. + * + * @return the dialog settings to be used + */ + private static IDialogSettings getDialogSettings() { + IDialogSettings settings = PlatformUI + .getDialogSettingsProvider(FrameworkUtil.getBundle(FindReplaceOverlayFirstTimePopup.class)) + .getDialogSettings(); + return settings; + } + + private static void disableUseOverlayPreference() { + IEclipsePreferences preferences = InstanceScope.INSTANCE.getNode(PREFERENCE_NODE_NAME); // $NON-NLS-1$ + preferences.putBoolean(USE_FIND_REPLACE_OVERLAY, false); + } + + /** + * Displays a popup indicating that instead of the FindReplaceDialog, the + * FindReplaceOverlay is currently being used. Only displays the popup on the + * first time use of FindReplaceOverlay. + * + * The popup is bound to the bottom right corner of the principal computer + * Monitor. + * + * @param shellToUse the shell to bind the popup to + */ + public static void displayPopupIfNotAlreadyShown(Shell shellToUse) { + IDialogSettings settings = getDialogSettings(); + + if (!settings.getBoolean(SETTING_POPUP_WAS_SHOWN_BEFORE)) { + settings.put(SETTING_POPUP_WAS_SHOWN_BEFORE, true); + + Display displayToUse = Objects.nonNull(shellToUse) ? shellToUse.getDisplay() : Display.getDefault(); + + NotificationPopup.forDisplay(displayToUse).content(t -> createFirstTimeNotification(t)) + .title(FindReplaceMessages.FindReplaceOverlayFirstTimePopup_FindReplaceOverlayFirstTimePopup_title, + true) + .delay(POPUP_VANISH_TIME.toMillis()).open(); + } + + } + + private static Control createFirstTimeNotification(Composite composite) { + Link messageBody = new Link(composite, SWT.WRAP); + + messageBody + .setText(FindReplaceMessages.FindReplaceOverlayFirstTimePopup_FindReplaceOverlayFirstTimePopup_message); + messageBody.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1)); + messageBody.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + disableUseOverlayPreference(); + composite.getShell().close(); + })); + + return messageBody; + } +} diff --git a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceOverlayImages.java b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceOverlayImages.java new file mode 100644 index 00000000000..2017e13f966 --- /dev/null +++ b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceOverlayImages.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ +package org.eclipse.ui.texteditor; + +import java.net.URL; + +import org.osgi.framework.Bundle; + +import org.eclipse.swt.graphics.Image; + +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Platform; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; + +import org.eclipse.ui.internal.texteditor.TextEditorPlugin; + +/** + * Provides Icons for the editor overlay used for performing + * find/replace-operations. + */ +class FindReplaceOverlayImages { + static final String PREFIX_OBJ = TextEditorPlugin.PLUGIN_ID + ".obj."; //$NON-NLS-1$ + + static final String OBJ_FIND_NEXT = PREFIX_OBJ + "select_next.png"; //$NON-NLS-1$ + + static final String OBJ_FIND_PREV = PREFIX_OBJ + "select_prev.png"; //$NON-NLS-1$ + + static final String OBJ_FIND_REGEX = PREFIX_OBJ + "regex_gear.gif"; //$NON-NLS-1$ + + static final String OBJ_REPLACE = PREFIX_OBJ + "replace.png"; //$NON-NLS-1$ + + static final String OBJ_REPLACE_ALL = PREFIX_OBJ + "replace_all.png"; //$NON-NLS-1$ + + static final String OBJ_WHOLE_WORD = PREFIX_OBJ + "whole_word.png"; //$NON-NLS-1$ + + static final String OBJ_CASE_SENSITIVE = PREFIX_OBJ + "case_sensitive.png"; //$NON-NLS-1$ + + static final String OBJ_SEARCH_ALL = PREFIX_OBJ + "search_all.png"; //$NON-NLS-1$ + + static final String OBJ_SEARCH_IN_AREA = PREFIX_OBJ + "search_in_selection.png"; //$NON-NLS-1$ + + /** + * The image registry containing {@link Image images}. + */ + private static ImageRegistry fgImageRegistry; + + private static String ICONS_PATH = "$nl$/icons/full/"; //$NON-NLS-1$ + + private final static String OBJ = ICONS_PATH + "obj16/"; //$NON-NLS-1$ + + /** + * Declare all images + */ + private static void declareImages() { + declareRegistryImage(OBJ_FIND_NEXT, OBJ + "select_next.png"); //$NON-NLS-1$ + declareRegistryImage(OBJ_FIND_PREV, OBJ + "select_prev.png"); //$NON-NLS-1$ + declareRegistryImage(OBJ_FIND_REGEX, OBJ + "regex.png"); //$NON-NLS-1$ + declareRegistryImage(OBJ_REPLACE_ALL, OBJ + "replace_all.png"); //$NON-NLS-1$ + declareRegistryImage(OBJ_REPLACE, OBJ + "replace.png"); //$NON-NLS-1$ + declareRegistryImage(OBJ_WHOLE_WORD, OBJ + "whole_word.png"); //$NON-NLS-1$ + declareRegistryImage(OBJ_CASE_SENSITIVE, OBJ + "case_sensitive.png"); //$NON-NLS-1$ + declareRegistryImage(OBJ_SEARCH_ALL, OBJ + "search_all.png"); //$NON-NLS-1$ + declareRegistryImage(OBJ_SEARCH_IN_AREA, OBJ + "search_in_area.png"); //$NON-NLS-1$ + } + + /** + * Declare an Image in the registry table. + * + * @param key the key to use when registering the image + * @param path the path where the image can be found. This path is relative to + * where this plugin class is found (i.e. typically the packages + * directory) + */ + private final static void declareRegistryImage(String key, String path) { + ImageDescriptor desc = ImageDescriptor.getMissingImageDescriptor(); + Bundle bundle = Platform.getBundle(TextEditorPlugin.PLUGIN_ID); + URL url = null; + if (bundle != null) { + url = FileLocator.find(bundle, IPath.fromOSString(path), null); + desc = ImageDescriptor.createFromURL(url); + } + fgImageRegistry.put(key, desc); + } + + /** + * Returns the ImageRegistry. + * + * @return image registry + */ + public static ImageRegistry getImageRegistry() { + if (fgImageRegistry == null) { + initializeImageRegistry(); + } + return fgImageRegistry; + } + + /** + * Initialize the image registry by declaring all of the required graphics. This + * involves creating JFace image descriptors describing how to create/find the + * image should it be needed. The image is not actually allocated until + * requested. + * + * Prefix conventions Wizard Banners WIZBAN_ Preference Banners PREF_BAN_ + * Property Page Banners PROPBAN_ Color toolbar CTOOL_ Enable toolbar ETOOL_ + * Disable toolbar DTOOL_ Local enabled toolbar ELCL_ Local Disable toolbar + * DLCL_ Object large OBJL_ Object small OBJS_ View VIEW_ Product images PROD_ + * Misc images MISC_ + * + * Where are the images? The images (typically pngs) are found in the same + * location as this plugin class. This may mean the same package directory as + * the package holding this class. The images are declared using this.getClass() + * to ensure they are looked up via this plugin class. + * + * @return the image registry + * @see org.eclipse.jface.resource.ImageRegistry + */ + public static ImageRegistry initializeImageRegistry() { + fgImageRegistry = TextEditorPlugin.getDefault().getImageRegistry(); + declareImages(); + return fgImageRegistry; + } + + /** + * Returns the image managed under the given key in this registry. + * + * @param key the image's key + * @return the image managed under the given key + */ + public static Image get(String key) { + return getImageRegistry().get(key); + } + + /** + * Returns the image descriptor for the given key in this registry. + * + * @param key the image's key + * @return the image descriptor for the given key + */ + public static ImageDescriptor getDescriptor(String key) { + return getImageRegistry().getDescriptor(key); + } +} diff --git a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/PropertyDialog.java b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/PropertyDialog.java index 0bca15c1800..538e049600c 100644 --- a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/PropertyDialog.java +++ b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/PropertyDialog.java @@ -17,8 +17,6 @@ import java.util.Iterator; import org.eclipse.core.runtime.Adapters; -import org.eclipse.jface.dialogs.DialogSettings; -import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPreferenceNode; import org.eclipse.jface.preference.PreferenceManager; @@ -31,10 +29,8 @@ import org.eclipse.ui.PlatformUI; import org.eclipse.ui.internal.IWorkbenchHelpContextIds; import org.eclipse.ui.internal.WorkbenchMessages; -import org.eclipse.ui.internal.WorkbenchPlugin; import org.eclipse.ui.model.IContributionService; import org.eclipse.ui.model.IWorkbenchAdapter; -import org.osgi.framework.Bundle; /** * This dialog is created and shown when 'Properties' action is performed while @@ -184,11 +180,4 @@ protected String getContributionType() { return IContributionService.TYPE_PROPERTY; } - @Override - protected IDialogSettings getDialogBoundsSettings() { - Bundle bundle = WorkbenchPlugin.getDefault().getBundle(); - IDialogSettings settings = PlatformUI.getDialogSettingsProvider(bundle).getDialogSettings(); - String name = getClass().getSimpleName() + ".dialogBounds"; //$NON-NLS-1$ - return DialogSettings.getOrCreateSection(settings, name); - } } diff --git a/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF b/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF index e5c807580b1..0d745f6d08b 100644 --- a/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.ui.workbench; singleton:=true -Bundle-Version: 3.132.0.qualifier +Bundle-Version: 3.132.100.qualifier Bundle-Activator: org.eclipse.ui.internal.WorkbenchPlugin Bundle-ActivationPolicy: lazy Bundle-Vendor: %providerName diff --git a/bundles/org.eclipse.urischeme/META-INF/MANIFEST.MF b/bundles/org.eclipse.urischeme/META-INF/MANIFEST.MF index 5beac56f5a8..cb9d7e714a3 100644 --- a/bundles/org.eclipse.urischeme/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.urischeme/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Plugin.name Bundle-SymbolicName: org.eclipse.urischeme;singleton:=true -Bundle-Version: 1.3.300.qualifier +Bundle-Version: 1.3.400.qualifier Automatic-Module-Name: org.eclipse.urischeme Bundle-RequiredExecutionEnvironment: JavaSE-17 Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.8.0,4.0.0)", diff --git a/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/AutoRegisterSchemeHandlersJob.java b/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/AutoRegisterSchemeHandlersJob.java index eede649fde8..079b20bcbbc 100644 --- a/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/AutoRegisterSchemeHandlersJob.java +++ b/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/AutoRegisterSchemeHandlersJob.java @@ -79,8 +79,7 @@ protected IStatus run(IProgressMonitor monitor) { return Status.OK_STATUS; } - IOperatingSystemRegistration osRegistration = testOsRegistration != null ? testOsRegistration - : IOperatingSystemRegistration.getInstance(); + IOperatingSystemRegistration osRegistration = getOsRegistration(); try { toProcessSchemes = osRegistration.getSchemesInformation(toProcessSchemes).stream() // .filter(scheme -> !scheme.schemeIsHandledByOther()) // @@ -103,9 +102,16 @@ protected IStatus run(IProgressMonitor monitor) { return Status.OK_STATUS; } + private IOperatingSystemRegistration getOsRegistration() { + IOperatingSystemRegistration osRegistration = testOsRegistration != null ? testOsRegistration + : IOperatingSystemRegistration.getInstance(); + return osRegistration; + } + @Override public boolean shouldSchedule() { + IOperatingSystemRegistration osRegistration = getOsRegistration(); return !(alreadyTriggered || Platform.getPreferencesService().getBoolean(UriSchemeExtensionReader.PLUGIN_ID, - SKIP_PREFERENCE, false, null)); + SKIP_PREFERENCE, false, null) || osRegistration.supportsRegistration()); } } diff --git a/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/IOperatingSystemRegistration.java b/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/IOperatingSystemRegistration.java index 1deaabdc55c..195d3df776d 100644 --- a/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/IOperatingSystemRegistration.java +++ b/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/IOperatingSystemRegistration.java @@ -83,4 +83,13 @@ static IOperatingSystemRegistration getInstance() { */ boolean canOverwriteOtherApplicationsRegistration(); + /** + * This method returns if the current operating system allows to register uri + * schemes at all. + * + * @return true if the registration of uri schemes is supported - + * false otherwise. + */ + boolean supportsRegistration(); + } diff --git a/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/internal/registration/RegistrationLinux.java b/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/internal/registration/RegistrationLinux.java index 73eef786228..3396a58c680 100644 --- a/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/internal/registration/RegistrationLinux.java +++ b/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/internal/registration/RegistrationLinux.java @@ -57,6 +57,9 @@ public RegistrationLinux(IFileProvider fileProvider, IProcessExecutor processExe @Override public void handleSchemes(Collection toAdd, Collection toRemove) throws Exception { + if (!supportsRegistration()) { + return; + } String desktopFileName = getDesktopFileName(); changeDesktopFile(toAdd, toRemove, PATH_TO_LOCAL_SHARE_APPS + desktopFileName); @@ -180,4 +183,9 @@ private String getEclipseHomeLocation() { public boolean canOverwriteOtherApplicationsRegistration() { return false; } + + @Override + public boolean supportsRegistration() { + return true; + } } diff --git a/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/internal/registration/RegistrationMacOsX.java b/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/internal/registration/RegistrationMacOsX.java index f0abba08479..6b9f70475c8 100644 --- a/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/internal/registration/RegistrationMacOsX.java +++ b/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/internal/registration/RegistrationMacOsX.java @@ -131,6 +131,9 @@ private void registerAppWithLsregister(String pathToEclipseApp) throws Exception } private void changePlistFile(Collection toAdd, Collection toRemove, String pathToEclipseApp) { + if (!supportsRegistration()) { + return; + } String plistPath = pathToEclipseApp + PLIST_PATH_SUFFIX; PlistFileWriter writer = getPlistFileWriter(plistPath); @@ -186,4 +189,13 @@ private String getPathToEclipseApp() { public boolean canOverwriteOtherApplicationsRegistration() { return false; } + + @Override + public boolean supportsRegistration() { + // if the application is signed we cannot register URI schemes because this + // would break the signature of the applications + // applications with broken signature cannot be executed any more (at least on + // newer macOS versions). + return !fileProvider.fileExists(getPathToEclipseApp() + "/Contents/_CodeSignature"); //$NON-NLS-1$ + } } \ No newline at end of file diff --git a/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/internal/registration/RegistrationWindows.java b/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/internal/registration/RegistrationWindows.java index a7b87f42e3b..9f26b153763 100644 --- a/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/internal/registration/RegistrationWindows.java +++ b/bundles/org.eclipse.urischeme/src/org/eclipse/urischeme/internal/registration/RegistrationWindows.java @@ -56,6 +56,9 @@ public RegistrationWindows(IRegistryWriter registryWriter, IFileProvider filePro @Override public void handleSchemes(Collection toAdd, Collection toRemove) throws Exception { + if (!supportsRegistration()) { + return; + } String eclipseLauncher = getEclipseLauncher(); if (eclipseLauncher != null) { for (IScheme scheme : toAdd) { @@ -117,6 +120,11 @@ public boolean canOverwriteOtherApplicationsRegistration() { return true; } + @Override + public boolean supportsRegistration() { + return true; + } + private String getLauncherFromLauncherProperty() { String launcher = System.getProperty("eclipse.launcher"); //$NON-NLS-1$ if (launcher != null && this.fileProvider.fileExists(launcher) && !fileProvider.isDirectory(launcher)) { diff --git a/examples/pom.xml b/examples/pom.xml index ee294e7dd2b..48748d474e8 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -14,7 +14,7 @@ org.eclipse.platform eclipse.platform.ui - 4.32.0-SNAPSHOT + 4.33.0-SNAPSHOT eclipse.platform.ui.examples pom diff --git a/features/org.eclipse.e4.rcp/feature.xml b/features/org.eclipse.e4.rcp/feature.xml index 80fa29aae44..d7bd0a8289f 100644 --- a/features/org.eclipse.e4.rcp/feature.xml +++ b/features/org.eclipse.e4.rcp/feature.xml @@ -2,7 +2,7 @@ diff --git a/pom.xml b/pom.xml index 91526f609eb..c87923b468f 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ org.eclipse eclipse-platform-parent - 4.32.0-SNAPSHOT + 4.33.0-SNAPSHOT ../eclipse-platform-parent diff --git a/tests/org.eclipse.tests.urischeme/META-INF/MANIFEST.MF b/tests/org.eclipse.tests.urischeme/META-INF/MANIFEST.MF index cc6bf2f910c..fae263234dd 100644 --- a/tests/org.eclipse.tests.urischeme/META-INF/MANIFEST.MF +++ b/tests/org.eclipse.tests.urischeme/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Plugin.name Bundle-Vendor: %Plugin.Providername Bundle-SymbolicName: org.eclipse.tests.urischeme -Bundle-Version: 1.2.400.qualifier +Bundle-Version: 1.2.500.qualifier Bundle-Localization: plugin Fragment-Host: org.eclipse.urischeme;bundle-version="1.1.100" Automatic-Module-Name: org.eclipse.urischeme.tests diff --git a/tests/org.eclipse.tests.urischeme/src/org/eclipse/urischeme/TestUnitAutoRegisterSchemeHandlersJob.java b/tests/org.eclipse.tests.urischeme/src/org/eclipse/urischeme/TestUnitAutoRegisterSchemeHandlersJob.java index bcf6930618a..2b2079b1376 100644 --- a/tests/org.eclipse.tests.urischeme/src/org/eclipse/urischeme/TestUnitAutoRegisterSchemeHandlersJob.java +++ b/tests/org.eclipse.tests.urischeme/src/org/eclipse/urischeme/TestUnitAutoRegisterSchemeHandlersJob.java @@ -40,10 +40,13 @@ public class TestUnitAutoRegisterSchemeHandlersJob { private final static IScheme helloScheme = new Scheme(HELLO_URI_SCHEME, "helloScheme"); private static final String HELLO1_URI_SCHEME = "hello1"; private final static IScheme hello1Scheme = new Scheme(HELLO1_URI_SCHEME, "hello1Scheme"); + private static final boolean SUPPORTS_REGISTRATION = true; + private static final boolean DOESNT_SUPPORT_REGISTRATION = false; @Test public void noSchemeDoesNothing() throws Exception { - AutoRegisterSchemeHandlersJob job = createJob(Collections.emptyList(), "", Collections.emptyList()); + AutoRegisterSchemeHandlersJob job = createJob(Collections.emptyList(), "", Collections.emptyList(), + SUPPORTS_REGISTRATION); job.run(new NullProgressMonitor()); @@ -57,7 +60,7 @@ public void noSchemeDoesNothing() throws Exception { @Test public void noNewSchemeDoesNothing() throws Exception { AutoRegisterSchemeHandlersJob job = createJob(Arrays.asList(helloScheme), HELLO_URI_SCHEME, - Collections.emptyList()); + Collections.emptyList(), SUPPORTS_REGISTRATION); job.run(new NullProgressMonitor()); @@ -78,7 +81,7 @@ public void newSchemeRegistersNewScheme() throws Exception { List schemeInfos = Arrays.asList(hello1SchemeInfo); AutoRegisterSchemeHandlersJob job = createJob(Arrays.asList(helloScheme, hello1Scheme), HELLO_URI_SCHEME, - schemeInfos); + schemeInfos, SUPPORTS_REGISTRATION); job.run(new NullProgressMonitor()); @@ -100,7 +103,7 @@ public void newAlreadyRegisteredSchemeDoesNoting() throws Exception { List schemeInfos = new ArrayList<>(); AutoRegisterSchemeHandlersJob job = createJob(Arrays.asList(helloScheme, hello1Scheme), HELLO_URI_SCHEME, - schemeInfos); + schemeInfos, SUPPORTS_REGISTRATION); job.run(new NullProgressMonitor()); @@ -120,7 +123,7 @@ public void unregisteredSchemeThatWasAutoregisterdOnceDoesNotAutoregisterAgain() List schemeInfos = Arrays.asList(helloSchemeInfo, hello1SchemeInfo); AutoRegisterSchemeHandlersJob job = createJob(Arrays.asList(helloScheme, hello1Scheme), - HELLO_URI_SCHEME + "," + HELLO1_URI_SCHEME, schemeInfos); + HELLO_URI_SCHEME + "," + HELLO1_URI_SCHEME, schemeInfos, SUPPORTS_REGISTRATION); job.run(new NullProgressMonitor()); @@ -130,10 +133,17 @@ public void unregisteredSchemeThatWasAutoregisterdOnceDoesNotAutoregisterAgain() assertNull("No schemes should be un-registered", osRegistration.removedSchemes); } + @Test + public void registrationOnUnsupportedRegistrationDoesNothing() throws Exception { + AutoRegisterSchemeHandlersJob job = createJob(new ArrayList<>(), "dontCare", new ArrayList<>(), + DOESNT_SUPPORT_REGISTRATION); + assertFalse("Job should not run on OSes that don't support registration", job.shouldSchedule()); + } + private AutoRegisterSchemeHandlersJob createJob(Collection installedSchemes, - String alreadyProcessedSchemes, List registedSchemes) { + String alreadyProcessedSchemes, List registeredSchemes, boolean supportsRegistration) { ExtensionReaderStub extensionReader = new ExtensionReaderStub(installedSchemes); - osRegistration = new OperatingSystemRegistrationMock(registedSchemes); + osRegistration = new OperatingSystemRegistrationMock(registeredSchemes, supportsRegistration); preferenceNode = new PreferenceMock(); preferenceNode.currentValue = alreadyProcessedSchemes; @@ -194,29 +204,24 @@ public IUriSchemeHandler getHandlerFromExtensionPoint(String uriScheme) { private static final class OperatingSystemRegistrationMock implements IOperatingSystemRegistration { private final List schemeInformations; - public Exception schemeInformationReadException = null; - public Exception schemeInformationRegisterException = null; public Collection addedSchemes = null; public Collection removedSchemes = null; + public boolean supportsRegistration; - public OperatingSystemRegistrationMock(List schemeInformations) { + public OperatingSystemRegistrationMock(List schemeInformations, + boolean supportsRegistration) { this.schemeInformations = schemeInformations; + this.supportsRegistration = supportsRegistration; } @Override public void handleSchemes(Collection toAdd, Collection toRemove) throws Exception { - if (schemeInformationRegisterException != null) { - throw schemeInformationRegisterException; - } this.addedSchemes = toAdd; this.removedSchemes = toRemove; } @Override public List getSchemesInformation(Collection schemes) throws Exception { - if (schemeInformationReadException != null) { - throw schemeInformationReadException; - } return schemeInformations; } @@ -229,5 +234,10 @@ public String getEclipseLauncher() { public boolean canOverwriteOtherApplicationsRegistration() { return false; } + + @Override + public boolean supportsRegistration() { + return supportsRegistration; + } } } diff --git a/tests/org.eclipse.tests.urischeme/src/org/eclipse/urischeme/internal/registration/TestUnitRegistrationMacOsX.java b/tests/org.eclipse.tests.urischeme/src/org/eclipse/urischeme/internal/registration/TestUnitRegistrationMacOsX.java index 0ff0cbf8143..af37f021fb8 100644 --- a/tests/org.eclipse.tests.urischeme/src/org/eclipse/urischeme/internal/registration/TestUnitRegistrationMacOsX.java +++ b/tests/org.eclipse.tests.urischeme/src/org/eclipse/urischeme/internal/registration/TestUnitRegistrationMacOsX.java @@ -39,6 +39,7 @@ public class TestUnitRegistrationMacOsX { private static final String OWN_APP_PLIST_PATH = "/Users/myuser/Applications/Eclipse.app/Contents/Info.plist"; + private static final String PATH_TO_CODE_SIGNATURE = "/Users/myuser/Applications/Eclipse.app/Contents/_CodeSignature"; private static final String OTHER_APP_PLIST_PATH = "/Users/myuser/Applications/OtherApp.app/Contents/Info.plist"; private static final String OTHER_APP_BUNDLE_PATH = "/Users/myuser/Applications/OtherApp.app"; @@ -62,12 +63,14 @@ public class TestUnitRegistrationMacOsX { public void setup() { fileProvider = new FileProviderMock(); fileProvider.writer = new StringWriter(); + fileProvider.fileExistsAnswers.put(PATH_TO_CODE_SIGNATURE, false); processStub = new ProcessSpy(); System.setProperty("eclipse.home.location", "file:/Users/myuser/Applications/Eclipse.app/Contents/Eclipse/"); System.setProperty("eclipse.launcher", "/Users/myuser/Applications/Eclipse.app/Contents/MacOS/eclipse"); + registration = new RegistrationMacOsX(fileProvider, processStub); InputStream inputStream = getClass().getResourceAsStream("lsregisterForOtherApp.txt"); @@ -267,6 +270,17 @@ public void givesSchemeInfoForSchemeHandledByOtherAppAndInOwnPlistFile() throws assertEquals(OTHER_APP_BUNDLE_PATH, infos.get(0).getHandlerInstanceLocation()); } + @Test + public void doesSupportRegistrationIfAppIsNotSigned() throws Exception { + assertTrue(registration.supportsRegistration()); + } + + @Test + public void doesNotSupportRegistrationIfAppIsSigned() throws Exception { + fileProvider.fileExistsAnswers.put(PATH_TO_CODE_SIGNATURE, true); + assertFalse(registration.supportsRegistration()); + } + private void assertFilePathIs(String filePath) { assertEquals(filePath, fileProvider.recordedReadPaths.get(0)); assertEquals(filePath, fileProvider.writePath); diff --git a/tests/org.eclipse.text.tests/META-INF/MANIFEST.MF b/tests/org.eclipse.text.tests/META-INF/MANIFEST.MF index 25cc5cd96b5..bfd99b7350f 100644 --- a/tests/org.eclipse.text.tests/META-INF/MANIFEST.MF +++ b/tests/org.eclipse.text.tests/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Plugin.name Bundle-SymbolicName: org.eclipse.text.tests -Bundle-Version: 3.14.500.qualifier +Bundle-Version: 3.14.600.qualifier Bundle-Vendor: %Plugin.providerName Bundle-Localization: plugin Export-Package: diff --git a/tests/org.eclipse.text.tests/src/org/eclipse/text/tests/Accessor.java b/tests/org.eclipse.text.tests/src/org/eclipse/text/tests/Accessor.java index 262da670f00..15887e83d06 100644 --- a/tests/org.eclipse.text.tests/src/org/eclipse/text/tests/Accessor.java +++ b/tests/org.eclipse.text.tests/src/org/eclipse/text/tests/Accessor.java @@ -161,9 +161,15 @@ public Object invoke(String methodName, Object[] arguments) { public Object invoke(String methodName, Class[] types, Object[] arguments) { Method method= null; try { - method= fClass.getDeclaredMethod(methodName, types); - } catch (SecurityException | NoSuchMethodException e) { - throw (AssertionFailedException) new AssertionFailedException(e.getLocalizedMessage()).initCause(e); + method = fClass.getDeclaredMethod(methodName, types); + Assert.isNotNull(method); + method.setAccessible(true); + } catch (SecurityException | NoSuchMethodException __) { + try { + method = fClass.getMethod(methodName, types); + } catch (SecurityException | NoSuchMethodException e) { + throw (AssertionFailedException) new AssertionFailedException(e.getLocalizedMessage()).initCause(e); + } } Assert.isNotNull(method); method.setAccessible(true); diff --git a/tests/org.eclipse.ui.ide.application.tests/src/org/eclipse/ui/internal/ide/application/dialogs/UriSchemeHandlerPreferencePageTest.java b/tests/org.eclipse.ui.ide.application.tests/src/org/eclipse/ui/internal/ide/application/dialogs/UriSchemeHandlerPreferencePageTest.java index c3aee65aa5c..a709ed69963 100644 --- a/tests/org.eclipse.ui.ide.application.tests/src/org/eclipse/ui/internal/ide/application/dialogs/UriSchemeHandlerPreferencePageTest.java +++ b/tests/org.eclipse.ui.ide.application.tests/src/org/eclipse/ui/internal/ide/application/dialogs/UriSchemeHandlerPreferencePageTest.java @@ -125,7 +125,7 @@ public void checkOtherAppSchemeGivesWarningAndRevertsClick() throws Exception { MessageDialogWrapperSpy spy = (MessageDialogWrapperSpy) page.messageDialogWrapper; - assertEquals(IDEWorkbenchMessages.UriHandlerPreferencePage_Warning_OtherApp, spy.title); + assertEquals(IDEWorkbenchMessages.UriHandlerPreferencePage_Warning_NotPossible, spy.title); String expected = NLS.bind(IDEWorkbenchMessages.UriHandlerPreferencePage_Warning_OtherApp_Description, OTHER_ECLIPSE_HANDLER_LOCATION, "hello2"); @@ -135,6 +135,22 @@ public void checkOtherAppSchemeGivesWarningAndRevertsClick() throws Exception { assertHandlerTextForSelection(page, 2, OTHER_ECLIPSE_HANDLER_LOCATION); } + @Test + public void checkSchemeOnRegistrationNotSupportedGivesWarningAndRevertsClick() throws Exception { + this.page.createContents(this.page.getShell()); + waitForJob(); + operatingSystemRegistration.supportsRegistration = false; + + clickTableViewerCheckbox(0, true); + + MessageDialogWrapperSpy spy = (MessageDialogWrapperSpy) page.messageDialogWrapper; + assertEquals(IDEWorkbenchMessages.UriHandlerPreferencePage_Warning_NotPossible, spy.title); + String expected = NLS.bind(IDEWorkbenchMessages.UrlHandlerPreferencePage_RegistrationUnsupported, + OTHER_ECLIPSE_HANDLER_LOCATION, "hello"); + assertEquals(expected, spy.message); + assertScheme(getTableItem(0), false, noAppSchemeInfo); + } + @Test public void checkOtherAppSchemeOnWindowsIsAllowed() throws Exception { this.page.createContents(this.page.getShell()); @@ -512,6 +528,7 @@ private static final class OperatingSystemRegistrationMock implements IOperating public Collection removedSchemes = Collections.emptyList(); public boolean canOverwriteOtherApplicationsRegistration = false; public String launcherPath = THIS_ECLIPSE_HANDLER_LOCATION; + public boolean supportsRegistration = true; public OperatingSystemRegistrationMock(List schemeInformations) { this.schemeInformations = schemeInformations; @@ -544,6 +561,11 @@ public boolean canOverwriteOtherApplicationsRegistration() { return canOverwriteOtherApplicationsRegistration; } + @Override + public boolean supportsRegistration() { + return supportsRegistration; + } + } private static class IOExceptionWithoutStackTrace extends IOException { diff --git a/tests/org.eclipse.ui.workbench.texteditor.tests/META-INF/MANIFEST.MF b/tests/org.eclipse.ui.workbench.texteditor.tests/META-INF/MANIFEST.MF index 1c93cf1de71..407818d48bd 100644 --- a/tests/org.eclipse.ui.workbench.texteditor.tests/META-INF/MANIFEST.MF +++ b/tests/org.eclipse.ui.workbench.texteditor.tests/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Plugin.name Bundle-SymbolicName: org.eclipse.ui.workbench.texteditor.tests -Bundle-Version: 3.14.400.qualifier +Bundle-Version: 3.14.500.qualifier Bundle-Vendor: %Plugin.providerName Bundle-Localization: plugin Export-Package: diff --git a/tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/FindReplaceOverlayTest.java b/tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/FindReplaceOverlayTest.java new file mode 100644 index 00000000000..d59aee0fee4 --- /dev/null +++ b/tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/FindReplaceOverlayTest.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ +package org.eclipse.ui.workbench.texteditor.tests; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertEquals; + +import java.util.ResourceBundle; + +import org.junit.Test; + +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.text.tests.Accessor; + +import org.eclipse.jface.text.IFindReplaceTarget; +import org.eclipse.jface.text.TextViewer; + +import org.eclipse.ui.internal.findandreplace.SearchOptions; + +public class FindReplaceOverlayTest extends FindReplaceUITest { + + @Override + public OverlayAccess openUIFromTextViewer(TextViewer viewer) { + OverlayAccess ret; + + Accessor fFindReplaceAction; + fFindReplaceAction= new Accessor("org.eclipse.ui.texteditor.FindReplaceAction", getClass().getClassLoader(), + new Class[] { ResourceBundle.class, String.class, Shell.class, IFindReplaceTarget.class }, + new Object[] { ResourceBundle.getBundle("org.eclipse.ui.texteditor.ConstructedEditorMessages"), "Editor.FindReplace.", viewer.getControl().getShell(), + getTextViewer().getFindReplaceTarget() }); + fFindReplaceAction.invoke("showOverlayInEditor", null); + Accessor overlayAccessor= new Accessor(fFindReplaceAction.get("overlay"), "org.eclipse.ui.texteditor.FindReplaceOverlay", getClass().getClassLoader()); + + ret= new OverlayAccess(overlayAccessor); + return ret; + } + + @Test + public void testDirectionalSearchButtons() { + initializeTextViewerWithFindReplaceUI("line\nline\nline\nline"); + OverlayAccess dialog= getDialog(); + + dialog.setFindText("line"); + IFindReplaceTarget target= dialog.getTarget(); + + assertEquals(0, (target.getSelection()).x); + assertEquals(4, (target.getSelection()).y); + + dialog.pressSearch(true); + assertEquals(5, (target.getSelection()).x); + assertEquals(4, (target.getSelection()).y); + + dialog.pressSearch(true); + assertEquals(10, (target.getSelection()).x); + assertEquals(4, (target.getSelection()).y); + + dialog.pressSearch(false); + assertEquals(5, (target.getSelection()).x); + assertEquals(4, (target.getSelection()).y); + + dialog.pressSearch(true); + assertEquals(10, (target.getSelection()).x); + assertEquals(4, (target.getSelection()).y); + + dialog.pressSearch(false); + assertEquals(5, (target.getSelection()).x); + assertEquals(4, (target.getSelection()).y); + + dialog.pressSearch(false); + assertEquals(0, (target.getSelection()).x); + assertEquals(4, (target.getSelection()).y); + } + + @Test + public void testIncrementalSearchUpdatesAfterChangingOptions() { + initializeTextViewerWithFindReplaceUI("alinee\naLinee\nline\nline"); + OverlayAccess dialog= getDialog(); + IFindReplaceTarget target= dialog.getTarget(); + + dialog.setFindText("Line"); + dialog.select(SearchOptions.CASE_SENSITIVE); + assertThat(dialog.getTarget().getSelectionText(), is("Line")); + + dialog.unselect(SearchOptions.CASE_SENSITIVE); + assertEquals(1, (target.getSelection()).x); + assertEquals(4, (target.getSelection()).y); + + dialog.select(SearchOptions.WHOLE_WORD); + assertEquals(14, (target.getSelection()).x); + assertEquals(4, (target.getSelection()).y); + + dialog.unselect(SearchOptions.CASE_SENSITIVE); + dialog.unselect(SearchOptions.WHOLE_WORD); + assertEquals(1, (target.getSelection()).x); + assertEquals(4, (target.getSelection()).y); + assertThat(dialog.getTarget().getSelectionText(), is("line")); + } + + @Test + public void testCantOpenReplaceDialogInReadOnlyEditor() { + openTextViewer("text"); + getTextViewer().setEditable(false); + initializeFindReplaceUIForTextViewer(); + OverlayAccess dialog= getDialog(); + + dialog.openReplaceDialog(); + reopenFindReplaceUIForTextViewer(); + dialog= getDialog(); + assertThat(dialog.isReplaceDialogOpen(), is(false)); + } + + @Test + public void testRememberReplaceExpandState() { + initializeTextViewerWithFindReplaceUI("text"); + OverlayAccess dialog= getDialog(); + + dialog.openReplaceDialog(); + assertThat(dialog.isReplaceDialogOpen(), is(true)); + reopenFindReplaceUIForTextViewer(); + dialog= getDialog(); + assertThat(dialog.isReplaceDialogOpen(), is(true)); + + dialog.closeReplaceDialog(); + reopenFindReplaceUIForTextViewer(); + dialog= getDialog(); + assertThat(dialog.isReplaceDialogOpen(), is(false)); + + dialog.openReplaceDialog(); + getTextViewer().setEditable(false); + reopenFindReplaceUIForTextViewer(); + dialog= getDialog(); + assertThat(dialog.isReplaceDialogOpen(), is(false)); + } + + @Test + public void testSearchBackwardsWithRegEx() { + initializeTextViewerWithFindReplaceUI("text text text"); + + OverlayAccess dialog= getDialog(); + dialog.select(SearchOptions.REGEX); + dialog.setFindText("text"); // with RegEx enabled, there is no incremental search! + dialog.pressSearch(true); + assertThat(dialog.getTarget().getSelection().y, is(4)); + dialog.pressSearch(true); + assertThat(dialog.getTarget().getSelection().x, is("text ".length())); + dialog.pressSearch(true); + assertThat(dialog.getTarget().getSelection().x, is("text text ".length())); + dialog.pressSearch(false); + assertThat(dialog.getTarget().getSelection().x, is("text ".length())); + } + +} diff --git a/tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/OverlayAccess.java b/tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/OverlayAccess.java new file mode 100644 index 00000000000..575d4e67c87 --- /dev/null +++ b/tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/OverlayAccess.java @@ -0,0 +1,328 @@ +/******************************************************************************* + * Copyright (c) 2024 Vector Informatik GmbH and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Vector Informatik GmbH - initial API and implementation + *******************************************************************************/ +package org.eclipse.ui.workbench.texteditor.tests; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.Objects; +import java.util.Set; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.ToolItem; + +import org.eclipse.text.tests.Accessor; + +import org.eclipse.jface.text.IFindReplaceTarget; +import org.eclipse.jface.text.IFindReplaceTargetExtension; + +import org.eclipse.ui.internal.findandreplace.FindReplaceLogic; +import org.eclipse.ui.internal.findandreplace.IFindReplaceLogic; +import org.eclipse.ui.internal.findandreplace.SearchOptions; + +class OverlayAccess implements IFindReplaceUIAccess { + FindReplaceLogic findReplaceLogic; + + Text find; + + Text replace; + + ToolItem inSelection; + + ToolItem caseSensitive; + + ToolItem wholeWord; + + ToolItem regEx; + + ToolItem searchForward; + + ToolItem searchBackward; + + Button openReplaceDialog; + + ToolItem replaceButton; + + ToolItem replaceAllButton; + + private Runnable closeOperation; + + Accessor dialogAccessor; + + private Supplier shellRetriever; + + OverlayAccess(Accessor findReplaceOverlayAccessor) { + dialogAccessor= findReplaceOverlayAccessor; + findReplaceLogic= (FindReplaceLogic) findReplaceOverlayAccessor.get("findReplaceLogic"); + find= (Text) findReplaceOverlayAccessor.get("searchBar"); + replace= (Text) findReplaceOverlayAccessor.get("replaceBar"); + caseSensitive= (ToolItem) findReplaceOverlayAccessor.get("caseSensitiveSearchButton"); + wholeWord= (ToolItem) findReplaceOverlayAccessor.get("wholeWordSearchButton"); + regEx= (ToolItem) findReplaceOverlayAccessor.get("regexSearchButton"); + searchForward= (ToolItem) findReplaceOverlayAccessor.get("searchDownButton"); + searchBackward= (ToolItem) findReplaceOverlayAccessor.get("searchUpButton"); + closeOperation= () -> findReplaceOverlayAccessor.invoke("close", null); + openReplaceDialog= (Button) findReplaceOverlayAccessor.get("replaceToggle"); + replaceButton= (ToolItem) findReplaceOverlayAccessor.get("replaceButton"); + replaceAllButton= (ToolItem) findReplaceOverlayAccessor.get("replaceAllButton"); + inSelection= (ToolItem) findReplaceOverlayAccessor.get("searchInSelectionButton"); + shellRetriever= () -> ((Shell) findReplaceOverlayAccessor.invoke("getShell", null)); + } + + @Override + public IFindReplaceTarget getTarget() { + return findReplaceLogic.getTarget(); + } + + private void restoreInitialConfiguration() { + find.setText(""); + select(SearchOptions.GLOBAL); + unselect(SearchOptions.REGEX); + unselect(SearchOptions.CASE_SENSITIVE); + unselect(SearchOptions.WHOLE_WORD); + } + + @Override + public void closeAndRestore() { + restoreInitialConfiguration(); + assertInitialConfiguration(); + closeOperation.run(); + } + + @Override + public void close() { + closeOperation.run(); + } + + @Override + public void select(SearchOptions option) { + ToolItem button= getButtonForSearchOption(option); + if (button == null) { + return; + } + button.setSelection(true); + if (option == SearchOptions.GLOBAL) { + button.setSelection(false); + } + button.notifyListeners(SWT.Selection, null); + } + + @Override + public void unselect(SearchOptions option) { + ToolItem button= getButtonForSearchOption(option); + if (button == null) { + return; + } + button.setSelection(false); + if (option == SearchOptions.GLOBAL) { + button.setSelection(true); + } + button.notifyListeners(SWT.Selection, null); + } + + @Override + public void simulateEnterInFindInputField(boolean shiftPressed) { + simulateKeyPressInFindInputField(SWT.CR, shiftPressed); + } + + @Override + public void simulateKeyPressInFindInputField(int keyCode, boolean shiftPressed) { + final Event event= new Event(); + event.type= SWT.KeyDown; + event.keyCode= keyCode; + if (shiftPressed) { + event.stateMask= SWT.SHIFT; + } + find.notifyListeners(SWT.KeyDown, event); + find.traverse(SWT.TRAVERSE_RETURN, event); + FindReplaceTestUtil.runEventQueue(); + } + + @Override + public String getFindText() { + return find.getText(); + } + + @Override + public String getReplaceText() { + return replace.getText(); + } + + @Override + public void setFindText(String text) { + find.setText(text); + find.notifyListeners(SWT.Modify, null); + } + + @Override + public void setReplaceText(String text) { + openReplaceDialog(); + replace.setText(text); + } + + @Override + public ToolItem getButtonForSearchOption(SearchOptions option) { + switch (option) { + case CASE_SENSITIVE: + return caseSensitive; + case REGEX: + return regEx; + case WHOLE_WORD: + return wholeWord; + case GLOBAL: + return inSelection; + //$CASES-OMITTED$ + default: + return null; + } + } + + private Set getEnabledOptions() { + return Arrays.stream(SearchOptions.values()) + .filter(option -> (getButtonForSearchOption(option) != null && getButtonForSearchOption(option).getEnabled())) + .collect(Collectors.toSet()); + } + + private Set getSelectedOptions() { + return Arrays.stream(SearchOptions.values()) + .filter(isOptionSelected()) + .collect(Collectors.toSet()); + } + + private Predicate isOptionSelected() { + return option -> { + ToolItem buttonForSearchOption= getButtonForSearchOption(option); + if (option == SearchOptions.GLOBAL) { + return !buttonForSearchOption.getSelection();// The "Global" option is mapped to a button that + // selects whether to search in the selection, thus inverting the semantic + } + return buttonForSearchOption != null && buttonForSearchOption.getSelection(); + }; + } + + public void pressSearch(boolean forward) { + if (forward) { + searchForward.notifyListeners(SWT.Selection, null); + } else { + searchBackward.notifyListeners(SWT.Selection, null); + } + } + + @Override + public IFindReplaceLogic getFindReplaceLogic() { + return findReplaceLogic; + } + + @Override + public void performReplaceAll() { + openReplaceDialog(); + replaceAllButton.notifyListeners(SWT.Selection, null); + } + + @Override + public void performReplace() { + openReplaceDialog(); + replaceButton.notifyListeners(SWT.Selection, null); + } + + public boolean isReplaceDialogOpen() { + return dialogAccessor.getBoolean("replaceBarOpen"); + } + + public void openReplaceDialog() { + if (!isReplaceDialogOpen() && Objects.nonNull(openReplaceDialog)) { + openReplaceDialog.notifyListeners(SWT.Selection, null); + replace= (Text) dialogAccessor.get("replaceBar"); + replaceButton= (ToolItem) dialogAccessor.get("replaceButton"); + replaceAllButton= (ToolItem) dialogAccessor.get("replaceAllButton"); + } + } + + public void closeReplaceDialog() { + if (isReplaceDialogOpen() && Objects.nonNull(openReplaceDialog)) { + openReplaceDialog.notifyListeners(SWT.Selection, null); + replace= null; + replaceButton= null; + replaceAllButton= null; + } + } + + @Override + public void performReplaceAndFind() { + performReplace(); + } + + @Override + public void assertInitialConfiguration() { + assertUnselected(SearchOptions.REGEX); + assertUnselected(SearchOptions.WHOLE_WORD); + assertUnselected(SearchOptions.CASE_SENSITIVE); + if (!doesTextViewerHaveMultiLineSelection(findReplaceLogic.getTarget())) { + assertSelected(SearchOptions.GLOBAL); + assertTrue(findReplaceLogic.isActive(SearchOptions.GLOBAL)); + } else { + assertUnselected(SearchOptions.GLOBAL); + assertFalse(findReplaceLogic.isActive(SearchOptions.GLOBAL)); + } + assertEnabled(SearchOptions.GLOBAL); + assertEnabled(SearchOptions.REGEX); + assertEnabled(SearchOptions.CASE_SENSITIVE); + if (getFindText().equals("") || findReplaceLogic.isWholeWordSearchAvailable(getFindText())) { + assertEnabled(SearchOptions.WHOLE_WORD); + } else { + assertDisabled(SearchOptions.WHOLE_WORD); + } + } + + private boolean doesTextViewerHaveMultiLineSelection(IFindReplaceTarget target) { + if (target instanceof IFindReplaceTargetExtension scopeProvider) { + return scopeProvider.getScope() != null; // null is returned for global scope + } + return false; + } + + @Override + public void assertUnselected(SearchOptions option) { + assertFalse(getSelectedOptions().contains(option)); + } + + @Override + public void assertSelected(SearchOptions option) { + assertTrue(getSelectedOptions().contains(option)); + } + + @Override + public void assertDisabled(SearchOptions option) { + assertFalse(getEnabledOptions().contains(option)); + } + + @Override + public void assertEnabled(SearchOptions option) { + assertTrue(getEnabledOptions().contains(option)); + } + + @Override + public Shell getActiveShell() { + return shellRetriever.get(); + } + +} diff --git a/tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/WorkbenchTextEditorTestSuite.java b/tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/WorkbenchTextEditorTestSuite.java index f8e114d0b1e..df7a9d9a56e 100644 --- a/tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/WorkbenchTextEditorTestSuite.java +++ b/tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/WorkbenchTextEditorTestSuite.java @@ -34,7 +34,6 @@ */ @RunWith(Suite.class) @SuiteClasses({ - FindReplaceDialogTest.class, HippieCompletionTest.class, RangeTest.class, ChangeRegionTest.class, @@ -47,6 +46,8 @@ MinimapWidgetTest.class, TextEditorPluginTest.class, TextViewerDeleteLineTargetTest.class, + FindReplaceDialogTest.class, + FindReplaceOverlayTest.class, FindReplaceLogicTest.class, }) public class WorkbenchTextEditorTestSuite {