KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > core > output2 > Controller


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.core.output2;
21
22 import java.awt.Component JavaDoc;
23 import java.awt.Container JavaDoc;
24 import java.awt.FileDialog JavaDoc;
25 import java.awt.Frame JavaDoc;
26 import java.awt.KeyboardFocusManager JavaDoc;
27 import java.awt.Point JavaDoc;
28 import java.awt.Toolkit JavaDoc;
29 import java.awt.event.ActionEvent JavaDoc;
30 import java.awt.event.KeyEvent JavaDoc;
31 import java.beans.PropertyChangeListener JavaDoc;
32 import java.io.CharConversionException JavaDoc;
33 import java.io.File JavaDoc;
34 import java.io.FileOutputStream JavaDoc;
35 import java.io.IOException JavaDoc;
36 import java.io.OutputStream JavaDoc;
37 import java.util.HashSet JavaDoc;
38 import java.util.Iterator JavaDoc;
39 import java.util.regex.Matcher JavaDoc;
40 import javax.swing.AbstractAction JavaDoc;
41 import javax.swing.Action JavaDoc;
42 import javax.swing.JCheckBoxMenuItem JavaDoc;
43 import javax.swing.JComponent JavaDoc;
44 import javax.swing.JFileChooser JavaDoc;
45 import javax.swing.JOptionPane JavaDoc;
46 import javax.swing.JPopupMenu JavaDoc;
47 import javax.swing.JSeparator JavaDoc;
48 import javax.swing.KeyStroke JavaDoc;
49 import javax.swing.SwingUtilities JavaDoc;
50 import javax.swing.UIManager JavaDoc;
51 import javax.swing.event.PopupMenuEvent JavaDoc;
52 import javax.swing.event.PopupMenuListener JavaDoc;
53 import javax.swing.text.BadLocationException JavaDoc;
54 import org.netbeans.core.output2.ui.AbstractOutputTab;
55 import org.openide.actions.FindAction;
56 import org.openide.util.Exceptions;
57 import org.openide.util.Mutex;
58 import org.openide.util.NbBundle;
59 import org.openide.util.Utilities;
60 import org.openide.windows.OutputEvent;
61 import org.openide.windows.OutputListener;
62 import org.openide.windows.WindowManager;
63 import org.openide.xml.XMLUtil;
64
65 /**
66  * Master controller for an output window, and supplier of the default instance.
67  * The controller handles all actions of interest in an output window - the components
68  * are merely containers for data which pass events of interest up the component hierarchy
69  * to the controller via OutputWindow.getController(), for processing by the master
70  * controller. The controller is fully stateless, and stores information of interest in
71  * the components as appropriate.
72  */

73 public class Controller { //XXX public only for debug access to logging code
74

75     public static void ensureViewInDefault (final NbIO io, final boolean reuse) {
76         Mutex.EVENT.readAccess(new Runnable JavaDoc() {
77             public void run() {
78                 OutputWindow.findDefault();
79                 IOEvent evt = new IOEvent (io, IOEvent.CMD_CREATE, reuse);
80                 NbIO.post(evt);
81             }
82         });
83     }
84
85     private static final int ACTION_COPY = 0;
86     private static final int ACTION_WRAP = 1;
87     private static final int ACTION_SAVEAS = 2;
88     private static final int ACTION_CLOSE = 3;
89     private static final int ACTION_NEXTERROR = 4;
90     private static final int ACTION_PREVERROR = 5;
91     private static final int ACTION_SELECTALL = 6;
92     private static final int ACTION_FIND = 7;
93     private static final int ACTION_FINDNEXT = 8;
94     private static final int ACTION_NAVTOLINE = 9;
95     private static final int ACTION_POSTMENU = 10;
96     private static final int ACTION_FINDPREVIOUS = 11;
97     private static final int ACTION_CLEAR = 12;
98     private static final int ACTION_NEXTTAB = 13;
99     private static final int ACTION_PREVTAB = 14;
100 // issue 59447
101
// private static final int ACTION_TO_EDITOR = 15;
102

103
104     //Package private for unit tests
105
Action JavaDoc copyAction = new ControllerAction (ACTION_COPY,
106             "ACTION_COPY"); //NOI18N
107
Action JavaDoc wrapAction = new ControllerAction (ACTION_WRAP,
108             "ACTION_WRAP"); //NOI18N
109
Action JavaDoc saveAsAction = new ControllerAction (ACTION_SAVEAS,
110             "ACTION_SAVEAS"); //NOI18N
111
Action JavaDoc closeAction = new ControllerAction (ACTION_CLOSE,
112             "ACTION_CLOSE"); //NOI18N
113
Action JavaDoc nextErrorAction = new ControllerAction (ACTION_NEXTERROR,
114             "ACTION_NEXT_ERROR" ); //NOI18N
115
Action JavaDoc prevErrorAction = new ControllerAction (ACTION_PREVERROR,
116             "ACTION_PREV_ERROR" ); //NOI18N
117
Action JavaDoc selectAllAction = new ControllerAction (ACTION_SELECTALL,
118             "ACTION_SELECT_ALL"); //NOI18N
119
Action JavaDoc findAction = new ControllerAction (ACTION_FIND,
120             "ACTION_FIND"); //NOI18N
121
Action JavaDoc findNextAction = new ControllerAction (ACTION_FINDNEXT,
122             "ACTION_FIND_NEXT"); //NOI18N
123
Action JavaDoc findPreviousAction = new ControllerAction (ACTION_FINDPREVIOUS,
124             "ACTION_FIND_PREVIOUS"); //NOI18N
125
Action JavaDoc navToLineAction = new ControllerAction (ACTION_NAVTOLINE, "navToLine", //NOI18N
126
KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0));
127     Action JavaDoc postMenuAction = new ControllerAction (ACTION_POSTMENU, "postMenu", //NOI18N
128
KeyStroke.getKeyStroke(KeyEvent.VK_F10, KeyEvent.SHIFT_DOWN_MASK));
129     Action JavaDoc clearAction = new ControllerAction (ACTION_CLEAR, "ACTION_CLEAR");
130     
131     Action JavaDoc nextTabAction = new ControllerAction (ACTION_NEXTTAB, "NextViewAction", //NOI18N
132
(KeyStroke JavaDoc)null);
133     Action JavaDoc prevTabAction = new ControllerAction (ACTION_PREVTAB, "PreviousViewAction", //NOI18N
134
(KeyStroke JavaDoc)null);
135 // issue 59447
136
// Action toEditorAction = new ControllerAction (ACTION_TO_EDITOR, "ToEditorAction",
137
// // if you ever change or remove the shortcut, check the popup hiding hack. in postPopuMenu()
138
// KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0));
139

140     private Object JavaDoc[] popupItems = new Object JavaDoc[] {
141         copyAction, new JSeparator JavaDoc(), findAction, findNextAction,
142         new JSeparator JavaDoc(),
143         wrapAction, new JSeparator JavaDoc(), saveAsAction, clearAction, closeAction,
144     };
145     
146     private Action JavaDoc[] kbdActions = new Action JavaDoc[] {
147         copyAction, selectAllAction, findAction, findNextAction,
148         findPreviousAction, wrapAction, saveAsAction, closeAction,
149         navToLineAction, postMenuAction, clearAction, //toEditorAction,
150
};
151
152     Controller() {}
153
154     private OutputTab createOutputTab (OutputWindow win, NbIO io, boolean activateContainer, boolean reuse) {
155         AbstractOutputTab[] ov = win.getTabs();
156         OutputTab result = null;
157         if (LOG) log ("Find or create component for nbio " + io);
158         
159         for (int i=0; i < ov.length; i++) {
160             OutputTab oc = (OutputTab) ov[i];
161             if (oc.getIO() == io) {
162                 if (LOG) log ("Found an existing tab");
163                 result = oc;
164                 break;
165             }
166         }
167         if (result == null) {
168             if (LOG) log ("Didn't find an existing open tab, checking hidden tabs");
169             OutputTab[] hidden = win.getHiddenTabs();
170             for (int i=0; i < hidden.length; i++) {
171                 OutputTab oc = hidden[i];
172                 if (hidden[i].getIO() == io) {
173                     if (LOG) log ("Found a hidden tab with the same IO. Unhiding it for reuse");
174                     result = oc;
175                     unhideHiddenView (win, result);
176                     break;
177                 }
178             }
179         }
180         
181         if (LOG) log ("FindOrCreate: " + io.getName() + " found=" + (result !=
182             null) + " for io " + io);
183         
184         if (result == null) {
185             if (LOG) log ("Find or create creating " + io.getName());
186             result = createAndInstallView (win, io);
187         }
188         if (result != null) {
189             // install handlers to prev/next actions
190
result.getActionMap ().put ("jumpPrev", this.prevErrorAction); // NOI18N
191
result.getActionMap ().put ("jumpNext", this.nextErrorAction); // NOI18N
192
result.getActionMap ().put (FindAction.class.getName (), this.findAction);
193             result.getActionMap ().put (javax.swing.text.DefaultEditorKit.copyAction, this.copyAction);
194         }
195
196         if (result != null) {
197             win.setSelectedTab(result);
198         }
199
200         return result;
201     }
202
203     /**
204      * Creates and installs an output view
205      *
206      * @param win The owning container
207      * @param io The IO whose output is to be displayed
208      * @return A new OutputTab attached to the passed IO
209      */

210     private OutputTab createAndInstallView (OutputWindow win, NbIO io) {
211         if (LOG) log ("Create and install a new tab for : " + io.getName());
212         OutputTab result = new OutputTab (io);
213         result.setName (io.getName() + " ");
214         Action JavaDoc[] a = io.getToolbarActions();
215         if (a != null) {
216             result.setToolbarActions(a);
217         }
218         for (int i=0; i < kbdActions.length; i++) {
219             result.installKeyboardAction(kbdActions[i]);
220         }
221         
222         if (LOG) log ("Adding and selecting new tab " + result);
223         win.add (result);
224         win.setSelectedTab(result);
225         //Make sure names are boldfaced for all open streams - if the tabbed
226
//pane was just added in, it will just have used the name of the
227
//component, which won't contain html
228
AbstractOutputTab[] aot = win.getTabs();
229         for (int i=0; i < aot.length; i++) {
230             updateName(win, (OutputTab) aot[i]);
231         }
232         return result;
233     }
234
235     /**
236      * Output views can be hidden by the user invoking close before the output stream for
237      * the output has been closed. In this case, they are stored in the OutputWindow,
238      * and can be reopened if new output arrives. This method will remove a component
239      * from the set of hidden components and re-add it to the component hierarchy.
240      *
241      * @param win The owning container
242      * @param hidden The output component which is hidden but was not closed when it was hidden
243      */

244     private void unhideHiddenView (OutputWindow win, OutputTab hidden) {
245         if (LOG) log ("Unhiding hidden tab for " + hidden.getIO());
246         win.add (hidden);
247         win.removeHiddenView(hidden);
248     }
249
250     /**
251      * Boldfaces the name of the output component if its NbIO's stream is open.
252      * The update is delayed, and runs subsequently on the event queue - a process may
253      * synchronously open and close tabs, all of which affects names, so we use this
254      * technique and the CoalescedNameUpdater to coalesce all name changes - otherwise
255      * the name change may be delayed.
256      *
257      * @param tab The component whose name may need adjusting
258      */

259     private void updateName (OutputWindow win, OutputTab tab) {
260         if (nameUpdater == null) {
261             if (LOG) log ("Update name for " + tab.getIO() + " dispatching a name updater");
262             nameUpdater = new CoalescedNameUpdater(win);
263             SwingUtilities.invokeLater(nameUpdater);
264         }
265         nameUpdater.add (tab);
266     }
267
268     private CoalescedNameUpdater nameUpdater = null;
269     /**
270      * Calls to methods invoked on NbIO done on the EQ are invoked synchronously
271      * (this avoids a delay in the output window appearing, so output starts
272      * immediately). However, we want to avoid multiple name changes being
273      * propagated up to the window system because one tab was removed, another
274      * was added, and so forth - the result is the title won't be updated until
275      * the output run is nearly done, otherwise. Also, the call to update the
276      * TopComponent name is not terribly quick, so we don't want to do it any
277      * more times than we need to.
278      * <p>
279      * This class coalesces name changes, which are run afterward on the event
280      * queue.
281      */

282     private class CoalescedNameUpdater implements Runnable JavaDoc {
283         private HashSet JavaDoc components = new HashSet JavaDoc();
284         private OutputWindow win;
285         CoalescedNameUpdater (OutputWindow win) {
286             this.win = win;
287         }
288
289         /**
290          * Add a tab whose name should be changed.
291          * @param tab The tab
292          */

293         public void add (OutputTab tab) {
294             components.add (tab);
295         }
296         
297         public void remove(OutputTab tab) {
298             components.remove(tab);
299         }
300
301         public void run() {
302             for (Iterator JavaDoc i=components.iterator(); i.hasNext();) {
303                 OutputTab t = (OutputTab) i.next();
304                 NbIO io = t.getIO();
305                 if (LOG) {
306                     log ("Update name for " + io.getName() + " stream " +
307                         "closed is " + io.isStreamClosed());
308                 }
309                 if (win.isAncestorOf(t)) {
310                     String JavaDoc escaped;
311                     try {
312                         escaped = XMLUtil.toAttributeValue(io.getName());
313                     } catch (CharConversionException JavaDoc e) {
314                         escaped = io.getName();
315                     }
316                     boolean wasReset = io.checkReset();
317                     boolean useHtml = io.isStreamClosed() && !wasReset;
318                     
319                     String JavaDoc name = useHtml ? io.getName() + " " :
320                             "<html><b>" + escaped
321                             + " </b>&nbsp;</html>"; //NOI18N
322

323                     if (LOG) log (" set name to " + name);
324                     //#88204 apostophes are escaped in xm but not html
325
win.setTabTitle (t, name.replace("&apos;", "'"));
326                 }
327             }
328             nameUpdater = null;
329         }
330     }
331     
332     private void forceName(OutputWindow win, OutputTab tab) {
333         if (LOG) log ("ForceName ensuring non-html tab name");
334         if (nameUpdater != null) {
335             if (LOG) log (" an update was queued, aborting it");
336             nameUpdater.remove(tab);
337         }
338         if (win.isAncestorOf(tab)) {
339             String JavaDoc escaped;
340             try {
341                 escaped = XMLUtil.toAttributeValue(tab.getIO().getName() + " ");
342             } catch (CharConversionException JavaDoc e) {
343                 escaped = tab.getIO().getName() + " ";
344             }
345             if (LOG) log (" setting non-html name " + escaped);
346             //#88204 apostophes are escaped in xm but not html
347
win.setTabTitle (tab, escaped.replace("&apos;", "'"));
348         }
349     }
350
351     /**
352      * Called when a ControllerAction is invoked, either by the keyboard or
353      * from the popup menu.
354      *
355      * @param win The output window where it was invoked
356      * @param tab The tab it was invoked on
357      * @param id The ID of the action
358      */

359     public void actionPerformed(OutputWindow win, OutputTab tab, int id) {
360         switch (id) {
361             case ACTION_COPY:
362                 tab.getOutputPane().copy();
363                 break;
364             case ACTION_WRAP:
365                 boolean wrapped = tab.getOutputPane().isWrapped();
366                 tab.getOutputPane().setWrapped(!wrapped);
367                 break;
368             case ACTION_SAVEAS:
369                 saveAs (tab);
370                 break;
371             case ACTION_CLOSE:
372                 close (win, tab, false);
373                 break;
374             case ACTION_NEXTERROR:
375                 sendCaretToError(win, tab, false);
376                 break;
377             case ACTION_PREVERROR:
378                 sendCaretToError(win, tab, true);
379                 break;
380             case ACTION_SELECTALL:
381                 tab.getOutputPane().selectAll();
382                 break;
383             case ACTION_FIND:
384                 int start = tab.getOutputPane().getSelectionStart();
385                 int end = tab.getOutputPane().getSelectionEnd();
386                 String JavaDoc str = null;
387                 if (start > 0 && end > start) {
388                     try {
389                         str = tab.getOutputPane().getDocument().getText(start, end - start);
390                     } catch (BadLocationException JavaDoc ex) {
391                         ex.printStackTrace();
392                     }
393                 }
394                 FindDialogPanel.showFindDialog(tab.getFindActionListener(findNextAction, findPreviousAction, copyAction), str);
395                 break;
396             case ACTION_FINDNEXT:
397                 findNext (tab);
398                 break;
399             case ACTION_FINDPREVIOUS :
400                 findPrevious (tab);
401                 break;
402             case ACTION_NAVTOLINE :
403                 if (LOG) log ("Action NAVTOLINE received");
404                 openLineIfError (tab);
405                 break;
406             case ACTION_POSTMENU :
407                 if (LOG) log ("Action POSTMENU received");
408                 postPopupMenu(win, tab, new Point JavaDoc(0,0), tab);
409                 break;
410             case ACTION_CLEAR :
411                 if (LOG) log ("Action CLEAR receieved");
412                 NbIO io = tab.getIO();
413
414                 if (io != null) {
415                     NbWriter writer = io.writer();
416                     if (writer != null) {
417                         try {
418                             if (LOG) log ("Resetting the writer for Clear");
419                             writer.reset();
420                             forceName(win, tab);
421                         } catch (IOException JavaDoc ioe) {
422                             Exceptions.printStackTrace(ioe);
423                         }
424                     } else if (LOG) {
425                         log ("IO's NbWriter is null");
426                     }
427                 } else if (LOG) {
428                     log ("Clear on a tab with no IO");
429                 }
430                 break;
431             case ACTION_NEXTTAB :
432                 if (LOG) log ("Action NEXTTAB received");
433                 win.selectNextTab(tab);
434                 break;
435             case ACTION_PREVTAB :
436                 if (LOG) log ("Action PREVTAB received");
437                 win.selectPreviousTab(tab);
438                 break;
439 // #issue 59447
440
// case ACTION_TO_EDITOR :
441
// if (log) log ("Action TO_EDITOR received"); //NOI18N
442
// Mode m = WindowManager.getDefault().findMode ("editor"); //NOI18N
443
// if (m != null) {
444
// TopComponent tc = m.getSelectedTopComponent();
445
// if (tc != null) {
446
// tc.requestActive();
447
// }
448
// }
449
// break;
450

451             default :
452                 assert false;
453         }
454     }
455
456     /**
457      * Called when a line is clicked - if an output listener is listening on that
458      * line, it will be sent <code>outputLineAction</code>.
459      * @param tab
460      */

461     private void openLineIfError(OutputTab tab) {
462         OutWriter out = tab.getIO().out();
463         if (out != null) {
464             int line = tab.getOutputPane().getCaretLine();
465             OutputListener lis = out.getLines().getListenerForLine(line);
466             if (lis != null) {
467                 if (LOG) log (" Sending action for getLine " + line);
468                 ignoreCaretChanges = true;
469                 tab.getOutputPane().sendCaretToLine(line, true);
470                 ignoreCaretChanges = false;
471                 ControllerOutputEvent coe = new ControllerOutputEvent (tab.getIO(), line);
472                 lis.outputLineAction(coe);
473             }
474         }
475     }
476
477
478
479     /**
480      * Find the next match for the previous search contents, starting at
481      * the current caret position.
482      *
483      * @param tab The tab
484      */

485     private void findNext (OutputTab tab) {
486         OutWriter out = tab.getIO().out();
487         if (out != null) {
488             String JavaDoc lastPattern = FindDialogPanel.getPanel().getPattern();
489             if (lastPattern != null) {
490                 out.getLines().find(lastPattern);
491             }
492             Matcher JavaDoc matcher = out.getLines().getForwardMatcher();
493             int pos = tab.getOutputPane().getCaretPos();
494             if (pos >= tab.getOutputPane().getLength() || pos < 0) {
495                 pos = 0;
496             }
497
498             if (matcher != null && matcher.find (pos)) {
499                 tab.getOutputPane().setSelection(matcher.start(), matcher.end());
500                 copyAction.setEnabled(true);
501             } else {
502                 Toolkit.getDefaultToolkit().beep();
503             }
504         }
505     }
506
507     /**
508      * Find the match before the current caret position, using the previously
509      * searched for value.
510      *
511      * @param tab The tab
512      */

513     private void findPrevious (OutputTab tab) {
514         OutWriter out = tab.getIO().out();
515         if (out != null) {
516             String JavaDoc lastPattern = FindDialogPanel.getPanel().getPattern();
517             if (lastPattern != null) {
518                 out.getLines().find(lastPattern);
519             }
520             Matcher JavaDoc matcher = out.getLines().getReverseMatcher();
521
522             int length = tab.getOutputPane().getLength();
523             int pos = length - tab.getOutputPane().getSelectionStart();
524
525             if (pos >= tab.getOutputPane().getLength()-1 || pos < 0) {
526                 pos = 0;
527             }
528             if (LOG) log ("Reverse search from " + pos);
529             if (matcher != null && matcher.find (pos)) {
530                 int start = length - matcher.end();
531                 int end = length - matcher.start();
532                 tab.getOutputPane().setSelection(start, end);
533                 copyAction.setEnabled(true);
534             } else {
535                 Toolkit.getDefaultToolkit().beep();
536             }
537         }
538     }
539
540     /**
541      * Update the enabled state of the actions based on the state of the passed
542      * tab. If the tab is not currently selected, does nothing.
543      *
544      * @param win The output window
545      * @param tab The tab, presumably the selected one
546      */

547     private void updateActions (OutputWindow win, OutputTab tab) {
548         if (tab == win.getSelectedTab()) {
549             OutputPane pane = (OutputPane) tab.getOutputPane();
550             int len = pane.getLength();
551             boolean enable = len > 0;
552             findAction.setEnabled (enable);
553             OutWriter out = tab.getIO().out();
554 // findNextAction.setEnabled (out != null && out.getLines().getForwardMatcher() != null);
555
// findPreviousAction.setEnabled (out != null && out.getLines().getForwardMatcher() != null);
556
saveAsAction.setEnabled (enable);
557             selectAllAction.setEnabled(enable);
558             copyAction.setEnabled(pane.hasSelection());
559             boolean hasErrors = out == null ? false : out.getLines().firstListenerLine() != -1;
560             nextErrorAction.setEnabled(hasErrors);
561             prevErrorAction.setEnabled(hasErrors);
562         }
563     }
564
565     /**
566      * Close the tab. If <code>programmatic</code> is false and it is the last
567      * tab, the output window will be closed as well.
568      *
569      * @param win The owning output window
570      * @param tab The tab
571      * @param programmatic False if the user requested the tab to be closed
572      */

573     public void close(OutputWindow win, OutputTab tab, boolean programmatic) {
574         //NotifyRemoved callback will take care of putting it into the hidden view list if
575
//its output is still open.
576
Component JavaDoc focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner();
577         boolean hadFocus = focusOwner != null && (focusOwner == win || win.isAncestorOf(focusOwner));
578
579         win.remove(tab); //Triggers a call to notifyRemoved()
580
boolean winClosed = false;
581         if (!programmatic && win.getTabs().length == 0) {
582             if (LOG) log ("Last tab closed by user, closing output window.");
583             win.close();
584             winClosed = true;
585         }
586         if (hadFocus) {
587             if (!winClosed && win.getSelectedTab() != null) {
588                 if (LOG) log ("Trying to send focus to the newly selected tab");
589                 win.getSelectedTab().requestFocus();
590             }
591         }
592         if (LOG) log ("Close received, removing " + tab + " from component");
593     }
594
595     /**
596      * Holds the last written to directory for the save as file chooser.
597      */

598     private static String JavaDoc lastDir = null;
599
600     /**
601      * Invokes a file dialog and if a file is chosen, saves the output to
602      * that file.
603      *
604      * @param tab The tab
605      */

606     private void saveAs(OutputTab tab) {
607         OutWriter out = tab.getIO().out();
608         if (out == null) {
609             return;
610         }
611         File JavaDoc f = showFileChooser (tab);
612         if (f != null) {
613             try {
614                 synchronized (out) {
615                     out.getLines().saveAs(f.getPath());
616                 }
617             } catch (IOException JavaDoc ioe) {
618                 Exceptions.printStackTrace(ioe);
619             }
620         }
621     }
622
623     /**
624      * Shows a file dialog and an overwrite dialog if the file exists, returning
625      * null if the user chooses not to overwrite. Will use an AWT FileDialog for
626      * Aqua, per Apple UI guidelines.
627      *
628      * @param owner A parent component for the dialog - the top level ancestor will
629      * actually be used so positioning is correct
630      * @return A file to write to
631      */

632     private static File JavaDoc showFileChooser (JComponent JavaDoc owner) {
633         File JavaDoc f = null;
634         String JavaDoc dlgTtl = NbBundle.getMessage (Controller.class, "TITLE_SAVE_DLG"); //NOI18N
635

636         boolean isAqua = "Aqua".equals(UIManager.getLookAndFeel().getID()); //NOI18N
637

638         if (isAqua) {
639             //Apple UI guidelines recommend against ever using JFileChooser
640
FileDialog JavaDoc fd = new FileDialog JavaDoc((Frame JavaDoc) owner.getTopLevelAncestor(), dlgTtl, FileDialog.SAVE);
641             if (lastDir != null && new File JavaDoc (lastDir).exists()) {
642                 fd.setDirectory(lastDir);
643             }
644             fd.setModal(true);
645             fd.setVisible(true);
646             String JavaDoc s = fd.getDirectory() + fd.getFile();
647             f = new File JavaDoc(s);
648             if (f.exists() && f.isDirectory()) {
649                 f = null;
650             }
651         } else {
652             JFileChooser JavaDoc jfc = new JFileChooser JavaDoc();
653             if (lastDir != null && new File JavaDoc(lastDir).exists()) {
654                 File JavaDoc dir = new File JavaDoc (lastDir);
655                 if (dir.exists()) {
656                     jfc.setCurrentDirectory(dir);
657                 }
658             }
659             jfc.setName(dlgTtl);
660             jfc.setDialogTitle(dlgTtl);
661
662             if (jfc.showSaveDialog(owner.getTopLevelAncestor()) == JFileChooser.APPROVE_OPTION) {
663                 f = jfc.getSelectedFile();
664             }
665         }
666
667         if (f != null && f.exists() && !isAqua) { //Aqua's file dialog takes care of this
668
String JavaDoc msg = NbBundle.getMessage(Controller.class,
669                 "FMT_FILE_EXISTS", new Object JavaDoc[] { f.getName() }); //NOI18N
670
String JavaDoc title = NbBundle.getMessage(Controller.class,
671                 "TITLE_FILE_EXISTS"); //NOI18N
672
if (JOptionPane.showConfirmDialog(owner.getTopLevelAncestor(), msg, title,
673             JOptionPane.OK_CANCEL_OPTION) != JOptionPane.OK_OPTION) {
674                 f = null;
675             }
676         }
677         if (f != null) {
678             lastDir = f.getParent();
679         }
680         return f;
681     }
682
683     /**
684      * Called when the selected component is changed in an output container.
685      *
686      * @param win The owning container
687      * @param former The previously selected output view, or null
688      * @param current The newly selected output view, or null
689      */

690     public void selectionChanged(OutputWindow win, OutputTab former,
691                                                      OutputTab current) {
692         if (former != null) {
693             former.updateTimestamp();
694         }
695         if (current != null) {
696             current.updateTimestamp();
697             updateActions (win, current);
698         }
699     }
700
701     /**
702      * Messaged when the container becomes activated in the netbeans window system
703      * @param win The container
704      */

705     public void notifyActivated(OutputWindow win) {
706         OutputTab tab = (OutputTab) win.getSelectedTab();
707         if (tab != null) {
708             updateActions (win, tab);
709         }
710     }
711
712     private boolean firstF12 = true;
713     /**
714      * Sends the caret in a tab to the nearest error line to its current position, selecting
715      * that line.
716      *
717      * @param win The output window
718      * @param tab the tab
719      * @param backward If the search should be done in reverse
720      */

721     private void sendCaretToError(OutputWindow win, OutputTab tab, boolean backward) {
722         if (tab == null) {
723             //We're being invoked from SystemAction via main menu - no associated component
724
tab = (OutputTab) win.getSelectedTab();
725             if (tab == null) {
726                 return;
727             }
728         }
729         OutWriter out = tab.getIO().out();
730         if (out != null) {
731             int line = firstF12 ? 0 : Math.max(0, tab.getOutputPane().getCaretLine());
732             if (line >= tab.getOutputPane().getLineCount()-1) {
733                 line = 0;
734             }
735             //FirstF12: #48485 - caret is already on the first listener line,
736
//so F12 jumps to the second error. So search from 0 the first time after a reset
737
int newline = out.getLines().nearestListenerLine(line, backward);
738             if (LOG) {
739                 log ("sendCaretToError - caret line: " + line +
740                     " nearest listener line " + newline);
741             }
742             if (newline == line) {
743                 if (!backward && line != tab.getOutputPane().getLineCount()) {
744                     newline = out.getLines().nearestListenerLine(line+1, backward);
745                 } else if (backward && line > 0) {
746                     newline = out.getLines().nearestListenerLine(line-1, backward);
747                 } else {
748                     return;
749                 }
750             }
751             if (newline != -1) {
752                 if (LOG)
753                     log("Sending caret to error line " + newline);
754                 tab.getOutputPane().sendCaretToLine(newline, true);
755                 if (!win.isActivated()) {
756                     OutputListener l = out.getLines().getListenerForLine(newline);
757                     
758                     ControllerOutputEvent ce = new ControllerOutputEvent (tab.getIO(), newline);
759                     l.outputLineAction(ce);
760                 }
761             }
762             firstF12 = false;
763         }
764     }
765
766     /**
767      * Called when an output tab has been removed from the component hierarchy.
768      * If its io is not closed, holds a reference to it in a list of closed
769      * tabs, re-showing it on request, or finally disposing its IO and
770      * releasing it if it has not been shown again.
771      */

772     public void notifyRemoved(OutputTab tab) {
773         assert SwingUtilities.isEventDispatchThread();
774         if (LOG) log ("Tab " + tab + " has been CLOSED. Disposing its IO.");
775         NbIO io = tab.getIO();
776         if (io != null) {
777             io.setClosed(true);
778         }
779         NbWriter w = io.writer();
780         if (w != null && w.isClosed()) {
781             //Will dispose the document
782
tab.setDocument(null);
783         } else if (w != null) {
784             //Something is still writing to the stream, but we're getting rid of the tab. Don't dispose
785
//the writer, just kill the tab's document
786
tab.getDocument().disposeQuietly();
787         }
788     }
789
790     /**
791      * Called when input has been sent by the user via the input component
792      *
793      * @param win The output window
794      * @param tab The tab component
795      * @param txt The input entered
796      */

797     public void notifyInput(OutputWindow win, OutputTab tab, String JavaDoc txt) {
798         if (Controller.LOG) Controller.log ("Notify input on " + tab + " - " + txt);
799         NbIO io = tab.getIO();
800         if (io != null) {
801             NbIO.IOReader in = io.in();
802             if (in != null) {
803                 if (Controller.LOG) Controller.log ("Sending input to " + in);
804
805                 in.pushText (txt + "\n");
806                 //#56070 - copy input to output, TODO - make it more different color..
807
io.getOut().println(txt);
808             }
809         }
810     }
811
812     /**
813      * Fetch the output listener for a given line in a given tab
814      *
815      * @param tab The output tab
816      * @param line The line to find a listener on
817      * @return An output listener or null
818      */

819     private OutputListener listenerForLine (OutputTab tab, int line) {
820         OutWriter out = tab.getIO().out();
821         if (out != null) {
822             return out.getLines().getListenerForLine(line);
823         }
824         return null;
825     }
826
827     /**
828      * Called when the user has clicked a line in the text view
829      * @param win The output window
830      * @param tab The tab
831      * @param line The line which was clicked
832      */

833     public void lineClicked(OutputWindow win, OutputTab tab, int line) {
834         OutputListener l = listenerForLine (tab, line);
835         if (l != null) {
836             ControllerOutputEvent oe = new ControllerOutputEvent (tab.getIO(), line);
837             l.outputLineAction(oe);
838             //Select the text on click
839
tab.getOutputPane().sendCaretToLine(line, true);
840         }
841     }
842
843     /**
844      * Post the output window's popup menu
845      *
846      * @param win The output window
847      * @param tab The tab
848      * @param p The point clicked
849      * @param src The source of the click event
850      */

851     public void postPopupMenu(OutputWindow win, OutputTab tab, Point JavaDoc p, Component JavaDoc src) {
852         if (LOG) {
853             log ("post popup menu for " + tab.getName());
854         }
855         JPopupMenu JavaDoc popup = new JPopupMenu JavaDoc();
856         popup.putClientProperty ("container", win); //NOI18N
857
popup.putClientProperty ("component", tab); //NOI18N
858
Action JavaDoc[] a = tab.getToolbarActions();
859         if (a.length > 0) {
860             boolean added = false;
861             for (int i=0; i < a.length; i++) {
862                 if (a[i].getValue(Action.NAME) != null) {
863                     // add the proxy that doesn't show icons #67451
864
popup.add (new ProxyAction(a[i]));
865                     added = true;
866                 }
867             }
868             if (added) {
869                 popup.add (new JSeparator JavaDoc());
870             }
871         }
872         for (int i=0; i < popupItems.length; i++) {
873             if (popupItems[i] instanceof JSeparator JavaDoc) {
874                 popup.add ((JSeparator JavaDoc) popupItems[i]);
875             } else {
876                 if (popupItems[i] != wrapAction) {
877                     popup.add ((Action JavaDoc) popupItems[i]);
878                 } else {
879                     JCheckBoxMenuItem JavaDoc item =
880                         new JCheckBoxMenuItem JavaDoc((Action JavaDoc) popupItems[i]);
881                     
882                     item.setSelected(tab.getOutputPane().isWrapped());
883                     popup.add (item);
884                 }
885             }
886         }
887         // hack to remove the esc keybinding when doing popup..
888
KeyStroke JavaDoc esc = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
889         JComponent JavaDoc c = tab.getOutputPane().getTextView();
890         Object JavaDoc escHandle = c.getInputMap().get(esc);
891         c.getInputMap().remove(esc);
892         tab.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).remove(esc);
893         
894         popup.addPopupMenuListener(new PMListener(popupItems, escHandle));
895         popup.show(src, p.x, p.y);
896         
897     }
898     
899     private static class ProxyAction implements Action JavaDoc {
900         private Action JavaDoc orig;
901         ProxyAction(Action JavaDoc original) {
902             orig = original;
903         }
904
905         public Object JavaDoc getValue(String JavaDoc key) {
906             if (Action.SMALL_ICON.equals(key)) {
907                 return null;
908             }
909             return orig.getValue(key);
910         }
911
912         public void putValue(String JavaDoc key, Object JavaDoc value) {
913             orig.putValue(key, value);
914         }
915
916         public void setEnabled(boolean b) {
917             orig.setEnabled(b);
918         }
919
920         public boolean isEnabled() {
921             return orig.isEnabled();
922         }
923
924         public void addPropertyChangeListener(PropertyChangeListener JavaDoc listener) {
925             orig.addPropertyChangeListener(listener);
926         }
927
928         public void removePropertyChangeListener(PropertyChangeListener JavaDoc listener) {
929             orig.removePropertyChangeListener(listener);
930         }
931
932         public void actionPerformed(ActionEvent JavaDoc e) {
933             orig.actionPerformed(e);
934         }
935     }
936     
937     /**
938      * #47166 - a disposed tab which has had its popup menu shown remains
939      * referenced through PopupItems->JSeparator->PopupMenu->Invoker->OutputPane->OutputTab
940      */

941     private static class PMListener implements PopupMenuListener JavaDoc {
942         private Object JavaDoc[] popupItems;
943         private Object JavaDoc handle;
944         PMListener (Object JavaDoc[] popupItems, Object JavaDoc escHandle) {
945             this.popupItems = popupItems;
946             handle = escHandle;
947         }
948         
949         public void popupMenuWillBecomeInvisible(PopupMenuEvent JavaDoc e) {
950             JPopupMenu JavaDoc popup = (JPopupMenu JavaDoc) e.getSource();
951             popup.removeAll();
952             popup.setInvoker(null);
953             // hack
954
AbstractOutputTab tab = (AbstractOutputTab)popup.getClientProperty("component");
955             KeyStroke JavaDoc esc = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
956             JComponent JavaDoc c = tab.getOutputPane().getTextView();
957             c.getInputMap().put(esc, handle);
958             tab.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(esc, handle);
959             
960             //hack end
961
popup.putClientProperty ("container", null); //NOI18N
962
popup.putClientProperty ("component", null); //NOI18N
963
popup.removePopupMenuListener(this);
964             for (int i=0; i < popupItems.length; i++) {
965                 if (popupItems[i] instanceof ControllerAction) {
966                     ((ControllerAction) popupItems[i]).clearListeners();
967                 }
968             }
969         }
970         
971         public void popupMenuCanceled(PopupMenuEvent JavaDoc e) {
972             popupMenuWillBecomeInvisible(e);
973         }
974         
975         public void popupMenuWillBecomeVisible(PopupMenuEvent JavaDoc e) {
976             //do nothing
977
}
978     }
979
980     /**
981      * Called when the text caret has changed lines - will call OutputListener.outputLineSelected if
982      * there is a listener for that line.
983      *
984      * @param tab The output tab
985      * @param line The line the caret is in
986      */

987     public void caretEnteredLine(OutputTab tab, int line) {
988         if (!ignoreCaretChanges) {
989             OutputListener l = listenerForLine (tab, line);
990             if (LOG) {
991                 log ("Caret entered line " + line + " notifying listener " + l);
992             }
993             if (l != null) {
994                 ControllerOutputEvent oe = new ControllerOutputEvent (tab.getIO(), line);
995                 l.outputLineSelected(oe);
996             }
997         } else {
998             if (LOG) {
999                 log ("Caret entered line " + line + " which has no listener");
1000            }
1001        }
1002    }
1003
1004    /**
1005     * Called when an event has been received from the document (indicating that new
1006     * output has been appended. Handles InputOutput.isFocusTaken(), and updates the
1007     * tab title in the case the stream has been closed, and updates the actions if
1008     * it is the selected tab.
1009     *
1010     * @param win
1011     * @param tab
1012     */

1013    public void documentChanged(OutputWindow win, OutputTab tab) {
1014        if (tab.getIO().isFocusTaken()) {
1015            //Not at all sure that isFocusTaken() is a terribly bright idea to begin with
1016
win.setSelectedTab(tab);
1017            win.requestVisible();
1018        }
1019        updateName(win, tab);
1020        if (tab == win.getSelectedTab() && win.isActivated()) {
1021            updateActions(win, tab);
1022        }
1023    }
1024
1025    /**
1026     * Handles IOEvents posted into the AWT Event Queue by NbIO instances whose methods have
1027     * been called, as received by an OutputTab which has identified the event as being
1028     * intended for it.
1029     *
1030     * @param win The output container owning the IO, or in the case of CMD_CREATE, the
1031     * one that received the event
1032     * @param tab The output component associated with this IO, if any
1033     * @param io The IO which originated the event
1034     * @param command The ID, one of those defined in IOEvent, of the command
1035     * @param value The boolean value of the command, if pertinent
1036     * @param data The data associated with the command, if pertinent
1037     */

1038    public void performCommand(OutputWindow win, OutputTab tab, NbIO io, int command,
1039                               boolean value, Object JavaDoc data) {
1040
1041        if (LOG) {
1042            log ("PERFORMING: " + IOEvent.cmdToString(command) + " value=" + value + " on " + io + " tob " + tab);
1043        }
1044
1045        OutWriter out = io.out();
1046
1047        switch (command) {
1048            case IOEvent.CMD_CREATE :
1049                createOutputTab(win, io, io.isFocusTaken(), value);
1050                break;
1051            case IOEvent.CMD_INPUT_VISIBLE :
1052                if (value && tab == null) {
1053                    tab = createOutputTab(win, io, io.isFocusTaken(), value);
1054                }
1055                if (tab != null) {
1056                    tab.setInputVisible(value);
1057                    win.setSelectedTab(tab);
1058                }
1059                break;
1060            case IOEvent.CMD_SELECT :
1061                if (tab == null) {
1062                    tab = createOutputTab(win, io, io.isFocusTaken(), value);
1063                }
1064                if (!win.isOpened()) {
1065                    win.open();
1066                }
1067                //#60960 creating component doesn't make the tabs visible, do it in select (also #58738)
1068
if (!io.isFocusTaken()) {
1069                    win.requestVisibleForNewTab();
1070                } else {
1071                    win.requestActiveForNewTab();
1072                }
1073                if (win.getSelectedTab() != tab) {
1074                    if (tab.getParent() == null) {
1075                        //It was hidden
1076
win.add(tab);
1077                    }
1078                    win.setSelectedTab(tab);
1079                    updateName(win,tab);
1080                }
1081                break;
1082            case IOEvent.CMD_SET_TOOLBAR_ACTIONS :
1083                if (tab == null && data != null) {
1084                    tab = createOutputTab(win, io, io.isFocusTaken(), value);
1085                }
1086                Action JavaDoc[] a = (Action JavaDoc[]) data;
1087                tab.setToolbarActions(a);
1088                break;
1089            case IOEvent.CMD_CLOSE :
1090                if (tab != null) {
1091                    close(win, tab, true);
1092                    win.revalidate();
1093                    win.repaint();
1094                } else {
1095                    io.dispose();
1096                }
1097                break;
1098            case IOEvent.CMD_STREAM_CLOSED :
1099                if (value) {
1100                    if (tab == null) {
1101                        //The tab was already closed, throw away the storage.
1102
if (io.out() != null) {
1103                            io.out().dispose();
1104                        }
1105                    } else {
1106                        if (tab.getParent() != null) {
1107                            updateName(win, tab);
1108                            if (tab.getIO().out() != null && tab.getIO().out().getLines().firstListenerLine() == -1) {
1109                                tab.getOutputPane().ensureCaretPosition();
1110                            }
1111                            if (tab == win.getSelectedTab()) {
1112                                updateActions (win, tab);
1113                            }
1114                        } else {
1115                            //The tab had been kept around to be re-shown, but now the stream is closed, dispose it
1116
win.removeHiddenView(tab);
1117                            if (io.out() != null) {
1118                                io.out().dispose();
1119                            }
1120                        }
1121                    }
1122                } else {
1123                    if (tab != null && tab.getParent() != null) {
1124                        updateName(win, tab);
1125                    }
1126                }
1127                break;
1128            case IOEvent.CMD_RESET :
1129                firstF12 = true;
1130                if (tab == null) {
1131                    if (LOG) log ("Got a reset on an io with no tab. Creating a tab.");
1132                    performCommand (win, null, io, IOEvent.CMD_CREATE, value, data);
1133                    win.requestVisible();
1134                    return;
1135                }
1136                if (LOG) log ("Setting io " + io + " on tab " + tab);
1137// tab.setDocument (new OutputDocument((OutWriter)io.getOut()));
1138
tab.setIO(io);
1139                win.setSelectedTab(tab);
1140                updateName(win, tab);
1141                // Undesirable in practice: win.requestVisibleForNewTab();
1142
if (LOG) log ("Reset on " + tab + " tab displayable " + tab.isDisplayable() + " io " + io + " io.out " + io.out());
1143                break;
1144            case IOEvent.CMD_ICON :
1145                win.setTabIcon(tab, io.getIcon());
1146                break;
1147        }
1148    }
1149
1150    /**
1151     * Called when the output stream has been closed, to navigate to the
1152     * first line which shows an error (if any).
1153     *
1154     * @param comp The output component whose IO's stream has been closed.
1155     */

1156    private void navigateToFirstErrorLine (OutputTab comp) {
1157        OutWriter out = comp.getIO().out();
1158        if (out != null) {
1159            int line = comp.getFirstNavigableListenerLine();
1160            if (Controller.LOG) Controller.log ("NAV TO FIRST LISTENER LINE: " + line);
1161            if (line > 0) {
1162                comp.getOutputPane().sendCaretToLine (line, false);
1163                if (isSDI(comp)) {
1164                    comp.requestActive();
1165                }
1166            }
1167        }
1168    }
1169    
1170    
1171    private static boolean isSDI (OutputTab comp) {
1172        Container JavaDoc c = comp.getTopLevelAncestor();
1173        return (c != WindowManager.getDefault().getMainWindow());
1174    }
1175    
1176    /**
1177     * Flag used to block navigating the editor to the first error line when
1178     * selecting the error line in the output window after a build (or maybe
1179     * it should navigate the editor there? Could be somewhat rude...)
1180     */

1181    boolean ignoreCaretChanges = false;
1182
1183    public void hasSelectionChanged(OutputWindow outputWindow, OutputTab tab, boolean val) {
1184        if (tab == outputWindow.getSelectedTab()) {
1185            copyAction.setEnabled(val);
1186            selectAllAction.setEnabled(!tab.getOutputPane().isAllSelected());
1187        }
1188    }
1189
1190    public void hasOutputListenersChanged(OutputWindow win, OutputTab tab, boolean hasOutputListeners) {
1191        if (hasOutputListeners && win.getSelectedTab() == tab && tab.isShowing()) {
1192            navigateToFirstErrorLine(tab);
1193        }
1194    }
1195    
1196    /**
1197     * A stateless action which will find the owning OutputTab's controller and call
1198     * actionPerformed with its ID as an argument.
1199     */

1200    private static class ControllerAction extends AbstractAction JavaDoc {
1201        private int id;
1202        /**
1203         * Create a ControllerAction with the specified action ID (constants defined in Controller),
1204         * using the specified bundle key. Expects the following contents in the bundle:
1205         * <ul>
1206         * <li>A name for the action matching the passed key</li>
1207         * <li>An accelerator for the action matching [key].accel</li>
1208         * </ul>
1209         * @param id An action ID
1210         * @param bundleKey A key for the bundle associated with the Controller class
1211         * @see org.openide.util.Utilities#stringToKey
1212         */

1213        ControllerAction (int id, String JavaDoc bundleKey) {
1214            if (bundleKey != null) {
1215                String JavaDoc name = NbBundle.getMessage(Controller.class, bundleKey);
1216                KeyStroke JavaDoc accelerator = getAcceleratorFor(bundleKey);
1217                this.id = id;
1218                putValue (NAME, name);
1219                putValue (ACCELERATOR_KEY, accelerator);
1220            }
1221        }
1222
1223        /**
1224         * Create a ControllerAction with the specified ID, name and keystroke. Actions created
1225         * using this constructor will not be added to the popup menu of the component.
1226         *
1227         * @param id The ID
1228         * @param name A programmatic name for the item
1229         * @param stroke An accelerator keystroke
1230         */

1231        ControllerAction (int id, String JavaDoc name, KeyStroke JavaDoc stroke) {
1232            this.id = id;
1233            putValue (NAME, name);
1234            putValue (ACCELERATOR_KEY, stroke);
1235        }
1236        
1237        void clearListeners() {
1238            PropertyChangeListener JavaDoc[] l = changeSupport.getPropertyChangeListeners();
1239            for (int i=0; i < l.length; i++) {
1240                removePropertyChangeListener (l[i]);
1241            }
1242        }
1243
1244        /**
1245         * Get a keyboard accelerator from the resource bundle, with special handling
1246         * for the mac keyboard layout.
1247         *
1248         * @param name The bundle key prefix
1249         * @return A keystroke
1250         */

1251        private static KeyStroke JavaDoc getAcceleratorFor (String JavaDoc name) {
1252            String JavaDoc key = name + ".accel"; //NOI18N
1253
if (Utilities.isMac()) {
1254                key += ".mac"; //NOI18N
1255
}
1256            return Utilities.stringToKey(NbBundle.getMessage(Controller.class, key));
1257        }
1258
1259        public int getID() {
1260            return id;
1261        }
1262
1263        public void actionPerformed(ActionEvent JavaDoc e) {
1264            if (LOG) log ("ACTION PERFORMED: " + getValue(NAME));
1265            Component JavaDoc c = (Component JavaDoc) e.getSource();
1266
1267            OutputTab outComp = c instanceof OutputTab ? (OutputTab) c :
1268                c instanceof OutputWindow ? null :
1269                (OutputTab) SwingUtilities.getAncestorOfClass(OutputTab.class, c);
1270
1271            OutputWindow win= c instanceof OutputWindow ? (OutputWindow) c :
1272                (OutputWindow) SwingUtilities.getAncestorOfClass(OutputWindow.class, outComp);
1273
1274            if (win == null) {
1275                win = OutputWindow.findDefault();
1276            }
1277            if (outComp == null && win != null) {
1278                outComp = (OutputTab) win.getSelectedTab();
1279            }
1280
1281            if (win == null && outComp == null) {
1282                //For popup menus, we store the component they were invoked over in
1283
//client properties
1284
JPopupMenu JavaDoc jpm = (JPopupMenu JavaDoc) SwingUtilities.getAncestorOfClass (JPopupMenu JavaDoc.class, c);
1285                if (jpm != null) {
1286                    win = (OutputWindow) jpm.getClientProperty ("win"); //NOI18N
1287
outComp = (OutputTab) jpm.getClientProperty ("component"); //NOI18N
1288
}
1289            }
1290            Controller cont = win.getController();
1291            if (cont != null) {
1292                cont.actionPerformed (win, outComp, getID());
1293            }
1294        }
1295    }
1296
1297    /**
1298     * An OutputEvent implementation with a settable line index so it can be
1299     * reused.
1300     */

1301    static class ControllerOutputEvent extends OutputEvent {
1302        private int line;
1303        ControllerOutputEvent (NbIO io, int line) {
1304            super (io);
1305            this.line = line;
1306        }
1307
1308        void setLine (int line) {
1309            this.line = line;
1310        }
1311
1312        public String JavaDoc getLine() {
1313            NbIO io = (NbIO) getSource();
1314            OutWriter out = io.out();
1315            try {
1316                if (out != null) {
1317                    String JavaDoc s = out.getLines().getLine(line);
1318                    //#46892 - newlines should not be appended to returned strings
1319
if (s.endsWith("\n")) { //NOI18N
1320
s = s.substring(0, s.length()-1);
1321                    }
1322                    //#70008 on windows \r can also be there..
1323
if (s.endsWith("\r")) { //NOI18N
1324
s = s.substring(0, s.length()-1);
1325                    }
1326                    return s;
1327                }
1328            } catch (IOException JavaDoc ioe) {
1329                IOException JavaDoc nue = new IOException JavaDoc ("Could not fetch line " + line + " on " + io.getName()); //NOI18N
1330
nue.initCause(ioe);
1331                Exceptions.printStackTrace(ioe);
1332            }
1333            return null;
1334        }
1335    }
1336
1337    public static final boolean LOG = Boolean.getBoolean("nb.output.log") || Boolean.getBoolean("nb.output.log.verbose"); //NOI18N
1338
public static final boolean VERBOSE = Boolean.getBoolean("nb.output.log.verbose");
1339    static final boolean logStdOut = Boolean.getBoolean("nb.output.log.stdout"); //NOI18N
1340
public static void log (String JavaDoc s) {
1341        s = Long.toString(System.currentTimeMillis()) + ":" + s + "(" + Thread.currentThread() + ") ";
1342        if (logStdOut) {
1343            System.out.println(s);
1344            return;
1345        }
1346        OutputStream JavaDoc os = getLogStream();
1347        byte b[] = new byte[s.length() + 1];
1348        char[] c = s.toCharArray();
1349        for (int i=0; i < c.length; i++) {
1350            b[i] = (byte) c[i];
1351        }
1352        b[b.length-1] = (byte) '\n';
1353        try {
1354            os.write(b);
1355        } catch (Exception JavaDoc e) {
1356            e.printStackTrace();
1357            System.err.println(s);
1358        }
1359        try {
1360            os.flush();
1361        } catch (Exception JavaDoc e ) {}
1362    }
1363    
1364    public static void logStack() {
1365        if (logStdOut) {
1366            new Exception JavaDoc().printStackTrace();
1367            return;
1368        }
1369        Exception JavaDoc e = new Exception JavaDoc();
1370        e.fillInStackTrace();
1371        StackTraceElement JavaDoc[] ste = e.getStackTrace();
1372
1373        for (int i=1; i < Math.min (22, ste.length); i++) {
1374            log (" * " + ste[i]);
1375        }
1376    }
1377
1378    private static OutputStream JavaDoc logStream = null;
1379    private static OutputStream JavaDoc getLogStream() {
1380        if (logStream == null) {
1381            String JavaDoc spec = System.getProperty ("java.io.tmpdir") + File.separator + "outlog.txt";
1382            synchronized (Controller.class) {
1383                try {
1384                    File JavaDoc f = new File JavaDoc (spec);
1385                    if (f.exists()) {
1386                        f.delete();
1387                    }
1388                    f.createNewFile();
1389                    logStream = new FileOutputStream JavaDoc(f);
1390                } catch (Exception JavaDoc e) {
1391                    e.printStackTrace();
1392                    logStream = System.err;
1393                }
1394            }
1395        }
1396        return logStream;
1397    }
1398
1399    public void inputEof(OutputTab tab) {
1400        if (Controller.LOG) Controller.log ("Input EOF");
1401        NbIO io = tab.getIO();
1402        NbIO.IOReader in = io.in();
1403        if (in != null) {
1404            in.eof();
1405        }
1406    }
1407}
1408
1409
Popular Tags