Eclipse plugin - is there any editable TreeSelection class - eclipse

I am looking for a solution to make this tree selection editable in the package explorer view itself.
the idea
for example- if we click rename on any class in package explorer, it will prompt a new window to rename. This functionality is same for any class that implement TreeSelection Class.
But the Solution i am looking for is - when rename is invoked, the rename option is shown at the tree itself (like we have in Windows Explorer view)
any suggestion on how to attain this behavior on eclipse.

You don't need to have some special editable selection, you just want to make the tree editable. For this you use EditingSupport, like this (adapted from http://www.vogella.com/articles/EclipseJFaceTableAdvanced/article.html#jfacetable_editor):
public class NameEditingSupport extends EditingSupport {
private final TreeViewer viewer;
public FirstNameEditingSupport(TreeViewer viewer) {
super(viewer);
this.viewer = viewer;
}
#Override
protected CellEditor getCellEditor(Object element) {
return new TextCellEditor(viewer.getTree());
}
#Override
protected boolean canEdit(Object element) {
return true;
}
#Override
protected Object getValue(Object element) {
// return the name
}
#Override
protected void setValue(Object element, Object value) {
// update the name of your object
viewer.update(element, null);
}
}
// in the code creating the tree
treeViewer.setEditingSupport(new NameEditingSupport(treeViewer));

Related

Eclipse CDT extend AdapterFactory

I try to override the functionality of CDT ResumeAtLine, MoveToLine, RunToLine. For this reason I created a custom SuspendResumeAdapterFactory but it isn't loaded but compiles and runs without error. Do I maybe need a custom adaptableType too?
Here is the content of my plugin.xml.
<extension point="org.eclipse.core.runtime.adapters">
<factory
class="my.package.CustomSuspendResumeAdapterFactory"
adaptableType="org.eclipse.cdt.dsf.ui.viewmodel.IVMContext">
<adapter type="org.eclipse.debug.core.model.ISuspendResume"/>
</factory>
</extension>
And here my CustomSuspendResumeAdapterFactory this class is reconstructed from memory not 100% sure if the syntax is correct, but I think it should be clear to see what I want to do.
package my.package;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.debug.internal.ui.actions.MoveToLine;
import org.eclipse.cdt.dsf.debug.internal.ui.actions.ResumeAtLine;
import org.eclipse.cdt.dsf.debug.internal.ui.actions.RunToLine;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IAdapterFactory;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.ISuspendResume;
public class CustomSuspendResumeAdapterFactory implements IAdapterFactory {
static class SuspendResume implements ISuspendResume, IAdaptable {
private final CustomRunToLine fRunToLine;
private final CustomMoveToLine fMoveToLine;
private final CustomResumeAtLine fResumeAtLine;
SuspendResume(IExecutionDMContext execCtx) {
fRunToLine = new CustomRunToLine(execCtx);
fMoveToLine = new CustomMoveToLine(execCtx);
fResumeAtLine = new CustomResumeAtLine(execCtx);
}
#SuppressWarnings("unchecked")
#Override
public <T> T getAdapter(Class<T> adapter) {
if (adapter.isInstance(RunToLine.class)) {
System.out.println("CUSTOM RUNTOLINE");
return (T)fRunToLine;
}
if (adapter.isInstance(MoveToLine.class)) {
System.out.println("CUSTOM MOVETOLINE");
return (T)fMoveToLine;
}
if (adapter.isInstance(ResumeToLine.class)) {
System.out.println("CUSTOM RESUMEATLINE");
return (T)fResumeAtLine;
}
return null;
}
#Override
public boolean canResume() { return false; }
#Override
public boolean canSuspend() { return false; }
// This must return true because the platform
// RunToLineActionDelegate will only enable the
// action if we are suspended
#Override
public boolean isSuspended() { return true; }
#Override
public void resume() throws DebugException {}
#Override
public void suspend() throws DebugException {}
}
#SuppressWarnings("unchecked")
#Override
public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) {
if (ISuspendResume.class.equals(adapterType)) {
if (adaptableObject instanceof IDMVMContext) {
IExecutionDMContext execDmc = DMContexts.getAncestorOfType(
((IDMVMContext)adaptableObject).getDMContext(),
IExecutionDMContext.class);
// It only makes sense to RunToLine, MoveToLine or
// ResumeAtLine if we are dealing with a thread, not a container
if (execDmc != null && !(execDmc instanceof IContainerDMContext)) {
return (T)new SuspendResume(execDmc);
}
}
}
return null;
}
#Override
public Class<?>[] getAdapterList() {
return new Class[] { ISuspendResume.class };
}
}
Why your code is not run
You have provided a new adapter factory that converts object types that are already handled. i.e. your plugin.xml says you can convert IVMContext to ISuspendResume. But the DSF plug-in already provides such an adapter factory. If you have a new target type (like IMySpecialRunToLine) you could install a factory for that, it would take IVMContext and convert it to a IMySpecialRunToLine).
Although dated, the Eclipse Corner Article on Adapter Pattern may be useful if this is a new concept.
How to do custom Run To Line implementation
If you want to provide different implementation of Run To Line, you need to provide your own version of org.eclipse.cdt.dsf.debug.service.IRunControl2.runToLine(IExecutionDMContext, String, int, boolean, RequestMonitor). The org.eclipse.cdt.dsf.debug.internal.ui.actions.RunToLine class is simply glue to connect UI features (such as buttons/etc some provided directly, some by the core eclipse debug) to the DSF backend. i.e. if you look at what RunToLine does, all it actually does is get the IRunControl2 service and call runToLine on it.
The way to provider your own implementation of IRunControl2 is override org.eclipse.cdt.dsf.gdb.service.GdbDebugServicesFactory.createRunControlService(DsfSession) and provide your own GdbDebugServicesFactory in your custom launch delegate by overriding org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate.newServiceFactory(ILaunchConfiguration, String)
RunToLine will be triggered when the user select Run To Line from the popup menu in the editor, as per this screenshot:

Updating Eclipse JFace Treeviewer when model changes?

I am developing a RCP application with a TreeViewer. While there are good number of articles to explain how to add editing support to the Viewer (and how changes in view are updated in the model), I don't find much for updating the Treeview when the underlaying model changes. my question in short:
TreeView ----> Model updation ------ there are lots of examples
Model ----> Treeview updation ----- this is my question
Edit:
This is what I tried and it works. comments please
viewer.getTree().addKeyListener(new KeyListener(){
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
if(e.keyCode==SWT.F3){
System.out.println("F3 pressed... new element will be added");
TreeParent root = (TreeParent) viewer.getInput();
TreeParent activityRoot = (TreeParent) root.getChildren()[0];
activityRoot.addChild(new TreeObject("NEW_ACTIVITY"));
//viewer.update(root, null);
viewer.refresh();
}
}
});
The data model is provided by your content provider, TreeViewer does not provide any means of changing this data - you must do that it your own code. When you have changed to model you can use the following methods to tell the TreeViewer about the change:
If you have just changed what needs to be shown for a single item in the tree use
TreeViewer.update(object, null);
to get that item in the tree updated. There is also an array version of this to update multiple objects.
If you have added or removed objects in the tree use
TreeViewer.refresh();
to rebuild the whole tree or
TreeViewer.refresh(object);
to refresh the part of the tree start at object.
To tell the tree about adding and removing objects there are
TreeViewer.add(parent, object);
TreeViewer.remove(object);
there are also array variants of these.
To help the TreeViewer find the objects call
TreeViewer.setUseHashlookup(true);
(must be called before TreeViewer.setInput). Since this uses a hash table the objects should have sensible hashCode and equals methods. You can also use TreeViewer.setComparer to specify a different class to do the hash code and comparison.
Based on the comments in this thread,one of the eclipse corner articles on using TreeViewer and few experimenting I had created a working model.
Here are the steps:
Create a listener interface like the following
public interface TreeModelListener extends EventListener {
public void onDelete(TreeObject obj);
}
Let the tree Content provider to add listeners to each tree model item and implement this interface like below
public class TreeContentProvider implements IStructuredContentProvider,ITreeContentProvider,TreeModelListener {
TreeViewer tv;
public TreeContentProvider(TreeViewer tv){
this.tv=tv;
}
int cnt=0;
public void inputChanged(Viewer v, Object oldInput, Object newInput) {
cnt ++;
System.out.println("inputChanged() called "+oldInput+" new: "+newInput);
if(newInput!=null){
((TreeParent)newInput).setListener(this);
TreeObject []items = ((TreeParent)newInput).getChildren();
for(TreeObject obj : items){
if(obj instanceof TreeParent){
((TreeParent) obj).setListener(this);
}
}
}
}
....
#Override
public void onDelete(TreeObject obj) {
System.out.println("Delete of "+obj+" handled by content handler ");
TreeParent parent = obj.getParent();
if(parent.getChildren().length<=1){
return;
}
parent.removeChild(obj);
this.tv.refresh();
}
}
Add a method to the TreeModel class as below . And obviously TreeParent class should have an ArrayList of listeners that is being used in #1 above
public void fireChildDelete(final TreeObject obj){
if(this.listener!=null){
new Runnable(){
#Override
public void run() {
System.out.println("New thread spawned with ID "+Thread.currentThread().getId());
listener.onDelete(obj);
}
}.run();
}
}
Finally add KeyListener to the TreeViewer Object to handle Delete key as below:
tv.getTree().addKeyListener(new KeyListener(){
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
if(e.keyCode==SWT.F3){
System.out.println("F3 pressed... new element will be added");
TreeParent root = (TreeParent) tv.getInput();
TreeParent activityRoot = (TreeParent) root.getChildren()[0];
activityRoot.addChild(new TreeObject("NEW_ACTIVITY"));
//viewer.update(root, null);
tv.refresh();
}
if(e.keyCode==SWT.DEL){
System.out.println("DEL key pressed... element will be deleted "+((Tree)e.getSource()).getSelection().length);
if(((Tree)e.getSource()).getSelection().length>0){
final IStructuredSelection selection = (IStructuredSelection) tv
.getSelection();
System.out.println("DEL#2 key pressed... element will be deleted "+selection.getFirstElement().getClass());
TreeParent parent = ((TreeObject)selection.getFirstElement()).getParent();
parent.fireChildDelete((TreeObject) selection.getFirstElement());
//tv.remove(selection.getFirstElement());
//viewer.update(viewer.getInput(),null);
//tv.refresh();
}
}
}
});

How can I observe the changed state of model items in an ObservableList?

I have an ObservableList of model items. The model item is enabled for property binding (the setter fires a property changed event). The list is the content provider to a TableViewer which allows cell editing. I also intend to add a way of adding new rows (model items) via the TableViewer so the number of items in the list may vary with time.
So far, so good.
As this is all within an eclipse editor, I would like to know when the model gets changed. I just need one changed event from any changed model item in order to set the editor 'dirty'. I guess I could attach some kind of listener to each individual list item object but I wonder if there is a clever way to do it.
I think that I might have a solution. The following class is an inline Text editor. Changes to the model bean (all instances) are picked up using the listener added in doCreateElementObservable. My eclipse editor just needs to add its' own change listener to be kept informed.
public class InlineEditingSupport extends ObservableValueEditingSupport
{
private CellEditor cellEditor;
private String property;
private DataBindingContext dbc;
IChangeListener changeListener = new IChangeListener()
{
#Override
public void handleChange(ChangeEvent event)
{
for (ITableEditorChangeListener listener : listenersChange)
{
listener.changed();
}
}
};
public InlineEditingSupport(ColumnViewer viewer, DataBindingContext dbc, String property)
{
super(viewer, dbc);
cellEditor = new TextCellEditor((Composite) viewer.getControl());
this.property = property;
this.dbc = dbc;
}
protected CellEditor getCellEditor(Object element)
{
return cellEditor;
}
#Override
protected IObservableValue doCreateCellEditorObservable(CellEditor cellEditor)
{
return SWTObservables.observeText(cellEditor.getControl(), SWT.Modify);
}
#Override
protected IObservableValue doCreateElementObservable(Object element, ViewerCell cell)
{
IObservableValue value = BeansObservables.observeValue(element, property);
value.addChangeListener(changeListener); // ADD THIS LINE TO GET CHANGE EVENTS
return value;
}
private List<ITableEditorChangeListener> listenersChange = new ArrayList<ITableEditorChangeListener>();
public void addChangeListener(ITableEditorChangeListener listener)
{
listenersChange.remove(listener);
listenersChange.add(listener);
}
public void removeChangeListener(ITableEditorChangeListener listener)
{
listenersChange.remove(listener);
}
}

How do I tell a GWT cell widget data has changed via the Event Bus?

I have a GWT Cell Tree that I use to display a file structure from a CMS. I am using a AsyncDataProvider that loads data from a custom RPC class I created. I also have a Web Socket system that will broadcast events (File create, renamed, moved, deleted etc) from other clients also working in the system.
What I am trying to wrap my head around is when I recieve one of these events, how I correctly update my Cell Tree?
I suppose this problem would be analogus to having two instances of my Cell Tree on the page, which are presenting the same server-side data and wanting to ensure that when the user updated one, that the other updated as well, via using the EventBus.
I feel this should be pretty simple but I have spent about 6 hours on it now with no headway. My code is included below:
NOTE: I am not using RequestFactory even though it may look like I am it is my custom RPC framework. Also, FileEntity is just a simple representation of a file which has a name accessible by getName().
private void drawTree() {
// fileService is injected earlier on and is my own custom rpc service
TreeViewModel model = new CustomTreeModel(new FileDataProvider(fileService));
CellTree tree = new CellTree(model, "Root");
tree.setAnimationEnabled(true);
getView().getWorkspace().add(tree);
}
private static class CustomTreeModel implements TreeViewModel {
// I am trying to use a single AsyncDataProvider so I have a single point of loading data which I can manipulate (Not sure if this is the correct way to go)
public CustomTreeModel(FileDataProvider dataProvider) {
this.provider = provider;
}
public <T> NodeInfo<?> getNodeInfo(final T value) {
if (!(value instanceof FileEntity)) {
// I already have the root File loaded in my presenter, if we are at the root of the tree, I just add it via a list here
ListDataProvider<FileEntity> dataProvider = new ListDataProvider<FileEntity>();
dataProvider.getList().add(TreeWorkspacePresenter.rootFolder);
return new DefaultNodeInfo<FileEntity>(dataProvider,
new FileCell());
} else {
// Otherwise I know that we are loading some tree child data, and I invoke the AsyncProvider to load it from the server
provider.setFocusFile(value);
return new DefaultNodeInfo<FileEntity>(provider,
new FileCell());
}
}
public boolean isLeaf(Object value) {
if(value == null || value instanceof Folder)
return false;
return true;
}
}
public class FileDataProvider extends AsyncDataProvider<FileEntity> {
private FileEntity focusFile;
private FileService service;
#Inject
public FileDataProvider(FileService service){
this.service = service;
}
public void setFocusFile(FileEntity focusFile){
this.focusFile = focusFile;
}
#Override
protected void onRangeChanged(HasData<FileEntity> display) {
service.getChildren(((Folder) focusFile),
new Reciever<List<FileEntity>>() {
#Override
public void onSuccess(List<FileEntity> files) {
updateRowData(0, files);
}
#Override
public void onFailure(Throwable error) {
Window.alert(error.toString());
}
});
}
}
/**
* The cell used to render Files.
*/
public static class FileCell extends AbstractCell<FileEntity> {
private FileEntity file;
public FileEntity getFile() {
return file;
}
#Override
public void render(Context context, FileEntity file, SafeHtmlBuilder sb) {
if (file != null) {
this.file = file;
sb.appendEscaped(file.getName());
}
}
}
Currently there is no direct support for individual tree item refresh even in the latest gwt version.
But there is a workaround for this. Each tree item is associated with an value. Using this value you can get the corresponding tree item.
In your case, i assume, you know which item to update/refresh ie you know which File Entity has changed. Use this file entity to search for the corresponding tree item. Once you get the tree item you just need to expand and collapse or collapse and expand its parent item. This makes parent item to re-render its children. Your changed file entity is one among the children. So it get refreshed.
public void refreshFileEntity(FileEntity fileEntity)
{
TreeNode fileEntityNode = getFileEntityNode(fileEntity, cellTree.getRootTreeNode()
// For expnad and collapse run this for loop
for ( int i = 0; i < fileEntityNode.getParent().getChildCount(); i++ )
{
if ( !fileEntityNode.getParent().isChildLeaf( i ) )
{
fileEntityNode.getParent().setChildOpen( i, true );
}
}
}
public TreeNode getFileEntityNode(FileEntity fileEntity, TreeNode treeNode)
{
if(treeNode.getChildren == null)
{
return null;
}
for(TreeNode node : treeNode.getChildren())
{
if(fileEntity.getId().equals( node.getValue.getId() ))
{
return node;
}
getEntityNode(fileEntity, node);
}
}
You can use the dataprovider to update the celltree.
You can update the complete cell tree with:
provider.setList(pList);
provider.refresh();
If you want to update only a special cell you can get the listwrapper from the dataprovider and only set one element.
provider.getList().set(12, element);

Enable/Disbale eclispe rcp editor via menu when clicking on a TreeViewer element

In my eclipse RCP application I have a TreeViewer from where I can select different editors, for drawing elements, which show up after double clicking. In my top menu I have an option that allows to enable/disbale the drawing. The action for the editors looks like the following:
public class EnableEditorAction implements IEditorActionDelegate {
IEditor hallEditor = null;
#Override
public void run(IAction action) {
if (hallEditor != null){
hallEditor.setMachineHallEditMode(true);
}
}
#Override
public void setActiveEditor(IAction action, IEditorPart targetEditor) {
// check for enabled
boolean bEnabled = false;
if (targetEditor != null && targetEditor instanceof IMachineHallEditor) {
hallEditor = (IMachineHallEditor) targetEditor;
bEnabled = !hallEditor.isMachineHallEditingMode();
}
action.setEnabled(bEnabled);
}
#Override
public void selectionChanged(IAction action, ISelection selection) {
if (hallEditor != null) {
action.setEnabled(!hallEditor.isMachineHallEditingMode());
}
}
}
The problem I have is that the menu option is only enabled when clicking inside an editor. What i want is to enable the menu option also after clicking on one of the editors in the TreeViewer to the left.
How would I do that?
First, you don't need to check if targetEditor is null, since the action is already hooked to the editor via plugin.xml.
Second, I can see that you have an API isMachineHallEditingMode(). This should tell you if the left tree is selected, and the action should work properly.
It's important to set your action to always enabled in the plugin.xml. The Enables for: parameter should be empty, because enablement handling is done in your selectionChanged.
public class EnableEditorAction implements IEditorActionDelegate {
IEditor hallEditor;
#Override
public void run(IAction action) {
hallEditor.setMachineHallEditMode(true);
}
#Override
public void setActiveEditor(IAction action, IEditorPart targetEditor) {
hallEditor = (IMachineHallEditor) targetEditor;
}
#Override
public void selectionChanged(IAction action, ISelection selection) {
action.setEnabled(!hallEditor.isMachineHallEditingMode());
}
}