/******************************************************************************* * Copyright (c) 2006-2012 * Software Technology Group, Dresden University of Technology * DevBoost GmbH, Berlin, Amtsgericht Charlottenburg, HRB 140026 * * 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: * Software Technology Group - TU Dresden, Germany; * DevBoost GmbH - Berlin, Germany * - initial API and implementation ******************************************************************************/ package org.reuseware.sokan.ui.views; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.edit.provider.ComposedAdapterFactory; import org.eclipse.emf.edit.provider.IItemPropertyDescriptor; import org.eclipse.emf.edit.provider.IItemPropertySource; import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory; import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory; import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider; import org.eclipse.emf.edit.ui.provider.PropertyDescriptor; import org.eclipse.emf.edit.ui.provider.PropertySource; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.LocalSelectionTransfer; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.DecoratingLabelProvider; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ILabelDecorator; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IMemento; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.DrillDownAdapter; import org.eclipse.ui.part.ViewPart; import org.eclipse.ui.views.properties.IPropertyDescriptor; import org.eclipse.ui.views.properties.IPropertySheetPage; import org.eclipse.ui.views.properties.IPropertySource; import org.eclipse.ui.views.properties.PropertySheetPage; import org.reuseware.sokan.ID; import org.reuseware.sokan.index.notify.IndexListener; import org.reuseware.sokan.index.util.IndexUtil; import org.reuseware.sokan.index.util.ResourceUtil; import org.reuseware.sokan.ui.SokanUIPlugin; import org.reuseware.sokan.ui.internal.actions.FiltersAction; import org.reuseware.sokan.ui.internal.actions.NewAction; import org.reuseware.sokan.ui.internal.actions.OpenAction; import org.reuseware.sokan.ui.internal.filters.ArtifactFilter; import org.reuseware.sokan.ui.internal.views.BasicRepositoryIndexListener; import org.reuseware.sokan.ui.internal.views.BasicRepositoryTreeViewer; import org.reuseware.sokan.ui.internal.views.ErrorMarkingAdapterFactoryLabelProvider; import org.reuseware.sokan.ui.internal.views.LocalSelectionTransferViewerDragAdapter; import org.reuseware.sokan.ui.model.sokanui.Artifact; import org.reuseware.sokan.ui.model.sokanui.Container; import org.reuseware.sokan.ui.model.sokanui.Root; import org.reuseware.sokan.ui.model.sokanui.SokanuiFactory; import org.reuseware.sokan.ui.model.sokanui.util.SokanuiUtil; /** * A view on the complete Sokan repository. The view is extendible to * more specialized views on the repository. */ public class BasicRepositoryView extends ViewPart implements IPropertyChangeListener, ISelectionChangedListener { /** * Constant representing that the artifacts are currently structured in packages. */ public static final int MODE_PACKAGE = 0; /** * Constant representing that the artifacts are currently structured in folders. */ public static final int MODE_FOLDER = 1; /** * This view's ID. */ public static final String ID = "org.reuseware.sokan.ui.eclipse.BasicRepositoryView"; private IPreferenceStore preferenceStore; private int mode = MODE_PACKAGE; // viewer and adapters for views private TreeViewer viewer; private DrillDownAdapter drillDownAdapter; private ComposedAdapterFactory emfEditAdapterFactory; private IndexListener indexListener; private ResourceSet resourceSet; // actions // TODO #1449: private Action structureAction = new ChangeStructureAction(this); private Action openAction = new OpenAction(this); private Action newAction = new NewAction(this); private Action filtersAction = new FiltersAction(this); // filter private ArtifactFilter filter = new ArtifactFilter(); private IMemento memento; private static final String MEMENTO_TAG_FILTER = "TAG_FILTER"; // mapping files to their root EObject private Map<IResource, EObject> resource2EObjectMap = new HashMap<IResource, EObject>(); /** * The constructor. */ public BasicRepositoryView() { this.emfEditAdapterFactory = new ComposedAdapterFactory( ComposedAdapterFactory.Descriptor.Registry.INSTANCE); emfEditAdapterFactory .addAdapterFactory(new ResourceItemProviderAdapterFactory()); emfEditAdapterFactory .addAdapterFactory(new ReflectiveItemProviderAdapterFactory()); preferenceStore = SokanUIPlugin.getDefault().getPreferenceStore(); preferenceStore.addPropertyChangeListener(this); } /** * @return the utilized EMF edit adapter factory */ public ComposedAdapterFactory getAdapterFactory() { return this.emfEditAdapterFactory; } protected List<Action> getAdditionalActions() { return Collections.emptyList(); } /** * @param element an element displayed in the view * @return the URI of the artifact associated with the given element */ public URI getURIForElement(EObject element) { if (element == null) { return null; } if (element instanceof Artifact) { Artifact artifact = (Artifact) element; return ResourceUtil.uriFrom(ResourceUtil.idFrom(artifact.getId())); } if (element.eResource() == null) { return null; } return element.eResource().getURI(); } /** * Associate a certain resource with a certain element displayed in the view. * * @param resource the resource * @param element the element */ public void addResource2EObjectMapping(IResource resource, EObject element) { resource2EObjectMap.put(resource, element); } /** * @return the currently used artifact filter */ public ArtifactFilter getFilter() { return filter; } /** * This is a callback that will allow us to create the viewer and initialize it. * * @param parent the view's parent */ public void createPartControl(Composite parent) { viewer = new BasicRepositoryTreeViewer(this, parent); drillDownAdapter = new DrillDownAdapter(viewer); viewer.setContentProvider(new AdapterFactoryContentProvider( emfEditAdapterFactory)); ILabelDecorator decorator = PlatformUI.getWorkbench() .getDecoratorManager().getLabelDecorator(); viewer.setLabelProvider(new DecoratingLabelProvider( new ErrorMarkingAdapterFactoryLabelProvider(this, emfEditAdapterFactory), decorator)); getSite().setSelectionProvider(viewer); viewer.setSorter(new ViewerSorter()); viewer.addDragSupport(DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK, new Transfer[] { LocalSelectionTransfer.getTransfer() }, new LocalSelectionTransferViewerDragAdapter(viewer)); hookContextMenu(); contributeToActionBars(); viewer.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(DoubleClickEvent event) { boolean includesContainers = false; for (EObject selected : getSelectedElements()) { if (selected instanceof Container) { if (viewer.getExpandedState(selected)) { viewer.collapseToLevel(selected, 1); } else { viewer.expandToLevel(selected, 1); } includesContainers = true; } } if (!includesContainers) { getDoubleClickAction().run(); } } }); if (memento != null) { // restore filter IMemento filterMemento = memento.getChild(MEMENTO_TAG_FILTER); if (filterMemento != null) { filter.restoreState(filterMemento); } } viewer.addFilter(filter); viewer.addSelectionChangedListener(this); newRoot(); indexListener = new BasicRepositoryIndexListener(this); IndexUtil.INSTANCE.addListener(indexListener); } @Override public void dispose() { IndexUtil.INSTANCE.removeListener(indexListener); super.dispose(); } /** * Recreate the view's model by replacing the model's root element. */ public void newRoot() { Root root = null; resourceSet = IndexUtil.INSTANCE.createNewResourceSet(); if (mode == MODE_FOLDER) { root = SokanuiFactory.eINSTANCE.createRootFolder(); } else /* mode == MODE_PACKAGE */ { root = SokanuiFactory.eINSTANCE.createRootPackage(); } root.setResourceSet(resourceSet); root.getVisibleTypes().addAll(getVisibleTypes()); SokanuiUtil.init(root); viewer.setInput(root); refreshViewer(); } /** * @return the resource set this view is working with to load artifacts */ public ResourceSet getResourceSet() { return resourceSet; } /** * Updates the parts of the view's model that represent * the artifacts with the given IDs. * * @param delta IDs of the artifacts to update */ public void updateViewModel(Set<ID> delta) { SokanuiUtil.update((Root) viewer.getInput(), delta); } protected List<EClass> getVisibleTypes() { return Collections.emptyList(); } private void hookContextMenu() { MenuManager menuMgr = new MenuManager("#PopupMenu"); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { BasicRepositoryView.this.fillContextMenu(manager); } }); Menu menu = menuMgr.createContextMenu(viewer.getControl()); viewer.getControl().setMenu(menu); getSite().registerContextMenu(menuMgr, viewer); } private void contributeToActionBars() { IActionBars bars = getViewSite().getActionBars(); fillLocalPullDown(bars.getMenuManager()); fillLocalToolBar(bars.getToolBarManager()); } private void fillLocalPullDown(IMenuManager manager) { manager.add(openAction); manager.add(newAction); manager.add(new Separator()); for (Action additinalAction : getAdditionalActions()) { manager.add(additinalAction); } drillDownAdapter.addNavigationActions(manager); } private void fillContextMenu(IMenuManager manager) { manager.add(openAction); manager.add(newAction); manager.add(new Separator()); for (Action additinalAction : getAdditionalActions()) { manager.add(additinalAction); } manager.add(new Separator()); drillDownAdapter.addNavigationActions(manager); // Other plug-ins can contribute there actions here // manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); } private void fillLocalToolBar(IToolBarManager manager) { manager.add(openAction); manager.add(newAction); manager.add(new Separator()); for (Action additinalAction : getAdditionalActions()) { manager.add(additinalAction); } manager.add(new Separator()); manager.add(filtersAction); // TODO #1449: manager.add(structureAction); manager.add(new Separator()); drillDownAdapter.addNavigationActions(manager); } /** * @return the currently selected elements */ public List<EObject> getSelectedElements() { List<EObject> selectedElements = new ArrayList<EObject>(); TreeSelection selection = (TreeSelection) viewer.getSelection(); // to // update the selectedPackagedElements list Iterator<?> selectionIterator = selection.iterator(); while (selectionIterator.hasNext()) { Object next = selectionIterator.next(); EObject element = null; if (next instanceof EObject) { element = (EObject) next; } if (next instanceof IResource) { element = resource2EObjectMap.get(next); } if (element != null) { selectedElements.add(element); } } return selectedElements; } protected Action getDoubleClickAction() { return openAction; } /** * Refreshes the viewer. */ public void refreshViewer() { viewer.refresh(); } /** * Saves the state of the artifact filter. * Is is called when the Eclipse application is closed. * * @param memento the memento to save */ public void saveState(IMemento memento) { if (viewer == null) { if (this.memento != null) { memento.putMemento(this.memento); } return; } filter.saveState(memento.createChild(MEMENTO_TAG_FILTER)); } /** * Passing the focus request to the viewer's control. */ public void setFocus() { viewer.getControl().setFocus(); } /** * Refreshes the viewer. * * @param event the property change event */ public void propertyChange(PropertyChangeEvent event) { // viewPackageMgr.setPackageList(event.getNewValue().toString()); viewer.refresh(); } /** * Update the view upon a filter change. */ public void filterChanged() { BusyIndicator.showWhile(viewer.getControl().getShell().getDisplay(), new Runnable() { public void run() { // Filter has already been updated by dialog; just // refresh. viewer.getControl().setRedraw(false); viewer.refresh(false); viewer.getControl().setRedraw(true); // update after refresh since the content provider // caches summary info // updateStatusMessage(); // updateTitle(); } }); } @Override public Object getAdapter(@SuppressWarnings("rawtypes") Class required) { if (required.equals(IPropertySheetPage.class)) { return getPropertySheetPage(); } return super.getAdapter(required); } private PropertySheetPage propertySheetPage = null; /** * @return property sheet page (for the property view) associated with this repository view. */ public IPropertySheetPage getPropertySheetPage() { if (propertySheetPage == null) { propertySheetPage = new PropertySheetPage(); propertySheetPage .setPropertySourceProvider(new AdapterFactoryContentProvider( emfEditAdapterFactory) { protected IPropertySource createPropertySource( Object object, IItemPropertySource itemPropertySource) { return new PropertySource(object, itemPropertySource) { protected IPropertyDescriptor createPropertyDescriptor( IItemPropertyDescriptor itemPropertyDescriptor) { return new PropertyDescriptor(object, itemPropertyDescriptor) { public CellEditor createPropertyEditor( Composite composite) { return null; } }; } }; } }); } return propertySheetPage; } protected IFile getFileForSelection() { List<EObject> selections = getSelectedElements(); IFile file = null; if (!selections.isEmpty() && selections.get(0) instanceof EObject) { EObject element = (EObject) selections.get(0); file = getFileForElement(element); } return file; } /** * @param element an element displayed in the view * @return the file associated with the element */ public IFile getFileForElement(EObject element) { URI uri = getURIForElement(element); IFile file = null; if (uri != null) { file = ResourceUtil.fileFor(uri); } return file; } /** * Update the view's actions according to changes in the selection. * * @param event the selection changed event */ public void selectionChanged(SelectionChangedEvent event) { boolean includesContainers = false; for (EObject selected : getSelectedElements()) { if (selected instanceof Container) { includesContainers = true; break; } } if (includesContainers) { openAction.setEnabled(false); newAction.setEnabled(true); for (Action additinalAction : getAdditionalActions()) { additinalAction.setEnabled(false); } } else { openAction.setEnabled(true); newAction.setEnabled(false); for (Action additinalAction : getAdditionalActions()) { for (EObject selected : getSelectedElements()) { additinalAction.setEnabled(true); if (!canHandle(additinalAction, selected)) { additinalAction.setEnabled(false); break; } } } } } protected boolean canHandle(Action additinalAction, EObject selected) { return true; } /** * Swap between <i>package</i> and <i>folder</i> display mode. */ public void swapMode() { switch (mode) { case MODE_FOLDER: mode = MODE_PACKAGE; break; case MODE_PACKAGE: mode = MODE_FOLDER; break; default: break; } } }