Using a JEditorPane, is there any way to tell if the caret is currently on the last line? I cannot find anything in the API or more importantly JTextComponent of which JEditorPane is derived. I need to find this out when the user uses the down arrow key to move down the text. My current idea is this:
private boolean isEndOfText() {
int tmpCurrent = editor.getCaretPosition();
editor.getActionMap().get(DefaultEditorKit.endLineAction).actionPerformed(null);
int tmpEnd = editor.getCaretPosition();
try { editor.setCaretPosition(tmpEnd + 1); } catch (Exception e) { editor.setCaretPosition(tmpCurrent); return true; }
editor.setCaretPosition(tmpCurrent);
return false;
}
This code would run whenever the down key is pressed and would return whether or not it is in fact the end of the text by detecting if an error occurs if the caret is being put after the last possible position which would be the end of the line (if it is in fact the last line) otherwise it means the end of text has not been reached.
You should be able to use the Text Utilities. One method returns the total lines and another method return the line at the caret.
I've never played with a JEditorPane that much since I don't like its HTML support. You might also be able to use editor.getDocument().getLength() to determine if the caret is at the end of the document. This will work with a JTextArea or a JTextPane that only displays text, not HTML. Not sure how it works in a JEditorPane.
There's probably a better way, but you could try this:
return editor.getText().indexOf("\n", editor.getCaretPosition()) == -1;
Related
I have seen questions about moving a cursor using the Robot class by an x and y coordinate, but I am trying to figure out how to reposition a cursor among text in a JTextField.
I have an open parenthesis button that when clicked will take whatever text might be in the JTextField already, concat "(" to it and set this to the JTextField.
I was wondering how I might add the closing parenthesis as well, BUT put the cursor in between the 2 so the user can keep typing uninterrupted. Any suggestions?
If you want to move the Caret in a JTextField to a specific location from a button then one way to do this would be to set focus upon it first using the JTextField.requestFocus() method then you would need to use the JTextField.setCaretPosition() method to actually relocate the Caret.
If you have a JTextField named jTextField1 and you want to move the Caret to the end of the text contained within then you can use:
jTextField1.requestFocus(); //
jTextField1.setCaretPosition(jTextField1.getText().length());
You need to be careful not to exceed the length of text within the JTextField otherwise an IllegalArgumentException will occur which you can catch by surrounding the above code within a try/catch block. You will also need to consider those times when there might not be any text within the JTextField.
try {
jTextField1.requestFocus();
jTextField1.setCaretPosition(jTextField1.getText().length());
}
catch (IllegalArgumentException ex) {
///Do Something Here...
}
To Append Brackets to the end of a JTextField then place the Caret between them would be something like this:
String txt = jTextField1.getText(); // Get the text contained in Textfield (if any)
if (txt.equals("")) { txt+= "()"; } // Nothing for text so just add Parenthases
else { txt+= " ()"; } // Some text there so add a space and Parenthases
jTextField1.setText(txt);
try {
// Set focus to JTextField
jTextField1.requestFocus();
// Move the caret between the Parenthases
jTextField1.setCaretPosition(jTextField1.getText().length()-1);
}
catch (IllegalArgumentException ex) {
// Do something here...
}
I have an open parenthesis button that when clicked will take whatever text might be in the JTextField already, concat "(" to it and set this to the JTextField.
Don't use getText()/setText() to do this.
Instead you just want to "append" the new text to the text field.
So the logic in your ActonListener might be something like:
int end = textField.getDocument.getLength();
textField.setCaretPosition(end);
textfield.replaceSelection("()");
textField.setCaretPosition(end + 1);
Appending text is more efficient because you only generate a DocumentEvent for the added text.
If you use the getText()/setText() approach then you generate a DocumentEvent for the remove text and then a second event for the text added, which does not reflect what actually happened.
Also, using the length from the Document instead of getting the text is also more efficient since you don't need to actually create a String object.
I'm not sure if this would be a simple question to answer on here, just because I'm using the Standard Draw class written by Princeton University, and I'm not sure if it's a globally known class.
But I'd much appreciate any feedback from those familiar with the StdDraw library.
What I'm trying to do is fairly straight-forward; check to see if the mouse of the user clicks onto an input box I drew, and if it is clicked, clear the existing text (which simply says "input") to make an empty String.
This is what it looks like so far:
public boolean handleClick(double x, double y) {
if(!super.handleClick(x,y)){
value = false;}
else {
if(highlighted){
value = true;
StdDraw.textLeft(xCentre+0.005,yCentre," ");} //Add the label
else{
value = false;}
}
return value; //I handled it. Nobody else should.}
}//handleClick
super.handleClick(x,y) is simply a method in the super class that draws the dimensions of the box:
public void draw(){
StdDraw.setPenColor(StdDraw.WHITE);
StdDraw.filledRectangle(xCentre,yCentre,halfWidth,halfHeight);
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.setPenRadius(); //Default thin line.
StdDraw.rectangle(xCentre,yCentre,halfWidth,halfHeight);
}
value is simply an instance variable of this class that will return true if all conditions are satisfied:
private boolean value;
highlighted is a boolean instance variable from the super class that simply states if the box is an input or output box.
My main question would be, is the line
StdDraw.textLeft(xCentre+0.005,yCentre," ");
the right way to clear the existing text and create an empty String with StdDraw? As it's not clearing the line, but maybe there's a bug elsewhere in my code that I'm missing and this line should work?
I am trying to implement a text editor in NetBeans with simple functions like: text styling (bold, italic, underlined ...), open file, save file and search. Search function searches for a specified string in the document and highlights the results. The problem occurs when I am trying to remove those highlights or add new ones for different search. Currently I use StyledDocument object together with jTextPane.
private void textHighlight(int startAt, int endAt, Color c) {
Style sCh;
sCh = textEditor.addStyle("TextBackground", null);
StyleConstants.setBackground(sCh, c);
StyledDocument sDoc = textEditor.getStyledDocument();
sDoc.setCharacterAttributes(startAt, endAt - startAt, sCh, false);
}
private void textFind(java.awt.event.ActionEvent evt) {
int searchIndex = 0;
String searchValue = searchField.getText();
if(lastIndex != -1) {
while(searchIndex < lastIndex) {
countOccurencies++;
int i = textEditor.getText().indexOf(searchValue, searchIndex);
textHighlight(i, i+searchValue.length(), Color.MAGENTA);
searchIndex = i+searchValue.length();
}
statusLabel.setText(countOccurencies + " rezultatov.");
} else {
statusLabel.setText("Ni rezultatov!");
}
}
}
private void searchEnd(java.awt.event.ActionEvent evt) {
textEditor.removeStyle("TextBackground");
}
removeStyle() doesn't seem to be working.
Must admit I don't know how Styles work, but maybe the attributes of the Style are copied to the Document at the time you add the Style?
Another option is to use the Highlighter class. See textPane.getHighlighter(). Then you can keep track of the individual highlights you add in an ArrayList and then use the ArrayList to remove the highlights when you want the clear the text pan.
Also, inside your search loop you have a couple of problems:
Don't use the getText() method. This can cause problems with text offsets being off by one for every line of text in the text pane. See Text and New Lines for more information and the solution.
You are getting the text inside the loop which is not very efficient. You should only get the text once outside the loop.
I have a find function that locates a string in a JTable with quite a few thousand entries. Michael Meyers was kind enough to help me out with the goto portion of the function. There appears to be a bug though...
When the user searches for the string the application correctly finds the line in the JTable and highlights it. It also attempts to focus on it, but does not always. Sometimes it will jump 10+ lines short of the line I am looking for and I need to scroll down to see it. As I said, there are a few thousand entries in this JTable and if I'm searching for something, it's difficult to scroll around. Is it possible to focus the selected entry in the center of the visible area?
if (logs.get(i).getLine().contains(findStr))
{
logTable.scrollRectToVisible(logTable.getCellRect(thisPos, 1, true)); // goto
logTable.setRowSelectionInterval(thisPos, thisPos); // highlight
}
I'm not sure that it helps any, but here is the JTable setup code:
JTable logTable = new JTable(logTableModel);
logTable.setShowGrid(true);
logTable.setShowVerticalLines(true);
logTable.setShowHorizontalLines(false);
logTable.setRowSorter(sorter);
logTable.getSelectionModel().addListSelectionListener(new LogRowListener());
JScrollPane scrollPane = new JScrollPane();
scrollPane.getViewport().add(logTable);
scrollPane.setPreferredSize(new Dimension(800, 450));
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
Thanks
EDIT
Below is a link to download a .jar file. This file is a limited version of the code that illustrates the problem. This version seems to consistently jump 2-3 lines short, that is not always the case in the full version.
Demo.jar
The code even for this demo is still a few hundred lines, so below is the sections that I believe are relevant.
public class Proto extends JFrame implements ActionListener
{
public Proto() { ... }
#Override
public void actionPerformed(ActionEvent event)
{
String command = event.getActionCommand();
if (BUTTON_NEXT_FIND.equals(command))
{
findNext();
}
}
...
private void findNext()
{
String findStr = findField.getText();
int pos = selectedLogRow;
// if we're searching for the same string again step forward once
if (pos == lastFoundPos)
++pos;
// search through the log for the string
while (pos < logs.size())
{
if (logs.get(pos).getLine().contains(findStr))
{
logTable.scrollRectToVisible(logTable.getCellRect(pos, 1, true));
logTable.setRowSelectionInterval(pos, pos);
lastFoundPos = pos;
break;
}
++pos;
}
}
...
}
Sometimes it will jump 10+ lines short of the line I am looking for and I need to scroll down to see it.
Post your SSCCE that demonstrates the problem. You can just create a table with a thousand rows and then use setValueAt(...) method to populate a few random cells with the text you are searching for.
Is it possible to focus the selected entry in the center of the visible area?
You need to adjust the row number you want to scroll to. That is if you are searching down you would need to subtract "x" from the row number so the viewport is position on a row before your row that contains the text.
What is the general approach with Java swing to update a textarea with lines of text (say from a Thread) and then have the text caret flow to the bottom of the textarea as text is being added. Also update the scrollbar so that it is at the bottom.
I was thinking that I would have a stringbuffer and append text to that and then set the string in the textarea and position the scrollbar at the bottom.
Use append() to add the text, then setCaretPosition() to make sure you scroll with it.
myTextPane.append(textFromSomewhere);
myTextPane.setCaretPosition(myTextPane.getDocument().getLength());
The append() method doesn't do what you want?
And although you didn't ask: when you're generating something in a background thread, be sure to use SwingUtilities.invokeLater() to update your components.
From another thread, you should use java.awt.EventQueue.invokeLater to get on the EDT and then everything works.
So:
java.awt.EventQueue.invokeLater(new Runnable() { public void run() {
Document doc = text.getDocument();
int origLen = doc.getLength()
try {
doc.insertString(origLen, msg, null);
} catch (BadLocationException exc) {
// Odd APIs forces us to deal with this nonsense.
IndexOutOfBoundsException wrapExc = new IndexOutOfBoundsException();
wrapExc.initCause(exc);
throw wrapExc;
}
// IIRC, Position is a bit odd and
if (origLen == 0) {
text.setCaretPosition(doc.getLength());
}
}});
Should anyone read the API docs for JTextArea.append it claims to be thread-safe. JDK7 removes that unlikely claim (reminder: threading is hard). As a rule, in Swing I tend to always go straight for the model/Document.
I believe if the caret is at the end it should get moved on after an append. The only exception is if there is no text, because of the strange API. If it has been moved, then we probably don't want to update it after the append.
Note: If multiple threads are doing this, you don't necessarily know which will get there first.
If you are updating from a Thread, dont forget to use SwingWorker or some other AWT Thread-safe approach.
You can update the scrollbar without reading doc.length with:
scrollbar.setValue(scrollbar.getMaximum());
Update (wrapped into Invoke later, code from Tom Hawtin)
java.awt.EventQueue.invokeLater(new Runnable() { public void run() {
try {
textArea.append(msg);
} catch (BadLocationException exc) {
// Odd APIs forces us to deal with this nonsense.
IndexOutOfBoundsException wrapExc = new IndexOutOfBoundsException();
wrapExc.initCause(exc);
throw wrapExc;
}
JScrollBar bar = scrollPane.getVerticalScrollBar();
bar.setValue(bar.getMaximum());
}});