/******************************************************************************* * Copyright (c) 2014, 2016 Wind River Systems, Inc. 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: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.te.tcf.processes.ui.internal.dialogs; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.EventObject; import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.viewers.DecoratingLabelProvider; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; 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.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.tcf.protocol.Protocol; import org.eclipse.tcf.te.runtime.callback.Callback; import org.eclipse.tcf.te.runtime.events.ChangeEvent; import org.eclipse.tcf.te.runtime.events.EventManager; import org.eclipse.tcf.te.runtime.interfaces.events.IEventListener; import org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer; import org.eclipse.tcf.te.runtime.model.interfaces.IContainerModelNode; import org.eclipse.tcf.te.runtime.model.interfaces.IModelNode; import org.eclipse.tcf.te.runtime.properties.PropertiesContainer; import org.eclipse.tcf.te.runtime.services.ServiceUtils; import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepAttributes; import org.eclipse.tcf.te.tcf.core.interfaces.IContextDataProperties; import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode; import org.eclipse.tcf.te.tcf.locator.utils.PeerNodeDataHelper; import org.eclipse.tcf.te.tcf.processes.core.interfaces.IProcessContextItem; import org.eclipse.tcf.te.tcf.processes.core.interfaces.IProcessesDataProperties; import org.eclipse.tcf.te.tcf.processes.core.model.ModelManager; import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNode; import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.runtime.IRuntimeModel; import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.runtime.IRuntimeModelLookupService; import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.runtime.IRuntimeModelRefreshService; import org.eclipse.tcf.te.tcf.processes.core.util.ProcessDataHelper; import org.eclipse.tcf.te.tcf.processes.ui.editor.tree.ContentProvider; import org.eclipse.tcf.te.tcf.processes.ui.interfaces.IProcessMonitorUIDelegate; import org.eclipse.tcf.te.tcf.processes.ui.nls.Messages; import org.eclipse.tcf.te.ui.interfaces.IDataExchangeDialog; import org.eclipse.tcf.te.ui.jface.dialogs.CustomTitleAreaDialog; import org.eclipse.tcf.te.ui.trees.TreeViewerSorterCaseInsensitive; import org.eclipse.tcf.te.ui.views.navigator.DelegatingLabelProvider; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.FilteredTree; import org.eclipse.ui.dialogs.PatternFilter; /** * Dialog for selecting contexts to attach to. */ public class AttachContextSelectionDialog extends CustomTitleAreaDialog implements IEventListener, IDataExchangeDialog { protected TreeViewer viewer; protected FilteredTree filteredTree; private boolean initDone = false; protected boolean hasAttachedContexts; IPeerNode peerNode; IPropertiesContainer data = null; /* (non-Javadoc) * @see org.eclipse.jface.dialogs.Dialog#isResizable() */ @Override protected boolean isResizable() { return true; } /* (non-Javadoc) * @see org.eclipse.jface.dialogs.Dialog#getDialogBoundsSettings() */ @Override protected IDialogSettings getDialogBoundsSettings() { return getDialogSettings(); } /** * Constructor */ public AttachContextSelectionDialog(Shell parent, String contextHelpId) { super(parent, contextHelpId); } /* (non-Javadoc) * @see org.eclipse.tcf.te.ui.jface.dialogs.CustomTitleAreaDialog#createDialogAreaContent(org.eclipse.swt.widgets.Composite) */ @Override protected void createDialogAreaContent(Composite parent) { super.createDialogAreaContent(parent); // Set dialog title and default message setDialogTitle(Messages.AttachContextSelectionDialog_dialogTitle); setTitle(Messages.AttachContextSelectionDialog_title); setDefaultMessage(Messages.AttachContextSelectionDialog_message, IMessageProvider.INFORMATION); // Create the inner panel Composite panel = new Composite(parent, SWT.NULL); GridLayout layout = new GridLayout(1, false); layout.marginHeight = 0; layout.marginWidth = 0; panel.setLayout(layout); panel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); Label filterLabel = new Label(panel, SWT.NONE); filterLabel.setText(Messages.AttachContextSelectionDialog_filter_label); PatternFilter filter = new PatternFilter() { @Override public boolean isElementSelectable(final Object element) { return element instanceof IProcessContextNode; } @Override protected boolean isLeafMatch(Viewer viewer, final Object element) { if (element instanceof IProcessContextNode) { final AtomicBoolean canAttach = new AtomicBoolean(); Protocol.invokeAndWait(new Runnable() { @Override public void run() { canAttach.set(canAttach(element)); } }); return canAttach.get() && super.isLeafMatch(viewer, element); } return true; } }; filter.setIncludeLeadingWildcard(true); filter.setPattern("org.eclipse.ui.keys.optimization.false"); //$NON-NLS-1$ filteredTree = new FilteredTree(panel, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER, filter, true) { /* (non-Javadoc) * @see org.eclipse.ui.dialogs.FilteredTree#getFilterString() */ @Override protected String getFilterString() { String filter = super.getFilterString(); if (filter != null) { filter = filter.trim(); if (filter.length() == 0) { return "*"; //$NON-NLS-1$ } return filter; } return null; } }; viewer = filteredTree.getViewer(); GridData gd = new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL); gd.minimumHeight = 250; gd.minimumWidth = 300; gd.widthHint = 300; gd.heightHint = 300; filteredTree.setLayoutData(gd); // needs to be set using reflection as it is e4 // filteredTree.setQuickSelectionMode(true); try { Method method = filteredTree.getClass().getMethod("setQuickSelectionMode", Boolean.TYPE); //$NON-NLS-1$ method.invoke(filteredTree, Boolean.TRUE); } catch (Throwable e) { } viewer.setContentProvider(new ContentProvider()); DelegatingLabelProvider labelProvider = new DelegatingLabelProvider() { /* (non-Javadoc) * @see org.eclipse.tcf.te.ui.views.navigator.DelegatingLabelProvider#decorateText(java.lang.String, java.lang.Object) */ @Override public String decorateText(String text, final Object element) { if (element instanceof IProcessContextNode) { final AtomicBoolean isAttached = new AtomicBoolean(); final AtomicLong pid = new AtomicLong(); Protocol.invokeAndWait(new Runnable() { @Override public void run() { isAttached.set(isAttached(element)); pid.set(((IProcessContextNode)element).getSysMonitorContext().getPID()); } }); String id = pid.get() >= 0 ? Long.toString(pid.get()) : ""; //$NON-NLS-1$ if (id.startsWith("P")) id = id.substring(1); //$NON-NLS-1$ IPeerNode peerNode = (IPeerNode)((IProcessContextNode)element).getAdapter(IPeerNode.class); IProcessMonitorUIDelegate delegate = ServiceUtils.getUIServiceDelegate(peerNode, peerNode, IProcessMonitorUIDelegate.class); String newId = delegate != null ? delegate.getText(element, "PID", id) : null; //$NON-NLS-1$ if (newId != null) { text = NLS.bind(Messages.AttachContextSelectionDialog_pid_decoration, text, newId); } if (isAttached.get()) { text = NLS.bind(Messages.AttachContextSelectionDialog_allReadyAttached_decoration, text); } } return text; } /* (non-Javadoc) * @see org.eclipse.tcf.te.ui.views.navigator.DelegatingLabelProvider#getForeground(java.lang.Object) */ @Override public Color getForeground(final Object element) { final AtomicBoolean canAttach = new AtomicBoolean(); final AtomicBoolean isAttached = new AtomicBoolean(); Protocol.invokeAndWait(new Runnable() { @Override public void run() { canAttach.set(canAttach(element)); isAttached.set(isAttached(element)); } }); if (!canAttach.get() || isAttached.get()) { return PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY); } return null; } }; viewer.setLabelProvider(new DecoratingLabelProvider(labelProvider, labelProvider) ); viewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { validate(); } }); viewer.addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick(DoubleClickEvent event) { if (event.getSelection() instanceof IStructuredSelection && ((IStructuredSelection)event.getSelection()).size() > 0) { final Object selected = ((IStructuredSelection)event.getSelection()).getFirstElement(); final AtomicBoolean valid = new AtomicBoolean(); Protocol.invokeAndWait(new Runnable() { @Override public void run() { valid.set(isValid(selected)); } }); if (valid.get()) { okPressed(); } } } }); viewer.setSorter(new TreeViewerSorterCaseInsensitive()); EventManager.getInstance().addEventListener(this, ChangeEvent.class); restoreWidgetValues(); setupData(data); applyDialogFont(panel); initDone = true; } /* (non-Javadoc) * @see org.eclipse.jface.dialogs.TitleAreaDialog#createContents(org.eclipse.swt.widgets.Composite) */ @Override protected Control createContents(Composite parent) { Control control = super.createContents(parent); validate(); return control; } protected void validate() { if (!initDone) { return; } final AtomicBoolean valid = new AtomicBoolean(hasAttachedContexts); final ISelection selection = viewer.getSelection(); if (selection instanceof IStructuredSelection && !selection.isEmpty()) { valid.set(true); Protocol.invokeAndWait(new Runnable() { @Override public void run() { Iterator<?> it = ((IStructuredSelection)selection).iterator(); while (valid.get() && it.hasNext()) { Object sel = it.next(); valid.set(isValid(sel)); } } }); } if (valid.get()) { setErrorMessage(null); } getButton(IDialogConstants.OK_ID).setEnabled(valid.get()); } protected boolean canAttach(Object selection) { if (selection instanceof IProcessContextNode) { IProcessContextNode node = (IProcessContextNode)selection; if (node.getProcessContext() != null) { if (node.getProcessContext().getProperties().containsKey("CanAttach")) { //$NON-NLS-1$ Boolean value = (Boolean)node.getProcessContext().getProperties().get("CanAttach"); //$NON-NLS-1$ return value != null && value.booleanValue(); } return true; } } return false; } protected boolean isAttached(Object selection) { if (selection instanceof IProcessContextNode) { IProcessContextNode node = (IProcessContextNode)selection; boolean isAttached = node.getProcessContext() != null && node.getProcessContext().isAttached(); if (!hasAttachedContexts) hasAttachedContexts = isAttached; return isAttached; } return false; } protected boolean isValid(Object selection) { return canAttach(selection) || isAttached(selection); } /* * (non-Javadoc) * @see com.windriver.ide.target.ui.dialogs.UnifiedDialog#saveWidgetValues() */ @Override public void saveWidgetValues() { } /* (non-Javadoc) * @see org.eclipse.tcf.te.ui.jface.dialogs.CustomTitleAreaDialog#restoreWidgetValues() */ @Override protected void restoreWidgetValues() { super.restoreWidgetValues(); } /* (non-Javadoc) * @see org.eclipse.tcf.te.ui.jface.dialogs.CustomTitleAreaDialog#dispose() */ @Override protected void dispose() { super.dispose(); viewer = null; EventManager.getInstance().removeEventListener(this); } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.events.IEventListener#eventFired(java.util.EventObject) */ @Override public void eventFired(EventObject event) { if (event instanceof ChangeEvent) { final ChangeEvent e = (ChangeEvent)event; if (IContainerModelNode.NOTIFY_CHANGED.equals(e.getEventId()) && (e.getSource() instanceof IRuntimeModel || e.getSource() instanceof IProcessContextNode)) { IPeerNode node = (IPeerNode)Platform.getAdapterManager().getAdapter(e.getSource(), IPeerNode.class); if (node == peerNode) { getShell().getDisplay().asyncExec(new Runnable() { @Override public void run() { viewer.refresh(e.getSource(), true); } }); } } } } /* (non-Javadoc) * @see org.eclipse.tcf.te.ui.interfaces.data.IDataExchangeNode#setupData(org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer) */ @Override public void setupData(IPropertiesContainer data) { this.data = new PropertiesContainer(); this.data.setProperties(data.getProperties()); IPeerNode[] contexts = PeerNodeDataHelper.decodeContextList(data.getStringProperty(IContextDataProperties.PROPERTY_CONTEXT_LIST)); IPeerNode newPeerNode = contexts != null && contexts.length > 0 ? contexts[0] : null; if (peerNode == null || peerNode != newPeerNode) { peerNode = newPeerNode; if (viewer != null) { viewer.setInput(peerNode); viewer.expandAll(); viewer.refresh(true); } Protocol.invokeLater(new Runnable() { @Override public void run() { IRuntimeModelRefreshService refresh = ModelManager.getRuntimeModel(peerNode).getService(IRuntimeModelRefreshService.class); refresh.refresh(new Callback() { @Override protected void internalDone(Object caller, IStatus status) { if (viewer != null) { getShell().getDisplay().asyncExec(new Runnable() { @Override public void run() { internalSetupData(); } }); } } }); } }); } else { internalSetupData(); } } protected void internalSetupData() { if (viewer != null) { viewer.setInput(peerNode); viewer.expandAll(); viewer.refresh(true); final List<IProcessContextNode> ctxNodes = new ArrayList<IProcessContextNode>(); Protocol.invokeAndWait(new Runnable() { @Override public void run() { for (IProcessContextItem item : ProcessDataHelper.decodeProcessContextItems(AttachContextSelectionDialog.this.data.getStringProperty(IProcessesDataProperties.PROPERTY_CONTEXT_LIST))) { IRuntimeModelLookupService lkup = ModelManager.getRuntimeModel(peerNode).getService(IRuntimeModelLookupService.class); IModelNode[] nodes = null; if (item.getId() != null) { nodes = lkup.lkupModelNodesById(item.getId()); } if (nodes == null || nodes.length == 0) { nodes = lkup.lkupModelNodesByName(item.getName()); } if (nodes != null) { for (IModelNode node : nodes) { if (node instanceof IProcessContextNode) { ctxNodes.add((IProcessContextNode)node); } } } } } }); viewer.setSelection(new StructuredSelection(ctxNodes)); } } /* (non-Javadoc) * @see org.eclipse.tcf.te.ui.interfaces.data.IDataExchangeNode#extractData(org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer) */ @Override public void extractData(IPropertiesContainer data) { Assert.isNotNull(data); data.setProperties(this.data.getProperties()); } /* (non-Javadoc) * @see org.eclipse.tcf.te.ui.jface.dialogs.CustomTrayDialog#okPressed() */ @Override protected void okPressed() { // Extract the properties if (data == null) { data = new PropertiesContainer(); } if (viewer != null) { final ISelection sel = viewer.getSelection(); final List<IProcessContextItem> items = new ArrayList<IProcessContextItem>(); if (sel instanceof IStructuredSelection) { Protocol.invokeAndWait(new Runnable() { @Override public void run() { final Iterator<?> it = ((IStructuredSelection)sel).iterator(); while (it.hasNext()) { Object obj = it.next(); if (obj instanceof IProcessContextNode) { IProcessContextItem item = ProcessDataHelper.getProcessContextItem((IProcessContextNode)obj); if (item != null && !items.contains(item) && !isAttached(obj)) { items.add(item); } } } } }); } data.setProperty(IProcessesDataProperties.PROPERTY_CONTEXT_LIST, ProcessDataHelper.encodeProcessContextItems(items.toArray(new IProcessContextItem[items.size()]))); if (items.size() == 0) // No recent action history persistence data.setProperty(IStepAttributes.PROP_SKIP_LAST_RUN_HISTORY, true); } super.okPressed(); } }