Java Swing. JTextPane. Getting start and end character seen by user [duplicate] - java

I have a JeditorPane in a JScrollPane. At certain points in the application, I would like to retrieve the text that is visible in the scrollPane (the text that is currently showing) and only this text. Is there a way to do this?
Thank you,
Elliott

You can use the viewport to get the view position and size.
JViewport viewport = scrollPane.getViewport();
Point startPoint = viewport.getViewPosition();
Dimension size = viewport.getExtentSize();
Point endPoint = new Point(startPoint.x + size.width, startPoint.y + size.height);
Once you know the start/end points of the viewport you can use:
int start = editorPane.viewToModel( startPoint );
int end = editorPane.viewToModel( endPoint );
Once you know the offsets of the text you want you can get the text from the component:
String text = editorPane.getText(start, end - start);
None of the code is tested.

Related

JtextPane with a touch interface

I'm developing a touch based windows 7 app using a tuio client-server setup and a touch library that extends multitouch 4 java.
One of the functionality I'm struggling with is to enable text highlighting when using touch.
I display a simple txt file using a JTextPane to display the text, the highlighting is done by through a drag action.
I get the clicked position where the drag event starts and then when it stops and tried to convert those coordinates to the text panel's space but I get different values from the ones that I should have, usually before the actual text.
The code I'm using to display the document is the following:
//Create the JDialog that is the container of it
window = new JDialog(parent);
window.setUndecorated(true);
//Create the JTextPane
text = new JTextPane();
text.setPage(newFile.toURI().toURL());
text.setEditable(false);
text.setHighlighter(null);
//ScrollPane that will be used to display the text
JScrollPane scroll = new JScrollPane(text);
scroll.setPreferredSize(new Dimension(500, 700));
window.getContentPane().add(scroll, BorderLayout.CENTER);
window.pack();
window.setVisible(true);
window.validate();
Where the JDialog parent is the main display component used in my app.
The drag is handled as follows:
#Override
public boolean processGestureEvent(GestureEvent ge) {
if((ge instanceof DragEvent) && this.component.isHighlight())
{
tapCount=0;
if(this.component.isHighlight())
{
//do highlighting
DragEvent drag = (DragEvent) ge;
switch (drag.getId()) {
case GestureEvent.GESTURE_STARTED:
Point start = drag.getFrom();
Point calcStart = new Point(start.x - compPosition.x, start.y - compPosition.y);
startPos = this.textDisplay.viewToModel(calcStart);
break;
case GestureEvent.GESTURE_ENDED:
Point end = drag.getTo();
Point calcEnd = new Point(end.x - compPosition.x, end.y - compPosition.y);
endPos = this.textDisplay.viewToModel(calcEnd);
System.out.println("I have this positions:" + startPos + "/" + endPos);
System.out.println("Should have " + this.textDisplay.getSelectionStart() + "/" + this.textDisplay.getSelectionEnd());
System.out.println("And the text is: " + this.textDisplay.getText().substring(startPos, endPos));
break;
case GestureEvent.GESTURE_CANCELED:
startPos = 0;
endPos = 0;
break;
}
}
return true;
}
In which the compPosition is the JDialog's position that holds the text pane. I'm simulating touch with the mouse so the correct text position for the highlighting I'm getting from the built-in highlighting functionality of the text pane with mouse.
Is the problem because of the JDialog and the JScroll pane that somehow skews the conversion?
The coordinate system for the point's I get from the touch are with the origin in the top left corner of the screen and the text pane's coordinate system origin is in the same top lef corner.
Any ideas on how can I solve the problem? Any suggestions are appreciated.
LE:
I was doing something wrong in that I was adding the gesture processor when I initialized the component and it's position was (0,0) and only afterwards I moved it where I wanted to.
I changed the position calculations as follows:
Point calcStart = new Point(start.x - this.component.getLocation().x, start.y -this.component.getLocation().y);
passing instead a reference to the actual component and getting the location when needed.
try instead of
Point calcStart = new Point(start.x - compPosition.x, start.y - compPosition.y);
to use
Point calcStart = new Point(start.x, start.y);
i wonder how it gonna end, so give us some value of what you get

Calculating the dimensions of a rendered string of text (before rendering it)

I want to align a String drawn by a canvas perfectly in the middle. So if I have code like so
Paint p = new Paint();
Canvas c = holder.lockCanvas();
String centered_text = "Hello World";
and then set my Paint style and size
p.setTextSize(35);
p.setStyle(Style.STROKE);
Now to draw my String in the middle I would do
c.drawText(centered_text, c.getWidth()/2, c.getHeight()/2, p);
But this wouldn't actually center my text, it would put the top left corner in the center. To center it I would need to know the Strings litteral width and height in pixels not characters.
int string_width = centered_text.getWidth(); //String.getWidth() is not a real methode
int string_height = centered_text.getHeight(); //String.getHeight() is not a real methode
c.drawText(centered_text, (c.getWidth()/2)-(string_width/2), (c.getHeight()/2)-(string_height/2), p);
This would center the text but String.getWidth() and String.getHeight() are not real methodes. So my idea is to use the size of the text and the size of the String to find the width and the height. So something along te lines of:
int string_width = centered_text.length()*p.getTextSize();
int string_height = p.getTextSize();
But I feel like this is wrong because different chars are different sizes... anyone have any ideas.
Assuming you're in Java Swing, what you want is FontMetrics.
new FontMetrics(canvas.getFont()).getStringBounds(someString, canvas.getGraphics());
will return a Rectangle2D with the size of your string in it.

Force JScrollBar to scroll to line head

I am dealing with JEditorPane to display HTML documents and I created a button which scroll me to next view port every time I click it, "as if I am turning a page of a book".
However, sometimes I see a part of a line at the top of the view port, so is there any way to force JScrollBar to scroll to ahead of a line?
I tried setBlockIncrement() member method but it didn't work at all.
Here you are my best attempt:
//get the visible rectangle as well as the most bottom right point.
Rectangle rec = jEditorPane1.getVisibleRect();
Point pt = new Point((int)rec.getX() +(int)rec.getWidth(),(int)rec.getY() + (int)rec.getHeight());
// get the offset of the most bottom right point
int off = jEditorPane1.viewToModel(pt);
try {
//get next viewable rectangle and scroll to it
rec = jEditorPane1.modelToView(off+100);
rec.height = jEditorPane1.getVisibleRect().height;
jEditorPane1.scrollRectToVisible(rec);
} catch (BadLocationException ex) {
Logger.getLogger(NewJFrame2.class.getName()).log(Level.SEVERE, null, ex);
}
Guess you have a position in the Document you want to show. You can get visible rect of the position using modelToView() method. and use the y position to set your viewport. E.g. use scrollRectToVisible passing the y and viewport height in the rectangle parameter.

Why do JTextArea and TextLayout wrap words differently?

We have an app that draws text, but then displays a JTextArea for the user to edit the text when they click on the text. However, the wrapping between these two text-handling components differs. They use the same width, text String, and Font.
For the text-drawing, I'm using the from the Java tutorial, which I've also seen used by others in related questions here and other forums. Here's that part of the code:
FontRenderContext frc = g2d.getFontRenderContext();
TextLayout layout;
AttributedString attrString = new AttributedString(myText);
AttributedCharacterIterator charIterator;
int paragraphStart;
int paragraphEnd;
LineBreakMeasurer lineMeasurer;
float breakWidth;
float drawPosX;
float drawPosY;
attrString.addAttribute(TextAttribute.FONT, myFont);
charIterator = attrString.getIterator();
paragraphStart = charIterator.getBeginIndex();
paragraphEnd = charIterator.getEndIndex();
lineMeasurer = new LineBreakMeasurer(charIterator, frc);
// Set break width to width of Component.
breakWidth = myTextWidth;
drawPosY = startY
// Set position to the index of the first character in the paragraph.
lineMeasurer.setPosition(paragraphStart);
textBounds = new Rectangle(startX, startY(), 0, 0);
// Get lines from until the entire paragraph has been displayed.
while (lineMeasurer.getPosition() < paragraphEnd) {
layout = lineMeasurer.nextLayout(breakWidth);
// Compute pen x position. If the paragraph is right-to-left we
// will align the TextLayouts to the right edge of the panel.
drawPosX = layout.isLeftToRight()
? startX() : breakWidth - layout.getAdvance();
// Draw the TextLayout at (drawPosX, drawPosY).
layout.draw(g2d, drawPosX, drawPosY);
lineBounds = new Rectangle2D.Float(drawPosX, drawPosY - layout.getAscent(), layout.getAdvance(), (layout.getAscent() + layout.getDescent() + layout.getLeading()));
// Move y-coordinate in preparation for next layout.
drawPosY += layout.getAscent() + layout.getDescent() + layout.getLeading();
}
The JTextArea is much simpler:
JTextArea textArea = new JTextArea(myText);
textArea.setSize(myTextWidth, myTextThing.getHeight());
textArea.setOpaque(true);
textArea.setVisible(true);
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setFont(myFont);
textArea.setBorder(null);
I set the border to null because I have another rectangle drawn outside the bounds of the text area with a dashed area to show where it is. Might seem silly now, but we use it to show the bounds of the text area when the user first selects the text they want to edit. At that point, the JTextArea isn't yet created. They have to click on it again to begin editing. The reason for this is that once a text area is selected, they may also drag and resize the text area, and that gets messy and more confusing if they had a live JTextArea when they started dragging and resizing.
Separately, both the drawn TextLayouts and the JTextArea appear to wrap words just fine. but when used together you can see the difference. The problem with this is that while the user is editing the text, the JTextArea is doing its thing to wrap the text. But when the user JTextArea loses focus, it is converted to the drawn text, and then the words may be wrapped differently.
Fill the text area with i or l characters. Grab a UI ruler or magnifying glass and count the size of your text area in pixels from the leftmost character of the longest line to the rightmost. Do the same with n, m, and a few other characters for a few more data points. I suspect that the text area has an invisible border of a few pixels it uses even when set to no border. If this is the case, add the same border around the TextLayout component and they should appear identical.
(Alternatively to counting pixels, you could set a background color for the text or the components, but I wouldn't necessarily trust it.)

Getting the visible text in a JEditorPane

I have a JeditorPane in a JScrollPane. At certain points in the application, I would like to retrieve the text that is visible in the scrollPane (the text that is currently showing) and only this text. Is there a way to do this?
Thank you,
Elliott
You can use the viewport to get the view position and size.
JViewport viewport = scrollPane.getViewport();
Point startPoint = viewport.getViewPosition();
Dimension size = viewport.getExtentSize();
Point endPoint = new Point(startPoint.x + size.width, startPoint.y + size.height);
Once you know the start/end points of the viewport you can use:
int start = editorPane.viewToModel( startPoint );
int end = editorPane.viewToModel( endPoint );
Once you know the offsets of the text you want you can get the text from the component:
String text = editorPane.getText(start, end - start);
None of the code is tested.

Categories