From 9b87c23aacfd4a8e8a557e4d96665ed7fad52374 Mon Sep 17 00:00:00 2001 From: Sebastian Ratz Date: Mon, 8 Jul 2024 14:31:54 +0100 Subject: [PATCH] Product file editor: Support for on macOS Support editing of entries in the product editor. These are translated to CFBundleURLTypes entries in the Info.plist dictionary of the macOS app bundle. Contributes to https://github.com/eclipse-platform/eclipse.platform.ui/issues/1901. --- .../internal/core/iproduct/ILauncherInfo.java | 36 ++- .../core/iproduct/IMacBundleUrlType.java | 26 ++ .../core/iproduct/IProductModelFactory.java | 5 +- .../internal/core/product/LauncherInfo.java | 82 +++++- .../core/product/MacBundleUrlType.java | 68 +++++ .../core/product/ProductModelFactory.java | 9 +- .../pde/internal/ui/PDEUIMessages.java | 15 +- .../ui/editor/product/LauncherSection.java | 255 +++++++++++++++++- .../pde/internal/ui/pderesources.properties | 17 +- 9 files changed, 489 insertions(+), 24 deletions(-) create mode 100644 ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/iproduct/IMacBundleUrlType.java create mode 100644 ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/product/MacBundleUrlType.java diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/iproduct/ILauncherInfo.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/iproduct/ILauncherInfo.java index 8db2f35c4b..5b4fd173f6 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/iproduct/ILauncherInfo.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/iproduct/ILauncherInfo.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2016 IBM Corporation and others. + * Copyright (c) 2005, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -11,26 +11,29 @@ * Contributors: * IBM Corporation - initial API and implementation * Martin Karpisek - Bug 438509 + * SAP SE - support macOS bundle URL types *******************************************************************************/ package org.eclipse.pde.internal.core.iproduct; +import java.util.List; + public interface ILauncherInfo extends IProductObject { - public static final String LINUX_ICON = "linuxIcon"; //$NON-NLS-1$ + String LINUX_ICON = "linuxIcon"; //$NON-NLS-1$ - public static final String MACOSX_ICON = "macosxIcon"; //$NON-NLS-1$ + String MACOSX_ICON = "macosxIcon"; //$NON-NLS-1$ - public static final String WIN32_16_LOW = "winSmallLow"; //$NON-NLS-1$ - public static final String WIN32_16_HIGH = "winSmallHigh"; //$NON-NLS-1$ - public static final String WIN32_32_LOW = "winMediumLow"; //$NON-NLS-1$ - public static final String WIN32_32_HIGH = "winMediumHigh"; //$NON-NLS-1$ - public static final String WIN32_48_LOW = "winLargeLow"; //$NON-NLS-1$ - public static final String WIN32_48_HIGH = "winLargeHigh"; //$NON-NLS-1$ - public static final String WIN32_256_HIGH = "winExtraLargeHigh"; //$NON-NLS-1$ + String WIN32_16_LOW = "winSmallLow"; //$NON-NLS-1$ + String WIN32_16_HIGH = "winSmallHigh"; //$NON-NLS-1$ + String WIN32_32_LOW = "winMediumLow"; //$NON-NLS-1$ + String WIN32_32_HIGH = "winMediumHigh"; //$NON-NLS-1$ + String WIN32_48_LOW = "winLargeLow"; //$NON-NLS-1$ + String WIN32_48_HIGH = "winLargeHigh"; //$NON-NLS-1$ + String WIN32_256_HIGH = "winExtraLargeHigh"; //$NON-NLS-1$ - public static final String P_USE_ICO = "useIco"; //$NON-NLS-1$ - public static final String P_ICO_PATH = "icoFile"; //$NON-NLS-1$ - public static final String P_LAUNCHER = "launcher"; //$NON-NLS-1$ + String P_USE_ICO = "useIco"; //$NON-NLS-1$ + String P_ICO_PATH = "icoFile"; //$NON-NLS-1$ + String P_LAUNCHER = "launcher"; //$NON-NLS-1$ String getLauncherName(); @@ -43,4 +46,11 @@ public interface ILauncherInfo extends IProductObject { boolean usesWinIcoFile(); void setUseWinIcoFile(boolean use); + + List getMacBundleUrlTypes(); + + void addMacBundleUrlTypes(List schemes); + + void removeMacBundleUrlTypes(List schemes); + } diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/iproduct/IMacBundleUrlType.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/iproduct/IMacBundleUrlType.java new file mode 100644 index 0000000000..def18a634f --- /dev/null +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/iproduct/IMacBundleUrlType.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2024 SAP SE 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: + * SAP SE - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.core.iproduct; + +public interface IMacBundleUrlType extends IProductObject { + + String getScheme(); + + void setScheme(String scheme); + + String getName(); + + void setName(String name); + +} diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/iproduct/IProductModelFactory.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/iproduct/IProductModelFactory.java index 98ecec815e..2072445d6d 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/iproduct/IProductModelFactory.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/iproduct/IProductModelFactory.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2014 IBM Corporation and others. + * Copyright (c) 2005, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -12,6 +12,7 @@ * IBM Corporation - initial API and implementation * Code 9 Corporation - ongoing enhancements * Rapicorp Corporation - ongoing enhancements + * SAP SE - support macOS bundle URL types *******************************************************************************/ package org.eclipse.pde.internal.core.iproduct; @@ -51,4 +52,6 @@ public interface IProductModelFactory { ICSSInfo createCSSInfo(); + IMacBundleUrlType createMacBundleUrlType(); + } diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/product/LauncherInfo.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/product/LauncherInfo.java index cf5d8056f0..f7d8b320eb 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/product/LauncherInfo.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/product/LauncherInfo.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2016 IBM Corporation and others. + * Copyright (c) 2005, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -11,15 +11,22 @@ * Contributors: * IBM Corporation - initial API and implementation * Martin Karpisek - Bug 438509 + * SAP SE - support macOS bundle URL types *******************************************************************************/ package org.eclipse.pde.internal.core.product; import java.io.PrintWriter; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; +import org.eclipse.pde.core.IModelChangedEvent; import org.eclipse.pde.internal.core.iproduct.ILauncherInfo; +import org.eclipse.pde.internal.core.iproduct.IMacBundleUrlType; import org.eclipse.pde.internal.core.iproduct.IProductModel; +import org.eclipse.pde.internal.core.iproduct.IProductObject; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -30,6 +37,7 @@ public class LauncherInfo extends ProductObject implements ILauncherInfo { private boolean fUseIcoFile; private final Map fIcons = new HashMap<>(); private String fLauncherName; + private final TreeMap fMacBundleUrlTypes = new TreeMap<>(); public LauncherInfo(IProductModel model) { super(model); @@ -131,6 +139,26 @@ private void parseWin(Element element) { private void parseMac(Element element) { fIcons.put(MACOSX_ICON, element.getAttribute("icon")); //$NON-NLS-1$ + NodeList children = element.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + if (children.item(i).getNodeType() == Node.ELEMENT_NODE) { + Node child = children.item(i); + String name = child.getNodeName(); + if (name.equals("bundleUrlTypes")) { //$NON-NLS-1$ + NodeList grandChildren = child.getChildNodes(); + for (int j = 0; j < grandChildren.getLength(); j++) { + Node grandChild = grandChildren.item(j); + if (grandChild.getNodeType() == Node.ELEMENT_NODE) { + if (grandChild.getNodeName().equals("bundleUrlType")) { //$NON-NLS-1$ + IMacBundleUrlType bundleUrlType = getModel().getFactory().createMacBundleUrlType(); + bundleUrlType.parse(grandChild); + fMacBundleUrlTypes.put(bundleUrlType.getScheme(), bundleUrlType); + } + } + } + } + } + } } private void parseLinux(Element element) { @@ -180,8 +208,22 @@ private void writeIcon(String indent, String iconId, PrintWriter writer) { private void writeMac(String indent, PrintWriter writer) { String icon = fIcons.get(MACOSX_ICON); - if (icon != null && icon.length() > 0) { - writer.println(indent + ""); //$NON-NLS-1$ //$NON-NLS-2$ + if (icon != null && icon.length() > 0 || !fMacBundleUrlTypes.isEmpty()) { + writer.print(indent + " 0) { + writer.print(" icon=\"" + getWritableString(icon) + "\""); //$NON-NLS-1$ //$NON-NLS-2$ + } + if (fMacBundleUrlTypes.isEmpty()) { + writer.println("/>"); //$NON-NLS-1$ + } else { + writer.println(">"); //$NON-NLS-1$ + writer.println(indent + " "); //$NON-NLS-1$ + for (IMacBundleUrlType bundleUrlType : fMacBundleUrlTypes.values()) { + bundleUrlType.write(indent + " ", writer); //$NON-NLS-1$ + } + writer.println(indent + " "); //$NON-NLS-1$ + writer.println(indent + ""); //$NON-NLS-1$ + } } } @@ -192,4 +234,38 @@ private void writeLinux(String indent, PrintWriter writer) { } } + @Override + public List getMacBundleUrlTypes() { + return List.copyOf(fMacBundleUrlTypes.values()); + } + + @Override + public void addMacBundleUrlTypes(List bundleUrlTypes) { + IMacBundleUrlType[] addedUrlSchemes = bundleUrlTypes.stream().map(urlType -> { + if (urlType != null) { + String scheme = urlType.getScheme(); + if (scheme != null && !fMacBundleUrlTypes.containsKey(scheme)) { + urlType.setModel(getModel()); + fMacBundleUrlTypes.put(scheme, urlType); + return urlType; + } + } + return null; + }).filter(Objects::nonNull).toArray(IMacBundleUrlType[]::new); + if (addedUrlSchemes.length > 0 && isEditable()) { + fireStructureChanged(addedUrlSchemes, IModelChangedEvent.INSERT); + } + } + + @Override + public void removeMacBundleUrlTypes(List bundleUrlTypes) { + IProductObject[] removedUrlSchemes = bundleUrlTypes.stream() // + .map(IMacBundleUrlType::getScheme).map(fMacBundleUrlTypes::remove) // + .filter(Objects::nonNull).toArray(IProductObject[]::new); + + if (removedUrlSchemes.length > 0 && isEditable()) { + fireStructureChanged(removedUrlSchemes, IModelChangedEvent.REMOVE); + } + } + } diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/product/MacBundleUrlType.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/product/MacBundleUrlType.java new file mode 100644 index 0000000000..65d34cae93 --- /dev/null +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/product/MacBundleUrlType.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2024 SAP SE 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: + * SAP SE - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.core.product; + +import java.io.PrintWriter; + +import org.eclipse.pde.internal.core.iproduct.IMacBundleUrlType; +import org.eclipse.pde.internal.core.iproduct.IProductModel; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +public class MacBundleUrlType extends ProductObject implements IMacBundleUrlType { + + private static final long serialVersionUID = 1L; + private String fScheme; + private String fName; + + public MacBundleUrlType(IProductModel model) { + super(model); + } + + @Override + public void parse(Node node) { + if (node.getNodeType() == Node.ELEMENT_NODE) { + Element element = (Element) node; + fScheme = element.getAttribute("scheme"); //$NON-NLS-1$ + fName = element.getAttribute("name"); //$NON-NLS-1$ + } + } + + @Override + public void write(String indent, PrintWriter writer) { + writer.println(indent + ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + + + @Override + public String getScheme() { + return fScheme; + } + + @Override + public void setScheme(String scheme) { + fScheme = scheme; + } + + @Override + public String getName() { + return fName; + } + + @Override + public void setName(String name) { + fName = name; + } + +} diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/product/ProductModelFactory.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/product/ProductModelFactory.java index 6d0afece80..c23af892d4 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/product/ProductModelFactory.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/product/ProductModelFactory.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2014 IBM Corporation and others. + * Copyright (c) 2005, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -12,6 +12,7 @@ * IBM Corporation - initial API and implementation * EclipseSource Corporation - ongoing enhancements * Rapicorp Corporation - ongoing enhancements + * SAP SE - support macOS bundle URL types *******************************************************************************/ package org.eclipse.pde.internal.core.product; @@ -24,6 +25,7 @@ import org.eclipse.pde.internal.core.iproduct.IJREInfo; import org.eclipse.pde.internal.core.iproduct.ILauncherInfo; import org.eclipse.pde.internal.core.iproduct.ILicenseInfo; +import org.eclipse.pde.internal.core.iproduct.IMacBundleUrlType; import org.eclipse.pde.internal.core.iproduct.IPluginConfiguration; import org.eclipse.pde.internal.core.iproduct.IPreferencesInfo; import org.eclipse.pde.internal.core.iproduct.IProduct; @@ -128,4 +130,9 @@ public ICSSInfo createCSSInfo() { return new CSSInfo(fModel); } + @Override + public IMacBundleUrlType createMacBundleUrlType() { + return new MacBundleUrlType(fModel); + } + } diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java index 57ab4acfdc..3eea5272e9 100644 --- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014, 2023 IBM Corporation and others. + * Copyright (c) 2014, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -17,6 +17,7 @@ * Axel Richard (Obeo) - Bug 41353 - Launch configurations prototypes * Kit Lo (IBM) - Bug 244461 - Duplicating colon in error message * Alexander Fedorov - Bug 547222 + * SAP SE - support macOS bundle URL types *******************************************************************************/ package org.eclipse.pde.internal.ui; @@ -1176,6 +1177,18 @@ public class PDEUIMessages extends NLS { public static String LauncherSection_256High; public static String LauncherSection_linuxLabel; public static String LauncherSection_macLabel; + public static String LauncherSection_macBundleUrlTypes_DialogTitle; + public static String LauncherSection_macBundleUrlTypes_Scheme; + public static String LauncherSection_macBundleUrlTypes_Name; + public static String LauncherSection_macBundleUrlTypes_ErrorNoScheme; + public static String LauncherSection_macBundleUrlTypes_ErrorNoName; + public static String LauncherSection_macBundleUrlTypes_ErrorSchemeExists; + public static String LauncherSection_macBundleUrlTypes_Add; + public static String LauncherSection_macBundleUrlTypes_Edit; + public static String LauncherSection_macBundleUrlTypes_Remove; + public static String LauncherSection_macBundleUrlTypes_LauncherSection_macBundleUrlTypesTitle; + public static String LauncherSection_macBundleUrlTypes_SchemeColumn; + public static String LauncherSection_macBundleUrlTypes_NameColumn; public static String OSGiBundlesTab_frameworkLabel; // Preferences #################################### diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/product/LauncherSection.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/product/LauncherSection.java index 3a5a5d6a9d..3cf6c7a45a 100644 --- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/product/LauncherSection.java +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/product/LauncherSection.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2016 IBM Corporation and others. + * Copyright (c) 2005, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -11,29 +11,48 @@ * Contributors: * IBM Corporation - initial API and implementation * Martin Karpisek - Bug 438509 + * SAP SE - support macOS bundle URL types *******************************************************************************/ package org.eclipse.pde.internal.ui.editor.product; import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter; import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Status; import org.eclipse.equinox.bidi.StructuredTextTypeHandlerFactory; import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.StatusDialog; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.util.BidiUtils; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.jface.window.Window; +import org.eclipse.osgi.util.NLS; import org.eclipse.pde.core.IModelChangedEvent; import org.eclipse.pde.core.plugin.TargetPlatform; import org.eclipse.pde.internal.core.iproduct.ILauncherInfo; +import org.eclipse.pde.internal.core.iproduct.IMacBundleUrlType; import org.eclipse.pde.internal.core.iproduct.IProduct; import org.eclipse.pde.internal.core.iproduct.IProductModel; +import org.eclipse.pde.internal.core.iproduct.IProductModelFactory; +import org.eclipse.pde.internal.ui.PDELabelProvider; import org.eclipse.pde.internal.ui.PDEPlugin; import org.eclipse.pde.internal.ui.PDEPluginImages; import org.eclipse.pde.internal.ui.PDEUIMessages; +import org.eclipse.pde.internal.ui.SWTFactory; import org.eclipse.pde.internal.ui.editor.EditorUtilities; import org.eclipse.pde.internal.ui.editor.FormEntryAdapter; import org.eclipse.pde.internal.ui.editor.FormLayoutFactory; @@ -41,6 +60,7 @@ import org.eclipse.pde.internal.ui.editor.PDESection; import org.eclipse.pde.internal.ui.editor.validation.TextValidator; import org.eclipse.pde.internal.ui.parts.FormEntry; +import org.eclipse.pde.internal.ui.parts.TablePart; import org.eclipse.pde.internal.ui.util.FileExtensionFilter; import org.eclipse.pde.internal.ui.util.FileValidator; import org.eclipse.swt.SWT; @@ -48,7 +68,10 @@ import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.custom.StackLayout; import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; @@ -57,6 +80,9 @@ import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Layout; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IActionBars; import org.eclipse.ui.PartInitException; @@ -92,6 +118,7 @@ public class LauncherSection extends PDESection { private Composite fLinuxSection; private Composite fMacSection; private Composite fWin32Section; + private MacBundleUrlTypesComposite fMacBundleUrlTypesComposite; class IconEntry extends FormEntry { String fIconId; @@ -323,11 +350,229 @@ private Composite addLinuxSection(Composite parent, FormToolkit toolkit) { } private Composite addMacSection(Composite parent, FormToolkit toolkit) { - Composite comp = createComposite(parent, toolkit); + Composite compOuter = toolkit.createComposite(parent); + GridLayoutFactory.fillDefaults().numColumns(2).applyTo(compOuter); + Composite comp = createComposite(compOuter, toolkit); + GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(comp); createLabel(comp, toolkit, PDEUIMessages.LauncherSection_macLabel, 3); fIcons.add(new IconEntry(comp, toolkit, PDEUIMessages.LauncherSection_file, ILauncherInfo.MACOSX_ICON)); + fMacBundleUrlTypesComposite = new MacBundleUrlTypesComposite(compOuter, toolkit); toolkit.paintBordersFor(comp); - return comp; + return compOuter; + } + + private class MacBundleUrlTypesComposite { + + private MacBundleUrlTypesPartAdapter fTablePart; + private TableViewer fBundleUrlTypesTable; + + private class LabelProvider extends PDELabelProvider { + @Override + public Image getColumnImage(Object obj, int index) { + return null; + } + + @Override + public String getColumnText(Object obj, int index) { + IMacBundleUrlType bundleUrlType = (IMacBundleUrlType) obj; + return switch (index) { + case 0 -> bundleUrlType.getScheme(); + case 1 -> bundleUrlType.getName(); + default -> null; + }; + } + } + + public MacBundleUrlTypesComposite(Composite comp, FormToolkit toolkit) { + createLabel(comp, toolkit, + PDEUIMessages.LauncherSection_macBundleUrlTypes_LauncherSection_macBundleUrlTypesTitle, 2); + fTablePart = new MacBundleUrlTypesPartAdapter( + new String[] { PDEUIMessages.LauncherSection_macBundleUrlTypes_Add, + PDEUIMessages.LauncherSection_macBundleUrlTypes_Edit, + PDEUIMessages.LauncherSection_macBundleUrlTypes_Remove }); + fTablePart.createControl(comp, SWT.FULL_SELECTION, 1, toolkit); + fBundleUrlTypesTable = (TableViewer) fTablePart.getViewer(); + fBundleUrlTypesTable.setComparator(new ViewerComparator()); + fBundleUrlTypesTable.addDoubleClickListener(event -> handleEdit()); + fBundleUrlTypesTable.getTable().addKeyListener(KeyListener.keyReleasedAdapter(e -> { + if (e.keyCode == SWT.DEL) { + handleRemove(); + } + })); + + Table table = fBundleUrlTypesTable.getTable(); + TableColumn schemeColumn = new TableColumn(table, SWT.LEFT); + schemeColumn.setText(PDEUIMessages.LauncherSection_macBundleUrlTypes_SchemeColumn); + schemeColumn.setWidth(160); + + TableColumn nameColumn = new TableColumn(table, SWT.LEFT); + nameColumn.setText(PDEUIMessages.LauncherSection_macBundleUrlTypes_NameColumn); + nameColumn.setWidth(220); + + table.addControlListener(ControlListener.controlResizedAdapter(e -> { + int size = table.getSize().x; + schemeColumn.setWidth(size / 10 * 4); + nameColumn.setWidth(size / 10 * 6); + })); + + table.setHeaderVisible(true); + fBundleUrlTypesTable.setLabelProvider(new LabelProvider()); + fBundleUrlTypesTable.setContentProvider((IStructuredContentProvider) inputElement -> { + if (inputElement instanceof IProduct product) { + return product.getLauncherInfo().getMacBundleUrlTypes().toArray(); + } + return new Object[0]; + }); + fBundleUrlTypesTable.setInput(getProduct()); + + updateButtons(); + } + + private class MacBundleUrlTypesPartAdapter extends TablePart { + + public MacBundleUrlTypesPartAdapter(String[] buttonLabels) { + super(buttonLabels); + } + + @Override + public void selectionChanged(IStructuredSelection selection) { + getManagedForm().fireSelectionChanged(LauncherSection.this, selection); + updateButtons(); + } + + @Override + public void buttonSelected(Button button, int index) { + switch (index) { + case 0 -> handleAdd(); + case 1 -> handleEdit(); + case 2 -> handleRemove(); + } + } + } + + private void handleAdd() { + openBundleURLTypeDialog(null, getExistingBundleUrlTypes()); + } + + private void handleEdit() { + IStructuredSelection ssel = fBundleUrlTypesTable.getStructuredSelection(); + if (!ssel.isEmpty() && ssel.getFirstElement() instanceof IMacBundleUrlType propertyToEdit) { + Set existing = getExistingBundleUrlTypes(); + existing.remove(propertyToEdit); + openBundleURLTypeDialog(propertyToEdit, existing); + } + } + + private void openBundleURLTypeDialog(IMacBundleUrlType propertyToEdit, Set existing) { + BundleUrlTypeDialog dialog = new BundleUrlTypeDialog(PDEPlugin.getActiveWorkbenchShell(), propertyToEdit, + existing); + if (dialog.open() == Window.OK) { + IMacBundleUrlType result = dialog.getResult(); + if (result != null) { + fBundleUrlTypesTable.refresh(); + fBundleUrlTypesTable.setSelection(new StructuredSelection(result)); + updateButtons(); + } + } + } + + private Set getExistingBundleUrlTypes() { + return new HashSet<>(getProduct().getLauncherInfo().getMacBundleUrlTypes()); + } + + private void handleRemove() { + IStructuredSelection ssel = fBundleUrlTypesTable.getStructuredSelection(); + if (!ssel.isEmpty()) { + List bundleUrlTypes = ssel.toList(); + getProduct().getLauncherInfo().removeMacBundleUrlTypes(bundleUrlTypes); + fBundleUrlTypesTable.refresh(false); + } + } + + private void updateButtons() { + ISelection selection = fBundleUrlTypesTable.getStructuredSelection(); + fTablePart.setButtonEnabled(0, isEditable()); + fTablePart.setButtonEnabled(1, isEditable() && !selection.isEmpty()); + fTablePart.setButtonEnabled(2, isEditable() && !selection.isEmpty()); + } + + private class BundleUrlTypeDialog extends StatusDialog { + + private Text fScheme; + private Text fName; + private IMacBundleUrlType fEdit; + private final Set fExistingBundleUrlTypes; + + public BundleUrlTypeDialog(Shell shell, IMacBundleUrlType bundleUrlType, + Set existingBundleUrlTypes) { + super(shell); + fEdit = bundleUrlType; + fExistingBundleUrlTypes = existingBundleUrlTypes; + setTitle(PDEUIMessages.LauncherSection_macBundleUrlTypes_DialogTitle); + } + + @Override + protected Control createDialogArea(Composite parent) { + Composite comp = (Composite) super.createDialogArea(parent); + ((GridLayout) comp.getLayout()).numColumns = 2; + SWTFactory.createLabel(comp, PDEUIMessages.LauncherSection_macBundleUrlTypes_Scheme, 1); + fScheme = SWTFactory.createSingleText(comp, 1); + fScheme.addModifyListener(e -> validate()); + SWTFactory.createLabel(comp, PDEUIMessages.LauncherSection_macBundleUrlTypes_Name, 1); + fName = SWTFactory.createSingleText(comp, 1); + fName.addModifyListener(e -> validate()); + + if (fEdit != null) { + if (fEdit.getScheme() != null) { + fScheme.setText(fEdit.getScheme()); + } + if (fEdit.getName() != null) { + fName.setText(fEdit.getName()); + } + } + // Disable ok button on startup + updateStatus(Status.error("")); //$NON-NLS-1$ + return comp; + } + + protected void validate() { + String scheme = fScheme.getText().trim(); + String name = fName.getText().trim(); + if (scheme.length() == 0) { + updateStatus(Status.error(PDEUIMessages.LauncherSection_macBundleUrlTypes_ErrorNoScheme)); + } else if (name.length() == 0) { + updateStatus(Status.error(PDEUIMessages.LauncherSection_macBundleUrlTypes_ErrorNoName)); + } else if (fExistingBundleUrlTypes.stream().map(IMacBundleUrlType::getScheme) + .anyMatch(scheme::equals)) { + updateStatus(Status.error( + NLS.bind(PDEUIMessages.LauncherSection_macBundleUrlTypes_ErrorSchemeExists, scheme))); + } else { + updateStatus(Status.OK_STATUS); + } + } + + @Override + protected void okPressed() { + if (fEdit != null) { + getProduct().getLauncherInfo().removeMacBundleUrlTypes(List.of(fEdit)); + } + IProductModelFactory factory = getModel().getFactory(); + fEdit = factory.createMacBundleUrlType(); + fEdit.setScheme(fScheme.getText().trim()); + fEdit.setName(fName.getText().trim()); + getProduct().getLauncherInfo().addMacBundleUrlTypes(List.of(fEdit)); + super.okPressed(); + } + + @Override + protected Control createHelpControl(Composite parent) { + return parent; + } + + public IMacBundleUrlType getResult() { + return fEdit; + } + } } private Composite createComposite(Composite parent, FormToolkit toolkit) { @@ -358,6 +603,10 @@ public void refresh() { updateWinEntries(useIco); + fMacBundleUrlTypesComposite.fBundleUrlTypesTable.setInput(getProduct()); + fMacBundleUrlTypesComposite.fBundleUrlTypesTable.refresh(); + fMacBundleUrlTypesComposite.updateButtons(); + super.refresh(); } diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties index e2a84ecfa2..b1ef2e0fa9 100644 --- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2000, 2023 IBM Corporation and others. +# Copyright (c) 2000, 2024 IBM Corporation and others. # # This program and the accompanying materials # are made available under the terms of the Eclipse Public License 2.0 @@ -19,6 +19,7 @@ # Axel Richard (Obeo) - Bug 41353 - Launch configurations prototypes # Kit Lo (IBM) - Bug 244461 - Duplicating colon in error message # Alexander Fedorov - Bug 547222 +# SAP SE - support macOS bundle URL types ############################################################################### ##################################### # PDE resource strings @@ -660,7 +661,7 @@ LauncherUtils_generateConfigIni=The config.ini template file you specified does Launcher_error_displayInSystemEditor=Yes, in an editor LauncherSection_browse=Browse... LauncherSection_title=Program Launcher -LauncherSection_label=Customizing the launcher icon varies per platform. +LauncherSection_label=Further customization of the launcher varies per platform. LauncherSection_bmpImages=Specify 7 separate BMP images: LauncherSection_Low16=16x16 (8-bit): LauncherSection_High16=16x16 (32-bit): @@ -671,6 +672,18 @@ LauncherSection_48High=48x48 (32-bit): LauncherSection_256High=256x256 (32-bit): LauncherSection_linuxLabel=A single XPM icon is required: LauncherSection_macLabel=A single ICNS file is required: +LauncherSection_macBundleUrlTypes_DialogTitle=Scheme +LauncherSection_macBundleUrlTypes_Scheme=Scheme +LauncherSection_macBundleUrlTypes_Name=Name +LauncherSection_macBundleUrlTypes_ErrorNoScheme=Scheme is not set +LauncherSection_macBundleUrlTypes_ErrorNoName=Name is not set +LauncherSection_macBundleUrlTypes_ErrorSchemeExists=Specified scheme already exists +LauncherSection_macBundleUrlTypes_Add=Add... +LauncherSection_macBundleUrlTypes_Edit=Edit... +LauncherSection_macBundleUrlTypes_Remove=Remove +LauncherSection_macBundleUrlTypes_LauncherSection_macBundleUrlTypesTitle=URL schemes to be handled by the macOS app bundle: +LauncherSection_macBundleUrlTypes_SchemeColumn=Scheme +LauncherSection_macBundleUrlTypes_NameColumn=Name OpenSchemaAction_msgUnknown=Unknown ###### Preferences ####################################