1 2 3 4 5 Previous Next 63 Replies Latest reply on Jan 21, 2005 4:59 PM by 843804 Go to original post
      • 15. Re: Line Number in JTextPane
        843804
        Its good and does what I want but when the line number reach 1000 lines of codes the area doesnt seem to adjust or it doesnt expand ? how do we fix that ?
        • 16. Re: Line Number in JTextPane
          843804
          Thanks for your codes Can you convert that for JTextPane ?
          • 17. Re: Line Number in JTextPane
            843804
            I don't know if you're talking about my version or uncle_alice's.
            I thought mine would revalidate in such a case.
            As for the conversion to JTextPane, it shouldn't be a problem. I'll do it.
            • 18. Re: Line Number in JTextPane
              843804
              Thanks uncle_alice's version was good but does not resize.
              I'll be waiting for the JTextPane version, thanks.
              • 19. Re: Line Number in JTextPane
                843804
                @weebib: What do you mean by wrapped lines are not added? In my editor, the line number is aligned with the first row of the associated logical line; if the line is wrapped, there's a blank space below the number. Is that not what you're seeing?

                @The_Dev: Do you mean when the line count goes over 100,000? Because it should be able to handle that much as is. But you're right: it should adjust to accomodate larger numbers. I've never bothered to code that because I never edit files that large. I'll look into that, but in the meantime, you can always add more nines to WIDTH_TEMPLATE.
                • 20. Re: Line Number in JTextPane
                  843804
                  uncle_alice : If you look at and test my version, although it is not as elegant as yours, both behaviors are almost identical. I was merely trying to say that there is another behavior than the one we both implemented. I seems a bit harder to code. It would mean adding visible lines, not strictly logical ones : the total number of lines would depend on the current JTextComponent's size.
                  • 21. Re: Line Number in JTextPane
                    843804
                    Thanks uncle_alice's version was good but does not
                    resize.
                    I'll be waiting for the JTextPane version, thanks.
                    I suggest you give a much more thorough look at uncle_alice's version and give him the dukes. It has very interesting optimizations that make the repainting much faster. In any case, here is my final version that you might use to modify his :
                    import java.awt.*;
                    
                    import javax.swing.*;
                    import javax.swing.event.DocumentListener;
                    import javax.swing.event.DocumentEvent;
                    import javax.swing.text.BadLocationException;
                    import javax.swing.text.JTextComponent;
                    import javax.swing.text.Document;
                    import javax.swing.text.Element;
                    
                    public class LineNumberPanel extends JPanel implements DocumentListener {
                         private final static Color DEFAULT_BACKGROUND = new Color(230, 163, 4);
                         private final static int MARGIN = 5;
                         private JTextComponent theTextComponent;
                         private FontMetrics theFontMetrics;
                         private int currentRowWidth;
                    
                         public LineNumberPanel(JTextComponent aTextComponent) {
                              theTextComponent = aTextComponent;
                              theTextComponent.getDocument().addDocumentListener(this);
                              setOpaque(true);
                              setBackground(DEFAULT_BACKGROUND);
                              setFont(theTextComponent.getFont());
                              theFontMetrics = getFontMetrics(getFont());
                              setForeground(theTextComponent.getForeground());
                              currentRowWidth = getDesiredRowWidth();
                         }
                    
                         private void update() {
                              int desiredRowWidth = getDesiredRowWidth();
                              if (desiredRowWidth != currentRowWidth) {
                                   currentRowWidth = desiredRowWidth;
                                   revalidate();
                              }
                              repaint();
                         }
                    
                         private int getDesiredRowWidth() {
                              Document doc = theTextComponent.getDocument();
                              int length = doc.getLength();
                              Element map = doc.getDefaultRootElement();
                              int nbLines =  map.getElementIndex(length) + 1;
                              return theFontMetrics.stringWidth(Integer.toString(nbLines));
                         }
                    
                         public void paintComponent(Graphics g) {
                              super.paintComponent(g);
                              try {
                                   Document doc = theTextComponent.getDocument();
                                   int length = doc.getLength();
                                   Element map = doc.getDefaultRootElement();
                                   int startLine =  map.getElementIndex(0);
                                   int endline = map.getElementIndex(length);
                                   for (int line = startLine; line <= endline; line++) {
                                        int y = theTextComponent.modelToView(map.getElement(line).getStartOffset()).y + theFontMetrics.getHeight() - theFontMetrics.getDescent();
                                        String s = Integer.toString(line + 1);
                                        int width = theFontMetrics.stringWidth(s);
                                        g.drawString(s, MARGIN + currentRowWidth - width, y);
                                   }
                              } catch (BadLocationException e) {
                                   e.printStackTrace();
                              }
                    
                         }
                    
                         public Dimension getPreferredSize() {
                              return new Dimension(2 * MARGIN + currentRowWidth, theTextComponent.getHeight());
                         }
                    
                         public void insertUpdate(DocumentEvent e) {
                              update();
                         }
                    
                         public void removeUpdate(DocumentEvent e) {
                              update();
                         }
                    
                         public void changedUpdate(DocumentEvent e) {
                              update();
                         }
                    
                         public static void main(String[] args) {
                              final JFrame frame = new JFrame();
                              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                              JTextPane textPane = new JTextPane();
                              JScrollPane scrollPane = new JScrollPane(textPane);
                              scrollPane.setRowHeaderView(new LineNumberPanel(textPane));
                              frame.setContentPane(scrollPane);
                              SwingUtilities.invokeLater(new Runnable() {
                                   public void run() {
                                        frame.setSize(new Dimension(400, 400));
                                        frame.show();
                                   }
                              });
                         }
                    }
                    • 22. Re: Line Number in JTextPane
                      843804
                      http://www.developer.com/java/other/article.php/3318421

                      check this link. Hope this helps.

                      Stas
                      • 23. Re: Line Number in JTextPane
                        843804
                        Can make the changes for uncle_alice's version because it does not resize.
                        • 24. Re: Line Number in JTextPane
                          843804
                          I cant get uncle_alice's version to resize ? can you help.

                          Or on my previous codes it handles the line wraping we just have to change the code from this :

                                    for (int i = startLineNumber; i <= endLineNumber; i++) {
                                         
                                    String lineNumber = String.valueOf(i);
                                    int width = fontMetrics.stringWidth( lineNumber );
                                    g.drawString(lineNumber, MARGIN + currentRowWidth - width, start + 5);
                                    start += lineHeight;
                                    }
                          • 25. Re: Line Number in JTextPane
                            843804
                            Very nice, Stas! That solves the problem of numbering by visible line instead of by logical line, if that's what you need. It has the same problem as mine, though, in that the width of the gutter is hard-coded. Once they get over 999, the line numbers become unreadable. Below is an updated version of LineNumberView with fully dynamic width setting. I had some trouble getting it to use the correct width when first displayed, but I think I've got that licked. Let me know if you have any problems with it.
                            import java.awt.Color;
                            import java.awt.Dimension;
                            import java.awt.Font;
                            import java.awt.FontMetrics;
                            import java.awt.Graphics;
                            import java.awt.Rectangle;
                            import java.awt.event.ComponentAdapter;
                            import java.awt.event.ComponentEvent;
                            
                            import java.beans.PropertyChangeEvent;
                            import java.beans.PropertyChangeListener;
                            
                            import javax.swing.*;
                            import javax.swing.event.DocumentEvent;
                            import javax.swing.event.DocumentListener;
                            import javax.swing.text.BadLocationException;
                            import javax.swing.text.Document;
                            import javax.swing.text.Element;
                            import javax.swing.text.JTextComponent;
                            
                            /**
                             * LineNumberView is a simple line-number gutter that works correctly
                             * even when lines are wrapped in the associated text component.  This
                             * is meant to be used as the RowHeaderView in a JScrollPane that
                             * contains the associated text component.  Example usage:
                             *<pre>
                             *   JTextArea ta = new JTextArea();
                             *   ta.setLineWrap(true);
                             *   ta.setWrapStyleWord(true);
                             *   JScrollPane sp = new JScrollPane(ta);
                             *   sp.setRowHeaderView(new LineNumberView(ta));
                             *</pre>
                             *
                             * @author Alan Moore
                             */
                            public class LineNumberView extends JComponent
                            {
                              // This is for the border to the right of the line numbers.
                              // There's probably a UIDefaults value that could be used for this.
                              private static final Color BORDER_COLOR = Color.GRAY;
                            
                              // The minimum width of the gutter, in number of digits
                              private static final int MIN_DIGITS = 3;
                            
                              private static final int MARGIN = 5;
                            
                              private FontMetrics viewFontMetrics;
                              private int digitWidth;
                              private int maxDigits;
                              private int componentWidth;
                            
                              private int textTopInset;
                              private int textFontAscent;
                              private int textFontHeight;
                            
                              private JTextComponent text;
                              private SizeSequence sizes;
                              private int startLine = 0;
                              private boolean structureChanged = true;
                            
                              /**
                               * Construct a LineNumberView and attach it to the given text component.
                               * The LineNumberView will listen for certain kinds of events from the
                               * text component and update itself accordingly.
                               * 
                               * @param text the associated text component
                               */
                              public LineNumberView(JTextComponent text)
                              {
                                if (text == null)
                                {
                                  throw new IllegalArgumentException("Text component cannot be null");
                                }
                                this.text = text;
                                updateCachedMetrics();
                            
                                UpdateHandler handler = new UpdateHandler();
                                text.getDocument().addDocumentListener(handler);
                                text.addPropertyChangeListener(handler);
                                text.addComponentListener(handler);
                            
                                setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, BORDER_COLOR));
                              }
                            
                              /**
                               * Schedule a repaint because one or more line heights may have changed.
                               * 
                               * @param startLine the line that changed, if there's only one
                               * @param structureChanged if <tt>true</tt>, ignore the line number and 
                               *     update all the line heights.
                               */
                              private void viewChanged(int startLine, boolean structureChanged)
                              {
                                this.startLine = startLine;
                                this.structureChanged = structureChanged;
                                if (structureChanged)
                                {
                                  maxDigits = 0;
                                  componentWidth = 0;
                                }
                                revalidate();
                                repaint();
                              }
                            
                              /** Update the line heights as needed. */
                              private void updateSizes()
                              {
                                if (startLine < 0)
                                {
                                  return;
                                }
                            
                                if (structureChanged)
                                {
                                  int count = getAdjustedLineCount();
                                  sizes = new SizeSequence(count);
                                  for (int i = 0; i < count; i++)
                                  {
                                    sizes.setSize(i, getLineHeight(i));
                                  }
                                  structureChanged = false;
                                }
                                else
                                {
                                  sizes.setSize(startLine, getLineHeight(startLine));
                                }
                            
                                startLine = -1;
                              }
                            
                              private int getMaxDigits()
                              {
                                if (maxDigits <= 0)
                                {
                                  int lineCount = getAdjustedLineCount();
                                  maxDigits = Math.max(MIN_DIGITS, String.valueOf(lineCount).length());
                                }
                                return maxDigits;
                              }
                            
                              private int getComponentWidth()
                              {
                                if (componentWidth <= 0)
                                {
                                  componentWidth = getMaxDigits() * digitWidth + MARGIN * 2;
                                }
                                return componentWidth;
                              }
                            
                              /* Copied from javax.swing.text.PlainDocument */
                              private int getAdjustedLineCount()
                              {
                                // There is an implicit break being modeled at the end of the
                                // document to deal with boundary conditions at the end.  This
                                // is not desired in the line count, so we detect it and remove
                                // its effect if throwing off the count.
                                Element map = text.getDocument().getDefaultRootElement();
                                int n = map.getElementCount();
                                Element lastLine = map.getElement(n - 1);
                                if ((lastLine.getEndOffset() - lastLine.getStartOffset()) > 1)
                                {
                                  return n;
                                }
                            
                                return n - 1;
                              }
                            
                              /**
                               * Get the height of a line from the JTextComponent.
                               * 
                               * @param index the line number
                               * @param the height, in pixels
                               */
                              private int getLineHeight(int index)
                              {
                                int lastPos = sizes.getPosition(index) + textTopInset;
                                int height = textFontHeight;
                                try
                                {
                                  Element map = text.getDocument().getDefaultRootElement();
                                  int lastChar = map.getElement(index).getEndOffset() - 1;
                                  Rectangle r = text.modelToView(lastChar);
                                  height = (r.y - lastPos) + r.height;
                                }
                                catch (BadLocationException ex)
                                {
                                  ex.printStackTrace();
                                }
                                return height;
                              }
                            
                              /**
                               * Cache some values that are used a lot in painting or size
                               * calculations. Also ensures that the line-number font is not
                               * larger than the text component's font (by point-size, anyway).
                               */
                              private void updateCachedMetrics()
                              {
                                Font textFont = text.getFont();
                                FontMetrics fm = getFontMetrics(textFont);
                                textFontHeight = fm.getHeight();
                                textFontAscent = fm.getAscent();
                                textTopInset = text.getInsets().top;
                            
                                Font viewFont = getFont();
                                boolean changed = false;
                                if (viewFont == null)
                                {
                                  viewFont = UIManager.getFont("Label.font");
                                  changed = true;
                                }
                                if (viewFont.getSize() > textFont.getSize())
                                {
                                  viewFont = viewFont.deriveFont(textFont.getSize2D());
                                  changed = true;
                                }
                            
                                viewFontMetrics = getFontMetrics(viewFont);
                                digitWidth = viewFontMetrics.stringWidth("0");
                            
                                if (changed)
                                {
                                  super.setFont(viewFont);
                                }
                              }
                            
                              public Dimension getPreferredSize()
                              {
                                return new Dimension(getComponentWidth(), text.getHeight());
                              }
                            
                              public void setFont(Font font)
                              {
                                super.setFont(font);
                                updateCachedMetrics();
                              }
                            
                              public void paintComponent(Graphics g)
                              {
                                updateSizes();
                                Rectangle clip = g.getClipBounds();
                            
                                g.setColor(getBackground());
                                g.fillRect(clip.x, clip.y, clip.width, clip.height);
                            
                                g.setColor(getForeground());
                                int base = clip.y - textTopInset;
                                int first = sizes.getIndex(base);
                                int last = sizes.getIndex(base + clip.height);
                                String text = "";
                                
                                for (int i = first; i <= last; i++)
                                {
                                  text = String.valueOf(i+1);
                                  int x = getComponentWidth() - MARGIN - viewFontMetrics.stringWidth(text);
                                  int y = sizes.getPosition(i) + textFontAscent + textTopInset;
                                  g.drawString(text, x, y);
                                }
                              }
                            
                            
                              class UpdateHandler extends ComponentAdapter
                                  implements PropertyChangeListener, DocumentListener
                              {
                                /**
                                 * The text component was resized.
                                 */
                                public void componentResized(ComponentEvent evt)
                                {
                                  viewChanged(0, true);
                                }
                            
                                /**
                                 * A bound property was changed on the text component. Properties
                                 * like the font, border, and tab size affect the layout of the
                                 * whole document, so we invalidate all the line heights here.
                                 */
                                public void propertyChange(PropertyChangeEvent evt)
                                {
                                  Object oldValue = evt.getOldValue();
                                  Object newValue = evt.getNewValue();
                                  String propertyName = evt.getPropertyName();
                                  if ("document".equals(propertyName))
                                  {
                                    if (oldValue != null && oldValue instanceof Document)
                                    {
                                      ((Document)oldValue).removeDocumentListener(this);
                                    }
                                    if (newValue != null && newValue instanceof Document)
                                    {
                                      ((Document)newValue).addDocumentListener(this);
                                    }
                                  }
                            
                                  updateCachedMetrics();
                                  viewChanged(0, true);
                                }
                            
                                /**
                                 * Text was inserted into the document.
                                 */
                                public void insertUpdate(DocumentEvent evt)
                                {
                                  update(evt);
                                }
                              
                                /**
                                 * Text was removed from the document.
                                 */
                                public void removeUpdate(DocumentEvent evt)
                                {
                                  update(evt);
                                }
                              
                                /**
                                 * Text attributes were changed.  In a source-code editor based on
                                 * StyledDocument, attribute changes should be applied automatically
                                 * in response to inserts and removals.  Since we're already 
                                 * listening for those, this method should be redundant, but YMMV.
                                 */
                                public void changedUpdate(DocumentEvent evt)
                                {
                            //      update(evt);
                                }
                              
                                /**
                                 * If the edit was confined to a single line, invalidate that
                                 * line's height.  Otherwise, invalidate them all.
                                 */
                                private void update(DocumentEvent evt)
                                {
                                  Element map = text.getDocument().getDefaultRootElement();
                                  int line = map.getElementIndex(evt.getOffset());
                                  DocumentEvent.ElementChange ec = evt.getChange(map);
                                  viewChanged(line, ec != null);
                                }
                              }
                            
                              public static void main(String[] args)
                              {
                                final JFrame frame = new JFrame();
                                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                                JTextPane textPane = new JTextPane();
                                JScrollPane scrollPane = new JScrollPane(textPane);
                                scrollPane.setRowHeaderView(new LineNumberView(textPane));
                                frame.setContentPane(scrollPane);
                                SwingUtilities.invokeLater(
                                      new Runnable()
                                      {
                                        public void run()
                                        {
                                          frame.setSize(new Dimension(400, 400));
                                          frame.setLocationRelativeTo(null);
                                          frame.setVisible(true);
                                        }
                                      });
                              }
                            }
                            • 26. Re: Line Number in JTextPane
                              843804
                              I found a bit of a bug, I tried inserting some strings but the line number overlaps the string by one line. How do we fix that ?
                              One more thing how do I set my own font in your codes ? I tried altering it but it seems to be not working.
                              /**
                               * Created on Jan 14, 2005
                               *
                               * @author The_Developer
                               *
                               * TODO To change the template for this generated type comment go to
                               * Window - Preferences - Java - Code Style - Code Templates
                               */
                              import java.awt.Color;
                              import java.awt.Dimension;
                              import java.awt.Font;
                              import java.awt.FontMetrics;
                              import java.awt.Graphics;
                              import java.awt.Rectangle;
                              import java.awt.event.ComponentAdapter;
                              import java.awt.event.ComponentEvent;
                              
                              import java.beans.PropertyChangeEvent;
                              import java.beans.PropertyChangeListener;
                              import java.util.Date;
                              
                              import javax.swing.*;
                              import javax.swing.event.DocumentEvent;
                              import javax.swing.event.DocumentListener;
                              import javax.swing.text.BadLocationException;
                              import javax.swing.text.Document;
                              import javax.swing.text.Element;
                              import javax.swing.text.JTextComponent;
                              
                              /**
                               * LineNumberView is a simple line-number gutter that works correctly
                               * even when lines are wrapped in the associated text component.  This
                               * is meant to be used as the RowHeaderView in a JScrollPane that
                               * contains the associated text component.  Example usage:
                               *<pre>
                               *   JTextArea ta = new JTextArea();
                               *   ta.setLineWrap(true);
                               *   ta.setWrapStyleWord(true);
                               *   JScrollPane sp = new JScrollPane(ta);
                               *   sp.setRowHeaderView(new LineNumberView(ta));
                               *</pre>
                               *
                               * @author Alan Moore
                               */
                              public class LineNumberView extends JComponent
                              {
                                   // This is for the border to the right of the line numbers.
                                   // There's probably a UIDefaults value that could be used for this.
                                   private static final Color BORDER_COLOR = Color.GRAY;
                                   
                                   // The minimum width of the gutter, in number of digits
                                   private static final int MIN_DIGITS = 3;
                                   
                                   private static final int MARGIN = 5;
                                   
                                   private FontMetrics viewFontMetrics;
                                   private int digitWidth;
                                   private int maxDigits;
                                   private int componentWidth;
                                   
                                   private int textTopInset;
                                   private int textFontAscent;
                                   private int textFontHeight;
                                   
                                   private JTextComponent text;
                                   private SizeSequence sizes;
                                   private int startLine = 0;
                                   private boolean structureChanged = true;
                                   
                                   /**
                                    * Construct a LineNumberView and attach it to the given text component.
                                    * The LineNumberView will listen for certain kinds of events from the
                                    * text component and update itself accordingly.
                                    * 
                                    * @param text the associated text component
                                    */
                                   public LineNumberView(JTextComponent text)
                                   {
                                        if (text == null)
                                        {
                                             throw new IllegalArgumentException("Text component cannot be null");
                                        }
                                        this.text = text;
                                        updateCachedMetrics();
                                        
                                        UpdateHandler handler = new UpdateHandler();
                                        text.getDocument().addDocumentListener(handler);
                                        text.addPropertyChangeListener(handler);
                                        text.addComponentListener(handler);
                                        
                                        setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, BORDER_COLOR));
                                   }
                                   
                                   /**
                                    * Schedule a repaint because one or more line heights may have changed.
                                    * 
                                    * @param startLine the line that changed, if there's only one
                                    * @param structureChanged if <tt>true</tt>, ignore the line number and 
                                    *     update all the line heights.
                                    */
                                   private void viewChanged(int startLine, boolean structureChanged)
                                   {
                                        this.startLine = startLine;
                                        this.structureChanged = structureChanged;
                                        if (structureChanged)
                                        {
                                             maxDigits = 0;
                                             componentWidth = 0;
                                        }
                                        revalidate();
                                        repaint();
                                   }
                                   
                                   /** Update the line heights as needed. */
                                   private void updateSizes()
                                   {
                                        if (startLine < 0)
                                        {
                                             return;
                                        }
                                        
                                        if (structureChanged)
                                        {
                                             int count = getAdjustedLineCount();
                                             sizes = new SizeSequence(count);
                                             for (int i = 0; i < count; i++)
                                             {
                                                  sizes.setSize(i, getLineHeight(i));
                                             }
                                             structureChanged = false;
                                        }
                                        else
                                        {
                                             sizes.setSize(startLine, getLineHeight(startLine));
                                        }
                                        
                                        startLine = -1;
                                   }
                                   
                                   private int getMaxDigits()
                                   {
                                        if (maxDigits <= 0)
                                        {
                                             int lineCount = getAdjustedLineCount();
                                             maxDigits = Math.max(MIN_DIGITS, String.valueOf(lineCount).length());
                                        }
                                        return maxDigits;
                                   }
                                   
                                   private int getComponentWidth()
                                   {
                                        if (componentWidth <= 0)
                                        {
                                             componentWidth = getMaxDigits() * digitWidth + MARGIN * 2;
                                        }
                                        return componentWidth;
                                   }
                                   
                                   /* Copied from javax.swing.text.PlainDocument */
                                   private int getAdjustedLineCount()
                                   {
                                        // There is an implicit break being modeled at the end of the
                                        // document to deal with boundary conditions at the end.  This
                                        // is not desired in the line count, so we detect it and remove
                                        // its effect if throwing off the count.
                                        Element map = text.getDocument().getDefaultRootElement();
                                        int n = map.getElementCount();
                                        Element lastLine = map.getElement(n - 1);
                                        if ((lastLine.getEndOffset() - lastLine.getStartOffset()) > 1)
                                        {
                                             return n;
                                        }
                                        
                                        return n - 1;
                                   }
                                   
                                   /**
                                    * Get the height of a line from the JTextComponent.
                                    * 
                                    * @param index the line number
                                    * @param the height, in pixels
                                    */
                                   private int getLineHeight(int index)
                                   {
                                        int lastPos = sizes.getPosition(index) + textTopInset;
                                        int height = textFontHeight;
                                        try
                                        {
                                             Element map = text.getDocument().getDefaultRootElement();
                                             int lastChar = map.getElement(index).getEndOffset() - 1;
                                             Rectangle r = text.modelToView(lastChar);
                                             height = (r.y - lastPos) + r.height;
                                        }
                                        catch (BadLocationException ex)
                                        {
                                             ex.printStackTrace();
                                        }
                                        return height;
                                   }
                                   
                                   /**
                                    * Cache some values that are used a lot in painting or size
                                    * calculations. Also ensures that the line-number font is not
                                    * larger than the text component's font (by point-size, anyway).
                                    */
                                   private void updateCachedMetrics()
                                   {
                                        Font textFont = text.getFont();
                                        FontMetrics fm = getFontMetrics(textFont);
                                        textFontHeight = fm.getHeight();
                                        textFontAscent = fm.getAscent();
                                        textTopInset = text.getInsets().top;
                                        
                                        Font viewFont = getFont();
                                        boolean changed = false;
                                        if (viewFont == null)
                                        {
                                             viewFont = UIManager.getFont("Label.font");
                                             changed = true;
                                        }
                                        if (viewFont.getSize() > textFont.getSize())
                                        {
                                             viewFont = viewFont.deriveFont(textFont.getSize2D());
                                             changed = true;
                                        }
                                        
                                        viewFontMetrics = getFontMetrics(viewFont);
                                        digitWidth = viewFontMetrics.stringWidth("0");
                                        
                                        if (changed)
                                        {
                                             super.setFont(viewFont);
                                        }
                                   }
                                   
                                   public Dimension getPreferredSize()
                                   {
                                        return new Dimension(getComponentWidth(), text.getHeight());
                                   }
                                   
                                   public void setFont(Font font)
                                   {
                                        super.setFont(font);
                                        updateCachedMetrics();
                                   }
                                   
                                   public void paintComponent(Graphics g)
                                   {
                                        updateSizes();
                                        Rectangle clip = g.getClipBounds();
                                        
                                        g.setColor(getBackground());
                                        g.fillRect(clip.x, clip.y, clip.width, clip.height);
                                        
                                        g.setColor(getForeground());
                                        int base = clip.y - textTopInset;
                                        int first = sizes.getIndex(base);
                                        int last = sizes.getIndex(base + clip.height);
                                        String text = "";
                                        
                                        for (int i = first; i <= last; i++)
                                        {
                                             text = String.valueOf(i+1);
                                             int x = getComponentWidth() - MARGIN - viewFontMetrics.stringWidth(text);
                                             int y = sizes.getPosition(i) + textFontAscent + textTopInset;
                                             g.drawString(text, x, y);
                                        }
                                   }
                                   
                                   
                                   class UpdateHandler extends ComponentAdapter
                                   implements PropertyChangeListener, DocumentListener
                                   {
                                        /**
                                         * The text component was resized.
                                         */
                                        public void componentResized(ComponentEvent evt)
                                        {
                                             viewChanged(0, true);
                                        }
                                        
                                        /**
                                         * A bound property was changed on the text component. Properties
                                         * like the font, border, and tab size affect the layout of the
                                         * whole document, so we invalidate all the line heights here.
                                         */
                                        public void propertyChange(PropertyChangeEvent evt)
                                        {
                                             Object oldValue = evt.getOldValue();
                                             Object newValue = evt.getNewValue();
                                             String propertyName = evt.getPropertyName();
                                             if ("document".equals(propertyName))
                                             {
                                                  if (oldValue != null && oldValue instanceof Document)
                                                  {
                                                       ((Document)oldValue).removeDocumentListener(this);
                                                  }
                                                  if (newValue != null && newValue instanceof Document)
                                                  {
                                                       ((Document)newValue).addDocumentListener(this);
                                                  }
                                             }
                                             
                                             updateCachedMetrics();
                                             viewChanged(0, true);
                                        }
                                        
                                        /**
                                         * Text was inserted into the document.
                                         */
                                        public void insertUpdate(DocumentEvent evt)
                                        {
                                             update(evt);
                                        }
                                        
                                        /**
                                         * Text was removed from the document.
                                         */
                                        public void removeUpdate(DocumentEvent evt)
                                        {
                                             update(evt);
                                        }
                                        
                                        /**
                                         * Text attributes were changed.  In a source-code editor based on
                                         * StyledDocument, attribute changes should be applied automatically
                                         * in response to inserts and removals.  Since we're already 
                                         * listening for those, this method should be redundant, but YMMV.
                                         */
                                        public void changedUpdate(DocumentEvent evt)
                                        {
                                             //      update(evt);
                                        }
                                        
                                        /**
                                         * If the edit was confined to a single line, invalidate that
                                         * line's height.  Otherwise, invalidate them all.
                                         */
                                        private void update(DocumentEvent evt)
                                        {
                                             Element map = text.getDocument().getDefaultRootElement();
                                             int line = map.getElementIndex(evt.getOffset());
                                             DocumentEvent.ElementChange ec = evt.getChange(map);
                                             viewChanged(line, ec != null);
                                        }
                                   }
                                   
                                   public static void main(String[] args) {
                              
                                        final JFrame frame = new JFrame();
                                        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                                        JTextPane textPane = new JTextPane();
                              
                                        Date date = new Date();
                                        String user = System.getProperty("user.name");
                              
                                        textPane.setText("/**\n * Created On : " + date + "\n * Author : "
                                                  + user + "\n */\n" + "\npublic class " + "Sample" + " {\n"
                                                  + "\n\tpublic " + "Sample" + "() {\n\n\n" + "\t}" + "\n}");
                              
                                        JScrollPane scrollPane = new JScrollPane(textPane);
                                        scrollPane.setRowHeaderView(new LineNumberView(textPane));
                                        frame.setContentPane(scrollPane);
                                        SwingUtilities.invokeLater(new Runnable() {
                                             public void run() {
                                                  frame.setSize(new Dimension(400, 400));
                                                  frame.setLocationRelativeTo(null);
                                                  frame.setVisible(true);
                                             }
                                        });
                                   }
                              }
                              • 27. Re: Line Number in JTextPane
                                843804
                                Do you mean the way it shows that final line number (13 in your example) even though the line before it doesn't end in a newline? I don't see that as a problem, myself.

                                As for setting the font, just call setFont() on it. If you give it a font that's larger than that of the text component, it silently reduces the point size to keep the numbers from overlapping each other. But this works for me:
                                    JScrollPane scrollPane = new JScrollPane(textPane);
                                    LineNumberView lnv = new LineNumberView(textPane);
                                    lnv.setFont(new Font("monospaced", Font.PLAIN, 12));
                                    scrollPane.setRowHeaderView(lnv);
                                Some of those settings, like the font, margins and minimum width, really should be configurable. I suppose I could have it look for them in the UIDefaults table under keys like "LineNumberView.font", and only use the hard-coded defaults if the keys aren't present.
                                • 28. Re: Line Number in JTextPane
                                  843804
                                  What I mean is the code I posted displays some pieces of codes but the line number excides w/ one. Where as I did not added any new line. What do you think I can do about it ? thanks.
                                  • 29. Re: Line Number in JTextPane
                                    843804
                                    I'm still not sure this is what you're talking about, but you can get rid of that extra line number at the end by changing the getAdjustedLineCount() method to the following:
                                      private int getAdjustedLineCount()
                                      {
                                        Element map = text.getDocument().getDefaultRootElement();
                                        return map.getElementCount() - 1;
                                      }
                                    It seems to work just fine. In fact, it's probably a good idea to change the method name to simply getLineCount(), to avoid future confusion (I'd rather not post the whole class again).

                                    I don't remember why that method was needed, but apparently the need no longer exists, because I can't find it in the jdk source any more.