KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > properties > FindPerformer


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20
21 package org.netbeans.modules.properties;
22
23
24 import java.awt.event.*;
25 import java.awt.Rectangle JavaDoc;
26 import java.beans.PropertyChangeEvent JavaDoc;
27 import java.beans.PropertyChangeListener JavaDoc;
28 import java.lang.ref.SoftReference JavaDoc;
29 import java.util.HashSet JavaDoc;
30 import java.util.Set JavaDoc;
31 import javax.swing.DefaultComboBoxModel JavaDoc;
32 import javax.swing.JButton JavaDoc;
33 import javax.swing.JComponent JavaDoc;
34 import javax.swing.JDialog JavaDoc;
35 import javax.swing.JTable JavaDoc;
36 import javax.swing.JTextField JavaDoc;
37 import javax.swing.KeyStroke JavaDoc;
38 import javax.swing.SwingUtilities JavaDoc;
39 import org.openide.DialogDescriptor;
40 import org.openide.DialogDisplayer;
41 import org.openide.util.actions.SystemAction;
42 import org.openide.util.NbBundle;
43 import org.openide.util.RequestProcessor;
44 import org.openide.util.WeakListeners;
45
46
47 /**
48  * FindPerformer is a performer of the FindAction which is invoked on Resource Bundles table view component.
49  * Does actual dirty job, search on the actual activated table and sets the results as highlighted text on particular cell.
50  *
51  * @author Peter Zavadsky
52  * @author Marian Petras
53  */

54 public class FindPerformer extends javax.swing.AbstractAction JavaDoc
55                            implements PropertyChangeListener JavaDoc {
56
57     /** Table on which perform the search. */
58     private JTable JavaDoc table;
59     
60     /** String to find. */
61     private String JavaDoc findString;
62     
63     /** Stores values which are used to start search from and store the results for next search.
64      * 1st item - row index of cell with found string,
65      * 2nd item - column index of cell with found string,
66      * 3rd item - start offset of found string.
67      * 4th item - end offset of found string.
68      */

69     private int[] searchValues;
70
71     /** Flag if it is set highliht search. */
72     private boolean highlightSearch = true;
73     
74     /** Flag if it is set match case search. */
75     private boolean matchCaseSearch = false;
76     
77     /** Flag if it is set forward search. */
78     private boolean backwardSearch = false;
79     
80     /** Flag if it is set wrap search. */
81     private boolean wrapSearch = true;
82     
83     /** Flag if it is set search by rows. */
84     private boolean rowSearch = true;
85     
86     /** Listener for registering keystrokes to find next action. */
87     private final ActionListener findNextActionListener;
88     
89     /** Listener for registering keystrokes to find previous action. */
90     private final ActionListener findPreviousActionListener;
91     
92     /** Listener for registering keystrokes to toggle highlight action. */
93     private final ActionListener toggleHighlightListener;
94     
95     /** Keeps history of found strings. */
96     private Set JavaDoc<String JavaDoc> history = new HashSet JavaDoc<String JavaDoc>();
97
98     /** Helper variable keeping <code>settings</code>. */
99     private TableViewSettings settings;
100
101     // Initializes action listener use to register for key strokes to table.
102
{
103         findNextActionListener = new ActionListener() {
104             public void actionPerformed(ActionEvent evt) {
105                 // Find next action invoked (default F3) -> search next occurence.
106
if(searchValues != null) {
107                     synchronized(this) {
108                         backwardSearch = false;
109                         performSearch();
110                     }
111                 }
112             }
113         };
114         findPreviousActionListener = new ActionListener() {
115             public void actionPerformed(ActionEvent evt) {
116                 // Find previous action invoked (default Shift-F3)-> search previous occurence.
117
if(searchValues != null) {
118                     synchronized(this) {
119                         backwardSearch = true;
120                         performSearch();
121                     }
122                 }
123             }
124         };
125
126         toggleHighlightListener = new ActionListener() {
127             public void actionPerformed(ActionEvent evt) {
128                 // Toggle highlight action invoked (defauilt Shift-Alt-H) -> toggle highlight.
129
highlightSearch = !highlightSearch;
130                 table.repaint();
131             }
132         };
133     } // end of initializer
134

135     /** Soft reference for caching singleton find performer on last table view. */
136     private static SoftReference JavaDoc<FindPerformer> softRef;
137     
138     /** Dialog for perform search. */
139     private static JDialog JavaDoc findDialog;
140     
141     /** Name of client property used to store search result values. */
142     public static final String JavaDoc TABLE_SEARCH_RESULT = "table.search.result"; // NOI18N
143

144     
145     /** Creates new FindPerformer. Used internally only, for getting the singleton use #getFindPerformer method. */
146     private FindPerformer(JTable JavaDoc table) {
147         this.table = table;
148         
149         settings = TableViewSettings.getDefault();
150         settings.addPropertyChangeListener(
151             WeakListeners.propertyChange(this, settings)
152         );
153         
154                     registerKeyStrokes();
155                 }
156             
157         
158     /**
159      * Listen on setting changes.
160      */

161     public void propertyChange(PropertyChangeEvent JavaDoc evt) {
162         // Settings were changed reset registered key strokes.
163
registerKeyStrokes();
164     }
165     
166     /** Gets find performer. */
167     public static FindPerformer getFindPerformer(JTable JavaDoc table) {
168         if(softRef != null) {
169             FindPerformer fp = softRef.get();
170             if(fp != null) {
171                 if(!fp.validateTable(table)) {
172                     fp.resetTable(table);
173                     fp.registerKeyStrokes();
174                 }
175                 return fp;
176             }
177         }
178         
179         FindPerformer fp = new FindPerformer(table);
180         softRef = new SoftReference JavaDoc<FindPerformer>(fp);
181         
182         return fp;
183     }
184     
185     /** Resets the table if necessary. */
186     private void resetTable(JTable JavaDoc table) {
187         this.table = table;
188     }
189     
190     /** Validates if the table is the same one as last opened find panel. */
191     private boolean validateTable(JTable JavaDoc table) {
192         if(this.table != null && this.table.equals(table))
193             return true;
194         
195         return false;
196     }
197     
198     /** Register key strokes F3 and Shift-F3 (next & previous search) to table. */
199     private synchronized void registerKeyStrokes() {
200         // Register key strokes to table.
201
KeyStroke JavaDoc[] keyStrokes = settings.getKeyStrokesFindNext();
202         for(int i=0; i<keyStrokes.length; i++) {
203             table.registerKeyboardAction(
204                 findNextActionListener,
205                 keyStrokes[i],
206                 JComponent.WHEN_IN_FOCUSED_WINDOW
207             );
208         }
209
210         keyStrokes = settings.getKeyStrokesFindPrevious();
211         for(int i=0; i<keyStrokes.length; i++) {
212             table.registerKeyboardAction(
213                 findPreviousActionListener,
214                 keyStrokes[i],
215                 JComponent.WHEN_IN_FOCUSED_WINDOW
216             );
217         }
218
219         keyStrokes = settings.getKeyStrokesToggleHighlight();
220         for(int i=0; i<keyStrokes.length; i++) {
221             table.registerKeyboardAction(
222                 toggleHighlightListener,
223                 keyStrokes[i],
224                 JComponent.WHEN_IN_FOCUSED_WINDOW
225             );
226         }
227     }
228     
229     /** Getter for find string. */
230     public String JavaDoc getFindString() {
231         return findString;
232     }
233     
234     /** Getter for highlight search flag. */
235     public boolean isHighlightSearch() {
236         return highlightSearch;
237     }
238
239     /* implements interface javax.swing.Action */
240     public void actionPerformed(java.awt.event.ActionEvent JavaDoc e) {
241         if(findDialog == null)
242             createFindDialog();
243         else {
244             // There is already the find dialog open.
245
findDialog.setVisible(true);
246             findDialog.requestFocusInWindow();
247         }
248     }
249
250     /** Methods which does the dirty job. It creates and shows find dialog. */
251     private void createFindDialog() {
252         final JDialog JavaDoc dialog;
253         final FindPanel panel = new FindPanel();
254         DialogDescriptor dd = new DialogDescriptor(
255             panel,
256             NbBundle.getBundle(FindPerformer.class).getString("LBL_Title"), // title
257
false, // modal
258
new JButton JavaDoc[0], // empty options, we provide our owns
259
null, // initvalue
260
DialogDescriptor.DEFAULT_ALIGN,
261             null, // helpCtx
262
null // listener
263
);
264
265         dialog = (javax.swing.JDialog JavaDoc)DialogDisplayer.getDefault().createDialog(dd);
266
267         // Static reference to the dialog.
268
findDialog = dialog;
269
270         // set findButton as default
271
dialog.getRootPane().setDefaultButton(panel.getButtons()[0]);
272         dialog.setFocusable(false);
273         dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
274
275         // set listeners
276
// find button
277
panel.getButtons()[0].addActionListener(
278             new ActionListener() {
279                 public void actionPerformed(ActionEvent evt) {
280                     findString = ((JTextField JavaDoc)panel.getComboBox().getEditor().getEditorComponent()).getText();
281
282                     // Set flags.
283
highlightSearch = panel.getHighlightCheck().isSelected();
284                     matchCaseSearch = panel.getMatchCaseCheck().isSelected();
285                     backwardSearch = panel.getBackwardCheck().isSelected();
286                     wrapSearch = panel.getWrapCheck().isSelected();
287                     rowSearch = panel.getRowCheck().isSelected();
288                     
289                     dialog.dispose();
290                     
291                     if(findString != null && !findString.trim().equals("")) {
292
293                         history.add(findString.intern());
294                         
295                         performSearch();
296                     }
297                     
298                     // For case previous search results need to be erased.
299
table.repaint();
300                 }
301             }
302         );
303
304         // cancel button
305
panel.getButtons()[1].addActionListener(
306             new ActionListener() {
307                 public void actionPerformed(ActionEvent evt) {
308                     dialog.dispose();
309                 }
310             }
311         );
312         
313         dialog.addWindowListener(new WindowAdapter() {
314             public void windowClosed(WindowEvent e) {
315                 findDialog = null;
316             }
317         });
318         
319         // Set flags.
320
panel.getHighlightCheck().setSelected(highlightSearch);
321         panel.getMatchCaseCheck().setSelected(matchCaseSearch);
322         panel.getBackwardCheck().setSelected(backwardSearch);
323         panel.getWrapCheck().setSelected(wrapSearch);
324         panel.getRowCheck().setSelected(rowSearch);
325
326         // Set combo box list items.
327
panel.getComboBox().setModel(new DefaultComboBoxModel JavaDoc(history.toArray()));
328         // Set last found string as selected if exist.
329
if (findString != null)
330             panel.getComboBox().setSelectedItem(findString);
331         
332         dialog.setVisible(true);
333         panel.requestFocusInWindow();
334     }
335     
336     /** Closes find dialog if one is opened. */
337     public void closeFindDialog() {
338         if(findDialog != null) {
339             synchronized(findDialog) {
340                 findDialog.setVisible(false);
341                 findDialog.dispose();
342                 findDialog = null;
343             }
344         }
345     }
346
347     /** Prepares searchValues before search. The search will start
348      * from selected cell or from beginning by forward or
349      * from end by backward search respectivelly.*/

350     private void prepareSearch() {
351         int row = table.getSelectedRow();
352         int column = table.getSelectedColumn();
353
354         // Is selected the cell found by previous search.
355
if(searchValues != null && row == searchValues[0] && column == searchValues[1])
356             return;
357         
358
359         if(row != -1 || column != -1) {
360             // Some cell is selected.
361
int startOffset, endOffset;
362             startOffset = endOffset = ((JTextField JavaDoc)((PropertiesTableCellEditor)table.getCellEditor(row, column)).getComponent()).getCaretPosition();
363             
364             searchValues = new int[] {row, column, startOffset, endOffset};
365         } else {
366             // Nothing is selected, set first (last) cell by forward (backward) search.
367
if(backwardSearch) {
368                 int lastRow = table.getRowCount()-1;
369                 int lastColumn = table.getColumnCount()-1;
370                 int startOffset, endOffset;
371                 
372                 startOffset = endOffset = ((PropertiesTableModel.StringPair)table.getValueAt(lastRow, lastColumn)).getValue().length()-1;
373
374                 searchValues = new int[] {lastRow, lastColumn, startOffset, endOffset};
375             } else {
376                 searchValues = new int[] {0, 0, 0, 0};
377             }
378         }
379         
380     }
381     
382     /** Perform search, store results and set editable cell if search was successful. */
383     private synchronized void performSearch() {
384         prepareSearch();
385         // perform search not in AWT-thread
386
PropertiesRequestProcessor.getInstance().post(
387             new Runnable JavaDoc() {
388                 public void run() {
389                     // Do wrap search?
390
boolean wrap = false;
391                     
392                     do {
393                         final int[] result = search(searchValues[0], searchValues[1], backwardSearch ? searchValues[2]-1 : searchValues[3]);
394                         
395                         if(wrapSearch && !wrap && result == null) {
396                             // Do wrapping.
397

398                             // Wrap search if was found something in second to last search.
399
if(backwardSearch) {
400                                 int lastRow = table.getRowCount()-1;
401                                 int lastColumn = table.getColumnCount()-1;
402
403                                 searchValues = new int[] {lastRow,
404                                     lastColumn,
405                                     ((PropertiesTableModel.StringPair)table.getValueAt(lastRow, lastColumn)).getValue().length()-1,
406                                     0};
407                             } else {
408                                 searchValues = new int[] {0, 0, 0, 0};
409                             }
410  
411                             wrap = true;
412                         } else {
413                             if(result != null) {
414                                 // store new search results
415
searchValues = result;
416
417                                 SwingUtilities.invokeLater(new Runnable JavaDoc() {
418                                     public void run() {
419                                         // autoscroll to cell if possible and necessary
420
if (table.getAutoscrolls()) {
421                                             Rectangle JavaDoc cellRect = table.getCellRect(result[0], result[1], false);
422                                             if (cellRect != null) {
423                                                 table.scrollRectToVisible(cellRect);
424                                             }
425                                         }
426                                         // update selection & edit
427
if (table.isEditing()) {
428                                             /*
429                                              * If the cell at (result[1], result[0]) is currently being edited,
430                                              * method setSelectionInterval does not finish the current editing
431                                              * session and the selection is not changed. But the result
432                                              * of calling setSelectionInterval(...) is that method
433                                              * BundleEditPanel.updateSelection(...) is called which sets value
434                                              * and comment from the cell at (result[1], result[0]).
435                                              * The consequence of these steps is that values from
436                                              * cell (result[1], result[0]) are set to the cell currently being
437                                              * edited - see bug #81934
438                                              * (http://www.netbeans.org/issues/show_bug.cgi?id=81934).
439                                              */

440                                             table.getCellEditor().stopCellEditing();
441                                         }
442                                         table.getColumnModel().getSelectionModel().setSelectionInterval(result[1], result[1]);
443                                         table.getSelectionModel().setSelectionInterval(result[0], result[0]);
444                                         // set editable cell
445
table.editCellAt(result[0], result[1]);
446                                     }
447                                 });
448                             }
449                             
450                             // Store new search results.
451
searchValues = result;
452
453                             wrap = false;
454                         }
455                     } while (wrap);
456                 } // end of run method
457
}
458         );
459     }
460     
461     /** Perform actual search on table started from the cell specified.
462      * @param startColumn Ccolumn index of cell to start the search.
463      * @param startRow Row index of cell to start the search.
464      * @param startOffset Offset from start the search.
465      * @return srray of integers where first is column index, second row index, third offset,
466      * in case no thing was found null is returned */

467     private int[] search(int startRow, int startColumn, int startOffset) {
468         // Helper for reseting startOffset after first iteration.
469
boolean firstIteration = true;
470         
471         // If rowSearch->row loop else->column loop.
472
for(int i= rowSearch ? startRow : startColumn;
473                 backwardSearch ? i>=0 : i<(rowSearch ? table.getRowCount() : table.getColumnCount());
474                 i = backwardSearch ? i-1 : i+1 ) {
475             // If rowSearch->column loop else->row loop.
476
for(int j= rowSearch ? startColumn : startRow;
477                     backwardSearch ? j>=0 : j<(rowSearch ? table.getColumnCount() : table.getRowCount());
478                     j = backwardSearch ? j-1 : j+1) {
479                 // Set row and column indexes for this iteration.
480
int row = rowSearch ? i : j;
481                 int column = rowSearch ? j : i;
482                 
483                 String JavaDoc str = ((PropertiesTableModel.StringPair)table.getValueAt(row, column)).toString();
484                 // Skip to next iteration if value is null or is the string in cell is shorter than string to find.
485
if(str == null || str.length() < findString.length())
486                     continue;
487                 
488                 if(!firstIteration)
489                     startOffset = backwardSearch ? str.length()-findString.length() : 0;
490                 
491                 int offset = containsFindString(str, startOffset);
492                 
493                 if(offset>=0) {
494                     // puts client property which is then used by cell editor
495
// for setting and highlighting the find string
496
table.putClientProperty(TABLE_SEARCH_RESULT, new int[] {row, column, offset, offset+findString.length()});
497                     return new int[] {row, column, offset, offset+findString.length()};
498                 }
499                 
500                 if(firstIteration) firstIteration = false;
501             }
502             
503             // Next inner loop from beginning(end) for forward (backward) search.
504
if(rowSearch)
505                 startColumn = backwardSearch ? table.getColumnCount()-1 : 0;
506             else
507                 startRow = backwardSearch ? table.getRowCount()-1 : 0;
508         }
509         
510         return null;
511     }
512     
513     /** The function search if findString occures whitin specified string.
514      * @param str String which is looked if contains find string.
515      * @param startOffset Offset from starts the search.
516      * @return Offset on which starts find string whitin str or -1. */

517     private int containsFindString(String JavaDoc str, int startOffset) {
518         if(startOffset < 0 || startOffset >= str.length())
519             return -1;
520         
521         for(int i=startOffset;
522                 backwardSearch ? i>=0 : i<(str.length()-findString.length()+1);
523                 i = backwardSearch ? i-1 : i+1) {
524                     
525             if(findString.regionMatches(!matchCaseSearch, 0, str, i, findString.length()))
526                 return i;
527         }
528         
529         return -1;
530     }
531
532 }
533
Popular Tags