diff --git a/bundles/org.eclipse.e4.ui.workbench.swt/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.ui.workbench.swt/META-INF/MANIFEST.MF index fc7c8b7746f..c21c53df8ad 100644 --- a/bundles/org.eclipse.e4.ui.workbench.swt/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.e4.ui.workbench.swt/META-INF/MANIFEST.MF @@ -9,7 +9,7 @@ Bundle-Activator: org.eclipse.e4.ui.internal.workbench.swt.WorkbenchSWTActivator Require-Bundle: org.eclipse.e4.ui.workbench;bundle-version="0.10.0", org.eclipse.e4.core.services;bundle-version="1.0.0", org.eclipse.e4.ui.services;bundle-version="0.1.0", - org.eclipse.jface;bundle-version="[3.39.0,4.0.0)", + org.eclipse.jface;bundle-version="[3.41.0,4.0.0)", org.eclipse.e4.ui.dialogs;bundle-version="1.1.600", org.eclipse.core.databinding;bundle-version="[1.2.0,2.0.0)", org.eclipse.jface.databinding;bundle-version="[1.3.0,2.0.0)", diff --git a/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/internal/workbench/swt/ImageURLModifierTracker.java b/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/internal/workbench/swt/ImageURLModifierTracker.java new file mode 100644 index 00000000000..5c21fa85225 --- /dev/null +++ b/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/internal/workbench/swt/ImageURLModifierTracker.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2026 vogella 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: + * Lars Vogel - initial API and implementation + *******************************************************************************/ +package org.eclipse.e4.ui.internal.workbench.swt; + +import org.eclipse.jface.resource.IImageURLModifier; +import org.eclipse.jface.resource.ImageDescriptor; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; + +/** + * Installs the highest ranked {@link IImageURLModifier} service in JFace. + * Tracked because the contributing bundle may start after the workbench. + */ +class ImageURLModifierTracker extends ServiceTracker { + + private ServiceReference installed; + + ImageURLModifierTracker(BundleContext context) { + super(context, IImageURLModifier.class, null); + } + + @Override + public synchronized IImageURLModifier addingService(ServiceReference reference) { + IImageURLModifier modifier = super.addingService(reference); + // not tracked until this returns, so it cannot be picked by installBest() + if (modifier != null && (installed == null || reference.compareTo(installed) > 0)) { + installed = reference; + ImageDescriptor.setURLModifier(modifier); + } + return modifier; + } + + @Override + public synchronized void modifiedService(ServiceReference reference, + IImageURLModifier service) { + installBest(); + } + + @Override + public synchronized void removedService(ServiceReference reference, + IImageURLModifier service) { + super.removedService(reference, service); + if (reference.equals(installed)) { + installBest(); + } + } + + private void installBest() { + installed = getServiceReference(); + ImageDescriptor.setURLModifier(installed == null ? null : getService(installed)); + } +} diff --git a/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/internal/workbench/swt/WorkbenchSWTActivator.java b/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/internal/workbench/swt/WorkbenchSWTActivator.java index 3a7f5a5cad2..3efea7fc8b1 100644 --- a/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/internal/workbench/swt/WorkbenchSWTActivator.java +++ b/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/internal/workbench/swt/WorkbenchSWTActivator.java @@ -43,6 +43,7 @@ import org.eclipse.core.runtime.Platform; import org.eclipse.jface.dialogs.DialogSettings; import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.resource.IImageURLModifier; import org.eclipse.osgi.service.datalocation.Location; import org.eclipse.osgi.service.debug.DebugOptions; import org.eclipse.osgi.service.debug.DebugOptionsListener; @@ -62,6 +63,7 @@ public class WorkbenchSWTActivator implements BundleActivator, DebugOptionsListe private BundleContext context; private ServiceTracker locationTracker; + private ServiceTracker imageURLModifierTracker; private static WorkbenchSWTActivator activator; private DebugTrace trace; @@ -89,11 +91,17 @@ public void start(BundleContext context) throws Exception { Hashtable props = new Hashtable<>(2); props.put(DebugOptions.LISTENER_SYMBOLICNAME, PI_RENDERERS); context.registerService(DebugOptionsListener.class, this, props); + imageURLModifierTracker = new ImageURLModifierTracker(context); + imageURLModifierTracker.open(); } @Override public void stop(BundleContext context) throws Exception { saveDialogSettings(); + if (imageURLModifierTracker != null) { + imageURLModifierTracker.close(); + imageURLModifierTracker = null; + } } public Bundle getBundle() { diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/IImageURLModifier.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/IImageURLModifier.java new file mode 100644 index 00000000000..5dfb2182ec1 --- /dev/null +++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/IImageURLModifier.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2026 vogella 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: + * Lars Vogel - initial API and implementation + *******************************************************************************/ +package org.eclipse.jface.resource; + +import java.net.URL; + +/** + * Rewrites image URLs before they are loaded, so icons can be substituted + * without changing the code that creates the image descriptors. The E4 + * workbench installs the highest ranked OSGi service of this type. + *

+ * EXPERIMENTAL. This interface has been added as part of a + * work in progress. There is no guarantee that this API will remain the same. + *

+ * + * @see ImageDescriptor#setURLModifier(IImageURLModifier) + * @since 3.41 + */ +@FunctionalInterface +public interface IImageURLModifier { + + /** + * Returns the URL to load instead of the given one, or null to + * keep it. Called for every image load, so it must be fast and thread-safe. + * + * @param originalURL the URL the image would be loaded from + * @return the replacement URL, or null to keep the original + */ + URL modifyURL(URL originalURL); +} diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/ImageDescriptor.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/ImageDescriptor.java index 231f9b71db5..757f25de501 100644 --- a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/ImageDescriptor.java +++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/ImageDescriptor.java @@ -82,6 +82,24 @@ protected ImageDescriptor() { super(shouldBeCached); } + /** Set from OSGi service events, read from any thread. */ + static volatile IImageURLModifier urlModifier; + + /** + * Installs the modifier consulted when an image is loaded from a URL. Images + * created before are not reloaded. + *

+ * EXPERIMENTAL. This method has been added as part of a work + * in progress. There is no guarantee that this API will remain the same. + *

+ * + * @param modifier the modifier, or null to remove it + * @since 3.41 + */ + public static void setURLModifier(IImageURLModifier modifier) { + urlModifier = modifier; + } + private static final ImageDescriptor NULL_IMAGE = createFromImageDataProvider(z -> null); /** diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/URLImageDescriptor.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/URLImageDescriptor.java index 646ca5cb90f..0f306a7913f 100644 --- a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/URLImageDescriptor.java +++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/URLImageDescriptor.java @@ -63,7 +63,7 @@ private ImageFileNameProvider createURLImageFileNameProvider() { // The calling image will do that itself! return getFilePath(tempURL, logIOException); } - return getZoomedImageSource(tempURL, url, zoom, u -> getFilePath(u, logIOException)); + return getZoomedImageSource(tempURL, zoom, u -> getFilePath(u, logIOException)); } return null; }; @@ -107,12 +107,12 @@ public ImageData getImageData(int zoom) { if (zoom == 100 || canLoadAtZoom(tempURL, zoom)) { return getImageData(tempURL, 100, zoom); } - return getZoomedImageSource(tempURL, url, zoom, u -> getImageData(u, zoom, zoom)); + return getZoomedImageSource(tempURL, zoom, u -> getImageData(u, zoom, zoom)); } return null; } - private static R getZoomedImageSource(URL url, String urlString, int zoom, Function getImage) { + private static R getZoomedImageSource(URL url, int zoom, Function getImage) { URL xUrl = getxURL(url, zoom); if (xUrl != null) { R xdata = getImage.apply(xUrl); @@ -120,9 +120,10 @@ private static R getZoomedImageSource(URL url, String urlString, int zoom, F return xdata; } } - String xpath = getxPath(urlString, zoom); + // derived from the already modified URL, so the modifier must not run again + String xpath = getxPath(url.toExternalForm(), zoom); if (xpath != null) { - URL xPathUrl = getURL(xpath); + URL xPathUrl = toURL(xpath); if (xPathUrl != null) { return getImage.apply(xPathUrl); } @@ -352,14 +353,30 @@ public Image createImage(boolean returnMissingImageOnError, Device device) { } } + /** + * Resolves the given URL string, applying the URL modifier. Every code path of + * this descriptor resolves its URL here. + */ private static URL getURL(String urlString) { - URL result = null; + URL result = toURL(urlString); + IImageURLModifier modifier = urlModifier; + if (result != null && modifier != null) { + URL modified = modifier.modifyURL(result); + if (modified != null) { + result = modified; + } + } + return result; + } + + @SuppressWarnings("deprecation") // keeps the lenient parsing of new URL(String) + private static URL toURL(String urlString) { try { - result = new URL(urlString); + return new URL(urlString); } catch (MalformedURLException e) { Policy.logException(e); + return null; } - return result; } @Override diff --git a/examples/org.eclipse.ui.examples.urlmodifier/.classpath b/examples/org.eclipse.ui.examples.urlmodifier/.classpath new file mode 100644 index 00000000000..81fe078c20c --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/examples/org.eclipse.ui.examples.urlmodifier/.project b/examples/org.eclipse.ui.examples.urlmodifier/.project new file mode 100644 index 00000000000..bf9ba539898 --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/.project @@ -0,0 +1,33 @@ + + + org.eclipse.ui.examples.urlmodifier + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.ds.core.builder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/examples/org.eclipse.ui.examples.urlmodifier/.settings/org.eclipse.core.resources.prefs b/examples/org.eclipse.ui.examples.urlmodifier/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..99f26c0203a --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/examples/org.eclipse.ui.examples.urlmodifier/.settings/org.eclipse.jdt.core.prefs b/examples/org.eclipse.ui.examples.urlmodifier/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..5f7f29a43c7 --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,338 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=17 +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 +org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=true +org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=true +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false +org.eclipse.jdt.core.formatter.comment.indent_root_tags=false +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert +org.eclipse.jdt.core.formatter.comment.line_length=80 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=120 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=tab +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true +org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false +org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true +org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true +org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/examples/org.eclipse.ui.examples.urlmodifier/.settings/org.eclipse.pde.ds.annotations.prefs b/examples/org.eclipse.ui.examples.urlmodifier/.settings/org.eclipse.pde.ds.annotations.prefs new file mode 100644 index 00000000000..b5c60ed2648 --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/.settings/org.eclipse.pde.ds.annotations.prefs @@ -0,0 +1,8 @@ +classpath=true +dsVersion=V1_4 +eclipse.preferences.version=1 +enabled=true +generateBundleActivationPolicyLazy=true +path=OSGI-INF +validationErrorLevel=error +validationErrorLevel.missingImplicitUnbindMethod=error diff --git a/examples/org.eclipse.ui.examples.urlmodifier/Application.e4xmi b/examples/org.eclipse.ui.examples.urlmodifier/Application.e4xmi new file mode 100644 index 00000000000..bbc5ff4f2df --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/Application.e4xmi @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/org.eclipse.ui.examples.urlmodifier/Icon Pack Demo.launch b/examples/org.eclipse.ui.examples.urlmodifier/Icon Pack Demo.launch new file mode 100644 index 00000000000..6dc8d371f22 --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/Icon Pack Demo.launch @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/org.eclipse.ui.examples.urlmodifier/META-INF/MANIFEST.MF b/examples/org.eclipse.ui.examples.urlmodifier/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..fd71f4bcef0 --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/META-INF/MANIFEST.MF @@ -0,0 +1,23 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Image URL Modifier Example +Bundle-Vendor: Eclipse.org +Bundle-SymbolicName: org.eclipse.ui.examples.urlmodifier;singleton:=true +Bundle-Version: 1.0.0.qualifier +Require-Bundle: org.eclipse.swt, + org.eclipse.e4.ui.model.workbench, + org.eclipse.jface, + org.eclipse.e4.ui.services, + org.eclipse.e4.ui.workbench, + org.eclipse.e4.core.di, + org.eclipse.e4.ui.di, + org.eclipse.e4.core.contexts, + org.eclipse.core.runtime +Bundle-RequiredExecutionEnvironment: JavaSE-17 +Automatic-Module-Name: org.eclipse.ui.examples.urlmodifier +Bundle-ActivationPolicy: lazy +Import-Package: jakarta.annotation;version="[2.1.0,4.0.0)", + jakarta.inject;version="[2.0.0,3.0.0)", + org.osgi.service.component.annotations;version="[1.3.0,2.0.0)";resolution:=optional +Require-Capability: eclipse.swt;filter:="(image.format=svg)" +Service-Component: OSGI-INF/org.eclipse.ui.examples.urlmodifier.IconPackModifier.xml diff --git a/examples/org.eclipse.ui.examples.urlmodifier/OSGI-INF/org.eclipse.ui.examples.urlmodifier.IconPackModifier.xml b/examples/org.eclipse.ui.examples.urlmodifier/OSGI-INF/org.eclipse.ui.examples.urlmodifier.IconPackModifier.xml new file mode 100644 index 00000000000..6b1d5118df7 --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/OSGI-INF/org.eclipse.ui.examples.urlmodifier.IconPackModifier.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/examples/org.eclipse.ui.examples.urlmodifier/README.md b/examples/org.eclipse.ui.examples.urlmodifier/README.md new file mode 100644 index 00000000000..bfc0e7a798f --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/README.md @@ -0,0 +1,10 @@ +# Image URL modifier example + +A minimal Eclipse 4 RCP application that swaps icons through an `IImageURLModifier` OSGi service. +`IconPackModifier` is a declarative services component, and the E4 workbench installs the service in JFace, so no startup code is needed. +The modifier looks up the file name of every requested icon in `iconpacks//` and returns `null` to keep icons the pack does not replace. +The pack replaces the part icon and the toolbar icons, which the application model loads from SVG files; the extra tool items use a handler that does nothing. + +The button in the part stores the selected pack in the instance preferences and restarts the workbench, since images that already exist are not reloaded. +Start it from `org.eclipse.ui.examples.urlmodifier.product` or with `Icon Pack Demo.launch`. +The launch configuration takes `org.eclipse.e4.ui.workbench.swt` from the workspace, which needs a version that contains the image URL modifier tracker. diff --git a/examples/org.eclipse.ui.examples.urlmodifier/build.properties b/examples/org.eclipse.ui.examples.urlmodifier/build.properties new file mode 100644 index 00000000000..1278ec60874 --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/build.properties @@ -0,0 +1,10 @@ +source.. = src/ +output.. = bin/ +bin.includes = plugin.xml,\ + META-INF/,\ + .,\ + icons/,\ + iconpacks/,\ + css/default.css,\ + Application.e4xmi,\ + OSGI-INF/org.eclipse.ui.examples.urlmodifier.IconPackModifier.xml diff --git a/examples/org.eclipse.ui.examples.urlmodifier/css/default.css b/examples/org.eclipse.ui.examples.urlmodifier/css/default.css new file mode 100644 index 00000000000..e69de29bb2d diff --git a/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/copy_edit.png b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/copy_edit.png new file mode 100644 index 00000000000..a8df26c528d Binary files /dev/null and b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/copy_edit.png differ diff --git a/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/copy_edit@2x.png b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/copy_edit@2x.png new file mode 100644 index 00000000000..3cfd7e123d9 Binary files /dev/null and b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/copy_edit@2x.png differ diff --git a/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/delete.png b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/delete.png new file mode 100644 index 00000000000..bdab440effc Binary files /dev/null and b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/delete.png differ diff --git a/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/delete@2x.png b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/delete@2x.png new file mode 100644 index 00000000000..93cbfa74802 Binary files /dev/null and b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/delete@2x.png differ diff --git a/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/home.png b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/home.png new file mode 100644 index 00000000000..308ccb4e552 Binary files /dev/null and b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/home.png differ diff --git a/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/home@2x.png b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/home@2x.png new file mode 100644 index 00000000000..b486fad1b0f Binary files /dev/null and b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/home@2x.png differ diff --git a/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/paste_edit.png b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/paste_edit.png new file mode 100644 index 00000000000..4b1bc2e5275 Binary files /dev/null and b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/paste_edit.png differ diff --git a/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/paste_edit@2x.png b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/paste_edit@2x.png new file mode 100644 index 00000000000..e9a3592e221 Binary files /dev/null and b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/paste_edit@2x.png differ diff --git a/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/redo_edit.png b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/redo_edit.png new file mode 100644 index 00000000000..bef8b8dfc45 Binary files /dev/null and b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/redo_edit.png differ diff --git a/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/redo_edit@2x.png b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/redo_edit@2x.png new file mode 100644 index 00000000000..3e7afef15b8 Binary files /dev/null and b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/redo_edit@2x.png differ diff --git a/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/refresh_nav.png b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/refresh_nav.png new file mode 100644 index 00000000000..6476c44be9c Binary files /dev/null and b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/refresh_nav.png differ diff --git a/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/refresh_nav@2x.png b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/refresh_nav@2x.png new file mode 100644 index 00000000000..256cf2ba5f7 Binary files /dev/null and b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/refresh_nav@2x.png differ diff --git a/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/save_edit.png b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/save_edit.png new file mode 100644 index 00000000000..ccfeb9e2f1b Binary files /dev/null and b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/save_edit.png differ diff --git a/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/save_edit@2x.png b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/save_edit@2x.png new file mode 100644 index 00000000000..784460bc526 Binary files /dev/null and b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/save_edit@2x.png differ diff --git a/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/undo_edit.png b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/undo_edit.png new file mode 100644 index 00000000000..f7033832e5c Binary files /dev/null and b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/undo_edit.png differ diff --git a/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/undo_edit@2x.png b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/undo_edit@2x.png new file mode 100644 index 00000000000..3d462b0a749 Binary files /dev/null and b/examples/org.eclipse.ui.examples.urlmodifier/iconpacks/bold/undo_edit@2x.png differ diff --git a/examples/org.eclipse.ui.examples.urlmodifier/icons/copy_edit.svg b/examples/org.eclipse.ui.examples.urlmodifier/icons/copy_edit.svg new file mode 100644 index 00000000000..ae0553dced7 --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/icons/copy_edit.svg @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + backpaper + + + page-lines-back + + + frontpaper + + + page-lines + + + fold + + + diff --git a/examples/org.eclipse.ui.examples.urlmodifier/icons/delete.svg b/examples/org.eclipse.ui.examples.urlmodifier/icons/delete.svg new file mode 100644 index 00000000000..b76f1ad4eb7 --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/icons/delete.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + x + + + diff --git a/examples/org.eclipse.ui.examples.urlmodifier/icons/home.svg b/examples/org.eclipse.ui.examples.urlmodifier/icons/home.svg new file mode 100644 index 00000000000..06a9eaeaf06 --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/icons/home.svg @@ -0,0 +1,20 @@ + + + + + + diff --git a/examples/org.eclipse.ui.examples.urlmodifier/icons/paste_edit.svg b/examples/org.eclipse.ui.examples.urlmodifier/icons/paste_edit.svg new file mode 100644 index 00000000000..47e168ce59d --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/icons/paste_edit.svg @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + clipboard + + + clasp + + + paper + + + paper-fold + + + paper-lines + + + diff --git a/examples/org.eclipse.ui.examples.urlmodifier/icons/redo_edit.svg b/examples/org.eclipse.ui.examples.urlmodifier/icons/redo_edit.svg new file mode 100644 index 00000000000..b38686f672a --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/icons/redo_edit.svg @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + redo-arrow + + + + diff --git a/examples/org.eclipse.ui.examples.urlmodifier/icons/refresh_nav.svg b/examples/org.eclipse.ui.examples.urlmodifier/icons/refresh_nav.svg new file mode 100644 index 00000000000..4abb2d68c02 --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/icons/refresh_nav.svg @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/org.eclipse.ui.examples.urlmodifier/icons/save_edit.svg b/examples/org.eclipse.ui.examples.urlmodifier/icons/save_edit.svg new file mode 100644 index 00000000000..223536d8022 --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/icons/save_edit.svg @@ -0,0 +1,20 @@ + + + + + + diff --git a/examples/org.eclipse.ui.examples.urlmodifier/icons/undo_edit.svg b/examples/org.eclipse.ui.examples.urlmodifier/icons/undo_edit.svg new file mode 100644 index 00000000000..2ebc181569d --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/icons/undo_edit.svg @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + undo-arrow + + + diff --git a/examples/org.eclipse.ui.examples.urlmodifier/org.eclipse.ui.examples.urlmodifier.product b/examples/org.eclipse.ui.examples.urlmodifier/org.eclipse.ui.examples.urlmodifier.product new file mode 100644 index 00000000000..6d7c55ce558 --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/org.eclipse.ui.examples.urlmodifier.product @@ -0,0 +1,26 @@ + + + + + + + + + + -clearPersistedState + + -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts + + + + + + + + + + + + + + diff --git a/examples/org.eclipse.ui.examples.urlmodifier/plugin.xml b/examples/org.eclipse.ui.examples.urlmodifier/plugin.xml new file mode 100644 index 00000000000..fb802de0111 --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/plugin.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + diff --git a/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/IconPackModifier.java b/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/IconPackModifier.java new file mode 100644 index 00000000000..6b8c44de2fd --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/IconPackModifier.java @@ -0,0 +1,39 @@ +package org.eclipse.ui.examples.urlmodifier; + +import java.net.URL; + +import org.eclipse.jface.resource.IImageURLModifier; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; + +/** + * Replaces any icon whose file name exists in iconpacks/<pack>/ of this + * bundle. + */ +@Component +public class IconPackModifier implements IImageURLModifier { + + private Bundle bundle; + private String pack; + + @Activate + void activate(BundleContext context) { + bundle = context.getBundle(); + pack = IconPacks.selected(); + } + + @Override + public URL modifyURL(URL url) { + if (pack == null) { + return null; + } + String path = url.getPath(); + String name = path.substring(path.lastIndexOf('/') + 1); + int dot = name.lastIndexOf('.'); + String baseName = dot < 0 ? name : name.substring(0, dot); + // null keeps the original icon + return bundle.getEntry("iconpacks/" + pack + "/" + baseName + ".png"); + } +} diff --git a/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/IconPacks.java b/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/IconPacks.java new file mode 100644 index 00000000000..00378cd1308 --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/IconPacks.java @@ -0,0 +1,33 @@ +package org.eclipse.ui.examples.urlmodifier; + +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.osgi.service.prefs.BackingStoreException; +import org.osgi.service.prefs.Preferences; + +/** Stores the icon pack selected for the next start. */ +public final class IconPacks { + + public static final String BOLD = "bold"; + private static final String KEY = "iconPack"; + + private IconPacks() { + } + + private static Preferences preferences() { + return InstanceScope.INSTANCE.getNode("org.eclipse.ui.examples.urlmodifier"); + } + + public static String selected() { + return preferences().get(KEY, null); + } + + public static void select(String pack) throws BackingStoreException { + Preferences preferences = preferences(); + if (pack == null) { + preferences.remove(KEY); + } else { + preferences.put(KEY, pack); + } + preferences.flush(); + } +} diff --git a/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/handlers/FakeHandler.java b/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/handlers/FakeHandler.java new file mode 100644 index 00000000000..08f113badf1 --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/handlers/FakeHandler.java @@ -0,0 +1,11 @@ +package org.eclipse.ui.examples.urlmodifier.handlers; + +import org.eclipse.e4.core.di.annotations.Execute; + +/** Does nothing; the tool items using it only show icons. */ +public class FakeHandler { + + @Execute + public void execute() { + } +} diff --git a/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/handlers/QuitHandler.java b/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/handlers/QuitHandler.java new file mode 100644 index 00000000000..890879ceb1d --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/handlers/QuitHandler.java @@ -0,0 +1,21 @@ +package org.eclipse.ui.examples.urlmodifier.handlers; + +import java.util.List; + +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.workbench.IWorkbench; +import org.eclipse.jface.dialogs.PlainMessageDialog; +import org.eclipse.swt.widgets.Shell; + +public class QuitHandler { + @Execute + public void execute(IWorkbench workbench, Shell shell) { + + PlainMessageDialog dialog = PlainMessageDialog.getBuilder(shell, "Confirmation") + .message("Do you want to exit?").buttonLabels(List.of("Exit", "Cancel")).build(); + + if (dialog.open() == 0) { + workbench.close(); + } + } +} diff --git a/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/handlers/SaveHandler.java b/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/handlers/SaveHandler.java new file mode 100644 index 00000000000..b086e410753 --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/handlers/SaveHandler.java @@ -0,0 +1,21 @@ +package org.eclipse.ui.examples.urlmodifier.handlers; + +import org.eclipse.e4.core.di.annotations.CanExecute; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.workbench.modeling.EPartService; + +public class SaveHandler { + + @CanExecute + public boolean canExecute(EPartService partService) { + if (partService != null) { + return !partService.getDirtyParts().isEmpty(); + } + return false; + } + + @Execute + public void execute(EPartService partService) { + partService.saveAll(false); + } +} \ No newline at end of file diff --git a/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/parts/SamplePart.java b/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/parts/SamplePart.java new file mode 100644 index 00000000000..35f67df29ce --- /dev/null +++ b/examples/org.eclipse.ui.examples.urlmodifier/src/org/eclipse/ui/examples/urlmodifier/parts/SamplePart.java @@ -0,0 +1,82 @@ +package org.eclipse.ui.examples.urlmodifier.parts; + +import java.util.Arrays; +import java.util.List; +import jakarta.annotation.PostConstruct; +import jakarta.inject.Inject; + +import org.eclipse.e4.ui.di.Focus; +import org.eclipse.e4.ui.di.Persist; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.workbench.IWorkbench; +import org.eclipse.jface.viewers.ArrayContentProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.widgets.ButtonFactory; +import org.eclipse.jface.widgets.LabelFactory; +import org.eclipse.jface.widgets.TextFactory; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.examples.urlmodifier.IconPacks; +import org.osgi.service.prefs.BackingStoreException; + +public class SamplePart { + + private TableViewer tableViewer; + + @Inject + private MPart part; + + @Inject + private IWorkbench workbench; + + @PostConstruct + public void createComposite(Composite parent) { + parent.setLayout(new GridLayout(1, false)); + + String pack = IconPacks.selected(); + LabelFactory.newLabel(SWT.NONE) // + .text(pack == null ? "Icon pack: none (default icons)" : "Icon pack: " + pack) // + .create(parent); + ButtonFactory.newButton(SWT.PUSH) // + .text(pack == null ? "Use the \"bold\" icon pack and restart" : "Use the default icons and restart") // + .onSelect(e -> switchPack(pack == null ? IconPacks.BOLD : null)) // + .create(parent); + + TextFactory.newText(SWT.BORDER) // + .message("Enter text to mark part as dirty") // + .onModify(e -> part.setDirty(true)) // + .layoutData(new GridData(GridData.FILL_HORIZONTAL))// + .create(parent); + + tableViewer = new TableViewer(parent); + + tableViewer.setContentProvider(ArrayContentProvider.getInstance()); + tableViewer.setInput(createInitialDataModel()); + tableViewer.getTable().setLayoutData(new GridData(GridData.FILL_BOTH)); + } + + private void switchPack(String pack) { + try { + IconPacks.select(pack); + } catch (BackingStoreException e) { + throw new IllegalStateException(e); + } + workbench.restart(); + } + + @Focus + public void setFocus() { + tableViewer.getTable().setFocus(); + } + + @Persist + public void save() { + part.setDirty(false); + } + + private List createInitialDataModel() { + return Arrays.asList("Sample item 1", "Sample item 2", "Sample item 3", "Sample item 4", "Sample item 5"); + } +} \ No newline at end of file diff --git a/examples/pom.xml b/examples/pom.xml index d362711e645..d0a0f69639c 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -38,6 +38,7 @@ org.eclipse.ui.examples.readmetool org.eclipse.ui.examples.undo org.eclipse.ui.examples.uriSchemeHandler + org.eclipse.ui.examples.urlmodifier org.eclipse.ui.examples.views.properties.tabbed.article org.eclipse.ui.forms.examples org.eclipse.ui.genericeditor.examples diff --git a/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/tests/UIAllTests.java b/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/tests/UIAllTests.java index f77ed00292a..9a9ef8c92ac 100644 --- a/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/tests/UIAllTests.java +++ b/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/tests/UIAllTests.java @@ -33,6 +33,7 @@ import org.eclipse.e4.ui.tests.workbench.ExtensionsSortTests; import org.eclipse.e4.ui.tests.workbench.HandlerActivationTest; import org.eclipse.e4.ui.tests.workbench.HandlerTest; +import org.eclipse.e4.ui.tests.workbench.ImageURLModifierTrackerTest; import org.eclipse.e4.ui.tests.workbench.InjectionEventTest; import org.eclipse.e4.ui.tests.workbench.MApplicationCommandAccessTest; import org.eclipse.e4.ui.tests.workbench.MMenuItemTest; @@ -101,6 +102,7 @@ ExtensionsSortTests.class, HandlerActivationTest.class, ModelAssemblerTests.class, + ImageURLModifierTrackerTest.class, ModelAssemblerFragmentOrderingTests.class, E4ResourceTest.class, AreaRendererTest.class, diff --git a/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/tests/workbench/ImageURLModifierTrackerTest.java b/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/tests/workbench/ImageURLModifierTrackerTest.java new file mode 100644 index 00000000000..2bbcb3f3886 --- /dev/null +++ b/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/tests/workbench/ImageURLModifierTrackerTest.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2026 vogella 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: + * Lars Vogel - initial API and implementation + *******************************************************************************/ +package org.eclipse.e4.ui.tests.workbench; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.Adapters; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.resource.IImageURLModifier; +import org.eclipse.jface.resource.ImageDescriptor; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceRegistration; + +/** + * Tests that the E4 workbench installs the highest ranked + * {@link IImageURLModifier} service in JFace. + */ +public class ImageURLModifierTrackerTest { + + private final BundleContext context = FrameworkUtil.getBundle(ImageURLModifierTrackerTest.class) + .getBundleContext(); + private final List> registrations = new ArrayList<>(); + private ImageDescriptor descriptor; + + @BeforeAll + public static void startWorkbenchBundle() throws Exception { + // lazily activated, and earlier tests may not have loaded its classes + Platform.getBundle("org.eclipse.e4.ui.workbench.swt").start(Bundle.START_TRANSIENT); + } + + @BeforeEach + public void setUp() throws Exception { + descriptor = ImageDescriptor.createFromURL(URI.create("file:/original.png").toURL()); + } + + @AfterEach + public void tearDown() { + for (ServiceRegistration registration : registrations) { + try { + registration.unregister(); + } catch (IllegalStateException e) { + // already unregistered by the test + } + } + } + + @Test + public void testHighestRankedModifierIsInstalled() { + register("a", 1); + register("b", 5); + register("c", 3); + + assertEquals("file:/b.png", resolvedURL()); + } + + @Test + public void testRemovingInstalledModifierFallsBackToNext() { + register("a", 1); + ServiceRegistration b = register("b", 5); + + b.unregister(); + assertEquals("file:/a.png", resolvedURL()); + + registrations.get(0).unregister(); + assertEquals("file:/original.png", resolvedURL()); + } + + @Test + public void testRankingChangeIsApplied() { + ServiceRegistration a = register("a", 1); + register("b", 5); + + a.setProperties(FrameworkUtil.asDictionary(Map.of(Constants.SERVICE_RANKING, 10))); + + assertEquals("file:/a.png", resolvedURL()); + } + + private ServiceRegistration register(String name, int ranking) { + IImageURLModifier modifier = _ -> { + try { + return URI.create("file:/" + name + ".png").toURL(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + }; + ServiceRegistration registration = context.registerService(IImageURLModifier.class, + modifier, FrameworkUtil.asDictionary(Map.of(Constants.SERVICE_RANKING, ranking))); + registrations.add(registration); + return registration; + } + + private String resolvedURL() { + return Adapters.adapt(descriptor, URL.class).toExternalForm(); + } +} diff --git a/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/images/UrlImageDescriptorTest.java b/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/images/UrlImageDescriptorTest.java index 31ef2696927..ad88d6ac8e0 100644 --- a/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/images/UrlImageDescriptorTest.java +++ b/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/images/UrlImageDescriptorTest.java @@ -33,6 +33,7 @@ import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.ImageFileNameProvider; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -202,4 +203,115 @@ public void testAdaptToURL() { assertEquals(imageDataOrig200.height, imageDataURL200.height); } -} \ No newline at end of file + @AfterEach + public void clearURLModifier() { + ImageDescriptor.setURLModifier(null); + } + + @Test + public void testNoURLModifierInstalledByDefault() { + ImageDescriptor descriptor = ImageDescriptor + .createFromURL(FileImageDescriptorTest.class.getResource("/icons/imagetests/zoomIn.png")); + assertEquals("zoomIn.png", IPath.fromOSString(imagePath(descriptor, 100)).lastSegment()); + } + + @Test + public void testURLModifierRedirectsImageData() { + URL zoomIn = FileImageDescriptorTest.class.getResource("/icons/imagetests/zoomIn.png"); + URL rectangular = FileImageDescriptorTest.class.getResource("/icons/imagetests/rectangular-57x16.png"); + ImageData original = ImageDescriptor.createFromURL(zoomIn).getImageData(100); + ImageData replacement = ImageDescriptor.createFromURL(rectangular).getImageData(100); + assertNotEquals(original.width, replacement.width, "test images are indistinguishable"); + + ImageDescriptor descriptor = ImageDescriptor.createFromURL(zoomIn); + ImageDescriptor.setURLModifier(url -> zoomIn.toExternalForm().equals(url.toExternalForm()) ? rectangular : url); + + assertEquals(replacement.width, descriptor.getImageData(100).width); + } + + @Test + public void testURLModifierLeavesUnmatchedURLsAlone() { + ImageDescriptor descriptor = ImageDescriptor + .createFromURL(FileImageDescriptorTest.class.getResource("/icons/imagetests/zoomIn.png")); + ImageDescriptor.setURLModifier(url -> url); + + assertEquals("zoomIn.png", IPath.fromOSString(imagePath(descriptor, 100)).lastSegment()); + } + + @Test + public void testURLModifierReturningNullKeepsOriginalURL() { + ImageDescriptor descriptor = ImageDescriptor + .createFromURL(FileImageDescriptorTest.class.getResource("/icons/imagetests/zoomIn.png")); + ImageDescriptor.setURLModifier(_ -> null); + + assertEquals("zoomIn.png", IPath.fromOSString(imagePath(descriptor, 100)).lastSegment()); + } + + @Test + public void testURLModifierIsClearedByNull() { + URL zoomIn = FileImageDescriptorTest.class.getResource("/icons/imagetests/zoomIn.png"); + URL rectangular = FileImageDescriptorTest.class.getResource("/icons/imagetests/rectangular-57x16.png"); + ImageDescriptor descriptor = ImageDescriptor.createFromURL(zoomIn); + + ImageDescriptor.setURLModifier(_ -> rectangular); + assertEquals("rectangular-57x16.png", IPath.fromOSString(imagePath(descriptor, 100)).lastSegment()); + + ImageDescriptor.setURLModifier(null); + assertEquals("zoomIn.png", IPath.fromOSString(imagePath(descriptor, 100)).lastSegment()); + } + + /** + * The @2x variant is derived from the rewritten URL, so an icon pack only + * supplies the base name. + */ + @Test + public void testURLModifierIsAppliedBeforeHiDpiLookup() { + URL rectangular = FileImageDescriptorTest.class.getResource("/icons/imagetests/rectangular-57x16.png"); + URL zoomIn = FileImageDescriptorTest.class.getResource("/icons/imagetests/zoomIn.png"); + ImageDescriptor descriptor = ImageDescriptor.createFromURL(rectangular); + + ImageDescriptor.setURLModifier(url -> url.getPath().contains("rectangular-57x16") ? zoomIn : url); + + assertEquals("zoomIn.png", IPath.fromOSString(imagePath(descriptor, 100)).lastSegment()); + assertEquals("zoomIn@2x.png", IPath.fromOSString(imagePath(descriptor, 200)).lastSegment()); + } + + /** + * The scaled name fallback is derived from the rewritten URL too, so an icon + * pack is not mixed with platform icons at higher zoom levels. + */ + @Test + public void testURLModifierIsAppliedBeforeScaledNameLookup() { + URL zoomIn = FileImageDescriptorTest.class.getResource("/icons/imagetests/zoomIn.png"); + URL rectangular = FileImageDescriptorTest.class.getResource("/icons/imagetests/rectangular-57x16.png"); + ImageDescriptor descriptor = ImageDescriptor.createFromURL(zoomIn); + + ImageDescriptor.setURLModifier(url -> url.getPath().endsWith("/zoomIn.png") ? rectangular : url); + + assertEquals("rectangular-114x32.png", IPath.fromOSString(imagePath(descriptor, 200)).lastSegment()); + assertEquals("rectangular-86x24.png", IPath.fromOSString(imagePath(descriptor, 150)).lastSegment()); + assertEquals(114, descriptor.getImageData(200).width); + } + + @Test + public void testURLModifierIsAppliedToAdaptedURL() { + URL zoomIn = FileImageDescriptorTest.class.getResource("/icons/imagetests/zoomIn.png"); + URL rectangular = FileImageDescriptorTest.class.getResource("/icons/imagetests/rectangular-57x16.png"); + ImageDescriptor descriptor = ImageDescriptor.createFromURL(zoomIn); + + ImageDescriptor.setURLModifier(_ -> rectangular); + + URL adapted = Adapters.adapt(descriptor, URL.class); + assertNotNull(adapted, "URLImageDescriptor does not adapt to URL"); + assertEquals(rectangular.toExternalForm(), adapted.toExternalForm()); + } + + private static String imagePath(ImageDescriptor descriptor, int zoom) { + ImageFileNameProvider fileNameProvider = Adapters.adapt(descriptor, ImageFileNameProvider.class); + assertNotNull(fileNameProvider, "URLImageDescriptor does not adapt to ImageFileNameProvider"); + String path = fileNameProvider.getImagePath(zoom); + assertNotNull(path, "URLImageDescriptor ImageFileNameProvider does not return the " + zoom + "% path"); + return path; + } + +}