blob: 91009e27eb56b90b686eda036c4869db3000bb4d [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2009, 2020 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ltk.ui.sourceediting.assist;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.ibm.icu.text.Collator;
import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.CommandManager;
import org.eclipse.core.commands.IParameter;
import org.eclipse.core.commands.Parameterization;
import org.eclipse.core.commands.ParameterizedCommand;
import org.eclipse.core.commands.common.NotDefinedException;
import org.eclipse.core.commands.contexts.ContextManager;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.list.WritableList;
import org.eclipse.jface.bindings.BindingManager;
import org.eclipse.jface.bindings.Scheme;
import org.eclipse.jface.bindings.TriggerSequence;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.ViewerCell;
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.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.keys.IBindingService;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.statet.ecommons.preferences.core.Preference;
import org.eclipse.statet.ecommons.preferences.ui.ManagedConfigurationBlock;
import org.eclipse.statet.ecommons.runtime.core.StatusChangeListener;
import org.eclipse.statet.ecommons.ui.components.ButtonGroup;
import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
import org.eclipse.statet.ecommons.ui.viewers.ViewerUtils;
import org.eclipse.statet.ecommons.ui.viewers.ViewerUtils.CheckboxTableComposite;
import org.eclipse.statet.internal.ltk.ui.EditingMessages;
import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditorCommandIds;
public class AdvancedContentAssistConfigurationBlock extends ManagedConfigurationBlock {
private class CategoryKindLabelProvider extends CellLabelProvider {
@Override
public void update(final ViewerCell cell) {
final ContentAssistCategory category= (ContentAssistCategory) cell.getElement();
cell.setImage(getImage(category.getImageDescriptor()));
cell.setText(category.getDisplayName());
}
}
private static BindingManager gLocalBindingManager;
private CheckboxTableViewer defaultList;
private CheckboxTableViewer circlingList;
private ButtonGroup<ContentAssistCategory> circlingOrderButtons;
private final Map<Object, Image> images= new HashMap<>();
private final ContentAssistComputerRegistry registry;
private IObservableList<ContentAssistCategory> orderedCategories;
private Command specificCommand;
private IParameter specificParam;
public AdvancedContentAssistConfigurationBlock(final ContentAssistComputerRegistry registry,
final StatusChangeListener statusListener) {
super(null, statusListener);
this.registry= registry;
final ICommandService commandSvc= PlatformUI.getWorkbench().getAdapter(ICommandService.class);
this.specificCommand= commandSvc.getCommand(ISourceEditorCommandIds.SPECIFIC_CONTENT_ASSIST_COMMAND_ID);
}
private void prepareKeybindingInfo() {
if (this.specificCommand == null) {
return;
}
if (gLocalBindingManager == null) {
gLocalBindingManager= new BindingManager(new ContextManager(), new CommandManager());
final IBindingService bindingService= PlatformUI.getWorkbench().getService(IBindingService.class);
gLocalBindingManager.setLocale(bindingService.getLocale());
gLocalBindingManager.setPlatform(bindingService.getPlatform());
final Scheme[] definedSchemes= bindingService.getDefinedSchemes();
if (definedSchemes != null) {
try {
for (int i= 0; i < definedSchemes.length; i++) {
final Scheme scheme= definedSchemes[i];
final Scheme copy= gLocalBindingManager.getScheme(scheme.getId());
copy.define(scheme.getName(), scheme.getDescription(), scheme.getParentId());
}
}
catch (final NotDefinedException e) {
}
}
}
try {
this.specificParam= this.specificCommand.getParameters()[0];
}
catch (final Exception x) {
this.specificCommand= null;
this.specificParam= null;
}
}
private String getDefaultKeybindingAsString() {
final ICommandService commandSvc= PlatformUI.getWorkbench().getAdapter(ICommandService.class);
final Command command= commandSvc.getCommand(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
final ParameterizedCommand pCmd= new ParameterizedCommand(command, null);
final String key= getKeybindingAsString(pCmd);
return key;
}
private String getSpecificKeybindingAsString(final ContentAssistCategory category) {
if (gLocalBindingManager == null || category == null) {
return null;
}
final Parameterization[] params= { new Parameterization(this.specificParam, category.getId()) };
final ParameterizedCommand pCmd= new ParameterizedCommand(this.specificCommand, params);
return getKeybindingAsString(pCmd);
}
private String getKeybindingAsString(final ParameterizedCommand command) {
if (gLocalBindingManager == null) {
return null;
}
final IBindingService bindingService= PlatformUI.getWorkbench().getService(IBindingService.class);
try {
gLocalBindingManager.setBindings(bindingService.getBindings());
final Scheme activeScheme= bindingService.getActiveScheme();
if (activeScheme != null) {
gLocalBindingManager.setActiveScheme(activeScheme);
}
final TriggerSequence[] binding= gLocalBindingManager.getActiveBindingsDisregardingContextFor(command);
if (binding.length > 0) {
return binding[0].format();
}
return null;
}
catch (final NotDefinedException e) {
return null;
}
}
@Override
protected void createBlockArea(final Composite pageComposite) {
final Map<Preference<?>, String> prefs= new HashMap<>();
prefs.put(this.registry.getPrefDefaultDisabledCategoryIds(), this.registry.getSettingsGroupId());
prefs.put(this.registry.getPrefCirclingOrderedCategoryIds(), this.registry.getSettingsGroupId());
setupPreferenceManager(prefs);
prepareKeybindingInfo();
final Composite composite= new Composite(pageComposite, SWT.NONE);
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
composite.setLayout(LayoutUtils.newCompositeGrid(1));
// final Composite composite= pageComposite;
final Link control= addLinkControl(composite,
EditingMessages.ContentAssistAdvancedConfig_message_KeyBindingHint);
control.setLayoutData(applyWrapWidth(new GridData(SWT.FILL, SWT.FILL, true, false)));
{ final Label defaultKeyBindingLabel= new Label(composite, SWT.NONE);
final String defaultKeyBinding= getDefaultKeybindingAsString();
defaultKeyBindingLabel.setText((defaultKeyBinding != null) ?
NLS.bind(EditingMessages.ContentAssistAdvancedConfig_message_DefaultKeyBinding, defaultKeyBinding) :
EditingMessages.ContentAssistAdvancedConfig_message_NoDefaultKeyBinding );
defaultKeyBindingLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
}
{ // Default
final Group group= new Group(composite, SWT.NONE);
group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
group.setLayout(LayoutUtils.newGroupGrid(1));
group.setText(EditingMessages.ContentAssistAdvancedConfig_Default_label);
final Label label= new Label(group, SWT.NONE);
label.setText(EditingMessages.ContentAssistAdvancedConfig_DefaultTable_label);
label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
final Composite table= createDefaultTable(group);
table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
}
{ // Cicling
final Group group= new Group(composite, SWT.NONE);
group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
group.setLayout(LayoutUtils.newGroupGrid(2));
group.setText(EditingMessages.ContentAssistAdvancedConfig_Cicling_label);
final Label label= new Label(group, SWT.WRAP);
label.setText(EditingMessages.ContentAssistAdvancedConfig_CiclingTable_label);
label.setLayoutData(applyWrapWidth(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)));
final Composite table= createCirclingTable(group);
table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
this.circlingOrderButtons= new ButtonGroup<>(group);
this.circlingOrderButtons.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true));
this.circlingOrderButtons.addUpButton(null);
this.circlingOrderButtons.addDownButton(null);
}
this.orderedCategories= new WritableList<>();
this.circlingList.setInput(this.orderedCategories);
this.circlingOrderButtons.connectTo(this.circlingList, this.orderedCategories, null);
ViewerUtils.scheduleStandardSelection(this.circlingList);
ViewerUtils.scheduleStandardSelection(this.defaultList);
updateControls();
}
protected Composite createDefaultTable(final Composite parent) {
final CheckboxTableComposite composite= new ViewerUtils.CheckboxTableComposite(parent, SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION);
this.defaultList= composite.viewer;
composite.table.setHeaderVisible(true);
composite.table.setLinesVisible(true);
composite.viewer.setContentProvider(new ArrayContentProvider());
{ final TableViewerColumn column= new TableViewerColumn(composite.viewer, SWT.NONE);
composite.layout.setColumnData(column.getColumn(), new ColumnWeightData(3));
column.getColumn().setText(EditingMessages.ContentAssistAdvancedConfig_ProposalKinds_label);
column.setLabelProvider(new CategoryKindLabelProvider());
}
{ final TableViewerColumn column= new TableViewerColumn(composite.viewer, SWT.NONE);
composite.layout.setColumnData(column.getColumn(), new ColumnWeightData(1));
column.getColumn().setText(EditingMessages.ContentAssistAdvancedConfig_KeyBinding_label);
column.setLabelProvider(new CellLabelProvider() {
@Override
public void update(final ViewerCell cell) {
final ContentAssistCategory category= (ContentAssistCategory) cell.getElement();
final String keybindingAsString= getSpecificKeybindingAsString(category);
cell.setText((keybindingAsString != null) ? keybindingAsString : ""); //$NON-NLS-1$
}
});
}
return composite;
}
protected Composite createCirclingTable(final Composite parent) {
final CheckboxTableComposite composite= new ViewerUtils.CheckboxTableComposite(parent, SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION);
this.circlingList= composite.viewer;
composite.table.setHeaderVisible(true);
composite.table.setLinesVisible(true);
composite.viewer.setContentProvider(new ArrayContentProvider());
{ final TableViewerColumn column= new TableViewerColumn(composite.viewer, SWT.NONE);
composite.layout.setColumnData(column.getColumn(), new ColumnWeightData(1));
column.getColumn().setText(EditingMessages.ContentAssistAdvancedConfig_ProposalKinds_label);
column.setLabelProvider(new CategoryKindLabelProvider());
}
return composite;
}
private Image getImage(final ImageDescriptor imgDesc) {
if (imgDesc == null) {
return null;
}
Image img= this.images.get(imgDesc);
if (img == null) {
img= imgDesc.createImage(false);
this.images.put(imgDesc, img);
}
return img;
}
@Override
public void dispose() {
super.dispose();
for (final Image img : this.images.values()) {
img.dispose();
}
this.images.clear();
}
@Override
protected void updateControls() {
final List<ContentAssistCategory> orderedCategories= this.registry.applyPreferences(this, this.registry.getCopyOfCategories());
final List<ContentAssistCategory> defaultCategories= new ArrayList<>(orderedCategories);
Collections.sort(defaultCategories, new Comparator<ContentAssistCategory>() {
private final Collator NAMES_COLLARTOR= Collator.getInstance();
@Override
public int compare(final ContentAssistCategory o1, final ContentAssistCategory o2) {
return this.NAMES_COLLARTOR.compare(o1.getDisplayName(), o2.getDisplayName());
}
});
this.defaultList.setInput(defaultCategories);
for (final ContentAssistCategory category : defaultCategories) {
this.defaultList.setChecked(category, category.isIncludedInDefault);
}
this.orderedCategories.clear();
this.orderedCategories.addAll(orderedCategories);
this.circlingList.refresh();
for (final ContentAssistCategory category : orderedCategories) {
this.circlingList.setChecked(category, category.isEnabledAsSeparate);
}
this.circlingOrderButtons.refresh();
}
@Override
protected void updatePreferences() {
final List<ContentAssistCategory> orderedCategories= new ArrayList<>(this.orderedCategories);
for (final ContentAssistCategory category : orderedCategories) {
category.isIncludedInDefault= this.defaultList.getChecked(category);
category.isEnabledAsSeparate= this.circlingList.getChecked(category);
}
final Map<Preference<?>, Object> preferences= this.registry.createPreferences(orderedCategories);
setPrefValues(preferences);
}
}