### Eclipse Workspace Patch 1.0 #P org.eclipse.mylyn.context.ui Index: src/org/eclipse/mylyn/internal/context/ui/BrowseFilteredListener.java =================================================================== RCS file: /cvsroot/mylyn/org.eclipse.mylyn.contexts/org.eclipse.mylyn.context.ui/src/org/eclipse/mylyn/internal/context/ui/BrowseFilteredListener.java,v retrieving revision 1.22 diff -u -r1.22 BrowseFilteredListener.java --- src/org/eclipse/mylyn/internal/context/ui/BrowseFilteredListener.java 14 Feb 2011 00:29:04 -0000 1.22 +++ src/org/eclipse/mylyn/internal/context/ui/BrowseFilteredListener.java 14 Feb 2011 19:21:40 -0000 @@ -42,6 +42,8 @@ private final StructuredViewer viewer; + private boolean wasExternalClick = false; + public BrowseFilteredListener(StructuredViewer viewer) { this.viewer = viewer; } @@ -78,6 +80,10 @@ } } + public void setWasExternalClick(boolean wasExternalClick) { + this.wasExternalClick = wasExternalClick; + } + public void keyPressed(KeyEvent event) { // ignore } @@ -130,8 +136,9 @@ unfilter(filter, treeViewer, selectedObject); } else { if (event.button == 1) { - if ((event.stateMask & SWT.MOD1) != 0) { + if ((event.stateMask & SWT.MOD1) != 0 || wasExternalClick) { viewer.refresh(selectedObject); + wasExternalClick = false; } else { final Object unfiltered = filter.getLastTemporarilyUnfiltered(); if (unfiltered != null) { Index: src/org/eclipse/mylyn/internal/context/ui/FocusedViewerManager.java =================================================================== RCS file: /cvsroot/mylyn/org.eclipse.mylyn.contexts/org.eclipse.mylyn.context.ui/src/org/eclipse/mylyn/internal/context/ui/FocusedViewerManager.java,v retrieving revision 1.49 diff -u -r1.49 FocusedViewerManager.java --- src/org/eclipse/mylyn/internal/context/ui/FocusedViewerManager.java 8 Jun 2010 23:04:49 -0000 1.49 +++ src/org/eclipse/mylyn/internal/context/ui/FocusedViewerManager.java 14 Feb 2011 19:21:40 -0000 @@ -199,9 +199,34 @@ } } + private final Map decorationMap = new HashMap(); + + 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); + decorationMap.put(treeViewer, filteredViewDrawer); + filteredViewDrawer.applyToTreeViewer(); + + } + } + 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); @@ -214,6 +239,10 @@ if (viewer != null && action != null) { focusActions.put(viewer, action); } + BrowseFilteredListener listener = listenerMap.get(viewer); + if (listener != null) { + addFilterDecorations(viewer, listener); + } } @Deprecated @@ -224,6 +253,7 @@ } public void removeFilteredViewer(StructuredViewer viewer) { + removeFilterDecorations(viewer); focusActions.remove(viewer); filteredViewers.remove(viewer); } @@ -368,9 +398,8 @@ if (viewer instanceof TreeViewer && filteredViewers.contains(viewer) && hasInterestFilter(viewer, true) - && ContextUiPlugin.getDefault() - .getPreferenceStore() - .getBoolean(IContextUiPreferenceContstants.AUTO_MANAGE_EXPANSION)) { + && 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 Index: src/org/eclipse/mylyn/internal/context/ui/FilteredChildrenDecorationDrawer.java =================================================================== RCS file: src/org/eclipse/mylyn/internal/context/ui/FilteredChildrenDecorationDrawer.java diff -N src/org/eclipse/mylyn/internal/context/ui/FilteredChildrenDecorationDrawer.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/mylyn/internal/context/ui/FilteredChildrenDecorationDrawer.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,238 @@ +package org.eclipse.mylyn.internal.context.ui; + +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.mylyn.internal.provisional.commons.ui.CommonImages; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.MouseMoveListener; +import org.eclipse.swt.events.MouseTrackListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; + +/** + * @author Mik Kersten + */ + +public class FilteredChildrenDecorationDrawer implements Listener { + + private final class MoveListener implements MouseMoveListener, MouseListener, MouseTrackListener { + + private TreeItem lastItem; + + private final TreeViewer viewer; + + private final BrowseFilteredListener browseFilteredListener; + + public MoveListener(TreeViewer viewer, BrowseFilteredListener browseFilteredListener) { + this.viewer = viewer; + this.browseFilteredListener = browseFilteredListener; + } + + public void mouseEnter(MouseEvent e) { + mouseMove(e); + } + + public void mouseExit(MouseEvent e) { + if (lastItem != null && !lastItem.isDisposed()) { + lastItem.setData(ID_HOVER, NodeState.LESS); + } + lastItem = null; + // XXX MAKE SURE THAT THE TREE IS NOT DISPOSED! + redrawTree(); + } + + private void redrawTree() { + if (viewer.getTree() != null && !viewer.getTree().isDisposed()) { + viewer.getTree().redraw(); + } + } + + public void mouseHover(MouseEvent e) { + // ignore + + } + + public void mouseMove(MouseEvent e) { + if (!(e.widget instanceof Tree) || e.widget.isDisposed()) { + return; + } + Tree tree = (Tree) e.widget; + TreeItem item = findItem(tree, e.y); + if (item != null && !item.isDisposed()) { + if (lastItem != null && !lastItem.isDisposed() && !lastItem.equals(item)) { + lastItem.setData(ID_HOVER, NodeState.LESS); + } + + Object data = item.getData(ID_HAS_CHILDREN); + if (data == null || data == Boolean.TRUE) { + item.setData(ID_HOVER, NodeState.MORE); + } else { + item.setData(ID_HOVER, NodeState.LESS); + } + if (lastItem == null || (!lastItem.isDisposed() && !lastItem.equals(item))) { + redrawTree(); + } + lastItem = item; + } else { + if (lastItem != null && !lastItem.isDisposed() && !lastItem.equals(item)) { + lastItem.setData(ID_HOVER, NodeState.LESS); + redrawTree(); + } + lastItem = item; + } + } + + public void mouseDoubleClick(MouseEvent e) { + // ignore + + } + + public void mouseDown(MouseEvent e) { + if (!(e.widget instanceof Tree) || e.widget.isDisposed()) { + // we only handle tree's + return; + } + + Tree tree = (Tree) e.widget; + TreeItem item = findItem(tree, e.y); + + if (item == null || item.isDisposed()) { + // we can't do anything if we cant find the tree items + return; + } + + boolean shouldUnfilter = false; + int selectedX = e.x; + int endOfWidget = item.getBounds().x + +item.getBounds().width; + if (endOfWidget < selectedX + && item.getBounds().x + item.getBounds().width + IMAGE_PADDING + moreImage.getBounds().width > selectedX) { + // in the bounds of the image + shouldUnfilter = true; + } else if (endOfWidget > tree.getSize().x + && selectedX > tree.getSize().x - SCROLL_BAR_OFFSET - moreImage.getBounds().width) { + // edge of the view + shouldUnfilter = true; + } + + // XXX NEED TO MAKE SURE THAT WE DONT AFFECT ALT+CLICK + if (shouldUnfilter) { + browseFilteredListener.setWasExternalClick(true); + browseFilteredListener.unfilterSelection(viewer, new StructuredSelection(item.getData())); + } + if (item.getItemCount() == 0) { + item.setData(ID_HOVER, NodeState.MORE_ERROR); + item.setData(ID_HAS_CHILDREN, Boolean.FALSE); + redrawTree(); + } + } + + public void mouseUp(MouseEvent e) { + // ignore + + } + } + + private static final int SCROLL_BAR_OFFSET = 15; + + private static final int IMAGE_PADDING = 5; + + // XXX NEED TO CHANGE THE IMAGE! + private final Image moreImage = CommonImages.getImage(CommonImages.EXPAND_ALL); + + // XXX NEED TO CHANGE THE IMAGE! + private final Image moreErrorImage = CommonImages.getImage(CommonImages.DELETE); + + enum NodeState { + MORE, LESS, MORE_ERROR + }; + + private static final String ID_HOVER = "mylyn-context-hover"; //$NON-NLS-1$ + + private static final String ID_HAS_CHILDREN = "mylyn-context-has-children"; //$NON-NLS-1$ + + private final TreeViewer treeViewer; + + private MoveListener listener; + + private final BrowseFilteredListener browseFilteredListener; + + public FilteredChildrenDecorationDrawer(TreeViewer treeViewer, BrowseFilteredListener browseFilteredListener) { + this.treeViewer = treeViewer; + this.browseFilteredListener = browseFilteredListener; + } + + public void applyToTreeViewer() { + if (treeViewer.getTree() != null && !treeViewer.getTree().isDisposed()) { + treeViewer.getTree().addListener(SWT.PaintItem, this); + + listener = new MoveListener(treeViewer, browseFilteredListener); + treeViewer.getTree().addMouseMoveListener(listener); + treeViewer.getTree().addMouseListener(listener); + treeViewer.getTree().addMouseTrackListener(listener); + } + } + + public void dispose() { + if (treeViewer.getTree() == null || treeViewer.getTree().isDisposed()) { + return; + } + treeViewer.getTree().removeListener(SWT.PaintItem, this); + + treeViewer.getTree().removeMouseMoveListener(listener); + treeViewer.getTree().removeMouseListener(listener); + treeViewer.getTree().removeMouseTrackListener(listener); + } + + /* + * NOTE: MeasureItem, PaintItem and EraseItem are called repeatedly. + * Therefore, it is critical for performance that these methods be as + * efficient as possible. + */ + public void handleEvent(Event event) { + + if (!(event.widget instanceof Tree)) { + // we only handle tree's + return; + } + + switch (event.type) { + case SWT.PaintItem: { + Tree tree = (Tree) event.widget; + if (tree.isDisposed()) { + return; + } + TreeItem item = findItem(tree, event.y); + if (item == null || item.isDisposed()) { + return; + } + + int imageStartX = event.x + event.width + IMAGE_PADDING; + + NodeState value = (NodeState) item.getData(ID_HOVER); + if (value != null && value.equals(NodeState.MORE)) { + event.gc.drawImage(moreImage, imageStartX, event.y); + } else if (value != null && value.equals(NodeState.MORE_ERROR)) { + event.gc.drawImage(moreErrorImage, imageStartX, event.y); + } + break; + } + } + } + + private TreeItem findItem(Tree tree, int y) { + TreeItem item = null; + Point size = tree.getSize(); + final int RATE = 17; + for (int i = 0; i <= RATE && item == null; i++) { + int position = size.x / RATE + (i * size.x / RATE); + item = tree.getItem(new Point(position, y)); + } + return item; + } +}