Source code: com/port80/eclipse/jdt/actions/RegexFindDialog.java
1 package com.port80.eclipse.jdt.actions;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 import org.apache.oro.text.perl.Perl5Util;
7 import org.eclipse.jface.dialogs.Dialog;
8 import org.eclipse.jface.viewers.AbstractTreeViewer;
9 import org.eclipse.jface.viewers.IBaseLabelProvider;
10 import org.eclipse.jface.viewers.ILabelProvider;
11 import org.eclipse.jface.viewers.ISelection;
12 import org.eclipse.jface.viewers.IStructuredSelection;
13 import org.eclipse.jface.viewers.ITreeContentProvider;
14 import org.eclipse.jface.viewers.LabelProvider;
15 import org.eclipse.jface.viewers.StructuredSelection;
16 import org.eclipse.jface.viewers.TableViewer;
17 import org.eclipse.jface.viewers.Viewer;
18 import org.eclipse.jface.viewers.ViewerSorter;
19 import org.eclipse.swt.SWT;
20 import org.eclipse.swt.events.DisposeEvent;
21 import org.eclipse.swt.events.DisposeListener;
22 import org.eclipse.swt.events.ModifyEvent;
23 import org.eclipse.swt.events.ModifyListener;
24 import org.eclipse.swt.events.MouseAdapter;
25 import org.eclipse.swt.events.MouseEvent;
26 import org.eclipse.swt.events.SelectionAdapter;
27 import org.eclipse.swt.events.SelectionEvent;
28 import org.eclipse.swt.graphics.Color;
29 import org.eclipse.swt.graphics.Font;
30 import org.eclipse.swt.graphics.FontMetrics;
31 import org.eclipse.swt.graphics.GC;
32 import org.eclipse.swt.graphics.Point;
33 import org.eclipse.swt.layout.GridData;
34 import org.eclipse.swt.widgets.Button;
35 import org.eclipse.swt.widgets.Combo;
36 import org.eclipse.swt.widgets.Composite;
37 import org.eclipse.swt.widgets.Control;
38 import org.eclipse.swt.widgets.Display;
39 import org.eclipse.swt.widgets.Label;
40 import org.eclipse.swt.widgets.Shell;
41 import org.eclipse.swt.widgets.Table;
42 import org.eclipse.swt.widgets.TableItem;
43
44 import com.port80.eclipse.jdt.JdtPlugin;
45 import com.port80.eclipse.jdt.ThemeManager;
46 import com.port80.eclipse.util.UIUtil;
47 import com.port80.util.struct.IntList;
48
49 /**
50 * @author chrisl
51 *
52 * To change this generated comment edit the template variable "typecomment":
53 * Window>Preferences>Java>Templates.
54 * To enable and disable the creation of type comments go to
55 * Window>Preferences>Java>Code Generation.
56 */
57 public class RegexFindDialog extends Dialog {
58
59 ////////////////////////////////////////////////////////////////////////
60
61 private static final String NAME = "RegexFindDialog";
62 private static final boolean DEBUG = false;
63 private static final int MAX_PERSISTENT = 16;
64 private static final int STATUS_PERIOD = 0x01ff;
65 private static final int WIDTH=32;
66
67 ////////////////////////////////////////////////////////////////////////
68
69 private static RegexFindDialog fDefault;
70 private static Point fWindowPosition;
71 private static List fPatterns = new ArrayList();
72 private static Perl5Util fReEngine;
73
74 private Viewer fViewer;
75 private List fElements;
76 private String[] fLabels;
77 private LabelProvider fLabelProvider;
78 private int fStart;
79 private int fFound;
80 private int fAnchor;
81 //
82 private boolean fAbort;
83 private boolean fLoaded;
84 private boolean fError;
85 //
86 private Combo fPattern;
87 private Button fButtonFind;
88 private Button fButtonFindBackward;
89 private Button fButtonSelectAll;
90 private Button fCaseSensitive;
91 private Label fStatus;
92 private Color fColorRed;
93 private Color fColorBlack;
94
95 ////////////////////////////////////////////////////////////////////////
96
97 public static RegexFindDialog getDefault(Shell shell, AbstractTreeViewer viewer) {
98 if (fDefault == null || fDefault.getShell() == null || fDefault.getShell().isDisposed()) {
99 fDefault = new RegexFindDialog(shell);
100 fDefault.init(viewer);
101 } else
102 fDefault.init(viewer);
103 return fDefault;
104 }
105
106 public static RegexFindDialog getDefault(Shell shell, TableViewer viewer) {
107 if (fDefault == null || fDefault.getShell() == null || fDefault.getShell().isDisposed()) {
108 fDefault = new RegexFindDialog(shell);
109 fDefault.init(viewer);
110 } else
111 fDefault.init(viewer);
112 return fDefault;
113 }
114
115 ////////////////////////////////////////////////////////////////////////
116
117 /**
118 * Constructor for RegexReplaceDialog.
119 * @param parentShell
120 */
121 private RegexFindDialog(Shell parentShell) {
122 super(parentShell);
123 //
124 setShellStyle(SWT.DIALOG_TRIM);
125 // setBlockOnOpen(true);
126 }
127
128 ////////////////////////////////////////////////////////////////////////
129
130 private void init(Viewer viewer) {
131 fViewer = viewer;
132 fReEngine = new Perl5Util();
133 ThemeManager manager = JdtPlugin.getThemeManager();
134 fColorRed = manager.getColor("0xff0000");
135 fColorBlack = manager.getColor("0x000000");
136 }
137
138 void dispose() {
139 fReEngine = null;
140 fElements = null;
141 fLabels = null;
142 }
143
144 ////////////////////////////////////////////////////////////////////////
145
146 void loadCompleted() {
147 if (fAbort) {
148 fError = true;
149 fLoaded = true;
150 actionClose();
151 return;
152 }
153 //
154 if (fElements.size() == 0)
155 fError = true;
156 String message;
157 if (!fError) {
158 fStart = findSelectedElement(fViewer);
159 if (fStart >= 0) {
160 fAnchor = fStart;
161 fFound = fStart;
162 } else {
163 fStart = 0;
164 fAnchor = 0;
165 fFound = -1;
166 }
167 if (DEBUG)
168 System.err.println(
169 NAME
170 + ".init(AbstractTreeViewer): fElements.size()="
171 + fElements.size()
172 + ", fStart="
173 + fStart);
174 message = "OK: " + fElements.size() + " elements loaded";
175 fStatus.setForeground(fColorBlack);
176 fStatus.setText(message);
177 fPattern.setEnabled(true);
178 setEnableActions(fPattern.getText().length() > 0);
179 } else {
180 message = "FAILED: " + fElements.size() + " elements loaded";
181 fStatus.setForeground(fColorRed);
182 fStatus.setText(message);
183 setEnableActions(false);
184 }
185 fLoaded = true;
186 }
187
188 private int findSelectedElement(Viewer viewer) {
189 ISelection s = viewer.getSelection();
190 if (s instanceof IStructuredSelection) {
191 IStructuredSelection selection = (IStructuredSelection) s;
192 if (selection.size() > 0) {
193 Object a = selection.getFirstElement();
194 for (int i = 0; i < fElements.size(); ++i) {
195 if (a.equals(fElements.get(i)))
196 return i;
197 }
198 }
199 }
200 return -1;
201 }
202
203 ////////////////////////////////////////////////////////////////////////
204
205 /**
206 * @see org.eclipse.jface.window.Window#constrainShellSize()
207 */
208 protected void constrainShellSize() {
209 Shell shell = getShell();
210 if (fWindowPosition != null) {
211 System.err.println("Last Window location=" + fWindowPosition);
212 shell.setLocation(fWindowPosition);
213 } else {
214 fWindowPosition = shell.getLocation();
215 shell.setLocation(fWindowPosition.x + 200, fWindowPosition.y - 100);
216 }
217 super.constrainShellSize();
218 }
219
220 /**
221 * @see org.eclipse.jface.window.Window#createContents(Composite)
222 */
223 protected Control createContents(Composite parent) {
224 Composite top = UIUtil.createBox(parent, 1, 1, true, true, 1, 10, 10, 0, 5, null);
225 createDialogArea(top);
226 createButtonBar(top);
227 setEnableActions(false);
228 return top;
229 }
230
231 protected Control createDialogArea(Composite parent) {
232 ThemeManager manager = JdtPlugin.getThemeManager();
233 Font boldfont = manager.getFont("Bold");
234 Color lightcolor = manager.getColor("LightBG");
235 String title = JdtPlugin.getString("RegexFind.title");
236 String str_search = JdtPlugin.getString("RegexFind.search") + ": ";
237 getShell().setText(title);
238 getShell().addDisposeListener(new DisposeListener() {
239 public void widgetDisposed(DisposeEvent e) {
240 if (DEBUG)
241 System.err.println(NAME + ": shell disposed.");
242 dispose();
243 }
244 });
245 Composite top = UIUtil.createBox(parent, 1, 1, true, true, 10, 0, 0, 5, 5, true, null);
246 Label label =
247 UIUtil.createGridLabel(
248 top,
249 SWT.RIGHT,
250 str_search,
251 null,
252 null,
253 boldfont,
254 2,
255 3,
256 GridData.FILL,
257 GridData.CENTER,
258 false,
259 false);
260 GC gc = new GC(label);
261 gc.setFont(label.getFont());
262 FontMetrics fm = gc.getFontMetrics();
263 ((GridData) label.getLayoutData()).widthHint = fm.getAverageCharWidth() * WIDTH;
264 fPattern = new Combo(top, SWT.FLAT);
265 fPattern.setLayoutData(UIUtil.alignCell(2, 5, GridData.FILL, GridData.CENTER, true, false));
266 fPattern.setBackground(lightcolor);
267 ((GridData) fPattern.getLayoutData()).widthHint = fm.getAverageCharWidth() * WIDTH;
268 gc.dispose();
269 //
270 fButtonFind = new Button(top, SWT.PUSH);
271 fButtonFind.setLayoutData(UIUtil.fillCell(2, 1, false, false));
272 fButtonFind.setToolTipText("Find Next");
273 fButtonFind.setImage(manager.getImage("DownArrow"));
274 fButtonFind.addMouseListener(new MouseAdapter() {
275 public void mouseDown(MouseEvent e) {
276 actionFind();
277 }
278 });
279 //
280 fButtonFindBackward = new Button(top, SWT.PUSH);
281 fButtonFindBackward.setLayoutData(UIUtil.fillCell(2, 1, false, false));
282 fButtonFindBackward.setToolTipText("Find Previous");
283 fButtonFindBackward.setImage(manager.getImage("UpArrow"));
284 fButtonFindBackward.addMouseListener(new MouseAdapter() {
285 public void mouseDown(MouseEvent e) {
286 actionFindBackward();
287 }
288 });
289 //
290 fStatus = new Label(top, SWT.LEFT);
291 fStatus.setLayoutData(UIUtil.fillCell(3, 6, true, true));
292 ((GridData) fStatus.getLayoutData()).heightHint = fm.getHeight() * 2;
293 //
294 fCaseSensitive = UIUtil.createButton(top, SWT.CHECK, "Case sensitive", null, null);
295 fCaseSensitive.setLayoutData(UIUtil.alignCell(3, 4, GridData.END, GridData.CENTER, false, true));
296 //new Label(top,SWT.LEFT).setLayoutData(UIUtil.fillCell(1,4,false,false));
297 //
298 if (fPatterns.size() > 0) {
299 fPattern.setItems((String[]) fPatterns.toArray(new String[fPatterns.size()]));
300 fPattern.setText((String) fPatterns.get(0));
301 }
302 fPattern.addModifyListener(new ModifyListener() {
303 public void modifyText(ModifyEvent e) {
304 patternChanged(e);
305 }
306 });
307 fPattern.setEnabled(false);
308 //
309 Thread loader = new Thread(new ElementLoader(fViewer), "ElementLoader");
310 loader.start();
311 //
312 return top;
313 }
314
315 protected Control createButtonBar(Composite parent) {
316 Composite top = UIUtil.createBox(parent, 2, 10, 2, 0, 0, null);
317 Button b;
318 fButtonSelectAll = UIUtil.createButton(top, SWT.CENTER, "Select All", null, null, 2, 1, true, false);
319 fButtonSelectAll.addSelectionListener(new SelectionAdapter() {
320 public void widgetSelected(SelectionEvent e) {
321 actionSelectAll();
322 }
323 });
324 b = UIUtil.createButton(top, SWT.CENTER, "Close", null, null, 2, 1, true, false);
325 b.addSelectionListener(new SelectionAdapter() {
326 public void widgetSelected(SelectionEvent e) {
327 actionClose();
328 }
329 });
330 return top;
331 }
332
333 ////////////////////////////////////////////////////////////////////////
334
335 private void setEnableActions(boolean enable) {
336 if (fLabels == null || fLabels.length == 0)
337 enable = false;
338 fButtonFind.setEnabled(enable);
339 fButtonFindBackward.setEnabled(enable);
340 fButtonSelectAll.setEnabled(enable);
341 }
342
343 private void savePattern(String pat) {
344 if (fPatterns.size() > 0 && pat.equals(fPatterns.get(0)))
345 return;
346 if (fPatterns.size() >= MAX_PERSISTENT)
347 fPatterns.remove(fPatterns.size() - 1);
348 fPatterns.add(0, pat);
349 }
350
351 private boolean find(String pat, int start) {
352 pat = Util.escapeRegex(pat);
353 pat = "/" + pat + (fCaseSensitive.getSelection() ? "/" : "/i");
354 try {
355 for (int i = start; i < fLabels.length; ++i) {
356 if (fReEngine.match(pat, fLabels[i])) {
357 //
358 fFound = i;
359 fStatus.setForeground(fColorBlack);
360 fStatus.setText("index=" + i);
361 return true;
362 }
363 }
364 fStatus.setForeground(fColorRed);
365 fStatus.setText((DEBUG) ? "Not Found: start=" + start : "Not Found");
366 return false;
367 } catch (Exception e) {
368 fStatus.setForeground(fColorRed);
369 fStatus.setText("ERROR: " + e);
370 return false;
371 }
372 }
373
374 private boolean findBackward(String pat, int start) {
375 pat = Util.escapeRegex(pat);
376 pat = "/" + pat + (fCaseSensitive.getSelection() ? "/" : "/i");
377 try {
378 for (int i = start; i >= 0; --i) {
379 if (fReEngine.match(pat, fLabels[i])) {
380 //
381 fFound = i;
382 fStatus.setForeground(fColorBlack);
383 fStatus.setText("index=" + i);
384 return true;
385 }
386 }
387 fStatus.setForeground(fColorRed);
388 fStatus.setText((DEBUG) ? "Not Found: start=" + start : "Not Found");
389 return false;
390 } catch (Exception e) {
391 fStatus.setForeground(fColorRed);
392 fStatus.setText("ERROR: " + e);
393 return false;
394 }
395 }
396
397 private void setSelections(IntList a) {
398 int index;
399 List selections = new ArrayList();
400 for (int i = 0; i < a.size(); ++i) {
401 index = a.get(i);
402 selections.add(fElements.get(index));
403 }
404 if (fViewer instanceof TableViewer) {
405 Table table = ((TableViewer) fViewer).getTable();
406 table.setSelection((TableItem[]) selections.toArray(new TableItem[selections.size()]));
407 table.showSelection();
408 } else {
409 fViewer.setSelection(new StructuredSelection(selections), true);
410 }
411 }
412
413 private void setSelection(int index) {
414 if (fViewer instanceof TableViewer) {
415 Table table = ((TableViewer) fViewer).getTable();
416 table.setSelection(new TableItem[] {(TableItem) fElements.get(index)});
417 table.showSelection();
418 } else {
419 fViewer.setSelection(new StructuredSelection(fElements.get(index)), true);
420 }
421 }
422
423 ////////////////////////////////////////////////////////////////////////
424
425 Label getStatus() {
426 return fStatus;
427 }
428
429 ////////////////////////////////////////////////////////////////////////
430
431 void actionClose() {
432 if (!fLoaded) {
433 fAbort = true;
434 return;
435 }
436 fWindowPosition = getShell().getLocation();
437 savePattern(fPattern.getText());
438 okPressed();
439 }
440
441 void actionSelectAll() {
442 String pat = fPattern.getText();
443 savePattern(pat);
444 if (DEBUG)
445 System.err.println(NAME + ".actionSelectAll(): pattern=" + pat);
446 int start = fFound;
447 IntList a = new IntList();
448 fFound=-1;
449 while (find(pat, fFound+1)) {
450 a.add(fFound);
451 }
452 fFound=start;
453 fStatus.setForeground(fColorBlack);
454 fStatus.setText("Found " + a.size()+" elements.");
455 setSelections(a);
456 }
457
458 void actionFind() {
459 String pat = fPattern.getText();
460 savePattern(pat);
461 if (DEBUG)
462 System.err.println(NAME + ".actionFind(): pattern=" + pat);
463 int start = fFound + 1;
464 if (fFound < 0)
465 start = 0;
466 if (find(pat, start)) {
467 setSelection(fFound);
468 fAnchor = fFound;
469 } else {
470 getShell().getDisplay().beep();
471 fFound = -1;
472 }
473 }
474
475 void actionFindBackward() {
476 String pat = fPattern.getText();
477 savePattern(pat);
478 if (DEBUG)
479 System.err.println(NAME + ".actionFindBackward(): pattern=" + pat);
480 int start = fFound - 1;
481 if (fFound < 0)
482 start = fElements.size() - 1;
483 if (findBackward(pat, start)) {
484 setSelection(fFound);
485 fAnchor = fFound;
486 } else {
487 getShell().getDisplay().beep();
488 fFound = -1;
489 }
490 }
491
492 void patternChanged(ModifyEvent event) {
493 String pat = fPattern.getText();
494 if (pat.length() == 0) {
495 setSelection(fAnchor);
496 return;
497 }
498 if (find(pat, fAnchor)) {
499 setSelection(fFound);
500 } else {
501 setSelection(fAnchor);
502 }
503 setEnableActions(pat.length() > 0);
504 }
505
506 ////////////////////////////////////////////////////////////////////////
507 // These are used by ElementLoader to load elements in a separate thread.
508
509 void load(AbstractTreeViewer viewer, IStructuredSelection selection) {
510 ITreeContentProvider provider = (ITreeContentProvider) viewer.getContentProvider();
511 IBaseLabelProvider lp = viewer.getLabelProvider();
512 if (!(lp instanceof ILabelProvider)) {
513 fError = true;
514 return;
515 }
516 Object[] roots;
517 if (selection == null || selection.isEmpty() || selection.size() == 0) {
518 roots = provider.getChildren(viewer.getInput());
519 } else {
520 roots = selection.toArray();
521 }
522 fElements = new ArrayList(100);
523 getElements(viewer, provider, roots, fElements);
524 if (fError)
525 return;
526 fLabels = new String[fElements.size()];
527 ILabelProvider labelprovider = (ILabelProvider) lp;
528 for (int i = 0; i < fLabels.length; ++i) {
529 fLabels[i] = labelprovider.getText(fElements.get(i));
530 }
531 }
532
533 void load(TableViewer viewer, IStructuredSelection selection) {
534 Object[] items;
535 // if (selection == null || selection.isEmpty() || selection.size() == 0) {
536 items = viewer.getTable().getItems();
537 // } else {
538 // items = selection.toArray();
539 // }
540 //
541 fElements = new ArrayList(100);
542 fLabels = new String[items.length];
543 for (int i = 0; i < items.length; ++i) {
544 fElements.add(items[i]);
545 fLabels[i] = ((TableItem) items[i]).getText();
546 }
547 }
548
549 private void getElements(
550 AbstractTreeViewer viewer,
551 ITreeContentProvider provider,
552 Object[] parents,
553 List ret) {
554 if (parents == null || parents.length == 0)
555 return;
556 if (fAbort) {
557 fError = true;
558 return;
559 }
560 ViewerSorter sorter = viewer.getSorter();
561 Object[] children;
562 if (sorter != null) {
563 children = parents;
564 parents = new Object[children.length];
565 System.arraycopy(children, 0, parents, 0, children.length);
566 sorter.sort(viewer, parents);
567 }
568 for (int i = 0; i < parents.length; ++i) {
569 if ((ret.size() & STATUS_PERIOD) == 0) {
570 getShell().getDisplay().asyncExec(new StatusReporter("" + ret.size() + " loaded"));
571 if (fAbort) {
572 fError = true;
573 return;
574 }
575 }
576 ret.add(parents[i]);
577 children = provider.getChildren(parents[i]);
578 getElements(viewer, provider, children, ret);
579 }
580 }
581
582 ////////////////////////////////////////////////////////////////////////
583
584 /**
585 * Since TableViewer.getItems() and TableItem.getText() requires to run in the display thread,
586 * table entries are loaded in the contructor for now instead of in the thread.run().
587 */
588 private class ElementLoader implements Runnable {
589 private Viewer fViewer;
590 private IStructuredSelection fSelection;
591 private Display fDisplay;
592 public ElementLoader(Viewer viewer) {
593 fViewer = viewer;
594 fSelection = (IStructuredSelection) fViewer.getSelection();
595 if (!(fViewer instanceof AbstractTreeViewer)) {
596 load((TableViewer) fViewer, fSelection);
597 }
598 }
599 public void run() {
600 if (fViewer instanceof AbstractTreeViewer)
601 load((AbstractTreeViewer) fViewer, fSelection);
602 getShell().getDisplay().asyncExec(new Runnable() {
603 public void run() {
604 loadCompleted();
605 }
606 });
607 }
608
609 }
610
611 ////////////////////////////////////////////////////////////////////////
612
613 private class StatusReporter implements Runnable {
614 private String fMessage;
615 public StatusReporter(String message) {
616 fMessage = message;
617 }
618 public void run() {
619 getStatus().setText(fMessage);
620 }
621 }
622
623 ////////////////////////////////////////////////////////////////////////
624
625 }