Mega Code Archive

 
Categories / Java / Tiny Application
 

Sample application using the simple text editor component that supports only one font

/*  * @(#)Notepad.java  1.31 05/11/17  *   * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.  *   * Redistribution and use in source and binary forms, with or without  * modification, are permitted provided that the following conditions are met:  *   * -Redistribution of source code must retain the above copyright notice, this  *  list of conditions and the following disclaimer.  *   * -Redistribution in binary form must reproduce the above copyright notice,   *  this list of conditions and the following disclaimer in the documentation  *  and/or other materials provided with the distribution.  *   * Neither the name of Sun Microsystems, Inc. or the names of contributors may   * be used to endorse or promote products derived from this software without   * specific prior written permission.  *   * This software is provided "AS IS," without a warranty of any kind. ALL   * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING  * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE  * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")  * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE  * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS  * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST   * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,   * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY   * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,   * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.  *   * You acknowledge that this software is not designed, licensed or intended  * for use in the design, construction, operation or maintenance of any  * nuclear facility.  */ /*  * @(#)Notepad.java  1.31 05/11/17  */ import java.awt.*; import java.awt.event.*; import java.beans.*; import java.io.*; import java.net.URL; import java.util.*; import javax.swing.text.*; import javax.swing.undo.*; import javax.swing.event.*; import javax.swing.*; /**  * Sample application using the simple text editor component that  * supports only one font.  *  * @author  Timothy Prinzing  * @version 1.31 11/17/05   */ class Notepad extends JPanel {     private static ResourceBundle resources;     private final static String EXIT_AFTER_PAINT = new String("-exit");     private static boolean exitAfterFirstPaint;     static {         try {             resources = ResourceBundle.getBundle("resources.Notepad",                                                   Locale.getDefault());         } catch (MissingResourceException mre) {             System.err.println("resources/Notepad.properties not found");             System.exit(1);         }     }     public void paintChildren(Graphics g) {         super.paintChildren(g);         if (exitAfterFirstPaint) {             System.exit(0);         }     }     Notepad() {   super(true);   // Force SwingSet to come up in the Cross Platform L&F   try {       UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());       // If you want the System L&F instead, comment out the above line and       // uncomment the following:       // UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());   } catch (Exception exc) {       System.err.println("Error loading L&F: " + exc);   }   setBorder(BorderFactory.createEtchedBorder());   setLayout(new BorderLayout());   // create the embedded JTextComponent   editor = createEditor();   // Add this as a listener for undoable edits.   editor.getDocument().addUndoableEditListener(undoHandler);   // install the command table   commands = new Hashtable();   Action[] actions = getActions();   for (int i = 0; i < actions.length; i++) {       Action a = actions[i];       //commands.put(a.getText(Action.NAME), a);       commands.put(a.getValue(Action.NAME), a);   }      JScrollPane scroller = new JScrollPane();   JViewport port = scroller.getViewport();   port.add(editor);   try {       String vpFlag = resources.getString("ViewportBackingStore");       Boolean bs = Boolean.valueOf(vpFlag);       port.setBackingStoreEnabled(bs.booleanValue());   } catch (MissingResourceException mre) {       // just use the viewport default   }   menuItems = new Hashtable();   JPanel panel = new JPanel();   panel.setLayout(new BorderLayout());     panel.add("North",createToolbar());   panel.add("Center", scroller);   add("Center", panel);   add("South", createStatusbar());     }     public static void main(String[] args) {         try {         String vers = System.getProperty("java.version");         if (vers.compareTo("1.1.2") < 0) {             System.out.println("!!!WARNING: Swing must be run with a " +                                "1.1.2 or higher version VM!!!");         }         if (args.length > 0 && args[0].equals(EXIT_AFTER_PAINT)) {             exitAfterFirstPaint = true;         }         JFrame frame = new JFrame();         frame.setTitle(resources.getString("Title"));   frame.setBackground(Color.lightGray);   frame.getContentPane().setLayout(new BorderLayout());         Notepad notepad = new Notepad();   frame.getContentPane().add("Center", notepad);         frame.setJMenuBar(notepad.createMenubar());   frame.addWindowListener(new AppCloser());   frame.pack();   frame.setSize(500, 600);         frame.show();         } catch (Throwable t) {             System.out.println("uncaught exception: " + t);             t.printStackTrace();         }     }     /**      * Fetch the list of actions supported by this      * editor.  It is implemented to return the list      * of actions supported by the embedded JTextComponent      * augmented with the actions defined locally.      */     public Action[] getActions() {   return TextAction.augmentList(editor.getActions(), defaultActions);     }     /**      * Create an editor to represent the given document.        */     protected JTextComponent createEditor() {   JTextComponent c = new JTextArea();   c.setDragEnabled(true);   c.setFont(new Font("monospaced", Font.PLAIN, 12));   return c;     }     /**       * Fetch the editor contained in this panel      */     protected JTextComponent getEditor() {   return editor;     }     /**      * To shutdown when run as an application.  This is a      * fairly lame implementation.   A more self-respecting      * implementation would at least check to see if a save      * was needed.      */     protected static final class AppCloser extends WindowAdapter {         public void windowClosing(WindowEvent e) {       System.exit(0);   }     }     /**      * Find the hosting frame, for the file-chooser dialog.      */     protected Frame getFrame() {   for (Container p = getParent(); p != null; p = p.getParent()) {       if (p instanceof Frame) {     return (Frame) p;       }   }   return null;     }     /**      * This is the hook through which all menu items are      * created.  It registers the result with the menuitem      * hashtable so that it can be fetched with getMenuItem().      * @see #getMenuItem      */     protected JMenuItem createMenuItem(String cmd) {   JMenuItem mi = new JMenuItem(getResourceString(cmd + labelSuffix));         URL url = getResource(cmd + imageSuffix);   if (url != null) {       mi.setHorizontalTextPosition(JButton.RIGHT);       mi.setIcon(new ImageIcon(url));   }   String astr = getResourceString(cmd + actionSuffix);   if (astr == null) {       astr = cmd;   }   mi.setActionCommand(astr);   Action a = getAction(astr);   if (a != null) {       mi.addActionListener(a);       a.addPropertyChangeListener(createActionChangeListener(mi));       mi.setEnabled(a.isEnabled());   } else {       mi.setEnabled(false);   }   menuItems.put(cmd, mi);   return mi;     }     /**      * Fetch the menu item that was created for the given      * command.      * @param cmd  Name of the action.      * @returns item created for the given command or null      *  if one wasn't created.      */     protected JMenuItem getMenuItem(String cmd) {   return (JMenuItem) menuItems.get(cmd);     }     protected Action getAction(String cmd) {   return (Action) commands.get(cmd);     }     protected String getResourceString(String nm) {   String str;   try {       str = resources.getString(nm);   } catch (MissingResourceException mre) {       str = null;   }   return str;     }     protected URL getResource(String key) {   String name = getResourceString(key);   if (name != null) {       URL url = this.getClass().getResource(name);       return url;   }   return null;     }     protected Container getToolbar() {   return toolbar;     }     protected JMenuBar getMenubar() {   return menubar;     }     /**      * Create a status bar      */     protected Component createStatusbar() {   // need to do something reasonable here   status = new StatusBar();   return status;     }     /**      * Resets the undo manager.      */     protected void resetUndoManager() {   undo.discardAllEdits();   undoAction.update();   redoAction.update();     }     /**      * Create the toolbar.  By default this reads the       * resource file for the definition of the toolbar.      */     private Component createToolbar() {   toolbar = new JToolBar();   String[] toolKeys = tokenize(getResourceString("toolbar"));   for (int i = 0; i < toolKeys.length; i++) {       if (toolKeys[i].equals("-")) {     toolbar.add(Box.createHorizontalStrut(5));       } else {     toolbar.add(createTool(toolKeys[i]));       }   }   toolbar.add(Box.createHorizontalGlue());   return toolbar;     }     /**      * Hook through which every toolbar item is created.      */     protected Component createTool(String key) {   return createToolbarButton(key);     }     /**      * Create a button to go inside of the toolbar.  By default this      * will load an image resource.  The image filename is relative to      * the classpath (including the '.' directory if its a part of the      * classpath), and may either be in a JAR file or a separate file.      *       * @param key The key in the resource file to serve as the basis      *  of lookups.      */     protected JButton createToolbarButton(String key) {   URL url = getResource(key + imageSuffix);         JButton b = new JButton(new ImageIcon(url)) {             public float getAlignmentY() { return 0.5f; }   };         b.setRequestFocusEnabled(false);         b.setMargin(new Insets(1,1,1,1));   String astr = getResourceString(key + actionSuffix);   if (astr == null) {       astr = key;   }   Action a = getAction(astr);   if (a != null) {       b.setActionCommand(astr);       b.addActionListener(a);   } else {       b.setEnabled(false);   }   String tip = getResourceString(key + tipSuffix);   if (tip != null) {       b.setToolTipText(tip);   }           return b;     }     /**      * Take the given string and chop it up into a series      * of strings on whitespace boundaries.  This is useful      * for trying to get an array of strings out of the      * resource file.      */     protected String[] tokenize(String input) {   Vector v = new Vector();   StringTokenizer t = new StringTokenizer(input);   String cmd[];   while (t.hasMoreTokens())       v.addElement(t.nextToken());   cmd = new String[v.size()];   for (int i = 0; i < cmd.length; i++)       cmd[i] = (String) v.elementAt(i);   return cmd;     }     /**      * Create the menubar for the app.  By default this pulls the      * definition of the menu from the associated resource file.       */     protected JMenuBar createMenubar() {   JMenuItem mi;   JMenuBar mb = new JMenuBar();   String[] menuKeys = tokenize(getResourceString("menubar"));   for (int i = 0; i < menuKeys.length; i++) {       JMenu m = createMenu(menuKeys[i]);       if (m != null) {     mb.add(m);       }   }         this.menubar = mb;   return mb;     }     /**      * Create a menu for the app.  By default this pulls the      * definition of the menu from the associated resource file.      */     protected JMenu createMenu(String key) {   String[] itemKeys = tokenize(getResourceString(key));   JMenu menu = new JMenu(getResourceString(key + "Label"));   for (int i = 0; i < itemKeys.length; i++) {       if (itemKeys[i].equals("-")) {     menu.addSeparator();       } else {     JMenuItem mi = createMenuItem(itemKeys[i]);     menu.add(mi);       }   }   return menu;     }     // Yarked from JMenu, ideally this would be public.     protected PropertyChangeListener createActionChangeListener(JMenuItem b) {   return new ActionChangedListener(b);     }     // Yarked from JMenu, ideally this would be public.     private class ActionChangedListener implements PropertyChangeListener {         JMenuItem menuItem;                  ActionChangedListener(JMenuItem mi) {             super();             this.menuItem = mi;         }         public void propertyChange(PropertyChangeEvent e) {             String propertyName = e.getPropertyName();             if (e.getPropertyName().equals(Action.NAME)) {                 String text = (String) e.getNewValue();                 menuItem.setText(text);             } else if (propertyName.equals("enabled")) {                 Boolean enabledState = (Boolean) e.getNewValue();                 menuItem.setEnabled(enabledState.booleanValue());             }         }     }     private JTextComponent editor;     private Hashtable commands;     private Hashtable menuItems;     private JMenuBar menubar;     private JToolBar toolbar;     private JComponent status;     private JFrame elementTreeFrame;     protected ElementTreePanel elementTreePanel;     protected FileDialog fileDialog;     /**      * Listener for the edits on the current document.      */     protected UndoableEditListener undoHandler = new UndoHandler();     /** UndoManager that we add edits to. */     protected UndoManager undo = new UndoManager();     /**      * Suffix applied to the key used in resource file      * lookups for an image.      */     public static final String imageSuffix = "Image";     /**      * Suffix applied to the key used in resource file      * lookups for a label.      */     public static final String labelSuffix = "Label";     /**      * Suffix applied to the key used in resource file      * lookups for an action.      */     public static final String actionSuffix = "Action";     /**      * Suffix applied to the key used in resource file      * lookups for tooltip text.      */     public static final String tipSuffix = "Tooltip";     public static final String openAction = "open";     public static final String newAction  = "new";     public static final String saveAction = "save";     public static final String exitAction = "exit";     public static final String showElementTreeAction = "showElementTree";     class UndoHandler implements UndoableEditListener {   /**    * Messaged when the Document has created an edit, the edit is    * added to <code>undo</code>, an instance of UndoManager.    */         public void undoableEditHappened(UndoableEditEvent e) {       undo.addEdit(e.getEdit());       undoAction.update();       redoAction.update();   }     }     /**      * FIXME - I'm not very useful yet      */     class StatusBar extends JComponent {         public StatusBar() {       super();       setLayout(new BoxLayout(this, BoxLayout.X_AXIS));   }         public void paint(Graphics g) {       super.paint(g);   }     }     // --- action implementations -----------------------------------     private UndoAction undoAction = new UndoAction();     private RedoAction redoAction = new RedoAction();     /**      * Actions defined by the Notepad class      */     private Action[] defaultActions = {   new NewAction(),   new OpenAction(),         new SaveAction(),   new ExitAction(),   new ShowElementTreeAction(),         undoAction,         redoAction     };     class UndoAction extends AbstractAction {   public UndoAction() {       super("Undo");       setEnabled(false);   }   public void actionPerformed(ActionEvent e) {       try {     undo.undo();       } catch (CannotUndoException ex) {     System.out.println("Unable to undo: " + ex);     ex.printStackTrace();       }       update();       redoAction.update();   }   protected void update() {       if(undo.canUndo()) {     setEnabled(true);     putValue(Action.NAME, undo.getUndoPresentationName());       }       else {     setEnabled(false);     putValue(Action.NAME, "Undo");       }   }     }     class RedoAction extends AbstractAction {   public RedoAction() {       super("Redo");       setEnabled(false);   }   public void actionPerformed(ActionEvent e) {       try {     undo.redo();       } catch (CannotRedoException ex) {     System.out.println("Unable to redo: " + ex);     ex.printStackTrace();       }       update();       undoAction.update();   }   protected void update() {       if(undo.canRedo()) {     setEnabled(true);     putValue(Action.NAME, undo.getRedoPresentationName());       }       else {     setEnabled(false);     putValue(Action.NAME, "Redo");       }   }     }     class OpenAction extends NewAction {   OpenAction() {       super(openAction);   }         public void actionPerformed(ActionEvent e) {       Frame frame = getFrame();             JFileChooser chooser = new JFileChooser();             int ret = chooser.showOpenDialog(frame);             if (ret != JFileChooser.APPROVE_OPTION) {     return;       }             File f = chooser.getSelectedFile();       if (f.isFile() && f.canRead()) {     Document oldDoc = getEditor().getDocument();     if(oldDoc != null)         oldDoc.removeUndoableEditListener(undoHandler);     if (elementTreePanel != null) {         elementTreePanel.setEditor(null);     }     getEditor().setDocument(new PlainDocument());                 frame.setTitle(f.getName());     Thread loader = new FileLoader(f, editor.getDocument());     loader.start();       } else {                 JOptionPane.showMessageDialog(getFrame(),                         "Could not open file: " + f,                         "Error opening file",                         JOptionPane.ERROR_MESSAGE);       }   }     }          class SaveAction extends AbstractAction {   SaveAction() {       super(saveAction);   }         public void actionPerformed(ActionEvent e) {             Frame frame = getFrame();             JFileChooser chooser = new JFileChooser();             int ret = chooser.showSaveDialog(frame);             if (ret != JFileChooser.APPROVE_OPTION) {                 return;             }             File f = chooser.getSelectedFile();             frame.setTitle(f.getName());             Thread saver = new FileSaver(f, editor.getDocument());             saver.start();   }     }     class NewAction extends AbstractAction {   NewAction() {       super(newAction);   }   NewAction(String nm) {       super(nm);   }         public void actionPerformed(ActionEvent e) {       Document oldDoc = getEditor().getDocument();       if(oldDoc != null)     oldDoc.removeUndoableEditListener(undoHandler);       getEditor().setDocument(new PlainDocument());       getEditor().getDocument().addUndoableEditListener(undoHandler);       resetUndoManager();             getFrame().setTitle(resources.getString("Title"));       revalidate();   }     }     /**      * Really lame implementation of an exit command      */     class ExitAction extends AbstractAction {   ExitAction() {       super(exitAction);   }         public void actionPerformed(ActionEvent e) {       System.exit(0);   }     }     /**      * Action that brings up a JFrame with a JTree showing the structure      * of the document.      */     class ShowElementTreeAction extends AbstractAction {   ShowElementTreeAction() {       super(showElementTreeAction);   }   ShowElementTreeAction(String nm) {       super(nm);   }         public void actionPerformed(ActionEvent e) {       if(elementTreeFrame == null) {     // Create a frame containing an instance of      // ElementTreePanel.     try {         String    title = resources.getString                       ("ElementTreeFrameTitle");         elementTreeFrame = new JFrame(title);     } catch (MissingResourceException mre) {         elementTreeFrame = new JFrame();     }     elementTreeFrame.addWindowListener(new WindowAdapter() {         public void windowClosing(WindowEvent weeee) {       elementTreeFrame.setVisible(false);         }     });     Container fContentPane = elementTreeFrame.getContentPane();     fContentPane.setLayout(new BorderLayout());     elementTreePanel = new ElementTreePanel(getEditor());     fContentPane.add(elementTreePanel);     elementTreeFrame.pack();       }       elementTreeFrame.show();   }     }     /**      * Thread to load a file into the text storage model      */     class FileLoader extends Thread {   FileLoader(File f, Document doc) {       setPriority(4);       this.f = f;       this.doc = doc;   }         public void run() {       try {     // initialize the statusbar     status.removeAll();     JProgressBar progress = new JProgressBar();     progress.setMinimum(0);     progress.setMaximum((int) f.length());     status.add(progress);     status.revalidate();     // try to start reading     Reader in = new FileReader(f);     char[] buff = new char[4096];     int nch;     while ((nch = in.read(buff, 0, buff.length)) != -1) {         doc.insertString(doc.getLength(), new String(buff, 0, nch), null);         progress.setValue(progress.getValue() + nch);     }       }       catch (IOException e) {                 final String msg = e.getMessage();                 SwingUtilities.invokeLater(new Runnable() {                     public void run() {                         JOptionPane.showMessageDialog(getFrame(),                                 "Could not open file: " + msg,                                 "Error opening file",                                 JOptionPane.ERROR_MESSAGE);       }                 });             }       catch (BadLocationException e) {     System.err.println(e.getMessage());       }             doc.addUndoableEditListener(undoHandler);             // we are done... get rid of progressbar             status.removeAll();             status.revalidate();             resetUndoManager();       if (elementTreePanel != null) {     SwingUtilities.invokeLater(new Runnable() {         public void run() {       elementTreePanel.setEditor(getEditor());         }     });       }   }   Document doc;   File f;     }     /**      * Thread to save a document to file      */     class FileSaver extends Thread {         Document doc;         File f;   FileSaver(File f, Document doc) {       setPriority(4);       this.f = f;       this.doc = doc;   }         public void run() {       try {     // initialize the statusbar     status.removeAll();     JProgressBar progress = new JProgressBar();     progress.setMinimum(0);     progress.setMaximum((int) doc.getLength());     status.add(progress);     status.revalidate();     // start writing     Writer out = new FileWriter(f);                 Segment text = new Segment();                 text.setPartialReturn(true);                 int charsLeft = doc.getLength();     int offset = 0;                 while (charsLeft > 0) {                     doc.getText(offset, Math.min(4096, charsLeft), text);                     out.write(text.array, text.offset, text.count);                     charsLeft -= text.count;                     offset += text.count;                     progress.setValue(offset);                     try {                         Thread.sleep(10);                     } catch (InterruptedException e) {                         e.printStackTrace();                     }                 }                 out.flush();                 out.close();       }       catch (IOException e) {                 final String msg = e.getMessage();                 SwingUtilities.invokeLater(new Runnable() {                     public void run() {                         JOptionPane.showMessageDialog(getFrame(),                                 "Could not save file: " + msg,                                 "Error saving file",                                 JOptionPane.ERROR_MESSAGE);       }                 });       }       catch (BadLocationException e) {     System.err.println(e.getMessage());       }             // we are done... get rid of progressbar             status.removeAll();             status.revalidate();   }     } } /*  * @(#)ElementTreePanel.java  1.17 05/11/17  *   * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.  *   * Redistribution and use in source and binary forms, with or without  * modification, are permitted provided that the following conditions are met:  *   * -Redistribution of source code must retain the above copyright notice, this  *  list of conditions and the following disclaimer.  *   * -Redistribution in binary form must reproduce the above copyright notice,   *  this list of conditions and the following disclaimer in the documentation  *  and/or other materials provided with the distribution.  *   * Neither the name of Sun Microsystems, Inc. or the names of contributors may   * be used to endorse or promote products derived from this software without   * specific prior written permission.  *   * This software is provided "AS IS," without a warranty of any kind. ALL   * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING  * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE  * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")  * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE  * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS  * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST   * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,   * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY   * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,   * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.  *   * You acknowledge that this software is not designed, licensed or intended  * for use in the design, construction, operation or maintenance of any  * nuclear facility.  */ /*  * @(#)ElementTreePanel.java  1.17 05/11/17  */ import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; import javax.swing.tree.*; import javax.swing.undo.*; import java.awt.*; import java.beans.*; import java.util.*; /**  * Displays a tree showing all the elements in a text Document. Selecting  * a node will result in reseting the selection of the JTextComponent.  * This also becomes a CaretListener to know when the selection has changed  * in the text to update the selected item in the tree.  *  * @author Scott Violet  * @version 1.17 11/17/05  */ public class ElementTreePanel extends JPanel implements CaretListener, DocumentListener, PropertyChangeListener, TreeSelectionListener {     /** Tree showing the documents element structure. */     protected JTree             tree;     /** Text component showing elemenst for. */     protected JTextComponent    editor;     /** Model for the tree. */     protected ElementTreeModel  treeModel;     /** Set to true when updatin the selection. */     protected boolean           updatingSelection;     public ElementTreePanel(JTextComponent editor) {   this.editor = editor;   Document document = editor.getDocument();   // Create the tree.   treeModel = new ElementTreeModel(document);   tree = new JTree(treeModel) {       public String convertValueToText(Object value, boolean selected,                boolean expanded, boolean leaf,                int row, boolean hasFocus) {     // Should only happen for the root     if(!(value instanceof Element))         return value.toString();     Element        e = (Element)value;     AttributeSet   as = e.getAttributes().copyAttributes();     String         asString;     if(as != null) {         StringBuffer       retBuffer = new StringBuffer("[");         Enumeration        names = as.getAttributeNames();         while(names.hasMoreElements()) {       Object        nextName = names.nextElement();       if(nextName != StyleConstants.ResolveAttribute) {           retBuffer.append(" ");           retBuffer.append(nextName);           retBuffer.append("=");           retBuffer.append(as.getAttribute(nextName));       }         }         retBuffer.append(" ]");         asString = retBuffer.toString();     }     else         asString = "[ ]";     if(e.isLeaf())         return e.getName() + " [" + e.getStartOffset() +       ", " + e.getEndOffset() +"] Attributes: " + asString;     return e.getName() + " [" + e.getStartOffset() +         ", " + e.getEndOffset() + "] Attributes: " +              asString;       }   };   tree.addTreeSelectionListener(this);   tree.setDragEnabled(true);   // Don't show the root, it is fake.   tree.setRootVisible(false);   // Since the display value of every node after the insertion point   // changes every time the text changes and we don't generate a change   // event for all those nodes the display value can become off.   // This can be seen as '...' instead of the complete string value.   // This is a temporary workaround, increase the needed size by 15,   // hoping that will be enough.   tree.setCellRenderer(new DefaultTreeCellRenderer() {       public Dimension getPreferredSize() {     Dimension retValue = super.getPreferredSize();     if(retValue != null)         retValue.width += 15;     return retValue;       }   });   // become a listener on the document to update the tree.   document.addDocumentListener(this);   // become a PropertyChangeListener to know when the Document has   // changed.   editor.addPropertyChangeListener(this);   // Become a CaretListener   editor.addCaretListener(this);   // configure the panel and frame containing it.   setLayout(new BorderLayout());   add(new JScrollPane(tree), BorderLayout.CENTER);   // Add a label above tree to describe what is being shown   JLabel     label = new JLabel("Elements that make up the current document", SwingConstants.CENTER);   label.setFont(new Font("Dialog", Font.BOLD, 14));   add(label, BorderLayout.NORTH);   setPreferredSize(new Dimension(400, 400));     }     /**      * Resets the JTextComponent to <code>editor</code>. This will update      * the tree accordingly.      */     public void setEditor(JTextComponent editor) {   if (this.editor == editor) {       return;   }   if (this.editor != null) {       Document      oldDoc = this.editor.getDocument();       oldDoc.removeDocumentListener(this);       this.editor.removePropertyChangeListener(this);       this.editor.removeCaretListener(this);   }   this.editor = editor;   if (editor == null) {       treeModel = null;       tree.setModel(null);   }   else {       Document   newDoc = editor.getDocument();       newDoc.addDocumentListener(this);       editor.addPropertyChangeListener(this);       editor.addCaretListener(this);       treeModel = new ElementTreeModel(newDoc);       tree.setModel(treeModel);   }     }     // PropertyChangeListener     /**      * Invoked when a property changes. We are only interested in when the      * Document changes to reset the DocumentListener.      */     public void propertyChange(PropertyChangeEvent e) {   if (e.getSource() == getEditor() &&       e.getPropertyName().equals("document")) {       JTextComponent      editor = getEditor();       Document            oldDoc = (Document)e.getOldValue();       Document            newDoc = (Document)e.getNewValue();       // Reset the DocumentListener       oldDoc.removeDocumentListener(this);       newDoc.addDocumentListener(this);       // Recreate the TreeModel.       treeModel = new ElementTreeModel(newDoc);       tree.setModel(treeModel);   }     }     // DocumentListener     /**      * Gives notification that there was an insert into the document.  The      * given range bounds the freshly inserted region.      *      * @param e the document event      */     public void insertUpdate(DocumentEvent e) {   updateTree(e);     }     /**      * Gives notification that a portion of the document has been      * removed.  The range is given in terms of what the view last      * saw (that is, before updating sticky positions).      *      * @param e the document event      */     public void removeUpdate(DocumentEvent e) {   updateTree(e);     }     /**      * Gives notification that an attribute or set of attributes changed.      *      * @param e the document event      */     public void changedUpdate(DocumentEvent e) {   updateTree(e);     }     // CaretListener     /**      * Messaged when the selection in the editor has changed. Will update      * the selection in the tree.      */     public void caretUpdate(CaretEvent e) {   if(!updatingSelection) {       JTextComponent     editor = getEditor();       int                selBegin = Math.min(e.getDot(), e.getMark());       int                end = Math.max(e.getDot(), e.getMark());       Vector             paths = new Vector();       TreeModel          model = getTreeModel();       Object             root = model.getRoot();       int                rootCount = model.getChildCount(root);       // Build an array of all the paths to all the character elements       // in the selection.       for(int counter = 0; counter < rootCount; counter++) {     int            start = selBegin;     while(start <= end) {         TreePath    path = getPathForIndex(start, root,                (Element)model.getChild(root, counter));         Element     charElement = (Element)path.                              getLastPathComponent();         paths.addElement(path);         if(start >= charElement.getEndOffset())       start++;         else       start = charElement.getEndOffset();     }       }       // If a path was found, select it (them).       int               numPaths = paths.size();       if(numPaths > 0) {     TreePath[]    pathArray = new TreePath[numPaths];     paths.copyInto(pathArray);     updatingSelection = true;     try {         getTree().setSelectionPaths(pathArray);         getTree().scrollPathToVisible(pathArray[0]);     }     finally {         updatingSelection = false;     }       }   }     }     // TreeSelectionListener     /**       * Called whenever the value of the selection changes.       * @param e the event that characterizes the change.       */     public void valueChanged(TreeSelectionEvent e) {   JTree       tree = getTree();   if(!updatingSelection && tree.getSelectionCount() == 1) {       TreePath      selPath = tree.getSelectionPath();       Object        lastPathComponent = selPath.getLastPathComponent();       if(!(lastPathComponent instanceof DefaultMutableTreeNode)) {     Element       selElement = (Element)lastPathComponent;     updatingSelection = true;     try {         getEditor().select(selElement.getStartOffset(),                selElement.getEndOffset());     }     finally {         updatingSelection = false;     }       }   }     }     // Local methods     /**      * @return tree showing elements.      */     protected JTree getTree() {   return tree;     }     /**      * @return JTextComponent showing elements for.      */     protected JTextComponent getEditor() {   return editor;     }     /**      * @return TreeModel implementation used to represent the elements.      */     public DefaultTreeModel getTreeModel() {   return treeModel;     }     /**      * Updates the tree based on the event type. This will invoke either      * updateTree with the root element, or handleChange.      */     protected void updateTree(DocumentEvent event) {   updatingSelection = true;   try {       TreeModel        model = getTreeModel();       Object           root = model.getRoot();       for(int counter = model.getChildCount(root) - 1; counter >= 0;     counter--) {     updateTree(event, (Element)model.getChild(root, counter));       }   }   finally {       updatingSelection = false;   }     }     /**      * Creates TreeModelEvents based on the DocumentEvent and messages      * the treemodel. This recursively invokes this method with children      * elements.      * @param event indicates what elements in the tree hierarchy have      * changed.      * @param element Current element to check for changes against.      */     protected void updateTree(DocumentEvent event, Element element) {         DocumentEvent.ElementChange ec = event.getChange(element);         if (ec != null) {       Element[]       removed = ec.getChildrenRemoved();       Element[]       added = ec.getChildrenAdded();       int             startIndex = ec.getIndex();       // Check for removed.       if(removed != null && removed.length > 0) {     int[]            indices = new int[removed.length];     for(int counter = 0; counter < removed.length; counter++) {         indices[counter] = startIndex + counter;     }     getTreeModel().nodesWereRemoved((TreeNode)element, indices,             removed);       }       // check for added       if(added != null && added.length > 0) {     int[]            indices = new int[added.length];     for(int counter = 0; counter < added.length; counter++) {         indices[counter] = startIndex + counter;     }     getTreeModel().nodesWereInserted((TreeNode)element, indices);       }         }   if(!element.isLeaf()) {       int        startIndex = element.getElementIndex                            (event.getOffset());       int        elementCount = element.getElementCount();       int        endIndex = Math.min(elementCount - 1,              element.getElementIndex              (event.getOffset() + event.getLength()));       if(startIndex > 0 && startIndex < elementCount &&          element.getElement(startIndex).getStartOffset() ==          event.getOffset()) {     // Force checking the previous element.     startIndex--;       }       if(startIndex != -1 && endIndex != -1) {     for(int counter = startIndex; counter <= endIndex; counter++) {         updateTree(event, element.getElement(counter));     }       }   }   else {       // Element is a leaf, assume it changed       getTreeModel().nodeChanged((TreeNode)element);   }     }     /**      * Returns a TreePath to the element at <code>position</code>.      */     protected TreePath getPathForIndex(int position, Object root,                Element rootElement) {   TreePath         path = new TreePath(root);   Element          child = rootElement.getElement                               (rootElement.getElementIndex(position));   path = path.pathByAddingChild(rootElement);   path = path.pathByAddingChild(child);   while(!child.isLeaf()) {       child = child.getElement(child.getElementIndex(position));       path = path.pathByAddingChild(child);   }   return path;     }     /**      * ElementTreeModel is an implementation of TreeModel to handle displaying      * the Elements from a Document. AbstractDocument.AbstractElement is      * the default implementation used by the swing text package to implement      * Element, and it implements TreeNode. This makes it trivial to create      * a DefaultTreeModel rooted at a particular Element from the Document.      * Unfortunately each Document can have more than one root Element.      * Implying that to display all the root elements as a child of another      * root a fake node has be created. This class creates a fake node as      * the root with the children being the root elements of the Document      * (getRootElements).      * <p>This subclasses DefaultTreeModel. The majority of the TreeModel      * methods have been subclassed, primarily to special case the root.      */     public static class ElementTreeModel extends DefaultTreeModel {   protected Element[]         rootElements;   public ElementTreeModel(Document document) {       super(new DefaultMutableTreeNode("root"), false);       rootElements = document.getRootElements();   }   /**    * Returns the child of <I>parent</I> at index <I>index</I> in    * the parent's child array.  <I>parent</I> must be a node    * previously obtained from this data source. This should    * not return null if <i>index</i> is a valid index for    * <i>parent</i> (that is <i>index</i> >= 0 && <i>index</i>    * < getChildCount(<i>parent</i>)).    *    * @param   parent  a node in the tree, obtained from this data source    * @return  the child of <I>parent</I> at index <I>index</I>    */   public Object getChild(Object parent, int index) {       if(parent == root)     return rootElements[index];       return super.getChild(parent, index);   }   /**    * Returns the number of children of <I>parent</I>.  Returns 0    * if the node is a leaf or if it has no children.    * <I>parent</I> must be a node previously obtained from this    * data source.    *    * @param   parent  a node in the tree, obtained from this data source    * @return  the number of children of the node <I>parent</I>    */   public int getChildCount(Object parent) {       if(parent == root)     return rootElements.length;       return super.getChildCount(parent);   }   /**    * Returns true if <I>node</I> is a leaf.  It is possible for    * this method to return false even if <I>node</I> has no    * children.  A directory in a filesystem, for example, may    * contain no files; the node representing the directory is    * not a leaf, but it also has no children.    *    * @param   node    a node in the tree, obtained from this data source    * @return  true if <I>node</I> is a leaf    */   public boolean isLeaf(Object node) {       if(node == root)     return false;       return super.isLeaf(node);   }   /**    * Returns the index of child in parent.    */   public int getIndexOfChild(Object parent, Object child) {       if(parent == root) {     for(int counter = rootElements.length - 1; counter >= 0;         counter--) {         if(rootElements[counter] == child)       return counter;     }     return -1;       }       return super.getIndexOfChild(parent, child);   }   /**    * Invoke this method after you've changed how node is to be    * represented in the tree.    */   public void nodeChanged(TreeNode node) {       if(listenerList != null && node != null) {     TreeNode         parent = node.getParent();     if(parent == null && node != root) {         parent = root;     }     if(parent != null) {         int        anIndex = getIndexOfChild(parent, node);         if(anIndex != -1) {       int[]        cIndexs = new int[1];       cIndexs[0] = anIndex;       nodesChanged(parent, cIndexs);         }     }       }         }   /**    * Returns the path to a particluar node. This is recursive.    */   protected TreeNode[] getPathToRoot(TreeNode aNode, int depth) {       TreeNode[]              retNodes;       /* Check for null, in case someone passed in a null node, or          they passed in an element that isn't rooted at root. */       if(aNode == null) {     if(depth == 0)         return null;     else         retNodes = new TreeNode[depth];       }       else {     depth++;     if(aNode == root)         retNodes = new TreeNode[depth];     else {         TreeNode parent = aNode.getParent();         if(parent == null)       parent = root;         retNodes = getPathToRoot(parent, depth);     }     retNodes[retNodes.length - depth] = aNode;       }       return retNodes;   }     } }          Notepad.zip( 51 k)