Skip to main content

JList in JXLayer in JScrollPane

19 replies [Last post]
lebesnec
Offline
Joined: 2007-09-13
Points: 0

hello all,

I'm using a JList wrapped inside a JXLayer, and everything is working well. There is just one small problem when I then put the layer in a JScrollpane :

<br />
JXLayer layer = new JXLayer(myList);<br />
gridLayer.setUI(myUI);<br />
JScrollPane scrollPane = new JScrollPane(layer);<br />

the component did not extend itself vertically if it's shorter than the JScrollpane, so a grey rect is visible. The Jlist implementation of the "Scrollable" interface use this in "getScrollableTracksViewportHeight" in order to avoid this effect (I believe some others standard components also do this) :

<br />
public boolean getScrollableTracksViewportHeight() {<br />
        ....<br />
	if (getParent() instanceof JViewport) {<br />
	    return (((JViewport)getParent()).getHeight() > getPreferredSize().height);<br />
	}<br />
	return false;<br />
    }<br />

but since the list is now wrapped in the layer, the parent is no longer the viewport of the scrollpane, and the result is "false" instead of "true".
I have tried to extend "getScrollableTracksViewportHeight" in JXLayer but the class is final ...

is there a solution ?

btw I have used JXLyer in a "demo" app and written a post about it, see http://swing-fx.blogspot.com/2008/09/jgrid-folder-like-view-enhanced-wit...
It is a real pleasure to use this library :)

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
pietblok
Offline
Joined: 2003-07-17
Points: 0

Hi,

That's very strange since JXLayer implements Scrollable. It delegates the call to the LayerUI, if present, that in turn delegates it to the view (if the view implements Scrollable) or provides default values in line with what you expect.

If JXLayer has no LayerUI, it just returns false.

I suspect that your JXLayer has no LayerUI set. Your code:

[code]
JXLayer layer = new JXLayer(myList);
gridLayer.setUI(myUI);
JScrollPane scrollPane = new JScrollPane(layer);
[/code]
You set the myUI on gridLayer, not on layer. Or is this a typo?

Hope this helps

Piet

lebesnec
Offline
Joined: 2007-09-13
Points: 0

Hi,

yes, that is a typo, the line should be : layer.setUI(myUI);
(and "myUI" is not null :) )

You're right, JXLayer delegate the call to the view, but the problem (I think) is that the result return by the view is wrong because it is in the layer. See the implementation of Scrollable.getScrollableTracksViewportHeight in JList :

[code]
public boolean getScrollableTracksViewportHeight() {
....
if (getParent() instanceof JViewport) {
return (((JViewport)getParent()).getHeight() > getPreferredSize().height);
}
return false;
}
[/code]

thx for your help !

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

Hi,

the JXLayer code:
[code]
public boolean getScrollableTracksViewportHeight() {
if (getUI() != null) {
return getUI().getScrollableTracksViewportHeight(this);
}
if (getParent() instanceof JViewport) {
return ((getParent()).getHeight() > getPreferredSize().height);
}
return false;
}
[/code]

and the LayerUI code:
[code]
public boolean getScrollableTracksViewportHeight(JXLayer l) {
if (l.getView() instanceof Scrollable) {
return ((Scrollable)l.getView()).getScrollableTracksViewportHeight();
}
if (l.getParent() instanceof JViewport) {
return (((JViewport)l.getParent()).getHeight() > l.getPreferredSize().height);
}
return false;
}
[/code]

This seems perfectly correct to me. I didn't test it with a JList, but with other component types that implement Scrollable and that worked.

Now the JList implementation:
[code]
public boolean getScrollableTracksViewportHeight() {
if (getLayoutOrientation() == VERTICAL_WRAP &&
getVisibleRowCount() <= 0) {
return true;
}
if (getParent() instanceof JViewport) {
return (((JViewport)getParent()).getHeight() > getPreferredSize().height);
}
return false;
}
[/code]

Yes, now I see what you mean. And, in general, I ask myself: why do implementations require that the parent is a JViewport in order to return a usable result? Why not ask for the parents height (or width) even if that parent is not a JViewport? Because, ultimately, the method is, directly or indirectly, invoked from a component type like JViewport.

Perhaps because, when there is another component between the target and the JViewport, the parent size has not yet been established? In that case one would expect that the chain upwards would be examined, to find the JViewport that initiated the invocation.

I think I leave it from here to the experts.

Piet

PS: Thinking about it further, I guess that the Scrollable interface should have been designed such that the JViewport that is invoking these methods, passes itself as a parameter. Than there would be no ambiguity. But I understand that that can't be changed anymore. :(

Added a PS

Message was edited by: pietblok

alexfromsun
Offline
Joined: 2005-09-05
Points: 0

Hello Christophe

Your blog is really cool, great job!
I am gonna mention it in the jxlayer project page, if you don't mind

Piet's evaluation for this scrolling problem is perfect, as usual

In my opinion the design of Scrollable interface and it's implementations
could be done better, I see no reasons why this unfortunate code

[code]
if (getParent() instanceof JViewport) {
return (((JViewport)getParent()).getWidth() > getPreferredSize().width);
}
[/code]

can't be changed like this:

[code]
return getParent().getWidth() > getPreferredSize().width;
[/code]

Anyway, if a component checks instanceof getParent(), the only workaround is to override
those methods in your LayerUI, here is the example how to implement the JList's behavior:

[code]
JXLayer layer = new JXLayer(list);

layer.setUI(new AbstractLayerUI() {
public boolean getScrollableTracksViewportHeight(JXLayer layer) {
JList list = layer.getView();
if (list.getLayoutOrientation() == JList.VERTICAL_WRAP &&
list.getVisibleRowCount() <= 0) {
return true;
}
if (layer.getParent() instanceof JViewport) {
return layer.getParent().getHeight() > list.getPreferredSize().height;
}
return false;
}

public boolean getScrollableTracksViewportWidth(JXLayer layer) {
JList list = layer.getView();
if (list.getLayoutOrientation() == JList.HORIZONTAL_WRAP &&
list.getVisibleRowCount() <= 0) {
return true;
}
if (layer.getParent() instanceof JViewport) {
return layer.getParent().getWidth() > list.getPreferredSize().width;
}
return false;
}
});

frame.add(new JScrollPane(layer));
[/code]

Thanks
alexp

lebesnec
Offline
Joined: 2007-09-13
Points: 0

Hi Alex,

so the solution was to override the layer UI (and not the layer itself) ! Thanks for your help :) (and also Piet!)

Overridden JList (to remove the test on the parent) should also probably work.

PS : I'll be happy to be in the jxlayer project page !

alexfromsun
Offline
Joined: 2005-09-05
Points: 0

Hello Christophe

You are welcome

By the way, could you please make a webstart link to you demo and post it in the blog?
It would be really helpful

Thanks
alexp

lebesnec
Offline
Joined: 2007-09-13
Points: 0

This is a good idea.

I have never done a webstart before, so it may took some time :)

thanks,
Christophe

alexfromsun
Offline
Joined: 2005-09-05
Points: 0

Hello Christophe

Webstart link is very useful, because it allows to run the demo straight away
and it is not difficult to set it up, you basically need to make a jar file for your demo
and write a simple jnlp file, here is SpotLightDemo.jnlp for one of my demos:

[code]



SpotLightDemo in action
Alexander Potochkin

SpotLightDemo









[/code]

In tag put only necessary jars (delete jxlayer-demo.jar)

Then you put your jar and jnlp files to the webserver and make a link on your page:

[code]

src="http://java.sun.com/products/jfc/tsc/sightings/images/webstart.small.jpg">

[/code]

That's it

alexp

lebesnec
Offline
Joined: 2007-09-13
Points: 0

weird :(

I have written the JNLP file :
http://sites.google.com/site/christophe/files-1/ilist.jnlp

but when I launch it, I got an error saying the field is missing ?!? the xml seems correct to me, with a tag at the root ...

also, when I open the file with IE or Opera, webstart is not launched, it only display the content of the jnlp file. Only firefox open it correctly :(

any ideas ?

alexfromsun
Offline
Joined: 2005-09-05
Points: 0

Hello Christophe

When I click on the link you give in the previous message,
instead of direct download of ilist.jnlp, the "Download Attachment" page is open
this seems to be the reason of this problem

Click jnlp file on the "Download Attachment" page and when webstart failed to launch it
press the "Details" button - you will see what webstart actually got from the server,
I can see some html there which has nothing to do with the actual jnlp file

So this problem seems to be specific to the way Google give the access to the file,
I would ask Google support is it possible to disable redirection or try another server

alexp

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

Hi,

Some time ago I encountered a similar problem (not with a google server) that boiled down to the server not sending the correct mime type for a jnlp file in the answer. The mime type should be [b]application/x-java-jnlp-file[/b]

Not sending the correct mime type results in displaying the jnlp file as xml.

Piet

lebesnec
Offline
Joined: 2007-09-13
Points: 0

hello again,

sorry for the delay (and for being off topic here).
I have moved the jnlp and all the jar to another server (http://lebesnec.free.fr/ilist.jnlp).
It work with opera and IE now, and it look like the jars are downloaded. But there is still an error at startup :

[code]
JNLPException[category: Erreur dans le fichier de lancement : Exception: null : LaunchDesc:


Sample swing application
Christophe Le Besnerais

Sample swing application













]
at com.sun.javaws.LaunchDownload.getMainClassName(Unknown Source)
at com.sun.javaws.Launcher.doLaunchApp(Unknown Source)
at com.sun.javaws.Launcher.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
[/code]

It look like the main class is not found in ilist.jar (http://lebesnec.free.fr/ilist.jar). I have tried to set the "main" attribute to true, without success ...

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

Hi Christophe,

I remember having a similar problem some time ago. I could solve it by moving the jar that contains the main class to be the first jar mentioned in the jnlp file. In your case that is the ilist.jar. That should be the first, not the last jar in your jnlp.

Just clicking on your link didn't work, because for some reason the closing parenthesis was also taken. Perhaps you should ensure that a link is always preceded and ended by white space?

Piet

lebesnec
Offline
Joined: 2007-09-13
Points: 0

the parenthesis is a typo, the link is :
http://lebesnec.free.fr/ilist.jnlp

You were right, the jar with the main class have to be in first position.
I have now some problem with signed jars :
"[i]JAR resources in JNLP file are not signed by same certificate[/i]"

I should be able to resolve this error with this:
http://weblogs.java.net/blog/kirillcool/archive/2005/05/signing_jars_fo....

thx for your help !!

alexfromsun
Offline
Joined: 2005-09-05
Points: 0

Hello Christophe

I run ilist.jnlp and got the same "Could not fine main class",

let me know if moving ilist.jar to the first position fix this problem

"JAR resources in JNLP file are not signed by same certificate"

Hm... it is surprising, you don't ask any special permissions in the jnlp file,

I thought all signatures were unimportant in this case

Anyway, you almost fix all problems with jnlp file
keep up the good job!

alexp

lebesnec
Offline
Joined: 2007-09-13
Points: 0

[i]"I run ilist.jnlp and got the same "Could not fine main class""[/i]

This is because I forget to re-upload the jnlp ...
You should have the same error now "[i]JAR resources in JNLP file are not signed by same certificate[/i]".

May be it's because I have signed my jar ...

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

Hi Christophe,

Just tried to run your demo, but this time got a SecurityException, somwhere in your main method where apparently you want some system property.

If that operation is essential for your demo, you have to sign your jars and ask all-permissions.

The simplest way to do this is sign them all with your certificate, including those that you do not really own.

The better way to do this is sign your jars only and only name those in your jnlp file. Then, add an extension reference to the resources section with a separate jnlp file for the rest of the jars.

As a reference here my two jnlp files (for my web start demo):

WrapTestNew.jnlp
[code]



JXLayer wrap test
Piet Blok

JXLayer demonstration
JXLayer demo.











[/code]

and

jxlayer.jnlp
[code]



JXlayer
Alexander Potochkin









[/code]

I don't think the attribute java-vm-args="-esa -Xnoclassgc" is very relevant. It is just there because I copied and pasted my jnlp files from samples that I found.

Good luck!

Piet

lebesnec
Offline
Joined: 2007-09-13
Points: 0

I have move the others jar in separate jnlp. The application ask for the permission of the user now, but it seem that JNA (which I used for the translucency) create some security problem :

java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.sun.javaws.Launcher.executeApplication(Unknown Source)
at com.sun.javaws.Launcher.executeMainClass(Unknown Source)
at com.sun.javaws.Launcher.continueLaunch(Unknown Source)
at com.sun.javaws.Launcher.handleApplicationDesc(Unknown Source)
at com.sun.javaws.Launcher.handleLaunchFile(Unknown Source)
at com.sun.javaws.Launcher.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.ExceptionInInitializerError
at com.sun.jna.examples.win32.User32.(User32.java:30)
at com.sun.jna.examples.WindowUtils$W32WindowUtils$2.run(WindowUtils.java:753)
at com.sun.jna.examples.WindowUtils$NativeWindowUtils$2.hierarchyChanged(WindowUtils.java:314)
at java.awt.Component.processHierarchyEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Window.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.Component.addNotify(Unknown Source)
at java.awt.Container.addNotify(Unknown Source)
at java.awt.Window.addNotify(Unknown Source)
at java.awt.Frame.addNotify(Unknown Source)
at java.awt.Window.show(Unknown Source)
at java.awt.Component.show(Unknown Source)
at java.awt.Component.setVisible(Unknown Source)
at ilist.MainPlugin.init(MainPlugin.java:67)
at ilist.MainPlugin.main(MainPlugin.java:76)
... 11 more
Caused by: [b]java.security.AccessControlException: access denied (java.util.PropertyPermission w32.ascii read)[/b]
at java.security.AccessControlContext.checkPermission(Unknown Source)
at java.security.AccessController.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkPropertyAccess(Unknown Source)
at java.lang.System.getProperty(Unknown Source)
at java.lang.Boolean.getBoolean(Unknown Source)
at com.sun.jna.examples.win32.W32API.(W32API.java:47)
... 31 more

I have tried to do the same as the jnlp in this page : http://rabbit-hole.blogspot.com/2007/06/improved-window-shaping.html :

- download bunch of jar from https://jna.dev.java.net/servlets/ProjectDocumentList?folderID=7408&expa...
- sign all of them
- create this jnlp (and link it in the main jnlp) : http://lebesnec.free.fr/jna.jnlp

lebesnec
Offline
Joined: 2007-09-13
Points: 0

hello again,

for the moment I have made a "special" version without the translucency : http://lebesnec.free.fr/ilist.jnlp

which should work. There is still some problems : the toolbar is opaque and the "plugins" button does not work. It's strange, because these problems does not appear when I run directly the app ...