blob: 7f8e7e20c8c176d7791d0102aff1af3869d8288e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2018 IBM Corporation and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
*******************************************************************************/
package org.eclipse.dltk.internal.ui.search;
import java.util.HashMap;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.dltk.core.IMember;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.internal.corext.util.Messages;
import org.eclipse.dltk.internal.ui.dnd.DLTKViewerDragAdapter;
import org.eclipse.dltk.internal.ui.dnd.EditorInputTransferDragAdapter;
import org.eclipse.dltk.internal.ui.dnd.ResourceTransferDragAdapter;
import org.eclipse.dltk.internal.ui.scriptview.SelectionTransferDragAdapter;
import org.eclipse.dltk.ui.DLTKPluginImages;
import org.eclipse.dltk.ui.DLTKUIPlugin;
import org.eclipse.dltk.ui.search.IMatchPresentation;
import org.eclipse.dltk.ui.util.ExceptionHandler;
import org.eclipse.dltk.ui.viewsupport.ProblemTableViewer;
import org.eclipse.dltk.ui.viewsupport.ProblemTreeViewer;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.DecoratingLabelProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.search.ui.IContextMenuConstants;
import org.eclipse.search.ui.ISearchResultViewPart;
import org.eclipse.search.ui.NewSearchUI;
import org.eclipse.search.ui.text.AbstractTextSearchResult;
import org.eclipse.search.ui.text.AbstractTextSearchViewPage;
import org.eclipse.search.ui.text.Match;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionContext;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.part.IShowInTargetList;
import org.eclipse.ui.part.ResourceTransfer;
import org.eclipse.ui.texteditor.ITextEditor;
public class DLTKSearchResultPage extends AbstractTextSearchViewPage
implements IAdaptable {
public static class DecoratorIgnoringViewerSorter extends ViewerComparator {
private final ILabelProvider fLabelProvider;
public DecoratorIgnoringViewerSorter(ILabelProvider labelProvider) {
super(null); // lazy initialization
fLabelProvider = labelProvider;
}
@Override
public int compare(Viewer viewer, Object e1, Object e2) {
String name1 = fLabelProvider.getText(e1);
String name2 = fLabelProvider.getText(e2);
if (name1 == null)
name1 = "";//$NON-NLS-1$
if (name2 == null)
name2 = "";//$NON-NLS-1$
return getComparator().compare(name1, name2);
}
}
private static final int DEFAULT_ELEMENT_LIMIT = 1000;
private static final String FALSE = "FALSE"; //$NON-NLS-1$
private static final String TRUE = "TRUE"; //$NON-NLS-1$
private static final String KEY_GROUPING = "org.eclipse.dltk.search.resultpage.grouping"; //$NON-NLS-1$
private static final String KEY_SORTING = "org.eclipse.dltk.search.resultpage.sorting"; //$NON-NLS-1$
private static final String KEY_LIMIT_ENABLED = "org.eclipse.dltk.search.resultpage.limit_enabled"; //$NON-NLS-1$
private static final String KEY_LIMIT = "org.eclipse.dltk.search.resultpage.limit"; //$NON-NLS-1$
private static final String GROUP_GROUPING = "org.eclipse.dltk.search.resultpage.grouping"; //$NON-NLS-1$
private static final String GROUP_FILTERING = "org.eclipse.dltk.search.resultpage.filtering"; //$NON-NLS-1$
private NewSearchViewActionGroup fActionGroup;
private DLTKSearchContentProvider fContentProvider;
private int fCurrentSortOrder;
private SortAction fSortByNameAction;
private SortAction fSortByParentName;
private SortAction fSortByPathAction;
private GroupAction fGroupTypeAction;
private GroupAction fGroupFileAction;
private GroupAction fGroupPackageAction;
private GroupAction fGroupProjectAction;
private int fCurrentGrouping;
private static final String[] SHOW_IN_TARGETS = new String[] {
DLTKUIPlugin.ID_SCRIPT_EXPLORER, IPageLayout.ID_RES_NAV };
public static final IShowInTargetList SHOW_IN_TARGET_LIST = () -> SHOW_IN_TARGETS;
private DLTKSearchEditorOpener fEditorOpener = new DLTKSearchEditorOpener();
public DLTKSearchResultPage() {
initSortActions();
initGroupingActions();
// initFilterActions();
setElementLimit(Integer.valueOf(DEFAULT_ELEMENT_LIMIT));
}
private void initSortActions() {
fSortByNameAction = new SortAction(
SearchMessages.DLTKSearchResultPage_sortByName, this,
SortingLabelProvider.SHOW_ELEMENT_CONTAINER);
fSortByPathAction = new SortAction(
SearchMessages.DLTKSearchResultPage_sortByPath, this,
SortingLabelProvider.SHOW_PATH);
fSortByParentName = new SortAction(
SearchMessages.DLTKSearchResultPage_sortByParentName, this,
SortingLabelProvider.SHOW_CONTAINER_ELEMENT);
}
private void initGroupingActions() {
fGroupProjectAction = new GroupAction(
SearchMessages.DLTKSearchResultPage_groupby_project,
SearchMessages.DLTKSearchResultPage_groupby_project_tooltip,
this, LevelTreeContentProvider.LEVEL_PROJECT);
DLTKPluginImages.setLocalImageDescriptors(fGroupProjectAction,
"prj_mode.png"); //$NON-NLS-1$
fGroupPackageAction = new GroupAction(
SearchMessages.DLTKSearchResultPage_groupby_package,
SearchMessages.DLTKSearchResultPage_groupby_package_tooltip,
this, LevelTreeContentProvider.LEVEL_PACKAGE);
DLTKPluginImages.setLocalImageDescriptors(fGroupPackageAction,
"package_mode.png"); //$NON-NLS-1$
fGroupFileAction = new GroupAction(
SearchMessages.DLTKSearchResultPage_groupby_file,
SearchMessages.DLTKSearchResultPage_groupby_file_tooltip, this,
LevelTreeContentProvider.LEVEL_FILE);
DLTKPluginImages.setLocalImageDescriptors(fGroupFileAction,
"file_mode.png"); //$NON-NLS-1$
fGroupTypeAction = new GroupAction(
SearchMessages.DLTKSearchResultPage_groupby_type,
SearchMessages.DLTKSearchResultPage_groupby_type_tooltip, this,
LevelTreeContentProvider.LEVEL_TYPE);
DLTKPluginImages.setLocalImageDescriptors(fGroupTypeAction,
"type_mode.png"); //$NON-NLS-1$
}
@Override
public void setViewPart(ISearchResultViewPart part) {
super.setViewPart(part);
fActionGroup = new NewSearchViewActionGroup(part);
}
@Override
public void showMatch(Match match, int offset, int length, boolean activate)
throws PartInitException {
IEditorPart editor;
try {
editor = fEditorOpener.openMatch(match);
} catch (ModelException e) {
throw new PartInitException(e.getStatus());
}
if (editor != null && activate)
editor.getEditorSite().getPage().activate(editor);
Object element = match.getElement();
if (editor instanceof ITextEditor) {
ITextEditor textEditor = (ITextEditor) editor;
textEditor.selectAndReveal(offset, length);
} else if (editor != null) {
if (element instanceof IFile) {
IFile file = (IFile) element;
showWithMarker(editor, file, offset, length);
}
} else {
DLTKSearchResult result = (DLTKSearchResult) getInput();
IMatchPresentation participant = result
.getSearchParticpant(element);
if (participant != null)
participant.showMatch(match, offset, length, activate);
}
}
private void showWithMarker(IEditorPart editor, IFile file, int offset,
int length) throws PartInitException {
try {
IMarker marker = file.createMarker(NewSearchUI.SEARCH_MARKER);
HashMap<String, Integer> attributes = new HashMap<>(4);
attributes.put(IMarker.CHAR_START, Integer.valueOf(offset));
attributes.put(IMarker.CHAR_END, Integer.valueOf(offset + length));
marker.setAttributes(attributes);
IDE.gotoMarker(editor, marker);
marker.delete();
} catch (CoreException e) {
throw new PartInitException(
SearchMessages.DLTKSearchResultPage_error_marker, e);
}
}
@Override
protected void fillContextMenu(IMenuManager mgr) {
super.fillContextMenu(mgr);
addSortActions(mgr);
fActionGroup.setContext(new ActionContext(
getSite().getSelectionProvider().getSelection()));
fActionGroup.fillContextMenu(mgr);
}
private void addSortActions(IMenuManager mgr) {
if (getLayout() != FLAG_LAYOUT_FLAT)
return;
MenuManager sortMenu = new MenuManager(
SearchMessages.DLTKSearchResultPage_sortBylabel);
sortMenu.add(fSortByNameAction);
sortMenu.add(fSortByPathAction);
sortMenu.add(fSortByParentName);
fSortByNameAction.setChecked(
fCurrentSortOrder == fSortByNameAction.getSortOrder());
fSortByPathAction.setChecked(
fCurrentSortOrder == fSortByPathAction.getSortOrder());
fSortByParentName.setChecked(
fCurrentSortOrder == fSortByParentName.getSortOrder());
mgr.appendToGroup(IContextMenuConstants.GROUP_VIEWER_SETUP, sortMenu);
}
@Override
protected void fillToolbar(IToolBarManager tbm) {
super.fillToolbar(tbm);
if (getLayout() != FLAG_LAYOUT_FLAT)
addGroupActions(tbm);
}
private void addGroupActions(IToolBarManager mgr) {
mgr.appendToGroup(IContextMenuConstants.GROUP_VIEWER_SETUP,
new Separator(GROUP_GROUPING));
mgr.appendToGroup(GROUP_GROUPING, fGroupProjectAction);
mgr.appendToGroup(GROUP_GROUPING, fGroupPackageAction);
mgr.appendToGroup(GROUP_GROUPING, fGroupFileAction);
mgr.appendToGroup(GROUP_GROUPING, fGroupTypeAction);
updateGroupingActions();
}
private void updateGroupingActions() {
fGroupProjectAction.setChecked(
fCurrentGrouping == LevelTreeContentProvider.LEVEL_PROJECT);
fGroupPackageAction.setChecked(
fCurrentGrouping == LevelTreeContentProvider.LEVEL_PACKAGE);
fGroupFileAction.setChecked(
fCurrentGrouping == LevelTreeContentProvider.LEVEL_FILE);
fGroupTypeAction.setChecked(
fCurrentGrouping == LevelTreeContentProvider.LEVEL_TYPE);
}
@Override
public void dispose() {
fActionGroup.dispose();
super.dispose();
}
@Override
protected void elementsChanged(Object[] objects) {
if (fContentProvider != null)
fContentProvider.elementsChanged(objects);
}
@Override
protected void clear() {
if (fContentProvider != null)
fContentProvider.clear();
}
private void addDragAdapters(StructuredViewer viewer) {
Transfer[] transfers = new Transfer[] {
LocalSelectionTransfer.getTransfer(),
ResourceTransfer.getInstance() };
int ops = DND.DROP_COPY | DND.DROP_LINK;
DLTKViewerDragAdapter dragAdapter = new DLTKViewerDragAdapter(viewer);
dragAdapter.addDragSourceListener(
new SelectionTransferDragAdapter(viewer));
dragAdapter.addDragSourceListener(
new EditorInputTransferDragAdapter(viewer));
dragAdapter
.addDragSourceListener(new ResourceTransferDragAdapter(viewer));
viewer.addDragSupport(ops, transfers, dragAdapter);
}
@Override
protected void configureTableViewer(TableViewer viewer) {
viewer.setUseHashlookup(true);
SortingLabelProvider sortingLabelProvider = new SortingLabelProvider(
this);
viewer.setLabelProvider(new ColorDecoratingLabelProvider(
sortingLabelProvider, PlatformUI.getWorkbench()
.getDecoratorManager().getLabelDecorator()));
fContentProvider = new DLTKSearchTableContentProvider(this);
viewer.setContentProvider(fContentProvider);
viewer.setComparator(
new DecoratorIgnoringViewerSorter(sortingLabelProvider));
setSortOrder(fCurrentSortOrder);
addDragAdapters(viewer);
}
@Override
protected void configureTreeViewer(TreeViewer viewer) {
PostfixLabelProvider postfixLabelProvider = new PostfixLabelProvider(
this);
viewer.setUseHashlookup(true);
viewer.setComparator(
new DecoratorIgnoringViewerSorter(postfixLabelProvider));
viewer.setLabelProvider(new ColorDecoratingLabelProvider(
postfixLabelProvider, PlatformUI.getWorkbench()
.getDecoratorManager().getLabelDecorator()));
fContentProvider = new LevelTreeContentProvider(this, fCurrentGrouping);
viewer.setContentProvider(fContentProvider);
addDragAdapters(viewer);
}
@Override
protected TreeViewer createTreeViewer(Composite parent) {
return new ProblemTreeViewer(parent,
SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
}
@Override
protected TableViewer createTableViewer(Composite parent) {
return new ProblemTableViewer(parent,
SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
}
void setSortOrder(int order) {
fCurrentSortOrder = order;
StructuredViewer viewer = getViewer();
viewer.getControl().setRedraw(false);
DecoratingLabelProvider dlp = (DecoratingLabelProvider) viewer
.getLabelProvider();
((SortingLabelProvider) dlp.getLabelProvider()).setOrder(order);
viewer.getControl().setRedraw(true);
viewer.refresh();
getSettings().put(KEY_SORTING, fCurrentSortOrder);
}
@Override
public void init(IPageSite site) {
super.init(site);
IMenuManager menuManager = site.getActionBars().getMenuManager();
menuManager.insertBefore(IContextMenuConstants.GROUP_PROPERTIES,
new Separator(GROUP_FILTERING));
fActionGroup.fillActionBars(site.getActionBars());
menuManager.appendToGroup(IContextMenuConstants.GROUP_PROPERTIES,
new Action(
SearchMessages.DLTKSearchResultPage_preferences_label) {
@Override
public void run() {
String pageId = "org.eclipse.search.preferences.SearchPreferencePage"; //$NON-NLS-1$
PreferencesUtil.createPreferenceDialogOn(
DLTKUIPlugin.getActiveWorkbenchShell(), pageId,
null, null).open();
}
});
}
/**
* Precondition here: the viewer must be showing a tree with a
* LevelContentProvider.
*
* @param grouping
*/
void setGrouping(int grouping) {
fCurrentGrouping = grouping;
StructuredViewer viewer = getViewer();
LevelTreeContentProvider cp = (LevelTreeContentProvider) viewer
.getContentProvider();
cp.setLevel(grouping);
updateGroupingActions();
getSettings().put(KEY_GROUPING, fCurrentGrouping);
getViewPart().updateLabel();
}
@Override
protected StructuredViewer getViewer() {
// override so that it's visible in the package.
return super.getViewer();
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.search.ui.text.AbstractTextSearchViewPage#restoreState(org.
* eclipse.ui.IMemento)
*/
@Override
public void restoreState(IMemento memento) {
super.restoreState(memento);
try {
fCurrentSortOrder = getSettings().getInt(KEY_SORTING);
} catch (NumberFormatException e) {
fCurrentSortOrder = SortingLabelProvider.SHOW_ELEMENT_CONTAINER;
}
try {
fCurrentGrouping = getSettings().getInt(KEY_GROUPING);
} catch (NumberFormatException e) {
fCurrentGrouping = LevelTreeContentProvider.LEVEL_PACKAGE;
}
int elementLimit = DEFAULT_ELEMENT_LIMIT;
if (FALSE.equals(getSettings().get(KEY_LIMIT_ENABLED))) {
elementLimit = -1;
} else {
try {
elementLimit = getSettings().getInt(KEY_LIMIT);
} catch (NumberFormatException e) {
}
}
if (memento != null) {
Integer value = memento.getInteger(KEY_GROUPING);
if (value != null)
fCurrentGrouping = value.intValue();
value = memento.getInteger(KEY_SORTING);
if (value != null)
fCurrentSortOrder = value.intValue();
boolean limitElements = !FALSE
.equals(memento.getString(KEY_LIMIT_ENABLED));
value = memento.getInteger(KEY_LIMIT);
if (value != null)
elementLimit = limitElements ? value.intValue() : -1;
}
setElementLimit(Integer.valueOf(elementLimit));
}
@Override
public void saveState(IMemento memento) {
super.saveState(memento);
memento.putInteger(KEY_GROUPING, fCurrentGrouping);
memento.putInteger(KEY_SORTING, fCurrentSortOrder);
int limit = getElementLimit().intValue();
if (limit != -1)
memento.putString(KEY_LIMIT_ENABLED, TRUE);
else
memento.putString(KEY_LIMIT_ENABLED, FALSE);
memento.putInteger(KEY_LIMIT, getElementLimit().intValue());
}
private boolean isQueryRunning() {
AbstractTextSearchResult result = getInput();
if (result != null) {
return NewSearchUI.isQueryRunning(result.getQuery());
}
return false;
}
@Override
public String getLabel() {
String label = super.getLabel();
AbstractTextSearchResult input = getInput();
if (input != null && input.getActiveMatchFilters() != null
&& input.getActiveMatchFilters().length > 0) {
if (isQueryRunning()) {
String message = SearchMessages.DLTKSearchResultPage_filtered_message;
return Messages.format(message, label);
}
int filteredOut = input.getMatchCount() - getFilteredMatchCount();
String message = SearchMessages.DLTKSearchResultPage_filteredWithCount_message;
return Messages.format(message, label, String.valueOf(filteredOut));
}
return label;
}
private int getFilteredMatchCount() {
StructuredViewer viewer = getViewer();
if (viewer instanceof TreeViewer) {
ITreeContentProvider tp = (ITreeContentProvider) viewer
.getContentProvider();
return getMatchCount(tp, getRootElements((TreeViewer) getViewer()));
}
return getMatchCount((TableViewer) viewer);
}
private Object[] getRootElements(TreeViewer viewer) {
Tree t = viewer.getTree();
Item[] roots = t.getItems();
Object[] elements = new Object[roots.length];
for (int i = 0; i < elements.length; i++) {
elements[i] = roots[i].getData();
}
return elements;
}
private Object[] getRootElements(TableViewer viewer) {
Table t = viewer.getTable();
Item[] roots = t.getItems();
Object[] elements = new Object[roots.length];
for (int i = 0; i < elements.length; i++) {
elements[i] = roots[i].getData();
}
return elements;
}
private int getMatchCount(ITreeContentProvider cp, Object[] elements) {
int count = 0;
for (int j = 0; j < elements.length; j++) {
count += getDisplayedMatchCount(elements[j]);
Object[] children = cp.getChildren(elements[j]);
count += getMatchCount(cp, children);
}
return count;
}
private int getMatchCount(TableViewer viewer) {
Object[] elements = getRootElements(viewer);
int count = 0;
for (int i = 0; i < elements.length; i++) {
count += getDisplayedMatchCount(elements[i]);
}
return count;
}
@SuppressWarnings("unchecked")
@Override
public <T> T getAdapter(Class<T> adapter) {
if (IShowInTargetList.class.equals(adapter)) {
return (T) SHOW_IN_TARGET_LIST;
}
return null;
}
@Override
protected void handleOpen(OpenEvent event) {
Object firstElement = ((IStructuredSelection) event.getSelection())
.getFirstElement();
if (firstElement instanceof ISourceModule
|| firstElement instanceof IMember) {
if (getDisplayedMatchCount(firstElement) == 0) {
try {
fEditorOpener.openElement(firstElement);
} catch (CoreException e) {
ExceptionHandler.handle(e, getSite().getShell(),
SearchMessages.DLTKSearchResultPage_open_editor_error_title,
SearchMessages.DLTKSearchResultPage_open_editor_error_message);
}
return;
}
}
super.handleOpen(event);
}
@Override
public void setElementLimit(Integer elementLimit) {
super.setElementLimit(elementLimit);
int limit = elementLimit.intValue();
getSettings().put(KEY_LIMIT, limit);
getSettings().put(KEY_LIMIT_ENABLED, limit != -1 ? TRUE : FALSE);
}
}