package bndtools.editor.project; import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.util.ArrayList; import java.util.List; import org.bndtools.core.ui.icons.Icons; import org.bndtools.utils.jface.StrikeoutStyler; import org.bndtools.utils.swt.AddRemoveButtonBarPart; import org.bndtools.utils.swt.AddRemoveButtonBarPart.AddRemoveListener; import org.bndtools.utils.swt.SWTConcurrencyUtil; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.preference.JFacePreferences; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.ICheckStateProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StyledCellLabelProvider; import org.eclipse.jface.viewers.StyledString; import org.eclipse.jface.viewers.StyledString.Styler; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.jface.window.ToolTip; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StackLayout; 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.Control; import org.eclipse.swt.widgets.Table; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.forms.IMessageManager; import org.eclipse.ui.forms.events.HyperlinkAdapter; import org.eclipse.ui.forms.events.HyperlinkEvent; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.Hyperlink; import org.eclipse.ui.forms.widgets.Section; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.part.EditorPart; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.service.repository.Repository; import org.osgi.util.promise.Deferred; import org.osgi.util.promise.Promise; import org.osgi.util.promise.Success; import aQute.bnd.build.Workspace; import aQute.bnd.build.model.BndEditModel; import aQute.bnd.build.model.clauses.HeaderClause; import aQute.bnd.header.Attrs; import aQute.bnd.service.Actionable; import aQute.bnd.service.RepositoryPlugin; import aQute.lib.exceptions.Exceptions; import bndtools.BndConstants; import bndtools.Plugin; import bndtools.central.Central; import bndtools.central.WorkspaceR5Repository; import bndtools.editor.common.BndEditorPart; import bndtools.editor.common.UpDownButtonBarPart; import bndtools.editor.common.UpDownButtonBarPart.UpDownListener; import bndtools.shared.URLDialog; public class RepositorySelectionPart extends BndEditorPart { private final Image refreshImg = AbstractUIPlugin.imageDescriptorFromPlugin(Plugin.PLUGIN_ID, "icons/arrow_refresh.png").createImage(); private final Image bundleImg = Icons.desc("bundle").createImage(); private final Image nonObrRepoImg = AbstractUIPlugin.imageDescriptorFromPlugin(Plugin.PLUGIN_ID, "icons/warning_obj.gif").createImage(); private final Image imgUp = AbstractUIPlugin.imageDescriptorFromPlugin(Plugin.PLUGIN_ID, "/icons/arrow_up.png").createImage(); private final Image imgDown = AbstractUIPlugin.imageDescriptorFromPlugin(Plugin.PLUGIN_ID, "/icons/arrow_down.png").createImage(); private final Image imgLink = Icons.desc("link").createImage(); private final Image projectImg = PlatformUI.getWorkbench().getSharedImages().getImage(IDE.SharedImages.IMG_OBJ_PROJECT); private final Image repoImg = PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER); private final Object MESSAGE_KEY = new Object(); private final EditorPart editor; private final Button btnStandaloneCheckbox; private final StackLayout stackLayout; private final Control saveToRefreshControl; private final CheckboxTableViewer runReposViewer; private final UpDownButtonBarPart upDownReposPart; private RepositoriesEditModel repositories; private AddRemoveButtonBarPart addRemove; /** * Create the SectionPart. * * @param parent * @param toolkit * @param style */ public RepositorySelectionPart(final EditorPart editor, Composite parent, FormToolkit toolkit, int style) { super(parent, toolkit, style); this.editor = editor; Section section = getSection(); section.setText("Repositories"); GridLayout gl; GridData gd; // Create main container with -standalone checkbox Composite cmpMainContainer = toolkit.createComposite(section); section.setClient(cmpMainContainer); gl = new GridLayout(1, false); gl.marginWidth = 0; gl.marginHeight = 0; cmpMainContainer.setLayout(gl); // Create -standalone checkbox btnStandaloneCheckbox = toolkit.createButton(cmpMainContainer, "Standalone Mode", SWT.CHECK); btnStandaloneCheckbox.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); btnStandaloneCheckbox.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { try { boolean standalone = btnStandaloneCheckbox.getSelection(); repositories = repositories.setStandalone(standalone, model); markDirty(); refreshFromModel(); } catch (Exception e1) { throw Exceptions.duck(e1); } } }); // Create stacked container for the three(!) possible contents Composite cmpStackContainer = toolkit.createComposite(cmpMainContainer); stackLayout = new StackLayout(); stackLayout.marginHeight = 0; stackLayout.marginWidth = 0; cmpStackContainer.setLayout(stackLayout); gd = new GridData(SWT.FILL, SWT.TOP, true, false); gd.heightHint = 100; cmpStackContainer.setLayoutData(gd); // Create contents for the "save to refresh" control Composite cmpSaveToRefresh = toolkit.createComposite(cmpStackContainer); Hyperlink btnSaveToRefresh = toolkit.createHyperlink(cmpSaveToRefresh, "Save file to reload repositories...", SWT.NONE); saveToRefreshControl = cmpSaveToRefresh; stackLayout.topControl = saveToRefreshControl; gl = new GridLayout(1, true); btnSaveToRefresh.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); cmpSaveToRefresh.setLayout(gl); btnSaveToRefresh.addHyperlinkListener(new HyperlinkAdapter() { @Override public void linkActivated(HyperlinkEvent ev) { IRunnableWithProgress runnable = new IRunnableWithProgress() { @Override public void run(IProgressMonitor monitor) { editor.doSave(monitor); } }; try { editor.getSite().getWorkbenchWindow().run(false, false, runnable); } catch (InterruptedException e) { // let it go } catch (InvocationTargetException e) { ErrorDialog.openError(editor.getSite().getShell(), "Error", null, new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, "Save error", e.getTargetException())); } } }); // Create contents for bnd layout workspace Composite cmpBndLayout = toolkit.createComposite(cmpStackContainer); gl = new GridLayout(2, false); gl.marginWidth = 0; gl.marginHeight = 0; cmpBndLayout.setLayout(gl); Table table = toolkit.createTable(cmpBndLayout, SWT.CHECK | SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL); gd = new GridData(SWT.FILL, SWT.FILL, true, true); gd.widthHint = 50; table.setLayoutData(gd); runReposViewer = new CheckboxTableViewer(table); runReposViewer.setContentProvider(ArrayContentProvider.getInstance()); runReposViewer.setCheckStateProvider(new ICheckStateProvider() { @Override public boolean isChecked(Object element) { return repositories.isIncluded((Repository) element); } @Override public boolean isGrayed(Object element) { return false; } }); runReposViewer.addCheckStateListener(new ICheckStateListener() { @Override public void checkStateChanged(CheckStateChangedEvent event) { repositories.setIncluded(event.getChecked(), (Repository) event.getElement()); } }); upDownReposPart = new UpDownButtonBarPart(runReposViewer); Control upDownReposControl = upDownReposPart.createControl(cmpBndLayout, SWT.FLAT | SWT.VERTICAL); upDownReposControl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); upDownReposPart.setEnabledUp(false); upDownReposPart.setEnabledDown(false); upDownReposPart.addListener(new UpDownListener() { @SuppressWarnings("unchecked") @Override public void changed(List<Object> order) { @SuppressWarnings("rawtypes") List l = order; repositories.setOrder(l); updateButtons(); markDirty(); } }); addRemove = new AddRemoveButtonBarPart(); Control addRemoveControl = addRemove.createControl(cmpBndLayout, SWT.FLAT | SWT.HORIZONTAL); addRemoveControl.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); addRemove.setRemoveEnabled(false); addRemove.setAddEnabled(false); addRemove.addListener(new AddRemoveListener() { @Override public void addSelected() { doAddStandaloneLink(); } @Override public void removeSelected() { doRemoveStandaloneLink(); } }); final Styler strikeoutStyler = new StrikeoutStyler(StyledString.QUALIFIER_STYLER, JFaceResources.getColorRegistry().get(JFacePreferences.QUALIFIER_COLOR)); runReposViewer.setLabelProvider(new StyledCellLabelProvider() { @Override public void update(ViewerCell cell) { Object element = cell.getElement(); String label = null; Image image = null; Styler styler = null; Repository repo = (Repository) element; label = repo.toString(); if (repo instanceof RepositoryPlugin) { RepositoryPlugin repositoryPlugin = (RepositoryPlugin) repo; label = repositoryPlugin.getName(); } else if (repo instanceof aQute.bnd.deployer.repository.wrapper.Plugin) { @SuppressWarnings("resource") aQute.bnd.deployer.repository.wrapper.Plugin wrapper = (aQute.bnd.deployer.repository.wrapper.Plugin) repo; wrapper.init(); label = wrapper.toString(); } image = repoImg; if (repo instanceof WorkspaceR5Repository) { image = projectImg; } boolean included = repositories.isIncluded(repo); styler = included ? null : strikeoutStyler; StyledString styledLabel = new StyledString(label, styler); cell.setText(styledLabel.getString()); cell.setStyleRanges(styledLabel.getStyleRanges()); cell.setImage(image); } @Override public String getToolTipText(Object element) { String tooltip = null; if (element instanceof Actionable) { try { tooltip = ((Actionable) element).tooltip(new Object[] { element }); } catch (Exception e) { // ignore } } if (tooltip != null) return tooltip; if (repositories.isIncluded((Repository) element)) { tooltip = "Included for resolution."; } else { tooltip = "Excluded from resolution."; } return tooltip; } }); runReposViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { updateButtons(); } }); stackLayout.topControl = cmpBndLayout; gd = new GridData(SWT.FILL, SWT.FILL, true, true); cmpStackContainer.setLayoutData(gd); cmpMainContainer.layout(true, true); ColumnViewerToolTipSupport.enableFor(runReposViewer, ToolTip.NO_RECREATE); } void updateButtons() { boolean enableDown = false; boolean enableUp = false; boolean remove = false; boolean add = repositories.isStandalone(); IStructuredSelection sel = (IStructuredSelection) runReposViewer.getSelection(); if (!sel.isEmpty()) { @SuppressWarnings({ "unchecked" }) List<Repository> list = sel.toList(); List<Repository> ordered = repositories.getOrdered(); for (Repository r : list) { int index = ordered.indexOf(r); if (index > 0) { enableUp = true; } if (index < ordered.size() - 1) { enableDown = true; } if (repositories.isStandaloneRepository(r)) { remove = true; } } } upDownReposPart.setEnabledUp(enableUp); upDownReposPart.setEnabledDown(enableDown); addRemove.setRemoveEnabled(remove); addRemove.setAddEnabled(add); } private void reloadRepos() { final IMessageManager messages = getManagedForm().getMessageManager(); messages.removeMessage(MESSAGE_KEY, runReposViewer.getControl()); final List<Repository> allRepos = new ArrayList<>(); try { allRepos.addAll(repositories.getOrdered()); runReposViewer.setInput(allRepos); } catch (Exception e) { messages.addMessage(MESSAGE_KEY, "Repository List: Unable to load OSGi Repositories. " + e.getMessage(), e, IMessageProvider.ERROR, runReposViewer.getControl()); // Load the repos and clear the error message if the Workspace is initialised later. Central.onWorkspaceInit(new Success<Workspace,Void>() { @Override public Promise<Void> call(final Promise<Workspace> resolved) throws Exception { final Deferred<Void> completion = new Deferred<>(); SWTConcurrencyUtil.execForControl(runReposViewer.getControl(), true, new Runnable() { @Override public void run() { try { allRepos.clear(); allRepos.addAll(resolved.getValue().getPlugins(Repository.class)); runReposViewer.setInput(allRepos); messages.removeMessage(MESSAGE_KEY, runReposViewer.getControl()); completion.resolve(null); } catch (Exception e) { completion.fail(e); } } }); return completion.getPromise(); } }); } updateButtons(); } private void doAddStandaloneLink() { try { URLDialog dialog = new URLDialog(editor.getSite().getShell(), "Add repository URL"); if (dialog.open() == Window.OK) { URI location = dialog.getLocation(); Attrs attrs = new Attrs(); if (dialog.getName() != null) attrs.put("name", dialog.getName()); HeaderClause clause = new HeaderClause(location.toString(), attrs); repositories.add(clause); refreshFromModel(); markDirty(); } } catch (Exception e) { throw Exceptions.duck(e); } } private void doRemoveStandaloneLink() { try { ISelection selection = runReposViewer.getSelection(); if (selection.isEmpty()) return; if (selection instanceof IStructuredSelection) { @SuppressWarnings("unchecked") List<Object> list = ((IStructuredSelection) selection).toList(); for (Object o : list) { if (repositories.remove((Repository) o)) { refreshFromModel(); markDirty(); } } } } catch (Exception e) { throw Exceptions.duck(e); } } @Override protected String[] getProperties() { return new String[] { BndConstants.RUNREPOS, BndEditModel.PROP_WORKSPACE }; } @Override protected void refreshFromModel() { repositories = new RepositoriesEditModel(model); btnStandaloneCheckbox.setSelection(repositories.isStandalone()); updateButtons(); reloadRepos(); } @Override protected void commitToModel(boolean onSave) { repositories.commitToModel(model); } @Override public void dispose() { refreshImg.dispose(); bundleImg.dispose(); nonObrRepoImg.dispose(); imgUp.dispose(); imgDown.dispose(); imgLink.dispose(); super.dispose(); } }