Mac L&F problems: Differing behavior of JTextField.requestFocus() - java

I have a problem with JTextField.requestFocus() behavior that appears to be different on Mac OS X.
Here is my situation: I have a dialog with a JList and a JTextField. The user is supposed to write a boolean expression in the text field, and the list contains the names of all the variables that might be entered in the expression. Because the user is expected to continue entering the expression after clicking on a variable from the list, the program helpfully calls JTextField.requestFocus(). This way you can e.g click "pvalue" from the list and then type " < 0.05" without the need to click on the textfield in between.
This all works fine on my development machine (Linux), but I got a bug report from a Mac user that clicking on the list actually selects all text in the text field, making it easy to accidentally overwrite what was entered before.
I suspected this is a problem with the Mac look-and-feel, after some searching it seems that indeed there is a "Quaqua.TextComponent.autoSelect" property for the mac look-and-feel that seems to be related to this problem: http://www.randelshofer.ch/quaqua/guide/jtextcomponent.html
My general question is:
Can you suggest a workaround for this problem?
In case that is too broad, an answer to these subquestions would already be a big help:
A possible solution could be to change the property "Quaqua.TextComponent.autoSelect". How do I do that?
I'm not even sure what "Quaqua" is. It looks like it is a customized look and feel. What is the default look and feel for Mac OS X? Does it have a property similar to Quaqua.TextComponent.autoSelect?
Is there a possibility to tweak look and feel for a single component instance only? If so, how?
Is it possible to set the Mac look and feel on my Linux development machine so that I can actually confirm this bug (all the above is really based on hunches and suspicions)? If so, how?

Seems this is a bug of Mac OS. JTextFields select their contents when they gain focus though keyboard tab cycling. If the insertion point is in the middle of the text, the insertion point will remain and the entire text will not be selected.
As a workaround you can override this behavior with the following, it works fine for me:
textfield.setCaret(new DefaultCaret()).
More details you can refer to this and this.

To modify the default behaviour, you can set the system property to false before initializing the UI components: System.setProperty("Quaqua.TextComponent.autoSelect", "false"); To modify a single component, you can use JTextField#putClientProperty("Quaqua.TextComponent.autoSelect", Boolean.FALSE);.
You can find other MacOS L&F specific properties here:
Quaqua Look & Feel - User Guide

A workaround might be (and I haven't tested this) to make the JList that inserts the variable names unfocusable. That way the focus will remain in the text field when you click on an item in the list. I'd recommend to use setRequestEnabled(false) on the JList, so that they are still focusable if you tab to them, but clicking them with the mouse will not focus them.

Sorry to add to an old question, but I just came across this problem and used the following code, which seems a little more complete than the previous example:
// JTextField linkedText
final int
startBefore = linkedText.getSelectionStart(),
endBefore = linkedText.getSelectionEnd();
linkedText.requestFocus(); // this was the original code line!
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
linkedText.setSelectionStart(startBefore);
linkedText.setSelectionEnd(endBefore);
}
});
This appears to protect the current cursor position or selection. (Note: This code must already run in the event dispatch thread, but you need invokeLater anyway or it doesn't work.)
I have an 'is Mac' function, so I did this inside a test for that, but it probably doesn't do any harm to do it on all platforms.

I noticed when looking through the JavaDocs that requestFocus() "is discouraged because its behavior is platform dependent." You should use requestFocusInWindow() instead and see if the same problem occurs with it.
requestFocusInWindow is part of the Focus subsystem, introduced in Java 1.4.
On a side note, the default Apple Look and Feel has at least one property in the apple.laf namespace: apple.laf.useScreenMenuBar
Edit: According to Sun, the Macintosh look and feel is only available on Macs.

While using requestFocusInWindow() is indeed encouraged over requestFocus(), it still produces the same problematic behavior on Macs (e.g., highlighting of full text field).
One workaround I got to work was to explicitly set the cursor position after requesting focus:
JTextField.requestFocusInWindow();
JTextField.setCaretPosition(JTextField.getDocument().getLength() - 1);
Note the "-1" is necessary, otherwise it will continue to highlight the entire field.
I'm curious to know if this solution is platform independent. Does this screw up the desired Linux or Windows behavior?

Mac will select the contents of the text field when the field gains focus. You can restore the state of the text field if you listen for the focus change event.
// JTextField linkedText
// Cache the state of the JTextField prior to requesting focus
final int
startBefore = linkedText.getSelectionStart(),
endBefore = linkedText.getSelectionEnd();
linkedText.requestFocus(); // this was the original code line!
// Use a focus listener to listen for the focus change and then
// reset the selected text to protect the cursor position
linkedText.addFocusListener ( new FocusListener()
{
public void focusGained( FocusEvent event ) {
linkedText.setSelectionStart( startBefore );
linkedText.setSelectionEnd( endBefore );
}
public void focusLost( FocusEvent event ) {
// do nothing
}
} );

Thank you for sharing your ideas. I had the same problem on my java application where on my windows system there wasn't a problem, but on my Mac OS X Yosemite I couldn't change the input. The focus wouldn't stay on the JTextField. Thanks to this thread I was able to fix my problem.
If you change the look and feel of the buttons and input boxes you maintain the focus and you can type again. The reset of the frame stays in the standard Mac OS look.
This is my code that I use in my java main methode. If you want to fix the problem past the try-catch code in your main methode.
public class Venster extends JFrame {
public static void main(String[] args) {
//Change L&F for mac
//Mac JTextField Bug Fix
try {
// Set cross-platform Java L&F (also called "Metal")
UIManager.setLookAndFeel(
UIManager.getCrossPlatformLookAndFeelClassName());
} catch (UnsupportedLookAndFeelException e) {
System.out.println("L&F not supported" + e.getMessage());
} catch (ClassNotFoundException e) {
System.out.println("Fout: " + e.getMessage());
} catch (InstantiationException e) {
System.out.println("Fout: " + e.getMessage());
} catch (IllegalAccessException e) {
System.out.println("Fout: " + e.getMessage());
}
//The app
JFrame frame = new JFrame();
frame.setSize(1000, 520);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("10 More Bullets by Frank Peters");
frame.setContentPane(new SpeelVeld());
frame.setVisible(true);
frame.setLocationRelativeTo(null); //start app in center
}
}
Soure:
http://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html

Related

Change mnemonic modifier key in Java/Swing

Setting focus hot keys in Swing is very easy:
tfldPlantsNeeded = new JTextField( FIELD_LEN_MED );
lblPlantsNeeded = new JLabel( "Plants Needed" );
lblPlantsNeeded.setDisplayedMnemonic( 'p' );
lblPlantsNeeded.setLabelFor( tfldPlantsNeeded );
This will give focus to the tfldPlantsNeeded JTextField when the user presses ALT+p. It also highlights/displays the character that will trigger the focus change. (In this case, when ALT is pressed, the 'P' in "Plants" is underlined.)
This is great ... well, kinda. On a Mac, when the user presses ALT (which is also Option on the Mac keyboard) the mnemonic is highlited, but the focus change isn't triggered when p is pressed too. If, however, the user presses Control + Option + p, then it works as "expected" and focus is changed. (As an aside, if the user DOES press Option + p, the currently focused text field will get funny characters inserted.)
I know that I can do this myself by specifying custom keybindings via getInputMap and getActionMap, but is there a way to change the application global mnemonic modifier so that we can use the automatic keybindings and trigger character highlighting? (In my case, I would like to use Command or Meta as the mnemonic modifer key.)
Apparently this isn't as straightforward as you might think, but there is a way.
First of all, for menus (JMenu) there is a property which is controlled by the look and feel called Menu.shortcutKeyswhich you can set manually. This sets the mnemonic modifier for menus in the specific look and feel. If you want more information about this feel free to ask.
In order to set the mnemonic modifier for everything, you need to override the default toolkit (Toolkit). First of all, run a main method to find what it is with the following lines
System.out.println(System.getProperty("java.awt.headless"));
System.out.println(System.getProperty("awt.toolkit"));
If the first line is null of false (see java.awt.Toolkit getDefaultToolkit()) then the second line will give you the class name which is used as the default Toolkit for your system. I use Windows and the second line gives the output sun.awt.windows.WToolkit. Now create a class that overrides getFocusAcceleratorKeyMask in your default toolkit. For me it looks like this
public class MyToolkit extends WToolkit {
#Override
public int getFocusAcceleratorKeyMask() {
return InputEvent.CTRL_DOWN_MASK;
}
}
Finally, we have to tell the system to use it. In your application, put the line
System.setProperty("awt.toolkit", "packagename.MyToolkit");
where you need to set the correct package path to your class. Make sure this line is placed before starting any GUI related code, preferably in the first lines of main. This should now set CONTROL as the global mnemonic modifier (or use META_DOWN_MASK if that's what you want. Look at java.awt.event.InputEvent for MASK list.).

Control Impress presentation window from openoffice API

I'm currently attempting to use the openoffice API to display a powerpoint presentation from Java - I've got a fair way in that I've managed to open a presentation and display it. However, there's a couple of things that I'd like to be able to do I can't figure out with the API as it stands:
I don't want the main Impress window to appear, just the presentation window. Now, I can start it minimized no problem with a property, but then the actual presentation window is minimised as well, which I don't want. I can also grab the window and call setVisible(false) on it, but it's still visible for a second or so while it's loading.
I want to be able to control the monitor which the presentation appears on (I'm using it in a multi-monitor setup.) I thought I might be able to grab the Window of the presentation and move it around that way as I need to, but I can't see how - for the main window I can do something like:
XModel xModel = UnoRuntime.queryInterface(XModel.class, xDrawDoc);
xModel.getCurrentController().getFrame().getContainerWindow().blah();
...but I haven't yet found a way to get the presentation Window. I'd like to be able to set the bounds of the window directly (x, y, width, height) rather than just being constrained by positioning on a single monitor.
I can live with the first point, the critical one I need to solve for my use case is the second.
Any ideas on the above? I'm an experienced Java programmer but new to UNO.
Seems the second point can be solved, ish, with the display property:
public void start() {
try {
xPresentation.setPropertyValue("Display", 1);
}
catch (Exception ex) {
ex.printStackTrace();
}
xPresentation.start();
}
Note however a few of things - firstly the display index is base 1, not 0. Secondly, trying to set the properties in an array and passing them to xPresentation on creation didn't seem to have any effect - it only worked for me setting the property later as above. Thirdly, it doesn't allow fine grained control over the window as I wanted, just control of the display the presentation appears on.

Changing the default appearance of netbeans GUI controls

Hey I'm new to netbeans and I noticed a lot of applications (from textbooks) have a default style/appearance to their controls (buttons etc) as shown below.
(source: iforce.co.nz)
.
the appearance when I'm creating a GUI is just the standard windows xp or 7 button style. Is there a way to change this to the style shown in the image above?
Here is the appearance I am currently getting:
(source: iforce.co.nz)
.
Thanks in advance.
Yes, you can give Swing a Windows like look and feel with the following code:
try{
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
}
catch(Exception e){
System.out.println("UIManager Exception : "+e);
}
NetBeans will automatically choose a Look and Feel depending on your JDK and operating system. NB generated some code to set the L&F when you created the JFrame which made everything look like Windows components. If you want to change the L&F, look at the source for your JFrame and look for a collapsed bit of code that says something like "Look and feel setting code." If you expand it you can change it as you like, or even delete it, which will cause it to simply use the default L&F ("Metal"), which is the one in your picture. Bear in mind that you really shouldn't really just delete generated code, but I'm just trying to make a point here. If you're new to swing in general, I'd recommend writing some applications by hand, and they should just use the "Metal" L&F by default. This will allow you to get comfortable with working with swing. See here for more information.
See the nested layout example for code that offers a combo containing the available PLAFs, and allows the user to change the PLAF at run-time.
You can add Look and Feels. There are some free great looking ones which can be downloaded freely. If you only want Windows look and feel you can just add
try{
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
}
catch(Exception e){
e.printStacktrace();
}
Hope this answers your question.

Java KeyEvents on Mac

I am trying to write a program that uses key events to activate a method. The code works on Windows machines but when transered to Mac it does no respond to my "Spacebar" being pressed. I presume this is to do with the different key codes used.
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
System.out.println("SPACEBAR");
grid.stepGame();
}
}
Any ideas how i can get this working on Mac?
Edit - The problem has been solved using the answer below - For note though it seems that on Mac the Frame never automatically regains focus, hence why the keylistener doesn't work is another JComponent is activated.
I'm uncertain as to your particular issue but it's a good bet that if you switch to using key bindings instead of key listeners your issue would disapear.
From the Java Tutorials site:
Note:
To define special reactions to particular keys, use key bindings instead of a key listener.
As an example
// Component that you want listening to your key
JComponent component = ...;
component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0),
"actionMapKey");
component.getActionMap().put("actionMapKey",
someAction);

How to get a SWT Tray or TrayItem location

Any ideas how to get the location of the system tray (Tray) or item's on it (TrayItem) with SWT? Getting the bounds from the display only gives me the entire screen's bound. ie
item.getDisplay().getBounds();
will give me (0, 0, 1024, 1024) on my Windows box.
I'd also like to know if the system tray position (left, right, top, bottom) but can probably guess given the location. This is all so I can popup a message near the system tray.
This is a duplicate of this post but I want to offer a bounty (and so control what I consider a correct answer).
If you just want to display a shell near the tray item reacting to a user event (as is my case). You can get the pointer location when the event is triggered over the tray item:
trayItem.addSelectionListener (new SelectionListener () {
#Override public void widgetDefaultSelected (SelectionEvent aEvent) {
widgetSelected (aEvent);
}
#Override public void widgetSelected (SelectionEvent aEvent) {
if (mWindow.isVisible ()) {
Shell wnd = mWindow;
mWindow = new Shell (mDisplay);
wnd.close ();
}
else {
mWindow.setLocation (mDisplay.getCursorLocation ());
mWindow.open ();
}
}
});
You can find the whole source code here.
If you want to notify some other event (not generated by user input), I guess your best bet is to use a ToolTip as Sandman points out in the previous answer.
Good luck!!!
widget.getDisplay() always returns the display used for the specified widget, so that will never work.
I far as I can see for both Win32 and MacOS, you will not get the location before the first mouse event on the item itself ;-(
If you are willing to add some architecture dependent code, you can try the following...
For MacOS - though not tested:
sub-class TrayItem (remember to override checkSubClass())
use getLocation() to return the current location of the item
For Win32 - again not tested - you can try the same but this time override messageProc(...). One of the first messages will allow you to query the current location of the handle...
Guessing from the API, it looks like there is no such option (in contrast with, e.g. a ToolItem). :(
Thanks to those who tried to answer. However, as with the other post, I think its just not possible so I'm answering my own question in the negative :(
You can map from one coordinate system to another by using Display.map. You can map your item's 0,0 (Upper Left) into Display coordinates by doing:
Display.map(item, null, 0, 0)
Use null as the "to" control for mapping to the Display coordinate space.

Categories

Resources