We have AbstractContributionFactorys like these:
final AbstractContributionFactory contributions = new AbstractContributionFactory("org.acme.mainMenu", null) {
#Override
public void createContributionItems(final IServiceLocator serviceLocator,
final IContributionRoot contributionRoot) {
String subMenuId ="org.acme.subMenu";
final MenuManager subMenu = new MenuManager("Sub menu", subMenuId );
contributionRoot.addContributionItem(subMenu, AlwaysEnabledExpression.INSTANCE);
menuService.addContributionFactory(new AbstractContributionFactory("menu:" + subMenuId, null) {
#Override
public void createContributionItems(final IServiceLocator serviceLocator1,
final IContributionRoot additions) {
additions.addContributionItem(new ActionContributionItem(new Action("Sub action") {
}), AlwaysEnabledExpression.INSTANCE);
}
});
}
};
menuService.addContributionFactory(contributions);
This code worked perfectly in Eclipse 3.x, but stopped working in E4. So while searching for the bug we found a lot uncommented code in the E4 framework, as much as two blocks in WorkbenchMenuService.addContributionFactory(...) alone. What I assume produces the bug is:
// // OK, now update any managers that use this uri
// for (Map.Entry<ContributionManager, MenuLocationURI> entry :
// managers.entrySet()) {
// MenuLocationURI mgrURI = entry.getValue();
// if (mgrURI.getScheme().equals(location.getScheme())
// && mgrURI.getPath().equals(location.getPath())) {
// ContributionManager mgr = entry.getKey();
// populateContributionManager(mgr, mgrURI.toString());
// mgr.update(true);
// }
// }
According to the comments on the associated bug a lot of people have the same problem.
Did anyone find a workaround for the bug?
I also wanted to programmatically add some menu items to the main menu of an RCP application and was caught out by this bug.
Instead of using the WorkbenchMenuService.addContributionFactory(...) method I found you can add contibution items using the by extending the ExtensionContributionFactory class (which also has a createContributionItems() method), and then add this using the org.eclipse.ui.menus extension point as a new menuContribution.
I found this solution in this blog by Vogella.
Related
We want to achieve an RCP application which may have multiple windows (MWindow)
for distinct data. The Windows must be independent (unlike the Eclipse IDE new
window menu entry), but it must be possible to copy & paste, drag & drop things from
one window into another one. Imagine an application like Word where you can
have multiple documents open. We tried various approaches, but it is quiet
difficult to find out the right e4 way:
1. Creating a new E4Application for each window
Our first approach was to create and run a complete new E4Application for each
new window. But this sounds not to be the right e4 way. Also it is buggy: Key
bindings does not work correct and also the LifecycleManager is called for each new
application and therefor for each new window, which should not be.
E4Application application = new E4Application();
BundleContext context = InternalPlatform.getDefault().getBundleContext();
ServiceReference<?> appContextService = context.getServiceReference(IApplicationContext.class);
IApplicationContext iac = (IApplicationContext) context.getService(appContextService);
IWorkbench workbench = application.createE4Workbench(iac, display);
final E4Workbench implementation = (E4Workbench) workbench;
implementation.createAndRunUI(workbench.getApplication());
This seems not the right approach to do it.
2. The Eclipse IDE approach
In the Eclipse IDE you can go to the menu and click Window -> New Window which
will open a complete new top level window. But it is synchronized: Open the
same text file in both windows and editing it in the first one will alter it in
the other one too. Albeit we tried that approach by simply copy and pasting it
from org.eclipse.ui.actions.OpenInNewWindowAction#run():
// Does not work because we do not have the RCP3 workbench in RCP4.
final IWorkbench workbench = PlatformUI.getWorkbench();
final IWorkbenchWindow workbenchWindow = workbench.getActiveWorkbenchWindow();
final IWorkbenchPage activePage = workbenchWindow.getActivePage();
final String perspectiveId;
if (activePage != null && activePage.getPerspective() != null) {
perspectiveId = activePage.getPerspective().getId();
} else {
perspectiveId = workbenchWindow.getWorkbench().getPerspectiveRegistry().getDefaultPerspective();
}
workbenchWindow.getWorkbench().openWorkbenchWindow(perspectiveId, null);
It looks like that the Eclipse IDE uses the RCP3 compatibility layer. We didn't
found a way to obtain the IWorkbench object. Neither by
PlatformUI#getWorkbench(), nor via the application context, nor the bundle
context.
3. Clone the main window
We stumbled upon Opening multiple instances of an MTrimmedWindow complete with perspectives etc
n-mtrimmedwindow-complete-with-perspectives-etc and did a lot of trial and
error and came up with this muddy code:
class ElementCloningBasedCreator {
EModelService models = ...; // injected
MApplication app = ...; // injected
public void openNewWindow() {
MWindow originWindow = (MWindow) models.find("the.main.window.id", app);
MWindow newWindow = (MWindow) models.cloneElement(originWindow, null);
MPerspectiveStack newPerspectiveStack =
(MPerspectiveStack) models.find(the.main.perspective.stack.id, newWindow);
newPerspectiveStack.setParent((MElementContainer) newWindow);
addTo(app, newWindow);
// Clone the shared elements. If we don't do that the rendering somewhere
// deep in the rabbit hole throws assertion erros because the recurisve
// finding of an element fails because the search root is null.
for (final MUIElement originSharedElement : originWindow.getSharedElements()) {
final MUIElement clonedSharedElement = models.cloneElement(originSharedElement, null);
clonedSharedElement.setParent((MElementContainer) newWindow);
newWindow.getSharedElements().add(clonedSharedElement);
}
cloneSnippets(app, originWindow, newPerspectiveStack, newWindow);
newWindow.setContext(createContextForNewWindow(originWindow, newWindow));
newWindow.setToBeRendered(true);
newWindow.setVisible(true);
newWindow.setOnTop(true);
models.bringToTop(newWindow);
}
#SuppressWarnings({ "rawtypes", "unchecked" })
private void addTo(MElementContainer target, MUIElement child) {
child.setParent(target);
target.getChildren().add(child);
}
/**
* Clone each snippet that is a perspective and add the cloned perspective
* into the main PerspectiveStack.
*/
private void cloneSnippets(MApplication app, MWindow originWindow,
MPerspectiveStack newPerspectiveStack, MWindow newWindow) {
boolean isFirstSnippet = true;
for (MUIElement snippet : app.getSnippets()) {
if (ignoreSnippet(snippet)) {
continue;
}
String snipetId = snippet.getElementId();
MPerspective clonedPerspective =
(MPerspective) models.cloneSnippet(app, snipetId, originWindow);
findPlaceholdersAndCloneReferencedParts(clonedPerspective, newWindow);
addTo(newPerspectiveStack, clonedPerspective);
if (isFirstSnippet) {
newPerspectiveStack.setSelectedElement(clonedPerspective);
isFirstSnippet = false;
}
}
}
private boolean ignoreSnippet(MUIElement snippet) {
return !(snippet instanceof MPerspective);
}
private void findPlaceholdersAndCloneReferencedParts(MPerspective clonedPerspective, MWindow newWindow) {
List<MPlaceholder> placeholders =
models.findElements(clonedPerspective, null, MPlaceholder.class, null);
for (MPlaceholder placeholder : placeholders) {
MUIElement reference = placeholder.getRef();
if (reference != null) {
placeholder.setRef(models.cloneElement(placeholder.getRef(), null));
placeholder.getRef().setParent((MElementContainer) newWindow);
}
}
}
}
This code does not really work and we really need some hints/advices how to do
it right, because of the lack of official documentation. The questions open are:
Do we need to clone the shared objects and if not how do we prevent the
errors during rendering)?
We only saw code where the cloned elements are
added to the parent via getChildren().add(), but we found out that the
children din't get the parent automatically and it is null though. Is it the
right pattern to add the parent to the child too?
We have the deep feeling
that we are doing it not right. It looks way too complicated what we do here. Is
there a simpler/better approach?
You can use the EModelService cloneSnippet method to do this.
Design your MTrimmedWindow (or whatever type of window you want) in the Snippets section of the Application.e4xmi. Be sure that the To Be Rendered and Visible flags are checked. You may need to set the width and height bounds (and you may want to set the x and y position as well).
Your command handler to create the new window would simply be:
#Execute
public void execute(EModelService modelService, MApplication app)
{
MTrimmedWindow newWin = (MTrimmedWindow)modelService.cloneSnippet(app, "id of the snippet", null);
app.getChildren().add(newWin);
}
I have spent almost three days trying to do a simple enable / disable of Actions in the netbeans plaform, something that I though was going to be simple, and should be a common feature is more complex than I thought.
At the begging I tried to see if there was an setEnable() method on the default actions generated and to my surprise there is not. Then I started looking into that and I found that most common method to do it was setting a conditionally enabled action (which depends on a Cookie class), So I figured out how to add a fake class to the Lookup so it gets enabled and disabled, I did it the following way. To test it out I added the following code to another action which should enable or disable the second one.
private final PlottingStarted plottingStarted = new PlottingStarted();
#Override
public void actionPerformed(ActionEvent e) {
// TODO implement action body
if (Lookup.getDefault().lookup(PlottingStarted.class) == null) {
ic.add(plottingStarted);
}else{
ic.remove(plottingStarted);
}
So PlottingStarted is a fake object I created which only purpose is being in the lookup to disable or enable the action.
For some reason it did not do anything at all an the Action was always disabled. I tried many things and finally I gave up.
Then I tried a different approach and was using AbstractActions which do have the setEnabled() ability.
To retrieve the action I based myself on one the Geertjan blogs and I created the following method
public Action findAction(String actionName) {
FileObject myActionsFolder = FileUtil.getConfigFile("Actions/RealTimeViewer");
if (myActionsFolder != null){
FileObject[] myActionsFolderKids = myActionsFolder.getChildren();
for (FileObject fileObject : myActionsFolderKids) {
//Probably want to make this more robust,
//but the point is that here we find a particular Action:
if (fileObject.getName().contains(actionName)) {
try {
DataObject dob = DataObject.find(fileObject);
InstanceCookie ic = dob.getLookup().lookup(InstanceCookie.class);
if (ic != null) {
Object instance = ic.instanceCreate();
if (instance instanceof Action) {
Action a = (Action) instance;
return a;
}
}
} catch (Exception e) {
ErrorManager.getDefault().notify(ErrorManager.WARNING, e);
return null;
}
}
}
}
return null;
}
This method worked perfectly and I was able to retrieve the action and call its setEnabled() method. Unfortunately no matter why I did the Action was always enabled.
Reading some literature I found that I should add the following to the registration of the action "lazy = false" and finally I was able to enable and disable the Action... But off course the default registration is lost and I have no Icons and Names.
Now I decided to post again because I cannot believe that it need to be that complex, there must be a way to do it easier. The only thing I need is to have a PLAY / STOP functionality, when PLAY is enabled STOP is disabled and vice-versa.
I have not done this myself but it seems to be covered in Chapter 5.1.2.1 "Complex Enablement" of the book "Netbeans Platform for Beginners". https://leanpub.com/nbp4beginners
The book is not free but the corresponding code sample is available on
github. https://github.com/walternyland/nbp4beginners/tree/master/chapters/ch05/5.1.2.1 He extends AbstractAction overrides the resultChanged method and uses super.setEnabled().
#ActionID(id = "org.carsales.evaluator.EvaluateCarAction1", category = "Car")
#ActionRegistration(displayName = "not-used", lazy = false)
public class EvaluateCarAction extends AbstractAction
implements ContextAwareAction, LookupListener {
// ...
#Override
public void resultChanged(LookupEvent le) {
//Optionally, check if the property is set to the value you're interested in
//prior to enabling the Action.
super.setEnabled(result.allInstances().size() > 0);
}
Thanks to everybody for your responses. I finally got it to work by extending AbstractAction, it seems that even if you register "lazy = false" some of the registration is still being done by the platform and you just need some minor tweaking in the Action constructor. The final result was
#ActionID(
category = "RealTimeViewer",
id = "main.java.com.graph.actions.StopPlotting"
)
#ActionRegistration(
//iconBase = "main/java/com/graph/images/stop-plotting-24x24.png",
displayName = "#CTL_StopPlotting",
lazy = false
)
#ActionReference(path = "Toolbars/RealTimeViewer", position = 600)
#Messages("CTL_StopPlotting=Stop Plotting")
public final class StopPlotting extends AbstractAction{
private static final String ICON = "main/java/com/dacsys/cna/core/graph/images/stop-plotting-24x24.png";
public StopPlotting() {
putValue(SMALL_ICON, ImageUtilities.loadImageIcon(ICON, false));
putValue(NAME, Bundle.CTL_StopPlotting());
this.setEnabled(false);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO implement action body
Action a = new ActionsHelper().findAction("StartPlotting");
if (a != null){
if (a != null){
if (a.isEnabled()){
a.setEnabled(false);
this.setEnabled(true);
}else{
a.setEnabled(true);
this.setEnabled(false);
}
}
}
}
}
We are working on our Gis application, I am using gwt-openlayers and we're creating Vaadin wrappers.
So I've extended the MapWidget and created the required layers and vector layers and added a DrawFeatureControl:
DrawFeatureOptions drawFeatureOptions = new DrawFeatureOptions();
private DrawFeature drawFeaturePoint = = new DrawFeature(vectorLayer, new PointHandler(), drawFeatureOptions);
and to catch the event:
getWidget().getDrawFeatureOptions().onFeatureAdded(new () {
#Override
public void (com.openlayers.client.feature.VectorFeature vectorFeature) {
Window.alert("Feature Added" + vectorFeature.getFID());
serverRpc.featureAdded(buildVectorFeature(vectorFeature));
}
});
for some reason this is not working; although the following which should be almost the same is working fine:
getWidget().getVectorLayer().addVectorFeatureSelectedListener(new () {
#Override
public void onFeatureSelected(FeatureSelectedEvent eventObject) {
serverRpc.featureSelected(buildVectorFeature(eventObject.getVectorFeature()));
}
});
using (addVectorFeatureAddedListener) on the vector layer will be fired everytime a Feature is added to the VectorLayer, and will not be fired when the DrawFeatureControl is used.
Can someone help me to catch the Features that are drawn using the DrawFeatureControl.
By The way I have a Cluster and BBox stratigies on the MapWidget, I don't know if this changes anything.
Are you aware that Vaadin have their own wrappers for OpenLayers to use OpenLayers in Vaadin ?
https://vaadin.com/directory#addon/openlayers-wrapper
I found it, for some reason
getDrawFeatureOptions().onFeatureAdded
doesn't do the trick, I had to inject the listener in GWT using:
getWidget().getDrawFeaturePoint().eventListeners.addListener(getWidget().getDrawFeaturePoint(), featurePointAddedlistener, EventType.VECTOR_FEATURE_ADDED, new EventHandler() {
#Override
public void onHandle(EventObject eventObject) {
FeatureAddedEvent e = new FeatureAddedEvent(eventObject);
featurePointAddedlistener.onFeatureAdded(e);
}
});
So I'm having a heck of a time creating a Datagrid with GWT. I've created my table according the the docs for GWT, and I've added my data, but I can't get it or the datagrid itself to show up at all. What am I missing? I feel I've been tearing my hair out over this. I feel like making an aysnc call might be an issue, but I get no errors. When i compile and execute this portion of my code nothing shows up on the screen and the area where the datagrid is supposed to be on the dock is empty. Am I forgetting something trivial?
static int orderID = 1001;
private static List<OrderLine> orderLineList = new ArrayList<OrderLine>();
final DataGrid<OrderLine> dgOrder = new DataGrid<OrderLine>();
dgOrder.setWidth("100%");
//set columns
TextColumn<OrderLine> orderLineIdColumn = new TextColumn<OrderLine>(){
#Override
public String getValue(OrderLine object) {
return Integer.toString(object.getOrderLineID());
}
};
dgOrder.addColumn(orderLineIdColumn, "OrderLine ID");
TextColumn<OrderLine> productColumn = new TextColumn<OrderLine>(){
#Override
public String getValue(OrderLine object) {
return getProductName(object.getProductNumber());
}
};
dgOrder.addColumn(productColumn, "Product");
TextColumn<OrderLine> quantityColumn = new TextColumn<OrderLine>(){
#Override
public String getValue(OrderLine object) {
return Integer.toString(object.getQuantity());
}
};
dgOrder.addColumn(quantityColumn, "Quantity");
// add data to datagrid
Ioma.dataservice.getOrderLines(orderID, new AsyncCallback<ArrayList<OrderLine>>(){
#Override
public void onFailure(Throwable caught) {// TODO Auto-generated method stub
System.out.println("error in retrieving GP.getOrderLines" + caught.toString());
}
#Override
public void onSuccess(ArrayList<OrderLine> result) {
// TODO Auto-generated method stub
//yes i realize I could also set it to "result" but I use the result in other places as well, I have also tried setting it to result with no success
orderLineList = result;
dgOrder.setRowData(0, orderLineList);
}
});
//add datagrid to the dock
dock.add(dgOrder, DockPanel.EAST);
//add dock to mainPanel
return dock;
When you use the DataGrid you need to give it an explicit size. For CellTable you don't need to so that's why it worked.
When you were using the DataGrid is was there but had both height and width of 0. So you either need to explicitly set the size:
DataGrid<OrderLine> grid = new DataGrid<OrderLine>;
grid.setWidth("500px");
grid.setHeight("600px");
dock.add(dgOrder, DockPanel.EAST);
or you can put the gird in a ProvidesResize widget. So in your case I believe you can make it work with a DockLayoutPanel as opposed to a DockPanel which is what you seem to be using.
I had a smilliar problem . Try to put the datagrid in a Panel that implements the ProvidesResize interface, like SimpleLayoutPanel. Also in my case it helped to set the size of the SimpleLayoutPanel.
Turns out my problem was my declaration.
final DataGrid<OrderLine> dgOrder = new DataGrid<OrderLine>();
should be
final CellTable<OrderLine> dgOrder = new CellTable<OrderLine>();
I can't find out why this works, but it does. I can see my table now. are you not supposed to explicitly call an instance of datagrid I wonder?
today I changed my Eclipse IDE from 3.7 to 4.2 and my plugin-project has a new feature in the Statusbar of the UI called QuickAccess. But I dont need it, so how can I disable this feature, because the position of my button bar has changed...
For all who have the same problem, it seems that this new feature is hardcoded and can't be disabled :/ https://bugs.eclipse.org/bugs/show_bug.cgi?id=362420
Go to Help --> Install New Software
https://raw.github.com/atlanto/eclipse-4.x-filler/master/pdt_tools.eclipse-4.x-filler.update/
Install that Plugin and Restart the Eclipse. Quick Access automatically hide.
or else you have an option to hide Window --> Hide Quick Access.
Here's a post that shows a way to hide it with CSS. Verified with Eclipse 4.3
Lars Vogel just reported in his blog post "Porting Eclipse 3.x RCP application to Eclipse 4.4 – now without QuickAccess box":
Bug 411821 ([QuickAccess] Contribute SearchField through a fragment or other means)
is now solved.
Thanks to René Brandstetter:
If a RCP app doesn't provide the QuickAccess element in its model, than it will not be visible. So the default is no QuickAcces, easy enough? :)
See the commit 839ee2 for more details
Provide the "QuickAccess" via a e4 application model fragment inside of the "org.eclipse.ui.ide.application".
This removes the "QuickAccess" search field from every none "org.eclipse.ui.ide.application".
You could also hide it and make it work comparable to how it used to work in Eclipse3.7: when user presses ctrl+3 Quick Access functionality pops up (In Eclipse4.3 the ctrl+3 shortcut is still available).
Example of code you could add to your implementation of WorkbenchWindowAdvisor (for Eclipse4.3 rcp application)
private IHandlerActivation quickAccessHandlerActivation;
#Override
public void postWindowOpen() {
hideQuickAccess();
}
private void hideQuickAccess() {
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
setQuickAccessVisible(window, false);
final IHandlerService service = (IHandlerService) window.getService(IHandlerService.class);
quickAccessHandlerActivation = service.activateHandler(QUICK_ACCESS_COMMAND_ID, new CustomQuickAccessHandler());
}
private void setQuickAccessVisible(IWorkbenchWindow window, boolean visible) {
if (window instanceof WorkbenchWindow) {
MTrimBar topTrim = ((WorkbenchWindow) window).getTopTrim();
for (MTrimElement element : topTrim.getChildren()) {
if (QUICK_ACCESS_ELEMENT_ID.equals(element.getElementId())) {
element.setVisible(visible);
if (visible) {
Composite control = (Composite) element.getWidget();
control.getChildren()[0].addFocusListener(new QuickAccessFocusListener());
}
break;
}
}
}
}
private class QuickAccessFocusListener implements FocusListener {
#Override
public void focusGained(FocusEvent e) {
//not interested
}
#Override
public void focusLost(FocusEvent e) {
((Control) e.widget).removeFocusListener(this);
hideQuickAccess();
}
}
private class CustomQuickAccessHandler extends AbstractHandler {
#Override
public Object execute(ExecutionEvent event) throws ExecutionException {
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
final IHandlerService service = (IHandlerService) window.getService(IHandlerService.class);
setQuickAccessVisible(window, true);
if (quickAccessHandlerActivation != null) {
service.deactivateHandler(quickAccessHandlerActivation);
try {
return service.executeCommand(QUICK_ACCESS_COMMAND_ID, null);
} catch (NotDefinedException e) {
} catch (NotEnabledException e) {
} catch (NotHandledException e) {
}
}
return null;
}
}