I’ve noticed some strange behaviour when working with multiple monitors - if I drag [externalize] a dockable to my 2nd monitor - the dockable externalizes, but it doesn’t paint() - it’s just black.
There are several scenarios with multiple monitors, here’s a few:
1a. Frame resides on PRIMARY -> Externalize to PRIMARY -> OK
1b. Drag Externalized dockable back to Frame -> OK
2a. Frame resides on PRIMARY -> Externalize to SECONDARY -> BLACK
2b. Drag Externalized dockable from Secondary to Primary -> Stays BLACK
2c. Drag Externalized dockable back to Frame -> OK
3a. Frame resides on SECONDARY -> Externalize to PRIMARY -> OK
3b. Frame resides on SECONDARY -> Externalize to SECONDARY -> OK
3c. Drag Externalized dockable to Primary or Secondary -> OK
I’m running on Windows 7 [ultimate] with an ATI Radeon graphics card. I’ve tried this with Aero enabled and disabled (Aero is infamous for painting problems with non .NET code :D.
Is there anything I could try to get the paint working on the the secondary monitor?
That’s an interesting bug. To me this really sounds like an issue of the driver and/or Java, because an externalized Dockable is nothing more than a Component on a JDialog. Unfortunately I don’t have access to this combination of soft- and hardware and cannot test it myself. I assume you do use a recent version of Java?
Could you execute a little experiment? Create a JFrame, open it on the PRIMARY. Then create a JDialog with some content (e.g. JButtons…) with the parent being that frame, and open that dialog on the SECONDARY. What happens? If the dialog is black, then I don’t have much hope of fixing this.
Something like this, you might need to update the boundaries.
import java.awt.EventQueue;
import java.lang.reflect.InvocationTargetException;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
public class BlackTest {
public static void main( String[] args ) throws InterruptedException, InvocationTargetException{
EventQueue.invokeAndWait( new Runnable() {
public void run(){
JFrame frame = new JFrame("Test");
frame.setBounds( 0, 0, 100, 20 );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setVisible( true );
JDialog dialog = new JDialog( frame );
dialog.add( new JButton( "Hello" ) );
dialog.setBounds( 1024, 0, 400, 200 );
dialog.setVisible( true );
}
});
}
}
I believe this is a Windows 7 bug (or is it a feature? I can never tell with Windows). I’m using JRE 1.6.0_22.
I’ve had loads of paint problems in various GUI areas using Java on Win7 - but these are usually associated with Aero (aero does lots of optimizations and trickery to do its transparency thing).
Unfortunately, the directX disable doesn’t fix anything on Win7.
*** What I have found is that the multi-monitor problem occurs only when a (heavyweight) java window is undecorated (JWindow, JFrame, JDialog) - and externalized CDockables are undecorated JDialogs, yes?
Ultimately, this is an incompatibility between Windows 7 and Java (what? you mean Sun and MS don’t get along? (:eek:)
Rather than wait for Oracle and MS to kiss and make up, do you use a property or similar to set the decoration mode of externalized dockables?
App code could then detect Windows 7 and set such a property to ‘decorated’ to get things working.
That’s great that you’ve used the factory pattern throughout, so [external] issues like this can be addressed - excellent!
Switching the decorated property to false gets 'round the multi-monitor problem on Win7, and all externalized dockables display correctly regardless of monitor.
This of course puts a windows bar + border on externalized dockables, but it actually looks very good like this. It also exposes OS windows buttons/cmds (max/min/close), which is quite handy.
Now all we have to do is wait for MS and Oracle java to start working together ::crazy
Yes, keeping the current settings is the prudent thing to do. It’s simple enough to change it at the app level.
I suspect there aren’t that many users just yet on Win7 with multiple monitors. Over time, they’ll no doubt be more, but, hey, this post will guide them seemlessly toward a solution!
One good thing is that Win7 has paint problems not just with java, but with other technologies as well, including Silverlight (MS’ own!), so this should help in motivating MS to fix their video compat issues!
If the ScreenDockWindow is set to not be undecorated (i.e. it **is **decorated), on Windows at least, this means the close button is visible but doesn’t do anything.
The reason for this is the default ScreenDockWindow createWindow() method sets the dialog to JDialog.DO_NOTHING_ON_CLOSE. There is no direct property to set this.
It’s ok though, you just need override the DefaultScreenDockWindowFactory’s createWindow() method like this:
DefaultScreenDockWindowFactory factory = new DefaultScreenDockWindowFactory()
{
public ScreenDockWindow createWindow(ScreenDockStation station)
{
ScreenDockWindow window = super.createWindow(station);
if (window instanceof ScreenDockDialog && !isUndecorated())
((ScreenDockDialog) window).getDialog().setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
return window;
}
};
factory.setUndecorated(false);
dockController.putProperty(ScreenDockStation.WINDOW_FACTORY, factory);
If you don’t want the dialog to be able to be closed, setting as undecorated and not having a close button in the dockable (at least during externalized state) is the only option, as java has no cross-platform way of controlling OS window buttons.
I hope this helps anyone interested in this behaviour.
Careful: closing the dialog without removing the Dockable from the ScreenDockStation will have the funny side effect of a memory leak… So if you do this you should also add a WindowListener to the dialog - and call the “ScreenDockStation.drag” method once the dialog is closed (But only if the user closes the dialog. If the user closes the Dockable then the framework already takes care of this).
Here’s the modified code snippet to remove the dialog’s ScreenDockStation’s dockable if the JDialog’s close button is actioned:
DefaultScreenDockWindowFactory factory = new DefaultScreenDockWindowFactory()
{ //Ensure the dialog's close button will dismiss the dialog
@Override
public ScreenDockWindow createWindow(ScreenDockStation station)
{
final ScreenDockWindow window = super.createWindow(station);
if (window instanceof ScreenDockDialog && !isUndecorated())
{
((ScreenDockDialog) window).getDialog().setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
((ScreenDockDialog) window).getDialog().addWindowListener(new WindowAdapter()
{ //Prevents a memory leak by removing the dialog's station dockable so it doesn't get orphaned
@Override
public void windowClosed(WindowEvent we)
{
((ScreenDockDialog) window).getStation().drag(((ScreenDockDialog) window).getDockable());
}
});
}
return window;
}
};
factory.setUndecorated(false);
dockController.putProperty(ScreenDockStation.WINDOW_FACTORY, factory);
Hello, yes I know, it’s a old thread. But I hade the same problem and found this out. This problem only exists with windooze7 and java6. If you use java7, there is no problem anymore.
Hi,
for those who still have to deploy for JRE 6 environments, I have an alternate solution without using the window decoration approach.
In case of a window open event I check if the externalized (child) window is on another screen than its parent and simply switch the externalized window to the parents screen and back again. On a reasonably fast machine you hardly recognize this.
Java Code:
[ol]
[li] [/li]
[li]…[/li]
[li] aeroJava6MultiMonitorWorkaround(myCControl);[/li]
[li]…[/li]
[li] private void aeroJava6MultiMonitorWorkaround(CControl control) {[/li]
[li] DefaultScreenDockWindowFactory screenDockWindowfactory = new DefaultScreenDockWindowFactory() {[/li]
[li] @Override[/li]
[li] public ScreenDockWindow createWindow(ScreenDockStation station, WindowConfiguration configuration) {[/li]
[li] final ScreenDockWindow window = super.createWindow(station, configuration);[/li]
[li] if (window instanceof ScreenDockDialog && isUndecorated()) {[/li]
[li] ((ScreenDockDialog) window).getDialog().addWindowListener(newWindowAdapter() {[/li]
[li] @Override[/li]
[li] public void windowOpened(WindowEvent e) {[/li]
[li] super.windowOpened(e);[/li]
[li] finalWindow childWindow = e.getWindow();[/li]
[li] finalContainer parentWindow = childWindow.getParent();[/li]
[li] Point childCenterLocation = childWindow.getLocationOnScreen();[/li]
[li] childCenterLocation.translate(childWindow.getWidth() / 2, childWindow.getHeight() / 2);[/li]
[li] Point parentCenterLocation = parentWindow.getLocationOnScreen();[/li]
[li] parentCenterLocation.translate(parentWindow.getWidth() / 2, parentWindow.getHeight() / 2);[/li]
[li] GraphicsDevice childDevice = null;[/li]
[li] GraphicsDevice parentDevice = null;[/li]
[li] finalGraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();[/li]
[li] finalGraphicsDevice[] devices = environment.getScreenDevices();[/li]
[li] if (devices.length < 2) { // do we have more than one screen?[/li]
[li] return;[/li]
[li] }[/li]
[li] for (GraphicsDevice device : devices) {[/li]
[li] finalRectangle bounds = device.getDefaultConfiguration().getBounds();[/li]
[li] if (bounds.contains(childCenterLocation)) {[/li]
[li] childDevice = device;[/li]
[li] }[/li]
[li] if (bounds.contains(parentCenterLocation)) {[/li]
[li] parentDevice = device;[/li]
[li] }[/li]
[li] }[/li]
[li] if (childDevice.equals(parentDevice)) { // nothing to do if both windows are on the same screen[/li]
[li] return;[/li]
[li] }[/li]
[li] // toggle screen[/li]
[li] finalPoint childWindowSavedLoaction = childWindow.getLocationOnScreen();[/li]
[li] childWindow.setLocation(parentWindow.getLocationOnScreen());[/li]
[li] childWindow.setLocation(childWindowSavedLoaction);[/li]
[li] }[/li]
[li] [/li]
[li] });[/li]
[li] }[/li]
[li] return window;[/li]
[li] }[/li]
[li] [/li]
[li] };[/li]
[li] control.putProperty(ScreenDockStation.WINDOW_FACTORY, screenDockWindowfactory);[/li]
[li] }[/li]
[/ol]
I allowed myself to format your code And if it is ok with you, I will add it in one of the tutorials (so it will not get lost).
DefaultScreenDockWindowFactory screenDockWindowfactory = new DefaultScreenDockWindowFactory() {
@Override
public ScreenDockWindow createWindow(ScreenDockStation station, WindowConfiguration configuration) {
final ScreenDockWindow window = super.createWindow(station, configuration);
if (window instanceof ScreenDockDialog && isUndecorated()) {
((ScreenDockDialog) window).getDialog().addWindowListener(new WindowAdapter() {
@Override
public void windowOpened(WindowEvent e) {
super.windowOpened(e);
final Window childWindow = e.getWindow();
final Container parentWindow = childWindow.getParent();
Point childCenterLocation = childWindow.getLocationOnScreen();
childCenterLocation.translate(childWindow.getWidth() / 2, childWindow.getHeight() / 2);
Point parentCenterLocation = parentWindow.getLocationOnScreen();
parentCenterLocation.translate(parentWindow.getWidth() / 2, parentWindow.getHeight() / 2);
GraphicsDevice childDevice = null;
GraphicsDevice parentDevice = null;
final GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
final GraphicsDevice[] devices = environment.getScreenDevices();
if (devices.length < 2) { // do we have more than one screen?
return;
}
for (GraphicsDevice device : devices) {
final Rectangle bounds = device.getDefaultConfiguration().getBounds();
if (bounds.contains(childCenterLocation)) {
childDevice = device;
}
if (bounds.contains(parentCenterLocation)) {
parentDevice = device;
}
}
if (childDevice.equals(parentDevice)) { // nothing to do if both windows are on the same screen
return;
}
// toggle screen
final Point childWindowSavedLoaction = childWindow.getLocationOnScreen();
childWindow.setLocation(parentWindow.getLocationOnScreen());
childWindow.setLocation(childWindowSavedLoaction);
}
});
}
return window;
}
};
control.putProperty(ScreenDockStation.WINDOW_FACTORY, screenDockWindowfactory);
}```
Thank you for formatting, yesterday I had trouble answering the „Zufällige Frage“. First my formatting was lost after previewing my answer, second I was not able to post another entry because my answer to the „Zufällige Frage“ was considered wrong all the time.:verzweifel:
But if you can read this, it means it’s working again
P.S.: Of course you can add my code. I would feel honored…