/* * Editor.java * * Created on August 4, 2000, 9:06 AM */ package org.netbeans.editor.example.res; import org.netbeans.editor.*; import org.netbeans.editor.ext.ExtSettingsInitializer; import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileView; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import javax.swing.undo.UndoManager; import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.URL; import java.util.*; import java.util.List; /** * * @author Petr Nejedly * @version 0.2 */ public class StudioEditor extends JFrame { private static final File distributionDirectory; static { URL url = StudioEditor.class.getProtectionDomain().getCodeSource().getLocation(); String protocol = url.getProtocol(); File file = new File(url.getFile()); if (!file.isDirectory()) file = file.getParentFile(); distributionDirectory = file; } /** Document property holding String name of associated file */ private static final String FILE = "file"; /** Document property holding Boolean if document was created or opened */ private static final String CREATED = "created"; /** Document property holding Boolean modified information */ private static final String MODIFIED = "modified"; private ResourceBundle settings = ResourceBundle.getBundle("settings"); private JFileChooser fileChooser; private boolean createBackups; private boolean safeSave; private int fileCounter = -1; Map com2text = new HashMap(); private Impl impl = new Impl("org.netbeans.editor.Bundle"); private class Impl extends FileView implements WindowListener, ActionListener, LocaleSupport.Localizer { private ResourceBundle bundle; public Impl(String bundleName) { bundle = ResourceBundle.getBundle(bundleName); } // FileView implementation public String getName(File f) { return null; } public String getDescription(File f) { return null; } public String getTypeDescription(File f) { return null; } public Boolean isTraversable(File f) { return null; } public Icon getIcon(File f) { if (f.isDirectory()) return null; KitInfo ki = KitInfo.getKitInfoForFile(f); return ki == null ? null : ki.getIcon(); } // Localizer public String getString(String key) { return bundle.getString(key); } // Mostly no-op WindowListener for close public void windowActivated(WindowEvent evt) { } public void windowClosed(WindowEvent evt) { } public void windowDeactivated(WindowEvent evt) { } public void windowDeiconified(WindowEvent evt) { } public void windowIconified(WindowEvent evt) { } public void windowOpened(WindowEvent evt) { } public void windowClosing(WindowEvent evt) { doExit(); } // ActionListener for menu items public void actionPerformed(ActionEvent evt) { Object src = evt.getSource(); if (!handleOpenRecent(src)) { if (src == openItem) { fileChooser.setMultiSelectionEnabled(true); int returnVal = fileChooser.showOpenDialog(StudioEditor.this); if (returnVal == JFileChooser.APPROVE_OPTION) { File[] files = fileChooser.getSelectedFiles(); for (int i = 0; i < files.length; i++) openFile(files[i]); } fileChooser.setMultiSelectionEnabled(false); } else if (src == closeItem) { Component editor = tabPane.getSelectedComponent(); if (checkClose(editor)) { doCloseEditor(editor); } } else if (src == saveItem) { saveFile(tabPane.getSelectedComponent()); } else if (src == saveAsItem) { saveAs(tabPane.getSelectedComponent()); } else if (src == saveAllItem) { int index = tabPane.getSelectedIndex(); for (int i = 0; i < tabPane.getComponentCount(); i++) { saveFile(tabPane.getComponentAt(i)); } tabPane.setSelectedIndex(index); } else if (src == exitItem) { doExit(); } else if (src instanceof JMenuItem) { Object ki = ((JMenuItem) src).getClientProperty("kitInfo"); if (ki instanceof KitInfo) { createNewFile((KitInfo) ki); } } } } } public StudioEditor() { super("NetBeans Editor"); LocaleSupport.addLocalizer(impl); // Feed our kits with their default Settings Settings.addInitializer(new BaseSettingsInitializer(), Settings.CORE_LEVEL); Settings.addInitializer(new ExtSettingsInitializer(), Settings.CORE_LEVEL); Settings.reset(); // Create visual hierarchy initComponents(); openItem.addActionListener(impl); closeItem.addActionListener(impl); saveItem.addActionListener(impl); saveAsItem.addActionListener(impl); saveAllItem.addActionListener(impl); exitItem.addActionListener(impl); addWindowListener(impl); // Prepare the editor kits and such things readSettings(); // Do the actual layout setLocation(150, 150); pack(); fileToMenu = new HashMap(); menuToFile = new HashMap(); recentFiles = new Vector(); maxRecent = 4; createBackups = false; safeSave = true; } public Dimension getPreferredSize() { Dimension size = new Dimension(640, 480); return size; } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ private void initComponents() {//GEN-BEGIN:initComponents tabPane = new JTabbedPane(); menuBar = new JMenuBar(); fileMenu = new JMenu(); newMenu = new JMenu(); openItem = new JMenuItem(); closeItem = new JMenuItem(); sep1 = new JSeparator(); saveItem = new JMenuItem(); saveAsItem = new JMenuItem(); saveAllItem = new JMenuItem(); sep2 = new JSeparator(); exitItem = new JMenuItem(); getContentPane().setLayout(new java.awt.GridLayout(1, 1)); setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); getContentPane().add(tabPane); fileMenu.setMnemonic(KeyEvent.VK_F); fileMenu.setText("File"); newMenu.setMnemonic(KeyEvent.VK_N); newMenu.setText("New..."); fileMenu.add(newMenu); openItem.setMnemonic(KeyEvent.VK_O); openItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_MASK)); openItem.setText("Open File..."); fileMenu.add(openItem); closeItem.setMnemonic(KeyEvent.VK_C); closeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, InputEvent.CTRL_MASK)); closeItem.setText("Close"); fileMenu.add(closeItem); fileMenu.add(sep1); saveItem.setMnemonic(KeyEvent.VK_S); saveItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK)); saveItem.setText("Save"); fileMenu.add(saveItem); saveAsItem.setMnemonic(KeyEvent.VK_A); saveAsItem.setText("Save As..."); fileMenu.add(saveAsItem); saveAllItem.setMnemonic(KeyEvent.VK_L); saveAllItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_MASK)); saveAllItem.setText("Save All"); fileMenu.add(saveAllItem); fileMenu.add(sep2); exitItem.setMnemonic(KeyEvent.VK_E); exitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK)); exitItem.setText("Exit"); fileMenu.add(exitItem); menuBar.add(fileMenu); setJMenuBar(menuBar); }//GEN-END:initComponents private boolean saveFile(Component comp, File file, boolean checkOverwrite) { if (comp == null) return false; tabPane.setSelectedComponent(comp); JTextComponent edit = (JTextComponent) com2text.get(comp); Document doc = edit.getDocument(); if (checkOverwrite && file.exists()) { tabPane.setSelectedComponent(comp); int choice = JOptionPane.showOptionDialog(this, "File " + file.getName() + " already exists, overwrite?", "File exists", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, // don't use a custom Icon null, // use standard button titles null // no default selection ); if (choice != 0) return false; } File safeSaveFile = new File(file.getAbsolutePath() + "~~"); File backupFile = new File(file.getAbsolutePath() + "~"); if (safeSave || createBackups) { file.renameTo(safeSaveFile); } FileWriter output = null; try { output = new FileWriter(file); edit.write(output); if (createBackups) { safeSaveFile.renameTo(backupFile); } else { if (safeSave) { safeSaveFile.delete(); } } } catch (IOException exc) { JOptionPane.showMessageDialog(this, "Can't write to file '" + file.getName() + "'.", "Error", JOptionPane.ERROR_MESSAGE); if (safeSave) safeSaveFile.renameTo(file); return false; } finally { if (output != null) { try { output.close(); } catch (IOException e) { e.printStackTrace(System.err); } } } doc.putProperty(MODIFIED, Boolean.FALSE); doc.putProperty(CREATED, Boolean.FALSE); doc.putProperty(FILE, file); doc.addDocumentListener(new MarkingDocumentListener(comp)); int index = tabPane.indexOfComponent(comp); tabPane.setTitleAt(index, file.getName()); return true; } private boolean saveFile(Component comp) { if (comp == null) return false; JTextComponent edit = (JTextComponent) com2text.get(comp); Document doc = edit.getDocument(); File file = (File) doc.getProperty(FILE); boolean created = ((Boolean) doc.getProperty(CREATED)).booleanValue(); return saveFile(comp, file, created); } private boolean saveAs(Component comp) { if (comp == null) return false; JTextComponent edit = (JTextComponent) com2text.get(comp); File file = (File) edit.getDocument().getProperty(FILE); fileChooser.setCurrentDirectory(file.getParentFile()); fileChooser.setSelectedFile(file); KitInfo fileInfo = KitInfo.getKitInfoOrDefault(file); if (fileInfo != null) fileChooser.setFileFilter(fileInfo); // show the dialog, test the result if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) return saveFile(comp, fileChooser.getSelectedFile(), true); else return false; // Cancel was pressed - not saved } private void openFile(File file) { KitInfo info = KitInfo.getKitInfoOrDefault(file); final JEditorPane pane = new JEditorPane(info.getType(), ""); try { pane.read(new FileInputStream(file), file.getCanonicalPath()); } catch (IOException exc) { JOptionPane.showMessageDialog(this, "Can't read from file '" + file.getName() + "'.", "Error", JOptionPane.ERROR_MESSAGE); return; } addEditorPane(pane, info.getIcon(), file, false); removeFromRecent(file.getAbsolutePath()); } private void doExit() { boolean exit = true; int components = tabPane.getComponentCount(); for (int cntr = 0; cntr < components; cntr++) { Component editor = tabPane.getComponentAt(cntr); if (!checkClose(editor)) { exit = false; return; } } if (!exit) { System.err.println("keeping"); return; } writeUserConfiguration(); while (tabPane.getComponentCount() > 0) { Component editor = tabPane.getComponentAt(0); if ((editor != null) && (com2text.get(editor) != null)) doCloseEditor(editor); } if (exit) System.exit(0); } private void doCloseEditor(Component editor) { JTextComponent editorPane = (JTextComponent) com2text.get(editor); if (editorPane != null) { File file = (File) editorPane.getDocument().getProperty(FILE); addToRecent(file.getAbsolutePath()); } tabPane.remove(editor); com2text.remove(editor); } private boolean checkClose(Component comp) { if (comp == null) return false; JTextComponent edit = (JTextComponent) com2text.get(comp); Document doc = edit.getDocument(); Object mod = doc.getProperty(MODIFIED); if (mod == null || !((Boolean) mod).booleanValue()) return true; tabPane.setSelectedComponent(comp); File file = (File) doc.getProperty(FILE); for (; ;) { int choice = JOptionPane.showOptionDialog(this, "File " + file.getName() + " was modified, save it?", "File modified", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, // don't use a custom Icon new String[]{"Save", "Save As...", "Discard", "Cancel"}, // use standard button titles "Cancel" //default selection ); switch (choice) { case JOptionPane.CLOSED_OPTION: case 4: return false; // Cancel or Esc pressed case 1: if (!saveAs(comp)) continue; // Ask for fileName, then save return true; case 0: if (!saveFile(comp)) continue; // else fall through case 2: return true; // Discard changes, close window } return false; } } private void addEditorPane(JEditorPane pane, Icon icon, File file, boolean created) { final JComponent c = (pane.getUI() instanceof BaseTextUI) ? Utilities.getEditorUI(pane).getExtComponent() : new JScrollPane(pane); Document doc = pane.getDocument(); doc.addDocumentListener(new MarkingDocumentListener(c)); doc.putProperty(FILE, file); doc.putProperty(CREATED, created ? Boolean.TRUE : Boolean.FALSE); UndoManager um = new UndoManager(); doc.addUndoableEditListener(um); doc.putProperty(BaseDocument.UNDO_MANAGER_PROP, um); com2text.put(c, pane); tabPane.addTab(file.getName(), icon, c, file.getAbsolutePath()); tabPane.setSelectedComponent(c); pane.requestFocus(); } private void createNewFile(KitInfo info) { final String fileName = ((++fileCounter == 0) ? "unnamed" : ("unnamed" + fileCounter)) + info.getDefaultExtension(); final File file = new File(fileName).getAbsoluteFile(); final JEditorPane pane = new JEditorPane(info.getType(), ""); URL template = info.getTemplate(); if (template != null) { try { pane.read(template.openStream(), file.getCanonicalPath()); } catch (IOException e) { JOptionPane.showMessageDialog(this, "Can't read template", "Error", JOptionPane.ERROR_MESSAGE); } } addEditorPane(pane, info.getIcon(), file, true); } public static File getDistributionDirectory() { return distributionDirectory; } /** * @param args the command line arguments */ public static void main(String args[]) { if (!getDistributionDirectory().canRead()) { System.err.println("Fatal error while startup - can read from distribution directory."); System.exit(0); } StudioEditor editor = new StudioEditor(); editor.show(); editor.readUserConfiguration(); for (int i = 0; i < args.length; i++) { String fileName = args[i]; editor.openFile(new File(fileName)); } } private Map fileToMenu; private Map menuToFile; private Vector recentFiles; private int maxRecent; private JSeparator recentSeparator; private int separatorIndex; private String[] getOpenedFiles() { ArrayList opened = new ArrayList(); int components = tabPane.getComponentCount(); for (int cntr = 0; cntr < components; cntr++) { Component editorComponent = tabPane.getComponentAt(cntr); JTextComponent editor = (JTextComponent) com2text.get(editorComponent); if (editor == null) { continue; } Document doc = editor.getDocument(); File file = (File) doc.getProperty(FILE); if (file != null) { opened.add(file.getAbsolutePath()); } } return (String[]) opened.toArray(new String[opened.size()]); } private int findInRecent(String fileToFind) { for (int cntr = 0; cntr < recentFiles.size(); cntr++) { String file = (String) recentFiles.get(cntr); if (fileToFind.equals(file)) return cntr; } return -1; } private boolean handleOpenRecent(Object source) { String fileName = (String) menuToFile.get(source); if (fileName == null) return false; openFile(new File(fileName)); return true; } private String generateMenuItemName(int index, String file) { return "" + index + ". " + file; } private void addToRecent(String fileToAdd) { //Remove possible previous occurence: removeFromRecent(fileToAdd); if (recentFiles.size() >= maxRecent) { while (recentFiles.size() >= maxRecent) { removeFromRecent(recentFiles.size() - 1); } } recentFiles.add(0, fileToAdd); JMenuItem newItem = new JMenuItem(generateMenuItemName(1, fileToAdd)); if (recentFiles.size() == 1) { recentSeparator = new JSeparator(); fileMenu.add(recentSeparator); separatorIndex = fileMenu.getMenuComponentCount(); } newItem.addActionListener(impl); fileMenu.insert(newItem, separatorIndex); fileToMenu.put(fileToAdd, newItem); menuToFile.put(newItem, fileToAdd); correctItemNumbers(); } private void correctItemNumbers() { for (int cntr = 0; cntr < recentFiles.size(); cntr++) { JMenuItem item = (JMenuItem) fileToMenu.get(recentFiles.get(cntr)); item.setText(generateMenuItemName(cntr + 1, (String) recentFiles.get(cntr))); } } private void removeFromRecent(String fileToRemove) { int position = findInRecent(fileToRemove); if (position != (-1)) removeFromRecent(position); } private void removeFromRecent(int indexToRemove) { String file = (String) recentFiles.get(indexToRemove); recentFiles.remove(indexToRemove); JMenuItem fileItem = (JMenuItem) fileToMenu.get(file); fileMenu.remove(fileItem); fileToMenu.remove(file); menuToFile.remove(fileItem); correctItemNumbers(); if (recentFiles.size() == 0) { fileMenu.remove(recentSeparator); recentSeparator = null; separatorIndex = -1; } } private String[] readStrings(ResourceBundle bundle, String prefix) { int count = 0; boolean finish = false; ArrayList result = new ArrayList(); while (!finish) { try { String current = bundle.getString(prefix + "_" + count); result.add(current); count++; } catch (MissingResourceException e) { finish = true; } } return (String[]) result.toArray(new String[result.size()]); } private void readUserConfiguration(ResourceBundle bundle) { String[] openedFiles = readStrings(bundle, "Open-File"); String[] recentFiles = readStrings(bundle, "Recent-File"); String recentFilesMaxCount = bundle.getString("Max-Recent-Files"); String safeSaveString = bundle.getString("Safe-Save"); String createBackupsString = bundle.getString("Create-Backups"); this.maxRecent = Integer.parseInt(recentFilesMaxCount); this.safeSave = Boolean.valueOf(safeSaveString).booleanValue(); this.createBackups = Boolean.valueOf(createBackupsString).booleanValue(); for (int cntr = recentFiles.length; cntr > 0; cntr--) { addToRecent(recentFiles[cntr - 1]); } for (int cntr = 0; cntr < openedFiles.length; cntr++) { openFile(new File(openedFiles[cntr])); } } private void writeUserConfiguration(PrintWriter output) { output.println("Max-Recent-Files=" + maxRecent); output.println("Safe-Save=" + safeSave); output.println("Create-Backups=" + createBackups); for (int cntr = 0; cntr < recentFiles.size(); cntr++) { output.println("Recent-File_" + cntr + "=" + recentFiles.get(cntr)); } String[] openFiles = getOpenedFiles(); for (int cntr = 0; cntr < openFiles.length; cntr++) { output.println("Open-File_" + cntr + "=" + openFiles[cntr]); } } private File getConfigurationFileName() { File homedir = new File(System.getProperty("user.home")).getAbsoluteFile(); File configurationFile = new File(homedir, ".nb-editor"); return configurationFile; } private void writeUserConfiguration() { File configurationFile = getConfigurationFileName(); File configurationFileBackup = new File(configurationFile.getAbsolutePath() + "~"); boolean backup = false; if (configurationFile.exists()) { backup = true; configurationFile.renameTo(configurationFileBackup); } PrintWriter output = null; try { output = new PrintWriter(new FileWriter(configurationFile)); writeUserConfiguration(output); if (backup) { if (!output.checkError()) { configurationFileBackup.delete(); } else { //Put back to original configuration: configurationFileBackup.renameTo(configurationFile); } } } catch (IOException e) { e.printStackTrace(); } finally { if (output != null) { output.close(); } } } private void readUserConfiguration() { File configurationFileName = getConfigurationFileName(); InputStream in = null; try { in = new FileInputStream(configurationFileName); readUserConfiguration(new PropertyResourceBundle(in)); } catch (FileNotFoundException e) { //The file containing user-defined configuration not found. //This is nothing really important. try { System.err.println("User configuration not found in \"" + configurationFileName.getCanonicalPath() + "\"."); } catch (IOException f) { } } catch (IOException e) { e.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } } // Variables declaration - do not modify//GEN-BEGIN:variables private JSeparator sep2; private JSeparator sep1; private JMenu newMenu; private JMenuItem saveAllItem; private JMenuItem closeItem; private JMenuBar menuBar; private JMenuItem exitItem; private JTabbedPane tabPane; private JMenuItem saveAsItem; private JMenu fileMenu; private JMenuItem openItem; private JMenuItem saveItem; // End of variables declaration//GEN-END:variables private void readSettings() throws MissingResourceException { File currentPath = new File(System.getProperty("user.dir")).getAbsoluteFile(); fileChooser = new JFileChooser(currentPath); fileChooser.setFileView(impl); String kits = settings.getString("InstalledEditors"); String defaultKit = settings.getString("DefaultEditor"); StringTokenizer st = new StringTokenizer(kits, ","); while (st.hasMoreTokens()) { String kitName = st.nextToken(); // At the first, we have to read ALL info about kit String contentType = settings.getString(kitName + "_ContentType"); String extList = settings.getString(kitName + "_ExtensionList"); String menuTitle = settings.getString(kitName + "_NewMenuTitle"); char menuMnemonic = settings.getString(kitName + "_NewMenuMnemonic").charAt(0); String templateURL = settings.getString(kitName + "_Template"); String iconName = settings.getString(kitName + "_Icon"); String filterTitle = settings.getString(kitName + "_FileFilterTitle"); String kit = settings.getString(kitName + "_KitClass"); // At the second, we surely need an instance of kitClass Class kitClass; try { kitClass = Class.forName(kit); } catch (ClassNotFoundException exc) { // we really need it throw new MissingResourceException("Missing class", kit, "KitClass"); } // At the third, it is nice to have icon although we could live without one Icon icon = null; ClassLoader loader = kitClass.getClassLoader(); if (loader == null) loader = ClassLoader.getSystemClassLoader(); URL resource = loader.getResource(iconName); if (resource == null) resource = ClassLoader.getSystemResource(iconName); if (resource != null) icon = new ImageIcon(resource); // At the fourth, try to get URL for template URL template = loader.getResource(templateURL); if (resource == null) template = ClassLoader.getSystemResource(templateURL); // Finally, convert the list of extensions to, ehm, List :-) List l = new ArrayList(5); StringTokenizer extST = new StringTokenizer(extList, ","); while (extST.hasMoreTokens()) l.add(extST.nextToken()); // Actually create the KitInfo from provided informations KitInfo ki = new KitInfo(contentType, l, template, icon, filterTitle, kitClass, loader, defaultKit.equals(kitName)); // Make the MenuItem for it JMenuItem item = new JMenuItem(menuTitle, icon); item.setMnemonic(menuMnemonic); item.putClientProperty("kitInfo", ki); item.addActionListener(impl); newMenu.add(item); // Register a FileFilter for given type of file fileChooser.addChoosableFileFilter(ki); } // Finally, add fileFilter that would recognize files of all kits fileChooser.addChoosableFileFilter(new FileFilter() { public String getDescription() { return "All recognized files"; } public boolean accept(File f) { return f.isDirectory() || KitInfo.getKitInfoForFile(f) != null; } }); if (KitInfo.getDefault() == null) throw new MissingResourceException("Missing default kit definition", defaultKit, "DefaultEditor"); } private static final class KitInfo extends FileFilter { private static List kits = new ArrayList(); private static KitInfo defaultKitInfo; public static List getKitList() { return new ArrayList(kits); } public static KitInfo getDefault() { return defaultKitInfo; } public static KitInfo getKitInfoOrDefault(File f) { KitInfo ki = getKitInfoForFile(f); return ki == null ? defaultKitInfo : ki; } public static KitInfo getKitInfoForFile(File f) { for (int i = 0; i < kits.size(); i++) { if (((KitInfo) kits.get(i)).accept(f)) return (KitInfo) kits.get(i); } return null; } private String type; private String[] extensions; private URL template; private Icon icon; private Class kitClass; private String description; public KitInfo(String type, List exts, URL template, Icon icon, String description, Class kitClass, ClassLoader loader, boolean isDefault) { // Fill in the structure this.type = type; this.extensions = (String[]) exts.toArray(new String[0]); this.template = template; this.icon = icon; this.description = description; this.kitClass = kitClass; // Register us JEditorPane.registerEditorKitForContentType(type, kitClass.getName(), loader); kits.add(this); if (isDefault) defaultKitInfo = this; } public String getType() { return type; } public String getDefaultExtension() { return extensions[0]; } public URL getTemplate() { return template; } public Icon getIcon() { return icon; } public Class getKitClass() { return kitClass; } public String getDescription() { return description; } public boolean accept(File f) { if (f.isDirectory()) return true; String fileName = f.getName(); for (int i = 0; i < extensions.length; i++) { if (fileName.endsWith(extensions[i])) return true; } return false; } } /** Listener listening for document changes on opened documents. There is * initially one instance per opened document, but this listener is * one-fire only - as soon as it gets fired, markes changes and removes * itself from document. On save, new Listener is hooked again. */ private class MarkingDocumentListener implements DocumentListener { private Component comp; public MarkingDocumentListener(Component comp) { this.comp = comp; } private void markChanged(DocumentEvent evt) { Document doc = evt.getDocument(); doc.putProperty(MODIFIED, Boolean.TRUE); File file = (File) doc.getProperty(FILE); int index = tabPane.indexOfComponent(comp); tabPane.setTitleAt(index, file.getName() + '*'); doc.removeDocumentListener(this); } public void changedUpdate(DocumentEvent e) { } public void insertUpdate(DocumentEvent evt) { markChanged(evt); } public void removeUpdate(DocumentEvent evt) { markChanged(evt); } } }