/*******************************************************************************
* Copyright (c) 2011 Tasktop Technologies.
* 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:
* David Green - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.internal.wikitext.ui.editor;
import java.util.Stack;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IExecutionListener;
import org.eclipse.core.commands.NotHandledException;
import org.eclipse.jface.text.IFindReplaceTarget;
import org.eclipse.jface.text.IFindReplaceTargetExtension;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextListener;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.ITextViewerExtension;
import org.eclipse.jface.text.TextEvent;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.texteditor.IWorkbenchActionDefinitionIds;
/**
* Implementation based on {@link org.eclipse.ui.texteditor.IncrementalFindTarget}
*
* @author David Green
*/
public class FindAndReplaceTarget implements IFindReplaceTarget, IFindReplaceTargetExtension, VerifyKeyListener,
MouseListener, FocusListener, ISelectionChangedListener, ITextListener, IExecutionListener {
private final ITextViewer textViewer;
private final IFindReplaceTarget findReplaceTarget;
private final IFindReplaceTargetExtension findReplaceTargetExtension;
private boolean installed;
private boolean searching;
private final Stack<Result> state = new Stack<>();
private String findString = ""; //$NON-NLS-1$
private int index = 0;
private boolean findHit = true;
private String previousFindString = ""; //$NON-NLS-1$
private static final class Result {
private final Point selection;
private final String findString;
private final int index;
private final boolean findHit;
public Result(Point selection, String findString, int index, boolean findHit) {
this.findString = findString;
this.index = index;
this.selection = new Point(selection.x, selection.y);
this.findHit = findHit;
}
@Override
public String toString() {
return "Result [selection=" + selection + ", findString=" + findString + ", index=" + index + ", findHit=" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ findHit + "]"; //$NON-NLS-1$
}
}
public FindAndReplaceTarget(ITextViewer textViewer) {
this.textViewer = textViewer;
findReplaceTarget = textViewer.getFindReplaceTarget();
if (findReplaceTarget instanceof IFindReplaceTargetExtension) {
findReplaceTargetExtension = (IFindReplaceTargetExtension) findReplaceTarget;
} else {
findReplaceTargetExtension = null;
}
}
public boolean canPerformFind() {
return findReplaceTarget.canPerformFind();
}
public int findAndSelect(int widgetOffset, String findString, boolean searchForward, boolean caseSensitive,
boolean wholeWord) {
return findReplaceTarget.findAndSelect(widgetOffset, findString, searchForward, caseSensitive, wholeWord);
}
public Point getSelection() {
return findReplaceTarget.getSelection();
}
public String getSelectionText() {
return findReplaceTarget.getSelectionText();
}
public boolean isEditable() {
return findReplaceTarget.isEditable();
}
public void replaceSelection(String text) {
findReplaceTarget.replaceSelection(text);
}
public void beginSession() {
if (installed) {
next();
return;
}
clearState();
index = 0;
findHit = true;
findString = ""; //$NON-NLS-1$
StyledText textWidget = textViewer.getTextWidget();
if (textWidget != null && !textWidget.isDisposed()) {
index = textWidget.getCaretOffset();
textWidget.setSelection(index);
}
install();
if (findReplaceTargetExtension != null) {
findReplaceTargetExtension.beginSession();
}
}
private void next() {
if (findHit) {
saveState();
}
repeatSearch();
}
private void saveState() {
Result newState = new Result(new Point(index, index + findString.length()), findString, index, findHit);
state.push(newState);
}
private void clearState() {
state.clear();
}
private void restoreState() {
StyledText textWidget = textViewer.getTextWidget();
if (textWidget == null || textWidget.isDisposed()) {
return;
}
Result result = null;
if (!state.isEmpty()) {
result = state.pop();
}
if (result == null) {
textWidget.getDisplay().beep();
return;
}
findString = result.findString;
index = result.index;
findHit = result.findHit;
textWidget.setSelection(result.selection);
textWidget.showSelection();
}
private void addSearchCharacter(char character) {
findString += character;
doFind();
}
private void repeatSearch() {
if (findString.length() == 0) {
findString = previousFindString;
}
if (findString.length() == 0) {
findHit = true;
return;
}
// check for wrap search
if (!findHit) {
index = 0;
} else {
StyledText textWidget = textViewer.getTextWidget();
index = textWidget.getCaretOffset();
}
doFind();
}
private void doFind() {
boolean hasUpperCase = false;
for (int x = 0; x < findString.length(); ++x) {
char c = findString.charAt(x);
if (Character.isUpperCase(c) && Character.toLowerCase(c) != c) {
hasUpperCase = true;
break;
}
}
searching = true;
StyledText textWidget = textViewer.getTextWidget();
textWidget.setRedraw(false);
int foundIndex = findReplaceTarget.findAndSelect(index, findString, true, hasUpperCase, false);
textWidget.setRedraw(true);
boolean findHit = foundIndex >= 0;
if (findHit) {
index = foundIndex;
} else if (this.findHit) {
textWidget.getDisplay().beep();
}
this.findHit = findHit;
searching = false;
}
public void endSession() {
if (findReplaceTargetExtension != null) {
findReplaceTargetExtension.endSession();
}
}
public IRegion getScope() {
if (findReplaceTargetExtension != null) {
return findReplaceTargetExtension.getScope();
}
return null;
}
public void setScope(IRegion scope) {
if (findReplaceTargetExtension != null) {
findReplaceTargetExtension.setScope(scope);
}
}
public Point getLineSelection() {
if (findReplaceTargetExtension != null) {
return findReplaceTargetExtension.getLineSelection();
}
return null;
}
public void setSelection(int offset, int length) {
if (findReplaceTargetExtension != null) {
findReplaceTargetExtension.setSelection(offset, length);
}
}
public void setScopeHighlightColor(Color color) {
if (findReplaceTargetExtension != null) {
findReplaceTargetExtension.setScopeHighlightColor(color);
}
}
public void setReplaceAllMode(boolean replaceAll) {
if (findReplaceTargetExtension != null) {
findReplaceTargetExtension.setReplaceAllMode(replaceAll);
}
}
private void install() {
if (installed) {
return;
}
StyledText textWidget = textViewer.getTextWidget();
if (textWidget == null) {
return;
}
textViewer.addTextListener(this);
textWidget.addMouseListener(this);
textWidget.addFocusListener(this);
ISelectionProvider selectionProvider = textViewer.getSelectionProvider();
if (selectionProvider != null) {
selectionProvider.addSelectionChangedListener(this);
}
if (textViewer instanceof ITextViewerExtension) {
((ITextViewerExtension) textViewer).prependVerifyKeyListener(this);
} else {
textWidget.addVerifyKeyListener(this);
}
ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getAdapter(ICommandService.class);
if (commandService != null) {
commandService.addExecutionListener(this);
}
installed = true;
}
private void stop() {
if (findString.length() > 0) {
previousFindString = findString;
findString = ""; //$NON-NLS-1$
}
index = 0;
findHit = true;
clearState();
uninstall();
}
private void uninstall() {
if (!installed) {
return;
}
StyledText textWidget = textViewer.getTextWidget();
if (textWidget == null) {
return;
}
textViewer.removeTextListener(this);
textWidget.removeMouseListener(this);
textWidget.removeFocusListener(this);
ISelectionProvider selectionProvider = textViewer.getSelectionProvider();
if (selectionProvider != null) {
selectionProvider.removeSelectionChangedListener(this);
}
if (textViewer instanceof ITextViewerExtension) {
((ITextViewerExtension) textViewer).removeVerifyKeyListener(this);
} else {
textWidget.removeVerifyKeyListener(this);
}
ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getAdapter(ICommandService.class);
if (commandService != null) {
commandService.removeExecutionListener(this);
}
installed = false;
}
public void notHandled(String commandId, NotHandledException exception) {
// ignore
}
public void postExecuteFailure(String commandId, ExecutionException exception) {
// ignore
}
public void postExecuteSuccess(String commandId, Object returnValue) {
// ignore
}
public void preExecute(String commandId, ExecutionEvent event) {
if (!IWorkbenchActionDefinitionIds.FIND_INCREMENTAL.equals(commandId)) {
stop();
}
}
public void textChanged(TextEvent event) {
if (event.getDocumentEvent() != null) {
stop();
}
}
public void focusGained(FocusEvent e) {
stop();
}
public void focusLost(FocusEvent e) {
stop();
}
public void mouseDoubleClick(MouseEvent e) {
stop();
}
public void mouseDown(MouseEvent e) {
stop();
}
public void mouseUp(MouseEvent e) {
stop();
}
public void verifyKey(VerifyEvent event) {
if (!event.doit) {
return;
}
if (event.character == 0) {
switch (event.keyCode) {
case SWT.ARROW_LEFT:
case SWT.ARROW_RIGHT:
case SWT.ARROW_UP:
case SWT.HOME:
case SWT.END:
case SWT.PAGE_DOWN:
case SWT.PAGE_UP:
stop();
break;
case SWT.ARROW_DOWN:
next();
event.doit = false;
break;
}
} else {
switch (event.character) {
case SWT.ESC:
case SWT.CR:
stop();
event.doit = false;
break;
case SWT.BS:
case SWT.DEL:
restoreState();
event.doit = false;
break;
default:
if (event.stateMask == 0 || event.stateMask == SWT.SHIFT) {
saveState();
addSearchCharacter(event.character);
event.doit = false;
}
}
}
}
public void selectionChanged(SelectionChangedEvent event) {
if (!searching) {
stop();
}
}
}