/******************************************************************************* * Copyright (c) 2006, 2010 QNX Software Systems 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: * QNX - Initial API and implementation * Ed Swartz (Nokia) * Andrey Eremchenko (LEDAS) * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.ui.search; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.search.ui.text.AbstractTextSearchViewPage; import org.eclipse.search.ui.text.Match; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IMemento; import org.eclipse.ui.PartInitException; import org.eclipse.ui.part.IPageSite; import org.eclipse.ui.texteditor.ITextEditor; import org.eclipse.cdt.core.index.IIndexFileLocation; import org.eclipse.cdt.core.index.IndexLocationFactory; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.ui.CUIPlugin; import org.eclipse.cdt.internal.ui.util.EditorUtility; import org.eclipse.cdt.internal.ui.viewsupport.ColoringLabelProvider; /** * Implementation of the search view page for index based searches. */ public class PDOMSearchViewPage extends AbstractTextSearchViewPage { public static final int LOCATION_COLUMN_INDEX = 0; public static final int DEFINITION_COLUMN_INDEX = 1; public static final int MATCH_COLUMN_INDEX = 2; private static final String[] fColumnLabels = new String[] { CSearchMessages.PDOMSearchViewPageLocationColumn_label, CSearchMessages.PDOMSearchViewPageDefinitionColumn_label, CSearchMessages.PDOMSearchViewPageMatchColumn_label }; private static final String KEY_LOCATION_COLUMN_WIDTH = "locationColumnWidth"; //$NON-NLS-1$ private static final String KEY_DEFINITION_COLUMN_WIDTH = "definitionColumnWidth"; //$NON-NLS-1$ private static final String KEY_MATCH_COLUMN_WIDTH = "matchColumnWidth"; //$NON-NLS-1$ private static final String KEY_SHOW_ENCLOSING_DEFINITIONS = "showEnclosingDefinitions"; //$NON-NLS-1$ private IPDOMSearchContentProvider contentProvider; private boolean fShowEnclosingDefinitions; private ShowEnclosingDefinitionsAction fShowEnclosingDefinitionsAction; private int[] fColumnWidths = { 300, 150, 300 }; private class ShowEnclosingDefinitionsAction extends Action { public ShowEnclosingDefinitionsAction() { super(CSearchMessages.PDOMSearchViewPage_ShowEnclosingDefinitions_actionLabel, SWT.CHECK); setChecked(fShowEnclosingDefinitions); } @Override public void run() { setShowEnclosingDefinitions(isChecked()); } } public PDOMSearchViewPage(int supportedLayouts) { super(supportedLayouts); } public PDOMSearchViewPage() { super(); } @Override public void init(IPageSite pageSite) { super.init(pageSite); fShowEnclosingDefinitionsAction = new ShowEnclosingDefinitionsAction(); IMenuManager menuManager= pageSite.getActionBars().getMenuManager(); menuManager.add(fShowEnclosingDefinitionsAction); menuManager.updateAll(true); pageSite.getActionBars().updateActionBars(); } @Override public void restoreState(IMemento memento) { super.restoreState(memento); IDialogSettings settings = getSettings(); boolean showEnclosingDefinitions = true; if (settings.get(KEY_SHOW_ENCLOSING_DEFINITIONS) != null) showEnclosingDefinitions = settings.getBoolean(KEY_SHOW_ENCLOSING_DEFINITIONS); if (memento != null) { Boolean value = memento.getBoolean(KEY_SHOW_ENCLOSING_DEFINITIONS); if (value != null) showEnclosingDefinitions = value.booleanValue(); String[] keys = { KEY_LOCATION_COLUMN_WIDTH, KEY_DEFINITION_COLUMN_WIDTH, KEY_MATCH_COLUMN_WIDTH }; for (int i = 0; i < keys.length; i++) { Integer width = memento.getInteger(keys[i]); if (width == null) continue; if (width > 0) fColumnWidths[i] = width; } } setShowEnclosingDefinitions(showEnclosingDefinitions); } @Override public void saveState(IMemento memento) { super.saveState(memento); saveColumnWidths(); memento.putInteger(KEY_DEFINITION_COLUMN_WIDTH, fColumnWidths[DEFINITION_COLUMN_INDEX]); memento.putInteger(KEY_LOCATION_COLUMN_WIDTH, fColumnWidths[LOCATION_COLUMN_INDEX]); memento.putInteger(KEY_MATCH_COLUMN_WIDTH, fColumnWidths[MATCH_COLUMN_INDEX]); memento.putBoolean(KEY_SHOW_ENCLOSING_DEFINITIONS, fShowEnclosingDefinitions); } public void setShowEnclosingDefinitions(boolean showEnclosingDefinitions) { if (fShowEnclosingDefinitions == showEnclosingDefinitions) return; fShowEnclosingDefinitions = showEnclosingDefinitions; getSettings().put(KEY_SHOW_ENCLOSING_DEFINITIONS, fShowEnclosingDefinitions); if (fShowEnclosingDefinitionsAction.isChecked() != showEnclosingDefinitions) fShowEnclosingDefinitionsAction.setChecked(showEnclosingDefinitions); StructuredViewer viewer = getViewer(); if (viewer instanceof TableViewer) { TableViewer tableViewer = (TableViewer) viewer; TableColumn tableColumn = tableViewer.getTable().getColumn(DEFINITION_COLUMN_INDEX); if (fShowEnclosingDefinitions) { tableColumn.setWidth(fColumnWidths[DEFINITION_COLUMN_INDEX]); tableColumn.setResizable(true); } else { fColumnWidths[DEFINITION_COLUMN_INDEX] = tableColumn.getWidth(); tableColumn.setWidth(0); tableColumn.setResizable(false); } } if (viewer != null) viewer.refresh(); } public boolean isShowEnclosingDefinitions() { return fShowEnclosingDefinitions; } @Override protected void elementsChanged(Object[] objects) { if (contentProvider != null) contentProvider.elementsChanged(objects); } @Override protected void clear() { if (contentProvider != null) contentProvider.clear(); } /** * Supply a sorter for the list and tree content providers to supply some order to the * large numbers of matches that may result. * <p> * This sorter categorizes the different kinds of ICElement matches (as well as IStatus * messages and External Files groups) to place them in groups. The items within a * category are sorted in the default way {@link ViewerSorter#compare(Viewer, Object, Object)} works, * by comparing text labels. * <p> * A potential concern here is that, in sorting the elements by name, the user may * find himself randomly jumping around a file when navigating search results in order. * As this only happens when a search matches different identifiers or identifiers of * different types, and since the user can use a textual search within a file to navigate * the same results (ignoring extraneous hits in comments or disabled code), I argue it's not * a big deal. Furthermore, usually it would be a wildcard search that would result in * this situation -- indicating the user doesn't know the identifier and wants to find it using * search. In such a case, a sorted list of results in much more friendly to navigate. * @author eswartz * */ private class SearchViewerComparator extends ViewerComparator { /* (non-Javadoc) * @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) */ @Override public int compare(Viewer viewer, Object e1, Object e2) { if (e1 instanceof LineSearchElement && e2 instanceof LineSearchElement) { LineSearchElement l1 = (LineSearchElement) e1; LineSearchElement l2 = (LineSearchElement) e2; if (viewer instanceof TableViewer) { String p1 = l1.getLocation().getURI().getPath(); String p2 = l2.getLocation().getURI().getPath(); int cmp = p1.compareTo(p2); if (cmp != 0) return cmp; } return l1.getLineNumber() - l2.getLineNumber(); } return super.compare(viewer, e1, e2); } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ViewerComparator#category(java.lang.Object) */ @Override public int category(Object element) { // place status messages first if (element instanceof IStatus) { return -1000; } // keep elements of the same type together if (element instanceof TypeInfoSearchElement) { TypeInfoSearchElement searchElement = (TypeInfoSearchElement)element; int type = searchElement.getTypeInfo().getCElementType(); // handle unknown types if (type < 0) { type = 0; } return type; } else if (element instanceof ICElement) { int type = ((ICElement) element).getElementType(); // handle unknown types if (type < 0) { type = 0; } return Math.min(Math.max(0, type), 900); } // place external folders next to last if (element instanceof IPath || element instanceof IIndexFileLocation) { return 999; } // place external file matches last if (element == IPDOMSearchContentProvider.URI_CONTAINER) { return 1000; } return 2000; } } @Override protected void configureTreeViewer(TreeViewer viewer) { contentProvider = new PDOMSearchTreeContentProvider(this); viewer.setComparator(new SearchViewerComparator()); viewer.setContentProvider((PDOMSearchTreeContentProvider)contentProvider); PDOMSearchTreeLabelProvider innerLabelProvider = new PDOMSearchTreeLabelProvider(this); ColoringLabelProvider labelProvider = new ColoringLabelProvider(innerLabelProvider); viewer.setLabelProvider(labelProvider); } @Override protected void configureTableViewer(TableViewer viewer) { createColumns(viewer); contentProvider = new PDOMSearchListContentProvider(this); viewer.setComparator(new SearchViewerComparator()); viewer.setContentProvider((PDOMSearchListContentProvider)contentProvider); } @Override protected TableViewer createTableViewer(Composite parent) { TableViewer tableViewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION); tableViewer.getControl().addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { saveColumnWidths(); } }); return tableViewer; } private void saveColumnWidths() { StructuredViewer viewer = getViewer(); if (viewer instanceof TableViewer) { TableViewer tableViewer = (TableViewer) viewer; for (int i = 0; i < fColumnLabels.length; i++) { if (i == DEFINITION_COLUMN_INDEX && !fShowEnclosingDefinitions) continue; fColumnWidths[i] = tableViewer.getTable().getColumn(i).getWidth(); } } } private void createColumns(TableViewer viewer) { for (int i = 0; i < fColumnLabels.length; i++) { TableViewerColumn viewerColumn = new TableViewerColumn(viewer, SWT.NONE); viewerColumn.setLabelProvider(new PDOMSearchListLabelProvider(this, i)); TableColumn tableColumn = viewerColumn.getColumn(); tableColumn.setText(fColumnLabels[i]); tableColumn.setWidth(fColumnWidths[i]); tableColumn.setResizable(true); tableColumn.setMoveable(false); if (i == DEFINITION_COLUMN_INDEX && !fShowEnclosingDefinitions) { tableColumn.setWidth(0); tableColumn.setResizable(false); } } Table table = viewer.getTable(); table.setHeaderVisible(true); table.setLinesVisible(true); } @Override protected void showMatch(Match match, int currentOffset, int currentLength, boolean activate) throws PartInitException { if (!(match instanceof PDOMSearchMatch)) return; try { Object element= ((PDOMSearchMatch)match).getElement(); IIndexFileLocation ifl= ((PDOMSearchElement)element).getLocation(); IPath path = IndexLocationFactory.getPath(ifl); IEditorPart editor = EditorUtility.openInEditor(path, null, activate); if (editor instanceof ITextEditor) { ITextEditor textEditor = (ITextEditor)editor; textEditor.selectAndReveal(currentOffset, currentLength); } } catch (CoreException e) { CUIPlugin.log(e); } } @Override public StructuredViewer getViewer() { return super.getViewer(); } }