KickJava   Java API By Example, From Geeks To Geeks.

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


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-2006 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.Dimension JavaDoc;
25 import java.awt.EventQueue JavaDoc;
26 import java.awt.Rectangle JavaDoc;
27 import javax.swing.BorderFactory JavaDoc;
28 import javax.swing.Box JavaDoc;
29 import javax.swing.JEditorPane JavaDoc;
30 import javax.swing.JLabel JavaDoc;
31 import javax.swing.JPanel JavaDoc;
32 import javax.swing.JScrollBar JavaDoc;
33 import javax.swing.JScrollPane JavaDoc;
34 import javax.swing.JTree JavaDoc;
35 import javax.swing.SwingConstants JavaDoc;
36 import javax.swing.UIManager JavaDoc;
37 import javax.swing.border.Border JavaDoc;
38 import javax.swing.event.TreeSelectionEvent JavaDoc;
39 import javax.swing.event.TreeSelectionListener JavaDoc;
40 import javax.swing.text.BadLocationException JavaDoc;
41 import javax.swing.text.Document JavaDoc;
42 import javax.swing.text.Element JavaDoc;
43 import javax.swing.text.StyledDocument JavaDoc;
44 import javax.swing.tree.TreePath JavaDoc;
45 import org.netbeans.modules.search.types.TextDetail;
46 import org.openide.ErrorManager;
47 import org.openide.text.NbDocument;
48 import org.openide.util.NbBundle;
49 import org.openide.util.RequestProcessor;
50 import static java.lang.Thread.NORM_PRIORITY JavaDoc;
51
52 /**
53  * Panel for displaying context of a matching string within a file.
54  * When a node representing a matching string is selected in the tree
55  * of results, this panel displays a part of the file surrounding the selected
56  * matching string, with the matching string highlighted.
57  * When a node representing the whole file is selected, the beginning
58  * of the file is displayed.
59  *
60  * @author Tim Boudreau
61  * @author Marian Petras
62  */

63 final class ContextView extends JPanel JavaDoc implements TreeSelectionListener JavaDoc {
64     
65     /** */
66     private static final String JavaDoc FILE_VIEW = "file view"; //NOI18N
67
/** */
68     private static final String JavaDoc MESSAGE_VIEW = "message view"; //NOI18N
69

70     /** */
71     private final CardLayout JavaDoc cardLayout;
72     /** editor pane actually displaying (part of) the file */
73     private final JEditorPane JavaDoc editorPane = new JEditorPane JavaDoc();
74     /** scroll pane containing the editor pane */
75     private final JScrollPane JavaDoc editorScroll;
76     /** displays location of the file above the editor pane */
77     private final JLabel JavaDoc lblPath = new JLabel JavaDoc();
78     /** displays message if no file is displayed */
79     private final JLabel JavaDoc lblMessage = new JLabel JavaDoc();
80     /**
81      * displays content of file after it has been asynchronously loaded
82      * by the {@link #requestProcessor}
83      */

84     private final Displayer displayer = new Displayer();
85     /** used for asynchronous loading of files' contents */
86     private final RequestProcessor requestProcessor
87             = new RequestProcessor("TextView", NORM_PRIORITY, true); //NOI18N
88

89     /** */
90     private ResultModel resultModel;
91     /** */
92     private RequestProcessor.Task task = null;
93     /** */
94     private TextFetcher textFetcher = null;
95     /** */
96     private String JavaDoc displayedCard = null;
97     /** */
98     private String JavaDoc msgNoFileSelected = null;
99     /** */
100     private String JavaDoc msgMultipleFilesSelected = null;
101     /** the current MIME-type set for the {@link #editorPane} */
102     private String JavaDoc editorMimeType = null;
103     
104     /**
105      *
106      * @author Tim Boudreau
107      * @author Marian Petras
108      */

109     public ContextView(ResultModel resultModel) {
110         Border JavaDoc b = BorderFactory.createCompoundBorder(
111                 BorderFactory.createMatteBorder( //outside border
112
0, 0, 1, 0,
113                                 UIManager.getColor("controlShadow")), //NOI18N
114
BorderFactory.createEmptyBorder( //inside border
115
5, 5, 1, 5));
116         lblPath.setBorder(b);
117         
118         editorPane.setEditable(false);
119         editorPane.getCaret().setBlinkRate(0);
120         
121         editorScroll = new JScrollPane JavaDoc(editorPane);
122         editorScroll.setViewportBorder(BorderFactory.createEmptyBorder());
123         editorScroll.setBorder(BorderFactory.createEmptyBorder());
124         
125         JPanel JavaDoc fileViewPanel = new JPanel JavaDoc();
126         fileViewPanel.setLayout(new BorderLayout JavaDoc());
127         fileViewPanel.add(lblPath, BorderLayout.NORTH);
128         fileViewPanel.add(editorScroll, BorderLayout.CENTER);
129         
130         Box JavaDoc messagePanel = Box.createVerticalBox();
131         messagePanel.add(Box.createVerticalGlue());
132         messagePanel.add(lblMessage);
133         messagePanel.add(Box.createVerticalGlue());
134         lblMessage.setAlignmentX(0.5f);
135         lblMessage.setHorizontalAlignment(SwingConstants.CENTER);
136         lblMessage.setEnabled(false);
137         
138         setLayout(cardLayout = new CardLayout JavaDoc());
139         add(fileViewPanel, FILE_VIEW);
140         add(messagePanel, MESSAGE_VIEW);
141         
142         setResultModel(resultModel);
143     }
144     
145     @Override JavaDoc
146     public Dimension JavaDoc getMinimumSize() {
147         /*
148          * Without this, the minimum width would be equal to the width
149          * of the {@linkplain #lblPath file path label}.
150          */

151         Dimension JavaDoc minSize = super.getMinimumSize();
152         minSize.width = 0;
153         return minSize;
154     }
155     
156     /**
157      */

158     void setResultModel(ResultModel resultModel) {
159         if (resultModel == this.resultModel) {
160             return;
161         }
162         
163         synchronized (this) { //PENDING - review synchronization
164
if (textFetcher != null) {
165                 textFetcher.cancel();
166                 textFetcher = null;
167             }
168         }
169         this.resultModel = resultModel;
170     }
171     
172     /**
173      */

174     void bindToTreeSelection(final JTree JavaDoc tree) {
175         assert EventQueue.isDispatchThread();
176         
177         displaySelectedFiles(tree);
178         tree.addTreeSelectionListener(this);
179     }
180     
181     /**
182      */

183     void unbindFromTreeSelection(final JTree JavaDoc tree) {
184         assert EventQueue.isDispatchThread();
185         
186         tree.removeTreeSelectionListener(this);
187         
188         synchronized (this) { //PENDING - review synchronization
189
if (textFetcher != null) {
190                 textFetcher.cancel();
191                 textFetcher = null;
192             }
193         }
194     }
195
196     /**
197      * Called when selection of nodes in the result tree changes.
198      */

199     public void valueChanged(TreeSelectionEvent JavaDoc e) {
200         displaySelectedFiles((JTree JavaDoc) e.getSource());
201     }
202     
203     /**
204      * Displays file(s) selected in the given tree.
205      *
206      * @author Marian Petras
207      */

208     private void displaySelectedFiles(final JTree JavaDoc tree) {
209         final TreePath JavaDoc[] selectedPaths = tree.getSelectionPaths();
210         if ((selectedPaths == null) || (selectedPaths.length == 0)) {
211             displayNoFileSelected();
212         } else if (selectedPaths.length > 1) {
213             displayMultipleItemsSelected();
214         } else {
215             assert selectedPaths.length == 1;
216             
217             final TreePath JavaDoc path = selectedPaths[0];
218             int pathCount = path.getPathCount();
219             if (pathCount == 1) { //root node selected
220
displayNoFileSelected();
221             } else {
222                 assert pathCount == 2 || pathCount == 3;
223                 MatchingObject matchingObj;
224                 int matchIndex;
225                 if (pathCount == 2) { //file node selected
226
matchingObj = (MatchingObject) path.getLastPathComponent();
227                     matchIndex = -1;
228                 } else { //match node selected
229
TreePath JavaDoc matchingObjPath = path.getParentPath();
230                     matchingObj = (MatchingObject)
231                                   matchingObjPath.getLastPathComponent();
232                     int matchingObjRow = tree.getRowForPath(matchingObjPath);
233                     int matchRow = tree.getRowForPath(path);
234                     matchIndex = matchRow - matchingObjRow - 1;
235                 }
236                 displayFile(matchingObj, matchIndex);
237             }
238         }
239     }
240     
241     /**
242      */

243     private void displayNoFileSelected() {
244         if (msgNoFileSelected == null) {
245             msgNoFileSelected = NbBundle.getMessage(
246                                             getClass(),
247                                             "MsgNoFileSelected"); //NOI18N
248
}
249         displayMessage(msgNoFileSelected);
250     }
251     
252     /**
253      */

254     private void displayMultipleItemsSelected() {
255         if (msgMultipleFilesSelected == null) {
256             msgMultipleFilesSelected = NbBundle.getMessage(
257                                             getClass(),
258                                             "MsgMultipleFilesSelected");//NOI18N
259
}
260         displayMessage(msgMultipleFilesSelected);
261     }
262     
263     /**
264      */

265     private void displayMessage(String JavaDoc message) {
266         lblMessage.setText(message);
267         if (displayedCard != MESSAGE_VIEW) {
268             cardLayout.show(this, displayedCard = MESSAGE_VIEW);
269         }
270     }
271     
272     /**
273      * @author Tim Boudreau
274      * @author Marian Petras
275      */

276     private void displayFile(final MatchingObject matchingObj,
277                              final int partIndex) {
278         assert EventQueue.isDispatchThread();
279         
280         synchronized (displayer) { //PENDING - review synchronization
281
if (task != null) {
282                 task.cancel();
283                 task = null;
284             }
285             
286             final Item item = new Item(resultModel, matchingObj, partIndex);
287             
288             MatchingObject.InvalidityStatus invalidityStatus
289                                             = matchingObj.checkValidity();
290             if (invalidityStatus != null) {
291                 displayMessage(invalidityStatus.getDescription(
292                                             matchingObj.getFile().getPath()));
293                 return;
294             }
295             
296             requestText(item, displayer);
297             String JavaDoc description = matchingObj.getDescription();
298             lblPath.setText(description);
299             lblPath.setToolTipText(description); //in case it doesn't fit
300
}
301     }
302     
303     /**
304      * Fetch the text of an {@code Item}. Since the text is retrieved
305      * asynchronously, this method is passed a {@code TextDisplayer},
306      * which will get its {@code setText()} method called on the event thread
307      * after it has been loaded on a background thread.
308      *
309      * @param item item to be displayed by the text displayer
310      * @param textDisplayer displayer that should display the item
311      *
312      * @author Tim Boudreau
313      */

314     private void requestText(Item item, TextDisplayer textDisplayer) {
315         assert EventQueue.isDispatchThread();
316         
317         synchronized (this) { //PENDING - review synchronization
318
if (textFetcher != null) {
319                 if (textFetcher.replaceLocation(item, textDisplayer)) {
320                     return;
321                 } else {
322                     textFetcher.cancel();
323                     textFetcher = null;
324                 }
325             }
326             if (textFetcher == null) {
327                 textFetcher = new TextFetcher(item,
328                                               textDisplayer,
329                                               requestProcessor);
330             }
331         }
332     }
333
334     /**
335      * Implementation of {@code TextDisplayer} which is passed to get the text
336      * of an item. The text is fetched from the file asynchronously, and then
337      * passed to {@link #setText()} to set the text, select the text the item
338      * represents and scroll it into view.
339      *
340      * @see TextReceiver
341      * @author Tim Boudreau
342      * @author Marian Petras
343      */

344     private class Displayer implements TextDisplayer, Runnable JavaDoc {
345         
346         private TextDetail location;
347         
348         /**
349          * @author Tim Boudreau
350          */

351         public void setText(final String JavaDoc text,
352                             String JavaDoc mimeType,
353                             final TextDetail location) {
354             assert EventQueue.isDispatchThread();
355             
356             if ("content/unknown".equals(mimeType)) { //NOI18N
357
mimeType = "text/plain"; //Good idea? Bad? Hmm... //NOI18N
358
}
359             
360             /*
361              * Changing content type clears the text - so the content type
362              * (in this case, MIME-type only) must be set _before_ the text
363              * is set.
364              */

365             if ((editorMimeType == null) || !editorMimeType.equals(mimeType)) {
366                 editorPane.setContentType(mimeType);
367                 editorMimeType = mimeType;
368             }
369             editorPane.setText(text);
370             
371             if (displayedCard != FILE_VIEW) {
372                 cardLayout.show(ContextView.this, displayedCard = FILE_VIEW);
373             }
374             
375             if (location != null) {
376                 //Let the L&F do anything it needs to do before we try to fiddle
377
//with it - get out of its way. Some Swing View classes don't
378
//have accurate position data until they've painted once.
379
this.location = location;
380                 EventQueue.invokeLater(this);
381             } else {
382                 scrollToTop();
383             }
384         }
385
386         /**
387          *
388          * @author Tim Boudreau
389          * @author Marian Petras
390          */

391         public void run() {
392             assert EventQueue.isDispatchThread();
393             
394             boolean scrolled = false;
395             try {
396                 if (!editorPane.isShowing()) {
397                     return;
398                 }
399                 
400                 if (location != null) {
401                     final Document JavaDoc document = editorPane.getDocument();
402                     if (document instanceof StyledDocument JavaDoc) {
403                         StyledDocument JavaDoc styledDocument
404                                 = (StyledDocument JavaDoc) document;
405                         int cursorOffset = getCursorOffset(
406                                                     (StyledDocument JavaDoc) document,
407                                                     location.getLine() - 1);
408                         int startOff = cursorOffset + location.getColumn() - 1;
409                         int endOff = startOff + location.getMarkLength();
410                         editorPane.setSelectionStart(startOff);
411                         editorPane.setSelectionEnd(endOff);
412                         Rectangle JavaDoc r = editorPane.modelToView(startOff);
413                         if (r != null) {
414                             //Editor kit not yet updated, what to do
415
editorPane.scrollRectToVisible(r);
416                             scrolled = true;
417                         }
418                     }
419                     editorPane.getCaret().setBlinkRate(0);
420                     editorPane.repaint();
421                 }
422             } catch (BadLocationException JavaDoc e) {
423                 //Maybe not even notify this - not all editors
424
//will have a 1:1 correspondence to file positions -
425
//it's perfectly reasonable for this to be thrown
426
ErrorManager.getDefault().notify( //PENDING - ErrorManager?
427
ErrorManager.INFORMATIONAL, e);
428             }
429             if (!scrolled) {
430                 scrollToTop();
431             }
432         }
433         
434         /**
435          * Computes cursor offset of a given line of a document.
436          * The line number must be non-negative.
437          * If the line number is greater than number of the last line,
438          * the returned offset corresponds to the last line of the document.
439          *
440          * @param doc document to computer offset for
441          * @param line line number (first line = <code>0</code>)
442          * @return cursor offset of the beginning of the given line
443          *
444          * @author Marian Petras
445          */

446         private int getCursorOffset(StyledDocument JavaDoc doc, int line) {
447             assert EventQueue.isDispatchThread();
448             assert line >= 0;
449
450             try {
451                 return NbDocument.findLineOffset(doc, line);
452             } catch (IndexOutOfBoundsException JavaDoc ex) {
453                 /* probably line number out of bounds */
454
455                 Element JavaDoc lineRootElement = NbDocument.findLineRootElement(doc);
456                 int lineCount = lineRootElement.getElementCount();
457                 if (line >= lineCount) {
458                     return NbDocument.findLineOffset(doc, lineCount - 1);
459                 } else {
460                     throw ex;
461                 }
462             }
463         }
464     
465         /**
466          */

467         private void scrollToTop() {
468             JScrollBar JavaDoc scrollBar;
469
470             scrollBar = editorScroll.getHorizontalScrollBar();
471             scrollBar.setValue(scrollBar.getMinimum());
472
473             scrollBar = editorScroll.getVerticalScrollBar();
474             scrollBar.setValue(scrollBar.getMinimum());
475         }
476         
477     }
478
479 }
480
Popular Tags