Skip to main content

JXLayer, PBTransform, and JInternalFrame

6 replies [Last post]
oadisplay
Offline
Joined: 2009-09-10
Points: 0

Hello,

First I want to say how wonderful jxlayer and the PBTransform example are. You guys are really doing a great job!

And I also want to thank davidcw for posting "Problem using Piet Block's JXLayer Transform Demo" because I was having that problem, too. I also downloaded the updated PBTransform.jar and the issue went away... will the src zip also be released for that?

Anyway, to the issue I was having. Utilizing the jxlayer.jar and PBTransform.jar, I wanted to create the zooming effect on the entire GUI of this little program I've been messing around with. So far, the zooming itself works great. The problem comes when I have JInternalFrames. When I add the JInternalFrame via the EventQueue and a Runnable, only one button (seems to be the closest to coordinate 0,0) receives action events (e.g. clicking on it). All buttons receive mouse events, though. If I add directly without the EventQueue and Runnable, it seems ok - but I definitely do not want to do that in practice.

The basic format of the GUI itself is a JDesktopPane that is layered using JXLayer and TransformUI; the layer is added to a JFrame. The JInternalFrames are then added to the JDesktopPane.
Below are samples of classes that should replicate the issue, Thanks!:
======== Driver.java ========
import javax.swing.SwingUtilities;

public class Driver {

public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run() {
createAndShowGUI();
}
});
}

private static void createAndShowGUI() {
MainFrame test = new MainFrame();
}

}

======= MainFrame.java =======
import java.awt.Color;
import java.awt.Dimension;

import javax.swing.JComponent;
import javax.swing.JFrame;
import org.jdesktop.jxlayer.JXLayer;
import org.pbjar.jxlayer.plaf.ext.TransformUI;
import org.pbjar.jxlayer.plaf.ext.transform.DefaultTransformModel;

public class MainFrame
extends JFrame
{

private static final long serialVersionUID = 1L;
private static Dimension SCREEN_SIZE = new Dimension(1280, 1024);

public MainFrame()
{
super();
setSize(SCREEN_SIZE);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setBackground(Color.DARK_GRAY);

JXLayer<?> layer = null;
DefaultTransformModel model = new DefaultTransformModel();
model.setScale(1.0);
model.setPreserveAspectRatio(false);
TransformUI ui = new TransformUI(model);
layer = new JXLayer(new MyLayeredPane(SCREEN_SIZE), ui);
add(layer);

setVisible(true);
}

}
======== MyLayeredPane.java =========
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;

import javax.swing.JDesktopPane;
import javax.swing.JPanel;

public class MyLayeredPane extends JDesktopPane {

private static final long serialVersionUID = 1L;
public static int BOTTOM_LEVEL = 0;
public static int POPUP_LEVEL = 100;

public MyLayeredPane(Dimension size)
{
super();
setLayout(null);
setLocation(0, 0);
setSize(size);
setPreferredSize(size);
setBackground(Color.white);

initialize();
}

private void initialize()
{
JPanel soloPanel = new JPanel();
soloPanel.setSize(900, 800);
soloPanel.setLocation(100, 100);
soloPanel.setBackground(Color.red);
soloPanel.setLayout(null);
soloPanel.setOpaque(true);

add(soloPanel, BOTTOM_LEVEL);

// THIS IS IT!!!!!!!!!!!!!!!!!!!!!!!!! WHY!!!
EventQueue.invokeLater(new Runnable(){public void run(){
MyInternalFrame frame = new MyInternalFrame();
add(frame, POPUP_LEVEL);
frame.toFront();
}});

}

}
========= MyInternalFrame.java =============
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JPanel;

public class MyInternalFrame extends JInternalFrame implements ActionListener {

private static final long serialVersionUID = 1L;

private static Dimension SIZE = new Dimension(500, 500);

public MyInternalFrame() {
super("Test", false, false, false, false);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setOpaque(false);
setVisible(true);
setBackground(Color.yellow);

initialize();
}

private void initialize() {
add(getContent());
pack();
}

private JPanel getContent() {
JPanel soloPanel = new JPanel();
soloPanel.setSize(SIZE);
soloPanel.setPreferredSize(SIZE);
soloPanel.setBackground(Color.blue);
soloPanel.setLayout(null);
soloPanel.setVisible(true);

JButton label1 = new JButton("Label 1");
label1.setBackground(Color.white);
label1.setSize(200, 50);
label1.setLocation(25, 449);
label1.setActionCommand("1");
label1.addActionListener(this);

JButton label2 = new JButton("Label 2");
label2.setBackground(Color.white);
label2.setSize(200, 50);
label2.setLocation(250, 449);
label2.setActionCommand("2");
label2.addActionListener(this);

JButton label4 = new JButton("Label 4");
label4.setBackground(Color.white);
label4.setSize(200, 50);
label4.setLocation(250, 125);
label4.setActionCommand("4");
label4.addActionListener(this);
soloPanel.add(label4);

JPanel p1 = new JPanel();
p1.setSize(225, 100);
p1.setPreferredSize(new Dimension(225, 100));
p1.setLocation(0, 0);
p1.setBackground(Color.yellow);
p1.setLayout(null);

JPanel p = new JPanel();
p.setSize(250, 250);
p.setPreferredSize(new Dimension(250, 250));
p.setLocation(50, 50);
p.setBackground(Color.green);
p.setLayout(null);
JButton label3 = new JButton("Label 3");
label3.setBackground(Color.white);
label3.setSize(200, 50);
label3.setLocation(0, 0);
label3.setActionCommand("3");
label3.addActionListener(this);
// Add button 3 to mini panel
p1.add(label3);
// add mini panel to other panel panel
p.add(p1);
// add other panel to content
soloPanel.add(p);
// add button 1 to content
soloPanel.add(label1);
// add button 2 to content
soloPanel.add(label2);
return soloPanel;
}

public void actionPerformed(ActionEvent arg0) {
System.out.println("********************* " + arg0 + " ****************");
}

}

Thank you!

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
oadisplay
Offline
Joined: 2009-09-10
Points: 0

More information that I just discovered.

Regardless of how many JInternalFrames I add via the Event Queue and Runnable, if I add one or more JInternalFrames directly (without the event queue) all the buttons seems to work fine. The directly added one can be completely empty, can be removed/disposed immediately, and can be added before, in between, or after the EventQueue ones.

pietblok
Offline
Joined: 2003-07-17
Points: 0

Hi,

Yes, I can see the described problem. And yes, directly adding some dummy JInternalFrame directly seems to work around that problem. I don't know yet what is causing it. But is must have something to do with the TransformUI, because when I instantiate the JXLayer without a LayerUI, or with an empty AbstractLayerUI there is no problem at all.

So something seems to be wrong with the TransformUI for this particular use case.

I need some time to investigate. When (if) I find a solution I will report it. Meanwhile you may use the work around that you found.

By the way, the demo page has been refreshed completely with the new PBTransform.jar, and includes the refreshed sources as well, (Though I'm not aware of any changes that I may have made). I'm still wondering what caused that no method found error. Bizarre indeed.

Piet

oadisplay
Offline
Joined: 2009-09-10
Points: 0

Thank you for looking.

If memory serves me right, when I ran into the no method found error I believe it came from the signature of the "getGlassPane" method.
It was looking for "getGlassPane()" but was finding "getGlassPane(Component)" perhaps? I don't remember where that method comes from...
It may be just outdated versions floating around? I can't remember.

pietblok
Offline
Joined: 2003-07-17
Points: 0

Hi,

I did some further investigation on the JDesktopPane and JInternalFrame problem. I don't have a solution yet, but now I know that the event queue has nothing to do with it.

The problem arises when the first JInternalFrame is added to a JInternalPane when that desktop pane is already added to a JXLayer AND the TransformUI has been set.

If a internal frame is added before the TransformUI is set, there seem to be no problem at all.

I created some code to better test this behaviour. Please see below.

I checked the redispatching of mouse events and that seemed OK. Also, when moving the mouse over the buttons, they react to those movements by changing their border. So I'm completely in the dark why pressing does not work.

When resizing the frame, I discovered that sometimes button presses do work. Sometimes on one, sometimes on two and sometimes all four four buttons. Very weird. I suspect it has something to do with very specific behavior of the JDesktopPane and its JInternalFrames. When adding some other component, like a JPanel, to the JDesktopPane, everything seems to work as expected.

If anyone has a clue, please let me know.

Meanwhile, when working on it I discovered two other problems that I solved
(the demo web site has been refreshed again):

1) When dragging a internal frame around the desktop and when the mouse leaves the desktop, the internal frame jumps to the upper left corner. This is because of some check that I made in the MouseEventUI. I don't exactly remember why I put in that check, but when I removed it, no disasters happened. Hopefully the original problem has been solved by some previous change.

2) When setting the internal frame to resizable, the resize cursor remains on the blank area of the desktop. I solved this by only setting the cursor on the glass pane when a mouse event is not an entered or exit event. For entered or exit events, the cursor is now set to null.

Piet

[code]
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import org.jdesktop.jxlayer.JXLayer;
import org.pbjar.jxlayer.plaf.ext.TransformUI;
import org.pbjar.jxlayer.plaf.ext.transform.DefaultTransformModel;

public class TestDesktop {

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {

@Override
public void run() {
new TestDesktop().createGUI();
}
});
}

JFrame frame;

private JPanel mainPanel;

private JXLayer layer;

private JDesktopPane desktop;

private void createGUI() {
frame = new JFrame();
mainPanel = new JPanel(new BorderLayout());
frame.add(mainPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
desktop = new JDesktopPane();

DefaultTransformModel model = new DefaultTransformModel();
TransformUI ui = new TransformUI(model);
model.setPreserveAspectRatio(false);
layer = new JXLayer();

// This solves the problem
// createInternalFrame(desktop, "before all", 20, 20);

layer.setView(desktop);

// This solves the problem as well
// createInternalFrame(desktop, "before ui", 140, 50);

layer.setUI(ui);

createInternalFrame(desktop, "after ui", 260, 80);

mainPanel.add(layer);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

private void createInternalFrame(JDesktopPane desktopPane, String name,
int x, int y) {
desktopPane.setPreferredSize(new Dimension(400, 300));
JPanel content = new JPanel(new GridLayout(0, 1));

/*
* When internal instantiated as a JPanel
*/
// JPanel internal = new JPanel();
// content.add(new JLabel(name));

/*
* When internal instantiated as a JInternalFrame
*/
JInternalFrame internal = new JInternalFrame(name, true, false, false,
false);

internal.add(content);
for (int index = 0; index < 4; index++) {
JButton button = new JButton(new AbstractAction(" Button " + index
+ " ") {

private static final long serialVersionUID = 1L;

@Override
public void actionPerformed(ActionEvent e) {
frame.setTitle("Action on " + this.getValue(Action.NAME));
System.out.println("Action on "
+ this.getValue(Action.NAME));
}
});
content.add(button);
}
desktopPane.add(internal);

/*
* When internal is a JPanel
*/
// internal.setSize(internal.getPreferredSize());

/*
* When internal is a JInternalFrame
*/
internal.pack();
internal.toFront();

internal.setLocation(x, y);
internal.setVisible(true);

}

}

[/code]

pietblok
Offline
Joined: 2003-07-17
Points: 0

Hi,

Meanwhile I could isolate the problem further. It has something to do with internal JInternalFrame code. Apparently, it contains some static initialization code that MUST be executed before the TransformUI is set to a JXLayer, containing a JDesktopPane. If not, it seems that the dispatchEvent method on child components is not working correctly.

When merely intantiating a JInternalFrame before the setUI method, everything seems to work OK.

So, as a work around, I added a static intializer in the MouseEventUI (the superclass of TransformUI, responsible for redispatching of MouseEvents) that does just that.

If anyone can explain why this works, please don't hesitate to report.

The demo project page: http://www.pbjar.org/blogs/jxlayer/jxlayer40/index.html
has been refreshed.

Please test the new PBTransform.jar.

Piet

oadisplay
Offline
Joined: 2009-09-10
Points: 0

This is excellent news! Especially the previous two fixes you mentioned. Now that you had mentioned them, I think I may have seen that behavior but dismissed it as my own jdesktop problem that I wasn't concerned with at the time.

I apologize for my late response - I've been out of town the past few days. I will certainly try this out within the next 24 hours or so. Thanks!