views:

86

answers:

2

I have an AWT canvas which I cannot convert to a Swing component (it comes from VTK). I wish to display a few of these canvases inside of a JSplitPane. I've read about mixing heavy and light weight components in Java and know that it's a pain in the butt, but I don't have a choice. If I wrap the AWT canvas inside of a JPanel and then put that on the split pane the split pane doesn't function at all. However, if I put the AWT canvas inside of a JPanel and then that inside of a JScrollPane and then those scroll panes on the JSplitPane the split pane does function, but the AWT canvas components don't resize properly. I'm lost about how to get the AWT canvas components to resize properly when the JSplitPane's divider is moved. I can catch the divider moving operation and operate on the AWT canvases at that time, but I don't know what to do. I've tried calling invalidate() then validate() then repaint(), but that didn't work.

Any ideas?

Here's a example of the problem

import javax.swing.*;
import java.awt.*;

public class SwingAWTError {
    public static void main(String[] args) {
        Canvas leftCanvas = new Canvas();
        Canvas rightCanvas = new Canvas();
        leftCanvas.setBackground(Color.RED);
        rightCanvas.setBackground(Color.BLUE);

        JPanel leftPanel = new JPanel();
        JPanel rightPanel = new JPanel();
        leftPanel.setLayout(new BorderLayout());
        rightPanel.setLayout(new BorderLayout());
        leftPanel.add(leftCanvas, BorderLayout.CENTER);
        rightPanel.add(rightCanvas, BorderLayout.CENTER);

        JScrollPane leftScroll = new JScrollPane();
        JScrollPane rightScroll = new JScrollPane();
        leftScroll.getViewport().add(leftPanel);
        rightScroll.getViewport().add(rightPanel);

        JSplitPane split = new JSplitPane();
        split.setLeftComponent(leftScroll);
        split.setRightComponent(rightScroll);
        split.setDividerLocation(400);

        JFrame frame = new JFrame();
        frame.getContentPane().setLayout(new BorderLayout());
        frame.getContentPane().add(split, BorderLayout.CENTER);
        frame.setSize(800, 800);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}
+1  A: 

You are kind of out of luck here. There's a pretty good article on this on the sun/oracle website: http://java.sun.com/products/jfc/tsc/articles/mixing/

Essentially it boils down to this guideline (taken from that link, under the z-ordering heading):

Do not mix lightweight (Swing) and heavyweight (AWT) components within a container where the lightweight component is expected to overlap the heavyweight one.

Edit: I kept browsing that site and came across another link, and it would appear that the situation has improved slightly: http://java.sun.com/developer/technicalArticles/GUI/mixing_components/ But I think your case is one of those listed at the bottom in the limitations section:

Limitations

A few situations are not supported:

* Non-opaque lightweight components that have translucent

pixels (0 < alpha < 255) are not supported. If a partially translucent lightweight component overlaps a heavyweight component, the heavyweight component will not show through.

* Embedded heavyweight components must belong to the process that

created the frame or applet. The heavyweight component must have a valid peer within the main process of the application (or applet).

* Advanced Swing key events, such as those events maintained in an

InputMap, might not work correctly where lightweight and heavyweight components are being mixed. There are no known workarounds.

Rulmeq
Well obviously this is not what I wanted to hear. Which one of the three situations you listed does mine fall under? Also, is there any talk of fixing these issues in future Java releases?
Jon
Sorry, I'd only be guessing. I haven't had to use Swing for years, which is why I didn't even know they had a fix for some of the cases.
Rulmeq
+2  A: 

It is a dirty way but this will solve it:

When you call pack() without resizing the window, not much happens. So, when you first resize the window and then call pack(), your components are correcly drawn. This means you can put this dirty method in your divider moved listener method:

frame.setPreferredSize(frame.getSize()); // store the current size to restore it after packing.
frame.setSize(frame.getWidth() + 1, frame.getHeight()); // resize it!!
frame.pack();

I don't know what it is exactly but it is a strange behavour in Java...
Hope this helps until you've found a better solution...

Martijn Courteaux
This is dirty, but it works for me. Thanks.
Jon
@Jon: You're welcome.
Martijn Courteaux