How to find out line numbers of selected text? - java

Assume this very small program:
1. package ex1;
2. public interface Resizable {
3. void resize();
4. }
In my editor, if I select line 2-3 using mouse and say click on a button, I want to highlight these texts and also print, which line numbers were selected exactly for the button.
I can do the highlighting part, but I don't know how to find the line numbers of highlighted texts, As I think, I should use a listener, which will detect any changes in editor.
I think I should use an action listener, which will detect when the button is pressed after selecting text blocks. But how I will know, which lines are selected exactly?

The start and end of the highlight can be taken from the caret position dot and mark respectively. These are offsets in the Document. You must then calculate the number of newlines from the start of the document until the mark/do
textArea.addCaretListener(new CaretListener() {
#Override
public void caretUpdate(CaretEvent e) {
int startLine = getLine(e.getDot());
int endLine = getLine(e.getMark());
...
}
});
private int getLine(int offset) {
String text = textArea.getDocument().getText(0, offset);
int linenr = 0;
int idx = text.indexOf("\n");
while (idx != -1) {
linenr++;
idx = text.indexOf("\n", idx);
}
return linenr;
}

Related

Select lines from line margin - mouseDragged event not keeping up with selection processing

I am building a text editor and trying to add the ability to select lines using the line number margin. My current approach is to use mouseDragged to update the selected lines. This works fine when doing slow mouse movements, but when doing faster movements the selection isn't able to keep up and just stops updating.
I have tried using a new thread for the processing of the selected range but it still freezes.
Update: Changed to a mouse range of two values (min/max) rather than every line - this fixed the issue
The mouseDragged method
private void mouseDragged(MouseEvent event, Mouse mouse) {
int eventY = event.getY();
int currentLineNumber = this.getLineNumber(eventY);
mouse.endRange(currentLineNumber);
if(mouse.getRange()[0] != mouse.getRange()[1]) {
this.selectLineRange(mouse);
} else {
this.selectLineForOffset(eventY);
}
}
Mouse state
private class Mouse {
int mouseY = -1;
int[] range = new int[2];
private void resetMouse(boolean resetBeginLine) {
this.mouseY = -1;
this.range = new int[2];
}
void endRange(int lineNumber) {
range[1] = lineNumber;
}
void beginRange(int lineNumber) {
range[0] = lineNumber;
}
int[] getRange() {
return range;
}
boolean validRange() {
return ((range[0] | range[1]) > 0);
}
}
And finally the select line range method
private void selectLineRange(Mouse mouse) {
if (mouse.validRange()) {
int minLine = Math.min(mouse.getRange()[0], mouse.getRange()[1]);
int maxLine = Math.max(mouse.getRange()[0], mouse.getRange()[1]);;
Element root = editor.getDocument().getDefaultRootElement();
int startSelection = root.getElement(minLine).getStartOffset();
int endSelection = root.getElement(maxLine).getEndOffset();
//editor.setCaretPosition(mouse.mouseDirection == Direction.UP ? startSelection : endSelection - 1);
editor.select(startSelection, Math.max(endSelection - 1, 0));
}
}
Having code in the same function that knows both about the concept of a mouse and the concept of a document is a recipe for disaster. Split your code into multiple functions where each function works at a different level of abstraction.
All you need is to know at which Y the mouse went down, and at which Y the mouse currently is. From this, you can at any given moment re-calculate the range of selected lines. First you convert viewport-Y to workspace-Y, then you convert workspace-Y to line-number, and voila, you have each line number.
This: selectedLineNumbers.add(currentLineNumber); presumes that you will receive a mouse event on each line. If you don't, then your list will contain gaps. And you won't, because mouse events come few and far apart when you are moving the mouse too quickly. That's why your selectedLineNumbers should be a range, (startingLineNumber, endingLineNumber) not a list of distinct line numbers.

JTextArea: Limit string width and line count?

The user needs to enter text to be printed on a fixed-size label. Given a font, the label has a fixed number of lines and also a fixed width in pixels. How can I adapt a JTextArea (or something else in Swing, if there is another option) to this use case?
Maximum number of lines
Each line of characters not exceeding a certain pixel width
Text wrapping from line to line at the word level for lines attempting to exceed the maximum pixel width
I have a PlainDocument that limits the length of a single line of text according to the width in pixels of the string, measured in capital Ws (the widest character in my font):
public class StandardDocument extends PlainDocument {
/****** VARIABLES **********************************************/
public boolean upperCase = true;
private int textLimit;
private int textWidth;
private int fieldWidth;
private JTextField textField = new JTextField();
/** Get the maximum width of this text field, measured in capital Ws.
* #return textWidth - int
**/
public int getTextWidth() {
return textWidth;
}
/** Get the maximum width of this text field, measured in pixels.
* #return textWidth - int
**/
protected int getFieldWidth() {
return fieldWidth;
}
/** Core method for inserting value provided by user after "cleaning" user's value automatically. **/
public void insertString(int offs, String str, AttributeSet attr) throws BadLocationException {
if (str == null) { return; }
// Set field value when value is within character limit of field.
if (textWidth > 0) {
int attemptedWidth = textField.getFontMetrics(Constants.defaultFontLabels).stringWidth(getText(0, getLength()) + str);
if (attemptedWidth > fieldWidth) {
return;
}
}
// Set value in field.
super.insertString(offs, str, attr);
}
/** Set the maximum text width as a maximum number of capital Ws this field may hold. **/
public void setTextWidth(int wLimit) {
if (wLimit >= 0) {
textWidth = wLimit;
StringBuffer outputBuffer = new StringBuffer(wLimit);
for (int i = 0; i < wLimit; i++){
outputBuffer.append("W");
}
fieldWidth = textField.getFontMetrics(Constants.defaultFontLabels).stringWidth(outputBuffer.toString());
}
}
}
Wrapping is supported by a JTextArea. You turn it on using:
textArea.setLineWrap( true );
textArea.setWrapStyleWord( true );
The line count will be a little more difficult since you don't know if the newly added text will cause the line to wrap until the Document has been updated.
So maybe you need a cross edit that you can invoke when you click the "Print" button. Maybe you can use the getWrappedLines(...) method from theText Utilities class. If the wrapped lines is greater than the maximum you prevent the printing.
Or, maybe you automatically insert the text into the Document. Then you check the number of lines. If greater than the maximum you display a message and then invoke the remove(...) method right away.

JTextArea line numbers of menu item

I am trying to develop a project using AWT And SWING concepts of Java.
In that I have an one menu item called "Viewline Numbers (i.e. we have chosen a JCheckBox for that)". When I check the Check-box it is displaying line numbers in another Document. But, I want to display the line numbers in same using Document like as Editplus Editor.
Here is my code
private void ViewLineNumbersActionPerformed(java.awt.event.ActionEvent evt) {
lines = new JTextArea("");
lines.setBackground(Color.LIGHT_GRAY);
lines.setEditable(false);
lines.setSize(10,10);
tx.getDocument().addDocumentListener(new DocumentListener(){
public String getText(){
int caretPosition = tx.getDocument().getLength();
// System.out.println("caretPosition"+ caretPosition);
Element root = tx.getDocument().getDefaultRootElement();
// System.out.println("root"+ root);
String text = "1" + System.getProperty("line.separator");
int c=root.getElementIndex( caretPosition );
// System.out.println(c);
for(int i = 2; i < c + 2; i++){
text += i + System.getProperty("line.separator");
}
return text;
}
#Override
public void `enter code here`changedUpdate(DocumentEvent de) {
lines.setText(getText());
}
#Override
public void insertUpdate(DocumentEvent de) {
lines.setText(getText());
}
#Override
public void removeUpdate(DocumentEvent de) {
lines.setText(getText());
}
});
sp.getViewport().add(tx);
// sp.setViewportView(tx);
sp.setRowHeaderView(lines);
}
But, I want to display the line numbers in same using Document like as Editplus Editor.
I doubt very much that the line numbers are part of the Document. They may appear to be displayed as part of the same component, but I'm sure that when you copy/paste text you don't get the line numbers included.
Assuming my above statement is correct you can try using the Text Component Line Number. Since this a component displayed in the row header of the scroll pane you should be able to toggle the visibility of the component based on your checkbox.

identify line in a jtextpane when the row number is entered

public static void setJTextPaneFont(JTextPane jtp, Color c, int start_index,int end_index) {
MutableAttributeSet attrs = jtp.getInputAttributes();
StyleConstants.setForeground(attrs, c);
StyledDocument doc = jtp.getStyledDocument();
doc.setCharacterAttributes(start_index, end_index, attrs, false);
}
i created above code to change the forground of of specific word when i enter the start ndex and end index.But now i need to change the the forground when i pass the row number,start_index, and end index.Can you help me with this.How i identify a specific line when i enter the row number.
public void gotoStartOfLine(JTextComponent component, int line) {
Element root = component.getDocument().getDefaultRootElement();
line = Math.max(line, 1);
line = Math.min(line, root.getElementCount());
component.setCaretPosition(root.getElement(line - 1).getStartOffset());
}
i tried above code to go to specific row.but it didint work
How i identify a specific line when i enter the row number.
I think you mean you want the offset of the text for the given row. If so then take a look at the gotoStartOfLine() method from Text Utilities.
That is the code that sets the caret position will give you the starting offset of the line. Then you just add the start/end values to get the offsets of the text to highlight.
Look at using the javax.swing.text.Utilities class, especially the getRowStart(...) and getRowEnd(...) methods.

Why does my Java Swing JScrollPane keep scrolling to the top?

I'm developing a small calculator widget that keeps a running log of calculations. It's supposed to scroll to the bottom of the log every time a new entry is added. This part seems to be working fine.
The problem is, when I press a calculator button that does not add to the log, the log pane always scrolls back to the top, and the scrollbar disappears. How can I keep it from doing this?
The code that adds to the log is:
private JTextPane logArea; //This is placed inside a JScrollPane
private void log(String m, SimpleAttributeSet a) {
int len = logArea.getDocument().getLength();
logArea.setEditable(true);
logArea.setCaretPosition(len);
logArea.setCharacterAttributes(a, false);
logArea.replaceSelection(m);
logArea.scrollRectToVisible(new Rectangle(0,logArea.getBounds(null).height,1,1));
logArea.setEditable(false);
}
The code that seems to be messing with the scroll is:
private void addDigit(char digit) {
if (clearDisplayBeforeDigit) {
clearNumDisplay();
}
if (numInDisplay.getText().length() < maxNumDigits) {
if (digit == '.') { //Point
if (!hasPoint) { //Only one point allowed
hasPoint = true;
String newText = numInDisplay.getText() + ".";
numInDisplay.setText(newText);
}
} else { //New digit
String newText = numInDisplay.getText() + digit;
numInDisplay.setText(newText);
}
}
}
The code you think is causing the problem doesn't even reference the logArea, so why would you think this causes the problem?
You don't need to use the scrollRectToVisible(...) method. The setCaretPosition(...) should do the trick. Although you should get the length of the document and invoke that method AFTER you update the document.
Check out Text Area Scrolling for more information.
Edit:
I also don't see any reason for changing the editability of the text area.

Categories