Java tutorial
/******************************************************************************* * Copyright (c) 2004, 2010 Tasktop Technologies 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: * Tasktop Technologies - initial API and implementation *******************************************************************************/ package org.eclipse.mylyn.internal.context.ui; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.viewers.AbstractTreeViewer; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.mylyn.commons.core.StatusHandler; import org.eclipse.mylyn.commons.workbench.DelayedRefreshJob; import org.eclipse.mylyn.context.core.AbstractContextListener; import org.eclipse.mylyn.context.core.AbstractContextStructureBridge; import org.eclipse.mylyn.context.core.ContextChangeEvent; import org.eclipse.mylyn.context.core.ContextCore; import org.eclipse.mylyn.context.core.IInteractionElement; import org.eclipse.mylyn.context.ui.AbstractFocusViewAction; import org.eclipse.mylyn.context.ui.InterestFilter; import org.eclipse.mylyn.internal.context.core.ContextCorePlugin; import org.eclipse.ui.ISelectionListener; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PlatformUI; /** * Encapsulates the element refresh and expansion state policy for all viewers focused on context. * * @author Mik Kersten * @author Shawn Minto */ public class FocusedViewerManager extends AbstractContextListener implements ISelectionListener { private final CopyOnWriteArrayList<StructuredViewer> managedViewers = new CopyOnWriteArrayList<StructuredViewer>(); private final CopyOnWriteArrayList<StructuredViewer> filteredViewers = new CopyOnWriteArrayList<StructuredViewer>(); private final Map<StructuredViewer, AbstractFocusViewAction> focusActions = new HashMap<StructuredViewer, AbstractFocusViewAction>(); private final Map<StructuredViewer, BrowseFilteredListener> listenerMap = new HashMap<StructuredViewer, BrowseFilteredListener>(); private final Map<IWorkbenchPart, StructuredViewer> partToViewerMap = new HashMap<IWorkbenchPart, StructuredViewer>(); private final Map<StructuredViewer, FocusedViewerDelayedRefreshJob> fullRefreshJobs = new HashMap<StructuredViewer, FocusedViewerDelayedRefreshJob>(); // TODO: consider merging in order to discard minors when majors come in, see bug 209846 private final Map<StructuredViewer, FocusedViewerDelayedRefreshJob> minorRefreshJobs = new HashMap<StructuredViewer, FocusedViewerDelayedRefreshJob>(); private class FocusedViewerDelayedRefreshJob extends DelayedRefreshJob { private boolean minor = false; private boolean updateExpansion; public FocusedViewerDelayedRefreshJob(StructuredViewer viewer, String name, boolean minor) { super(viewer, name); this.minor = minor; } @Override protected void doRefresh(Object[] items) { try { if (viewer == null) { return; } else if (viewer.getControl().isDisposed()) { managedViewers.remove(viewer); } else { if (items == null || items.length == 0) { if (!minor) { viewer.refresh(false); if (updateExpansion) { FocusedViewerManager.this.updateExpansionState(viewer, null); } } else { try { viewer.getControl().setRedraw(false); viewer.refresh(true); if (updateExpansion) { FocusedViewerManager.this.updateExpansionState(viewer, null); } } finally { viewer.getControl().setRedraw(true); } } } else { if (filteredViewers.contains(viewer)) { try { viewer.getControl().setRedraw(false); viewer.refresh(minor); if (updateExpansion) { FocusedViewerManager.this.updateExpansionState(viewer, null); } } finally { viewer.getControl().setRedraw(true); } } else { // don't need to worry about content changes try { viewer.getControl().setRedraw(false); for (Object item : items) { Object objectToRefresh = item; if (item instanceof IInteractionElement) { IInteractionElement node = (IInteractionElement) item; AbstractContextStructureBridge structureBridge = ContextCorePlugin .getDefault().getStructureBridge(node.getContentType()); objectToRefresh = structureBridge .getObjectForHandle(node.getHandleIdentifier()); } if (objectToRefresh != null) { viewer.update(objectToRefresh, null); if (updateExpansion) { FocusedViewerManager.this.updateExpansionState(viewer, objectToRefresh); } } } } finally { viewer.getControl().setRedraw(true); } } } } } finally { updateExpansion = false; } } public void refreshElements(Object[] elements, boolean updateExpansion) { this.updateExpansion |= updateExpansion; super.refreshElements(elements); } @Override public void refreshElements(Object[] elements) { refreshElements(elements, true); } } /** * For testing. */ private boolean syncRefreshMode = false; // private boolean internalExpandExceptionLogged; public FocusedViewerManager() { // NOTE: no longer using viewer part tracker due to bug 162346 // VIEWER_PART_TRACKER.install(PlatformUI.getWorkbench()); } public void dispose() { // VIEWER_PART_TRACKER.dispose(PlatformUI.getWorkbench()); } public void selectionChanged(IWorkbenchPart part, ISelection selection) { // ignore } public void addManagedViewer(StructuredViewer viewer, IWorkbenchPart viewPart) { if (viewer != null && !managedViewers.contains(viewer)) { managedViewers.add(viewer); partToViewerMap.put(viewPart, viewer); BrowseFilteredListener listener = new BrowseFilteredListener(viewer); listenerMap.put(viewer, listener); viewer.getControl().addMouseListener(listener); viewer.getControl().addKeyListener(listener); try { // NOTE: this needs to be done because some views (e.g. Project Explorer) are not // correctly initialized on startup and do not have the dummy selection event // sent to them. See PartPluginAction and bug 213545. // TODO consider a mechanism to identify only views that provide focus FocusedViewerManager.initializeViewerSelection(viewPart); Set<IInteractionElement> emptySet = Collections.emptySet(); refreshViewer(emptySet, true, viewer, true); } catch (Exception e) { StatusHandler.log(new Status(IStatus.ERROR, ContextUiPlugin.ID_PLUGIN, "Could not initialize focused viewer", e)); //$NON-NLS-1$ } } } private final Map<TreeViewer, FilteredChildrenDecorationDrawer> decorationMap = new HashMap<TreeViewer, FilteredChildrenDecorationDrawer>(); private void removeFilterDecorations(StructuredViewer viewer) { if (viewer instanceof TreeViewer) { TreeViewer treeViewer = (TreeViewer) viewer; FilteredChildrenDecorationDrawer filterViewDrawer = decorationMap.remove(treeViewer); if (filterViewDrawer != null) { filterViewDrawer.dispose(); } } } private void addFilterDecorations(StructuredViewer viewer, BrowseFilteredListener listener) { if (viewer instanceof TreeViewer) { TreeViewer treeViewer = (TreeViewer) viewer; FilteredChildrenDecorationDrawer filteredViewDrawer = new FilteredChildrenDecorationDrawer(treeViewer, listener); if (filteredViewDrawer.applyToTreeViewer()) { decorationMap.put(treeViewer, filteredViewDrawer); } } } public void removeManagedViewer(StructuredViewer viewer, IWorkbenchPart viewPart) { managedViewers.remove(viewer); partToViewerMap.remove(viewPart); removeFilterDecorations(viewer); BrowseFilteredListener listener = listenerMap.get(viewer); if (listener != null && viewer != null && !viewer.getControl().isDisposed()) { viewer.getControl().removeMouseListener(listener); viewer.getControl().removeKeyListener(listener); } } public void addFilteredViewer(StructuredViewer viewer, AbstractFocusViewAction action) { addFilteredViewer(viewer); if (viewer != null && action != null) { focusActions.put(viewer, action); } BrowseFilteredListener listener = listenerMap.get(viewer); if (listener != null) { addFilterDecorations(viewer, listener); } } @Deprecated public void addFilteredViewer(StructuredViewer viewer) { if (viewer != null && !filteredViewers.contains(viewer)) { filteredViewers.add(viewer); } } public void removeFilteredViewer(StructuredViewer viewer) { removeFilterDecorations(viewer); focusActions.remove(viewer); filteredViewers.remove(viewer); } @Override public void contextChanged(ContextChangeEvent event) { switch (event.getEventKind()) { case ACTIVATED: refreshViewers(); break; case DEACTIVATED: refreshViewers(); for (StructuredViewer structuredViewer : managedViewers) { if (structuredViewer instanceof TreeViewer) { ((TreeViewer) structuredViewer).collapseAll(); } } break; case CLEARED: if (event.isActiveContext()) { // ensure we dont refresh the viewers if a context other than the active one is deleted or cleared // bug #265688 refreshViewers(); for (StructuredViewer structuredViewer : managedViewers) { if (structuredViewer instanceof TreeViewer) { ((TreeViewer) structuredViewer).collapseAll(); } } } break; case INTEREST_CHANGED: if (event.isActiveContext()) { refreshViewers(event.getElements(), false, true); } break; case LANDMARKS_ADDED: if (event.isActiveContext()) { refreshViewers(event.getElements(), true, false); } break; case LANDMARKS_REMOVED: if (event.isActiveContext()) { refreshViewers(event.getElements(), true, false); } break; case ELEMENTS_DELETED: if (event.isActiveContext()) { /* * TODO: consider making this work per-element and parent * Should we collect all parents before calling refresh? */ ArrayList<IInteractionElement> toRefresh = new ArrayList<IInteractionElement>(); for (IInteractionElement interactionElement : event.getElements()) { AbstractContextStructureBridge structureBridge = ContextCore .getStructureBridge(interactionElement.getContentType()); IInteractionElement parent = ContextCore.getContextManager() .getElement(structureBridge.getParentHandle(interactionElement.getHandleIdentifier())); if (parent != null) { toRefresh.add(parent); } } refreshViewers(toRefresh, false, false); } break; } } protected void refreshViewers() { List<IInteractionElement> toRefresh = Collections.emptyList(); refreshViewers(toRefresh, true, true); } @Deprecated protected void refreshViewers(IInteractionElement node, boolean updateLabels) { refreshViewers(node, updateLabels, true); } protected void refreshViewers(IInteractionElement node, boolean updateLabels, boolean updateExpansion) { List<IInteractionElement> toRefresh = new ArrayList<IInteractionElement>(); toRefresh.add(node); refreshViewers(toRefresh, updateLabels, updateExpansion); } @Deprecated protected void refreshViewers(final List<IInteractionElement> nodesToRefresh, final boolean updateLabels) { refreshViewers(nodesToRefresh, updateLabels, true); } protected void refreshViewers(final List<IInteractionElement> nodesToRefresh, final boolean updateLabels, final boolean updateExpansion) { // TODO replace by Assert.isNotNull(nodesToRefresh); if (nodesToRefresh == null) { return; } if (syncRefreshMode) { internalRefresh(new HashSet<IInteractionElement>(nodesToRefresh), updateLabels, updateExpansion); } else { PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { public void run() { internalRefresh(new HashSet<IInteractionElement>(nodesToRefresh), updateLabels, updateExpansion); } }); } } private void internalRefresh(final Set<IInteractionElement> nodesToRefresh, final boolean updateLabels, final boolean updateExpansion) { try { for (StructuredViewer viewer : managedViewers) { refreshViewer(nodesToRefresh, updateLabels, viewer, updateExpansion); } } catch (Throwable t) { StatusHandler.log(new Status(IStatus.ERROR, ContextUiPlugin.ID_PLUGIN, "Could not refresh viewer", t)); //$NON-NLS-1$ } } @Deprecated public void refreshViewer(final Set<IInteractionElement> nodesToRefresh, final boolean updateLabels, StructuredViewer viewer) { refreshViewer(nodesToRefresh, updateLabels, viewer, true); } public void refreshViewer(final Set<IInteractionElement> nodesToRefresh, final boolean updateLabels, StructuredViewer viewer, boolean updateExpansion) { Map<StructuredViewer, FocusedViewerDelayedRefreshJob> refreshJobs = null; if (updateLabels) { refreshJobs = minorRefreshJobs; } else { refreshJobs = fullRefreshJobs; } FocusedViewerDelayedRefreshJob job = refreshJobs.get(viewer); if (job == null) { job = new FocusedViewerDelayedRefreshJob(viewer, "refresh viewer", updateLabels); //$NON-NLS-1$ refreshJobs.put(viewer, job); } job.refreshElements(nodesToRefresh.toArray(), updateExpansion); } private void updateExpansionState(StructuredViewer viewer, Object objectToRefresh) { if (viewer instanceof TreeViewer && filteredViewers.contains(viewer) && hasInterestFilter(viewer, true) && ContextUiPlugin.getDefault().getPreferenceStore() .getBoolean(IContextUiPreferenceContstants.AUTO_MANAGE_EXPANSION)) { TreeViewer treeViewer = (TreeViewer) viewer; // HACK to fix bug 278569: [context] errors with Markers view and active Mylyn task if ("org.eclipse.ui.internal.views.markers.MarkersTreeViewer".equals(treeViewer.getClass() //$NON-NLS-1$ .getCanonicalName())) { objectToRefresh = null; } if (objectToRefresh == null) { treeViewer.expandAll(); } else { treeViewer.expandToLevel(objectToRefresh, AbstractTreeViewer.ALL_LEVELS); } } } private boolean hasInterestFilter(StructuredViewer viewer, boolean tryToReinstall) { for (ViewerFilter filter : viewer.getFilters()) { if (filter instanceof InterestFilter) { return true; } } if (tryToReinstall) { AbstractFocusViewAction action = focusActions.get(viewer); if (action != null) { action.run(action); viewer.refresh(); return hasInterestFilter(viewer, false); } } return false; } /** * Set to true for testing */ public void setSyncRefreshMode(boolean syncRefreshMode) { this.syncRefreshMode = syncRefreshMode; } public void forceRefresh() { refreshViewers(); } public static void initializeViewerSelection(IWorkbenchPart part) { ISelectionProvider selectionProvider = part.getSite().getSelectionProvider(); if (selectionProvider != null) { ISelection selection = selectionProvider.getSelection(); try { if (selection != null) { selectionProvider.setSelection(selection); } else { selectionProvider.setSelection(StructuredSelection.EMPTY); } } catch (UnsupportedOperationException e) { // ignore if the selection does not support setting a selection, see bug 217634 } } } }