My selection listener doesn't seem to be registering properly - eclipse-rcp

I am creating an Eclipse RCP application with multiple views. One of my views is a multi-page editor view. Each of those pages has a a master/details block. I need to register all of those TableViewers as selection providers for my other view to listen to.
After much research online, I came across this article about multiple selection providers in a single view. I followed the instructions to create this selection provider for multiple viewers.
class MyMultipleSelectionProvider implements ISelectionProvider {
private final ListenerList selectionListeners = new ListenerList();
private ISelectionProvider delegate;
private final ISelectionChangedListener selectionListener = new ISelectionChangedListener() {
#Override
public void selectionChanged(final SelectionChangedEvent event) {
if (event.getSelectionProvider() == AdaptabilityProfileSelectionProvider.this.delegate) {
fireSelectionChanged( event.getSelection() );
}
}
};
/**
* Sets a new selection provider to delegate to. Selection listeners
* registered with the previous delegate are removed before.
*
* #param newDelegate new selection provider
*/
public void setSelectionProviderDelegate(final ISelectionProvider newDelegate) {
if (this.delegate == newDelegate) {
return;
}
if (this.delegate != null) {
this.delegate.removeSelectionChangedListener(this.selectionListener);
}
this.delegate = newDelegate;
if (newDelegate != null) {
newDelegate.addSelectionChangedListener(this.selectionListener);
fireSelectionChanged(newDelegate.getSelection());
}
}
#Override
public void addSelectionChangedListener(final ISelectionChangedListener listener) {
this.selectionListeners.add(listener);
}
#Override
public ISelection getSelection() {
return this.delegate == null ? null : this.delegate.getSelection();
}
#Override
public void removeSelectionChangedListener(final ISelectionChangedListener listener) {
this.selectionListeners.remove(listener);
}
#Override
public void setSelection(final ISelection selection) {
if (this.delegate != null) {
this.delegate.setSelection(selection);
}
}
protected void fireSelectionChanged(final ISelection selection) {
fireSelectionChanged(this.selectionListeners, selection);
}
private void fireSelectionChanged(final ListenerList list, final ISelection selection) {
final SelectionChangedEvent event = new SelectionChangedEvent(this.delegate, selection);
final Object[] listeners = list.getListeners();
for (int i = 0; i < listeners.length; i++) {
final ISelectionChangedListener listener = (ISelectionChangedListener) listeners[i];
listener.selectionChanged(event);
}
}
}
I added a focusListener on all of the edior's viewers so they become the delegate:
tree.addFocusListener(new FocusAdapter() {
#Override
public void focusGained(final FocusEvent e) {
editor.getSelectionProvider().setSelectionProviderDelegate(MyEditorPage.this.treeViewer);
}
});
And I registered this as the selection provider for my editor:
site.setSelectionProvider( this.selectionProvider );
Then, within my view that needs to hear about the selection, I registered a selection listener for this editor:
getSite().getPage().addSelectionListener(MyEditor.ID, this.selectionListener);
When I run the application, I see that the delegate is being changed and the selection events are being fired. However, the listener list is empty.
I am never calling addSelectionChangeListener() directly. I was under the impression that that was what the selection service is for. Am I wrong? Should I be calling it? If so, when? If not, who is supposed to be adding the listener, and why isn't it happening?

If your code is based on FormEditor (or MultiPageEditorPart) then the selection provider is set to MultiPageSelectionProvider at the end of the init method. This may be overriding your site.setSelectionProvider call.
Using:
#Override
public void init(IEditorSite site, IEditorInput input)
throws PartInitException {
super.init(site, input);
site.setSelectionProvider(this.selectionProvider);
}
should make sure your provider is the one used.

Related

propertyChange not called when restoring default values

I am building a preference page extending the FieldEditorPreferencePage class.
This is the code (some obvious code not displayed):
public class PreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
public static final String PREF_KEY_1 = "checkBoxPref";
public static final String PREF_KEY_2 = "filePref";
private FileFieldEditor pathField;
private BooleanFieldEditor yesOrNoField;
private Composite pathFieldParent;
#Override
public void init(IWorkbench workbench) {
setPreferenceStore(new ScopedPreferenceStore(InstanceScope.INSTANCE, Activator.PLUGIN_ID));
}
#Override
protected void createFieldEditors() {
this.yesOrNoField = new BooleanFieldEditor(PREF_KEY_1, "Check this box!", getFieldEditorParent());
this.pathFieldParent = getFieldEditorParent();
this.pathField = new FileFieldEditor(PREF_KEY_2, "Path:", this.pathFieldParent);
addField(this.yesOrNoField);
addField(this.pathField);
boolean isChecked = getPreferenceStore().getBoolean(PREF_KEY_1);
updatePathFieldEnablement(! isChecked);
}
/**
* Updates the fields according to entered values
*/
private void updatePathFieldEnablement(boolean enabled) {
this.pathField.setEnabled(enabled, this.pathFieldParent);
}
#SuppressWarnings("boxing")
#Override
public void propertyChange(PropertyChangeEvent event) {
if (event.getProperty().equals(FieldEditor.VALUE) && event.getSource() == this.yesOrNoField) {
updatePathFieldEnablement(! (boolean) event.getNewValue());
}
super.propertyChange(event);
}
}
The propertyChange method is there to enable/disable the FileFieldEditor depending on the BooleanFieldEditor value.
It works OK if I change the BooleanFieldEditor valeu by checking or unchecking it, but the propertyChange is not called when I hit the "Restore default values" button.
Do someone see a reason for that?
OK, I think I've got my response.
I went further in my investigation and I got to this code which seems suspect to me:
In class BooleanFieldEditor :
#Override
protected void doLoadDefault() {
if (checkBox != null) {
boolean value = getPreferenceStore().getDefaultBoolean(getPreferenceName());
checkBox.setSelection(value);
wasSelected = value;
}
}
and in class StringFieldEditor
#Override
protected void doLoadDefault() {
if (textField != null) {
String value = getPreferenceStore().getDefaultString(
getPreferenceName());
textField.setText(value);
}
valueChanged();
}
We can see that the FileFieldEditor (that inherits from StringFieldEditor) launches an PropertyChangeEvent to its listeners (valueChanged();) but not the BooleanFieldEditor. I did not find any code indicating that BooleanFieldEditor are using another mechanism. I think this is a bug in jFace.
To get around this problem, I just had to override the FieldEditorPreferencePage#performDefaults method and the result's fine.

How to enable content proposal in NatTable TextCellEditor?

I am currently looking for content assist feature in Nattable TextCellEditor.I have found the way to attach the ContentProposalAdapter and IContentProposalProvider by extending the Nattable TextCellEditor. but ,The selected value from the proposed list is not updating in the text control.
Snippet :
#Override
protected Text createEditorControl(final Composite parent, final int Style) {
this.textControl = super.createEditorControl(parent, style);
contentProposalAdapter =
new ContentProposalAdapter(this.textControl, new TextContentAdapter(), contentProposalProvider, keyStroke,
null);
contentProposalAdapter.addContentProposalListener(new IContentProposalListener() {
#Override
public void proposalAccepted(IContentProposal proposal) {
System.out.println(proposal.getContent());
}
});
}
The problem you have is the internal FocusListener that is triggered while selecting a value in the popup. To add the support you also need to override the internal FocusListener with a listener that doesn't fire if the content proposal popup is open.
An example would be to add a boolean flag that indicates that the popup is open and add a listener that sets the flag accordingly.
private boolean popupOpen = false;
...
contentProposalAdapter.addContentProposalListener(new IContentProposalListener2() {
#Override
public void proposalPopupClosed(ContentProposalAdapter adapter) {
this.popupOpen = false;
}
#Override
public void proposalPopupOpened(ContentProposalAdapter adapter) {
this.popupOpen = true;
}
});
And then implement and set a FocusListener in the constructor that takes care of that flag.
this.focusListener = new FocusAdapter() {
#Override
public void focusLost(FocusEvent e) {
if (!TextCellEditor.this.popupOpen) {
if (!commit(MoveDirectionEnum.NONE, true)) {
if (e.widget instanceof Control && !e.widget.isDisposed()) {
((Control) e.widget).forceFocus();
}
} else {
if (!TextCellEditor.this.parent.isDisposed())
TextCellEditor.this.parent.forceFocus();
}
}
}
};
In case the value should be immediately committed after it is selected, you need to add a listener that performs the commit after selection.
contentProposalAdapter.addContentProposalListener(new IContentProposalListener() {
#Override
public void proposalAccepted(IContentProposal proposal) {
commit(MoveDirectionEnum.NONE);
}
});
Unfortunately the AbstractCellEditor#InlineFocusListener is private and can therefore not be extended.
Feel free to file an enhancement ticket for NatTable to introduce the ability to easily add content proposals to a text cell editor.
https://bugs.eclipse.org/bugs/enter_bug.cgi?product=NatTable

GWT - button inside v3 google maps infowindow

I am trying to figure out how to propagate events for components inside google maps InfoWindow.
I create anchor or a button and want to handle click event on any of those.
I have found solutions described here
and
here
but those both are using google maps wrappers for gwt.
I would like to avoid those libraries.
QUESTION:
Do you know any way how can I propagate those events from info window to some GWT panel which wraps google maps?
Based on code found here:
http://gwt-maps3.googlecode.com/svn/trunk/src/com/googlecode/maps3/client/
I have created this class that solves problem with using no external library (you have to take Only InfoWindowJSO source from link given)
And then instead passing InnerHtml as string to setContent... you just pass Widget element.
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Widget;
public class InfoWindow
{
static class FakePanel extends ComplexPanel
{
public FakePanel(Widget w)
{
w.removeFromParent();
getChildren().add(w);
adopt(w);
}
#Override
public boolean isAttached()
{
return true;
}
public void detachWidget()
{
this.remove(0);
}
}
/** */
InfoWindowJSO jso;
/** If we have a widget, this will exist so we can detach later */
FakePanel widgetAttacher;
/** Keep track of this so we can get it again later */
Widget widgetContent;
/** */
public InfoWindow()
{
this.jso = InfoWindowJSO.newInstance();
}
/** */
public InfoWindow(InfoWindowOptions opts)
{
this.jso = InfoWindowJSO.newInstance(opts);
}
/** Detaches the handler and closes */
public void close()
{
this.detachWidget();
this.jso.close();
}
/** Detaches the content widget, if it exists */
private void detachWidget()
{
if (this.widgetAttacher != null)
{
this.widgetAttacher.detachWidget();
this.widgetAttacher = null;
}
}
/** */
public void open(JavaScriptObject map)
{
this.jso.open(map);
}
public void open(JavaScriptObject map, JavaScriptObject marker)
{
this.jso.open(map, marker);
}
/** */
public void setOptions(InfoWindowOptions value)
{
this.jso.setOptions(value);
}
/** */
public void setContent(String value)
{
this.widgetContent = null;
this.detachWidget();
this.jso.setContent(value);
}
/** */
public void setContent(Element value)
{
this.widgetContent = null;
this.detachWidget();
this.jso.setContent(value);
}
/** */
public void setContent(Widget value)
{
this.widgetContent = value;
this.detachWidget();
this.jso.setContent(value.getElement());
if (this.widgetAttacher == null)
{
// Add a hook for the close button click
this.jso.addListener("closeclick", new Runnable() {
#Override
public void run()
{
detachWidget();
}
});
this.widgetAttacher = new FakePanel(value);
}
else if (this.widgetAttacher.getWidget(0) != value)
{
this.widgetAttacher.detachWidget();
this.widgetAttacher = new FakePanel(value);
}
}
/** #return the widget, if a widget was set */
public Widget getContentWidget()
{
return this.widgetContent;
}
/** */
public JavaScriptObject getPosition()
{
return this.jso.getPosition();
}
/** */
public void setPosition(JavaScriptObject value)
{
this.jso.setPosition(value);
}
/** */
public int getZIndex()
{
return this.jso.getZIndex();
}
/** */
public void setZIndex(int value)
{
this.jso.setZIndex(value);
}
/** */
public void addListener(String whichEvent, Runnable handler)
{
this.jso.addListener(whichEvent, handler);
}
}
A. Browser events bubble all the way to the top of the DOM tree. You can attach your click handlers to a widget that is parent to both the maps InfoWindow and your widget. Then, when a user clicks on your button, you need to check for the source of event to make sure it came from your button.
public void onClick(final ClickEvent event) {
Element e = Element.as(event.getNativeEvent().getEventTarget());
// check if e is your button
}
B. You can create a regular GWT button, attach a ClickHandler to it. Do not put it inside the InfoWindow: place it on top it using absolute positioning and a higher z-index.
I use the static value nextAnchorId to uniquely generate IDs for each InfoWindow, and when the InfoWindow is ready (usually when you call infoWindow.open(map);), I get the anchor by element ID and add my click handler to it. This is kind of what Manolo is doing, but this implementation doesn't require gwtquery, which means that I can run my code in Super Dev Mode.
private static int nextAnchorId = 1;
public InfoWindow makeInfo() {
InfoWindowOptions infoWindowOptions = InfoWindowOptions.create();
FlowPanel infoContentWidget = new FlowPanel();
final String theAnchorId_str = "theAnchor" + nextAnchorId;
HTML theAnchor = new HTML("<a id=\"" + theAnchorId_str + "\">Click me!</a>");
infoContentWidget.add(theAnchor);
infoWindowOptions.setContent(infoContentWidget.getElement());
InfoWindow infoWindow = InfoWindow.create(infoWindowOptions);
infoWindow.addDomReadyListenerOnce(new InfoWindow.DomReadyHandler() {
#Override
public void handle() {
com.google.gwt.user.client.Element muffinButton = (com.google.gwt.user.client.Element) Document.get().getElementById(theAnchorId_str);
DOM.sinkEvents(muffinButton, Event.ONCLICK);
DOM.setEventListener(muffinButton, new EventListener() {
#Override
public void onBrowserEvent(Event event) {
Window.alert("You clicked on the anchor!");
// This is where your click handling for the link goes.
}
});
}
});
nextAnchorId++;
return infoWindow
}
A very simple solution is to use gwtquery:
Identify the anchor in the map you want to add the click handler and define a css selector for that (for instance id=my_link)
Use gquery to locate it and to add the event.
$('#my_link').click(new Function() {
public boolean f(Event e) {
[...]
return false; //false means stop propagation and prevent default
}
});
Note that gwtquery is not a wrapper of jquery but an entire gwt implementation of its api, so including it in your project will not overload it, and the compiler will pick up just the stuff you use.

Getting header column names in Cell table on click in GWT

I am using Cell Table of GWT 2.2 version. I want to get the name of the header column on which I have clicked. I didn't get any click event on the same.
Is there any work around by which I can accomplish my task.
Something like this? ;)
public class CellTableExample implements EntryPoint, ClickHandler {
private static class SomeEntity {
/* ... */
}
private static class ClickableTextHeader extends TextHeader {
private ClickHandler handler;
public ClickableTextHeader(String text, ClickHandler handler) {
super(text);
this.handler = handler;
}
#Override
public void onBrowserEvent(Context context, final Element elem,
final NativeEvent event) {
//maybe hijack click event
if(handler != null) {
if(Event.ONCLICK == Event.getTypeInt(event.getType())) {
handler.onClick(new ClickEvent() {
{
setNativeEvent(event);
setRelativeElement(elem);
setSource(ClickableTextHeader.this);
}
});
}
}
//default dom event handler
super.onBrowserEvent(context, elem, event);
}
}
CellTable<SomeEntity> cellTable;
TextColumn<SomeEntity> firstColumn;
TextColumn<SomeEntity> secondColumn;
TextColumn<SomeEntity> thirdColumn;
#Override
public void onModuleLoad() {
/* somehow init columns - it's not the point for this example */
cellTable.addColumn(firstColumn, new ClickableTextHeader("First column header", this));
cellTable.addColumn(secondColumn, new ClickableTextHeader("Second column header", this));
cellTable.addColumn(thirdColumn, new ClickableTextHeader("Third column header", this));
}
#Override
public void onClick(ClickEvent event) {
ClickableTextHeader source = (ClickableTextHeader) event.getSource();
Window.alert(source.getValue());
}
}
Hijacking event could look simpler if we used "simple listener interface" - i just wanted to be "semanticaly compliant with out-of-the-box Handlers" :)

GWT: how can i add/remove a button in a celltable on the go

I have this button cell in my CellTable
ButtonCell reListCell = new ButtonCell();
reListColumn = new Column<EmployerJobs, String>(reListCell) {
#Override
public String getValue(EmployerJobs object) {
return "ReList";
}
};
ctJobs.addColumn(reListColumn,
EmployerDashBoardConstants.EMPTYHEADERCOLUMN);
but i only want this cell to be appear if the below condition pass
public void getDateDiff(final EmployerJobs object) {
rpcService.getDateDiff(object.getJobValidDate(), new AsyncCallback<Boolean>() {
public void onFailure(Throwable caught) {
}
public void onSuccess(Boolean jobExpired) {
if(jobExpired) {
// HERE I WANT TO SHOW MY RELISTCELL, means if the job is expired only then
// there will be a button showing relist would be appear in that row ,for
// the jobs which are not expired NO button should appear..
}
}
});
}
how can i achieve this?
thanks
I agree with DTing.
Quering the backend for each cell/row is not really efficient.
I would rather put the info (jobExpired) into your EmployerJobs class and transfer the info when you request the list of your EmployerJobs to be displayed in your CellTable.
You can update the list periodically to account for changes (see the expenses sample on how to do that).
But to your initial question (hiding the cell). There are two solutions:
Use an ActionCell and override the render method.
ActionCell:
ActionCell<EmployerJobs> reListCell = new ActionCell<EmployerJobs>("ReList",
new ActionCell.Delegate<EmployerJobs>() {
#Override
public void execute(EmployerJobs object) {
// code to be executed
}
})
{
#Override
public void render(Cell.Context context,EmployerJobs value,SafeHtmlBuilder sb) {
if (value.isJobExpired()) // isJobExpired returns the field jobExpired.
super.render(context,value,sb);
}
};
reListColumn = new Column<EmployerJobs, EmployerJobs>(reListCell) {
#Override
public String getValue(EmployerJobs object) {
return object;
}
};
ctJobs.addColumn(reListColumn,
EmployerDashBoardConstants.EMPTYHEADERCOLUMN);
Use a ButtonCell and override the render method of your Column.
ButtonCell:
ButtonCell reListCell = new ButtonCell();
reListColumn = new Column<EmployerJobs, String>(reListCell) {
#Override
public String getValue(EmployerJobs object) {
return "ReList";
}
#Override
public void render(Cell.Context context,EmployerJobs object,SafeHtmlBuilder sb) {
if (value.isJobExpired()) // isJobExpired returns the field jobExpired.
super.render(context,value,sb);
}
};
ctJobs.addColumn(reListColumn,
EmployerDashBoardConstants.EMPTYHEADERCOLUMN);
Just tried Umit solution #2 ButtonCell. It works!
To link an specific action to the button, reListColumn.setFieldUpdater(new FieldUpdater....
would be needed
I tried ButtonCell solution too. But if you click in a cell who as no button then an error on client side occur:
com.google.gwt.core.client.JavaScriptException: (TypeError) #com.google.gwt.core.client.impl.Impl::apply(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)([JavaScript object(445), JavaScript object(240), JavaScript object(637)]): parent is null
So I added this to override the Event and avoid Event if I want:
#Override
public void onBrowserEvent(com.google.gwt.cell.client.Cell.Context context,
Element parent, YourObject object, NativeEvent event) {
if (object.isCompleted())
super.onBrowserEvent( context, parent, object, event);
}
I don't know if it's the better way to do it but it works.