/******************************************************************************* * Copyright (c) 2000, 2016 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.help.ui.internal.views; import java.util.Observable; import java.util.Observer; import org.eclipse.help.IHelpResource; import org.eclipse.help.IIndexEntry; import org.eclipse.help.IIndexSee; import org.eclipse.help.IToc; import org.eclipse.help.ITopic; import org.eclipse.help.base.AbstractHelpScope; import org.eclipse.help.internal.base.scope.ScopeUtils; import org.eclipse.help.internal.base.scope.WorkingSetScope; import org.eclipse.help.internal.search.federated.LocalHelpScope; import org.eclipse.help.internal.workingset.WorkingSet; import org.eclipse.help.search.ISearchScope; import org.eclipse.help.ui.internal.HelpUIResources; import org.eclipse.help.ui.internal.IHelpUIConstants; import org.eclipse.help.ui.internal.Messages; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseTrackAdapter; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.IMemento; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.forms.AbstractFormPart; import org.eclipse.ui.forms.events.HyperlinkEvent; import org.eclipse.ui.forms.widgets.FormToolkit; public abstract class HyperlinkTreePart extends AbstractFormPart implements IHelpPart { public class ScopeObserver implements Observer { @Override public void update(Observable o, Object arg) { if (o instanceof ScopeSetManager) { refilter(); } } } private class ScopeFilter extends ViewerFilter { public ScopeFilter(AbstractHelpScope scope) { this.scope = scope; } AbstractHelpScope scope; @Override public boolean select(Viewer viewer, Object parentElement, Object element) { if (element instanceof IToc) { return ScopeUtils.showInTree((IToc)element, scope); } if (element instanceof ITopic) { return ScopeUtils.showInTree((ITopic)element, scope); } if (element instanceof IIndexEntry) { return ScopeUtils.showInTree((IIndexEntry)element, scope); } if (element instanceof IIndexSee) { return ScopeUtils.showInTree((IIndexSee)element, scope); } return true; } } protected ReusableHelpPart parent; private String id; //private Composite container; protected TreeViewer treeViewer; private TreeItem lastItem; private Cursor handCursor; private ScopeObserver scopeObserver; /** * @param parent * @param toolkit * @param style */ public HyperlinkTreePart(Composite parent, final FormToolkit toolkit, IToolBarManager tbm) { handCursor = new Cursor(parent.getDisplay(), SWT.CURSOR_HAND); /* container = toolkit.createComposite(parent); GridLayout layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; layout.verticalSpacing = 0; container.setLayout(layout); Composite sep = toolkit.createCompositeSeparator(container); GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL); gd.heightHint = 1; sep.setLayoutData(gd); treeViewer = new TreeViewer(container, SWT.H_SCROLL | SWT.V_SCROLL); */ treeViewer = new TreeViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL); treeViewer.getTree().setMenu(parent.getMenu()); treeViewer.getTree().setForeground( toolkit.getHyperlinkGroup().getForeground()); configureTreeViewer(); treeViewer.setInput(this); treeViewer.getControl().setLayoutData(new GridData(GridData.FILL_BOTH)); treeViewer.addOpenListener(event -> doOpenSelection((IStructuredSelection) event.getSelection())); treeViewer.addSelectionChangedListener( event -> handleSelectionChanged((IStructuredSelection) event.getSelection())); treeViewer.getTree().addMouseListener(new MouseAdapter() { long lastTime; @Override public void mouseUp(MouseEvent e) { long eventTime = e.time & 0xFFFFFFFFL; if (eventTime - lastTime <= e.display.getDoubleClickTime()) return; if (e.button != 1) return; lastTime = eventTime; Point p = new Point(e.x, e.y); TreeItem item = treeViewer.getTree().getItem(p); if (item != null) { Object obj = item.getData(); if (obj != null) { doOpen(obj); } } } }); treeViewer.getTree().addPaintListener(e -> { validateLastItem(); if (lastItem == null) return; Rectangle bounds = lastItem.getBounds(); boolean selected = false; TreeItem[] items = lastItem.getParent().getSelection(); for (int i = 0; i < items.length; i++) { if (items[i].equals(lastItem)) { selected = true; break; } } if (selected) e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT)); else e.gc.setForeground(toolkit.getHyperlinkGroup().getActiveForeground()); FontMetrics fm = e.gc.getFontMetrics(); int height = fm.getHeight(); int lineY = bounds.y + height; e.gc.drawLine(bounds.x, lineY, bounds.x + bounds.width - 1, lineY); }); treeViewer.getTree().addMouseTrackListener(new MouseTrackAdapter() { @Override public void mouseExit(MouseEvent e) { validateLastItem(); if (lastItem != null) { TreeItem item = lastItem; lastItem = null; item.setForeground(null); } } }); treeViewer.getTree().addMouseMoveListener(e -> { Point p = new Point(e.x, e.y); TreeItem item = treeViewer.getTree().getItem(p); validateLastItem(); if (item != null) { if (lastItem != null) { if (!lastItem.equals(item)) { lastItem.setForeground(null); repaintItem(lastItem); updateStatus(null); lastItem = null; } else return; } Object obj = item.getData(); treeViewer.getTree().setCursor(handCursor); IStructuredSelection ssel = (IStructuredSelection) treeViewer.getSelection(); if (ssel.getFirstElement() == obj) item.setForeground(e.display.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT)); else item.setForeground(toolkit.getHyperlinkGroup().getActiveForeground()); lastItem = item; repaintItem(lastItem); if (obj instanceof IHelpResource) updateStatus((IHelpResource) obj); else updateStatus(null); return; } else if (lastItem != null) { lastItem.setForeground(null); repaintItem(lastItem); lastItem = null; updateStatus(null); } treeViewer.getTree().setCursor(null); }); contributeToToolBar(tbm); scopeObserver = new ScopeObserver(); ScopeState.getInstance().getScopeSetManager().addObserver(scopeObserver); } @Override public void dispose() { handCursor.dispose(); if (scopeObserver != null) { ScopeState.getInstance().getScopeSetManager().deleteObserver(scopeObserver); } super.dispose(); } private void repaintItem(TreeItem item) { Rectangle bounds = item.getBounds(); item.getParent().redraw(bounds.x, bounds.y, bounds.width, bounds.height, false); } protected void contributeToToolBar(IToolBarManager tbm) { Action collapseAllAction = new Action() { @Override public void run() { BusyIndicator.showWhile(getControl().getDisplay(), () -> doCollapseAll()); } }; collapseAllAction.setImageDescriptor(HelpUIResources .getImageDescriptor(IHelpUIConstants.IMAGE_COLLAPSE_ALL)); collapseAllAction .setToolTipText(Messages.AllTopicsPart_collapseAll_tooltip); tbm.insertBefore("back", collapseAllAction); //$NON-NLS-1$ tbm.insertBefore("back", new Separator()); //$NON-NLS-1$ } private void doCollapseAll() { Object[] expanded = treeViewer.getExpandedElements(); treeViewer.collapseAll(); treeViewer.update(expanded, null); } @Override public Control getControl() { return treeViewer.getControl(); } @Override public void init(ReusableHelpPart parent, String id, IMemento memento) { this.parent = parent; this.id = id; refilter(); } @Override public String getId() { return id; } @Override public void setVisible(boolean visible) { getControl().setVisible(visible); if (visible) treeViewer.refresh(); } private void doOpenSelection(IStructuredSelection sel) { Object obj = sel.getFirstElement(); if (obj != null) { doOpen(obj); } } protected void handleSelectionChanged(IStructuredSelection sel) { Object obj = sel.getFirstElement(); if (lastItem != null && !lastItem.isDisposed()) { Object lastObj = lastItem.getData(); if (lastObj==obj) lastItem.setForeground(getControl().getDisplay() .getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT)); else lastItem.setForeground(parent.getForm().getToolkit().getHyperlinkGroup() .getActiveForeground()); repaintItem(lastItem); } if (obj instanceof IHelpResource) { IHelpResource res = (IHelpResource) obj; updateStatus(res, false); } else updateStatus(null, false); } private void updateStatus(IHelpResource res) { updateStatus(res, true); } private void updateStatus(IHelpResource res, boolean defaultToSelection) { if (defaultToSelection && res == null) { IStructuredSelection ssel = (IStructuredSelection) treeViewer .getSelection(); Object obj = ssel.getFirstElement(); if (obj instanceof IHelpResource) res = (IHelpResource) obj; } if (res != null) { String label = res.getLabel(); String href = getHref(res); HyperlinkTreePart.this.parent.handleLinkEntered(new HyperlinkEvent( treeViewer.getTree(), href, label, SWT.NULL)); } else { HyperlinkTreePart.this.parent.handleLinkExited(null); } } protected String getHref(IHelpResource res) { return res.getHref(); } protected abstract void configureTreeViewer(); protected abstract void doOpen(Object obj); protected void postUpdate(final Object obj) { treeViewer.getControl().getDisplay().asyncExec(() -> treeViewer.update(obj, null)); } @Override public boolean fillContextMenu(IMenuManager manager) { return parent.fillSelectionProviderMenu(treeViewer, manager, canAddBookmarks()); } protected abstract boolean canAddBookmarks(); @Override public boolean hasFocusControl(Control focusControl) { return treeViewer.getControl().equals(focusControl); } @Override public void setFocus() { if (treeViewer != null) treeViewer.getTree().setFocus(); } @Override public IAction getGlobalAction(String id) { if (id.equals(ActionFactory.COPY.getId())) return parent.getCopyAction(); return null; } @Override public void stop() { } private void validateLastItem() { if (lastItem != null && lastItem.isDisposed()) lastItem = null; } @Override public void refilter() { ScopeSetManager manager = ScopeState.getInstance().getScopeSetManager(); ScopeSet set = manager.getActiveSet(); EngineDescriptor[] engineDescriptors = parent.getEngineManager().getDescriptors(); ISearchScope scope = null; for (int i = 0; i < engineDescriptors.length; i++) { final EngineDescriptor ed = engineDescriptors[i]; if (ed.getEngineTypeId().equals("org.eclipse.help.ui.localSearch") //$NON-NLS-1$ && ed.getEngine() != null) { scope = ed.createSearchScope(set.getPreferenceStore()); } } WorkingSet workingSet = null; LocalHelpScope localScope = (LocalHelpScope) scope; workingSet = localScope.getWorkingSet() ; treeViewer.resetFilters(); if (workingSet != null) { WorkingSetScope helpScope = new WorkingSetScope(workingSet, set.getName()); treeViewer.addFilter(new ScopeFilter(helpScope)); } treeViewer.refresh(); } }