package org.archstudio.bna.logics.editing; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.concurrent.CopyOnWriteArrayList; import org.archstudio.bna.BNAModelEvent; import org.archstudio.bna.IBNAModelListener; import org.archstudio.bna.IBNAWorld; import org.archstudio.bna.logics.AbstractThingLogic; import org.archstudio.bna.utils.BNAUtils; import org.archstudio.swtutils.SWTWidgetUtils; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.ui.IWorkbenchSite; import com.google.common.collect.Lists; public abstract class EclipseSelectionProviderLogic extends AbstractThingLogic implements IBNAModelListener { private static class WorkbenchSiteSelectionProvider implements ISelectionProvider { private final IWorkbenchSite workbenchSite; public WorkbenchSiteSelectionProvider(IWorkbenchSite workbenchSite) { this.workbenchSite = workbenchSite; } private final CopyOnWriteArrayList<EclipseSelectionProviderLogic> allEclipseSelectionProviderLogics = Lists .newCopyOnWriteArrayList(); public void addEclipseSelectionProvider(EclipseSelectionProviderLogic eclipseSelectionProviderLogic) { allEclipseSelectionProviderLogics.add(eclipseSelectionProviderLogic); } public void removeEclipseSelectionProvider(EclipseSelectionProviderLogic eclipseSelectionProviderLogic) { allEclipseSelectionProviderLogics.remove(eclipseSelectionProviderLogic); } private final CopyOnWriteArrayList<ISelectionChangedListener> selectionChangedListeners = Lists .newCopyOnWriteArrayList(); @Override public void addSelectionChangedListener(ISelectionChangedListener listener) { selectionChangedListeners.add(listener); } @Override public void removeSelectionChangedListener(ISelectionChangedListener listener) { selectionChangedListeners.remove(listener); } public void fireSelectionChangedEvent() { final SelectionChangedEvent evt = new SelectionChangedEvent(this, getSelection()); SWTWidgetUtils.async(workbenchSite.getShell(), new Runnable() { @Override public void run() { for (ISelectionChangedListener l : selectionChangedListeners) { l.selectionChanged(evt); } } }); } private final Collection<Object> selection = new ArrayList<Object>(); @Override public ISelection getSelection() { return new StructuredSelection(selection.toArray()); } @Override public void setSelection(ISelection selection) { // TODO: not sure how to support this } public void setSelection(EclipseSelectionProviderLogic eclipseSelectionProviderLogic, Object[] selectedObjects) { for (EclipseSelectionProviderLogic l : allEclipseSelectionProviderLogics) { if (l != eclipseSelectionProviderLogic) { l._unselectAll(); } } if (!Arrays.equals(selectedObjects, selection.toArray(new Object[selection.size()]))) { selection.clear(); selection.addAll(Arrays.asList(selectedObjects)); fireSelectionChangedEvent(); } } } private final WorkbenchSiteSelectionProvider workbenchSiteSelectionProvider; public EclipseSelectionProviderLogic(IBNAWorld world, IWorkbenchSite workbenchSite) { super(world); if (workbenchSite.getSelectionProvider() == null) { workbenchSite.setSelectionProvider(workbenchSiteSelectionProvider = new WorkbenchSiteSelectionProvider( workbenchSite)); } else if (workbenchSite.getSelectionProvider() instanceof WorkbenchSiteSelectionProvider) { workbenchSiteSelectionProvider = (WorkbenchSiteSelectionProvider) workbenchSite.getSelectionProvider(); } else { throw new RuntimeException( "EclipseSelectionProviderLogic cannot register itself as the selection provider."); } workbenchSiteSelectionProvider.addEclipseSelectionProvider(this); } @Override public void dispose() { BNAUtils.checkLock(); workbenchSiteSelectionProvider.removeEclipseSelectionProvider(this); super.dispose(); } private static final String BEGIN_IGNORING_SELECTION_EVENTS_NOTIFICATION = EclipseSelectionProviderLogic.class .getName() + ":BeginIgnoringSelectionEvents"; private static final String END_IGNORING_SELECTION_EVENTS_NOTIFICATION = EclipseSelectionProviderLogic.class .getName() + ":EndIgnoringSelectionEvents"; private int inBulkChange = 0; private int ignoreSelection = 0; @Override public void bnaModelChanged(BNAModelEvent evt) { BNAUtils.checkLock(); switch (evt.getEventType()) { case BULK_CHANGE_BEGIN: inBulkChange++; break; case BULK_CHANGE_END: if (--inBulkChange <= 0) { inBulkChange = 0; } break; case STREAM_NOTIFICATION_EVENT: if (BEGIN_IGNORING_SELECTION_EVENTS_NOTIFICATION.equals(evt.getStreamNotification())) { ignoreSelection++; } else if (END_IGNORING_SELECTION_EVENTS_NOTIFICATION.equals(evt.getStreamNotification())) { if (--ignoreSelection <= 0) { ignoreSelection = 0; } } default: // do nothing } } private void _unselectAll() { model.fireStreamNotificationEvent(BEGIN_IGNORING_SELECTION_EVENTS_NOTIFICATION); unselectAll(); model.fireStreamNotificationEvent(END_IGNORING_SELECTION_EVENTS_NOTIFICATION); } abstract protected void unselectAll(); protected void setSelection(Object[] selectedObjects) { if (ignoreSelection == 0) { workbenchSiteSelectionProvider.setSelection(this, selectedObjects); } } }