/**
* Copyright (c) 20015 by Brainwy Software Ltda. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.shared_ui.search;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.search.ui.ISearchPage;
import org.eclipse.search.ui.ISearchPageContainer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.python.pydev.shared_core.log.Log;
import org.python.pydev.shared_core.string.FastStringBuffer;
import org.python.pydev.shared_core.string.StringUtils;
import org.python.pydev.shared_ui.dialogs.ProjectSelectionDialog;
public abstract class AbstractSearchIndexPage extends DialogPage implements ISearchPage {
protected SearchIndexDataHistory searchIndexDataHistory;
protected Text fPattern;
protected ISearchPageContainer fContainer;
protected boolean fFirstTime = true;
protected Button fIsCaseSensitiveCheckbox;
protected Button fIsWholeWordCheckbox;
// Scope
protected Button fModulesScopeRadio;
protected Button fWorkspaceScopeRadio;
protected Button fProjectsScopeRadio;
// Scope data
protected Text fModuleNames;
protected Text fProjectNames;
protected Button fHistory;
protected Button fSelectProjects;
protected Button fSelectFolders;
public AbstractSearchIndexPage(AbstractUIPlugin plugin) {
searchIndexDataHistory = new SearchIndexDataHistory(plugin);
}
@Override
public void createControl(Composite parent) {
initializeDialogUnits(parent);
searchIndexDataHistory.readConfiguration();
Composite composite = new Composite(parent, SWT.NONE);
composite.setFont(parent.getFont());
GridLayout layout = new GridLayout(10, false);
composite.setLayout(layout);
// Line 1
createLabel(composite, SWT.LEAD,
"&Text (* = any string, ? = any character, \\\\ = escape).",
10);
createComponents(composite);
if (fSelectProjects != null && fProjectNames != null) {
fSelectProjects.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
Shell activeShell = Display.getCurrent().getActiveShell();
ProjectSelectionDialog dialog = new ProjectSelectionDialog(activeShell, null, true);
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
String text = fProjectNames.getText();
ArrayList<Object> lst = new ArrayList<>();
for (String s : StringUtils.split(text, ',')) {
s = s.trim();
IProject project = root.getProject(s);
if (project != null && project.exists() && project.isAccessible()) {
lst.add(project);
}
}
dialog.setInitialElementSelections(lst);
int open = dialog.open();
if (open == Window.OK) {
Object[] result = dialog.getResult();
if (result != null) {
FastStringBuffer buf = new FastStringBuffer();
for (Object object : result) {
if (object instanceof IProject) {
if (buf.length() > 0) {
buf.append(", ");
}
buf.append(((IProject) object).getName());
}
}
fProjectNames.setText(buf.toString());
setRadioSelection(fProjectsScopeRadio);
}
}
}
});
}
setControl(composite);
Dialog.applyDialogFont(composite);
}
protected void setRadioSelection(Button bt) {
// We must deselect others
Composite parent = bt.getParent();
Control[] children = parent.getChildren();
for (int i = 0; i < children.length; i++) {
Control child = children[i];
if (bt != child && child instanceof Button && (child.getStyle() & SWT.RADIO) != 0) {
((Button) child).setSelection(false);
}
}
bt.setSelection(true);
}
protected void createComponents(Composite composite) {
// Line 2
fPattern = createText(composite, SWT.SINGLE | SWT.BORDER, 5, 50);
// fHistory = createButton(composite, SWT.PUSH, "...", 1);
// ((GridData) fHistory.getLayoutData()).widthHint = 25;
fIsCaseSensitiveCheckbox = createButton(composite, SWT.CHECK, SearchMessages.SearchPage_caseSensitive, 5);
// Line 2 (part 2)
createLabel(composite, SWT.NONE, "", 5);
fIsWholeWordCheckbox = createButton(composite, SWT.CHECK, SearchMessages.SearchPage_wholeWord, 5);
// Line 3
createLabel(composite, SWT.LEAD, "Scope", 1);
fWorkspaceScopeRadio = createButton(composite, SWT.RADIO, "&Workspace", 1);
fModulesScopeRadio = createButton(composite, SWT.RADIO, "&Module(s)", 1);
fModuleNames = createText(composite, SWT.SINGLE | SWT.BORDER, 2, 50);
createLabel(composite, SWT.NONE, "", 5);
// Line 4
createLabel(composite, SWT.NONE, "", 1);
fProjectsScopeRadio = createButton(composite, SWT.RADIO, "&Project(s)", 1);
fProjectNames = createText(composite, SWT.SINGLE | SWT.BORDER, 2, 50);
fSelectProjects = createButton(composite, SWT.PUSH, "...", 1);
((GridData) fSelectProjects.getLayoutData()).widthHint = 25;
createLabel(composite, SWT.LEAD,
"\n\nNote: only modules in the PyDev index will be searched (valid modules below a source folder).",
10);
createLabel(composite, SWT.LEAD,
"Note: wildcards may be used for modules and project matching.",
10);
}
protected Text createText(Composite composite, int style, int cols, int charsLen) {
Text text = new Text(composite, style);
text.setFont(composite.getFont());
GridData data = new GridData(GridData.FILL, GridData.FILL, true, false, cols, 1);
data.widthHint = convertWidthInCharsToPixels(charsLen);
text.setLayoutData(data);
return text;
}
protected Label createLabel(Composite composite, int style, String string, int cols) {
Label label = new Label(composite, style);
label.setText(string);
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, cols, 1));
label.setFont(composite.getFont());
return label;
}
protected Button createButton(Composite composite, int style, String string, int cols) {
Button bt = new Button(composite, style);
bt.setText(string);
bt.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
}
});
bt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, cols, 1));
bt.setFont(composite.getFont());
return bt;
}
protected ScopeAndData getScopeAndData() {
if (fModulesScopeRadio.getSelection()) {
return new ScopeAndData(SearchIndexData.SCOPE_MODULES, fModuleNames.getText());
}
if (fWorkspaceScopeRadio.getSelection()) {
return new ScopeAndData(SearchIndexData.SCOPE_WORKSPACE, "");
}
if (fProjectsScopeRadio.getSelection()) {
return new ScopeAndData(SearchIndexData.SCOPE_PROJECTS, fProjectNames.getText());
}
// If nothing works, use workspace!
return new ScopeAndData(SearchIndexData.SCOPE_WORKSPACE, "");
}
@Override
public void setContainer(ISearchPageContainer container) {
fContainer = container;
}
@Override
public void setVisible(boolean visible) {
if (visible && fPattern != null) {
if (fFirstTime) {
fFirstTime = false;
// Load settings from last activation
SearchIndexData last = initializeFromLast();
if (last != null) {
this.fIsCaseSensitiveCheckbox.setSelection(last.isCaseSensitive);
this.fIsWholeWordCheckbox.setSelection(last.isWholeWord);
}
// Override some settings from the current selection
initializeFromSelection(last);
}
}
super.setVisible(visible);
if (visible && fPattern != null) {
fPattern.selectAll();
fPattern.setFocus();
}
updateOKStatus();
}
protected SearchIndexData initializeFromLast() {
SearchIndexData last = searchIndexDataHistory.getLast();
if (last != null) {
String text = last.textPattern;
if (text != null && text.length() > 0) {
fPattern.setText(text);
return last;
}
}
return null;
}
protected void updateOKStatus() {
fContainer.setPerformActionEnabled(true);
}
protected void initializeFromSelection(SearchIndexData last) {
ISelection selection = fContainer.getSelection();
if (selection instanceof ITextSelection && !selection.isEmpty()
&& ((ITextSelection) selection).getLength() > 0) {
boolean regularPath = true;
// As we have a checkbox for whole word now, the code below shouldn't be needed anymore.
// if (selection instanceof TextSelection) {
// // If we got a substring, add * as needed before/after.
// TextSelection tx = (TextSelection) selection;
// IDocument doc = getDocument(tx);
// if (doc != null) {
// int offset = tx.getOffset();
// int length = tx.getLength();
// try {
// String txt = doc.get(offset, length);
// if (!txt.startsWith("*")) {
// if (offset > 0) {
// char c = doc.getChar(offset - 1);
// if (Character.isJavaIdentifierPart(c)) {
// txt = '*' + txt;
// }
// }
// }
//
// if (!txt.endsWith("*")) {
// if (doc.getLength() > offset + length) {
// char c = doc.getChar(offset + length);
// if (Character.isJavaIdentifierPart(c)) {
// txt = txt + '*';
// }
// }
// }
// fPattern.setText(txt);
// regularPath = false;
// } catch (BadLocationException e) {
// // Ignore
// }
// }
// }
if (regularPath) {
String text = ((ITextSelection) selection).getText();
if (text != null) {
fPattern.setText(text);
}
}
}
Collection<String> projectNames = new HashSet<>();
Collection<String> moduleNames = new HashSet<>();
ISelection sel = fContainer.getSelection();
boolean hasNonEditorSelection = true;
if (sel instanceof IStructuredSelection && !sel.isEmpty()) {
Iterator<?> iter = ((IStructuredSelection) sel).iterator();
while (iter.hasNext()) {
Object curr = iter.next();
if (curr instanceof IWorkingSet) {
IWorkingSet workingSet = (IWorkingSet) curr;
if (workingSet.isAggregateWorkingSet() && workingSet.isEmpty()) {
// Empty working set: ignore
continue;
}
IAdaptable[] elements = workingSet.getElements();
for (int i = 0; i < elements.length; i++) {
IResource resource = elements[i].getAdapter(IResource.class);
checkSelectedResource(projectNames, moduleNames, resource);
}
} else if (curr instanceof ICustomLineElement) {
IResource resource = ((ICustomLineElement) curr).getParent();
checkSelectedResource(projectNames, moduleNames, resource);
} else if (curr instanceof IAdaptable) {
IResource resource = ((IAdaptable) curr).getAdapter(IResource.class);
checkSelectedResource(projectNames, moduleNames, resource);
}
}
} else if (fContainer.getActiveEditorInput() != null) {
hasNonEditorSelection = false;
checkSelectedResource(projectNames, moduleNames, fContainer.getActiveEditorInput().getAdapter(IFile.class));
}
this.fModuleNames.setText(StringUtils.join(", ", moduleNames));
this.fProjectNames.setText(StringUtils.join(", ", projectNames));
// Set the scope (with early return)
if (hasNonEditorSelection) {
if (!moduleNames.isEmpty()) {
this.fModulesScopeRadio.setSelection(true);
return;
} else if (!projectNames.isEmpty()) {
this.fProjectsScopeRadio.setSelection(true);
return;
}
}
if (last != null) {
if (initializeScopeFromLast(last)) {
return;
}
}
//All others failed: go for workspace selection
this.fWorkspaceScopeRadio.setSelection(true);
}
// Hack to get document from text selection.
private IDocument getDocument(TextSelection tx) {
try {
Method method = TextSelection.class.getDeclaredMethod("getDocument");
method.setAccessible(true);
return (IDocument) method.invoke(tx);
} catch (Exception e) {
Log.log(e);
return null;
}
}
protected boolean initializeScopeFromLast(SearchIndexData last) {
int scope = last.scope;
switch (scope) {
case SearchIndexData.SCOPE_WORKSPACE:
this.fWorkspaceScopeRadio.setSelection(true);
return true;
case SearchIndexData.SCOPE_MODULES:
this.fModulesScopeRadio.setSelection(true);
return true;
case SearchIndexData.SCOPE_PROJECTS:
this.fProjectsScopeRadio.setSelection(true);
return true;
}
return false;
}
/**
* Subclasses should override so that given the selected resource the project names/ module names are properly filled
* for the initial values.
*/
protected abstract void checkSelectedResource(Collection<String> projectNames, Collection<String> moduleNames,
IResource resource);
}