Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 306039

Summary: [Widgets] Windows 7 Jump List items impose "standard" on arguments passed to new instance
Product: [Eclipse Project] Platform Reporter: Lukasz Milewski <lukasz.milewski>
Component: SWTAssignee: Felipe Heidrich <eclipse.felipe>
Status: RESOLVED FIXED QA Contact:
Severity: minor    
Priority: P3 CC: chrriis, eclipse.felipe, markus.kell.r, mik.kersten, remy.suen, shawn.minto, Silenio_Quarti, skovatch
Version: 3.6   
Target Milestone: 3.7 M6   
Hardware: PC   
OS: Windows 7   
Whiteboard:
Attachments:
Description Flags
TaskBar patch
none
TaskBar patch
none
TaskBar patch
none
TaskBar patch none

Description Lukasz Milewski CLA 2010-03-16 12:13:34 EDT
Build Identifier: 3.6M6

Recently implemented Windows 7 Jump List (which is awesome) items impose "standard" on argumants that are passed to new instance with pattern like shown below:

--launcher.openFile /SWTINTERNAL_ID101

It's used solely on Eclipse, but for applications that are not using Eclipse exe launcher are confusing and in some cases troublesome.

I've made quick fix on my local build of SWT, but would like to suggest streamlined version of it, for use in other applications, I can provide patch for it, essentially I've used Widget's data property "argument" that overrides default argument passed to new instance.

Reproducible: Always

Steps to Reproduce:
1. Create Windows 7 Jump List items (TaskItem inside TaskBar)
2. Use newly created item to launch new instance of application
Comment 1 Felipe Heidrich CLA 2010-03-16 12:27:14 EDT
Right now the jump list only works with the eclipse exe launcher.
What are you passing to IShellLink::SetPath ?
Comment 2 Lukasz Milewski CLA 2010-03-16 12:33:30 EDT
Actually it works with launcher I'm using (Jar2Exe Wizard), but I have to create a different way to pass arguments (using Windows native calls - if anyone is interested I can collaborate on that subject in different medium - blog or email) to application and already have created a set of arguments and don't really like the idea of relying of ID to determine which item was selected.

Here's a code I've added to TaskBar class, works for me, maybe worth streamlining.

if (item.getData("argument") != null) {
  text = (String) item.getData("argument");
} else {
  text = Display.LAUNCHER_PREFIX + Display.TASKBAR_EVENT + item.id;
}
Comment 3 Felipe Heidrich CLA 2010-04-13 10:20:25 EDT
That is very cool that you got jump list to work with Jar2Exe.

I don't think I can add the setData() support you suggested (we are passed API freeze and jar2exe is not supported by eclipse.org), that said I'd like to help you.

Silenio, can you think of anything ?

Lukasz, is it possible for you to get Jar2Exe to send opendocuments messages to the swt message window (the same way eclipse.exe does) ?
If you can get Jar2Exe to simulate what eclipse.exe you won't need to hack swt.
Comment 4 Lukasz Milewski CLA 2010-04-13 10:44:18 EDT
Felipe, it is possible to adapt my code to use the "--launcher...." arguments (though probably will not do it and just compile SWT myself with my changes), but my goal with this issue was to raise awareness that this *API* (awesome if I had do reiterate) although made completely in SWT is very Eclipse centric/dependent, which in my case is not an option(my application uses only SWT). 

-=-=-=-

As for Jar2Exe Wizard intergration, actually there's none ;-) All of my code is written purely in Java (almost, I use JNA), What I did is, mind it's Windows only:

First instance
1) Create system-wide Mutex with application name
2) Main window handles WM_COPYDATA message

Second instance
1) Look for Mutex, if found
2) Find previous main window
3) Send WM_COPYDATA to handle of previous main window

Unfortunately I was forced to do that in Java (mutex and sending WM_COPYDATA) because Jar2Exe Wizard is not supporting that and it's not open source. Additionally I found that SWT is missing some of the required structures and methods to accomplish that, so I've used JNA for that.

-=-=-=-

Certainly I can wait for fix to this bug till next release (whether it's major or minor) and in the meantime can provide patch, either with additional (missing from my point of view) elements in OS and SWT classes to not use JNA anymore or without it.
Comment 5 Felipe Heidrich CLA 2010-04-13 12:56:49 EDT
I see,

You re-implemented in Java some of the work that eclipse.exe would do for you.
But you changed the way the arguments are handled. Why did you change it ? Is our design wrong ? Or was it just too hard to duplicate all the work eclipse.exe is doing to make the code work.
Comment 6 Lukasz Milewski CLA 2010-04-13 16:29:37 EDT
Could not argue with that, although when I worked on that particular thing in my application it was November (actually on December I've made breakthrough with memory allocation using JNA) and launcher didn't support that and frankly I was scared with proposal of using HTTP server for passing thru arguments between instances. Hinted, I've looked at launcher java code and native and must say my approach is much inferior to new Eclipse's just because JVM needs to launch to do the WinAPI magic between instances. Unfortunately for me Jar2Exe is not open-sources so I wasn't able to modify native launch.

As for using Eclipse's launcher I'm not using it from two, important for me, reasons, one is I cannot bundle all jars into single exe file (I'm doing application that tries to be as much user friendly as possible and it even means providing one single exe file with couple dll files), inclusion of ini files and tampering that file can potentially render my application useless. Might suspect there are other developers that share my opinion on using Eclipse launcher (de facto is not part of the SWT, but Equinox).
Comment 7 Christopher Deckers CLA 2010-12-15 16:15:54 EST
Felipe,

As Lukasz mentioned, this SWT API does not work for pure SWT applications. This is very annoying considering how great such feature is.

Currently:
- The exe cannot be an alternate exe.
- The arguments passed to that exe cannot be controlled.

A pure SWT application without a specific launcher ("javaw.exe") should be supported in which case the developer would like to pass "-cp <params> <MainClass>" or "-jar MainJar.jar".
Moreover, I have an application that launches another exe which contains the SWT code, so in case of a task item the first exe should be invoked.

I think the easiest would simply be to allow controlling the exe path and the full argument list that is used to create the Windows link. For example:
  item.setData("executable", javaExePath);
  item.setData("arguments", new String[] {"-jar", "MainJar.jar", "--link", "1"});

A null "executable" tells SWT to use the launching exe (90% of the use cases) but can be set if another launcher is to be used. This can then be a dedicated exe meant to handle all jump list links, etc.

Please let me know what you think!
Comment 8 Felipe Heidrich CLA 2010-12-16 11:25:03 EST
(In reply to comment #7)
> I think the easiest would simply be to allow controlling the exe path and the
> full argument list that is used to create the Windows link. For example:
>   item.setData("executable", javaExePath);
>   item.setData("arguments", new String[] {"-jar", "MainJar.jar", "--link",
> "1"});
> A null "executable" tells SWT to use the launching exe (90% of the use cases)
> but can be set if another launcher is to be used. This can then be a dedicated
> exe meant to handle all jump list links, etc.
> Please let me know what you think!

I am okay with that change.
Silenio, do you see any problems ?
Comment 9 Silenio Quarti CLA 2010-12-16 14:26:01 EST
I see no problem other than this is really Windows specific. All these extra datas attached to the item cannot be used in the cocoa implementation. The way cocoa handles tasks when the app is not running is quite different from Windows and for running apps there is nothing special needed.

http://developer.apple.com/library/mac/#documentation/AppKit/Reference/NSDockTilePlugIn_Protocol/Reference/Reference.html

We still have not find any way of implementing this feature on GTK (Gnome or KDE). So we do not know what would be necessary.

Should we at least use a key that shows that the data is specific to Windows:

item.setData("org.eclipse.swt.win32.executable", javaExePath);
Comment 10 Christopher Deckers CLA 2010-12-17 02:08:59 EST
"org.eclipse.swt.win32.executable" is fine. Maybe there should be "taskbar" in the name too:
"org.eclipse.swt.win32.taskbar.executable"
"org.eclipse.swt.win32.taskbar.arguments"

Maybe there should also be "org.eclipse.swt.win32.taskbar.ico" which when defined would prevent the auto-creation of the ".ico" file by specifying a place on disk where that icon can be found.
Comment 11 Felipe Heidrich CLA 2010-12-17 13:59:39 EST
(In reply to comment #10)
> Maybe there should also be "org.eclipse.swt.win32.taskbar.ico" which when
> defined would prevent the auto-creation of the ".ico" file by specifying a
> place on disk where that icon can be found.

The code that creates the default folder where the images are extracted runs before the first menu item is processed. Suppose all menu items have a "org.eclipse.swt.win32.taskbar.ico" set, then that default folder was created for nothing. Besides, who is responsible by creating the "org.eclipse.swt.win32.taskbar.ico" folders (the caller) ?
Comment 12 Christopher Deckers CLA 2010-12-17 14:29:35 EST
> Suppose all menu items have a
> "org.eclipse.swt.win32.taskbar.ico" set, then that default folder was created
> for nothing.

Indeed, but can't the code be adapted to only create the folder when the item is processed if that folder does not exist?

> Besides, who is responsible by creating the
> "org.eclipse.swt.win32.taskbar.ico" folders (the caller) ?

If "org.eclipse.swt.win32.taskbar.ico" is defined, the developer has to make sure the file exists of course. This allows people to ship their application with their ico files (not a converted file) with all the resolutions and design they like.
The developer has many strategies, like shipping their application as a tree containing the ico files, or they have a Windows installer that does the job, or the ico files are auto extracted from the JARs to an appdata subfolder, etc.
Comment 13 Felipe Heidrich CLA 2010-12-17 14:56:30 EST
The code that creates the default folder where the images are extracted runs
before the first menu item is processed. Suppose all menu items have a
"org.eclipse.swt.win32.taskbar.ico" set, then that default folder was created
for nothing. Besides, who is responsible by creating the
"org.eclipse.swt.win32.taskbar.ico" folders (the caller) ?
Comment 14 Felipe Heidrich CLA 2010-12-21 15:55:06 EST
Does anyone have the energy/time to make a patch for us ? That would be much appreciated.
Comment 15 Lukasz Milewski CLA 2010-12-21 16:20:19 EST
I'll be able to provide patch against Jump List.
Comment 16 Felipe Heidrich CLA 2010-12-22 09:22:45 EST
(In reply to comment #15)
> I'll be able to provide patch against Jump List.

Thaank you, please use the names Christopher suggested in comment 10.
Comment 17 Lukasz Milewski CLA 2011-01-17 15:14:22 EST
Created attachment 186943 [details]
TaskBar patch
Comment 18 Lukasz Milewski CLA 2011-01-17 15:23:36 EST
Created attachment 186944 [details]
TaskBar patch

Previous patch required icon to be set on widget even when external image was set.
Comment 19 Christopher Deckers CLA 2011-01-17 15:31:55 EST
(In reply to comment #18)
> Created attachment 186944 [details]
> TaskBar patch

Nice!

> Previous patch required icon to be set on widget even when external image was
> set.

Yes, I was about to mention that and your updated code arrived :)

I have a question though: what if an argument has a space in it? You seem to construct a single string containing all the arguments so shouldn't such case be escaped with double quotes (unless they are already present)?

Something like:
argsBuffer.append((args[i].indexOf(' ') >= 0? '"' + args[i] + '"': args[i]) + " ");
Comment 20 Lukasz Milewski CLA 2011-01-17 15:37:51 EST
> I have a question though: what if an argument has a space in it? You seem to
> construct a single string containing all the arguments so shouldn't such case
> be escaped with double quotes (unless they are already present)?
> 
> Something like:
> argsBuffer.append((args[i].indexOf(' ') >= 0? '"' + args[i] + '"': args[i]) + "
> ");

Interesting point. I'll prepare new patch shortly with the change.
Comment 21 Christopher Deckers CLA 2011-01-17 15:56:52 EST
> Interesting point. I'll prepare new patch shortly with the change.

It would be good to test because I honestly don't know how jumplists behaves with spaces and double quotes :)

Correction to take into account already escaped strings:
argsBuffer.append((args[i].indexOf(' ') >= 0 && args[i].charAt(0) != '"'? '"' + args[i] + '"': args[i]) + " ");
Comment 22 Lukasz Milewski CLA 2011-01-17 16:00:10 EST
(In reply to comment #21)
> > Interesting point. I'll prepare new patch shortly with the change.
> 
> It would be good to test because I honestly don't know how jumplists behaves
> with spaces and double quotes :)

I've planed to do that today and post patch/response soon, not sure if I make it by end of today ;-)
Comment 23 Lukasz Milewski CLA 2011-01-21 08:50:12 EST
Here's an example cod for the changes.


import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TaskItem;

public class TestTaskBarChanges {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		final Display d = new Display();
		final Shell shell = new Shell(d, SWT.SHELL_TRIM);

		shell.setSize(250, 200);
		shell.setText("TaskBar changes");

		TaskItem item = d.getSystemTaskBar().getItem(null);
		Menu menu = new Menu(shell, SWT.NONE);
		MenuItem i = new MenuItem(menu, SWT.NONE);
		i.setText("Program Files");
		i.setData(SWT.TASKBAR_ITEM_EXECUTABLE, "c:\\windows\\explorer.exe");
		i.setData(SWT.TASKBAR_ITEM_ARGUMENTS, new String[]{"c:\\program files\\"});
		i.setData(SWT.TASKBAR_ITEM_ICON, "c:\\Alarm.ico");
		item.setMenu(menu);

		shell.open();
		while (!shell.isDisposed()) {
			if (!d.readAndDispatch())
				d.sleep();
		}
		d.dispose();
	}

}
Comment 24 Lukasz Milewski CLA 2011-01-21 09:01:47 EST
Created attachment 187285 [details]
TaskBar patch
Comment 25 Lukasz Milewski CLA 2011-01-21 09:44:21 EST
Created attachment 187286 [details]
TaskBar patch

Apologize for bugspam. This patch has a new feature I've accidentally found. When you provide exe file, dll file or ico file that has multiple icons you can use new data argument to specify index of ico file to use.


 i.setData(SWT.TASKBAR_ITEM_ICON, "c:\\windows\\explorer.exe");
 i.setData(SWT.TASKBAR_ITEM_ICON_INDEX, 1);

This will pick up second icon from explorer.exe
Comment 26 Christopher Deckers CLA 2011-01-21 16:47:19 EST
> Apologize for bugspam.

No problem for me, I am very pleased with seeing progress.

> you can use new data argument to specify index of ico file to use.

Very good addition! Multiple icons in a single file are quite common indeed.

Felipe, is there any hope to get this included in 3.7?
Comment 27 Felipe Heidrich CLA 2011-01-26 11:06:59 EST
(In reply to comment #26)
> Felipe, is there any hope to get this included in 3.7?

Sure thing, I'll work on it next week (once M5 is out).

The one thing I will have to do is to remove from the patch the constants in SWT. We just can't have constants that are so windows-centric in the SWT class (which is suppose to be platform neutral). What I will do instead is write a F.A.Q entry explainging how to use this support:
- Windows only
- "Back door" for people who have knowledge in win32 to change the default behaviour


Does that make sense to you ?
Comment 28 Felipe Heidrich CLA 2011-01-26 11:09:15 EST
My concern is: Most likely we will never be able to implement these constans anywhere but windows. For that reason, I rather not add them to SWT.
Comment 29 Christopher Deckers CLA 2011-01-26 11:59:41 EST
> The one thing I will have to do is to remove from the patch the constants in
> SWT.
> Does that make sense to you ?

It makes perfect sense. In fact I had not noticed it was public and in the SWT class. Windows users who want to use these new features would have to know the string to use so I agree with you on the FAQ entry.
Comment 30 Felipe Heidrich CLA 2011-02-08 12:34:31 EST
I released the code to HEAD:

Here is my test snippet:


package tests;

import java.util.Properties;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.*;

public class TaskbarPlay3 {
public static void main(String[] args) {
	String name = args.length > 0 ? args[0] : "Noname";
	Display.setAppName("TaskbarPlay"+name);
	Display display = new Display();
	final Shell shell = new Shell(display);
	shell.setText(name);
	
	Properties properties = System.getProperties();
	String executable = properties.get("java.home") + "\\bin\\javaw.exe";
	StringBuffer buffer = new StringBuffer();
	buffer.append("-cp ");
	buffer.append(properties.get("java.class.path"));
	String libraryPath = (String)properties.get("java.library.path");
	if (libraryPath != null) {
		buffer.append(" -Djava.library.path=");
		buffer.append(libraryPath);
	}
	buffer.append(" tests.TaskbarPlay3");
	buffer.append(" TEST");
	
	Menu menu = new Menu(shell, SWT.POP_UP);
	MenuItem menuItem = new MenuItem(menu, SWT.PUSH);
	menuItem.setData("org.eclipse.swt.win32.taskbar.executable", executable);
	menuItem.setData("org.eclipse.swt.win32.taskbar.arguments", buffer.toString());
	menuItem.setData("org.eclipse.swt.win32.taskbar.icon", "C:\\windows\\system32\\shell32.dll");
	menuItem.setData("org.eclipse.swt.win32.taskbar.icon.index", "4");
	menuItem.setText("TEST");
	
	TaskBar bar = display.getSystemTaskBar();
	TaskItem item = bar.getItem(null);
	item.setMenu(menu);
	
	shell.setSize(200, 200);
	shell.open();
    while (!shell.isDisposed()) {
        if (!display.readAndDispatch()) display.sleep();
    }
    display.dispose();
}
}
Comment 31 Christopher Deckers CLA 2011-02-08 12:52:14 EST
Hi Felipe,

> I released the code to HEAD:

Very good! Welcome addition to SWT :)

> Here is my test snippet:

>     String executable = properties.get("java.home") + "\\bin\\javaw.exe";

Is there a way to get the exe path of the running process with SWT API? That way, applications that do use a custom launcher would not have to fiddle to get the full path to their launcher and same for the ones without launchers (though they would have to do what you did with the Java params).

>     String libraryPath = (String)properties.get("java.library.path");

Does "swt.library.path" work? If so, shouldn't you also set it if defined?
Comment 32 Lukasz Milewski CLA 2011-02-08 13:02:38 EST
(In reply to comment #31)
> Is there a way to get the exe path of the running process with SWT API? That
> way, applications that do use a custom launcher would not have to fiddle to get
> the full path to their launcher and same for the ones without launchers (though
> they would have to do what you did with the Java params).

That's the default behavior if you don't specify new data argument
Comment 33 Felipe Heidrich CLA 2011-02-08 13:04:20 EST
Fixed in HEAD
FAQ added http://www.eclipse.org/swt/faq.php#jumplist
Comment 34 Felipe Heidrich CLA 2011-02-08 13:09:28 EST
(In reply to comment #31)
> 
> Does "swt.library.path" work? If so, shouldn't you also set it if defined?

It should work. Note that it is just my snippet, works fine for me (using SWT from the CVS http://www.eclipse.org/swt/cvs.php). I suppose a real application would set the cp differently, if you put the swt.jar in the classpath than you don't need to bother setting the library path (at least not for the swt dlls).