/** * Copyright (c) 2013-2016 Angelo ZERR. * 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: * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation */ package tern.eclipse.ide.ui.controls; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.IElementComparer; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Font; 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.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; import org.eclipse.swt.widgets.Table; import tern.eclipse.ide.core.IIDETernProject; import tern.eclipse.ide.core.TernCorePlugin; import tern.eclipse.ide.internal.ui.TernUIMessages; import tern.eclipse.ide.internal.ui.Trace; import tern.eclipse.ide.internal.ui.controls.DependenciesPanel; import tern.eclipse.ide.internal.ui.controls.DetailsPanel; import tern.eclipse.ide.internal.ui.controls.OptionsPanel; import tern.eclipse.ide.internal.ui.viewers.TernModuleVersionEditingSupport; import tern.eclipse.ide.ui.TernUIPlugin; import tern.eclipse.ide.ui.viewers.TernModuleLabelProvider; import tern.metadata.TernModuleMetadata; import tern.repository.ITernRepository; import tern.server.ITernModule; import tern.server.ITernModuleConfigurable; import tern.utils.StringUtils; import tern.utils.TernModuleHelper; /** * Block to select Tern plugins + JSON Type Definitions. * */ public class TernModulesBlock extends AbstractTableBlock { private static final IElementComparer TERN_MODULES_COMPARER = new IElementComparer() { @Override public boolean equals(Object a, Object b) { return a.equals(b); } @Override public int hashCode(Object element) { return element.hashCode(); } }; private final String tableLabel; private final IProject project; private final Map<String, ITernModule> ternModules = new HashMap<String, ITernModule>(); private CheckboxTableViewer tableViewer; private DetailsPanel detailsPanel; private DependenciesPanel dependenciesPanel; private OptionsPanel optionsPanel; private TabItem optionsTabItem; private TabFolder tabFolder; private TabItem detailsTabItem; private Button selectDependenciesCheckbox; private Collection<ITernModule> checkedModules; private boolean checkUpdating; public TernModulesBlock(IProject project, String tableLabel) { this.project = project; this.tableLabel = tableLabel; } public Control createControl(Composite ancestor) { Composite parent = new Composite(ancestor, SWT.NULL); GridLayout layout = new GridLayout(); layout.numColumns = 1; layout.marginHeight = 0; layout.marginWidth = 0; parent.setLayout(layout); Font font = ancestor.getFont(); parent.setFont(font); GridData data; if (tableLabel != null) { Composite header = new Composite(parent, SWT.NONE); data = new GridData(); data.horizontalSpan = 2; header.setLayoutData(data); header.setLayout(new GridLayout(2, false)); // Create description Label tableLabel = new Label(header, SWT.NONE); tableLabel.setText(this.tableLabel); tableLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); tableLabel.setFont(font); // Create dependencies checkbox selectDependenciesCheckbox = new Button(header, SWT.CHECK); selectDependenciesCheckbox.setSelection(true); } SashForm sashForm = new SashForm(parent, SWT.HORIZONTAL | SWT.SMOOTH); data = new GridData(SWT.FILL, SWT.FILL, true, true); sashForm.setLayoutData(data); createModulesMaster(sashForm); createModulesDetails(sashForm); Dialog.applyDialogFont(parent); return parent; } /** * Create table of tern modules. * * @param ancestor */ private void createModulesMaster(Composite ancestor) { Composite parent = new Composite(ancestor, SWT.NULL); GridLayout layout = new GridLayout(); layout.numColumns = 2; layout.marginHeight = 0; layout.marginWidth = 0; parent.setLayout(layout); Font font = ancestor.getFont(); parent.setFont(font); Table table = new Table(parent, SWT.CHECK | SWT.BORDER | SWT.FULL_SELECTION | SWT.V_SCROLL); GridData data = new GridData(GridData.FILL_BOTH); data.widthHint = 350; data.heightHint = 400; table.setLayoutData(data); table.setFont(parent.getFont()); table.setHeaderVisible(true); table.setLinesVisible(true); tableViewer = new CheckboxTableViewer(table); // create name column TableViewerColumn nameColumn = new TableViewerColumn(tableViewer, SWT.NONE); nameColumn.getColumn().setWidth(180); nameColumn.getColumn().setResizable(true); nameColumn.getColumn().setText( TernUIMessages.TernModulesBlock_moduleName); nameColumn.getColumn().addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { sortByName(); } }); // create version column TableViewerColumn versionColumn = new TableViewerColumn(tableViewer, SWT.NONE); versionColumn.getColumn().setWidth(100); versionColumn.getColumn().setResizable(true); versionColumn.getColumn().setText( TernUIMessages.TernModulesBlock_moduleVersion); final TernModuleVersionEditingSupport versionEditiongSupport = new TernModuleVersionEditingSupport( tableViewer); versionColumn.setEditingSupport(versionEditiongSupport); tableViewer.setComparer(TERN_MODULES_COMPARER); tableViewer.setLabelProvider(TernModuleLabelProvider.getInstance()); tableViewer.setContentProvider(ArrayContentProvider.getInstance()); // when a module is checked and dependencies checkbox is checked, tern // module dependencies must be selected too addCheckStateListener(new ICheckStateListener() { @Override public void checkStateChanged(CheckStateChangedEvent e) { if (checkUpdating) { return; } try { checkUpdating = true; ITernModule module = ((ITernModule) e.getElement()); // update checked modules list updateCheckedModules(module, e.getChecked()); if (e.getChecked() && isSelectDependencies()) { TernModuleMetadata metadata = module.getMetadata(); if (metadata != null) { ITernModule dependencyModule = null; // loop for each dependencies and check it if needed for (String moduleName : metadata .getDependencies(module.getVersion())) { dependencyModule = ternModules.get(moduleName); if (dependencyModule != null) { // update module selection if (!tableViewer .getChecked(dependencyModule)) { tableViewer.setChecked( dependencyModule, true); } // update checked modules list updateCheckedModules(dependencyModule, true); if (dependencyModule instanceof ITernModuleConfigurable) { ITernModuleConfigurable configurable = (ITernModuleConfigurable) dependencyModule; if (configurable.hasVersion()) { // update version String version = configurable .getModule(moduleName) .getVersion(); versionEditiongSupport.setValue( dependencyModule, version); } } } } } } } finally { checkUpdating = false; } } }); // when a module is selected, details, dependencies, options tabs must // be // refreshed. addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent e) { if (!e.getSelection().isEmpty()) { ITernModule module = (ITernModule) ((IStructuredSelection) e .getSelection()).getFirstElement(); refreshModule(module); } else { refreshModule(null); } } }); restoreColumnSettings(); } private void createModulesDetails(Composite parent) { // Create tab folder. tabFolder = new TabFolder(parent, SWT.NONE); GridData data = new GridData(GridData.FILL_HORIZONTAL); data.heightHint = 80; tabFolder.setLayoutData(data); // create details tab this.detailsPanel = new DetailsPanel(tabFolder, project); detailsTabItem = new TabItem(tabFolder, SWT.NULL); detailsTabItem.setControl(this.detailsPanel); detailsTabItem.setText(TernUIMessages.TernModulesBlock_detailsTabLabel); // create dependencies tab this.dependenciesPanel = new DependenciesPanel(tabFolder, project); TabItem dependenciesTabItem = new TabItem(tabFolder, SWT.NULL); dependenciesTabItem.setControl(this.dependenciesPanel); dependenciesTabItem .setText(TernUIMessages.TernModulesBlock_dependenciesTabLabel); // create options panel. this.optionsPanel = new OptionsPanel(tabFolder, project); } /** * Refresh tab items with the given module information * * @param module */ private void refreshModule(ITernModule module) { if (TernModuleHelper.hasOptions(module)) { // module has options, create options tab if needed if (optionsTabItem == null) { this.optionsTabItem = new TabItem(tabFolder, SWT.NULL); optionsTabItem.setControl(this.optionsPanel); optionsTabItem .setText(TernUIMessages.TernModulesBlock_optionsTabLabel); } optionsPanel.refresh(module); } else { // module has no options, don't display options tab if (optionsTabItem != null) { optionsTabItem.dispose(); } optionsTabItem = null; } detailsPanel.refresh(module); dependenciesPanel.refresh(module); // select the details tab. tabFolder.setSelection(detailsTabItem); } public void addSelectionChangedListener(ISelectionChangedListener listener) { tableViewer.addSelectionChangedListener(listener); } public void removeSelectionChangedListener( ISelectionChangedListener listener) { tableViewer.removeSelectionChangedListener(listener); } public void addCheckStateListener(ICheckStateListener listener) { tableViewer.addCheckStateListener(listener); } public void removeCheckStateListener(ICheckStateListener listener) { tableViewer.removeCheckStateListener(listener); } /** * Sorts by name. */ private void sortByName() { tableViewer.setSorter(new ViewerSorter() { @Override public int compare(Viewer viewer, Object e1, Object e2) { if ((e1 instanceof ITernModule) && (e2 instanceof ITernModule)) { ITernModule left = (ITernModule) e1; ITernModule right = (ITernModule) e2; return getNameOrLabel(left).compareToIgnoreCase( getNameOrLabel(right)); } return super.compare(viewer, e1, e2); } private String getNameOrLabel(ITernModule module) { TernModuleMetadata metadata = module.getMetadata(); if (metadata == null) { return module.getName(); } return StringUtils.isEmpty(metadata.getLabel()) ? module .getName() : metadata.getLabel(); } @Override public boolean isSorterProperty(Object element, String property) { return true; } }); } public void setTernModules(ITernModule[] modules) { ternModules.clear(); for (ITernModule module : modules) { if (module instanceof ITernModuleConfigurable) { ITernModuleConfigurable configurable = (ITernModuleConfigurable) module; if (!configurable.hasVersion()) { ternModules.put(module.getName(), module); } else { Collection<ITernModule> mods = configurable.getModules(); for (ITernModule mod : mods) { ternModules.put(mod.getName(), module); } } } else { ternModules.put(module.getName(), module); } } tableViewer.setInput(modules); } public Collection<ITernModule> getCheckedModules() { if (checkedModules != null) { return checkedModules; } return Collections.emptyList(); } public void setCheckedModules(Collection<ITernModule> checkedModules) { this.checkedModules = checkedModules; tableViewer.setCheckedElements(checkedModules.toArray()); } public void setCheckedModule(ITernModule module, boolean selected) { updateCheckedModules(module, selected); tableViewer.setChecked(module, selected); } private void updateCheckedModules(ITernModule module, boolean checked) { if (checked) { if (!checkedModules.contains(module)) { checkedModules.add(module); } } else { checkedModules.remove(module); } } @Override protected void setSortColumn(int column) { switch (column) { case 1: sortByName(); break; } super.setSortColumn(column); } @Override protected Table getTable() { return tableViewer.getTable(); } @Override protected IDialogSettings getDialogSettings() { return TernUIPlugin.getDefault().getDialogSettings(); } @Override protected String getQualifier() { return TernUIPlugin.PLUGIN_ID + ".modules."; } /** * Load plugins from tern project. * * @param moduleNames * @param repository */ public void loadModules() { try { // load modules from the given tern project IIDETernProject ternProject = getTernProject(); if (ternProject != null) { // Add list of tern modules from the repository and local List<ITernModule> allModules = ternProject.getAllModules(); // Group by type allModules = TernModuleHelper.groupByType(allModules); // checked modules List<ITernModule> checkedModules = TernCorePlugin .getTernRepositoryManager().getCheckedModules( ternProject, allModules); refresh(allModules, checkedModules); /* * if (checkedModules.size() > 0) { ITernModule firstModule = * checkedModules.get(0); tableViewer.setSelection(new * StructuredSelection( firstModule)); } */ } } catch (Throwable e) { Trace.trace(Trace.SEVERE, "Error while loading plugins.", e); } } /** * Load modules from the given tern repository and check the modules with * the given checked module names. * * @param repository * tern repository which hosts tern modules. * @param checkedModuleNames * module names to check. */ public void loadModules(ITernRepository repository, String[] checkedModuleNames) { try { // load modules from the given repository List<ITernModule> allModules = new ArrayList<ITernModule>( Arrays.asList(repository.getModules())); // Group by type List<ITernModule> groupedModules = TernModuleHelper .groupByType(allModules); // checked modules List<ITernModule> checkedModules = TernCorePlugin .getTernRepositoryManager().getCheckedModules( checkedModuleNames, allModules, groupedModules); refresh(groupedModules, checkedModules); } catch (Throwable e) { Trace.trace(Trace.SEVERE, "Error while loading plugins.", e); } } public void refresh(Collection<ITernModule> allModules, Collection<ITernModule> checkedModules) { this.setTernModules(allModules.toArray(ITernModule.EMPTY_MODULE)); this.setCheckedModules(checkedModules); } /** * Returns true if tern modules dependencies must be select when a tern * module is selected and false otherwise. * * @return true if tern modules dependencies must be select when a tern * module is selected and false otherwise. */ private boolean isSelectDependencies() { return selectDependenciesCheckbox != null && selectDependenciesCheckbox.getSelection(); } /** * Returns the tern project and null otherwise. * * @return the tern project and null otherwise. * * @throws CoreException */ public IIDETernProject getTernProject() throws CoreException { return project != null ? TernCorePlugin.getTernProject(project) : null; } /** * Returns the eclipse project and null otherwise. * * @return the eclipse project and null otherwise. */ public IProject getProject() { return project; } public void setEnabled(boolean enabled) { getTable().setEnabled(enabled); } public boolean isCheckUpdating() { return checkUpdating; } }