| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.netbeans.core.output2; |
| |
| import java.awt.Color; |
| import java.awt.Dialog; |
| import java.awt.Component; |
| import java.awt.Container; |
| import java.awt.Cursor; |
| import java.awt.EventQueue; |
| import java.awt.FileDialog; |
| import java.awt.Frame; |
| import java.awt.Point; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.awt.event.KeyEvent; |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.io.CharConversionException; |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.EnumMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.regex.Pattern; |
| import java.util.regex.PatternSyntaxException; |
| import javax.swing.AbstractAction; |
| import javax.swing.Action; |
| import javax.swing.JCheckBoxMenuItem; |
| import javax.swing.JComponent; |
| import javax.swing.JFileChooser; |
| import javax.swing.JMenu; |
| import javax.swing.JMenuItem; |
| import javax.swing.JOptionPane; |
| import javax.swing.JPopupMenu; |
| import javax.swing.JSeparator; |
| import javax.swing.KeyStroke; |
| import javax.swing.RootPaneContainer; |
| import javax.swing.SwingUtilities; |
| import javax.swing.UIDefaults; |
| import javax.swing.UIManager; |
| import javax.swing.event.PopupMenuEvent; |
| import javax.swing.event.PopupMenuListener; |
| import javax.swing.text.Document; |
| import javax.swing.text.JTextComponent; |
| import org.netbeans.api.options.OptionsDisplayer; |
| import org.netbeans.core.options.keymap.api.KeyStrokeUtils; |
| import org.netbeans.core.output2.Controller.ControllerOutputEvent; |
| import org.netbeans.core.output2.ui.AbstractOutputPane; |
| import org.netbeans.core.output2.ui.AbstractOutputTab; |
| import org.openide.DialogDisplayer; |
| import org.openide.NotifyDescriptor; |
| import org.openide.actions.FindAction; |
| import org.openide.awt.StatusDisplayer; |
| import org.openide.util.Exceptions; |
| import org.openide.util.NbBundle; |
| import org.openide.windows.IOContainer; |
| import org.openide.windows.OutputListener; |
| import org.openide.windows.WindowManager; |
| import org.openide.xml.XMLUtil; |
| |
| import static org.netbeans.core.output2.OutputTab.ACTION.*; |
| import org.netbeans.core.output2.options.OutputOptions; |
| import org.netbeans.core.output2.ui.OutputKeymapManager; |
| import org.openide.util.RequestProcessor; |
| import org.openide.util.WeakListeners; |
| import org.openide.windows.IOColors; |
| import org.openide.windows.OutputEvent; |
| |
| |
| /** |
| * A component representing one tab in the output window. |
| */ |
| final class OutputTab extends AbstractOutputTab implements IOContainer.CallBacks { |
| private static final RequestProcessor RP = |
| new RequestProcessor("OutputTab"); //NOI18N |
| private final NbIO io; |
| private OutWriter outWriter; |
| private PropertyChangeListener optionsListener; |
| private volatile boolean actionsLoaded = false; |
| |
| OutputTab(NbIO io) { |
| this.io = io; |
| if (Controller.LOG) Controller.log ("Created an output component for " + io); |
| outWriter = ((NbWriter) io.getOut()).out(); |
| OutputDocument doc = new OutputDocument(outWriter); |
| setDocument(doc); |
| applyOptions(); |
| initOptionsListener(); |
| loadAndInitActions(); |
| } |
| |
| private void applyOptions() { |
| Lines lines = getDocumentLines(); |
| if (lines != null) { |
| OutputOptions opts = io.getOptions(); |
| lines.setDefColor(IOColors.OutputType.OUTPUT, |
| opts.getColorStandard()); |
| lines.setDefColor(IOColors.OutputType.ERROR, |
| opts.getColorError()); |
| lines.setDefColor(IOColors.OutputType.INPUT, |
| opts.getColorInput()); |
| lines.setDefColor(IOColors.OutputType.HYPERLINK, |
| opts.getColorLink()); |
| lines.setDefColor(IOColors.OutputType.HYPERLINK_IMPORTANT, |
| opts.getColorLinkImportant()); |
| Color bg = io.getOptions().getColorBackground(); |
| getOutputPane().getFoldingSideBar().setForeground( |
| opts.getColorStandard()); |
| setTextViewBackground(getOutputPane().getTextView(), bg); |
| getOutputPane().setViewFont( |
| io.getOptions().getFont(getOutputPane().isWrapped())); |
| } |
| } |
| |
| /** |
| * Set text view background color correctly. See bug #225829. |
| */ |
| private void setTextViewBackground(JTextComponent textView, Color bg) { |
| getOutputPane().getTextView().setBackground(bg); |
| getOutputPane().getFoldingSideBar().setBackground(bg); |
| if ("Nimbus".equals(UIManager.getLookAndFeel().getName())) { //NOI18N |
| UIDefaults defaults = new UIDefaults(); |
| defaults.put("EditorPane[Enabled].backgroundPainter", bg); //NOI18N |
| textView.putClientProperty("Nimbus.Overrides", defaults); //NOI18N |
| textView.putClientProperty( |
| "Nimbus.Overrides.InheritDefaults", true); //NOI18N |
| textView.setBackground(bg); |
| } |
| } |
| |
| private final TabAction action(ACTION a) { |
| return actions.get(a); |
| } |
| |
| private void installKBActions() { |
| for (ACTION a : actionsToInstall) { |
| installKeyboardAction(action(a)); |
| } |
| } |
| |
| @Override |
| public void setDocument (Document doc) { |
| if (Controller.LOG) Controller.log ("Set document on " + this + " with " + io); |
| assert SwingUtilities.isEventDispatchThread(); |
| Document old = getDocument(); |
| hasOutputListeners = false; |
| super.setDocument(doc); |
| if (old != null && old instanceof OutputDocument) { |
| ((OutputDocument) old).dispose(); |
| } |
| applyOptions(); |
| } |
| |
| public void reset() { |
| if (origPane != null) { |
| setFilter(null, false, false); |
| } |
| // get new OutWriter |
| outWriter = io.out(); |
| |
| // Workaround for bug 242979. |
| Document actualDocument = getDocument(); |
| if (actualDocument instanceof OutputDocument) { |
| OutputDocument od = (OutputDocument) actualDocument; |
| if (od.getWriter() == outWriter) { |
| return; |
| } |
| } |
| setDocument(new OutputDocument(outWriter)); |
| applyOptions(); |
| } |
| |
| public OutputDocument getDocument() { |
| Document d = getOutputPane().getDocument(); |
| if (d instanceof OutputDocument) { |
| return (OutputDocument) d; |
| } |
| return null; |
| } |
| |
| protected AbstractOutputPane createOutputPane() { |
| return new OutputPane(this); |
| } |
| |
| public void inputSent(String txt) { |
| if (Controller.LOG) Controller.log("Input sent on OutputTab: " + txt); |
| getOutputPane().lockScroll(); |
| NbIO.IOReader in = io.in(); |
| if (in != null) { |
| if (Controller.LOG) Controller.log("Sending input to " + in); |
| in.pushText(txt + "\n"); |
| outWriter.print(txt, null, false, null, null, OutputKind.IN, true); |
| } |
| } |
| |
| protected void inputEof() { |
| if (Controller.LOG) Controller.log ("Input EOF"); |
| NbIO.IOReader in = io.in(); |
| if (in != null) { |
| in.eof(); |
| } |
| } |
| |
| public void hasSelectionChanged(boolean val) { |
| if (isShowing() && actionsLoaded) { |
| actions.get(ACTION.COPY).setEnabled(val); |
| actions.get(ACTION.SELECT_ALL).setEnabled(!getOutputPane().isAllSelected()); |
| } |
| } |
| |
| public NbIO getIO() { |
| return io; |
| } |
| |
| void requestActive() { |
| io.getIOContainer().requestActive(); |
| } |
| |
| public void lineClicked(int line, int pos) { |
| OutWriter out = getOut(); |
| int range[] = new int[2]; |
| OutputListener l = out.getLines().getListener(pos, range); |
| if (l != null) { |
| int size = out.getLines().getCharCount(); |
| assert range[1] < size : "Size: " + size + " range: " + range[0] + " " + range[1]; |
| ControllerOutputEvent oe = new ControllerOutputEvent(io, out, line); |
| l.outputLineAction(oe); |
| //Select the text on click if it is still visible |
| if (getOutputPane().getLength() >= range[1]) { // #179768 |
| getOutputPane().sendCaretToPos(range[0], range[1], true); |
| } |
| } |
| } |
| |
| void enterPressed(int caretMark, int caretDot) { |
| int start, end; |
| if (caretMark < caretDot) { |
| start = caretMark; |
| end = caretDot; |
| } else { |
| start = caretDot; |
| end = caretMark; |
| } |
| if (getOut().getLines().isListener(start, end)) { |
| int[] range = new int[2]; |
| OutputListener listener = getOut().getLines().nearestListener( |
| start, false, range); |
| if (listener != null && range[0] <= start && range[1] >= end) { |
| int line = getOut().getLines().getLineAt(start); |
| OutputEvent oe = new ControllerOutputEvent(io, line); |
| listener.outputLineAction(oe); |
| } |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return "OutputTab@" + System.identityHashCode(this) + " for " + io; |
| } |
| |
| private boolean hasOutputListeners = false; |
| |
| public void documentChanged(OutputPane pane) { |
| if (filtOut != null && pane == origPane) { |
| filtOut.readFrom(outWriter); |
| } |
| boolean hadOutputListeners = hasOutputListeners; |
| hasOutputListeners = getOut() != null && (getOut().getLines().firstListenerLine() >= 0 || getOut().getLines().firstImportantListenerLine() >= 0); |
| if (hasOutputListeners != hadOutputListeners) { |
| hasOutputListenersChanged(hasOutputListeners); |
| } |
| |
| IOContainer ioContainer = io.getIOContainer(); |
| if (io.isFocusTaken()) { |
| // The following two lines pulled up from select per bug#185209 |
| ioContainer.open(); |
| // ioContainer.requestVisible(); |
| |
| ioContainer.select(this); |
| ioContainer.requestVisible(); |
| } |
| Controller.getDefault().updateName(this); |
| if (this == ioContainer.getSelected() && ioContainer.isActivated()) { |
| updateActions(); |
| } |
| } |
| |
| /** |
| * Called when the output stream has been closed, to navigate to the |
| * first line which shows an error (if any). |
| */ |
| private void navigateToFirstErrorLine() { |
| OutWriter out = getOut(); |
| if (out != null) { |
| int line = out.getLines().firstImportantListenerLine(); |
| if (Controller.LOG) { |
| Controller.log("NAV TO FIRST LISTENER LINE: " + line); |
| } |
| if (line >= 0) { |
| getOutputPane().sendCaretToLine(line, false); |
| } |
| } |
| } |
| |
| void hasOutputListenersChanged(boolean hasOutputListeners) { |
| if (hasOutputListeners && getOutputPane().isScrollLocked()) { |
| navigateToFirstErrorLine(); |
| } |
| } |
| |
| public void activated() { |
| updateActions(); |
| } |
| |
| public void closed() { |
| io.setClosed(true); |
| io.setStreamClosed(true); |
| Controller.getDefault().removeTab(io); |
| NbWriter w = io.writer(); |
| if (w != null && w.isClosed()) { |
| //Will dispose the document |
| setDocument(null); |
| io.dispose(); |
| } else if (w != null) { |
| //Something is still writing to the stream, but we're getting rid of the tab. Don't dispose |
| //the writer, just kill the tab's document |
| if (getOut() != null) { |
| getOut().setDisposeOnClose(true); |
| } |
| getDocument().disposeQuietly(); |
| NbIOProvider.dispose(io); |
| } |
| } |
| |
| public void deactivated() { |
| } |
| |
| public void selected() { |
| } |
| |
| /** |
| * Determine if the new caret position is close enough that the scrollbar should be re-locked |
| * to the end of the document. |
| * |
| * @param dot The caret position |
| * @return if it should be locked |
| */ |
| public boolean shouldRelock(int dot) { |
| OutWriter w = getOut(); |
| if (w != null && !w.isClosed()) { |
| int dist = Math.abs(w.getLines().getCharCount() - dot); |
| return dist < 100; |
| } |
| return false; |
| } |
| |
| /** |
| * Flag used to block navigating the editor to the first error line when |
| * selecting the error line in the output window after a build (or maybe |
| * it should navigate the editor there? Could be somewhat rude...) |
| */ |
| boolean ignoreCaretChanges = false; |
| |
| /** |
| * Called when the text caret has changed position - will call OutputListener.outputLineSelected if |
| * there is a listener for that position. |
| * |
| * @param pos The line the caret is in |
| */ |
| int lastCaretListenerRange[]; |
| void caretPosChanged(int pos) { |
| if (!ignoreCaretChanges) { |
| if (lastCaretListenerRange != null && pos >= lastCaretListenerRange[0] && pos < lastCaretListenerRange[1]) { |
| return; |
| } |
| OutWriter out = getOut(); |
| if (out != null) { |
| int[] range = new int[2]; |
| OutputListener l = out.getLines().getListener(pos, range); |
| if (l != null) { |
| ControllerOutputEvent oe = new ControllerOutputEvent(io, out.getLines().getLineAt(pos)); |
| l.outputLineSelected(oe); |
| lastCaretListenerRange = range; |
| } else { |
| lastCaretListenerRange = null; |
| } |
| } |
| } |
| } |
| |
| private void sendCaretToError(boolean backward) { |
| OutWriter out = getOut(); |
| if (out != null) { |
| AbstractOutputPane op = getOutputPane(); |
| int selStart = op.getSelectionStart(); |
| int selEnd = op.getSelectionEnd(); |
| int pos = op.getCaretPos(); |
| |
| // check if link is selected |
| if (selStart != selEnd && pos == selStart && out.getLines().isListener(selStart, selEnd)) { |
| pos = backward ? selStart - 1 : selEnd + 1; |
| } |
| int[] lpos = new int[2]; |
| OutputListener l = out.getLines().nearestListener(pos, backward, lpos); |
| if (l != null) { |
| op.sendCaretToPos(lpos[0], lpos[1], true); |
| if (!io.getIOContainer().isActivated()) { |
| ControllerOutputEvent ce = new ControllerOutputEvent(io, out.getLines().getLineAt(lpos[0])); |
| l.outputLineAction(ce); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Searching from current position |
| * @param reversed true for reverse search |
| * @return true if found |
| */ |
| private boolean find(boolean reversed) { |
| OutWriter out = getOut(); |
| if (out != null) { |
| String lastPattern = FindDialogPanel.result(); |
| if (lastPattern == null) { |
| return false; |
| } |
| int pos = reversed ? getOutputPane().getSelectionStart() : getOutputPane().getCaretPos(); |
| if (pos > getOutputPane().getLength() || pos < 0) { |
| pos = 0; |
| } |
| boolean regExp = FindDialogPanel.regExp(); |
| boolean matchCase = FindDialogPanel.matchCase(); |
| int[] sel = reversed ? out.getLines().rfind(pos, lastPattern, regExp, matchCase) |
| : out.getLines().find(pos, lastPattern, regExp, matchCase); |
| String appendMsg = null; |
| if (sel == null) { |
| sel = reversed ? out.getLines().rfind(out.getLines().getCharCount(), lastPattern, regExp, matchCase) |
| : out.getLines().find(0, lastPattern, regExp, matchCase); |
| if (sel != null) { |
| appendMsg = NbBundle.getMessage(OutputTab.class, reversed ? "MSG_SearchFromEnd" : "MSG_SearchFromBeg"); |
| } |
| } |
| String msg; |
| if (sel != null) { |
| getOutputPane().unlockScroll(); |
| int line = out.getLines().getLineAt(sel[0]); |
| ensureLineVisible(out, line); |
| getOutputPane().setSelection(sel[0], sel[1]); |
| int col = sel[0] - out.getLines().getLineStart(line); |
| msg = NbBundle.getMessage(OutputTab.class, "MSG_Found", lastPattern, line + 1, col + 1); |
| if (appendMsg != null) { |
| msg = msg + "; " + appendMsg; |
| } |
| } else { |
| msg = NbBundle.getMessage(OutputTab.class, "MSG_NotFound", lastPattern); |
| } |
| StatusDisplayer.getDefault().setStatusText(msg); |
| return sel != null; |
| } |
| return false; |
| } |
| |
| /** |
| * Ensure that a line is visible (not inside a collapsed fold). If a change |
| * in the lines object is needed, fire immediately. |
| */ |
| private void ensureLineVisible(OutWriter out, int line) { |
| if (!out.getLines().isVisible(line)) { |
| out.getLines().showFoldsForLine(line); |
| if (out.getLines() instanceof ActionListener) { |
| ((ActionListener) out.getLines()).actionPerformed(null); |
| } |
| } |
| } |
| |
| /** |
| * Holds the last written to directory for the save as file chooser. |
| */ |
| private static String lastDir = null; |
| |
| /** |
| * Invokes a file dialog and if a file is chosen, saves the output to that file. |
| */ |
| void saveAs() { |
| OutWriter out = getOut(); |
| if (out == null) { |
| return; |
| } |
| File f = showFileChooser(this); |
| if (f != null) { |
| try { |
| synchronized (out) { |
| out.getLines().saveAs(f.getPath()); |
| } |
| } catch (IOException ioe) { |
| NotifyDescriptor notifyDesc = new NotifyDescriptor( |
| NbBundle.getMessage(OutputTab.class, "MSG_SaveAsFailed", f.getPath()), |
| NbBundle.getMessage(OutputTab.class, "LBL_SaveAsFailedTitle"), |
| NotifyDescriptor.DEFAULT_OPTION, |
| NotifyDescriptor.ERROR_MESSAGE, |
| new Object[]{NotifyDescriptor.OK_OPTION}, |
| NotifyDescriptor.OK_OPTION); |
| |
| DialogDisplayer.getDefault().notify(notifyDesc); |
| } |
| } |
| } |
| |
| /** |
| * Shows a file dialog and an overwrite dialog if the file exists, returning |
| * null if the user chooses not to overwrite. Will use an AWT FileDialog for |
| * Aqua, per Apple UI guidelines. |
| * |
| * @param owner A parent component for the dialog - the top level ancestor will |
| * actually be used so positioning is correct |
| * @return A file to write to |
| */ |
| private static File showFileChooser(JComponent owner) { |
| File f = null; |
| String dlgTtl = NbBundle.getMessage(Controller.class, "TITLE_SAVE_DLG"); //NOI18N |
| |
| boolean isAqua = "Aqua".equals(UIManager.getLookAndFeel().getID()); //NOI18N |
| |
| if (isAqua) { |
| //Apple UI guidelines recommend against ever using JFileChooser |
| Container frameOrDialog = owner.getTopLevelAncestor(); |
| FileDialog fd; |
| if (frameOrDialog instanceof Frame) { |
| fd = new FileDialog((Frame) frameOrDialog, dlgTtl, FileDialog.SAVE); |
| } else { |
| fd = new FileDialog((Dialog) frameOrDialog, dlgTtl, FileDialog.SAVE); |
| } |
| if (lastDir != null && new File(lastDir).exists()) { |
| fd.setDirectory(lastDir); |
| } |
| fd.setModal(true); |
| fd.setVisible(true); |
| if (fd.getFile() != null && fd.getDirectory() != null) { |
| String s = fd.getDirectory() + fd.getFile(); |
| f = new File(s); |
| if (f.exists() && f.isDirectory()) { |
| f = null; |
| } |
| } |
| } else { |
| JFileChooser jfc = new JFileChooser(); |
| if (lastDir != null && new File(lastDir).exists()) { |
| File dir = new File(lastDir); |
| if (dir.exists()) { |
| jfc.setCurrentDirectory(dir); |
| } |
| } |
| jfc.setName(dlgTtl); |
| jfc.setDialogTitle(dlgTtl); |
| |
| if (jfc.showSaveDialog(owner.getTopLevelAncestor()) == JFileChooser.APPROVE_OPTION) { |
| f = jfc.getSelectedFile(); |
| } |
| } |
| |
| if (f != null && f.exists() && !isAqua) { //Aqua's file dialog takes care of this |
| String msg = NbBundle.getMessage(Controller.class, |
| "FMT_FILE_EXISTS", new Object[]{f.getName()}); //NOI18N |
| String title = NbBundle.getMessage(Controller.class, |
| "TITLE_FILE_EXISTS"); //NOI18N |
| if (JOptionPane.showConfirmDialog(owner.getTopLevelAncestor(), msg, title, |
| JOptionPane.OK_CANCEL_OPTION) != JOptionPane.OK_OPTION) { |
| f = null; |
| } |
| } |
| if (f != null) { |
| lastDir = f.getParent(); |
| } |
| return f; |
| } |
| |
| /** |
| * Called when a line is clicked - if an output listener is listening on that |
| * line, it will be sent <code>outputLineAction</code>. |
| */ |
| private void openHyperlink() { |
| OutWriter out = getOut(); |
| if (out != null) { |
| int pos = getOutputPane().getCaretPos(); |
| int[] range = new int[2]; |
| OutputListener l = out.getLines().getListener(pos, range); |
| if (l != null) { |
| ignoreCaretChanges = true; |
| getOutputPane().sendCaretToPos(range[0], range[1], true); |
| ignoreCaretChanges = false; |
| ControllerOutputEvent coe = new ControllerOutputEvent(io, out.getLines().getLineAt(pos)); |
| l.outputLineAction(coe); |
| } |
| } |
| } |
| |
| /** |
| * Post the output window's popup menu |
| * |
| * @param p The point clicked |
| * @param src The source of the click event |
| */ |
| @NbBundle.Messages({"STATUS_Initializing=Output Window is Initializing"}) |
| void postPopupMenu(Point p, Component src) { |
| if (!actionsLoaded) { |
| StatusDisplayer.getDefault().setStatusText( |
| Bundle.STATUS_Initializing()); |
| return; |
| } |
| JPopupMenu popup = new JPopupMenu(); |
| Action[] a = getToolbarActions(); |
| if (a.length > 0) { |
| boolean added = false; |
| for (int i = 0; i < a.length; i++) { |
| if (a[i].getValue(Action.NAME) != null) { |
| // add the proxy that doesn't show icons #67451 |
| popup.add(new ProxyAction(a[i])); |
| added = true; |
| } |
| } |
| if (added) { |
| popup.addSeparator(); |
| } |
| } |
| |
| List<TabAction> activeActions = new ArrayList<TabAction>(popupItems.length); |
| for (int i = 0; i < popupItems.length; i++) { |
| if (popupItems[i] == null) { |
| popup.addSeparator(); |
| } else { |
| TabAction ta = action(popupItems[i]); |
| if (popupItems[i] == ACTION.WRAP) { |
| JCheckBoxMenuItem item = new JCheckBoxMenuItem(ta); |
| item.setSelected(getOutputPane().isWrapped()); |
| activeActions.add(ta); |
| popup.add(item); |
| } else if (popupItems[i] == ACTION.FILTER) { |
| JCheckBoxMenuItem item = new JCheckBoxMenuItem(ta); |
| item.setSelected(origPane != null); |
| activeActions.add(ta); |
| popup.add(item); |
| } else { |
| if ((popupItems[i] == ACTION.CLOSE |
| && !io.getIOContainer().isCloseable(this))) { |
| continue; |
| } |
| JMenuItem item = popup.add(ta); |
| activeActions.add(ta); |
| if (popupItems[i] == ACTION.FIND) { |
| item.setMnemonic(KeyEvent.VK_F); |
| } |
| } |
| } |
| } |
| addFoldingActionsToPopUpMenu(popup, activeActions); |
| // hack to remove the esc keybinding when doing popup.. |
| KeyStroke esc = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); |
| JComponent c = getOutputPane().getTextView(); |
| Object escHandle = c.getInputMap().get(esc); |
| c.getInputMap().remove(esc); |
| getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).remove(esc); |
| |
| popup.addPopupMenuListener(new PMListener(activeActions, escHandle)); |
| popup.show(src, p.x, p.y); |
| |
| } |
| |
| private void addFoldingActionsToPopUpMenu(JPopupMenu menu, |
| List<TabAction> activeActions) { |
| JMenu submenu = new JMenu(NbBundle.getMessage( |
| OutputTab.class, "LBL_OutputFolds")); //NOI18N |
| for (ACTION a : popUpFoldItems) { |
| if (a == null) { |
| submenu.addSeparator(); |
| } else { |
| TabAction ta = action(a); |
| activeActions.add(ta); |
| submenu.add(new JMenuItem(ta)); |
| } |
| } |
| menu.addSeparator(); |
| menu.add(submenu); |
| } |
| |
| void updateActions() { |
| if (!actionsLoaded) { |
| return; |
| } |
| OutputPane pane = (OutputPane) getOutputPane(); |
| int len = pane.getLength(); |
| boolean enable = len > 0; |
| OutWriter out = getOut(); |
| action(SAVEAS).setEnabled(enable); |
| action(SELECT_ALL).setEnabled(enable); |
| action(COPY).setEnabled(pane.hasSelection()); |
| boolean hasErrors = out == null ? false : out.getLines().hasListeners(); |
| action(NEXT_ERROR).setEnabled(hasErrors); |
| action(PREV_ERROR).setEnabled(hasErrors); |
| } |
| |
| FilteredOutput filtOut; |
| AbstractOutputPane origPane; |
| |
| private void setFilter(String pattern, boolean regExp, boolean matchCase) { |
| if (pattern == null) { |
| assert origPane != null; |
| setOutputPane(origPane); |
| origPane = null; |
| filtOut.dispose(); |
| filtOut = null; |
| } else { |
| assert origPane == null; |
| origPane = getOutputPane(); |
| filtOut = new FilteredOutput(pattern, regExp, matchCase); |
| setOutputPane(filtOut.getPane()); |
| try { |
| waitCursor(true); |
| filtOut.readFrom(outWriter); |
| installKBActions(); |
| } finally { |
| waitCursor(false); |
| } |
| } |
| validate(); |
| getOutputPane().repaint(); |
| requestFocus(); |
| } |
| |
| private void waitCursor(boolean enable) { |
| RootPaneContainer root = ((RootPaneContainer) getTopLevelAncestor()); |
| Cursor cursor = Cursor.getPredefinedCursor(enable ? Cursor.WAIT_CURSOR : Cursor.DEFAULT_CURSOR); |
| root.getGlassPane().setCursor(cursor); |
| root.getGlassPane().setVisible(enable); |
| } |
| |
| OutWriter getOut() { |
| return origPane != null ? filtOut.getWriter() : outWriter; |
| } |
| |
| private void disableHtmlName() { |
| Controller.getDefault().removeFromUpdater(this); |
| String escaped; |
| try { |
| escaped = XMLUtil.toAttributeValue(io.getName() + " "); |
| } catch (CharConversionException e) { |
| escaped = io.getName() + " "; |
| } |
| //#88204 apostophes are escaped in xm but not html |
| io.getIOContainer().setTitle(this, escaped.replace("'", "'")); |
| } |
| |
| private void initOptionsListener() { |
| optionsListener = new PropertyChangeListener() { |
| @Override |
| public void propertyChange(PropertyChangeEvent evt) { |
| Lines lines = getDocumentLines(); |
| if (lines != null) { |
| String pn = evt.getPropertyName(); |
| OutputOptions opts = io.getOptions(); |
| updateOptionsProperty(pn, lines, opts); |
| OutputTab.this.repaint(); |
| } |
| } |
| }; |
| this.io.getOptions().addPropertyChangeListener( |
| WeakListeners.propertyChange(optionsListener, io.getOptions())); |
| } |
| |
| private void updateOptionsProperty(String pn, Lines lines, |
| OutputOptions opts) { |
| |
| if (OutputOptions.PROP_COLOR_STANDARD.equals(pn)) { |
| lines.setDefColor(IOColors.OutputType.OUTPUT, |
| opts.getColorStandard()); |
| getOutputPane().getFoldingSideBar().setForeground( |
| opts.getColorStandard()); |
| } else if (OutputOptions.PROP_COLOR_ERROR.equals(pn)) { |
| lines.setDefColor(IOColors.OutputType.ERROR, |
| opts.getColorError()); |
| } else if (OutputOptions.PROP_COLOR_INPUT.equals(pn)) { |
| lines.setDefColor(IOColors.OutputType.INPUT, |
| opts.getColorInput()); |
| } else if (OutputOptions.PROP_COLOR_LINK.equals(pn)) { |
| lines.setDefColor(IOColors.OutputType.HYPERLINK, |
| opts.getColorLink()); |
| } else if (OutputOptions.PROP_COLOR_LINK_IMPORTANT.equals(pn)) { |
| lines.setDefColor(IOColors.OutputType.HYPERLINK_IMPORTANT, |
| opts.getColorLinkImportant()); |
| } else if (OutputOptions.PROP_COLOR_BACKGROUND.equals(pn)) { |
| Color bg = opts.getColorBackground(); |
| setTextViewBackground(getOutputPane().getTextView(), bg); |
| } else if (OutputOptions.PROP_FONT.equals(pn)) { |
| if (!getOutputPane().isWrapped()) { |
| getOutputPane().setViewFont(opts.getFont()); |
| } |
| } else if (OutputOptions.PROP_FONT_SIZE_WRAP.equals(pn)) { |
| if (getOutputPane().isWrapped()) { |
| getOutputPane().setViewFont(opts.getFontForWrappedMode()); |
| } |
| } |
| } |
| |
| private void loadAndInitActions() { |
| RP.post(new Runnable() { |
| @Override |
| public void run() { |
| createActions(); |
| EventQueue.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| installKBActions(); |
| getActionMap().put("jumpPrev", //NOI18N |
| action(PREV_ERROR)); |
| getActionMap().put("jumpNext", //NOI18N |
| action(NEXT_ERROR)); |
| getActionMap().put(FindAction.class.getName(), |
| action(FIND)); |
| getActionMap().put( |
| javax.swing.text.DefaultEditorKit.copyAction, |
| action(COPY)); |
| actionsLoaded = true; |
| updateActions(); |
| setInputVisible(isInputVisible()); // update action |
| } |
| }); |
| } |
| }); |
| } |
| |
| static enum ACTION { COPY, WRAP, SAVEAS, CLOSE, NEXT_ERROR, PREV_ERROR, |
| SELECT_ALL, FIND, FIND_NEXT, NAVTOLINE, POSTMENU, |
| FIND_PREVIOUS, CLEAR, NEXTTAB, PREVTAB, LARGER_FONT, |
| SMALLER_FONT, SETTINGS, FILTER, PASTE, COLLAPSE_FOLD, |
| EXPAND_FOLD, COLLAPSE_ALL, EXPAND_ALL, COLLAPSE_TREE, |
| EXPAND_TREE} |
| |
| private static final ACTION[] popupItems = new ACTION[] { |
| COPY, PASTE, null, FIND, FIND_NEXT, FIND_PREVIOUS, FILTER, null, |
| WRAP, LARGER_FONT, SMALLER_FONT, SETTINGS, null, |
| SAVEAS, CLEAR, CLOSE, |
| }; |
| |
| private static final ACTION[] popUpFoldItems = new ACTION[]{ |
| COLLAPSE_FOLD, EXPAND_FOLD, null, |
| COLLAPSE_ALL, EXPAND_ALL, null, |
| COLLAPSE_TREE, EXPAND_TREE |
| }; |
| |
| private static final ACTION[] actionsToInstall = new ACTION[] { |
| COPY, SELECT_ALL, FIND, FIND_NEXT, FIND_PREVIOUS, WRAP, LARGER_FONT, |
| SMALLER_FONT, SAVEAS, CLOSE, COPY, NAVTOLINE, POSTMENU, CLEAR, FILTER, |
| COLLAPSE_FOLD, EXPAND_FOLD, COLLAPSE_ALL, EXPAND_ALL, COLLAPSE_TREE, |
| EXPAND_TREE |
| }; |
| |
| private final Map<ACTION, TabAction> actions = new EnumMap<ACTION, TabAction>(ACTION.class);; |
| |
| private void createActions() { |
| KeyStrokeUtils.refreshActionCache(); |
| for (ACTION a : ACTION.values()) { |
| TabAction action; |
| switch(a) { |
| case COPY: |
| case PASTE: |
| case WRAP: |
| case SAVEAS: |
| case CLOSE: |
| case NEXT_ERROR: |
| case PREV_ERROR: |
| case SELECT_ALL: |
| case FIND: |
| case FIND_NEXT: |
| case FIND_PREVIOUS: |
| case FILTER: |
| case LARGER_FONT: |
| case SMALLER_FONT: |
| case SETTINGS: |
| case CLEAR: |
| case COLLAPSE_FOLD: |
| case EXPAND_FOLD: |
| case COLLAPSE_ALL: |
| case EXPAND_ALL: |
| case COLLAPSE_TREE: |
| case EXPAND_TREE: |
| action = new TabAction(a, "ACTION_"+a.name()); |
| break; |
| case NAVTOLINE: |
| action = new TabAction(a, "navToLine", //NOI18N |
| KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)); |
| break; |
| case POSTMENU: |
| action = new TabAction(a, "postMenu", //NOI18N |
| KeyStroke.getKeyStroke(KeyEvent.VK_F10, KeyEvent.SHIFT_DOWN_MASK)); |
| break; |
| case NEXTTAB: |
| action = new TabAction(a, "NextViewAction", //NOI18N |
| (KeyStroke) null); |
| break; |
| case PREVTAB: |
| action = new TabAction(a, "PreviousViewAction", //NOI18N |
| (KeyStroke) null); |
| break; |
| default: |
| throw new IllegalStateException("Unhandled action "+a); |
| } |
| actions.put(a, action); |
| } |
| } |
| |
| /** |
| * A stateless action which will find the owning OutputTab's controller and call |
| * actionPerformed with its ID as an argument. |
| */ |
| class TabAction extends AbstractAction { |
| |
| private ACTION action; |
| |
| /** |
| * Create a ControllerAction with the specified action ID (constants defined in Controller), |
| * using the specified bundle key. Expects the following contents in the bundle: |
| * <ul> |
| * <li>A name for the action matching the passed key</li> |
| * <li>An accelerator for the action matching [key].accel</li> |
| * </ul> |
| * @param id An action ID |
| * @param bundleKey A key for the bundle associated with the Controller class |
| * @see org.openide.util.Utilities#stringToKey |
| */ |
| TabAction(ACTION action, String bundleKey) { |
| if (bundleKey != null) { |
| String name = NbBundle.getMessage(OutputTab.class, bundleKey); |
| List<KeyStroke[]> accels = getAcceleratorsFor(action); |
| this.action = action; |
| putValue(NAME, name); |
| if (accels != null && accels.size() > 0) { |
| List<KeyStroke> l = new ArrayList<KeyStroke>(accels.size()); |
| for (KeyStroke[] ks : accels) { |
| if (ks.length == 1) { // ignore multi-key accelerators |
| l.add(ks[0]); |
| } |
| } |
| if (l.size() > 0) { |
| putValue(ACCELERATORS_KEY, |
| l.toArray(new KeyStroke[l.size()])); |
| putValue(ACCELERATOR_KEY, l.get(0)); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Create a ControllerAction with the specified ID, name and keystroke. Actions created |
| * using this constructor will not be added to the popup menu of the component. |
| * |
| * @param id The ID |
| * @param name A programmatic name for the item |
| * @param stroke An accelerator keystroke |
| */ |
| TabAction(ACTION action, String name, KeyStroke stroke) { |
| this.action = action; |
| putValue(NAME, name); |
| putValue(ACCELERATOR_KEY, stroke); |
| } |
| |
| void clearListeners() { |
| PropertyChangeListener[] l = changeSupport.getPropertyChangeListeners(); |
| for (int i = 0; i < l.length; i++) { |
| removePropertyChangeListener(l[i]); |
| } |
| } |
| |
| /** |
| * Get a keyboard accelerator from the resource bundle, with special handling |
| * for the mac keyboard layout. |
| * |
| * @param action Action to get accelerator for. |
| * @return A keystroke |
| */ |
| private List<KeyStroke[]> getAcceleratorsFor(ACTION action) { |
| switch (action) { |
| case COPY: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| "copy-to-clipboard", null); //NOI18N |
| case PASTE: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| "paste-from-clipboard", null); //NOI18N |
| case SAVEAS: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| OutputKeymapManager.SAVE_AS_ACTION_ID, null); |
| case CLOSE: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| OutputKeymapManager.CLOSE_ACTION_ID, null); |
| case NEXT_ERROR: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| "next-error", null); //NOI18N |
| case PREV_ERROR: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| "previous-error", null); //NOI18N |
| case SELECT_ALL: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| "select-all", null); //NOI18N |
| case FIND: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| "org.openide.actions.FindAction", null); //NOI18N |
| case FIND_NEXT: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| "find-next", null); //NOI18N |
| case FIND_PREVIOUS: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| "find-previous", null); //NOI18N |
| case FILTER: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| OutputKeymapManager.FILTER_ACTION_ID, null); |
| case LARGER_FONT: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| OutputKeymapManager.LARGER_FONT_ACTION_ID, null); |
| case SMALLER_FONT: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| OutputKeymapManager.SMALLER_FONT_ACTION_ID, null); |
| case SETTINGS: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| OutputKeymapManager.OUTPUT_SETTINGS_ACTION_ID, null); |
| case CLEAR: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| OutputKeymapManager.CLEAR_ACTION_ID, null); |
| case WRAP: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| OutputKeymapManager.WRAP_ACTION_ID, null); |
| case COLLAPSE_FOLD: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| "collapse-fold", null); //NOI18N |
| case EXPAND_FOLD: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| "expand-fold", null); //NOI18N |
| case COLLAPSE_ALL: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| "collapse-all-folds", null); //NOI18N |
| case EXPAND_ALL: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| "expand-all-folds", null); //NOI18N |
| case COLLAPSE_TREE: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| "collapse-fold-tree", null); //NOI18N |
| case EXPAND_TREE: |
| return KeyStrokeUtils.getKeyStrokesForAction( |
| "expand-fold-tree", null); //NOI18N |
| default: |
| return null; |
| } |
| } |
| |
| public ACTION getAction() { |
| return action; |
| } |
| |
| @Override |
| public boolean isEnabled() { |
| if (getIO().isClosed()) { |
| // Cached action for an already closed tab. |
| // Try to find the appropriate action for a new active tab... |
| JComponent selected = getIO().getIOContainer().getSelected(); |
| if (OutputTab.this != selected && selected instanceof OutputTab) { |
| OutputTab tab = (OutputTab) selected; |
| Action a = tab.action(action); |
| if (a != null) { |
| return a.isEnabled(); |
| } |
| } |
| return false; |
| } |
| return super.isEnabled(); |
| } |
| |
| public void actionPerformed(ActionEvent e) { |
| if (getIO().isClosed()) { |
| // Cached action for an already closed tab. |
| // Try to find the appropriate action for a new active tab... |
| JComponent selected = getIO().getIOContainer().getSelected(); |
| if (OutputTab.this != selected && selected instanceof OutputTab) { |
| OutputTab tab = (OutputTab) selected; |
| Action a = tab.action(action); |
| if (a != null) { |
| a.actionPerformed(e); |
| } |
| } |
| return ; |
| } |
| switch (getAction()) { |
| case COPY: |
| getOutputPane().copy(); |
| break; |
| case PASTE: |
| getOutputPane().paste(); |
| break; |
| case WRAP: |
| boolean wrapped = getOutputPane().isWrapped(); |
| getOutputPane().setWrapped(!wrapped); |
| break; |
| case SAVEAS: |
| saveAs(); |
| break; |
| case CLOSE: |
| io.getIOContainer().remove(OutputTab.this); |
| break; |
| case NEXT_ERROR: |
| sendCaretToError(false); |
| break; |
| case PREV_ERROR: |
| sendCaretToError(true); |
| break; |
| case SELECT_ALL: |
| getOutputPane().selectAll(); |
| break; |
| case FIND: |
| { |
| String pattern = getFindDlgResult(getOutputPane().getSelectedText(), "LBL_Find_Title", "LBL_Find_What", "BTN_Find"); //NOI18N |
| if (pattern != null && find(false)) { |
| Action findNext = action(FIND_NEXT); |
| Action findPrev = action(FIND_PREVIOUS); |
| if (findNext != null) { |
| findNext.setEnabled(true); |
| } |
| if (findPrev != null) { |
| findPrev.setEnabled(true); |
| } |
| requestFocus(); |
| } |
| } |
| break; |
| case FIND_NEXT: |
| find(false); |
| break; |
| case FIND_PREVIOUS: |
| find(true); |
| break; |
| case NAVTOLINE: |
| openHyperlink(); |
| break; |
| case POSTMENU: |
| postPopupMenu(new Point(0, 0), OutputTab.this); |
| break; |
| case CLEAR: |
| NbWriter writer = io.writer(); |
| if (writer != null) { |
| try { |
| boolean vis = isInputVisible(); |
| boolean closed = io.isStreamClosed(); |
| writer.reset(); |
| setInputVisible(vis); |
| io.setStreamClosed(closed); |
| } catch (IOException ioe) { |
| Exceptions.printStackTrace(ioe); |
| } |
| } |
| break; |
| case SMALLER_FONT: |
| Controller.getDefault().changeFontSizeBy(-1, getOutputPane().isWrapped()); |
| break; |
| case LARGER_FONT: |
| Controller.getDefault().changeFontSizeBy(1, getOutputPane().isWrapped()); |
| break; |
| case SETTINGS: |
| OptionsDisplayer.getDefault().open( |
| "Advanced/OutputSettings"); //NOI18N |
| break; |
| case FILTER: |
| if (origPane != null) { |
| setFilter(null, false, false); |
| } else { |
| String pattern = getFindDlgResult(getOutputPane().getSelectedText(), |
| "LBL_Filter_Title", "LBL_Filter_What", "BTN_Filter"); //NOI18N |
| if (pattern != null) { |
| setFilter(pattern, FindDialogPanel.regExp(), FindDialogPanel.matchCase()); |
| } |
| } |
| break; |
| case COLLAPSE_FOLD: |
| getOutputPane().collapseFold(); |
| break; |
| case EXPAND_FOLD: |
| getOutputPane().expandFold(); |
| break; |
| case COLLAPSE_ALL: |
| getOutputPane().collapseAllFolds(); |
| break; |
| case EXPAND_ALL: |
| getOutputPane().expandAllFolds(); |
| break; |
| case COLLAPSE_TREE: |
| getOutputPane().collapseFoldTree(); |
| break; |
| case EXPAND_TREE: |
| getOutputPane().expandFoldTree(); |
| break; |
| default: |
| assert false; |
| } |
| } |
| } |
| |
| @Override |
| public void setInputVisible(boolean val) { |
| super.setInputVisible(val); |
| Action pasteAction = action(PASTE); |
| if (pasteAction != null) { |
| pasteAction.setEnabled(val); |
| } |
| } |
| |
| private boolean validRegExp(String pattern) { |
| try { |
| Pattern.compile(pattern); |
| return true; |
| } catch (PatternSyntaxException ex) { |
| JOptionPane.showMessageDialog(getTopLevelAncestor(), |
| NbBundle.getMessage(OutputTab.class, "FMT_Invalid_RegExp", pattern), |
| NbBundle.getMessage(OutputTab.class, "LBL_Invalid_RegExp"), JOptionPane.ERROR_MESSAGE); |
| return false; |
| } |
| } |
| |
| private String getFindDlgResult(String selection, String title, String label, String button) { |
| String pattern = FindDialogPanel.getResult(selection, title, label, button); //NOI18N |
| while (pattern != null && FindDialogPanel.regExp()) { |
| if (validRegExp(pattern)) { |
| break; |
| } |
| pattern = FindDialogPanel.getResult(pattern, title, label, button); //NOI18N |
| } |
| return pattern; |
| } |
| |
| private static class ProxyAction implements Action { |
| |
| private Action orig; |
| |
| ProxyAction(Action original) { |
| orig = original; |
| } |
| |
| public Object getValue(String key) { |
| if (Action.SMALL_ICON.equals(key)) { |
| return null; |
| } |
| return orig.getValue(key); |
| } |
| |
| public void putValue(String key, Object value) { |
| orig.putValue(key, value); |
| } |
| |
| public void setEnabled(boolean b) { |
| orig.setEnabled(b); |
| } |
| |
| public boolean isEnabled() { |
| return orig.isEnabled(); |
| } |
| |
| public void addPropertyChangeListener(PropertyChangeListener listener) { |
| orig.addPropertyChangeListener(listener); |
| } |
| |
| public void removePropertyChangeListener(PropertyChangeListener listener) { |
| orig.removePropertyChangeListener(listener); |
| } |
| |
| public void actionPerformed(ActionEvent e) { |
| orig.actionPerformed(e); |
| } |
| } |
| |
| /** |
| * #47166 - a disposed tab which has had its popup menu shown remains |
| * referenced through PopupItems->JSeparator->PopupMenu->Invoker->OutputPane->OutputTab |
| */ |
| private class PMListener implements PopupMenuListener { |
| |
| private List<TabAction> popupItems; |
| private Object handle; |
| |
| PMListener(List<TabAction> popupItems, Object escHandle) { |
| this.popupItems = popupItems; |
| handle = escHandle; |
| } |
| |
| public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { |
| JPopupMenu popup = (JPopupMenu) e.getSource(); |
| popup.removeAll(); |
| popup.setInvoker(null); |
| // hack |
| KeyStroke esc = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); |
| JComponent c = getOutputPane().getTextView(); |
| c.getInputMap().put(esc, handle); |
| getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(esc, handle); |
| |
| //hack end |
| popup.removePopupMenuListener(this); |
| for (TabAction action : popupItems) { |
| action.clearListeners(); |
| } |
| } |
| |
| public void popupMenuCanceled(PopupMenuEvent e) { |
| popupMenuWillBecomeInvisible(e); |
| } |
| |
| public void popupMenuWillBecomeVisible(PopupMenuEvent e) { |
| //do nothing |
| } |
| } |
| |
| private class FilteredOutput { |
| String pattern; |
| OutWriter out; |
| OutputPane pane; |
| OutputDocument doc; |
| int readCount; |
| Pattern compPattern; |
| boolean regExp; |
| boolean matchCase; |
| |
| public FilteredOutput(String pattern, boolean regExp, boolean matchCase) { |
| this.pattern = (regExp || matchCase) ? pattern : pattern.toLowerCase(); |
| this.regExp = regExp; |
| this.matchCase = matchCase; |
| out = new OutWriter(); |
| pane = new OutputPane(OutputTab.this); |
| doc = new OutputDocument(out); |
| pane.setDocument(doc); |
| } |
| |
| boolean passFilter(String str) { |
| if (regExp) { |
| if (compPattern == null) { |
| compPattern = matchCase ? Pattern.compile(pattern) : Pattern.compile(pattern, Pattern.CASE_INSENSITIVE); |
| } |
| return compPattern.matcher(str).find(); |
| } else { |
| return matchCase ? str.contains(pattern) : str.toLowerCase().contains(pattern); |
| } |
| } |
| |
| OutputPane getPane() { |
| return pane; |
| } |
| |
| OutWriter getWriter() { |
| return out; |
| } |
| |
| synchronized void readFrom(OutWriter orig) { |
| AbstractLines lines = (AbstractLines) orig.getLines(); |
| // Last line is not guaranteed to be finished, see #219839. |
| int lineCount = lines.getLineCount() - (orig.isClosed() ? 0 : 1); |
| while (readCount < lineCount) { |
| try { |
| int line = readCount++; |
| String str = lines.getLine(line); |
| if (!passFilter(str)) { |
| continue; |
| } |
| LineInfo info = lines.getExistingLineInfo(line); |
| out.print(str, info, lines.isImportantLine(line)); |
| } catch (IOException ex) { |
| Exceptions.printStackTrace(ex); |
| } |
| } |
| } |
| |
| void dispose() { |
| out.dispose(); |
| } |
| } |
| |
| private Lines getDocumentLines() { |
| OutputDocument doc = getDocument(); |
| return doc == null ? null : doc.getLines(); |
| } |
| } |