package jifi.ui.view.tabbedpaneview.dndtabbedpane; import java.awt.AWTEvent; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.Stroke; import java.awt.SystemColor; import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; import java.text.MessageFormat; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.JComponent; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JTabbedPane; import static javax.swing.SwingConstants.BOTTOM; import static javax.swing.SwingConstants.LEFT; import static javax.swing.SwingConstants.RIGHT; import static javax.swing.SwingConstants.TOP; import javax.swing.SwingUtilities; import javax.swing.ToolTipManager; import javax.swing.event.MouseInputAdapter; import javax.swing.plaf.TabbedPaneUI; import jifi.ui.view.tabbedpaneview.JSplitPanel; import jifi.ui.view.tabbedpaneview.TabbedPaneView; /** * Copyright (C) 2004 Robert Futrell * http://fifesoft.com/rtext * Licensed under a modified BSD license. * See the included license file for details. * * @author Robert Futrell * @version 0.5 * @see DrawDnDIndicatorTabbedPane */ public class DnDTabbedPane extends JTabbedPane { private final DnDTabbedPaneTransferHandler transferHandler = new DnDTabbedPaneTransferHandler(); private int rightClickIndex; private int x, y, width, height; private Stroke wideStroke; private JPopupMenu popup; private boolean inCloseCurrentDocument; private TabbedPaneCloseAction closeAction; private Rectangle rBackward = new Rectangle(); private Rectangle rForward = new Rectangle(); private Rectangle rScroll = new Rectangle(); private Rectangle rSplitTop = new Rectangle(); private Rectangle rSplitBottom = new Rectangle(); private Rectangle rSplitLeft = new Rectangle(); private Rectangle rSplitRight = new Rectangle(); private int rwh = 20; private int buttonsize = 30; //XXX 30 is magic number of scroll button size private Point point = new Point(); private final AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f); private long lastUpdate = 0; private boolean forceUpdate = false; private boolean splitPanel = false; private TabbedPaneView tabbedPaneView = null; private int splitOrientation = 0; private static final int stroke = 4; public boolean splitPanel() { return splitPanel; } public int getSplitOrientation() { return splitOrientation; } private Rectangle getTabAreaBounds() { Rectangle tabbedRect = getBounds(); //pointed out by daryl. NullPointerException: i.e. addTab("Tab",null) //Rectangle compRect = getSelectedComponent().getBounds(); Component comp = getSelectedComponent(); int idx = 0; while (comp == null && idx < getTabCount()) { comp = getComponentAt(idx++); } Rectangle compRect = (comp == null) ? new Rectangle() : comp.getBounds(); int tabPlacement = getTabPlacement(); if (tabPlacement == TOP) { tabbedRect.height = tabbedRect.height - compRect.height; } else if (tabPlacement == BOTTOM) { tabbedRect.y = tabbedRect.y + compRect.y + compRect.height; tabbedRect.height = tabbedRect.height - compRect.height; } else if (tabPlacement == LEFT) { tabbedRect.width = tabbedRect.width - compRect.width; } else if (tabPlacement == RIGHT) { tabbedRect.x = tabbedRect.x + compRect.x + compRect.width; tabbedRect.width = tabbedRect.width - compRect.width; } tabbedRect.grow(2, 2); return tabbedRect; } private void clickArrowButton(String actionKey) { ActionMap map = getActionMap(); if (map != null) { Action action = map.get(actionKey); if (action != null && action.isEnabled()) { action.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, null, 0, 0)); } } } public DnDTabbedPane() { setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); x = y = -1; enableEvents(AWTEvent.MOUSE_EVENT_MASK); ToolTipManager.sharedInstance().registerComponent(this); //addChangeListener(this); setTransferHandler(transferHandler); try { getDropTarget().addDropTargetListener(transferHandler); } catch (java.util.TooManyListenersException tmle) { System.out.println(":/"); } TabDragListener tdl = new TabDragListener(); addMouseListener(tdl); addMouseMotionListener(tdl); } public void clearDnDIndicatorRect() { x = y = -1; repaint(); } protected JPopupMenu getTabPopupMenu() { if (popup == null) { popup = new JPopupMenu(); String title = "Close"; closeAction = new TabbedPaneCloseAction(title); JMenuItem item = new JMenuItem(closeAction); popup.add(item); title = "CloseOthers"; item = new JMenuItem( new TabbedPaneCloseOthersAction(title)); popup.add(item); // item = new JMenuItem(owner.getAction(RText.CLOSE_ALL_ACTION)); item.setToolTipText(null); popup.add(item); popup.add(new JPopupMenu.Separator()); title = "CopyPathToClipboard"; item = new JMenuItem( new TabbedPaneCopyPathAction(title)); popup.add(item); } return popup; } @Override public String getToolTipText(MouseEvent e) { TabbedPaneUI ui = getUI(); if (ui != null) { int index = ui.tabForCoordinate(this, e.getX(), e.getY()); if (index != -1) { return "whut1";//getRTextEditorPaneAt(index).getFileFullPath(); } } return super.getToolTipText(e); } @Override public void paint(Graphics g) { try { super.paint(g); } catch (Exception e) { } if (x != -1 || forceUpdate) { forceUpdate = false; Graphics2D g2d = (Graphics2D) g; if (wideStroke == null) { wideStroke = new BasicStroke(stroke); } Stroke temp = g2d.getStroke(); g2d.setStroke(wideStroke); g2d.setColor(SystemColor.controlDkShadow); Rectangle r = getTabAreaBounds(); if (point.y < r.y + r.height) { g2d.drawRect(x, y, width, height); } int ctabPlacement = getTabPlacement(); if (ctabPlacement == TOP || ctabPlacement == BOTTOM) { rBackward.setBounds(r.x, r.y, rwh, r.height); rForward.setBounds(r.x + r.width - rwh - buttonsize, r.y, rwh + buttonsize, r.height); } else if (ctabPlacement == LEFT || ctabPlacement == RIGHT) { rBackward.setBounds(r.x, r.y, r.width, rwh); rForward.setBounds(r.x, r.y + r.height - rwh - buttonsize, r.width, rwh + buttonsize); } int splitHeight = this.getHeight() / 5; int splitWidth = this.getWidth() / 5; rScroll.setBounds(r.x, r.y, this.getWidth(), r.height); rSplitTop.setBounds(stroke + r.x, stroke + r.y, this.getWidth() - stroke, splitHeight + r.height - stroke); rSplitBottom.setBounds(r.x + stroke, r.y + this.getHeight() - splitHeight + stroke, this.getWidth() - stroke, splitHeight - stroke); rSplitLeft.setBounds(r.x + stroke, r.y + stroke, splitWidth - stroke, this.getHeight() - stroke); rSplitRight.setBounds(r.x + this.getWidth() - splitWidth + stroke, r.y + stroke, splitWidth - stroke, this.getHeight() - stroke); rBackward = SwingUtilities.convertRectangle(getParent(), rBackward, this); rForward = SwingUtilities.convertRectangle(getParent(), rForward, this); rScroll = SwingUtilities.convertRectangle(getParent(), rScroll, this); rSplitTop = SwingUtilities.convertRectangle(getParent(), rSplitTop, this); rSplitBottom = SwingUtilities.convertRectangle(getParent(), rSplitBottom, this); rSplitLeft = SwingUtilities.convertRectangle(getParent(), rSplitLeft, this); rSplitRight = SwingUtilities.convertRectangle(getParent(), rSplitRight, this); // g2d.setPaint(Color.BLUE); // g2d.fillRect(point.x - 5, point.y - 5, 10, 10); // // g2d.setComposite(composite); // g2d.setPaint(Color.RED); // g2d.fill(rBackward); // g2d.fill(rForward); // // g2d.setPaint(Color.LIGHT_GRAY); // g2d.fill(rScroll); // // g2d.setPaint(Color.GREEN); // g2d.fill(rSplitTop); // g2d.fill(rSplitBottom); // g2d.fill(rSplitLeft); // g2d.fill(rSplitRight); // g2d.setPaint(Color.BLUE); splitPanel = !rScroll.contains(point); if (splitPanel) { if (rSplitTop.contains(point)) { g2d.draw(rSplitTop); splitOrientation = 1; } else if (rSplitBottom.contains(point)) { g2d.draw(rSplitBottom); splitOrientation = 2; } else if (rSplitLeft.contains(point)) { g2d.draw(rSplitLeft); splitOrientation = 3; } else if (rSplitRight.contains(point)) { g2d.draw(rSplitRight); splitOrientation = 4; } else { splitOrientation = 0; } } else { splitOrientation = 0; } g2d.setStroke(temp); } } @Override protected void processMouseEvent(MouseEvent e) { // NOTE: We don't allow RMB clicks that aren't popup triggers // to go into super.processMouseEvent() in the off-chance that // a popup trigger is mouse release. By default, tabbed panes // would then use the mouse "press" of the RMB to select the // armed tab, which we don't want. if (SwingUtilities.isRightMouseButton(e)) { if (e.isPopupTrigger()) { int x = e.getX(); int y = e.getY(); int index = indexAtLocation(x, y); if (index != -1) { rightClickIndex = index; JPopupMenu popup = getTabPopupMenu(); String name = "whut2";//RTextTabbedPaneView.this.getDocumentDisplayNameAt(index); closeAction.setDocumentName(name); popup.show(this, x, y); } } } else if (SwingUtilities.isMiddleMouseButton(e)) { if (e.getID() == MouseEvent.MOUSE_CLICKED && e.getClickCount() == 1 && !e.isPopupTrigger()) { int x = e.getX(); int y = e.getY(); int index = indexAtLocation(x, y); if (index != -1) { setSelectedIndex(index); closeCurrentDocument(); } } } else { super.processMouseEvent(e); } } public void setDnDIndicatorRect(int x, int y, int width, int height) { this.x = x; this.y = y; this.width = width; this.height = height; repaint(); } public void dragOver(Point mouseLocation) { point.x = mouseLocation.x; point.y = mouseLocation.y; forceUpdate = true; repaint(); try { if (System.currentTimeMillis() - lastUpdate > 400) { if (rBackward.contains(point)) { clickArrowButton("scrollTabsBackwardAction"); lastUpdate = System.currentTimeMillis(); } else if (rForward.contains(point)) { clickArrowButton("scrollTabsForwardAction"); lastUpdate = System.currentTimeMillis(); } } } catch (Exception e) { } } @Override public void updateUI() { super.updateUI(); if (popup != null) { SwingUtilities.updateComponentTreeUI(popup); } } /** * Action that closes the tab last right-clicked (e.g. the popup menu was * displayed for it) in the tabbed pane. */ private class TabbedPaneCloseAction extends AbstractAction { private String template; public TabbedPaneCloseAction(String template) { this.template = template; } public void actionPerformed(ActionEvent e) { if (rightClickIndex > -1) { setSelectedIndex(rightClickIndex); closeCurrentDocument(); } } public void setDocumentName(String name) { String text = MessageFormat.format(template, new Object[]{name}); putValue(AbstractAction.NAME, text); } } /** * Action that closes all tabs except the one last right-clicked (e.g. the * popup menu was displayed for it) in the tabbed pane. */ private class TabbedPaneCloseOthersAction extends AbstractAction { public TabbedPaneCloseOthersAction(String text) { putValue(AbstractAction.NAME, text); } public void actionPerformed(ActionEvent e) { if (rightClickIndex > -1) { closeAllDocumentsExcept(rightClickIndex); } } } /** * Action that copies the full path to the right-clicked tab to the * clipboard. */ private class TabbedPaneCopyPathAction extends AbstractAction { public TabbedPaneCopyPathAction(String text) { putValue(AbstractAction.NAME, text); } public void actionPerformed(ActionEvent e) { if (rightClickIndex > -1) { // RTextEditorPane textArea = RTextTabbedPaneView.this. // getRTextEditorPaneAt(rightClickIndex); // String path = textArea.getFileFullPath(); // Clipboard c = Toolkit.getDefaultToolkit(). // getSystemClipboard(); // c.setContents(new StringSelection(path), null); } } } /** * Listens for the user drag-and-dropping tabs in this tabbed pane. */ private class TabDragListener extends MouseInputAdapter { private int tab; private JComponent draggedTab; MouseEvent firstMouseEvent; @Override public void mouseDragged(MouseEvent e) { if (draggedTab == null) { return; } if (firstMouseEvent != null) { e.consume(); int action = javax.swing.TransferHandler.MOVE; int dx = Math.abs(e.getX() - firstMouseEvent.getX()); int dy = Math.abs(e.getY() - firstMouseEvent.getY()); //Arbitrarily define a 5-pixel shift as the //official beginning of a drag. if (dx > 5 || dy > 5) { //This is a drag, not a click. //Tell the transfer handler to initiate the drag. transferHandler.exportAsDrag(DnDTabbedPane.this, firstMouseEvent, action); firstMouseEvent = null; } } } @Override public void mousePressed(MouseEvent e) { tab = indexAtLocation(e.getX(), e.getY()); if (tab > -1) { draggedTab = (JComponent) getComponentAt(tab); firstMouseEvent = e; e.consume(); } } @Override public void mouseReleased(MouseEvent e) { draggedTab = null; firstMouseEvent = null; } } // // /** // * Adds a tab to the tabbed pane, and places a number beside documents // * opened multiple times. // * // * @param title The "display name" to use on the tab of the document. // * @param component The scroll pane containing the text editor to add. // * @param fileFullPath The path to the file this editor contains. // */ // @Override // protected void addTextAreaImpl(String title, Component component, // String fileFullPath) { // // // "Physically" add the tab. // JPanel temp = new JPanel(new BorderLayout()); // temp.add(component); // RTextScrollPane sp = (RTextScrollPane) component; // RTextEditorPane textArea = (RTextEditorPane) sp.getTextArea(); // temp.add(createErrorStrip(textArea), BorderLayout.LINE_END); // tabbedPane.addTab(title, getIconFor(sp), temp); // // // Loop through all tabs (documents) except the last (the one just added). // int tabCount = getNumDocuments(); // for (int i = 0; i < tabCount - 1; i++) { // // // If any of them is the same physical file as the just added one, do the numbering. // if (getRTextEditorPaneAt(i).getFileFullPath().equals(fileFullPath)) { // int count = 0; // for (int j = i; j < tabCount; j++) { // RTextEditorPane pane = getRTextEditorPaneAt(j); // if (pane.getFileFullPath().equals(fileFullPath)) { // String newTitle = title + " (" + (++count) + ")"; // if (pane.isDirty()) { // newTitle = newTitle + "*"; // } // try { // setDocumentDisplayNameAt(j, newTitle); // } catch (Exception e) { // owner.displayException(e); // } // } // } // break; // } // // } // // // Do any extra stuff. // // This updates currentTextArea and shifts focus too. // setSelectedIndex(tabCount - 1); // if (getCurrentTextArea().isDirty()) { // owner.setMessages(fileFullPath + "*", "Opened document '" + fileFullPath + "'"); // } else { // owner.setMessages(fileFullPath, "Opened document '" + fileFullPath + "'"); // } // // // RText's listeners will be updated by stateChanged() for all // // addTextAreaImpl() calls. // } /** * {@inheritDoc} */ protected synchronized boolean closeCurrentDocumentImpl() { // ResourceBundle msg = owner.getResourceBundle(); // Return code for if the user is prompted to save; returns yes for // closeAllDocuments(). // int rc = promptToSaveBeforeClosingIfDirty(); // if (rc == JOptionPane.CANCEL_OPTION) { // return false; // } inCloseCurrentDocument = true; // RTextEditorPane oldTextArea = getCurrentTextArea(); // Remove listeners from current text area IFF stateChanged() won't do it // (i.e., we're removing any document except the "rightmost" document). // boolean removingLastDocument = (getSelectedIndex()==getNumDocuments()-1); // if (removingLastDocument==false) { // oldTextArea.removeCaretListener(owner); // } // Remove the document from this tabbed pane. removeComponentAt(getSelectedIndex()); // If there are open documents, make sure any duplicates are numbered // correctly. If there are no open documents, add a new empty one. if (getNumDocuments() > 0) { // renumberDisplayNames(); } else { // addNewEmptyUntitledFile(); } // Request focus in the window for the new currentTextArea. // Note that this may also be done by stateChanged() but we need to // do it here too because code below relies on currentTextArea being // up-to-date. // oldTextArea = getCurrentTextArea(); // setCurrentTextArea(getRTextEditorPaneAt(getSelectedIndex())); // final RTextEditorPane currentTextArea = getCurrentTextArea(); // MUST be done by SwingUtilities.invokeLater(), I think because // currentTextArea is not yet visible on this line of code, so // calling requestFocusInWindow() now would do nothing. // SwingUtilities.invokeLater(new Runnable() { // public void run() { // // NOTE: This null check here is silly, but we have to do // // it because: Before this runnable runs, we are sure that // // at least 1 document is open in RText. However, when this // // Runnable runs, there is a case where there are no // // documents open: If one empty untitled document is open, // // and the user tries to open a file from the file history // // that no longer exists. In that case the current document // // will have been closed to be replaced with the new one, // // but the "does not exist, create it?" dialog pops up // // while no documents are in the tabbed pane, which causes // // currentTextArea to be null here. // if (currentTextArea != null) { // currentTextArea.requestFocusInWindow(); // } // } // }); // Update RText's listeners IFF the active document number doesn't // change (i.e., they closed any document except the "rightmost" one). // Closing the "rightmost" document means stateChanged() will handle // the listeners. // if (removingLastDocument==false) { // currentTextArea.addCaretListener(owner); // } inCloseCurrentDocument = false; // Let any listeners know that the current document changed. // firePropertyChange(CURRENT_DOCUMENT_PROPERTY, -1, getSelectedIndex()); // fireCurrentTextAreaEvent(CurrentTextAreaEvent.TEXT_AREA_CHANGED, // oldTextArea, currentTextArea); // Update the RText's status bar. // updateStatusBar(); // Update RText's title and the status bar message. // if (currentTextArea.isDirty()) { // owner.setMessages(currentTextArea.getFileFullPath() + "*", msg.getString("Ready")); // } else { // owner.setMessages(currentTextArea.getFileFullPath(), msg.getString("Ready")); // } return true; } /** * Returns the name being displayed for the specified document. * * @param index The index at which to find the name. If the index is * invalid, <code>null</code> is returned. * @return The name being displayed for this document. */ public String getDocumentDisplayNameAt(int index) { if (index >= 0 && index < getTabCount()) { return getTitleAt(index); } return null; } /** * Returns the location of the document selection area of this component. * * @return The location of the document selection area. */ public int getDocumentSelectionPlacement() { return getTabPlacement(); } /** * Returns the number of documents open in this container. * * @return The number of open documents. */ public int getNumDocuments() { return getTabCount(); } // /** // * {@inheritDoc} // */ // @Override // public RTextScrollPane getRTextScrollPaneAt(int index) { // if (index < 0 || index >= getNumDocuments()) //throw new IndexOutOfBoundsException(); // { // return null; // } // JPanel temp = (JPanel) tabbedPane.getComponentAt(index); // return (RTextScrollPane) temp.getComponent(0); // } /** * Returns the currently active component. * * @return The component. */ @Override public Component getSelectedComponent() { return getComponentAt(getSelectedIndex()); } // /** // * Repaints the display names for open documents. // */ // @Override // public void refreshDisplayNames() { // // Color defaultForeground = UIManager.getColor("tabbedpane.foreground"); // Color modifiedColor = getModifiedDocumentDisplayNamesColor(); // int numDocuments = getNumDocuments(); // // if (highlightModifiedDocumentDisplayNames() == true) { // for (int i = 0; i < numDocuments; i++) { // if (getRTextEditorPaneAt(i).isDirty() == true) { // tabbedPane.setForegroundAt(i, modifiedColor); // } else { // tabbedPane.setForegroundAt(i, defaultForeground); // } // } // } else { // for (int i = 0; i < numDocuments; i++) { // tabbedPane.setForegroundAt(i, defaultForeground); // } // } // // } /** * Removes a component from this container. Note that this method does not * update currentTextArea, you must do that yourself. */ protected void removeComponentAt(int index) { if (index >= 0 && index < getNumDocuments()) { removeTabAt(index); //currentTextArea = getRTextEditorPaneAt(getSelectedIndex()); } } // /** // * Sets the name of the document displayed on the document's tab. // * // * @param index The index of the document whose name you want to change. If // * this value is invalid, this method does nothing. // * @param displayName The name to display. // * @see #getDocumentDisplayNameAt // */ // @Override // public void setDocumentDisplayNameAt(int index, String displayName) { // if (index >= 0 && index < getNumDocuments()) { // setTitleAt(index, displayName); // // Hack-of-a-way to tell if this document is modified. // if (displayName.charAt(displayName.length() - 1) == '*') { // if (highlightModifiedDocumentDisplayNames() == true) { // setForegroundAt(index,getModifiedDocumentDisplayNamesColor()); // } else { // setForegroundAt(index,getForeground()); // } // } // Just set it to regular color (this may/may not be unnecessary...). // else { // setForegroundAt(index, getForeground()); // } // } // // May need to reset icon if extension has changed. // setIconAt(index, getIconFor(getRTextScrollPaneAt(index))); // } // /** // * Sets the currently active document. This updates currentTextArea. // * // * @param index The index of the document to make the active document. If // * this value is invalid, nothing happens. // */ // @Override // public void setSelectedIndex(int index) { // if (index >= 0 && index < getNumDocuments()) { // setSelectedIndex(index); // setCurrentTextArea(getRTextEditorPaneAt(index)); // updateStatusBar(); // getCurrentTextArea().requestFocusInWindow(); // } // } // public void stateChanged(ChangeEvent e) { // // // Skip this stuff if we're called from closeCurrentDocument(), as // // that method does this stuff to; let's not do it twice (listeners // // would get two CURRENT_DOCUMENT_PROPERTY events). // if (inCloseCurrentDocument) { // return; // } // // // TODO: Factor the code below and the similar code in // // closeCurrentDocument() into a common method. // // Remove the RText listeners associated with the current // // currentTextArea. // RTextEditorPane oldTextArea = getCurrentTextArea(); // if (oldTextArea != null) { // oldTextArea.removeCaretListener(owner); // } // // // The new currentTextArea will only be null when we're closing the // // only open document. Even then, after this a new document will be // // opened and this method will be re-called. // setCurrentTextArea(getRTextEditorPaneAt(getSelectedIndex())); // final RTextEditorPane currentTextArea = getCurrentTextArea(); // if (currentTextArea != null) { // // if (currentTextArea.isDirty()) { // owner.setMessages(currentTextArea.getFileFullPath() + "*", null); // } else { // owner.setMessages(currentTextArea.getFileFullPath(), null); // } // updateStatusBar(); // Update read-only and line/col. indicators. // // // Give the current text area focus. We have to do this in // // a Runnable as during this stateChanged() call, the text area's // // panel hasn't actually been made visible yet, and that must // // have happened for requestFocusInWindow to work. // SwingUtilities.invokeLater(new Runnable() { // public void run() { // currentTextArea.requestFocusInWindow(); // } // }); // // // Update RText actions associated with the current currentTextArea. // currentTextArea.addCaretListener(owner); // // // Trick the parent RText into updating the row/column indicator. // // We have to check mainView for null because this is called in // // RText's constructor, before RText has a mainView. // if (owner.getMainView() != null) // Null because caretUpdate doesn't actually use the caret event. // { // owner.caretUpdate(null); // } // // // Let any listeners know that the current document changed. // firePropertyChange(CURRENT_DOCUMENT_PROPERTY, -1, getSelectedIndex()); // fireCurrentTextAreaEvent(CurrentTextAreaEvent.TEXT_AREA_CHANGED, // null, currentTextArea); // // } // // } /** * Attempts to close all currently active documents. * * @return <code>true</code> if all active documents were closed, and * <code>false</code> if they weren't (i.e., the user hit cancel). */ public boolean closeAllDocuments() { return closeAllDocumentsExcept(-1); } /** * Attempts to close all currently active documents except the one * specified. * * @return <code>true</code> if the documents were all closed, and * <code>false</code> if they weren't (i.e., the user hit cancel). */ public boolean closeAllDocumentsExcept(int except) { int numDocuments = getNumDocuments(); setSelectedIndex(numDocuments - 1); // Start at the back. // Cycle through each document, one by one. for (int i = numDocuments - 1; i >= 0; i--) { if (i == except) { // Instead of removing this document, set focus to the // "first" document, and continue closing documents with the // next iteration. Since we're only keeping around 1 // document, this keeps it open. if (i > 0) { setSelectedIndex(0); } } else { // Try to close the document. boolean closed = closeCurrentDocument(); // If the user cancels out of it, quit the whole schibang. if (!closed) { // If the newly-active file is read-only, say so in the status bar. // owner.setStatusBarReadOnlyIndicatorEnabled( // currentTextArea==null ? false // : currentTextArea.isReadOnly()); return false; } } } // End of for (int i=tabCount-1; i>=0; i--). // If we got this far, then all documents were closed. // We'll just have an empty default-named file out there. return true; } /** * Attempts to close the current document. * * @return Whether the file was closed (e.g. the user didn't cancel the * operation). This will also return <code>false</code> if an IO error * occurs saving the file, if the user chooses to do so. */ public final boolean closeCurrentDocument() { // RTextEditorPane old = currentTextArea; boolean closed = closeCurrentDocumentImpl(); if (closed) { // old.clearParsers(); // firePropertyChange(TEXT_AREA_REMOVED_PROPERTY, null, old); } return closed; } }