Tracking the Caret in a JTextArea with Java

A lot of people on the board right now are making their own little text editors. It is a great little project to start when you are breaking into a language like Java. They are not exactly original with the names though like “nText” and “pText” and “zText” and “wtfText”. Usually their main focus is on highlighting or trying to mimic the functionality of some of the great text editors like Notepad++ or TextPad. You might want to do the same and one of these features you might want is the nice status bar that shows which line and column the user is on. So who is going to tell you about something like that?!?! If you said Martyr2, you are definitely a bright one with a bright future. We cover that topic right here on the Programming Underground!

To track the caret (the little cursor that appears in a control like a JTextField) is a pretty straight forward process if you know what is going on and what tools you need. Since the position of the caret changing is going to be an event (something triggered by the user) then we will need a listener. It makes sense right? The user triggers an event and our program listens for it and reacts.

The listener we are going to need is called a “caretListener”. By adding this listener to our JTextField (or any JTextComponent control) we can listen for when the caret in that control changes. When it does we do a few calculations to find out which line of the control it is on and what column. As with most listeners, the programmer has to declare a specific method inside the listener and then write code for that method. For the caretListener the method that interests us would be the caretUpdate() method. This method is passed a CaretEvent object representing data about the event (like the source of the event). Using the source, we will find the position of the caret, its line number, and then calculate is column.

The CaretListener is a Javax.Swing event, not the typical java.awt event. So you will need to import the javax.swing.event.*; package into your code. There we can use the “addCaretListener()” method of a JTextArea to attach the listener to it. I show you how this works in a fully working demo below. I have added in code comments to show you what is going on at each step of the process. Pay special attention to where we declare the listener and how we put it INSIDE the addCaretListener method call. This process is known as an Anonymous class because we never assign the class a name, we just declare it inline to the method call.

import java.awt.BorderLayout;
import javax.swing.*;
import javax.swing.event.*;

public class caretDemo extends JFrame {
	// Two controls, one is the editor and the other is our little status bar at the bottom.
	// When we update the editor, the change in caret will update the status text field.
	private JTextArea editor;
	private JTextField status;
	
	// Start of our caretDemo class
	public caretDemo() {
		setTitle("Caret Demo");
		setSize(500,500);
		
		// Lets create a border layout to make positioning of items easy and quick.
		setLayout(new BorderLayout());
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		
		editor = new JTextArea();
		
		// Add a caretListener to the editor. This is an anonymous class because it is inline and has no specific name.
		editor.addCaretListener(new CaretListener() {
			// Each time the caret is moved, it will trigger the listener and its method caretUpdate.
			// It will then pass the event to the update method including the source of the event (which is our textarea control)
			public void caretUpdate(CaretEvent e) {
				JTextArea editArea = (JTextArea)e.getSource();
				
				// Lets start with some default values for the line and column.
				int linenum = 1;
				int columnnum = 1;
				
				// We create a try catch to catch any exceptions. We will simply ignore such an error for our demonstration.
				try {
					// First we find the position of the caret. This is the number of where the caret is in relation to the start of the JTextArea
					// in the upper left corner. We use this position to find offset values (eg what line we are on for the given position as well as
					// what position that line starts on. 
					int caretpos = editArea.getCaretPosition();
					linenum = editArea.getLineOfOffset(caretpos);
					
					// We subtract the offset of where our line starts from the overall caret position.
					// So lets say that we are on line 5 and that line starts at caret position 100, if our caret position is currently 106
					// we know that we must be on column 6 of line 5.
					columnnum = caretpos - editArea.getLineStartOffset(linenum);
					
					// We have to add one here because line numbers start at 0 for getLineOfOffset and we want it to start at 1 for display.
					linenum += 1;
				}
				catch(Exception ex) { }
				
				// Once we know the position of the line and the column, pass it to a helper function for updating the status bar.
				updateStatus(linenum, columnnum);
			}
		});
		
		// Add the fields to the layout, the editor in the middle and the status at the bottom.
		add(editor, BorderLayout.CENTER);
		
		status = new JTextField();
		add(status, BorderLayout.SOUTH);
		
		// Give the status update value
		updateStatus(1,1);
	}
	
	// This helper function updates the status bar with the line number and column number.
	private void updateStatus(int linenumber, int columnnumber) {
		status.setText("Line: " + linenumber + " Column: " + columnnumber);
	}
	
	// Entry point to the program. It kicks off by creating an instance of our class and making it visible.
	public static void main(String args[]) {
		caretDemo caretDemoApp = new caretDemo();
		caretDemoApp.setVisible(true);
	}
}

In the code above we fire up the app and create a standard 500 x 500 JFrame. In the JFrame we create our two controls and places them in a BorderLayout calling them “editor” and “status”. If you are unfamiliar with layout managers, you can look them up at [url=”http://java.sun.com/docs/books/tutorial/uiswing/layout/using.html”]Using Layout Managers (The Java Tutorials) website[/url]. The “editor” is a JTextArea control and is placed in the center region. The status bar we call “status” is at the bottom (the south region).

But before we add the controls to the layout, we attach our caretListener to the editor control and implement the caretUpdate() method. Here is where the magic occurs. As the caret moves around we get its position relative to the top left corner of the editor. This returned integer is positive and keeps climbing upwards the more we write in the control. We also use it to calculate offsets. Think of offsets as marked positions of the caret. The first calculation is to figure out which line we are on given the current position of the caret. It will return an integer (starting from zero) to indicating the line. The second calculation uses that line number to determine what caret position starts the line.

If we were to hit the enter key 5 times, without typing any text, the start of that line would be 5. Our caret’s overall position would also be 5. If I was to instead type “A” on the first line and then hit the enter key five times, the start of the 5th line would be caret position 6. So if we take the caret’s current position (6) and subtract the start of the fifth line (6) we would get 0… the column number.

Lets say we went to the fifth line and typed the letter “B”. We would be at position 7 of the caret. We would calculate the position of the caret at the start of the fifth line and it would be 6. So we take 7 – 6 and it returns 1, we are on column 1 of the fifth line. I hope that part makes sense. Think of the offsets as “If the caret was at that position, what would its value be?” If our caret was at the beginning of the fifth line it would be value 6. There is also a offset for the end of the line as well. If the caret were at the end of line five, would would its position be? It would be 7 because we typed one letter. Even if the caret itself is elsewhere on that line.

The caretListener is going to be the key in updating the status line passing it the line and column numbers. The listener will also be instrumental in various other tasks including tracking how many characters are selected etc. Keep in mind that this event is going to get high traffic use since it is fired each time the cursor changes. For that reason, it is advised that you keep the calculations short and quick. I wouldn’t suggest creating large objects or complex calculations otherwise you will start to experience sluggish behavior when you are simply trying to type in the field.

Now you are armed with the caretListener and are ready to do battle on those little text editor programs. Feel free to use the code above, part it out, rip it to shreds or whatever for your general purposes. I toss it out into the public domain (as with all my code on this blog).

Enjoy and thanks for reading! 🙂

About The Author

Martyr2 is the founder of the Coders Lexicon and author of the new ebooks "The Programmers Idea Book" and "Diagnosing the Problem" . He has been a programmer for over 23 years. He works for a hot application development company in Vancouver Canada which service some of the biggest tech companies in the world. He has won numerous awards for his mentoring in software development and contributes regularly to several communities around the web. He is an expert in numerous languages including .NET, PHP, C/C++, Java and more.