KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > search > ResultView


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 package org.netbeans.modules.search;
21
22 import java.awt.BorderLayout JavaDoc;
23 import java.awt.CardLayout JavaDoc;
24 import java.awt.event.ActionEvent JavaDoc;
25 import java.awt.event.ActionListener JavaDoc;
26 import java.awt.EventQueue JavaDoc;
27 import java.awt.event.ItemEvent JavaDoc;
28 import java.awt.event.ItemListener JavaDoc;
29 import java.text.MessageFormat JavaDoc;
30 import java.util.ArrayList JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.ResourceBundle JavaDoc;
34 import java.lang.ref.WeakReference JavaDoc;
35 import javax.accessibility.AccessibleContext JavaDoc;
36 import javax.swing.BorderFactory JavaDoc;
37 import javax.swing.Box JavaDoc;
38 import javax.swing.BoxLayout JavaDoc;
39 import javax.swing.ImageIcon JavaDoc;
40 import javax.swing.JButton JavaDoc;
41 import javax.swing.JPanel JavaDoc;
42 import javax.swing.JScrollPane JavaDoc;
43 import javax.swing.JSplitPane JavaDoc;
44 import javax.swing.JToggleButton JavaDoc;
45 import javax.swing.JToolBar JavaDoc;
46 import javax.swing.JTree JavaDoc;
47 import javax.swing.SwingConstants JavaDoc;
48 import javax.swing.tree.TreePath JavaDoc;
49 import org.openide.awt.Mnemonics;
50 import org.openide.nodes.Node;
51 import org.openide.util.NbBundle;
52 import org.openide.util.Utilities;
53 import org.openide.windows.Mode;
54 import org.openide.windows.TopComponent;
55 import org.openide.windows.WindowManager;
56 import org.openidex.search.SearchType;
57 import static java.lang.Thread.NORM_PRIORITY JavaDoc;
58
59 /**
60  * Panel which displays search results in explorer like manner.
61  * This panel is a singleton.
62  *
63  * @see <a HREF="doc-files/results-class-diagram.png">Class diagram</a>
64  * @author Petr Kuzel, Jiri Mzourek, Peter Zavadsky
65  * @author Marian Petras
66  */

67 final class ResultView extends TopComponent {
68     
69     /** display the matching string location in context by default? */
70     private static final boolean SHOW_CONTEXT_BY_DEFAULT = true;
71     /** */
72     private static final String JavaDoc RESULTS_CARD = "results"; //NOI18N
73
/** */
74     private static final String JavaDoc ISSUES_CARD = "issues"; //NOI18N
75

76     /** should the context view be visible when doing search &amp; replace? */
77     private boolean contextViewEnabled = SHOW_CONTEXT_BY_DEFAULT;
78     /** is the context view visible? */
79     private boolean contextViewVisible = false;
80     /** */
81     private double dividerLocation = -1.0d;
82     /** */
83     private boolean ignoreContextButtonToggle = false;
84     /** */
85     private boolean hasResults = false; //accessed only from the EventQueue
86
/** */
87     private int objectsCount = 0; //accessed only from the EventQueue
88
/** */
89     private volatile boolean hasDetails = false;
90     /** */
91     private volatile boolean searchInProgress = false;
92                            
93     /** unique ID of <code>TopComponent</code> (singleton) */
94     private static final String JavaDoc ID = "search-results"; //NOI18N
95

96     /**
97      * instance/singleton of this class
98      *
99      * @see #getInstance
100      */

101     private static WeakReference JavaDoc instance = null;
102     
103     /**
104      * tree view for displaying found objects
105      */

106     private final JScrollPane JavaDoc treeView;
107     
108     /** Result data model. */
109     private ResultModel resultModel = null;
110     /** */
111     private ResultTreeModel treeModel = null;
112     
113     /** */
114     private final JTree JavaDoc tree;
115     /** listens on various actions performed on nodes in the tree */
116     private final NodeListener nodeListener;
117     
118     /** */
119     private Node[] lastSearchNodes;
120     /** */
121     private List JavaDoc lastEnabledSearchTypes;
122     
123     /** template for displaying number of matching files found so far */
124     private MessageFormat JavaDoc nodeCountFormat;
125     /**
126      * template for displaying of number of matching files and total number
127      * of matches found so far
128      */

129     private MessageFormat JavaDoc nodeCountFormatFullText;
130
131     /**
132      * Returns a singleton of this class.
133      *
134      * @return singleton of this <code>TopComponent</code>
135      */

136     static synchronized ResultView getInstance() {
137         ResultView view;
138         view = (ResultView) WindowManager.getDefault().findTopComponent(ID);
139         if (view == null) {
140             view = getDefault();
141         }
142         return view;
143     }
144
145     /**
146      * Singleton accessor reserved for the window systemm only. The window
147      * system calls this method to create an instance of this
148      * <code>TopComponent</code> from a <code>.settings</code> file.
149      * <p>
150      * <em>This method should not be called anywhere except from the window
151      * system's code. </em>
152      *
153      * @return singleton - instance of this class
154      */

155     public static synchronized ResultView getDefault() {
156         ResultView view;
157         if (instance == null) {
158             view = new ResultView();
159             instance = new WeakReference JavaDoc(view);
160         } else {
161             view = (ResultView) instance.get();
162             if (view == null) {
163                 view = new ResultView();
164                 instance = new WeakReference JavaDoc(view);
165             }
166         }
167         return view;
168     }
169     
170     private final CardLayout JavaDoc contentCards;
171     private final CardLayout JavaDoc resultViewCards;
172     private final JPanel JavaDoc mainPanel;
173     private final JPanel JavaDoc resultsPanel;
174     private final JButton JavaDoc btnReplace;
175     private final JButton JavaDoc btnModifySearch;
176     private final JButton JavaDoc btnShowDetails;
177     private final JButton JavaDoc btnStop;
178     private final JButton JavaDoc btnPrev;
179     private final JButton JavaDoc btnNext;
180     private final JToggleButton JavaDoc btnDisplayContext;
181     
182     private JSplitPane JavaDoc splitPane;
183     private ContextView contextView;
184     private IssuesPanel issuesPanel;
185     
186     /** Creates a new <code>ResultView</code>. */
187     private ResultView() {
188         //PENDING - icons, accessible names, accessible descriptions
189
JToolBar JavaDoc toolBar = new JToolBar JavaDoc(SwingConstants.VERTICAL);
190         btnDisplayContext = new JToggleButton JavaDoc();
191         btnDisplayContext.setIcon(new ImageIcon JavaDoc(Utilities.loadImage(
192                 "org/netbeans/modules/search/res/context.gif", true))); //NOI18N
193
btnDisplayContext.setToolTipText(
194                 NbBundle.getMessage(getClass(), "TOOLTIP_ShowContext"));//NOI18N
195
btnDisplayContext.getAccessibleContext().setAccessibleDescription(
196                 NbBundle.getMessage(getClass(), "ACSD_ShowContext")); //NOI18N
197
btnDisplayContext.setSelected(SHOW_CONTEXT_BY_DEFAULT);
198         btnPrev = new JButton JavaDoc();
199         btnPrev.setIcon(new ImageIcon JavaDoc(Utilities.loadImage(
200                 "org/netbeans/modules/search/res/prev.png", true))); //NOI18N
201
btnNext = new JButton JavaDoc();
202         btnNext.setIcon(new ImageIcon JavaDoc(Utilities.loadImage(
203                 "org/netbeans/modules/search/res/next.png", true))); //NOI18N
204
toolBar.add(btnDisplayContext);
205         toolBar.add(new JToolBar.Separator JavaDoc());
206         toolBar.add(btnPrev);
207         toolBar.add(btnNext);
208         toolBar.setRollover(true);
209         toolBar.setFloatable(false);
210         
211         treeModel = createTreeModel();
212         tree = createTree(treeModel, nodeListener = new NodeListener());
213         treeView = new JScrollPane JavaDoc(tree);
214         treeView.getAccessibleContext().setAccessibleDescription(
215                 NbBundle.getMessage(ResultView.class, "ACS_TREEVIEW")); //NOI18N
216
treeView.setBorder(BorderFactory.createEmptyBorder());
217         
218         resultsPanel = new JPanel JavaDoc(resultViewCards = new CardLayout JavaDoc());
219
220         btnShowDetails = new JButton JavaDoc();
221         btnModifySearch = new JButton JavaDoc();
222         btnStop = new JButton JavaDoc();
223         btnReplace = new JButton JavaDoc();
224         
225         /* initialize listening for buttons: */
226         ButtonListener buttonListener = new ButtonListener();
227         btnShowDetails.addActionListener(buttonListener);
228         btnModifySearch.addActionListener(buttonListener);
229         btnStop.addActionListener(buttonListener);
230         btnReplace.addActionListener(buttonListener);
231         btnPrev.addActionListener(buttonListener);
232         btnNext.addActionListener(buttonListener);
233         btnDisplayContext.addItemListener(buttonListener);
234         
235         Mnemonics.setLocalizedText(
236                 btnStop,
237                 NbBundle.getMessage(getClass(), "TEXT_BUTTON_STOP")); //NOI18N
238
Mnemonics.setLocalizedText(
239                 btnShowDetails,
240                 NbBundle.getMessage(getClass(), "TEXT_BUTTON_FILL")); //NOI18N
241
Mnemonics.setLocalizedText(
242                 btnReplace,
243                 NbBundle.getMessage(getClass(), "TEXT_BUTTON_REPLACE"));//NOI18N
244
Mnemonics.setLocalizedText(
245                 btnModifySearch,
246                 NbBundle.getMessage(getClass(),
247                                     "TEXT_BUTTON_CUSTOMIZE")); //NOI18N
248

249         btnStop.setEnabled(false);
250         btnShowDetails.setEnabled(false);
251         
252         btnReplace.setVisible(false);
253         
254         JPanel JavaDoc buttonsPanel = new JPanel JavaDoc();
255         buttonsPanel.setLayout(new BoxLayout JavaDoc(buttonsPanel, BoxLayout.X_AXIS));
256         buttonsPanel.add(btnReplace);
257         buttonsPanel.add(Box.createHorizontalGlue());
258         buttonsPanel.add(btnShowDetails);
259         buttonsPanel.add(btnModifySearch);
260         buttonsPanel.add(btnStop);
261
262         mainPanel = new JPanel JavaDoc();
263         mainPanel.setLayout(new BorderLayout JavaDoc(0, 5));
264         mainPanel.add(toolBar, BorderLayout.WEST);
265         mainPanel.add(resultsPanel, BorderLayout.CENTER);
266         mainPanel.add(buttonsPanel, BorderLayout.SOUTH);
267         //issue #46261 - "Search Results window must be opaque under GTK"
268
mainPanel.setOpaque(true);
269         mainPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 2, 0));
270         
271         setLayout(contentCards = new CardLayout JavaDoc());
272         add(mainPanel, RESULTS_CARD);
273
274         setName("Search Results"); //NOI18N
275
setDisplayName(NbBundle.getMessage(ResultView.class,
276                                            "TITLE_SEARCH_RESULTS")); //NOI18N
277
setToolTipText(NbBundle.getMessage(ResultView.class,
278                                            "TOOLTIP_SEARCH_RESULTS")); //NOI18N
279
setIcon(Utilities.loadImage(
280                 "org/netbeans/modules/search/res/find.gif")); //NOI18N
281

282         initAccessibility();
283         
284         resultModelChanged();
285     }
286     
287     /**
288      */

289     private static ResultTreeModel createTreeModel() {
290         ResultTreeModel treeModel = new ResultTreeModel(null);
291         treeModel.setRootDisplayName(getInitialRootNodeText());
292         return treeModel;
293     }
294     
295     /**
296      */

297     private static JTree JavaDoc createTree(ResultTreeModel treeModel,
298                                     NodeListener nodeListener) {
299         JTree JavaDoc tree = new JTree JavaDoc(treeModel);
300         tree.setCellRenderer(new NodeRenderer(false));
301         tree.putClientProperty("JTree.lineStyle", "Angled"); //NOI18N
302

303         tree.addMouseListener(nodeListener);
304         tree.addKeyListener(nodeListener);
305         tree.addTreeWillExpandListener(nodeListener);
306         tree.addTreeExpansionListener(nodeListener);
307         
308         tree.setToggleClickCount(0);
309         
310         return tree;
311     }
312     
313     /**
314      */

315     private static String JavaDoc getInitialRootNodeText() {
316         return NbBundle.getMessage(ResultView.class,
317                                    "TEXT_Search_in_filesystems"); //NOI18N
318
}
319     
320     /** Overriden to explicitely set persistence type of ResultView
321      * to PERSISTENCE_NEVER */

322     @Override JavaDoc
323     public int getPersistenceType() {
324         return TopComponent.PERSISTENCE_ALWAYS; // XXX protimluv
325
}
326
327     /** Replaces this in object stream. */
328     @Override JavaDoc
329     public Object JavaDoc writeReplace() {
330         return new ResolvableHelper();
331     }
332
333     final public static class ResolvableHelper implements java.io.Serializable JavaDoc {
334         static final long serialVersionUID = 7398708142639457544L;
335         public Object JavaDoc readResolve() {
336             return ResultView.getDefault();
337         }
338     }
339
340     /**
341      * Resolves to the {@linkplain #getDefault default instance} of this class.
342      *
343      * This method is necessary for correct functinality of window system's
344      * mechanism of persistence of top components.
345      */

346     private Object JavaDoc readResolve() throws java.io.ObjectStreamException JavaDoc {
347         return ResultView.getDefault();
348     }
349     
350     private void initAccessibility() {
351         ResourceBundle JavaDoc bundle = NbBundle.getBundle(ResultView.class);
352         getAccessibleContext ().setAccessibleName (bundle.getString ("ACSN_ResultViewTopComponent")); //NOI18N
353
getAccessibleContext ().setAccessibleDescription (bundle.getString ("ACSD_ResultViewTopComponent")); //NOI18N
354

355         AccessibleContext JavaDoc accessCtx;
356         
357         accessCtx = treeView.getHorizontalScrollBar().getAccessibleContext();
358         accessCtx.setAccessibleName(
359                 bundle.getString("ACSN_HorizontalScrollbar")); //NOI18N
360

361         accessCtx = treeView.getVerticalScrollBar().getAccessibleContext();
362         accessCtx.setAccessibleName(
363                 bundle.getString("ACSN_VerticalScrollbar")); //NOI18N
364

365         accessCtx = treeView.getAccessibleContext();
366         accessCtx.setAccessibleName(
367                 bundle.getString("ACSN_ResultTree")); //NOI18N
368
accessCtx.setAccessibleDescription(
369                 bundle.getString("ACSD_ResultTree")); //NOI18N
370

371         btnReplace.getAccessibleContext().setAccessibleDescription(bundle.getString("ACS_TEXT_BUTTON_REPLACE")); //NOI18N
372
btnModifySearch.getAccessibleContext().setAccessibleDescription(bundle.getString("ACS_TEXT_BUTTON_CUSTOMIZE")); //NOI18N
373
btnShowDetails.getAccessibleContext().setAccessibleDescription(bundle.getString("ACS_TEXT_BUTTON_FILL")); //NOI18N
374
btnStop.getAccessibleContext().setAccessibleDescription(bundle.getString("ACS_TEXT_BUTTON_STOP")); //NOI18N
375
}
376     
377     /**
378      * This method exists just to make the <code>close()</code> method
379      * accessible via <code>Class.getDeclaredMethod(String, Class[])</code>.
380      * It is used in <code>Manager</code>.
381      */

382     void closeResults() {
383         close();
384     }
385     
386     /** Send search details to output window. */
387     public void fillOutput() {
388         btnShowDetails.setEnabled(false);
389         Manager.getInstance()
390                .schedulePrintingDetails(resultModel.getFoundObjects(),
391                                         resultModel.getSearchGroup());
392     }
393     
394     /**
395      */

396     private void setRootDisplayName(String JavaDoc displayName) {
397         treeModel.setRootDisplayName(displayName);
398     }
399     
400     @Override JavaDoc
401     protected void componentOpened() {
402         assert EventQueue.isDispatchThread();
403         
404         Manager.getInstance().searchWindowOpened();
405         
406         setRootDisplayName(getInitialRootNodeText());
407         /*selectAndActivateNode(root);*/
408         if (lastSearchNodes == null) {
409             btnModifySearch.setEnabled(false);
410         }
411     }
412
413     @Override JavaDoc
414     protected void componentClosed() {
415         assert EventQueue.isDispatchThread();
416         
417         rememberInput(null, null);
418         Manager.getInstance().searchWindowClosed();
419         
420         if (contextView != null) {
421             contextView.unbindFromTreeSelection(tree);
422             contextView = null;
423         }
424         if (splitPane != null) {
425             rememberDividerLocation();
426             resultsPanel.remove(splitPane);
427             splitPane = null;
428         }
429         if (issuesPanel != null) {
430             removeIssuesPanel();
431         }
432         contextViewVisible = false;
433         contextViewEnabled = SHOW_CONTEXT_BY_DEFAULT;
434     }
435     
436     /**
437      * Displays a message informing about the task which blocks the search
438      * from being started. The search may also be blocked by a not yet finished
439      * previous search task.
440      *
441      * @param blockingTask constant identifying the blocking task
442      * @see Manager#SEARCHING
443      * @see Manager#CLEANING_RESULT
444      * @see Manager#PRINTING_DETAILS
445      */

446     void notifySearchPending(final int blockingTask) {
447         assert EventQueue.isDispatchThread();
448         
449         removeIssuesPanel();
450         
451         String JavaDoc msgKey = null;
452         switch (blockingTask) {
453             case Manager.SEARCHING:
454                 msgKey = "TEXT_FINISHING_PREV_SEARCH"; //NOI18N
455
break;
456             case Manager.CLEANING_RESULT:
457                 msgKey = "TEXT_CLEANING_RESULT"; //NOI18N
458
break;
459             case Manager.PRINTING_DETAILS:
460                 msgKey = "TEXT_PRINTING_DETAILS"; //NOI18N
461
break;
462             default:
463                 assert false;
464         }
465         setRootDisplayName(NbBundle.getMessage(ResultView.class, msgKey));
466         btnStop.setEnabled(true);
467         btnReplace.setEnabled(false);
468     }
469     
470     /**
471      */

472     void searchTaskStateChanged(final int changeType) {
473         switch (changeType) {
474             case Manager.EVENT_SEARCH_STARTED:
475                 searchStarted();
476                 break;
477             case Manager.EVENT_SEARCH_FINISHED:
478                 searchFinished();
479                 break;
480             case Manager.EVENT_SEARCH_INTERRUPTED:
481                 searchInterrupted();
482                 break;
483             case Manager.EVENT_SEARCH_CANCELLED:
484                 searchCancelled();
485                 break;
486             default:
487                 assert false;
488         }
489     }
490     
491     /**
492      */

493     void showAllDetailsFinished() {
494         assert EventQueue.isDispatchThread();
495         
496         updateShowAllDetailsBtn();
497     }
498     
499     /**
500      */

501     private void searchStarted() {
502         assert EventQueue.isDispatchThread();
503         
504         removeIssuesPanel();
505         
506         setRootDisplayName(NbBundle.getMessage(ResultView.class,
507                                                 "TEXT_SEARCHING___")); //NOI18N
508
nodeCountFormat = new MessageFormat JavaDoc(
509                 NbBundle.getMessage(getClass(),
510                                     "TXT_RootSearchedNodes")); //NOI18N
511
nodeCountFormatFullText = new MessageFormat JavaDoc(
512                 NbBundle.getMessage(getClass(),
513                                     "TXT_RootSearchedNodesFulltext")); //NOI18N
514

515         searchInProgress = true;
516         updateShowAllDetailsBtn();
517         btnModifySearch.setEnabled(true);
518         btnStop.setEnabled(true);
519         btnReplace.setEnabled(false);
520     }
521     
522     /**
523      */

524     private void searchFinished() {
525         assert EventQueue.isDispatchThread();
526         
527         setFinalRootNodeText();
528         
529         searchInProgress = false;
530         hasDetails = (resultModel != null) ? resultModel.hasDetails() : false;
531         updateShowAllDetailsBtn();
532         btnStop.setEnabled(false);
533         btnReplace.setEnabled(true);
534     }
535     
536     /**
537      */

538     private void searchInterrupted() {
539         assert EventQueue.isDispatchThread();
540         
541         searchFinished();
542     }
543     
544     /**
545      */

546     private void searchCancelled() {
547         assert EventQueue.isDispatchThread();
548         
549         setRootDisplayName(NbBundle.getMessage(ResultView.class,
550                                                "TEXT_TASK_CANCELLED"));//NOI18N
551

552         searchInProgress = true;
553         updateShowAllDetailsBtn();
554         btnStop.setEnabled(false);
555         btnReplace.setEnabled(true);
556     }
557     
558     /**
559      */

560     private void setFinalRootNodeText() {
561         assert EventQueue.isDispatchThread();
562         
563         int resultSize = resultModel.size();
564         
565         if (resultModel.wasLimitReached()) {
566             setRootDisplayName(
567                     NbBundle.getMessage(ResultView.class,
568                                         "TEXT_MSG_FOUND_X_NODES_LIMIT", //NOI18N
569
new Integer JavaDoc(resultSize)));
570             return;
571         }
572         
573         String JavaDoc baseMsg;
574         if (resultSize == 0) {
575             baseMsg = NbBundle.getMessage(ResultView.class,
576                                           "TEXT_MSG_NO_NODE_FOUND"); //NOI18N
577
} else {
578             String JavaDoc bundleKey;
579             Object JavaDoc[] args;
580             if (resultModel.searchAndReplace) {
581                 bundleKey = "TEXT_MSG_FOUND_X_NODES_REPLACE"; //NOI18N
582
args = new Object JavaDoc[4];
583             } else if (resultModel.fullTextOnly) {
584                 bundleKey = "TEXT_MSG_FOUND_X_NODES_FULLTEXT"; //NOI18N
585
args = new Object JavaDoc[2];
586             } else {
587                 bundleKey = "TEXT_MSG_FOUND_X_NODES"; //NOI18N
588
args = new Object JavaDoc[1];
589             }
590             args[0] = new Integer JavaDoc(objectsCount);
591             if (args.length > 1) {
592                 args[1] = new Integer JavaDoc(resultModel.getTotalDetailsCount());
593             }
594             if (args.length > 2) {
595                args[2] = resultModel.fullTextSearchType.getMatchTemplateDescr();
596                args[3] = resultModel.fullTextSearchType.getReplaceString();
597             }
598             baseMsg = NbBundle.getMessage(getClass(), bundleKey, args);
599         }
600         String JavaDoc exMsg = resultModel.getExceptionMsg();
601         String JavaDoc msg = exMsg == null ? baseMsg
602                                    : baseMsg + " (" + exMsg + ")"; //NOI18N
603
setRootDisplayName(msg);
604     }
605     
606     /**
607      */

608     private void updateShowAllDetailsBtn() {
609         assert EventQueue.isDispatchThread();
610         
611         btnShowDetails.setEnabled(hasResults
612                                   && !searchInProgress
613                                   && hasDetails);
614     }
615     
616     /** Set new model. */
617     void setResultModel(final ResultModel resultModel) {
618         assert EventQueue.isDispatchThread();
619         
620         if ((this.resultModel == null) && (resultModel == null)) {
621             return;
622         }
623         
624         boolean hadCheckBoxes = (this.resultModel != null)
625                                 && this.resultModel.searchAndReplace;
626         boolean hasCheckBoxes = (resultModel != null)
627                                 && resultModel.searchAndReplace;
628         
629         this.resultModel = resultModel; //may be null!
630

631         tree.setModel(treeModel = new ResultTreeModel(resultModel));
632         if (hasCheckBoxes != hadCheckBoxes) {
633             tree.setCellRenderer(new NodeRenderer(hasCheckBoxes));
634             btnReplace.setVisible(hasCheckBoxes);
635         }
636         if (resultModel != null) {
637             hasResults = !resultModel.isEmpty();
638             hasDetails = hasResults && resultModel.hasDetails();
639             resultModel.setObserver(this);
640         } else {
641             hasResults = false;
642             hasDetails = false;
643         }
644
645         resultModelChanged();
646         
647         updateShowAllDetailsBtn();
648     }
649     
650     /**
651      */

652     private void resultModelChanged() {
653         updateDisplayContextButton();
654         updateContextViewVisibility();
655         if (contextView != null) {
656             contextView.setResultModel(resultModel);
657         }
658         nodeListener.setSelectionChangeEnabled(true);
659         
660         btnPrev.setEnabled(resultModel != null);
661         btnNext.setEnabled(resultModel != null);
662         resetMatchingObjIndexCache();
663         
664         objectsCount = 0;
665     }
666     
667     /**
668      * Checks whether this result view displays search results for operation
669      * <em>search &amp; replace</em> or for plain search.
670      *
671      * @return {@code true} if results for <em>search &amp; replace</em>
672      * are displayed, {@code false} otherwise
673      */

674     private boolean isSearchAndReplace() {
675         return (resultModel != null) && resultModel.searchAndReplace;
676     }
677     
678     /**
679      * Enables or disables the <em>Display Context</em> button,
680      * according to the result model currently displayed.
681      *
682      * @see #updateContextViewVisibility
683      */

684     private void updateDisplayContextButton() {
685         boolean searchAndReplace = isSearchAndReplace();
686         btnDisplayContext.setEnabled(searchAndReplace);
687         
688         ignoreContextButtonToggle = true;
689         try {
690             btnDisplayContext.setSelected(searchAndReplace
691                                           && contextViewEnabled);
692         } finally {
693             ignoreContextButtonToggle = false;
694         }
695     }
696     
697     /**
698      * Shows or hides the context view, according to the state of the
699      * <em>Display Context</em> button.
700      *
701      * @see #updateDisplayContextButton
702      */

703     private void updateContextViewVisibility() {
704         setContextViewVisible(isSearchAndReplace() && contextViewEnabled);
705     }
706
707     /**
708      * Shows or hides the context view.
709      */

710     private void setContextViewVisible(boolean visible) {
711         assert EventQueue.isDispatchThread();
712         assert (splitPane == null) == (contextView == null);
713         
714         final int componentCount = resultsPanel.getComponentCount();
715         if ((visible == contextViewVisible) && (componentCount != 0)) {
716             return;
717         }
718         
719         this.contextViewVisible = visible;
720         
721         final String JavaDoc cardName;
722         if (visible == false) {
723             cardName = "tree only"; //NOI18N
724
/*
725              * This code is executed either the first time the result view
726              * is displayed or when the context view is closed.
727              * In the former case, we must add the tree view simply because
728              * the result view does not contain it yet.
729              * In the latter case, we must add it, too, because it was
730              * removed from the resultsPanel the last time the context view
731              * was displayed.
732              */

733             assert componentCount < 2;
734             /*
735              * The following line removes the treeView from the splitPane
736              * as a side-effect.
737              */

738             resultsPanel.add(treeView, cardName);
739             if (contextView != null) {
740                 contextView.unbindFromTreeSelection(tree);
741                 rememberDividerLocation();
742             }
743         } else {
744             assert resultModel != null;
745             
746             cardName = "tree and context"; //NOI18N
747
if (splitPane == null) {
748                 contextView = new ContextView(resultModel);
749                 splitPane = new JSplitPane JavaDoc(JSplitPane.HORIZONTAL_SPLIT,
750                                            true, //continuous layout
751
treeView, //left pane
752
contextView); //right pane
753
splitPane.setBorder(BorderFactory.createEmptyBorder());
754                 splitPane.setResizeWeight(0.4d);
755                 resultsPanel.add(splitPane, cardName);
756             } else {
757                 /*
758                  * The following line removes the treeView from the resultsPanel
759                  * as a side-effect.
760                  */

761                 splitPane.setLeftComponent(treeView);
762             }
763             setDividerLocation();
764             contextView.bindToTreeSelection(tree);
765         }
766         /*
767          * Changing cards hides the component that represented the previous
768          * card and shows the new card. In this case, if card "tree only"
769          * is going to be replaced with "tree and context", component 'treeView'
770          * is going to be hidden. But we want 'treeView' to be visible - just
771          * at a different context (inside the split pane). To ensure visibility
772          * of 'treeView', we must make call setVisible(true) explicitely
773          * after the card swap.
774          */

775         resultViewCards.show(resultsPanel, cardName);
776         treeView.setVisible(true);
777     }
778     
779     /**
780      */

781     private void rememberDividerLocation() {
782         if (splitPane == null) {
783             return;
784         }
785         
786         dividerLocation
787                = (double) splitPane.getDividerLocation()
788                  / (double) (splitPane.getWidth() - splitPane.getDividerSize());
789     }
790     
791     /**
792      */

793     private void setDividerLocation() {
794         assert splitPane != null;
795         
796         if (dividerLocation != -1.0d) {
797             splitPane.setDividerLocation(dividerLocation);
798         }
799     }
800     
801     /**
802      */

803     void objectFound(Object JavaDoc foundObject, final int totalDetailsCount) {
804         assert !EventQueue.isDispatchThread();
805         
806         EventQueue.invokeLater(new Runnable JavaDoc() {
807             public void run() {
808                 updateObjectsCount(totalDetailsCount);
809             }
810         });
811     }
812     
813     /**
814      * Updates the number of found nodes in the name of the root node.
815      */

816     private void updateObjectsCount(final int totalDetailsCount) {
817         assert EventQueue.isDispatchThread();
818         
819         objectsCount++;
820         hasResults = true;
821         
822         setRootDisplayName(
823                 resultModel.fullTextOnly
824                 ? nodeCountFormatFullText.format(
825                             new Object JavaDoc[] {new Integer JavaDoc(objectsCount),
826                                           new Integer JavaDoc(totalDetailsCount)})
827                 : nodeCountFormat.format(
828                             new Object JavaDoc[] {new Integer JavaDoc(objectsCount)}));
829     }
830     
831     /**
832      */

833     void removeIssuesPanel() {
834         if (issuesPanel != null) {
835             remove(issuesPanel);
836             issuesPanel = null;
837         }
838         contentCards.show(this, RESULTS_CARD);
839     }
840     
841     /**
842      * Jumps to the next or previous match.
843      *
844      * @param forward {@code true} for the <em>next</em> match,
845      * {@code false} for the <em>previous</em> match
846      * @see #goToPrev()
847      */

848     private void goToNext(final boolean forward) {
849         assert EventQueue.isDispatchThread();
850         assert resultModel != null;
851         
852         if (!hasResults) {
853             return;
854         }
855         
856         TreePath JavaDoc leadPath = tree.getLeadSelectionPath();
857         if (leadPath == null) {
858             leadPath = new TreePath JavaDoc(tree.getModel().getRoot());
859         }
860         
861         final TreePath JavaDoc nextPath = findNextPath(leadPath, forward);
862         
863         if (nextPath != null) {
864             /*
865              * If we did not expand the parent path explicitely,
866              * attached TreeSelectionListeners might not be able to get
867              * row for the 'nextPath' because of the parent path still
868              * collapsed.
869              */

870             tree.expandPath(nextPath.getParentPath());
871             
872             tree.setSelectionPath(nextPath);
873             tree.scrollRectToVisible(tree.getPathBounds(nextPath));
874         }
875     }
876     
877     /**
878      */

879     private TreePath JavaDoc findNextPath(final TreePath JavaDoc forPath,
880                                   final boolean forward) {
881         TreePath JavaDoc nextPath;
882         
883         Object JavaDoc root;
884         TreePath JavaDoc parentPath = forPath.getParentPath();
885         if (parentPath == null) { //selected = root node
886
root = forPath.getLastPathComponent();
887             nextPath = forward ? getNextDetail(root, null, -1, forward) : null;
888         } else {
889             MatchingObject matchingObj;
890             Object JavaDoc lastComp = forPath.getLastPathComponent();
891             if (lastComp.getClass() == MatchingObject.class) {
892                 root = parentPath.getLastPathComponent();
893                 matchingObj = (MatchingObject) lastComp;
894                 nextPath = getNextDetail(root, matchingObj, -1, forward);
895             } else {
896                 root = parentPath.getParentPath().getLastPathComponent();
897                 Object JavaDoc parentComp = parentPath.getLastPathComponent();
898                 assert parentComp.getClass() == MatchingObject.class;
899                 matchingObj = (MatchingObject) parentComp;
900                 
901                 int parentPathRow = tree.getRowForPath(parentPath);
902                 int row = tree.getRowForPath(forPath);
903                 int index = row - parentPathRow - 1;
904                 nextPath = getNextDetail(root, matchingObj, index, forward);
905             }
906         }
907         
908         return nextPath;
909     }
910     
911     /**
912      * Finds path to the first detail node following or preceding
913      * the node specified by couple ({@code MatchingObject}, detail index).
914      *
915      * @param root root object of the tree model
916      * (it will be used as a first component of any
917      * non-{@code null} returned path)
918      * @param matchingObj the currently selected {@code MatchingObject},
919      * or {@code null} if the tree's root is selected
920      * @param detailIndex index of the currently selected detail node,
921      * or {@code -1} if no detail node is selected
922      * @param forward {@code true} for forward search,
923      * {@code false} for backward search
924      * @return path to the next detail node,
925      * or {@code null} if no next detail node is available
926      */

927     private TreePath JavaDoc getNextDetail(final Object JavaDoc root,
928                                    final MatchingObject matchingObj,
929                                    final int detailIndex,
930                                    final boolean forward) {
931         if (matchingObj != null) {
932             int nextDetailIndex = forward ? detailIndex + 1
933                                           : detailIndex - 1;
934             if ((nextDetailIndex >= 0)
935                     && (nextDetailIndex < resultModel.getDetailsCount(
936                                                                 matchingObj))) {
937                 return new TreePath JavaDoc(new Object JavaDoc[] {
938                          root,
939                          matchingObj,
940                          resultModel.getDetails(matchingObj)[nextDetailIndex]});
941             }
942         } else /*(matchingObj == null)*/ if (!forward) {
943             return null;
944         }
945         
946         final MatchingObject[] matchingObjs = resultModel.getMatchingObjects();
947         int currMatchingObjIndex = getMatchingObjIndex(matchingObjs,
948                                                        matchingObj,
949                                                        forward);
950         MatchingObject nextMatchingObj;
951         int i;
952         
953         if (forward) {
954             for (i = currMatchingObjIndex + 1; i < matchingObjs.length; i++) {
955                 nextMatchingObj = matchingObjs[i];
956                 if (resultModel.hasDetails(nextMatchingObj)) {
957                     return new TreePath JavaDoc(new Object JavaDoc[] {
958                             root,
959                             nextMatchingObj,
960                             resultModel.getDetails(nextMatchingObj)[0]});
961                 }
962             }
963         } else {
964             for (i = currMatchingObjIndex - 1; i >= 0; i--) {
965                 nextMatchingObj = matchingObjs[i];
966                 if (resultModel.hasDetails(nextMatchingObj)) {
967                     Node[] details = resultModel.getDetails(nextMatchingObj);
968                     return new TreePath JavaDoc(new Object JavaDoc[] {
969                             root,
970                             nextMatchingObj,
971                             details[details.length - 1]});
972                 }
973             }
974         }
975         return null;
976     }
977     
978     private MatchingObject matchingObjIndexCacheObj = null;
979     private int matchingObjIndexCacheIndex = -1;
980     
981     /**
982      */

983     private void resetMatchingObjIndexCache() {
984         matchingObjIndexCacheObj = null;
985         matchingObjIndexCacheIndex = -1;
986     }
987     
988     /**
989      */

990     private int getMatchingObjIndex(final MatchingObject[] matchingObjs,
991                                     final MatchingObject matchingObj,
992                                     final boolean forward) {
993         if (matchingObj == null) {
994             return -1;
995         }
996         
997         if (matchingObj == matchingObjIndexCacheObj) {
998             assert (matchingObjIndexCacheIndex != -1);
999             return matchingObjIndexCacheIndex;
1000        }
1001        
1002        int foundIndex = -1;
1003        
1004        /* Probe several positions below and above the cached index: */
1005        if (matchingObjIndexCacheIndex != -1) {
1006            final int quickSearchRange = 3;
1007            int startIndex;
1008            int endIndex;
1009            int i;
1010            if (forward) {
1011                startIndex = Math.min(matchingObjIndexCacheIndex + 1,
1012                                      matchingObjs.length - 1);
1013                endIndex = Math.min(
1014                                  matchingObjIndexCacheIndex + quickSearchRange,
1015                                  matchingObjs.length - 1);
1016                for (i = startIndex; i <= endIndex; i++) {
1017                    if (matchingObjs[i] == matchingObj) {
1018                        foundIndex = i;
1019                        break;
1020                    }
1021                }
1022                if ((foundIndex == -1) && (matchingObjIndexCacheIndex > 0)) {
1023                    if (matchingObjs[i = matchingObjIndexCacheIndex - 1]
1024                            == matchingObj) {
1025                        foundIndex = i;
1026                    }
1027                }
1028            } else { /*backward*/
1029                startIndex = Math.max(matchingObjIndexCacheIndex - 1, 0);
1030                endIndex = Math.max(
1031                                  matchingObjIndexCacheIndex - quickSearchRange,
1032                                  0);
1033                for (i = startIndex; i >= endIndex; i--) {
1034                    if (matchingObjs[i] == matchingObj) {
1035                        foundIndex = i;
1036                        break;
1037                    }
1038                }
1039                if ((foundIndex == -1)
1040                    && (matchingObjIndexCacheIndex < matchingObjs.length - 1)) {
1041                    if (matchingObjs[i = matchingObjIndexCacheIndex + 1]
1042                            == matchingObj) {
1043                        foundIndex = i;
1044                    }
1045                }
1046            }
1047        }
1048        
1049        /* Nothing found near the cached position - search from the beginning */
1050        if (foundIndex == -1) {
1051            for (int i = 0; i < matchingObjs.length; i++) {
1052                if (matchingObj == matchingObjs[i]) {
1053                    foundIndex = i;
1054                    break;
1055                }
1056            }
1057        }
1058        
1059        /* If the matching index is found, store it to the cache: */
1060        if (foundIndex != -1) {
1061            matchingObjIndexCacheObj = matchingObj;
1062            matchingObjIndexCacheIndex = foundIndex;
1063        }
1064        
1065        return foundIndex;
1066    }
1067    
1068    /**
1069     */

1070    void rememberInput(Node[] nodes,
1071                       List JavaDoc enabledSearchTypes) {
1072        lastSearchNodes = nodes;
1073        lastEnabledSearchTypes = enabledSearchTypes;
1074    }
1075    
1076    /** (Re)open the dialog window for entering (new) search criteria. */
1077    private void customizeCriteria() {
1078        assert EventQueue.isDispatchThread();
1079        
1080        Node[] nodesToSearch;
1081        List JavaDoc searchTypes;
1082        
1083        assert (lastSearchNodes != null);
1084        nodesToSearch = lastSearchNodes;
1085        searchTypes = lastEnabledSearchTypes;
1086            
1087        /*
1088        if (resultModel != null) {
1089            nodesToSearch = resultModel.getSearchGroup().getSearchRoots();
1090            searchTypes = resultModel.getEnabledSearchTypes();
1091        } else {
1092            Node repositoryNode = RepositoryNodeFactory.getDefault()
1093                                  .repository(DataFilter.ALL);
1094            nodesToSearch = new Node[] {repositoryNode};
1095            searchTypes = SearchPerformer.getTypes(nodesToSearch);
1096        }
1097         */

1098
1099        /* Clone the list (deep copy): */
1100        List JavaDoc searchTypesClone = new ArrayList JavaDoc(searchTypes.size());
1101        for (Iterator JavaDoc it = searchTypes.iterator(); it.hasNext(); ) {
1102            searchTypesClone.add(((SearchType) it.next()).clone());
1103        }
1104        
1105        lastEnabledSearchTypes = searchTypesClone;
1106        
1107        SearchPanel searchPanel = new SearchPanel(searchTypesClone, true);
1108        searchPanel.showDialog();
1109        
1110        if (searchPanel.getReturnStatus() != SearchPanel.RET_OK) {
1111            return;
1112        }
1113        
1114        rememberInput(nodesToSearch,
1115                      Utils.cloneSearchTypes(searchTypesClone));
1116
1117        Manager.getInstance().scheduleSearchTask(
1118                new SearchTask(nodesToSearch,
1119                               searchTypesClone,
1120                               searchPanel.getCustomizedSearchTypes()));
1121    }
1122    
1123    /**
1124     * Called when the <em>Replace</em> button is pressed.
1125     */

1126    private void replaceMatches() {
1127        assert EventQueue.isDispatchThread();
1128        
1129        nodeListener.setSelectionChangeEnabled(false);
1130        btnReplace.setEnabled(false);
1131        
1132        Manager.getInstance().scheduleReplaceTask(
1133                        new ReplaceTask(resultModel.getMatchingObjects()));
1134    }
1135    
1136    /**
1137     */

1138    void closeAndSendFocusToEditor() {
1139        assert EventQueue.isDispatchThread();
1140        
1141        close();
1142        
1143        Mode m = WindowManager.getDefault().findMode("editor"); //NOI18N
1144
if (m != null) {
1145            TopComponent tc = m.getSelectedTopComponent();
1146            if (tc != null) {
1147                tc.requestActive();
1148            }
1149        }
1150    }
1151        
1152    /**
1153     */

1154    void displayIssuesToUser(String JavaDoc title, String JavaDoc[] problems, boolean reqAtt) {
1155        assert EventQueue.isDispatchThread();
1156        assert issuesPanel == null;
1157        
1158        issuesPanel = new IssuesPanel(title, problems);
1159        add(issuesPanel, ISSUES_CARD);
1160        contentCards.show(this, ISSUES_CARD);
1161        
1162        if (!isOpened()) {
1163            open();
1164        }
1165        if (reqAtt) {
1166            requestAttention(true);
1167        }
1168    }
1169    
1170    /**
1171     */

1172    void rescan() {
1173        assert EventQueue.isDispatchThread();
1174        
1175        removeIssuesPanel();
1176        Manager.getInstance().scheduleSearchTaskRerun();
1177    }
1178
1179    @Override JavaDoc
1180    protected String JavaDoc preferredID() {
1181        return getClass().getName();
1182    }
1183    
1184    /**
1185     */

1186    private class ButtonListener implements ActionListener JavaDoc, ItemListener JavaDoc {
1187        
1188        public void actionPerformed(ActionEvent JavaDoc e) {
1189            Object JavaDoc source = e.getSource();
1190            if (source == btnStop) {
1191                Manager.getInstance().stopSearching();
1192            } else if (source == btnModifySearch) {
1193                customizeCriteria();
1194            } else if (source == btnShowDetails) {
1195                fillOutput();
1196            } else if (source == btnReplace) {
1197                replaceMatches();
1198            } else if (source == btnPrev) {
1199                goToNext(false);
1200            } else if (source == btnNext) {
1201                goToNext(true);
1202            } else {
1203                assert false;
1204            }
1205        }
1206
1207        /**
1208         * Called when the Display Context button is selected or deselected.
1209         */

1210        public void itemStateChanged(ItemEvent JavaDoc e) {
1211            assert e.getSource() == btnDisplayContext;
1212            if (!ignoreContextButtonToggle) {
1213                contextViewEnabled = (e.getStateChange() == ItemEvent.SELECTED);
1214                updateContextViewVisibility();
1215            }
1216        }
1217        
1218    }
1219    
1220}
Popular Tags