How to set selection for treeviewer without firing selectionChanged()? - java

I am working on an eclipse RCP project. It has several views with TreeViewer and I use selectionChanged() method to handle selection events. However, at times I need to set selection programmatically.For this, in selectionChanged() method, I invoke setSelection() method of TreeViewer to set the desired selection. This method fires selectionChanged() method of all views thus resulting in cyclic calls to selectionChanged().
How can I select an item from the TreeViewer or StructuredViewer without firing selectionChanged() for other views?

Well, creating an event that triggers the Listener you're currently in is always a tricky situation. What I usually do is something along those lines:
Listener listener = new Listener()
{
private boolean ignore = false;
#Override
public void handleEvent(Event e)
{
if(ignore)
return;
ignore = true;
doPotentiallyCyclicStuff();
ignore = false;
}
};
It's not a very pretty solution, but it does work.
Looking forward to alternative solutions here, since this has been bothering me for a while now.

Related

How to implement pager change event in Tapestry's grid

How to implement pager change event in Tapestry's grid? I've read the documentation, but couldn't find the answer. So what I'm trying to do is, I want to reset the selectedItem to null everytime the selected page changing, and I noticed that actually setupRender() triggered, but I don't want to use it because it does a quite heavy process. There's no point to do this only for page changes. So is there any way to implement it specifically only for pager changes? Thank you. I'm using Tapestry 5.3.8 btw
If I understand your question correctly, you should implement your own GridPager and, for example, emit the event when a page is changing (you can then handle this event within page class). For example:
#Events({ PAGE_CHANGE_EVENT, InternalConstants.GRID_INPLACE_UPDATE + " (internal event)" })
public class CustomeGridPager
{
...
void onAction(int newPage)
{
currentPage = newPage;
this.resources.triggerEvent(PAGE_CHANGE_EVENT, new Object[] { newPage }, null);
}
}

Why Handler doesn't work while Listener does his job?

I struggle to catch events after famous DOM.appendChild. After many attempts of use handlers I found this answer where #Ă•zbek did the job via listener.
And now I don't get it why "listener" works while "handler" not.
As an example in code:
Button button = new Button("Test button");
DOM.appendChild(getElement(), button.getElement());
button.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
GWT.log("this doesn't work");
}
});
DOM.sinkEvents(button.getElement(), Event.ONCLICK);
DOM.setEventListener(button.getElement(), new EventListener() {
public void onBrowserEvent(Event event) {
GWT.log("this works perfectly!");
}
});
the listener will be working but handler not.
So where is difference beetwen them?
And how to force handler to work?
Is there are some way to do this on handlers ?
I'm trying to understand the difference between listeners and handlers. I read these answers which shows that there is no big difference but I still don't get it
Button is a Widget not an element, and if you add it as an element to the dom you would lose the events. You can either append it to the RootPanel:
RootPanel.get().add(button) if you want to use the handler.
if you want to use the element not the widget you can use DOM.createButton(), and use the listener.
setEventListener of widgets is only called when they are "attached" (to a parent widget, which is itself attached, or delayed until it is attached), and you never "attach" the button.
With an explicit serEventListener you completely bypass the widget internals (and lifecycle). You could actually just use a ButtonElement in this case.
TL;DR: don't do that, it's a symptom of a broken design.

Updating the JFace TreeViewer after the underlying model changes

I have a tree viewer in my view, which listens to EMF models from the standard Ecore editor and does further things with it. I have already registered a selection listener, which checks whether the selected elements are the types the tree viewer needs as input. So the problem is that if there any changes in the model (e.g. adding a new element or new information to an existing element etc.) the tree viewer shows the changed model only if the user changes the selection, i.e. clicks to any model element etc.
But what I need to do is, that the tree viewer gets directly notified if the underlying model changes and shows the new model element too without being have to click on the model to listen it.
I have found the following eclipse corner article ( https://www.eclipse.org/articles/Article-TreeViewer/TreeViewerArticle.htm#inputChanged ) and from "Responding th change" it seems that the inputChanged() and refresh() methods might be the solution I am looking for, isn't it?
Still I was wondering if there is maybe an easier way to do this without being have to change the model code, but only by making changes in the UI code? Thanks!
You can call the TreeViewer refresh() method to get it to refresh the whole tree from the model, or refresh(Object) to refresh the tree starting at the given model object.
If the tree structure has not changed you can call update(Object) to just update the display of a single object.
There are also add and remove methods for when you add and remove objects from the model tree.
Some of the methods also have Object [] variants so you can modify several objects at once.
Update:
Your model should support generating a model changed event which the content provider can listen to. You would set up this listener in the content provider inputChanged method and remove it in the dispose method. When model change events are received use the various TreeViewer methods to update the tree.
An example of how all this is used are the Eclipse views which show the files in the workspace (such as the Navigator view). The content provider for these uses the workspace resource change listener (IResourceChangeListener) to be notified of changes to the workspace and using the information in the event calls the methods I listed above to update the tree.
Update 2:
An example of using IResourceChangeListener in a content provider, extracted from org.eclipse.ui.views.tasklist.TaskListContentProvider
class TaskListContentProvider
implements IStructuredContentProvider, IResourceChangeListener
{
private TableViewer viewer;
private IResource input;
... other methods ....
public void dispose() {
if (input != null) {
input.getWorkspace().removeResourceChangeListener(this);
input = null;
}
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
if (input != null) {
input.getWorkspace().removeResourceChangeListener(this);
}
input = (IResource) newInput;
if (input != null) {
input.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
}
viewer = (TableViewer) viewer;
}
public void resourceChanged(IResourceChangeEvent event) {
... use resource change event to update viewer
}
}

Easier Custom Events Handling in Java

In Java, everytime I want to create a new custom event, I usually do it by add 3 methods namely:
addDogEventListener(EventListener listener);
removeDogEventListener(EventListener listener);
dispatchDogEventListener(DogEvent event);
Then now if I want to dispatch another event, say CatEvent, I will have to create all these 3 methods again:
addCatEventListener(EventListener listener);
removeCatEventListener(EventListener listener);
dispatchCatEventListener(CatEvent event);
Then if I want to manage just one kind of CatEvent event, say Meow, I have to copy and paste all these 3 methods again?! Like addCatMeowEventListener();... etc?
And usually, I need to dispatch more than one kind of events. It will be very untidy to have the whole class filled with so many methods to transmit and handle the events. Not only that, these functions have very similar code, like loop through the EventListenerList, add event to the list, etc.
Is this how I should do event dispatching in Java?
Is there a way like I can do it like:
mainApp.addEventListener(CatEvent.MEOW, new EventHandler() { meowHandler(Event e) { });
mainApp.addEventListener(CatEvent.EAT, new EventHandler() { eatHandler(Event e) { });
myCat.addEventListener(DogEvent.BARK, new EventHandler() { barkHandler(Event e) { myCat.run() });
In this way, I can just handle the different types of CatEvent in different eventHandler class and functions and I don't have to keep creating different event listener methods for different events?
Maybe I am missing something out about Java's event handling but is there a neater way that I don't have to keep copy and paste the 3 methods plus creating so many different kind of event objects for every different kind of methods I want to dispatch?
Thanks!
The event handling strategy I have is to publish by type which may suit you.
I have a broker which can examine the listener for an annotation which marks a method as listening to events. Using this approach you only need to add methods when you want to handle a specific class of event.
interface Subscriber { // marker interface for OSGi
}
#interface SubscriberCallback { // marker annotation
}
class Broker {
// uses reflections to find methods marked with #SubscriberCallback
public void addSubscriber(Subscriber subscriber);
public void removeSubscriber(Subscriber subscriber);
public <T> void publish(T... events);
}
class MyListener implements Subscriber {
#SubscriberCallback
public void onDogEvent(DogEvent... dogEvents) {
// called for one or more dog events
}
#SubscriberCallback
public void onCatEvent(CatEvent catEvent) {
// called for each CatEvent including subsclass of CatEvent.
}
}
Then if I want to manage just one kind of CatEvent event, say Meow, (and EAT)
The "action" of the event (MEOW or EAT) should be data defined in the CatEvent. Then your event listening code would check the action type and do the appropriate processing.
Maybe take a look at the TableModelEvent to see how this is done. It handles "insert", "delete" and "update" events using the same event.
Also you could probably model a general event listener based on the PropertyChangeListener. A PropertyChangeListener is used to handle events when various properties change on a Swing component. For example when you invoke setForeground() or setBackground() or setFont() or setText() or setIcon. The PropertyChangeListener uses a getName() method to determine which property has been changed. So for the above methods the names would be "foreground", "background", "font", "text", "icon". See How to Use Property Changes Listeners for an example of how this might work.
In your case the names would be "cat" and "dog". This approach would only work if the GeneralEvent you create can contain information that is relevant to each of your events (ie. "meow" and "bark").

Eclipse Plugin: Enablement of an Action based on the current selection

I am using the org.eclipse.ui.popupMenus extension point for adding a sub-menu whose Action that is bounded to the following class:
public class MyAction implements IObjectActionDelegate {
private Logic logic = Logic.getInstance(); // Singleton
public void setActivePart(IAction a, IWorkbenchPart targetPart) {
// Nothing here
}
public void run(IAction a) {
// Do something...
}
public void selectionChanged(IAction a, ISelection s) {
a.setEnabled(logic.isEnabled(s));
}
}
This action is working correctly in most cases (including the call a.setEnabled() in selectionChanged()).
My problem at the very first time my action is being invoked. The selectionChanged method is called only after the menu item has been displayed (and not when the user has made the selection) which means that the call to a.setEnabled() will have no affect.
Any ideas on how to make my action receive selectionChanged() notifications even before the fist time it is being invoked?
Eclipse uses so-called lazy plugin activation, so it first derives as much as possible from plugin.xml, and the behaviour you are observing is well-documented in the API. See this related question.
The words first time and after make me wonder about a synchronization problem. If the initialization of Logic.getInstance() is deferred, you might look at the Initialization On Demand Holder Idiom, also discussed in item 71 of Effective Java.

Categories

Resources