Maintaining previous position in an EditorPane when the text is refreshed - java

This is a problem that has vexed me for a number of years. The user is scrolling through a text document contained in an JEditorPane which is in turn contained in a JScrollPane. The user performs some function using a button which changes the text (it highlights certain sections of the text). I then refresh the document in the JeditorPane to reflect the highlighting (using html tags). But when I do this, the document scrolls back to the top. I want the document to stay in the same position that the user was in right before taking the action. Note, the user has not selected any text in the document so I can't scroll to a selection point (that technique does work if text is selected, but alas I can't do it in this case). How do I preserve the position in the JScrollPane and scroll to the position that the user was at prior to taking the action?
Thank you

You can use JViewport.scrollRectToVisible() to jump to a certain section of a document in a JScrollPane. You can snapshot the current position before performing the action with JViewPort.getViewRect()
http://download.oracle.com/javase/6/docs/api/javax/swing/JViewport.html

Note, the user has not selected any text in the document so I can't scroll to a selection point
Get the selection indices before updating, then call setCaretPosition(previousStart) then moveCaretPosition(previousEnd) afterwards.

You can also use:
Point p = scrollPane.getViewport().getViewPosition();
// do stuff
scrollPane.getViewport().setViewPosition();

I tried all of these potential solutions. Unfortunately, for whatever reason, they didn't work. This is the code that actually solved the problem for me.
/* This code is executed, before the JeditorPane is updated: */
int spoint = detailpane.scrollPane.getVerticalScrollBar().getValue();
/* Then to restore the scroll to where it was before the update, the following code is executed*/
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
detailpane.scrollPane.getVerticalScrollBar().setValue(spoint);
}
});
/* Note because of the usual weirdnesses associated with Swing which is not thread safe, you need to enclosed the setValue within in a Runnable. */

Related

How to make textflow selectable

I have made a TextFlow, as I need to use multiple font-postures (I have set particular "Text" to either italic or normal). Once I display the TextFlow, it's not selectable/copyable. All I need is the TextFlow to be selectable once it's displayed in a scene, so it can be copy/paste-able.
Text example with font-posture (only one for examples sake):
Text volumeText = new Text(volume.getText());
volumeText.setFill(Color.WHITE);
volumeText.setFont(Font.font("arial", FontPosture.ITALIC, 13));
TextFlow reference = new TextFlow(
lastNameText, miscelanous1, firstNameText, miscelanous2);
reference.setLayoutX(115);
reference.setLayoutY(480);
reference.setMaxWidth(500);
control.getChildren().add(reference);
Text and TextFlow in JavaFX are not "selectable".
There is an issue open for this : Text should have API for selecting group of characters based on their position similar to the DOM's Range.
Until the issue is taken care of, your best option is to use a 3rd party control like RichTextFX.
I think I got a nasty way of doing the similar job
Text could sense the mouse enter & exit move event
We could set a signal for mouse pressed and release event. Would be nice to have this event triggered on Textflow node
extends Text class to have mouse enter event handler, when the global signal is on and mouse enters text then copy the text to some places
and also record the text place in the textflow
using sth. like
var index = textflow.getChildren().indexof(text)
record the pair of index and text
when mouse release or exit event are triggered, sort the texts selected and generate the corresponding texts into clipboard

Uneditable ParagraphViews in Java

I would like to have a document editor where certain document lines are uneditable, unclickable, etc.
I currently am using a JTextPane for my editor and extensions of DefaultStyledDocument and LeafElement, StyledEditorKit, and ParagraphView, to parse the document text into the appropriate elements and assign them to the appropriate extensions of ParagraphView. Everything is peachy up to this point.
So how to take the next step? How to designate and enforce certain extended ParagraphViews are "read-only"?
My current approach is to override getNextVisualPositionFrom in the Leaf and Section ParagraphViews to prevent arrow keys from moving the cursor into a restricted ParagraphView, but preventing the mouse from clicking inside a restricted ParagraphView has to be handled separately.
Is there a simpler, more comprehensive approach to this?
Thanks!
After a bit more research, a different approach that seems to fit more elegantly is to use a ComponentView for the uneditable views, instead of ParagraphView.
I can then use something as simple as a JTextArea in the ComponentView in its createComponent() method and setEditable to false.
It automatically ignores mouse clicks in that view. I still have to include my code for nicely skipping the caret over the view when the user presses up, down, left, right. Otherwise, the caret will invisibly move through underlying model text. I will also have to prevent backspacing through the protected view. Even though the view is uneditable, the model data underneath can still be affected by the user.

Retrieving current user entered value from GWT SuggestBox

I'm new to GWT. I have a simple SuggestBox which is populated using a MultiWordSuggestOracle. Users input their data to this SuggestBox, and if they find any match with the existing Suggestions its well and good. I'm able to retrieve this value in the SelectionHandler code as below.
display.getSuggestBox().addSelectionHandler(new SelectionHandler<Suggestion>() {
public void onSelection(SelectionEvent<Suggestion> event) {
String selectedProperty = ((SuggestBox)event.getSource()).getValue();
// do something with the property value
}
});
But users are allowed to enter values which are not already in the Suggestion oracle, in which case I should read this value and do something with this,may be saving to db as a new data.(The thing which I'm looking for is something like a browsers navigation widget where we show suggestions, users can pick up any suggestion or he can type in his new entry and carry on.) What I needed is a way to retrieve this new text user has entered? Data will be read on a button click. What I tried out is this.
display.getSaveBtn().addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
String selectedProperty = display.getSuggestBox().getValue();
//String selectedProperty2 = display.getSuggestBox().getText();
// Blank in both cases :(
// tried display.getSuggestBox().getTextBox().getValue(),but blank again
}
});
I tried to employ onChange() event handlers (as shown below)
display.getSuggestBox().addValueChangeHandler(new ValueChangeHandler<String>() {
public void onValueChange(ValueChangeEvent<String> event) {
String selectedProperty = ((SuggestBox)event.getSource()).getValue();
Window.alert("on change -- "+selectedProperty);
}
});
This is working fine except one scenario. Suppose there are two suggestions in the oracle,say 'createTicketWsdl' and 'createTicketTimeout'. When the user types in 'cr', he is opted with these two options, and if he selects 'createTicketWsdl' by pressing keyboard ENTER, then my alert is printing 'createTicketWsdl' which is correct. But if he selects 'createTicketWsdl' using mouse, then my alert is printing 'cr' (I tried to post the screenshot to give a better understanding, but being a new user I'm not allowed).(which I wanted to get as 'createTicketWsdl'since thats what he has selected). Soon after printing my alert, the value in the SuggestBox changes to 'createTicketWsdl'.
Is there a way to retrieve the value of the suggest box? I saw a similiar thread GWT SuggestBox + ListBox Widget, where some source code for a custom widget is available. But I didn't take the pain of trying out that, since what I want is simply get the current value from the SuggestBox and I hope there should be some easy way.
Thanks for all your help!
Your question is not very clear. You need to clarify your language a lil' bit. For example - is the following a question or an assertion? I mean, it sounds like an assertion but it has a question mark.
What I needed is a way to retrieve this new text user has entered?
Also, I do not understand what you mean by "he is opted by". Did you mean to say, "he is presented with the options ..." ?
Therefore, I am guessing your situation.
You have a listbox of existing items.
You have a textbox which allows freeform text entry
Any items whose prefix values matches the current textbox entry, the listbox items would be filtered to be limited to the matching items.
Even if the current textbox entry presents matching prefixes to filtering the listbox, the user can still perform freeform text entry. So, there are two possible cases here
4.1 the user clicks on the list box to select one of the filtered items
4.2 the user press enter key, which triggers selection of the current value of the textbox.
However, you find your widget participating in a race condition, so that when you click on the widget, the ValueChangeHandler gets triggered rather than the SelectionHandler. I do not know the structure of your widget so that is my best guess.
The problem is that you are allowing two separate modes of obtaining an outcome and you probably did not have well-defined state machine to handle choosing the appropriate mode. One mode is by the textbox and the other is by selection on the listbox - and you do not have a well-defined way of which would mode would be effective at any moment.
If my guess is accurate, this is what you need to do:
You must restrict your outcome to coming from only the textbox.
Your listbox selection must not trigger any outcome. Any change in listbox selection must propagate back to the textbox - to allow the user the chance of making further freeform entry based on that value.
only the keyboard enter on the textbox will trigger the final outcome.

create link in table cell and open float popup by click from it

I have a CellTable and need a column with hyper links and onclick handlers inside cells.
1st question, what am doing wrong if I have:
Column<MyObject, Anchor> linkColumn = new Column<MyObject, Anchor>(
new AnchorCell()) {
#Override
public Anchor getValue(final obj) {
Anchor link = new Anchor("link");
link.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
Window.alert("clicked ");
}
});
return link;
}
};
cellTable.addColumn(linkColumn, "link column");
...
public class AnchorCell extends AbstractCell<Anchor> {
#Override
public void render(Context context, Anchor h, SafeHtmlBuilder sb) {
sb.append(SafeHtmlUtils.fromTrustedString(h.toString()));
}
}
-but clicking to link - happens nothing
and 2nd question: what's better way to open float (based on div or so, not separated browser window) pupup with text contents from that ClickHandler?
In a CellTable, you aren't adding the Anchor widget to the table. You're just adding some HTML. None of the widget's functions will work as they normally would, because the widget is not actually in the table.
You can override onBrowserEvent to get events like clicking on the cells. These events still happen because they are native to the browser and don't need the widget framework to propagate. I think this is the best way to achieve the effect you want.
I have a similar setup as you. A CellTable (now a DataGrid) with hyperlinks in it, but I want to popup an editor widget when the user clicks in the cell but not on the link-y bit. If he clicks on the link, I want the normal HTML behaviour.
Create your column using the ClickableTextCell class. What's stored in the cell? A string ID into my database of user records, which includes the name and email of the user. When I create the column, I override the render method so that the information is rendered as a email link:
Column<RowType, ColumnType> emailColumn = new Column<RowType, ColumnType>(new ClickableTextCell()){
#Override
public void render(Context context, T object, SafeHtmlBuilder sb) {
/* Code that pulls the value in this column at this row, uses
* it to look up the name and the email, then does sb.appendX
* to build up the "<a href='emaillink'>name</a>" SafeHtml
* construction.
*/
}
};
Actually, I have a subclass of Column, but you get the idea.
Voila, an active HTML link on your page, but a clickable text cell underneath. I found this a lot easier than dealing with browser events.
I use the same structure for many of my cells. ClickableTextCells underneath, and type-specific rendering code to present it to the user in the format expected. In particular, we have a dynamic picklist type of field -- that is, the picklist is not known until the click occurs. The standard selectionCell requires the list of picks to be established once at construction time, which is what got me to this solution. So instead of a standard SelectionCell dropdown, which wouldn't work anyway without some serious work**, I use this technique. When the ClickableTextCell fires, I have set the FieldUpdater to construct a popup with a DataGrid in it, this DataGrid listing the current set of legal values for this selection (as determined by the current state of the database). When the user makes his selection and hits the Save button, the popup closes, the middleware is called to update his choice, the updated row data is returned via that RPC call, the data returned being used to update the internal client-side database, which triggers an update of all the ListDataProviders driving the DataGrids, which automatically updates the main DataGrid (and any other DataGrid potentially visible on the screen).
Actually, my solution has extended the ClickableTextCell into a DoubleClickableTextCell so that you need to double-click to activate the editor. Not required, of course, but it allows for the user to idly click around the grid without popups exploding in front of him.
** Besides the dynamic aspect, this dynamic selections can be very long lists, so a dropdown is a poor choice. Better to present a DataGrid that the user can scroll through, search, filter, and so on.

How to prevent the cursor from moving after undoing a Document.Replace

If you have a text component in Java and you do a replace the cursor will not move, if you undo and redo that replace using a standard undo manager the cursor will move to the beginning or end of that insertion or deletion.
How would I prevent this behavior?
I was able to trigger this with the Java TextComponentDemo, where I added a simple replace action that did this:
doc.replace(doc.getText(0, doc.getLength()).indexOf("mouse"), 5, "cat", null);
If I then use the demo's undo's and redo's the cursor will move.
Java Swing isn't the only API that has this behaviour in the standard undo manager. Sometimes XUL (which is the base for Firefox and Thunderbird) does the same thing with text areas in 3rd-party extensions. Essentially, even though the original text and the replacement text are similar in this case, the text area has to treat the document as an entirely new one, just like if you had done a select all and paste to overwrite the old text. In general, restoring the same cursor position to the new document would be useless, and if the document is shorter, it might not even be possible.
I think the easiest way around this would be to create your own custom actions for replacing text. Listen for them and perform the actions instead of performing the default action. Your custom action should be a compound action which scans the document manually, replacing substrings in the existing document - scan and replace all the way to the end by performing a number of document changes. When you override the undo method, just go through the list of modifications you made, undoing each one in reverse order. As long as each action in the compound action sets the text and the cursor position correctly, and its undo method works properly, the whole compound action will also undo properly.
This guide should hopefully explain this concept a little more clearly. The example merges actions into groups while the user types. You just need to do the same, but with procedural edits instead.
Check out the caret update policy in the class DefaultCaret.
The following update policies are
allowed:
NEVER_UPDATE: the caret stays at the same absolute position in the
document regardless of any document
updates, except when document length
becomes less than the current caret
position due to removal. In that case
caret position is adjusted to the end
of the document. The caret doesn't try
to keep itself visible by scrolling
the associated view when using this
policy.
ALWAYS_UPDATE: the caret always tracks document changes. For regular
changes it increases its position if
an insertion occurs before or at its
current position, and decreases
position if a removal occurs before
its current position. For undo/redo
updates it is always moved to the
position where update occurred. The
caret also tries to keep itself
visible by calling adjustVisibility
method.
UPDATE_WHEN_ON_EDT: acts like ALWAYS_UPDATE if the document updates
are performed on the Event Dispatching
Thread and like NEVER_UPDATE if
updates are performed on other thread.
You can set the update policy to NEVER_UPDATE before an undo or redo action, then set it back to what it was after the action.
public void actionPerformed(ActionEvent e) {
int updatePolicy = caret.getUpdatePolicy();
caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
undoManager.undo();
caret.setUpdatePolicy(updatePolicy);
}

Categories