Hide certain actions from Swing's undo manager - java

I am trying to write a JTextPane which supports some sort of coloring: as the user is typing the text, I am running some code that colors the text according to a certain algorithm. This works well.
The problem is that the coloring operations is registered with the undo manager (a DefaultDocumentEvent with EventType.CHANGE). So when the user clicks undo the coloring disappears. Only at the second undo request the text itself is rolled back.
(Note that the coloring algorithm is somewhat slow so I cannot color the text as it is being inserted).
If I try to prevent the CHANGE events from reaching the undo manager I get an exception after several undo requests: this is because the document contents are not conforming to what the undoable-edit object expects.
Any ideas?

You could intercept the CHANGE edits and wrap each one in another UndoableEdit whose isSignificant() method returns false, before adding it to the UndoManager. Then each Undo command will undo the most recent INSERT or REMOVE edit, plus every CHANGE edit that occurred since then.
Ultimately, I think you'll find that the styling mechanism provided by JTextPane/StyledDocument/etc. is too limited for this kind of thing. It's slow, it uses too much memory, and it's based on the same Element tree that's used to keep track of the lexical structure of the document. It's okay (I guess) for applications in which the styles are applied by the user, like word processors, but not for a syntax highlighter that has to update the styles constantly as the user types.
There are several examples out there of syntax-highlighting editors based on custom implementations of the Swing JTextComponent, View and Document classes. Some, like JEdit, re-implement practically the whole javax.swing.text package, but I don't think you need to go that far.

How are you trying to prevent the CHANGE events from reaching the undo manager?
Can you not send the UndoManager a lastEdit().die() call immediately after the CHANGE is queued?

I can only assume how you are doing the text colouring. If you are doing it in the StyledDocuments change character attribute method you can get the undo listener and temporarily deregister it from the document for that operation and then once the colour change has finshed then you can reregister the listener.
Should be fine for what you are trying to do there.
hope that helps

I have just been through this problem. Here is my solution:
private class UndoManagerFix extends UndoManager {
private static final long serialVersionUID = 5335352180435980549L;
#Override
public synchronized void undo() throws CannotUndoException {
do {
UndoableEdit edit = editToBeUndone();
if (edit instanceof AbstractDocument.DefaultDocumentEvent) {
AbstractDocument.DefaultDocumentEvent event = (AbstractDocument.DefaultDocumentEvent) edit;
if (event.getType() == EventType.CHANGE) {
super.undo();
continue;
}
}
break;
} while (true);
super.undo();
}
#Override
public synchronized void redo() throws CannotRedoException {
super.redo();
int caretPosition = getCaretPosition();
do {
UndoableEdit edit = editToBeRedone();
if (edit instanceof AbstractDocument.DefaultDocumentEvent) {
AbstractDocument.DefaultDocumentEvent event = (AbstractDocument.DefaultDocumentEvent) edit;
if (event.getType() == EventType.CHANGE) {
super.redo();
continue;
}
}
break;
} while (true);
setCaretPosition(caretPosition);
}
}
It is an inner class in my custom JTextPane, so I can fix the caret position on redo.

Related

IPhone password field in AWT/SWT?

I want to create a special Password Dialog for my eclipse product, which is used with an on screen keyboard.
It would be very nice, if i could use a component like the IPhone Password field. In this field, the added character is shown for a second and after the second it is converted into the '*' character for hiding the complete password.
Did a jar/library exists, this is implemented in AWT or SWT?
Edit:
I could trying to implement it from scratch (SWT), but for these i would have to create a very special and complicated KeyListener for the password Text component. I would have to catch the keyReleased event and set the characters manually into the field.
So far i was not able to find any libraries in the web. Suggestion how this can be implemented are welcome too.
This is not really a full answer, rather than a discussion starter and I don't know of any out-of-the-box widgets which can do that.
My first idea was to inheriting the swt Text widget and overriding setEchoChar et al., but after looking at the code this doesn't really seem feasible, because this method is merely a wrapper around:
OS.SendMessage (handle, OS.EM_SETPASSWORDCHAR, echo, 0);
If anyone would know the OS specific low-level implementation, that might be helpful.
Anyway, on to a different approach. I would avoid the KeyListener and use a ModifyListener on the Text-Widget.
void addModifyListener(ModifyListener listener)
You could then build a wrapper which catches the entered text using this listener, appends it to a locally held string/stringbuffer (or e.g. the Eclipse Preferencestore) and send a modified full text to the Text widget using setText(String s), replacing all characters except the last by an echo character (e.g. *).
myText.setText((s.substring(0, s.length()-1)).replaceAll("[\\s\\S]","*")+s.charAt(s.length()-1));
This is a bit of a kludge, but it should work.
The not so straightforward bit is the 1 second timing, without stalling the whole view...
Depending on what Jules said the following code is some kind of working.
The code is quick and fast and i would like to have a more thread safe solution.
originalString = new StringBuffer();
passwordField.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
synchronized (passwordField) {
String s = passwordField.getText();
String newS = s.replaceAll("[\\s\\S]", "*");
if (newS.equals(s)) {
while (originalString.length() > s.length()) {
originalString = originalString.deleteCharAt(originalString.length() - 1);
}
usernameField.setText(originalString.toString());
return;
}
if (originalString.length() < s.length()) {
originalString.append(s.charAt(s.length() - 1));
}
try {
Thread.sleep(500);
} catch (InterruptedException e1) {
}
passwordField.setText(newS);
}
passwordField.redraw();
passwordField.setSelection(passwordField.getText().length());
}
});
Key Events are cached, so you can add more characters, also when the Thread is waiting.
Another Problem is the Cursor handling. the Cursor always moves to the first position, when you set the Text.
I think when this is working it is very near to the iphone solution.

altering JFileChooser behaviour : preventing "choose" on enter in file path JTextField

Greetings to Swing Pros, here's a good (I hope) question for you.
The below are the task requirements and possible solutions I see. I would like someone having had such a hardcore experience to share some thoughts about this.
This does not require coding or anything like that, I just need general advice as to which approach is more reliable regarding the fact I need to work with private symbols which reside in sun.swing and/or javax.swing.plaf packages.
The task is to modify/alter JFileChooser behaviour (just a little bit, actually).
when the user presses enter in the file name JTextField, and the field contains a path to a dir, don't "select" the dir, but switch to it instead. Yes, the dialog is configured to accept directories, but we need to accept only clicks on the "Open" button, and (possibly) double-clicks in the file listing table.
prevent user from selecting a dir/file with more than 1GB data via hitting enter in the file name text field
Here're couple of general solution options:
a. listen on the property-based changes that JFileChooser provides (which AFAICS are triggered after-the-fact and won't provide the degree of control we need here).
b. tinker with the javax.swing.plaf.basic.BasicFileChooserUI (via refrection, breaking the private-level encapsulation) and alter the reference to
private Action approveSelectionAction = new ApproveSelectionAction();
so that our custom action does the extra checks for 1 and 2. This approach links with plaf package and may fail in case this action is somehow overridden in some class below this UI class.
c. traverse the JFileChooser component hierarchy, find the JTextField (which apparently should occur only once in the component tree), decorate all the action listeners hanging on that JTextField with our custom checks. My debugging session shows that this JTextField is some anonymous subclass of JTextField living in the sun.swing.FilePane.
This approach seems to be more OO-friendly, but there's a chance that for some OS this text field is absent, or some other JTextField is also present in the hierarchy.
Well, it seems that public JFileChooser API would not suffice to achieve that behaviour, while the other two options are either deep magic or unportable (long-term), or even both.
So, the question is: which approach would you choose and why?
Regarding option2, you don't need to use reflection to customize the accept Action. You can just override the approveSelection() method. Something like:
JFileChooser chooser = new JFileChooser( new File(".") )
{
public void approveSelection()
{
if (getSelectedFile().exists())
{
System.out.println("duplicate");
return;
}
else
super.approveSelection();
}
};
I recently encountered the same requirement, i.e., pressing Enter in the JTextField of a JFileChooser should cause the displayed dialog to traverse a directory instead of returning from the dialog. Only clicking on the Open button should cause the final selection.
The solution was fairly simple (at least for my application) and has two components to it (Pardon the messed up formatting. I'm new to this forum and I'm not sure why the code is not displaying correctly).
1 - Register an AWTListener to keep track of the last event type generated by the user
class MyChooser extends JFileChooser implements java.awt.AWTEventListener {
...
MyChooser(){
Toolkit.getDefaultToolkit().addAWTEventListener(this,
AWTEvent.MOUSE_EVENT_MASK + AWTEvent.KEY_EVENT_MASK);
...
}
int lastEventId;
public void eventDispatched(AWTEvent e) {
lastEventId=e.getID();
}
}
2 - Override the approveSelection() method of JFileChooser and check whether the approval request is a result of a mouse event (likely caused by the user clicking on the Open button) or a key event caused by the user pressing Enter. The 'lastEventId' variable provides access to this information. My own approveSelection then looks as follows:
public void approveSelection() {
File f=getSelectedFile();
if (f.exists() && isTraversable(f) && lastEventId ==
KeyEvent.KEY_PRESSED) {
setCurrentDirectory(f);
return;
}
super.approveSelection(); }

Any workarounds to lack of PreSelection events in SWT/JFace?

In my application I want the user to save any changes before he leaves a tab (implemented as CTabFolder).
I tried to handle SelectionEvent, but it fires after the tab has been changed (so why does it even have a doit field? Does it fire before change for some other controls?)
Looking on Bugzilla, I've found https://bugs.eclipse.org/bugs/show_bug.cgi?id=193453 and https://bugs.eclipse.org/bugs/show_bug.cgi?id=193064, neither of which is fixed.
Since this requirement is probably common, does anybody have a workaround?
I have a workaround that works with org.eclipse.ui.part.MultiPageEditorPart which is backed by a CTabFolder. I'll adapt it for a straight CTabFolder implementation.
First off use the selection listener:
tabFolder.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
pageChange(tabFolder.indexOf((CTabItem) e.item));
}
});
Then I implement pageChange() like this:
protected void pageChange(int newPageIndex) {
boolean changingPages = this.changingPages;
this.changingPages = true;
int oldPageIndex = tabFolder.getSelectionIndex();
if (isDirty() && !changingPages) {
tabFolder.setSelection(oldPageIndex);
if (canChangePages()) {
tabFolder.setSelection(newPageIndex);
}
}
this.changingPages = false;
}
In canChangePages() I pop up a do you want to save dialog and give the user an opportunity to select yes, no, or cancel. Yes saves the info and returns true. No reverts the info to the last saved state and returns true. Cancel simply returns false. You may simply want to try saving and return false only if the save fails.
It may look weird that I switch back to the old page before calling canChangePages(). This call executes quickly so it gives the illusion the tab never switched. No matter how long canChangePages() takes the user will not see a tab change unless it is approved by that method.

Wicket: Conditional display in Template

Hy,
I want to display a certain part (a div for example) of my wicket-template only under a certain condition (for example only if I have the data to fill it). The problem is:
If I only add the panel (filling the div) if I got the data, an exception is thrown every time I call the page without the data (because the referenced wicket-id is not added to the component-tree).
The only solution which came to my mind was to add a empty panel if there is no data. This is not an ideal solution because I got some unneeded code in the java-code and many empty divs in my rendered html.
So is there a better solution to include several parts of a wicket-template only under a condition?
Although this is an old question here could be one more solution: wicket:enclosure (and this )
Update: Now I needed this functionality by my self (for jetwick). I'm using WebMarkupContainer one for loggedIn state and one for loggedOut state and set the right visibility:
if (loggedIn()) {
WebMarkupContainer loggedInContainer = new WebMarkupContainer("loggedIn");
//## do something with the user
User user = getUserSomeWhere();
loggedInContainer.add(new UserSearchLink("userSearchLink"));
add(loggedInContainer);
add(WebMarkupContainer("loggedOut").setVisible(false));
} else {
add(new WebMarkupContainer("loggedIn").setVisible(false));
WebMarkupContainer loggedOutContainer = WebMarkupContainer("loggedOut");
loggedOutContainer.add(new LoginLink() {...});
add(loggedOutContainer);
}
The advantage of this for me is that I prevent a NullpointerExc in the //## marked line and the enclose feature of wicket would look more ugly to me in this case I think.
Like #miaubiz said, you can call setVisible(false), or you can override the isVisible() method, if the visibility is conditional to some other state (fields filled, for example).
Yup, you want to override isVisible. This will keep the isVisible=false html markup from even rendering to the final html page. Also, according to the docs (mentioned in EmptyPanel), you can use the WebMarkupContainer as the wrapping component.
this.add(new SimpleResourceModelLabel(NO_DATA_LABEL){
private static final long serialVersionUID = 1L;
#Override
public boolean isVisible() { return myList.isEmpty(); }
});
final WebMarkupContainer table = new WebMarkupContainer(MY_DATA_TABLE){
private static final long serialVersionUID = 1L;
#Override
public boolean isVisible() { return !myList.isEmpty(); }
};
I guess this is why there's EmptyPanel. Without knowing about your code more I can only say that what I think you're doing is something I'd do with combination of some child of AbstractRepeater and Fragment. If you're willing to tell more about what you want to do and maybe provide some code too, I'll be happy to help as much as I can.
you can call setVisible(false); on the component you want to hide.

SWT, Maintain default tab ordering when adding Key Listner

I've been creating a custom TabFolder extension that adds a key listener to allow quick tab switching using an ALT + # hotkey.
By adding the KeyAdapter to my TabFolder, the event handler works properly only when you have a tab header selected (in which case the ALT + ARROW_LEFT/ARROW_RIGHT also work.). I need this hot key to be active when any Widget with-in the TabFolder is active; however, it shouldn't be active if the selection is in a different tab folder or widget outside of a tab folder.
In an attempt to solve this, I wrote a simple recursive function to apply the key listener to all of the children of the tab folder:
public void applyQuickSwitchKeyBindings() {
removeKeyListener(ka);
addKeyListener(ka);
for(Control c: getChildren())
applyQuickSwitchKeyBindingsToChildren(c);
}
private void applyQuickSwitchKeyBindingsToChildren(Control c) {
if(c==null) return;
if(c instanceof Composite) {
Control[] controls = ((Composite)c).getChildren();
for(Control c2: controls)
applyQuickSwitchKeyBindingsToChildren(c2);
if(controls.length < 1) {
c.removeKeyListener(ka);
c.addKeyListener(ka);
}
}
}
Then i call the applyQuickSwitchKeyBindings() after I add the controls to each TabItem in the tab group.
The good news was that the quick switch hot key (ALT + #) worked great!
The bad news was that the original TAB ordering based on z-index is now gone. When you hit the SWT.TAB key you lose focus on your current text box and don't gain focus on anything else...
Questions:
1.) Can each control only have one KeyListener?
2.) Why is the original TAB traversal not working anymore?
Thanks in advance!
to 1) I'm pretty sure that more than one KeyListener is allowed.
to 2) I'm not sure, that depends on what you're doing in your KeyAdapter. Maybe you can post that too?
I just the tab order is broken somehow, you can reset ( or change ) it with a call to setTabList( Control[] ).
setTablList( new Control[] {
control1,
control2,
control3,
....
} );
So after more time learning and developing with SWT i've discovered my problem. When you add a listener it is applied to the widget/control you call the addXXXListener function on. So if that control is not active the listeners will not be fired.
The solution seems to be SWT's global Filter mechanism which allows you to add global application(Display) scope listeners.
Display.getCurrent().addFilter(SWT.keyPress, new KeyPressListener());
Pardon the incorrectness of this line, but if you google it you'll see what i mean.
I have also read to use this sparingly.

Categories