blob: 464a4a1ccd027a5f252650b07dd2aae1555aa365 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018, 2021 SAP SE and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* SAP SE - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.internal.ide.application.dialogs;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UriHandlerPreferencePage_Confirm_Handle;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UriHandlerPreferencePage_Warning_OtherApp;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UriHandlerPreferencePage_Warning_OtherApp_Confirmation;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UriHandlerPreferencePage_Warning_OtherApp_Confirmation_Description;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UriHandlerPreferencePage_Warning_OtherApp_Description;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_ColumnName_Handler;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_ColumnName_SchemeDescription;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_ColumnName_SchemeName;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_Column_Handler_Text_Current_Application;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_Column_Handler_Text_Other_Application;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_Error_Reading_Scheme;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_Error_Writing_Scheme;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_Handler_Label;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_Handler_Text_No_Application;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_LauncherCannotBeDetermined;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_LoadingText;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_Page_Description;
import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_UnsupportedOperatingSystem;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.layout.PixelConverter;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.widgets.TableColumnFactory;
import org.eclipse.jface.widgets.WidgetFactory;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.IWorkbenchHelpContextIds;
import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.urischeme.IOperatingSystemRegistration;
import org.eclipse.urischeme.IScheme;
import org.eclipse.urischeme.ISchemeInformation;
import org.eclipse.urischeme.IUriSchemeExtensionReader;
/**
* This page contributes to URL handler for URISchemes in preference page of
* General section
*
*/
public class UriSchemeHandlerPreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
Text handlerLocation;
CheckboxTableViewer tableViewer;
private String currentLocation = null;
IStatusManagerWrapper statusManagerWrapper = new IStatusManagerWrapper() {
};
IMessageDialogWrapper messageDialogWrapper = new IMessageDialogWrapper() {
};
OsRegistrationReadingJob osRegistrationReadingJob = new OsRegistrationReadingJob();
IOperatingSystemRegistration operatingSystemRegistration = null;
IUriSchemeExtensionReader extensionReader = null;
private Composite handlerComposite;
private volatile boolean isLoading = false;
@SuppressWarnings("javadoc")
public UriSchemeHandlerPreferencePage() {
super.setDescription(UrlHandlerPreferencePage_Page_Description);
}
@Override
public void init(IWorkbench workbench) {
if (operatingSystemRegistration == null) {
operatingSystemRegistration = IOperatingSystemRegistration.getInstance();
}
if (extensionReader == null) {
extensionReader = IUriSchemeExtensionReader.newInstance();
}
if (operatingSystemRegistration != null) {
currentLocation = operatingSystemRegistration.getEclipseLauncher();
}
}
@Override
protected Control createContents(Composite parent) {
if (PlatformUI.isWorkbenchRunning()) {
// enabled plain JUnit tests
PlatformUI.getWorkbench().getHelpSystem().setHelp(parent,
IWorkbenchHelpContextIds.LINK_HANDLERS_PREFERENCE_PAGE);
}
noDefaultAndApplyButton();
addFiller(parent);
createTableViewerForSchemes(parent);
createHandlerLocationControls(parent);
if (operatingSystemRegistration == null) {
setErrorMessage(NLS.bind(UrlHandlerPreferencePage_UnsupportedOperatingSystem,
Platform.isRunning() ? Platform.getOS() : null)); // running check for plain JUnit tests
setDataOnTableViewer(Collections.emptyList());
} else if (currentLocation == null) {
setErrorMessage(UrlHandlerPreferencePage_LauncherCannotBeDetermined);
setDataOnTableViewer(Collections.emptyList());
} else {
setDataOnTableViewer(getLoadingSchemeInformationList());
startRegistrationReadingJob();
}
tableViewer.getControl().setEnabled(false);
return parent;
}
private void startRegistrationReadingJob() {
isLoading = true;
osRegistrationReadingJob.setSystem(true);
osRegistrationReadingJob.schedule();
}
private void addFiller(Composite composite) {
PixelConverter pixelConverter = new PixelConverter(composite);
GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
gd.horizontalSpan = 2;
gd.heightHint = pixelConverter.convertHeightInCharsToPixels(1) / 2;
WidgetFactory.label(SWT.LEFT).layoutData(gd).create(composite);
}
private void createTableViewerForSchemes(Composite parent) {
GridDataFactory gridDataFactory = GridDataFactory.fillDefaults().grab(true, true);
gridDataFactory.span(2, 1).indent(0, SWT.DEFAULT);
TableColumnLayout tableColumnLayout = new TableColumnLayout();
Composite tableComposite = WidgetFactory.composite(SWT.NONE).layoutData(gridDataFactory.create())
.layout(tableColumnLayout).create(parent);
Table schemeTable = WidgetFactory
.table(SWT.H_SCROLL | SWT.V_SCROLL | SWT.SINGLE | SWT.BORDER | SWT.FULL_SELECTION | SWT.CHECK)
.headerVisible(true).linesVisible(true).font(parent.getFont()).create(tableComposite);
TableColumnFactory columnFactory = WidgetFactory.tableColumn(SWT.NONE);
TableColumn nameColumn = columnFactory.text(UrlHandlerPreferencePage_ColumnName_SchemeName).create(schemeTable);
TableColumn descriptionColumn = columnFactory.text(UrlHandlerPreferencePage_ColumnName_SchemeDescription)
.create(schemeTable);
TableColumn appColumn = columnFactory.text(UrlHandlerPreferencePage_ColumnName_Handler).create(schemeTable);
tableColumnLayout.setColumnData(nameColumn, new ColumnWeightData(20));
tableColumnLayout.setColumnData(descriptionColumn, new ColumnWeightData(60));
tableColumnLayout.setColumnData(appColumn, new ColumnWeightData(20));
tableViewer = new CheckboxTableViewer(schemeTable);
tableViewer.setContentProvider(ArrayContentProvider.getInstance());
tableViewer.setLabelProvider(new ItemLabelProvider());
TableSchemeSelectionListener listener = new TableSchemeSelectionListener();
tableViewer.addSelectionChangedListener(listener);
tableViewer.addCheckStateListener(listener);
}
private void setDataOnTableViewer(Collection<UiSchemeInformation> schemeInformationList) {
// Gets the schemes from extension points for URI schemes
tableViewer.setInput(schemeInformationList);
for (UiSchemeInformation schemeInformation : schemeInformationList) {
tableViewer.setChecked(schemeInformation, schemeInformation.checked);
}
}
private void createHandlerLocationControls(Composite parent) {
handlerComposite = new Composite(parent, SWT.NONE);
GridLayoutFactory.fillDefaults().numColumns(2).applyTo(handlerComposite);
GridDataFactory.fillDefaults().grab(true, false).applyTo(handlerComposite);
WidgetFactory.label(SWT.NONE).text(UrlHandlerPreferencePage_Handler_Label).create(handlerComposite);
handlerLocation = WidgetFactory.text(SWT.READ_ONLY | SWT.BORDER)
.layoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)).create(handlerComposite);
handlerComposite.setVisible(false); // set visible on table selection
}
private Collection<UiSchemeInformation> getLoadingSchemeInformationList() {
Collection<UiSchemeInformation> returnList = new ArrayList<>();
Collection<IScheme> schemes = extensionReader.getSchemes();
for (IScheme scheme : schemes) {
returnList.add(new LoadingSchemeInformation(scheme));
}
return returnList;
}
@SuppressWarnings("unchecked")
@Override
public boolean performOk() {
if (operatingSystemRegistration == null || currentLocation == null || isLoading) {
return true;
}
List<IScheme> toAdd = new ArrayList<>();
List<IScheme> toRemove = new ArrayList<>();
for (UiSchemeInformation info : (Collection<UiSchemeInformation>) tableViewer.getInput()) {
if (info.checked && !info.information.isHandled()) {
toAdd.add(info.information);
}
if (!info.checked && info.information.isHandled()) {
toRemove.add(info.information);
}
}
try {
operatingSystemRegistration.handleSchemes(toAdd, toRemove);
} catch (Exception e) {
IStatus status = new Status(IStatus.ERROR, IDEWorkbenchPlugin.IDE_WORKBENCH, 1,
UrlHandlerPreferencePage_Error_Writing_Scheme, e);
statusManagerWrapper.handle(status, StatusManager.BLOCK | StatusManager.LOG);
}
return true;
}
private class TableSchemeSelectionListener implements ICheckStateListener, ISelectionChangedListener {
@Override
public void selectionChanged(SelectionChangedEvent event) {
handleSelection();
}
@Override
public void checkStateChanged(CheckStateChangedEvent event) {
handleCheckbox(event);
}
private void handleSelection() {
IStructuredSelection selection = tableViewer.getStructuredSelection();
Object firstElement = selection != null ? selection.getFirstElement() : null;
if (firstElement instanceof UiSchemeInformation) {
setSchemeDetails((UiSchemeInformation) firstElement);
handlerComposite.setVisible(true);
}
}
private void setSchemeDetails(UiSchemeInformation schemeInfo) {
if (schemeInfo.checked) {
handlerLocation.setText(currentLocation);
} else if (schemeInfo.information.schemeIsHandledByOther()) {
handlerLocation.setText(schemeInfo.information.getHandlerInstanceLocation());
} else {
// checkbox not checked and:
// - no other handler handles it
// - or this eclipse handled it before (checkbox was unchecked but not yet
// applied)
handlerLocation.setText(UrlHandlerPreferencePage_Handler_Text_No_Application);
}
}
private void handleCheckbox(CheckStateChangedEvent event) {
UiSchemeInformation schemeInformation = (UiSchemeInformation) event.getElement();
if (event.getChecked() && schemeInformation.information.schemeIsHandledByOther()) {
if (operatingSystemRegistration.canOverwriteOtherApplicationsRegistration()) {
boolean answer = messageDialogWrapper.openQuestion(getShell(),
UriHandlerPreferencePage_Warning_OtherApp_Confirmation,
NLS.bind(UriHandlerPreferencePage_Warning_OtherApp_Confirmation_Description,
schemeInformation.information.getHandlerInstanceLocation(),
schemeInformation.information.getName()));
if (!answer) {
schemeInformation.checked = false;
tableViewer.setChecked(schemeInformation, schemeInformation.checked);
return;
}
} else {
schemeInformation.checked = false;
tableViewer.setChecked(schemeInformation, schemeInformation.checked);
messageDialogWrapper.openWarning(getShell(), UriHandlerPreferencePage_Warning_OtherApp,
NLS.bind(UriHandlerPreferencePage_Warning_OtherApp_Description,
schemeInformation.information.getHandlerInstanceLocation(),
schemeInformation.information.getName()));
return;
}
}
schemeInformation.checked = event.getChecked();
setSchemeDetails(schemeInformation);
tableViewer.update(schemeInformation, null);
}
}
private final class ItemLabelProvider extends LabelProvider implements ITableLabelProvider {
@Override
public Image getColumnImage(Object element, int columnIndex) {
return null;
}
@Override
public String getColumnText(Object element, int columnIndex) {
if (element instanceof UiSchemeInformation) {
UiSchemeInformation schemeInfo = (UiSchemeInformation) element;
switch (columnIndex) {
case 0:
return schemeInfo.getName();
case 1:
return schemeInfo.getDescription();
case 2:
String text = ""; //$NON-NLS-1$
if (UrlHandlerPreferencePage_LoadingText.equals(schemeInfo.getHandlerInstanceLocation()))
text = schemeInfo.getHandlerInstanceLocation();
else if (schemeInfo.isChecked()) {
text = UrlHandlerPreferencePage_Column_Handler_Text_Current_Application;
} else if (schemeInfo.information.schemeIsHandledByOther()) {
text = UrlHandlerPreferencePage_Column_Handler_Text_Other_Application;
}
return text;
default:
throw new IllegalArgumentException("Unknown column"); //$NON-NLS-1$
}
}
return null; // cannot happen
}
@Override
public boolean isLabelProperty(Object element, String property) {
return false;
}
}
static class UiSchemeInformation {
private boolean checked;
private ISchemeInformation information;
public UiSchemeInformation(boolean checked, ISchemeInformation information) {
this.checked = checked;
this.information = information;
}
public String getHandlerInstanceLocation() {
return information.getHandlerInstanceLocation();
}
public String getName() {
return information.getName();
}
public String getDescription() {
return information.getDescription();
}
public boolean isChecked() {
return checked;
}
}
static final class LoadingSchemeInformation extends UiSchemeInformation {
private IScheme scheme;
public LoadingSchemeInformation(IScheme scheme) {
super(false, null);
this.scheme = scheme;
}
@Override
public String getName() {
return scheme.getName();
}
@Override
public String getDescription() {
return scheme.getDescription();
}
@Override
public String getHandlerInstanceLocation() {
return UrlHandlerPreferencePage_LoadingText;
}
@Override
public boolean isChecked() {
return false;
}
}
interface IStatusManagerWrapper {
default void handle(IStatus status, int style) {
StatusManager.getManager().handle(status, style);
}
}
interface IMessageDialogWrapper {
default void openWarning(Shell shell, String title, String message) {
MessageDialog.openWarning(shell, title, message);
}
default boolean openQuestion(Shell parent, String title, String message) {
MessageDialog dlg = new MessageDialog(parent, title, null, message, MessageDialog.CONFIRM, 0,
UriHandlerPreferencePage_Confirm_Handle, IDialogConstants.CANCEL_LABEL);
dlg.open();
return dlg.getReturnCode() == IDialogConstants.OK_ID;
}
}
class OsRegistrationReadingJob extends Job {
private OsRegistrationReadingJob() {
super("Retrieving Link Handlers registration status from Operating System"); //$NON-NLS-1$
}
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
Collection<UiSchemeInformation> schemeInformationList = retrieveSchemeInformationList();
if (!schemeInformationList.isEmpty()) {
Display.getDefault().asyncExec(() -> {
if (!UriSchemeHandlerPreferencePage.this.tableViewer.getControl().isDisposed()) {
setDataOnTableViewer(schemeInformationList);
UriSchemeHandlerPreferencePage.this.tableViewer.getControl().setEnabled(true);
}
});
}
} finally {
UriSchemeHandlerPreferencePage.this.isLoading = false;
}
return Status.OK_STATUS;
}
/**
* Schemes which are part of extension points for URI Schemes and are registered
* to operating system are consolidated here
*
* @return the supported and registered URI schemes of this instance of eclipse
* @throws Exception
*/
private Collection<UiSchemeInformation> retrieveSchemeInformationList() {
Collection<UiSchemeInformation> returnList = new ArrayList<>();
Collection<IScheme> schemes = extensionReader.getSchemes();
try {
for (ISchemeInformation info : operatingSystemRegistration.getSchemesInformation(schemes)) {
returnList.add(new UiSchemeInformation(info.isHandled(), info));
}
} catch (Exception e) {
IStatus status = new Status(IStatus.ERROR, IDEWorkbenchPlugin.IDE_WORKBENCH, 1,
UrlHandlerPreferencePage_Error_Reading_Scheme, e);
statusManagerWrapper.handle(status, StatusManager.BLOCK | StatusManager.LOG);
}
return returnList;
}
}
}