/**
* Copyright 2015 Fabrizio Iannetti.
*/
package com.github.fabeclipse.textedgrep.internal.ui;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.IntConsumer;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IMenuListener;
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.dialogs.InputDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.StringConverter;
import org.eclipse.jface.text.AbstractDocument;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.CursorLinePainter;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IFindReplaceTarget;
import org.eclipse.jface.text.IFindReplaceTargetExtension;
import org.eclipse.jface.text.IFindReplaceTargetExtension3;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.LineNumberRulerColumn;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CaretEvent;
import org.eclipse.swt.custom.CaretListener;
import org.eclipse.swt.custom.LineBackgroundEvent;
import org.eclipse.swt.custom.LineBackgroundListener;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.ColorDialog;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IPartService;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
import org.eclipse.ui.texteditor.AbstractTextEditor;
import com.github.fabeclipse.textedgrep.Activator;
import com.github.fabeclipse.textedgrep.GrepMonitor;
import com.github.fabeclipse.textedgrep.GrepTool;
import com.github.fabeclipse.textedgrep.IGrepContext;
import com.github.fabeclipse.textedgrep.IGrepTarget;
/**
* View to show the result of a grep operation on the
* content of an editor.
*
* @author fabrizio iannetti
*/
public class GrepView extends ViewPart implements IAdaptable {
private static final String LINE_NUMBER_RULER_COLOR = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER_COLOR;
public static final String VIEW_ID = "com.github.fabeclipse.textedgrep.grepview";
private static final String KEY_GREPREGEX = "grepregex";
private static final String KEY_CASESENSITIVE = "casesensitive";
private static final String KEY_HIGHLIGHTMULTIPLE = "highlightmultiple";
private static final String KEY_REGEX_HISTORY = "regexhistory";
private static final String KEY_DEFAULT_COLOR = "regexcolour";
class GrepOp {
private static final int GREP_SHOW_PROGRESS_THRESHOLD_PERCENT = 50;
private static final int GREP_SHOW_PROGRESS_THRESHOLD_MS = 300;
private final boolean multiple;
private final GrepTool tool;
private final IGrepContext context;
private final GrepMonitor monitor;
public GrepOp(IGrepTarget target, String[] rxList, boolean caseSensitive, boolean multiple) {
super();
monitor = new GrepMonitor();
this.multiple = multiple;
tool = new GrepTool(rxList, caseSensitive);
context = tool.grepStart(target);
}
public IGrepContext getContext() {
return context;
}
public boolean grep() {
final long tic = System.currentTimeMillis();
monitor.onProgress(new IntConsumer() {
private boolean showProgress;
@Override
public void accept(final int p) {
if (!showProgress) {
long elapsed = System.currentTimeMillis() - tic;
if (elapsed > GREP_SHOW_PROGRESS_THRESHOLD_MS &&
p < GREP_SHOW_PROGRESS_THRESHOLD_PERCENT) {
// seems to take long, enable a progress bar for the user
showProgress = true;
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
progress.onCancel(monitor);
showProgressBar(p);
}
});
}
} else {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
showProgressBar(p);
}
});
}
}
});
tool.grep(context, monitor, multiple);
return true;
}
void cancel() {
monitor.cancel();
}
}
private ArrayBlockingQueue<GrepOp> grepQueue = new ArrayBlockingQueue<GrepView.GrepOp>(10);
private AtomicReference<GrepOp> grepCurr = new AtomicReference<GrepView.GrepOp>();
private IGrepContext submitGrep(IGrepTarget target, String[] regex, boolean caseSensitive, boolean multi) {
GrepOp op = new GrepOp(target, regex, caseSensitive, multi);
// cancel all queued greps (actually one at most)
grepQueue.clear();
// queue the new grep
boolean queued = grepQueue.offer(op);
if (!queued) {
// TODO: log
} else {
// cancel running grep, if any
// (but not the one we just queued)
GrepOp cop = grepCurr.get();
if (cop != null && cop != op)
cop.cancel();
}
return op.getContext();
}
private final Thread grepThread = new Thread() {
public void run() {
ArrayList<GrepOp> ops = new ArrayList<GrepOp>();
for (;;) {
GrepOp op;
try {
op = grepQueue.take();
} catch (InterruptedException e1) {
// TODO log
System.out.println("grepThread interrupted, wait again");
continue;
}
grepQueue.drainTo(ops);
// only use the latest
if (!ops.isEmpty()) {
op = ops.get(ops.size() - 1);
ops.clear();
}
grepCurr.set(op);
final IGrepContext ctxt = op.getContext();
try {
if (op.grep())
GrepView.this.getSite().getShell().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
String text = ctxt.getText();
viewer.getDocument().set(text);
updateHighlightRanges();
}
});
} catch (Exception e) {
// grep was interrupted, just go on
} finally {
grepCurr.set(null);
GrepView.this.getSite().getShell().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
viewer.getControl().setEnabled(true);
viewer.getControl().setFocus();
}
});
}
}
};
};
/**
* @since 1.2
*/
protected static final int REGEX_HISTORY_MAX_SIZE = 20;
private Color failedSearchColor = new Color(Display.getDefault(), 255, 128, 128);
private Color successfulSearchColor;
private TextViewer viewer;
private String lastRegex;
private IGrepContext grepContext;
private Color cursorLineColor;
private Color highlightColor;
private boolean initialCaseSensitivity;
// index of last successful search
private int findIndex;
// position where to start the first find
private int firstFindIndex;
/**
* This object editor activation on the workbench page
* where the view is.
*/
private final IPartListener2 partListener = new IPartListener2() {
@Override
public void partVisible(IWorkbenchPartReference partRef) {}
@Override
public void partOpened(IWorkbenchPartReference partRef) {}
@Override
public void partInputChanged(IWorkbenchPartReference partRef) {}
@Override
public void partHidden(IWorkbenchPartReference partRef) {}
@Override
public void partDeactivated(IWorkbenchPartReference partRef) {}
@Override
public void partClosed(IWorkbenchPartReference partRef) {}
@Override
public void partBroughtToTop(IWorkbenchPartReference partRef) {}
@Override
public void partActivated(IWorkbenchPartReference partRef) {
IWorkbenchPart part = partRef.getPart(false);
if (linkToEditorAction.isChecked() && part instanceof EditorPart && !target.isSame(part)) {
// ok, it's a non null editor, and it is not the current one
// grep it
doGrep();
}
}
};
private Action linkToEditorAction;
private Action csAction;
private Action hmAction;
private boolean initialHighlightMultiple;
private Composite findbar;
private List<String> regexHistory = new ArrayList<String>();
// list of regex text entries
private List<RegexEntry> regexEntries = new ArrayList<RegexEntry>();
private IGrepTarget target;
private IEditorPart targetPart;
private ProgressWithCancel progress;
private Color lnColor;
private LineNumberRulerColumn lineNumberColumn;
@Override
public void createPartControl(final Composite parent) {
grepThread.start();
GridLayout layout = new GridLayout(1, false);
parent.setLayout(layout);
layout.marginHeight = 0;
layout.marginWidth = 0;
addRegexField(parent);
// progress indicator visible only during long running operations
addProgressBar(parent);
// vertical ruler that shows the original's line number
CompositeRuler ruler = new CompositeRuler();
lineNumberColumn = new LineNumberRulerColumn() {
@Override
protected int computeNumberOfDigits() {
// see SourceViewer, monkey see monkey do :)
if (grepContext != null) {
int digits= 2;
double lines = grepContext.getMaxOriginalLine() + 1;
while (lines > Math.pow(10, digits) -1) {
++digits;
}
return digits;
}
return super.computeNumberOfDigits();
}
@Override
protected String createDisplayString(int line) {
if (grepContext != null) {
return Integer.toString(grepContext.getOriginalLine(line) + 1);
}
return super.createDisplayString(line);
}
};
initializeColors();
lineNumberColumn.setForeground(lnColor);
ruler.addDecorator(0, lineNumberColumn);
viewer = new SourceViewer(parent, ruler , SWT.FLAT | SWT.V_SCROLL | SWT.H_SCROLL);
viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
// use the default text font (the same used in text editor)
viewer.getTextWidget().setFont(JFaceResources.getTextFont());
// this is a read-only view!
viewer.setEditable(false);
// highlight the cursor line
CursorLinePainter cursorLinePainter = new CursorLinePainter(viewer);
cursorLineColor = new Color(parent.getDisplay(), new RGB(200, 200, 0));
cursorLinePainter.setHighlightColor(cursorLineColor);
viewer.addPainter(cursorLinePainter);
viewer.getTextWidget().addLineBackgroundListener(new LineBackgroundListener() {
@Override
public void lineGetBackground(LineBackgroundEvent event) {
IGrepContext gc = grepContext;
IDocument document = viewer.getDocument();
if (gc != null) {
int line;
try {
line = document.getLineOfOffset(event.lineOffset);
// grep context can only operate on non empty documents
if (document.getLength() > 0) {
int index = gc.getColorForGrepLine(line);
RegexEntry entry = regexEntries.get(index);
event.lineBackground = entry.getRegexColor();
}
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
viewer.setDocument(new Document("grep result"));
// track cursor line and synchronise the cursor position in the editor
viewer.getTextWidget().addCaretListener(new CaretListener() {
@Override
public void caretMoved(CaretEvent event) {
int caretOffset = event.caretOffset;
if (grepContext != null) {
try {
int grepLine = viewer.getDocument().getLineOfOffset(caretOffset);
int line = grepContext.getOriginalLine(grepLine);
int offset = grepContext.getTarget().getLineOffset(line);
target.select(offset, 0);
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
// register double click and enter key to activate the current target
viewer.getTextWidget().addMouseListener(new MouseAdapter() {
@Override
public void mouseDoubleClick(MouseEvent e) {
if (targetPart != null) {
IWorkbenchWindow window = getViewSite().getWorkbenchWindow();
window.getActivePage().activate(targetPart);
}
}
});
viewer.getTextWidget().addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.keyCode == SWT.CR && targetPart != null) {
IWorkbenchWindow window = getViewSite().getWorkbenchWindow();
window.getActivePage().activate(targetPart);
}
}
});
// selection provider
getSite().setSelectionProvider(viewer);
MenuManager mm = new MenuManager();
mm.setRemoveAllWhenShown(true);
mm.addMenuListener(new IMenuListener() {
@Override
public void menuAboutToShow(IMenuManager manager) {
manager.add(new GroupMarker("GrepViewMenu"));
}
});
Menu contextMenu = mm.createContextMenu(viewer.getControl());
viewer.getTextWidget().setMenu(contextMenu);
getSite().registerContextMenu(mm, viewer);
IMenuManager menuManager = getViewSite().getActionBars().getMenuManager();
menuManager.add(new Action("Grep") {
@Override
public void run() {
doGrep();
viewer.getControl().setFocus();
}
});
menuManager.add(new Separator());
csAction = new Action("Case Sensitive", Action.AS_CHECK_BOX) {};
csAction.setChecked(initialCaseSensitivity);
menuManager.add(csAction);
hmAction = new Action("Highlight Multiple", Action.AS_CHECK_BOX) {};
hmAction.setChecked(initialHighlightMultiple);
menuManager.add(hmAction);
menuManager.add(new Separator());
Action colorAction = new Action("Highlight color...") {
@Override
public void run() {
Shell shell = getSite().getShell();
ColorDialog colorDialog = new ColorDialog(shell);
colorDialog.setText("Highlight color");
colorDialog.setRGB(highlightColor.getRGB());
RGB rgb = colorDialog.open();
if (rgb != null) {
Color oldColor = highlightColor;
highlightColor = new Color(shell.getDisplay(), rgb);
// TODO: move this in a long-running job, possible?
updateHighlightRanges();
oldColor.dispose();
}
}
};
menuManager.add(colorAction);
menuManager.add(new Action("Edit History...") {
@Override
public void run() {
String history = "";
for (String helem : GrepView.this.regexHistory) {
history += helem + "\n";
}
InputDialog inputDialog = new InputDialog(getViewSite().getShell(), "Regex history", null, history, null) {
@Override
protected int getInputTextStyle() {
return SWT.MULTI | SWT.BORDER;
}
@Override
protected boolean isResizable() {
return true;
}
@Override
protected Control createDialogArea(Composite parent) {
// TODO Auto-generated method stub
Control area = super.createDialogArea(parent);
getText().setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL | GridData.VERTICAL_ALIGN_FILL
| GridData.HORIZONTAL_ALIGN_FILL));
return area;
}
};
inputDialog.open();
history = inputDialog.getValue();
if (inputDialog.getReturnCode() == InputDialog.OK) {
regexHistory.clear();
if (history == null)
history = "";
String[] harray = history.split("\n");
for (String helem : harray) {
if (!helem.isEmpty())
regexHistory.add(helem);
}
for (RegexEntry rxe : regexEntries)
setRegexHistoryInComboBox(rxe);
}
}
});
fillActionBar();
// this code would use the standard find dialog, maybe offer it as an option?
// getViewSite().getActionBars().setGlobalActionHandler(ActionFactory.FIND.getId(), new FindReplaceAction(new ResourceBundle() {
// @Override
// protected Object handleGetObject(String key) { return null; }
// @Override
// public Enumeration<String> getKeys() { return null; }
// }, "GrepView.FindReplace", this));
getViewSite().getActionBars().setGlobalActionHandler(ActionFactory.FIND.getId(), new Action() {
@Override
public void run() {
createFindbar(parent);
}
});
IPartService partService = (IPartService) getViewSite().getService(IPartService.class);
partService.addPartListener(partListener);
// make tab key toggle between the regular expression text(s) and the viewer
makeTabList();
}
private void initializeColors() {
// get the line number ruler color from the editor plugin,
// to be consistent with the editor settings.
IPreferenceStore store = EditorsUI.getPreferenceStore();
final Display display = getViewSite().getShell().getDisplay();
store.addPropertyChangeListener(new IPropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
if (LINE_NUMBER_RULER_COLOR.equals(event.getProperty())) {
Object value = event.getNewValue();
// value for this property should always be a string
if (!(value instanceof String))
return;
final RGB newLnColor = StringConverter.asRGB((String) value);
display.asyncExec(new Runnable() {
@Override
public void run() {
updateLineNumberColor(newLnColor);
}
});
}
}
});
String key = LINE_NUMBER_RULER_COLOR;
RGB rgb= null;
if (store.contains(key)) {
if (store.isDefault(key))
rgb= PreferenceConverter.getDefaultColor(store, key);
else
rgb= PreferenceConverter.getColor(store, key);
} else {
rgb = new RGB(0, 0, 0); // TODO: use text color
}
updateLineNumberColor(rgb);
}
private void updateLineNumberColor(RGB rgb) {
lnColor = EditorsUI.getSharedTextColors().getColor(rgb);
if (lineNumberColumn != null) {
lineNumberColumn.setForeground(lnColor);
lineNumberColumn.redraw();
}
}
private void addProgressBar(Composite parent) {
progress = new ProgressWithCancel(parent, SWT.NONE);
progress.setVisible(false);
GridDataFactory.fillDefaults().grab(true, false).exclude(true).applyTo(progress);
}
private void showProgressBar(int percent) {
final GridData gd = (GridData) progress.getLayoutData();
if (percent == 100) {
gd.exclude = true;
progress.setVisible(false);
progress.getParent().layout();
} else {
if (gd.exclude) {
progress.setProgress(percent, true);
gd.exclude = false;
progress.setVisible(true);
progress.getParent().layout();
} else {
progress.setProgress(percent, false);
}
}
}
private void addRegexField(final Composite parent) {
// when pressing ENTER in the regexp field do a grep
IRegexEntryListener listener = new IRegexEntryListener() {
@Override
public void grep(String text, RegexEntry rxe) {
// the user pressed ENTER
doGrep();
// add regex to history if:
// * not empty
// * history is empty, or last element of history is not the same
if (!text.isEmpty() && (regexHistory.isEmpty() || !regexHistory.get(regexHistory.size() - 1).equals(text))) {
while (regexHistory.size() >= REGEX_HISTORY_MAX_SIZE)
regexHistory.remove(0);
regexHistory.add(text);
setRegexHistoryInComboBox(rxe);
}
}
};
RegexEntry rxe = new RegexEntry(parent, listener, -1);
rxe.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
rxe.setRegexpText(lastRegex);
setRegexHistoryInComboBox(rxe);
regexEntries.add(rxe);
// make sure the widget is just above the viewer
if (viewer != null && !viewer.getControl().isDisposed()) {
rxe.moveAbove(viewer.getControl());
}
}
private void fillActionBar() {
IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
linkToEditorAction = new Action("Link To Editor",Action.AS_CHECK_BOX) {};
ImageDescriptor image = Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/synced.gif");
linkToEditorAction.setImageDescriptor(image);
linkToEditorAction.setToolTipText("Sync Grep Content to active editor\nAs soon as an editor is activated its content is filtered");
toolBarManager.add(linkToEditorAction);
// TODO: leave commented for now, activate when ready
// (remove not possible at the moment)
// Action addRegexAction = new Action("+") {
// @Override
// public void run() {
// Composite parent = viewer.getControl().getParent();
// addRegexField(parent);
// makeTabList();
// parent.layout();
// }
// };
// toolBarManager.add(addRegexAction);
}
private void makeTabList() {
final Composite parent = viewer.getControl().getParent();
Control[] tabList = new Control[regexEntries.size() + 2];
int i = 0;
for (RegexEntry regexEntry : regexEntries) {
tabList[i++] = regexEntry;
}
tabList[i++] = viewer.getControl();
tabList[i] = tabList[0];
parent.setTabList(tabList);
}
private void setRegexHistoryInComboBox(RegexEntry regexEntry) {
String[] harray = new String[regexHistory.size()];
for (int i = 0; i < harray.length; i++)
harray[i] = regexHistory.get(harray.length - i - 1);
regexEntry.setRegexHistory(harray);
}
private void createFindbar(final Composite parent) {
final IFindReplaceTargetExtension target = (IFindReplaceTargetExtension)viewer.getFindReplaceTarget();
target.beginSession();
firstFindIndex = viewer.getSelectedRange().x;
findIndex = viewer.getSelectedRange().x;
if (findbar != null && !findbar.isDisposed()) {
findbar.setFocus();
return;
}
findbar = new Composite(parent, SWT.NONE);
findbar.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
GridLayout layout = new GridLayout(2, false);
layout.marginHeight = 0;
layout.marginWidth = 0;
findbar.setLayout(layout);
final Text findText = new Text(findbar, SWT.SINGLE | SWT.BORDER);
findText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, true));
successfulSearchColor = findText.getBackground();
ToolBar findBar = new ToolBar(findbar, SWT.FLAT | SWT.HORIZONTAL);
ToolItem closeItem = new ToolItem(findBar, SWT.PUSH);
closeItem.setText("X");
ToolItem nextfindItem = new ToolItem(findBar, SWT.FLAT);
nextfindItem.setText("Next");
ToolItem prevfindItem = new ToolItem(findBar, SWT.PUSH | SWT.FLAT);
prevfindItem.setText("Prev");
final Runnable closeBar = new Runnable() {
@Override
public void run() {
disposeFindbar();
parent.layout();
target.endSession();
}
};
final Runnable findNext = new Runnable() {
@Override
public void run() {
// find next
String toFind = findText.getText();
int index = viewer.getSelectedRange().x + 1;
int newIndex = ((IFindReplaceTargetExtension3)target).findAndSelect(index, toFind, true, false, false, false);
if (newIndex == -1) {
// not found
findText.setBackground(failedSearchColor);
} else {
findText.setBackground(successfulSearchColor);
findIndex = newIndex;
}
}
};
final Runnable findPrev = new Runnable() {
@Override
public void run() {
// find previous
String toFind = findText.getText();
int newIndex = ((IFindReplaceTargetExtension3)target).findAndSelect(findIndex, toFind, false, false, false, false);
if (newIndex == -1) {
// not found
findText.setBackground(failedSearchColor);
} else {
findText.setBackground(successfulSearchColor);
findIndex = newIndex;
}
}
};
closeItem.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
closeBar.run();
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {}
});
nextfindItem.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
findNext.run();
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {}
});
prevfindItem.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
findPrev.run();
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {}
});
findText.addKeyListener(new KeyListener() {
@Override
public void keyReleased(KeyEvent e) {}
@Override
public void keyPressed(KeyEvent e) {
if (e.keyCode == SWT.ESC) {
closeBar.run();
} else if (e.keyCode == SWT.ARROW_UP) {
findPrev.run();
} else if (e.keyCode == SWT.ARROW_DOWN) {
findNext.run();
}
}
});
findText.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
// text to find has changed, search again
// from begin of session
String toFind = findText.getText();
int newIndex = ((IFindReplaceTargetExtension3)target).findAndSelect(firstFindIndex, toFind, true, false, false, false);
if (newIndex == -1) {
// not found
findText.setBackground(failedSearchColor);
} else {
findText.setBackground(successfulSearchColor);
}
}
});
parent.layout();
findText.setFocus();
}
// ActionFactory.IWorkbenchAction a = ActionFactory.FIND.create(getViewSite().getWorkbenchWindow());
private void disposeFindbar() {
if (findbar != null && !findbar.isDisposed())
findbar.dispose();
findbar = null;
viewer.getControl().setFocus();
}
/**
* Filter the content of the currently watched editor using
* the regular expression in the text box.
*
* The resulting text is shown in the text viewer.
*/
private void doGrep() {
String[] rxList = new String[regexEntries.size()];
for (int rxi = 0; rxi < rxList.length; rxi++) {
// TODO: array for last regex too...
lastRegex = regexEntries.get(rxi).getRegexpText();
rxList[rxi] = lastRegex;
}
updateTarget();
if (target == null)
return;
grepContext = submitGrep(target, rxList, csAction.isChecked(), hmAction.isChecked());
Document document = new Document(grepContext.getText());
viewer.setDocument(document);
viewer.getControl().setToolTipText("source: " + target.getTitle());
// disable the text viewer, will be enabled again when the grep is done
viewer.getControl().setEnabled(false);
}
/*
private int computeRangeCount() {
int j = 0;
AbstractDocument document = (AbstractDocument) viewer.getDocument();
int lines = document.getNumberOfLines();
try {
int totalMatches = grepContext.getNumberOfMatches();
// 1 range for each highlight (background), 1 range for each line (foreground)
// int[] ranges = new int[totalMatches*2 + lines*2];
// StyleRange[] styles = new StyleRange[totalMatches + lines];
// int[] ranges = new int[lines*2];
// StyleRange[] styles = new StyleRange[lines];
int[] ranges = new int[totalMatches*2];
StyleRange[] styles = new StyleRange[totalMatches];
// this same style range object is used for all matches
// to save some memory, the real ranges are
// in the integer arrays
// StyleRange[] lineForegroundStyles = new StyleRange[regexEntries.size()];
// for (int i = 0; i < lineForegroundStyles.length; i++) {
// lineForegroundStyles[i] = new StyleRange();
// lineForegroundStyles[i].foreground = regexEntries.get(i).getRegexColor();
// }
// StyleRange[] matchHighLightStyles = new StyleRange[regexEntries.size()];
// for (int i = 0; i < matchHighLightStyles.length; i++) {
// matchHighLightStyles[i] = new StyleRange();
// matchHighLightStyles[i].foreground = regexEntries.get(i).getRegexColor();
// matchHighLightStyles[i].background = highlightColor;
// }
// this is the range used for all match highlights (background)
StyleRange highlightStyle = new StyleRange();
highlightStyle.background = highlightColor;
for (int i = 0 ; i < lines ; i++) {
// ranges[j*2] = document.getLineOffset(i);
// ranges[j*2 + 1] = document.getLineLength(i);
// styles[j++] = lineForegroundStyles[grepContext.getColorForGrepLine(i)];
int nm = grepContext.getNumberOfMatchesForGrepLine(i);
int lineOffset = document.getLineOffset(i);
int grepBegin = grepContext.getMatchBeginForGrepLine(i, 0);
if (grepBegin > 0) {
// there is a segment
}
int endOfLastMatch = 0;
for (int k = 0 ; k < nm ; k++) {
grepBegin = grepContext.getMatchBeginForGrepLine(i, k);
if (grepBegin > endOfLastMatch) {
// add the range for the text here
}
ranges[j*2] = document.getLineOffset(i) + grepBegin;
ranges[j*2 + 1] = grepContext.getMatchEndForGrepLine(i, k) - grepContext.getMatchBeginForGrepLine(i, k);
styles[j++] = highlightStyle;
}
}
viewer.getTextWidget().setStyleRanges(ranges, styles);
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return j;
}
*/
/**
* Set the highlight colour for match regions in the text viewer.
*
* Ranges are picked up from the current grepContext (if no context
* is currently present, the method returns).
*
*/
private void updateHighlightRanges() {
// need a grep context here
if (grepContext == null)
return;
AbstractDocument document = (AbstractDocument) viewer.getDocument();
// return immediately if the document is empty, as it will still
// report 1 line of content below and this can lead to invalid
// access to the grep data structures
if (document.getLength() == 0)
return;
int lines = document.getNumberOfLines();
int j = 0;
int totalMatches;
try {
totalMatches = grepContext.getNumberOfMatches();
// 1 range for each highlight (background), 1 range for each line (foreground)
// int[] ranges = new int[totalMatches*2 + lines*2];
// StyleRange[] styles = new StyleRange[totalMatches + lines];
// int[] ranges = new int[lines*2];
// StyleRange[] styles = new StyleRange[lines];
int[] ranges = new int[totalMatches*2];
StyleRange[] styles = new StyleRange[totalMatches];
// this same style range object is used for all matches
// to save some memory, the real ranges are
// in the integer arrays
// StyleRange[] lineForegroundStyles = new StyleRange[regexEntries.size()];
// for (int i = 0; i < lineForegroundStyles.length; i++) {
// lineForegroundStyles[i] = new StyleRange();
// lineForegroundStyles[i].foreground = regexEntries.get(i).getRegexColor();
// }
// StyleRange[] matchHighLightStyles = new StyleRange[regexEntries.size()];
// for (int i = 0; i < matchHighLightStyles.length; i++) {
// matchHighLightStyles[i] = new StyleRange();
// matchHighLightStyles[i].foreground = regexEntries.get(i).getRegexColor();
// matchHighLightStyles[i].background = highlightColor;
// }
// this is the range used for all match highlights (background)
StyleRange highlightStyle = new StyleRange();
highlightStyle.background = highlightColor;
for (int i = 0 ; i < lines ; i++) {
// ranges[j*2] = document.getLineOffset(i);
// ranges[j*2 + 1] = document.getLineLength(i);
// styles[j++] = lineForegroundStyles[grepContext.getColorForGrepLine(i)];
int nm = grepContext.getNumberOfMatchesForGrepLine(i);
for (int k = 0 ; k < nm ; k++) {
ranges[j*2] = document.getLineOffset(i) + grepContext.getMatchBeginForGrepLine(i, k);
ranges[j*2 + 1] = grepContext.getMatchEndForGrepLine(i, k) - grepContext.getMatchBeginForGrepLine(i, k);
styles[j++] = highlightStyle;
}
}
viewer.getTextWidget().setStyleRanges(ranges, styles);
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void updateTarget() {
IWorkbenchWindow window = getViewSite().getWorkbenchWindow();
IEditorPart activeEditor = window.getActivePage().getActiveEditor();
// first check if the editor can adapt to a grep target
IGrepTarget newTarget = (IGrepTarget) activeEditor.getAdapter(IGrepTarget.class);
// if not, and it's a text editor, use the default implementation
if (newTarget == null && activeEditor instanceof AbstractTextEditor)
newTarget = new DocumentGrepTarget((AbstractTextEditor) activeEditor);
if (newTarget != null) {
target = newTarget;
targetPart = activeEditor;
}
}
@Override
public void setFocus() {
regexEntries.get(0).setFocus();
}
@Override
public void saveState(IMemento memento) {
super.saveState(memento);
memento.putString(KEY_GREPREGEX, lastRegex);
memento.putBoolean(KEY_CASESENSITIVE, csAction.isChecked() );
memento.putBoolean(KEY_HIGHLIGHTMULTIPLE, hmAction.isChecked() );
String history = "";
for (String helement : regexHistory) {
history += helement + "\n";
}
memento.putString(KEY_REGEX_HISTORY, history);
Color color = highlightColor;
int defaultColor = color.getRed() << 16 | color.getGreen() << 8 | color.getBlue();
memento.putInteger(KEY_DEFAULT_COLOR, defaultColor);
}
@Override
public void init(IViewSite site, IMemento memento) throws PartInitException {
super.init(site, memento);
// default colour is yellow
int highlightCol = 0x00FFFF00;
// check if there is a value saved in the memento
if (memento != null) {
lastRegex = memento.getString(KEY_GREPREGEX);
Boolean cs = memento.getBoolean(KEY_CASESENSITIVE);
initialCaseSensitivity = cs == null ? false : cs;
Boolean hm = memento.getBoolean(KEY_HIGHLIGHTMULTIPLE);
initialCaseSensitivity = hm == null ? false : hm;
String history = memento.getString(KEY_REGEX_HISTORY);
if (history != null) {
String[] harray = history.split("\n");
for (String helem : harray) {
if (!helem.isEmpty())
regexHistory.add(helem);
}
}
Integer defaultColor = memento.getInteger(KEY_DEFAULT_COLOR);
if (defaultColor != null)
highlightCol = defaultColor;
}
highlightColor = new Color(site.getShell().getDisplay(),
(highlightCol >> 16) & 0x00FF,
(highlightCol >> 8) & 0x00FF,
(highlightCol) & 0x00FF);
// to make things simpler do not allow a null
// regular expression
if (lastRegex == null)
lastRegex = "";
}
@Override
public void dispose() {
if (highlightColor != null) {
highlightColor.dispose();
highlightColor = null;
}
if (failedSearchColor != null) {
failedSearchColor.dispose();
failedSearchColor = null;
}
if (cursorLineColor != null) {
cursorLineColor.dispose();
cursorLineColor = null;
}
super.dispose();
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public Object getAdapter(Class adapter) {
Object object = super.getAdapter(adapter);
if (object == null && adapter.equals(IFindReplaceTarget.class)) {
object = viewer.getFindReplaceTarget();
}
return object;
}
public void setGrepRegularExpression(String text) {
regexEntries.get(0).setRegexpText(text, true);
}
public IDocument getGrepContentAsDocument() {
return viewer.getDocument();
}
public String getOriginalForCurrentSelection() {
String text = "";
Point selectedRange = viewer.getSelectedRange();
IDocument document = viewer.getDocument();
if (selectedRange == null || document == null || isSelectionEmpty())
return text;
try {
final int selectedRangeStart = selectedRange.x;
final int selectedRangeEnd = selectedRangeStart + selectedRange.y;
final int startLine = document.getLineOfOffset(selectedRangeStart);
final int endLine = document.getLineOfOffset(selectedRangeEnd);
final int origStartLine = grepContext.getOriginalLine(startLine);
final int origEndLine = grepContext.getOriginalLine(endLine);
final int startDelta = selectedRangeStart - document.getLineOffset(startLine);
final int endDelta = document.getLineOffset(endLine) + document.getLineLength(endLine) - selectedRangeEnd;
text = grepContext.getTarget().getTextBetweenLines(origStartLine, origEndLine, startDelta, endDelta);
// if (startDelta > 0 || endDelta > 0) {
// System.out.printf("cropping text: startDelta=%d endDelta=%d\n", startDelta, endDelta);
// text = text.substring(startDelta, text.length() - endDelta);
// }
} catch (BadLocationException e) {
// TODO: log
}
return text;
}
public boolean hasGrepResult() {
if (viewer == null || grepContext == null)
return false;
return true;
}
public boolean isSelectionEmpty() {
if (viewer == null || grepContext == null)
return true;
Point range = viewer.getSelectedRange();
return range.x < 0 || range.y <= 0;
}
public Point getSelectedRange() {
return viewer.getSelectedRange();
}
}