Visibility of toolbar items in Eclipse 4 model - eclipse

I have a simple e4 RPC application, based on e4 model. I have two parts in PartSachContainer - left one and right one.
And I need a certain toolbar item to be visible when one part is active, and another item to be visible when another part is active.
I tried to use CoreExpression for this, but to no avail. I can't understand what do I need to write in the expression itself.
My config is as follows:
First part is defined as
ID: simple1.part.leftpart
Label: LeftPart
ClassURI: bundleclass://Simple1/simple1.parts.LeftPart
The second part is defined as
ID: simple1.part.rightpart
Label: RightPart
ClassURI: bundleclass://Simple1/simple1.parts.RightPart
The ToolBar Contribution is defined as
<toolBarContributions xmi:id="_gqB4gNaWEeO3iqh7a9kwnw" elementId="simple1.toolbarcontribution.0" parentId="simple1.toolbar.0">
<children xsi:type="menu:HandledToolItem" xmi:id="_PtGaINaSEeO3iqh7a9kwnw" elementId="simple1.handledtoolitem.leftpitem" iconURI="platform:/plugin/Simple1/icons/sample.png" command="_vFvtcNaREeO3iqh7a9kwnw">
<visibleWhen xsi:type="ui:CoreExpression" xmi:id="_z_87oNanEeO3iqh7a9kwnw" coreExpressionId="Simple1.RightPartActive"/>
</children>
</toolBarContributions>
When I set Visible-When Expression to I can see the toolbar item.
But when I try to use CoreExpression (and I tried various combinations) I never see the toolbar item.
This is what I start with:
<definition id="Simple1.RightPartActive">
<with variable="activePartId">
<equals
value="simple1.part.rightpart">
</equals>
</with>
</definition>
No luck.
How should I define the CoreExpression to see the toolbar item?
And here's to the real problem:
This right part should really be an editor (in e3 terms) - the same implementaion class for multiple various instances of editable data. So, I will use the runtime-generated unique IDs for this parts (the parts themleves would be created from PartDescription).
So how should I address these parts with generated IDs in CoreExpression?
UPDATE
Ok, I did some debugging and found out that there is a method
public EvaluationResult evaluate(IEvaluationContext context)
of the class
org.eclipse.core.internal.expressions.WithExpression
that is being called.
The very first line :
Object variable= context.getVariable(fVariable);
calls method of the class EclipseContext
public Object getVariable(String name) {
if (IEclipseContext.class.getName().equals(name)) {
return eclipseContext;
}
Object obj = eclipseContext.getActive(name);
return obj == null ? IEvaluationContext.UNDEFINED_VARIABLE : obj;
}
where
public Object getActive(final String name) {
return getActiveLeaf().get(name);
}
and
public IEclipseContext getActiveLeaf() {
IEclipseContext activeContext = this;
IEclipseContext child = getActiveChild();
while (child != null) {
activeContext = child;
child = child.getActiveChild();
}
return activeContext;
}
and [b]activeChild[/b] is always null.
And I don't see anything in EclipseContext which would be something like "activePartId" or anything close to it.
So why is it so??
How can we even use the CoreExpressions??

This looks like Eclipse bug 400217, which is saying that this does not currently work.

Since you're in e4 why not handle it the e4 way ?
Subscribe to the UIEvents.UILifeCycle.ACTIVATE event.
if (newActivePart == LeftPart)
<set 'toBeRendered' to true for LeftPart's items and false for RightPart's items
same for Right part...

Related

How do I render different shapes on each row of a SAPUI5 Gantt chart?

In my application, I have to render Projects, Tasks and Milestones. Projects and Tasks are differently coloured bars, and the Milestone is a Diamond (I'm using BaseRectangle and BaseDiamond respectively).
Since some items in my hierarchy are Projects, Some Tasks and Some Milestones, how can I render differing shapes on each row?
My first thought was to use the common "visible" property, but shapes don't have that, conversely "opacity" makes things invisible, but they still respond to mouse position.
I then tried using an Aggregation factory function, but although my chart renders correctly on first display, it doesn't recalculate the shapes on expanding or collapsing branches.
It seems to me that the factory function should work, but something is breaking in the chart that doesn't throw errors to console.
At the moment in my XML template, I have the following:
rowSettingTemplate has shapes1={path: factory:} and no shapes1 element.
Each of my BaseShapes is in a different fragment which are attached to my TreeTable as dependents.
Example Shape Fragment - Project.fragment.xml
<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:gnt2="sap.gantt.simple">
<gnt2:BaseRectangle id="shapeProject"
shapeId="{plandata>id}" countInBirdEye="true"
time="{plandata>start_date}" endTime="{plandata>end_date}"
resizable="true" selectable="true" draggable= "true" connectable="true"
title="{plandata>text}" showTitle="true"
tooltip=""
fill="#0c1" />
</core:FragmentDefinition>
Factory function:
shapeFactory: function(sId, oContext) {
var parentId = (/(.*)-\d+$/.exec(sId))[1];
var rowSettings = sap.ui.getCore().byId(parentId);
var node: Project.Node = oContext.getProperty();
if (String(node.id) == rowSettings.getProperty("rowId")) {
switch (node.type) {
case "project":
return this.byId('shapeProject').clone(sId);
case "task":
return this.byId('shapeTask').clone(sId);
case "milestone":
return this.byId('shapeMilestone').clone(sId);
default:
return this.byId('shapeErr').clone(sId);
}
} else {
return this.byId('shapeEmpty').clone(sId);
}
}
My empty shape is a BaseGroup - note that SAPUI5 crashes if I return a null from factory, so something has to be returned when I actually want nothing.
I also tried wrapping all my shapes in BaseGroup so that the chart always sees the same control type, but that doesn't work. Note also that if I return a clone of Empty each time without any special logic, then the chart works correctly.
I'm hoping that this is a settings or something to ensure that the aggregation works properly each time. My SAPUI5 version is 1.61.2 — I'll try 1.63.1 when I get some time, but I think that this issue is fairly deep down.
If anybody has any ideas or sample code, it would be greatly appreciated.
I have come up with a workaround for this, that may save somebody several hours. Basically instead of defining the shapes1 aggregation via a factory function, I have used the <shapes1> tag instead. My Shapes1 tag contains a reference to my own custom shape which derives from BaseRectangle. My custom shape can then render whatever SVG it requires based on the bound object context. Now my tree can expand and collapse whilst rendering whatever shapes are required.
My renderer now looks like this:
CustomChartShape.prototype.renderElementRectangle = BaseRectangle.prototype.renderElement;
CustomChartShape.prototype.renderElementDiamond = BaseDiamond.prototype.renderElement;
CustomChartShape.prototype.renderElement = function(oRm, oElement) {
// There is possibilities that x is invalid number.
// for instance wrong timestamp binded to time property
if (this.bHasInvalidPropValue) { return; }
var Node = this.getBindingInfo('endTime').binding.getContext().getProperty();
if (Node.type == "milestone") {
this.renderElementDiamond(oRm, oElement);
} else {
this.renderElementRectangle(oRm, oElement);
}
}
I had to provide a 'getD' function that has a fixed width, and I'll have o go through and rewrite several functions, but I think that this will work for me.

Should ItemSource and BindingContext both be set when using MVVM (Xamarin.Froms ListView)?

Model:
public class Question : INotifyPropertyChanged
{
private float? _answer;
public float? Answer
{
get => _answer;
set
{
_answer = value;
NotifyPropertyChanged();
}
}
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
View model:
public class QuestionViewModel
{
private ObservableCollection<Question> _questions;
public ObservableCollection<Question> Questions
{
get => _questions;
set
{
if (_questions != value)
{
_questions = value;
}
}
}
}
XAML:
<ListView x:Name="ListViewQuestions" SelectionMode="Single" HasUnevenRows="True" HeightRequest="250" VerticalOptions="FillAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Entry x:Name="EntryAnswer" Text="{Binding Answer,Mode=TwoWay}" Keyboard="Numeric" FontSize="Medium" VerticalOptions="End"
HorizontalOptions="FillAndExpand" Grid.Row="0" Grid.Column="1" >
<Entry.Behaviors>
<behaviors:EntryMaxValueBehavior MaxValue="{Binding MaxVal}" BindingContext="{Binding BindingContext, Source={x:Reference EntryAnswer}}" />
<behaviors:EntryMinValueBehavior MinValue="{Binding MinVal}" BindingContext="{Binding BindingContext, Source={x:Reference EntryAnswer}}" />
</Entry.Behaviors>
</Entry>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In my page OnAppearing method, I set the ListViewQuestions like this:
var questions = await DataStore.GetQuestions(_inspection.Id);
var questionsViewModel = new QuestionViewModel { Questions = new ObservableCollection<Question>(questions) };
ListViewQuestions.ItemsSource = null;
ListViewQuestions.ItemsSource = questionsViewModel.Questions;
However, when values are entered into EntryAnswer, the setter in the Question model is not called, as I would expect. I thought that maybe this was because the BindingContext for the ListView needed to be set, so I set it like this:
ListViewQuestions.BindingContext = questionsViewModel;
However, the setter in the Question model is still not called. I also tried implementing INotifyPropertyChanged in the QuestionViewModel, but still no joy. I checked that the ObservableCollection in the View Model is set correctly, with actual data, and it is. Can anyone spot what might be going wrong here?
Edit 1: I also tried not setting the ItemSource, but only setting the ListViewQuestions.BindingContext to the view model, but then the ListView was not being populated with any data.
Here is how this works together.
The BindingContext is the object that will be the scope for whatever bindings that are in the page or it's children, unless you specify a different context for a certain child object, but let's not overcomplicate things for now.
This means, that when you have set the BindingContext, all Bindings will now start looking into the object referenced in the BindingContext. In your case, you set the BindingContext to an instance of QuestionViewModel.
You want your ListView, to get its items from the QuestionViewModel.Questions property. So, you set a binding like this:
<ListView x:Name="ListViewQuestions" ItemsSource="{Binding Questions}" ...>.
Questions needs to be a public property in the BindingContext, in our case QuestionViewModel. You got this right already.
Now, whenever you assign something to Questions this should also propagate to your ListView because of the binding.
Inside your ListView you are using a ViewCell, now note, that the scope does change here. Each cell represents an instance of an object inside the ItemsSource. In our case, each cell will hold a Question. You are using this:
<Entry x:Name="EntryAnswer" Text="{Binding Answer,Mode=TwoWay}" ...>
This means Answer needs to be a public property inside Question. You got this right already.
When you implement it like this, basically the only thing you do is fill your view model and assign that to the BindingContext of your page. If you are using an MVVM framework, this might happen automatically.
At some point, you might run into some trouble that the UI doesn't update and you will have to implement the INotifyPropertyChanged interface. Have a close look at what object doesn't update on screen and implement the interface on that object along with the needed plumbing, but from what I can see in this code, this isn't needed right now. And besides, you have implemented it the right way in your Question right now.
I hope this makes sense! It's a bit hard to wrap your head around the first time, but once you get the swing of it, it is pretty easy!
In your Answer Setter try:
set
{
float? temp = null;
if(float.TryParse(value, out temp)
{
_answer = temp;
NotifyPropertyChanged("Answer");
}
}
It seems like for this to work though your setter would have to be called, and you indicate that it is not, so I think it must be the min, max binding where this is kicking out the error. For now perhaps get rid of that and see if the setter will get called.
In WPF using a converter is typical and I think will work with the Xamarin as well. See this for a good example of how to implement IValueConverter.

Using e3x property view with e4 Selection service from EMF Model

I built a small e4 RCP application containing both an "e4 xmi" tree view populated by emf generated model code (using ComposedAdapterFactory) and an "e3 properties view".
Tried following "dirksmetric tutorial" to display property view in application.e4xmi (shared elements) with an empty property view.
To get a tree's selected element displayed in my property sheet (IItemPropertySource), I did the following things :
On my e4 treeviewer side, I use the e4 selection service in #createComposite:
// Register the viewer as a selection provider (to be consumed by the property view...)
viewer.addSelectionChangedListener(new ISelectionChangedListener() {
#Override
public void selectionChanged(SelectionChangedEvent event) {
IStructuredSelection selection = (IStructuredSelection) event.getSelection();
// set the selection to the service
selectionService.setSelection(
selection.size() == 1? selection.getFirstElement(): selection.toArray());
}
});
On the e3 "classical" property sheet side, I defined a couple of things :
I called IDE.registerAdapters in my ApplicationWorkbenchAdvisor#initialize.
I declared my property source adapter as follow in my plugin.xml :
extension point="org.eclipse.core.runtime.adapters">
factory adaptableType="org.eclipse.emf.ecore.EObject"
class="myappmodeler.properties.ModelPropertiesAdapter">
adapter type="org.eclipse.ui.views.properties.IPropertySource">
My ModelPropertiesAdapter#getAdapter returns a property source :
public Object getAdapter(Object adaptableObject, Class adapterType) {
if (adapterType== IPropertySource.class && adaptableObject instanceof EObject){
emfGlobalFactory = new ComposedAdapterFactory();
emfGlobalFactory.addAdapterFactory(new RepositorystructureItemProviderAdapterFactory());
emfGlobalFactory.addAdapterFactory(new ApplicationItemProviderAdapterFactory());
emfGlobalFactory.addAdapterFactory(new ServiceItemProviderAdapterFactory());
return new AdapterFactoryContentProvider(emfGlobalFactory).getPropertySource(adaptableObject);
}
return null;
}
My problem is this adapter is not even executed.
Currently using Eclipse neon (it was recently updated to synchronise E3 and E4 selection service)
https://bugs.eclipse.org/bugs/show_bug.cgi?id=403930
There are different ways to fix this problem, but for my case, these are the steps
I took to these steps to solve mine
Take control of the base model - create an interface which extends EObject
Create custom property provider, source and descriptor - extends org.eclipse.emf.edit.ui.provider.*
at runtime we need IItemPropertySource
Create a content provider class (extends AdapterFactoryContentProvider) and override createPropertySource with custom property source
Note; I also developed a table layout which means implementing custom ItemProvider (implement ITableItemLabelProvider) for individual elements in the model
Worked perfectly with ESelectionService
Hope these notes helps somebody

Checking the state of menu items inside handlers

How to check pro-grammatically whether command contributed as menu item(s) is/are checked/unchecked(If of CHECK BOX type), selected or unselected(if of type RADIO button) inside handlers "execute" method.
See snap shot here https://docs.google.com/file/d/0B3pxBGD-v-ycWVFaeElnSGdyTE0/edit .
Check out this blog: http://eclipsesource.com/blogs/2009/01/15/toggling-a-command-contribution/
So, first of all ensure that your command has appropriate style:
<extension point="org.eclipse.ui.menus">
<menuContribution locationURI="...">
<command commandId="org.eclipse.example.command.toggle"
style="toggle" />
</menuContribution>
</extension>
Then, you can check the state like this:
ICommandService service =(ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class);
Command command = service.getCommand("org.eclipse.example.command.toggle");
State state = command.getState("org.eclipse.example.command.toggleState");
System.out.println(state.getValue());
//state.setValue(!(Boolean) state.getValue());
Also, consider taking a look at org.eclipse.ui.handlers.HandlerUtil, it might be sometime helpful.
Hope this helps.
I got solution,
Added this code in handler execute method method
public Object execute(ExecutionEvent event) throws ExecutionException {
Event selEvent = (Event) event.getTrigger();
MenuItem item = (MenuItem) selEvent.widget;
System.Out.Println(item.getSelection());
return null;
}

How to handle property sheet from customized editor in eclipse plugin development?

I have to bind my editor widget objects in property sheet.So that i can the property of my widget from property view.
Please help me on this, if possible provide me some code snippets.
You have a good example in the Getting started with Properties
Using the Properties view is simple enough.
Since it shows properties for the selected object, the first step to using it is to make sure that the workbench selection service knows about the object selected in your view. There’s an entire Eclipse Corner article written on the subject of the selection service
public void createPartControl(Composite parent) {
viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
viewer.setContentProvider(new ViewContentProvider());
viewer.setLabelProvider(new ViewLabelProvider());
getSite().setSelectionProvider(viewer);
viewer.setInput(getViewSite());
}
Once you have your view contributing to the workbench selection, you need to make sure that the objects that your view is selecting contribute properties
(extract)
public class Person implements IPropertySource {
private String name;
private Object street;
private Object city;
public Person(String name) {
this.name = name;
this.street = "";
this.city = "";
}
public Object getEditableValue() {
return this;
}
public IPropertyDescriptor[] getPropertyDescriptors() {
return new IPropertyDescriptor[] {
new TextPropertyDescriptor("name", "Name"),
new TextPropertyDescriptor("street", "Street"),
new TextPropertyDescriptor("city", "City")
};
}
I indicated earlier that this solution is “not necessarily [the] most correct”. This is because, for this to work, my domain object needs to know about the very view-centric (and Eclipse-centric) notion of being a property source; in short, there is a tight-coupling between the model and view and this not a good thing™.
Using adapter is a better approach, as described in this article:
Person should implement IAdaptable.
See also this recent article on how to create a custom property view
how to hack the Properties View to listen only to a specific view.
The isImportant() method is the one which decides whether to create an IPage for the specific IWorkbenchPart or not.
The idea is to override that method and return false for all the workbenchPart that we are not interested in. Lets create the view first:
<view
class="com.eclipse_tips.views.CustomPropertiesView"
icon="icons/sample.gif"
id="com.eclipse-tips.views.customePropertiesView"
name="My Properties View">
</view>
The CustomPropertiesView should extend PropertySheet and override the isImportant():
public class CustomPropertiesView extends PropertySheet {
#Override
protected boolean isImportant(IWorkbenchPart part) {
if (part.getSite().getId().equals(IPageLayout.ID_PROJECT_EXPLORER))
return true;
return false;
}
}
In this case, I'm making the view only to respond to Project Explorer and ignore other views
According to this thread, the same principle should be valid for an Editor instead of a View.
The property sheet listens to the workbench page selection provider.
The selection provider depends on what viewer/editor is active.
Each editor/viewer provides their own selection provider to use when that editor/viewer is active.
This way the property sheet doesn't care who is active, it just listens to the selection provider.
That way depending upon the view, a different set of properties are displayed.
For example, the Navigator view provides IResource selections, so the property sheet then displays IResource properties when the Navigator is active.
The Workbench Selection mechanism is illustrated in this article
The ISelectionListener is a simple interface with just one method.
A typical implementation looks like this:
private ISelectionListener mylistener = new ISelectionListener() {
public void selectionChanged(IWorkbenchPart sourcepart, ISelection selection) {
if (sourcepart != MyView.this && // 1
selection instanceof IStructuredSelection) { // 2
doSomething(((IStructuredSelection) selection).toList()); // 3
}
}
};
Depending on your requirements your listener implementation probably needs to deal with the following issues as shown in the code snippet above:
In case we also provide selections (e.g. a view or editor) we should exclude our own selection events from processing. This avoids unexpected results when the user selects elements within our part (1).
Check whether we can handle this kind of selection (2).
Get the selected content from the selection and process it (3).