/******************************************************************************* * Copyright (c) 2005, 2017 IBM Corporation 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 * *******************************************************************************/ package org.eclipse.dltk.tcl.internal.debug.ui.interpreters; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.eclipse.core.runtime.CoreException; import org.eclipse.dltk.compiler.util.Util; import org.eclipse.dltk.core.DLTKCore; import org.eclipse.dltk.core.IBuildpathEntry; import org.eclipse.dltk.core.IScriptProject; import org.eclipse.dltk.core.internal.environment.LocalEnvironment; import org.eclipse.dltk.internal.debug.ui.interpreters.AbstractInterpreterComboBlock; import org.eclipse.dltk.internal.debug.ui.interpreters.IInterpreterComboBlockContext; import org.eclipse.dltk.launching.IInterpreterInstall; import org.eclipse.dltk.launching.InterpreterContainerHelper; import org.eclipse.dltk.launching.ScriptRuntime; import org.eclipse.dltk.tcl.core.TclNature; import org.eclipse.dltk.tcl.core.TclPackagesManager; import org.eclipse.dltk.ui.DLTKPluginImages; import org.eclipse.dltk.ui.util.PixelConverter; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.ListDialog; /** * This class is used on Interpreter tab in Launch Configurations and * AbstractInterpreterContainerWizardPage. Configured packages are saved only on * the later one. On Interpreter tab packages could not be modified. */ public class TclInterpreterComboBlock extends AbstractInterpreterComboBlock { /** * @since 2.0 */ protected TclInterpreterComboBlock(IInterpreterComboBlockContext context) { super(context); } private Set<String> packages = new HashSet<>(); private Set<String> autoPackages = new HashSet<>(); public class PackagesLabelProvider extends LabelProvider { @Override public Image getImage(Object element) { if (element instanceof Package) { String packageName = ((Package) element).name; IInterpreterInstall install = getInterpreter(); if (install == null) { install = ScriptRuntime.getDefaultInterpreterInstall(TclNature.NATURE_ID, LocalEnvironment.getInstance()); } if (install != null) { final Set<String> names = TclPackagesManager.getPackageInfosAsString(install); if (!names.contains(packageName)) { return DLTKPluginImages.get(DLTKPluginImages.IMG_OBJS_ERROR); } } return DLTKPluginImages.get(DLTKPluginImages.IMG_OBJS_PACKAGE); } else if (element instanceof PackageCategory) { return DLTKPluginImages.get(DLTKPluginImages.IMG_OBJS_LIBRARY); } else { return null; } } @Override public String getText(Object element) { if (element instanceof Package) { return ((Package) element).name; } else if (element instanceof PackageCategory) { return ((PackageCategory) element).category; } return super.getText(element); } } private static class PackageComparator extends ViewerComparator { @Override public int category(Object element) { if (element instanceof PackageCategory) { final PackageCategory category = (PackageCategory) element; return category.readOnly ? 2 : 1; } return super.category(element); } } private static class PackagesContentProvider implements ITreeContentProvider { @Override public void dispose() { } @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } @Override public Object[] getChildren(Object parentElement) { if (parentElement instanceof PackageCategory) { return ((PackageCategory) parentElement).getPackages(); } else { return Util.EMPTY_ARRAY; } } @Override public Object getParent(Object element) { if (element instanceof Package) { return ((Package) element).parent; } return null; } @Override public boolean hasChildren(Object element) { if (element instanceof PackageCategory) { return !((PackageCategory) element).packages.isEmpty(); } return false; } @Override public Object[] getElements(Object inputElement) { if (inputElement instanceof PackageInput) { return ((PackageInput) inputElement).getCategories(); } return Util.EMPTY_ARRAY; } } private TreeViewer fElements; private IScriptProject scriptProject; private Button addButton; private Button addAllButton; private Button remove; @Override public void createControl(Composite ancestor) { super.createControl(ancestor); // use Composite created in super to place additional controls. final Composite mainComposite = (Composite) getControl(); Composite composite = new Composite(mainComposite, SWT.NONE); GridData compositeData = new GridData(SWT.FILL, SWT.FILL, true, true); compositeData.horizontalSpan = ((GridLayout) mainComposite.getLayout()).numColumns; composite.setLayoutData(compositeData); GridLayout gridLayout = new GridLayout(2, false); gridLayout.marginWidth = 0; composite.setLayout(gridLayout); this.fElements = new TreeViewer(composite); GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); data.heightHint = new PixelConverter(ancestor).convertHeightInCharsToPixels(8); this.fElements.getTree().setLayoutData(data); Composite buttons = new Composite(composite, SWT.NONE); buttons.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); GridLayout buttonsLayout = new GridLayout(1, true); buttonsLayout.marginLeft = buttonsLayout.marginWidth; buttonsLayout.marginWidth = 0; buttons.setLayout(buttonsLayout); final boolean editablePackages = getContext().getMode() == IInterpreterComboBlockContext.M_BUILDPATH; addButton = new Button(buttons, SWT.PUSH); addButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); addButton.setText(TclInterpreterMessages.TclInterpreterComboBlock_buttonAdd); if (editablePackages) { addButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { addPackage(); } }); } else { addButton.setEnabled(false); } addAllButton = new Button(buttons, SWT.PUSH); addAllButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); addAllButton.setText(TclInterpreterMessages.TclInterpreterComboBlock_buttonAddAll); if (editablePackages) { addAllButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { addAllPackages(); } }); } else { addAllButton.setEnabled(false); } remove = new Button(buttons, SWT.PUSH); remove.setText(TclInterpreterMessages.TclInterpreterComboBlock_buttonRemove); remove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); if (editablePackages) { remove.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { removePackage(); } }); remove.setEnabled(false); // disable initially } else { remove.setEnabled(false); } this.fElements.setContentProvider(new PackagesContentProvider()); this.fElements.setLabelProvider(new PackagesLabelProvider()); if (editablePackages) { this.fElements.addSelectionChangedListener(event -> remove.setEnabled(canRemove(event.getSelection()))); } this.fElements.setComparator(new PackageComparator()); showPackages(); this.addPropertyChangeListener(event -> { if (event.getProperty().equals(PROPERTY_INTERPRETER)) { refreshView(); } }); } private void showPackages() { if (fElements != null) { PackageInput input = new PackageInput(); input.addCategory(TclInterpreterMessages.TclInterpreterComboBlock_CategoryManual, this.packages, false); input.addCategory(TclInterpreterMessages.TclInterpreterComboBlock_CategoryAutomatic, this.autoPackages, true); fElements.setInput(input); fElements.expandToLevel(2); } } private static class PackageInput { private final List<PackageCategory> categories = new ArrayList<>(); public void addCategory(String category, Set<String> packagesSet, boolean readOnly) { categories.add(new PackageCategory(category, packagesSet, readOnly)); } public PackageCategory[] getCategories() { return categories.toArray(new PackageCategory[categories.size()]); } /** * @param categoryName * @return */ public PackageCategory get(String categoryName) { for (PackageCategory category : categories) { if (categoryName.equals(category.category)) { return category; } } throw new IllegalArgumentException(categoryName); } } private static class PackageCategory { final String category; final Set<String> packages; final boolean readOnly; public PackageCategory(String category, Set<String> packagesSet, boolean readOnly) { this.category = category; this.readOnly = readOnly; this.packages = packagesSet; } public Package[] getPackages() { final Package[] result = new Package[packages.size()]; int index = 0; for (String packageName : this.packages) { result[index++] = new Package(this, packageName); } return result; } @Override public int hashCode() { return category.hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof PackageCategory) { PackageCategory other = (PackageCategory) obj; return category.equals(other.category); } return false; } } private static class Package { final PackageCategory parent; final String name; public Package(PackageCategory parent, String name) { this.parent = parent; this.name = name; } @Override public int hashCode() { return name.hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof Package) { final Package other = (Package) obj; return name.equals(other.name) && parent.equals(other.parent); } return false; } } /** * @since 2.0 */ protected boolean canRemove(ISelection selection) { if (!selection.isEmpty()) { if (selection instanceof IStructuredSelection) { final IStructuredSelection ss = (IStructuredSelection) selection; for (Iterator<?> iterator = ss.iterator(); iterator.hasNext();) { final Object pkg = iterator.next(); if (pkg instanceof Package) { if (((Package) pkg).parent.readOnly) { return false; } if (!this.packages.contains(((Package) pkg).name)) { return false; } } else { return false; } } return true; } } return false; } protected void removePackage() { final ISelection selection = this.fElements.getSelection(); if (selection instanceof IStructuredSelection) { final IStructuredSelection ss = (IStructuredSelection) selection; int updates = 0; for (Iterator<String> iterator = ss.iterator(); iterator.hasNext();) { final Object pkg = iterator.next(); if (pkg instanceof Package && !((Package) pkg).parent.readOnly) { if (this.packages.remove(((Package) pkg).name)) { ++updates; } } } if (updates != 0) { refreshView(); } } } private void refreshView() { refreshView(null); } private void refreshView(final String selection) { PlatformUI.getWorkbench().getDisplay().asyncExec(() -> { if (fElements.getControl().isDisposed()) return; fElements.refresh(); if (selection != null) { PackageCategory category = ((PackageInput) fElements.getInput()) .get(TclInterpreterMessages.TclInterpreterComboBlock_CategoryManual); fElements.setSelection(new StructuredSelection(new Package(category, selection))); } }); } protected void addPackage() { IInterpreterInstall install = this.getInterpreter(); if (install == null) { install = ScriptRuntime.getDefaultInterpreterInstall(TclNature.NATURE_ID, LocalEnvironment.getInstance()); } if (install != null) { Set<String> packages = TclPackagesManager.getPackageInfosAsString(install); final List<String> names = new ArrayList<>(); names.addAll(packages); Collections.sort(names, String.CASE_INSENSITIVE_ORDER); ListDialog dialog = new ListDialog(this.fElements.getControl().getShell()); dialog.setTitle(TclInterpreterMessages.TclInterpreterComboBlock_title); dialog.setContentProvider(new IStructuredContentProvider() { @Override public Object[] getElements(Object inputElement) { return names.toArray(); } @Override public void dispose() { } @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } }); dialog.setLabelProvider(new PackagesLabelProvider()); dialog.setInput(names); if (dialog.open() == ListDialog.OK) { Object[] result = dialog.getResult(); for (int i = 0; i < result.length; i++) { String pkg = (String) result[i]; this.packages.add(pkg); } refreshView((String) (result.length != 0 ? result[0] : null)); } } else { MessageBox box = new MessageBox(this.fElements.getControl().getShell(), SWT.OK | SWT.ICON_WARNING | SWT.APPLICATION_MODAL); box.setText(TclInterpreterMessages.TclInterpreterComboBlock_errorTitle); box.setMessage(TclInterpreterMessages.TclInterpreterComboBlock_errorMessage); box.open(); } } protected void addAllPackages() { IInterpreterInstall install = null; try { install = ScriptRuntime.getInterpreterInstall(this.scriptProject); } catch (CoreException e) { if (DLTKCore.DEBUG) { e.printStackTrace(); } } if (install != null) { Set<String> packages = TclPackagesManager.getPackageInfosAsString(install); this.packages.addAll(packages); refreshView(); } else { MessageBox box = new MessageBox(this.fElements.getControl().getShell(), SWT.OK | SWT.ICON_INFORMATION | SWT.APPLICATION_MODAL); box.setText(TclInterpreterMessages.TclInterpreterComboBlock_errorTitle); box.setText(TclInterpreterMessages.TclInterpreterComboBlock_errorMessage); box.open(); } } /** * @since 2.0 */ public void initialize(IScriptProject project) { this.scriptProject = project; final Set<String> set = new HashSet<>(); final Set<String> autoSet = new HashSet<>(); InterpreterContainerHelper.getInterpreterContainerDependencies(project, set, autoSet); this.packages.clear(); this.packages.addAll(set); this.autoPackages.clear(); this.autoPackages.addAll(autoSet); showPackages(); } @Override public IBuildpathEntry getEntry() { IBuildpathEntry createPackagesContainer = InterpreterContainerHelper.createPackagesContainer(this.packages, this.autoPackages, getInterpreterPath()); return createPackagesContainer; } }