/** * Copyright (c) 2010, 2012 Darmstadt University of Technology. * 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: * Sebastian Proksch - initial API and implementation * Patrick Gottschaemmer, Olav Lenz - add Drag'n'Drop support * Olav Lenz - externalize Strings. */ package org.eclipse.recommenders.internal.apidocs.rcp; import static org.eclipse.recommenders.internal.apidocs.rcp.ApidocsViewUtils.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.inject.Inject; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.ui.JavaElementLabelProvider; import org.eclipse.jdt.ui.JavaElementLabels; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.jface.viewers.ViewerDropAdapter; import org.eclipse.recommenders.apidocs.rcp.ApidocProvider; import org.eclipse.recommenders.internal.apidocs.rcp.l10n.LogMessages; import org.eclipse.recommenders.internal.apidocs.rcp.l10n.Messages; import org.eclipse.recommenders.rcp.JavaElementSelectionEvent; import org.eclipse.recommenders.rcp.utils.PartListener2Adapter; import org.eclipse.recommenders.utils.Logs; import org.eclipse.recommenders.utils.rcp.Selections; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DragSourceAdapter; import org.eclipse.swt.dnd.DragSourceEvent; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.dnd.TransferData; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.ui.IWorkbenchPartReference; import org.eclipse.ui.part.ViewPart; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; import com.google.common.collect.Iterables; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; public class ApidocsView extends ViewPart { public static final String ID = "org.eclipse.recommenders.apidocs.rcp.views.apidocs"; //$NON-NLS-1$ private final EventBus workspaceBus; private final SubscriptionManager subscriptionManager; private final List<ApidocProvider> providers; private final ApidocsPreferences preferences; private static final long LABEL_FLAGS = JavaElementLabels.ALL_FULLY_QUALIFIED | JavaElementLabels.M_PRE_RETURNTYPE | JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_EXCEPTIONS | JavaElementLabels.F_PRE_TYPE_SIGNATURE | JavaElementLabels.T_TYPE_PARAMETERS; private static final int MOVE_AFTER = 1; private static final int MOVE_BEFORE = 0; private final JavaElementLabelProvider labelProvider = new JavaElementLabelProvider((int) LABEL_FLAGS); private SashForm sash; private ScrolledComposite scrollable; private Composite content; private TableViewer viewer; private List<ApidocProvider> providerRanking; private boolean visible = true; private ApidocProvider activeProvider; private JavaElementSelectionEvent activeSelection; @Inject public ApidocsView(final EventBus workspaceBus, final SubscriptionManager subscriptionManager, final List<ApidocProvider> providers, final ApidocsPreferences preferences) { this.workspaceBus = workspaceBus; this.subscriptionManager = subscriptionManager; this.providers = providers; this.preferences = preferences; providerRanking = loadProviderRanking(); } private List<ApidocProvider> loadProviderRanking() { List<ApidocProvider> providerRanking = new LinkedList<ApidocProvider>(); Map<String, ApidocProvider> providerMap = fillProviderMap(); List<String> providerIds = preferences.loadOrderedProviderIds(); LinkedList<ApidocProvider> remainingProviders = new LinkedList<ApidocProvider>(providers); for (String providerName : providerIds) { ApidocProvider tmpProvider = providerMap.get(providerName); if (tmpProvider != null) { providerRanking.add(tmpProvider); remainingProviders.remove(tmpProvider); } } providerRanking.addAll(remainingProviders); return providerRanking; } private Map<String, ApidocProvider> fillProviderMap() { HashMap<String, ApidocProvider> providerMap = new HashMap<String, ApidocProvider>(); for (ApidocProvider provider : providers) { providerMap.put(provider.getId(), provider); } return providerMap; } @VisibleForTesting public void storeProviderRanking() { preferences.storeProviderRanking(providerRanking); } @Override public void createPartControl(final Composite parent) { createSash(parent); createProviderOverview(); createContentArea(); addVisibilityListener(); applyUiPreferences(); workspaceBus.register(this); } private void createSash(final Composite parent) { sash = new SashForm(parent, SWT.SMOOTH); sash.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create()); sash.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(final DisposeEvent e) { preferences.storeSashWeights(sash.getWeights()); } }); } private void createProviderOverview() { viewer = new TableViewer(sash, SWT.SINGLE); addDnDSupport(); viewer.setComparator(new ViewerComparator() { @Override public int compare(Viewer viewer, Object first, Object second) { int indexFirst = providerRanking.indexOf(first); int indexSecond = providerRanking.indexOf(second); return indexFirst - indexSecond; } }); viewer.setContentProvider(new ArrayContentProvider()); viewer.setLabelProvider(new LabelProvider() { @Override public String getText(Object element) { return ((ApidocProvider) element).getDescription().getName(); } @Override public Image getImage(Object element) { return ((ApidocProvider) element).getDescription().getImage(); } }); viewer.setInput(providers); viewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { ApidocProvider newProvider = Selections.<ApidocProvider>getFirstSelected(event.getSelection()).orNull(); if (newProvider == activeProvider) { return; } activeProvider = newProvider; Job job = new Job(Messages.JOB_UPDATING_APIDOCS) { @Override protected IStatus run(IProgressMonitor monitor) { onJavaSelection(activeSelection); return Status.OK_STATUS; } }; job.setSystem(true); job.schedule(); } }); viewer.setSelection(new StructuredSelection(Iterables.getFirst(providers, null))); } private void addDnDSupport() { final int operations = DND.DROP_MOVE; final Transfer[] transferTypes = new Transfer[] { DnDProviderTransfer.getInstance() }; viewer.addDragSupport(operations, transferTypes, new DragSourceAdapter() { @Override public void dragSetData(final DragSourceEvent event) { final IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); if (DnDProviderTransfer.getInstance().isSupportedType(event.dataType)) { final ApidocProvider selectedProvider = (ApidocProvider) selection.getFirstElement(); DnDProviderTransfer.getInstance().setExtdocProvider(selectedProvider); } } }); viewer.addDropSupport(operations, transferTypes, new ViewerDropAdapter(viewer) { private int newIndex; private int currentFeedback; @Override public boolean validateDrop(final Object target, final int operation, final TransferData transferType) { return DnDProviderTransfer.getInstance().isSupportedType(transferType); } @Override public void dragOver(final DropTargetEvent event) { if (determineLocation(event) == ViewerDropAdapter.LOCATION_BEFORE && isFirstProvider(event)) { event.feedback = DND.FEEDBACK_INSERT_BEFORE; } else { event.feedback = DND.FEEDBACK_INSERT_AFTER; } currentFeedback = event.feedback; } private boolean isFirstProvider(final DropTargetEvent event) { return providerRanking.indexOf(determineTarget(event)) == 0; } @Override public void drop(final DropTargetEvent event) { if (event.item != null) { newIndex = providerRanking.indexOf(event.item.getData()); } else { newIndex = providerRanking.size() - 1; } performDrop(event.data); } @Override public boolean performDrop(final Object data) { final ApidocProvider provider = (ApidocProvider) data; final int oldIndex = providerRanking.indexOf(provider); if (currentFeedback == DND.FEEDBACK_INSERT_AFTER) { moveAfter(oldIndex, newIndex); } else { moveBefore(oldIndex, newIndex); } storeProviderRanking(); viewer.refresh(); return true; } }); } private void move(int oldIndex, int newIndex, int moveStyle) { if (newIndex == oldIndex) { return; } else if (newIndex < oldIndex) { ApidocProvider tmp = providerRanking.remove(oldIndex); providerRanking.add(newIndex + moveStyle, tmp); } else { ApidocProvider tmp = providerRanking.remove(oldIndex); providerRanking.add(newIndex - 1 + moveStyle, tmp); } } @VisibleForTesting public void moveAfter(int oldIndex, int newIndex) { move(oldIndex, newIndex, MOVE_AFTER); } @VisibleForTesting public void moveBefore(int oldIndex, int newIndex) { move(oldIndex, newIndex, MOVE_BEFORE); } @VisibleForTesting public List<ApidocProvider> getProviderRanking() { return providerRanking; } private void createContentArea() { scrollable = new ScrolledComposite(sash, SWT.H_SCROLL | SWT.V_SCROLL); scrollable.getVerticalBar().setIncrement(20); scrollable.setExpandHorizontal(true); scrollable.setExpandVertical(true); content = new Composite(scrollable, SWT.NONE); content.setLayout(new GridLayout()); content.setFont(JFaceResources.getDialogFont()); ApidocsViewUtils.setInfoBackgroundColor(content); scrollable.setContent(content); } private void applyUiPreferences() { sash.setWeights(preferences.loadSashWeights()); } private void addVisibilityListener() { getViewSite().getPage().addPartListener(new PartListener2Adapter() { @Override public void partHidden(final IWorkbenchPartReference partRef) { if (isExtdocView(partRef)) { visible = false; } } @Override public void partVisible(final IWorkbenchPartReference partRef) { if (isExtdocView(partRef)) { visible = true; } } private boolean isExtdocView(final IWorkbenchPartReference partRef) { return partRef.getPart(false) == ApidocsView.this; } }); } @Override public void dispose() { workspaceBus.unregister(this); super.dispose(); } @Subscribe public void onJavaSelection(final JavaElementSelectionEvent selection) { activeSelection = selection; if (visible && activeProvider != null && activeSelection != null) { try { disposeOldContentAndDisableRedrawOnContentArea(); runProvider(selection); refreshAndEnableDrawContentArea(); } catch (Exception e) { Logs.log(LogMessages.ERROR_FAILED_TO_UPDATE_VIEW, e, selection); } } } private void runProvider(JavaElementSelectionEvent selection) throws IllegalAccessException, InvocationTargetException { Optional<Method> opt = subscriptionManager.findSubscribedMethod(activeProvider, selection); if (opt.isPresent()) { Method method = opt.get(); IJavaElement element = selection.getElement(); method.invoke(activeProvider, element, selection, content); } } private void refreshAndEnableDrawContentArea() { content.getDisplay().asyncExec(new Runnable() { @Override public void run() { Point size = content.computeSize(SWT.DEFAULT, SWT.DEFAULT); scrollable.setMinSize(size); content.layout(); // content.setRedraw(true); } }); } private void disposeOldContentAndDisableRedrawOnContentArea() { content.getDisplay().syncExec(new Runnable() { @Override public void run() { // content.setRedraw(false); ApidocsViewUtils.disposeChildren(content); addCurrentSelectionHeader(); } }); } private void addCurrentSelectionHeader() { final IJavaElement element = activeSelection.getElement(); final String text; switch (element.getElementType()) { case IJavaElement.PACKAGE_FRAGMENT_ROOT: case IJavaElement.PACKAGE_FRAGMENT: text = element.getElementName(); break; case IJavaElement.LOCAL_VARIABLE: text = JavaElementLabels.getElementLabel(element, JavaElementLabels.F_PRE_TYPE_SIGNATURE | JavaElementLabels.F_POST_QUALIFIED); break; default: text = JavaElementLabels.getElementLabel(element, LABEL_FLAGS); break; } Composite header = new Composite(content, SWT.NONE); ApidocsViewUtils.setInfoBackgroundColor(header); header.setLayout(new GridLayout(2, false)); Label img = new Label(header, SWT.NONE); img.setImage(labelProvider.getImage(element)); setInfoForegroundColor(img); setInfoBackgroundColor(img); Label name = createLabel(header, text, true); name.setFont(JFaceResources.getHeaderFont()); } @Override public void setFocus() { viewer.getControl().setFocus(); } }