package gui; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.regex.PatternSyntaxException; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Point; 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.Dialog; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.Scale; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import util.WordIndexer; import util.WordIndexerWrapper; /** * @author Juan Erasmo Gómez */ public class FindReplaceDialog extends Dialog { private static final String FIND_HIGHLIGHT_DATA = "find"; private static final Color FIND_HIGHLIGHT_COLOR = Display.getCurrent().getSystemColor(SWT.COLOR_YELLOW); private StyledText text; private Button wholeWords; private Button matchCase; private Button highlightAll; private Button regex; private Button directionForward; private Button directionBackward; private Text findText; private Text replaceText; private Label find; private ListIterator<WordIndexerWrapper> resultsIterator; public FindReplaceDialog(Shell parent, StyledText text) { super(parent, SWT.DIALOG_TRIM); this.text = text; } public void open() { final Shell shell = new Shell(getParent(), getStyle()); shell.setText("Find/replace"); createContents(shell); shell.pack(); shell.open(); Listener researcherListener = new Listener() { public void handleEvent(Event event) { if (resultsIterator != null) resultsIterator = null; } }; text.addListener(SWT.Modify, researcherListener); Display display = getParent().getDisplay(); while (!shell.isDisposed()) if (!display.readAndDispatch()) display.sleep(); text.removeListener(SWT.Modify, researcherListener); clearSearchResults(); } private void createContents(final Shell shell) { shell.setLayout(new GridLayout(6, false)); find = new Label(shell, SWT.NONE); find.setText("Find:"); find.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); findText = new Text(shell, SWT.BORDER); findText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1)); findText.addListener(SWT.Modify, new Listener() { public void handleEvent(Event event) { clearSearchResults(); } }); Button findNextButton = new Button(shell, SWT.PUSH); findNextButton.setText("Find next"); findNextButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1)); shell.setDefaultButton(findNextButton); findNextButton.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { findNext(); } }); Label replace = new Label(shell, SWT.NONE); replace.setText("Replace:"); replace.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); replaceText = new Text(shell, SWT.BORDER); replaceText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1)); Button replaceButton = new Button(shell, SWT.PUSH); replaceButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1)); replaceButton.setText("Replace"); replaceButton.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { replace(); } }); new Label(shell, SWT.NONE).setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 4, 1)); Button replaceFind = new Button(shell, SWT.PUSH); replaceFind.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1)); replaceFind.setText("Replace/Find"); replaceFind.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { replaceFind(); } }); new Label(shell, SWT.NONE).setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 4, 1)); Button replaceAllButton = new Button(shell, SWT.PUSH); replaceAllButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1)); replaceAllButton.setText("Replace All"); replaceAllButton.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { replaceAll(); } }); // Options renderOptions(shell); renderDirection(shell); renderTransparency(shell); // Close button new Label(shell, SWT.NONE).setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 4, 1)); Button close = new Button(shell, SWT.PUSH); close.setText("Close"); close.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1)); close.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { shell.dispose(); } }); // Add the new search listeners Listener newSearchListener = new Listener() { @Override public void handleEvent(Event event) { restartSearch(); } }; findText.addListener(SWT.Modify, newSearchListener); highlightAll.addListener(SWT.Selection, newSearchListener); wholeWords.addListener(SWT.Selection, newSearchListener); } private boolean replaceFind() { replace(); return findNext(); } private void renderTransparency(final Shell shell) { Group group = new Group(shell, SWT.NONE); group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 6, 1)); group.setLayout(new GridLayout(1, false)); group.setText("Transparency"); final Scale transparencySlider = new Scale(group, SWT.HORIZONTAL); transparencySlider.setMinimum(20); transparencySlider.setMaximum(100); transparencySlider.setPageIncrement(90); transparencySlider.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); transparencySlider.setSelection(100); transparencySlider.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { shell.setAlpha(255 * transparencySlider.getSelection() / 100); } }); } private void renderDirection(Shell shell) { Group group = new Group(shell, SWT.NONE); group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1)); group.setLayout(new GridLayout(1, false)); group.setText("Direction"); directionForward = new Button(group, SWT.RADIO); directionForward.setText("Fordward"); directionBackward = new Button(group, SWT.RADIO); directionBackward.setText("Backwards"); directionForward.setSelection(true); } private void renderOptions(Shell shell) { Group group = new Group(shell, SWT.NONE); group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1)); group.setLayout(new GridLayout(1, false)); group.setText("Options"); wholeWords = createOption("Whole word", group); matchCase = createOption("Case sensitive", group); highlightAll = createOption("Highlight all", group); regex = createOption("Regular expression", group); highlightAll.setSelection(true); } private Button createOption(String option, Composite parent) { GridData optionsLD = new GridData(SWT.FILL, SWT.CENTER, false, false); Button opt = new Button(parent, SWT.CHECK); opt.setText(option); opt.setLayoutData(optionsLD); return opt; } private List<WordIndexerWrapper> findAll(String keyWord, boolean matchCase, boolean wholeWord, boolean regex, Point searchBounds) { String wholeText = text.getText(); if (!matchCase) { keyWord = keyWord.toLowerCase(); wholeText = wholeText.toLowerCase(); } if (!regex) { String temp = ""; for (int i = 0; i < keyWord.length(); i++) temp += ("[" + keyWord.charAt(i) + "]"); keyWord = temp; } if (wholeWord) keyWord = "\\b" + keyWord + "\\b"; System.out.println("looking for: " + keyWord); WordIndexer finder = new WordIndexer(wholeText); List<WordIndexerWrapper> indexes = new LinkedList<WordIndexerWrapper>(); try { indexes = finder.findIndexesForKeyword(keyWord, searchBounds.x, searchBounds.y); } catch (PatternSyntaxException e) { MessageBox diag = new MessageBox(Display.getCurrent().getActiveShell(), SWT.APPLICATION_MODAL | SWT.ICON_ERROR | SWT.OK); diag.setMessage("Regular expression error.\n\n" + e.getMessage()); diag.open(); } return indexes; } private boolean findNext() { if (text.getCharCount() == 0) return false; if (resultsIterator == null && !initSearch()) return false; if (directionForward.getSelection() && resultsIterator.hasNext()) { WordIndexerWrapper next = resultsIterator.next(); text.setSelection(next.start, next.end); return true; } else if (directionBackward.getSelection() && resultsIterator.hasPrevious()) { WordIndexerWrapper previous = resultsIterator.previous(); text.setSelection(previous.start, previous.end); return true; } else { Display.getCurrent().beep(); return false; } } private boolean initSearch() { text.setSelection(text.getSelection().x); Point searchBounds = new Point(0, text.getCharCount() - 1); if (searchBounds.x >= searchBounds.y) return false; List<WordIndexerWrapper> indexes = findAll(findText.getText(), matchCase.getSelection(), wholeWords.getSelection(), regex.getSelection(), searchBounds); resultsIterator = indexes.listIterator(); // Highlight the findings while (highlightAll.getSelection() && resultsIterator.hasNext()) { WordIndexerWrapper i = resultsIterator.next(); StyleRange highlight = new StyleRange(i.start, i.end - i.start, null, FIND_HIGHLIGHT_COLOR); highlight.data = new ReversibleStyleWrapper(FIND_HIGHLIGHT_DATA, text.getStyleRanges(i.start, i.end - i.start)); text.setStyleRange(highlight); } // Reset the iterator position resultsIterator = indexes.listIterator(); while (resultsIterator.hasNext()) { WordIndexerWrapper i = resultsIterator.next(); if (i.start >= text.getSelection().x) { resultsIterator.previous(); break; } } return true; } private int replaceAll() { int num = 0; while (replaceFind()) num++; return num; } private boolean replace() { Point x = text.getSelection(); if (!text.getSelectionText().isEmpty()) { text.replaceTextRange(text.getSelection().x, text.getSelection().y - text.getSelection().x, replaceText.getText()); text.setSelection(x.x + replaceText.getText().length()); return true; } return false; } private void clearSearchResults() { StyleRange[] styles = text.getStyleRanges(); for (int i = 0; i < styles.length; i++) if (styles[i].data instanceof ReversibleStyleWrapper && ((ReversibleStyleWrapper) styles[i].data).id.equals(FIND_HIGHLIGHT_DATA)) { text.replaceStyleRanges(styles[i].start, styles[i].length, ((ReversibleStyleWrapper) styles[i].data).ranges); i = 0; styles = text.getStyleRanges(); } } private void restartSearch() { resultsIterator = null; clearSearchResults(); } }