/* * Copyright (c) 2015 Eike Stepper (Berlin, Germany) 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: * Eike Stepper - initial API and implementation */ package org.eclipse.emf.cdo.explorer.ui; import org.eclipse.emf.cdo.explorer.CDOExplorerUtil; import org.eclipse.emf.cdo.explorer.checkouts.CDOCheckout; import org.eclipse.emf.cdo.explorer.ui.bundle.OM; import org.eclipse.emf.cdo.explorer.ui.checkouts.CDOCheckoutContentProvider; import org.eclipse.emf.cdo.explorer.ui.checkouts.CDOCheckoutLabelProvider; import org.eclipse.net4j.util.ui.views.ItemProvider; import org.eclipse.emf.ecore.EObject; 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.jface.viewers.CheckboxTreeViewer; import org.eclipse.jface.viewers.DecoratingStyledCellLabelProvider; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Control; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; /** * @author Eike Stepper */ public class ObjectListController { private final CDOCheckoutContentProvider contentProvider = new CDOCheckoutContentProvider(); private final LabelProvider labelProvider = new LabelProvider(contentProvider); private final AtomicBoolean refreshing = new AtomicBoolean(); private final Set<EObject> objects = new HashSet<EObject>(); private final List<Wrapper> wrappers = new ArrayList<Wrapper>(); private final CDOCheckout checkout; public ObjectListController(CDOCheckout checkout) { this.checkout = checkout; } public final CDOCheckout getCheckout() { return checkout; } public final EObject getObject(Object wrapper) { if (wrapper instanceof Wrapper) { return ((Wrapper)wrapper).getObject(); } return null; } public final void addObject(EObject object, boolean delete) { if (objects.add(object)) { String name = labelProvider.getSuperText(object); wrappers.add(new Wrapper(object, delete, name)); } } public void configure(final TreeViewer treeViewer) { Control control = treeViewer.getControl(); contentProvider.disposeWith(control); // The contentProvider field just makes the labelProvider happy. treeViewer.setContentProvider(new ContentProvider()); treeViewer.setLabelProvider(new DecoratingStyledCellLabelProvider(labelProvider, new LabelDecorator(), null)); treeViewer.setInput(wrappers); control.getDisplay().asyncExec(new Runnable() { public void run() { configureAsync(treeViewer, labelProvider); } }); } @SuppressWarnings("deprecation") protected void configureAsync(final TreeViewer treeViewer, final LabelProvider labelProvider) { if (treeViewer instanceof CheckboxTreeViewer) { CheckboxTreeViewer checkboxTreeViewer = (CheckboxTreeViewer)treeViewer; checkboxTreeViewer.setAllChecked(true); } new Job("Compute paths") { @Override protected IStatus run(IProgressMonitor monitor) { updateWrappers(treeViewer, labelProvider); return Status.OK_STATUS; } }.schedule(); } protected void updateWrappers(final TreeViewer treeViewer, final LabelProvider labelProvider) { for (Wrapper wrapper : wrappers) { try { boolean hasPath; synchronized (wrappers) { hasPath = wrapper.computePath(checkout, labelProvider); } if (hasPath) { refresh(treeViewer); } } catch (Exception ex) { //$FALL-THROUGH$ } } } private void refresh(final TreeViewer treeViewer) { if (!refreshing.getAndSet(true)) { Control control = treeViewer.getControl(); if (!control.isDisposed()) { control.getDisplay().asyncExec(new Runnable() { public void run() { refreshing.set(false); treeViewer.refresh(wrappers); } }); } } } /** * @author Eike Stepper */ private static final class Wrapper implements Comparable<Wrapper> { private final EObject object; private final boolean delete; private final String name; private String path; public Wrapper(EObject object, boolean delete, String name) { this.object = object; this.delete = delete; this.name = name; } public final EObject getObject() { return object; } public final boolean isDelete() { return delete; } public final String getPath() { return path; } public final String getName() { return name; } public boolean computePath(CDOCheckout checkout, LabelProvider labelProvider) { LinkedList<Object> nodes = CDOExplorerUtil.getPath(object); if (nodes != null) { nodes.removeLast(); StringBuilder builder = new StringBuilder(); boolean first = true; for (Object node : nodes) { builder.append('/'); if (first) { builder.append(checkout.getLabel()); first = false; } else { builder.append(labelProvider.getText(node)); } } path = builder.toString(); return true; } return false; } public int compareTo(Wrapper o) { return toString().compareTo(o.toString()); } @Override public String toString() { if (path != null) { return path + name; } return name; } } /** * @author Eike Stepper */ private final class ContentProvider implements ITreeContentProvider { public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } public void dispose() { } public Object[] getElements(Object element) { if (element == wrappers) { Object[] result = wrappers.toArray(); synchronized (wrappers) { Arrays.sort(result); } return result; } return ItemProvider.NO_ELEMENTS; } public Object getParent(Object element) { return null; } public Object[] getChildren(Object element) { return getElements(element); } public boolean hasChildren(Object element) { return element == wrappers && !wrappers.isEmpty(); } } /** * @author Eike Stepper */ private static final class LabelProvider extends CDOCheckoutLabelProvider { public LabelProvider(CDOCheckoutContentProvider contentProvider) { super(contentProvider); } @Override public Image getImage(Object element) { if (element instanceof Wrapper) { Wrapper wrapper = (Wrapper)element; return super.getImage(wrapper.getObject()); } return super.getImage(element); } @Override public String getText(Object element) { if (element instanceof Wrapper) { Wrapper wrapper = (Wrapper)element; String name = wrapper.getName(); if (name.length() == 0) { return "/"; } return name; } return getSuperText(element); } public String getSuperText(Object element) { return super.getText(element); } } /** * @author Eike Stepper */ private static final class LabelDecorator extends BaseLabelDecorator { private static final Image DELETE_OVERLAY_IMAGE = OM.getImage("icons/delete_ovr.gif"); @Override public Image decorateImage(Image image, Object element) { if (element instanceof Wrapper) { Wrapper wrapper = (Wrapper)element; if (wrapper.isDelete()) { return OM.getOverlayImage(image, DELETE_OVERLAY_IMAGE, 9, 0); } } return super.decorateImage(image, element); } @Override public String decorateText(String text, Object element) { if (element instanceof Wrapper && !text.equals("/")) { Wrapper wrapper = (Wrapper)element; String path = wrapper.getPath(); if (path != null) { text = path + "/" + text; } return text; } return super.decorateText(text, element); } } }