The Secondary ID
In many RCP applications your views (ViewParts) will not be singletons. Perhaps you have a tree or project view which is probably a “show only once” view, but for others it’s more likely you have them flagged in the plugin xml to say allowMultiple="true" in the <view /> tag. For multiple views sharing the same primary ID you are probably already aware that you can (and have to) set a secondary id on them to make them unique. This secondary id will stay with the view as long as that view is open (even if you restart your application as the secondary id is saved in the Eclipse metadata and then re-set when your RCP application starts up again – assuming the view is set to be restorable).
But there’s some things that can be a bit frustrating, which is the point of writing this article. First, as your view has to be defined in the plugin.xml and [should you want to] Eclipse/RCP has currently no way of hiding views in “Show View” (see Bug 249451 for details and possible workaround to hiding views from “show view”). Any view opened with Show View won’t have a secondary id.
Second, you may want to control what that secondary id looks like, as you may want to use it as an “ID” for saving custom view-specific settings (for example, grid-specific data such as column widths, column orders etc).
Third, you want a central place where the creation of those multi-views are handled where you know that the ID set on them will be of the format you defined.
And lastly, you probably want some of them to be automatically opened in your “default” perspective with proper secondary ids. So let’s get to it and see how we can make all this work for us with the goal of us not having to think about it any more once we’ve created the necessary code.
The ActionNewView Action
As we will continuously reference “the place” where the secondary id is set on the view, lets start with the “view creator”. This could be considered a Factory, but as it’s kind of nice to tie it in with Actions lets make this factory an Action which also allows us to use it in menus, toolbars, etc.
The action will take the [primary] ID of the view that should be opened as a parameter in the constructor (the same ID you specify in plugin.xml) and in the run() method it will create the view with a secondary id of our choice. This is all quite easy and we don’t have to write much code to accomplish it, in fact, you’ve undoubtedly done something like this already. In this article we will use a UUID as our secondary ID as it’s unique and the structure of it is good as it’s a long string without strange characters (except the ‘-’ character) and it’s more or less guaranteed to be unique. You may of course use whatever you prefer as a unique ID.
Our action class will look like this:
public class ActionNewView extends Action {
private String _viewIdToOpen = null;
public ActionNewView(String viewIdToOpen) {
_viewIdToOpen = viewIdToOpen;
}
@Override
public void run() {
try {
// this will become the secondary ID used in the view
String secondaryId = UUID.randomUUID().toString();
IWorkbenchWindow window = Activator.getDefault().getWorkbench().getActiveWorkbenchWindow();
if (window != null) {
IWorkbenchPage page = window.getActivePage();
if (page != null) {
// these two lines open (create) and focus on the view
page.showView(_viewIdToOpen, secondaryId, IWorkbenchPage.VIEW_CREATE);
IViewPart justActivated = page.showView(_viewIdToOpen, secondaryId, IWorkbenchPage.VIEW_ACTIVATE);
// ... do something with justActivated if needed
}
}
}
catch (Exception err) {
//TODO: deal with exception
}
}
}
I don’t think it needs much explanation as it’s a pretty straight forward “view creation” call with a secondary id, as specified in the Eclipse manual etc.
Dealing With “Show View”
Now that we have our “view opener class” we’re ready to deal with some of the aspects mentioned in the beginning. The next stop is to get secondary id’s onto views opened via “Show View” as they don’t get one by default. If you don’t already have an abstract parent class for all of your multi views, perhaps now is the time to create one as it will make it easier, but you can of course put the same snippet of code elsewhere.
One method is always called upon view creation, and that’s where we want to check if the view being created has a secondary id. If it doesn’t, we want to dispose (hide) that view right away and call our ActionNewView instead and open the same view again but with a secondary id. The method we check in is the createPartControl(Composite parent) method which is an implementation of the IWorkbenchPart interface. Here’s what our code will look like:
@Override
public void createPartControl(final Composite parent) {
try {
// get the secondary id that the view was created with
String secondaryId = getViewSite().getSecondaryId();
// If the secondary id was null that means that Eclipse opened the view, and not us. That's bad because it
// creates a view without a secondary ID and causes the view to be "different" from one that we opened ourselves.
// Thus, if we see a view opened without an ID, we quickly close it and tell the "open new action" to open
// the same view right away. The result may be a very slight flicker as the old view comes to existance and is
// destroyed right away, but it's mostly not noticeable by the end user.
if (secondaryId == null) {
// we need to do it asynchronously as we can't close the view until the create code has finished
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
// destroy ourselves
getViewSite().getPage().hideView(ThisAbstractClass.this);
// open the same view our way
ActionNewView anv = new ActionNewView(getViewSite().getId());
anv.run();
}
});
// return now as we're done with the non-secondary-id view
return;
}
// do normal view creation stuff here...
}
catch (Exception err) {
//TODO: deal with exception
}
}
ThisAbstractClass is of course whatever class you put this code in, but most likely it’s your abstract multi-view parent class.
So, as the comment states, when a view is created and it doesn’t have a secondary id, that view is discarded as soon as it finished being created and the same view is opened again but this time via our action, which in turn ensures it gets a secondary id to our standards.
Dealing With Perspectives
We’re almost done, but there’s one more place where views may get secondary id’s that aren’t what we want, and that’s the views that are opened when the Perspective is new or reset. In our DefaultPerspective class (or whatever perspective class you want) we define the layout in the createInitialLayout(IPageLayout layout) and we also set whatever views are open at the start and where non-opened views go (placeholders). In here we need to change a few things so that whatever views are opened from the start get opened with a secondary id like above.
The API documentation for IPageLayout states:
In some cases, multiple instances of a particular view may need to be added to the same layout. These are disambiguated using a secondary id. In layout methods taking a view id, the id can have the compound form: primaryId [':' secondaryId]. If a secondary id is given, the view must allow multiple instances by having specified allowMultiple="true" in its extension. View placeholders may also have a secondary id.
So we can either create the secondary id on the fly and call layout.showView("primaryId:secondaryId") or we can just add placeholders and asynchronously call our Action to create the views. It’s really up to you how you want to do it, if your ID generator is a simple static call, you may just want to put the secondary ID’s in there and be done. But for the sake of the article, lets have everything go through our action.
Presume we have a perspective layout of 1 folder on the left, and 3 folders on the right, each taking up 33% of the space vertically. Something like this:
+-----------+--------+ | |folder 1| | +--------+ | folder 0 |folder 2| | +--------+ | |folder 3| +-----------+--------+
The “folder 0″ will get a non-multi view, and folder 1,2,3 will get multi-views, 1 each for this example. We will tell the folder 1,2,3 that they contain view placeholders, and folder 0 will get a view right away. Our code will look like this:
public class DefaultPerspective implements IPerspectiveFactory {
// Marker for saying "any id following the primary id" (which is the secondary id)
private static final String MULTI_VIEW = ":*";
@Override
public void createInitialLayout(IPageLayout layout) {
String editorArea = layout.getEditorArea();
layout.setEditorAreaVisible(false);
// list of ID's that will be created at "reset perspective" / first startup
final String[] ids = new String[] { ViewForFolder1.ID, ViewForFolder2.ID, ViewForFolder3.ID };
layout.addShowViewShortcut(OurWorkspaceView.ID);
layout.addShowViewShortcut(ViewForFolder1.ID);
layout.addShowViewShortcut(ViewForFolder2.ID);
layout.addShowViewShortcut(ViewForFolder3.ID);
// left side
IFolderLayout workspace = layout.createFolder("Folder0", IPageLayout.LEFT, 0.2f, editorArea);
workspace.addView(OurWorkspaceView.ID);
// right side
IFolderLayout f1 = layout.createFolder("Folder1", IPageLayout.RIGHT, 0.25f, editorArea);
f1.addPlaceholder(ViewForFolder1.ID + MULTI_VIEW);
IFolderLayout f2 = layout.createFolder("Folder2", IPageLayout.BOTTOM, 0.33f, "Folder1");
f2.addPlaceholder(ViewForFolder2.ID + MULTI_VIEW);
IFolderLayout f3 = layout.createFolder("Folder3", IPageLayout.BOTTOM, 0.5f, "Folder2");
f3.addPlaceholder(ViewForFolder3.ID + MULTI_VIEW);
// create all views asynchronously as they need to be created after the folders etc
// have been laid-out. This ensures they have a secondary id according to our standards.
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
for (String id : ids) {
ActionNewView anv = new ActionNewView(id);
anv.run();
}
}
});
// rest of your code...
}
}
So when the Perspective is opened for the first time (or reset) we create the layout, set multi-view placeholders and then asynchronously call our action to create the views. As we told them where to go, they will show up in each respective folder in the order specified by the string array. As mentioned previously you can of course call addView directly with a secondary id, but I’m showing the “route it through our ActionNewView class” way here.
Done!
This concludes the article on harnessing the Secondary ID in your RCP (or Eclipse) applications. This is by no means the only way to do it as everyone seem to find their own way of doing similar things. This is simply “one way”, but the above way works well. Hopefully the article gave you an insight into how the Secondary ID is used in a multi-view environment and the places you may need to “catch it” as views can be created from the Eclipse framework without your direct interaction.
- Glazed Lists + SWT Tables == True If you haven’t heard about Glazed Lists until now, it’s...
- RCP Workspaces An “Eclipse workspace” is basically a directory where all your...
- MenuManagers and You If you’re using Eclipse RCP (or even standalone SWT with...



4 Responses to “The Secondary ID”
Manuel Selva - June 16th, 2009
Hi,
Nice post. I blogged (http://manuelselva.wordpress.com/2008/05/15/multi-instances-of-a-viewpart/) a while ago about the same subject.
My solution was to use the “Primary view == the one with no secondary ID” to access all the “secondary” views. Nevertheless I will keep in mind your solution for my next multi instances views ..
Manu
Mahbubul Syeed - July 8th, 2010
Hi,
This article is quite helpful and answers many of my questions. But i have one more problem in hand regarding this seondary id which i am not able to solve. Please put forward some solution to this,
I am creating a RCP application. I need to open multiple instances of the same view but with different data. I did it by setting secondary id for different instances of the same view. Specifically, my problem is as follows: Please take a look,
i have a graph view called “Views.GraphView”. I opened different instances of it from a command called “openGraphView” to show different graphs. the command is as follows:
page.showView(“Views.GraphView”, Integer.toString(instanceNum++), IWorkbenchPage.VIEW_ACTIVATE);
Now, I have a command called “TreeLayout” on this “Views.GraphView” toolbar, which suppose to change the layout of the graph and it will operate on each instace of the view. But for this, i think, i need to identify which instance of the view is active. the “TreeLayout” command looks something like this:
IViewPart findView = HandlerUtil.getActiveWorkbenchWindow(event).getActivePage(). findView( “Views.GraphView”); //i think in the findView i need to give the id of the view [but how can i put the secondary id!!]
GraphView view = (GraphView) findView; view.changeLayout(); //i wrote this method in the graph view to change the layout
//i just tried to print the secondary id, but it did not print anyting System.out.println(“From treelayout command:- ” + view.getViewSite().getSecondaryId());
So how can i identify whcih instance of the view is currently active and to operate on it? Please, provide some solution.
Thanks,
-Syeed
Emil - July 8th, 2010
Looks like you’re already being helped here: http://www.eclipse.org/forums/index.php?t=msg&goto=545574& , such long questions are better suited for a forum than here anyway.
sourabh - November 10th, 2010
Thanx………. this article helped me a lot.
Thanks a ton.
Leave a Reply