toggle me

If you want to add toggle menu options ie. menu optoin with check mark, here is how can you do it:

Commands can have states associated with it. A state id known by its id and it can have any value, which is stored in a subclass of org.eclipse.core.commands.State. To know whether a command is checked or not, the Command Framework looks for a state with the id 'org.eclipse.ui.commands.toggleState'. Lets specify the default checked state of Bold to true and Italic to false:


< command
defaultHandler="com.eclipse_tips.commandstate.BoldHandler"
id="com.eclipse-tips.commandState.boldCommand"
name="Bold">
< state
class="org.eclipse.ui.handlers.RegistryToggleState:true"
id="org.eclipse.ui.commands.toggleState">
< /state>
< /command>
< command
defaultHandler="com.eclipse_tips.commandstate.ItalicHandler"
id="com.eclipse-tips.commandState.italicCommand"
name="Italic">
< state
class="org.eclipse.ui.handlers.RegistryToggleState:false"
id="org.eclipse.ui.commands.toggleState">
< /state>
< /command>


The Framework expects a Boolean value from the state. It doesn't care about which class that holds the state. So why should we use the RegistryToggleState instead of our own state class? Two reasons:

It implements IExecutableExtension. So you can specify the default values in the plugin.xml as I've done above.
It extends PersistentState, so it can remember the value between Eclipse sessions.
So if the user had checked/unchecked the menu, restarts Eclipse, he will get the same state as he left before - all this, you get without even your plugin being loaded.
So how should the handler work for this command?

public Object execute(ExecutionEvent event) throws ExecutionException
{
Command command = event.getCommand();
boolean oldValue = HandlerUtil.toggleCommandState(command);
// use the old value and perform the operation
return null;
}


Basically, its the Handler's responsibility to update the Command's state. The HandlerUtil has a convenient method which toggles the state and gives you the old value of the state. You can use the value to perform the operation.

To know more about Radio style menu optoins :

http://blog.eclipse-tips.com/2009/03/commands-part-6-toggle-radio-menu.html

Eclipse Resources


Eclipse Platform Technical OverView, JDT Tooling Support and features

Eclipse : Change Editor Icon

if you want to set default icon for files opened in your Editor, you can do so when associating editor with file types in plugin.xml as below:

//MyEditor.plugin.xml
< extension point="org.eclipse.ui.editors" >
< editor
class="com.smaple.MyEditor"
contributorClass="com.sample.MyEditor.ActionContributor"
default="true" extensions="txt"
icon="icons/myTxtIcon.gif"
id="com.sample.MyEditor.txt"
name="%editor.txt.label">
< / editor >
< / extension >


Now if you want to change this icon at runtime (e.g. you may want to show different icon for files outside workspace), you can do so in your editor class as below:


public class MyEditor extends TextEditor{

private static final Map IMAGE_DESCRIPTOR_MAP = new HashMap();
public static final Image IMAGE_EXTERNAL_ICON;
static{
IMAGE_EXTERNAL_ICON = getImageDescriptor("icons/myIcon.gif").createImage();
}
protected void doSetInput(IEditorInput input) throws CoreException {
super.doSetInput(input);
IResource res = (IResource) getEditorInput().getAdapter(IResource.class);
if(res == null){
//set the icon for external files
Image image = getExternalImage();
if(image != null){
// call the setTitleImage defined in WorkbenchPath
setTitleImage(image);
}
}
}

private Image getExternalImage() {
IPath path = new Path(getEditorInput().getName());

if (isTxtFile(path)){
return IMAGE_EXTERNAL_ICON;
}
return null;
}

public static ImageDescriptor getImageDescriptor(String name) {
if (IMAGE_DESCRIPTOR_MAP.containsKey(name)) {
return (ImageDescriptor)IMAGE_DESCRIPTOR_MAP.get(name);
} else {
try {
URL url = MyEditorPlugin.getDefault().getBundle().getEntry(name);
ImageDescriptor d = ImageDescriptor.createFromURL(url);
IMAGE_DESCRIPTOR_MAP.put(name, d);
return d;
} catch (Exception e) { // should not happen
return ImageDescriptor.getMissingImageDescriptor();
}
}
}
}

Eclipse : Using Standard Source Lookup Tab

If you are writing your own debugger in eclipse, you may want to allow users to provide source lookup paths. Eclipse framework provides a standard Source Tab ("org.eclipse.debug.ui.sourcelookup.SourceLookupTab") that you can add to your debug launch configration tab groups.
By default source tab include java classpath entries and allows users to add various java source paths such as Archive file etc. In your launch configuaration you may want to limit the kind of entries users can add to source tab and control default entries displayed in source tab.

To ensure that Source Tab allows users to add only specific types of entries, in your "source lookup director" class override following method.

public boolean supportsSourceContainerType(ISourceContainerType type);

Here is a sample implmenentaion of source lookup director that allows users to add only workspace projects , workspace folders and external directories.


public class MySourceLookupDirector extends AbstractSourceLookupDirector {

@Override
public boolean supportsSourceContainerType(ISourceContainerType type) {
if(type.getId().equals(ProjectSourceContainer.TYPE_ID))
return true;
if(type.getId().equals(FolderSourceContainer.TYPE_ID))
return true;
if(type.getId().equals(DirectorySourceContainer.TYPE_ID))
return true;
return false;
}
}


To control default entries in Source Tab, make sure that you provide implementaion for ISourcePathComputerDelegate interface. Here is a sample implementation that displays all workspace project as default entries in source Tab:


public class MySourcePathComputerDelegate implements
ISourcePathComputerDelegate {

public ISourceContainer[] computeSourceContainers(ILaunchConfiguration configuration,
IProgressMonitor monitor)throws CoreException
{
return createSourceContainers(configuration);
}

protected ISourceContainer[] createSourceContainers(ILaunchConfiguration configuration)
throws CoreException {

List containers = new ArrayList();
IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();

for (IProject prj : projects) {
if(prj.isOpen())
containers.add(new ProjectSourceContainer(prj, true));
}
return (ISourceContainer[]) containers.toArray(new ISourceContainer[containers.size()]);
}
}


To perform the source lookup, implement ISourceLookupParticipant or extend from AbstractSourceLookupParticipant as below:


//used to when source is not found in specified source lookup paths
public class SampleSourceNotFound {

private Object artifact;
private String message;

public SampleSourceNotFound(Object artifact) {
this.artifact = artifact;
}

public SampleSourceNotFound(Object artifact,String message) {
this.artifact = artifact;
this.message = message;
}

public Object getArtifact() {
return artifact;
}

public String getMessage(){
return message;
}
}

public class MySourceLookupParticipant extends AbstractSourceLookupParticipant {

@Override
public Object[] findSourceElements(Object object) throws CoreException {

String name = getSourceName(object);
Object[] result = super.findSourceElements(name);
// super implmentaion will lookup using SourceContainers added by user and /
// or default provided by ISourcePathComputerDelegate

if (result == null || result.lenght == 0) {
result = new SampleSourceNotFound[1];
result[0] = new SampleSourceNotFound(object);
}
return result;
}
// object is generally stack frame or breakpoint instance.
// get the name of source file from object.
public String getSourceName(Object object) {
if (object instanceof MyStackFrame) {
return ((MyStackFrame)object).getSourceName();
}
return null;
}

}


For the returned result, eclipse debug framework will get the editor from debug model prensentation.
Eclipse provides a standard "source not found" editor (org.eclipse.debug.ui.sourcelookup.CommonSourceNotFoundEditorInput) that you can show when file is not found in user specified source lookup paths. e.g.


public class MyDebugModelPresentation implements IDebugModelPresentation {

public IEditorInput getEditorInput(Object element){
// you may want to add additional conditions based on type of sorce
// entries / containers supported by your source tab.

if (element instanceof IPath) {
IPath path = (IPath) element;
return getEditorInputFromPath(path);
}
if (element instanceof IFile) {
return new FileEditorInput((IFile) element);
}
if (element instanceof SampleSourceNotFound) {
return new CommonSourceNotFoundEditorInput(element);
}
}
}


You may want to show custom source not found editor (for various reasons such as displaying different message than the default one). In your debug model presentation, check if
result is instnace of SampleSourceNotFound and if so, return the id of your custom editor input.
Here is a sample implentation:


public class MySourceNotFoundEditorInput extends CommonSourceNotFoundEditorInput {

// The message to display in editor page instead of "Source not found.".
private String fMessage;

public MySourceNotFoundEditorInput(SampleSourceNotFound object) {

super(object.getArtifact());

// add getMessage() to SampleSourceNot Found or just use hardcode string
fMessage = object.getMessage();

//Or use something like :
fMessage = "Can not find" + object.getArtifact();
}

@Override
public String getToolTipText() {
// custom message to be displayed in the editor before Edit source lookup Button.
return fMessage;
}
}


To use this editor input, just modify your debug model presentation as below:


public class MyDebugModelPresentation implements IDebugModelPresentation {

public IEditorInput getEditorInput(Object element){
// :
// other conditions shown above
// :
if (element instanceof SampleSourceNotFound) {
return new MySourceNotFoundEditorInput(element);
}
}
}

Eclipse : Switching Perspectives Programatically

Here is how you can switch to a Perspective programatically:


Display.getDefault().asyncExec(new Runnable(){
public void run(){
IWorkbenchWindow w = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
IPerspectiveRegistry reg = PlatformUI.getWorkbench().getPerspectiveRegistry();
String perspId = "id of perspective you want to switch to";
w.getActivePage().setPerspective(reg.findPerspectiveWithId(perspId));
}
});


If you want to for example swithc to debug perspective when your custom launch configuration is launched, put this code at the end of your launch delegate.

Balsamoooooo

Balsamiq:

Often in the early phases of software development, one needs to design mock up screens. Having right tools can greatly reduce your development costs and efforts.

If you are like me bored with designing UI mockups with paintbrush, here is a good news:
try this free online tool: Balsamiq. You may also want to buy desktop / standalonne version of it.

Here is a sample design for google web page:



SWT,Swing,GWT Designer

Now if you want to design real UI using SWT, Swing, GWT etc. another great tool worth looking at is: WindowsProBuilder by www.instantiations.com.

I recently used their "SWT designer" for one of my IDE development feature. Being not a regular UI developer, i found it hard to design UI with Code. So another great tool for reducing development time and cost. This tool reduced my developmenbt efforts from 2 weeks to just 2 days.
It also allows you to create UI elements for your Eclipse RCP application such as Views,Perspectives,Preference Pages.

This company is recently acquired by Google. It offered 14 day trial download of various UI design tools with full functionality. But after acquision, they have temporarily stopped downloads. Hopefully are available soon. Stay tuned.

Good news : SWT Designer is free now at google site details here :

ITypeResolutionService for constructed / generic .net types

If you are writing your own customized form designer, one of the services you need to provide is ITypeResolutionService.
Given a name of type, this service is responsible for returning corresponding "System.Type".

A "Type" can be one of following :

1. default type (defined in one of the standard .net assemblies e.g. mscorlib.dll or system.dll etc provided by .net framework)
"System.Type" for such types can be retrieved using Type.GetType() API.
2 custom type defined in user / 3rd party assemblies:
For such types, you need to know the assembly name in which this type is defined.
First you have to load the Assembly and then look up for that type name in loaded assembly as below:

Assembly asm = Assembly.Load("referenced assembly name");
asm.GetType(strTypename);


Now let's see how .net generic types are represented.
Let's say we create a generic class ABC < T,U > in "XYZ" assembly.
Internal representation for ABC class would be ABC`2
(i.e. classname followed by backtick followed by number of generic type parameters).

To constuct an object of this class we have to provide 2 types such as
ABC < string,int > obj;
Here type of obj is a constructed type and is internally represented as "ABC`2[string,int]".

Now if you try to load Type for "ABC`2[string,int]" from XYZ assembly, it returns null because assembly knows about generic type "ABC`2". It doesn't know about its constructed types.
So to return System.Type for such constructed types, you need to first get its generic type from assembly and then on that type call Type.MakeGenericType() passing a Type array(Type[ ]) for its type arguments.

Here is a sample implementation of ITypeResolutionService:


Using System;

MyTypeResolutionService : ITypeResolutionService
{
public Type GetType(String name)
{
Type t = Type.GetType(name);
if(t == null)
{
// type is defined in some assembly.
//name must include name of assembly.

String assemblyName = GetAsmName(name);//helper method
if(String.IsNullOrEmpty(assemblyName))
return null;

String typeName = GetTypeName(name); //helper method

// check if this is a consturected type
String typeArgs;
bool isConstructed = false;
// look for backtick.
// if present means a generic type
int pos = name.IndexOf('`');
if(pos != -1)
{
// look for square bracket.
// if present means a constructed type.
pos = name.IndexOf('[',pos);
if(pos != -1){
isConstructed = true;
// get "abc'2" from "abc`2[string,int]"
String genericTypeName = typeName.SubString(0,pos);
// get "string,int" from "abc`2[string,int]"
typeArgs=
typeName.SubString(pos+1,typeName.Length - 1);

typeName = genericTypeName;
}
}


Assembly asm = Assembly.Load(assemblyName);
t = asm.GetType(typeName);

if( t != null && isConstructed)
{
Type[] types = getArgTypes(typeArgs);
//TODO: if types is null, return null
return t.MakeGenericType(types); .
}
}

return t;
} // end of GetType()

// here comma separated typeArgs passed is likely to contain
// again some other constructed types
// In such case GetType() is called recursively for all nested
// constructed typeArgs ..
private Type[] getArgTypes(String name)
{
// first split type argument names;
List < String > argList = new ArrayList < String > ();
int brackets;
int argStartpos = 0;
for(i =0;i < name.Length;i++)
{
if(name[i] == '[')
brackets++;
else if(name[i] == ']')
brackets--;
else if(name[i] == ',' && brackets == 0)
{
argList.Add(name.SubString(argStartpos,i));
// point to start of next type argument
argStartpos = i+1; .
}
}
// add the last type arg
argList.Add(name.SubString(argStartpos);

//now call GetType() for each typeArg in argList
Type[] retTypes = new Type[argList.Count];
for(int i=0;i < argList.Count;i++)
retTypes[i] = GetType(argList[i]);

return retTypes;
} // end of getTypeArgs()
} // end of MyTypeResolutionService

A tale of eclipse bundle classpath, jre6 and managed Assemblies

Recently I ported an eclipse RCP application from jre 5 to jre6.
The application worked fine with jre 5 but failed when run with jre6.
Problem was in the native dll that tried to access code in manged assembly.

My Eclipse application invoked a native method which in turn attempted to access code in managed code in a .net assembly. .NET CLR runtime loads an assembly when it is referenced first time. CLR runtime looks for that assembly in the APPDOMAIN base directory. With eclipse 3.4.2 and jre5, APPDOMAIN base directory happens to be eclipse bundle class path (".metadata/.plugins/org.eclipse.pde.core/yourlaunchConfigName/org.eclipse.osgi/bundles/someIntegerbundleID/1/.cp").

So RCP application copied all required managed assemblies in this eclipse bundle class path before invoking native method. And CLR runtime happily loaded them.

However when RCP application was run with JRE6 under eclispe 342, the APPDOMAIN base directory was JRE_HOME/bin. AND CLR runtime didn't find required assemblies here. So that was the reason for failure.

Fix for this is to use something called AssemblyResolver in .net terminolory. If CLR runtime can not find an assembly in APPDOMAIN base directory, it looks for any regirstered AssemblyResolvers. If one found, it notifies this resolver to load the required assembly. All this resolver did is to check the name of assembly to be loaded if it is the one used by RCP application, it loaded this assembly from its installed location.

I had to write Assembly Resolver in another mixed mode dll and register this resolver from native dll before accessing managed code.

Nightmares of nightly builds

If you are writing a dll for nt and nt64 bit platform here are some tips:

1. /clr:oldsyntax works on "nt" but fails to build on "nt64" bit platform. Use /clr:pure syntax instead.

2. /clr and /MTD options work fine in Debug mode. For release mode make sure you use /MD option.

3. when you add a resource file (.rc) to your vc project, it also generates resource.h. So make sure you checkin resource.h as well in your source control to avoid nightly build errors.

4. if you are using .vcproj file for "nt" platform, you may also need to create vcproj_9 for nt64 and you need to create this using visual studion 2008 or higher.

5. Make sure that you build your dll manually on both platforms before source control checkin.